* [PATCH net-next v3 0/2] ipv4: update rt_flush_dev() and two dst.dev readers
@ 2026-07-08 6:05 xuanqiang.luo
2026-07-08 6:05 ` [PATCH net-next v3 1/2] ipv4: use rcu_assign_pointer() in rt_flush_dev() xuanqiang.luo
2026-07-08 6:05 ` [PATCH net-next v3 2/2] ipv4: snapshot dst.dev in ip_rt_send_redirect() and ip_rt_get_source() xuanqiang.luo
0 siblings, 2 replies; 3+ messages in thread
From: xuanqiang.luo @ 2026-07-08 6:05 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Ahern, Ido Schimmel
Cc: Simon Horman, Kuniyuki Iwashima, netdev, linux-kernel,
Xuanqiang Luo
From: Xuanqiang Luo <xuanqiang.luo@linux.dev>
Patch 1 makes the rt_flush_dev() write to rt->dst.dev use
rcu_assign_pointer(), matching the existing dst_dev_rcu() readers.
Patch 2 makes ip_rt_send_redirect() and ip_rt_get_source() use one
dst.dev snapshot throughout each operation, so a concurrent rt_flush_dev()
update cannot make them use values from two devices.
v3:
- Split the rt_flush_dev() change into a separate patch.
- Update PATCH 2 subject and commit message to describe the actual scope
more clearly.
- Drop the Fixes tag.
- Fix local variable ordering for netdev reverse xmas tree style.
Thanks to Ido Schimmel for the feedback.
v2: https://lore.kernel.org/lkml/20260701032434.17500-1-xuanqiang.luo@linux.dev/
- Use dst_dev_rcu() and dev_net_rcu() for the RCU readers.
- Use rcu_assign_pointer() when publishing the uncached route device
replacement.
- Slightly adjust the commit message wording because this issue was found
by inspection, not from an observed user-visible failure.
v1: https://lore.kernel.org/all/20260630094250.29386-1-xuanqiang.luo@linux.dev/
Xuanqiang Luo (2):
ipv4: use rcu_assign_pointer() in rt_flush_dev()
ipv4: snapshot dst.dev in ip_rt_send_redirect() and ip_rt_get_source()
net/ipv4/route.c | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH net-next v3 1/2] ipv4: use rcu_assign_pointer() in rt_flush_dev()
2026-07-08 6:05 [PATCH net-next v3 0/2] ipv4: update rt_flush_dev() and two dst.dev readers xuanqiang.luo
@ 2026-07-08 6:05 ` xuanqiang.luo
2026-07-08 6:05 ` [PATCH net-next v3 2/2] ipv4: snapshot dst.dev in ip_rt_send_redirect() and ip_rt_get_source() xuanqiang.luo
1 sibling, 0 replies; 3+ messages in thread
From: xuanqiang.luo @ 2026-07-08 6:05 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Ahern, Ido Schimmel
Cc: Simon Horman, Kuniyuki Iwashima, netdev, linux-kernel,
Xuanqiang Luo
From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
rt_flush_dev() replaces rt->dst.dev with blackhole_netdev on uncached
routes. The field is also exposed as dst.dev_rcu, and existing readers
use dst_dev_rcu().
Use rcu_assign_pointer() for the replacement, as dst_dev_put() already
does for the same field.
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
net/ipv4/route.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 3f3de5164d6e5..b668375df71e2 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1565,7 +1565,7 @@ void rt_flush_dev(struct net_device *dev)
list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) {
if (rt->dst.dev != dev)
continue;
- rt->dst.dev = blackhole_netdev;
+ rcu_assign_pointer(rt->dst.dev_rcu, blackhole_netdev);
netdev_ref_replace(dev, blackhole_netdev,
&rt->dst.dev_tracker, GFP_ATOMIC);
list_del_init(&rt->dst.rt_uncached);
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH net-next v3 2/2] ipv4: snapshot dst.dev in ip_rt_send_redirect() and ip_rt_get_source()
2026-07-08 6:05 [PATCH net-next v3 0/2] ipv4: update rt_flush_dev() and two dst.dev readers xuanqiang.luo
2026-07-08 6:05 ` [PATCH net-next v3 1/2] ipv4: use rcu_assign_pointer() in rt_flush_dev() xuanqiang.luo
@ 2026-07-08 6:05 ` xuanqiang.luo
1 sibling, 0 replies; 3+ messages in thread
From: xuanqiang.luo @ 2026-07-08 6:05 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Ahern, Ido Schimmel
Cc: Simon Horman, Kuniyuki Iwashima, netdev, linux-kernel,
Xuanqiang Luo
From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
rt_flush_dev() can replace rt->dst.dev with blackhole_netdev while RCU
readers are running. ip_rt_send_redirect() and ip_rt_get_source() both
read rt->dst.dev more than once and use the results in one operation.
If rt->dst.dev changes between those reads, the operation can use values
from two devices. For example, ip_rt_send_redirect() can use in_dev from
the old device and the L3 master ifindex from blackhole_netdev.
Read rt->dst.dev once in these two functions and use the snapshot for the
later device accesses.
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
net/ipv4/route.c | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index b668375df71e2..4fed07cae7da2 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -874,21 +874,23 @@ void ip_rt_send_redirect(struct sk_buff *skb)
{
struct rtable *rt = skb_rtable(skb);
struct in_device *in_dev;
+ struct net_device *dev;
struct inet_peer *peer;
- struct net *net;
int log_martians;
+ struct net *net;
int vif;
rcu_read_lock();
- in_dev = __in_dev_get_rcu(rt->dst.dev);
+ dev = dst_dev_rcu(&rt->dst);
+ in_dev = __in_dev_get_rcu(dev);
if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev)) {
rcu_read_unlock();
return;
}
log_martians = IN_DEV_LOG_MARTIANS(in_dev);
- vif = l3mdev_master_ifindex_rcu(rt->dst.dev);
+ vif = l3mdev_master_ifindex_rcu(dev);
- net = dev_net(rt->dst.dev);
+ net = dev_net_rcu(dev);
peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif);
if (!peer) {
rcu_read_unlock();
@@ -1287,29 +1289,32 @@ void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt)
{
__be32 src;
- if (rt_is_output_route(rt))
+ rcu_read_lock();
+ if (rt_is_output_route(rt)) {
src = ip_hdr(skb)->saddr;
- else {
- struct fib_result res;
+ } else {
+ struct net_device *dev = dst_dev_rcu(&rt->dst);
+ struct net *net = dev_net_rcu(dev);
struct iphdr *iph = ip_hdr(skb);
+ struct fib_result res;
struct flowi4 fl4 = {
.daddr = iph->daddr,
.saddr = iph->saddr,
.flowi4_dscp = ip4h_dscp(iph),
- .flowi4_oif = rt->dst.dev->ifindex,
+ .flowi4_oif = dev->ifindex,
.flowi4_iif = skb->dev->ifindex,
.flowi4_mark = skb->mark,
};
- rcu_read_lock();
- if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res, 0) == 0)
- src = fib_result_prefsrc(dev_net(rt->dst.dev), &res);
+ if (fib_lookup(net, &fl4, &res, 0) == 0)
+ src = fib_result_prefsrc(net, &res);
else
- src = inet_select_addr(rt->dst.dev,
+ src = inet_select_addr(dev,
rt_nexthop(rt, iph->daddr),
RT_SCOPE_UNIVERSE);
- rcu_read_unlock();
}
+ rcu_read_unlock();
+
memcpy(addr, &src, 4);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-08 6:06 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-08 6:05 [PATCH net-next v3 0/2] ipv4: update rt_flush_dev() and two dst.dev readers xuanqiang.luo
2026-07-08 6:05 ` [PATCH net-next v3 1/2] ipv4: use rcu_assign_pointer() in rt_flush_dev() xuanqiang.luo
2026-07-08 6:05 ` [PATCH net-next v3 2/2] ipv4: snapshot dst.dev in ip_rt_send_redirect() and ip_rt_get_source() xuanqiang.luo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox