aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2010-08-22 20:33:53 -0400
committerDave Chinner <david@fromorbit.com>2010-08-22 20:33:53 -0400
commit144dcfc01221e1a79fa47ca897df7d5e3ab298e6 (patch)
tree70b0d0bf73815fb242502a562da3a8c7667843ba
parentb6dd08652e2b70e73661c4975ae46398066c06f8 (diff)
radix-tree: radix_tree_range_tag_if_tagged() can set incorrect tags
Commit ebf8aa44beed48cd17893a83d92a4403e5f9d9e2 ("radix-tree: omplement function radix_tree_range_tag_if_tagged") does not safely set tags on on intermediate tree nodes. The code walks down the tree setting tags before it has fully resolved the path to the leaf under the assumption there will be a leaf slot with the tag set in the range it is searching. Unfortunately, this is not a valid assumption - we can abort after setting a tag on an intermediate node if we overrun the number of tags we are allowed to set in a batch, or stop scanning because we we have passed the last scan index before we reach a leaf slot with the tag we are searching for set. As a result, we can leave the function with tags set on intemediate nodes which can be tripped over later by tag-based lookups. The result of these stale tags is that lookup may end prematurely or livelock because the lookup cannot make progress. The fix for the problem involves reocrding the traversal path we take to the leaf nodes, and only propagating the tags back up the tree once the tag is set in the leaf node slot. We are already recording the path for efficient traversal, so there is no additional overhead to do the intermediately node tag setting in this manner. This fixes a radix tree lookup livelock triggered by the new writeback sync livelock avoidance code introduced in commit f446daaea9d4a420d16c606f755f3689dcb2d0ce ("mm: implement writeback livelock avoidance using page tagging"). Signed-off-by: Dave Chinner <dchinner@redhat.com> Acked-by: Jan Kara <jack@suse.cz>
-rw-r--r--lib/radix-tree.c57
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;
677next: 708next:
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 }