]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/crimson/seastore/test_transaction_manager.cc
1148884a0c1939b5e6137dee533cd1e1ac930f6d
[ceph.git] / ceph / src / test / crimson / seastore / test_transaction_manager.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <random>
5
6 #include <boost/iterator/counting_iterator.hpp>
7
8 #include "test/crimson/gtest_seastar.h"
9 #include "test/crimson/seastore/transaction_manager_test_state.h"
10
11 #include "crimson/os/seastore/cache.h"
12 #include "crimson/os/seastore/transaction_manager.h"
13 #include "crimson/os/seastore/segment_manager/ephemeral.h"
14 #include "crimson/os/seastore/segment_manager.h"
15
16 #include "test/crimson/seastore/test_block.h"
17
18 using namespace crimson;
19 using namespace crimson::os;
20 using namespace crimson::os::seastore;
21
22 namespace {
23 [[maybe_unused]] seastar::logger& logger() {
24 return crimson::get_logger(ceph_subsys_test);
25 }
26 }
27
28 struct test_extent_record_t {
29 test_extent_desc_t desc;
30 unsigned refcount = 0;
31 test_extent_record_t() = default;
32 test_extent_record_t(
33 const test_extent_desc_t &desc,
34 unsigned refcount) : desc(desc), refcount(refcount) {}
35
36 void update(const test_extent_desc_t &to) {
37 desc = to;
38 }
39
40 bool operator==(const test_extent_desc_t &rhs) const {
41 return desc == rhs;
42 }
43 bool operator!=(const test_extent_desc_t &rhs) const {
44 return desc != rhs;
45 }
46 };
47
48 template<>
49 struct fmt::formatter<test_extent_record_t> : fmt::formatter<std::string_view> {
50 template <typename FormatContext>
51 auto format(const test_extent_record_t& r, FormatContext& ctx) const {
52 return fmt::format_to(ctx.out(), "test_extent_record_t({}, refcount={})",
53 r.desc, r.refcount);
54 }
55 };
56
57 struct transaction_manager_test_t :
58 public seastar_test_suite_t,
59 TMTestState {
60
61 std::random_device rd;
62 std::mt19937 gen;
63
64 transaction_manager_test_t(std::size_t num_main_devices, std::size_t num_cold_devices)
65 : TMTestState(num_main_devices, num_cold_devices), gen(rd()) {
66 }
67
68 laddr_t get_random_laddr(size_t block_size, laddr_t limit) {
69 return block_size *
70 std::uniform_int_distribution<>(0, (limit / block_size) - 1)(gen);
71 }
72
73 char get_random_contents() {
74 return static_cast<char>(std::uniform_int_distribution<>(0, 255)(gen));
75 }
76
77 seastar::future<> set_up_fut() final {
78 return tm_setup();
79 }
80
81 seastar::future<> tear_down_fut() final {
82 return tm_teardown();
83 }
84
85 struct test_extents_t : std::map<laddr_t, test_extent_record_t> {
86 using delta_t = std::map<laddr_t, std::optional<test_extent_record_t>>;
87 std::map<laddr_t, uint64_t> laddr_write_seq;
88
89 struct delta_overlay_t {
90 const test_extents_t &extents;
91 const delta_t &delta;
92
93 delta_overlay_t(
94 const test_extents_t &extents,
95 const delta_t &delta)
96 : extents(extents), delta(delta) {}
97
98
99 class iterator {
100 friend class test_extents_t;
101
102 const delta_overlay_t &parent;
103 test_extents_t::const_iterator biter;
104 delta_t::const_iterator oiter;
105 std::optional<std::pair<laddr_t, test_extent_record_t>> cur;
106
107 iterator(
108 const delta_overlay_t &parent,
109 test_extents_t::const_iterator biter,
110 delta_t::const_iterator oiter)
111 : parent(parent), biter(biter), oiter(oiter) {}
112
113 laddr_t get_bkey() {
114 return biter == parent.extents.end() ? L_ADDR_MAX : biter->first;
115 }
116
117 laddr_t get_okey() {
118 return oiter == parent.delta.end() ? L_ADDR_MAX : oiter->first;
119 }
120
121 bool is_end() {
122 return oiter == parent.delta.end() && biter == parent.extents.end();
123 }
124
125 bool is_valid() {
126 return is_end() ||
127 ((get_okey() < get_bkey()) && (oiter->second)) ||
128 (get_okey() > get_bkey());
129 }
130
131 auto get_pair() {
132 assert(is_valid());
133 assert(!is_end());
134 auto okey = get_okey();
135 auto bkey = get_bkey();
136 return (
137 bkey < okey ?
138 std::pair<laddr_t, test_extent_record_t>(*biter) :
139 std::make_pair(okey, *(oiter->second)));
140 }
141
142 void adjust() {
143 while (!is_valid()) {
144 if (get_okey() < get_bkey()) {
145 assert(!oiter->second);
146 ++oiter;
147 } else {
148 assert(get_okey() == get_bkey());
149 ++biter;
150 }
151 }
152 assert(is_valid());
153 if (!is_end()) {
154 cur = get_pair();
155 } else {
156 cur = std::nullopt;
157 }
158 }
159
160 public:
161 iterator(const iterator &) = default;
162 iterator(iterator &&) = default;
163
164 iterator &operator++() {
165 assert(is_valid());
166 assert(!is_end());
167 if (get_bkey() < get_okey()) {
168 ++biter;
169 } else {
170 ++oiter;
171 }
172 adjust();
173 return *this;
174 }
175
176 bool operator==(const iterator &o) const {
177 return o.biter == biter && o.oiter == oiter;
178 }
179 bool operator!=(const iterator &o) const {
180 return !(*this == o);
181 }
182
183 auto operator*() {
184 assert(!is_end());
185 return *cur;
186 }
187 auto operator->() {
188 assert(!is_end());
189 return &*cur;
190 }
191 };
192
193 iterator begin() {
194 auto ret = iterator{*this, extents.begin(), delta.begin()};
195 ret.adjust();
196 return ret;
197 }
198
199 iterator end() {
200 auto ret = iterator{*this, extents.end(), delta.end()};
201 // adjust unnecessary
202 return ret;
203 }
204
205 iterator lower_bound(laddr_t l) {
206 auto ret = iterator{*this, extents.lower_bound(l), delta.lower_bound(l)};
207 ret.adjust();
208 return ret;
209 }
210
211 iterator upper_bound(laddr_t l) {
212 auto ret = iterator{*this, extents.upper_bound(l), delta.upper_bound(l)};
213 ret.adjust();
214 return ret;
215 }
216
217 iterator find(laddr_t l) {
218 auto ret = lower_bound(l);
219 if (ret == end() || ret->first != l) {
220 return end();
221 } else {
222 return ret;
223 }
224 }
225 };
226 private:
227 void check_available(
228 laddr_t addr, extent_len_t len, const delta_t &delta
229 ) const {
230 delta_overlay_t overlay(*this, delta);
231 for (const auto &i: overlay) {
232 if (i.first < addr) {
233 EXPECT_FALSE(i.first + i.second.desc.len > addr);
234 } else {
235 EXPECT_FALSE(addr + len > i.first);
236 }
237 }
238 }
239
240 void check_hint(
241 laddr_t hint,
242 laddr_t addr,
243 extent_len_t len,
244 delta_t &delta) const {
245 delta_overlay_t overlay(*this, delta);
246 auto iter = overlay.lower_bound(hint);
247 laddr_t last = hint;
248 while (true) {
249 if (iter == overlay.end() || iter->first > addr) {
250 EXPECT_EQ(addr, last);
251 break;
252 }
253 EXPECT_FALSE(iter->first - last > len);
254 last = iter->first + iter->second.desc.len;
255 ++iter;
256 }
257 }
258
259 std::optional<test_extent_record_t> &populate_delta(
260 laddr_t addr, delta_t &delta, const test_extent_desc_t *desc) const {
261 auto diter = delta.find(addr);
262 if (diter != delta.end())
263 return diter->second;
264
265 auto iter = find(addr);
266 if (iter == end()) {
267 assert(desc);
268 auto ret = delta.emplace(
269 std::make_pair(addr, test_extent_record_t{*desc, 0}));
270 assert(ret.second);
271 return ret.first->second;
272 } else {
273 auto ret = delta.emplace(*iter);
274 assert(ret.second);
275 return ret.first->second;
276 }
277 }
278 public:
279 delta_overlay_t get_overlay(const delta_t &delta) const {
280 return delta_overlay_t{*this, delta};
281 }
282
283 void insert(TestBlock &extent, delta_t &delta) const {
284 check_available(extent.get_laddr(), extent.get_length(), delta);
285 delta[extent.get_laddr()] =
286 test_extent_record_t{extent.get_desc(), 1};
287 }
288
289 void alloced(laddr_t hint, TestBlock &extent, delta_t &delta) const {
290 check_hint(hint, extent.get_laddr(), extent.get_length(), delta);
291 insert(extent, delta);
292 }
293
294 bool contains(laddr_t addr, const delta_t &delta) const {
295 delta_overlay_t overlay(*this, delta);
296 return overlay.find(addr) != overlay.end();
297 }
298
299 test_extent_record_t get(laddr_t addr, const delta_t &delta) const {
300 delta_overlay_t overlay(*this, delta);
301 auto iter = overlay.find(addr);
302 assert(iter != overlay.end());
303 return iter->second;
304 }
305
306 void update(
307 laddr_t addr,
308 const test_extent_desc_t &desc,
309 delta_t &delta) const {
310 auto &rec = populate_delta(addr, delta, &desc);
311 assert(rec);
312 rec->desc = desc;
313 }
314
315 int inc_ref(
316 laddr_t addr,
317 delta_t &delta) const {
318 auto &rec = populate_delta(addr, delta, nullptr);
319 assert(rec);
320 return ++rec->refcount;
321 }
322
323 int dec_ref(
324 laddr_t addr,
325 delta_t &delta) const {
326 auto &rec = populate_delta(addr, delta, nullptr);
327 assert(rec);
328 assert(rec->refcount > 0);
329 rec->refcount--;
330 if (rec->refcount == 0) {
331 delta[addr] = std::nullopt;
332 return 0;
333 } else {
334 return rec->refcount;
335 }
336 }
337
338 void consume(const delta_t &delta, const uint64_t write_seq = 0) {
339 for (const auto &i : delta) {
340 if (i.second) {
341 if (laddr_write_seq.find(i.first) == laddr_write_seq.end() ||
342 laddr_write_seq[i.first] <= write_seq) {
343 (*this)[i.first] = *i.second;
344 laddr_write_seq[i.first] = write_seq;
345 }
346 } else {
347 erase(i.first);
348 }
349 }
350 }
351
352 } test_mappings;
353
354 struct test_transaction_t {
355 TransactionRef t;
356 test_extents_t::delta_t mapping_delta;
357 };
358
359 test_transaction_t create_transaction() {
360 return { create_mutate_transaction(), {} };
361 }
362
363 test_transaction_t create_read_test_transaction() {
364 return {create_read_transaction(), {} };
365 }
366
367 test_transaction_t create_weak_test_transaction() {
368 return { create_weak_transaction(), {} };
369 }
370
371 TestBlockRef alloc_extent(
372 test_transaction_t &t,
373 laddr_t hint,
374 extent_len_t len,
375 char contents) {
376 auto extent = with_trans_intr(*(t.t), [&](auto& trans) {
377 return tm->alloc_extent<TestBlock>(trans, hint, len);
378 }).unsafe_get0();
379 extent->set_contents(contents);
380 EXPECT_FALSE(test_mappings.contains(extent->get_laddr(), t.mapping_delta));
381 EXPECT_EQ(len, extent->get_length());
382 test_mappings.alloced(hint, *extent, t.mapping_delta);
383 return extent;
384 }
385
386 TestBlockRef alloc_extent(
387 test_transaction_t &t,
388 laddr_t hint,
389 extent_len_t len) {
390 return alloc_extent(
391 t,
392 hint,
393 len,
394 get_random_contents());
395 }
396
397 bool check_usage() {
398 return epm->check_usage();
399 }
400
401 void replay() {
402 EXPECT_TRUE(check_usage());
403 restart();
404 }
405
406 void check() {
407 check_mappings();
408 check_usage();
409 }
410
411 void check_mappings() {
412 auto t = create_weak_test_transaction();
413 check_mappings(t);
414 }
415
416 TestBlockRef get_extent(
417 test_transaction_t &t,
418 laddr_t addr,
419 extent_len_t len) {
420 ceph_assert(test_mappings.contains(addr, t.mapping_delta));
421 ceph_assert(test_mappings.get(addr, t.mapping_delta).desc.len == len);
422
423 auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
424 return tm->read_extent<TestBlock>(trans, addr, len);
425 }).unsafe_get0();
426 EXPECT_EQ(addr, ext->get_laddr());
427 return ext;
428 }
429
430 TestBlockRef try_get_extent(
431 test_transaction_t &t,
432 laddr_t addr) {
433 ceph_assert(test_mappings.contains(addr, t.mapping_delta));
434
435 using ertr = with_trans_ertr<TransactionManager::read_extent_iertr>;
436 using ret = ertr::future<TestBlockRef>;
437 auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
438 return tm->read_extent<TestBlock>(trans, addr);
439 }).safe_then([](auto ext) -> ret {
440 return ertr::make_ready_future<TestBlockRef>(ext);
441 }).handle_error(
442 [](const crimson::ct_error::eagain &e) {
443 return seastar::make_ready_future<TestBlockRef>();
444 },
445 crimson::ct_error::assert_all{
446 "get_extent got invalid error"
447 }
448 ).get0();
449 if (ext) {
450 EXPECT_EQ(addr, ext->get_laddr());
451 }
452 return ext;
453 }
454
455 TestBlockRef try_get_extent(
456 test_transaction_t &t,
457 laddr_t addr,
458 extent_len_t len) {
459 ceph_assert(test_mappings.contains(addr, t.mapping_delta));
460 ceph_assert(test_mappings.get(addr, t.mapping_delta).desc.len == len);
461
462 using ertr = with_trans_ertr<TransactionManager::read_extent_iertr>;
463 using ret = ertr::future<TestBlockRef>;
464 auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
465 return tm->read_extent<TestBlock>(trans, addr, len);
466 }).safe_then([](auto ext) -> ret {
467 return ertr::make_ready_future<TestBlockRef>(ext);
468 }).handle_error(
469 [](const crimson::ct_error::eagain &e) {
470 return seastar::make_ready_future<TestBlockRef>();
471 },
472 crimson::ct_error::assert_all{
473 "get_extent got invalid error"
474 }
475 ).get0();
476 if (ext) {
477 EXPECT_EQ(addr, ext->get_laddr());
478 }
479 return ext;
480 }
481
482 TestBlockRef try_read_pin(
483 test_transaction_t &t,
484 LBAMappingRef &&pin) {
485 using ertr = with_trans_ertr<TransactionManager::base_iertr>;
486 using ret = ertr::future<TestBlockRef>;
487 auto addr = pin->get_key();
488 auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
489 return tm->read_pin<TestBlock>(trans, std::move(pin));
490 }).safe_then([](auto ext) -> ret {
491 return ertr::make_ready_future<TestBlockRef>(ext);
492 }).handle_error(
493 [](const crimson::ct_error::eagain &e) {
494 return seastar::make_ready_future<TestBlockRef>();
495 },
496 crimson::ct_error::assert_all{
497 "read_pin got invalid error"
498 }
499 ).get0();
500 if (ext) {
501 EXPECT_EQ(addr, ext->get_laddr());
502 }
503 if (t.t->is_conflicted()) {
504 return nullptr;
505 }
506 return ext;
507 }
508
509 test_block_mutator_t mutator;
510 TestBlockRef mutate_extent(
511 test_transaction_t &t,
512 TestBlockRef ref) {
513 ceph_assert(test_mappings.contains(ref->get_laddr(), t.mapping_delta));
514 ceph_assert(
515 test_mappings.get(ref->get_laddr(), t.mapping_delta).desc.len ==
516 ref->get_length());
517
518 auto ext = tm->get_mutable_extent(*t.t, ref)->cast<TestBlock>();
519 EXPECT_EQ(ext->get_laddr(), ref->get_laddr());
520 EXPECT_EQ(ext->get_desc(), ref->get_desc());
521 mutator.mutate(*ext, gen);
522
523 test_mappings.update(ext->get_laddr(), ext->get_desc(), t.mapping_delta);
524 return ext;
525 }
526
527 TestBlockRef mutate_addr(
528 test_transaction_t &t,
529 laddr_t offset,
530 size_t length) {
531 auto ext = get_extent(t, offset, length);
532 mutate_extent(t, ext);
533 return ext;
534 }
535
536 LBAMappingRef get_pin(
537 test_transaction_t &t,
538 laddr_t offset) {
539 ceph_assert(test_mappings.contains(offset, t.mapping_delta));
540 auto pin = with_trans_intr(*(t.t), [&](auto& trans) {
541 return tm->get_pin(trans, offset);
542 }).unsafe_get0();
543 EXPECT_EQ(offset, pin->get_key());
544 return pin;
545 }
546
547 LBAMappingRef try_get_pin(
548 test_transaction_t &t,
549 laddr_t offset) {
550 ceph_assert(test_mappings.contains(offset, t.mapping_delta));
551 using ertr = with_trans_ertr<TransactionManager::get_pin_iertr>;
552 using ret = ertr::future<LBAMappingRef>;
553 auto pin = with_trans_intr(*(t.t), [&](auto& trans) {
554 return tm->get_pin(trans, offset);
555 }).safe_then([](auto pin) -> ret {
556 return ertr::make_ready_future<LBAMappingRef>(std::move(pin));
557 }).handle_error(
558 [](const crimson::ct_error::eagain &e) {
559 return seastar::make_ready_future<LBAMappingRef>();
560 },
561 crimson::ct_error::assert_all{
562 "get_extent got invalid error"
563 }
564 ).get0();
565 if (pin) {
566 EXPECT_EQ(offset, pin->get_key());
567 }
568 return pin;
569 }
570
571 void inc_ref(test_transaction_t &t, laddr_t offset) {
572 ceph_assert(test_mappings.contains(offset, t.mapping_delta));
573 ceph_assert(test_mappings.get(offset, t.mapping_delta).refcount > 0);
574
575 auto refcnt = with_trans_intr(*(t.t), [&](auto& trans) {
576 return tm->inc_ref(trans, offset);
577 }).unsafe_get0();
578 auto check_refcnt = test_mappings.inc_ref(offset, t.mapping_delta);
579 EXPECT_EQ(refcnt, check_refcnt);
580 }
581
582 void dec_ref(test_transaction_t &t, laddr_t offset) {
583 ceph_assert(test_mappings.contains(offset, t.mapping_delta));
584 ceph_assert(test_mappings.get(offset, t.mapping_delta).refcount > 0);
585
586 auto refcnt = with_trans_intr(*(t.t), [&](auto& trans) {
587 return tm->dec_ref(trans, offset);
588 }).unsafe_get0();
589 auto check_refcnt = test_mappings.dec_ref(offset, t.mapping_delta);
590 EXPECT_EQ(refcnt, check_refcnt);
591 if (refcnt == 0)
592 logger().debug("dec_ref: {} at refcount 0", offset);
593 }
594
595 void check_mappings(test_transaction_t &t) {
596 auto overlay = test_mappings.get_overlay(t.mapping_delta);
597 for (const auto &i: overlay) {
598 logger().debug("check_mappings: {}->{}", i.first, i.second);
599 auto ext = get_extent(t, i.first, i.second.desc.len);
600 EXPECT_EQ(i.second, ext->get_desc());
601 }
602 with_trans_intr(
603 *t.t,
604 [this, &overlay](auto &t) {
605 return lba_manager->scan_mappings(
606 t,
607 0,
608 L_ADDR_MAX,
609 [iter=overlay.begin(), &overlay](auto l, auto p, auto len) mutable {
610 EXPECT_NE(iter, overlay.end());
611 logger().debug(
612 "check_mappings: scan {}",
613 l);
614 EXPECT_EQ(l, iter->first);
615 ++iter;
616 });
617 }).unsafe_get0();
618 (void)with_trans_intr(
619 *t.t,
620 [=, this](auto &t) {
621 return lba_manager->check_child_trackers(t);
622 }).unsafe_get0();
623 }
624
625 bool try_submit_transaction(test_transaction_t t) {
626 using ertr = with_trans_ertr<TransactionManager::submit_transaction_iertr>;
627 using ret = ertr::future<bool>;
628 uint64_t write_seq = 0;
629 bool success = submit_transaction_fut_with_seq(*t.t
630 ).safe_then([&write_seq](auto seq) -> ret {
631 write_seq = seq;
632 return ertr::make_ready_future<bool>(true);
633 }).handle_error(
634 [](const crimson::ct_error::eagain &e) {
635 return seastar::make_ready_future<bool>(false);
636 },
637 crimson::ct_error::assert_all{
638 "try_submit_transaction hit invalid error"
639 }
640 ).then([this](auto ret) {
641 return epm->run_background_work_until_halt(
642 ).then([ret] { return ret; });
643 }).get0();
644
645 if (success) {
646 test_mappings.consume(t.mapping_delta, write_seq);
647 }
648
649 return success;
650 }
651
652 void submit_transaction(test_transaction_t &&t) {
653 bool success = try_submit_transaction(std::move(t));
654 EXPECT_TRUE(success);
655 }
656
657 void submit_transaction_expect_conflict(test_transaction_t &&t) {
658 bool success = try_submit_transaction(std::move(t));
659 EXPECT_FALSE(success);
660 }
661
662 auto allocate_sequentially(const size_t size, const int num, bool run_clean = true) {
663 return repeat_eagain([this, size, num] {
664 return seastar::do_with(
665 create_transaction(),
666 [this, size, num](auto &t) {
667 return with_trans_intr(
668 *t.t,
669 [&t, this, size, num](auto &) {
670 return trans_intr::do_for_each(
671 boost::make_counting_iterator(0),
672 boost::make_counting_iterator(num),
673 [&t, this, size](auto) {
674 return tm->alloc_extent<TestBlock>(
675 *(t.t), L_ADDR_MIN, size
676 ).si_then([&t, this, size](auto extent) {
677 extent->set_contents(get_random_contents());
678 EXPECT_FALSE(
679 test_mappings.contains(extent->get_laddr(), t.mapping_delta));
680 EXPECT_EQ(size, extent->get_length());
681 test_mappings.alloced(extent->get_laddr(), *extent, t.mapping_delta);
682 return seastar::now();
683 });
684 }).si_then([&t, this] {
685 return tm->submit_transaction(*t.t);
686 });
687 }).safe_then([&t, this] {
688 test_mappings.consume(t.mapping_delta);
689 });
690 });
691 }).safe_then([this, run_clean]() {
692 if (run_clean) {
693 return epm->run_background_work_until_halt();
694 } else {
695 return epm->background_process.trimmer->trim();
696 }
697 }).handle_error(
698 crimson::ct_error::assert_all{
699 "Invalid error in SeaStore::list_collections"
700 }
701 );
702 }
703
704 void test_parallel_extent_read() {
705 constexpr size_t TOTAL = 4<<20;
706 constexpr size_t BSIZE = 4<<10;
707 constexpr size_t BLOCKS = TOTAL / BSIZE;
708 run_async([this] {
709 for (unsigned i = 0; i < BLOCKS; ++i) {
710 auto t = create_transaction();
711 auto extent = alloc_extent(
712 t,
713 i * BSIZE,
714 BSIZE);
715 ASSERT_EQ(i * BSIZE, extent->get_laddr());
716 submit_transaction(std::move(t));
717 }
718
719 seastar::do_with(
720 create_read_test_transaction(),
721 [this](auto &t) {
722 return with_trans_intr(*(t.t), [this](auto &t) {
723 return trans_intr::parallel_for_each(
724 boost::make_counting_iterator(0lu),
725 boost::make_counting_iterator(BLOCKS),
726 [this, &t](auto i) {
727 return tm->read_extent<TestBlock>(t, i * BSIZE, BSIZE
728 ).si_then([](auto) {
729 return seastar::now();
730 });
731 });
732 });
733 }).unsafe_get0();
734 });
735 }
736
737 void test_random_writes_concurrent() {
738 constexpr unsigned WRITE_STREAMS = 256;
739
740 constexpr size_t TOTAL = 4<<20;
741 constexpr size_t BSIZE = 4<<10;
742 constexpr size_t BLOCKS = TOTAL / BSIZE;
743 run_async([this] {
744 std::for_each(
745 boost::make_counting_iterator(0u),
746 boost::make_counting_iterator(WRITE_STREAMS),
747 [&](auto idx) {
748 for (unsigned i = idx; i < BLOCKS; i += WRITE_STREAMS) {
749 while (true) {
750 auto t = create_transaction();
751 auto extent = alloc_extent(
752 t,
753 i * BSIZE,
754 BSIZE);
755 ASSERT_EQ(i * BSIZE, extent->get_laddr());
756 if (try_submit_transaction(std::move(t)))
757 break;
758 }
759 }
760 });
761
762 int writes = 0;
763 unsigned failures = 0;
764 seastar::parallel_for_each(
765 boost::make_counting_iterator(0u),
766 boost::make_counting_iterator(WRITE_STREAMS),
767 [&](auto) {
768 return seastar::async([&] {
769 while (writes < 300) {
770 auto t = create_transaction();
771 auto ext = try_get_extent(
772 t,
773 get_random_laddr(BSIZE, TOTAL),
774 BSIZE);
775 if (!ext){
776 failures++;
777 continue;
778 }
779 auto mut = mutate_extent(t, ext);
780 auto success = try_submit_transaction(std::move(t));
781 writes += success;
782 failures += !success;
783 }
784 });
785 }).get0();
786 replay();
787 logger().info("random_writes_concurrent: checking");
788 check();
789 logger().info(
790 "random_writes_concurrent: {} suceeded, {} failed",
791 writes,
792 failures
793 );
794 });
795 }
796
797 void test_evict() {
798 // only support segmented backend currently
799 ASSERT_EQ(epm->get_main_backend_type(), backend_type_t::SEGMENTED);
800 ASSERT_TRUE(epm->background_process.has_cold_tier());
801 constexpr size_t device_size =
802 segment_manager::DEFAULT_TEST_EPHEMERAL.size;
803 constexpr size_t block_size =
804 segment_manager::DEFAULT_TEST_EPHEMERAL.block_size;
805 constexpr size_t segment_size =
806 segment_manager::DEFAULT_TEST_EPHEMERAL.segment_size;
807 ASSERT_GE(segment_size, block_size * 20);
808
809 run_async([this] {
810 // indicates there is no available segments to reclaim
811 double stop_ratio = (double)segment_size / (double)device_size / 2;
812 // 1 segment
813 double default_ratio = stop_ratio * 2;
814 // 1.25 segment
815 double fast_ratio = stop_ratio * 2.5;
816
817 epm->background_process
818 .eviction_state
819 .init(stop_ratio, default_ratio, fast_ratio);
820
821 // these variables are described in
822 // EPM::BackgroundProcess::eviction_state_t::maybe_update_eviction_mode
823 size_t ratio_A_size = segment_size / 2 - block_size * 10;
824 size_t ratio_B_size = segment_size / 2 + block_size * 10;
825 size_t ratio_C_size = segment_size + block_size;
826 size_t ratio_D_size = segment_size * 1.25 + block_size;
827
828 auto run_until = [this](size_t size) -> seastar::future<> {
829 return seastar::repeat([this, size] {
830 size_t current_size = epm->background_process
831 .main_cleaner->get_stat().data_stored;
832 if (current_size >= size) {
833 return seastar::futurize_invoke([] {
834 return seastar::stop_iteration::yes;
835 });
836 } else {
837 int num = (size - current_size) / block_size;
838 return seastar::do_for_each(
839 boost::make_counting_iterator(0),
840 boost::make_counting_iterator(num),
841 [this](auto) {
842 // don't start background process to test the behavior
843 // of generation changes during alloc new extents
844 return allocate_sequentially(block_size, 1, false);
845 }).then([] {
846 return seastar::stop_iteration::no;
847 });
848 }
849 });
850 };
851
852 std::vector<extent_types_t> all_extent_types{
853 extent_types_t::ROOT,
854 extent_types_t::LADDR_INTERNAL,
855 extent_types_t::LADDR_LEAF,
856 extent_types_t::OMAP_INNER,
857 extent_types_t::OMAP_LEAF,
858 extent_types_t::ONODE_BLOCK_STAGED,
859 extent_types_t::COLL_BLOCK,
860 extent_types_t::OBJECT_DATA_BLOCK,
861 extent_types_t::RETIRED_PLACEHOLDER,
862 extent_types_t::ALLOC_INFO,
863 extent_types_t::JOURNAL_TAIL,
864 extent_types_t::TEST_BLOCK,
865 extent_types_t::TEST_BLOCK_PHYSICAL,
866 extent_types_t::BACKREF_INTERNAL,
867 extent_types_t::BACKREF_LEAF
868 };
869
870 std::vector<rewrite_gen_t> all_generations;
871 for (auto i = INIT_GENERATION; i < REWRITE_GENERATIONS; i++) {
872 all_generations.push_back(i);
873 }
874
875 // input target-generation -> expected generation after the adjustment
876 using generation_mapping_t = std::map<rewrite_gen_t, rewrite_gen_t>;
877 std::map<extent_types_t, generation_mapping_t> expected_generations;
878
879 // this loop should be consistent with EPM::adjust_generation
880 for (auto t : all_extent_types) {
881 expected_generations[t] = {};
882 if (!is_logical_type(t)) {
883 for (auto gen : all_generations) {
884 expected_generations[t][gen] = INLINE_GENERATION;
885 }
886 } else {
887 if (get_extent_category(t) == data_category_t::METADATA) {
888 expected_generations[t][INIT_GENERATION] = INLINE_GENERATION;
889 } else {
890 expected_generations[t][INIT_GENERATION] = OOL_GENERATION;
891 }
892
893 for (auto i = INIT_GENERATION + 1; i < REWRITE_GENERATIONS; i++) {
894 expected_generations[t][i] = i;
895 }
896 }
897 }
898
899 auto update_data_gen_mapping = [&](std::function<rewrite_gen_t(rewrite_gen_t)> func) {
900 for (auto t : all_extent_types) {
901 if (!is_logical_type(t)) {
902 continue;
903 }
904 for (auto i = INIT_GENERATION + 1; i < REWRITE_GENERATIONS; i++) {
905 expected_generations[t][i] = func(i);
906 }
907 }
908 // since background process didn't start in allocate_sequentially
909 // we update eviction mode manually.
910 epm->background_process.maybe_update_eviction_mode();
911 };
912
913 auto test_gen = [&](const char *caller) {
914 for (auto t : all_extent_types) {
915 for (auto gen : all_generations) {
916 auto epm_gen = epm->adjust_generation(
917 get_extent_category(t),
918 t,
919 placement_hint_t::HOT,
920 gen);
921 if (expected_generations[t][gen] != epm_gen) {
922 logger().error("caller: {}, extent type: {}, input generation: {}, "
923 "expected generation : {}, adjust result from EPM: {}",
924 caller, t, gen, expected_generations[t][gen], epm_gen);
925 }
926 EXPECT_EQ(expected_generations[t][gen], epm_gen);
927 }
928 }
929 };
930
931 // verify that no data should go to the cold tier
932 update_data_gen_mapping([](rewrite_gen_t gen) -> rewrite_gen_t {
933 if (gen == MIN_COLD_GENERATION) {
934 return MIN_COLD_GENERATION - 1;
935 } else {
936 return gen;
937 }
938 });
939 test_gen("init");
940
941 run_until(ratio_A_size).get();
942 EXPECT_TRUE(epm->background_process.eviction_state.is_stop_mode());
943 test_gen("exceed ratio A");
944 epm->run_background_work_until_halt().get();
945
946 run_until(ratio_B_size).get();
947 EXPECT_TRUE(epm->background_process.eviction_state.is_stop_mode());
948 test_gen("exceed ratio B");
949 epm->run_background_work_until_halt().get();
950
951 // verify that data may go to the cold tier
952 run_until(ratio_C_size).get();
953 update_data_gen_mapping([](rewrite_gen_t gen) { return gen; });
954 EXPECT_TRUE(epm->background_process.eviction_state.is_default_mode());
955 test_gen("exceed ratio C");
956 epm->run_background_work_until_halt().get();
957
958 // verify that data must go to the cold tier
959 run_until(ratio_D_size).get();
960 update_data_gen_mapping([](rewrite_gen_t gen) {
961 if (gen >= MIN_REWRITE_GENERATION && gen < MIN_COLD_GENERATION) {
962 return MIN_COLD_GENERATION;
963 } else {
964 return gen;
965 }
966 });
967 EXPECT_TRUE(epm->background_process.eviction_state.is_fast_mode());
968 test_gen("exceed ratio D");
969
970 auto main_size = epm->background_process.main_cleaner->get_stat().data_stored;
971 auto cold_size = epm->background_process.cold_cleaner->get_stat().data_stored;
972 EXPECT_EQ(cold_size, 0);
973 epm->run_background_work_until_halt().get();
974 auto new_main_size = epm->background_process.main_cleaner->get_stat().data_stored;
975 auto new_cold_size = epm->background_process.cold_cleaner->get_stat().data_stored;
976 EXPECT_GE(main_size, new_main_size);
977 EXPECT_NE(new_cold_size, 0);
978
979 update_data_gen_mapping([](rewrite_gen_t gen) { return gen; });
980 EXPECT_TRUE(epm->background_process.eviction_state.is_default_mode());
981 test_gen("finish evict");
982 });
983 }
984
985 using remap_entry = TransactionManager::remap_entry;
986 LBAMappingRef remap_pin(
987 test_transaction_t &t,
988 LBAMappingRef &&opin,
989 extent_len_t new_offset,
990 extent_len_t new_len) {
991 if (t.t->is_conflicted()) {
992 return nullptr;
993 }
994 auto o_laddr = opin->get_key();
995 auto pin = with_trans_intr(*(t.t), [&](auto& trans) {
996 return tm->remap_pin<TestBlock>(
997 trans, std::move(opin), std::array{
998 remap_entry(new_offset, new_len)}
999 ).si_then([](auto ret) {
1000 return std::move(ret[0]);
1001 });
1002 }).handle_error(crimson::ct_error::eagain::handle([] {
1003 LBAMappingRef t = nullptr;
1004 return t;
1005 }), crimson::ct_error::pass_further_all{}).unsafe_get0();
1006 if (t.t->is_conflicted()) {
1007 return nullptr;
1008 }
1009 test_mappings.dec_ref(o_laddr, t.mapping_delta);
1010 EXPECT_FALSE(test_mappings.contains(o_laddr, t.mapping_delta));
1011 EXPECT_TRUE(pin);
1012 EXPECT_EQ(pin->get_length(), new_len);
1013 EXPECT_EQ(pin->get_key(), o_laddr + new_offset);
1014
1015 auto extent = try_read_pin(t, pin->duplicate());
1016 if (extent) {
1017 test_mappings.alloced(pin->get_key(), *extent, t.mapping_delta);
1018 EXPECT_TRUE(extent->is_exist_clean());
1019 } else {
1020 ceph_assert(t.t->is_conflicted());
1021 return nullptr;
1022 }
1023 return pin;
1024 }
1025
1026 using _overwrite_pin_iertr = TransactionManager::get_pin_iertr;
1027 using _overwrite_pin_ret = _overwrite_pin_iertr::future<
1028 std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>;
1029 _overwrite_pin_ret _overwrite_pin(
1030 Transaction &t,
1031 LBAMappingRef &&opin,
1032 extent_len_t new_offset,
1033 extent_len_t new_len,
1034 ceph::bufferlist &bl) {
1035 auto o_laddr = opin->get_key();
1036 auto o_len = opin->get_length();
1037 if (new_offset != 0 && o_len != new_offset + new_len) {
1038 return tm->remap_pin<TestBlock, 2>(
1039 t,
1040 std::move(opin),
1041 std::array{
1042 remap_entry(
1043 0,
1044 new_offset),
1045 remap_entry(
1046 new_offset + new_len,
1047 o_len - new_offset - new_len)
1048 }
1049 ).si_then([this, new_offset, new_len, o_laddr, &t, &bl](auto ret) {
1050 return tm->alloc_extent<TestBlock>(t, o_laddr + new_offset, new_len
1051 ).si_then([this, ret = std::move(ret), new_len,
1052 new_offset, o_laddr, &t, &bl](auto ext) mutable {
1053 ceph_assert(ret.size() == 2);
1054 auto iter = bl.cbegin();
1055 iter.copy(new_len, ext->get_bptr().c_str());
1056 auto r_laddr = o_laddr + new_offset + new_len;
1057 // old pins expired after alloc new extent, need to get it.
1058 return tm->get_pin(t, o_laddr
1059 ).si_then([this, &t, ext = std::move(ext), r_laddr](auto lpin) mutable {
1060 return tm->get_pin(t, r_laddr
1061 ).si_then([lpin = std::move(lpin), ext = std::move(ext)]
1062 (auto rpin) mutable {
1063 return _overwrite_pin_iertr::make_ready_future<
1064 std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>(
1065 std::make_tuple(
1066 std::move(lpin), std::move(ext), std::move(rpin)));
1067 });
1068 });
1069 });
1070 });
1071 } else if (new_offset == 0 && o_len != new_offset + new_len) {
1072 return tm->remap_pin<TestBlock, 1>(
1073 t,
1074 std::move(opin),
1075 std::array{
1076 remap_entry(
1077 new_offset + new_len,
1078 o_len - new_offset - new_len)
1079 }
1080 ).si_then([this, new_offset, new_len, o_laddr, &t, &bl](auto ret) {
1081 return tm->alloc_extent<TestBlock>(t, o_laddr + new_offset, new_len
1082 ).si_then([this, ret = std::move(ret), new_offset, new_len,
1083 o_laddr, &t, &bl](auto ext) mutable {
1084 ceph_assert(ret.size() == 1);
1085 auto iter = bl.cbegin();
1086 iter.copy(new_len, ext->get_bptr().c_str());
1087 auto r_laddr = o_laddr + new_offset + new_len;
1088 return tm->get_pin(t, r_laddr
1089 ).si_then([ext = std::move(ext)](auto rpin) mutable {
1090 return _overwrite_pin_iertr::make_ready_future<
1091 std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>(
1092 std::make_tuple(
1093 nullptr, std::move(ext), std::move(rpin)));
1094 });
1095 });
1096 });
1097 } else if (new_offset != 0 && o_len == new_offset + new_len) {
1098 return tm->remap_pin<TestBlock, 1>(
1099 t,
1100 std::move(opin),
1101 std::array{
1102 remap_entry(
1103 0,
1104 new_offset)
1105 }
1106 ).si_then([this, new_offset, new_len, o_laddr, &t, &bl](auto ret) {
1107 return tm->alloc_extent<TestBlock>(t, o_laddr + new_offset, new_len
1108 ).si_then([this, ret = std::move(ret), new_len, o_laddr, &t, &bl]
1109 (auto ext) mutable {
1110 ceph_assert(ret.size() == 1);
1111 auto iter = bl.cbegin();
1112 iter.copy(new_len, ext->get_bptr().c_str());
1113 return tm->get_pin(t, o_laddr
1114 ).si_then([ext = std::move(ext)](auto lpin) mutable {
1115 return _overwrite_pin_iertr::make_ready_future<
1116 std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>(
1117 std::make_tuple(
1118 std::move(lpin), std::move(ext), nullptr));
1119 });
1120 });
1121 });
1122 } else {
1123 ceph_abort("impossible");
1124 return _overwrite_pin_iertr::make_ready_future<
1125 std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>>(
1126 std::make_tuple(nullptr, nullptr, nullptr));
1127 }
1128 }
1129
1130 using overwrite_pin_ret = std::tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>;
1131 overwrite_pin_ret overwrite_pin(
1132 test_transaction_t &t,
1133 LBAMappingRef &&opin,
1134 extent_len_t new_offset,
1135 extent_len_t new_len,
1136 ceph::bufferlist &bl) {
1137 if (t.t->is_conflicted()) {
1138 return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
1139 nullptr, nullptr, nullptr);
1140 }
1141 auto o_laddr = opin->get_key();
1142 auto o_paddr = opin->get_val();
1143 auto o_len = opin->get_length();
1144 auto res = with_trans_intr(*(t.t), [&](auto& trans) {
1145 return _overwrite_pin(
1146 trans, std::move(opin), new_offset, new_len, bl);
1147 }).handle_error(crimson::ct_error::eagain::handle([] {
1148 return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
1149 nullptr, nullptr, nullptr);
1150 }), crimson::ct_error::pass_further_all{}).unsafe_get0();
1151 if (t.t->is_conflicted()) {
1152 return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
1153 nullptr, nullptr, nullptr);
1154 }
1155 test_mappings.dec_ref(o_laddr, t.mapping_delta);
1156 EXPECT_FALSE(test_mappings.contains(o_laddr, t.mapping_delta));
1157 auto &[lpin, ext, rpin] = res;
1158
1159 EXPECT_TRUE(ext);
1160 EXPECT_TRUE(lpin || rpin);
1161 EXPECT_TRUE(o_len > ext->get_length());
1162 if (lpin) {
1163 EXPECT_EQ(lpin->get_key(), o_laddr);
1164 EXPECT_EQ(lpin->get_val(), o_paddr);
1165 EXPECT_EQ(lpin->get_length(), new_offset);
1166 auto lext = try_read_pin(t, lpin->duplicate());
1167 if (lext) {
1168 test_mappings.alloced(lpin->get_key(), *lext, t.mapping_delta);
1169 EXPECT_TRUE(lext->is_exist_clean());
1170 } else {
1171 ceph_assert(t.t->is_conflicted());
1172 return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
1173 nullptr, nullptr, nullptr);
1174 }
1175 }
1176 EXPECT_EQ(ext->get_laddr(), o_laddr + new_offset);
1177 EXPECT_EQ(ext->get_length(), new_len);
1178 test_mappings.alloced(ext->get_laddr(), *ext, t.mapping_delta);
1179 if (rpin) {
1180 EXPECT_EQ(rpin->get_key(), o_laddr + new_offset + new_len);
1181 EXPECT_EQ(rpin->get_val(), o_paddr.add_offset(new_offset)
1182 .add_offset(new_len));
1183 EXPECT_EQ(rpin->get_length(), o_len - new_offset - new_len);
1184 auto rext = try_read_pin(t, rpin->duplicate());
1185 if (rext) {
1186 test_mappings.alloced(rpin->get_key(), *rext, t.mapping_delta);
1187 EXPECT_TRUE(rext->is_exist_clean());
1188 } else {
1189 ceph_assert(t.t->is_conflicted());
1190 return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
1191 nullptr, nullptr, nullptr);
1192 }
1193 }
1194 return std::make_tuple<LBAMappingRef, TestBlockRef, LBAMappingRef>(
1195 std::move(lpin), std::move(ext), std::move(rpin));
1196 }
1197
1198 void test_remap_pin() {
1199 run_async([this] {
1200 constexpr size_t l_offset = 32 << 10;
1201 constexpr size_t l_len = 32 << 10;
1202 constexpr size_t r_offset = 64 << 10;
1203 constexpr size_t r_len = 32 << 10;
1204 {
1205 auto t = create_transaction();
1206 auto lext = alloc_extent(t, l_offset, l_len);
1207 lext->set_contents('l', 0, 16 << 10);
1208 auto rext = alloc_extent(t, r_offset, r_len);
1209 rext->set_contents('r', 16 << 10, 16 << 10);
1210 submit_transaction(std::move(t));
1211 }
1212 {
1213 auto t = create_transaction();
1214 auto lpin = get_pin(t, l_offset);
1215 auto rpin = get_pin(t, r_offset);
1216 //split left
1217 auto pin1 = remap_pin(t, std::move(lpin), 0, 16 << 10);
1218 ASSERT_TRUE(pin1);
1219 auto pin2 = remap_pin(t, std::move(pin1), 0, 8 << 10);
1220 ASSERT_TRUE(pin2);
1221 auto pin3 = remap_pin(t, std::move(pin2), 0, 4 << 10);
1222 ASSERT_TRUE(pin3);
1223 auto lext = get_extent(t, pin3->get_key(), pin3->get_length());
1224 EXPECT_EQ('l', lext->get_bptr().c_str()[0]);
1225 auto mlext = mutate_extent(t, lext);
1226 ASSERT_TRUE(mlext->is_exist_mutation_pending());
1227 ASSERT_TRUE(mlext.get() == lext.get());
1228
1229 //split right
1230 auto pin4 = remap_pin(t, std::move(rpin), 16 << 10, 16 << 10);
1231 ASSERT_TRUE(pin4);
1232 auto pin5 = remap_pin(t, std::move(pin4), 8 << 10, 8 << 10);
1233 ASSERT_TRUE(pin5);
1234 auto pin6 = remap_pin(t, std::move(pin5), 4 << 10, 4 << 10);
1235 ASSERT_TRUE(pin6);
1236 auto rext = get_extent(t, pin6->get_key(), pin6->get_length());
1237 EXPECT_EQ('r', rext->get_bptr().c_str()[0]);
1238 auto mrext = mutate_extent(t, rext);
1239 ASSERT_TRUE(mrext->is_exist_mutation_pending());
1240 ASSERT_TRUE(mrext.get() == rext.get());
1241
1242 submit_transaction(std::move(t));
1243 check();
1244 }
1245 replay();
1246 check();
1247 });
1248 }
1249
1250 void test_overwrite_pin() {
1251 run_async([this] {
1252 constexpr size_t m_offset = 8 << 10;
1253 constexpr size_t m_len = 56 << 10;
1254 constexpr size_t l_offset = 64 << 10;
1255 constexpr size_t l_len = 64 << 10;
1256 constexpr size_t r_offset = 128 << 10;
1257 constexpr size_t r_len = 64 << 10;
1258 {
1259 auto t = create_transaction();
1260 auto m_ext = alloc_extent(t, m_offset, m_len);
1261 m_ext->set_contents('a', 0 << 10, 8 << 10);
1262 m_ext->set_contents('b', 16 << 10, 4 << 10);
1263 m_ext->set_contents('c', 36 << 10, 4 << 10);
1264 m_ext->set_contents('d', 52 << 10, 4 << 10);
1265
1266 auto l_ext = alloc_extent(t, l_offset, l_len);
1267 auto r_ext = alloc_extent(t, r_offset, r_len);
1268 submit_transaction(std::move(t));
1269 }
1270 {
1271 auto t = create_transaction();
1272 auto mpin = get_pin(t, m_offset);
1273 auto lpin = get_pin(t, l_offset);
1274 auto rpin = get_pin(t, r_offset);
1275
1276 bufferlist mbl1, mbl2, mbl3;
1277 mbl1.append(ceph::bufferptr(ceph::buffer::create(8 << 10, 0)));
1278 mbl2.append(ceph::bufferptr(ceph::buffer::create(16 << 10, 0)));
1279 mbl3.append(ceph::bufferptr(ceph::buffer::create(12 << 10, 0)));
1280 auto [mlp1, mext1, mrp1] = overwrite_pin(
1281 t, std::move(mpin), 8 << 10 , 8 << 10, mbl1);
1282 auto [mlp2, mext2, mrp2] = overwrite_pin(
1283 t, std::move(mrp1), 4 << 10 , 16 << 10, mbl2);
1284 auto [mlpin3, me3, mrpin3] = overwrite_pin(
1285 t, std::move(mrp2), 4 << 10 , 12 << 10, mbl3);
1286 auto mlext1 = get_extent(t, mlp1->get_key(), mlp1->get_length());
1287 auto mlext2 = get_extent(t, mlp2->get_key(), mlp2->get_length());
1288 auto mlext3 = get_extent(t, mlpin3->get_key(), mlpin3->get_length());
1289 auto mrext3 = get_extent(t, mrpin3->get_key(), mrpin3->get_length());
1290 EXPECT_EQ('a', mlext1->get_bptr().c_str()[0]);
1291 EXPECT_EQ('b', mlext2->get_bptr().c_str()[0]);
1292 EXPECT_EQ('c', mlext3->get_bptr().c_str()[0]);
1293 EXPECT_EQ('d', mrext3->get_bptr().c_str()[0]);
1294 auto mutate_mlext1 = mutate_extent(t, mlext1);
1295 auto mutate_mlext2 = mutate_extent(t, mlext2);
1296 auto mutate_mlext3 = mutate_extent(t, mlext3);
1297 auto mutate_mrext3 = mutate_extent(t, mrext3);
1298 ASSERT_TRUE(mutate_mlext1->is_exist_mutation_pending());
1299 ASSERT_TRUE(mutate_mlext2->is_exist_mutation_pending());
1300 ASSERT_TRUE(mutate_mlext3->is_exist_mutation_pending());
1301 ASSERT_TRUE(mutate_mrext3->is_exist_mutation_pending());
1302 ASSERT_TRUE(mutate_mlext1.get() == mlext1.get());
1303 ASSERT_TRUE(mutate_mlext2.get() == mlext2.get());
1304 ASSERT_TRUE(mutate_mlext3.get() == mlext3.get());
1305 ASSERT_TRUE(mutate_mrext3.get() == mrext3.get());
1306
1307 bufferlist lbl1, rbl1;
1308 lbl1.append(ceph::bufferptr(ceph::buffer::create(32 << 10, 0)));
1309 auto [llp1, lext1, lrp1] = overwrite_pin(
1310 t, std::move(lpin), 0 , 32 << 10, lbl1);
1311 EXPECT_FALSE(llp1);
1312 EXPECT_TRUE(lrp1);
1313 EXPECT_TRUE(lext1);
1314
1315 rbl1.append(ceph::bufferptr(ceph::buffer::create(32 << 10, 0)));
1316 auto [rlp1, rext1, rrp1] = overwrite_pin(
1317 t, std::move(rpin), 32 << 10 , 32 << 10, rbl1);
1318 EXPECT_TRUE(rlp1);
1319 EXPECT_TRUE(rext1);
1320 EXPECT_FALSE(rrp1);
1321
1322 submit_transaction(std::move(t));
1323 check();
1324 }
1325 replay();
1326 check();
1327 });
1328 }
1329
1330 void test_remap_pin_concurrent() {
1331 run_async([this] {
1332 constexpr unsigned REMAP_NUM = 32;
1333 constexpr size_t offset = 0;
1334 constexpr size_t length = 256 << 10;
1335 {
1336 auto t = create_transaction();
1337 auto extent = alloc_extent(t, offset, length);
1338 ASSERT_EQ(length, extent->get_length());
1339 submit_transaction(std::move(t));
1340 }
1341 int success = 0;
1342 int early_exit = 0;
1343 int conflicted = 0;
1344
1345 seastar::parallel_for_each(
1346 boost::make_counting_iterator(0u),
1347 boost::make_counting_iterator(REMAP_NUM),
1348 [&](auto) {
1349 return seastar::async([&] {
1350 uint32_t pieces = std::uniform_int_distribution<>(6, 31)(gen);
1351 std::set<uint32_t> split_points;
1352 for (uint32_t i = 0; i < pieces; i++) {
1353 auto p = std::uniform_int_distribution<>(1, 256)(gen);
1354 split_points.insert(p - p % 4);
1355 }
1356
1357 auto t = create_transaction();
1358 auto pin0 = try_get_pin(t, offset);
1359 if (!pin0 || pin0->get_length() != length) {
1360 early_exit++;
1361 return;
1362 }
1363
1364 auto last_pin = pin0->duplicate();
1365 ASSERT_TRUE(!split_points.empty());
1366 for (auto off : split_points) {
1367 if (off == 0 || off >= 255) {
1368 continue;
1369 }
1370 auto new_off = (off << 10) - last_pin->get_key();
1371 auto new_len = last_pin->get_length() - new_off;
1372 //always remap right extent at new split_point
1373 auto pin = remap_pin(t, std::move(last_pin), new_off, new_len);
1374 if (!pin) {
1375 conflicted++;
1376 return;
1377 }
1378 last_pin = pin->duplicate();
1379 }
1380 auto last_ext = try_get_extent(t, last_pin->get_key());
1381 if (last_ext) {
1382 auto last_ext1 = mutate_extent(t, last_ext);
1383 ASSERT_TRUE(last_ext1->is_exist_mutation_pending());
1384 } else {
1385 conflicted++;
1386 return;
1387 }
1388
1389 if (try_submit_transaction(std::move(t))) {
1390 success++;
1391 logger().info("transaction {} submit the transction",
1392 static_cast<void*>(t.t.get()));
1393 } else {
1394 conflicted++;
1395 }
1396 });
1397 }).handle_exception([](std::exception_ptr e) {
1398 logger().info("{}", e);
1399 }).get0();
1400 logger().info("test_remap_pin_concurrent: "
1401 "early_exit {} conflicted {} success {}",
1402 early_exit, conflicted, success);
1403 ASSERT_TRUE(success == 1);
1404 ASSERT_EQ(success + conflicted + early_exit, REMAP_NUM);
1405 replay();
1406 check();
1407 });
1408 }
1409
1410 void test_overwrite_pin_concurrent() {
1411 run_async([this] {
1412 constexpr unsigned REMAP_NUM = 32;
1413 constexpr size_t offset = 0;
1414 constexpr size_t length = 256 << 10;
1415 {
1416 auto t = create_transaction();
1417 auto extent = alloc_extent(t, offset, length);
1418 ASSERT_EQ(length, extent->get_length());
1419 submit_transaction(std::move(t));
1420 }
1421 int success = 0;
1422 int early_exit = 0;
1423 int conflicted = 0;
1424
1425 seastar::parallel_for_each(
1426 boost::make_counting_iterator(0u),
1427 boost::make_counting_iterator(REMAP_NUM),
1428 [&](auto) {
1429 return seastar::async([&] {
1430 uint32_t pieces = std::uniform_int_distribution<>(6, 31)(gen);
1431 if (pieces % 2 == 1) {
1432 pieces++;
1433 }
1434 std::list<uint32_t> split_points;
1435 for (uint32_t i = 0; i < pieces; i++) {
1436 auto p = std::uniform_int_distribution<>(1, 120)(gen);
1437 split_points.push_back(p - p % 4);
1438 }
1439 split_points.sort();
1440
1441 auto t = create_transaction();
1442 auto pin0 = try_get_pin(t, offset);
1443 if (!pin0 || pin0->get_length() != length) {
1444 early_exit++;
1445 return;
1446 }
1447
1448 auto empty_transaction = true;
1449 auto last_rpin = pin0->duplicate();
1450 ASSERT_TRUE(!split_points.empty());
1451 while(!split_points.empty()) {
1452 // new overwrite area: start_off ~ end_off
1453 auto start_off = split_points.front();
1454 split_points.pop_front();
1455 auto end_off = split_points.front();
1456 split_points.pop_front();
1457 ASSERT_TRUE(start_off <= end_off);
1458 if (((end_off << 10) == pin0->get_key() + pin0->get_length())
1459 || (start_off == end_off)) {
1460 if (split_points.empty() && empty_transaction) {
1461 early_exit++;
1462 return;
1463 }
1464 continue;
1465 }
1466 empty_transaction = false;
1467 auto new_off = (start_off << 10) - last_rpin->get_key();
1468 auto new_len = (end_off - start_off) << 10;
1469 bufferlist bl;
1470 bl.append(ceph::bufferptr(ceph::buffer::create(new_len, 0)));
1471 auto [lpin, ext, rpin] = overwrite_pin(
1472 t, last_rpin->duplicate(), new_off, new_len, bl);
1473 if (!ext) {
1474 conflicted++;
1475 return;
1476 }
1477 // lpin is nullptr might not cause by confliction,
1478 // it might just not exist.
1479 if (lpin) {
1480 auto lext = try_get_extent(t, lpin->get_key());
1481 if (!lext) {
1482 conflicted++;
1483 return;
1484 }
1485 if (get_random_contents() % 2 == 0) {
1486 auto lext1 = mutate_extent(t, lext);
1487 ASSERT_TRUE(lext1->is_exist_mutation_pending());
1488 }
1489 }
1490 ASSERT_TRUE(rpin);
1491 last_rpin = rpin->duplicate();
1492 }
1493 auto last_rext = try_get_extent(t, last_rpin->get_key());
1494 if (!last_rext) {
1495 conflicted++;
1496 return;
1497 }
1498 if (get_random_contents() % 2 == 0) {
1499 auto last_rext1 = mutate_extent(t, last_rext);
1500 ASSERT_TRUE(last_rext1->is_exist_mutation_pending());
1501 }
1502
1503 if (try_submit_transaction(std::move(t))) {
1504 success++;
1505 logger().info("transaction {} submit the transction",
1506 static_cast<void*>(t.t.get()));
1507 } else {
1508 conflicted++;
1509 }
1510 });
1511 }).handle_exception([](std::exception_ptr e) {
1512 logger().info("{}", e);
1513 }).get0();
1514 logger().info("test_overwrite_pin_concurrent: "
1515 "early_exit {} conflicted {} success {}",
1516 early_exit, conflicted, success);
1517 ASSERT_TRUE(success == 1 || early_exit == REMAP_NUM);
1518 ASSERT_EQ(success + conflicted + early_exit, REMAP_NUM);
1519 replay();
1520 check();
1521 });
1522 }
1523 };
1524
1525 struct tm_single_device_test_t :
1526 public transaction_manager_test_t {
1527
1528 tm_single_device_test_t() : transaction_manager_test_t(1, 0) {}
1529 };
1530
1531 struct tm_multi_device_test_t :
1532 public transaction_manager_test_t {
1533
1534 tm_multi_device_test_t() : transaction_manager_test_t(3, 0) {}
1535 };
1536
1537 struct tm_multi_tier_device_test_t :
1538 public transaction_manager_test_t {
1539
1540 tm_multi_tier_device_test_t() : transaction_manager_test_t(1, 2) {}
1541 };
1542
1543 TEST_P(tm_single_device_test_t, basic)
1544 {
1545 constexpr laddr_t SIZE = 4096;
1546 run_async([this] {
1547 constexpr laddr_t ADDR = 0xFF * SIZE;
1548 {
1549 auto t = create_transaction();
1550 auto extent = alloc_extent(
1551 t,
1552 ADDR,
1553 SIZE,
1554 'a');
1555 ASSERT_EQ(ADDR, extent->get_laddr());
1556 check_mappings(t);
1557 check();
1558 submit_transaction(std::move(t));
1559 check();
1560 }
1561 });
1562 }
1563
1564 TEST_P(tm_single_device_test_t, mutate)
1565 {
1566 constexpr laddr_t SIZE = 4096;
1567 run_async([this] {
1568 constexpr laddr_t ADDR = 0xFF * SIZE;
1569 {
1570 auto t = create_transaction();
1571 auto extent = alloc_extent(
1572 t,
1573 ADDR,
1574 SIZE,
1575 'a');
1576 ASSERT_EQ(ADDR, extent->get_laddr());
1577 check_mappings(t);
1578 check();
1579 submit_transaction(std::move(t));
1580 check();
1581 }
1582 ASSERT_TRUE(check_usage());
1583 replay();
1584 {
1585 auto t = create_transaction();
1586 auto ext = get_extent(
1587 t,
1588 ADDR,
1589 SIZE);
1590 auto mut = mutate_extent(t, ext);
1591 check_mappings(t);
1592 check();
1593 submit_transaction(std::move(t));
1594 check();
1595 }
1596 ASSERT_TRUE(check_usage());
1597 replay();
1598 check();
1599 });
1600 }
1601
1602 TEST_P(tm_single_device_test_t, allocate_lba_conflict)
1603 {
1604 constexpr laddr_t SIZE = 4096;
1605 run_async([this] {
1606 constexpr laddr_t ADDR = 0xFF * SIZE;
1607 constexpr laddr_t ADDR2 = 0xFE * SIZE;
1608 auto t = create_transaction();
1609 auto t2 = create_transaction();
1610
1611 // These should conflict as they should both modify the lba root
1612 auto extent = alloc_extent(
1613 t,
1614 ADDR,
1615 SIZE,
1616 'a');
1617 ASSERT_EQ(ADDR, extent->get_laddr());
1618 check_mappings(t);
1619 check();
1620
1621 auto extent2 = alloc_extent(
1622 t2,
1623 ADDR2,
1624 SIZE,
1625 'a');
1626 ASSERT_EQ(ADDR2, extent2->get_laddr());
1627 check_mappings(t2);
1628 extent2.reset();
1629
1630 submit_transaction(std::move(t2));
1631 submit_transaction_expect_conflict(std::move(t));
1632 });
1633 }
1634
1635 TEST_P(tm_single_device_test_t, mutate_lba_conflict)
1636 {
1637 constexpr laddr_t SIZE = 4096;
1638 run_async([this] {
1639 {
1640 auto t = create_transaction();
1641 for (unsigned i = 0; i < 300; ++i) {
1642 auto extent = alloc_extent(
1643 t,
1644 laddr_t(i * SIZE),
1645 SIZE);
1646 }
1647 check_mappings(t);
1648 submit_transaction(std::move(t));
1649 check();
1650 }
1651
1652 constexpr laddr_t ADDR = 150 * SIZE;
1653 {
1654 auto t = create_transaction();
1655 auto t2 = create_transaction();
1656
1657 mutate_addr(t, ADDR, SIZE);
1658 mutate_addr(t2, ADDR, SIZE);
1659
1660 submit_transaction(std::move(t));
1661 submit_transaction_expect_conflict(std::move(t2));
1662 }
1663 check();
1664
1665 {
1666 auto t = create_transaction();
1667 mutate_addr(t, ADDR, SIZE);
1668 submit_transaction(std::move(t));
1669 }
1670 check();
1671 });
1672 }
1673
1674 TEST_P(tm_single_device_test_t, concurrent_mutate_lba_no_conflict)
1675 {
1676 constexpr laddr_t SIZE = 4096;
1677 constexpr size_t NUM = 500;
1678 constexpr laddr_t addr = 0;
1679 constexpr laddr_t addr2 = SIZE * (NUM - 1);
1680 run_async([this] {
1681 {
1682 auto t = create_transaction();
1683 for (unsigned i = 0; i < NUM; ++i) {
1684 auto extent = alloc_extent(
1685 t,
1686 laddr_t(i * SIZE),
1687 SIZE);
1688 }
1689 submit_transaction(std::move(t));
1690 }
1691
1692 {
1693 auto t = create_transaction();
1694 auto t2 = create_transaction();
1695
1696 mutate_addr(t, addr, SIZE);
1697 mutate_addr(t2, addr2, SIZE);
1698
1699 submit_transaction(std::move(t));
1700 submit_transaction(std::move(t2));
1701 }
1702 check();
1703 });
1704 }
1705
1706 TEST_P(tm_single_device_test_t, create_remove_same_transaction)
1707 {
1708 constexpr laddr_t SIZE = 4096;
1709 run_async([this] {
1710 constexpr laddr_t ADDR = 0xFF * SIZE;
1711 {
1712 auto t = create_transaction();
1713 auto extent = alloc_extent(
1714 t,
1715 ADDR,
1716 SIZE,
1717 'a');
1718 ASSERT_EQ(ADDR, extent->get_laddr());
1719 check_mappings(t);
1720 dec_ref(t, ADDR);
1721 check_mappings(t);
1722
1723 extent = alloc_extent(
1724 t,
1725 ADDR,
1726 SIZE,
1727 'a');
1728
1729 submit_transaction(std::move(t));
1730 check();
1731 }
1732 replay();
1733 check();
1734 });
1735 }
1736
1737 TEST_P(tm_single_device_test_t, split_merge_read_same_transaction)
1738 {
1739 constexpr laddr_t SIZE = 4096;
1740 run_async([this] {
1741 {
1742 auto t = create_transaction();
1743 for (unsigned i = 0; i < 300; ++i) {
1744 auto extent = alloc_extent(
1745 t,
1746 laddr_t(i * SIZE),
1747 SIZE);
1748 }
1749 check_mappings(t);
1750 submit_transaction(std::move(t));
1751 check();
1752 }
1753 {
1754 auto t = create_transaction();
1755 for (unsigned i = 0; i < 240; ++i) {
1756 dec_ref(
1757 t,
1758 laddr_t(i * SIZE));
1759 }
1760 check_mappings(t);
1761 submit_transaction(std::move(t));
1762 check();
1763 }
1764 });
1765 }
1766
1767 TEST_P(tm_single_device_test_t, inc_dec_ref)
1768 {
1769 constexpr laddr_t SIZE = 4096;
1770 run_async([this] {
1771 constexpr laddr_t ADDR = 0xFF * SIZE;
1772 {
1773 auto t = create_transaction();
1774 auto extent = alloc_extent(
1775 t,
1776 ADDR,
1777 SIZE,
1778 'a');
1779 ASSERT_EQ(ADDR, extent->get_laddr());
1780 check_mappings(t);
1781 check();
1782 submit_transaction(std::move(t));
1783 check();
1784 }
1785 replay();
1786 {
1787 auto t = create_transaction();
1788 inc_ref(t, ADDR);
1789 check_mappings(t);
1790 check();
1791 submit_transaction(std::move(t));
1792 check();
1793 }
1794 {
1795 auto t = create_transaction();
1796 dec_ref(t, ADDR);
1797 check_mappings(t);
1798 check();
1799 submit_transaction(std::move(t));
1800 check();
1801 }
1802 replay();
1803 {
1804 auto t = create_transaction();
1805 dec_ref(t, ADDR);
1806 check_mappings(t);
1807 check();
1808 submit_transaction(std::move(t));
1809 check();
1810 }
1811 });
1812 }
1813
1814 TEST_P(tm_single_device_test_t, cause_lba_split)
1815 {
1816 constexpr laddr_t SIZE = 4096;
1817 run_async([this] {
1818 for (unsigned i = 0; i < 200; ++i) {
1819 auto t = create_transaction();
1820 auto extent = alloc_extent(
1821 t,
1822 i * SIZE,
1823 SIZE,
1824 (char)(i & 0xFF));
1825 ASSERT_EQ(i * SIZE, extent->get_laddr());
1826 submit_transaction(std::move(t));
1827 }
1828 check();
1829 });
1830 }
1831
1832 TEST_P(tm_single_device_test_t, random_writes)
1833 {
1834 constexpr size_t TOTAL = 4<<20;
1835 constexpr size_t BSIZE = 4<<10;
1836 constexpr size_t PADDING_SIZE = 256<<10;
1837 constexpr size_t BLOCKS = TOTAL / BSIZE;
1838 run_async([this] {
1839 for (unsigned i = 0; i < BLOCKS; ++i) {
1840 auto t = create_transaction();
1841 auto extent = alloc_extent(
1842 t,
1843 i * BSIZE,
1844 BSIZE);
1845 ASSERT_EQ(i * BSIZE, extent->get_laddr());
1846 submit_transaction(std::move(t));
1847 }
1848
1849 for (unsigned i = 0; i < 4; ++i) {
1850 for (unsigned j = 0; j < 65; ++j) {
1851 auto t = create_transaction();
1852 for (unsigned k = 0; k < 2; ++k) {
1853 auto ext = get_extent(
1854 t,
1855 get_random_laddr(BSIZE, TOTAL),
1856 BSIZE);
1857 auto mut = mutate_extent(t, ext);
1858 // pad out transaction
1859 auto padding = alloc_extent(
1860 t,
1861 TOTAL + (k * PADDING_SIZE),
1862 PADDING_SIZE);
1863 dec_ref(t, padding->get_laddr());
1864 }
1865 submit_transaction(std::move(t));
1866 }
1867 replay();
1868 logger().info("random_writes: {} checking", i);
1869 check();
1870 logger().info("random_writes: {} done replaying/checking", i);
1871 }
1872 });
1873 }
1874
1875 TEST_P(tm_single_device_test_t, find_hole_assert_trigger)
1876 {
1877 constexpr unsigned max = 10;
1878 constexpr size_t BSIZE = 4<<10;
1879 int num = 40;
1880 run([&, this] {
1881 return seastar::parallel_for_each(
1882 boost::make_counting_iterator(0u),
1883 boost::make_counting_iterator(max),
1884 [&, this](auto idx) {
1885 return allocate_sequentially(BSIZE, num);
1886 });
1887 });
1888 }
1889
1890 TEST_P(tm_single_device_test_t, remap_lazy_read)
1891 {
1892 constexpr laddr_t offset = 0;
1893 constexpr size_t length = 256 << 10;
1894 run_async([this, offset] {
1895 {
1896 auto t = create_transaction();
1897 auto extent = alloc_extent(
1898 t,
1899 offset,
1900 length,
1901 'a');
1902 ASSERT_EQ(offset, extent->get_laddr());
1903 check_mappings(t);
1904 submit_transaction(std::move(t));
1905 check();
1906 }
1907 replay();
1908 {
1909 auto t = create_transaction();
1910 auto pin = get_pin(t, offset);
1911 auto rpin = remap_pin(t, std::move(pin), 0, 128 << 10);
1912 check_mappings(t);
1913 submit_transaction(std::move(t));
1914 check();
1915 }
1916 replay();
1917 {
1918 auto t = create_transaction();
1919 auto pin = get_pin(t, offset);
1920 bufferlist bl;
1921 bl.append(ceph::bufferptr(ceph::buffer::create(64 << 10, 0)));
1922 auto [lpin, ext, rpin] = overwrite_pin(
1923 t, std::move(pin), 4 << 10 , 64 << 10, bl);
1924 check_mappings(t);
1925 submit_transaction(std::move(t));
1926 check();
1927 }
1928 replay();
1929 });
1930 }
1931
1932 TEST_P(tm_single_device_test_t, random_writes_concurrent)
1933 {
1934 test_random_writes_concurrent();
1935 }
1936
1937 TEST_P(tm_multi_device_test_t, random_writes_concurrent)
1938 {
1939 test_random_writes_concurrent();
1940 }
1941
1942 TEST_P(tm_multi_tier_device_test_t, evict)
1943 {
1944 test_evict();
1945 }
1946
1947 TEST_P(tm_single_device_test_t, parallel_extent_read)
1948 {
1949 test_parallel_extent_read();
1950 }
1951
1952 TEST_P(tm_single_device_test_t, test_remap_pin)
1953 {
1954 test_remap_pin();
1955 }
1956
1957 TEST_P(tm_single_device_test_t, test_overwrite_pin)
1958 {
1959 test_overwrite_pin();
1960 }
1961
1962 TEST_P(tm_single_device_test_t, test_remap_pin_concurrent)
1963 {
1964 test_remap_pin_concurrent();
1965 }
1966
1967 TEST_P(tm_single_device_test_t, test_overwrite_pin_concurrent)
1968 {
1969 test_overwrite_pin_concurrent();
1970 }
1971
1972 INSTANTIATE_TEST_SUITE_P(
1973 transaction_manager_test,
1974 tm_single_device_test_t,
1975 ::testing::Values (
1976 "segmented",
1977 "circularbounded"
1978 )
1979 );
1980
1981 INSTANTIATE_TEST_SUITE_P(
1982 transaction_manager_test,
1983 tm_multi_device_test_t,
1984 ::testing::Values (
1985 "segmented"
1986 )
1987 );
1988
1989 INSTANTIATE_TEST_SUITE_P(
1990 transaction_manager_test,
1991 tm_multi_tier_device_test_t,
1992 ::testing::Values (
1993 "segmented"
1994 )
1995 );