]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/objectstore/BitAllocator_test.cc
bump version to 12.2.11-pve1
[ceph.git] / ceph / src / test / objectstore / BitAllocator_test.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Bitmap based in-memory allocator unit test cases.
5 * Author: Ramesh Chander, Ramesh.Chander@sandisk.com
6 */
7
8 #include "include/Context.h"
9 #include "os/bluestore/BitAllocator.h"
10 #include <stdio.h>
11 #include <assert.h>
12 #include <math.h>
13 #include <sstream>
14 #include <gtest/gtest.h>
15
16
17 //#define bmap_test_assert(x) ASSERT_EQ(true, (x))
18 #define bmap_test_assert(x) assert((x))
19 #define NUM_THREADS 16
20 #define MAX_BLOCKS (1024 * 1024 * 1)
21
22 TEST(BitAllocator, test_bmap_iter)
23 {
24 int num_items = 5;
25 int off = 2;
26
27 class BmapEntityTmp {
28 int64_t m_num;
29 int64_t m_len;
30 public:
31 void init(int index) {
32 m_num = index;
33 }
34 BmapEntityTmp() {
35
36 }
37 BmapEntityTmp(int num) {
38 m_num = num;
39 m_len = num;
40 }
41
42 int64_t get_index() {
43 return m_num;
44 }
45 bool is_allocated(int64_t s, int64_t num)
46 {
47 return true;
48 }
49 };
50 BmapEntityTmp *obj = NULL;
51 int i = 0;
52 mempool::bluestore_alloc::vector<BmapEntityTmp> *arr = new mempool::bluestore_alloc::vector<BmapEntityTmp>(num_items);
53 for (i = 0; i < num_items; i++) {
54 (*arr)[i].init(i);
55 }
56 BitMapEntityIter<BmapEntityTmp> iter = BitMapEntityIter<BmapEntityTmp>(arr, off, false);
57
58 i = off;
59 int count = 0;
60 int64_t last_idx = off;
61 while ((obj = iter.next())) {
62 bmap_test_assert(obj->get_index() == last_idx);
63 bmap_test_assert(obj->get_index() == i);
64 bmap_test_assert(obj == &(*arr)[i]);
65 last_idx = iter.index();
66 i++;
67 count++;
68 }
69 bmap_test_assert(i == num_items);
70 bmap_test_assert(count == num_items - off);
71
72 iter = BitMapEntityIter<BmapEntityTmp>(arr, off, true);
73
74 i = off;
75 last_idx = off;
76 count = 0;
77 while ((obj = iter.next())) {
78 bmap_test_assert(obj->get_index() == last_idx);
79 bmap_test_assert(obj->get_index() == i);
80 bmap_test_assert(obj == &(*arr)[i]);
81 last_idx = iter.index();
82
83 i = (i + 1) % num_items;
84 count++;
85 }
86 bmap_test_assert(i == off + 1);
87 bmap_test_assert(count == num_items + 1);
88
89 delete arr;
90
91 num_items = 4;
92 off = num_items - 1;
93
94 arr = new mempool::bluestore_alloc::vector<BmapEntityTmp>(num_items);
95 for (i = 0; i < num_items; i++) {
96 (*arr)[i].init(i);
97 }
98 iter = BitMapEntityIter<BmapEntityTmp>(arr, off, true);
99 i = off;
100 last_idx = off;
101 count = 0;
102 while ((obj = static_cast<BmapEntityTmp*>(iter.next()))) {
103 bmap_test_assert(obj->get_index() == last_idx);
104 bmap_test_assert(obj->get_index() == i);
105 bmap_test_assert(obj == &(*arr)[i]);
106 last_idx = iter.index();
107 i = (i + 1) % num_items;
108 count++;
109 }
110 bmap_test_assert(i == (off + 1)%num_items);
111 bmap_test_assert(count == num_items + 1);
112
113 delete arr;
114
115 /*
116 * BitMapArea Iter tests.
117 */
118 BitMapArea *area = nullptr;
119 std::vector<BitMapArea*> children;
120 children.reserve(num_items);
121 for (i = 0; i < num_items; i++) {
122 children.emplace_back(new BitMapAreaLeaf(
123 g_ceph_context,
124 BitMapArea::get_span_size(g_ceph_context), i, false));
125 }
126
127 off = 0;
128 BitMapAreaList *area_list = \
129 new BitMapAreaList(std::vector<BitMapArea*>(children));
130 BmapEntityListIter area_iter = BmapEntityListIter(
131 area_list, (int64_t) 0);
132 i = off;
133 last_idx = off;
134 count = 0;
135 while ((area = area_iter.next())) {
136 bmap_test_assert(area->get_index() == last_idx);
137 bmap_test_assert(area->get_index() == i);
138 bmap_test_assert(area == children[i]);
139 last_idx = area_iter.index();
140 i = (i + 1) % num_items;
141 count++;
142 }
143 bmap_test_assert(i == off);
144 bmap_test_assert(count == num_items);
145
146 off = 0;
147 area_iter = BmapEntityListIter(area_list, off, true);
148 i = off;
149 last_idx = off;
150 count = 0;
151 while ((area = area_iter.next())) {
152 bmap_test_assert(area->get_index() == last_idx);
153 bmap_test_assert(area->get_index() == i);
154 bmap_test_assert(area == children[i]);
155 last_idx = area_iter.index();
156 i = (i + 1) % num_items;
157 count++;
158 }
159 bmap_test_assert(i == (off + 1)%num_items);
160 bmap_test_assert(count == num_items + 1);
161
162 for (i = 0; i < num_items; i++)
163 delete children[i];
164
165 delete area_list;
166 }
167
168 TEST(BitAllocator, test_bmap_entry)
169 {
170 int i = 0;
171 int start = 0;
172 int64_t scanned = 0;
173 int64_t allocated = 0;
174 int size = BmapEntry::size();
175
176 BmapEntry *bmap = new BmapEntry(g_ceph_context, true);
177
178 // Clear bits one by one and check they are cleared
179 for (i = 0; i < size; i++) {
180 bmap->clear_bit(i);
181 bmap_test_assert(!bmap->check_bit(i));
182 }
183
184 // Set all bits again using set_bits
185 bmap->set_bits(0, size);
186
187 // clear 4 bits at a time and then check allocated
188 for (i = 0; i < size/4; i++) {
189 bmap->clear_bits(i * 4, 4);
190 bmap_test_assert(!bmap->is_allocated(i * 4, 4));
191 }
192
193 // set all bits again
194 bmap->set_bits(0, size);
195
196 // clear alternate bits, check and set those bits
197 for (i = 0; i < size/2; i++) {
198 bmap->clear_bit(i * 2 + 1);
199 bmap_test_assert(!bmap->check_bit(i * 2 + 1));
200 bmap_test_assert(bmap->check_n_set_bit(i * 2 + 1));
201 }
202
203 // free 1, 2 and size bits at a time and try to find n cont bits
204 for (i = 0; i < size / 4; i++) {
205 bmap->clear_bits(i * 2 + 1, i + 1);
206 bmap_test_assert(!bmap->check_bit(i * 2 + 1));
207 bmap_test_assert(bmap->find_n_cont_bits(i * 2 + 1, i + 1) ==
208 i + 1);
209 }
210
211 // free 1, 2 and size bits at a time and try to find any cont bits
212 for (i = 0; i < size / 4; i++) {
213 bmap->clear_bits(i * 2 + 1, i + 1);
214 bmap_test_assert(!bmap->is_allocated(i * 2 + 1, i + 1));
215 }
216
217 for (i = 0; i < size / 4; i++) {
218 bmap->clear_bits(i * 2 + 1, i + 1);
219 allocated = bmap->find_first_set_bits(i + 1, 0, &start, &scanned);
220
221 bmap_test_assert(allocated == i + 1);
222 bmap_test_assert(scanned == ((i * 2 + 1) + (i + 1)));
223 bmap_test_assert(start == i * 2 + 1);
224 bmap->set_bits(0, BmapEntry::size());
225
226 }
227
228
229
230 // Find few bits at end of bitmap and find those
231 bmap->clear_bits(0, 4);
232 bmap->clear_bits(BmapEntry::size() - 12, 5);
233 bmap->clear_bits(BmapEntry::size() - 6, 6);
234 allocated = bmap->find_first_set_bits(6, 0, &start, &scanned);
235
236 bmap_test_assert(allocated == 6);
237 bmap_test_assert(scanned == BmapEntry::size() - 6 + 6);
238 bmap_test_assert(start == BmapEntry::size() - 6);
239 bmap_test_assert(bmap->is_allocated(start, 6));
240
241 delete bmap;
242
243 {
244
245 bmap = new BmapEntry(g_ceph_context, false);
246 start = -1;
247 scanned = 0;
248 allocated = 0;
249 allocated = bmap->find_first_set_bits(1, 1, &start, &scanned);
250 bmap_test_assert(allocated == 1);
251 bmap_test_assert(start == 1);
252
253 allocated = bmap->find_first_set_bits(1, BmapEntry::size() - 2, &start, &scanned);
254 bmap_test_assert(allocated == 1);
255 bmap_test_assert(start == BmapEntry::size() - 2);
256
257 bmap->clear_bits(0, BmapEntry::size());
258 bmap->set_bits(0, BmapEntry::size() / 4);
259 allocated = bmap->find_first_set_bits(4, 2, &start, &scanned);
260 bmap_test_assert(allocated == 4);
261 bmap_test_assert(start == BmapEntry::size() / 4);
262 delete bmap;
263 }
264
265 bmap = new BmapEntry(g_ceph_context, false);
266 bmap->set_bits(4, BmapEntry::size() - 4);
267 bmap_test_assert(bmap->is_allocated(4, BmapEntry::size() - 4));
268 bmap_test_assert(!bmap->is_allocated(0, 4));
269 bmap->set_bits(0, 4);
270 bmap_test_assert(bmap->is_allocated(0, BmapEntry::size()));
271 delete bmap;
272
273 }
274
275 TEST(BitAllocator, test_zone_alloc)
276 {
277 int total_blocks = 1024;
278 int64_t allocated = 0;
279
280 BitMapZone *zone = new BitMapZone(g_ceph_context, total_blocks, 0);
281
282 // Allocate all blocks and see that it is allocating in order.
283 bool lock = zone->lock_excl_try();
284 bmap_test_assert(lock);
285
286 int64_t blk_size = 1024;
287 AllocExtentVector extents;
288 ExtentList *block_list = new ExtentList(&extents, blk_size);
289 allocated = zone->alloc_blocks_dis(zone->size() / 2, 1, 0, 0, block_list);
290 bmap_test_assert(allocated == zone->size() / 2);
291
292
293 {
294 int64_t blk_size = 1024;
295 AllocExtentVector extents;
296 ExtentList *block_list = new ExtentList(&extents, blk_size);
297
298 zone = new BitMapZone(g_ceph_context, total_blocks, 0);
299 lock = zone->lock_excl_try();
300 bmap_test_assert(lock);
301 for (int i = 0; i < zone->size(); i += 4) {
302 block_list->reset();
303 allocated = zone->alloc_blocks_dis(1, 1, i, 0, block_list);
304 bmap_test_assert(allocated == 1);
305 EXPECT_EQ(extents[0].offset, (uint64_t) i * blk_size);
306 }
307
308 for (int i = 0; i < zone->size(); i += 4) {
309 zone->free_blocks(i, 1);
310 }
311 }
312
313 /*
314 * Min alloc size cases.
315 */
316 {
317 int64_t blk_size = 1;
318 AllocExtentVector extents;
319
320 for (int i = 1; i <= total_blocks - BmapEntry::size(); i = i << 1) {
321 for (int64_t j = 0; j <= BmapEntry::size(); j = 1 << j) {
322 extents.clear();
323 ExtentList *block_list = new ExtentList(&extents, blk_size);
324 zone = new BitMapZone(g_ceph_context, total_blocks, 0);
325 lock = zone->lock_excl_try();
326 bmap_test_assert(lock);
327
328 block_list->reset();
329 int64_t need_blks = (((total_blocks - j) / i) * i);
330 allocated = zone->alloc_blocks_dis(need_blks, i, j, 0, block_list);
331 bmap_test_assert(allocated == need_blks);
332 bmap_test_assert(extents[0].offset == (uint64_t) j);
333 delete block_list;
334 delete zone;
335 }
336 }
337
338 //allocation in loop
339 {
340 extents.clear();
341 ExtentList *block_list = new ExtentList(&extents, blk_size);
342 zone = new BitMapZone(g_ceph_context, total_blocks, 0);
343 lock = zone->lock_excl_try();
344
345 for (int iter = 1; iter < 5; iter++) {
346 for (int i = 1; i <= total_blocks; i = i << 1) {
347 for (int j = 0; j < total_blocks; j +=i) {
348 bmap_test_assert(lock);
349 block_list->reset();
350 int64_t need_blks = i;
351 allocated = zone->alloc_blocks_dis(need_blks, i, 0, 0, block_list);
352 bmap_test_assert(allocated == need_blks);
353 bmap_test_assert(extents[0].offset == (uint64_t) j);
354 block_list->reset();
355 }
356 {
357 allocated = zone->alloc_blocks_dis(1, 1, 0, 0, block_list);
358 bmap_test_assert(allocated == 0);
359 block_list->reset();
360 }
361
362 for (int j = 0; j < total_blocks; j +=i) {
363 zone->free_blocks(j, i);
364 }
365 }
366 }
367 delete block_list;
368 delete zone;
369 }
370
371 {
372 extents.clear();
373 ExtentList *block_list = new ExtentList(&extents, blk_size);
374 zone = new BitMapZone(g_ceph_context, total_blocks, 0);
375 lock = zone->lock_excl_try();
376 bmap_test_assert(lock);
377
378 block_list->reset();
379 allocated = zone->alloc_blocks_dis(total_blocks + 1, total_blocks + 1, 0, 1024, block_list);
380 bmap_test_assert(allocated == 0);
381
382 block_list->reset();
383 allocated = zone->alloc_blocks_dis(total_blocks, total_blocks, 1, 1024, block_list);
384 bmap_test_assert(allocated == 0);
385
386 block_list->reset();
387 allocated = zone->alloc_blocks_dis(total_blocks, total_blocks, 0, 0, block_list);
388 bmap_test_assert(allocated == total_blocks);
389 bmap_test_assert(extents[0].offset == 0);
390
391 zone->free_blocks(extents[0].offset, allocated);
392
393 delete block_list;
394 extents.clear();
395 block_list = new ExtentList(&extents, blk_size, total_blocks / 4 * blk_size);
396 allocated = zone->alloc_blocks_dis(total_blocks, total_blocks / 4, 0, 0, block_list);
397 bmap_test_assert(allocated == total_blocks);
398 for (int i = 0; i < 4; i++) {
399 bmap_test_assert(extents[i].offset == (uint64_t) i * (total_blocks / 4));
400 }
401 }
402 }
403 }
404
405 TEST(BitAllocator, test_bmap_alloc)
406 {
407 const int max_iter = 3;
408
409 for (int round = 0; round < 3; round++) {
410 // Test zone of different sizes: 512, 1024, 2048
411 int64_t zone_size = 512ull << round;
412 ostringstream val;
413 val << zone_size;
414 g_conf->set_val("bluestore_bitmapallocator_blocks_per_zone", val.str());
415
416 // choose randomized span_size
417 int64_t span_size = 512ull << (rand() % 4);
418 val.str("");
419 val << span_size;
420 g_conf->set_val("bluestore_bitmapallocator_span_size", val.str());
421 g_ceph_context->_conf->apply_changes(NULL);
422
423 int64_t total_blocks = zone_size * 4;
424 int64_t allocated = 0;
425
426 BitAllocator *alloc = new BitAllocator(g_ceph_context, total_blocks,
427 zone_size, CONCURRENT);
428 int64_t alloc_size = 2;
429 for (int64_t iter = 0; iter < max_iter; iter++) {
430 for (int64_t j = 0; alloc_size <= total_blocks; j++) {
431 int64_t blk_size = 1024;
432 AllocExtentVector extents;
433 ExtentList *block_list = new ExtentList(&extents, blk_size, alloc_size);
434 for (int64_t i = 0; i < total_blocks; i += alloc_size) {
435 bmap_test_assert(alloc->reserve_blocks(alloc_size) == true);
436 allocated = alloc->alloc_blocks_dis_res(alloc_size, MIN(alloc_size, zone_size),
437 0, block_list);
438 bmap_test_assert(alloc_size == allocated);
439 bmap_test_assert(block_list->get_extent_count() ==
440 (alloc_size > zone_size? alloc_size / zone_size: 1));
441 bmap_test_assert(extents[0].offset == (uint64_t) i * blk_size);
442 bmap_test_assert((int64_t) extents[0].length ==
443 ((alloc_size > zone_size? zone_size: alloc_size) * blk_size));
444 block_list->reset();
445 }
446 for (int64_t i = 0; i < total_blocks; i += alloc_size) {
447 alloc->free_blocks(i, alloc_size);
448 }
449 alloc_size = 2 << j;
450 }
451 }
452
453 int64_t blk_size = 1024;
454 AllocExtentVector extents;
455
456 ExtentList *block_list = new ExtentList(&extents, blk_size);
457
458 ASSERT_EQ(alloc->reserve_blocks(alloc->size() / 2), true);
459 allocated = alloc->alloc_blocks_dis_res(alloc->size()/2, 1, 0, block_list);
460 ASSERT_EQ(alloc->size()/2, allocated);
461
462 block_list->reset();
463 ASSERT_EQ(alloc->reserve_blocks(1), true);
464 allocated = alloc->alloc_blocks_dis_res(1, 1, 0, block_list);
465 bmap_test_assert(allocated == 1);
466
467 alloc->free_blocks(alloc->size()/2, 1);
468
469 block_list->reset();
470 ASSERT_EQ(alloc->reserve_blocks(1), true);
471 allocated = alloc->alloc_blocks_dis_res(1, 1, 0, block_list);
472 bmap_test_assert(allocated == 1);
473
474 bmap_test_assert((int64_t) extents[0].offset == alloc->size()/2 * blk_size);
475
476 delete block_list;
477 delete alloc;
478
479 }
480
481 // restore to typical value
482 g_conf->set_val("bluestore_bitmapallocator_blocks_per_zone", "1024");
483 g_conf->set_val("bluestore_bitmapallocator_span_size", "1024");
484 g_ceph_context->_conf->apply_changes(NULL);
485 }
486
487 bool alloc_extents_max_block(BitAllocator *alloc,
488 int64_t max_alloc,
489 int64_t total_alloc)
490 {
491 int64_t blk_size = 1;
492 int64_t allocated = 0;
493 int64_t verified = 0;
494 int64_t count = 0;
495 AllocExtentVector extents;
496
497 ExtentList *block_list = new ExtentList(&extents, blk_size, max_alloc);
498
499 EXPECT_EQ(alloc->reserve_blocks(total_alloc), true);
500 allocated = alloc->alloc_blocks_dis_res(total_alloc, blk_size, 0, block_list);
501 EXPECT_EQ(allocated, total_alloc);
502
503 max_alloc = total_alloc > max_alloc? max_alloc: total_alloc;
504
505 for (auto &p: extents) {
506 count++;
507 EXPECT_EQ(p.length, max_alloc);
508 verified += p.length;
509 if (verified >= total_alloc) {
510 break;
511 }
512 }
513
514 EXPECT_EQ(total_alloc / max_alloc, count);
515 return true;
516 }
517
518 TEST(BitAllocator, test_bmap_alloc2)
519 {
520 int64_t total_blocks = 1024 * 4;
521 int64_t zone_size = 1024;
522 BitAllocator *alloc = new BitAllocator(g_ceph_context, total_blocks,
523 zone_size, CONCURRENT);
524
525 alloc_extents_max_block(alloc, 1, 16);
526 alloc_extents_max_block(alloc, 4, 16);
527 alloc_extents_max_block(alloc, 16, 16);
528 alloc_extents_max_block(alloc, 32, 16);
529 }
530
531 __thread int my_tid;
532
533 void
534 do_work_dis(BitAllocator *alloc)
535 {
536 int num_iters = 10;
537 int64_t alloced = 0;
538 int64_t num_blocks = alloc->size() / NUM_THREADS;
539
540 AllocExtentVector extents;
541 ExtentList *block_list = new ExtentList(&extents, 4096);
542
543 while (num_iters--) {
544 alloc_assert(alloc->reserve_blocks(num_blocks));
545 alloced = alloc->alloc_blocks_dis_res(num_blocks, 1, 0, block_list);
546 alloc_assert(alloced == num_blocks);
547
548 alloc_assert(alloc->is_allocated_dis(block_list, num_blocks));
549 alloc->free_blocks_dis(num_blocks, block_list);
550 block_list->reset();
551 }
552 }
553
554 int tid = 0;
555 static bool cont = true;
556
557 void *
558 worker(void *args)
559 {
560 my_tid = __sync_fetch_and_add(&tid, 1);
561 BitAllocator *alloc = (BitAllocator *) args;
562 printf("Starting thread %d", my_tid);
563 do_work_dis(alloc);
564
565 return NULL;
566 }
567
568 TEST(BitAllocator, test_bmap_alloc_concurrent)
569 {
570 int64_t total_blocks = MAX_BLOCKS;
571 int64_t zone_size = 1024;
572 pthread_t pthreads[NUM_THREADS] = {0};
573
574 bmap_test_assert(total_blocks <= MAX_BLOCKS);
575
576 BitAllocator *alloc = new BitAllocator(g_ceph_context, total_blocks,
577 zone_size, CONCURRENT);
578
579 for (int k = 0; k < 2; k++) {
580 cont = k;
581 printf("Spawning %d threads for parallel test. Mode Cont = %d.....\n", NUM_THREADS, cont);
582 for (int j = 0; j < NUM_THREADS; j++) {
583 if (pthread_create(&pthreads[j], NULL, worker, alloc)) {
584 printf("Unable to create worker thread.\n");
585 exit(0);
586 }
587 }
588
589 for (int j = 0; j < NUM_THREADS; j++) {
590 pthread_join(pthreads[j], NULL);
591 }
592 }
593 }