#include "librbd/deep_copy/Utils.h"
#include "librbd/image/CloseRequest.h"
#include "librbd/image/OpenRequest.h"
+#include "librbd/object_map/DiffRequest.h"
#include "osdc/Striper.h"
#define dout_subsys ceph_subsys_rbd
return;
}
- send_object_copies();
+ compute_diff();
}
template <typename I>
m_canceled = true;
}
+template <typename I>
+void ImageCopyRequest<I>::compute_diff() {
+ ldout(m_cct, 10) << dendl;
+
+ auto ctx = create_context_callback<
+ ImageCopyRequest<I>, &ImageCopyRequest<I>::handle_compute_diff>(this);
+ auto req = object_map::DiffRequest<I>::create(m_src_image_ctx, m_src_snap_id_start,
+ m_src_snap_id_end, &m_object_diff_state,
+ ctx);
+ req->send();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::handle_compute_diff(int r) {
+ ldout(m_cct, 10) << "r=" << r << dendl;
+
+ if (r < 0) {
+ ldout(m_cct, 10) << "fast-diff optimization disabled" << dendl;
+ m_object_diff_state.resize(0);
+ }
+
+ send_object_copies();
+}
+
template <typename I>
void ImageCopyRequest<I>::send_object_copies() {
m_object_no = 0;
bool complete;
{
std::lock_guard locker{m_lock};
- for (uint64_t i = 0;
- i < m_src_image_ctx->config.template get_val<uint64_t>("rbd_concurrent_management_ops");
- ++i) {
- send_next_object_copy();
- if (m_ret_val < 0 && m_current_ops == 0) {
+ auto max_ops = m_src_image_ctx->config.template get_val<uint64_t>(
+ "rbd_concurrent_management_ops");
+
+ // attempt to schedule at least 'max_ops' initial requests where
+ // some objects might be skipped if fast-diff notes no change
+ while (m_current_ops < max_ops) {
+ int r = send_next_object_copy();
+ if (r < 0) {
break;
}
}
+
complete = (m_current_ops == 0) && !m_updating_progress;
}
}
template <typename I>
-void ImageCopyRequest<I>::send_next_object_copy() {
+int ImageCopyRequest<I>::send_next_object_copy() {
ceph_assert(ceph_mutex_is_locked(m_lock));
if (m_canceled && m_ret_val == 0) {
m_ret_val = -ECANCELED;
}
- if (m_ret_val < 0 || m_object_no >= m_end_object_no) {
- return;
+ if (m_ret_val < 0) {
+ return m_ret_val;
+ } else if (m_object_no >= m_end_object_no) {
+ return -ENODATA;
}
uint64_t ono = m_object_no++;
+ if (ono < m_object_diff_state.size() &&
+ m_object_diff_state[ono] == object_map::DIFF_STATE_NONE) {
+ ldout(m_cct, 20) << "skipping clean object " << ono << dendl;
+ return 1;
+ }
ldout(m_cct, 20) << "object_num=" << ono << dendl;
-
++m_current_ops;
Context *ctx = new LambdaContext(
m_src_image_ctx, m_dst_image_ctx, m_src_snap_id_start, m_dst_snap_id_start,
m_snap_map, ono, m_flatten, ctx);
req->send();
+ return 0;
}
template <typename I>
}
}
- send_next_object_copy();
+ while (true) {
+ r = send_next_object_copy();
+ if (r != 1) {
+ break;
+ }
+ }
+
complete = (m_current_ops == 0) && !m_updating_progress;
}
#include "include/rbd/librbd.hpp"
#include "librbd/ImageCtx.h"
#include "librbd/ImageState.h"
+#include "librbd/internal.h"
#include "librbd/Operations.h"
#include "librbd/deep_copy/ImageCopyRequest.h"
#include "librbd/deep_copy/ObjectCopyRequest.h"
#include "librbd/image/CloseRequest.h"
#include "librbd/image/OpenRequest.h"
-#include "librbd/internal.h"
+#include "librbd/object_map/DiffRequest.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librbd/test_support.h"
} // namespace image
+namespace object_map {
+
+template <>
+struct DiffRequest<MockTestImageCtx> {
+ BitVector<2>* object_diff_state = nullptr;
+ Context* on_finish = nullptr;
+ static DiffRequest* s_instance;
+ static DiffRequest* create(MockTestImageCtx *image_ctx,
+ uint64_t snap_id_start, uint64_t snap_id_end,
+ BitVector<2>* object_diff_state,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->object_diff_state = object_diff_state;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ DiffRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+DiffRequest<MockTestImageCtx>* DiffRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace object_map
} // namespace librbd
// template definitions
using ::testing::_;
using ::testing::InSequence;
+using ::testing::Invoke;
using ::testing::Return;
class TestMockDeepCopyImageCopyRequest : public TestMockFixture {
public:
typedef ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
typedef ObjectCopyRequest<librbd::MockTestImageCtx> MockObjectCopyRequest;
+ typedef object_map::DiffRequest<librbd::MockTestImageCtx> MockDiffRequest;
librbd::ImageCtx *m_src_image_ctx;
librbd::ImageCtx *m_dst_image_ctx;
.WillOnce(Return(size)).RetiresOnSaturation();
}
+ void expect_diff_send(MockDiffRequest& mock_request,
+ const BitVector<2>& diff_state, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([this, &mock_request, diff_state, r]() {
+ if (r >= 0) {
+ *mock_request.object_diff_state = diff_state;
+ }
+ m_work_queue->queue(mock_request.on_finish, r);
+ }));
+ }
+
void expect_object_copy_send(MockObjectCopyRequest &mock_object_copy_request) {
EXPECT_CALL(mock_object_copy_request, send());
}
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
expect_get_image_size(mock_src_image_ctx, 0);
expect_object_copy_send(mock_object_copy_request);
ASSERT_EQ(0, ctx.wait());
}
+TEST_F(TestMockDeepCopyImageCopyRequest, FastDiff) {
+ librados::snap_t snap_id_end;
+ ASSERT_EQ(0, create_snap("copy", &snap_id_end));
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+
+ InSequence seq;
+
+ MockDiffRequest mock_diff_request;
+ BitVector<2> diff_state;
+ diff_state.resize(1);
+ expect_diff_send(mock_diff_request, diff_state, 0);
+
+ expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
+ expect_get_image_size(mock_src_image_ctx, 0);
+
+ librbd::NoOpProgressContext no_op;
+ C_SaferCond ctx;
+ auto request = new MockImageCopyRequest(&mock_src_image_ctx,
+ &mock_dst_image_ctx,
+ 0, snap_id_end, 0, false, boost::none,
+ m_snap_seqs, &no_op, &ctx);
+ request->send();
+
+ ASSERT_EQ(0, ctx.wait());
+}
+
TEST_F(TestMockDeepCopyImageCopyRequest, OutOfOrder) {
std::string max_ops_str;
ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
MockObjectCopyRequest mock_object_copy_request;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
expect_get_image_size(mock_src_image_ctx,
object_count * (1 << m_src_image_ctx->order));
expect_get_image_size(mock_src_image_ctx, 0);
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
expect_get_image_size(mock_src_image_ctx, 0);
expect_get_image_size(mock_src_image_ctx, 0);
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order));
expect_get_image_size(mock_src_image_ctx, 0);
expect_object_copy_send(mock_object_copy_request);
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
expect_get_image_size(mock_src_image_ctx, 0);
expect_object_copy_send(mock_object_copy_request);
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ MockDiffRequest mock_diff_request;
+ expect_diff_send(mock_diff_request, {}, -EINVAL);
expect_get_image_size(mock_src_image_ctx, 6 * (1 << m_src_image_ctx->order));
expect_get_image_size(mock_src_image_ctx, m_image_size);