]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: moved all maintenance ops to new Operations class
authorJason Dillaman <dillaman@redhat.com>
Thu, 17 Dec 2015 15:46:00 +0000 (10:46 -0500)
committerJason Dillaman <dillaman@redhat.com>
Fri, 15 Jan 2016 15:40:28 +0000 (10:40 -0500)
This will permit unit testing and will facilitate proper
serialization of requests (when necessary).

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/CMakeLists.txt
src/librbd/ImageCtx.cc
src/librbd/ImageCtx.h
src/librbd/ImageWatcher.cc
src/librbd/Makefile.am
src/librbd/Operations.cc [new file with mode: 0644]
src/librbd/Operations.h [new file with mode: 0644]
src/librbd/internal.cc
src/librbd/internal.h
src/librbd/librbd.cc

index 10d86c2ab7f6f700845705aebc1b40f3b3254550..ad31eb1fede796c9a0a276ee74cee31bf4b590cd 100644 (file)
@@ -978,6 +978,7 @@ if(${WITH_RBD})
     librbd/LibrbdAdminSocketHook.cc
     librbd/LibrbdWriteback.cc
     librbd/ObjectMap.cc
+    librbd/Operations.cc
     librbd/Utils.cc
     librbd/exclusive_lock/AcquireRequest.cc
     librbd/exclusive_lock/ReleaseRequest.cc
index ef7861f332df1801a14e82766d1a29b1bbfb2c6c..e432d637dd56ef719499879eb2741d853d978f48 100644 (file)
@@ -21,6 +21,7 @@
 #include "librbd/Journal.h"
 #include "librbd/LibrbdAdminSocketHook.h"
 #include "librbd/ObjectMap.h"
+#include "librbd/Operations.h"
 #include "librbd/operation/ResizeRequest.h"
 #include "librbd/Utils.h"
 
