diff options
-rw-r--r-- | include/linux/radix-tree.h | 38 | ||||
-rw-r--r-- | lib/radix-tree.c | 69 |
2 files changed, 64 insertions, 43 deletions
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index 430e4a8c1382..b6116b4445c7 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h | |||
@@ -26,28 +26,31 @@ | |||
26 | #include <linux/rcupdate.h> | 26 | #include <linux/rcupdate.h> |
27 | 27 | ||
28 | /* | 28 | /* |
29 | * A direct pointer (root->rnode pointing directly to a data item, | 29 | * An indirect pointer (root->rnode pointing to a radix_tree_node, rather |
30 | * rather than another radix_tree_node) is signalled by the low bit | 30 | * than a data item) is signalled by the low bit set in the root->rnode |
31 | * set in the root->rnode pointer. | 31 | * pointer. |
32 | * | 32 | * |
33 | * In this case root->height is also NULL, but the direct pointer tests are | 33 | * In this case root->height is > 0, but the indirect pointer tests are |
34 | * needed for RCU lookups when root->height is unreliable. | 34 | * needed for RCU lookups (because root->height is unreliable). The only |
35 | * time callers need worry about this is when doing a lookup_slot under | ||
36 | * RCU. | ||
35 | */ | 37 | */ |
36 | #define RADIX_TREE_DIRECT_PTR 1 | 38 | #define RADIX_TREE_INDIRECT_PTR 1 |
39 | #define RADIX_TREE_RETRY ((void *)-1UL) | ||
37 | 40 | ||
38 | static inline void *radix_tree_ptr_to_direct(void *ptr) | 41 | static inline void *radix_tree_ptr_to_indirect(void *ptr) |
39 | { | 42 | { |
40 | return (void *)((unsigned long)ptr | RADIX_TREE_DIRECT_PTR); | 43 | return (void *)((unsigned long)ptr | RADIX_TREE_INDIRECT_PTR); |
41 | } | 44 | } |
42 | 45 | ||
43 | static inline void *radix_tree_direct_to_ptr(void *ptr) | 46 | static inline void *radix_tree_indirect_to_ptr(void *ptr) |
44 | { | 47 | { |
45 | return (void *)((unsigned long)ptr & ~RADIX_TREE_DIRECT_PTR); | 48 | return (void *)((unsigned long)ptr & ~RADIX_TREE_INDIRECT_PTR); |
46 | } | 49 | } |
47 | 50 | ||
48 | static inline int radix_tree_is_direct_ptr(void *ptr) | 51 | static inline int radix_tree_is_indirect_ptr(void *ptr) |
49 | { | 52 | { |
50 | return (int)((unsigned long)ptr & RADIX_TREE_DIRECT_PTR); | 53 | return (int)((unsigned long)ptr & RADIX_TREE_INDIRECT_PTR); |
51 | } | 54 | } |
52 | 55 | ||
53 | /*** radix-tree API starts here ***/ | 56 | /*** radix-tree API starts here ***/ |
@@ -130,7 +133,10 @@ do { \ | |||
130 | */ | 133 | */ |
131 | static inline void *radix_tree_deref_slot(void **pslot) | 134 | static inline void *radix_tree_deref_slot(void **pslot) |
132 | { | 135 | { |
133 | return radix_tree_direct_to_ptr(*pslot); | 136 | void *ret = *pslot; |
137 | if (unlikely(radix_tree_is_indirect_ptr(ret))) | ||
138 | ret = RADIX_TREE_RETRY; | ||
139 | return ret; | ||
134 | } | 140 | } |
135 | /** | 141 | /** |
136 | * radix_tree_replace_slot - replace item in a slot | 142 | * radix_tree_replace_slot - replace item in a slot |
@@ -142,10 +148,8 @@ static inline void *radix_tree_deref_slot(void **pslot) | |||
142 | */ | 148 | */ |
143 | static inline void radix_tree_replace_slot(void **pslot, void *item) | 149 | static inline void radix_tree_replace_slot(void **pslot, void *item) |
144 | { | 150 | { |
145 | BUG_ON(radix_tree_is_direct_ptr(item)); | 151 | BUG_ON(radix_tree_is_indirect_ptr(item)); |
146 | rcu_assign_pointer(*pslot, | 152 | rcu_assign_pointer(*pslot, item); |
147 | (void *)((unsigned long)item | | ||
148 | ((unsigned long)*pslot & RADIX_TREE_DIRECT_PTR))); | ||
149 | } | 153 | } |
150 | 154 | ||
151 | int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); | 155 | int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); |
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 7af368a4401b..0eee4020a7be 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -104,7 +104,7 @@ radix_tree_node_alloc(struct radix_tree_root *root) | |||
104 | rtp->nr--; | 104 | rtp->nr--; |
105 | } | 105 | } |
106 | } | 106 | } |
107 | BUG_ON(radix_tree_is_direct_ptr(ret)); | 107 | BUG_ON(radix_tree_is_indirect_ptr(ret)); |
108 | return ret; | 108 | return ret; |
109 | } | 109 | } |
110 | 110 | ||
@@ -240,7 +240,7 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) | |||
240 | return -ENOMEM; | 240 | return -ENOMEM; |
241 | 241 | ||
242 | /* Increase the height. */ | 242 | /* Increase the height. */ |
243 | node->slots[0] = radix_tree_direct_to_ptr(root->rnode); | 243 | node->slots[0] = radix_tree_indirect_to_ptr(root->rnode); |
244 | 244 | ||
245 | /* Propagate the aggregated tag info into the new root */ | 245 | /* Propagate the aggregated tag info into the new root */ |
246 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { | 246 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { |
@@ -251,6 +251,7 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) | |||
251 | newheight = root->height+1; | 251 | newheight = root->height+1; |
252 | node->height = newheight; | 252 | node->height = newheight; |
253 | node->count = 1; | 253 | node->count = 1; |
254 | node = radix_tree_ptr_to_indirect(node); | ||
254 | rcu_assign_pointer(root->rnode, node); | 255 | rcu_assign_pointer(root->rnode, node); |
255 | root->height = newheight; | 256 | root->height = newheight; |
256 | } while (height > root->height); | 257 | } while (height > root->height); |
@@ -274,7 +275,7 @@ int radix_tree_insert(struct radix_tree_root *root, | |||
274 | int offset; | 275 | int offset; |
275 | int error; | 276 | int error; |
276 | 277 | ||
277 | BUG_ON(radix_tree_is_direct_ptr(item)); | 278 | BUG_ON(radix_tree_is_indirect_ptr(item)); |
278 | 279 | ||
279 | /* Make sure the tree is high enough. */ | 280 | /* Make sure the tree is high enough. */ |
280 | if (index > radix_tree_maxindex(root->height)) { | 281 | if (index > radix_tree_maxindex(root->height)) { |
@@ -283,7 +284,8 @@ int radix_tree_insert(struct radix_tree_root *root, | |||
283 | return error; | 284 | return error; |
284 | } | 285 | } |
285 | 286 | ||
286 | slot = root->rnode; | 287 | slot = radix_tree_indirect_to_ptr(root->rnode); |
288 | |||
287 | height = root->height; | 289 | height = root->height; |
288 | shift = (height-1) * RADIX_TREE_MAP_SHIFT; | 290 | shift = (height-1) * RADIX_TREE_MAP_SHIFT; |
289 | 291 | ||
@@ -298,7 +300,8 @@ int radix_tree_insert(struct radix_tree_root *root, | |||
298 | rcu_assign_pointer(node->slots[offset], slot); | 300 | rcu_assign_pointer(node->slots[offset], slot); |
299 | node->count++; | 301 | node->count++; |
300 | } else | 302 | } else |
301 | rcu_assign_pointer(root->rnode, slot); | 303 | rcu_assign_pointer(root->rnode, |
304 | radix_tree_ptr_to_indirect(slot)); | ||
302 | } | 305 | } |
303 | 306 | ||
304 | /* Go a level down */ | 307 | /* Go a level down */ |
@@ -318,7 +321,7 @@ int radix_tree_insert(struct radix_tree_root *root, | |||
318 | BUG_ON(tag_get(node, 0, offset)); | 321 | BUG_ON(tag_get(node, 0, offset)); |
319 | BUG_ON(tag_get(node, 1, offset)); | 322 | BUG_ON(tag_get(node, 1, offset)); |
320 | } else { | 323 | } else { |
321 | rcu_assign_pointer(root->rnode, radix_tree_ptr_to_direct(item)); | 324 | rcu_assign_pointer(root->rnode, item); |
322 | BUG_ON(root_tag_get(root, 0)); | 325 | BUG_ON(root_tag_get(root, 0)); |
323 | BUG_ON(root_tag_get(root, 1)); | 326 | BUG_ON(root_tag_get(root, 1)); |
324 | } | 327 | } |
@@ -350,11 +353,12 @@ void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) | |||
350 | if (node == NULL) | 353 | if (node == NULL) |
351 | return NULL; | 354 | return NULL; |
352 | 355 | ||
353 | if (radix_tree_is_direct_ptr(node)) { | 356 | if (!radix_tree_is_indirect_ptr(node)) { |
354 | if (index > 0) | 357 | if (index > 0) |
355 | return NULL; | 358 | return NULL; |
356 | return (void **)&root->rnode; | 359 | return (void **)&root->rnode; |
357 | } | 360 | } |
361 | node = radix_tree_indirect_to_ptr(node); | ||
358 | 362 | ||
359 | height = node->height; | 363 | height = node->height; |
360 | if (index > radix_tree_maxindex(height)) | 364 | if (index > radix_tree_maxindex(height)) |
@@ -398,11 +402,12 @@ void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) | |||
398 | if (node == NULL) | 402 | if (node == NULL) |
399 | return NULL; | 403 | return NULL; |
400 | 404 | ||
401 | if (radix_tree_is_direct_ptr(node)) { | 405 | if (!radix_tree_is_indirect_ptr(node)) { |
402 | if (index > 0) | 406 | if (index > 0) |
403 | return NULL; | 407 | return NULL; |
404 | return radix_tree_direct_to_ptr(node); | 408 | return node; |
405 | } | 409 | } |
410 | node = radix_tree_indirect_to_ptr(node); | ||
406 | 411 | ||
407 | height = node->height; | 412 | height = node->height; |
408 | if (index > radix_tree_maxindex(height)) | 413 | if (index > radix_tree_maxindex(height)) |
@@ -447,7 +452,7 @@ void *radix_tree_tag_set(struct radix_tree_root *root, | |||
447 | height = root->height; | 452 | height = root->height; |
448 | BUG_ON(index > radix_tree_maxindex(height)); | 453 | BUG_ON(index > radix_tree_maxindex(height)); |
449 | 454 | ||
450 | slot = root->rnode; | 455 | slot = radix_tree_indirect_to_ptr(root->rnode); |
451 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; | 456 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; |
452 | 457 | ||
453 | while (height > 0) { | 458 | while (height > 0) { |
@@ -497,7 +502,7 @@ void *radix_tree_tag_clear(struct radix_tree_root *root, | |||
497 | 502 | ||
498 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; | 503 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; |
499 | pathp->node = NULL; | 504 | pathp->node = NULL; |
500 | slot = root->rnode; | 505 | slot = radix_tree_indirect_to_ptr(root->rnode); |
501 | 506 | ||
502 | while (height > 0) { | 507 | while (height > 0) { |
503 | int offset; | 508 | int offset; |
@@ -562,8 +567,9 @@ int radix_tree_tag_get(struct radix_tree_root *root, | |||
562 | if (node == NULL) | 567 | if (node == NULL) |
563 | return 0; | 568 | return 0; |
564 | 569 | ||
565 | if (radix_tree_is_direct_ptr(node)) | 570 | if (!radix_tree_is_indirect_ptr(node)) |
566 | return (index == 0); | 571 | return (index == 0); |
572 | node = radix_tree_indirect_to_ptr(node); | ||
567 | 573 | ||
568 | height = node->height; | 574 | height = node->height; |
569 | if (index > radix_tree_maxindex(height)) | 575 | if (index > radix_tree_maxindex(height)) |
@@ -716,13 +722,13 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, | |||
716 | if (!node) | 722 | if (!node) |
717 | return 0; | 723 | return 0; |
718 | 724 | ||
719 | if (radix_tree_is_direct_ptr(node)) { | 725 | if (!radix_tree_is_indirect_ptr(node)) { |
720 | if (first_index > 0) | 726 | if (first_index > 0) |
721 | return 0; | 727 | return 0; |
722 | node = radix_tree_direct_to_ptr(node); | 728 | results[0] = node; |
723 | results[0] = rcu_dereference(node); | ||
724 | return 1; | 729 | return 1; |
725 | } | 730 | } |
731 | node = radix_tree_indirect_to_ptr(node); | ||
726 | 732 | ||
727 | max_index = radix_tree_maxindex(node->height); | 733 | max_index = radix_tree_maxindex(node->height); |
728 | 734 | ||
@@ -844,13 +850,13 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, | |||
844 | if (!node) | 850 | if (!node) |
845 | return 0; | 851 | return 0; |
846 | 852 | ||
847 | if (radix_tree_is_direct_ptr(node)) { | 853 | if (!radix_tree_is_indirect_ptr(node)) { |
848 | if (first_index > 0) | 854 | if (first_index > 0) |
849 | return 0; | 855 | return 0; |
850 | node = radix_tree_direct_to_ptr(node); | 856 | results[0] = node; |
851 | results[0] = rcu_dereference(node); | ||
852 | return 1; | 857 | return 1; |
853 | } | 858 | } |
859 | node = radix_tree_indirect_to_ptr(node); | ||
854 | 860 | ||
855 | max_index = radix_tree_maxindex(node->height); | 861 | max_index = radix_tree_maxindex(node->height); |
856 | 862 | ||
@@ -880,12 +886,22 @@ EXPORT_SYMBOL(radix_tree_gang_lookup_tag); | |||
880 | static inline void radix_tree_shrink(struct radix_tree_root *root) | 886 | static inline void radix_tree_shrink(struct radix_tree_root *root) |
881 | { | 887 | { |
882 | /* try to shrink tree height */ | 888 | /* try to shrink tree height */ |
883 | while (root->height > 0 && | 889 | while (root->height > 0) { |
884 | root->rnode->count == 1 && | ||
885 | root->rnode->slots[0]) { | ||
886 | struct radix_tree_node *to_free = root->rnode; | 890 | struct radix_tree_node *to_free = root->rnode; |
887 | void *newptr; | 891 | void *newptr; |
888 | 892 | ||
893 | BUG_ON(!radix_tree_is_indirect_ptr(to_free)); | ||
894 | to_free = radix_tree_indirect_to_ptr(to_free); | ||
895 | |||
896 | /* | ||
897 | * The candidate node has more than one child, or its child | ||
898 | * is not at the leftmost slot, we cannot shrink. | ||
899 | */ | ||
900 | if (to_free->count != 1) | ||
901 | break; | ||
902 | if (!to_free->slots[0]) | ||
903 | break; | ||
904 | |||
889 | /* | 905 | /* |
890 | * We don't need rcu_assign_pointer(), since we are simply | 906 | * We don't need rcu_assign_pointer(), since we are simply |
891 | * moving the node from one part of the tree to another. If | 907 | * moving the node from one part of the tree to another. If |
@@ -894,8 +910,8 @@ static inline void radix_tree_shrink(struct radix_tree_root *root) | |||
894 | * one (root->rnode). | 910 | * one (root->rnode). |
895 | */ | 911 | */ |
896 | newptr = to_free->slots[0]; | 912 | newptr = to_free->slots[0]; |
897 | if (root->height == 1) | 913 | if (root->height > 1) |
898 | newptr = radix_tree_ptr_to_direct(newptr); | 914 | newptr = radix_tree_ptr_to_indirect(newptr); |
899 | root->rnode = newptr; | 915 | root->rnode = newptr; |
900 | root->height--; | 916 | root->height--; |
901 | /* must only free zeroed nodes into the slab */ | 917 | /* must only free zeroed nodes into the slab */ |
@@ -930,12 +946,12 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) | |||
930 | goto out; | 946 | goto out; |
931 | 947 | ||
932 | slot = root->rnode; | 948 | slot = root->rnode; |
933 | if (height == 0 && root->rnode) { | 949 | if (height == 0) { |
934 | slot = radix_tree_direct_to_ptr(slot); | ||
935 | root_tag_clear_all(root); | 950 | root_tag_clear_all(root); |
936 | root->rnode = NULL; | 951 | root->rnode = NULL; |
937 | goto out; | 952 | goto out; |
938 | } | 953 | } |
954 | slot = radix_tree_indirect_to_ptr(slot); | ||
939 | 955 | ||
940 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; | 956 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; |
941 | pathp->node = NULL; | 957 | pathp->node = NULL; |
@@ -977,7 +993,8 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) | |||
977 | radix_tree_node_free(to_free); | 993 | radix_tree_node_free(to_free); |
978 | 994 | ||
979 | if (pathp->node->count) { | 995 | if (pathp->node->count) { |
980 | if (pathp->node == root->rnode) | 996 | if (pathp->node == |
997 | radix_tree_indirect_to_ptr(root->rnode)) | ||
981 | radix_tree_shrink(root); | 998 | radix_tree_shrink(root); |
982 | goto out; | 999 | goto out; |
983 | } | 1000 | } |