aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorJarek Poplawski <jarkao2@gmail.com>2009-06-18 03:28:51 -0400
committerDavid S. Miller <davem@davemloft.net>2009-06-18 03:28:51 -0400
commit7b85576d15bf2574b0a451108f59f9ad4170dd3f (patch)
treea2d8075b9d234b0a937d7dd82b95cb16b953aec7 /net/ipv4
parentb964758050f856e44f5fe645d03bea8a1b0b66bd (diff)
ipv4: Fix fib_trie rebalancing, part 2
My previous patch, which explicitly delays freeing of tnodes by adding them to the list to flush them after the update is finished, isn't strict enough. It treats exceptionally tnodes without parent, assuming they are newly created, so "invisible" for the read side yet. But the top tnode doesn't have parent as well, so we have to exclude all exceptions (at least until a better way is found). Additionally we need to move rcu assignment of this node before flushing, so the return type of the trie_rebalance() function is changed. Reported-by: Yan Zheng <zheng.yan@oracle.com> Signed-off-by: Jarek Poplawski <jarkao2@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/fib_trie.c24
1 files changed, 10 insertions, 14 deletions
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index d1a39b1277d6..012cf5a68581 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -391,13 +391,8 @@ static inline void tnode_free(struct tnode *tn)
391static void tnode_free_safe(struct tnode *tn) 391static void tnode_free_safe(struct tnode *tn)
392{ 392{
393 BUG_ON(IS_LEAF(tn)); 393 BUG_ON(IS_LEAF(tn));
394 394 tn->tnode_free = tnode_free_head;
395 if (node_parent((struct node *) tn)) { 395 tnode_free_head = tn;
396 tn->tnode_free = tnode_free_head;
397 tnode_free_head = tn;
398 } else {
399 tnode_free(tn);
400 }
401} 396}
402 397
403static void tnode_free_flush(void) 398static void tnode_free_flush(void)
@@ -1009,7 +1004,7 @@ fib_find_node(struct trie *t, u32 key)
1009 return NULL; 1004 return NULL;
1010} 1005}
1011 1006
1012static struct node *trie_rebalance(struct trie *t, struct tnode *tn) 1007static void trie_rebalance(struct trie *t, struct tnode *tn)
1013{ 1008{
1014 int wasfull; 1009 int wasfull;
1015 t_key cindex, key; 1010 t_key cindex, key;
@@ -1033,12 +1028,13 @@ static struct node *trie_rebalance(struct trie *t, struct tnode *tn)
1033 } 1028 }
1034 1029
1035 /* Handle last (top) tnode */ 1030 /* Handle last (top) tnode */
1036 if (IS_TNODE(tn)) { 1031 if (IS_TNODE(tn))
1037 tn = (struct tnode *)resize(t, (struct tnode *)tn); 1032 tn = (struct tnode *)resize(t, (struct tnode *)tn);
1038 tnode_free_flush();
1039 }
1040 1033
1041 return (struct node *)tn; 1034 rcu_assign_pointer(t->trie, (struct node *)tn);
1035 tnode_free_flush();
1036
1037 return;
1042} 1038}
1043 1039
1044/* only used from updater-side */ 1040/* only used from updater-side */
@@ -1186,7 +1182,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
1186 1182
1187 /* Rebalance the trie */ 1183 /* Rebalance the trie */
1188 1184
1189 rcu_assign_pointer(t->trie, trie_rebalance(t, tp)); 1185 trie_rebalance(t, tp);
1190done: 1186done:
1191 return fa_head; 1187 return fa_head;
1192} 1188}
@@ -1605,7 +1601,7 @@ static void trie_leaf_remove(struct trie *t, struct leaf *l)
1605 if (tp) { 1601 if (tp) {
1606 t_key cindex = tkey_extract_bits(l->key, tp->pos, tp->bits); 1602 t_key cindex = tkey_extract_bits(l->key, tp->pos, tp->bits);
1607 put_child(t, (struct tnode *)tp, cindex, NULL); 1603 put_child(t, (struct tnode *)tp, cindex, NULL);
1608 rcu_assign_pointer(t->trie, trie_rebalance(t, tp)); 1604 trie_rebalance(t, tp);
1609 } else 1605 } else
1610 rcu_assign_pointer(t->trie, NULL); 1606 rcu_assign_pointer(t->trie, NULL);
1611 1607