@@ -162,8 +163,11 @@ struct C_InvalidateCache : public Context {
       object_cacher(NULL), writeback_handler(NULL), object_set(NULL),
       readahead(),
       total_bytes_read(0),
-      state(new ImageState<>(this)), exclusive_lock(nullptr),
-      object_map(nullptr), aio_work_queue(NULL), op_work_queue(NULL)
+      state(new ImageState<>(this)),
+      operations(new Operations<>(*this)),
+      exclusive_lock(nullptr), object_map(nullptr),
+      aio_work_queue(nullptr), op_work_queue(nullptr),
+      asok_hook(nullptr)
   {
     md_ctx.dup(p);
     data_ctx.dup(p);
@@ -217,6 +221,7 @@ struct C_InvalidateCache : public Context {
     delete op_work_queue;
     delete aio_work_queue;
     delete asok_hook;
+    delete operations;
     delete state;
   }
 
index 451d14ff72cea670eceb46b0cf0519d494c98cad..7606f6fbf7d615150dbd62691c35b696e7bf01a1 100644 (file)
@@ -50,6 +50,7 @@ namespace librbd {
   template <typename> class Journal;
   class LibrbdAdminSocketHook;
   class ObjectMap;
+  template <typename> class Operations;
 
   namespace operation {
   template <typename> class ResizeRequest;
@@ -137,11 +138,11 @@ namespace librbd {
     std::list<Context*> async_requests_waiters;
 
     ImageState<ImageCtx> *state;
+    Operations<ImageCtx> *operations;
+
     ExclusiveLock<ImageCtx> *exclusive_lock;
     ObjectMap *object_map;
 
-    atomic_t async_request_seq;
-
     xlist<operation::ResizeRequest<ImageCtx>*> resize_reqs;
 
     AioImageRequestWQ *aio_work_queue;
index ea3dbe0d7b6d0551e0dcd4f063502b2da4beecb7..d28541f396848e60a0a71147c92ea24766d8846d 100644 (file)
@@ -7,6 +7,7 @@
 #include "librbd/ImageState.h"
 #include "librbd/internal.h"
 #include "librbd/ObjectMap.h"
+#include "librbd/Operations.h"
 #include "librbd/TaskFinisher.h"
 #include "librbd/Utils.h"
 #include "include/encoding.h"
@@ -630,7 +631,7 @@ bool ImageWatcher::handle_payload(const FlattenPayload &payload,
     if (new_request) {
       ldout(m_image_ctx.cct, 10) << this << " remote flatten request: "
                                 << payload.async_request_id << dendl;
-      librbd::async_flatten(&m_image_ctx, ctx, *prog_ctx);
+      m_image_ctx.operations->flatten(*prog_ctx, ctx);
     }
 
     ::encode(ResponseMessage(r), ack_ctx->out);
@@ -652,7 +653,7 @@ bool ImageWatcher::handle_payload(const ResizePayload &payload,
       ldout(m_image_ctx.cct, 10) << this << " remote resize request: "
                                 << payload.async_request_id << " "
                                 << payload.size << dendl;
-      librbd::async_resize(&m_image_ctx, ctx, payload.size, *prog_ctx);
+      m_image_ctx.operations->resize(payload.size, *prog_ctx, ctx);
     }
 
     ::encode(ResponseMessage(r), ack_ctx->out);
@@ -668,8 +669,8 @@ bool ImageWatcher::handle_payload(const SnapCreatePayload &payload,
     ldout(m_image_ctx.cct, 10) << this << " remote snap_create request: "
                               << payload.snap_name << dendl;
 
-    librbd::snap_create_helper(&m_image_ctx, new C_ResponseMessage(ack_ctx),
-                               payload.snap_name.c_str());
+    m_image_ctx.operations->snap_create(payload.snap_name.c_str(),
+                                        new C_ResponseMessage(ack_ctx));
     return false;
   }
   return true;
@@ -684,8 +685,9 @@ bool ImageWatcher::handle_payload(const SnapRenamePayload &payload,
                               << payload.snap_id << " to "
                               << payload.snap_name << dendl;
 
-    librbd::snap_rename_helper(&m_image_ctx, new C_ResponseMessage(ack_ctx),
-                               payload.snap_id, payload.snap_name.c_str());
+    m_image_ctx.operations->snap_rename(payload.snap_id,
+                                        payload.snap_name.c_str(),
+                                        new C_ResponseMessage(ack_ctx));
     return false;
   }
   return true;
@@ -699,8 +701,8 @@ bool ImageWatcher::handle_payload(const SnapRemovePayload &payload,
     ldout(m_image_ctx.cct, 10) << this << " remote snap_remove request: "
                               << payload.snap_name << dendl;
 
-    librbd::snap_remove_helper(&m_image_ctx, new C_ResponseMessage(ack_ctx),
-                               payload.snap_name.c_str());
+    m_image_ctx.operations->snap_remove(payload.snap_name.c_str(),
+                                        new C_ResponseMessage(ack_ctx));
     return false;
   }
   return true;
@@ -714,8 +716,8 @@ bool ImageWatcher::handle_payload(const SnapProtectPayload& payload,
     ldout(m_image_ctx.cct, 10) << this << " remote snap_protect request: "
                                << payload.snap_name << dendl;
 
-    librbd::snap_protect_helper(&m_image_ctx, new C_ResponseMessage(ack_ctx),
-                                payload.snap_name.c_str());
+    m_image_ctx.operations->snap_protect(payload.snap_name.c_str(),
+                                         new C_ResponseMessage(ack_ctx));
     return false;
   }
   return true;
@@ -729,8 +731,8 @@ bool ImageWatcher::handle_payload(const SnapUnprotectPayload& payload,
     ldout(m_image_ctx.cct, 10) << this << " remote snap_unprotect request: "
                                << payload.snap_name << dendl;
 
-    librbd::snap_unprotect_helper(&m_image_ctx, new C_ResponseMessage(ack_ctx),
-                                  payload.snap_name.c_str());
+    m_image_ctx.operations->snap_unprotect(payload.snap_name.c_str(),
+                                           new C_ResponseMessage(ack_ctx));
     return false;
   }
   return true;
@@ -750,7 +752,7 @@ bool ImageWatcher::handle_payload(const RebuildObjectMapPayload& payload,
       ldout(m_image_ctx.cct, 10) << this
                                  << " remote rebuild object map request: "
                                  << payload.async_request_id << dendl;
-      librbd::async_rebuild_object_map(&m_image_ctx, ctx, *prog_ctx);
+      m_image_ctx.operations->rebuild_object_map(*prog_ctx, ctx);
     }
 
     ::encode(ResponseMessage(r), ack_ctx->out);
@@ -766,8 +768,8 @@ bool ImageWatcher::handle_payload(const RenamePayload& payload,
     ldout(m_image_ctx.cct, 10) << this << " remote rename request: "
                                << payload.image_name << dendl;
 
-    librbd::rename_helper(&m_image_ctx, new C_ResponseMessage(ack_ctx),
-                          payload.image_name.c_str());
+    m_image_ctx.operations->rename(payload.image_name.c_str(),
+                                   new C_ResponseMessage(ack_ctx));
     return false;
   }
   return true;
index c02ca981abe9862323c488cc29fb6d904189bb41..775d7027551efdfd129ed17d91eadc254822249b 100644 (file)
@@ -26,6 +26,7 @@ librbd_internal_la_SOURCES = \
        librbd/LibrbdAdminSocketHook.cc \
        librbd/LibrbdWriteback.cc \
        librbd/ObjectMap.cc \
+       librbd/Operations.cc \
        librbd/Utils.cc \
        librbd/exclusive_lock/AcquireRequest.cc \
        librbd/exclusive_lock/ReleaseRequest.cc \
@@ -100,6 +101,7 @@ noinst_HEADERS += \
        librbd/LibrbdAdminSocketHook.h \
        librbd/LibrbdWriteback.h \
        librbd/ObjectMap.h \
+       librbd/Operations.h \
        librbd/parent_types.h \
        librbd/SnapInfo.h \
        librbd/TaskFinisher.h \
diff --git a/src/librbd/Operations.cc b/src/librbd/Operations.cc
new file mode 100644 (file)
index 0000000..4564040
--- /dev/null
@@ -0,0 +1,809 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/Operations.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/operation/FlattenRequest.h"
+#include "librbd/operation/RebuildObjectMapRequest.h"
+#include "librbd/operation/RenameRequest.h"
+#include "librbd/operation/ResizeRequest.h"
+#include "librbd/operation/SnapshotCreateRequest.h"
+#include "librbd/operation/SnapshotProtectRequest.h"
+#include "librbd/operation/SnapshotRemoveRequest.h"
+#include "librbd/operation/SnapshotRenameRequest.h"
+#include "librbd/operation/SnapshotRollbackRequest.h"
+#include "librbd/operation/SnapshotUnprotectRequest.h"
+#include <boost/bind.hpp>
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::Operations: "
+
+namespace librbd {
+
+template <typename I>
+Operations<I>::Operations(I &image_ctx) : m_image_ctx(image_ctx) {
+}
+
+template <typename I>
+int Operations<I>::flatten(ProgressContext &prog_ctx) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 20) << "flatten" << dendl;
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  {
+    RWLock::RLocker parent_locker(m_image_ctx.parent_lock);
+    if (m_image_ctx.parent_md.spec.pool_id == -1) {
+      lderr(cct) << "image has no parent" << dendl;
+      return -EINVAL;
+    }
+  }
+
+  uint64_t request_id = m_async_request_seq.inc();
+  r = invoke_async_request("flatten", false,
+                           boost::bind(&Operations<I>::flatten, this,
+                                       boost::ref(prog_ctx), _1),
+                           boost::bind(&ImageWatcher::notify_flatten,
+                                       m_image_ctx.image_watcher, request_id,
+                                       boost::ref(prog_ctx)));
+
+  if (r < 0 && r != -EINVAL) {
+    return r;
+  }
+
+  notify_change();
+  ldout(cct, 20) << "flatten finished" << dendl;
+  return 0;
+}
+
+template <typename I>
+void Operations<I>::flatten(ProgressContext &prog_ctx, Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  assert(m_image_ctx.exclusive_lock == nullptr ||
+         m_image_ctx.exclusive_lock->is_lock_owner());
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 20) << "flatten" << dendl;
+
+  uint64_t object_size;
+  uint64_t overlap_objects;
+  ::SnapContext snapc;
+
+  {
+    uint64_t overlap;
+    RWLock::RLocker l(m_image_ctx.snap_lock);
+    RWLock::RLocker l2(m_image_ctx.parent_lock);
+
+    if (m_image_ctx.read_only) {
+      on_finish->complete(-EROFS);
+      return;
+    }
+
+    // can't flatten a non-clone
+    if (m_image_ctx.parent_md.spec.pool_id == -1) {
+      lderr(cct) << "image has no parent" << dendl;
+      on_finish->complete(-EINVAL);
+      return;
+    }
+    if (m_image_ctx.snap_id != CEPH_NOSNAP) {
+      lderr(cct) << "snapshots cannot be flattened" << dendl;
+      on_finish->complete(-EROFS);
+      return;
+    }
+
+    snapc = m_image_ctx.snapc;
+    assert(m_image_ctx.parent != NULL);
+    int r = m_image_ctx.get_parent_overlap(CEPH_NOSNAP, &overlap);
+    assert(r == 0);
+    assert(overlap <= m_image_ctx.size);
+
+    object_size = m_image_ctx.get_object_size();
+    overlap_objects = Striper::get_num_objects(m_image_ctx.layout, overlap);
+  }
+
+  operation::FlattenRequest<I> *req = new operation::FlattenRequest<I>(
+    m_image_ctx, on_finish, object_size, overlap_objects, snapc, prog_ctx);
+  req->send();
+}
+
+template <typename I>
+int Operations<I>::rebuild_object_map(ProgressContext &prog_ctx) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 10) << "rebuild_object_map" << dendl;
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  uint64_t request_id = m_async_request_seq.inc();
+  r = invoke_async_request("rebuild object map", true,
+                           boost::bind(&Operations<I>::rebuild_object_map, this,
+                                       boost::ref(prog_ctx), _1),
+                           boost::bind(&ImageWatcher::notify_rebuild_object_map,
+                                       m_image_ctx.image_watcher, request_id,
+                                       boost::ref(prog_ctx)));
+
+  ldout(cct, 10) << "rebuild object map finished" << dendl;
+  if (r < 0) {
+    return r;
+  }
+
+  notify_change();
+  return 0;
+}
+
+template <typename I>
+void Operations<I>::rebuild_object_map(ProgressContext &prog_ctx,
+                                       Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  assert(m_image_ctx.exclusive_lock == nullptr ||
+         m_image_ctx.exclusive_lock->is_lock_owner());
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << dendl;
+
+  if (m_image_ctx.read_only) {
+    on_finish->complete(-EROFS);
+    return;
+  }
+  if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) {
+    on_finish->complete(-EINVAL);
+    return;
+  }
+
+  operation::RebuildObjectMapRequest<I> *req =
+    new operation::RebuildObjectMapRequest<I>(m_image_ctx, on_finish, prog_ctx);
+  req->send();
+}
+
+template <typename I>
+int Operations<I>::rename(const char *dstname) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": dest_name=" << dstname
+                << dendl;
+
+  int r = librbd::detect_format(m_image_ctx.md_ctx, dstname, NULL, NULL);
+  if (r < 0 && r != -ENOENT) {
+    lderr(cct) << "error checking for existing image called "
+               << dstname << ":" << cpp_strerror(r) << dendl;
+    return r;
+  }
+  if (r == 0) {
+    lderr(cct) << "rbd image " << dstname << " already exists" << dendl;
+    return -EEXIST;
+  }
+
+  if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
+    r = invoke_async_request("rename", true,
+                             boost::bind(&Operations<I>::rename, this,
+                                         dstname, _1),
+                             boost::bind(&ImageWatcher::notify_rename,
+                                         m_image_ctx.image_watcher, dstname));
+    if (r < 0 && r != -EEXIST) {
+      return r;
+    }
+  } else {
+    RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+    C_SaferCond cond_ctx;
+    rename(dstname, &cond_ctx);
+
+    r = cond_ctx.wait();
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  if (m_image_ctx.old_format) {
+    notify_change();
+  }
+  return 0;
+}
+
+template <typename I>
+void Operations<I>::rename(const char *dstname, Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
+    assert(m_image_ctx.exclusive_lock == nullptr ||
+           m_image_ctx.exclusive_lock->is_lock_owner());
+  }
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": dest_name=" << dstname
+                << dendl;
+
+  operation::RenameRequest<I> *req =
+    new operation::RenameRequest<I>(m_image_ctx, on_finish, dstname);
+  req->send();
+}
+
+template <typename I>
+int Operations<I>::resize(uint64_t size, ProgressContext& prog_ctx) {
+  CephContext *cct = m_image_ctx.cct;
+
+  m_image_ctx.snap_lock.get_read();
+  ldout(cct, 5) << this << " " << __func__ << ": "
+                << "size=" << m_image_ctx.size << ", "
+                << "new_size=" << size << dendl;
+  m_image_ctx.snap_lock.put_read();
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  uint64_t request_id = m_async_request_seq.inc();
+  r = invoke_async_request("resize", false,
+                           boost::bind(&Operations<I>::resize, this,
+                                       size, boost::ref(prog_ctx), _1),
+                           boost::bind(&ImageWatcher::notify_resize,
+                                       m_image_ctx.image_watcher, request_id,
+                                       size, boost::ref(prog_ctx)));
+
+  m_image_ctx.perfcounter->inc(l_librbd_resize);
+  notify_change();
+  ldout(cct, 2) << "resize finished" << dendl;
+  return r;
+}
+
+template <typename I>
+void Operations<I>::resize(uint64_t size, ProgressContext &prog_ctx,
+                           Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  assert(m_image_ctx.exclusive_lock == nullptr ||
+         m_image_ctx.exclusive_lock->is_lock_owner());
+
+  CephContext *cct = m_image_ctx.cct;
+  m_image_ctx.snap_lock.get_read();
+  ldout(cct, 5) << this << " " << __func__ << ": "
+                << "size=" << m_image_ctx.size << ", "
+                << "new_size=" << size << dendl;
+  m_image_ctx.snap_lock.put_read();
+
+  {
+    RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+    if (m_image_ctx.snap_id != CEPH_NOSNAP || m_image_ctx.read_only) {
+      on_finish->complete(-EROFS);
+      return;
+    }
+  }
+
+  operation::ResizeRequest<I> *req = new operation::ResizeRequest<I>(
+    m_image_ctx, on_finish, size, prog_ctx);
+  req->send();
+}
+
+template <typename I>
+int Operations<I>::snap_create(const char *snap_name) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0)
+    return r;
+
+  {
+    RWLock::RLocker l(m_image_ctx.snap_lock);
+    if (m_image_ctx.get_snap_id(snap_name) != CEPH_NOSNAP) {
+      return -EEXIST;
+    }
+  }
+
+  r = invoke_async_request("snap_create", true,
+                           boost::bind(&Operations<I>::snap_create, this,
+                                       snap_name, _1),
+                           boost::bind(&ImageWatcher::notify_snap_create,
+                                       m_image_ctx.image_watcher, snap_name));
+  if (r < 0 && r != -EEXIST) {
+    return r;
+  }
+
+  m_image_ctx.perfcounter->inc(l_librbd_snap_create);
+  notify_change();
+  return 0;
+}
+
+template <typename I>
+void Operations<I>::snap_create(const char *snap_name, Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  assert(m_image_ctx.exclusive_lock == nullptr ||
+         m_image_ctx.exclusive_lock->is_lock_owner());
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  operation::SnapshotCreateRequest<I> *req =
+    new operation::SnapshotCreateRequest<I>(m_image_ctx, on_finish, snap_name);
+  req->send();
+}
+
+template <typename I>
+int Operations<I>::snap_rollback(const char *snap_name,
+                                 ProgressContext& prog_ctx) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0)
+    return r;
+
+  RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
+  {
+    // need to drop snap_lock before invalidating cache
+    RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+    if (!m_image_ctx.snap_exists) {
+      return -ENOENT;
+    }
+
+    if (m_image_ctx.snap_id != CEPH_NOSNAP || m_image_ctx.read_only) {
+      return -EROFS;
+    }
+
+    uint64_t snap_id = m_image_ctx.get_snap_id(snap_name);
+    if (snap_id == CEPH_NOSNAP) {
+      lderr(cct) << "No such snapshot found." << dendl;
+      return -ENOENT;
+    }
+  }
+
+  r = prepare_image_update();
+  if (r < 0) {
+    return -EROFS;
+  }
+  if (m_image_ctx.exclusive_lock != nullptr &&
+      !m_image_ctx.exclusive_lock->is_lock_owner()) {
+    return -EROFS;
+  }
+
+  C_SaferCond cond_ctx;
+  snap_rollback(snap_name, prog_ctx, &cond_ctx);
+  r = cond_ctx.wait();
+  if (r < 0) {
+    return r;
+  }
+
+  m_image_ctx.perfcounter->inc(l_librbd_snap_rollback);
+  notify_change();
+  return r;
+}
+
+template <typename I>
+void Operations<I>::snap_rollback(const char *snap_name,
+                                  ProgressContext& prog_ctx,
+                                  Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  uint64_t snap_id;
+  uint64_t new_size;
+  {
+    RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+    snap_id = m_image_ctx.get_snap_id(snap_name);
+    if (snap_id == CEPH_NOSNAP) {
+      lderr(cct) << "No such snapshot found." << dendl;
+      on_finish->complete(-ENOENT);
+      return;
+    }
+
+    new_size = m_image_ctx.get_image_size(snap_id);
+  }
+
+  // async mode used for journal replay
+  operation::SnapshotRollbackRequest<I> *request =
+    new operation::SnapshotRollbackRequest<I>(m_image_ctx, on_finish, snap_name,
+                                              snap_id, new_size, prog_ctx);
+  request->send();
+}
+
+template <typename I>
+int Operations<I>::snap_remove(const char *snap_name) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  if (m_image_ctx.read_only)
+    return -EROFS;
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0)
+    return r;
+
+  bool proxy_op = false;
+  {
+    RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+    if (m_image_ctx.get_snap_id(snap_name) == CEPH_NOSNAP) {
+      return -ENOENT;
+    }
+    proxy_op = ((m_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0 ||
+                (m_image_ctx.features & RBD_FEATURE_JOURNALING) != 0);
+  }
+
+  if (proxy_op) {
+    r = invoke_async_request("snap_remove", true,
+                             boost::bind(&Operations<I>::snap_remove, this,
+                                         snap_name, _1),
+                             boost::bind(&ImageWatcher::notify_snap_remove,
+                                         m_image_ctx.image_watcher, snap_name));
+    if (r < 0 && r != -ENOENT) {
+      return r;
+    }
+  } else {
+    RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+    C_SaferCond cond_ctx;
+    snap_remove(snap_name, &cond_ctx);
+
+    r = cond_ctx.wait();
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  m_image_ctx.perfcounter->inc(l_librbd_snap_remove);
+  notify_change();
+  return 0;
+}
+
+template <typename I>
+void Operations<I>::snap_remove(const char *snap_name, Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  {
+    if ((m_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0) {
+      assert(m_image_ctx.exclusive_lock == nullptr ||
+             m_image_ctx.exclusive_lock->is_lock_owner());
+    }
+  }
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  uint64_t snap_id;
+  {
+    RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+    snap_id = m_image_ctx.get_snap_id(snap_name);
+    if (snap_id == CEPH_NOSNAP) {
+      lderr(m_image_ctx.cct) << "No such snapshot found." << dendl;
+      on_finish->complete(-ENOENT);
+      return;
+    }
+
+    bool is_protected;
+    int r = m_image_ctx.is_snap_protected(snap_id, &is_protected);
+    if (r < 0) {
+      on_finish->complete(r);
+      return;
+    } else if (is_protected) {
+      lderr(m_image_ctx.cct) << "snapshot is protected" << dendl;
+      on_finish->complete(-EBUSY);
+      return;
+    }
+  }
+
+  operation::SnapshotRemoveRequest<I> *req =
+    new operation::SnapshotRemoveRequest<I>(m_image_ctx, on_finish, snap_name,
+                                            snap_id);
+  req->send();
+}
+
+template <typename I>
+int Operations<I>::snap_rename(const char *srcname, const char *dstname) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": "
+                << "snap_name=" << srcname << ", "
+                << "new_snap_name=" << dstname << dendl;
+
+  snapid_t snap_id;
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0)
+    return r;
+
+  {
+    RWLock::RLocker l(m_image_ctx.snap_lock);
+    snap_id = m_image_ctx.get_snap_id(srcname);
+    if (snap_id == CEPH_NOSNAP) {
+      return -ENOENT;
+    }
+    if (m_image_ctx.get_snap_id(dstname) != CEPH_NOSNAP) {
+      return -EEXIST;
+    }
+  }
+
+  if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
+    r = invoke_async_request("snap_rename", true,
+                             boost::bind(&Operations<I>::snap_rename, this,
+                                         snap_id, dstname, _1),
+                             boost::bind(&ImageWatcher::notify_snap_rename,
+                                         m_image_ctx.image_watcher, snap_id,
+                                         dstname));
+    if (r < 0 && r != -EEXIST) {
+      return r;
+    }
+  } else {
+    RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+    C_SaferCond cond_ctx;
+    snap_rename(snap_id, dstname, &cond_ctx);
+
+    r = cond_ctx.wait();
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  m_image_ctx.perfcounter->inc(l_librbd_snap_rename);
+  notify_change();
+  return 0;
+}
+
+template <typename I>
+void Operations<I>::snap_rename(const uint64_t src_snap_id,
+                                const char *dst_name, Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  if ((m_image_ctx.features & RBD_FEATURE_JOURNALING) != 0) {
+    assert(m_image_ctx.exclusive_lock == nullptr ||
+           m_image_ctx.exclusive_lock->is_lock_owner());
+  }
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": "
+                << "snap_id=" << src_snap_id << ", "
+                << "new_snap_name=" << dst_name << dendl;
+
+  operation::SnapshotRenameRequest<I> *req =
+    new operation::SnapshotRenameRequest<I>(m_image_ctx, on_finish, src_snap_id,
+                                            dst_name);
+  req->send();
+}
+
+template <typename I>
+int Operations<I>::snap_protect(const char *snap_name) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  {
+    RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+    bool is_protected;
+    r = m_image_ctx.is_snap_protected(m_image_ctx.get_snap_id(snap_name),
+                                      &is_protected);
+    if (r < 0) {
+      return r;
+    }
+
+    if (is_protected) {
+      return -EBUSY;
+    }
+  }
+
+  if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
+    r = invoke_async_request("snap_protect", true,
+                             boost::bind(&Operations<I>::snap_protect, this,
+                                         snap_name, _1),
+                             boost::bind(&ImageWatcher::notify_snap_protect,
+                                         m_image_ctx.image_watcher, snap_name));
+    if (r < 0 && r != -EBUSY) {
+      return r;
+    }
+  } else {
+    RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+    C_SaferCond cond_ctx;
+    snap_protect(snap_name, &cond_ctx);
+
+    r = cond_ctx.wait();
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  notify_change();
+  return 0;
+}
+
+template <typename I>
+void Operations<I>::snap_protect(const char *snap_name, Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
+    assert(m_image_ctx.exclusive_lock == nullptr ||
+           m_image_ctx.exclusive_lock->is_lock_owner());
+  }
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  operation::SnapshotProtectRequest<I> *request =
+    new operation::SnapshotProtectRequest<I>(m_image_ctx, on_finish, snap_name);
+  request->send();
+}
+
+template <typename I>
+int Operations<I>::snap_unprotect(const char *snap_name) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  if (m_image_ctx.read_only) {
+    return -EROFS;
+  }
+
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  {
+    RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+    bool is_unprotected;
+    r = m_image_ctx.is_snap_unprotected(m_image_ctx.get_snap_id(snap_name),
+                                  &is_unprotected);
+    if (r < 0) {
+      return r;
+    }
+
+    if (is_unprotected) {
+      return -EINVAL;
+    }
+  }
+
+  if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
+    r = invoke_async_request("snap_unprotect", true,
+                             boost::bind(&Operations<I>::snap_unprotect, this,
+                                         snap_name, _1),
+                             boost::bind(&ImageWatcher::notify_snap_unprotect,
+                                         m_image_ctx.image_watcher, snap_name));
+    if (r < 0 && r != -EINVAL) {
+      return r;
+    }
+  } else {
+    RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+    C_SaferCond cond_ctx;
+    snap_unprotect(snap_name, &cond_ctx);
+
+    r = cond_ctx.wait();
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  notify_change();
+  return 0;
+}
+
+template <typename I>
+void Operations<I>::snap_unprotect(const char *snap_name, Context *on_finish) {
+  assert(m_image_ctx.owner_lock.is_locked());
+  if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
+    assert(m_image_ctx.exclusive_lock == nullptr ||
+           m_image_ctx.exclusive_lock->is_lock_owner());
+  }
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
+                << dendl;
+
+  operation::SnapshotUnprotectRequest<I> *request =
+    new operation::SnapshotUnprotectRequest<I>(m_image_ctx, on_finish,
+                                               snap_name);
+  request->send();
+}
+
+template <typename I>
+int Operations<I>::prepare_image_update() {
+  assert(m_image_ctx.owner_lock.is_locked() &&
+         !m_image_ctx.owner_lock.is_wlocked());
+  if (m_image_ctx.image_watcher == NULL) {
+    return -EROFS;
+  }
+
+  // need to upgrade to a write lock
+  int r = 0;
+  bool trying_lock = false;
+  C_SaferCond ctx;
+  m_image_ctx.owner_lock.put_read();
+  {
+    RWLock::WLocker owner_locker(m_image_ctx.owner_lock);
+    if (m_image_ctx.exclusive_lock != nullptr &&
+        !m_image_ctx.exclusive_lock->is_lock_owner()) {
+      m_image_ctx.exclusive_lock->try_lock(&ctx);
+      trying_lock = true;
+    }
+  }
+
+  if (trying_lock) {
+    r = ctx.wait();
+  }
+  m_image_ctx.owner_lock.get_read();
+
+  return r;
+}
+
+template <typename I>
+int Operations<I>::invoke_async_request(const std::string& request_type,
+                                        bool permit_snapshot,
+                                        const boost::function<void(Context*)>& local_request,
+                                        const boost::function<int()>& remote_request) {
+  CephContext *cct = m_image_ctx.cct;
+  int r;
+  do {
+    C_SaferCond ctx;
+    {
+      RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
+      {
+        RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+        if (m_image_ctx.read_only ||
+            (!permit_snapshot && m_image_ctx.snap_id != CEPH_NOSNAP)) {
+          return -EROFS;
+        }
+      }
+
+      while (m_image_ctx.exclusive_lock != nullptr) {
+        r = prepare_image_update();
+        if (r < 0) {
+          return -EROFS;
+        } else if (m_image_ctx.exclusive_lock->is_lock_owner()) {
+          break;
+        }
+
+        r = remote_request();
+        if (r != -ETIMEDOUT && r != -ERESTART) {
+          return r;
+        }
+        ldout(cct, 5) << request_type << " timed out notifying lock owner"
+                      << dendl;
+      }
+
+      local_request(&ctx);
+    }
+
+    r = ctx.wait();
+    if (r == -ERESTART) {
+      ldout(cct, 5) << request_type << " interrupted: restarting" << dendl;
+    }
+  } while (r == -ERESTART);
+  return r;
+}
+
+template <typename I>
+void Operations<I>::notify_change() {
+  m_image_ctx.state->handle_update_notification();
+  ImageWatcher::notify_header_update(m_image_ctx.md_ctx,
+                                     m_image_ctx.header_oid);
+}
+
+} // namespace librbd
+
+template class librbd::Operations<librbd::ImageCtx>;
diff --git a/src/librbd/Operations.h b/src/librbd/Operations.h
new file mode 100644 (file)
index 0000000..e600950
--- /dev/null
@@ -0,0 +1,73 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_OPERATIONS_H
+#define CEPH_LIBRBD_OPERATIONS_H
+
+#include "include/int_types.h"
+#include "include/atomic.h"
+#include <string>
+#include <boost/function.hpp>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+class ProgressContext;
+
+template <typename ImageCtxT = ImageCtx>
+class Operations {
+public:
+  Operations(ImageCtxT &image_ctx);
+
+  int flatten(ProgressContext &prog_ctx);
+  void flatten(ProgressContext &prog_ctx, Context *on_finish);
+
+  int rebuild_object_map(ProgressContext &prog_ctx);
+  void rebuild_object_map(ProgressContext &prog_ctx, Context *on_finish);
+
+  int rename(const char *dstname);
+  void rename(const char *dstname, Context *on_finish);
+
+  int resize(uint64_t size, ProgressContext& prog_ctx);
+  void resize(uint64_t size, ProgressContext &prog_ctx, Context *on_finish);
+
+  int snap_create(const char *snap_name);
+  void snap_create(const char *snap_name, Context *on_finish);
+
+  int snap_rollback(const char *snap_name, ProgressContext& prog_ctx);
+  void snap_rollback(const char *snap_name, ProgressContext& prog_ctx,
+                     Context *on_finish);
+
+  int snap_remove(const char *snap_name);
+  void snap_remove(const char *snap_name, Context *on_finish);
+
+  int snap_rename(const char *srcname, const char *dstname);
+  void snap_rename(const uint64_t src_snap_id, const char *dst_name,
+                   Context *on_finish);
+
+  int snap_protect(const char *snap_name);
+  void snap_protect(const char *snap_name, Context *on_finish);
+
+  int snap_unprotect(const char *snap_name);
+  void snap_unprotect(const char *snap_name, Context *on_finish);
+
+  int prepare_image_update();
+
+private:
+  ImageCtxT &m_image_ctx;
+  atomic_t m_async_request_seq;
+
+  int invoke_async_request(const std::string& request_type,
+                           bool permit_snapshot,
+                           const boost::function<void(Context*)>& local_request,
+                           const boost::function<int()>& remote_request);
+  void notify_change();
+};
+
+} // namespace librbd
+
+extern template class librbd::Operations<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_OPERATIONS_H
index 6f034f45352e1c3ebd4b3be6b48ad47c0967ade6..a926e03731cf4d0c3f07a187ad70dbe8c01677bd 100644 (file)
 #include "librbd/internal.h"
 #include "librbd/Journal.h"
 #include "librbd/ObjectMap.h"
