]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: add rbd object-map check 8562/head
authorDouglas Fuller <dfuller@redhat.com>
Thu, 31 Mar 2016 15:45:41 +0000 (08:45 -0700)
committerDouglas Fuller <dfuller@redhat.com>
Thu, 19 May 2016 13:09:37 +0000 (06:09 -0700)
Add a CLI option to verify the object map for an unmapped image
for debugging purposes. Syntax:

rbd object-map check <image-name>

This operation is not supported on currently mapped images.

Any inconsistencies that could affect the correctness of future
operations are noted and the object map (and fast diff state, if
enabled) is invalidated. Inconsistencies that do not affect
correctness will be emitted at debug level 1 or higher.

Consolidate code path with object-map rebuild and remove newly
unused code.

Fixes: http://tracker.ceph.com/issues/14867
Signed-off-by: Douglas Fuller <dfuller@redhat.com>
src/include/rbd/librbd.hpp
src/librbd/Makefile.am
src/librbd/Operations.cc
src/librbd/Operations.h
src/librbd/librbd.cc
src/librbd/operation/ObjectMapIterate.cc [new file with mode: 0644]
src/librbd/operation/ObjectMapIterate.h [new file with mode: 0644]
src/librbd/operation/RebuildObjectMapRequest.cc
src/test/librbd/test_librbd.cc
src/tools/rbd/action/ObjectMap.cc

index e4c434ab490f478f7174fff845f46421d07560cc..4ce59d82d534e157372f4d750622206980c34e9f 100644 (file)
@@ -204,6 +204,8 @@ public:
   /* object map feature */
   int rebuild_object_map(ProgressContext &prog_ctx);
 
+  int check_object_map(ProgressContext &prog_ctx);
+
   int copy(IoCtx& dest_io_ctx, const char *destname);
   int copy2(Image& dest);
   int copy3(IoCtx& dest_io_ctx, const char *destname, ImageOptions& opts);
index 08c9738806ee21246fd2241625b4c9a675116c5d..7b9416c0ca2fe37600f406ae906584824a3fe3ad 100644 (file)
@@ -55,6 +55,7 @@ librbd_internal_la_SOURCES = \
        librbd/object_map/UpdateRequest.cc \
        librbd/operation/FlattenRequest.cc \
        librbd/operation/RebuildObjectMapRequest.cc \
+       librbd/operation/ObjectMapIterate.cc \
        librbd/operation/RenameRequest.cc \
        librbd/operation/Request.cc \
        librbd/operation/ResizeRequest.cc \
@@ -145,6 +146,7 @@ noinst_HEADERS += \
        librbd/object_map/UpdateRequest.h \
        librbd/operation/FlattenRequest.h \
        librbd/operation/RebuildObjectMapRequest.h \
+       librbd/operation/ObjectMapIterate.h \
        librbd/operation/RenameRequest.h \
        librbd/operation/Request.h \
        librbd/operation/ResizeRequest.h \
index c523b912680d3d2a36a2139dbb57207edad72fdd..9c7ea32ad44361a274665623644b922da7f07b5b 100644 (file)
@@ -13,6 +13,7 @@
 #include "librbd/Utils.h"
 #include "librbd/operation/FlattenRequest.h"
 #include "librbd/operation/RebuildObjectMapRequest.h"
+#include "librbd/operation/ObjectMapIterate.h"
 #include "librbd/operation/RenameRequest.h"
 #include "librbd/operation/ResizeRequest.h"
 #include "librbd/operation/SnapshotCreateRequest.h"
@@ -274,6 +275,18 @@ struct C_InvokeAsyncRequest : public Context {
   }
 };
 
+template <typename I>
+bool needs_invalidate(I& image_ctx, uint64_t object_no,
+                    uint8_t current_state, uint8_t new_state) {
+  if ( (current_state == OBJECT_EXISTS ||
+       current_state == OBJECT_EXISTS_CLEAN) &&
+       (new_state == OBJECT_NONEXISTENT ||
+       new_state == OBJECT_PENDING)) {
+    return false;
+  }
+  return true;
+}
+
 } // anonymous namespace
 
 template <typename I>
@@ -423,6 +436,48 @@ void Operations<I>::execute_rebuild_object_map(ProgressContext &prog_ctx,
   req->send();
 }
 
