The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [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