ceph: fix freeing inode vs removing session caps race
remove_session_caps() uses iterate_session_caps() to remove caps, but iterate_session_caps() skips inodes that are being deleted. So session->s_nr_caps can be non-zero after iterate_session_caps() return. We can fix the issue by waiting until deletions are complete. __wait_on_freeing_inode() is designed for the job, but it is not exported, so we use lookup inode function to access it. Signed-off-by: Yan, Zheng <zheng.z.yan@intel.com>
This commit is contained in:
parent
4d1829a59d
commit
6f60f88947
|
@ -61,6 +61,14 @@ struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino)
|
||||||
return inode;
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct inode *ceph_lookup_inode(struct super_block *sb, struct ceph_vino vino)
|
||||||
|
{
|
||||||
|
struct inode *inode;
|
||||||
|
ino_t t = ceph_vino_to_ino(vino);
|
||||||
|
inode = ilookup5_nowait(sb, t, ceph_ino_compare, &vino);
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get/constuct snapdir inode for a given directory
|
* get/constuct snapdir inode for a given directory
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1031,6 +1031,37 @@ static void remove_session_caps(struct ceph_mds_session *session)
|
||||||
{
|
{
|
||||||
dout("remove_session_caps on %p\n", session);
|
dout("remove_session_caps on %p\n", session);
|
||||||
iterate_session_caps(session, remove_session_caps_cb, NULL);
|
iterate_session_caps(session, remove_session_caps_cb, NULL);
|
||||||
|
|
||||||
|
spin_lock(&session->s_cap_lock);
|
||||||
|
if (session->s_nr_caps > 0) {
|
||||||
|
struct super_block *sb = session->s_mdsc->fsc->sb;
|
||||||
|
struct inode *inode;
|
||||||
|
struct ceph_cap *cap, *prev = NULL;
|
||||||
|
struct ceph_vino vino;
|
||||||
|
/*
|
||||||
|
* iterate_session_caps() skips inodes that are being
|
||||||
|
* deleted, we need to wait until deletions are complete.
|
||||||
|
* __wait_on_freeing_inode() is designed for the job,
|
||||||
|
* but it is not exported, so use lookup inode function
|
||||||
|
* to access it.
|
||||||
|
*/
|
||||||
|
while (!list_empty(&session->s_caps)) {
|
||||||
|
cap = list_entry(session->s_caps.next,
|
||||||
|
struct ceph_cap, session_caps);
|
||||||
|
if (cap == prev)
|
||||||
|
break;
|
||||||
|
prev = cap;
|
||||||
|
vino = cap->ci->i_vino;
|
||||||
|
spin_unlock(&session->s_cap_lock);
|
||||||
|
|
||||||
|
inode = ceph_lookup_inode(sb, vino);
|
||||||
|
iput(inode);
|
||||||
|
|
||||||
|
spin_lock(&session->s_cap_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&session->s_cap_lock);
|
||||||
|
|
||||||
BUG_ON(session->s_nr_caps > 0);
|
BUG_ON(session->s_nr_caps > 0);
|
||||||
BUG_ON(!list_empty(&session->s_cap_flushing));
|
BUG_ON(!list_empty(&session->s_cap_flushing));
|
||||||
cleanup_cap_releases(session);
|
cleanup_cap_releases(session);
|
||||||
|
|
|
@ -677,6 +677,8 @@ extern void ceph_destroy_inode(struct inode *inode);
|
||||||
|
|
||||||
extern struct inode *ceph_get_inode(struct super_block *sb,
|
extern struct inode *ceph_get_inode(struct super_block *sb,
|
||||||
struct ceph_vino vino);
|
struct ceph_vino vino);
|
||||||
|
extern struct inode *ceph_lookup_inode(struct super_block *sb,
|
||||||
|
struct ceph_vino vino);
|
||||||
extern struct inode *ceph_get_snapdir(struct inode *parent);
|
extern struct inode *ceph_get_snapdir(struct inode *parent);
|
||||||
extern int ceph_fill_file_size(struct inode *inode, int issued,
|
extern int ceph_fill_file_size(struct inode *inode, int issued,
|
||||||
u32 truncate_seq, u64 truncate_size, u64 size);
|
u32 truncate_seq, u64 truncate_size, u64 size);
|
||||||
|
|
Loading…
Reference in New Issue