Commit a5f51c966720fa519c6ce69b169107dbc5769cdf
Committed by
Linus Torvalds
1 parent
d5274261ea
Exists in
master
and in
4 other branches
[PATCH] radix-tree: reduce tree height upon partial truncation
Shrink the height of a radix tree when it is partially truncated - we only do shrinkage of full truncation at present. Signed-off-by: Nick Piggin <npiggin@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Showing 1 changed file with 36 additions and 10 deletions Side-by-side Diff
lib/radix-tree.c
... | ... | @@ -253,7 +253,7 @@ |
253 | 253 | shift = (height-1) * RADIX_TREE_MAP_SHIFT; |
254 | 254 | |
255 | 255 | offset = 0; /* uninitialised var warning */ |
256 | - while (height > 0) { | |
256 | + do { | |
257 | 257 | if (slot == NULL) { |
258 | 258 | /* Have to add a child node. */ |
259 | 259 | if (!(slot = radix_tree_node_alloc(root))) |
260 | 260 | |
... | ... | @@ -271,18 +271,16 @@ |
271 | 271 | slot = node->slots[offset]; |
272 | 272 | shift -= RADIX_TREE_MAP_SHIFT; |
273 | 273 | height--; |
274 | - } | |
274 | + } while (height > 0); | |
275 | 275 | |
276 | 276 | if (slot != NULL) |
277 | 277 | return -EEXIST; |
278 | 278 | |
279 | - if (node) { | |
280 | - node->count++; | |
281 | - node->slots[offset] = item; | |
282 | - BUG_ON(tag_get(node, 0, offset)); | |
283 | - BUG_ON(tag_get(node, 1, offset)); | |
284 | - } else | |
285 | - root->rnode = item; | |
279 | + BUG_ON(!node); | |
280 | + node->count++; | |
281 | + node->slots[offset] = item; | |
282 | + BUG_ON(tag_get(node, 0, offset)); | |
283 | + BUG_ON(tag_get(node, 1, offset)); | |
286 | 284 | |
287 | 285 | return 0; |
288 | 286 | } |
... | ... | @@ -680,6 +678,29 @@ |
680 | 678 | EXPORT_SYMBOL(radix_tree_gang_lookup_tag); |
681 | 679 | |
682 | 680 | /** |
681 | + * radix_tree_shrink - shrink height of a radix tree to minimal | |
682 | + * @root radix tree root | |
683 | + */ | |
684 | +static inline void radix_tree_shrink(struct radix_tree_root *root) | |
685 | +{ | |
686 | + /* try to shrink tree height */ | |
687 | + while (root->height > 1 && | |
688 | + root->rnode->count == 1 && | |
689 | + root->rnode->slots[0]) { | |
690 | + struct radix_tree_node *to_free = root->rnode; | |
691 | + | |
692 | + root->rnode = to_free->slots[0]; | |
693 | + root->height--; | |
694 | + /* must only free zeroed nodes into the slab */ | |
695 | + tag_clear(to_free, 0, 0); | |
696 | + tag_clear(to_free, 1, 0); | |
697 | + to_free->slots[0] = NULL; | |
698 | + to_free->count = 0; | |
699 | + radix_tree_node_free(to_free); | |
700 | + } | |
701 | +} | |
702 | + | |
703 | +/** | |
683 | 704 | * radix_tree_delete - delete an item from a radix tree |
684 | 705 | * @root: radix tree root |
685 | 706 | * @index: index key |
686 | 707 | |
... | ... | @@ -755,8 +776,13 @@ |
755 | 776 | /* Now free the nodes we do not need anymore */ |
756 | 777 | for (pathp = orig_pathp; pathp->node; pathp--) { |
757 | 778 | pathp->node->slots[pathp->offset] = NULL; |
758 | - if (--pathp->node->count) | |
779 | + pathp->node->count--; | |
780 | + | |
781 | + if (pathp->node->count) { | |
782 | + if (pathp->node == root->rnode) | |
783 | + radix_tree_shrink(root); | |
759 | 784 | goto out; |
785 | + } | |
760 | 786 | |
761 | 787 | /* Node with zero slots in use so free it */ |
762 | 788 | radix_tree_node_free(pathp->node); |