+#include "librbd/Operations.h"
 #include "librbd/parent_types.h"
 #include "librbd/Utils.h"
-#include "librbd/operation/FlattenRequest.h"
-#include "librbd/operation/RebuildObjectMapRequest.h"
-#include "librbd/operation/RenameRequest.h"
-#include "librbd/operation/ResizeRequest.h"
-#include "librbd/operation/SnapshotCreateRequest.h"
-#include "librbd/operation/SnapshotProtectRequest.h"
-#include "librbd/operation/SnapshotRemoveRequest.h"
-#include "librbd/operation/SnapshotRenameRequest.h"
-#include "librbd/operation/SnapshotRollbackRequest.h"
-#include "librbd/operation/SnapshotUnprotectRequest.h"
 #include "librbd/operation/TrimRequest.h"
 #include "include/util.h"
 
@@ -127,7 +118,6 @@ int create_object_map(ImageCtx *ictx) {
   return 0;
 }
 
-
 int update_all_flags(ImageCtx *ictx, uint64_t flags, uint64_t mask) {
   assert(ictx->snap_lock.is_locked());
   CephContext *cct = ictx->cct;
@@ -152,79 +142,6 @@ int update_all_flags(ImageCtx *ictx, uint64_t flags, uint64_t mask) {
   return 0;
 }
 
-int prepare_image_update(ImageCtx *ictx) {
-  assert(ictx->owner_lock.is_locked() && !ictx->owner_lock.is_wlocked());
-  if (ictx->image_watcher == NULL) {
-    return -EROFS;
-  }
-
-  // need to upgrade to a write lock
-  int r = 0;
-  bool trying_lock = false;
-  C_SaferCond ctx;
-  ictx->owner_lock.put_read();
-  {
-    RWLock::WLocker owner_locker(ictx->owner_lock);
-    if (ictx->exclusive_lock != nullptr &&
-        !ictx->exclusive_lock->is_lock_owner()) {
-      ictx->exclusive_lock->try_lock(&ctx);
-      trying_lock = true;
-    }
-  }
-
-  if (trying_lock) {
-    r = ctx.wait();
-  }
-  ictx->owner_lock.get_read();
-
-  return r;
-}
-
-int invoke_async_request(ImageCtx *ictx, const std::string& request_type,
-                         bool permit_snapshot,
-                         const boost::function<void(Context*)>& local_request,
-                         const boost::function<int()>& remote_request) {
-  int r;
-  do {
-    C_SaferCond ctx;
-    {
-      RWLock::RLocker l(ictx->owner_lock);
-      {
-        RWLock::RLocker snap_locker(ictx->snap_lock);
-        if (ictx->read_only ||
-            (!permit_snapshot && ictx->snap_id != CEPH_NOSNAP)) {
-          return -EROFS;
-        }
-      }
-
-      while (ictx->exclusive_lock != nullptr) {
-        r = prepare_image_update(ictx);
-        if (r < 0) {
-          return -EROFS;
-        } else if (ictx->exclusive_lock->is_lock_owner()) {
-          break;
-        }
-
-        r = remote_request();
-        if (r != -ETIMEDOUT && r != -ERESTART) {
-          return r;
-        }
-        ldout(ictx->cct, 5) << request_type << " timed out notifying lock owner"
-                            << dendl;
-      }
-
-      local_request(&ctx);
-    }
-
-    r = ctx.wait();
-    if (r == -ERESTART) {
-      ldout(ictx->cct, 5) << request_type << " interrupted: restarting"
-                          << dendl;
-    }
-  } while (r == -ERESTART);
-  return r;
-}
-
 int validate_pool(IoCtx &io_ctx, CephContext *cct) {
   if (!cct->_conf->rbd_validate_pool) {
     return 0;
@@ -776,340 +693,6 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
     return 0;
   }
 
-  int snap_create(ImageCtx *ictx, const char *snap_name)
-  {
-    ldout(ictx->cct, 20) << "snap_create " << ictx << " " << snap_name << dendl;
-
-    if (ictx->read_only) {
-      return -EROFS;
-    }
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0)
-      return r;
-
-    {
-      RWLock::RLocker l(ictx->snap_lock);
-      if (ictx->get_snap_id(snap_name) != CEPH_NOSNAP) {
-        return -EEXIST;
-      }
-    }
-
-    r = invoke_async_request(ictx, "snap_create", true,
-                             boost::bind(&snap_create_helper, ictx, _1,
-                                         snap_name),
-                             boost::bind(&ImageWatcher::notify_snap_create,
-                                         ictx->image_watcher, snap_name));
-    if (r < 0 && r != -EEXIST) {
-      return r;
-    }
-
-    ictx->perfcounter->inc(l_librbd_snap_create);
-    notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-    return 0;
-  }
-
-  void snap_create_helper(ImageCtx* ictx, Context* ctx, const char* snap_name) {
-    assert(ictx->owner_lock.is_locked());
-    assert(ictx->exclusive_lock == nullptr ||
-          ictx->exclusive_lock->is_lock_owner());
-
-    ldout(ictx->cct, 20) << "snap_create_helper " << ictx << " " << snap_name
-                         << dendl;
-
-    operation::SnapshotCreateRequest<> *req =
-      new operation::SnapshotCreateRequest<>(*ictx, ctx, snap_name);
-    req->send();
-  }
-
-  int snap_remove(ImageCtx *ictx, const char *snap_name)
-  {
-    ldout(ictx->cct, 20) << "snap_remove " << ictx << " " << snap_name << dendl;
-
-    if (ictx->read_only)
-      return -EROFS;
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0)
-      return r;
-
-    bool proxy_op = false;
-    {
-      RWLock::RLocker snap_locker(ictx->snap_lock);
-      if (ictx->get_snap_id(snap_name) == CEPH_NOSNAP) {
-        return -ENOENT;
-      }
-      proxy_op = ((ictx->features & RBD_FEATURE_FAST_DIFF) != 0 ||
-                  (ictx->features & RBD_FEATURE_JOURNALING) != 0);
-    }
-
-    if (proxy_op) {
-      r = invoke_async_request(ictx, "snap_remove", true,
-                               boost::bind(&snap_remove_helper, ictx, _1,
-                                           snap_name),
-                               boost::bind(&ImageWatcher::notify_snap_remove,
-                                           ictx->image_watcher, snap_name));
-      if (r < 0 && r != -ENOENT) {
-        return r;
-      }
-    } else {
-      RWLock::RLocker owner_lock(ictx->owner_lock);
-      C_SaferCond cond_ctx;
-      snap_remove_helper(ictx, &cond_ctx, snap_name);
-
-      r = cond_ctx.wait();
-      if (r < 0) {
-        return r;
-      }
-    }
-
-    notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-
-    ictx->perfcounter->inc(l_librbd_snap_remove);
-    return 0;
-  }
-
-  void snap_remove_helper(ImageCtx *ictx, Context *ctx, const char *snap_name)
-  {
-    assert(ictx->owner_lock.is_locked());
-    {
-      if ((ictx->features & RBD_FEATURE_FAST_DIFF) != 0) {
-        assert(ictx->exclusive_lock == nullptr ||
-              ictx->exclusive_lock->is_lock_owner());
-      }
-    }
-
-    ldout(ictx->cct, 20) << "snap_remove_helper " << ictx << " " << snap_name
-                         << dendl;
-
-    uint64_t snap_id;
-    {
-      RWLock::RLocker snap_locker(ictx->snap_lock);
-      snap_id = ictx->get_snap_id(snap_name);
-      if (snap_id == CEPH_NOSNAP) {
-        lderr(ictx->cct) << "No such snapshot found." << dendl;
-        ctx->complete(-ENOENT);
-        return;
-      }
-
-      bool is_protected;
-      int r = ictx->is_snap_protected(snap_id, &is_protected);
-      if (r < 0) {
-        ctx->complete(r);
-        return;
-      } else if (is_protected) {
-        lderr(ictx->cct) << "snapshot is protected" << dendl;
-        ctx->complete(-EBUSY);
-        return;
-      }
-    }
-
-    operation::SnapshotRemoveRequest<> *req =
-      new operation::SnapshotRemoveRequest<>(*ictx, ctx, snap_name, snap_id);
-    req->send();
-  }
-
-  int snap_rename(ImageCtx *ictx, const char *srcname, const char *dstname)
-  {
-    ldout(ictx->cct, 20) << "snap_rename " << ictx << " from " << srcname << " to " << dstname << dendl;
-
-    snapid_t snap_id;
-    if (ictx->read_only) {
-      return -EROFS;
-    }
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0)
-      return r;
-
-    {
-      RWLock::RLocker l(ictx->snap_lock);
-      snap_id = ictx->get_snap_id(srcname);
-      if (snap_id == CEPH_NOSNAP) {
-        return -ENOENT;
-      }
-      if (ictx->get_snap_id(dstname) != CEPH_NOSNAP) {
-        return -EEXIST;
-      }
-    }
-
-    if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
-      r = invoke_async_request(ictx, "snap_rename", true,
-                               boost::bind(&snap_rename_helper, ictx, _1,
-                                           snap_id, dstname),
-                               boost::bind(&ImageWatcher::notify_snap_rename,
-                                           ictx->image_watcher, snap_id,
-                                           dstname));
-      if (r < 0 && r != -EEXIST) {
-        return r;
-      }
-    } else {
-      RWLock::RLocker owner_lock(ictx->owner_lock);
-      C_SaferCond cond_ctx;
-      snap_rename_helper(ictx, &cond_ctx, snap_id, dstname);
-
-      r = cond_ctx.wait();
-      if (r < 0) {
-        return r;
-      }
-    }
-
-    ictx->perfcounter->inc(l_librbd_snap_rename);
-    notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-    return 0;
-  }
-
-  void snap_rename_helper(ImageCtx* ictx, Context* ctx,
-                          const uint64_t src_snap_id, const char* dst_name) {
-    assert(ictx->owner_lock.is_locked());
-    if ((ictx->features & RBD_FEATURE_JOURNALING) != 0) {
-      assert(ictx->exclusive_lock == nullptr ||
-            ictx->exclusive_lock->is_lock_owner());
-    }
-    ldout(ictx->cct, 20) << __func__ << " " << ictx << " from "
-                         << src_snap_id << " to " << dst_name << dendl;
-
-    operation::SnapshotRenameRequest<> *req =
-      new operation::SnapshotRenameRequest<>(*ictx, ctx, src_snap_id, dst_name);
-    req->send();
-  }
-
-  int snap_protect(ImageCtx *ictx, const char *snap_name)
-  {
-    ldout(ictx->cct, 20) << "snap_protect " << ictx << " " << snap_name
-                        << dendl;
-
-    if (ictx->read_only) {
-      return -EROFS;
-    }
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0) {
-      return r;
-    }
-
-    {
-      RWLock::RLocker snap_locker(ictx->snap_lock);
-      bool is_protected;
-      r = ictx->is_snap_protected(ictx->get_snap_id(snap_name), &is_protected);
-      if (r < 0) {
-        return r;
-      }
-
-      if (is_protected) {
-        return -EBUSY;
-      }
-    }
-
-    if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
-      r = invoke_async_request(ictx, "snap_protect", true,
-                               boost::bind(&snap_protect_helper, ictx, _1,
-                                           snap_name),
-                               boost::bind(&ImageWatcher::notify_snap_protect,
-                                           ictx->image_watcher, snap_name));
-      if (r < 0 && r != -EBUSY) {
-        return r;
-      }
-    } else {
-      RWLock::RLocker owner_lock(ictx->owner_lock);
-      C_SaferCond cond_ctx;
-      snap_protect_helper(ictx, &cond_ctx, snap_name);
-
-      r = cond_ctx.wait();
-      if (r < 0) {
-        return r;
-      }
-    }
-
-    notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-    return 0;
-  }
-
-  void snap_protect_helper(ImageCtx *ictx, Context* ctx, const char *snap_name)
-  {
-    assert(ictx->owner_lock.is_locked());
-    if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
-      assert(ictx->exclusive_lock == nullptr ||
-            ictx->exclusive_lock->is_lock_owner());
-    }
-
-    ldout(ictx->cct, 20) << "snap_protect_helper " << ictx << " " << snap_name
-                         << dendl;
-
-    operation::SnapshotProtectRequest<> *request =
-      new operation::SnapshotProtectRequest<>(*ictx, ctx, snap_name);
-    request->send();
-  }
-
-  int snap_unprotect(ImageCtx *ictx, const char *snap_name)
-  {
-    ldout(ictx->cct, 20) << "snap_unprotect " << ictx << " " << snap_name
-                        << dendl;
-
-    if (ictx->read_only) {
-      return -EROFS;
-    }
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0) {
-      return r;
-    }
-
-    {
-      RWLock::RLocker snap_locker(ictx->snap_lock);
-      bool is_unprotected;
-      r = ictx->is_snap_unprotected(ictx->get_snap_id(snap_name),
-                                    &is_unprotected);
-      if (r < 0) {
-        return r;
-      }
-
-      if (is_unprotected) {
-        return -EINVAL;
-      }
-    }
-
-    if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
-      r = invoke_async_request(ictx, "snap_unprotect", true,
-                               boost::bind(&snap_unprotect_helper, ictx, _1,
-                                           snap_name),
-                               boost::bind(&ImageWatcher::notify_snap_unprotect,
-                                           ictx->image_watcher, snap_name));
-      if (r < 0 && r != -EINVAL) {
-        return r;
-      }
-    } else {
-      RWLock::RLocker owner_lock(ictx->owner_lock);
-      C_SaferCond cond_ctx;
-      snap_unprotect_helper(ictx, &cond_ctx, snap_name);
-
-      r = cond_ctx.wait();
-      if (r < 0) {
-        return r;
-      }
-    }
-
-    notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-    return 0;
-  }
-
-  void snap_unprotect_helper(ImageCtx *ictx, Context* ctx,
-                             const char *snap_name)
-  {
-    assert(ictx->owner_lock.is_locked());
-    if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
-      assert(ictx->exclusive_lock == nullptr ||
-            ictx->exclusive_lock->is_lock_owner());
-    }
-
-    ldout(ictx->cct, 20) << "snap_unprotect_helper " << ictx << " " << snap_name
-                         << dendl;
-
-    operation::SnapshotUnprotectRequest<> *request =
-      new operation::SnapshotUnprotectRequest<>(*ictx, ctx, snap_name);
-    request->send();
-  }
-
   int snap_is_protected(ImageCtx *ictx, const char *snap_name,
                        bool *is_protected)
   {
@@ -1676,57 +1259,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
       ictx->state->close();
     } BOOST_SCOPE_EXIT_END
 
