Skip to content

Commit

Permalink
dm btree remove: fix a bug when rebalancing nodes after removal
Browse files Browse the repository at this point in the history
Commit 4c7e309 ("dm btree remove: fix bug in redistribute3") wasn't
a complete fix for redistribute3().

The redistribute3 function takes 3 btree nodes and shares out the entries
evenly between them.  If the three nodes in total contained
(MAX_ENTRIES * 3) - 1 entries between them then this was erroneously getting
rebalanced as (MAX_ENTRIES - 1) on the left and right, and (MAX_ENTRIES + 1) in
the center.

Fix this issue by being more careful about calculating the target number
of entries for the left and right nodes.

Unit tested in userspace using this program:
https://github.com/jthornber/redistribute3-test/blob/master/redistribute3_t.c

Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org
  • Loading branch information
jthornber authored and snitm committed Oct 23, 2015
1 parent ba30670 commit 2871c69
Showing 1 changed file with 11 additions and 6 deletions.
17 changes: 11 additions & 6 deletions drivers/md/persistent-data/dm-btree-remove.c
Expand Up @@ -301,11 +301,16 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent,
{
int s;
uint32_t max_entries = le32_to_cpu(left->header.max_entries);
unsigned target = (nr_left + nr_center + nr_right) / 3;
BUG_ON(target > max_entries);
unsigned total = nr_left + nr_center + nr_right;
unsigned target_right = total / 3;
unsigned remainder = (target_right * 3) != total;
unsigned target_left = target_right + remainder;

BUG_ON(target_left > max_entries);
BUG_ON(target_right > max_entries);

if (nr_left < nr_right) {
s = nr_left - target;
s = nr_left - target_left;

if (s < 0 && nr_center < -s) {
/* not enough in central node */
Expand All @@ -316,10 +321,10 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent,
} else
shift(left, center, s);

shift(center, right, target - nr_right);
shift(center, right, target_right - nr_right);

} else {
s = target - nr_right;
s = target_right - nr_right;
if (s > 0 && nr_center < s) {
/* not enough in central node */
shift(center, right, nr_center);
Expand All @@ -329,7 +334,7 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent,
} else
shift(center, right, s);

shift(left, center, nr_left - target);
shift(left, center, nr_left - target_left);
}

*key_ptr(parent, c->index) = center->keys[0];
Expand Down

0 comments on commit 2871c69

Please sign in to comment.