// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "test/crimson/gtest_seastar.h" #include "crimson/common/log.h" #include "crimson/os/seastore/cache.h" #include "crimson/os/seastore/segment_manager/ephemeral.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 cache_test_t : public seastar_test_suite_t { segment_manager::EphemeralSegmentManagerRef segment_manager; ExtentPlacementManagerRef epm; CacheRef cache; paddr_t current; journal_seq_t seq = JOURNAL_SEQ_MIN; cache_test_t() = default; seastar::future submit_transaction( TransactionRef t) { auto record = cache->prepare_record(*t, JOURNAL_SEQ_NULL, JOURNAL_SEQ_NULL); bufferlist bl; for (auto &&block : record.extents) { bl.append(block.bl); } ceph_assert((segment_off_t)bl.length() < segment_manager->get_segment_size()); if (current.as_seg_paddr().get_segment_off() + (segment_off_t)bl.length() > segment_manager->get_segment_size()) current = paddr_t::make_seg_paddr( segment_id_t( current.as_seg_paddr().get_segment_id().device_id(), current.as_seg_paddr().get_segment_id().device_segment_id() + 1), 0); auto prev = current; current.as_seg_paddr().set_segment_off( current.as_seg_paddr().get_segment_off() + bl.length()); return segment_manager->segment_write( prev, std::move(bl), true ).safe_then( [this, prev, t=std::move(t)]() mutable { cache->complete_commit(*t, prev, seq /* TODO */); return prev; }, crimson::ct_error::all_same_way([](auto e) { ASSERT_FALSE("failed to submit"); }) ); } auto get_transaction() { return cache->create_transaction( Transaction::src_t::MUTATE, "test_cache", false); } template auto get_extent(Transaction &t, Args&&... args) { return with_trans_intr( t, [this](auto &&... args) { return cache->get_extent(args...); }, std::forward(args)...); } seastar::future<> set_up_fut() final { segment_manager = segment_manager::create_test_ephemeral(); return segment_manager->init( ).safe_then([this] { return segment_manager->mkfs( segment_manager::get_ephemeral_device_config(0, 1, 0)); }).safe_then([this] { epm.reset(new ExtentPlacementManager()); cache.reset(new Cache(*epm)); current = paddr_t::make_seg_paddr(segment_id_t(segment_manager->get_device_id(), 0), 0); epm->test_init_no_background(segment_manager.get()); return seastar::do_with( get_transaction(), [this](auto &ref_t) { cache->init(); return with_trans_intr(*ref_t, [&](auto &t) { return cache->mkfs(t); }).safe_then([this, &ref_t] { return submit_transaction(std::move(ref_t) ).then([](auto p) {}); }); }); }).handle_error( crimson::ct_error::all_same_way([](auto e) { ASSERT_FALSE("failed to submit"); }) ); } seastar::future<> tear_down_fut() final { return cache->close( ).safe_then([this] { segment_manager.reset(); epm.reset(); cache.reset(); }).handle_error( Cache::close_ertr::assert_all{} ); } }; TEST_F(cache_test_t, test_addr_fixup) { run_async([this] { paddr_t addr; int csum = 0; { auto t = get_transaction(); auto extent = cache->alloc_new_extent( *t, TestBlockPhysical::SIZE, placement_hint_t::HOT, 0); extent->set_contents('c'); csum = extent->get_crc32c(); submit_transaction(std::move(t)).get0(); addr = extent->get_paddr(); } { auto t = get_transaction(); auto extent = get_extent( *t, addr, TestBlockPhysical::SIZE).unsafe_get0(); ASSERT_EQ(extent->get_paddr(), addr); ASSERT_EQ(extent->get_crc32c(), csum); } }); } TEST_F(cache_test_t, test_dirty_extent) { run_async([this] { paddr_t addr; int csum = 0; int csum2 = 0; { // write out initial test block auto t = get_transaction(); auto extent = cache->alloc_new_extent( *t, TestBlockPhysical::SIZE, placement_hint_t::HOT, 0); extent->set_contents('c'); csum = extent->get_crc32c(); auto reladdr = extent->get_paddr(); ASSERT_TRUE(reladdr.is_relative()); { // test that read with same transaction sees new block though // uncommitted auto extent = get_extent( *t, reladdr, TestBlockPhysical::SIZE).unsafe_get0(); ASSERT_TRUE(extent->is_clean()); ASSERT_TRUE(extent->is_pending()); ASSERT_TRUE(extent->get_paddr().is_relative()); ASSERT_EQ(extent->get_version(), 0); ASSERT_EQ(csum, extent->get_crc32c()); } submit_transaction(std::move(t)).get0(); addr = extent->get_paddr(); } { // test that consecutive reads on the same extent get the same ref auto t = get_transaction(); auto extent = get_extent( *t, addr, TestBlockPhysical::SIZE).unsafe_get0(); auto t2 = get_transaction(); auto extent2 = get_extent( *t2, addr, TestBlockPhysical::SIZE).unsafe_get0(); ASSERT_EQ(&*extent, &*extent2); } { // read back test block auto t = get_transaction(); auto extent = get_extent( *t, addr, TestBlockPhysical::SIZE).unsafe_get0(); // duplicate and reset contents extent = cache->duplicate_for_write(*t, extent)->cast(); extent->set_contents('c'); csum2 = extent->get_crc32c(); ASSERT_EQ(extent->get_paddr(), addr); { // test that concurrent read with fresh transaction sees old // block auto t2 = get_transaction(); auto extent = get_extent( *t2, addr, TestBlockPhysical::SIZE).unsafe_get0(); ASSERT_TRUE(extent->is_clean()); ASSERT_FALSE(extent->is_pending()); ASSERT_EQ(addr, extent->get_paddr()); ASSERT_EQ(extent->get_version(), 0); ASSERT_EQ(csum, extent->get_crc32c()); } { // test that read with same transaction sees new block auto extent = get_extent( *t, addr, TestBlockPhysical::SIZE).unsafe_get0(); ASSERT_TRUE(extent->is_dirty()); ASSERT_TRUE(extent->is_pending()); ASSERT_EQ(addr, extent->get_paddr()); ASSERT_EQ(extent->get_version(), 1); ASSERT_EQ(csum2, extent->get_crc32c()); } // submit transaction submit_transaction(std::move(t)).get0(); ASSERT_TRUE(extent->is_dirty()); ASSERT_EQ(addr, extent->get_paddr()); ASSERT_EQ(extent->get_version(), 1); ASSERT_EQ(extent->get_crc32c(), csum2); } { // test that fresh transaction now sees newly dirty block auto t = get_transaction(); auto extent = get_extent( *t, addr, TestBlockPhysical::SIZE).unsafe_get0(); ASSERT_TRUE(extent->is_dirty()); ASSERT_EQ(addr, extent->get_paddr()); ASSERT_EQ(extent->get_version(), 1); ASSERT_EQ(csum2, extent->get_crc32c()); } }); }