From: netdev-bot+sashiko@kernel.org
To: kuniyu@google.com
Cc: dsahern@kernel.org, idosch@nvidia.com, davem@davemloft.net,
edumazet@kernel.org, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, carges@cloudflare.com, kuni1840@gmail.com,
netdev@vger.kernel.org
Subject: Re: [PATCH v1 net-next 5/5] ipv6: Batch rt6_uncached_list_flush_dev() for dying netns.
Date: Tue, 29 Sep 2026 06:26:54 +0000 [thread overview]
Message-ID: <179066321461.434549.4404823988585129411@kernel.org> (raw)
In-Reply-To: <20260927202429.2452589-6-kuniyu@google.com>
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 2 · Low: 0
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] The new ->pre_exit_batch() callback can crash the IPv6 init
error path.
- [Medium] Race between the batch flush and rt6_uncached_list_add().
--
ipv6: Batch rt6_uncached_list_flush_dev() for dying netns.
This commit flushes IPv6 uncached routes for dying netns from a new
->pre_exit_batch() callback, which runs about once per cleanup_net().
Once NET_PRE_EXIT_DONE is set, the per-device rt6_uncached_list_flush_dev()
now returns early. rt6_uncached_list_add() now checks check_net() under
ul->lock, and a route for a dying netns is moved straight to
blackhole_netdev instead of being linked.
Closes: https://lore.kernel.org/netdev/20260917-hash-bucket-route-lists-v3-0-30493a37b6eb@cloudflare.com/
Link: https://lore.kernel.org/netdev/aq2B8PSfjn-xau4V@20HS2G4/
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index 475ced827ec57..7747e4f20fee6 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
[ ... ]
> @@ -143,7 +159,12 @@ void rt6_uncached_list_add(struct rt6_info *rt)
> rt->dst.rt_uncached_list = ul;
>
> spin_lock_bh(&ul->lock);
> - list_add_tail(&rt->dst.rt_uncached, &ul->head);
> +
> + if (check_net(dst_dev_net_rcu(&rt->dst)))
> + list_add_tail(&rt->dst.rt_uncached, &ul->head);
> + else
> + rt6_uncached_list_replace(rt);
> +
> spin_unlock_bh(&ul->lock);
> }
>
> @@ -162,6 +183,9 @@ static void rt6_uncached_list_flush_dev(struct net_device *dev)
> {
> int cpu;
>
> + if (dev && net_pre_exit_done(dev_net(dev)))
> + return;
> +
> for_each_possible_cpu(cpu) {
> struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
> struct rt6_info *rt, *safe;
[Severity: Medium]
Because of this early return, the batch flush from ->pre_exit_batch() is
the only flush for devices in a dying netns. Can it miss a route that is
being added at the same time?
The dev == NULL path still skips a CPU's list with a lockless check,
before it takes ul->lock:
if (list_empty(&ul->head))
continue;
Here is the sequence I have in mind:
CPU A, rt6_uncached_list_add() (e.g. from ip6_pol_route() or
icmp6_dst_alloc()):
spin_lock_bh(&ul->lock); /* its own per-cpu list, empty */
check_net() returns true
<delayed by a hardirq, vCPU preemption or RT preemption>
CPU B, cleanup_net()->ops_undo_list():
ops_pre_exit_list()
ip6_route_net_pre_exit_batch()
rt6_uncached_list_flush_dev(NULL)
list_empty() on CPU A's list is true, so it is skipped
CPU A:
list_add_tail(&rt->dst.rt_uncached, &ul->head);
spin_unlock_bh(&ul->lock);
CPU B:
synchronize_rcu();
WRITE_ONCE(net->undo_state, NET_PRE_EXIT_DONE);
...
NETDEV_UNREGISTER: addrconf_ifdown()->rt6_disable_ip()->
rt6_uncached_list_flush_dev(dev) returns early
->pre_exit_batch() runs before the synchronize_rcu() in ops_undo_list(),
so CPU A's RCU/BH section is not waited for before the scan.
Would the late route then stay on the uncached list? It would still hold
its dst.dev reference (dev_tracker) and its rt6i_idev reference, and it
would never be moved to blackhole_netdev.
Before this patch, the per-device flush always ran at NETDEV_UNREGISTER,
after the synchronize_net() in unregister_netdevice_many().
If something holds that dst for a long time, could netdev_wait_allrefs()
hang and stall netns cleanup?
One option is to drop the list_empty() shortcut when dev is NULL. Another
is to run the batch flush after the grace period and before
NET_PRE_EXIT_DONE is published. Would either of those work?
[ ... ]
> @@ -6801,6 +6819,11 @@ static int __net_init ip6_route_net_init(struct net *net)
> goto out;
> }
>
> +static void __net_exit ip6_route_net_pre_exit_batch(struct list_head *net_exit_list)
> +{
> + rt6_uncached_list_flush_dev(NULL);
> +}
> +
[Severity: Medium]
Could this callback crash on the ip6_route_init() error path?
ip6_route_init() registers ip6_route_net_ops before the per-cpu uncached
lists are initialized. They are only set up at the very end of the
function:
ret = register_pernet_subsys(&ip6_route_net_ops);
if (ret)
goto out_register_inetpeer;
...
for_each_possible_cpu(cpu) {
struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
INIT_LIST_HEAD(&ul->head);
spin_lock_init(&ul->lock);
}
If a later step fails, the unwind calls
unregister_pernet_subsys(&ip6_route_net_ops). Those steps are fib6_init(),
xfrm6_init(), fib6_rules_init(), registering ip6_route_net_late_ops,
rtnl_register_many(), register_netdevice_notifier() and
bpf_iter_register().
With CONFIG_NET_NS=y, the same undo also runs inside
register_pernet_subsys() if ip6_route_net_init() fails for a netns. That
path goes through __register_pernet_operations()->ops_undo_single().
Both paths reach ops_pre_exit_list(). It calls ->pre_exit_batch() even
when net_exit_list is empty:
if (ops->pre_exit_batch)
ops->pre_exit_batch(net_exit_list);
which then calls:
ip6_route_net_pre_exit_batch()
rt6_uncached_list_flush_dev(NULL)
At this point the static per-cpu heads are still zeroed. With head.next
== NULL, list_empty(&ul->head) returns false. spin_lock_bh() then takes a
lock that was never initialized. list_for_each_entry_safe() also derives
pos from the NULL next pointer and dereferences it.
Before this patch, ip6_route_net_ops had no ->pre_exit_batch(), so this
rollback did not touch the lists.
Would it make sense to initialize the per-cpu uncached lists before
registering ip6_route_net_ops?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260927202429.2452589-1-kuniyu%40google.com
prev parent reply other threads:[~2026-09-29 6:26 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-27 20:23 [PATCH v1 net-next 0/5] ip: Batch flushing uncached routes for dying netns Kuniyuki Iwashima
2026-09-27 20:23 ` [PATCH v1 net-next 1/5] net: Remove net->is_dying Kuniyuki Iwashima
2026-09-29 6:26 ` netdev-bot+sashiko
2026-09-27 20:23 ` [PATCH v1 net-next 2/5] net: Add ->pre_exit_batch() to struct pernet_operations Kuniyuki Iwashima
2026-09-29 6:26 ` netdev-bot+sashiko
2026-09-27 20:23 ` [PATCH v1 net-next 3/5] net: Track state in ops_undo_list() Kuniyuki Iwashima
2026-09-27 20:23 ` [PATCH v1 net-next 4/5] ipv4: Batch rt_flush_dev() for dying netns Kuniyuki Iwashima
2026-09-27 22:51 ` Eric Dumazet
2026-09-28 16:33 ` Kuniyuki Iwashima
2026-09-29 6:26 ` netdev-bot+sashiko
2026-09-27 20:23 ` [PATCH v1 net-next 5/5] ipv6: Batch rt6_uncached_list_flush_dev() " Kuniyuki Iwashima
2026-09-29 6:26 ` netdev-bot+sashiko [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=179066321461.434549.4404823988585129411@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=carges@cloudflare.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=kuni1840@gmail.com \
--cc=kuniyu@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox