|
@@ -640,6 +640,11 @@ static struct fib6_node *fib6_add_1(struct net *net,
|
|
|
if (!(fn->fn_flags & RTN_RTINFO)) {
|
|
|
RCU_INIT_POINTER(fn->leaf, NULL);
|
|
|
rt6_release(leaf);
|
|
|
+ /* remove null_entry in the root node */
|
|
|
+ } else if (fn->fn_flags & RTN_TL_ROOT &&
|
|
|
+ rcu_access_pointer(fn->leaf) ==
|
|
|
+ net->ipv6.ip6_null_entry) {
|
|
|
+ RCU_INIT_POINTER(fn->leaf, NULL);
|
|
|
}
|
|
|
|
|
|
return fn;
|
|
@@ -1270,13 +1275,17 @@ out:
|
|
|
return err;
|
|
|
|
|
|
failure:
|
|
|
- /* fn->leaf could be NULL if fn is an intermediate node and we
|
|
|
- * failed to add the new route to it in both subtree creation
|
|
|
- * failure and fib6_add_rt2node() failure case.
|
|
|
- * In both cases, fib6_repair_tree() should be called to fix
|
|
|
- * fn->leaf.
|
|
|
+ /* fn->leaf could be NULL and fib6_repair_tree() needs to be called if:
|
|
|
+ * 1. fn is an intermediate node and we failed to add the new
|
|
|
+ * route to it in both subtree creation failure and fib6_add_rt2node()
|
|
|
+ * failure case.
|
|
|
+ * 2. fn is the root node in the table and we fail to add the first
|
|
|
+ * default route to it.
|
|
|
*/
|
|
|
- if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)))
|
|
|
+ if (fn &&
|
|
|
+ (!(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)) ||
|
|
|
+ (fn->fn_flags & RTN_TL_ROOT &&
|
|
|
+ !rcu_access_pointer(fn->leaf))))
|
|
|
fib6_repair_tree(info->nl_net, table, fn);
|
|
|
/* Always release dst as dst->__refcnt is guaranteed
|
|
|
* to be taken before entering this function
|
|
@@ -1531,6 +1540,12 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
|
|
|
struct fib6_walker *w;
|
|
|
int iter = 0;
|
|
|
|
|
|
+ /* Set fn->leaf to null_entry for root node. */
|
|
|
+ if (fn->fn_flags & RTN_TL_ROOT) {
|
|
|
+ rcu_assign_pointer(fn->leaf, net->ipv6.ip6_null_entry);
|
|
|
+ return fn;
|
|
|
+ }
|
|
|
+
|
|
|
for (;;) {
|
|
|
struct fib6_node *fn_r = rcu_dereference_protected(fn->right,
|
|
|
lockdep_is_held(&table->tb6_lock));
|
|
@@ -1685,10 +1700,15 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
|
|
|
}
|
|
|
read_unlock(&net->ipv6.fib6_walker_lock);
|
|
|
|
|
|
- /* If it was last route, expunge its radix tree node */
|
|
|
+ /* If it was last route, call fib6_repair_tree() to:
|
|
|
+ * 1. For root node, put back null_entry as how the table was created.
|
|
|
+ * 2. For other nodes, expunge its radix tree node.
|
|
|
+ */
|
|
|
if (!rcu_access_pointer(fn->leaf)) {
|
|
|
- fn->fn_flags &= ~RTN_RTINFO;
|
|
|
- net->ipv6.rt6_stats->fib_route_nodes--;
|
|
|
+ if (!(fn->fn_flags & RTN_TL_ROOT)) {
|
|
|
+ fn->fn_flags &= ~RTN_RTINFO;
|
|
|
+ net->ipv6.rt6_stats->fib_route_nodes--;
|
|
|
+ }
|
|
|
fn = fib6_repair_tree(net, table, fn);
|
|
|
}
|
|
|
|