]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ipv6: Preallocate rt->fib6_nh->rt6i_pcpu in ip6_route_info_create().
authorKuniyuki Iwashima <kuniyu@amazon.com>
Fri, 18 Apr 2025 00:03:48 +0000 (17:03 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 24 Apr 2025 07:29:56 +0000 (09:29 +0200)
ip6_route_info_create_nh() will be called under RCU.

Then, fib6_nh_init() is also under RCU, but per-cpu memory allocation
is very likely to fail with GFP_ATOMIC while bulk-adding IPv6 routes
and we would see a bunch of this message in dmesg.

  percpu: allocation failed, size=8 align=8 atomic=1, atomic alloc failed, no space left
  percpu: allocation failed, size=8 align=8 atomic=1, atomic alloc failed, no space left

Let's preallocate rt->fib6_nh->rt6i_pcpu in ip6_route_info_create().

If something fails before the original memory allocation in
fib6_nh_init(), ip6_route_info_create_nh() calls fib6_info_release(),
which releases the preallocated per-cpu memory.

Note that rt->fib6_nh->rt6i_pcpu is not preallocated when called via
ipv6_stub, so we still need alloc_percpu_gfp() in fib6_nh_init().

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250418000443.43734-8-kuniyu@amazon.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/ipv6/route.c

index bba35cb2dc259fb6dbb95d3bd5dc4a7d6084028e..1155cb0173430348fb6763ce1bdc4a985aa7bafb 100644 (file)
@@ -3665,10 +3665,12 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
                goto out;
 
 pcpu_alloc:
-       fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags);
        if (!fib6_nh->rt6i_pcpu) {
-               err = -ENOMEM;
-               goto out;
+               fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags);
+               if (!fib6_nh->rt6i_pcpu) {
+                       err = -ENOMEM;
+                       goto out;
+               }
        }
 
        fib6_nh->fib_nh_dev = dev;
@@ -3728,6 +3730,15 @@ void fib6_nh_release_dsts(struct fib6_nh *fib6_nh)
        }
 }
 
+static int fib6_nh_prealloc_percpu(struct fib6_nh *fib6_nh, gfp_t gfp_flags)
+{
+       fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags);
+       if (!fib6_nh->rt6i_pcpu)
+               return -ENOMEM;
+
+       return 0;
+}
+
 static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
                                               gfp_t gfp_flags,
                                               struct netlink_ext_ack *extack)
@@ -3765,6 +3776,12 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
                goto free;
        }
 
+       if (!cfg->fc_nh_id) {
+               err = fib6_nh_prealloc_percpu(&rt->fib6_nh[0], gfp_flags);
+               if (err)
+                       goto free_metrics;
+       }
+
        if (cfg->fc_flags & RTF_ADDRCONF)
                rt->dst_nocount = true;
 
@@ -3789,6 +3806,8 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
        rt->fib6_src.plen = cfg->fc_src_len;
 #endif
        return rt;
+free_metrics:
+       ip_fib_metrics_put(rt->fib6_metrics);
 free:
        kfree(rt);
 err: