123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- From: Alexander Duyck <alexander.h.duyck@redhat.com>
- Date: Thu, 22 Jan 2015 15:51:14 -0800
- Subject: [PATCH] fib_trie: Fix RCU bug and merge similar bits of inflate/halve
- This patch addresses two issues.
- The first issue is the fact that I believe I had the RCU freeing sequence
- slightly out of order. As a result we could get into an issue if a caller
- went into a child of a child of the new node, then backtraced into the to be
- freed parent, and then attempted to access a child of a child that may have
- been consumed in a resize of one of the new nodes children. To resolve this I
- have moved the resize after we have freed the oldtnode. The only side effect
- of this is that we will now be calling resize on more nodes in the case of
- inflate due to the fact that we don't have a good way to test to see if a
- full_tnode on the new node was there before or after the allocation. This
- should have minimal impact however since the node should already be
- correctly size so it is just the cost of calling should_inflate that we
- will be taking on the node which is only a couple of cycles.
- The second issue is the fact that inflate and halve were essentially doing
- the same thing after the new node was added to the trie replacing the old
- one. As such it wasn't really necessary to keep the code in both functions
- so I have split it out into two other functions, called replace and
- update_children.
- Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
- Signed-off-by: David S. Miller <davem@davemloft.net>
- ---
- --- a/net/ipv4/fib_trie.c
- +++ b/net/ipv4/fib_trie.c
- @@ -396,8 +396,30 @@ static void put_child(struct tnode *tn,
- rcu_assign_pointer(tn->child[i], n);
- }
-
- -static void put_child_root(struct tnode *tp, struct trie *t,
- - t_key key, struct tnode *n)
- +static void update_children(struct tnode *tn)
- +{
- + unsigned long i;
- +
- + /* update all of the child parent pointers */
- + for (i = tnode_child_length(tn); i;) {
- + struct tnode *inode = tnode_get_child(tn, --i);
- +
- + if (!inode)
- + continue;
- +
- + /* Either update the children of a tnode that
- + * already belongs to us or update the child
- + * to point to ourselves.
- + */
- + if (node_parent(inode) == tn)
- + update_children(inode);
- + else
- + node_set_parent(inode, tn);
- + }
- +}
- +
- +static inline void put_child_root(struct tnode *tp, struct trie *t,
- + t_key key, struct tnode *n)
- {
- if (tp)
- put_child(tp, get_index(key, tp), n);
- @@ -434,10 +456,35 @@ static void tnode_free(struct tnode *tn)
- }
- }
-
- +static void replace(struct trie *t, struct tnode *oldtnode, struct tnode *tn)
- +{
- + struct tnode *tp = node_parent(oldtnode);
- + unsigned long i;
- +
- + /* setup the parent pointer out of and back into this node */
- + NODE_INIT_PARENT(tn, tp);
- + put_child_root(tp, t, tn->key, tn);
- +
- + /* update all of the child parent pointers */
- + update_children(tn);
- +
- + /* all pointers should be clean so we are done */
- + tnode_free(oldtnode);
- +
- + /* resize children now that oldtnode is freed */
- + for (i = tnode_child_length(tn); i;) {
- + struct tnode *inode = tnode_get_child(tn, --i);
- +
- + /* resize child node */
- + if (tnode_full(tn, inode))
- + resize(t, inode);
- + }
- +}
- +
- static int inflate(struct trie *t, struct tnode *oldtnode)
- {
- - struct tnode *inode, *node0, *node1, *tn, *tp;
- - unsigned long i, j, k;
- + struct tnode *tn;
- + unsigned long i;
- t_key m;
-
- pr_debug("In inflate\n");
- @@ -446,13 +493,18 @@ static int inflate(struct trie *t, struc
- if (!tn)
- return -ENOMEM;
-
- + /* prepare oldtnode to be freed */
- + tnode_free_init(oldtnode);
- +
- /* Assemble all of the pointers in our cluster, in this case that
- * represents all of the pointers out of our allocated nodes that
- * point to existing tnodes and the links between our allocated
- * nodes.
- */
- for (i = tnode_child_length(oldtnode), m = 1u << tn->pos; i;) {
- - inode = tnode_get_child(oldtnode, --i);
- + struct tnode *inode = tnode_get_child(oldtnode, --i);
- + struct tnode *node0, *node1;
- + unsigned long j, k;
-
- /* An empty child */
- if (inode == NULL)
- @@ -464,6 +516,9 @@ static int inflate(struct trie *t, struc
- continue;
- }
-
- + /* drop the node in the old tnode free list */
- + tnode_free_append(oldtnode, inode);
- +
- /* An internal node with two children */
- if (inode->bits == 1) {
- put_child(tn, 2 * i + 1, tnode_get_child(inode, 1));
- @@ -488,9 +543,9 @@ static int inflate(struct trie *t, struc
- node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1);
- if (!node1)
- goto nomem;
- - tnode_free_append(tn, node1);
- + node0 = tnode_new(inode->key, inode->pos, inode->bits - 1);
-
- - node0 = tnode_new(inode->key & ~m, inode->pos, inode->bits - 1);
- + tnode_free_append(tn, node1);
- if (!node0)
- goto nomem;
- tnode_free_append(tn, node0);
- @@ -512,53 +567,9 @@ static int inflate(struct trie *t, struc
- put_child(tn, 2 * i, node0);
- }
-
- - /* setup the parent pointer into and out of this node */
- - tp = node_parent(oldtnode);
- - NODE_INIT_PARENT(tn, tp);
- - put_child_root(tp, t, tn->key, tn);
- + /* setup the parent pointers into and out of this node */
- + replace(t, oldtnode, tn);
-
- - /* prepare oldtnode to be freed */
- - tnode_free_init(oldtnode);
- -
- - /* update all child nodes parent pointers to route to us */
- - for (i = tnode_child_length(oldtnode); i;) {
- - inode = tnode_get_child(oldtnode, --i);
- -
- - /* A leaf or an internal node with skipped bits */
- - if (!tnode_full(oldtnode, inode)) {
- - node_set_parent(inode, tn);
- - continue;
- - }
- -
- - /* drop the node in the old tnode free list */
- - tnode_free_append(oldtnode, inode);
- -
- - /* fetch new nodes */
- - node1 = tnode_get_child(tn, 2 * i + 1);
- - node0 = tnode_get_child(tn, 2 * i);
- -
- - /* bits == 1 then node0 and node1 represent inode's children */
- - if (inode->bits == 1) {
- - node_set_parent(node1, tn);
- - node_set_parent(node0, tn);
- - continue;
- - }
- -
- - /* update parent pointers in child node's children */
- - for (k = tnode_child_length(inode), j = k / 2; j;) {
- - node_set_parent(tnode_get_child(inode, --k), node1);
- - node_set_parent(tnode_get_child(inode, --j), node0);
- - node_set_parent(tnode_get_child(inode, --k), node1);
- - node_set_parent(tnode_get_child(inode, --j), node0);
- - }
- -
- - /* resize child nodes */
- - resize(t, node1);
- - resize(t, node0);
- - }
- -
- - /* we completed without error, prepare to free old node */
- - tnode_free(oldtnode);
- return 0;
- nomem:
- /* all pointers should be clean so we are done */
- @@ -568,7 +579,7 @@ nomem:
-
- static int halve(struct trie *t, struct tnode *oldtnode)
- {
- - struct tnode *tn, *tp, *inode, *node0, *node1;
- + struct tnode *tn;
- unsigned long i;
-
- pr_debug("In halve\n");
- @@ -577,14 +588,18 @@ static int halve(struct trie *t, struct
- if (!tn)
- return -ENOMEM;
-
- + /* prepare oldtnode to be freed */
- + tnode_free_init(oldtnode);
- +
- /* Assemble all of the pointers in our cluster, in this case that
- * represents all of the pointers out of our allocated nodes that
- * point to existing tnodes and the links between our allocated
- * nodes.
- */
- for (i = tnode_child_length(oldtnode); i;) {
- - node1 = tnode_get_child(oldtnode, --i);
- - node0 = tnode_get_child(oldtnode, --i);
- + struct tnode *node1 = tnode_get_child(oldtnode, --i);
- + struct tnode *node0 = tnode_get_child(oldtnode, --i);
- + struct tnode *inode;
-
- /* At least one of the children is empty */
- if (!node1 || !node0) {
- @@ -609,34 +624,8 @@ static int halve(struct trie *t, struct
- put_child(tn, i / 2, inode);
- }
-
- - /* setup the parent pointer out of and back into this node */
- - tp = node_parent(oldtnode);
- - NODE_INIT_PARENT(tn, tp);
- - put_child_root(tp, t, tn->key, tn);
- -
- - /* prepare oldtnode to be freed */
- - tnode_free_init(oldtnode);
- -
- - /* update all of the child parent pointers */
- - for (i = tnode_child_length(tn); i;) {
- - inode = tnode_get_child(tn, --i);
- -
- - /* only new tnodes will be considered "full" nodes */
- - if (!tnode_full(tn, inode)) {
- - node_set_parent(inode, tn);
- - continue;
- - }
- -
- - /* Two nonempty children */
- - node_set_parent(tnode_get_child(inode, 1), inode);
- - node_set_parent(tnode_get_child(inode, 0), inode);
- -
- - /* resize child node */
- - resize(t, inode);
- - }
- -
- - /* all pointers should be clean so we are done */
- - tnode_free(oldtnode);
- + /* setup the parent pointers into and out of this node */
- + replace(t, oldtnode, tn);
-
- return 0;
- }
|