diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/radix-tree.c | 69 |
1 files changed, 43 insertions, 26 deletions
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 | } |