diff options
author | Matthew Wilcox <willy@linux.intel.com> | 2016-05-20 20:03:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-20 20:58:30 -0400 |
commit | d604c324524bf61c68182bb27db64656a78fe911 (patch) | |
tree | 5fedc0671345f97f40e214dfefac23db56b19f8b /lib | |
parent | 89148aa40201def3fa552f9d07dd99740d880ab2 (diff) |
radix-tree: introduce radix_tree_replace_clear_tags()
In addition to replacing the entry, we also clear all associated tags.
This is really a one-off special for page_cache_tree_delete() which had
far too much detailed knowledge about how the radix tree works.
For efficiency, factor node_tag_clear() out of radix_tree_tag_clear() It
can be used by radix_tree_delete_item() as well as
radix_tree_replace_clear_tags().
Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Kirill Shutemov <kirill.shutemov@linux.intel.com>
Cc: Jan Kara <jack@suse.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/radix-tree.c | 76 |
1 files changed, 47 insertions, 29 deletions
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 9d9b4b9af4b6..c7114d233b38 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -740,6 +740,26 @@ void *radix_tree_tag_set(struct radix_tree_root *root, | |||
740 | } | 740 | } |
741 | EXPORT_SYMBOL(radix_tree_tag_set); | 741 | EXPORT_SYMBOL(radix_tree_tag_set); |
742 | 742 | ||
743 | static void node_tag_clear(struct radix_tree_root *root, | ||
744 | struct radix_tree_node *node, | ||
745 | unsigned int tag, unsigned int offset) | ||
746 | { | ||
747 | while (node) { | ||
748 | if (!tag_get(node, tag, offset)) | ||
749 | return; | ||
750 | tag_clear(node, tag, offset); | ||
751 | if (any_tag_set(node, tag)) | ||
752 | return; | ||
753 | |||
754 | offset = node->offset; | ||
755 | node = node->parent; | ||
756 | } | ||
757 | |||
758 | /* clear the root's tag bit */ | ||
759 | if (root_tag_get(root, tag)) | ||
760 | root_tag_clear(root, tag); | ||
761 | } | ||
762 | |||
743 | /** | 763 | /** |
744 | * radix_tree_tag_clear - clear a tag on a radix tree node | 764 | * radix_tree_tag_clear - clear a tag on a radix tree node |
745 | * @root: radix tree root | 765 | * @root: radix tree root |
@@ -776,28 +796,9 @@ void *radix_tree_tag_clear(struct radix_tree_root *root, | |||
776 | offset = radix_tree_descend(parent, &node, offset); | 796 | offset = radix_tree_descend(parent, &node, offset); |
777 | } | 797 | } |
778 | 798 | ||
779 | if (node == NULL) | 799 | if (node) |
780 | goto out; | 800 | node_tag_clear(root, parent, tag, offset); |
781 | 801 | ||
782 | index >>= shift; | ||
783 | |||
784 | while (parent) { | ||
785 | if (!tag_get(parent, tag, offset)) | ||
786 | goto out; | ||
787 | tag_clear(parent, tag, offset); | ||
788 | if (any_tag_set(parent, tag)) | ||
789 | goto out; | ||
790 | |||
791 | index >>= RADIX_TREE_MAP_SHIFT; | ||
792 | offset = index & RADIX_TREE_MAP_MASK; | ||
793 | parent = parent->parent; | ||
794 | } | ||
795 | |||
796 | /* clear the root's tag bit */ | ||
797 | if (root_tag_get(root, tag)) | ||
798 | root_tag_clear(root, tag); | ||
799 | |||
800 | out: | ||
801 | return node; | 802 | return node; |
802 | } | 803 | } |
803 | EXPORT_SYMBOL(radix_tree_tag_clear); | 804 | EXPORT_SYMBOL(radix_tree_tag_clear); |
@@ -1525,14 +1526,9 @@ void *radix_tree_delete_item(struct radix_tree_root *root, | |||
1525 | 1526 | ||
1526 | offset = get_slot_offset(node, slot); | 1527 | offset = get_slot_offset(node, slot); |
1527 | 1528 | ||
1528 | /* | 1529 | /* Clear all tags associated with the item to be deleted. */ |
1529 | * Clear all tags associated with the item to be deleted. | 1530 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) |
1530 | * This way of doing it would be inefficient, but seldom is any set. | 1531 | node_tag_clear(root, node, tag, offset); |
1531 | */ | ||
1532 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { | ||
1533 | if (tag_get(node, tag, offset)) | ||
1534 | radix_tree_tag_clear(root, index, tag); | ||
1535 | } | ||
1536 | 1532 | ||
1537 | delete_sibling_entries(node, node_to_entry(slot), offset); | 1533 | delete_sibling_entries(node, node_to_entry(slot), offset); |
1538 | node->slots[offset] = NULL; | 1534 | node->slots[offset] = NULL; |
@@ -1559,6 +1555,28 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) | |||
1559 | } | 1555 | } |
1560 | EXPORT_SYMBOL(radix_tree_delete); | 1556 | EXPORT_SYMBOL(radix_tree_delete); |
1561 | 1557 | ||
1558 | struct radix_tree_node *radix_tree_replace_clear_tags( | ||
1559 | struct radix_tree_root *root, | ||
1560 | unsigned long index, void *entry) | ||
1561 | { | ||
1562 | struct radix_tree_node *node; | ||
1563 | void **slot; | ||
1564 | |||
1565 | __radix_tree_lookup(root, index, &node, &slot); | ||
1566 | |||
1567 | if (node) { | ||
1568 | unsigned int tag, offset = get_slot_offset(node, slot); | ||
1569 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) | ||
1570 | node_tag_clear(root, node, tag, offset); | ||
1571 | } else { | ||
1572 | /* Clear root node tags */ | ||
1573 | root->gfp_mask &= __GFP_BITS_MASK; | ||
1574 | } | ||
1575 | |||
1576 | radix_tree_replace_slot(slot, entry); | ||
1577 | return node; | ||
1578 | } | ||
1579 | |||
1562 | /** | 1580 | /** |
1563 | * radix_tree_tagged - test whether any items in the tree are tagged | 1581 | * radix_tree_tagged - test whether any items in the tree are tagged |
1564 | * @root: radix tree root | 1582 | * @root: radix tree root |