]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: check for watchers before trimming an image on 'rbd rm' 1411/head
authorIlya Dryomov <ilya.dryomov@inktank.com>
Wed, 29 Jan 2014 14:12:01 +0000 (16:12 +0200)
committerJosh Durgin <josh.durgin@inktank.com>
Mon, 10 Mar 2014 05:53:43 +0000 (22:53 -0700)
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
Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
Signed-off-by: Ilya Dryomov <ilya.dryomov@inktank.com>
(cherry picked from commit 0a553cfa81b06e75585ab3c39927e307ec0f4cb6)

src/librbd/internal.cc
src/test/pybind/test_rbd.py

index 1fd7942f3b8e95de8875a67f688cafab0dd54384..e70fe22fc5623dc78a76a2f9906a08f726004f7a 100644 (file)
@@ -1347,15 +1347,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 5f14e62bbded50f308d550baadb6d1c5694e257d..123d0bff457ff67f5066b5e5e077db0c675fc162 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)