Skip to content

Commit

Permalink
ceph: fix freeing inode vs removing session caps race
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yan, Zheng authored and Sage Weil committed Aug 10, 2013
1 parent 4d1829a commit 6f60f88
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 0 deletions.
8 changes: 8 additions & 0 deletions fs/ceph/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino)
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
*/
Expand Down
31 changes: 31 additions & 0 deletions fs/ceph/mds_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,37 @@ static void remove_session_caps(struct ceph_mds_session *session)
{
dout("remove_session_caps on %p\n", session);
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(!list_empty(&session->s_cap_flushing));
cleanup_cap_releases(session);
Expand Down
2 changes: 2 additions & 0 deletions fs/ceph/super.h
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,8 @@ extern void ceph_destroy_inode(struct inode *inode);

extern struct inode *ceph_get_inode(struct super_block *sb,
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 int ceph_fill_file_size(struct inode *inode, int issued,
u32 truncate_seq, u64 truncate_size, u64 size);
Expand Down

0 comments on commit 6f60f88

Please sign in to comment.