aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/radix-tree.c69
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);
880static inline void radix_tree_shrink(struct radix_tree_root *root) 886static 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 }