mirror of
https://github.com/ceph/ceph
synced 2025-01-10 21:20:46 +00:00
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:
parent
4456dc3939
commit
5b9d85c136
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user