radix-tree: move rcu_head into a union with private_list
authorMatthew Wilcox <willy@infradead.org>
Wed, 14 Dec 2016 23:08:34 +0000 (15:08 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 15 Dec 2016 00:04:10 +0000 (16:04 -0800)
I want to be able to reference node->parent after freeing node.

Currently node->parent is in a union with rcu_head, so it is overwritten
when the node is put on the RCU list.  We know that private_list is not
referenced after the node is freed, so it is safe for these two members
to share space.

Link: http://lkml.kernel.org/r/1480369871-5271-50-git-send-email-mawilcox@linuxonhyperv.com
Signed-off-by: Matthew Wilcox <willy@infradead.org>
Tested-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Ross Zwisler <ross.zwisler@linux.intel.com>
Cc: Matthew Wilcox <mawilcox@microsoft.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/radix-tree.h
lib/radix-tree.c

index 744486057e9e748151f88b699a6b214e8af71de2..d04073ac3a9f863f95b0f076c95e5c54271795e2 100644 (file)
@@ -85,18 +85,12 @@ struct radix_tree_node {
        unsigned char   offset;         /* Slot offset in parent */
        unsigned char   count;          /* Total entry count */
        unsigned char   exceptional;    /* Exceptional entry count */
+       struct radix_tree_node *parent;         /* Used when ascending tree */
+       void *private_data;                     /* For tree user */
        union {
-               struct {
-                       /* Used when ascending tree */
-                       struct radix_tree_node *parent;
-                       /* For tree user */
-                       void *private_data;
-               };
-               /* Used when freeing node */
-               struct rcu_head rcu_head;
+               struct list_head private_list;  /* For tree user */
+               struct rcu_head rcu_head;       /* Used when freeing node */
        };
-       /* For tree user */
-       struct list_head private_list;
        void __rcu      *slots[RADIX_TREE_MAP_SIZE];
        unsigned long   tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
 };
index 60c10361cbfaaf8b95565e378c57502582a4eecc..8c5911eae5e228417b05e4e880d22145babc2985 100644 (file)
@@ -325,6 +325,7 @@ static void radix_tree_node_rcu_free(struct rcu_head *head)
                tag_clear(node, i, 0);
 
        node->slots[0] = NULL;
+       INIT_LIST_HEAD(&node->private_list);
 
        kmem_cache_free(radix_tree_node_cachep, node);
 }