diff options
Diffstat (limited to 'net/ipv4/fib_trie.c')
-rw-r--r-- | net/ipv4/fib_trie.c | 141 |
1 files changed, 78 insertions, 63 deletions
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index f48534577f8d..d8b68b4de532 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1400,25 +1400,6 @@ found: EXPORT_SYMBOL_GPL(fib_table_lookup); /* - * Remove the leaf and return parent. - */ -static void trie_leaf_remove(struct trie *t, struct tnode *l) -{ - struct tnode *tp = node_parent(l); - - pr_debug("entering trie_leaf_remove(%p)\n", l); - - if (tp) { - put_child(tp, get_index(l->key, tp), NULL); - trie_rebalance(t, tp); - } else { - RCU_INIT_POINTER(t->trie, NULL); - } - - node_free(l); -} - -/* * Caller must hold RTNL. */ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) @@ -1483,8 +1464,18 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) if (!plen) tb->tb_num_default--; - if (hlist_empty(&l->leaf)) - trie_leaf_remove(t, l); + if (hlist_empty(&l->leaf)) { + struct tnode *tp = node_parent(l); + + if (tp) { + put_child(tp, get_index(l->key, tp), NULL); + trie_rebalance(t, tp); + } else { + RCU_INIT_POINTER(t->trie, NULL); + } + + node_free(l); + } if (fa->fa_state & FA_S_ACCESSED) rt_cache_flush(cfg->fc_nlinfo.nl_net); @@ -1494,33 +1485,6 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) return 0; } -static int trie_flush_leaf(struct tnode *l) -{ - struct hlist_node *tmp; - unsigned char slen = 0; - struct fib_alias *fa; - int found = 0; - - hlist_for_each_entry_safe(fa, tmp, &l->leaf, fa_list) { - struct fib_info *fi = fa->fa_info; - - if (fi && (fi->fib_flags & RTNH_F_DEAD)) { - hlist_del_rcu(&fa->fa_list); - fib_release_info(fa->fa_info); - alias_free_mem_rcu(fa); - found++; - - continue; - } - - slen = fa->fa_slen; - } - - l->slen = slen; - - return found; -} - /* Scan for the next right leaf starting at node p->child[idx] * Since we have back pointer, no recursion necessary. */ @@ -1588,30 +1552,81 @@ static struct tnode *trie_leafindex(struct trie *t, int index) */ int fib_table_flush(struct fib_table *tb) { - struct trie *t = (struct trie *) tb->tb_data; - struct tnode *l, *ll = NULL; + struct trie *t = (struct trie *)tb->tb_data; + struct hlist_node *tmp; + struct fib_alias *fa; + struct tnode *n, *pn; + unsigned long cindex; + unsigned char slen; int found = 0; - for (l = trie_firstleaf(t); l; l = trie_nextleaf(l)) { - found += trie_flush_leaf(l); + n = rcu_dereference(t->trie); + if (!n) + goto flush_complete; + + pn = NULL; + cindex = 0; + + while (IS_TNODE(n)) { + /* record pn and cindex for leaf walking */ + pn = n; + cindex = 1ul << n->bits; +backtrace: + /* walk trie in reverse order */ + do { + while (!(cindex--)) { + t_key pkey = pn->key; + + n = pn; + pn = node_parent(n); + + /* resize completed node */ + resize(t, n); + + /* if we got the root we are done */ + if (!pn) + goto flush_complete; - if (ll) { - if (hlist_empty(&ll->leaf)) - trie_leaf_remove(t, ll); - else - leaf_pull_suffix(ll); + cindex = get_index(pkey, pn); + } + + /* grab the next available node */ + n = tnode_get_child(pn, cindex); + } while (!n); + } + + /* track slen in case any prefixes survive */ + slen = 0; + + hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { + struct fib_info *fi = fa->fa_info; + + if (fi && (fi->fib_flags & RTNH_F_DEAD)) { + hlist_del_rcu(&fa->fa_list); + fib_release_info(fa->fa_info); + alias_free_mem_rcu(fa); + found++; + + continue; } - ll = l; + slen = fa->fa_slen; } - if (ll) { - if (hlist_empty(&ll->leaf)) - trie_leaf_remove(t, ll); - else - leaf_pull_suffix(ll); + /* update leaf slen */ + n->slen = slen; + + if (hlist_empty(&n->leaf)) { + put_child_root(pn, t, n->key, NULL); + node_free(n); + } else { + leaf_pull_suffix(n); } + /* if trie is leaf only loop is completed */ + if (pn) + goto backtrace; +flush_complete: pr_debug("trie_flush found=%d\n", found); return found; } |