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 | |
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>
-rw-r--r-- | include/linux/radix-tree.h | 9 | ||||
-rw-r--r-- | lib/radix-tree.c | 76 | ||||
-rw-r--r-- | mm/filemap.c | 23 |
3 files changed, 56 insertions, 52 deletions
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index bad63105e37e..11c8e7cc3920 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h | |||
@@ -281,9 +281,12 @@ bool __radix_tree_delete_node(struct radix_tree_root *root, | |||
281 | struct radix_tree_node *node); | 281 | struct radix_tree_node *node); |
282 | void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *); | 282 | void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *); |
283 | void *radix_tree_delete(struct radix_tree_root *, unsigned long); | 283 | void *radix_tree_delete(struct radix_tree_root *, unsigned long); |
284 | unsigned int | 284 | struct radix_tree_node *radix_tree_replace_clear_tags( |
285 | radix_tree_gang_lookup(struct radix_tree_root *root, void **results, | 285 | struct radix_tree_root *root, |
286 | unsigned long first_index, unsigned int max_items); | 286 | unsigned long index, void *entry); |
287 | unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, | ||
288 | void **results, unsigned long first_index, | ||
289 | unsigned int max_items); | ||
287 | unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root, | 290 | unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root, |
288 | void ***results, unsigned long *indices, | 291 | void ***results, unsigned long *indices, |
289 | unsigned long first_index, unsigned int max_items); | 292 | unsigned long first_index, unsigned int max_items); |
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 |
diff --git a/mm/filemap.c b/mm/filemap.c index b418405903bc..9665b1d4f318 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
@@ -114,14 +114,11 @@ static void page_cache_tree_delete(struct address_space *mapping, | |||
114 | struct page *page, void *shadow) | 114 | struct page *page, void *shadow) |
115 | { | 115 | { |
116 | struct radix_tree_node *node; | 116 | struct radix_tree_node *node; |
117 | unsigned long index; | ||
118 | unsigned int offset; | ||
119 | unsigned int tag; | ||
120 | void **slot; | ||
121 | 117 | ||
122 | VM_BUG_ON(!PageLocked(page)); | 118 | VM_BUG_ON(!PageLocked(page)); |
123 | 119 | ||
124 | __radix_tree_lookup(&mapping->page_tree, page->index, &node, &slot); | 120 | node = radix_tree_replace_clear_tags(&mapping->page_tree, page->index, |
121 | shadow); | ||
125 | 122 | ||
126 | if (shadow) { | 123 | if (shadow) { |
127 | mapping->nrexceptional++; | 124 | mapping->nrexceptional++; |
@@ -135,23 +132,9 @@ static void page_cache_tree_delete(struct address_space *mapping, | |||
135 | } | 132 | } |
136 | mapping->nrpages--; | 133 | mapping->nrpages--; |
137 | 134 | ||
138 | if (!node) { | 135 | if (!node) |
139 | /* Clear direct pointer tags in root node */ | ||
140 | mapping->page_tree.gfp_mask &= __GFP_BITS_MASK; | ||
141 | radix_tree_replace_slot(slot, shadow); | ||
142 | return; | 136 | return; |
143 | } | ||
144 | |||
145 | /* Clear tree tags for the removed page */ | ||
146 | index = page->index; | ||
147 | offset = index & RADIX_TREE_MAP_MASK; | ||
148 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { | ||
149 | if (test_bit(offset, node->tags[tag])) | ||
150 | radix_tree_tag_clear(&mapping->page_tree, index, tag); | ||
151 | } | ||
152 | 137 | ||
153 | /* Delete page, swap shadow entry */ | ||
154 | radix_tree_replace_slot(slot, shadow); | ||
155 | workingset_node_pages_dec(node); | 138 | workingset_node_pages_dec(node); |
156 | if (shadow) | 139 | if (shadow) |
157 | workingset_node_shadows_inc(node); | 140 | workingset_node_shadows_inc(node); |