diff options
author | Matthew Wilcox <willy@linux.intel.com> | 2016-02-02 19:57:52 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-02-03 11:28:43 -0500 |
commit | 46437f9a554fbe3e110580ca08ab703b59f2f95a (patch) | |
tree | 826c5a8180792c3c68f57686d41bf69f01b2985d /lib/radix-tree.c | |
parent | 3c1da7beeee02560cd0f0c66c5a59fce3c6746e3 (diff) |
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: cebbd29e1c2f ("radix-tree: rewrite gang lookup using iterator")
Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Ohad Ben-Cohen <ohad@wizery.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib/radix-tree.c')
-rw-r--r-- | lib/radix-tree.c | 12 |
1 files changed, 10 insertions, 2 deletions
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index fcf5d98574ce..6b79e9026e24 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -1019,9 +1019,13 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, | |||
1019 | return 0; | 1019 | return 0; |
1020 | 1020 | ||
1021 | radix_tree_for_each_slot(slot, root, &iter, first_index) { | 1021 | 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); |
1023 | if (!results[ret]) | 1023 | if (!results[ret]) |
1024 | continue; | 1024 | continue; |
1025 | if (radix_tree_is_indirect_ptr(results[ret])) { | ||
1026 | slot = radix_tree_iter_retry(&iter); | ||
1027 | continue; | ||
1028 | } | ||
1025 | if (++ret == max_items) | 1029 | if (++ret == max_items) |
1026 | break; | 1030 | break; |
1027 | } | 1031 | } |
@@ -1098,9 +1102,13 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, | |||
1098 | return 0; | 1102 | return 0; |
1099 | 1103 | ||
1100 | radix_tree_for_each_tagged(slot, root, &iter, first_index, tag) { | 1104 | 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); |
1102 | if (!results[ret]) | 1106 | if (!results[ret]) |
1103 | continue; | 1107 | continue; |
1108 | if (radix_tree_is_indirect_ptr(results[ret])) { | ||
1109 | slot = radix_tree_iter_retry(&iter); | ||
1110 | continue; | ||
1111 | } | ||
1104 | if (++ret == max_items) | 1112 | if (++ret == max_items) |
1105 | break; | 1113 | break; |
1106 | } | 1114 | } |