|
@@ -1743,8 +1743,10 @@ struct fib_table *fib_trie_unmerge(struct fib_table *oldtb)
|
|
|
local_l = fib_find_node(lt, &local_tp, l->key);
|
|
|
|
|
|
if (fib_insert_alias(lt, local_tp, local_l, new_fa,
|
|
|
- NULL, l->key))
|
|
|
+ NULL, l->key)) {
|
|
|
+ kmem_cache_free(fn_alias_kmem, new_fa);
|
|
|
goto out;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* stop loop if key wrapped back to 0 */
|
|
@@ -1760,6 +1762,71 @@ out:
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+/* Caller must hold RTNL */
|
|
|
+void fib_table_flush_external(struct fib_table *tb)
|
|
|
+{
|
|
|
+ struct trie *t = (struct trie *)tb->tb_data;
|
|
|
+ struct key_vector *pn = t->kv;
|
|
|
+ unsigned long cindex = 1;
|
|
|
+ struct hlist_node *tmp;
|
|
|
+ struct fib_alias *fa;
|
|
|
+
|
|
|
+ /* walk trie in reverse order */
|
|
|
+ for (;;) {
|
|
|
+ unsigned char slen = 0;
|
|
|
+ struct key_vector *n;
|
|
|
+
|
|
|
+ if (!(cindex--)) {
|
|
|
+ t_key pkey = pn->key;
|
|
|
+
|
|
|
+ /* cannot resize the trie vector */
|
|
|
+ if (IS_TRIE(pn))
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* resize completed node */
|
|
|
+ pn = resize(t, pn);
|
|
|
+ cindex = get_index(pkey, pn);
|
|
|
+
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* grab the next available node */
|
|
|
+ n = get_child(pn, cindex);
|
|
|
+ if (!n)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (IS_TNODE(n)) {
|
|
|
+ /* record pn and cindex for leaf walking */
|
|
|
+ pn = n;
|
|
|
+ cindex = 1ul << n->bits;
|
|
|
+
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) {
|
|
|
+ /* if alias was cloned to local then we just
|
|
|
+ * need to remove the local copy from main
|
|
|
+ */
|
|
|
+ if (tb->tb_id != fa->tb_id) {
|
|
|
+ hlist_del_rcu(&fa->fa_list);
|
|
|
+ alias_free_mem_rcu(fa);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* record local slen */
|
|
|
+ slen = fa->fa_slen;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* update leaf slen */
|
|
|
+ n->slen = slen;
|
|
|
+
|
|
|
+ if (hlist_empty(&n->leaf)) {
|
|
|
+ put_child_root(pn, n->key, NULL);
|
|
|
+ node_free(n);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* Caller must hold RTNL. */
|
|
|
int fib_table_flush(struct net *net, struct fib_table *tb)
|
|
|
{
|