ceph: fix freeing inode vs removing session caps race
authorYan, Zheng <zheng.z.yan@intel.com>
Wed, 24 Jul 2013 04:22:11 +0000 (12:22 +0800)
committerSage Weil <sage@inktank.com>
Sat, 10 Aug 2013 00:55:32 +0000 (17:55 -0700)
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>
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/super.h

index 55aaddb4047ea9ca01dc9d0ea203e562e58dccd8..3b0abed667c2e79b8cbef7ed654868a70bf91ed3 100644 (file)
@@ -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
  */
index 6b40d8112c64080e800ec5e2722dcfca15b76ad5..cbf08203e00dce56e472ca0ac7ff76b3a71bf832 100644 (file)
@@ -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);
index cbded572345e77a107e539aa4e433d6f6f7964c0..afcd62a68916e358676d4fa4e7b7abf3c94ecb43 100644 (file)
@@ -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);