aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2007-10-16 04:24:42 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-16 12:42:53 -0400
commitc0bc9875b701c588e448302d41181995c21e8040 (patch)
treec320855d4c04bd51ded6b4888bc5895ab539165f /lib
parentb55ed816235cf41c29159d22a4cdeec7deb5821c (diff)
radix-tree: use indirect bit
Rather than sign direct radix-tree pointers with a special bit, sign the indirect one that hangs off the root. This means that, given a lookup_slot operation, the invalid result will be differentiated from the valid (previously, valid results could have the bit either set or clear). This does not affect slot lookups which occur under lock -- they can never return an invalid result. Is needed in future for lockless pagecache. Signed-off-by: Nick Piggin <npiggin@suse.de> Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Hugh Dickins <hugh@veritas.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
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 }