diff options
Diffstat (limited to 'lib/radix-tree.c')
-rw-r--r-- | lib/radix-tree.c | 143 |
1 files changed, 85 insertions, 58 deletions
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 88511c3805ad..c0bd4a914803 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -137,18 +137,31 @@ out: | |||
137 | 137 | ||
138 | static inline void tag_set(struct radix_tree_node *node, int tag, int offset) | 138 | static inline void tag_set(struct radix_tree_node *node, int tag, int offset) |
139 | { | 139 | { |
140 | if (!test_bit(offset, &node->tags[tag][0])) | 140 | __set_bit(offset, node->tags[tag]); |
141 | __set_bit(offset, &node->tags[tag][0]); | ||
142 | } | 141 | } |
143 | 142 | ||
144 | static inline void tag_clear(struct radix_tree_node *node, int tag, int offset) | 143 | static inline void tag_clear(struct radix_tree_node *node, int tag, int offset) |
145 | { | 144 | { |
146 | __clear_bit(offset, &node->tags[tag][0]); | 145 | __clear_bit(offset, node->tags[tag]); |
147 | } | 146 | } |
148 | 147 | ||
149 | static inline int tag_get(struct radix_tree_node *node, int tag, int offset) | 148 | static inline int tag_get(struct radix_tree_node *node, int tag, int offset) |
150 | { | 149 | { |
151 | return test_bit(offset, &node->tags[tag][0]); | 150 | return test_bit(offset, node->tags[tag]); |
151 | } | ||
152 | |||
153 | /* | ||
154 | * Returns 1 if any slot in the node has this tag set. | ||
155 | * Otherwise returns 0. | ||
156 | */ | ||
157 | static inline int any_tag_set(struct radix_tree_node *node, int tag) | ||
158 | { | ||
159 | int idx; | ||
160 | for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { | ||
161 | if (node->tags[tag][idx]) | ||
162 | return 1; | ||
163 | } | ||
164 | return 0; | ||
152 | } | 165 | } |
153 | 166 | ||
154 | /* | 167 | /* |
@@ -185,15 +198,9 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) | |||
185 | * into the newly-pushed top-level node(s) | 198 | * into the newly-pushed top-level node(s) |
186 | */ | 199 | */ |
187 | for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { | 200 | for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { |
188 | int idx; | ||
189 | |||
190 | tags[tag] = 0; | 201 | tags[tag] = 0; |
191 | for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { | 202 | if (any_tag_set(root->rnode, tag)) |
192 | if (root->rnode->tags[tag][idx]) { | 203 | tags[tag] = 1; |
193 | tags[tag] = 1; | ||
194 | break; | ||
195 | } | ||
196 | } | ||
197 | } | 204 | } |
198 | 205 | ||
199 | do { | 206 | do { |
@@ -246,7 +253,7 @@ int radix_tree_insert(struct radix_tree_root *root, | |||
246 | shift = (height-1) * RADIX_TREE_MAP_SHIFT; | 253 | shift = (height-1) * RADIX_TREE_MAP_SHIFT; |
247 | 254 | ||
248 | offset = 0; /* uninitialised var warning */ | 255 | offset = 0; /* uninitialised var warning */ |
249 | while (height > 0) { | 256 | do { |
250 | if (slot == NULL) { | 257 | if (slot == NULL) { |
251 | /* Have to add a child node. */ | 258 | /* Have to add a child node. */ |
252 | if (!(slot = radix_tree_node_alloc(root))) | 259 | if (!(slot = radix_tree_node_alloc(root))) |
@@ -264,18 +271,16 @@ int radix_tree_insert(struct radix_tree_root *root, | |||
264 | slot = node->slots[offset]; | 271 | slot = node->slots[offset]; |
265 | shift -= RADIX_TREE_MAP_SHIFT; | 272 | shift -= RADIX_TREE_MAP_SHIFT; |
266 | height--; | 273 | height--; |
267 | } | 274 | } while (height > 0); |
268 | 275 | ||
269 | if (slot != NULL) | 276 | if (slot != NULL) |
270 | return -EEXIST; | 277 | return -EEXIST; |
271 | 278 | ||
272 | if (node) { | 279 | BUG_ON(!node); |
273 | node->count++; | 280 | node->count++; |
274 | node->slots[offset] = item; | 281 | node->slots[offset] = item; |
275 | BUG_ON(tag_get(node, 0, offset)); | 282 | BUG_ON(tag_get(node, 0, offset)); |
276 | BUG_ON(tag_get(node, 1, offset)); | 283 | BUG_ON(tag_get(node, 1, offset)); |
277 | } else | ||
278 | root->rnode = item; | ||
279 | 284 | ||
280 | return 0; | 285 | return 0; |
281 | } | 286 | } |
@@ -367,7 +372,8 @@ void *radix_tree_tag_set(struct radix_tree_root *root, | |||
367 | int offset; | 372 | int offset; |
368 | 373 | ||
369 | offset = (index >> shift) & RADIX_TREE_MAP_MASK; | 374 | offset = (index >> shift) & RADIX_TREE_MAP_MASK; |
370 | tag_set(slot, tag, offset); | 375 | if (!tag_get(slot, tag, offset)) |
376 | tag_set(slot, tag, offset); | ||
371 | slot = slot->slots[offset]; | 377 | slot = slot->slots[offset]; |
372 | BUG_ON(slot == NULL); | 378 | BUG_ON(slot == NULL); |
373 | shift -= RADIX_TREE_MAP_SHIFT; | 379 | shift -= RADIX_TREE_MAP_SHIFT; |
@@ -427,13 +433,11 @@ void *radix_tree_tag_clear(struct radix_tree_root *root, | |||
427 | goto out; | 433 | goto out; |
428 | 434 | ||
429 | do { | 435 | do { |
430 | int idx; | 436 | if (!tag_get(pathp->node, tag, pathp->offset)) |
431 | 437 | goto out; | |
432 | tag_clear(pathp->node, tag, pathp->offset); | 438 | tag_clear(pathp->node, tag, pathp->offset); |
433 | for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { | 439 | if (any_tag_set(pathp->node, tag)) |
434 | if (pathp->node->tags[tag][idx]) | 440 | goto out; |
435 | goto out; | ||
436 | } | ||
437 | pathp--; | 441 | pathp--; |
438 | } while (pathp->node); | 442 | } while (pathp->node); |
439 | out: | 443 | out: |
@@ -674,6 +678,29 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, | |||
674 | EXPORT_SYMBOL(radix_tree_gang_lookup_tag); | 678 | EXPORT_SYMBOL(radix_tree_gang_lookup_tag); |
675 | 679 | ||
676 | /** | 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 | /** | ||
677 | * radix_tree_delete - delete an item from a radix tree | 704 | * radix_tree_delete - delete an item from a radix tree |
678 | * @root: radix tree root | 705 | * @root: radix tree root |
679 | * @index: index key | 706 | * @index: index key |
@@ -691,6 +718,8 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) | |||
691 | void *ret = NULL; | 718 | void *ret = NULL; |
692 | char tags[RADIX_TREE_TAGS]; | 719 | char tags[RADIX_TREE_TAGS]; |
693 | int nr_cleared_tags; | 720 | int nr_cleared_tags; |
721 | int tag; | ||
722 | int offset; | ||
694 | 723 | ||
695 | height = root->height; | 724 | height = root->height; |
696 | if (index > radix_tree_maxindex(height)) | 725 | if (index > radix_tree_maxindex(height)) |
@@ -701,16 +730,14 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) | |||
701 | slot = root->rnode; | 730 | slot = root->rnode; |
702 | 731 | ||
703 | for ( ; height > 0; height--) { | 732 | for ( ; height > 0; height--) { |
704 | int offset; | ||
705 | |||
706 | if (slot == NULL) | 733 | if (slot == NULL) |
707 | goto out; | 734 | goto out; |
708 | 735 | ||
736 | pathp++; | ||
709 | offset = (index >> shift) & RADIX_TREE_MAP_MASK; | 737 | offset = (index >> shift) & RADIX_TREE_MAP_MASK; |
710 | pathp[1].offset = offset; | 738 | pathp->offset = offset; |
711 | pathp[1].node = slot; | 739 | pathp->node = slot; |
712 | slot = slot->slots[offset]; | 740 | slot = slot->slots[offset]; |
713 | pathp++; | ||
714 | shift -= RADIX_TREE_MAP_SHIFT; | 741 | shift -= RADIX_TREE_MAP_SHIFT; |
715 | } | 742 | } |
716 | 743 | ||
@@ -723,35 +750,39 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) | |||
723 | /* | 750 | /* |
724 | * Clear all tags associated with the just-deleted item | 751 | * Clear all tags associated with the just-deleted item |
725 | */ | 752 | */ |
726 | memset(tags, 0, sizeof(tags)); | 753 | nr_cleared_tags = 0; |
727 | do { | 754 | for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { |
728 | int tag; | 755 | if (tag_get(pathp->node, tag, pathp->offset)) { |
756 | tag_clear(pathp->node, tag, pathp->offset); | ||
757 | tags[tag] = 0; | ||
758 | nr_cleared_tags++; | ||
759 | } else | ||
760 | tags[tag] = 1; | ||
761 | } | ||
729 | 762 | ||
730 | nr_cleared_tags = RADIX_TREE_TAGS; | 763 | for (pathp--; nr_cleared_tags && pathp->node; pathp--) { |
731 | for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { | 764 | for (tag = 0; tag < RADIX_TREE_TAGS; tag++) { |
732 | int idx; | ||
733 | |||
734 | if (tags[tag]) | 765 | if (tags[tag]) |
735 | continue; | 766 | continue; |
736 | 767 | ||
737 | tag_clear(pathp->node, tag, pathp->offset); | 768 | tag_clear(pathp->node, tag, pathp->offset); |
738 | 769 | if (any_tag_set(pathp->node, tag)) { | |
739 | for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { | 770 | tags[tag] = 1; |
740 | if (pathp->node->tags[tag][idx]) { | 771 | nr_cleared_tags--; |
741 | tags[tag] = 1; | ||
742 | nr_cleared_tags--; | ||
743 | break; | ||
744 | } | ||
745 | } | 772 | } |
746 | } | 773 | } |
747 | pathp--; | 774 | } |
748 | } while (pathp->node && nr_cleared_tags); | ||
749 | 775 | ||
750 | /* Now free the nodes we do not need anymore */ | 776 | /* Now free the nodes we do not need anymore */ |
751 | for (pathp = orig_pathp; pathp->node; pathp--) { | 777 | for (pathp = orig_pathp; pathp->node; pathp--) { |
752 | pathp->node->slots[pathp->offset] = NULL; | 778 | pathp->node->slots[pathp->offset] = NULL; |
753 | 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); | ||
754 | goto out; | 784 | goto out; |
785 | } | ||
755 | 786 | ||
756 | /* Node with zero slots in use so free it */ | 787 | /* Node with zero slots in use so free it */ |
757 | radix_tree_node_free(pathp->node); | 788 | radix_tree_node_free(pathp->node); |
@@ -770,15 +801,11 @@ EXPORT_SYMBOL(radix_tree_delete); | |||
770 | */ | 801 | */ |
771 | int radix_tree_tagged(struct radix_tree_root *root, int tag) | 802 | int radix_tree_tagged(struct radix_tree_root *root, int tag) |
772 | { | 803 | { |
773 | int idx; | 804 | struct radix_tree_node *rnode; |
774 | 805 | rnode = root->rnode; | |
775 | if (!root->rnode) | 806 | if (!rnode) |
776 | return 0; | 807 | return 0; |
777 | for (idx = 0; idx < RADIX_TREE_TAG_LONGS; idx++) { | 808 | return any_tag_set(rnode, tag); |
778 | if (root->rnode->tags[tag][idx]) | ||
779 | return 1; | ||
780 | } | ||
781 | return 0; | ||
782 | } | 809 | } |
783 | EXPORT_SYMBOL(radix_tree_tagged); | 810 | EXPORT_SYMBOL(radix_tree_tagged); |
784 | 811 | ||