Skip to content

Commit 46437f9

Browse files
Matthew Wilcoxtorvalds
authored andcommitted
radix-tree: fix race in gang lookup
If the indirect_ptr bit is set on a slot, that indicates we need to redo the lookup. Introduce a new function radix_tree_iter_retry() which forces the loop to retry the lookup by setting 'slot' to NULL and turning the iterator back to point at the problematic entry. This is a pretty rare problem to hit at the moment; the lookup has to race with a grow of the radix tree from a height of 0. The consequences of hitting this race are that gang lookup could return a pointer to a radix_tree_node instead of a pointer to whatever the user had inserted in the tree. Fixes: cebbd29 ("radix-tree: rewrite gang lookup using iterator") Signed-off-by: Matthew Wilcox <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Ohad Ben-Cohen <[email protected]> Cc: Konstantin Khlebnikov <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 3c1da7b commit 46437f9

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

include/linux/radix-tree.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,22 @@ radix_tree_iter_init(struct radix_tree_iter *iter, unsigned long start)
378378
void **radix_tree_next_chunk(struct radix_tree_root *root,
379379
struct radix_tree_iter *iter, unsigned flags);
380380

381+
/**
382+
* radix_tree_iter_retry - retry this chunk of the iteration
383+
* @iter: iterator state
384+
*
385+
* If we iterate over a tree protected only by the RCU lock, a race
386+
* against deletion or creation may result in seeing a slot for which
387+
* radix_tree_deref_retry() returns true. If so, call this function
388+
* and continue the iteration.
389+
*/
390+
static inline __must_check
391+
void **radix_tree_iter_retry(struct radix_tree_iter *iter)
392+
{
393+
iter->next_index = iter->index;
394+
return NULL;
395+
}
396+
381397
/**
382398
* radix_tree_chunk_size - get current chunk size
383399
*

lib/radix-tree.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,9 +1019,13 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
10191019
return 0;
10201020

10211021
radix_tree_for_each_slot(slot, root, &iter, first_index) {
1022-
results[ret] = indirect_to_ptr(rcu_dereference_raw(*slot));
1022+
results[ret] = rcu_dereference_raw(*slot);
10231023
if (!results[ret])
10241024
continue;
1025+
if (radix_tree_is_indirect_ptr(results[ret])) {
1026+
slot = radix_tree_iter_retry(&iter);
1027+
continue;
1028+
}
10251029
if (++ret == max_items)
10261030
break;
10271031
}
@@ -1098,9 +1102,13 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
10981102
return 0;
10991103

11001104
radix_tree_for_each_tagged(slot, root, &iter, first_index, tag) {
1101-
results[ret] = indirect_to_ptr(rcu_dereference_raw(*slot));
1105+
results[ret] = rcu_dereference_raw(*slot);
11021106
if (!results[ret])
11031107
continue;
1108+
if (radix_tree_is_indirect_ptr(results[ret])) {
1109+
slot = radix_tree_iter_retry(&iter);
1110+
continue;
1111+
}
11041112
if (++ret == max_items)
11051113
break;
11061114
}

0 commit comments

Comments
 (0)