diff options
Diffstat (limited to 'lib/radix-tree.c')
-rw-r--r-- | lib/radix-tree.c | 134 |
1 files changed, 132 insertions, 2 deletions
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 05da38bcc298..efd16fa80b1c 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -174,14 +174,16 @@ static void radix_tree_node_rcu_free(struct rcu_head *head) | |||
174 | { | 174 | { |
175 | struct radix_tree_node *node = | 175 | struct radix_tree_node *node = |
176 | container_of(head, struct radix_tree_node, rcu_head); | 176 | container_of(head, struct radix_tree_node, rcu_head); |
177 | int i; | ||
177 | 178 | ||
178 | /* | 179 | /* |
179 | * must only free zeroed nodes into the slab. radix_tree_shrink | 180 | * must only free zeroed nodes into the slab. radix_tree_shrink |
180 | * can leave us with a non-NULL entry in the first slot, so clear | 181 | * can leave us with a non-NULL entry in the first slot, so clear |
181 | * that here to make sure. | 182 | * that here to make sure. |
182 | */ | 183 | */ |
183 | tag_clear(node, 0, 0); | 184 | for (i = 0; i < RADIX_TREE_MAX_TAGS; i++) |
184 | tag_clear(node, 1, 0); | 185 | tag_clear(node, i, 0); |
186 | |||
185 | node->slots[0] = NULL; | 187 | node->slots[0] = NULL; |
186 | node->count = 0; | 188 | node->count = 0; |
187 | 189 | ||
@@ -609,6 +611,134 @@ int radix_tree_tag_get(struct radix_tree_root *root, | |||
609 | EXPORT_SYMBOL(radix_tree_tag_get); | 611 | EXPORT_SYMBOL(radix_tree_tag_get); |
610 | 612 | ||
611 | /** | 613 | /** |
614 | * radix_tree_range_tag_if_tagged - for each item in given range set given | ||
615 | * tag if item has another tag set | ||
616 | * @root: radix tree root | ||
617 | * @first_indexp: pointer to a starting index of a range to scan | ||
618 | * @last_index: last index of a range to scan | ||
619 | * @nr_to_tag: maximum number items to tag | ||
620 | * @iftag: tag index to test | ||
621 | * @settag: tag index to set if tested tag is set | ||
622 | * | ||
623 | * This function scans range of radix tree from first_index to last_index | ||
624 | * (inclusive). For each item in the range if iftag is set, the function sets | ||
625 | * also settag. The function stops either after tagging nr_to_tag items or | ||
626 | * after reaching last_index. | ||
627 | * | ||
628 | * The tags must be set from the leaf level only and propagated back up the | ||
629 | * path to the root. We must do this so that we resolve the full path before | ||
630 | * setting any tags on intermediate nodes. If we set tags as we descend, then | ||
631 | * we can get to the leaf node and find that the index that has the iftag | ||
632 | * set is outside the range we are scanning. This reults in dangling tags and | ||
633 | * can lead to problems with later tag operations (e.g. livelocks on lookups). | ||
634 | * | ||
635 | * The function returns number of leaves where the tag was set and sets | ||
636 | * *first_indexp to the first unscanned index. | ||
637 | * WARNING! *first_indexp can wrap if last_index is ULONG_MAX. Caller must | ||
638 | * be prepared to handle that. | ||
639 | */ | ||
640 | unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root, | ||
641 | unsigned long *first_indexp, unsigned long last_index, | ||
642 | unsigned long nr_to_tag, | ||
643 | unsigned int iftag, unsigned int settag) | ||
644 | { | ||
645 | unsigned int height = root->height; | ||
646 | struct radix_tree_path path[height]; | ||
647 | struct radix_tree_path *pathp = path; | ||
648 | struct radix_tree_node *slot; | ||
649 | unsigned int shift; | ||
650 | unsigned long tagged = 0; | ||
651 | unsigned long index = *first_indexp; | ||
652 | |||
653 | last_index = min(last_index, radix_tree_maxindex(height)); | ||
654 | if (index > last_index) | ||
655 | return 0; | ||
656 | if (!nr_to_tag) | ||
657 | return 0; | ||
658 | if (!root_tag_get(root, iftag)) { | ||
659 | *first_indexp = last_index + 1; | ||
660 | return 0; | ||
661 | } | ||
662 | if (height == 0) { | ||
663 | *first_indexp = last_index + 1; | ||
664 | root_tag_set(root, settag); | ||
665 | return 1; | ||
666 | } | ||
667 | |||
668 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; | ||
669 | slot = radix_tree_indirect_to_ptr(root->rnode); | ||
670 | |||
671 | /* | ||
672 | * we fill the path from (root->height - 2) to 0, leaving the index at | ||
673 | * (root->height - 1) as a terminator. Zero the node in the terminator | ||
674 | * so that we can use this to end walk loops back up the path. | ||
675 | */ | ||
676 | path[height - 1].node = NULL; | ||
677 | |||
678 | for (;;) { | ||
679 | int offset; | ||
680 | |||
681 | offset = (index >> shift) & RADIX_TREE_MAP_MASK; | ||
682 | if (!slot->slots[offset]) | ||
683 | goto next; | ||
684 | if (!tag_get(slot, iftag, offset)) | ||
685 | goto next; | ||
686 | if (height > 1) { | ||
687 | /* Go down one level */ | ||
688 | height--; | ||
689 | shift -= RADIX_TREE_MAP_SHIFT; | ||
690 | path[height - 1].node = slot; | ||
691 | path[height - 1].offset = offset; | ||
692 | slot = slot->slots[offset]; | ||
693 | continue; | ||
694 | } | ||
695 | |||
696 | /* tag the leaf */ | ||
697 | tagged++; | ||
698 | tag_set(slot, settag, offset); | ||
699 | |||
700 | /* walk back up the path tagging interior nodes */ | ||
701 | pathp = &path[0]; | ||
702 | while (pathp->node) { | ||
703 | /* stop if we find a node with the tag already set */ | ||
704 | if (tag_get(pathp->node, settag, pathp->offset)) | ||
705 | break; | ||
706 | tag_set(pathp->node, settag, pathp->offset); | ||
707 | pathp++; | ||
708 | } | ||
709 | |||
710 | next: | ||
711 | /* Go to next item at level determined by 'shift' */ | ||
712 | index = ((index >> shift) + 1) << shift; | ||
713 | /* Overflow can happen when last_index is ~0UL... */ | ||
714 | if (index > last_index || !index) | ||
715 | break; | ||
716 | if (tagged >= nr_to_tag) | ||
717 | break; | ||
718 | while (((index >> shift) & RADIX_TREE_MAP_MASK) == 0) { | ||
719 | /* | ||
720 | * We've fully scanned this node. Go up. Because | ||
721 | * last_index is guaranteed to be in the tree, what | ||
722 | * we do below cannot wander astray. | ||
723 | */ | ||
724 | slot = path[height - 1].node; | ||
725 | height++; | ||
726 | shift += RADIX_TREE_MAP_SHIFT; | ||
727 | } | ||
728 | } | ||
729 | /* | ||
730 | * The iftag must have been set somewhere because otherwise | ||
731 | * we would return immediated at the beginning of the function | ||
732 | */ | ||
733 | root_tag_set(root, settag); | ||
734 | *first_indexp = index; | ||
735 | |||
736 | return tagged; | ||
737 | } | ||
738 | EXPORT_SYMBOL(radix_tree_range_tag_if_tagged); | ||
739 | |||
740 | |||
741 | /** | ||
612 | * radix_tree_next_hole - find the next hole (not-present entry) | 742 | * radix_tree_next_hole - find the next hole (not-present entry) |
613 | * @root: tree root | 743 | * @root: tree root |
614 | * @index: index key | 744 | * @index: index key |