Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dm btree: improve btree residency

This commit improves the residency of btrees built in the metadata for
dm-thin and dm-cache.

When inserting a new entry into a full btree node the current code
splits the node into two. This can result in very many half full nodes,
particularly if the insertions are occurring in an ascending order (as
happens in dm-thin with large writes).

With this commit, when we insert into a full node we first try and move
some entries to a neighbouring node that has space, failing that it
tries to split two neighbouring nodes into three.

Results are given below. 'Residency' is how full nodes are on average
as a percentage. Average instruction counts for the operations
are given to show the extra processing has little overhead.

+--------------------------+--------------------------+
| Before | After |
+------------+-----------+-----------+--------------+-----------+--------------+
| Test | Phase | Residency | Instructions | Residency | Instructions |
+------------+-----------+-----------+--------------+-----------+--------------+
| Ascending | insert | 50 | 1876 | 96 | 1930 |
| | overwrite | 50 | 1789 | 96 | 1746 |
| | lookup | 50 | 778 | 96 | 778 |
| Descending | insert | 50 | 3024 | 96 | 3181 |
| | overwrite | 50 | 1789 | 96 | 1746 |
| | lookup | 50 | 778 | 96 | 778 |
| Random | insert | 68 | 3800 | 84 | 3736 |
| | overwrite | 68 | 4254 | 84 | 3911 |
| | lookup | 68 | 779 | 84 | 779 |
| Runs | insert | 63 | 2546 | 82 | 2815 |
| | overwrite | 63 | 2013 | 82 | 1986 |
| | lookup | 63 | 778 | 82 | 779 |
+------------+-----------+-----------+--------------+-----------+--------------+

Ascending - keys are inserted in ascending order.
Descending - keys are inserted in descending order.
Random - keys are inserted in random order.
Runs - keys are split into ascending runs of ~20 length. Then
the runs are shuffled.

Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Colin Ian King <colin.king@canonical.com> # contains_key() fix
Signed-off-by: Mike Snitzer <snitzer@redhat.com>

authored by

Joe Thornber and committed by
Mike Snitzer
4eafdb15 7e768532