-    r = detect_format(io_ctx, dstname, NULL, NULL);
-    if (r < 0 && r != -ENOENT) {
-      lderr(cct) << "error checking for existing image called "
-                << dstname << ":" << cpp_strerror(r) << dendl;
-      return r;
-    }
-    if (r == 0) {
-      lderr(cct) << "rbd image " << dstname << " already exists" << dendl;
-      return -EEXIST;
-    }
-
-    if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
-      r = invoke_async_request(ictx, "rename", true,
-                               boost::bind(&rename_helper, ictx, _1,
-                                           dstname),
-                               boost::bind(&ImageWatcher::notify_rename,
-                                           ictx->image_watcher, dstname));
-      if (r < 0 && r != -EEXIST) {
-        return r;
-      }
-    } else {
-      RWLock::RLocker owner_lock(ictx->owner_lock);
-      C_SaferCond cond_ctx;
-      rename_helper(ictx, &cond_ctx, dstname);
-
-      r = cond_ctx.wait();
-      if (r < 0) {
-        return r;
-      }
-    }
-
-    if (ictx->old_format) {
-      notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-    }
-    return 0;
-  }
-
-  void rename_helper(ImageCtx *ictx, Context *ctx, const char *dstname)
-  {
-    assert(ictx->owner_lock.is_locked());
-    if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
-      assert(ictx->exclusive_lock == nullptr ||
-            ictx->exclusive_lock->is_lock_owner());
-    }
-
-    ldout(ictx->cct, 20) << "rename_helper " << ictx << " " << dstname
-                         << dendl;
-
-    operation::RenameRequest<> *req =
-      new operation::RenameRequest<>(*ictx, ctx, dstname);
-    req->send();
+    return ictx->operations->rename(dstname);
   }
 
   int info(ImageCtx *ictx, image_info_t& info, size_t infosize)
