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

radix-tree: fix multiorder BUG_ON in radix_tree_insert

These BUG_ON tests are to ensure that all the tags are clear when
inserting a new entry. If we insert a multiorder entry, we'll end up
looking at the tags for a different node, and so the BUG_ON can end up
triggering spuriously.

Also, we now have three tags, not two, so check all three are clear, and
check all the root tags with a single call to BUG_ON since the bits are
stored contiguously.

Include a test-case to ensure this problem does not reoccur.

Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Reviewed-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Kirill Shutemov <kirill.shutemov@linux.intel.com>
Cc: Jan Kara <jack@suse.com>
Cc: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Matthew Wilcox and committed by
Linus Torvalds
7b60e9ad 85829954

+22 -4
+10 -4
lib/radix-tree.c
··· 165 165 return (__force unsigned)root->gfp_mask & (1 << (tag + __GFP_BITS_SHIFT)); 166 166 } 167 167 168 + static inline unsigned root_tags_get(struct radix_tree_root *root) 169 + { 170 + return (__force unsigned)root->gfp_mask >> __GFP_BITS_SHIFT; 171 + } 172 + 168 173 /* 169 174 * Returns 1 if any slot in the node has this tag set. 170 175 * Otherwise returns 0. ··· 609 604 rcu_assign_pointer(*slot, item); 610 605 611 606 if (node) { 607 + unsigned offset = get_slot_offset(node, slot); 612 608 node->count++; 613 - BUG_ON(tag_get(node, 0, index & RADIX_TREE_MAP_MASK)); 614 - BUG_ON(tag_get(node, 1, index & RADIX_TREE_MAP_MASK)); 609 + BUG_ON(tag_get(node, 0, offset)); 610 + BUG_ON(tag_get(node, 1, offset)); 611 + BUG_ON(tag_get(node, 2, offset)); 615 612 } else { 616 - BUG_ON(root_tag_get(root, 0)); 617 - BUG_ON(root_tag_get(root, 1)); 613 + BUG_ON(root_tags_get(root)); 618 614 } 619 615 620 616 return 0;
+12
tools/testing/radix-tree/multiorder.c
··· 81 81 item_check_absent(&tree, i); 82 82 } 83 83 84 + static void multiorder_insert_bug(void) 85 + { 86 + RADIX_TREE(tree, GFP_KERNEL); 87 + 88 + item_insert(&tree, 0); 89 + radix_tree_tag_set(&tree, 0, 0); 90 + item_insert_order(&tree, 3 << 6, 6); 91 + 92 + item_kill_tree(&tree); 93 + } 94 + 84 95 void multiorder_checks(void) 85 96 { 86 97 int i; ··· 105 94 for (i = 0; i < 15; i++) 106 95 multiorder_shrink((1UL << (i + RADIX_TREE_MAP_SHIFT)), i); 107 96 97 + multiorder_insert_bug(); 108 98 }