From 07e6f3c47b2d85014f463ee3911aedd3b5dc89a5 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Wed, 28 Feb 2024 14:20:16 +0100 Subject: [PATCH] librbd: don't clip expanded diff on truncate in ObjectListSnapsRequest If the diff was expanded due to LIST_SNAPS_FLAG_WHOLE_OBJECT, clipping it when handling a truncate is wrong -- when subtracting that interval, we either split the expanded extent into two or chop off a piece of it. However the point of LIST_SNAPS_FLAG_WHOLE_OBJECT is to report a single extent covering the entire object. Signed-off-by: Ilya Dryomov (cherry picked from commit 0ebca5fe22d976f1a17f8078f20a47c49a057edb) --- src/librbd/io/ObjectRequest.cc | 3 +- src/test/librbd/io/test_mock_ObjectRequest.cc | 123 ++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/librbd/io/ObjectRequest.cc b/src/librbd/io/ObjectRequest.cc index 428f0fbdf5844..a7748623037e2 100644 --- a/src/librbd/io/ObjectRequest.cc +++ b/src/librbd/io/ObjectRequest.cc @@ -865,7 +865,8 @@ void ObjectListSnapsRequest::handle_list_snaps(int r) { // clip diff to size of object (in case it was truncated) interval_set zero_interval; - if (end_size < prev_end_size) { + if (end_size < prev_end_size && + (m_list_snaps_flags & LIST_SNAPS_FLAG_WHOLE_OBJECT) == 0) { zero_interval.insert(end_size, prev_end_size - end_size); zero_interval.intersection_of(object_interval); diff --git a/src/test/librbd/io/test_mock_ObjectRequest.cc b/src/test/librbd/io/test_mock_ObjectRequest.cc index cd553d2a509c4..55c1e096ab7bc 100644 --- a/src/test/librbd/io/test_mock_ObjectRequest.cc +++ b/src/test/librbd/io/test_mock_ObjectRequest.cc @@ -2410,6 +2410,129 @@ TEST_F(TestMockIoObjectRequest, ListSnapsWholeObject) { } } +TEST_F(TestMockIoObjectRequest, ListSnapsWholeObjectTruncate) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + mock_image_ctx.snaps = {3}; + + InSequence seq; + + librados::snap_set_t snap_set; + snap_set.seq = 3; + librados::clone_info_t clone_info; + + clone_info.cloneid = 3; + clone_info.snaps = {3}; + clone_info.overlap = std::vector>{ + {2, mock_image_ctx.layout.object_size - 4}}; + clone_info.size = mock_image_ctx.layout.object_size; + snap_set.clones.push_back(clone_info); + + clone_info.cloneid = CEPH_NOSNAP; + clone_info.snaps = {}; + clone_info.overlap = {}; + clone_info.size = mock_image_ctx.layout.object_size - 2; + snap_set.clones.push_back(clone_info); + + expect_list_snaps(mock_image_ctx, snap_set, 0); + + { + SnapshotDelta snapshot_delta; + C_SaferCond ctx; + auto req = MockObjectListSnapsRequest::create( + &mock_image_ctx, 0, {{1, mock_image_ctx.layout.object_size - 2}}, + {3, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + SnapshotDelta expected_snapshot_delta; + expected_snapshot_delta[{CEPH_NOSNAP,CEPH_NOSNAP}].insert( + 1, 1, {SPARSE_EXTENT_STATE_DATA, 1}); + expected_snapshot_delta[{CEPH_NOSNAP,CEPH_NOSNAP}].insert( + mock_image_ctx.layout.object_size - 2, 1, + {SPARSE_EXTENT_STATE_ZEROED, 1}); + EXPECT_EQ(expected_snapshot_delta, snapshot_delta); + } + + expect_list_snaps(mock_image_ctx, snap_set, 0); + + { + SnapshotDelta snapshot_delta; + C_SaferCond ctx; + auto req = MockObjectListSnapsRequest::create( + &mock_image_ctx, 0, {{1, mock_image_ctx.layout.object_size - 2}}, + {3, CEPH_NOSNAP}, LIST_SNAPS_FLAG_WHOLE_OBJECT, {}, &snapshot_delta, + &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + SnapshotDelta expected_snapshot_delta; + expected_snapshot_delta[{CEPH_NOSNAP,CEPH_NOSNAP}].insert( + 1, mock_image_ctx.layout.object_size - 2, + {SPARSE_EXTENT_STATE_DATA, mock_image_ctx.layout.object_size - 2}); + EXPECT_EQ(expected_snapshot_delta, snapshot_delta); + } +} + +TEST_F(TestMockIoObjectRequest, ListSnapsWholeObjectRemove) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + mock_image_ctx.snaps = {3}; + + InSequence seq; + + librados::snap_set_t snap_set; + snap_set.seq = 3; + librados::clone_info_t clone_info; + + clone_info.cloneid = 3; + clone_info.snaps = {3}; + clone_info.overlap = {}; + clone_info.size = mock_image_ctx.layout.object_size - 2; + snap_set.clones.push_back(clone_info); + + expect_list_snaps(mock_image_ctx, snap_set, 0); + + { + SnapshotDelta snapshot_delta; + C_SaferCond ctx; + auto req = MockObjectListSnapsRequest::create( + &mock_image_ctx, 0, {{1, mock_image_ctx.layout.object_size - 2}}, + {3, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + SnapshotDelta expected_snapshot_delta; + expected_snapshot_delta[{CEPH_NOSNAP,CEPH_NOSNAP}].insert( + 1, mock_image_ctx.layout.object_size - 3, + {SPARSE_EXTENT_STATE_ZEROED, mock_image_ctx.layout.object_size - 3}); + EXPECT_EQ(expected_snapshot_delta, snapshot_delta); + } + + expect_list_snaps(mock_image_ctx, snap_set, 0); + + { + SnapshotDelta snapshot_delta; + C_SaferCond ctx; + auto req = MockObjectListSnapsRequest::create( + &mock_image_ctx, 0, {{1, mock_image_ctx.layout.object_size - 2}}, + {3, CEPH_NOSNAP}, LIST_SNAPS_FLAG_WHOLE_OBJECT, {}, &snapshot_delta, + &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + SnapshotDelta expected_snapshot_delta; + expected_snapshot_delta[{CEPH_NOSNAP,CEPH_NOSNAP}].insert( + 1, mock_image_ctx.layout.object_size - 2, + {SPARSE_EXTENT_STATE_ZEROED, mock_image_ctx.layout.object_size - 2}); + EXPECT_EQ(expected_snapshot_delta, snapshot_delta); + } +} + TEST_F(TestMockIoObjectRequest, ListSnapsWholeObjectEndSize) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); -- 2.39.5