]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd: fix split() for SparseExtent and SparseBufferlistExtent
authorIlya Dryomov <idryomov@gmail.com>
Mon, 12 Feb 2024 10:00:45 +0000 (11:00 +0100)
committerIlya Dryomov <idryomov@gmail.com>
Wed, 14 Feb 2024 13:15:04 +0000 (14:15 +0100)
SparseExtents and SparseBufferlist are typedefs for interval_map.  In
both cases, split() handler is broken: for the former the extent isn't
actually split and for the latter incorrect bufferlist is attached to
the split extent.

Fortunately, both SnapshotDelta as produced by ObjectListSnapsRequest
and SparseBufferlist used in a couple of places seem to be collections
where only disjoint intervals are inserted and splitting doesn't occur
(at least in the common case).  But still, this is a landmine waiting
for someone to step on it.

Fixes: https://tracker.ceph.com/issues/64423
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
src/librbd/io/Types.h
src/test/librbd/io/test_mock_ObjectRequest.cc

index ad032a85334584f47fa86bfcfb3836122f6bb55f..c720a91e98da1eed2b04236b078d0be60c3dae13 100644 (file)
@@ -179,8 +179,9 @@ struct SparseExtent {
 std::ostream& operator<<(std::ostream& os, const SparseExtent& state);
 
 struct SparseExtentSplitMerge {
-  SparseExtent split(uint64_t offset, uint64_t length, SparseExtent &se) const {
-    return SparseExtent(se.state, se.length);
+  SparseExtent split(uint64_t offset, uint64_t length,
+                     const SparseExtent& se) const {
+    return SparseExtent(se.state, length);
   }
 
   bool can_merge(const SparseExtent& left, const SparseExtent& right) const {
@@ -231,10 +232,10 @@ struct SparseBufferlistExtent : public SparseExtent {
 
 struct SparseBufferlistExtentSplitMerge {
   SparseBufferlistExtent split(uint64_t offset, uint64_t length,
-                               SparseBufferlistExtent& sbe) const {
+                               const SparseBufferlistExtent& sbe) const {
     ceph::bufferlist bl;
     if (sbe.state == SPARSE_EXTENT_STATE_DATA) {
-      bl.substr_of(bl, offset, length);
+      bl.substr_of(sbe.bl, offset, length);
     }
     return SparseBufferlistExtent(sbe.state, length, std::move(bl));
   }
index d7f9156d7eda164e03bd9381813430685e68c352..5ea3aed923a8e5c2e75e97e040ec371cff8fcf3f 100644 (file)
@@ -2070,6 +2070,97 @@ TEST_F(TestMockIoObjectRequest, ListSnapsNoSnapsInSnapSet) {
   EXPECT_EQ(expected_snapshot_delta, snapshot_delta);
 }
 
+TEST(SparseExtents, Split) {
+  SparseExtents extents;
+  extents.insert(50, 100, {SPARSE_EXTENT_STATE_DATA, 100});
+  extents.erase(80, 30);
+  extents.insert(45, 10, {SPARSE_EXTENT_STATE_ZEROED, 10});
+  extents.insert(140, 20, {SPARSE_EXTENT_STATE_DNE, 20});
+  extents.insert(125, 5, {SPARSE_EXTENT_STATE_ZEROED, 5});
+
+  SparseExtents expected_extents = {
+    {45, {10, {SPARSE_EXTENT_STATE_ZEROED, 10}}},
+    {55, {25, {SPARSE_EXTENT_STATE_DATA, 25}}},
+    {110, {15, {SPARSE_EXTENT_STATE_DATA, 15}}},
+    {125, {5, {SPARSE_EXTENT_STATE_ZEROED, 5}}},
+    {130, {10, {SPARSE_EXTENT_STATE_DATA, 10}}},
+    {140, {20, {SPARSE_EXTENT_STATE_DNE, 20}}}
+  };
+  EXPECT_EQ(expected_extents, extents);
+}
+
+TEST(SparseBufferlist, Split) {
+  bufferlist bl;
+  bl.append(std::string(5, '1'));
+  bl.append(std::string(25, '2'));
+  bl.append(std::string(30, '3'));
+  bl.append(std::string(15, '4'));
+  bl.append(std::string(5, '5'));
+  bl.append(std::string(10, '6'));
+  bl.append(std::string(10, '7'));
+  bufferlist expected_bl1;
+  expected_bl1.append(std::string(25, '2'));
+  bufferlist expected_bl2;
+  expected_bl2.append(std::string(15, '4'));
+  bufferlist expected_bl3;
+  expected_bl3.append(std::string(10, '6'));
+
+  SparseBufferlist extents;
+  extents.insert(50, 100, {SPARSE_EXTENT_STATE_DATA, 100, std::move(bl)});
+  extents.erase(80, 30);
+  extents.insert(45, 10, {SPARSE_EXTENT_STATE_ZEROED, 10});
+  extents.insert(140, 20, {SPARSE_EXTENT_STATE_DNE, 20});
+  extents.insert(125, 5, {SPARSE_EXTENT_STATE_ZEROED, 5});
+
+  SparseBufferlist expected_extents = {
+    {45, {10, {SPARSE_EXTENT_STATE_ZEROED, 10}}},
+    {55, {25, {SPARSE_EXTENT_STATE_DATA, 25, std::move(expected_bl1)}}},
+    {110, {15, {SPARSE_EXTENT_STATE_DATA, 15, std::move(expected_bl2)}}},
+    {125, {5, {SPARSE_EXTENT_STATE_ZEROED, 5}}},
+    {130, {10, {SPARSE_EXTENT_STATE_DATA, 10, std::move(expected_bl3)}}},
+    {140, {20, {SPARSE_EXTENT_STATE_DNE, 20}}}
+  };
+  EXPECT_EQ(expected_extents, extents);
+}
+
+TEST(SparseBufferlist, SplitData) {
+  bufferlist bl1;
+  bl1.append(std::string(100, '1'));
+  bufferlist bl2;
+  bl2.append(std::string(15, '2'));
+  bufferlist bl3;
+  bl3.append(std::string(40, '3'));
+  bufferlist bl4;
+  bl4.append(std::string(10, '4'));
+  bufferlist expected_bl1 = bl2;
+  bufferlist expected_bl2;
+  expected_bl2.append(std::string(35, '1'));
+  bufferlist expected_bl3 = bl4;
+  bufferlist expected_bl4;
+  expected_bl4.append(std::string(30, '1'));
+  bufferlist expected_bl5;
+  expected_bl5.append(std::string(5, '3'));
+  bufferlist expected_bl6;
+  expected_bl6.append(std::string(15, '3'));
+
+  SparseBufferlist extents;
+  extents.insert(50, 100, {SPARSE_EXTENT_STATE_DATA, 100, std::move(bl1)});
+  extents.insert(40, 15, {SPARSE_EXTENT_STATE_DATA, 15, std::move(bl2)});
+  extents.insert(130, 40, {SPARSE_EXTENT_STATE_DATA, 40, std::move(bl3)});
+  extents.erase(135, 20);
+  extents.insert(90, 10, {SPARSE_EXTENT_STATE_DATA, 10, std::move(bl4)});
+
+  SparseBufferlist expected_extents = {
+    {40, {15, {SPARSE_EXTENT_STATE_DATA, 15, std::move(expected_bl1)}}},
+    {55, {35, {SPARSE_EXTENT_STATE_DATA, 35, std::move(expected_bl2)}}},
+    {90, {10, {SPARSE_EXTENT_STATE_DATA, 10, std::move(expected_bl3)}}},
+    {100, {30, {SPARSE_EXTENT_STATE_DATA, 30, std::move(expected_bl4)}}},
+    {130, {5, {SPARSE_EXTENT_STATE_DATA, 5, std::move(expected_bl5)}}},
+    {155, {15, {SPARSE_EXTENT_STATE_DATA, 15, std::move(expected_bl6)}}}
+  };
+  EXPECT_EQ(expected_extents, extents);
+}
+
 } // namespace io
 } // namespace librbd