]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: check for watchers before trimming an image on 'rbd rm'
authorIlya Dryomov <ilya.dryomov@inktank.com>
Wed, 29 Jan 2014 14:12:01 +0000 (16:12 +0200)
committerIlya Dryomov <ilya.dryomov@inktank.com>
Thu, 30 Jan 2014 12:47:45 +0000 (14:47 +0200)
Check for watchers before trimming image data to try to avoid getting
into the following situation:

  - user does 'rbd rm' on a mapped image with an fs mounted from it
  - 'rbd rm' trims (removes) all image data, only header is left
  - 'rbd rm' tries to remove a header and fails because krbd has a
    watcher registered on the header
  - at this point image cannot be unmapped because of the mounted fs
  - fs cannot be unmounted because all its data and metadata is gone

Unfortunately, this fix doesn't make it impossible to happen (the
required atomicity isn't there), but it's a big improvement over the
status quo.

Fixes: http://tracker.ceph.com/issues/7076
Signed-off-by: Ilya Dryomov <ilya.dryomov@inktank.com>
src/librbd/internal.cc
src/test/pybind/test_rbd.py

index 3d73bd0251b73c21ac6d2018bd56789b3e324a16..2cd6b8d8565b756b1ac5b6bd81eec9c5993f3298 100644 (file)
@@ -1353,15 +1353,31 @@ reprotect_and_return_err:
     if (r < 0) {
       ldout(cct, 2) << "error opening image: " << cpp_strerror(-r) << dendl;
     } else {
+      string header_oid = ictx->header_oid;
+      old_format = ictx->old_format;
+      unknown_format = false;
+      id = ictx->id;
+
       if (ictx->snaps.size()) {
        lderr(cct) << "image has snapshots - not removing" << dendl;
        close_image(ictx);
        return -ENOTEMPTY;
       }
-      string header_oid = ictx->header_oid;
-      old_format = ictx->old_format;
-      unknown_format = false;
-      id = ictx->id;
+
+      std::list<obj_watch_t> watchers;
+      r = io_ctx.list_watchers(header_oid, &watchers);
+      if (r < 0) {
+        lderr(cct) << "error listing watchers" << dendl;
+        close_image(ictx);
+        return r;
+      }
+      if (watchers.size() > 1) {
+        lderr(cct) << "image has watchers - not removing" << dendl;
+        close_image(ictx);
+        return -EBUSY;
+      }
+      assert(watchers.size() == 1);
+
       ictx->md_lock.get_read();
       trim_image(ictx, 0, prog_ctx);
       ictx->md_lock.put_read();
index 00db0dac741e8115193308d3f308d9fd192f61ac..e098805b6157149fcd366b8e2d555635612c440e 100644 (file)
@@ -363,7 +363,11 @@ class TestImage(object):
         self.image.remove_snap('snap1')
 
     def test_remove_with_watcher(self):
+        data = rand_data(256)
+        self.image.write(data, 0)
         assert_raises(ImageBusy, remove_image)
+        read = self.image.read(0, 256)
+        eq(read, data)
 
     def test_rollback_to_snap(self):
         self.image.write('\0' * 256, 0)