aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolay Borisov <nborisov@suse.com>2019-06-03 06:06:02 -0400
committerDavid Sterba <dsterba@suse.com>2019-07-01 07:35:02 -0400
commit1eaebb341d2b4183d0112b76e31ccff3e1fe3092 (patch)
tree91ab17f1a296d66d5707c5063f1a61084611ae0d
parent53460a4572585b508dc4cb6f09653ac50ba3fc49 (diff)
btrfs: Don't trim returned range based on input value in find_first_clear_extent_bit
Currently find_first_clear_extent_bit always returns a range whose starting value is >= passed 'start'. This implicit trimming behavior is somewhat subtle and an implementation detail. Instead, this patch modifies the function such that now it always returns the range which contains passed 'start' and has the given bits unset. This range could either be due to presence of existing records which contains 'start' but have the bits unset or because there are no records that contain the given starting offset. This patch also adds test cases which cover find_first_clear_extent_bit since they were missing up until now. Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Nikolay Borisov <nborisov@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/extent_io.c52
-rw-r--r--fs/btrfs/tests/extent-io-tests.c87
2 files changed, 133 insertions, 6 deletions
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 9591ecfaa57e..105b3d2e86f2 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -1537,8 +1537,8 @@ out:
1537} 1537}
1538 1538
1539/** 1539/**
1540 * find_first_clear_extent_bit - finds the first range that has @bits not set 1540 * find_first_clear_extent_bit - find the first range that has @bits not set.
1541 * and that starts after @start 1541 * This range could start before @start.
1542 * 1542 *
1543 * @tree - the tree to search 1543 * @tree - the tree to search
1544 * @start - the offset at/after which the found extent should start 1544 * @start - the offset at/after which the found extent should start
@@ -1578,12 +1578,52 @@ void find_first_clear_extent_bit(struct extent_io_tree *tree, u64 start,
1578 goto out; 1578 goto out;
1579 } 1579 }
1580 } 1580 }
1581 /*
1582 * At this point 'node' either contains 'start' or start is
1583 * before 'node'
1584 */
1581 state = rb_entry(node, struct extent_state, rb_node); 1585 state = rb_entry(node, struct extent_state, rb_node);
1582 if (in_range(start, state->start, state->end - state->start + 1) && 1586
1583 (state->state & bits)) { 1587 if (in_range(start, state->start, state->end - state->start + 1)) {
1584 start = state->end + 1; 1588 if (state->state & bits) {
1589 /*
1590 * |--range with bits sets--|
1591 * |
1592 * start
1593 */
1594 start = state->end + 1;
1595 } else {
1596 /*
1597 * 'start' falls within a range that doesn't
1598 * have the bits set, so take its start as
1599 * the beginning of the desired range
1600 *
1601 * |--range with bits cleared----|
1602 * |
1603 * start
1604 */
1605 *start_ret = state->start;
1606 break;
1607 }
1585 } else { 1608 } else {
1586 *start_ret = start; 1609 /*
1610 * |---prev range---|---hole/unset---|---node range---|
1611 * |
1612 * start
1613 *
1614 * or
1615 *
1616 * |---hole/unset--||--first node--|
1617 * 0 |
1618 * start
1619 */
1620 if (prev) {
1621 state = rb_entry(prev, struct extent_state,
1622 rb_node);
1623 *start_ret = state->end + 1;
1624 } else {
1625 *start_ret = 0;
1626 }
1587 break; 1627 break;
1588 } 1628 }
1589 } 1629 }
diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c
index 7bf4d5734dbe..f0ccfb6449d2 100644
--- a/fs/btrfs/tests/extent-io-tests.c
+++ b/fs/btrfs/tests/extent-io-tests.c
@@ -432,6 +432,89 @@ out:
432 return ret; 432 return ret;
433} 433}
434 434
435static int test_find_first_clear_extent_bit(void)
436{
437 struct extent_io_tree tree;
438 u64 start, end;
439
440 test_msg("running find_first_clear_extent_bit test");
441 extent_io_tree_init(NULL, &tree, IO_TREE_SELFTEST, NULL);
442
443 /*
444 * Set 1M-4M alloc/discard and 32M-64M thus leaving a hole between
445 * 4M-32M
446 */
447 set_extent_bits(&tree, SZ_1M, SZ_4M - 1,
448 CHUNK_TRIMMED | CHUNK_ALLOCATED);
449
450 find_first_clear_extent_bit(&tree, SZ_512K, &start, &end,
451 CHUNK_TRIMMED | CHUNK_ALLOCATED);
452
453 if (start != 0 || end != SZ_1M -1)
454 test_err("error finding beginning range: start %llu end %llu",
455 start, end);
456
457 /* Now add 32M-64M so that we have a hole between 4M-32M */
458 set_extent_bits(&tree, SZ_32M, SZ_64M - 1,
459 CHUNK_TRIMMED | CHUNK_ALLOCATED);
460
461 /*
462 * Request first hole starting at 12M, we should get 4M-32M
463 */
464 find_first_clear_extent_bit(&tree, 12 * SZ_1M, &start, &end,
465 CHUNK_TRIMMED | CHUNK_ALLOCATED);
466
467 if (start != SZ_4M || end != SZ_32M - 1)
468 test_err("error finding trimmed range: start %llu end %llu",
469 start, end);
470
471 /*
472 * Search in the middle of allocated range, should get the next one
473 * available, which happens to be unallocated -> 4M-32M
474 */
475 find_first_clear_extent_bit(&tree, SZ_2M, &start, &end,
476 CHUNK_TRIMMED | CHUNK_ALLOCATED);
477
478 if (start != SZ_4M || end != SZ_32M -1)
479 test_err("error finding next unalloc range: start %llu end %llu",
480 start, end);
481
482 /*
483 * Set 64M-72M with CHUNK_ALLOC flag, then search for CHUNK_TRIMMED flag
484 * being unset in this range, we should get the entry in range 64M-72M
485 */
486 set_extent_bits(&tree, SZ_64M, SZ_64M + SZ_8M - 1, CHUNK_ALLOCATED);
487 find_first_clear_extent_bit(&tree, SZ_64M + SZ_1M, &start, &end,
488 CHUNK_TRIMMED);
489
490 if (start != SZ_64M || end != SZ_64M + SZ_8M - 1)
491 test_err("error finding exact range: start %llu end %llu",
492 start, end);
493
494 find_first_clear_extent_bit(&tree, SZ_64M - SZ_8M, &start, &end,
495 CHUNK_TRIMMED);
496
497 /*
498 * Search in the middle of set range whose immediate neighbour doesn't
499 * have the bits set so it must be returned
500 */
501 if (start != SZ_64M || end != SZ_64M + SZ_8M - 1)
502 test_err("error finding next alloc range: start %llu end %llu",
503 start, end);
504
505 /*
506 * Search beyond any known range, shall return after last known range
507 * and end should be -1
508 */
509 find_first_clear_extent_bit(&tree, -1, &start, &end, CHUNK_TRIMMED);
510 if (start != SZ_64M + SZ_8M || end != -1)
511 test_err(
512 "error handling beyond end of range search: start %llu end %llu",
513 start, end);
514
515 return 0;
516}
517
435int btrfs_test_extent_io(u32 sectorsize, u32 nodesize) 518int btrfs_test_extent_io(u32 sectorsize, u32 nodesize)
436{ 519{
437 int ret; 520 int ret;
@@ -442,6 +525,10 @@ int btrfs_test_extent_io(u32 sectorsize, u32 nodesize)
442 if (ret) 525 if (ret)
443 goto out; 526 goto out;
444 527
528 ret = test_find_first_clear_extent_bit();
529 if (ret)
530 goto out;
531
445 ret = test_eb_bitmaps(sectorsize, nodesize); 532 ret = test_eb_bitmaps(sectorsize, nodesize);
446out: 533out:
447 return ret; 534 return ret;