Commit 0a2efc6c authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Linus Torvalds

radix-tree: rewrite radix_tree_locate_item

Use the new multi-order support functions to rewrite
radix_tree_locate_item().  Modify the locate tests to test multiorder
entries too.

[hughd@google.com: radix_tree_locate_item() is often returning the wrong index]
  Link: http://lkml.kernel.org/r/alpine.LSU.2.11.1605012108490.1166@eggly.anvilsSigned-off-by: default avatarMatthew Wilcox <willy@linux.intel.com>
Reviewed-by: default avatarRoss 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 8a14f4d8
...@@ -1303,58 +1303,54 @@ EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot); ...@@ -1303,58 +1303,54 @@ EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot);
#if defined(CONFIG_SHMEM) && defined(CONFIG_SWAP) #if defined(CONFIG_SHMEM) && defined(CONFIG_SWAP)
#include <linux/sched.h> /* for cond_resched() */ #include <linux/sched.h> /* for cond_resched() */
struct locate_info {
unsigned long found_index;
bool stop;
};
/* /*
* This linear search is at present only useful to shmem_unuse_inode(). * This linear search is at present only useful to shmem_unuse_inode().
*/ */
static unsigned long __locate(struct radix_tree_node *slot, void *item, static unsigned long __locate(struct radix_tree_node *slot, void *item,
unsigned long index, unsigned long *found_index) unsigned long index, struct locate_info *info)
{ {
unsigned int shift, height; unsigned int shift, height;
unsigned long i; unsigned long i;
height = slot->path & RADIX_TREE_HEIGHT_MASK; height = slot->path & RADIX_TREE_HEIGHT_MASK;
shift = (height-1) * RADIX_TREE_MAP_SHIFT; shift = height * RADIX_TREE_MAP_SHIFT;
for ( ; height > 1; height--) { do {
i = (index >> shift) & RADIX_TREE_MAP_MASK; shift -= RADIX_TREE_MAP_SHIFT;
for (;;) {
if (slot->slots[i] != NULL)
break;
index &= ~((1UL << shift) - 1);
index += 1UL << shift;
if (index == 0)
goto out; /* 32-bit wraparound */
i++;
if (i == RADIX_TREE_MAP_SIZE)
goto out;
}
slot = rcu_dereference_raw(slot->slots[i]); for (i = (index >> shift) & RADIX_TREE_MAP_MASK;
if (slot == NULL) i < RADIX_TREE_MAP_SIZE;
goto out; i++, index += (1UL << shift)) {
if (!radix_tree_is_indirect_ptr(slot)) { struct radix_tree_node *node =
if (slot == item) { rcu_dereference_raw(slot->slots[i]);
*found_index = index + i; if (node == RADIX_TREE_RETRY)
index = 0; goto out;
} else { if (!radix_tree_is_indirect_ptr(node)) {
index += shift; if (node == item) {
info->found_index = index;
info->stop = true;
goto out;
}
continue;
} }
goto out; node = indirect_to_ptr(node);
if (is_sibling_entry(slot, node))
continue;
slot = node;
break;
} }
slot = indirect_to_ptr(slot); if (i == RADIX_TREE_MAP_SIZE)
shift -= RADIX_TREE_MAP_SHIFT; break;
} } while (shift);
/* Bottom level: check items */
for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
if (slot->slots[i] == item) {
*found_index = index + i;
index = 0;
goto out;
}
}
index += RADIX_TREE_MAP_SIZE;
out: out:
if ((index == 0) && (i == RADIX_TREE_MAP_SIZE))
info->stop = true;
return index; return index;
} }
...@@ -1372,7 +1368,10 @@ unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item) ...@@ -1372,7 +1368,10 @@ unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item)
struct radix_tree_node *node; struct radix_tree_node *node;
unsigned long max_index; unsigned long max_index;
unsigned long cur_index = 0; unsigned long cur_index = 0;
unsigned long found_index = -1; struct locate_info info = {
.found_index = -1,
.stop = false,
};
do { do {
rcu_read_lock(); rcu_read_lock();
...@@ -1380,24 +1379,24 @@ unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item) ...@@ -1380,24 +1379,24 @@ unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item)
if (!radix_tree_is_indirect_ptr(node)) { if (!radix_tree_is_indirect_ptr(node)) {
rcu_read_unlock(); rcu_read_unlock();
if (node == item) if (node == item)
found_index = 0; info.found_index = 0;
break; break;
} }
node = indirect_to_ptr(node); node = indirect_to_ptr(node);
max_index = radix_tree_maxindex(node->path &
RADIX_TREE_HEIGHT_MASK); max_index = node_maxindex(node);
if (cur_index > max_index) { if (cur_index > max_index) {
rcu_read_unlock(); rcu_read_unlock();
break; break;
} }
cur_index = __locate(node, item, cur_index, &found_index); cur_index = __locate(node, item, cur_index, &info);
rcu_read_unlock(); rcu_read_unlock();
cond_resched(); cond_resched();
} while (cur_index != 0 && cur_index <= max_index); } while (!info.stop && cur_index <= max_index);
return found_index; return info.found_index;
} }
#else #else
unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item) unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item)
......
...@@ -232,17 +232,18 @@ void copy_tag_check(void) ...@@ -232,17 +232,18 @@ void copy_tag_check(void)
item_kill_tree(&tree); item_kill_tree(&tree);
} }
void __locate_check(struct radix_tree_root *tree, unsigned long index) void __locate_check(struct radix_tree_root *tree, unsigned long index,
unsigned order)
{ {
struct item *item; struct item *item;
unsigned long index2; unsigned long index2;
item_insert(tree, index); item_insert_order(tree, index, order);
item = item_lookup(tree, index); item = item_lookup(tree, index);
index2 = radix_tree_locate_item(tree, item); index2 = radix_tree_locate_item(tree, item);
if (index != index2) { if (index != index2) {
printf("index %ld inserted; found %ld\n", printf("index %ld order %d inserted; found %ld\n",
index, index2); index, order, index2);
abort(); abort();
} }
} }
...@@ -250,21 +251,26 @@ void __locate_check(struct radix_tree_root *tree, unsigned long index) ...@@ -250,21 +251,26 @@ void __locate_check(struct radix_tree_root *tree, unsigned long index)
static void locate_check(void) static void locate_check(void)
{ {
RADIX_TREE(tree, GFP_KERNEL); RADIX_TREE(tree, GFP_KERNEL);
unsigned order;
unsigned long offset, index; unsigned long offset, index;
for (offset = 0; offset < (1 << 3); offset++) { for (order = 0; order < 20; order++) {
for (index = 0; index < (1UL << 5); index++) { for (offset = 0; offset < (1 << (order + 3));
__locate_check(&tree, index + offset); offset += (1UL << order)) {
} for (index = 0; index < (1UL << (order + 5));
if (radix_tree_locate_item(&tree, &tree) != -1) index += (1UL << order)) {
abort(); __locate_check(&tree, index + offset, order);
}
if (radix_tree_locate_item(&tree, &tree) != -1)
abort();
item_kill_tree(&tree); item_kill_tree(&tree);
}
} }
if (radix_tree_locate_item(&tree, &tree) != -1) if (radix_tree_locate_item(&tree, &tree) != -1)
abort(); abort();
__locate_check(&tree, -1); __locate_check(&tree, -1, 0);
if (radix_tree_locate_item(&tree, &tree) != -1) if (radix_tree_locate_item(&tree, &tree) != -1)
abort(); abort();
item_kill_tree(&tree); item_kill_tree(&tree);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment