Merge tag 'sound-5.16-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[linux-2.6-microblaze.git] / net / ipv4 / nexthop.c
index 9e81007..5dbd4b5 100644 (file)
@@ -1899,15 +1899,36 @@ static void remove_nexthop(struct net *net, struct nexthop *nh,
 /* if any FIB entries reference this nexthop, any dst entries
  * need to be regenerated
  */
-static void nh_rt_cache_flush(struct net *net, struct nexthop *nh)
+static void nh_rt_cache_flush(struct net *net, struct nexthop *nh,
+                             struct nexthop *replaced_nh)
 {
        struct fib6_info *f6i;
+       struct nh_group *nhg;
+       int i;
 
        if (!list_empty(&nh->fi_list))
                rt_cache_flush(net);
 
        list_for_each_entry(f6i, &nh->f6i_list, nh_list)
                ipv6_stub->fib6_update_sernum(net, f6i);
+
+       /* if an IPv6 group was replaced, we have to release all old
+        * dsts to make sure all refcounts are released
+        */
+       if (!replaced_nh->is_group)
+               return;
+
+       /* new dsts must use only the new nexthop group */
+       synchronize_net();
+
+       nhg = rtnl_dereference(replaced_nh->nh_grp);
+       for (i = 0; i < nhg->num_nh; i++) {
+               struct nh_grp_entry *nhge = &nhg->nh_entries[i];
+               struct nh_info *nhi = rtnl_dereference(nhge->nh->nh_info);
+
+               if (nhi->family == AF_INET6)
+                       ipv6_stub->fib6_nh_release_dsts(&nhi->fib6_nh);
+       }
 }
 
 static int replace_nexthop_grp(struct net *net, struct nexthop *old,
@@ -2247,7 +2268,7 @@ static int replace_nexthop(struct net *net, struct nexthop *old,
                err = replace_nexthop_single(net, old, new, extack);
 
        if (!err) {
-               nh_rt_cache_flush(net, old);
+               nh_rt_cache_flush(net, old, new);
 
                __remove_nexthop(net, new, NULL);
                nexthop_put(new);
@@ -2544,11 +2565,15 @@ static int nh_create_ipv6(struct net *net,  struct nexthop *nh,
        /* sets nh_dev if successful */
        err = ipv6_stub->fib6_nh_init(net, fib6_nh, &fib6_cfg, GFP_KERNEL,
                                      extack);
-       if (err)
+       if (err) {
+               /* IPv6 is not enabled, don't call fib6_nh_release */
+               if (err == -EAFNOSUPPORT)
+                       goto out;
                ipv6_stub->fib6_nh_release(fib6_nh);
-       else
+       } else {
                nh->nh_flags = fib6_nh->fib_nh_flags;
-
+       }
+out:
        return err;
 }