+439 -31
+422 -29
drivers/md/persistent-data/dm-btree.c
··· 500 500 501 501 EXPORT_SYMBOL_GPL(dm_btree_lookup_next); 502 502 503 + /*----------------------------------------------------------------*/ 504 + 505 + /* 506 + * Copies entries from one region of a btree node to another. The regions 507 + * must not overlap. 508 + */ 509 + static void copy_entries(struct btree_node *dest, unsigned dest_offset, 510 + struct btree_node *src, unsigned src_offset, 511 + unsigned count) 512 + { 513 + size_t value_size = le32_to_cpu(dest->header.value_size); 514 + memcpy(dest->keys + dest_offset, src->keys + src_offset, count * sizeof(uint64_t)); 515 + memcpy(value_ptr(dest, dest_offset), value_ptr(src, src_offset), count * value_size); 516 + } 517 + 518 + /* 519 + * Moves entries from one region fo a btree node to another. The regions 520 + * may overlap. 521 + */ 522 + static void move_entries(struct btree_node *dest, unsigned dest_offset, 523 + struct btree_node *src, unsigned src_offset, 524 + unsigned count) 525 + { 526 + size_t value_size = le32_to_cpu(dest->header.value_size); 527 + memmove(dest->keys + dest_offset, src->keys + src_offset, count * sizeof(uint64_t)); 528 + memmove(value_ptr(dest, dest_offset), value_ptr(src, src_offset), count * value_size); 529 + } 530 + 531 + /* 532 + * Erases the first 'count' entries of a btree node, shifting following 533 + * entries down into their place. 534 + */ 535 + static void shift_down(struct btree_node *n, unsigned count) 536 + { 537 + move_entries(n, 0, n, count, le32_to_cpu(n->header.nr_entries) - count); 538 + } 539 + 540 + /* 541 + * Moves entries in a btree node up 'count' places, making space for 542 + * new entries at the start of the node. 543 + */ 544 + static void shift_up(struct btree_node *n, unsigned count) 545 + { 546 + move_entries(n, count, n, 0, le32_to_cpu(n->header.nr_entries)); 547 + } 548 + 549 + /* 550 + * Redistributes entries between two btree nodes to make them 551 + * have similar numbers of entries. 552 + */ 553 + static void redistribute2(struct btree_node *left, struct btree_node *right) 554 + { 555 + unsigned nr_left = le32_to_cpu(left->header.nr_entries); 556 + unsigned nr_right = le32_to_cpu(right->header.nr_entries); 557 + unsigned total = nr_left + nr_right; 558 + unsigned target_left = total / 2; 559 + unsigned target_right = total - target_left; 560 + 561 + if (nr_left < target_left) { 562 + unsigned delta = target_left - nr_left; 563 + copy_entries(left, nr_left, right, 0, delta); 564 + shift_down(right, delta); 565 + } else if (nr_left > target_left) { 566 + unsigned delta = nr_left - target_left; 567 + if (nr_right) 568 + shift_up(right, delta); 569 + copy_entries(right, 0, left, target_left, delta); 570 + } 571 + 572 + left->header.nr_entries = cpu_to_le32(target_left); 573 + right->header.nr_entries = cpu_to_le32(target_right); 574 + } 575 + 576 + /* 577 + * Redistribute entries between three nodes. Assumes the central 578 + * node is empty. 579 + */ 580 + static void redistribute3(struct btree_node *left, struct btree_node *center, 581 + struct btree_node *right) 582 + { 583 + unsigned nr_left = le32_to_cpu(left->header.nr_entries); 584 + unsigned nr_center = le32_to_cpu(center->header.nr_entries); 585 + unsigned nr_right = le32_to_cpu(right->header.nr_entries); 586 + unsigned total, target_left, target_center, target_right; 587 + 588 + BUG_ON(nr_center); 589 + 590 + total = nr_left + nr_right; 591 + target_left = total / 3; 592 + target_center = (total - target_left) / 2; 593 + target_right = (total - target_left - target_center); 594 + 595 + if (nr_left < target_left) { 596 + unsigned left_short = target_left - nr_left; 597 + copy_entries(left, nr_left, right, 0, left_short); 598 + copy_entries(center, 0, right, left_short, target_center); 599 + shift_down(right, nr_right - target_right); 600 + 601 + } else if (nr_left < (target_left + target_center)) { 602 + unsigned left_to_center = nr_left - target_left; 603 + copy_entries(center, 0, left, target_left, left_to_center); 604 + copy_entries(center, left_to_center, right, 0, target_center - left_to_center); 605 + shift_down(right, nr_right - target_right); 606 + 607 + } else { 608 + unsigned right_short = target_right - nr_right; 609 + shift_up(right, right_short); 610 + copy_entries(right, 0, left, nr_left - right_short, right_short); 611 + copy_entries(center, 0, left, target_left, nr_left - target_left); 612 + } 613 + 614 + left->header.nr_entries = cpu_to_le32(target_left); 615 + center->header.nr_entries = cpu_to_le32(target_center); 616 + right->header.nr_entries = cpu_to_le32(target_right); 617 + } 618 + 503 619 /* 504 620 * Splits a node by creating a sibling node and shifting half the nodes 505 621 * contents across. Assumes there is a parent node, and it has room for ··· 646 530 * 647 531 * Where A* is a shadow of A. 648 532 */ 649 - static int btree_split_sibling(struct shadow_spine *s, unsigned parent_index, 650 - uint64_t key) 533 + static int split_one_into_two(struct shadow_spine *s, unsigned parent_index, 534 + struct dm_btree_value_type *vt, uint64_t key) 651 535 { 652 536 int r; 653 - size_t size; 654 - unsigned nr_left, nr_right; 655 537 struct dm_block *left, *right, *parent; 656 538 struct btree_node *ln, *rn, *pn; 657 539 __le64 location; ··· 663 549 ln = dm_block_data(left); 664 550 rn = dm_block_data(right); 665 551 666 - nr_left = le32_to_cpu(ln->header.nr_entries) / 2; 667 - nr_right = le32_to_cpu(ln->header.nr_entries) - nr_left; 668 - 669 - ln->header.nr_entries = cpu_to_le32(nr_left); 670 - 671 552 rn->header.flags = ln->header.flags; 672 - rn->header.nr_entries = cpu_to_le32(nr_right); 553 + rn->header.nr_entries = cpu_to_le32(0); 673 554 rn->header.max_entries = ln->header.max_entries; 674 555 rn->header.value_size = ln->header.value_size; 675 - memcpy(rn->keys, ln->keys + nr_left, nr_right * sizeof(rn->keys[0])); 556 + redistribute2(ln, rn); 676 557 677 - size = le32_to_cpu(ln->header.flags) & INTERNAL_NODE ? 678 - sizeof(uint64_t) : s->info->value_type.size; 679 - memcpy(value_ptr(rn, 0), value_ptr(ln, nr_left), 680 - size * nr_right); 681 - 682 - /* 683 - * Patch up the parent 684 - */ 558 + /* patch up the parent */ 685 559 parent = shadow_parent(s); 686 - 687 560 pn = dm_block_data(parent); 688 - location = cpu_to_le64(dm_block_location(left)); 689 - __dm_bless_for_disk(&location); 690 - memcpy_disk(value_ptr(pn, parent_index), 691 - &location, sizeof(__le64)); 692 561 693 562 location = cpu_to_le64(dm_block_location(right)); 694 563 __dm_bless_for_disk(&location); 695 - 696 564 r = insert_at(sizeof(__le64), pn, parent_index + 1, 697 565 le64_to_cpu(rn->keys[0]), &location); 698 566 if (r) { ··· 682 586 return r; 683 587 } 684 588 589 + /* patch up the spine */ 685 590 if (key < le64_to_cpu(rn->keys[0])) { 686 591 unlock_block(s->info, right); 687 592 s->nodes[1] = left; ··· 693 596 694 597 return 0; 695 598 } 599 + 600 + /* 601 + * We often need to modify a sibling node. This function shadows a particular 602 + * child of the given parent node. Making sure to update the parent to point 603 + * to the new shadow. 604 + */ 605 + static int shadow_child(struct dm_btree_info *info, struct dm_btree_value_type *vt, 606 + struct btree_node *parent, unsigned index, 607 + struct dm_block **result) 608 + { 609 + int r, inc; 610 + dm_block_t root; 611 + struct btree_node *node; 612 + 613 + root = value64(parent, index); 614 + 615 + r = dm_tm_shadow_block(info->tm, root, &btree_node_validator, 616 + result, &inc); 617 + if (r) 618 + return r; 619 + 620 + node = dm_block_data(*result); 621 + 622 + if (inc) 623 + inc_children(info->tm, node, vt); 624 + 625 + *((__le64 *) value_ptr(parent, index)) = 626 + cpu_to_le64(dm_block_location(*result)); 627 + 628 + return 0; 629 + } 630 + 631 + /* 632 + * Splits two nodes into three. This is more work, but results in fuller 633 + * nodes, so saves metadata space. 634 + */ 635 + static int split_two_into_three(struct shadow_spine *s, unsigned parent_index, 636 + struct dm_btree_value_type *vt, uint64_t key) 637 + { 638 + int r; 639 + unsigned middle_index; 640 + struct dm_block *left, *middle, *right, *parent; 641 + struct btree_node *ln, *rn, *mn, *pn; 642 + __le64 location; 643 + 644 + parent = shadow_parent(s); 645 + pn = dm_block_data(parent); 646 + 647 + if (parent_index == 0) { 648 + middle_index = 1; 649 + left = shadow_current(s); 650 + r = shadow_child(s->info, vt, pn, parent_index + 1, &right); 651 + if (r) 652 + return r; 653 + } else { 654 + middle_index = parent_index; 655 + right = shadow_current(s); 656 + r = shadow_child(s->info, vt, pn, parent_index - 1, &left); 657 + if (r) 658 + return r; 659 + } 660 + 661 + r = new_block(s->info, &middle); 662 + if (r < 0) 663 + return r; 664 + 665 + ln = dm_block_data(left); 666 + mn = dm_block_data(middle); 667 + rn = dm_block_data(right); 668 + 669 + mn->header.nr_entries = cpu_to_le32(0); 670 + mn->header.flags = ln->header.flags; 671 + mn->header.max_entries = ln->header.max_entries; 672 + mn->header.value_size = ln->header.value_size; 673 + 674 + redistribute3(ln, mn, rn); 675 + 676 + /* patch up the parent */ 677 + pn->keys[middle_index] = rn->keys[0]; 678 + location = cpu_to_le64(dm_block_location(middle)); 679 + __dm_bless_for_disk(&location); 680 + r = insert_at(sizeof(__le64), pn, middle_index, 681 + le64_to_cpu(mn->keys[0]), &location); 682 + if (r) { 683 + if (shadow_current(s) != left) 684 + unlock_block(s->info, left); 685 + 686 + unlock_block(s->info, middle); 687 + 688 + if (shadow_current(s) != right) 689 + unlock_block(s->info, right); 690 + 691 + return r; 692 + } 693 + 694 + 695 + /* patch up the spine */ 696 + if (key < le64_to_cpu(mn->keys[0])) { 697 + unlock_block(s->info, middle); 698 + unlock_block(s->info, right); 699 + s->nodes[1] = left; 700 + } else if (key < le64_to_cpu(rn->keys[0])) { 701 + unlock_block(s->info, left); 702 + unlock_block(s->info, right); 703 + s->nodes[1] = middle; 704 + } else { 705 + unlock_block(s->info, left); 706 + unlock_block(s->info, middle); 707 + s->nodes[1] = right; 708 + } 709 + 710 + return 0; 711 + } 712 + 713 + /*----------------------------------------------------------------*/ 696 714 697 715 /* 698 716 * Splits a node by creating two new children beneath the given node. ··· 902 690 return 0; 903 691 } 904 692 693 + /*----------------------------------------------------------------*/ 694 + 695 + /* 696 + * Redistributes a node's entries with its left sibling. 697 + */ 698 + static int rebalance_left(struct shadow_spine *s, struct dm_btree_value_type *vt, 699 + unsigned parent_index, uint64_t key) 700 + { 701 + int r; 702 + struct dm_block *sib; 703 + struct btree_node *left, *right, *parent = dm_block_data(shadow_parent(s)); 704 + 705 + r = shadow_child(s->info, vt, parent, parent_index - 1, &sib); 706 + if (r) 707 + return r; 708 + 709 + left = dm_block_data(sib); 710 + right = dm_block_data(shadow_current(s)); 711 + redistribute2(left, right); 712 + *key_ptr(parent, parent_index) = right->keys[0]; 713 + 714 + if (key < le64_to_cpu(right->keys[0])) { 715 + unlock_block(s->info, s->nodes[1]); 716 + s->nodes[1] = sib; 717 + } else { 718 + unlock_block(s->info, sib); 719 + } 720 + 721 + return 0; 722 + } 723 + 724 + /* 725 + * Redistributes a nodes entries with its right sibling. 726 + */ 727 + static int rebalance_right(struct shadow_spine *s, struct dm_btree_value_type *vt, 728 + unsigned parent_index, uint64_t key) 729 + { 730 + int r; 731 + struct dm_block *sib; 732 + struct btree_node *left, *right, *parent = dm_block_data(shadow_parent(s)); 733 + 734 + r = shadow_child(s->info, vt, parent, parent_index + 1, &sib); 735 + if (r) 736 + return r; 737 + 738 + left = dm_block_data(shadow_current(s)); 739 + right = dm_block_data(sib); 740 + redistribute2(left, right); 741 + *key_ptr(parent, parent_index + 1) = right->keys[0]; 742 + 743 + if (key < le64_to_cpu(right->keys[0])) { 744 + unlock_block(s->info, sib); 745 + } else { 746 + unlock_block(s->info, s->nodes[1]); 747 + s->nodes[1] = sib; 748 + } 749 + 750 + return 0; 751 + } 752 + 753 + /* 754 + * Returns the number of spare entries in a node. 755 + */ 756 + static int get_node_free_space(struct dm_btree_info *info, dm_block_t b, unsigned *space) 757 + { 758 + int r; 759 + unsigned nr_entries; 760 + struct dm_block *block; 761 + struct btree_node *node; 762 + 763 + r = bn_read_lock(info, b, &block); 764 + if (r) 765 + return r; 766 + 767 + node = dm_block_data(block); 768 + nr_entries = le32_to_cpu(node->header.nr_entries); 769 + *space = le32_to_cpu(node->header.max_entries) - nr_entries; 770 + 771 + unlock_block(info, block); 772 + return 0; 773 + } 774 + 775 + /* 776 + * Make space in a node, either by moving some entries to a sibling, 777 + * or creating a new sibling node. SPACE_THRESHOLD defines the minimum 778 + * number of free entries that must be in the sibling to make the move 779 + * worth while. If the siblings are shared (eg, part of a snapshot), 780 + * then they are not touched, since this break sharing and so consume 781 + * more space than we save. 782 + */ 783 + #define SPACE_THRESHOLD 8 784 + static int rebalance_or_split(struct shadow_spine *s, struct dm_btree_value_type *vt, 785 + unsigned parent_index, uint64_t key) 786 + { 787 + int r; 788 + struct btree_node *parent = dm_block_data(shadow_parent(s)); 789 + unsigned nr_parent = le32_to_cpu(parent->header.nr_entries); 790 + unsigned free_space; 791 + int left_shared = 0, right_shared = 0; 792 + 793 + /* Should we move entries to the left sibling? */ 794 + if (parent_index > 0) { 795 + dm_block_t left_b = value64(parent, parent_index - 1); 796 + r = dm_tm_block_is_shared(s->info->tm, left_b, &left_shared); 797 + if (r) 798 + return r; 799 + 800 + if (!left_shared) { 801 + r = get_node_free_space(s->info, left_b, &free_space); 802 + if (r) 803 + return r; 804 + 805 + if (free_space >= SPACE_THRESHOLD) 806 + return rebalance_left(s, vt, parent_index, key); 807 + } 808 + } 809 + 810 + /* Should we move entries to the right sibling? */ 811 + if (parent_index < (nr_parent - 1)) { 812 + dm_block_t right_b = value64(parent, parent_index + 1); 813 + r = dm_tm_block_is_shared(s->info->tm, right_b, &right_shared); 814 + if (r) 815 + return r; 816 + 817 + if (!right_shared) { 818 + r = get_node_free_space(s->info, right_b, &free_space); 819 + if (r) 820 + return r; 821 + 822 + if (free_space >= SPACE_THRESHOLD) 823 + return rebalance_right(s, vt, parent_index, key); 824 + } 825 + } 826 + 827 + /* 828 + * We need to split the node, normally we split two nodes 829 + * into three. But when inserting a sequence that is either 830 + * monotonically increasing or decreasing it's better to split 831 + * a single node into two. 832 + */ 833 + if (left_shared || right_shared || (nr_parent <= 2) || 834 + (parent_index == 0) || (parent_index + 1 == nr_parent)) { 835 + return split_one_into_two(s, parent_index, vt, key); 836 + } else { 837 + return split_two_into_three(s, parent_index, vt, key); 838 + } 839 + } 840 + 841 + /* 842 + * Does the node contain a particular key? 843 + */ 844 + static bool contains_key(struct btree_node *node, uint64_t key) 845 + { 846 + int i = lower_bound(node, key); 847 + 848 + if (i >= 0 && le64_to_cpu(node->keys[i]) == key) 849 + return true; 850 + 851 + return false; 852 + } 853 + 854 + /* 855 + * In general we preemptively make sure there's a free entry in every 856 + * node on the spine when doing an insert. But we can avoid that with 857 + * leaf nodes if we know it's an overwrite. 858 + */ 859 + static bool has_space_for_insert(struct btree_node *node, uint64_t key) 860 + { 861 + if (node->header.nr_entries == node->header.max_entries) { 862 + if (le32_to_cpu(node->header.flags) & LEAF_NODE) { 863 + /* we don't need space if it's an overwrite */ 864 + return contains_key(node, key); 865 + } 866 + 867 + return false; 868 + } 869 + 870 + return true; 871 + } 872 + 905 873 static int btree_insert_raw(struct shadow_spine *s, dm_block_t root, 906 874 struct dm_btree_value_type *vt, 907 875 uint64_t key, unsigned *index) ··· 1111 719 1112 720 node = dm_block_data(shadow_current(s)); 1113 721 1114 - if (node->header.nr_entries == node->header.max_entries) { 722 + if (!has_space_for_insert(node, key)) { 1115 723 if (top) 1116 724 r = btree_split_beneath(s, key); 1117 725 else 1118 - r = btree_split_sibling(s, i, key); 726 + r = rebalance_or_split(s, vt, i, key); 1119 727 1120 728 if (r < 0) 1121 729 return r; 1122 - } 1123 730 1124 - node = dm_block_data(shadow_current(s)); 731 + /* making space can cause the current node to change */ 732 + node = dm_block_data(shadow_current(s)); 733 + } 1125 734 1126 735 i = lower_bound(node, key); 1127 736
+9
drivers/md/persistent-data/dm-transaction-manager.c
··· 379 379 return dm_sm_get_count(tm->sm, b, result); 380 380 } 381 381 382 + int dm_tm_block_is_shared(struct dm_transaction_manager *tm, dm_block_t b, 383 + int *result) 384 + { 385 + if (tm->is_clone) 386 + return -EWOULDBLOCK; 387 + 388 + return dm_sm_count_is_more_than_one(tm->sm, b, result); 389 + } 390 + 382 391 struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm) 383 392 { 384 393 return tm->bm;
+8 -2
drivers/md/persistent-data/dm-transaction-manager.h
··· 103 103 104 104 void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b); 105 105 106 - int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b, 107 - uint32_t *result); 106 + int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b, uint32_t *result); 107 + 108 + /* 109 + * Finds out if a given block is shared (ie. has a reference count higher 110 + * than one). 111 + */ 112 + int dm_tm_block_is_shared(struct dm_transaction_manager *tm, dm_block_t b, 113 + int *result); 108 114 109 115 struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm); 110 116