@@ -2050,7 +1583,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
 
       ictx->owner_lock.get_read();
       if (ictx->exclusive_lock != nullptr) {
-        r = prepare_image_update(ictx);
+        r = ictx->operations->prepare_image_update();
         if (r < 0 || !ictx->exclusive_lock->is_lock_owner()) {
          lderr(cct) << "cannot obtain exclusive lock - not removing" << dendl;
          ictx->owner_lock.put_read();
@@ -2161,60 +1694,6 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
     return 0;
   }
 
-  int resize(ImageCtx *ictx, uint64_t size, ProgressContext& prog_ctx)
-  {
-    CephContext *cct = ictx->cct;
-
-    ictx->snap_lock.get_read();
-    ldout(cct, 20) << "resize " << ictx << " " << ictx->size << " -> "
-                  << size << dendl;
-    ictx->snap_lock.put_read();
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0) {
-      return r;
-    }
-
-    uint64_t request_id = ictx->async_request_seq.inc();
-    r = invoke_async_request(ictx, "resize", false,
-                             boost::bind(&async_resize, ictx, _1, size,
-                                         boost::ref(prog_ctx)),
-                             boost::bind(&ImageWatcher::notify_resize,
-                                         ictx->image_watcher, request_id, size,
-                                         boost::ref(prog_ctx)));
-
-    ictx->perfcounter->inc(l_librbd_resize);
-    notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-    ldout(cct, 2) << "resize finished" << dendl;
-    return r;
-  }
-
-  void async_resize(ImageCtx *ictx, Context *ctx, uint64_t size,
-                    ProgressContext &prog_ctx)
-  {
-    assert(ictx->owner_lock.is_locked());
-    assert(ictx->exclusive_lock == nullptr ||
-          ictx->exclusive_lock->is_lock_owner());
-
-    CephContext *cct = ictx->cct;
-    ictx->snap_lock.get_read();
-    ldout(cct, 20) << "async_resize " << ictx << " " << ictx->size << " -> "
-                  << size << dendl;
-    ictx->snap_lock.put_read();
-
-    {
-      RWLock::RLocker snap_locker(ictx->snap_lock);
-      if (ictx->snap_id != CEPH_NOSNAP || ictx->read_only) {
-        ctx->complete(-EROFS);
-        return;
-      }
-    }
-
-    operation::ResizeRequest<> *req = new operation::ResizeRequest<>(
-      *ictx, ctx, size, prog_ctx);
-    req->send();
-  }
-
   int snap_list(ImageCtx *ictx, vector<snap_info_t>& snaps)
   {
     ldout(ictx->cct, 20) << "snap_list " << ictx << dendl;
@@ -2249,85 +1728,6 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
     return 0;
   }
 
