#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)
{
}
// 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;
}
namespace cls {
namespace rbd {
+static const uint32_t MAX_OBJECT_MAP_OBJECT_COUNT = 256000000;
+
enum MirrorMode {
MIRROR_MODE_DISABLED = 0,
MIRROR_MODE_IMAGE = 1,
#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"
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();
}
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;
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
* @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
*/
void send_resize();
Context *handle_resize(int *ret_val);
+ void send_invalidate_and_close();
+ Context *handle_invalidate_and_close(int *ret_val);
+
void apply();
};
};
TEST_F(TestMockObjectMapRefreshRequest, SuccessHead) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
}
TEST_F(TestMockObjectMapRefreshRequest, SuccessSnapshot) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
}
TEST_F(TestMockObjectMapRefreshRequest, LoadError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
}
TEST_F(TestMockObjectMapRefreshRequest, LoadCorrupt) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
}
TEST_F(TestMockObjectMapRefreshRequest, TooSmall) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
}
TEST_F(TestMockObjectMapRefreshRequest, TooLarge) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
}
TEST_F(TestMockObjectMapRefreshRequest, ResizeError) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
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