]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: add client-side memory constraint when loading object map
authorJason Dillaman <dillaman@redhat.com>
Wed, 4 May 2016 19:01:58 +0000 (15:01 -0400)
committerJason Dillaman <dillaman@redhat.com>
Tue, 10 May 2016 17:31:47 +0000 (13:31 -0400)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
(cherry picked from commit 2dd34b2c7b8c376817aafe5cfdda7aa9cfb60e1a)

src/cls/rbd/cls_rbd.cc
src/cls/rbd/cls_rbd_types.h
src/librbd/object_map/RefreshRequest.cc
src/librbd/object_map/RefreshRequest.h
src/test/librbd/object_map/test_mock_RefreshRequest.cc

index 1b38eabfbe1903e2cb55590ed46554bebeb11377..f56e4e0da4832d5f2f22e1f536ea9c10c8bf0e54 100644 (file)
@@ -138,7 +138,6 @@ cls_method_handle_t h_mirror_image_status_remove_down;
 #define RBD_DIR_ID_KEY_PREFIX "id_"
 #define RBD_DIR_NAME_KEY_PREFIX "name_"
 #define RBD_METADATA_KEY_PREFIX "metadata_"
-#define RBD_MAX_OBJECT_MAP_OBJECT_COUNT 256000000
 
 static int snap_read_header(cls_method_context_t hctx, bufferlist& bl)
 {
@@ -2268,7 +2267,7 @@ int object_map_resize(cls_method_context_t hctx, bufferlist *in, bufferlist *out
   }
 
   // protect against excessive memory requirements
-  if (object_count > RBD_MAX_OBJECT_MAP_OBJECT_COUNT) {
+  if (object_count > cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT) {
     CLS_ERR("object map too large: %" PRIu64, object_count);
     return -EINVAL;
   }
index c8780f06820d5eecc42633391661d32ae15ba25e..f98a942e2adc9155e4990046989cbf892eeed3e4 100644 (file)
@@ -16,6 +16,8 @@ namespace ceph { class Formatter; }
 namespace cls {
 namespace rbd {
 
+static const uint32_t MAX_OBJECT_MAP_OBJECT_COUNT = 256000000;
+
 enum MirrorMode {
   MIRROR_MODE_DISABLED = 0,
   MIRROR_MODE_IMAGE    = 1,
index 048f72fcdd2e0b0ad8ab29a1b89dffc9495f526b..1bd465e10bebfedeaa8785d769f3b9e73fd4216e 100644 (file)
@@ -3,9 +3,11 @@
 
 #include "librbd/object_map/RefreshRequest.h"
 #include "cls/rbd/cls_rbd_client.h"
+#include "cls/rbd/cls_rbd_types.h"
 #include "cls/lock/cls_lock_client.h"
 #include "common/dout.h"
 #include "common/errno.h"
+#include "common/WorkQueue.h"
 #include "librbd/ImageCtx.h"
 #include "librbd/ObjectMap.h"
 #include "librbd/object_map/InvalidateRequest.h"
@@ -42,6 +44,10 @@ void RefreshRequest<I>::send() {
       m_image_ctx.layout, m_image_ctx.get_image_size(m_snap_id));
   }
 
+
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 20) << this << " " << __func__ << ": "
+                 << "object_count=" << m_object_count << dendl;
   send_lock();
 }
 
@@ -60,12 +66,15 @@ void RefreshRequest<I>::apply() {
 
 template <typename I>
 void RefreshRequest<I>::send_lock() {
-  if (m_snap_id != CEPH_NOSNAP) {
+  CephContext *cct = m_image_ctx.cct;
+  if (m_object_count > cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT) {
+    send_invalidate_and_close();
+    return;
+  } else if (m_snap_id != CEPH_NOSNAP) {
     send_load();
     return;
   }
 
-  CephContext *cct = m_image_ctx.cct;
   std::string oid(ObjectMap::object_map_name(m_image_ctx.id, m_snap_id));
   ldout(cct, 10) << this << " " << __func__ << ": oid=" << oid << dendl;
 
@@ -248,6 +257,35 @@ Context *RefreshRequest<I>::handle_resize(int *ret_val) {
   return m_on_finish;
 }
 
+template <typename I>
+void RefreshRequest<I>::send_invalidate_and_close() {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 10) << this << " " << __func__ << dendl;
+
+  using klass = RefreshRequest<I>;
+  Context *ctx = create_context_callback<
+    klass, &klass::handle_invalidate_and_close>(this);
+  InvalidateRequest<I> *req = InvalidateRequest<I>::create(
+    m_image_ctx, m_snap_id, false, ctx);
+
+  lderr(cct) << "object map too large: " << m_object_count << dendl;
+  RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
+  RWLock::WLocker snap_locker(m_image_ctx.snap_lock);
+  req->send();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_invalidate_and_close(int *ret_val) {
+  CephContext *cct = m_image_ctx.cct;
+  ldout(cct, 10) << this << " " << __func__ << ": r=" << *ret_val << dendl;
+
+  assert(*ret_val == 0);
+
+  *ret_val = -EFBIG;
+  m_object_map->clear();
+  return m_on_finish;
+}
+
 } // namespace object_map
 } // namespace librbd
 
index 2c94d1ed25c254dcb126599b51e2cab172ebd07a..9ae1f270d83f7c43b96be280b1a45bfc731ebd1a 100644 (file)
@@ -28,24 +28,25 @@ private:
    * @verbatim
    *
    * <start> -----> LOCK (skip if snapshot)
-   *                  |
-   *                  v  (other errors)
-   *                LOAD * * * * * * * > INVALIDATE ------------\
-   *                  |    *                                    |
-   *                  |    * (-EINVAL or too small)             |
-   *                  |    * * * * * * > INVALIDATE_AND_RESIZE  |
-   *                  |                      |              *   |
-   *                  |                      |              *   |
-   *                  |                      v              *   |
-   *                  |                    RESIZE           *   |
-   *                  |                      |              *   |
-   *                  |                      |  * * * * * * *   |
-   *                  |                      |  *               |
-   *                  |                      v  v               |
-   *                  \--------------------> LOCK <-------------/
-   *                                          |
-   *                                          v
-   *                                      <finish>
+   *    *             |
+   *    *             v  (other errors)
+   *    *           LOAD * * * * * * * > INVALIDATE ------------\
+   *    *             |    *                                    |
+   *    *             |    * (-EINVAL or too small)             |
+   *    *             |    * * * * * * > INVALIDATE_AND_RESIZE  |
+   *    *             |                      |              *   |
+   *    *             |                      |              *   |
+   *    *             |                      v              *   |
+   *    *             |                    RESIZE           *   |
+   *    *             |                      |              *   |
+   *    *             |                      |  * * * * * * *   |
+   *    *             |                      |  *               |
+   *    *             |                      v  v               |
+   *    *             \--------------------> LOCK <-------------/
+   *    *                                     |
+   *    v                                     v
+   * INVALIDATE_AND_CLOSE ---------------> <finish>
+   *
    * @endverbatim
    */
 
@@ -74,6 +75,9 @@ private:
   void send_resize();
   Context *handle_resize(int *ret_val);
 
+  void send_invalidate_and_close();
+  Context *handle_invalidate_and_close(int *ret_val);
+
   void apply();
 };
 
index 0dfbb4c07d6fe792fbfb31a79185aa291fb00641..be982ce684e8968504e17c94b0fd13188addcae7 100644 (file)
@@ -142,6 +142,8 @@ public:
 };
 
 TEST_F(TestMockObjectMapRefreshRequest, SuccessHead) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -170,6 +172,8 @@ TEST_F(TestMockObjectMapRefreshRequest, SuccessHead) {
 }
 
 TEST_F(TestMockObjectMapRefreshRequest, SuccessSnapshot) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -196,6 +200,8 @@ TEST_F(TestMockObjectMapRefreshRequest, SuccessSnapshot) {
 }
 
 TEST_F(TestMockObjectMapRefreshRequest, LoadError) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -224,6 +230,8 @@ TEST_F(TestMockObjectMapRefreshRequest, LoadError) {
 }
 
 TEST_F(TestMockObjectMapRefreshRequest, LoadCorrupt) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -254,6 +262,8 @@ TEST_F(TestMockObjectMapRefreshRequest, LoadCorrupt) {
 }
 
 TEST_F(TestMockObjectMapRefreshRequest, TooSmall) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -285,6 +295,8 @@ TEST_F(TestMockObjectMapRefreshRequest, TooSmall) {
 }
 
 TEST_F(TestMockObjectMapRefreshRequest, TooLarge) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -312,6 +324,8 @@ TEST_F(TestMockObjectMapRefreshRequest, TooLarge) {
 }
 
 TEST_F(TestMockObjectMapRefreshRequest, ResizeError) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
 
@@ -342,6 +356,33 @@ TEST_F(TestMockObjectMapRefreshRequest, ResizeError) {
   ASSERT_EQ(0, ctx.wait());
 }
 
+TEST_F(TestMockObjectMapRefreshRequest, LargeImageError) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockObjectMapImageCtx mock_image_ctx(*ictx);
+
+  ceph::BitVector<2> on_disk_object_map;
+  init_object_map(mock_image_ctx, &on_disk_object_map);
+
+  C_SaferCond ctx;
+  ceph::BitVector<2> object_map;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &object_map,
+                                                   TEST_SNAP_ID, &ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, TEST_SNAP_ID,
+                        std::numeric_limits<int64_t>::max());
+
+  MockInvalidateRequest invalidate_request;
+  expect_invalidate_request(mock_image_ctx, invalidate_request);
+
+  req->send();
+  ASSERT_EQ(-EFBIG, ctx.wait());
+}
+
 } // namespace object_map
 } // namespace librbd