Context *ctx = create_context_callback<
DeepCopyRequest<I>, &DeepCopyRequest<I>::handle_copy_snapshots>(this);
m_snapshot_copy_request = SnapshotCopyRequest<I>::create(
- m_src_image_ctx, m_dst_image_ctx, m_src_snap_id_end, m_flatten,
- m_work_queue, m_snap_seqs, ctx);
+ m_src_image_ctx, m_dst_image_ctx, m_src_snap_id_start, m_src_snap_id_end,
+ m_dst_snap_id_start, m_flatten, m_work_queue, m_snap_seqs, ctx);
m_snapshot_copy_request->get();
m_lock.unlock();
C_SaferCond on_snapshot_copy;
auto snapshot_copy_req = librbd::deep_copy::SnapshotCopyRequest<I>::create(
- m_src_image_ctx, dst_image_ctx, CEPH_NOSNAP, m_flatten,
+ m_src_image_ctx, dst_image_ctx, 0, CEPH_NOSNAP, 0, m_flatten,
m_src_image_ctx->op_work_queue, &snap_seqs, &on_snapshot_copy);
snapshot_copy_req->send();
r = on_snapshot_copy.wait();
template <typename I>
SnapshotCopyRequest<I>::SnapshotCopyRequest(I *src_image_ctx,
I *dst_image_ctx,
- librados::snap_t snap_id_end,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
bool flatten, ContextWQ *work_queue,
SnapSeqs *snap_seqs,
Context *on_finish)
: RefCountedObject(dst_image_ctx->cct), m_src_image_ctx(src_image_ctx),
- m_dst_image_ctx(dst_image_ctx), m_snap_id_end(snap_id_end),
+ m_dst_image_ctx(dst_image_ctx), m_src_snap_id_start(src_snap_id_start),
+ m_src_snap_id_end(src_snap_id_end), m_dst_snap_id_start(dst_snap_id_start),
m_flatten(flatten), m_work_queue(work_queue), m_snap_seqs_result(snap_seqs),
m_snap_seqs(*snap_seqs), m_on_finish(on_finish), m_cct(dst_image_ctx->cct),
m_lock(ceph::make_mutex(unique_lock_name("SnapshotCopyRequest::m_lock", this))) {
+ ceph_assert((m_src_snap_id_start == 0 && m_dst_snap_id_start == 0) ||
+ (m_src_snap_id_start > 0 && m_dst_snap_id_start > 0));
+
// snap ids ordered from oldest to newest
+ m_src_image_ctx->image_lock.lock_shared();
m_src_snap_ids.insert(src_image_ctx->snaps.begin(),
src_image_ctx->snaps.end());
+ m_src_image_ctx->image_lock.unlock_shared();
+
+ m_dst_image_ctx->image_lock.lock_shared();
m_dst_snap_ids.insert(dst_image_ctx->snaps.begin(),
dst_image_ctx->snaps.end());
- if (m_snap_id_end != CEPH_NOSNAP) {
- m_src_snap_ids.erase(m_src_snap_ids.upper_bound(m_snap_id_end),
+ m_dst_image_ctx->image_lock.unlock_shared();
+
+ if (m_src_snap_id_end != CEPH_NOSNAP) {
+ m_src_snap_ids.erase(m_src_snap_ids.upper_bound(m_src_snap_id_end),
m_src_snap_ids.end());
}
}
SnapIdSet::iterator snap_id_it = m_dst_snap_ids.begin();
if (m_prev_snap_id != CEPH_NOSNAP) {
snap_id_it = m_dst_snap_ids.upper_bound(m_prev_snap_id);
+ } else if (m_dst_snap_id_start > 0) {
+ snap_id_it = m_dst_snap_ids.upper_bound(m_dst_snap_id_start);
}
for (; snap_id_it != m_dst_snap_ids.end(); ++snap_id_it) {
if (r < 0) {
lderr(m_cct) << "failed to unprotect snapshot '" << m_snap_name << "': "
- << cpp_strerror(r) << dendl;
+ << cpp_strerror(r) << dendl;
finish(r);
return;
}
SnapIdSet::iterator snap_id_it = m_dst_snap_ids.begin();
if (m_prev_snap_id != CEPH_NOSNAP) {
snap_id_it = m_dst_snap_ids.upper_bound(m_prev_snap_id);
+ } else if (m_dst_snap_id_start > 0) {
+ snap_id_it = m_dst_snap_ids.upper_bound(m_dst_snap_id_start);
}
for (; snap_id_it != m_dst_snap_ids.end(); ++snap_id_it) {
SnapIdSet::iterator snap_id_it = m_src_snap_ids.begin();
if (m_prev_snap_id != CEPH_NOSNAP) {
snap_id_it = m_src_snap_ids.upper_bound(m_prev_snap_id);
+ } else if (m_src_snap_id_start > 0) {
+ snap_id_it = m_src_snap_ids.upper_bound(m_src_snap_id_start);
}
for (; snap_id_it != m_src_snap_ids.end(); ++snap_id_it) {
return;
}
- // if the source snapshot isn't in our mapping table, create it
- if (m_snap_seqs.find(src_snap_id) == m_snap_seqs.end() &&
- boost::get<cls::rbd::UserSnapshotNamespace>(&snap_namespace) != nullptr) {
- break;
+ if (m_snap_seqs.find(src_snap_id) == m_snap_seqs.end()) {
+ // the source snapshot is not in our mapping table, ...
+ if (boost::get<cls::rbd::UserSnapshotNamespace>(&snap_namespace) !=
+ nullptr) {
+ // ... create it since it's a user snapshot
+ break;
+ } else if (src_snap_id == m_src_snap_id_end) {
+ // ... map it to destination HEAD since it's not a user snapshot that we
+ // will create (e.g. MirrorPrimarySnapshotNamespace)
+ m_snap_seqs[src_snap_id] = CEPH_NOSNAP;
+ }
}
}
if (r < 0) {
lderr(m_cct) << "failed to create snapshot '" << m_snap_name << "': "
- << cpp_strerror(r) << dendl;
+ << cpp_strerror(r) << dendl;
finish(r);
return;
}
SnapIdSet::iterator snap_id_it = m_src_snap_ids.begin();
if (m_prev_snap_id != CEPH_NOSNAP) {
snap_id_it = m_src_snap_ids.upper_bound(m_prev_snap_id);
+ } else if (m_src_snap_id_start > 0) {
+ snap_id_it = m_src_snap_ids.upper_bound(m_src_snap_id_start);
}
for (; snap_id_it != m_src_snap_ids.end(); ++snap_id_it) {
// if destination snapshot is not protected, protect it
auto snap_seq_it = m_snap_seqs.find(src_snap_id);
ceph_assert(snap_seq_it != m_snap_seqs.end());
+ if (snap_seq_it->second == CEPH_NOSNAP) {
+ // implies src end snapshot is mapped to a non-copyable snapshot
+ ceph_assert(src_snap_id == m_src_snap_id_end);
+ break;
+ }
m_dst_image_ctx->image_lock.lock_shared();
bool dst_protected;
template <typename I>
void SnapshotCopyRequest<I>::send_set_head() {
- if (m_snap_id_end != CEPH_NOSNAP) {
+ auto snap_seq_it = m_snap_seqs.find(m_src_snap_id_end);
+ if (m_src_snap_id_end != CEPH_NOSNAP &&
+ (snap_seq_it == m_snap_seqs.end() ||
+ snap_seq_it->second != CEPH_NOSNAP)) {
+ // not copying to src nor dst HEAD revision
finish(0);
return;
}
uint64_t parent_overlap = 0;
{
std::shared_lock src_locker{m_src_image_ctx->image_lock};
- size = m_src_image_ctx->size;
- if (!m_flatten) {
- parent_spec = m_src_image_ctx->parent_md.spec;
- parent_overlap = m_src_image_ctx->parent_md.overlap;
+ auto snap_info_it = m_src_image_ctx->snap_info.find(m_src_snap_id_end);
+ if (snap_info_it != m_src_image_ctx->snap_info.end()) {
+ auto& snap_info = snap_info_it->second;
+ size = snap_info.size;
+ if (!m_flatten) {
+ parent_spec = snap_info.parent.spec;
+ parent_overlap = snap_info.parent.overlap;
+ }
+ } else {
+ size = m_src_image_ctx->size;
+ if (!m_flatten) {
+ parent_spec = m_src_image_ctx->parent_md.spec;
+ parent_overlap = m_src_image_ctx->parent_md.overlap;
+ }
}
}
void SnapshotCopyRequest<I>::send_resize_object_map() {
int r = 0;
- if (m_snap_id_end == CEPH_NOSNAP &&
- m_dst_image_ctx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ if (m_dst_image_ctx->test_features(RBD_FEATURE_OBJECT_MAP)) {
std::shared_lock owner_locker{m_dst_image_ctx->owner_lock};
std::shared_lock image_locker{m_dst_image_ctx->image_lock};
public:
static SnapshotCopyRequest* create(ImageCtxT *src_image_ctx,
ImageCtxT *dst_image_ctx,
- librados::snap_t snap_id_end, bool flatten,
- ContextWQ *work_queue, SnapSeqs *snap_seqs,
- Context *on_finish) {
- return new SnapshotCopyRequest(src_image_ctx, dst_image_ctx, snap_id_end,
- flatten, work_queue, snap_seqs, on_finish);
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ bool flatten, ContextWQ *work_queue,
+ SnapSeqs *snap_seqs, Context *on_finish) {
+ return new SnapshotCopyRequest(src_image_ctx, dst_image_ctx,
+ src_snap_id_start, src_snap_id_end,
+ dst_snap_id_start, flatten, work_queue,
+ snap_seqs, on_finish);
}
SnapshotCopyRequest(ImageCtxT *src_image_ctx, ImageCtxT *dst_image_ctx,
- librados::snap_t snap_id_end, bool flatten,
- ContextWQ *work_queue, SnapSeqs *snap_seqs,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ bool flatten, ContextWQ *work_queue, SnapSeqs *snap_seqs,
Context *on_finish);
void send();
ImageCtxT *m_src_image_ctx;
ImageCtxT *m_dst_image_ctx;
- librados::snap_t m_snap_id_end;
+ librados::snap_t m_src_snap_id_start;
+ librados::snap_t m_src_snap_id_end;
+ librados::snap_t m_dst_snap_id_start;
bool m_flatten;
ContextWQ *m_work_queue;
SnapSeqs *m_snap_seqs_result;
void expect_get_snap_namespace(librbd::MockTestImageCtx &mock_image_ctx,
uint64_t snap_id) {
EXPECT_CALL(mock_image_ctx, get_snap_namespace(snap_id, _))
- .WillOnce(DoAll(SetArgPointee<1>(cls::rbd::UserSnapshotNamespace()),
- Return(0)));
+ .WillOnce(Invoke([&mock_image_ctx](uint64_t snap_id,
+ cls::rbd::SnapshotNamespace* snap_ns) {
+ auto it = mock_image_ctx.snap_info.find(snap_id);
+ *snap_ns = it->second.snap_namespace;
+ return 0;
+ }));
}
void expect_snap_create(librbd::MockTestImageCtx &mock_image_ctx,
MockSnapshotCopyRequest *create_request(
librbd::MockTestImageCtx &mock_src_image_ctx,
- librbd::MockTestImageCtx &mock_dst_image_ctx, Context *on_finish,
- librados::snap_t snap_id_end = CEPH_NOSNAP) {
+ librbd::MockTestImageCtx &mock_dst_image_ctx,
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ Context *on_finish) {
return new MockSnapshotCopyRequest(&mock_src_image_ctx, &mock_dst_image_ctx,
- snap_id_end, false, m_work_queue,
+ src_snap_id_start, src_snap_id_end,
+ dst_snap_id_start, false, m_work_queue,
&m_snap_seqs, on_finish);
}
- int create_snap(librbd::ImageCtx *image_ctx, const std::string &snap_name,
- bool protect = false) {
- int r = image_ctx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
- snap_name.c_str());
+ int create_snap(librbd::ImageCtx *image_ctx,
+ const cls::rbd::SnapshotNamespace& snap_ns,
+ const std::string &snap_name, bool protect) {
+ int r = image_ctx->operations->snap_create(snap_ns, snap_name.c_str());
if (r < 0) {
return r;
}
if (protect) {
- r = image_ctx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
- snap_name.c_str());
+ EXPECT_TRUE(boost::get<cls::rbd::UserSnapshotNamespace>(&snap_ns) !=
+ nullptr);
+ r = image_ctx->operations->snap_protect(snap_ns, snap_name.c_str());
if (r < 0) {
return r;
}
return 0;
}
+ int create_snap(librbd::ImageCtx *image_ctx, const std::string &snap_name,
+ bool protect = false) {
+ return create_snap(image_ctx, cls::rbd::UserSnapshotNamespace{}, snap_name,
+ protect);
+ }
+
void validate_snap_seqs(const librbd::SnapSeqs &snap_seqs) {
ASSERT_EQ(snap_seqs, m_snap_seqs);
}
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(0, ctx.wait());
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(0, ctx.wait());
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
expect_test_features(mock_dst_image_ctx);
InSequence seq;
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(0, ctx.wait());
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(0, ctx.wait());
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(-EBUSY, ctx.wait());
}
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
expect_test_features(mock_dst_image_ctx);
InSequence seq;
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(0, ctx.wait());
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(0, ctx.wait());
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(0, ctx.wait());
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
expect_test_features(mock_dst_image_ctx);
InSequence seq;
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx);
+ mock_dst_image_ctx, 0,
+ CEPH_NOSNAP, 0, &ctx);
request->send();
ASSERT_EQ(-EINVAL, ctx.wait());
}
C_SaferCond ctx;
MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
- mock_dst_image_ctx, &ctx,
- src_snap_id1);
+ mock_dst_image_ctx,0,
+ src_snap_id1, 0, &ctx);
request->send();
ASSERT_EQ(0, ctx.wait());
validate_snap_seqs({{src_snap_id1, 12}});
}
+TEST_F(TestMockDeepCopySnapshotCopyRequest, StartEndLimit) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", false));
+ ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap2", false));
+ ASSERT_EQ(0, create_snap(m_src_image_ctx,
+ {cls::rbd::MirrorPrimarySnapshotNamespace{
+ false, {{"peer uuid1"}}}},
+ "snap3", false));
+ auto src_snap_id1 = m_src_image_ctx->snaps[2];
+ auto src_snap_id2 = m_src_image_ctx->snaps[1];
+ auto src_snap_id3 = m_src_image_ctx->snaps[0];
+
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap0", true));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", false));
+ ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap3", false));
+ auto dst_snap_id1 = m_dst_image_ctx->snaps[1];
+ auto dst_snap_id3 = m_dst_image_ctx->snaps[0];
+
+ librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+ librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+ MockSnapshotCreateRequest mock_snapshot_create_request;
+
+ librbd::MockExclusiveLock mock_exclusive_lock;
+ prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+ expect_test_features(mock_dst_image_ctx);
+
+ InSequence seq;
+ expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id3,
+ true, 0);
+
+ expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id3);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_remove(mock_dst_image_ctx, "snap3", 0);
+
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id2);
+ expect_start_op(mock_exclusive_lock);
+ expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap2",
+ 12, 0);
+ expect_get_snap_namespace(mock_src_image_ctx, src_snap_id3);
+
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id2, false, 0);
+ expect_snap_is_protected(mock_src_image_ctx, src_snap_id3, false, 0);
+
+ MockSetHeadRequest mock_set_head_request;
+ expect_set_head(mock_set_head_request, 0);
+
+ C_SaferCond ctx;
+ MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+ mock_dst_image_ctx,
+ src_snap_id1,
+ src_snap_id3,
+ dst_snap_id1, &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+
+ validate_snap_seqs({{src_snap_id2, 12}, {src_snap_id3, CEPH_NOSNAP}});
+}
+
} // namespace deep_copy
} // namespace librbd
static SnapshotCopyRequest* create(librbd::MockTestImageCtx *src_image_ctx,
librbd::MockTestImageCtx *dst_image_ctx,
- librados::snap_t snap_id_end, bool flatten,
- ContextWQ *work_queue, SnapSeqs *snap_seqs,
- Context *on_finish) {
+ librados::snap_t src_snap_id_start,
+ librados::snap_t src_snap_id_end,
+ librados::snap_t dst_snap_id_start,
+ bool flatten, ContextWQ *work_queue,
+ SnapSeqs *snap_seqs, Context *on_finish) {
ceph_assert(s_instance != nullptr);
s_instance->on_finish = on_finish;
return s_instance;