From: Shai Fultheim Date: Thu, 11 Jun 2026 18:56:06 +0000 (+0300) Subject: test/crimson/seastore: assert omap extent retirement on split/merge/balance X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=d3cada2c858b3190bb4c2f64e55a1a42bf455ad2;p=ceph.git test/crimson/seastore: assert omap extent retirement on split/merge/balance 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 --- diff --git a/src/test/crimson/seastore/test_omap_manager.cc b/src/test/crimson/seastore/test_omap_manager.cc index 2504771d1a6..2a0fb452d79 100644 --- a/src/test/crimson/seastore/test_omap_manager.cc +++ b/src/test/crimson/seastore/test_omap_manager.cc @@ -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); }); }