From: Yan, Zheng Date: Wed, 24 Jul 2013 04:22:11 +0000 (+0800) Subject: ceph: fix freeing inode vs removing session caps race X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=6f60f889470aecf747610279545c054a99aadca3;p=linux-beck.git 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 --- diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 55aaddb4047e..3b0abed667c2 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -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 */ diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 6b40d8112c64..cbf08203e00d 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -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); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index cbded572345e..afcd62a68916 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -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);