diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/radix-tree.c | 57 |
1 files changed, 44 insertions, 13 deletions
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 1014171dd1c5..e0ee8cb37e41 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -625,6 +625,13 @@ EXPORT_SYMBOL(radix_tree_tag_get); | |||
625 | * also settag. The function stops either after tagging nr_to_tag items or | 625 | * also settag. The function stops either after tagging nr_to_tag items or |
626 | * after reaching last_index. | 626 | * after reaching last_index. |
627 | * | 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 | * | ||
628 | * The function returns number of leaves where the tag was set and sets | 635 | * The function returns number of leaves where the tag was set and sets |
629 | * *first_indexp to the first unscanned index. | 636 | * *first_indexp to the first unscanned index. |
630 | */ | 637 | */ |
@@ -633,9 +640,13 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root, | |||
633 | unsigned long nr_to_tag, | 640 | unsigned long nr_to_tag, |
634 | unsigned int iftag, unsigned int settag) | 641 | unsigned int iftag, unsigned int settag) |
635 | { | 642 | { |
636 | unsigned int height = root->height, shift; | 643 | unsigned int height = root->height; |
637 | unsigned long tagged = 0, index = *first_indexp; | 644 | struct radix_tree_path path[height]; |
638 | struct radix_tree_node *open_slots[height], *slot; | 645 | struct radix_tree_path *pathp = path; |
646 | struct radix_tree_node *slot; | ||
647 | unsigned int shift; | ||
648 | unsigned long tagged = 0; | ||
649 | unsigned long index = *first_indexp; | ||
639 | 650 | ||
640 | last_index = min(last_index, radix_tree_maxindex(height)); | 651 | last_index = min(last_index, radix_tree_maxindex(height)); |
641 | if (index > last_index) | 652 | if (index > last_index) |
@@ -655,6 +666,13 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root, | |||
655 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; | 666 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; |
656 | slot = radix_tree_indirect_to_ptr(root->rnode); | 667 | slot = radix_tree_indirect_to_ptr(root->rnode); |
657 | 668 | ||
669 | /* | ||
670 | * we fill the path from (root->height - 2) to 0, leaving the index at | ||
671 | * (root->height - 1) as a terminator. Zero the node in the terminator | ||
672 | * so that we can use this to end walk loops back up the path. | ||
673 | */ | ||
674 | path[height - 1].node = NULL; | ||
675 | |||
658 | for (;;) { | 676 | for (;;) { |
659 | int offset; | 677 | int offset; |
660 | 678 | ||
@@ -663,17 +681,30 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root, | |||
663 | goto next; | 681 | goto next; |
664 | if (!tag_get(slot, iftag, offset)) | 682 | if (!tag_get(slot, iftag, offset)) |
665 | goto next; | 683 | goto next; |
684 | if (height > 1) { | ||
685 | /* Go down one level */ | ||
686 | height--; | ||
687 | shift -= RADIX_TREE_MAP_SHIFT; | ||
688 | path[height - 1].node = slot; | ||
689 | path[height - 1].offset = offset; | ||
690 | slot = slot->slots[offset]; | ||
691 | continue; | ||
692 | } | ||
693 | |||
694 | /* tag the leaf */ | ||
695 | tagged++; | ||
666 | tag_set(slot, settag, offset); | 696 | tag_set(slot, settag, offset); |
667 | if (height == 1) { | 697 | |
668 | tagged++; | 698 | /* walk back up the path tagging interior nodes */ |
669 | goto next; | 699 | pathp = &path[0]; |
700 | while (pathp->node) { | ||
701 | /* stop if we find a node with the tag already set */ | ||
702 | if (tag_get(pathp->node, settag, pathp->offset)) | ||
703 | break; | ||
704 | tag_set(pathp->node, settag, pathp->offset); | ||
705 | pathp++; | ||
670 | } | 706 | } |
671 | /* Go down one level */ | 707 | |
672 | height--; | ||
673 | shift -= RADIX_TREE_MAP_SHIFT; | ||
674 | open_slots[height] = slot; | ||
675 | slot = slot->slots[offset]; | ||
676 | continue; | ||
677 | next: | 708 | next: |
678 | /* Go to next item at level determined by 'shift' */ | 709 | /* Go to next item at level determined by 'shift' */ |
679 | index = ((index >> shift) + 1) << shift; | 710 | index = ((index >> shift) + 1) << shift; |
@@ -687,7 +718,7 @@ next: | |||
687 | * last_index is guaranteed to be in the tree, what | 718 | * last_index is guaranteed to be in the tree, what |
688 | * we do below cannot wander astray. | 719 | * we do below cannot wander astray. |
689 | */ | 720 | */ |
690 | slot = open_slots[height]; | 721 | slot = path[height - 1].node; |
691 | height++; | 722 | height++; |
692 | shift += RADIX_TREE_MAP_SHIFT; | 723 | shift += RADIX_TREE_MAP_SHIFT; |
693 | } | 724 | } |