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

Configure Feed

Select the types of activity you want to include in your feed.

maple_tree: add test for rebalance calculation off-by-one

During the big node removal, an incorrect rebalance step went too far up
the tree causing insufficient nodes. Test the faulty condition by
recreating the scenario in the userspace testing.

Link: https://lkml.kernel.org/r/20260130205935.2559335-24-Liam.Howlett@oracle.com
Signed-off-by: Liam R. Howlett <Liam.Howlett@oracle.com>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Andrew Ballance <andrewjballance@gmail.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Christian Kujau <lists@nerdbynature.de>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: SeongJae Park <sj@kernel.org>
Cc: Sidhartha Kumar <sidhartha.kumar@oracle.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Liam R. Howlett and committed by
Andrew Morton
ebfee00c 971f0db1

+125
+125
tools/testing/radix-tree/maple.c
··· 35888 35888 return ret; 35889 35889 } 35890 35890 35891 + static noinline void __init check_erase_rebalance(struct maple_tree *mt) 35892 + { 35893 + unsigned long val; 35894 + void *enode; 35895 + int ret; 35896 + 35897 + MA_STATE(mas, mt, 0, 0); 35898 + 35899 + /* 35900 + * During removal of big node, the rebalance started going too high, 35901 + * which resulted in too many nodes trying to be used. 35902 + * 35903 + * Create a rebalance which results in an exactly full parent (0-9) that 35904 + * does not need to be rebalanced. This required two full levels, 35905 + * followed by an insufficient level which will be rebalanced into two 35906 + * nodes, finally leaves that need to be rebalanced into one node. 35907 + * 35908 + * The bugs tree: 35909 + * root 4 Label R 35910 + * /\ /\ 35911 + * 9 X F 35912 + * /\ /\ / 35913 + * 9 X E 35914 + * /\ /\ /\ 35915 + * 4 8 C D 35916 + * /\ /\ 35917 + * 6 9 A B 35918 + * ^ becomes 5 with the write. 35919 + * 35920 + * Below, the reconstruction leaves the root with 2 entries, the setup 35921 + * uses the letter labels above. 35922 + */ 35923 + 35924 + ret = build_full_tree(mt, MT_FLAGS_ALLOC_RANGE, 4); 35925 + MT_BUG_ON(mt, ret); 35926 + 35927 + /* Cheap expansion to 5 levels */ 35928 + mtree_store(mt, ULONG_MAX, xa_mk_value(0), GFP_KERNEL); 35929 + /* rcu is used to ensure node use */ 35930 + mt_set_in_rcu(mt); 35931 + mas_lock(&mas); 35932 + 35933 + /* Node A had 6 entries */ 35934 + mas_walk(&mas); 35935 + MAS_BUG_ON(&mas, mas_data_end(&mas) < 6); 35936 + while (mas_data_end(&mas) > 6) { 35937 + mas_erase(&mas); 35938 + mas_next(&mas, ULONG_MAX); 35939 + } 35940 + 35941 + /* Move to Node B */ 35942 + enode = (void*) mas.node; 35943 + while (mas.node == enode) 35944 + mas_next(&mas, ULONG_MAX); 35945 + 35946 + /* Node B had 9 entries */ 35947 + MAS_BUG_ON(&mas, mas_data_end(&mas) < 9); 35948 + while (mas_data_end(&mas) > 9) { 35949 + mas_erase(&mas); 35950 + mas_next(&mas, ULONG_MAX); 35951 + } 35952 + 35953 + /* Move to Node C */ 35954 + mas_ascend(&mas); 35955 + val = mas.max; 35956 + /* Adjust entries to be 4 */ 35957 + while (mas_data_end(&mas) > 4) { 35958 + mas_set(&mas, val); 35959 + mas_erase(&mas); 35960 + mas_prev(&mas, 0); 35961 + val = mas.index; 35962 + mas_ascend(&mas); 35963 + } 35964 + 35965 + /* Move to Node D */ 35966 + mas_ascend(&mas); 35967 + mas.offset = 1; 35968 + mas_descend(&mas); 35969 + val = mas.max; 35970 + /* Adjust entries to be 8 */ 35971 + while (mas_data_end(&mas) < 8) { 35972 + mas_set(&mas, val--); 35973 + mas_store_gfp(&mas, &mas, GFP_KERNEL); 35974 + mas_ascend(&mas); 35975 + } 35976 + 35977 + /* Move to Node E */ 35978 + mas_ascend(&mas); 35979 + val = mas.max; 35980 + MAS_BUG_ON(&mas, mas_data_end(&mas) > 9); 35981 + /* Adjust Node E to 9 entries */ 35982 + while (mas_data_end(&mas) < 9) { 35983 + mas_set(&mas, val--); 35984 + mas_store_gfp(&mas, &mas, GFP_KERNEL); 35985 + mas_ascend(&mas); 35986 + mas_ascend(&mas); 35987 + } 35988 + 35989 + /* Move to Node F */ 35990 + mas_ascend(&mas); 35991 + val = mas.max; 35992 + MAS_BUG_ON(&mas, mas_data_end(&mas) > 9); 35993 + /* Adjust Node F to 9 entries */ 35994 + while (mas_data_end(&mas) < 9) { 35995 + mas_set(&mas, val--); 35996 + mas_store_gfp(&mas, &mas, GFP_KERNEL); 35997 + mas_ascend(&mas); 35998 + mas_ascend(&mas); 35999 + mas_ascend(&mas); 36000 + } 36001 + 36002 + /* Test is set up, walk to first entry */ 36003 + mas_set(&mas, 0); 36004 + mas_next(&mas, ULONG_MAX); 36005 + /* overwrite the entry to cause a rebalance, which was 1 too few */ 36006 + mas_set_range(&mas, 0, mas.last); 36007 + mas_preallocate(&mas, NULL, GFP_KERNEL); 36008 + mas_store_prealloc(&mas, NULL); 36009 + mas_unlock(&mas); 36010 + } 36011 + 35891 36012 static noinline void __init check_mtree_dup(struct maple_tree *mt) 35892 36013 { 35893 36014 DEFINE_MTREE(new); ··· 36368 36247 36369 36248 mt_init_flags(&tree, 0); 36370 36249 check_mtree_dup(&tree); 36250 + mtree_destroy(&tree); 36251 + 36252 + mt_init_flags(&tree, MT_FLAGS_ALLOC_RANGE); 36253 + check_erase_rebalance(&tree); 36371 36254 mtree_destroy(&tree); 36372 36255 36373 36256 /* RCU testing */