+template <typename I>
+int Operations<I>::check_object_map(ProgressContext &prog_ctx) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << dendl;
+  int r = m_image_ctx.state->refresh_if_required();
+  if (r < 0) {
+    return r;
+  }
+
+  r = invoke_async_request("check object map", true,
+                           boost::bind(&Operations<I>::check_object_map, this,
+                                       boost::ref(prog_ctx), _1),
+                          [] (Context *c) { c->complete(-EOPNOTSUPP); });
+
+  return r;
+}
+
+template <typename I>
+void Operations<I>::object_map_iterate(ProgressContext &prog_ctx,
+                                      operation::ObjectIterateWork<I> handle_mismatch,
+                                      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());
+
+  if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) {
+    on_finish->complete(-EINVAL);
+    return;
+  }
+
+  operation::ObjectMapIterateRequest<I> *req =
+    new operation::ObjectMapIterateRequest<I>(m_image_ctx, on_finish,
+                                             prog_ctx, handle_mismatch);
+  req->send();
+}
+
+template <typename I>
+void Operations<I>::check_object_map(ProgressContext &prog_ctx,
+                                    Context *on_finish) {
+  object_map_iterate(prog_ctx, needs_invalidate, on_finish);
+}
+
 template <typename I>
 int Operations<I>::rename(const char *dstname) {
   CephContext *cct = m_image_ctx.cct;
index 95af4dcf1e96369c1b4c5f046d368c4c9abb2e1b..a6fc91b7912aec64840c013dacd4521c38a583ee 100644 (file)
@@ -5,6 +5,7 @@
 #define CEPH_LIBRBD_OPERATIONS_H
 
 #include "include/int_types.h"
+#include "librbd/operation/ObjectMapIterate.h"
 #include <atomic>
 #include <string>
 #include <boost/function.hpp>
@@ -28,6 +29,13 @@ public:
   void execute_rebuild_object_map(ProgressContext &prog_ctx,
                                   Context *on_finish);
 
+  int check_object_map(ProgressContext &prog_ctx);
+  void check_object_map(ProgressContext &prog_ctx, Context *on_finish);
+
+  void object_map_iterate(ProgressContext &prog_ctx,
+                         operation::ObjectIterateWork<ImageCtxT> handle_mismatch,
+                         Context* on_finish);
+
   int rename(const char *dstname);
   void execute_rename(const char *dstname, Context *on_finish);
 
index 2cb5132ed24bf927fc8cec4fc55464eb137a83c5..eb6df2a85db04051dc22c71c926b1270eccdfe43 100644 (file)
@@ -713,6 +713,12 @@ namespace librbd {
     return ictx->operations->rebuild_object_map(prog_ctx);
   }
 
+  int Image::check_object_map(ProgressContext &prog_ctx)
+  {
+    ImageCtx *ictx = reinterpret_cast<ImageCtx*>(ctx);
+    return ictx->operations->check_object_map(prog_ctx);
+  }
+
   int Image::copy(IoCtx& dest_io_ctx, const char *destname)
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
diff --git a/src/librbd/operation/ObjectMapIterate.cc b/src/librbd/operation/ObjectMapIterate.cc
new file mode 100644 (file)
index 0000000..87fab84
--- /dev/null
@@ -0,0 +1,293 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/operation/ObjectMapIterate.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/AsyncObjectThrottle.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/operation/ResizeRequest.h"
+#include "librbd/object_map/InvalidateRequest.h"
+#include "librbd/Utils.h"
+#include <boost/lambda/bind.hpp>
+#include <boost/lambda/construct.hpp>
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::ObjectMapIterateRequest: "
+
+namespace librbd {
+namespace operation {
+
+namespace {
+
+template <typename I>
+class C_VerifyObjectCallback : public C_AsyncObjectThrottle<I> {
+public:
+  C_VerifyObjectCallback(AsyncObjectThrottle<I> &throttle, I *image_ctx,
+                        uint64_t snap_id, uint64_t object_no,
+                        ObjectIterateWork<I> handle_mismatch,
+                        std::atomic_flag *invalidate)
+    : C_AsyncObjectThrottle<I>(throttle, *image_ctx),
+    m_snap_id(snap_id), m_object_no(object_no),
+    m_oid(image_ctx->get_object_name(m_object_no)),
+    m_handle_mismatch(handle_mismatch),
+    m_invalidate(invalidate)
+  {
+    m_io_ctx.dup(image_ctx->md_ctx);
+    m_io_ctx.snap_set_read(CEPH_SNAPDIR);
+  }
+
+  virtual void complete(int r) {
+    I &image_ctx = this->m_image_ctx;
+    if (should_complete(r)) {
+      ldout(image_ctx.cct, 20) << m_oid << " C_VerifyObjectCallback completed "
+                              << dendl;
+      this->finish(r);
+      delete this;
+    }
+  }
+
+  virtual int send() {
+    send_list_snaps();
+    return 0;
+  }
+
+private:
+  librados::IoCtx m_io_ctx;
+  uint64_t m_snap_id;
+  uint64_t m_object_no;
+  std::string m_oid;
+  ObjectIterateWork<I> m_handle_mismatch;
+  std::atomic_flag *m_invalidate;
+
+  librados::snap_set_t m_snap_set;
+  int m_snap_list_ret;
+
+  bool should_complete(int r) {
+    I &image_ctx = this->m_image_ctx;
+    CephContext *cct = image_ctx.cct;
+    if (r == 0) {
+      r = m_snap_list_ret;
+    }
+    if (r < 0 && r != -ENOENT) {
+      lderr(cct) << m_oid << " C_VerifyObjectCallback::should_complete: "
+                 << "encountered an error: " << cpp_strerror(r) << dendl;
+      return true;
+    }
+
+    ldout(cct, 20) << m_oid << " C_VerifyObjectCallback::should_complete: "
+                  << " r="
+                   << r << dendl;
+    return object_map_action(get_object_state());
+  }
+
+  void send_list_snaps() {
+    I &image_ctx = this->m_image_ctx;
+    assert(image_ctx.owner_lock.is_locked());
+    ldout(image_ctx.cct, 5) << m_oid
+                           << " C_VerifyObjectCallback::send_list_snaps"
+                            << dendl;
+
+    librados::ObjectReadOperation op;
+    op.list_snaps(&m_snap_set, &m_snap_list_ret);
+
+    librados::AioCompletion *comp = util::create_rados_safe_callback(this);
+    int r = m_io_ctx.aio_operate(m_oid, comp, &op, NULL);
+    assert(r == 0);
+    comp->release();
+  }
+
+  uint8_t get_object_state() {
+    I &image_ctx = this->m_image_ctx;
+    RWLock::RLocker snap_locker(image_ctx.snap_lock);
+    for (std::vector<librados::clone_info_t>::const_iterator r =
+           m_snap_set.clones.begin(); r != m_snap_set.clones.end(); ++r) {
+      librados::snap_t from_snap_id;
+      librados::snap_t to_snap_id;
+      if (r->cloneid == librados::SNAP_HEAD) {
+        from_snap_id = next_valid_snap_id(m_snap_set.seq + 1);
+        to_snap_id = librados::SNAP_HEAD;
+      } else {
+        from_snap_id = next_valid_snap_id(r->snaps[0]);
+        to_snap_id = r->snaps[r->snaps.size()-1];
+      }
+
+      if (to_snap_id < m_snap_id) {
+        continue;
+      } else if (m_snap_id < from_snap_id) {
+        break;
+      }
+
+      if ((image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0 &&
+          from_snap_id != m_snap_id) {
+        return OBJECT_EXISTS_CLEAN;
+      }
+      return OBJECT_EXISTS;
+    }
+    return OBJECT_NONEXISTENT;
+  }
+
+  uint64_t next_valid_snap_id(uint64_t snap_id) {
+    I &image_ctx = this->m_image_ctx;
+    assert(image_ctx.snap_lock.is_locked());
+
+    std::map<librados::snap_t, SnapInfo>::iterator it =
+      image_ctx.snap_info.lower_bound(snap_id);
+    if (it == image_ctx.snap_info.end()) {
+      return CEPH_NOSNAP;
+    }
+    return it->first;
+  }
+
+  bool object_map_action(uint8_t new_state) {
+    I &image_ctx = this->m_image_ctx;
+    CephContext *cct = image_ctx.cct;
+    RWLock::RLocker owner_locker(image_ctx.owner_lock);
+
+    // should have been canceled prior to releasing lock
+    assert(image_ctx.exclusive_lock == nullptr ||
+           image_ctx.exclusive_lock->is_lock_owner());
+
+    RWLock::RLocker snap_locker(image_ctx.snap_lock);
+    assert(image_ctx.object_map != nullptr);
+
+    RWLock::WLocker l(image_ctx.object_map_lock);
+    uint8_t state = (*image_ctx.object_map)[m_object_no];
+
+    ldout(cct, 10) << "C_VerifyObjectCallback::object_map_action"
+                  << " object " << image_ctx.get_object_name(m_object_no)
+                  << " state " << (int)state
+                  << " new_state " << (int)new_state << dendl;
+
+    if (state != new_state) {
+      int r = 0;
+
+      assert(m_handle_mismatch);
+      r = m_handle_mismatch(image_ctx, m_object_no, state, new_state);
+      if (r) {
+       lderr(cct) << "object map error: object "
+                  << image_ctx.get_object_name(m_object_no)
+                  << " marked as " << (int)state << ", but should be "
+                  << (int)new_state << dendl;
+       m_invalidate->test_and_set();
+      } else {
+       ldout(cct, 1) << "object map inconsistent: object "
+                  << image_ctx.get_object_name(m_object_no)
+                  << " marked as " << (int)state << ", but should be "
+                  << (int)new_state << dendl;
+      }
+    }
+
+    return true;
+  }
+};
+
+} // anonymous namespace
+
+template <typename I>
+void ObjectMapIterateRequest<I>::send() {
+  send_verify_objects();
+}
+
+template <typename I>
+bool ObjectMapIterateRequest<I>::should_complete(int r) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 5) << this << " should_complete: " << " r=" << r << dendl;
+
+  RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
+  switch (m_state) {
+  case STATE_VERIFY_OBJECTS:
+    if (m_invalidate.test_and_set()) {
+      send_invalidate_object_map();
+    } else if (r == 0) {
+      return true;
+    }
+    break;
+
+  case STATE_INVALIDATE_OBJECT_MAP:
+    if (r == 0) {
+      return true;
+    }
+    break;
+
+  default:
+    assert(false);
+    break;
+  }
+
+  if (r < 0) {
+    lderr(cct) << "object map operation encountered an error: "
+              << cpp_strerror(r)
+               << dendl;
+    return true;
+  }
+
+  return false;
+}
+
+template <typename I>
+void ObjectMapIterateRequest<I>::send_verify_objects() {
+  assert(m_image_ctx.owner_lock.is_locked());
+  CephContext *cct = m_image_ctx.cct;
+
+  uint64_t snap_id;
+  uint64_t num_objects;
+  {
+    RWLock::RLocker l(m_image_ctx.snap_lock);
+    snap_id = m_image_ctx.snap_id;
+    num_objects = Striper::get_num_objects(m_image_ctx.layout,
+                                           m_image_ctx.get_image_size(snap_id));
+  }
+  ldout(cct, 5) << this << " send_verify_objects" << dendl;
+
+  m_state = STATE_VERIFY_OBJECTS;
+
+  typename AsyncObjectThrottle<I>::ContextFactory context_factory(
+    boost::lambda::bind(boost::lambda::new_ptr<C_VerifyObjectCallback<I> >(),
+                       boost::lambda::_1, &m_image_ctx, snap_id,
+                       boost::lambda::_2, m_handle_mismatch, &m_invalidate));
+  AsyncObjectThrottle<I> *throttle = new AsyncObjectThrottle<I>(
+    this, m_image_ctx, context_factory, this->create_callback_context(),
+    &m_prog_ctx, 0, num_objects);
+  throttle->start_ops(cct->_conf->rbd_concurrent_management_ops);
+}
+
+template <typename I>
+uint64_t ObjectMapIterateRequest<I>::get_image_size() const {
+  assert(m_image_ctx.snap_lock.is_locked());
+  if (m_image_ctx.snap_id == CEPH_NOSNAP) {
+    if (!m_image_ctx.resize_reqs.empty()) {
+      return m_image_ctx.resize_reqs.front()->get_image_size();
+    } else {
+      return m_image_ctx.size;
+    }
+  }
+  return  m_image_ctx.get_image_size(m_image_ctx.snap_id);
+}
+
+template <typename I>
+void ObjectMapIterateRequest<I>::send_invalidate_object_map() {
+  CephContext *cct = m_image_ctx.cct;
+
+  ldout(cct, 5) << this << " send_invalidate_object_map" << dendl;
+  m_state = STATE_INVALIDATE_OBJECT_MAP;
+
+  object_map::InvalidateRequest<I>*req =
+    object_map::InvalidateRequest<I>::create(m_image_ctx, m_image_ctx.snap_id,
+                                            true,
+                                            this->create_callback_context());
+
+  assert(m_image_ctx.owner_lock.is_locked());
+  RWLock::WLocker snap_locker(m_image_ctx.snap_lock);
+  req->send();
+}
+
+} // namespace operation
+} // namespace librbd
+
+template class librbd::operation::ObjectMapIterateRequest<librbd::ImageCtx>;
diff --git a/src/librbd/operation/ObjectMapIterate.h b/src/librbd/operation/ObjectMapIterate.h
new file mode 100644 (file)
index 0000000..cc2dc34
--- /dev/null
@@ -0,0 +1,66 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_LIBRBD_OPERATION_OBJECT_MAP_ITERATE_H
+#define CEPH_LIBRBD_OPERATION_OBJECT_MAP_ITERATE_H
+
+#include <iostream>
+#include <atomic>
+
+#include "include/int_types.h"
+#include "include/rbd/object_map_types.h"
+#include "librbd/AsyncRequest.h"
+
+namespace librbd {
+
+class ImageCtx;
+class ProgressContext;
+
+namespace operation {
+
+template <typename ImageCtxT = ImageCtx>
+using ObjectIterateWork = bool(*)(ImageCtxT &image_ctx,
+                                 uint64_t object_no,
+                                 uint8_t current_state,
+                                 uint8_t new_state);
+
+template <typename ImageCtxT = ImageCtx>
+class ObjectMapIterateRequest : public AsyncRequest<ImageCtxT> {
+public:
+  ObjectMapIterateRequest(ImageCtxT &image_ctx, Context *on_finish,
+                         ProgressContext &prog_ctx,
+                         ObjectIterateWork<ImageCtxT> handle_mismatch)
+    : AsyncRequest<ImageCtxT>(image_ctx, on_finish), m_image_ctx(image_ctx),
+    m_prog_ctx(prog_ctx), m_handle_mismatch(handle_mismatch),
+    m_invalidate(ATOMIC_FLAG_INIT)
+  {
+  }
+
+  virtual void send();
+
+protected:
+  virtual bool should_complete(int r);
+
+private:
+  enum State {
+    STATE_VERIFY_OBJECTS,
+    STATE_INVALIDATE_OBJECT_MAP
+  };
+
+  ImageCtxT &m_image_ctx;
+  ProgressContext &m_prog_ctx;
+  ObjectIterateWork<ImageCtxT> m_handle_mismatch;
+  std::atomic_flag m_invalidate;
+  State m_state;
+
+  void send_verify_objects();
+  void send_invalidate_object_map();
+
+  uint64_t get_image_size() const;
+};
+
+} // namespace operation
+} // namespace librbd
+
+extern template class librbd::operation::ObjectMapIterateRequest<librbd::ImageCtx>;
+
+#endif
index da2e744ce90432f8127a4f84761c460f82ff78bc..aa65618b60ae9c3d57dd1692b2fea17218bfb221 100644 (file)
@@ -12,6 +12,7 @@
 #include "librbd/ObjectMap.h"
 #include "librbd/operation/ResizeRequest.h"
 #include "librbd/operation/TrimRequest.h"
