]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "test/crimson/gtest_seastar.h" | |
5 | ||
6 | #include "crimson/common/log.h" | |
7 | ||
8 | #include "crimson/os/seastore/journal.h" | |
9 | #include "crimson/os/seastore/cache.h" | |
10 | #include "crimson/os/seastore/segment_manager/ephemeral.h" | |
11 | #include "crimson/os/seastore/lba_manager/btree/btree_lba_manager.h" | |
12 | ||
13 | #include "test/crimson/seastore/test_block.h" | |
14 | ||
15 | namespace { | |
16 | [[maybe_unused]] seastar::logger& logger() { | |
17 | return crimson::get_logger(ceph_subsys_test); | |
18 | } | |
19 | } | |
20 | ||
21 | using namespace crimson; | |
22 | using namespace crimson::os; | |
23 | using namespace crimson::os::seastore; | |
24 | using namespace crimson::os::seastore::lba_manager; | |
25 | using namespace crimson::os::seastore::lba_manager::btree; | |
26 | ||
20effc67 | 27 | struct btree_test_base : |
1e59de90 | 28 | public seastar_test_suite_t, SegmentProvider, JournalTrimmer { |
20effc67 | 29 | |
f67539c2 | 30 | segment_manager::EphemeralSegmentManagerRef segment_manager; |
1e59de90 | 31 | SegmentManagerGroupRef sms; |
20effc67 | 32 | JournalRef journal; |
1e59de90 | 33 | ExtentPlacementManagerRef epm; |
20effc67 | 34 | CacheRef cache; |
f67539c2 | 35 | |
20effc67 | 36 | size_t block_size; |
f67539c2 | 37 | |
20effc67 TL |
38 | WritePipeline pipeline; |
39 | ||
40 | segment_id_t next; | |
41 | ||
1e59de90 TL |
42 | std::map<segment_id_t, segment_seq_t> segment_seqs; |
43 | std::map<segment_id_t, segment_type_t> segment_types; | |
44 | ||
45 | journal_seq_t dummy_tail; | |
46 | ||
47 | mutable segment_info_t tmp_info; | |
48 | ||
20effc67 | 49 | btree_test_base() = default; |
f67539c2 | 50 | |
1e59de90 TL |
51 | /* |
52 | * JournalTrimmer interfaces | |
53 | */ | |
54 | journal_seq_t get_journal_head() const final { return dummy_tail; } | |
55 | ||
56 | void set_journal_head(journal_seq_t) final {} | |
57 | ||
58 | journal_seq_t get_dirty_tail() const final { return dummy_tail; } | |
59 | ||
60 | journal_seq_t get_alloc_tail() const final { return dummy_tail; } | |
61 | ||
62 | void update_journal_tails(journal_seq_t, journal_seq_t) final {} | |
63 | ||
64 | bool try_reserve_inline_usage(std::size_t) final { return true; } | |
65 | ||
66 | void release_inline_usage(std::size_t) final {} | |
67 | ||
68 | std::size_t get_trim_size_per_cycle() const final { | |
69 | return 0; | |
70 | } | |
71 | ||
72 | /* | |
73 | * SegmentProvider interfaces | |
74 | */ | |
75 | const segment_info_t& get_seg_info(segment_id_t id) const final { | |
76 | tmp_info = {}; | |
77 | tmp_info.seq = segment_seqs.at(id); | |
78 | tmp_info.type = segment_types.at(id); | |
79 | return tmp_info; | |
80 | } | |
20effc67 | 81 | |
1e59de90 TL |
82 | segment_id_t allocate_segment( |
83 | segment_seq_t seq, | |
84 | segment_type_t type, | |
85 | data_category_t, | |
86 | rewrite_gen_t | |
87 | ) final { | |
20effc67 TL |
88 | auto ret = next; |
89 | next = segment_id_t{ | |
1e59de90 | 90 | segment_manager->get_device_id(), |
20effc67 | 91 | next.device_segment_id() + 1}; |
1e59de90 TL |
92 | segment_seqs[ret] = seq; |
93 | segment_types[ret] = type; | |
94 | return ret; | |
f67539c2 TL |
95 | } |
96 | ||
1e59de90 TL |
97 | void close_segment(segment_id_t) final {} |
98 | ||
99 | void update_segment_avail_bytes(segment_type_t, paddr_t) final {} | |
100 | ||
101 | void update_modify_time(segment_id_t, sea_time_point, std::size_t) final {} | |
102 | ||
103 | SegmentManagerGroup* get_segment_manager_group() final { return sms.get(); } | |
f67539c2 | 104 | |
20effc67 TL |
105 | virtual void complete_commit(Transaction &t) {} |
106 | seastar::future<> submit_transaction(TransactionRef t) | |
f67539c2 | 107 | { |
1e59de90 | 108 | auto record = cache->prepare_record(*t, JOURNAL_SEQ_NULL, JOURNAL_SEQ_NULL); |
20effc67 TL |
109 | return journal->submit_record(std::move(record), t->get_handle()).safe_then( |
110 | [this, t=std::move(t)](auto submit_result) mutable { | |
111 | cache->complete_commit( | |
112 | *t, | |
113 | submit_result.record_block_base, | |
114 | submit_result.write_result.start_seq); | |
115 | complete_commit(*t); | |
116 | }).handle_error(crimson::ct_error::assert_all{}); | |
f67539c2 TL |
117 | } |
118 | ||
20effc67 | 119 | virtual LBAManager::mkfs_ret test_structure_setup(Transaction &t) = 0; |
f67539c2 | 120 | seastar::future<> set_up_fut() final { |
20effc67 | 121 | segment_manager = segment_manager::create_test_ephemeral(); |
f67539c2 TL |
122 | return segment_manager->init( |
123 | ).safe_then([this] { | |
1e59de90 TL |
124 | return segment_manager->mkfs( |
125 | segment_manager::get_ephemeral_device_config(0, 1, 0)); | |
126 | }).safe_then([this] { | |
127 | sms.reset(new SegmentManagerGroup()); | |
128 | journal = journal::make_segmented(*this, *this); | |
129 | epm.reset(new ExtentPlacementManager()); | |
130 | cache.reset(new Cache(*epm)); | |
131 | ||
132 | block_size = segment_manager->get_block_size(); | |
133 | next = segment_id_t{segment_manager->get_device_id(), 0}; | |
134 | sms->add_segment_manager(segment_manager.get()); | |
135 | epm->test_init_no_background(segment_manager.get()); | |
136 | journal->set_write_pipeline(&pipeline); | |
137 | ||
138 | return journal->open_for_mkfs().discard_result(); | |
139 | }).safe_then([this] { | |
140 | dummy_tail = journal_seq_t{0, | |
141 | paddr_t::make_seg_paddr(segment_id_t(segment_manager->get_device_id(), 0), 0)}; | |
142 | return epm->open_for_write(); | |
143 | }).safe_then([this] { | |
f67539c2 | 144 | return seastar::do_with( |
20effc67 TL |
145 | cache->create_transaction( |
146 | Transaction::src_t::MUTATE, "test_set_up_fut", false), | |
147 | [this](auto &ref_t) { | |
148 | return with_trans_intr(*ref_t, [&](auto &t) { | |
149 | cache->init(); | |
150 | return cache->mkfs(t | |
151 | ).si_then([this, &t] { | |
152 | return test_structure_setup(t); | |
153 | }); | |
154 | }).safe_then([this, &ref_t] { | |
155 | return submit_transaction(std::move(ref_t)); | |
f67539c2 TL |
156 | }); |
157 | }); | |
158 | }).handle_error( | |
159 | crimson::ct_error::all_same_way([] { | |
160 | ceph_assert(0 == "error"); | |
161 | }) | |
162 | ); | |
163 | } | |
164 | ||
20effc67 | 165 | virtual void test_structure_reset() {} |
f67539c2 | 166 | seastar::future<> tear_down_fut() final { |
20effc67 | 167 | return cache->close( |
f67539c2 | 168 | ).safe_then([this] { |
20effc67 | 169 | return journal->close(); |
1e59de90 TL |
170 | }).safe_then([this] { |
171 | return epm->close(); | |
20effc67 TL |
172 | }).safe_then([this] { |
173 | test_structure_reset(); | |
174 | segment_manager.reset(); | |
1e59de90 | 175 | sms.reset(); |
20effc67 | 176 | journal.reset(); |
1e59de90 | 177 | epm.reset(); |
20effc67 | 178 | cache.reset(); |
f67539c2 TL |
179 | }).handle_error( |
180 | crimson::ct_error::all_same_way([] { | |
181 | ASSERT_FALSE("Unable to close"); | |
182 | }) | |
183 | ); | |
184 | } | |
20effc67 TL |
185 | }; |
186 | ||
187 | struct lba_btree_test : btree_test_base { | |
188 | std::map<laddr_t, lba_map_val_t> check; | |
189 | ||
190 | auto get_op_context(Transaction &t) { | |
1e59de90 | 191 | return op_context_t<laddr_t>{*cache, t}; |
20effc67 TL |
192 | } |
193 | ||
194 | LBAManager::mkfs_ret test_structure_setup(Transaction &t) final { | |
195 | return cache->get_root( | |
196 | t | |
197 | ).si_then([this, &t](RootBlockRef croot) { | |
198 | auto mut_croot = cache->duplicate_for_write( | |
199 | t, croot | |
200 | )->cast<RootBlock>(); | |
1e59de90 TL |
201 | mut_croot->root.lba_root = |
202 | LBABtree::mkfs(mut_croot, get_op_context(t)); | |
20effc67 TL |
203 | }); |
204 | } | |
205 | ||
20effc67 TL |
206 | template <typename F> |
207 | auto lba_btree_update(F &&f) { | |
208 | auto tref = cache->create_transaction( | |
209 | Transaction::src_t::MUTATE, "test_btree_update", false); | |
210 | auto &t = *tref; | |
211 | with_trans_intr( | |
212 | t, | |
213 | [this, tref=std::move(tref), f=std::forward<F>(f)](auto &t) mutable { | |
214 | return cache->get_root( | |
215 | t | |
1e59de90 | 216 | ).si_then([f=std::move(f), &t](RootBlockRef croot) { |
20effc67 | 217 | return seastar::do_with( |
1e59de90 TL |
218 | LBABtree(croot), |
219 | [f=std::move(f), &t](auto &btree) mutable { | |
20effc67 TL |
220 | return std::invoke( |
221 | std::move(f), btree, t | |
1e59de90 | 222 | ); |
20effc67 TL |
223 | }); |
224 | }).si_then([this, tref=std::move(tref)]() mutable { | |
225 | return submit_transaction(std::move(tref)); | |
226 | }); | |
227 | }).unsafe_get0(); | |
228 | } | |
229 | ||
230 | template <typename F> | |
231 | auto lba_btree_read(F &&f) { | |
232 | auto t = cache->create_transaction( | |
233 | Transaction::src_t::READ, "test_btree_read", false); | |
234 | return with_trans_intr( | |
235 | *t, | |
236 | [this, f=std::forward<F>(f)](auto &t) mutable { | |
237 | return cache->get_root( | |
238 | t | |
239 | ).si_then([f=std::move(f), &t](RootBlockRef croot) mutable { | |
240 | return seastar::do_with( | |
1e59de90 | 241 | LBABtree(croot), |
20effc67 TL |
242 | [f=std::move(f), &t](auto &btree) mutable { |
243 | return std::invoke( | |
244 | std::move(f), btree, t | |
245 | ); | |
246 | }); | |
247 | }); | |
248 | }).unsafe_get0(); | |
249 | } | |
250 | ||
251 | static auto get_map_val(extent_len_t len) { | |
aee94f69 | 252 | return lba_map_val_t{0, (pladdr_t)P_ADDR_NULL, len, 0}; |
20effc67 TL |
253 | } |
254 | ||
1e59de90 TL |
255 | device_off_t next_off = 0; |
256 | paddr_t get_paddr() { | |
257 | next_off += block_size; | |
258 | return make_fake_paddr(next_off); | |
259 | } | |
260 | ||
20effc67 TL |
261 | void insert(laddr_t addr, extent_len_t len) { |
262 | ceph_assert(check.count(addr) == 0); | |
263 | check.emplace(addr, get_map_val(len)); | |
1e59de90 TL |
264 | lba_btree_update([=, this](auto &btree, auto &t) { |
265 | auto extent = cache->alloc_new_extent<TestBlock>( | |
266 | t, | |
267 | TestBlock::SIZE, | |
268 | placement_hint_t::HOT, | |
269 | 0, | |
270 | get_paddr()); | |
20effc67 | 271 | return btree.insert( |
1e59de90 TL |
272 | get_op_context(t), addr, get_map_val(len), extent.get() |
273 | ).si_then([addr, extent](auto p){ | |
274 | auto& [iter, inserted] = p; | |
275 | assert(inserted); | |
276 | extent->set_laddr(addr); | |
277 | }); | |
20effc67 TL |
278 | }); |
279 | } | |
280 | ||
281 | void remove(laddr_t addr) { | |
282 | auto iter = check.find(addr); | |
283 | ceph_assert(iter != check.end()); | |
284 | auto len = iter->second.len; | |
285 | check.erase(iter++); | |
1e59de90 | 286 | lba_btree_update([=, this](auto &btree, auto &t) { |
20effc67 TL |
287 | return btree.lower_bound( |
288 | get_op_context(t), addr | |
289 | ).si_then([this, len, addr, &btree, &t](auto iter) { | |
290 | EXPECT_FALSE(iter.is_end()); | |
291 | EXPECT_TRUE(iter.get_key() == addr); | |
292 | EXPECT_TRUE(iter.get_val().len == len); | |
293 | return btree.remove( | |
294 | get_op_context(t), iter | |
295 | ); | |
296 | }); | |
297 | }); | |
298 | } | |
299 | ||
300 | void check_lower_bound(laddr_t addr) { | |
301 | auto iter = check.lower_bound(addr); | |
1e59de90 | 302 | auto result = lba_btree_read([=, this](auto &btree, auto &t) { |
20effc67 TL |
303 | return btree.lower_bound( |
304 | get_op_context(t), addr | |
305 | ).si_then([](auto iter) | |
306 | -> std::optional<std::pair<const laddr_t, const lba_map_val_t>> { | |
307 | if (iter.is_end()) { | |
308 | return std::nullopt; | |
309 | } else { | |
310 | return std::make_optional( | |
311 | std::make_pair(iter.get_key(), iter.get_val())); | |
312 | } | |
313 | }); | |
314 | }); | |
315 | if (iter == check.end()) { | |
316 | EXPECT_FALSE(result); | |
317 | } else { | |
318 | EXPECT_TRUE(result); | |
319 | decltype(result) to_check = *iter; | |
320 | EXPECT_EQ(to_check, *result); | |
321 | } | |
322 | } | |
323 | }; | |
324 | ||
325 | TEST_F(lba_btree_test, basic) | |
326 | { | |
327 | run_async([this] { | |
328 | constexpr unsigned total = 16<<10; | |
329 | for (unsigned i = 0; i < total; i += 16) { | |
330 | insert(i, 8); | |
331 | } | |
332 | ||
333 | for (unsigned i = 0; i < total; i += 16) { | |
334 | check_lower_bound(i); | |
335 | check_lower_bound(i + 4); | |
336 | check_lower_bound(i + 8); | |
337 | check_lower_bound(i + 12); | |
338 | } | |
339 | }); | |
340 | } | |
341 | ||
342 | struct btree_lba_manager_test : btree_test_base { | |
343 | BtreeLBAManagerRef lba_manager; | |
344 | ||
345 | btree_lba_manager_test() = default; | |
346 | ||
1e59de90 | 347 | void complete_commit(Transaction &t) final {} |
20effc67 TL |
348 | |
349 | LBAManager::mkfs_ret test_structure_setup(Transaction &t) final { | |
1e59de90 | 350 | lba_manager.reset(new BtreeLBAManager(*cache)); |
20effc67 TL |
351 | return lba_manager->mkfs(t); |
352 | } | |
f67539c2 | 353 | |
20effc67 TL |
354 | void test_structure_reset() final { |
355 | lba_manager.reset(); | |
356 | } | |
f67539c2 TL |
357 | |
358 | struct test_extent_t { | |
359 | paddr_t addr; | |
360 | size_t len = 0; | |
361 | unsigned refcount = 0; | |
362 | }; | |
363 | using test_lba_mapping_t = std::map<laddr_t, test_extent_t>; | |
364 | test_lba_mapping_t test_lba_mappings; | |
365 | struct test_transaction_t { | |
366 | TransactionRef t; | |
367 | test_lba_mapping_t mappings; | |
368 | }; | |
369 | ||
20effc67 | 370 | auto create_transaction(bool create_fake_extent=true) { |
f67539c2 | 371 | auto t = test_transaction_t{ |
20effc67 TL |
372 | cache->create_transaction( |
373 | Transaction::src_t::MUTATE, "test_mutate_lba", false), | |
f67539c2 TL |
374 | test_lba_mappings |
375 | }; | |
20effc67 | 376 | if (create_fake_extent) { |
1e59de90 TL |
377 | cache->alloc_new_extent<TestBlockPhysical>( |
378 | *t.t, | |
379 | TestBlockPhysical::SIZE, | |
380 | placement_hint_t::HOT, | |
381 | 0); | |
20effc67 | 382 | }; |
f67539c2 TL |
383 | return t; |
384 | } | |
385 | ||
386 | auto create_weak_transaction() { | |
387 | auto t = test_transaction_t{ | |
20effc67 TL |
388 | cache->create_transaction( |
389 | Transaction::src_t::READ, "test_read_weak", true), | |
f67539c2 TL |
390 | test_lba_mappings |
391 | }; | |
392 | return t; | |
393 | } | |
394 | ||
395 | void submit_test_transaction(test_transaction_t t) { | |
20effc67 | 396 | submit_transaction(std::move(t.t)).get(); |
f67539c2 TL |
397 | test_lba_mappings.swap(t.mappings); |
398 | } | |
399 | ||
400 | auto get_overlap(test_transaction_t &t, laddr_t addr, size_t len) { | |
401 | auto bottom = t.mappings.upper_bound(addr); | |
402 | if (bottom != t.mappings.begin()) | |
403 | --bottom; | |
404 | if (bottom != t.mappings.end() && | |
405 | bottom->first + bottom->second.len <= addr) | |
406 | ++bottom; | |
407 | ||
408 | auto top = t.mappings.lower_bound(addr + len); | |
409 | return std::make_pair( | |
410 | bottom, | |
411 | top | |
412 | ); | |
413 | } | |
414 | ||
1e59de90 | 415 | device_off_t next_off = 0; |
f67539c2 TL |
416 | paddr_t get_paddr() { |
417 | next_off += block_size; | |
418 | return make_fake_paddr(next_off); | |
419 | } | |
420 | ||
421 | auto alloc_mapping( | |
422 | test_transaction_t &t, | |
423 | laddr_t hint, | |
1e59de90 | 424 | size_t len) { |
20effc67 TL |
425 | auto ret = with_trans_intr( |
426 | *t.t, | |
1e59de90 TL |
427 | [=, this](auto &t) { |
428 | auto extent = cache->alloc_new_extent<TestBlock>( | |
429 | t, | |
430 | TestBlock::SIZE, | |
431 | placement_hint_t::HOT, | |
432 | 0, | |
433 | get_paddr()); | |
434 | return lba_manager->alloc_extent( | |
aee94f69 | 435 | t, hint, len, extent->get_paddr(), *extent); |
20effc67 | 436 | }).unsafe_get0(); |
f67539c2 TL |
437 | logger().debug("alloc'd: {}", *ret); |
438 | EXPECT_EQ(len, ret->get_length()); | |
1e59de90 | 439 | auto [b, e] = get_overlap(t, ret->get_key(), len); |
f67539c2 TL |
440 | EXPECT_EQ(b, e); |
441 | t.mappings.emplace( | |
442 | std::make_pair( | |
1e59de90 | 443 | ret->get_key(), |
f67539c2 | 444 | test_extent_t{ |
1e59de90 | 445 | ret->get_val(), |
f67539c2 TL |
446 | ret->get_length(), |
447 | 1 | |
448 | } | |
449 | )); | |
450 | return ret; | |
451 | } | |
452 | ||
f67539c2 TL |
453 | auto decref_mapping( |
454 | test_transaction_t &t, | |
455 | laddr_t addr) { | |
456 | return decref_mapping(t, t.mappings.find(addr)); | |
457 | } | |
458 | ||
459 | void decref_mapping( | |
460 | test_transaction_t &t, | |
461 | test_lba_mapping_t::iterator target) { | |
462 | ceph_assert(target != t.mappings.end()); | |
463 | ceph_assert(target->second.refcount > 0); | |
464 | target->second.refcount--; | |
465 | ||
1e59de90 | 466 | (void) with_trans_intr( |
f67539c2 | 467 | *t.t, |
1e59de90 | 468 | [=, this](auto &t) { |
20effc67 TL |
469 | return lba_manager->decref_extent( |
470 | t, | |
aee94f69 TL |
471 | target->first, |
472 | true | |
1e59de90 TL |
473 | ).si_then([this, &t, target](auto result) { |
474 | EXPECT_EQ(result.refcount, target->second.refcount); | |
475 | if (result.refcount == 0) { | |
aee94f69 TL |
476 | return cache->retire_extent_addr( |
477 | t, result.addr.get_paddr(), result.length); | |
1e59de90 TL |
478 | } |
479 | return Cache::retire_extent_iertr::now(); | |
480 | }); | |
481 | }).unsafe_get0(); | |
f67539c2 TL |
482 | if (target->second.refcount == 0) { |
483 | t.mappings.erase(target); | |
484 | } | |
485 | } | |
486 | ||
487 | auto incref_mapping( | |
488 | test_transaction_t &t, | |
489 | laddr_t addr) { | |
490 | return incref_mapping(t, t.mappings.find(addr)); | |
491 | } | |
492 | ||
493 | void incref_mapping( | |
494 | test_transaction_t &t, | |
495 | test_lba_mapping_t::iterator target) { | |
496 | ceph_assert(target->second.refcount > 0); | |
497 | target->second.refcount++; | |
20effc67 | 498 | auto refcnt = with_trans_intr( |
f67539c2 | 499 | *t.t, |
1e59de90 | 500 | [=, this](auto &t) { |
20effc67 TL |
501 | return lba_manager->incref_extent( |
502 | t, | |
503 | target->first); | |
504 | }).unsafe_get0().refcount; | |
f67539c2 TL |
505 | EXPECT_EQ(refcnt, target->second.refcount); |
506 | } | |
507 | ||
508 | std::vector<laddr_t> get_mapped_addresses() { | |
509 | std::vector<laddr_t> addresses; | |
510 | addresses.reserve(test_lba_mappings.size()); | |
511 | for (auto &i: test_lba_mappings) { | |
512 | addresses.push_back(i.first); | |
513 | } | |
514 | return addresses; | |
515 | } | |
516 | ||
517 | std::vector<laddr_t> get_mapped_addresses(test_transaction_t &t) { | |
518 | std::vector<laddr_t> addresses; | |
519 | addresses.reserve(t.mappings.size()); | |
520 | for (auto &i: t.mappings) { | |
521 | addresses.push_back(i.first); | |
522 | } | |
523 | return addresses; | |
524 | } | |
525 | ||
526 | void check_mappings() { | |
527 | auto t = create_transaction(); | |
528 | check_mappings(t); | |
529 | } | |
530 | ||
531 | void check_mappings(test_transaction_t &t) { | |
1e59de90 TL |
532 | (void)with_trans_intr( |
533 | *t.t, | |
534 | [=, this](auto &t) { | |
535 | return lba_manager->check_child_trackers(t); | |
536 | }).unsafe_get0(); | |
f67539c2 | 537 | for (auto &&i: t.mappings) { |
20effc67 TL |
538 | auto laddr = i.first; |
539 | auto len = i.second.len; | |
540 | ||
541 | auto ret_list = with_trans_intr( | |
542 | *t.t, | |
1e59de90 | 543 | [=, this](auto &t) { |
20effc67 TL |
544 | return lba_manager->get_mappings( |
545 | t, laddr, len); | |
546 | }).unsafe_get0(); | |
f67539c2 TL |
547 | EXPECT_EQ(ret_list.size(), 1); |
548 | auto &ret = *ret_list.begin(); | |
1e59de90 TL |
549 | EXPECT_EQ(i.second.addr, ret->get_val()); |
550 | EXPECT_EQ(laddr, ret->get_key()); | |
20effc67 TL |
551 | EXPECT_EQ(len, ret->get_length()); |
552 | ||
553 | auto ret_pin = with_trans_intr( | |
554 | *t.t, | |
1e59de90 | 555 | [=, this](auto &t) { |
20effc67 TL |
556 | return lba_manager->get_mapping( |
557 | t, laddr); | |
558 | }).unsafe_get0(); | |
1e59de90 TL |
559 | EXPECT_EQ(i.second.addr, ret_pin->get_val()); |
560 | EXPECT_EQ(laddr, ret_pin->get_key()); | |
20effc67 | 561 | EXPECT_EQ(len, ret_pin->get_length()); |
f67539c2 | 562 | } |
20effc67 | 563 | with_trans_intr( |
f67539c2 | 564 | *t.t, |
1e59de90 | 565 | [=, &t, this](auto &) { |
20effc67 TL |
566 | return lba_manager->scan_mappings( |
567 | *t.t, | |
568 | 0, | |
569 | L_ADDR_MAX, | |
570 | [iter=t.mappings.begin(), &t](auto l, auto p, auto len) mutable { | |
571 | EXPECT_NE(iter, t.mappings.end()); | |
572 | EXPECT_EQ(l, iter->first); | |
573 | EXPECT_EQ(p, iter->second.addr); | |
574 | EXPECT_EQ(len, iter->second.len); | |
575 | ++iter; | |
576 | }); | |
f67539c2 TL |
577 | }).unsafe_get(); |
578 | } | |
579 | }; | |
580 | ||
581 | TEST_F(btree_lba_manager_test, basic) | |
582 | { | |
583 | run_async([this] { | |
584 | laddr_t laddr = 0x12345678 * block_size; | |
585 | { | |
586 | // write initial mapping | |
587 | auto t = create_transaction(); | |
588 | check_mappings(t); // check in progress transaction sees mapping | |
589 | check_mappings(); // check concurrent does not | |
1e59de90 | 590 | auto ret = alloc_mapping(t, laddr, block_size); |
f67539c2 TL |
591 | submit_test_transaction(std::move(t)); |
592 | } | |
593 | check_mappings(); // check new transaction post commit sees it | |
594 | }); | |
595 | } | |
596 | ||
597 | TEST_F(btree_lba_manager_test, force_split) | |
598 | { | |
599 | run_async([this] { | |
600 | for (unsigned i = 0; i < 40; ++i) { | |
601 | auto t = create_transaction(); | |
602 | logger().debug("opened transaction"); | |
603 | for (unsigned j = 0; j < 5; ++j) { | |
1e59de90 | 604 | auto ret = alloc_mapping(t, 0, block_size); |
f67539c2 TL |
605 | if ((i % 10 == 0) && (j == 3)) { |
606 | check_mappings(t); | |
607 | check_mappings(); | |
608 | } | |
609 | } | |
610 | logger().debug("submitting transaction"); | |
611 | submit_test_transaction(std::move(t)); | |
612 | check_mappings(); | |
613 | } | |
614 | }); | |
615 | } | |
616 | ||
617 | TEST_F(btree_lba_manager_test, force_split_merge) | |
618 | { | |
619 | run_async([this] { | |
620 | for (unsigned i = 0; i < 80; ++i) { | |
621 | auto t = create_transaction(); | |
622 | logger().debug("opened transaction"); | |
623 | for (unsigned j = 0; j < 5; ++j) { | |
1e59de90 | 624 | auto ret = alloc_mapping(t, 0, block_size); |
f67539c2 TL |
625 | // just to speed things up a bit |
626 | if ((i % 100 == 0) && (j == 3)) { | |
627 | check_mappings(t); | |
628 | check_mappings(); | |
629 | } | |
1e59de90 TL |
630 | incref_mapping(t, ret->get_key()); |
631 | decref_mapping(t, ret->get_key()); | |
f67539c2 TL |
632 | } |
633 | logger().debug("submitting transaction"); | |
634 | submit_test_transaction(std::move(t)); | |
635 | if (i % 50 == 0) { | |
636 | check_mappings(); | |
637 | } | |
638 | } | |
639 | { | |
640 | auto addresses = get_mapped_addresses(); | |
641 | auto t = create_transaction(); | |
642 | for (unsigned i = 0; i != addresses.size(); ++i) { | |
643 | if (i % 2 == 0) { | |
644 | incref_mapping(t, addresses[i]); | |
645 | decref_mapping(t, addresses[i]); | |
646 | decref_mapping(t, addresses[i]); | |
647 | } | |
648 | logger().debug("submitting transaction"); | |
649 | if (i % 7 == 0) { | |
650 | submit_test_transaction(std::move(t)); | |
651 | t = create_transaction(); | |
652 | } | |
653 | if (i % 13 == 0) { | |
654 | check_mappings(); | |
655 | check_mappings(t); | |
656 | } | |
657 | } | |
658 | submit_test_transaction(std::move(t)); | |
659 | } | |
660 | { | |
661 | auto addresses = get_mapped_addresses(); | |
662 | auto t = create_transaction(); | |
663 | for (unsigned i = 0; i != addresses.size(); ++i) { | |
664 | incref_mapping(t, addresses[i]); | |
665 | decref_mapping(t, addresses[i]); | |
666 | decref_mapping(t, addresses[i]); | |
667 | } | |
668 | check_mappings(t); | |
669 | submit_test_transaction(std::move(t)); | |
670 | check_mappings(); | |
671 | } | |
672 | }); | |
673 | } | |
674 | ||
675 | TEST_F(btree_lba_manager_test, single_transaction_split_merge) | |
676 | { | |
677 | run_async([this] { | |
678 | { | |
679 | auto t = create_transaction(); | |
20effc67 | 680 | for (unsigned i = 0; i < 400; ++i) { |
1e59de90 | 681 | alloc_mapping(t, 0, block_size); |
f67539c2 TL |
682 | } |
683 | check_mappings(t); | |
684 | submit_test_transaction(std::move(t)); | |
685 | } | |
686 | check_mappings(); | |
687 | ||
688 | { | |
689 | auto addresses = get_mapped_addresses(); | |
690 | auto t = create_transaction(); | |
691 | for (unsigned i = 0; i != addresses.size(); ++i) { | |
692 | if (i % 4 != 0) { | |
693 | decref_mapping(t, addresses[i]); | |
694 | } | |
695 | } | |
696 | check_mappings(t); | |
697 | submit_test_transaction(std::move(t)); | |
698 | } | |
699 | check_mappings(); | |
700 | ||
701 | { | |
702 | auto t = create_transaction(); | |
703 | for (unsigned i = 0; i < 600; ++i) { | |
1e59de90 | 704 | alloc_mapping(t, 0, block_size); |
f67539c2 TL |
705 | } |
706 | auto addresses = get_mapped_addresses(t); | |
707 | for (unsigned i = 0; i != addresses.size(); ++i) { | |
708 | decref_mapping(t, addresses[i]); | |
709 | } | |
710 | check_mappings(t); | |
711 | submit_test_transaction(std::move(t)); | |
712 | } | |
713 | check_mappings(); | |
714 | }); | |
715 | } | |
20effc67 TL |
716 | |
717 | TEST_F(btree_lba_manager_test, split_merge_multi) | |
718 | { | |
719 | run_async([this] { | |
720 | auto iterate = [&](auto f) { | |
721 | for (uint64_t i = 0; i < (1<<10); ++i) { | |
722 | auto t = create_transaction(false); | |
723 | logger().debug("opened transaction"); | |
724 | for (unsigned j = 0; j < 5; ++j) { | |
725 | f(t, (i * 5) + j); | |
726 | } | |
727 | logger().debug("submitting transaction"); | |
728 | submit_test_transaction(std::move(t)); | |
729 | } | |
730 | }; | |
731 | iterate([&](auto &t, auto idx) { | |
1e59de90 | 732 | alloc_mapping(t, idx * block_size, block_size); |
20effc67 TL |
733 | }); |
734 | check_mappings(); | |
735 | iterate([&](auto &t, auto idx) { | |
736 | if ((idx % 32) > 0) { | |
737 | decref_mapping(t, idx * block_size); | |
738 | } | |
739 | }); | |
740 | check_mappings(); | |
741 | iterate([&](auto &t, auto idx) { | |
742 | if ((idx % 32) > 0) { | |
1e59de90 | 743 | alloc_mapping(t, idx * block_size, block_size); |
20effc67 TL |
744 | } |
745 | }); | |
746 | check_mappings(); | |
747 | iterate([&](auto &t, auto idx) { | |
748 | decref_mapping(t, idx * block_size); | |
749 | }); | |
750 | check_mappings(); | |
751 | }); | |
752 | } |