-  int snap_rollback(ImageCtx *ictx, const char *snap_name,
-                   ProgressContext& prog_ctx)
-  {
-    CephContext *cct = ictx->cct;
-    ldout(cct, 20) << "snap_rollback " << ictx << " snap = " << snap_name
-                  << dendl;
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0)
-      return r;
-
-    RWLock::RLocker owner_locker(ictx->owner_lock);
-    snap_t snap_id;
-    uint64_t new_size;
-    {
-      {
-       // need to drop snap_lock before invalidating cache
-       RWLock::RLocker snap_locker(ictx->snap_lock);
-       if (!ictx->snap_exists) {
-         return -ENOENT;
-       }
-
-       if (ictx->snap_id != CEPH_NOSNAP || ictx->read_only) {
-         return -EROFS;
-       }
-
-       snap_id = ictx->get_snap_id(snap_name);
-       if (snap_id == CEPH_NOSNAP) {
-         lderr(cct) << "No such snapshot found." << dendl;
-         return -ENOENT;
-       }
-      }
-
-      r = prepare_image_update(ictx);
-      if (r < 0) {
-       return -EROFS;
-      }
-      if (ictx->exclusive_lock != nullptr &&
-         !ictx->exclusive_lock->is_lock_owner()) {
-       return -EROFS;
-      }
-
-      ictx->snap_lock.get_read();
-      if (ictx->journal != NULL) {
-        C_SaferCond journal_ctx;
-        ictx->journal->wait_for_journal_ready(&journal_ctx);
-
-        ictx->snap_lock.put_read();
-        r = journal_ctx.wait();
-        if (r < 0) {
-          lderr(cct) << "Failed to initialize journal: " << cpp_strerror(r)
-                     << dendl;
-          return r;
-        }
-
-        ictx->snap_lock.get_read();
-      }
-
-      new_size = ictx->get_image_size(snap_id);
-      ictx->snap_lock.put_read();
-    }
-
-    // TODO need to wait for journal replay to complete (if enabled)
-    C_SaferCond cond_ctx;
-    operation::SnapshotRollbackRequest<> *request =
-      new operation::SnapshotRollbackRequest<>(*ictx, &cond_ctx, snap_name,
-                                               snap_id, new_size, prog_ctx);
-    request->send();
-    r = cond_ctx.wait();
-    if (r < 0) {
-      return r;
-    }
-
-    notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-
-    ictx->perfcounter->inc(l_librbd_snap_rollback);
-    return r;
-  }
-
   struct CopyProgressCtx {
     CopyProgressCtx(ProgressContext &p)
       : destictx(NULL), src_size(0), prog_ctx(p)
@@ -2530,143 +1930,6 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
     return 0;
   }
 
