1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
6 #include <boost/iterator/counting_iterator.hpp>
8 #include "test/crimson/gtest_seastar.h"
9 #include "test/crimson/seastore/transaction_manager_test_state.h"
11 #include "crimson/os/seastore/segment_cleaner.h"
12 #include "crimson/os/seastore/cache.h"
13 #include "crimson/os/seastore/transaction_manager.h"
14 #include "crimson/os/seastore/segment_manager/ephemeral.h"
15 #include "crimson/os/seastore/segment_manager.h"
17 #include "test/crimson/seastore/test_block.h"
19 using namespace crimson
;
20 using namespace crimson::os
;
21 using namespace crimson::os::seastore
;
24 [[maybe_unused
]] seastar::logger
& logger() {
25 return crimson::get_logger(ceph_subsys_test
);
29 struct test_extent_record_t
{
30 test_extent_desc_t desc
;
31 unsigned refcount
= 0;
32 test_extent_record_t() = default;
34 const test_extent_desc_t
&desc
,
35 unsigned refcount
) : desc(desc
), refcount(refcount
) {}
37 void update(const test_extent_desc_t
&to
) {
41 bool operator==(const test_extent_desc_t
&rhs
) const {
44 bool operator!=(const test_extent_desc_t
&rhs
) const {
49 std::ostream
&operator<<(std::ostream
&lhs
, const test_extent_record_t
&rhs
) {
50 return lhs
<< "test_extent_record_t(" << rhs
.desc
51 << ", refcount=" << rhs
.refcount
<< ")";
54 struct transaction_manager_test_t
:
55 public seastar_test_suite_t
,
58 std::random_device rd
;
61 transaction_manager_test_t()
65 laddr_t
get_random_laddr(size_t block_size
, laddr_t limit
) {
67 std::uniform_int_distribution
<>(0, (limit
/ block_size
) - 1)(gen
);
70 char get_random_contents() {
71 return static_cast<char>(std::uniform_int_distribution
<>(0, 255)(gen
));
74 seastar::future
<> set_up_fut() final
{
78 seastar::future
<> tear_down_fut() final
{
82 struct test_extents_t
: std::map
<laddr_t
, test_extent_record_t
> {
83 using delta_t
= std::map
<laddr_t
, std::optional
<test_extent_record_t
>>;
85 struct delta_overlay_t
{
86 const test_extents_t
&extents
;
90 const test_extents_t
&extents
,
92 : extents(extents
), delta(delta
) {}
96 friend class test_extents_t
;
98 const delta_overlay_t
&parent
;
99 test_extents_t::const_iterator biter
;
100 delta_t::const_iterator oiter
;
101 std::optional
<std::pair
<laddr_t
, test_extent_record_t
>> cur
;
104 const delta_overlay_t
&parent
,
105 test_extents_t::const_iterator biter
,
106 delta_t::const_iterator oiter
)
107 : parent(parent
), biter(biter
), oiter(oiter
) {}
110 return biter
== parent
.extents
.end() ? L_ADDR_MAX
: biter
->first
;
114 return oiter
== parent
.delta
.end() ? L_ADDR_MAX
: oiter
->first
;
118 return oiter
== parent
.delta
.end() && biter
== parent
.extents
.end();
123 ((get_okey() < get_bkey()) && (oiter
->second
)) ||
124 (get_okey() > get_bkey());
130 auto okey
= get_okey();
131 auto bkey
= get_bkey();
134 std::pair
<laddr_t
, test_extent_record_t
>(*biter
) :
135 std::make_pair(okey
, *(oiter
->second
)));
139 while (!is_valid()) {
140 if (get_okey() < get_bkey()) {
141 assert(!oiter
->second
);
144 assert(get_okey() == get_bkey());
157 iterator(const iterator
&) = default;
158 iterator(iterator
&&) = default;
160 iterator
&operator++() {
163 if (get_bkey() < get_okey()) {
172 bool operator==(const iterator
&o
) const {
173 return o
.biter
== biter
&& o
.oiter
== oiter
;
175 bool operator!=(const iterator
&o
) const {
176 return !(*this == o
);
190 auto ret
= iterator
{*this, extents
.begin(), delta
.begin()};
196 auto ret
= iterator
{*this, extents
.end(), delta
.end()};
197 // adjust unnecessary
201 iterator
lower_bound(laddr_t l
) {
202 auto ret
= iterator
{*this, extents
.lower_bound(l
), delta
.lower_bound(l
)};
207 iterator
upper_bound(laddr_t l
) {
208 auto ret
= iterator
{*this, extents
.upper_bound(l
), delta
.upper_bound(l
)};
213 iterator
find(laddr_t l
) {
214 auto ret
= lower_bound(l
);
215 if (ret
== end() || ret
->first
!= l
) {
223 void check_available(
224 laddr_t addr
, extent_len_t len
, const delta_t
&delta
226 delta_overlay_t
overlay(*this, delta
);
227 for (const auto &i
: overlay
) {
228 if (i
.first
< addr
) {
229 EXPECT_FALSE(i
.first
+ i
.second
.desc
.len
> addr
);
231 EXPECT_FALSE(addr
+ len
> i
.first
);
240 delta_t
&delta
) const {
241 delta_overlay_t
overlay(*this, delta
);
242 auto iter
= overlay
.lower_bound(hint
);
245 if (iter
== overlay
.end() || iter
->first
> addr
) {
246 EXPECT_EQ(addr
, last
);
249 EXPECT_FALSE(iter
->first
- last
> len
);
250 last
= iter
->first
+ iter
->second
.desc
.len
;
255 std::optional
<test_extent_record_t
> &populate_delta(
256 laddr_t addr
, delta_t
&delta
, const test_extent_desc_t
*desc
) const {
257 auto diter
= delta
.find(addr
);
258 if (diter
!= delta
.end())
259 return diter
->second
;
261 auto iter
= find(addr
);
264 auto ret
= delta
.emplace(
265 std::make_pair(addr
, test_extent_record_t
{*desc
, 0}));
267 return ret
.first
->second
;
269 auto ret
= delta
.emplace(*iter
);
271 return ret
.first
->second
;
275 delta_overlay_t
get_overlay(const delta_t
&delta
) const {
276 return delta_overlay_t
{*this, delta
};
279 void insert(TestBlock
&extent
, delta_t
&delta
) const {
280 check_available(extent
.get_laddr(), extent
.get_length(), delta
);
281 delta
[extent
.get_laddr()] =
282 test_extent_record_t
{extent
.get_desc(), 1};
285 void alloced(laddr_t hint
, TestBlock
&extent
, delta_t
&delta
) const {
286 check_hint(hint
, extent
.get_laddr(), extent
.get_length(), delta
);
287 insert(extent
, delta
);
290 bool contains(laddr_t addr
, const delta_t
&delta
) const {
291 delta_overlay_t
overlay(*this, delta
);
292 return overlay
.find(addr
) != overlay
.end();
295 test_extent_record_t
get(laddr_t addr
, const delta_t
&delta
) const {
296 delta_overlay_t
overlay(*this, delta
);
297 auto iter
= overlay
.find(addr
);
298 assert(iter
!= overlay
.end());
304 const test_extent_desc_t
&desc
,
305 delta_t
&delta
) const {
306 auto &rec
= populate_delta(addr
, delta
, &desc
);
313 delta_t
&delta
) const {
314 auto &rec
= populate_delta(addr
, delta
, nullptr);
316 return ++rec
->refcount
;
321 delta_t
&delta
) const {
322 auto &rec
= populate_delta(addr
, delta
, nullptr);
324 assert(rec
->refcount
> 0);
326 if (rec
->refcount
== 0) {
327 delta
[addr
] = std::nullopt
;
330 return rec
->refcount
;
334 void consume(const delta_t
&delta
) {
335 for (const auto &i
: delta
) {
337 (*this)[i
.first
] = *i
.second
;
346 struct test_transaction_t
{
348 test_extents_t::delta_t mapping_delta
;
351 test_transaction_t
create_transaction() {
352 return { create_mutate_transaction(), {} };
355 test_transaction_t
create_weak_test_transaction() {
356 return { create_weak_transaction(), {} };
359 TestBlockRef
alloc_extent(
360 test_transaction_t
&t
,
364 auto extent
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
365 return tm
->alloc_extent
<TestBlock
>(trans
, hint
, len
);
367 extent
->set_contents(contents
);
368 EXPECT_FALSE(test_mappings
.contains(extent
->get_laddr(), t
.mapping_delta
));
369 EXPECT_EQ(len
, extent
->get_length());
370 test_mappings
.alloced(hint
, *extent
, t
.mapping_delta
);
374 TestBlockRef
alloc_extent(
375 test_transaction_t
&t
,
382 get_random_contents());
386 auto t
= create_weak_test_transaction();
387 SpaceTrackerIRef
tracker(segment_cleaner
->get_empty_space_tracker());
390 [this, &tracker
](auto &t
) {
391 return lba_manager
->scan_mapped_space(
393 [&tracker
](auto offset
, auto len
) {
395 offset
.as_seg_paddr().get_segment_id(),
396 offset
.as_seg_paddr().get_segment_off(),
400 return segment_cleaner
->debug_check_space(*tracker
);
404 logger().debug("{}: begin", __func__
);
405 EXPECT_TRUE(check_usage());
407 logger().debug("{}: end", __func__
);
415 void check_mappings() {
416 auto t
= create_weak_test_transaction();
420 TestBlockRef
get_extent(
421 test_transaction_t
&t
,
424 ceph_assert(test_mappings
.contains(addr
, t
.mapping_delta
));
425 ceph_assert(test_mappings
.get(addr
, t
.mapping_delta
).desc
.len
== len
);
427 auto ext
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
428 return tm
->read_extent
<TestBlock
>(trans
, addr
, len
);
430 EXPECT_EQ(addr
, ext
->get_laddr());
434 TestBlockRef
try_get_extent(
435 test_transaction_t
&t
,
438 ceph_assert(test_mappings
.contains(addr
, t
.mapping_delta
));
439 ceph_assert(test_mappings
.get(addr
, t
.mapping_delta
).desc
.len
== len
);
441 using ertr
= with_trans_ertr
<TransactionManager::read_extent_iertr
>;
442 using ret
= ertr::future
<TestBlockRef
>;
443 auto ext
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
444 return tm
->read_extent
<TestBlock
>(trans
, addr
, len
);
445 }).safe_then([](auto ext
) -> ret
{
446 return ertr::make_ready_future
<TestBlockRef
>(ext
);
448 [](const crimson::ct_error::eagain
&e
) {
449 return seastar::make_ready_future
<TestBlockRef
>();
451 crimson::ct_error::assert_all
{
452 "get_extent got invalid error"
456 EXPECT_EQ(addr
, ext
->get_laddr());
461 test_block_mutator_t mutator
;
462 TestBlockRef
mutate_extent(
463 test_transaction_t
&t
,
465 ceph_assert(test_mappings
.contains(ref
->get_laddr(), t
.mapping_delta
));
467 test_mappings
.get(ref
->get_laddr(), t
.mapping_delta
).desc
.len
==
470 auto ext
= tm
->get_mutable_extent(*t
.t
, ref
)->cast
<TestBlock
>();
471 EXPECT_EQ(ext
->get_laddr(), ref
->get_laddr());
472 EXPECT_EQ(ext
->get_desc(), ref
->get_desc());
473 mutator
.mutate(*ext
, gen
);
475 test_mappings
.update(ext
->get_laddr(), ext
->get_desc(), t
.mapping_delta
);
479 TestBlockRef
mutate_addr(
480 test_transaction_t
&t
,
483 auto ext
= get_extent(t
, offset
, length
);
484 mutate_extent(t
, ext
);
488 void inc_ref(test_transaction_t
&t
, laddr_t offset
) {
489 ceph_assert(test_mappings
.contains(offset
, t
.mapping_delta
));
490 ceph_assert(test_mappings
.get(offset
, t
.mapping_delta
).refcount
> 0);
492 auto refcnt
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
493 return tm
->inc_ref(trans
, offset
);
495 auto check_refcnt
= test_mappings
.inc_ref(offset
, t
.mapping_delta
);
496 EXPECT_EQ(refcnt
, check_refcnt
);
499 void dec_ref(test_transaction_t
&t
, laddr_t offset
) {
500 ceph_assert(test_mappings
.contains(offset
, t
.mapping_delta
));
501 ceph_assert(test_mappings
.get(offset
, t
.mapping_delta
).refcount
> 0);
503 auto refcnt
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
504 return tm
->dec_ref(trans
, offset
);
506 auto check_refcnt
= test_mappings
.dec_ref(offset
, t
.mapping_delta
);
507 EXPECT_EQ(refcnt
, check_refcnt
);
509 logger().debug("dec_ref: {} at refcount 0", offset
);
512 void check_mappings(test_transaction_t
&t
) {
513 auto overlay
= test_mappings
.get_overlay(t
.mapping_delta
);
514 for (const auto &i
: overlay
) {
515 logger().debug("check_mappings: {}->{}", i
.first
, i
.second
);
516 auto ext
= get_extent(t
, i
.first
, i
.second
.desc
.len
);
517 EXPECT_EQ(i
.second
, ext
->get_desc());
521 [this, &overlay
](auto &t
) {
522 return lba_manager
->scan_mappings(
526 [iter
=overlay
.begin(), &overlay
](auto l
, auto p
, auto len
) mutable {
527 EXPECT_NE(iter
, overlay
.end());
529 "check_mappings: scan {}",
531 EXPECT_EQ(l
, iter
->first
);
537 bool try_submit_transaction(test_transaction_t t
) {
538 using ertr
= with_trans_ertr
<TransactionManager::submit_transaction_iertr
>;
539 using ret
= ertr::future
<bool>;
540 bool success
= submit_transaction_fut(*t
.t
541 ).safe_then([]() -> ret
{
542 return ertr::make_ready_future
<bool>(true);
544 [](const crimson::ct_error::eagain
&e
) {
545 return seastar::make_ready_future
<bool>(false);
547 crimson::ct_error::assert_all
{
548 "try_submit_transaction hit invalid error"
550 ).then([this](auto ret
) {
551 return segment_cleaner
->run_until_halt().then([ret
] { return ret
; });
555 test_mappings
.consume(t
.mapping_delta
);
561 void submit_transaction(test_transaction_t
&&t
) {
562 bool success
= try_submit_transaction(std::move(t
));
563 EXPECT_TRUE(success
);
566 void submit_transaction_expect_conflict(test_transaction_t
&&t
) {
567 bool success
= try_submit_transaction(std::move(t
));
568 EXPECT_FALSE(success
);
571 auto allocate_sequentially(const size_t& size
, int &num
) {
572 return repeat_eagain([&, this] {
573 return seastar::do_with(
574 create_transaction(),
576 return with_trans_intr(
579 return trans_intr::do_for_each(
580 boost::make_counting_iterator(0),
581 boost::make_counting_iterator(num
),
583 return tm
->alloc_extent
<TestBlock
>(
584 *(t
.t
), L_ADDR_MIN
, size
585 ).si_then([&, this](auto extent
) {
586 extent
->set_contents(get_random_contents());
588 test_mappings
.contains(extent
->get_laddr(), t
.mapping_delta
));
589 EXPECT_EQ(size
, extent
->get_length());
590 test_mappings
.alloced(extent
->get_laddr(), *extent
, t
.mapping_delta
);
591 return seastar::now();
593 }).si_then([&t
, this] {
594 return tm
->submit_transaction(*t
.t
);
596 }).safe_then([&t
, this] {
597 test_mappings
.consume(t
.mapping_delta
);
600 }).safe_then([this]() {
601 return segment_cleaner
->run_until_halt();
603 crimson::ct_error::assert_all
{
604 "Invalid error in SeaStore::list_collections"
610 TEST_F(transaction_manager_test_t
, basic
)
612 constexpr laddr_t SIZE
= 4096;
614 constexpr laddr_t ADDR
= 0xFF * SIZE
;
616 auto t
= create_transaction();
617 auto extent
= alloc_extent(
622 ASSERT_EQ(ADDR
, extent
->get_laddr());
625 submit_transaction(std::move(t
));
631 TEST_F(transaction_manager_test_t
, mutate
)
633 constexpr laddr_t SIZE
= 4096;
635 constexpr laddr_t ADDR
= 0xFF * SIZE
;
637 auto t
= create_transaction();
638 auto extent
= alloc_extent(
643 ASSERT_EQ(ADDR
, extent
->get_laddr());
646 submit_transaction(std::move(t
));
649 ASSERT_TRUE(check_usage());
652 auto t
= create_transaction();
653 auto ext
= get_extent(
657 auto mut
= mutate_extent(t
, ext
);
660 submit_transaction(std::move(t
));
663 ASSERT_TRUE(check_usage());
669 TEST_F(transaction_manager_test_t
, allocate_lba_conflict
)
671 constexpr laddr_t SIZE
= 4096;
673 constexpr laddr_t ADDR
= 0xFF * SIZE
;
674 constexpr laddr_t ADDR2
= 0xFE * SIZE
;
675 auto t
= create_transaction();
676 auto t2
= create_transaction();
678 // These should conflict as they should both modify the lba root
679 auto extent
= alloc_extent(
684 ASSERT_EQ(ADDR
, extent
->get_laddr());
688 auto extent2
= alloc_extent(
693 ASSERT_EQ(ADDR2
, extent2
->get_laddr());
697 submit_transaction(std::move(t2
));
698 submit_transaction_expect_conflict(std::move(t
));
702 TEST_F(transaction_manager_test_t
, mutate_lba_conflict
)
704 constexpr laddr_t SIZE
= 4096;
707 auto t
= create_transaction();
708 for (unsigned i
= 0; i
< 300; ++i
) {
709 auto extent
= alloc_extent(
715 submit_transaction(std::move(t
));
719 constexpr laddr_t ADDR
= 150 * SIZE
;
721 auto t
= create_transaction();
722 auto t2
= create_transaction();
724 mutate_addr(t
, ADDR
, SIZE
);
725 mutate_addr(t2
, ADDR
, SIZE
);
727 submit_transaction(std::move(t
));
728 submit_transaction_expect_conflict(std::move(t2
));
733 auto t
= create_transaction();
734 mutate_addr(t
, ADDR
, SIZE
);
735 submit_transaction(std::move(t
));
741 TEST_F(transaction_manager_test_t
, concurrent_mutate_lba_no_conflict
)
743 constexpr laddr_t SIZE
= 4096;
744 constexpr size_t NUM
= 500;
745 constexpr laddr_t addr
= 0;
746 constexpr laddr_t addr2
= SIZE
* (NUM
- 1);
749 auto t
= create_transaction();
750 for (unsigned i
= 0; i
< NUM
; ++i
) {
751 auto extent
= alloc_extent(
756 submit_transaction(std::move(t
));
760 auto t
= create_transaction();
761 auto t2
= create_transaction();
763 mutate_addr(t
, addr
, SIZE
);
764 mutate_addr(t2
, addr2
, SIZE
);
766 submit_transaction(std::move(t
));
767 submit_transaction(std::move(t2
));
773 TEST_F(transaction_manager_test_t
, create_remove_same_transaction
)
775 constexpr laddr_t SIZE
= 4096;
777 constexpr laddr_t ADDR
= 0xFF * SIZE
;
779 auto t
= create_transaction();
780 auto extent
= alloc_extent(
785 ASSERT_EQ(ADDR
, extent
->get_laddr());
790 extent
= alloc_extent(
796 submit_transaction(std::move(t
));
804 TEST_F(transaction_manager_test_t
, split_merge_read_same_transaction
)
806 constexpr laddr_t SIZE
= 4096;
809 auto t
= create_transaction();
810 for (unsigned i
= 0; i
< 300; ++i
) {
811 auto extent
= alloc_extent(
817 submit_transaction(std::move(t
));
821 auto t
= create_transaction();
822 for (unsigned i
= 0; i
< 240; ++i
) {
828 submit_transaction(std::move(t
));
834 TEST_F(transaction_manager_test_t
, inc_dec_ref
)
836 constexpr laddr_t SIZE
= 4096;
838 constexpr laddr_t ADDR
= 0xFF * SIZE
;
840 auto t
= create_transaction();
841 auto extent
= alloc_extent(
846 ASSERT_EQ(ADDR
, extent
->get_laddr());
849 submit_transaction(std::move(t
));
854 auto t
= create_transaction();
858 submit_transaction(std::move(t
));
862 auto t
= create_transaction();
866 submit_transaction(std::move(t
));
871 auto t
= create_transaction();
875 submit_transaction(std::move(t
));
881 TEST_F(transaction_manager_test_t
, cause_lba_split
)
883 constexpr laddr_t SIZE
= 4096;
885 for (unsigned i
= 0; i
< 200; ++i
) {
886 auto t
= create_transaction();
887 auto extent
= alloc_extent(
892 ASSERT_EQ(i
* SIZE
, extent
->get_laddr());
893 submit_transaction(std::move(t
));
899 TEST_F(transaction_manager_test_t
, random_writes
)
901 constexpr size_t TOTAL
= 4<<20;
902 constexpr size_t BSIZE
= 4<<10;
903 constexpr size_t PADDING_SIZE
= 256<<10;
904 constexpr size_t BLOCKS
= TOTAL
/ BSIZE
;
906 for (unsigned i
= 0; i
< BLOCKS
; ++i
) {
907 auto t
= create_transaction();
908 auto extent
= alloc_extent(
912 ASSERT_EQ(i
* BSIZE
, extent
->get_laddr());
913 submit_transaction(std::move(t
));
916 for (unsigned i
= 0; i
< 4; ++i
) {
917 for (unsigned j
= 0; j
< 65; ++j
) {
918 auto t
= create_transaction();
919 for (unsigned k
= 0; k
< 2; ++k
) {
920 auto ext
= get_extent(
922 get_random_laddr(BSIZE
, TOTAL
),
924 auto mut
= mutate_extent(t
, ext
);
925 // pad out transaction
926 auto padding
= alloc_extent(
928 TOTAL
+ (k
* PADDING_SIZE
),
930 dec_ref(t
, padding
->get_laddr());
932 submit_transaction(std::move(t
));
935 logger().debug("random_writes: checking");
937 logger().debug("random_writes: done replaying/checking");
942 TEST_F(transaction_manager_test_t
, random_writes_concurrent
)
944 constexpr unsigned WRITE_STREAMS
= 256;
946 constexpr size_t TOTAL
= 4<<20;
947 constexpr size_t BSIZE
= 4<<10;
948 constexpr size_t BLOCKS
= TOTAL
/ BSIZE
;
950 seastar::parallel_for_each(
951 boost::make_counting_iterator(0u),
952 boost::make_counting_iterator(WRITE_STREAMS
),
954 for (unsigned i
= idx
; i
< BLOCKS
; i
+= WRITE_STREAMS
) {
956 auto t
= create_transaction();
957 auto extent
= alloc_extent(
961 ASSERT_EQ(i
* BSIZE
, extent
->get_laddr());
962 if (try_submit_transaction(std::move(t
)))
969 unsigned failures
= 0;
970 seastar::parallel_for_each(
971 boost::make_counting_iterator(0u),
972 boost::make_counting_iterator(WRITE_STREAMS
),
974 return seastar::async([&] {
975 while (writes
< 300) {
976 auto t
= create_transaction();
977 auto ext
= try_get_extent(
979 get_random_laddr(BSIZE
, TOTAL
),
985 auto mut
= mutate_extent(t
, ext
);
986 auto success
= try_submit_transaction(std::move(t
));
988 failures
+= !success
;
993 logger().debug("random_writes: checking");
996 "random_writes: {} suceeded, {} failed",
1003 TEST_F(transaction_manager_test_t
, find_hole_assert_trigger
)
1005 constexpr unsigned max
= 10;
1006 constexpr size_t BSIZE
= 4<<10;
1009 return seastar::parallel_for_each(
1010 boost::make_counting_iterator(0u),
1011 boost::make_counting_iterator(max
),
1012 [&, this](auto idx
) {
1013 return allocate_sequentially(BSIZE
, num
);