aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYan, Zheng <zyan@redhat.com>2014-11-06 02:09:41 -0500
committerIlya Dryomov <idryomov@redhat.com>2014-12-17 12:09:51 -0500
commit97c85a828f36bbfffe9d77b977b65a5872b6cad4 (patch)
treedd432537785a075981f8e1ea5525b5f4f0006cf3
parent7cfa0313d0dc52e4da9f196f8ad5bfdf266fc1fb (diff)
ceph: introduce global empty snap context
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>
-rw-r--r--fs/ceph/snap.c26
-rw-r--r--fs/ceph/super.c10
-rw-r--r--fs/ceph/super.h2
3 files changed, 35 insertions, 3 deletions
diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c
index c1cc993225e3..f64576e72b79 100644
--- a/fs/ceph/snap.c
+++ b/fs/ceph/snap.c
@@ -288,6 +288,9 @@ static int cmpu64_rev(const void *a, const void *b)
288 return 0; 288 return 0;
289} 289}
290 290
291
292static struct ceph_snap_context *empty_snapc;
293
291/* 294/*
292 * build the snap context for a given realm. 295 * build the snap context for a given realm.
293 */ 296 */
@@ -328,6 +331,12 @@ static int build_snap_context(struct ceph_snap_realm *realm)
328 return 0; 331 return 0;
329 } 332 }
330 333
334 if (num == 0 && realm->seq == empty_snapc->seq) {
335 ceph_get_snap_context(empty_snapc);
336 snapc = empty_snapc;
337 goto done;
338 }
339
331 /* alloc new snap context */ 340 /* alloc new snap context */
332 err = -ENOMEM; 341 err = -ENOMEM;
333 if (num > (SIZE_MAX - sizeof(*snapc)) / sizeof(u64)) 342 if (num > (SIZE_MAX - sizeof(*snapc)) / sizeof(u64))
@@ -365,6 +374,7 @@ static int build_snap_context(struct ceph_snap_realm *realm)
365 realm->ino, realm, snapc, snapc->seq, 374 realm->ino, realm, snapc, snapc->seq,
366 (unsigned int) snapc->num_snaps); 375 (unsigned int) snapc->num_snaps);
367 376
377done:
368 ceph_put_snap_context(realm->cached_context); 378 ceph_put_snap_context(realm->cached_context);
369 realm->cached_context = snapc; 379 realm->cached_context = snapc;
370 return 0; 380 return 0;
@@ -465,6 +475,9 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci)
465 cap_snap. lucky us. */ 475 cap_snap. lucky us. */
466 dout("queue_cap_snap %p already pending\n", inode); 476 dout("queue_cap_snap %p already pending\n", inode);
467 kfree(capsnap); 477 kfree(capsnap);
478 } else if (ci->i_snap_realm->cached_context == empty_snapc) {
479 dout("queue_cap_snap %p empty snapc\n", inode);
480 kfree(capsnap);
468 } else if (dirty & (CEPH_CAP_AUTH_EXCL|CEPH_CAP_XATTR_EXCL| 481 } else if (dirty & (CEPH_CAP_AUTH_EXCL|CEPH_CAP_XATTR_EXCL|
469 CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR)) { 482 CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR)) {
470 struct ceph_snap_context *snapc = ci->i_head_snapc; 483 struct ceph_snap_context *snapc = ci->i_head_snapc;
@@ -925,5 +938,16 @@ out:
925 return; 938 return;
926} 939}
927 940
941int __init ceph_snap_init(void)
942{
943 empty_snapc = ceph_create_snap_context(0, GFP_NOFS);
944 if (!empty_snapc)
945 return -ENOMEM;
946 empty_snapc->seq = 1;
947 return 0;
948}
928 949
929 950void ceph_snap_exit(void)
951{
952 ceph_put_snap_context(empty_snapc);
953}
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index f6e12377335c..3b5c1e3335db 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -1028,15 +1028,20 @@ static int __init init_ceph(void)
1028 1028
1029 ceph_flock_init(); 1029 ceph_flock_init();
1030 ceph_xattr_init(); 1030 ceph_xattr_init();
1031 ret = ceph_snap_init();
1032 if (ret)
1033 goto out_xattr;
1031 ret = register_filesystem(&ceph_fs_type); 1034 ret = register_filesystem(&ceph_fs_type);
1032 if (ret) 1035 if (ret)
1033 goto out_icache; 1036 goto out_snap;
1034 1037
1035 pr_info("loaded (mds proto %d)\n", CEPH_MDSC_PROTOCOL); 1038 pr_info("loaded (mds proto %d)\n", CEPH_MDSC_PROTOCOL);
1036 1039
1037 return 0; 1040 return 0;
1038 1041
1039out_icache: 1042out_snap:
1043 ceph_snap_exit();
1044out_xattr:
1040 ceph_xattr_exit(); 1045 ceph_xattr_exit();
1041 destroy_caches(); 1046 destroy_caches();
1042out: 1047out:
@@ -1047,6 +1052,7 @@ static void __exit exit_ceph(void)
1047{ 1052{
1048 dout("exit_ceph\n"); 1053 dout("exit_ceph\n");
1049 unregister_filesystem(&ceph_fs_type); 1054 unregister_filesystem(&ceph_fs_type);
1055 ceph_snap_exit();
1050 ceph_xattr_exit(); 1056 ceph_xattr_exit();
1051 destroy_caches(); 1057 destroy_caches();
1052} 1058}
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index aca22879b41f..fc1c8255dead 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -699,6 +699,8 @@ extern void ceph_queue_cap_snap(struct ceph_inode_info *ci);
699extern int __ceph_finish_cap_snap(struct ceph_inode_info *ci, 699extern int __ceph_finish_cap_snap(struct ceph_inode_info *ci,
700 struct ceph_cap_snap *capsnap); 700 struct ceph_cap_snap *capsnap);
701extern void ceph_cleanup_empty_realms(struct ceph_mds_client *mdsc); 701extern void ceph_cleanup_empty_realms(struct ceph_mds_client *mdsc);
702extern int ceph_snap_init(void);
703extern void ceph_snap_exit(void);
702 704
703/* 705/*
704 * a cap_snap is "pending" if it is still awaiting an in-progress 706 * a cap_snap is "pending" if it is still awaiting an in-progress