]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
test/crimson/seastore: assert omap extent retirement on split/merge/balance 69482/head
authorShai Fultheim <shai.fultheim@gmail.com>
Thu, 11 Jun 2026 18:56:06 +0000 (21:56 +0300)
committerShai Fultheim <shai.fultheim@gmail.com>
Thu, 18 Jun 2026 10:55:49 +0000 (13:55 +0300)
There was no coverage for extent leaks or double-frees during structural
omap B-tree transitions. While investigating a suspected extent leak, these
assertions were added and confirmed the caller-layer (parent-node) already
retires nodes correctly — no leak and no double-free.

Add count_live_omap_extents() to omap_manager_test_t, which scans the LBA
mapping space via scan_mapped_space() to count live OMAP_INNER/OMAP_LEAF
extents, and assert against it in leafnode/innernode_split_merge_balancing:
the live count returns to baseline after contraction, and is exactly 0 after
clearing the tree. This pins the caller-layer (parent-node) retirement
contract in CI.

Signed-off-by: Shai Fultheim <shai.fultheim@gmail.com>
src/test/crimson/seastore/test_omap_manager.cc

index 2504771d1a65c7e5a5cda916c8fd4b3c923bf2e1..2a0fb452d79b7603f9e19cd8d36be590d7999122 100644 (file)
@@ -195,6 +195,22 @@ struct omap_manager_test_t :
     return keys;
   }
 
+  unsigned count_live_omap_extents() {
+    auto t = create_read_transaction();
+    unsigned count = 0;
+    with_trans_intr(*t, [this, &count](auto &t) {
+      return tm->get_lba_manager()->scan_mapped_space(
+        t,
+        [&count](paddr_t paddr, extent_len_t len, extent_types_t type, laddr_t laddr) {
+          if (type == extent_types_t::OMAP_INNER ||
+              type == extent_types_t::OMAP_LEAF) {
+            count++;
+          }
+        });
+    }).unsafe_get();
+    return count;
+  }
+
   void list(
     const omap_root_t &omap_root,
     Transaction &t,
@@ -529,6 +545,17 @@ TEST_P(omap_manager_test_t, leafnode_split_merge_balancing)
       submit_transaction(std::move(t));
       check_mappings(omap_root);
     }
+    auto final_count = count_live_omap_extents();
+    logger().debug("leafnode_split_merge_balancing final live omap extents: {}",
+                   final_count);
+    // After contracting back to depth 1, the tree consists of exactly the root leaf node.
+    EXPECT_EQ(final_count, 1U);
+
+    logger().debug("== clearing entire tree and verifying no leaks");
+    auto t_clear = create_mutate_transaction();
+    clear(omap_root, *t_clear);
+    submit_transaction(std::move(t_clear));
+    EXPECT_EQ(count_live_omap_extents(), 0U);
   });
 }
 
@@ -581,6 +608,18 @@ TEST_P(omap_manager_test_t, innernode_split_merge_balancing)
       submit_transaction(std::move(t));
     }
     check_mappings(omap_root);
+    auto final_count = count_live_omap_extents();
+    logger().debug("innernode_split_merge_balancing final live omap extents: {}",
+                   final_count);
+    // Bound derivation: 64 KiB OMAP_LEAF_BLOCK_SIZE / ~580 B entry size (50 B key + 512 B value
+    // + overhead) => healthy post-merge leaves hold >=55 entries, so count is <= size / 55 + C.
+    EXPECT_LE(final_count, test_omap_mappings.size() / 40 + 20U);
+
+    logger().debug("== clearing entire tree and verifying no leaks");
+    auto t_clear = create_mutate_transaction();
+    clear(omap_root, *t_clear);
+    submit_transaction(std::move(t_clear));
+    EXPECT_EQ(count_live_omap_extents(), 0U);
   });
 }
 
@@ -603,6 +642,7 @@ TEST_P(omap_manager_test_t, clear)
     auto t = create_mutate_transaction();
     clear(omap_root, *t);
     submit_transaction(std::move(t));
+    EXPECT_EQ(count_live_omap_extents(), 0U);
   });
 }