From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- .../crimson/seastore/test_transaction_manager.cc | 495 +++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 src/test/crimson/seastore/test_transaction_manager.cc (limited to 'src/test/crimson/seastore/test_transaction_manager.cc') diff --git a/src/test/crimson/seastore/test_transaction_manager.cc b/src/test/crimson/seastore/test_transaction_manager.cc new file mode 100644 index 000000000..9906f938a --- /dev/null +++ b/src/test/crimson/seastore/test_transaction_manager.cc @@ -0,0 +1,495 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include + +#include "test/crimson/gtest_seastar.h" +#include "test/crimson/seastore/transaction_manager_test_state.h" + +#include "crimson/os/seastore/segment_cleaner.h" +#include "crimson/os/seastore/cache.h" +#include "crimson/os/seastore/transaction_manager.h" +#include "crimson/os/seastore/segment_manager/ephemeral.h" +#include "crimson/os/seastore/segment_manager.h" + +#include "test/crimson/seastore/test_block.h" + +using namespace crimson; +using namespace crimson::os; +using namespace crimson::os::seastore; + +namespace { + [[maybe_unused]] seastar::logger& logger() { + return crimson::get_logger(ceph_subsys_test); + } +} + +struct test_extent_record_t { + test_extent_desc_t desc; + unsigned refcount = 0; + test_extent_record_t() = default; + test_extent_record_t( + const test_extent_desc_t &desc, + unsigned refcount) : desc(desc), refcount(refcount) {} + + void update(const test_extent_desc_t &to) { + desc = to; + } + + bool operator==(const test_extent_desc_t &rhs) const { + return desc == rhs; + } + bool operator!=(const test_extent_desc_t &rhs) const { + return desc != rhs; + } +}; + +std::ostream &operator<<(std::ostream &lhs, const test_extent_record_t &rhs) { + return lhs << "test_extent_record_t(" << rhs.desc + << ", refcount=" << rhs.refcount << ")"; +} + +struct transaction_manager_test_t : + public seastar_test_suite_t, + TMTestState { + + std::random_device rd; + std::mt19937 gen; + + transaction_manager_test_t() + : gen(rd()) { + init(); + } + + laddr_t get_random_laddr(size_t block_size, laddr_t limit) { + return block_size * + std::uniform_int_distribution<>(0, (limit / block_size) - 1)(gen); + } + + char get_random_contents() { + return static_cast(std::uniform_int_distribution<>(0, 255)(gen)); + } + + seastar::future<> set_up_fut() final { + return tm_setup(); + } + + seastar::future<> tear_down_fut() final { + return tm_teardown(); + } + + struct test_extents_t : std::map { + private: + void check_available(laddr_t addr, extent_len_t len) { + auto iter = upper_bound(addr); + if (iter != begin()) { + auto liter = iter; + liter--; + EXPECT_FALSE(liter->first + liter->second.desc.len > addr); + } + if (iter != end()) { + EXPECT_FALSE(iter->first < addr + len); + } + } + void check_hint(laddr_t hint, laddr_t addr, extent_len_t len) { + auto iter = lower_bound(hint); + laddr_t last = hint; + while (true) { + if (iter == end() || iter->first > addr) { + EXPECT_EQ(addr, last); + break; + } + EXPECT_FALSE(iter->first - last > len); + last = iter->first + iter->second.desc.len; + ++iter; + } + } + public: + void insert(TestBlock &extent) { + check_available(extent.get_laddr(), extent.get_length()); + emplace( + std::make_pair( + extent.get_laddr(), + test_extent_record_t{extent.get_desc(), 1} + )); + } + void alloced(laddr_t hint, TestBlock &extent) { + check_hint(hint, extent.get_laddr(), extent.get_length()); + insert(extent); + } + } test_mappings; + + struct test_transaction_t { + TransactionRef t; + test_extents_t mappings; + }; + + test_transaction_t create_transaction() { + return { tm->create_transaction(), test_mappings }; + } + + test_transaction_t create_weak_transaction() { + return { tm->create_weak_transaction(), test_mappings }; + } + + TestBlockRef alloc_extent( + test_transaction_t &t, + laddr_t hint, + extent_len_t len, + char contents) { + auto extent = tm->alloc_extent( + *(t.t), + hint, + len).unsafe_get0(); + extent->set_contents(contents); + EXPECT_FALSE(t.mappings.count(extent->get_laddr())); + EXPECT_EQ(len, extent->get_length()); + t.mappings.alloced(hint, *extent); + return extent; + } + + TestBlockRef alloc_extent( + test_transaction_t &t, + laddr_t hint, + extent_len_t len) { + return alloc_extent( + t, + hint, + len, + get_random_contents()); + } + + bool check_usage() { + auto t = create_weak_transaction(); + SpaceTrackerIRef tracker(segment_cleaner->get_empty_space_tracker()); + lba_manager->scan_mapped_space( + *t.t, + [&tracker](auto offset, auto len) { + tracker->allocate( + offset.segment, + offset.offset, + len); + }).unsafe_get0(); + return segment_cleaner->debug_check_space(*tracker); + } + + void replay() { + logger().debug("{}: begin", __func__); + EXPECT_TRUE(check_usage()); + restart(); + logger().debug("{}: end", __func__); + } + + void check() { + check_mappings(); + check_usage(); + } + + void check_mappings() { + auto t = create_weak_transaction(); + check_mappings(t); + } + + TestBlockRef get_extent( + test_transaction_t &t, + laddr_t addr, + extent_len_t len) { + ceph_assert(t.mappings.count(addr)); + ceph_assert(t.mappings[addr].desc.len == len); + + auto ret_list = tm->read_extents( + *t.t, addr, len + ).unsafe_get0(); + EXPECT_EQ(ret_list.size(), 1); + auto &ext = ret_list.begin()->second; + auto &laddr = ret_list.begin()->first; + EXPECT_EQ(addr, laddr); + EXPECT_EQ(addr, ext->get_laddr()); + return ext; + } + + test_block_mutator_t mutator; + TestBlockRef mutate_extent( + test_transaction_t &t, + TestBlockRef ref) { + ceph_assert(t.mappings.count(ref->get_laddr())); + ceph_assert(t.mappings[ref->get_laddr()].desc.len == ref->get_length()); + auto ext = tm->get_mutable_extent(*t.t, ref)->cast(); + EXPECT_EQ(ext->get_laddr(), ref->get_laddr()); + EXPECT_EQ(ext->get_desc(), ref->get_desc()); + mutator.mutate(*ext, gen); + t.mappings[ext->get_laddr()].update(ext->get_desc()); + return ext; + } + + void inc_ref(test_transaction_t &t, laddr_t offset) { + ceph_assert(t.mappings.count(offset)); + ceph_assert(t.mappings[offset].refcount > 0); + auto refcnt = tm->inc_ref(*t.t, offset).unsafe_get0(); + t.mappings[offset].refcount++; + EXPECT_EQ(refcnt, t.mappings[offset].refcount); + } + + void dec_ref(test_transaction_t &t, laddr_t offset) { + ceph_assert(t.mappings.count(offset)); + ceph_assert(t.mappings[offset].refcount > 0); + auto refcnt = tm->dec_ref(*t.t, offset).unsafe_get0(); + t.mappings[offset].refcount--; + EXPECT_EQ(refcnt, t.mappings[offset].refcount); + if (t.mappings[offset].refcount == 0) { + t.mappings.erase(offset); + } + } + + void check_mappings(test_transaction_t &t) { + for (auto &i: t.mappings) { + logger().debug("check_mappings: {}->{}", i.first, i.second); + auto ext = get_extent(t, i.first, i.second.desc.len); + EXPECT_EQ(i.second, ext->get_desc()); + } + auto lt = create_weak_transaction(); + lba_manager->scan_mappings( + *lt.t, + 0, + L_ADDR_MAX, + [iter=lt.mappings.begin(), <](auto l, auto p, auto len) mutable { + EXPECT_NE(iter, lt.mappings.end()); + EXPECT_EQ(l, iter->first); + ++iter; + }).unsafe_get0(); + } + + void submit_transaction(test_transaction_t t) { + tm->submit_transaction(std::move(t.t)).unsafe_get(); + test_mappings = t.mappings; + } +}; + +TEST_F(transaction_manager_test_t, basic) +{ + constexpr laddr_t SIZE = 4096; + run_async([this] { + constexpr laddr_t ADDR = 0xFF * SIZE; + { + auto t = create_transaction(); + auto extent = alloc_extent( + t, + ADDR, + SIZE, + 'a'); + ASSERT_EQ(ADDR, extent->get_laddr()); + check_mappings(t); + check(); + submit_transaction(std::move(t)); + check(); + } + }); +} + +TEST_F(transaction_manager_test_t, mutate) +{ + constexpr laddr_t SIZE = 4096; + run_async([this] { + constexpr laddr_t ADDR = 0xFF * SIZE; + { + auto t = create_transaction(); + auto extent = alloc_extent( + t, + ADDR, + SIZE, + 'a'); + ASSERT_EQ(ADDR, extent->get_laddr()); + check_mappings(t); + check(); + submit_transaction(std::move(t)); + check(); + } + ASSERT_TRUE(check_usage()); + replay(); + { + auto t = create_transaction(); + auto ext = get_extent( + t, + ADDR, + SIZE); + auto mut = mutate_extent(t, ext); + check_mappings(t); + check(); + submit_transaction(std::move(t)); + check(); + } + ASSERT_TRUE(check_usage()); + replay(); + check(); + }); +} + +TEST_F(transaction_manager_test_t, create_remove_same_transaction) +{ + constexpr laddr_t SIZE = 4096; + run_async([this] { + constexpr laddr_t ADDR = 0xFF * SIZE; + { + auto t = create_transaction(); + auto extent = alloc_extent( + t, + ADDR, + SIZE, + 'a'); + ASSERT_EQ(ADDR, extent->get_laddr()); + check_mappings(t); + dec_ref(t, ADDR); + check_mappings(t); + + extent = alloc_extent( + t, + ADDR, + SIZE, + 'a'); + + submit_transaction(std::move(t)); + check(); + } + replay(); + check(); + }); +} + +TEST_F(transaction_manager_test_t, split_merge_read_same_transaction) +{ + constexpr laddr_t SIZE = 4096; + run_async([this] { + { + auto t = create_transaction(); + for (unsigned i = 0; i < 300; ++i) { + auto extent = alloc_extent( + t, + laddr_t(i * SIZE), + SIZE); + } + check_mappings(t); + submit_transaction(std::move(t)); + check(); + } + { + auto t = create_transaction(); + for (unsigned i = 0; i < 240; ++i) { + dec_ref( + t, + laddr_t(i * SIZE)); + } + check_mappings(t); + submit_transaction(std::move(t)); + check(); + } + }); +} + + +TEST_F(transaction_manager_test_t, inc_dec_ref) +{ + constexpr laddr_t SIZE = 4096; + run_async([this] { + constexpr laddr_t ADDR = 0xFF * SIZE; + { + auto t = create_transaction(); + auto extent = alloc_extent( + t, + ADDR, + SIZE, + 'a'); + ASSERT_EQ(ADDR, extent->get_laddr()); + check_mappings(t); + check(); + submit_transaction(std::move(t)); + check(); + } + replay(); + { + auto t = create_transaction(); + inc_ref(t, ADDR); + check_mappings(t); + check(); + submit_transaction(std::move(t)); + check(); + } + { + auto t = create_transaction(); + dec_ref(t, ADDR); + check_mappings(t); + check(); + submit_transaction(std::move(t)); + check(); + } + replay(); + { + auto t = create_transaction(); + dec_ref(t, ADDR); + check_mappings(t); + check(); + submit_transaction(std::move(t)); + check(); + } + }); +} + +TEST_F(transaction_manager_test_t, cause_lba_split) +{ + constexpr laddr_t SIZE = 4096; + run_async([this] { + for (unsigned i = 0; i < 200; ++i) { + auto t = create_transaction(); + auto extent = alloc_extent( + t, + i * SIZE, + SIZE, + (char)(i & 0xFF)); + ASSERT_EQ(i * SIZE, extent->get_laddr()); + submit_transaction(std::move(t)); + } + check(); + }); +} + +TEST_F(transaction_manager_test_t, random_writes) +{ + constexpr size_t TOTAL = 4<<20; + constexpr size_t BSIZE = 4<<10; + constexpr size_t PADDING_SIZE = 256<<10; + constexpr size_t BLOCKS = TOTAL / BSIZE; + run_async([this] { + for (unsigned i = 0; i < BLOCKS; ++i) { + auto t = create_transaction(); + auto extent = alloc_extent( + t, + i * BSIZE, + BSIZE); + ASSERT_EQ(i * BSIZE, extent->get_laddr()); + submit_transaction(std::move(t)); + } + + for (unsigned i = 0; i < 4; ++i) { + for (unsigned j = 0; j < 65; ++j) { + auto t = create_transaction(); + for (unsigned k = 0; k < 2; ++k) { + auto ext = get_extent( + t, + get_random_laddr(BSIZE, TOTAL), + BSIZE); + auto mut = mutate_extent(t, ext); + // pad out transaction + auto padding = alloc_extent( + t, + TOTAL + (k * PADDING_SIZE), + PADDING_SIZE); + dec_ref(t, padding->get_laddr()); + } + submit_transaction(std::move(t)); + } + replay(); + logger().debug("random_writes: checking"); + check(); + logger().debug("random_writes: done replaying/checking"); + } + }); +} -- cgit v1.2.3