+#include "librbd/operation/ObjectMapIterate.h"
 #include "librbd/Utils.h"
 #include <boost/lambda/bind.hpp>
 #include <boost/lambda/construct.hpp>
 namespace librbd {
 namespace operation {
 
-namespace {
-
-template <typename I>
-class C_VerifyObject : public C_AsyncObjectThrottle<I> {
-public:
-  C_VerifyObject(AsyncObjectThrottle<I> &throttle, I *image_ctx,
-                 uint64_t snap_id, uint64_t object_no)
-    : C_AsyncObjectThrottle<I>(throttle, *image_ctx), m_snap_id(snap_id),
-      m_object_no(object_no),
-      m_oid(image_ctx->get_object_name(m_object_no)),
-      m_snap_list_ret(0)
-  {
-    m_io_ctx.dup(image_ctx->md_ctx);
-    m_io_ctx.snap_set_read(CEPH_SNAPDIR);
-  }
-
-  virtual void complete(int r) {
-    I &image_ctx = this->m_image_ctx;
-    if (should_complete(r)) {
-      ldout(image_ctx.cct, 20) << m_oid << " C_VerifyObject completed "
-                                 << dendl;
-      this->finish(r);
-      delete this;
-    }
-  }
-
-  virtual int send() {
-    send_list_snaps();
-    return 0;
-  }
-
-private:
-  librados::IoCtx m_io_ctx;
-  uint64_t m_snap_id;
-  uint64_t m_object_no;
-  std::string m_oid;
-
-  librados::snap_set_t m_snap_set;
-  int m_snap_list_ret;
-
-  bool should_complete(int r) {
-    I &image_ctx = this->m_image_ctx;
-    CephContext *cct = image_ctx.cct;
-    if (r == 0) {
-      r = m_snap_list_ret;
-    }
-    if (r < 0 && r != -ENOENT) {
-      lderr(cct) << m_oid << " C_VerifyObject::should_complete: "
-                 << "encountered an error: " << cpp_strerror(r) << dendl;
-      return true;
-    }
-
-    ldout(cct, 20) << m_oid << " C_VerifyObject::should_complete: " << " r="
-                   << r << dendl;
-    return update_object_map(get_object_state());
-  }
-
-  void send_list_snaps() {
-    I &image_ctx = this->m_image_ctx;
-    assert(image_ctx.owner_lock.is_locked());
-    ldout(image_ctx.cct, 5) << m_oid << " C_VerifyObject::send_list_snaps"
-                            << dendl;
-
-    librados::ObjectReadOperation op;
-    op.list_snaps(&m_snap_set, &m_snap_list_ret);
-
-    librados::AioCompletion *comp = util::create_rados_safe_callback(this);
-    int r = m_io_ctx.aio_operate(m_oid, comp, &op, NULL);
-    assert(r == 0);
-    comp->release();
-  }
-
-  uint8_t get_object_state() {
-    I &image_ctx = this->m_image_ctx;
-    RWLock::RLocker snap_locker(image_ctx.snap_lock);
-    for (std::vector<librados::clone_info_t>::const_iterator r =
-           m_snap_set.clones.begin(); r != m_snap_set.clones.end(); ++r) {
-      librados::snap_t from_snap_id;
-      librados::snap_t to_snap_id;
-      if (r->cloneid == librados::SNAP_HEAD) {
-        from_snap_id = next_valid_snap_id(m_snap_set.seq + 1);
-        to_snap_id = librados::SNAP_HEAD;
-      } else {
-        from_snap_id = next_valid_snap_id(r->snaps[0]);
-        to_snap_id = r->snaps[r->snaps.size()-1];
-      }
-
-      if (to_snap_id < m_snap_id) {
-        continue;
-      } else if (m_snap_id < from_snap_id) {
-        break;
-      }
-
-      if ((image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0 &&
-          from_snap_id != m_snap_id) {
-        return OBJECT_EXISTS_CLEAN;
-      }
-      return OBJECT_EXISTS;
-    }
-    return OBJECT_NONEXISTENT;
-  }
-
-  uint64_t next_valid_snap_id(uint64_t snap_id) {
-    I &image_ctx = this->m_image_ctx;
-    assert(image_ctx.snap_lock.is_locked());
-
-    std::map<librados::snap_t, SnapInfo>::iterator it =
-      image_ctx.snap_info.lower_bound(snap_id);
-    if (it == image_ctx.snap_info.end()) {
-      return CEPH_NOSNAP;
-    }
-    return it->first;
-  }
-
-  bool update_object_map(uint8_t new_state) {
-    I &image_ctx = this->m_image_ctx;
-    RWLock::RLocker owner_locker(image_ctx.owner_lock);
-    CephContext *cct = image_ctx.cct;
-
-    // should have been canceled prior to releasing lock
-    assert(image_ctx.exclusive_lock == nullptr ||
-           image_ctx.exclusive_lock->is_lock_owner());
-
-    RWLock::RLocker snap_locker(image_ctx.snap_lock);
-    assert(image_ctx.object_map != nullptr);
-
-    RWLock::WLocker l(image_ctx.object_map_lock);
-    uint8_t state = (*image_ctx.object_map)[m_object_no];
-    if (state == OBJECT_EXISTS && new_state == OBJECT_NONEXISTENT &&
-        m_snap_id == CEPH_NOSNAP) {
-      // might be writing object to OSD concurrently
-      new_state = state;
-    }
-
-    if (new_state != state) {
-      ldout(cct, 15) << m_oid << " C_VerifyObject::update_object_map "
-                     << static_cast<uint32_t>(state) << "->"
-                     << static_cast<uint32_t>(new_state) << dendl;
-      (*image_ctx.object_map)[m_object_no] = new_state;
-    }
-    return true;
-  }
-};
-
-} // anonymous namespace
-
 template <typename I>
 void RebuildObjectMapRequest<I>::send() {
   send_resize_object_map();
@@ -294,34 +149,42 @@ void RebuildObjectMapRequest<I>::send_trim_image() {
 }
 
 template <typename I>
-void RebuildObjectMapRequest<I>::send_verify_objects() {
-  assert(m_image_ctx.owner_lock.is_locked());
-  CephContext *cct = m_image_ctx.cct;
+bool update_object_map(I& image_ctx, uint64_t object_no, uint8_t current_state,
+                     uint8_t new_state) {
+  CephContext *cct = image_ctx.cct;
+  uint64_t snap_id = image_ctx.snap_id;
 
-  uint64_t snap_id;
-  uint64_t num_objects;
-  {
-    RWLock::RLocker l(m_image_ctx.snap_lock);
-    snap_id = m_image_ctx.snap_id;
-    num_objects = Striper::get_num_objects(m_image_ctx.layout,
-                                           m_image_ctx.get_image_size(snap_id));
+  uint8_t state = (*image_ctx.object_map)[object_no];
+  if (state == OBJECT_EXISTS && new_state == OBJECT_NONEXISTENT &&
+      snap_id == CEPH_NOSNAP) {
+    // might be writing object to OSD concurrently
+    new_state = state;
   }
 
-  if (num_objects == 0) {
-    send_save_object_map();
-    return;
+  if (new_state != state) {
+    ldout(cct, 15) << image_ctx.get_object_name(object_no)
+      << " rebuild updating object map "
+      << static_cast<uint32_t>(state) << "->"
+      << static_cast<uint32_t>(new_state) << dendl;
+    (*image_ctx.object_map)[object_no] = new_state;
   }
+  return false;
+}
+
+template <typename I>
+void RebuildObjectMapRequest<I>::send_verify_objects() {
+  assert(m_image_ctx.owner_lock.is_locked());
+  CephContext *cct = m_image_ctx.cct;
 
   m_state = STATE_VERIFY_OBJECTS;
   ldout(cct, 5) << this << " send_verify_objects" << dendl;
 
-  typename AsyncObjectThrottle<I>::ContextFactory context_factory(
-    boost::lambda::bind(boost::lambda::new_ptr<C_VerifyObject<I> >(),
-      boost::lambda::_1, &m_image_ctx, snap_id, boost::lambda::_2));
-  AsyncObjectThrottle<I> *throttle = new AsyncObjectThrottle<I>(
-    this, m_image_ctx, context_factory, this->create_callback_context(),
-    &m_prog_ctx, 0, num_objects);
-  throttle->start_ops(cct->_conf->rbd_concurrent_management_ops);
+  ObjectMapIterateRequest<I> *req =
+    new ObjectMapIterateRequest<I>(m_image_ctx,
+                                  this->create_callback_context(),
+                                  m_prog_ctx, update_object_map);
+
+  req->send();
 }
 
 template <typename I>
index c3754610b12d05469d9f1f68e004dbcbfe0da8b9..0a84c868cd0ab477d23992877ab748f1c0ec3b71 100644 (file)
@@ -3669,6 +3669,71 @@ TEST_F(TestLibRBD, RebuildNewObjectMap)
   rados_ioctx_destroy(ioctx);
 }
 
+TEST_F(TestLibRBD, CheckObjectMap)
+{
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+  librbd::RBD rbd;
+  std::string name = get_temp_image_name();
+  uint64_t size = 1 << 20;
+  int order = 18;
+  ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+  PrintProgress prog_ctx;
+  bufferlist bl1;
+  bufferlist bl2;
+  bl1.append("foo");
+  {
+    librbd::Image image;
+    ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+    uint64_t features;
+    ASSERT_EQ(0, image.features(&features));
+
+    ASSERT_EQ(bl1.length(), image.write(0, bl1.length(), bl1));
+
+    ASSERT_EQ(0, image.snap_create("snap1"));
+    ASSERT_EQ(bl1.length(), image.write(1<<order, bl1.length(), bl1));
+  }
+
+  librbd::Image image1;
+  ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+  std::string image_id;
+  ASSERT_EQ(0, get_image_id(image1, &image_id));
+
+  std::string object_map_oid = RBD_OBJECT_MAP_PREFIX + image_id;
+
+  ASSERT_LT(0, ioctx.read(object_map_oid, bl2, 1024, 0));
+
+  bool lock_owner;
+  ASSERT_EQ(bl1.length(), image1.write(3 * (1 << 18), bl1.length(), bl1));
+  ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+  ASSERT_TRUE(lock_owner);
+
+  //reopen image to reread now corrupt object map from disk
+  image1.close();
+
+  bl1.clear();
+  ASSERT_LT(0, ioctx.read(object_map_oid, bl1, 1024, 0));
+  ASSERT_FALSE(bl1.contents_equal(bl2));
+
+  ASSERT_EQ(0, ioctx.write_full(object_map_oid, bl2));
+  ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+  uint64_t flags;
+  ASSERT_EQ(0, image1.get_flags(&flags));
+  ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) == 0);
+
+  ASSERT_EQ(0, image1.check_object_map(prog_ctx));
+
+  ASSERT_EQ(0, image1.get_flags(&flags));
+  ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0);
+}
+
 TEST_F(TestLibRBD, BlockingAIO)
 {
   librados::IoCtx ioctx;
index 17525c896c4640d36cb8bec7adcec8dc31b15935..dfd387b4d94f598d60ba54d10b1d86403bcb32dc 100644 (file)
@@ -27,14 +27,14 @@ static int do_object_map_rebuild(librbd::Image &image, bool no_progress)
   return 0;
 }
 
-void get_arguments(po::options_description *positional,
-                   po::options_description *options) {
+void get_rebuild_arguments(po::options_description *positional,
+                          po::options_description *options) {
   at::add_image_or_snap_spec_options(positional, options,
                                      at::ARGUMENT_MODIFIER_NONE);
   at::add_no_progress_option(options);
 }
 
-int execute(const po::variables_map &vm) {
+int execute_rebuild(const po::variables_map &vm) {
   size_t arg_index = 0;
   std::string pool_name;
   std::string image_name;
@@ -65,9 +65,62 @@ int execute(const po::variables_map &vm) {
   return 0;
 }
 
-Shell::Action action(
+static int do_object_map_check(librbd::Image &image, bool no_progress)
+{
+  utils::ProgressContext pc("Object Map Check", no_progress);
+  int r = image.check_object_map(pc);
+  if (r < 0) {
+    pc.fail();
+    return r;
+  }
+  pc.finish();
+  return 0;
+}
+
+void get_check_arguments(po::options_description *positional,
+                  po::options_description *options) {
+  at::add_image_or_snap_spec_options(positional, options,
+                                    at::ARGUMENT_MODIFIER_NONE);
+  at::add_no_progress_option(options);
+}
+
+int execute_check(const po::variables_map &vm) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
+    &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
+    utils::SPEC_VALIDATION_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, image_name, snap_name, false,
+                                &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  r = do_object_map_check(image, vm[at::NO_PROGRESS].as<bool>());
+  if (r < 0) {
+    std::cerr << "rbd: checking object map failed: " << cpp_strerror(r)
+             << std::endl;
+    return r;
+  }
+  return 0;
+}
+
+Shell::Action action_rebuild(
   {"object-map", "rebuild"}, {}, "Rebuild an invalid object map.", "",
-  &get_arguments, &execute);
+  &get_rebuild_arguments, &execute_rebuild);
+Shell::Action action_check(
+  {"object-map", "check"}, {}, "Verify the object map is correct.", "",
+  &get_check_arguments, &execute_check);
 
 } // namespace object_map
 } // namespace action