ceph: introduce global empty snap context
authorYan, Zheng <zyan@redhat.com>
Thu, 6 Nov 2014 07:09:41 +0000 (15:09 +0800)
committerIlya Dryomov <idryomov@redhat.com>
Wed, 17 Dec 2014 17:09:51 +0000 (20:09 +0300)
Current snaphost code does not properly handle moving inode from one
empty snap realm to another empty snap realm. After changing inode's
snap realm, some dirty pages' snap context can be not equal to inode's
i_head_snap. This can trigger BUG() in ceph_put_wrbuffer_cap_refs()

The fix is introduce a global empty snap context for all empty snap
realm. This avoids triggering the BUG() for filesystem with no snapshot.

Fixes: http://tracker.ceph.com/issues/9928

Signed-off-by: Yan, Zheng <zyan@redhat.com>
Reviewed-by: Ilya Dryomov <idryomov@redhat.com>
fs/ceph/snap.c
fs/ceph/super.c
fs/ceph/super.h

index c1cc993225e3e31d536ea5a2d5f61034763ba836..f64576e72b7966d9a00428feea63d044c5a8252b 100644 (file)
@@ -288,6 +288,9 @@ static int cmpu64_rev(const void *a, const void *b)
        return 0;
 }
 
+
+static struct ceph_snap_context *empty_snapc;
+
 /*
  * build the snap context for a given realm.
  */
@@ -328,6 +331,12 @@ static int build_snap_context(struct ceph_snap_realm *realm)
                return 0;
        }
 
+       if (num == 0 && realm->seq == empty_snapc->seq) {
+               ceph_get_snap_context(empty_snapc);
+               snapc = empty_snapc;
+               goto done;
+       }
+
        /* alloc new snap context */
        err = -ENOMEM;
        if (num > (SIZE_MAX - sizeof(*snapc)) / sizeof(u64))
@@ -365,6 +374,7 @@ static int build_snap_context(struct ceph_snap_realm *realm)
             realm->ino, realm, snapc, snapc->seq,
             (unsigned int) snapc->num_snaps);
 
+done:
        ceph_put_snap_context(realm->cached_context);
        realm->cached_context = snapc;
        return 0;
@@ -465,6 +475,9 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci)
                   cap_snap.  lucky us. */
                dout("queue_cap_snap %p already pending\n", inode);
                kfree(capsnap);
+       } else if (ci->i_snap_realm->cached_context == empty_snapc) {
+               dout("queue_cap_snap %p empty snapc\n", inode);
+               kfree(capsnap);
        } else if (dirty & (CEPH_CAP_AUTH_EXCL|CEPH_CAP_XATTR_EXCL|
                            CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR)) {
                struct ceph_snap_context *snapc = ci->i_head_snapc;
@@ -925,5 +938,16 @@ out:
        return;
 }
 
+int __init ceph_snap_init(void)
+{
+       empty_snapc = ceph_create_snap_context(0, GFP_NOFS);
+       if (!empty_snapc)
+               return -ENOMEM;
+       empty_snapc->seq = 1;
+       return 0;
+}
 
-
+void ceph_snap_exit(void)
+{
+       ceph_put_snap_context(empty_snapc);
+}
index f6e12377335c1a2aa030e86f1d903daa365792b1..3b5c1e3335db80f89182969570847e3494189458 100644 (file)
@@ -1028,15 +1028,20 @@ static int __init init_ceph(void)
 
        ceph_flock_init();
        ceph_xattr_init();
+       ret = ceph_snap_init();
+       if (ret)
+               goto out_xattr;
        ret = register_filesystem(&ceph_fs_type);
        if (ret)
-               goto out_icache;
+               goto out_snap;
 
        pr_info("loaded (mds proto %d)\n", CEPH_MDSC_PROTOCOL);
 
        return 0;
 
-out_icache:
+out_snap:
+       ceph_snap_exit();
+out_xattr:
        ceph_xattr_exit();
        destroy_caches();
 out:
@@ -1047,6 +1052,7 @@ static void __exit exit_ceph(void)
 {
        dout("exit_ceph\n");
        unregister_filesystem(&ceph_fs_type);
+       ceph_snap_exit();
        ceph_xattr_exit();
        destroy_caches();
 }
index aca22879b41f3769f19ac47065fad8a916eb441f..fc1c8255dead1645a5153ce933fad8884e5bfe98 100644 (file)
@@ -699,6 +699,8 @@ extern void ceph_queue_cap_snap(struct ceph_inode_info *ci);
 extern int __ceph_finish_cap_snap(struct ceph_inode_info *ci,
                                  struct ceph_cap_snap *capsnap);
 extern void ceph_cleanup_empty_realms(struct ceph_mds_client *mdsc);
+extern int ceph_snap_init(void);
+extern void ceph_snap_exit(void);
 
 /*
  * a cap_snap is "pending" if it is still awaiting an in-progress