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 | ||
