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

dm btree: increase rebalance threshold in __rebalance2()

We got the following warnings from thin_check during thin-pool setup:

$ thin_check /dev/vdb
examining superblock
examining devices tree
missing devices: [1, 84]
too few entries in btree_node: 41, expected at least 42 (block 138, max_entries = 126)
examining mapping tree

The phenomenon is the number of entries in one node of details_info tree is
less than (max_entries / 3). And it can be easily reproduced by the following
procedures:

$ new a thin pool
$ presume the max entries of details_info tree is 126
$ new 127 thin devices (e.g. 1~127) to make the root node being full
and then split
$ remove the first 43 (e.g. 1~43) thin devices to make the children
reblance repeatedly
$ stop the thin pool
$ thin_check

The root cause is that the B-tree removal procedure in __rebalance2()
doesn't guarantee the invariance: the minimal number of entries in
non-root node should be >= (max_entries / 3).

Simply fix the problem by increasing the rebalance threshold to
make sure the number of entries in each child will be greater
than or equal to (max_entries / 3 + 1), so no matter which
child is used for removal, the number will still be valid.

Cc: stable@vger.kernel.org
Signed-off-by: Hou Tao <houtao1@huawei.com>
Acked-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>

authored by

Hou Tao and committed by
Mike Snitzer
474e5595 484e0d2b

+7 -1
+7 -1
drivers/md/persistent-data/dm-btree-remove.c
··· 203 203 struct btree_node *right = r->n; 204 204 uint32_t nr_left = le32_to_cpu(left->header.nr_entries); 205 205 uint32_t nr_right = le32_to_cpu(right->header.nr_entries); 206 - unsigned threshold = 2 * merge_threshold(left) + 1; 206 + /* 207 + * Ensure the number of entries in each child will be greater 208 + * than or equal to (max_entries / 3 + 1), so no matter which 209 + * child is used for removal, the number will still be not 210 + * less than (max_entries / 3). 211 + */ 212 + unsigned int threshold = 2 * (merge_threshold(left) + 1); 207 213 208 214 if (nr_left + nr_right < threshold) { 209 215 /*