librbd: the first post-migration snapshot isn't always dirty

Currently, the first post-migration snapshot is always marked EXISTS
(i.e. dirty).  This is wrong, because the data can be inherited from
a pre-migration snapshot, handled by deep copy.

Mark all post-migration snapshots EXISTS_CLEAN in this case.

Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
Ilya Dryomov 2019-04-25 16:43:48 +02:00
parent 4456dc3939
commit 5b9d85c136
3 changed files with 85 additions and 6 deletions

View File

@ -38,10 +38,12 @@ public:
C_UpdateObjectMap(AsyncObjectThrottle<I> &throttle, I *image_ctx,
uint64_t object_no, uint8_t head_object_map_state,
const std::vector<uint64_t> *snap_ids,
const ZTracer::Trace &trace, size_t snap_id_idx)
bool first_snap_is_clean, const ZTracer::Trace &trace,
size_t snap_id_idx)
: C_AsyncObjectThrottle<I>(throttle, *image_ctx), m_object_no(object_no),
m_head_object_map_state(head_object_map_state), m_snap_ids(*snap_ids),
m_trace(trace), m_snap_id_idx(snap_id_idx)
m_first_snap_is_clean(first_snap_is_clean), m_trace(trace),
m_snap_id_idx(snap_id_idx)
{
}
@ -79,7 +81,7 @@ public:
auto& image_ctx = this->m_image_ctx;
uint8_t state = OBJECT_EXISTS;
if (image_ctx.test_features(RBD_FEATURE_FAST_DIFF, image_ctx.snap_lock) &&
m_snap_id_idx > 0) {
(m_snap_id_idx > 0 || m_first_snap_is_clean)) {
// first snapshot should be exists+dirty since it contains
// the copyup data -- later snapshots inherit the data.
state = OBJECT_EXISTS_CLEAN;
@ -96,6 +98,7 @@ private:
uint64_t m_object_no;
uint8_t m_head_object_map_state;
const std::vector<uint64_t> &m_snap_ids;
bool m_first_snap_is_clean;
const ZTracer::Trace &m_trace;
size_t m_snap_id_idx;
};
@ -335,7 +338,7 @@ void CopyupRequest<I>::update_object_maps() {
typename AsyncObjectThrottle<I>::ContextFactory context_factory(
boost::lambda::bind(boost::lambda::new_ptr<C_UpdateObjectMap<I>>(),
boost::lambda::_1, m_image_ctx, m_object_no, head_object_map_state,
&m_snap_ids, m_trace, boost::lambda::_2));
&m_snap_ids, m_first_snap_is_clean, m_trace, boost::lambda::_2));
auto ctx = util::create_context_callback<
CopyupRequest<I>, &CopyupRequest<I>::handle_update_object_maps>(this);
auto throttle = new AsyncObjectThrottle<I>(
@ -591,6 +594,7 @@ void CopyupRequest<I>::compute_deep_copy_snap_ids() {
std::back_inserter(m_snap_ids),
[this, cct=m_image_ctx->cct, &deep_copied](uint64_t snap_id) {
if (deep_copied.count(snap_id)) {
m_first_snap_is_clean = true;
return false;
}

View File

@ -93,6 +93,7 @@ private:
AsyncOperation m_async_op;
std::vector<uint64_t> m_snap_ids;
bool m_first_snap_is_clean = false;
Mutex m_lock;
WriteRequests m_pending_requests;

View File

@ -583,7 +583,7 @@ TEST_F(TestMockIoCopyupRequest, DeepCopyOnRead) {
ictx->flush_async_operations();
}
TEST_F(TestMockIoCopyupRequest, DeepCopyWithSnaps) {
TEST_F(TestMockIoCopyupRequest, DeepCopyWithPostSnaps) {
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
librbd::ImageCtx *ictx;
@ -616,6 +616,77 @@ TEST_F(TestMockIoCopyupRequest, DeepCopyWithSnaps) {
InSequence seq;
MockAbstractObjectWriteRequest mock_write_request;
MockObjectCopyRequest mock_object_copy_request;
mock_image_ctx.migration_info = {1, "", "", "image id",
{{CEPH_NOSNAP, {2, 1}}},
ictx->size, true};
expect_is_empty_write_op(mock_write_request, false);
expect_object_copy(mock_image_ctx, mock_object_copy_request, true, 0);
expect_is_empty_write_op(mock_write_request, false);
expect_get_parent_overlap(mock_image_ctx, 1, 0, 0);
expect_get_parent_overlap(mock_image_ctx, 2, 1, 0);
expect_prune_parent_extents(mock_image_ctx, 1, 1);
expect_get_parent_overlap(mock_image_ctx, 3, 1, 0);
expect_prune_parent_extents(mock_image_ctx, 1, 1);
expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
OBJECT_EXISTS);
expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
expect_object_map_update(mock_image_ctx, 2, 0, OBJECT_EXISTS, true, 0);
expect_object_map_update(mock_image_ctx, 3, 0, OBJECT_EXISTS_CLEAN, true, 0);
expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
0);
expect_add_copyup_ops(mock_write_request);
expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", "", 0);
expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
{{0, 4096}}, {});
mock_image_ctx.copyup_list[0] = req;
req->append_request(&mock_write_request);
req->send();
ASSERT_EQ(0, mock_write_request.ctx.wait());
}
TEST_F(TestMockIoCopyupRequest, DeepCopyWithPreAndPostSnaps) {
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ictx->snap_lock.get_write();
ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "4", 4, ictx->size,
ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
0, {});
ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "3", 3, ictx->size,
ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
0, {});
ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "2", 2, ictx->size,
ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
0, {});
ictx->add_snap(cls::rbd::UserSnapshotNamespace(), "1", 1, ictx->size,
ictx->parent_md, RBD_PROTECTION_STATUS_UNPROTECTED,
0, {});
ictx->snapc = {4, {4, 3, 2, 1}};
ictx->snap_lock.put_write();
MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
MockExclusiveLock mock_exclusive_lock;
MockJournal mock_journal;
MockObjectMap mock_object_map;
initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
mock_object_map);
expect_test_features(mock_image_ctx);
expect_op_work_queue(mock_image_ctx);
expect_is_lock_owner(mock_image_ctx);
InSequence seq;
MockAbstractObjectWriteRequest mock_write_request;
MockObjectCopyRequest mock_object_copy_request;
mock_image_ctx.migration_info = {1, "", "", "image id",
@ -628,10 +699,13 @@ TEST_F(TestMockIoCopyupRequest, DeepCopyWithSnaps) {
expect_get_parent_overlap(mock_image_ctx, 2, 0, 0);
expect_get_parent_overlap(mock_image_ctx, 3, 1, 0);
expect_prune_parent_extents(mock_image_ctx, 1, 1);
expect_get_parent_overlap(mock_image_ctx, 4, 1, 0);
expect_prune_parent_extents(mock_image_ctx, 1, 1);
expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
OBJECT_EXISTS);
expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
expect_object_map_update(mock_image_ctx, 3, 0, OBJECT_EXISTS, true, 0);
expect_object_map_update(mock_image_ctx, 3, 0, OBJECT_EXISTS_CLEAN, true, 0);
expect_object_map_update(mock_image_ctx, 4, 0, OBJECT_EXISTS_CLEAN, true, 0);
expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
0);