-  // 'flatten' child image by copying all parent's blocks
-  int flatten(ImageCtx *ictx, ProgressContext &prog_ctx)
-  {
-    CephContext *cct = ictx->cct;
-    ldout(cct, 20) << "flatten" << dendl;
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0) {
-      return r;
-    }
-
-    if (ictx->read_only) {
-      return -EROFS;
-    }
-
-    {
-      RWLock::RLocker parent_locker(ictx->parent_lock);
-      if (ictx->parent_md.spec.pool_id == -1) {
-       lderr(cct) << "image has no parent" << dendl;
-       return -EINVAL;
-      }
-    }
-
-    uint64_t request_id = ictx->async_request_seq.inc();
-    r = invoke_async_request(ictx, "flatten", false,
-                             boost::bind(&async_flatten, ictx, _1,
-                                         boost::ref(prog_ctx)),
-                             boost::bind(&ImageWatcher::notify_flatten,
-                                         ictx->image_watcher, request_id,
-                                         boost::ref(prog_ctx)));
-
-    if (r < 0 && r != -EINVAL) {
-      return r;
-    }
-
-    notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-    ldout(cct, 20) << "flatten finished" << dendl;
-    return 0;
-  }
-
-  void async_flatten(ImageCtx *ictx, Context *ctx, ProgressContext &prog_ctx)
-  {
-    assert(ictx->owner_lock.is_locked());
-    assert(ictx->exclusive_lock == nullptr ||
-          ictx->exclusive_lock->is_lock_owner());
-
-    CephContext *cct = ictx->cct;
-    ldout(cct, 20) << "flatten" << dendl;
-
-    uint64_t object_size;
-    uint64_t overlap_objects;
-    ::SnapContext snapc;
-
-    {
-      uint64_t overlap;
-      RWLock::RLocker l(ictx->snap_lock);
-      RWLock::RLocker l2(ictx->parent_lock);
-
-      if (ictx->read_only) {
-        ctx->complete(-EROFS);
-        return;
-      }
-
-      // can't flatten a non-clone
-      if (ictx->parent_md.spec.pool_id == -1) {
-       lderr(cct) << "image has no parent" << dendl;
-        ctx->complete(-EINVAL);
-       return;
-      }
-      if (ictx->snap_id != CEPH_NOSNAP) {
-       lderr(cct) << "snapshots cannot be flattened" << dendl;
-        ctx->complete(-EROFS);
-       return;
-      }
-
-      snapc = ictx->snapc;
-      assert(ictx->parent != NULL);
-      int r = ictx->get_parent_overlap(CEPH_NOSNAP, &overlap);
-      assert(r == 0);
-      assert(overlap <= ictx->size);
-
-      object_size = ictx->get_object_size();
-      overlap_objects = Striper::get_num_objects(ictx->layout, overlap);
-    }
-
-    operation::FlattenRequest<> *req = new operation::FlattenRequest<>(
-      *ictx, ctx, object_size, overlap_objects, snapc, prog_ctx);
-    req->send();
-  }
-
-  int rebuild_object_map(ImageCtx *ictx, ProgressContext &prog_ctx) {
-    CephContext *cct = ictx->cct;
-    ldout(cct, 10) << "rebuild_object_map" << dendl;
-
-    int r = ictx->state->refresh_if_required();
-    if (r < 0) {
-      return r;
-    }
-
-    uint64_t request_id = ictx->async_request_seq.inc();
-    r = invoke_async_request(ictx, "rebuild object map", true,
-                             boost::bind(&async_rebuild_object_map, ictx, _1,
-                                         boost::ref(prog_ctx)),
-                             boost::bind(&ImageWatcher::notify_rebuild_object_map,
-                                         ictx->image_watcher, request_id,
-                                         boost::ref(prog_ctx)));
-
-    ldout(cct, 10) << "rebuild object map finished" << dendl;
-    if (r < 0) {
-      notify_change(ictx->md_ctx, ictx->header_oid, ictx);
-    }
-    return r;
-  }
-
-  void async_rebuild_object_map(ImageCtx *ictx, Context *ctx,
-                                ProgressContext &prog_ctx) {
-    assert(ictx->owner_lock.is_locked());
-    assert(ictx->exclusive_lock == nullptr ||
-          ictx->exclusive_lock->is_lock_owner());
-
-    CephContext *cct = ictx->cct;
-    ldout(cct, 20) << "async_rebuild_object_map " << ictx << dendl;
-
-    if (ictx->read_only) {
-      ctx->complete(-EROFS);
-      return;
-    }
-    if (!ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
-      ctx->complete(-EINVAL);
-      return;
-    }
-
-    operation::RebuildObjectMapRequest<> *req =
-      new operation::RebuildObjectMapRequest<>(*ictx, ctx, prog_ctx);
-    req->send();
-  }
-
   int list_lockers(ImageCtx *ictx,
                   std::list<locker_t> *lockers,
                   bool *exclusive,
index cbcc8b10c89c9fbc06b5597558edc3d8205404d8..9fac1c14cad06fb402c8f10aad28d2661220dcbe 100644 (file)
@@ -47,10 +47,6 @@ enum {
   l_librbd_last,
 };
 
-class Context;
-class RWLock;
-class SimpleThrottle;
-
 namespace librbd {
 
   struct AioCompletion;
@@ -107,7 +103,6 @@ namespace librbd {
   int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snap_name,
            IoCtx& c_ioctx, const char *c_name, ImageOptions& c_opts);
   int rename(librados::IoCtx& io_ctx, const char *srcname, const char *dstname);
-  void rename_helper(ImageCtx *ictx, Context *ctx, const char *dstname);
   int info(ImageCtx *ictx, image_info_t& info, size_t image_size);
   int get_old_format(ImageCtx *ictx, uint8_t *old);
   int get_size(ImageCtx *ictx, uint64_t *size);
@@ -122,33 +117,14 @@ namespace librbd {
 
   int remove(librados::IoCtx& io_ctx, const char *imgname,
             ProgressContext& prog_ctx);
-  int resize(ImageCtx *ictx, uint64_t size, ProgressContext& prog_ctx);
-  int snap_create(ImageCtx *ictx, const char *snap_name);
-  void snap_create_helper(ImageCtx *ictx, Context* ctx, const char *snap_name);
   int snap_list(ImageCtx *ictx, std::vector<snap_info_t>& snaps);
   int snap_exists(ImageCtx *ictx, const char *snap_name, bool *exists);
-  int snap_rollback(ImageCtx *ictx, const char *snap_name,
-                   ProgressContext& prog_ctx);
-  int snap_remove(ImageCtx *ictx, const char *snap_name);
-  void snap_remove_helper(ImageCtx *ictx, Context* ctx, const char *snap_name);
-  int snap_rename(ImageCtx *ictx, const char *srcname, const char *dstname);
-  void snap_rename_helper(ImageCtx *ictx, Context* ctx,
-                          const uint64_t src_snap_id, const char *dst_name);
-  int snap_protect(ImageCtx *ictx, const char *snap_name);
-  void snap_protect_helper(ImageCtx *ictx, Context* ctx, const char *snap_name);
-  int snap_unprotect(ImageCtx *ictx, const char *snap_name);
-  void snap_unprotect_helper(ImageCtx *ictx, Context* ctx,
-                             const char *snap_name);
   int snap_is_protected(ImageCtx *ictx, const char *snap_name,
                        bool *is_protected);
   int copy(ImageCtx *ictx, IoCtx& dest_md_ctx, const char *destname,
           ImageOptions& opts, ProgressContext &prog_ctx);
   int copy(ImageCtx *src, ImageCtx *dest, ProgressContext &prog_ctx);
 
-  int flatten(ImageCtx *ictx, ProgressContext &prog_ctx);
-
-  int rebuild_object_map(ImageCtx *ictx, ProgressContext &prog_ctx);
-
   /* cooperative locking */
   int list_lockers(ImageCtx *ictx,
                   std::list<locker_t> *locks,
@@ -189,12 +165,6 @@ namespace librbd {
   void readahead(ImageCtx *ictx,
                  const vector<pair<uint64_t,uint64_t> >& image_extents);
 
-  void async_flatten(ImageCtx *ictx, Context *ctx, ProgressContext &prog_ctx);
-  void async_resize(ImageCtx *ictx, Context *ctx, uint64_t size,
-                    ProgressContext &prog_ctx);
-  void async_rebuild_object_map(ImageCtx *ictx, Context *ctx,
-                                ProgressContext &prog_ctx);
-
   int flush(ImageCtx *ictx);
   int invalidate_cache(ImageCtx *ictx);
   int poll_io_events(ImageCtx *ictx, AioCompletion **comps, int numcomp);
index 0807359af169ebd7cb80afa78b831b657cddc38b..3fda9db3ce5dd06c3327f353f8524df8c2bdf972 100644 (file)
@@ -30,7 +30,7 @@
 #include "librbd/ImageCtx.h"
 #include "librbd/ImageState.h"
 #include "librbd/internal.h"
-#include "librbd/LibrbdWriteback.h"
+#include "librbd/Operations.h"
 
 #include <algorithm>
 #include <string>
@@ -439,7 +439,7 @@ namespace librbd {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, resize_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, size);
     librbd::NoOpProgressContext prog_ctx;
-    int r = librbd::resize(ictx, size, prog_ctx);
+    int r = ictx->operations->resize(size, prog_ctx);
     tracepoint(librbd, resize_exit, r);
     return r;
   }
@@ -448,7 +448,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, resize_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, size);
-    int r = librbd::resize(ictx, size, pctx);
+    int r = ictx->operations->resize(size, pctx);
     tracepoint(librbd, resize_exit, r);
     return r;
   }
@@ -566,7 +566,7 @@ namespace librbd {
   int Image::rebuild_object_map(ProgressContext &prog_ctx)
   {
     ImageCtx *ictx = reinterpret_cast<ImageCtx*>(ctx);
-    return librbd::rebuild_object_map(ictx, prog_ctx);
+    return ictx->operations->rebuild_object_map(prog_ctx);
   }
 
   int Image::copy(IoCtx& dest_io_ctx, const char *destname)
@@ -638,7 +638,7 @@ namespace librbd {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, flatten_enter, ictx, ictx->name.c_str(), ictx->id.c_str());
     librbd::NoOpProgressContext prog_ctx;
-    int r = librbd::flatten(ictx, prog_ctx);
+    int r = ictx->operations->flatten(prog_ctx);
     tracepoint(librbd, flatten_exit, r);
     return r;
   }
@@ -647,7 +647,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, flatten_enter, ictx, ictx->name.c_str(), ictx->id.c_str());
-    int r = librbd::flatten(ictx, prog_ctx);
+    int r = ictx->operations->flatten(prog_ctx);
     tracepoint(librbd, flatten_exit, r);
     return r;
   }
