diff options
author | Matthew Wilcox <mawilcox@microsoft.com> | 2017-01-28 09:56:22 -0500 |
---|---|---|
committer | Matthew Wilcox <mawilcox@microsoft.com> | 2017-02-13 16:09:55 -0500 |
commit | 0ac398ef391b53122976325ab6953456ce8e8310 (patch) | |
tree | 99318e48860947c93bb7a230e66834abd67bef7a | |
parent | 30b888ba950d9f77326b50a4aa2d7d99557d5718 (diff) |
radix-tree: Add radix_tree_iter_delete
Factor the deletion code out into __radix_tree_delete() and provide a
nice iterator-based wrapper around it. If we free the node, advance
the iterator to avoid reading from freed memory.
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
-rw-r--r-- | include/linux/radix-tree.h | 2 | ||||
-rw-r--r-- | lib/radix-tree.c | 91 |
2 files changed, 62 insertions, 31 deletions
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index 8bf4ef448ce1..05f715cb8062 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h | |||
@@ -311,6 +311,8 @@ void __radix_tree_delete_node(struct radix_tree_root *root, | |||
311 | struct radix_tree_node *node, | 311 | struct radix_tree_node *node, |
312 | radix_tree_update_node_t update_node, | 312 | radix_tree_update_node_t update_node, |
313 | void *private); | 313 | void *private); |
314 | void radix_tree_iter_delete(struct radix_tree_root *, | ||
315 | struct radix_tree_iter *iter, void **slot); | ||
314 | void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *); | 316 | void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *); |
315 | void *radix_tree_delete(struct radix_tree_root *, unsigned long); | 317 | void *radix_tree_delete(struct radix_tree_root *, unsigned long); |
316 | void radix_tree_clear_tags(struct radix_tree_root *root, | 318 | void radix_tree_clear_tags(struct radix_tree_root *root, |
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 40f3091c5a6b..7bf7d4e60e43 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -581,10 +581,12 @@ out: | |||
581 | * radix_tree_shrink - shrink radix tree to minimum height | 581 | * radix_tree_shrink - shrink radix tree to minimum height |
582 | * @root radix tree root | 582 | * @root radix tree root |
583 | */ | 583 | */ |
584 | static inline void radix_tree_shrink(struct radix_tree_root *root, | 584 | static inline bool radix_tree_shrink(struct radix_tree_root *root, |
585 | radix_tree_update_node_t update_node, | 585 | radix_tree_update_node_t update_node, |
586 | void *private) | 586 | void *private) |
587 | { | 587 | { |
588 | bool shrunk = false; | ||
589 | |||
588 | for (;;) { | 590 | for (;;) { |
589 | struct radix_tree_node *node = root->rnode; | 591 | struct radix_tree_node *node = root->rnode; |
590 | struct radix_tree_node *child; | 592 | struct radix_tree_node *child; |
@@ -645,20 +647,26 @@ static inline void radix_tree_shrink(struct radix_tree_root *root, | |||
645 | 647 | ||
646 | WARN_ON_ONCE(!list_empty(&node->private_list)); | 648 | WARN_ON_ONCE(!list_empty(&node->private_list)); |
647 | radix_tree_node_free(node); | 649 | radix_tree_node_free(node); |
650 | shrunk = true; | ||
648 | } | 651 | } |
652 | |||
653 | return shrunk; | ||
649 | } | 654 | } |
650 | 655 | ||
651 | static void delete_node(struct radix_tree_root *root, | 656 | static bool delete_node(struct radix_tree_root *root, |
652 | struct radix_tree_node *node, | 657 | struct radix_tree_node *node, |
653 | radix_tree_update_node_t update_node, void *private) | 658 | radix_tree_update_node_t update_node, void *private) |
654 | { | 659 | { |
660 | bool deleted = false; | ||
661 | |||
655 | do { | 662 | do { |
656 | struct radix_tree_node *parent; | 663 | struct radix_tree_node *parent; |
657 | 664 | ||
658 | if (node->count) { | 665 | if (node->count) { |
659 | if (node == entry_to_node(root->rnode)) | 666 | if (node == entry_to_node(root->rnode)) |
660 | radix_tree_shrink(root, update_node, private); | 667 | deleted |= radix_tree_shrink(root, update_node, |
661 | return; | 668 | private); |
669 | return deleted; | ||
662 | } | 670 | } |
663 | 671 | ||
664 | parent = node->parent; | 672 | parent = node->parent; |
@@ -672,9 +680,12 @@ static void delete_node(struct radix_tree_root *root, | |||
672 | 680 | ||
673 | WARN_ON_ONCE(!list_empty(&node->private_list)); | 681 | WARN_ON_ONCE(!list_empty(&node->private_list)); |
674 | radix_tree_node_free(node); | 682 | radix_tree_node_free(node); |
683 | deleted = true; | ||
675 | 684 | ||
676 | node = parent; | 685 | node = parent; |
677 | } while (node); | 686 | } while (node); |
687 | |||
688 | return deleted; | ||
678 | } | 689 | } |
679 | 690 | ||
680 | /** | 691 | /** |
@@ -1859,25 +1870,55 @@ void __radix_tree_delete_node(struct radix_tree_root *root, | |||
1859 | delete_node(root, node, update_node, private); | 1870 | delete_node(root, node, update_node, private); |
1860 | } | 1871 | } |
1861 | 1872 | ||
1873 | static bool __radix_tree_delete(struct radix_tree_root *root, | ||
1874 | struct radix_tree_node *node, void **slot) | ||
1875 | { | ||
1876 | unsigned offset = get_slot_offset(node, slot); | ||
1877 | int tag; | ||
1878 | |||
1879 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) | ||
1880 | node_tag_clear(root, node, tag, offset); | ||
1881 | |||
1882 | replace_slot(root, node, slot, NULL, true); | ||
1883 | return node && delete_node(root, node, NULL, NULL); | ||
1884 | } | ||
1885 | |||
1862 | /** | 1886 | /** |
1863 | * radix_tree_delete_item - delete an item from a radix tree | 1887 | * radix_tree_iter_delete - delete the entry at this iterator position |
1864 | * @root: radix tree root | 1888 | * @root: radix tree root |
1865 | * @index: index key | 1889 | * @iter: iterator state |
1866 | * @item: expected item | 1890 | * @slot: pointer to slot |
1867 | * | 1891 | * |
1868 | * Remove @item at @index from the radix tree rooted at @root. | 1892 | * Delete the entry at the position currently pointed to by the iterator. |
1893 | * This may result in the current node being freed; if it is, the iterator | ||
1894 | * is advanced so that it will not reference the freed memory. This | ||
1895 | * function may be called without any locking if there are no other threads | ||
1896 | * which can access this tree. | ||
1897 | */ | ||
1898 | void radix_tree_iter_delete(struct radix_tree_root *root, | ||
1899 | struct radix_tree_iter *iter, void **slot) | ||
1900 | { | ||
1901 | if (__radix_tree_delete(root, iter->node, slot)) | ||
1902 | iter->index = iter->next_index; | ||
1903 | } | ||
1904 | |||
1905 | /** | ||
1906 | * radix_tree_delete_item - delete an item from a radix tree | ||
1907 | * @root: radix tree root | ||
1908 | * @index: index key | ||
1909 | * @item: expected item | ||
1910 | * | ||
1911 | * Remove @item at @index from the radix tree rooted at @root. | ||
1869 | * | 1912 | * |
1870 | * Returns the address of the deleted item, or NULL if it was not present | 1913 | * Return: the deleted entry, or %NULL if it was not present |
1871 | * or the entry at the given @index was not @item. | 1914 | * or the entry at the given @index was not @item. |
1872 | */ | 1915 | */ |
1873 | void *radix_tree_delete_item(struct radix_tree_root *root, | 1916 | void *radix_tree_delete_item(struct radix_tree_root *root, |
1874 | unsigned long index, void *item) | 1917 | unsigned long index, void *item) |
1875 | { | 1918 | { |
1876 | struct radix_tree_node *node; | 1919 | struct radix_tree_node *node; |
1877 | unsigned int offset; | ||
1878 | void **slot; | 1920 | void **slot; |
1879 | void *entry; | 1921 | void *entry; |
1880 | int tag; | ||
1881 | 1922 | ||
1882 | entry = __radix_tree_lookup(root, index, &node, &slot); | 1923 | entry = __radix_tree_lookup(root, index, &node, &slot); |
1883 | if (!entry) | 1924 | if (!entry) |
@@ -1886,32 +1927,20 @@ void *radix_tree_delete_item(struct radix_tree_root *root, | |||
1886 | if (item && entry != item) | 1927 | if (item && entry != item) |
1887 | return NULL; | 1928 | return NULL; |
1888 | 1929 | ||
1889 | if (!node) { | 1930 | __radix_tree_delete(root, node, slot); |
1890 | root_tag_clear_all(root); | ||
1891 | root->rnode = NULL; | ||
1892 | return entry; | ||
1893 | } | ||
1894 | |||
1895 | offset = get_slot_offset(node, slot); | ||
1896 | |||
1897 | /* Clear all tags associated with the item to be deleted. */ | ||
1898 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) | ||
1899 | node_tag_clear(root, node, tag, offset); | ||
1900 | |||
1901 | __radix_tree_replace(root, node, slot, NULL, NULL, NULL); | ||
1902 | 1931 | ||
1903 | return entry; | 1932 | return entry; |
1904 | } | 1933 | } |
1905 | EXPORT_SYMBOL(radix_tree_delete_item); | 1934 | EXPORT_SYMBOL(radix_tree_delete_item); |
1906 | 1935 | ||
1907 | /** | 1936 | /** |
1908 | * radix_tree_delete - delete an item from a radix tree | 1937 | * radix_tree_delete - delete an entry from a radix tree |
1909 | * @root: radix tree root | 1938 | * @root: radix tree root |
1910 | * @index: index key | 1939 | * @index: index key |
1911 | * | 1940 | * |
1912 | * Remove the item at @index from the radix tree rooted at @root. | 1941 | * Remove the entry at @index from the radix tree rooted at @root. |
1913 | * | 1942 | * |
1914 | * Returns the address of the deleted item, or NULL if it was not present. | 1943 | * Return: The deleted entry, or %NULL if it was not present. |
1915 | */ | 1944 | */ |
1916 | void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) | 1945 | void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) |
1917 | { | 1946 | { |