diff options
author | Felix Fietkau <nbd@openwrt.org> | 2015-03-12 15:27:44 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2015-03-12 15:27:44 +0000 |
commit | 944612680d9382720671f1fb731a24eccbbf118c (patch) | |
tree | 66c5e327364854d239af23005e75553728c47128 /target/linux/generic/patches-3.18/080-16-fib_trie-inflate-halve-nodes-in-a-more-RCU-friendly-.patch | |
parent | 0e6ecf707d7e2f10bb7465508548b962cef56ac3 (diff) | |
download | master-31e0f0ae-944612680d9382720671f1fb731a24eccbbf118c.tar.gz master-31e0f0ae-944612680d9382720671f1fb731a24eccbbf118c.tar.bz2 master-31e0f0ae-944612680d9382720671f1fb731a24eccbbf118c.zip |
kernel: backport fib_trie improvements/fixes from 4.0-rc
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 44695
Diffstat (limited to 'target/linux/generic/patches-3.18/080-16-fib_trie-inflate-halve-nodes-in-a-more-RCU-friendly-.patch')
-rw-r--r-- | target/linux/generic/patches-3.18/080-16-fib_trie-inflate-halve-nodes-in-a-more-RCU-friendly-.patch | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/target/linux/generic/patches-3.18/080-16-fib_trie-inflate-halve-nodes-in-a-more-RCU-friendly-.patch b/target/linux/generic/patches-3.18/080-16-fib_trie-inflate-halve-nodes-in-a-more-RCU-friendly-.patch new file mode 100644 index 0000000000..caa2e0e4b1 --- /dev/null +++ b/target/linux/generic/patches-3.18/080-16-fib_trie-inflate-halve-nodes-in-a-more-RCU-friendly-.patch @@ -0,0 +1,345 @@ +From: Alexander Duyck <alexander.h.duyck@redhat.com> +Date: Wed, 31 Dec 2014 10:56:55 -0800 +Subject: [PATCH] fib_trie: inflate/halve nodes in a more RCU friendly + way + +This change pulls the node_set_parent functionality out of put_child_reorg +and instead leaves that to the function to take care of as well. By doing +this we can fully construct the new cluster of tnodes and all of the +pointers out of it before we start routing pointers into it. + +I am suspecting this will likely fix some concurency issues though I don't +have a good test to show as such. + +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 +@@ -391,8 +391,6 @@ static void put_child(struct tnode *tn, + else if (!wasfull && isfull) + tn->full_children++; + +- node_set_parent(n, tn); +- + rcu_assign_pointer(tn->child[i], n); + } + +@@ -436,10 +434,8 @@ static void tnode_free(struct tnode *tn) + + static int inflate(struct trie *t, struct tnode *oldtnode) + { +- unsigned long olen = tnode_child_length(oldtnode); +- struct tnode *tp = node_parent(oldtnode); +- struct tnode *tn; +- unsigned long i; ++ struct tnode *inode, *node0, *node1, *tn, *tp; ++ unsigned long i, j, k; + t_key m; + + pr_debug("In inflate\n"); +@@ -448,43 +444,13 @@ static int inflate(struct trie *t, struc + if (!tn) + return -ENOMEM; + +- /* +- * Preallocate and store tnodes before the actual work so we +- * don't get into an inconsistent state if memory allocation +- * fails. In case of failure we return the oldnode and inflate +- * of tnode is ignored. ++ /* 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 = 0, m = 1u << tn->pos; i < olen; i++) { +- struct tnode *inode = tnode_get_child(oldtnode, i); +- +- if (tnode_full(oldtnode, inode) && (inode->bits > 1)) { +- struct tnode *left, *right; +- +- left = tnode_new(inode->key & ~m, inode->pos, +- inode->bits - 1); +- if (!left) +- goto nomem; +- tnode_free_append(tn, left); +- +- right = tnode_new(inode->key | m, inode->pos, +- inode->bits - 1); +- +- if (!right) +- goto nomem; +- tnode_free_append(tn, right); +- +- put_child(tn, 2*i, left); +- put_child(tn, 2*i+1, right); +- } +- } +- +- /* prepare oldtnode to be freed */ +- tnode_free_init(oldtnode); +- +- for (i = 0; i < olen; i++) { +- struct tnode *inode = tnode_get_child(oldtnode, i); +- struct tnode *left, *right; +- unsigned long size, j; ++ for (i = tnode_child_length(oldtnode), m = 1u << tn->pos; i;) { ++ inode = tnode_get_child(oldtnode, --i); + + /* An empty child */ + if (inode == NULL) +@@ -496,65 +462,99 @@ 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, rtnl_dereference(inode->child[0])); +- put_child(tn, 2*i+1, rtnl_dereference(inode->child[1])); ++ put_child(tn, 2 * i + 1, tnode_get_child(inode, 1)); ++ put_child(tn, 2 * i, tnode_get_child(inode, 0)); + continue; + } + +- /* An internal node with more than two children */ +- + /* We will replace this node 'inode' with two new +- * ones, 'left' and 'right', each with half of the ++ * ones, 'node0' and 'node1', each with half of the + * original children. The two new nodes will have + * a position one bit further down the key and this + * means that the "significant" part of their keys + * (see the discussion near the top of this file) + * will differ by one bit, which will be "0" in +- * left's key and "1" in right's key. Since we are ++ * node0's key and "1" in node1's key. Since we are + * moving the key position by one step, the bit that + * we are moving away from - the bit at position +- * (inode->pos) - is the one that will differ between +- * left and right. So... we synthesize that bit in the +- * two new keys. +- * The mask 'm' below will be a single "one" bit at +- * the position (inode->pos) ++ * (tn->pos) - is the one that will differ between ++ * node0 and node1. So... we synthesize that bit in the ++ * two new keys. + */ ++ 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 & ~m, inode->pos, inode->bits - 1); ++ if (!node0) ++ goto nomem; ++ tnode_free_append(tn, node0); ++ ++ /* populate child pointers in new nodes */ ++ for (k = tnode_child_length(inode), j = k / 2; j;) { ++ put_child(node1, --j, tnode_get_child(inode, --k)); ++ put_child(node0, j, tnode_get_child(inode, j)); ++ put_child(node1, --j, tnode_get_child(inode, --k)); ++ put_child(node0, j, tnode_get_child(inode, j)); ++ } ++ ++ /* link new nodes to parent */ ++ NODE_INIT_PARENT(node1, tn); ++ NODE_INIT_PARENT(node0, tn); ++ ++ /* link parent to nodes */ ++ put_child(tn, 2 * i + 1, node1); ++ 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); + +- /* Use the old key, but set the new significant +- * bit to zero. +- */ ++ /* prepare oldtnode to be freed */ ++ tnode_free_init(oldtnode); + +- left = tnode_get_child(tn, 2*i); +- put_child(tn, 2*i, NULL); ++ /* update all child nodes parent pointers to route to us */ ++ for (i = tnode_child_length(oldtnode); i;) { ++ inode = tnode_get_child(oldtnode, --i); + +- BUG_ON(!left); ++ /* A leaf or an internal node with skipped bits */ ++ if (!tnode_full(oldtnode, inode)) { ++ node_set_parent(inode, tn); ++ continue; ++ } + +- right = tnode_get_child(tn, 2*i+1); +- put_child(tn, 2*i+1, NULL); ++ /* drop the node in the old tnode free list */ ++ tnode_free_append(oldtnode, inode); + +- BUG_ON(!right); ++ /* fetch new nodes */ ++ node1 = tnode_get_child(tn, 2 * i + 1); ++ node0 = tnode_get_child(tn, 2 * i); + +- size = tnode_child_length(left); +- for (j = 0; j < size; j++) { +- put_child(left, j, rtnl_dereference(inode->child[j])); +- put_child(right, j, rtnl_dereference(inode->child[j + size])); ++ /* 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; + } + +- put_child(tn, 2 * i, left); +- put_child(tn, 2 * i + 1, right); ++ /* 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, left); +- resize(t, right); ++ resize(t, node1); ++ resize(t, node0); + } + +- put_child_root(tp, t, tn->key, tn); +- + /* we completed without error, prepare to free old node */ + tnode_free(oldtnode); + return 0; +@@ -566,10 +566,8 @@ nomem: + + static int halve(struct trie *t, struct tnode *oldtnode) + { +- unsigned long olen = tnode_child_length(oldtnode); +- struct tnode *tp = node_parent(oldtnode); +- struct tnode *tn, *left, *right; +- int i; ++ struct tnode *tn, *tp, *inode, *node0, *node1; ++ unsigned long i; + + pr_debug("In halve\n"); + +@@ -577,68 +575,64 @@ static int halve(struct trie *t, struct + if (!tn) + return -ENOMEM; + +- /* +- * Preallocate and store tnodes before the actual work so we +- * don't get into an inconsistent state if memory allocation +- * fails. In case of failure we return the oldnode and halve +- * of tnode is ignored. ++ /* 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); + +- for (i = 0; i < olen; i += 2) { +- left = tnode_get_child(oldtnode, i); +- right = tnode_get_child(oldtnode, i+1); ++ /* At least one of the children is empty */ ++ if (!node1 || !node0) { ++ put_child(tn, i / 2, node1 ? : node0); ++ continue; ++ } + + /* Two nonempty children */ +- if (left && right) { +- struct tnode *newn; +- +- newn = tnode_new(left->key, oldtnode->pos, 1); +- if (!newn) { +- tnode_free(tn); +- return -ENOMEM; +- } +- tnode_free_append(tn, newn); +- +- put_child(tn, i/2, newn); ++ inode = tnode_new(node0->key, oldtnode->pos, 1); ++ if (!inode) { ++ tnode_free(tn); ++ return -ENOMEM; + } ++ tnode_free_append(tn, inode); + ++ /* initialize pointers out of node */ ++ put_child(inode, 1, node1); ++ put_child(inode, 0, node0); ++ NODE_INIT_PARENT(inode, tn); ++ ++ /* link parent to node */ ++ 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); + +- for (i = 0; i < olen; i += 2) { +- struct tnode *newBinNode; +- +- left = tnode_get_child(oldtnode, i); +- right = tnode_get_child(oldtnode, i+1); +- +- /* At least one of the children is empty */ +- if (left == NULL) { +- if (right == NULL) /* Both are empty */ +- continue; +- put_child(tn, i/2, right); +- continue; +- } +- +- if (right == NULL) { +- put_child(tn, i/2, left); ++ /* 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 */ +- newBinNode = tnode_get_child(tn, i/2); +- put_child(newBinNode, 0, left); +- put_child(newBinNode, 1, right); +- +- put_child(tn, i / 2, newBinNode); ++ node_set_parent(tnode_get_child(inode, 1), inode); ++ node_set_parent(tnode_get_child(inode, 0), inode); + + /* resize child node */ +- resize(t, newBinNode); ++ resize(t, inode); + } + +- put_child_root(tp, t, tn->key, tn); +- + /* all pointers should be clean so we are done */ + tnode_free(oldtnode); + |