aboutsummaryrefslogtreecommitdiffstats
path: root/lib/radix-tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/radix-tree.c')
-rw-r--r--lib/radix-tree.c143
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
138static inline void tag_set(struct radix_tree_node *node, int tag, int offset) 138static 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
144static inline void tag_clear(struct radix_tree_node *node, int tag, int offset) 143static 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
149static inline int tag_get(struct radix_tree_node *node, int tag, int offset) 148static 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 */
157static 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);
439out: 443out:
@@ -674,6 +678,29 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
674EXPORT_SYMBOL(radix_tree_gang_lookup_tag); 678EXPORT_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 */
684static 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 */
771int radix_tree_tagged(struct radix_tree_root *root, int tag) 802int 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}
783EXPORT_SYMBOL(radix_tree_tagged); 810EXPORT_SYMBOL(radix_tree_tagged);
784 811