@@ -723,7 +723,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, snap_create_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-    int r = librbd::snap_create(ictx, snap_name);
+    int r = ictx->operations->snap_create(snap_name);
     tracepoint(librbd, snap_create_exit, r);
     return r;
   }
@@ -732,7 +732,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, snap_remove_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-    int r = librbd::snap_remove(ictx, snap_name);
+    int r = ictx->operations->snap_remove(snap_name);
     tracepoint(librbd, snap_remove_exit, r);
     return r;
   }
@@ -742,7 +742,7 @@ namespace librbd {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, snap_rollback_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
     librbd::NoOpProgressContext prog_ctx;
-    int r = librbd::snap_rollback(ictx, snap_name, prog_ctx);
+    int r = ictx->operations->snap_rollback(snap_name, prog_ctx);
     tracepoint(librbd, snap_rollback_exit, r);
     return r;
   }
@@ -751,7 +751,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, snap_rename_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, srcname, dstname);
-    int r = librbd::snap_rename(ictx, srcname, dstname);
+    int r = ictx->operations->snap_rename(srcname, dstname);
     tracepoint(librbd, snap_rename_exit, r);
     return r;
   }
@@ -761,7 +761,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, snap_rollback_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-    int r = librbd::snap_rollback(ictx, snap_name, prog_ctx);
+    int r = ictx->operations->snap_rollback(snap_name, prog_ctx);
     tracepoint(librbd, snap_rollback_exit, r);
     return r;
   }
@@ -770,7 +770,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, snap_protect_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-    int r = librbd::snap_protect(ictx, snap_name);
+    int r = ictx->operations->snap_protect(snap_name);
     tracepoint(librbd, snap_protect_exit, r);
     return r;
   }
@@ -779,7 +779,7 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, snap_unprotect_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-    int r = librbd::snap_unprotect(ictx, snap_name);
+    int r = ictx->operations->snap_unprotect(snap_name);
     tracepoint(librbd, snap_unprotect_exit, r);
     return r;
   }
@@ -1527,7 +1527,7 @@ extern "C" int rbd_flatten(rbd_image_t image)
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, flatten_enter, ictx, ictx->name.c_str(), ictx->id.c_str());
   librbd::NoOpProgressContext prog_ctx;
-  int r = librbd::flatten(ictx, prog_ctx);
+  int r = ictx->operations->flatten(prog_ctx);
   tracepoint(librbd, flatten_exit, r);
   return r;
 }
@@ -1538,7 +1538,7 @@ extern "C" int rbd_flatten_with_progress(rbd_image_t image,
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, flatten_enter, ictx, ictx->name.c_str(), ictx->id.c_str());
   librbd::CProgressContext prog_ctx(cb, cbdata);
-  int r = librbd::flatten(ictx, prog_ctx);
+  int r = ictx->operations->flatten(prog_ctx);
   tracepoint(librbd, flatten_exit, r);
   return r;
 }
@@ -1611,7 +1611,7 @@ extern "C" int rbd_resize(rbd_image_t image, uint64_t size)
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, resize_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, size);
   librbd::NoOpProgressContext prog_ctx;
-  int r = librbd::resize(ictx, size, prog_ctx);
+  int r = ictx->operations->resize(size, prog_ctx);
   tracepoint(librbd, resize_exit, r);
   return r;
 }
@@ -1622,7 +1622,7 @@ extern "C" int rbd_resize_with_progress(rbd_image_t image, uint64_t size,
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, resize_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, size);
   librbd::CProgressContext prog_ctx(cb, cbdata);
-  int r = librbd::resize(ictx, size, prog_ctx);
+  int r = ictx->operations->resize(size, prog_ctx);
   tracepoint(librbd, resize_exit, r);
   return r;
 }
@@ -1779,7 +1779,7 @@ extern "C" int rbd_rebuild_object_map(rbd_image_t image,
 {
   librbd::ImageCtx *ictx = reinterpret_cast<librbd::ImageCtx*>(image);
   librbd::CProgressContext prog_ctx(cb, cbdata);
-  return librbd::rebuild_object_map(ictx, prog_ctx);
+  return ictx->operations->rebuild_object_map(prog_ctx);
 }
 
 /* snapshots */
@@ -1787,7 +1787,7 @@ extern "C" int rbd_snap_create(rbd_image_t image, const char *snap_name)
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, snap_create_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-  int r = librbd::snap_create(ictx, snap_name);
+  int r = ictx->operations->snap_create(snap_name);
   tracepoint(librbd, snap_create_exit, r);
   return r;
 }
@@ -1796,7 +1796,7 @@ extern "C" int rbd_snap_rename(rbd_image_t image, const char *srcname, const cha
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, snap_rename_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, srcname, dstname);
-  int r = librbd::snap_rename(ictx, srcname, dstname);
+  int r = ictx->operations->snap_rename(srcname, dstname);
   tracepoint(librbd, snap_rename_exit, r);
   return r;
 }
@@ -1805,7 +1805,7 @@ extern "C" int rbd_snap_remove(rbd_image_t image, const char *snap_name)
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, snap_remove_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-  int r = librbd::snap_remove(ictx, snap_name);
+  int r = ictx->operations->snap_remove(snap_name);
   tracepoint(librbd, snap_remove_exit, r);
   return r;
 }
@@ -1815,7 +1815,7 @@ extern "C" int rbd_snap_rollback(rbd_image_t image, const char *snap_name)
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, snap_rollback_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
   librbd::NoOpProgressContext prog_ctx;
-  int r = librbd::snap_rollback(ictx, snap_name, prog_ctx);
+  int r = ictx->operations->snap_rollback(snap_name, prog_ctx);
   tracepoint(librbd, snap_rollback_exit, r);
   return r;
 }
@@ -1828,7 +1828,7 @@ extern "C" int rbd_snap_rollback_with_progress(rbd_image_t image,
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, snap_rollback_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
   librbd::CProgressContext prog_ctx(cb, cbdata);
-  int r = librbd::snap_rollback(ictx, snap_name, prog_ctx);
+  int r = ictx->operations->snap_rollback(snap_name, prog_ctx);
   tracepoint(librbd, snap_rollback_exit, r);
   return r;
 }
@@ -1897,7 +1897,7 @@ extern "C" int rbd_snap_protect(rbd_image_t image, const char *snap_name)
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, snap_protect_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-  int r = librbd::snap_protect(ictx, snap_name);
+  int r = ictx->operations->snap_protect(snap_name);
   tracepoint(librbd, snap_protect_exit, r);
   return r;
 }
@@ -1906,7 +1906,7 @@ extern "C" int rbd_snap_unprotect(rbd_image_t image, const char *snap_name)
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   tracepoint(librbd, snap_unprotect_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
-  int r = librbd::snap_unprotect(ictx, snap_name);
+  int r = ictx->operations->snap_unprotect(snap_name);
   tracepoint(librbd, snap_unprotect_exit, r);
   return r;
 }