Netdev List
 help / color / mirror / Atom feed
* Re: [RFC PATCH] net/iucv: Descend into net/iucv when AFIUCV is enabled
From: Alexandra Winter @ 2026-07-07 14:54 UTC (permalink / raw)
  To: Pengpeng Hou, Thorsten Winkler, David Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Heiko Carstens, linux-s390, netdev, linux-kernel
In-Reply-To: <20260625061303.36326-1-pengpeng@iscas.ac.cn>



On 25.06.26 08:13, Pengpeng Hou wrote:
> AFIUCV can be enabled by the QETH_L3/HiperSockets path even when IUCV
> itself is not enabled.  However, the top-level net Makefile only descends
> into net/iucv/ under CONFIG_IUCV.
> 
> That creates a Kconfig/Kbuild carrier mismatch: CONFIG_AFIUCV=m can be

the same is true for CONFIG_AFIUCV=y; please mention both cases.
(Maybe also mention explicitely that CONFIG_IUCV=n is required to trigger the issue)

> selected, but af_iucv.o is never considered because the containing
> directory is skipped.
> 
> This RFC uses an always-descend model for net/iucv/.  The subdirectory
> Makefile already gates iucv.o and af_iucv.o on their own Kconfig symbols,
> so entering the directory does not force either provider object on.
> 
> This is intentionally RFC because s390 maintainers should confirm whether
> the QETH_L3-only AF_IUCV configuration is intended to build af_iucv.o
> without the base IUCV object.
> 
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
>  net/Makefile | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/Makefile b/net/Makefile
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -45,7 +45,7 @@
>  obj-$(CONFIG_MAC80211)		+= mac80211/
>  obj-$(CONFIG_TIPC)		+= tipc/
>  obj-$(CONFIG_NETLABEL)		+= netlabel/
> -obj-$(CONFIG_IUCV)		+= iucv/
> +obj-y				+= iucv/
>  obj-$(CONFIG_SMC)		+= smc/
>  obj-$(CONFIG_RFKILL)		+= rfkill/
>  obj-$(CONFIG_NET_9P)		+= 9p/


For the records: I do not see a usecase where somebody would
create  configuration with AF_IUCV=y/m and IUCV=n; i.e. where
somebody would support AF_IUCV over HiperSockets, but not via
z/VM. But technically, it is possible and who knows.

So please re-send as a regular problem patch to net and add my
Reviewed-by: Alexandra Winter <wintera@linux.ibm.com>

Thanks you.




^ permalink raw reply

* Re: [PATCH nf] netfilter: nf_conncount: fix zone comparison in tuple dedup
From: Florian Westphal @ 2026-07-07 14:58 UTC (permalink / raw)
  To: Yizhou Zhao
  Cc: netfilter-devel, Pablo Neira Ayuso, Phil Sutter, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, coreteam,
	netdev, linux-kernel, Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li,
	Ke Xu, stable
In-Reply-To: <20260706114820.74006-1-zhaoyz24@mails.tsinghua.edu.cn>

Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn> wrote:
> The "already exists" dedup logic in __nf_conncount_add() decides
> whether a connection has already been counted and can be skipped instead
> of incrementing the connlimit count.  It compares the conntrack zone of a
> list entry with the zone of the connection being added using
> nf_ct_zone_id() and nf_ct_zone_equal(), passing conn->zone.dir or
> zone->dir as the direction argument.

Right, thats bogus.

> @@ -211,8 +220,10 @@ static int __nf_conncount_add(struct net *net,
>  			/* Not found, but might be about to be confirmed */
>  			if (PTR_ERR(found) == -EAGAIN) {
>  				if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
> -				    nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
> -				    nf_ct_zone_id(zone, zone->dir))
> +				    nf_ct_zone_id(&conn->zone,
> +						  nf_conncount_zone_dir(&conn->zone)) ==
> +				    nf_ct_zone_id(zone,
> +						  nf_conncount_zone_dir(zone)))

Should this be a simpler:

                                if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
-                                   nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
-                                   nf_ct_zone_id(zone, zone->dir))
+                                   nf_ct_zone_equal(&conn->zone, &zone), IP_CT_DIR_ORIGINAL)

?

The tuple is always the 'original' direction, so it would follow that
we should not care about reply zone dir.

Also see:
https://sashiko.dev/#/patchset/20260706114820.74006-1-zhaoyz24%40mails.tsinghua.edu.cn


^ permalink raw reply

* [PATCH net v2] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Cen Zhang (Microsoft) @ 2026-07-07 15:06 UTC (permalink / raw)
  To: tung.quang.nguyen, jmaloy, davem, edumazet, kuba, pabeni, horms
  Cc: netdev, tipc-discussion, linux-kernel, AutonomousCodeSecurity,
	tgopinath, kys, blbllhy
In-Reply-To: <GV1P189MB198837B4F3A78F2A11C9614CC6F02@GV1P189MB1988.EURP189.PROD.OUTLOOK.COM>

Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied MTU
values but only enforce a minimum bound, not a maximum. When a user sets
the MTU to a value exceeding U16_MAX (65535), it passes validation but
is silently truncated when assigned to u16 fields l->mtu and
l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000)
truncate to 0, causing a division by zero in tipc_link_set_queue_limits()
which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing
values (e.g. 65537-131071) produce small incorrect MTU values, resulting
in link malfunction behaviors.

Crash stack (triggered as unprivileged user via user namespace):

  tipc_link_set_queue_limits  net/tipc/link.c:2531
  tipc_link_create            net/tipc/link.c:520
  tipc_node_check_dest        net/tipc/node.c:1279
  tipc_disc_rcv               net/tipc/discover.c:252
  tipc_rcv                    net/tipc/node.c:2129
  tipc_udp_recv               net/tipc/udp_media.c:392

Two independent paths lack the upper bound check:
1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET)
2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET)

Fix both by rejecting MTU values above U16_MAX.

Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU")
Reported-by: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
---
v2: Solved format issue

 net/tipc/bearer.c    | 6 ++++++
 net/tipc/udp_media.h | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 05dcd2f9e..0926692dd 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -1163,6 +1163,12 @@ int __tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)
 					       "MTU value is out-of-range");
 				return -EINVAL;
 			}
+			if (nla_get_u32(props[TIPC_NLA_PROP_MTU]) >
+			    U16_MAX) {
+				NL_SET_ERR_MSG(info->extack,
+					       "MTU value is out-of-range");
+				return -EINVAL;
+			}
 			b->mtu = nla_get_u32(props[TIPC_NLA_PROP_MTU]);
 			tipc_node_apply_property(net, b, TIPC_NLA_PROP_MTU);
 #endif
diff --git a/net/tipc/udp_media.h b/net/tipc/udp_media.h
index e7455cc73..0d62a57c8 100644
--- a/net/tipc/udp_media.h
+++ b/net/tipc/udp_media.h
@@ -48,6 +48,11 @@ int tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb);
 /* check if configured MTU is too low for tipc headers */
 static inline bool tipc_udp_mtu_bad(u32 mtu)
 {
+	if (mtu > U16_MAX) {
+		pr_warn("MTU too large for tipc bearer\n");
+		return true;
+	}
+
 	if (mtu >= (TIPC_MIN_BEARER_MTU + sizeof(struct iphdr) +
 	    sizeof(struct udphdr)))
 		return false;
-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Cen Zhang (Microsoft) @ 2026-07-07 15:11 UTC (permalink / raw)
  To: tung.quang.nguyen, jmaloy, davem, edumazet, kuba, pabeni, horms
  Cc: netdev, tipc-discussion, linux-kernel, AutonomousCodeSecurity,
	tgopinath, kys, blbllhy

Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied MTU
values but only enforce a minimum bound, not a maximum. When a user sets
the MTU to a value exceeding U16_MAX (65535), it passes validation but
is silently truncated when assigned to u16 fields l->mtu and
l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000)
truncate to 0, causing a division by zero in tipc_link_set_queue_limits()
which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing
values (e.g. 65537-131071) produce small incorrect MTU values, resulting
in link malfunction behaviors.

Crash stack (triggered as unprivileged user via user namespace):

  tipc_link_set_queue_limits  net/tipc/link.c:2531
  tipc_link_create            net/tipc/link.c:520
  tipc_node_check_dest        net/tipc/node.c:1279
  tipc_disc_rcv               net/tipc/discover.c:252
  tipc_rcv                    net/tipc/node.c:2129
  tipc_udp_recv               net/tipc/udp_media.c:392

Two independent paths lack the upper bound check:
1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET)
2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET)

Fix both by rejecting MTU values above U16_MAX.

Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU")
Reported-by: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
---
v2: Solved format issue

 net/tipc/bearer.c    | 6 ++++++
 net/tipc/udp_media.h | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 05dcd2f9e..0926692dd 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -1163,6 +1163,12 @@ int __tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)
 					       "MTU value is out-of-range");
 				return -EINVAL;
 			}
+			if (nla_get_u32(props[TIPC_NLA_PROP_MTU]) >
+			    U16_MAX) {
+				NL_SET_ERR_MSG(info->extack,
+					       "MTU value is out-of-range");
+				return -EINVAL;
+			}
 			b->mtu = nla_get_u32(props[TIPC_NLA_PROP_MTU]);
 			tipc_node_apply_property(net, b, TIPC_NLA_PROP_MTU);
 #endif
diff --git a/net/tipc/udp_media.h b/net/tipc/udp_media.h
index e7455cc73..0d62a57c8 100644
--- a/net/tipc/udp_media.h
+++ b/net/tipc/udp_media.h
@@ -48,6 +48,11 @@ int tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb);
 /* check if configured MTU is too low for tipc headers */
 static inline bool tipc_udp_mtu_bad(u32 mtu)
 {
+	if (mtu > U16_MAX) {
+		pr_warn("MTU too large for tipc bearer\n");
+		return true;
+	}
+
 	if (mtu >= (TIPC_MIN_BEARER_MTU + sizeof(struct iphdr) +
 	    sizeof(struct udphdr)))
 		return false;
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v6] net: gro: fix double aggregation of flush-marked skbs
From: Willem de Bruijn @ 2026-07-07 15:16 UTC (permalink / raw)
  To: Shiming Cheng, davem, edumazet, kuba, pabeni, horms, matthias.bgg,
	angelogioacchino.delregno, willemb, daniel.zahka, alice, sd,
	eilaimemedsnaimel, imv4bel, nbd, dsahern, netdev, linux-kernel,
	linux-arm-kernel, linux-mediatek
  Cc: stable, steffen.klassert, lena.wang, shiming.cheng
In-Reply-To: <20260707021425.483-1-shiming.cheng@mediatek.com>

Shiming Cheng wrote:
> The skb_gro_receive_list() function is missing a critical safety check
> that exists in the skb_gro_receive() implementation. Specifically, it
> does not validate NAPI_GRO_CB(skb)->flush before allowing packet
> aggregation, as of commit 0ab03f353d36 ("net-gro: Fix GRO flush
> when receiving a GSO packet.").

It does not check .. as of commit .. ?

No, skb_gro_receive checkos NAP_GRO_CB(skb)->flush as of that commit.
 
> This allows already-GRO'd packets with existing frag_list to be
> re-aggregated into a new GRO session, corrupting the frag_list chain
> structure. When skb_segment() attempts to unpack these malformed packets,
> it encounters invalid state and triggers a kernel panic.
> 
> Scenario (Tethering/Device forwarding):
>   1. Driver: Generated aggregated packet P1 via LRO with frag_list
>   2. Dev A: Receives aggregated fraglist packet and flush flag set
>   3. Dev A: Re-enters GRO, skb_gro_receive_list() is called
>   4. Missing flush check allows re-aggregation despite flush flag
>   5. Frag_list chain becomes corrupted (loops or dangling refs)
>   6. Dev B: TX path calls skb_segment(), crashes on corrupted frag_list
> 
> Root cause in skb_segment():
>   The check at line ~4891:
>     if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) &&
>         (skb_headlen(list_skb) == len || sg)) {
> 
>   When frag_list is corrupted by double aggregation, when list_skb is
>   a NULL pointer from skb->next, skb_headlen(list_skb) dereference
>   NULL/corrupted pointers occurs.
> 
> Call Trace:
>  skb_headlen(NULL skb)
>  skb_segment
>  tcp_gso_segment
>  tcp4_gso_segment
>  inet_gso_segment
>  skb_mac_gso_segment
>  __skb_gso_segment
>  skb_gso_segment
>  validate_xmit_skb
>  validate_xmit_skb_list
>  sch_direct_xmit
>  qdisc_restart
>  __qdisc_run
>  qdisc_run
>  net_tx_action
> 
> Fix: Add NAPI_GRO_CB(skb)->flush validation to the early-return check in
> skb_gro_receive_list(), matching the defensive programming pattern of
> skb_gro_receive().
> 
> Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.")
> Cc: stable@vger.kernel.org
> Signed-off-by: Shiming Cheng <shiming.cheng@mediatek.com>
> ---
>  net/core/gro.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/net/core/gro.c b/net/core/gro.c
> index 35f2f708f010..b413f4a6462b 100644
> --- a/net/core/gro.c
> +++ b/net/core/gro.c
> @@ -229,7 +229,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
>  
>  int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
>  {
> -	if (unlikely(p->len + skb->len >= 65536))
> +	/* make sure to check flush flag and to not merge */
> +	if (unlikely(p->len + skb->len >= 65536 ||
> +		     NAPI_GRO_CB(skb)->flush))
>  		return -E2BIG;
>  
>  	if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
> -- 
> 2.45.2
> 



^ permalink raw reply

* Re: [PATCH net-next] net: skbuff: use net_zcopy_get() instead of refcount_inc()
From: Willem de Bruijn @ 2026-07-07 15:18 UTC (permalink / raw)
  To: luyun, Willem de Bruijn, davem, edumazet, kuba, pabeni, horms,
	kerneljasonxing, kuniyu
  Cc: mhal, bjorn, jiayuan.chen, netdev, minhnguyen.080505
In-Reply-To: <dbcfecb3-7b5e-4d53-b4ac-486fded1e192@163.com>

luyun wrote:
> 
> 在 2026/7/6 21:55, Willem de Bruijn 写道:
> > Yun Lu wrote:
> >> From: Yun Lu <luyun@kylinos.cn>
> >>
> >> The net_zcopy_get() increments the uarg->refcnt, which is called both
> >> in pskb_carve_inside_header() and pskb_carve_inside_nonlinear().
> >> Also use net_zcopy_get() in pskb_expand_head for code consistency.
> >>
> >> No functional change intended.
> >>
> >> Signed-off-by: Yun Lu <luyun@kylinos.cn>
> >> ---
> >>   net/core/skbuff.c | 2 +-
> >>   1 file changed, 1 insertion(+), 1 deletion(-)
> >>
> >> diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> >> index 18dabb4e9cfa..bcf3b2c65fb9 100644
> >> --- a/net/core/skbuff.c
> >> +++ b/net/core/skbuff.c
> >> @@ -2326,7 +2326,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
> >>   		if (skb_orphan_frags(skb, gfp_mask))
> >>   			goto nofrags;
> >>   		if (skb_zcopy(skb))
> >> -			refcount_inc(&skb_uarg(skb)->refcnt);
> >> +			net_zcopy_get(skb_zcopy(skb));
> > This adds some unnecessary instructions in skb_zcopy.
> >
> > It's not terrible and I see the consistency argument. But would make
> > more sense to update the recently added pskb_carve_.. variants instead.
> 
> Thanks for your review.
> 
> The skb_zcopy() does indeed make repeated checks. So following your 
> suggestion,
> 
> I have modified the code as follows:
> 
> -                       net_zcopy_get(skb_zcopy(skb));
> +                       net_zcopy_get(skb_uarg(skb));
> 
> Does it seem more reasonable?
> 
> And I plan to modify all three functions in patch version 2.
> 

That sounds good to me, thanks.


^ permalink raw reply

* Re: [PATCH net] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Vadim Fedorenko @ 2026-07-07 15:25 UTC (permalink / raw)
  To: Cen Zhang, jmaloy, davem, edumazet, kuba, pabeni, horms
  Cc: netdev, tipc-discussion, linux-kernel, AutonomousCodeSecurity,
	tgopinath, kys
In-Reply-To: <CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com>

On 06/07/2026 19:57, Cen Zhang wrote:
> Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied MTU
> values but only enforce a minimum bound, not a maximum. When a user sets
> the MTU to a value exceeding U16_MAX (65535), it passes validation but
> is silently truncated when assigned to u16 fields l->mtu and
> l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000)
> truncate to 0, causing a division by zero in tipc_link_set_queue_limits()
> which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing
> values (e.g. 65537-131071) produce small incorrect MTU values, resulting
> in link malfunction behaviors.
> 
> Crash stack (triggered as unprivileged user):
> 
>    tipc_link_set_queue_limits  net/tipc/link.c:2531
>    tipc_link_create            net/tipc/link.c:520
>    tipc_node_check_dest        net/tipc/node.c:1279
>    tipc_disc_rcv               net/tipc/discover.c:252
>    tipc_rcv                    net/tipc/node.c:2129
>    tipc_udp_recv               net/tipc/udp_media.c:392
> 
> Two independent paths lack the upper bound check:
> 1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET)
> 2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET)
> 
> Fix both by rejecting MTU values above U16_MAX.
> 
> Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU")
> Reported-by: AutonomousCodeSecurity@microsoft.com
> Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
> ---
>   net/tipc/bearer.c    | 6 ++++++
>   net/tipc/udp_media.h | 5 +++++
>   2 files changed, 11 insertions(+)
> 
> diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
> index 05dcd2f9e..0926692dd 100644
> --- a/net/tipc/bearer.c
> +++ b/net/tipc/bearer.c
> @@ -1163,6 +1163,12 @@ int __tipc_nl_bearer_set(struct sk_buff *skb,
> struct genl_info *info)
>                                                 "MTU value is out-of-range");
>                                  return -EINVAL;
>                          }
> +                       if (nla_get_u32(props[TIPC_NLA_PROP_MTU]) >
> +                           U16_MAX) {
> +                               NL_SET_ERR_MSG(info->extack,
> +                                              "MTU value is out-of-range");
> +                               return -EINVAL;
> +                       }
>                          b->mtu = nla_get_u32(props[TIPC_NLA_PROP_MTU]);
>                          tipc_node_apply_property(net, b, TIPC_NLA_PROP_MTU);
>   #endif
Just wonder if this can be done with NLA_POLICY_RANGE/MAX/etc...


^ permalink raw reply

* Re: [PATCH 5/9] ax88179_178a: Add support for ethtool pause parameter configuration
From: Andrew Lunn @ 2026-07-07 15:27 UTC (permalink / raw)
  To: Birger Koblitz
  Cc: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, linux-usb, netdev, linux-kernel
In-Reply-To: <833f79c1-39be-4c04-bc4f-3ec7a983ab56@birger-koblitz.de>

> For me the way ahead is to stick with the simple MII-driver and just make sure that
> features such as pause configuration are properly implemented. Please tell me if that
> makes sense or there are better ways.

The better way forward is to refuse to support the device in Mainline,
until the vendor produces a firmware version which is not FUBAR.

The PHY ID is a contract, it states compatibility. If different
firmware gives it a different API, it must have a different ID.

Mainline can push back on vendors. We can say only their out of tree
vendor crap driver will be used with their hardware, if they don't
follow the basic rules as defined in 802.3.

There are plenty of USB dongles what just work in Linux, because they
follow the rules. So i don't see it as being a particularly big loss
to Mainline.

   Andrew

^ permalink raw reply

* [PATCH net v5] net: airoha: fix MIB stats collection to be lossless
From: Aniket Negi @ 2026-07-07 15:26 UTC (permalink / raw)
  To: netdev, lorenzo
  Cc: matthias.bgg, angelogioacchino.delregno, aniket.negi, Aniket Negi,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Christian Marangi, linux-arm-kernel,
	linux-mediatek, linux-kernel

REG_FE_GDM_MIB_CLEAR after every read creates a race window where
packets arriving between read and clear are lost from statistics.

Switch to a delta-based approach instead:

- 64-bit H+L registers (ok pkts/bytes, E64..L1023): read absolute
  hardware total directly into a local variable; clamp with max(new, old)
  to prevent torn-read regression when the counter carries between the
  two reads.

- 32-bit registers (drops, bc, mc, errors, runt, long): accumulate
  (u32)(curr - prev) into a 64-bit software counter; unsigned
  subtraction handles wrap-around transparently.

- tx/rx_len[0] ([0,64] bucket): combines RUNT_CNT (32-bit, delta via
  tx_runt/rx_runt) and E64_CNT (64-bit, absolute) into a single local
  accumulator; max(new, old) applied here too to guard against a torn
  read of E64 when the RUNT accumulator is unchanged between polls.

MIB counters are zeroed by the SCU FE reset (EN7581_FE_RST) asserted
in airoha_hw_init() at module load, so no explicit MIB clear is needed
in airoha_fe_init().

Merge airoha_dev_get_hw_stats() into airoha_update_hw_stats() and
move stats_lock inside. Plain spin_lock() is correct: the function
is only called from ndo_get_stats64() in process context. Each dev
refreshes only its own MIB counters; sibling devs on a shared GDM3/4
port are polled when their own netdev is queried.

Fixes: 8f4695fb67b2 ("net: airoha: better handle MIBs for GDM ports with multiple devs attached")
Signed-off-by: Aniket Negi <aniket.negi03@gmail.com>
---

Changes in v5:
- Link to V4: https://lore.kernel.org/20260706154730.36949-1-aniket.negi03@gmail.com
- Drop MIB clear loop from airoha_fe_init(): SCU FE reset (EN7581_FE_RST)
  resets MIB counters at module load, making the explicit clear redundant
- Rename local variable tmp -> data; drop prev variable; use + instead
  of | for H+L combination; inline max(data, dev->stats.x) per
  maintainer nits (Lorenzo Bianconi)
- Extend max() clamping to the hybrid tx/rx_len[0] (RUNT+E64) bucket to
  guard against E64 torn reads when the RUNT accumulator is stable
- Fix undefined behaviour: split i++ out of expressions that also read
  i as an array subscript in the same statement
- Retain REG_FE_GDM_MIB_CLEAR/FE_GDM_MIB_{RX,TX}_CLEAR_MASK definitions
  in airoha_regs.h as register documentation (per Lorenzo Bianconi)

Changes in v4:
- Add max(new, old) clamping for 64-bit H+L register pairs to ensure
  monotonically non-decreasing stats despite torn reads between H and L
- Use local variable for all 64-bit H+L computations to prevent lockless
  readers from seeing intermediate values during piecewise write
- Add one-shot MIB counter clear in airoha_fe_init() to establish a
  clean baseline (kexec, driver rebind, warm reboot)
- Document sibling dev polling design in commit message

Changes in v3:
- Link to V2: https://lore.kernel.org/20260701173941.314795-1-aniket.negi03@gmail.com/
- Add Acked-by tag from Lorenzo
- Rename from tx_runt_cnt to tx_runt, tx_long_cnt to tx_long,
  tx_runt_accum64 to tx_runt64
- Rename from rx_runt_cnt to rx_runt, rx_long_cnt to rx_long,
  rx_runt_accum64 to rx_runt64
- Condense the marked comments in V2, remove new line after comment

Changes in v2:
- Link to V1: https://lore.kernel.org/20260630111834.233643-1-aniket.negi03@gmail.com
- Store _CNT_L register reads in val before adding to stats
- Fix double-counting bug in the RUNT+E64 combined bucket
- Replace 7-element tx_len[]/rx_len[] shadow arrays with focused fields
- Rename inner struct hw_prev_stats to mib_prev
- Rename airoha_dev_get_hw_stats() to airoha_update_hw_stats() and
  move the port spin_lock inside, removing the separate wrapper
---
 drivers/net/ethernet/airoha/airoha_eth.c | 171 ++++++++++++++---------
 drivers/net/ethernet/airoha/airoha_eth.h |  27 ++++
 2 files changed, 132 insertions(+), 66 deletions(-)

diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 59001fd4b6f7..90aa8b0210bd 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1686,11 +1686,14 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
 	}
 }
 
-static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
 {
 	struct airoha_gdm_port *port = dev->port;
 	struct airoha_eth *eth = dev->eth;
 	u32 val, i = 0;
+	u64 data;
+
+	spin_lock(&port->stats_lock);
 
 	/* Read relevant MIB for GDM with multiple port attached */
 	if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
@@ -1701,152 +1704,188 @@ static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
 
 	u64_stats_update_begin(&dev->stats.syncp);
 
-	/* TX */
+	/* TX - 64-bit H+L registers: hw accumulates the total, read directly.
+	 * Use local variable to prevent readers from seeing intermediate values.
+	 * Clamp to prevent regression from torn reads between H and L.
+	 */
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
-	dev->stats.tx_ok_pkts += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
-	dev->stats.tx_ok_pkts += val;
+	data += val;
+	dev->stats.tx_ok_pkts = max(data, dev->stats.tx_ok_pkts);
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
-	dev->stats.tx_ok_bytes += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
-	dev->stats.tx_ok_bytes += val;
+	data += val;
+	dev->stats.tx_ok_bytes = max(data, dev->stats.tx_ok_bytes);
 
+	/* TX - 32-bit registers: accumulate delta to handle wrap-around. */
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
-	dev->stats.tx_drops += val;
+	dev->stats.tx_drops += (u32)(val - dev->stats.mib_prev.tx_drops);
+	dev->stats.mib_prev.tx_drops = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
-	dev->stats.tx_broadcast += val;
+	dev->stats.tx_broadcast += (u32)(val - dev->stats.mib_prev.tx_broadcast);
+	dev->stats.mib_prev.tx_broadcast = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
-	dev->stats.tx_multicast += val;
+	dev->stats.tx_multicast += (u32)(val - dev->stats.mib_prev.tx_multicast);
+	dev->stats.mib_prev.tx_multicast = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
-	dev->stats.tx_len[i] += val;
+	dev->stats.mib_prev.tx_runt64 +=
+		(u32)(val - dev->stats.mib_prev.tx_runt);
+	dev->stats.mib_prev.tx_runt = val;
 
+	/* tx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */
+	data = dev->stats.mib_prev.tx_runt64;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
-	dev->stats.tx_len[i] += ((u64)val << 32);
+	data += (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
-	dev->stats.tx_len[i++] += val;
+	data += val;
+	dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
-	dev->stats.tx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
-	dev->stats.tx_len[i++] += val;
+	data += val;
+	dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
-	dev->stats.tx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
-	dev->stats.tx_len[i++] += val;
+	data += val;
+	dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
-	dev->stats.tx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
-	dev->stats.tx_len[i++] += val;
+	data += val;
+	dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
-	dev->stats.tx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
-	dev->stats.tx_len[i++] += val;
+	data += val;
+	dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
-	dev->stats.tx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
-	dev->stats.tx_len[i++] += val;
+	data += val;
+	dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
-	dev->stats.tx_len[i++] += val;
+	dev->stats.tx_len[i++] += (u32)(val - dev->stats.mib_prev.tx_long);
+	dev->stats.mib_prev.tx_long = val;
 
 	/* RX */
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
-	dev->stats.rx_ok_pkts += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
-	dev->stats.rx_ok_pkts += val;
+	data += val;
+	dev->stats.rx_ok_pkts = max(data, dev->stats.rx_ok_pkts);
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
-	dev->stats.rx_ok_bytes += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
-	dev->stats.rx_ok_bytes += val;
+	data += val;
+	dev->stats.rx_ok_bytes = max(data, dev->stats.rx_ok_bytes);
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
-	dev->stats.rx_drops += val;
+	dev->stats.rx_drops += (u32)(val - dev->stats.mib_prev.rx_drops);
+	dev->stats.mib_prev.rx_drops = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
-	dev->stats.rx_broadcast += val;
+	dev->stats.rx_broadcast += (u32)(val - dev->stats.mib_prev.rx_broadcast);
+	dev->stats.mib_prev.rx_broadcast = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
-	dev->stats.rx_multicast += val;
+	dev->stats.rx_multicast += (u32)(val - dev->stats.mib_prev.rx_multicast);
+	dev->stats.mib_prev.rx_multicast = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
-	dev->stats.rx_errors += val;
+	dev->stats.rx_errors += (u32)(val - dev->stats.mib_prev.rx_errors);
+	dev->stats.mib_prev.rx_errors = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
-	dev->stats.rx_crc_error += val;
+	dev->stats.rx_crc_error += (u32)(val - dev->stats.mib_prev.rx_crc_error);
+	dev->stats.mib_prev.rx_crc_error = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
-	dev->stats.rx_over_errors += val;
+	dev->stats.rx_over_errors += (u32)(val - dev->stats.mib_prev.rx_over_errors);
+	dev->stats.mib_prev.rx_over_errors = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
-	dev->stats.rx_fragment += val;
+	dev->stats.rx_fragment += (u32)(val - dev->stats.mib_prev.rx_fragment);
+	dev->stats.mib_prev.rx_fragment = val;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
-	dev->stats.rx_jabber += val;
+	dev->stats.rx_jabber += (u32)(val - dev->stats.mib_prev.rx_jabber);
+	dev->stats.mib_prev.rx_jabber = val;
 
 	i = 0;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
-	dev->stats.rx_len[i] += val;
+	dev->stats.mib_prev.rx_runt64 +=
+		(u32)(val - dev->stats.mib_prev.rx_runt);
+	dev->stats.mib_prev.rx_runt = val;
 
+	/* rx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */
+	data = dev->stats.mib_prev.rx_runt64;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
-	dev->stats.rx_len[i] += ((u64)val << 32);
+	data += (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
-	dev->stats.rx_len[i++] += val;
+	data += val;
+	dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
-	dev->stats.rx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
-	dev->stats.rx_len[i++] += val;
+	data += val;
+	dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
-	dev->stats.rx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
-	dev->stats.rx_len[i++] += val;
+	data += val;
+	dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
-	dev->stats.rx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
-	dev->stats.rx_len[i++] += val;
+	data += val;
+	dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
-	dev->stats.rx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
-	dev->stats.rx_len[i++] += val;
+	data += val;
+	dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
-	dev->stats.rx_len[i] += ((u64)val << 32);
+	data = (u64)val << 32;
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
-	dev->stats.rx_len[i++] += val;
+	data += val;
+	dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]);
+	i++;
 
 	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
-	dev->stats.rx_len[i++] += val;
+	dev->stats.rx_len[i] += (u32)(val - dev->stats.mib_prev.rx_long);
+	dev->stats.mib_prev.rx_long = val;
 
 	u64_stats_update_end(&dev->stats.syncp);
-}
-
-static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
-{
-	struct airoha_gdm_port *port = dev->port;
-	int i;
-
-	spin_lock(&port->stats_lock);
-
-	for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
-		if (port->devs[i])
-			airoha_dev_get_hw_stats(port->devs[i]);
-	}
-
-	/* Reset MIB counters */
-	airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id),
-		      FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
 
 	spin_unlock(&port->stats_lock);
 }
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index f6d01a8e8da1..fe934f9ffe8a 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -245,6 +245,33 @@ struct airoha_hw_stats {
 	u64 rx_fragment;
 	u64 rx_jabber;
 	u64 rx_len[7];
+
+	struct {
+		/* Previous HW register values for 32-bit counter delta
+		 * tracking. Storing the last seen value and accumulating
+		 * (u32)(curr - prev) into the 64-bit software counter
+		 * handles wrap-around transparently via unsigned arithmetic.
+		 * tx_runt64/rx_runt64 hold the running sum of runt deltas.
+		 * These fields are never reported to userspace.
+		 */
+		u32 tx_drops;
+		u32 tx_broadcast;
+		u32 tx_multicast;
+		u32 tx_runt;
+		u32 tx_long;
+		u64 tx_runt64;
+		u32 rx_drops;
+		u32 rx_broadcast;
+		u32 rx_multicast;
+		u32 rx_errors;
+		u32 rx_crc_error;
+		u32 rx_over_errors;
+		u32 rx_fragment;
+		u32 rx_jabber;
+		u32 rx_runt;
+		u32 rx_long;
+		u64 rx_runt64;
+	} mib_prev;
 };
 
 enum {

base-commit: 60444706aa17616efc03190d099ac347e28b3d0a
-- 
2.43.0


^ permalink raw reply related

* RE: [Intel-wired-lan] [PATCH iwl-next v8 07/15] idpf: remove unused code for getting RSS info from device
From: Salin, Samuel @ 2026-07-07 15:30 UTC (permalink / raw)
  To: Loktionov, Aleksandr, Zaremba, Larysa,
	intel-wired-lan@lists.osuosl.org, Nguyen, Anthony L
  Cc: Lobakin, Aleksander, Samudrala, Sridhar, Michal Swiatkowski,
	Zaremba, Larysa, Fijalkowski, Maciej, Tantilov, Emil S,
	Chittim, Madhu, Hay, Joshua A, Keller, Jacob E,
	Shanmugam, Jayaprakash, Jiri Pirko, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Richard Cochran, Kitszel, Przemyslaw, Andrew Lunn,
	netdev@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <IA3PR11MB89866EC8B063AA4152CABB00E51C2@IA3PR11MB8986.namprd11.prod.outlook.com>



> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of
> Loktionov, Aleksandr
> Sent: Monday, June 8, 2026 8:11 AM
> To: Zaremba, Larysa <larysa.zaremba@intel.com>; intel-wired-
> lan@lists.osuosl.org; Nguyen, Anthony L <anthony.l.nguyen@intel.com>
> Cc: Lobakin, Aleksander <aleksander.lobakin@intel.com>; Samudrala, Sridhar
> <sridhar.samudrala@intel.com>; Michal Swiatkowski
> <michal.swiatkowski@linux.intel.com>; Zaremba, Larysa
> <larysa.zaremba@intel.com>; Fijalkowski, Maciej
> <maciej.fijalkowski@intel.com>; Tantilov, Emil S <emil.s.tantilov@intel.com>;
> Chittim, Madhu <madhu.chittim@intel.com>; Hay, Joshua A
> <joshua.a.hay@intel.com>; Keller, Jacob E <jacob.e.keller@intel.com>;
> Shanmugam, Jayaprakash <jayaprakash.shanmugam@intel.com>; Jiri Pirko
> <jiri@resnulli.us>; David S. Miller <davem@davemloft.net>; Eric Dumazet
> <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni
> <pabeni@redhat.com>; Simon Horman <horms@kernel.org>; Jonathan
> Corbet <corbet@lwn.net>; Richard Cochran <richardcochran@gmail.com>;
> Kitszel, Przemyslaw <przemyslaw.kitszel@intel.com>; Andrew Lunn
> <andrew+netdev@lunn.ch>; netdev@vger.kernel.org; linux-
> doc@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [Intel-wired-lan] [PATCH iwl-next v8 07/15] idpf: remove unused
> code for getting RSS info from device
> 
> 
> 
> > -----Original Message-----
> > From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> > Of Larysa Zaremba
> > Sent: Monday, June 8, 2026 4:41 PM
> > To: intel-wired-lan@lists.osuosl.org; Nguyen, Anthony L
> > <anthony.l.nguyen@intel.com>
> > Cc: Lobakin, Aleksander <aleksander.lobakin@intel.com>; Samudrala,
> > Sridhar <sridhar.samudrala@intel.com>; Michal Swiatkowski
> > <michal.swiatkowski@linux.intel.com>; Zaremba, Larysa
> > <larysa.zaremba@intel.com>; Fijalkowski, Maciej
> > <maciej.fijalkowski@intel.com>; Tantilov, Emil S
> > <emil.s.tantilov@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> > Hay, Joshua A <joshua.a.hay@intel.com>; Keller, Jacob E
> > <jacob.e.keller@intel.com>; Shanmugam, Jayaprakash
> > <jayaprakash.shanmugam@intel.com>; Jiri Pirko <jiri@resnulli.us>;
> > David S. Miller <davem@davemloft.net>; Eric Dumazet
> > <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni
> > <pabeni@redhat.com>; Simon Horman <horms@kernel.org>; Jonathan
> Corbet
> > <corbet@lwn.net>; Richard Cochran <richardcochran@gmail.com>; Kitszel,
> > Przemyslaw <przemyslaw.kitszel@intel.com>; Andrew Lunn
> > <andrew+netdev@lunn.ch>; netdev@vger.kernel.org; linux-
> > doc@vger.kernel.org; linux-kernel@vger.kernel.org
> > Subject: [Intel-wired-lan] [PATCH iwl-next v8 07/15] idpf: remove
> > unused code for getting RSS info from device
> >
> > idpf_send_get_set_rss_lut_msg() and idpf_send_get_set_rss_key_msg() do
> > not handle the get=true path properly. Response validation is
> > insufficient, memcpy size is wrong, LE-to-CPU conversion is missing.
> > Fortunately, those functions are never used with get=true. Given how
> > broken this dead code is, it is unlikely to be useful in the future.
> >
> > Rename idpf_send_get_set_rss_lut_msg() to idpf_send_set_rss_lut_msg(),
> > idpf_send_get_set_rss_key_msg() to idpf_send_set_rss_key_msg(), remove
> > the get parameter and remove all get=true cases from the function.
> >
> > Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> > Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
> > ---
> >  drivers/net/ethernet/intel/idpf/idpf_txrx.c   |   4 +-
> >  .../net/ethernet/intel/idpf/idpf_virtchnl.c   | 107 +++--------------
> > -
> >  .../net/ethernet/intel/idpf/idpf_virtchnl.h   |  10 +-
> >  3 files changed, 22 insertions(+), 99 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
> > b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
> > index f6b3b15364ff..d744db0efd3f 100644
> > --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
> > +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
> > @@ -4679,11 +4679,11 @@ int idpf_config_rss(struct idpf_vport *vport,
> > struct idpf_rss_data *rss_data)
> >  	u32 vport_id = vport->vport_id;
> >  	int err;
> >
> 
> ...
> 
> > vport_id);
> >  void idpf_vc_xn_shutdown(struct idpf_vc_xn_manager *vcxn_mngr);  int
> > idpf_idc_rdma_vc_send_sync(struct iidc_rdma_core_dev_info *cdev_info,
> >  			       u8 *send_msg, u16 msg_size,
> > --
> > 2.47.0
> 
> 
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>

Tested-by: Samuel Salin <Samuel.salin@intel.com>

^ permalink raw reply

* Re: [PATCH net v2] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
From: Petr Pavlu @ 2026-07-07 15:32 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: Qingfang Deng, Breno Leitao, Norbert Szetei, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Taegu Ha, Kees Cook, linux-ppp, linux-kernel, Guillaume Nault,
	netdev, Luis Chamberlain, Daniel Gomez, Sami Tolvanen,
	Aaron Tomlin, linux-modules, Paul E. McKenney
In-Reply-To: <20260706092926.PIlrhRKz@linutronix.de>

On 7/6/26 11:29 AM, Sebastian Andrzej Siewior wrote:
> + MODULE maintainer

+ Paul E. McKenney

> 
> On 2026-07-05 10:57:44 [+0800], Qingfang Deng wrote:
>> On 7/4/2026 at 12:32 AM, Breno Leitao wrote:
>>> On Fri, Jul 03, 2026 at 03:27:00PM +0800, Qingfang Deng wrote:
>>>> AI-review found an issue: https://sashiko.dev/#/patchset/D9C0245B-608B-4884-8A09-F55BA4A9F948%40doyensec.com
>>>>
>>>> An rcu_barrier() call is needed at the end of ppp_cleanup().
>>>
>>> I was initially unclear why rcu_barrier() would be necessary on a kfree path,
>>> but it appears to be required during module unload to ensure that
>>> ppp_release_channel_free() completes before the module's struct rcu_head is
>>> destroyed. Is that the correct understanding?
>>
>> It's required to ensure that all ppp_release_channel_free() callback
>> complete before the text segment of the module is unloaded.
> 
> So either a rcu_barrier() in ppp's module_exit() callback or a
> synchronize_rcu() instead of the call_rcu(). And all this because the
> module RCU callbacks pending which can be invoked after the module has
> been removed. There is a synchronize_rcu() during module exit but this
> is after the module code is gone.
> 
> I'm curious how many modules have a call_rcu() within their code but
> don't have anything to enforce its completion before module removal is
> complete? Wouldn't something like
> 
> 
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index 46dd8d25a6058..8eae1ea2d6eb4 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -858,6 +858,9 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
>  		goto out;
>  
>  	mutex_unlock(&module_mutex);
> +
> +	/* Ensure all rcu callbacks issued by the module have completed */
> +	rcu_barrier();
>  	/* Final destruction now no one is using it. */
>  	if (mod->exit != NULL)
>  		mod->exit();
> 
> make sense?

This is discussed in Documentation/RCU/rcubarrier.rst and
Documentation/RCU/Design/Requirements/Requirements.rst. The latter
contains:

| Loadable Modules
| ~~~~~~~~~~~~~~~~
| 
| The Linux kernel has loadable modules, and these modules can also be
| unloaded. After a given module has been unloaded, any attempt to call
| one of its functions results in a segmentation fault. The module-unload
| functions must therefore cancel any delayed calls to loadable-module
| functions, for example, any outstanding mod_timer() must be dealt
| with via timer_shutdown_sync() or similar.
| 
| Unfortunately, there is no way to cancel an RCU callback; once you
| invoke call_rcu(), the callback function is eventually going to be
| invoked, unless the system goes down first. Because it is normally
| considered socially irresponsible to crash the system in response to a
| module unload request, we need some other way to deal with in-flight RCU
| callbacks.
| 
| RCU therefore provides rcu_barrier(), which waits until all
| in-flight RCU callbacks have been invoked. If a module uses
| call_rcu(), its exit function should therefore prevent any future
| invocation of call_rcu(), then invoke rcu_barrier(). In theory,
| the underlying module-unload code could invoke rcu_barrier()
| unconditionally, but in practice this would incur unacceptable
| latencies.

I don't know if the last part about unacceptable latencies is still
relevant. I haven't done any measurements myself.

-- 
Thanks,
Petr

^ permalink raw reply

* [RESEND PATCH] net/core: consolidate RPS dispatch into netif_rps() helpers
From: Jemmy Wong @ 2026-07-07 15:48 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, andrew+netdev, davem, edumazet, kuba, pabeni, horms,
	Jemmy Wong
In-Reply-To: <20260702152830.39065-1-jemmywong512@gmail.com>

From: "Jemmy Wong" <jemmywong512@gmail.com>

The RPS steering logic in netif_rx_internal(), netif_receive_skb_internal()
and netif_receive_skb_list_internal() was open-coded three times, each with
its own #ifdef CONFIG_RPS block and manual rcu_read_lock()/unlock() pairs.

Factor it into two helpers, netif_rps() for the single-skb path and
netif_rps_list() for the list path, and switch the callers to
guard(rcu)/scoped_guard(rcu). A new internal NET_RX_UNHANDLED sentinel lets
a helper report "RPS did not take this skb" so the caller falls back to the
local enqueue / __netif_receive_skb() path; it never escapes to callers.

netif_rps_list() keeps the early static_branch_unlikely(&rps_needed) bail
out so the list is not needlessly walked and re-spliced when RPS is
compiled in but disabled.

No functional change intended.

Signed-off-by: Jemmy Wong <jemmywong512@gmail.com>
---
 include/linux/netdevice.h |  5 ++-
 net/core/dev.c            | 94 +++++++++++++++++++--------------------
 2 files changed, 48 insertions(+), 51 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9981d637f8b5..c265b78082e3 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -93,8 +93,9 @@ void netdev_set_default_ethtool_ops(struct net_device *dev,
 void netdev_sw_irq_coalesce_default_on(struct net_device *dev);

 /* Backlog congestion levels */
-#define NET_RX_SUCCESS		0	/* keep 'em coming, baby */
-#define NET_RX_DROP		1	/* packet dropped */
+#define NET_RX_UNHANDLED	-1
+#define NET_RX_SUCCESS		0
+#define NET_RX_DROP		1

 #define MAX_NEST_DEV 8

diff --git a/net/core/dev.c b/net/core/dev.c
index 4b3d5cfdf6e0..259f8c8e5657 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5426,6 +5426,38 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
 	return NET_RX_DROP;
 }

+static inline int netif_rps(struct sk_buff *skb)
+{
+#ifdef CONFIG_RPS
+	if (static_branch_unlikely(&rps_needed)) {
+		struct rps_dev_flow voidflow, *rflow = &voidflow;
+		int cpu = get_rps_cpu(skb->dev, skb, &rflow);
+
+		if (cpu >= 0)
+			return enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
+	}
+#endif
+	return NET_RX_UNHANDLED;
+}
+
+static inline void netif_rps_list(struct list_head *head)
+{
+#ifdef CONFIG_RPS
+	struct sk_buff *skb, *next;
+	LIST_HEAD(undo_list);
+
+	if (!static_branch_unlikely(&rps_needed))
+		return;
+
+	list_for_each_entry_safe(skb, next, head, list) {
+		skb_list_del_init(skb);
+		if (netif_rps(skb) == NET_RX_UNHANDLED)
+			list_add_tail(&skb->list, &undo_list);
+	}
+	list_splice_init(&undo_list, head);
+#endif
+}
+
 static struct netdev_rx_queue *netif_get_rxqueue(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
@@ -5695,33 +5727,20 @@ EXPORT_SYMBOL_GPL(do_xdp_generic);

 static int netif_rx_internal(struct sk_buff *skb)
 {
-	int ret;
+	int ret = NET_RX_UNHANDLED;
+	unsigned int qtail;

 	net_timestamp_check(READ_ONCE(net_hotdata.tstamp_prequeue), skb);

 	trace_netif_rx(skb);

-#ifdef CONFIG_RPS
-	if (static_branch_unlikely(&rps_needed)) {
-		struct rps_dev_flow voidflow, *rflow = &voidflow;
-		int cpu;
-
-		rcu_read_lock();
-
-		cpu = get_rps_cpu(skb->dev, skb, &rflow);
-		if (cpu < 0)
-			cpu = smp_processor_id();
+	scoped_guard(rcu)
+		ret = netif_rps(skb);
+	if (ret != NET_RX_UNHANDLED)
+		return ret;

-		ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
+	ret = enqueue_to_backlog(skb, smp_processor_id(), &qtail);

-		rcu_read_unlock();
-	} else
-#endif
-	{
-		unsigned int qtail;
-
-		ret = enqueue_to_backlog(skb, smp_processor_id(), &qtail);
-	}
 	return ret;
 }

@@ -6389,21 +6408,12 @@ static int netif_receive_skb_internal(struct sk_buff *skb)
 	if (skb_defer_rx_timestamp(skb))
 		return NET_RX_SUCCESS;

-	rcu_read_lock();
-#ifdef CONFIG_RPS
-	if (static_branch_unlikely(&rps_needed)) {
-		struct rps_dev_flow voidflow, *rflow = &voidflow;
-		int cpu = get_rps_cpu(skb->dev, skb, &rflow);
+	guard(rcu)();
+	ret = netif_rps(skb);
+	if (ret != NET_RX_UNHANDLED)
+		return ret;

-		if (cpu >= 0) {
-			ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
-			rcu_read_unlock();
-			return ret;
-		}
-	}
-#endif
 	ret = __netif_receive_skb(skb);
-	rcu_read_unlock();
 	return ret;
 }

@@ -6421,23 +6431,9 @@ void netif_receive_skb_list_internal(struct list_head *head)
 	}
 	list_splice_init(&sublist, head);

-	rcu_read_lock();
-#ifdef CONFIG_RPS
-	if (static_branch_unlikely(&rps_needed)) {
-		list_for_each_entry_safe(skb, next, head, list) {
-			struct rps_dev_flow voidflow, *rflow = &voidflow;
-			int cpu = get_rps_cpu(skb->dev, skb, &rflow);
-
-			if (cpu >= 0) {
-				/* Will be handled, remove from list */
-				skb_list_del_init(skb);
-				enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
-			}
-		}
-	}
-#endif
+	guard(rcu)();
+	netif_rps_list(head);
 	__netif_receive_skb_list(head);
-	rcu_read_unlock();
 }

 /**
--
2.25.1

^ permalink raw reply related

* Re: [PATCH net-next] net: phylink: reject unsupported speed/duplex in ksettings_set() with PHY
From: Andrew Lunn @ 2026-07-07 15:52 UTC (permalink / raw)
  To: muhammad.nazim.amirul.nazle.asmade
  Cc: linux, hkallweit1, davem, edumazet, kuba, pabeni, netdev,
	linux-kernel
In-Reply-To: <20260707062735.17776-1-muhammad.nazim.amirul.nazle.asmade@altera.com>

On Mon, Jul 06, 2026 at 11:27:35PM -0700, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
> 
> When using ethtool to change speed and duplex on a phylink-managed
> interface with a PHY attached, the requested speed/duplex combination
> is not validated against the MAC's supported capabilities before being
> passed down to the PHY layer.

How are you invoking ethtool here? And what other invocations have you
tested, with this patch in place?

> @@ -2989,6 +2989,10 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
>  	if (pl->phydev) {
>  		struct ethtool_link_ksettings phy_kset = *kset;
>  
> +		if (!phy_caps_lookup(kset->base.speed, kset->base.duplex,
> +				     pl->supported, true))
> +			return -EINVAL;

Are base.speed and base.duplex always set? When looking at
phy_ethtool_ksettings_set() the values are only used when base.autoneg
is false. So maybe they are not always valid?

   Andrew

^ permalink raw reply

* Re: [PATCH v3 05/20] driver core: update kerneldoc for platform_device_alloc()
From: Andy Shevchenko @ 2026-07-07 16:05 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel,
	Will Deacon, Robin Murphy, Doug Berger, Florian Fainelli,
	Broadcom internal kernel review list, Ulf Hansson, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
	Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu, Philipp Zabel,
	Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt, brgl, linux-kernel,
	netdev, linux-arm-msm, linux-sound, driver-core, devicetree,
	linuxppc-dev, linux-i2c, iommu, linux-pm, imx, linux-arm-kernel,
	intel-xe, dri-devel, linux-usb, linux-mips, platform-driver-x86,
	mfd, Manuel Ebner
In-Reply-To: <20260706-pdev-fwnode-ref-v3-5-1ff028e33779@oss.qualcomm.com>

On Mon, Jul 06, 2026 at 02:44:17PM +0200, Bartosz Golaszewski wrote:
> Users of platform_device_alloc() + platform_device_add() must not modify
> certain fields of the dynamically created platform device object. Update
> the kernel doc to say which fields are affected and which functions to
> use.

Consider using __private checker attribute for them as well. It will make
sparse scream.

> Suggested-by: Manuel Ebner <manuelebner@mailbox.org>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH net-next] net/tcp: Add explicit tracepoint for tcp_syn_ack_timeout()
From: Emil Tsalapatis @ 2026-07-07 16:08 UTC (permalink / raw)
  To: Eric Dumazet, Emil Tsalapatis
  Cc: netdev, linux-trace-kernel, ncardwell, kuniyu, rostedt, mhiramat,
	davem, kuba, pabeni
In-Reply-To: <CANn89iL23Rsu3iGXxhw4pAnCB-JgU7fnrryDLXKj4jHqSs=HOw@mail.gmail.com>

On Tue Jul 7, 2026 at 3:52 AM EDT, Eric Dumazet wrote:
> On Mon, Jul 6, 2026 at 6:01 PM Emil Tsalapatis <emil@etsalapatis.com> wrote:
>>
>> Clang can inline the tcp_syn_ack_timeout() function during compilation,
>> making it impossible to use kprobes for tracing without preventing
>> inlining. Add an explicit tracepoint to it instead.
>
> So much copy/pasting for a very small issue :/
>
>>
>> Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
>> ---
>>  include/trace/events/tcp.h | 72 ++++++++++++++++++++++++++++++++++++++
>>  net/ipv4/tcp_timer.c       |  3 ++
>>  2 files changed, 75 insertions(+)
>>
>
> tcp_syn_ack_timeout() is hardly a fast path, so you can instead:
>
> diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
> index 322db13333c7..ab2c3de19e46 100644
> --- a/net/ipv4/tcp_timer.c
> +++ b/net/ipv4/tcp_timer.c
> @@ -748,7 +748,7 @@ static void tcp_write_timer(struct timer_list *t)
>         sock_put(sk);
>  }
>
> -void tcp_syn_ack_timeout(const struct request_sock *req)
> +noinline_for_tracing void tcp_syn_ack_timeout(const struct request_sock *req)
>  {
>         struct net *net = read_pnet(&inet_rsk(req)->ireq_net);

Sounds good, I will respin and just mark it noinline.

^ permalink raw reply

* Re: [PATCH net-next v2 1/2] dt-bindings: net: nfc: samsung,s3fwrn5: add S3NRN4V and clk-req-gpios
From: Conor Dooley @ 2026-07-07 16:08 UTC (permalink / raw)
  To: Jorijn van der Graaf
  Cc: Krzysztof Kozlowski, David Heidelberg, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Rob Herring, Conor Dooley, oe-linux-nfc, netdev, devicetree,
	linux-kernel
In-Reply-To: <20260705190621.128257-2-jorijnvdgraaf@catcrafts.net>

[-- Attachment #1: Type: text/plain, Size: 52 bytes --]

Acked-by: Conor Dooley <conor.dooley@microchip.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* RE: [Intel-wired-lan] [PATCH iwl-net] idpf: fix max_vport related crash on allocation error during init
From: Salin, Samuel @ 2026-07-07 16:14 UTC (permalink / raw)
  To: Simon Horman, Tantilov, Emil S
  Cc: intel-wired-lan@lists.osuosl.org, netdev@vger.kernel.org,
	Nguyen, Anthony L, Kitszel, Przemyslaw, andrew+netdev@lunn.ch,
	davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
	pabeni@redhat.com, Chittim, Madhu
In-Reply-To: <20260622073058.GY827683@horms.kernel.org>



> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of
> Simon Horman
> Sent: Monday, June 22, 2026 12:31 AM
> To: Tantilov, Emil S <emil.s.tantilov@intel.com>
> Cc: intel-wired-lan@lists.osuosl.org; netdev@vger.kernel.org; Nguyen,
> Anthony L <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
> <przemyslaw.kitszel@intel.com>; andrew+netdev@lunn.ch;
> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com; Chittim, Madhu <madhu.chittim@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH iwl-net] idpf: fix max_vport related crash
> on allocation error during init
> 
> On Thu, Jun 18, 2026 at 12:23:25PM -0700, Emil Tantilov wrote:
> > Set adapter->max_vports only after successful allocation of vports,
> > netdevs and  vport_config buffers. This fixes possible crashes on
> > reset or rmmod, following failed allocation on init
> >
> > [  305.981402] idpf 0000:83:00.0: enabling device (0100 -> 0102) [
> > 305.994464] idpf 0000:83:00.0: Device HW Reset initiated [
> > 320.416872] BUG: kernel NULL pointer dereference, address:
> > 0000000000000000 [  320.416918] #PF: supervisor read access in kernel
> > mode [  320.416942] #PF: error_code(0x0000) - not-present page [
> > 320.416963] PGD 2099657067 P4D 0 [  320.416983] Oops: Oops: 0000
> [#1]
> > SMP NOPTI ...
> > [  320.417093] RIP: 0010:idpf_remove+0x118/0x200 [idpf] [  320.417130]
> > Code: 8b bb 98 09 00 00 e8 17 0f 5b e5 48 8b bb e8 08 00 00 e8 0b 0f
> > 5b e5 66 83 bb 28 06 00 00 00 48 8b bb 20 06 00 00 74 49 31 ed <48> 8b
> > 04 ef 48 85 c0 74 2f 48 8b 78 20 e8 66 58 91 e5 48 8b 83 20 [
> > 320.417183] RSP: 0018:ff7322212903fdb8 EFLAGS: 00010246 [
> 320.417205]
> > RAX: 0000000000000000 RBX: ff4463de40300000 RCX:
> ff7322212903fd4c [
> > 320.417228] RDX: 0000000000000001 RSI: ffffffffa7f7d100 RDI:
> > 0000000000000000 [  320.417250] RBP: 0000000000000000 R08:
> > 0000000000000001 R09: 0000000000000000 [  320.417272] R10:
> > 0000000000000001 R11: ff4463de3a638f58 R12: ff4463be89ac7000 [
> > 320.417294] R13: ff4463be89ac7198 R14: ff4463be94fc7198 R15:
> > ffffffffc0f10f20 [  320.417317] FS:  00007f963c0e6740(0000)
> > GS:ff4463fdd65d8000(0000) knlGS:0000000000000000 [  320.417342]
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [  320.417362] CR2:
> 0000000000000000 CR3: 00000020ba674002 CR4: 0000000000773ef0 [
> 320.417385] PKRU: 55555554 [  320.417398] Call Trace:
> > [  320.417412]  <TASK>
> > [  320.417429]  pci_device_remove+0x42/0xb0 [  320.417459]
> > device_release_driver_internal+0x1a9/0x210
> > [  320.417492]  driver_detach+0x4b/0x90 [  320.417516]
> > bus_remove_driver+0x70/0x100 [  320.417539]
> > pci_unregister_driver+0x2e/0xb0 [  320.417564]
> > __do_sys_delete_module.constprop.0+0x190/0x2f0
> > [  320.417592]  ? kmem_cache_free+0x31e/0x550 [  320.417619]  ?
> > lockdep_hardirqs_on_prepare+0xde/0x190
> > [  320.417644]  ? do_syscall_64+0x38/0x6b0 [  320.417665]
> > do_syscall_64+0xc8/0x6b0 [  320.417683]  ? clear_bhb_loop+0x30/0x80 [
> > 320.417706]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > [  320.417727] RIP: 0033:0x7f963bb30beb
> >
> > Fixes: 0fe45467a104 ("idpf: add create vport and netdev
> > configuration")
> > Reviewed-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
> 
> Reviewed-by: Simon Horman <horms@kernel.org>
> 
> FTR, an AI generated review of this patch is available on sashiko.dev.
> I think that the issue raised there can be looked at in the context of possible
> follow-up.


Tested-by: Samuel Salin <Samuel.salin@intel.com>

^ permalink raw reply

* [PATCH bpf] bpf, sockmap: Account for receive queue in FIONREAD without a verdict program
From: Mattia Meleleo via B4 Relay @ 2026-07-07 16:15 UTC (permalink / raw)
  To: John Fastabend, Jakub Sitnicki, Jiayuan Chen; +Cc: netdev, bpf, Mattia Meleleo

From: Mattia Meleleo <mattia.meleleo@coralogix.com>

tcp_bpf_ioctl() answers SIOCINQ from psock->msg_tot_len, which only
counts bytes in ingress_msg. Without a stream/skb verdict program
nothing is diverted there: data stays in sk_receive_queue, so FIONREAD
returns 0 even though read() returns data.

Add tcp_inq() to the reported value when the psock has no verdict
program. The two queues are disjoint, so bytes redirected into
ingress_msg from other sockets stay correctly accounted through
msg_tot_len.

Add a selftest covering FIONREAD without a verdict program.

Fixes: 929e30f93125 ("bpf, sockmap: Fix FIONREAD for sockmap")
Signed-off-by: Mattia Meleleo <mattia.meleleo@coralogix.com>
---
 net/ipv4/tcp_bpf.c                                 | 16 ++++++++-
 .../selftests/bpf/prog_tests/sockmap_basic.c       | 39 ++++++++++++++++++++++
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index cc0bd73f3..a001b1fff 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -334,6 +334,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
 
 static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
 {
+	struct sk_psock *psock;
 	bool slow;
 
 	if (cmd != SIOCINQ)
@@ -344,7 +345,20 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
 		return -EINVAL;
 
 	slow = lock_sock_fast(sk);
-	*karg = sk_psock_msg_inq(sk);
+	psock = sk_psock_get(sk);
+	if (unlikely(!psock)) {
+		unlock_sock_fast(sk, slow);
+		return tcp_ioctl(sk, cmd, karg);
+	}
+	*karg = sk_psock_get_msg_len_nolock(psock);
+	/* Without a verdict program, ingress data is never diverted to
+	 * ingress_msg: it stays in sk_receive_queue and is read through
+	 * the fallback to tcp_recvmsg(), so account for it like
+	 * tcp_ioctl() does.
+	 */
+	if (!psock->progs.stream_verdict && !psock->progs.skb_verdict)
+		*karg += tcp_inq(sk);
+	sk_psock_put(sk, psock);
 	unlock_sock_fast(sk, slow);
 
 	return 0;
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
index cb3229711..f0f368201 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
@@ -1373,6 +1373,43 @@ static void test_sockmap_multi_channels(int sotype)
 	test_sockmap_pass_prog__destroy(skel);
 }
 
+/* A socket in a sockmap without a verdict program keeps its ingress data
+ * in sk_receive_queue: FIONREAD must account for it.
+ */
+static void test_sockmap_no_verdict_fionread(void)
+{
+	int err, map, zero = 0, sent, avail;
+	int c0 = -1, c1 = -1, p0 = -1, p1 = -1;
+	struct test_sockmap_pass_prog *skel;
+	char buf[256] = "0123456789";
+
+	skel = test_sockmap_pass_prog__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "open_and_load"))
+		return;
+	map = bpf_map__fd(skel->maps.sock_map_rx);
+
+	err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1);
+	if (!ASSERT_OK(err, "create_socket_pairs()"))
+		goto out;
+
+	err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST);
+	if (!ASSERT_OK(err, "bpf_map_update_elem(c1)"))
+		goto out_close;
+
+	sent = xsend(p1, &buf, sizeof(buf), 0);
+	ASSERT_EQ(sent, sizeof(buf), "xsend(p1)");
+	avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC);
+	ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)");
+
+out_close:
+	close(c0);
+	close(p0);
+	close(c1);
+	close(p1);
+out:
+	test_sockmap_pass_prog__destroy(skel);
+}
+
 void test_sockmap_basic(void)
 {
 	if (test__start_subtest("sockmap create_update_free"))
@@ -1415,6 +1452,8 @@ void test_sockmap_basic(void)
 		test_sockmap_skb_verdict_shutdown();
 	if (test__start_subtest("sockmap skb_verdict fionread"))
 		test_sockmap_skb_verdict_fionread(true);
+	if (test__start_subtest("sockmap no_verdict fionread"))
+		test_sockmap_no_verdict_fionread();
 	if (test__start_subtest("sockmap skb_verdict fionread on drop"))
 		test_sockmap_skb_verdict_fionread(false);
 	if (test__start_subtest("sockmap skb_verdict change tail"))

---
base-commit: d2c9a99135da931377240942d44f3dea104cedb8
change-id: 20260707-fionread-no-verdict-a4f8697ac9f9

Best regards,
--  
Mattia Meleleo <mattia.meleleo@coralogix.com>



^ permalink raw reply related

* Re: [PATCH nf-next v4 3/6] net: netfilter: Add IPv4 over IPv6 tunnel flowtable acceleration
From: Lorenzo Bianconi @ 2026-07-07 16:25 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Felix Fietkau, Matthias Brugger,
	AngeloGioacchino Del Regno, Simon Horman, David Ahern,
	Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
	Shuah Khan
  Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
	coreteam, linux-kselftest
In-Reply-To: <20260703-b4-flowtable-sw-accel-ip6ip-v4-3-00398cd12382@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 7867 bytes --]

commenting on shashiko's report:
https://sashiko.dev/#/patchset/20260703-b4-flowtable-sw-accel-ip6ip-v4-0-00398cd12382%40kernel.org

> Introduce sw flowtable acceleration for the TX/RX paths of
> IPv4 over IPv6 tunnels, relying on the netfilter flowtable
> infrastructure.
> The feature can be tested with a forwarding scenario between two
> NICs (eth0 and eth1), where an IPv4 over IPv6 tunnel is used to
> reach a remote site via eth1 as the underlay device:
> 
>     ETH0 -- TUN0 <==> ETH1 -- [IP network] -- TUN1 (2001:db8:2::2)
> 
> [IP configuration]
> 
> 6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
>     link/ether 00:00:22:33:11:55 brd ff:ff:ff:ff:ff:ff
>     inet 192.168.0.2/24 scope global eth0
>        valid_lft forever preferred_lft forever
> 7: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
>     link/ether 00:11:22:33:11:55 brd ff:ff:ff:ff:ff:ff
>     inet6 2001:db8:2::1/64 scope global nodad
>        valid_lft forever preferred_lft forever
> 8: tun0@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
>     link/tunnel6 2001:db8:2::1 peer 2001:db8:2::2 permaddr ce9c:2940:7dcc::
>     inet 192.168.100.1/24 scope global tun0
>        valid_lft forever preferred_lft forever
> 
> $ ip route show
> default via 192.168.100.2 dev tun0
> 192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2
> 192.168.100.0/24 dev tun0 proto kernel scope link src 192.168.100.1
> 

[...]

>  	struct nf_flow_xmit xmit = {};
> +	struct in6_addr *ip6_daddr;
>  	struct flow_offload *flow;
>  	struct neighbour *neigh;
>  	struct rtable *rt;
> @@ -844,25 +872,41 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
>  	other_tuple = &flow->tuplehash[!dir].tuple;
>  	ip_daddr = other_tuple->src_v4.s_addr;
>  
> -	if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0)
> +	if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
> +				&ip6_daddr) < 0)
>  		return NF_DROP;
>  
>  	switch (tuplehash->tuple.xmit_type) {
> -	case FLOW_OFFLOAD_XMIT_NEIGH:
> -		rt = dst_rtable(tuplehash->tuple.dst_cache);
> +	case FLOW_OFFLOAD_XMIT_NEIGH: {
> +		struct dst_entry *dst;
> +
>  		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
>  		if (!xmit.outdev) {
>  			flow_offload_teardown(flow);
>  			return NF_DROP;
>  		}
> -		neigh = ip_neigh_gw4(rt->dst.dev, rt_nexthop(rt, ip_daddr));
> +		if (other_tuple->tun.encap_proto == AF_INET6 ||
> +		    ctx.tun.proto == IPPROTO_IPV6) {
> +			struct rt6_info *rt6;
> +
> +			rt6 = dst_rt6_info(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw6(rt6->dst.dev,
> +					     rt6_nexthop(rt6, ip6_daddr));
> +			dst = &rt6->dst;
> +		} else {
> +			rt = dst_rtable(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw4(rt->dst.dev,
> +					     rt_nexthop(rt, ip_daddr));
> +			dst = &rt->dst;
> +		}

- Does this logic correctly handle cross-family tunneling like an IPv6-in-IPv4
  packet (SIT) being forwarded out via another IPv4 tunnel?
  - SIT support is added by a following patch in the series.

>  		if (IS_ERR(neigh)) {
>  			flow_offload_teardown(flow);
>  			return NF_DROP;
>  		}
>  		xmit.dest = neigh->ha;
> -		skb_dst_set_noref(skb, &rt->dst);
> +		skb_dst_set_noref(skb, dst);
>  		break;
> +	}
>  	case FLOW_OFFLOAD_XMIT_DIRECT:
>  		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
>  		if (!xmit.outdev) {
> @@ -1112,8 +1156,12 @@ nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,
>  	if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IPV6)))
>  		return NULL;
>  
> -	if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0)
> +	if (ctx->tun.proto == IPPROTO_IPIP) {
> +		if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0)
> +			return NULL;
> +	} else if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0) {
>  		return NULL;
> +	}
>  
>  	return flow_offload_lookup(flow_table, &tuple);
>  }
> @@ -1140,7 +1188,11 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
>  	if (tuplehash == NULL)
>  		return NF_ACCEPT;
>  
> -	ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb);
> +	if (ctx.tun.proto == IPPROTO_IPIP)
> +		ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
> +	else
> +		ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash,
> +						   skb);
>  	if (ret < 0)
>  		return NF_DROP;
>  	else if (ret == 0)
> @@ -1164,21 +1216,38 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
>  		return NF_DROP;
>  
>  	switch (tuplehash->tuple.xmit_type) {
> -	case FLOW_OFFLOAD_XMIT_NEIGH:
> -		rt = dst_rt6_info(tuplehash->tuple.dst_cache);
> +	case FLOW_OFFLOAD_XMIT_NEIGH: {
> +		struct dst_entry *dst;
> +
>  		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
>  		if (!xmit.outdev) {
>  			flow_offload_teardown(flow);
>  			return NF_DROP;
>  		}
> -		neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, ip6_daddr));
> +		if (other_tuple->tun.encap_proto == AF_INET ||
> +		    ctx.tun.proto == IPPROTO_IPIP) {
> +			__be32 ip_daddr = other_tuple->src_v4.s_addr;
> +			struct rtable *rt4;
> +
> +			skb->protocol = htons(ETH_P_IP);
> +			rt4 = dst_rtable(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw4(rt4->dst.dev,
> +					     rt_nexthop(rt4, ip_daddr));
> +			dst = &rt4->dst;

- Can this branch incorrectly cast a struct rt6_info to a struct rtable
  if an IPv4-in-IPv6 packet is forwarded to another IPv6 tunnel?
  - Double tunnel encapsulation is not currently supported.

Regards,
Lorenzo

> +		} else {
> +			rt = dst_rt6_info(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw6(rt->dst.dev,
> +					     rt6_nexthop(rt, ip6_daddr));
> +			dst = &rt->dst;
> +		}
>  		if (IS_ERR(neigh)) {
>  			flow_offload_teardown(flow);
>  			return NF_DROP;
>  		}
>  		xmit.dest = neigh->ha;
> -		skb_dst_set_noref(skb, &rt->dst);
> +		skb_dst_set_noref(skb, dst);
>  		break;
> +	}
>  	case FLOW_OFFLOAD_XMIT_DIRECT:
>  		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
>  		if (!xmit.outdev) {
> diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
> index caaf48c5fd2a..5e84b7f18a26 100644
> --- a/net/netfilter/nf_flow_table_path.c
> +++ b/net/netfilter/nf_flow_table_path.c
> @@ -216,12 +216,13 @@ static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt,
>  	struct dst_entry *tun_dst = NULL;
>  	struct flowi fl = {};
>  
> -	switch (nft_pf(pkt)) {
> +	switch (tun->encap_proto) {
>  	case NFPROTO_IPV4:
>  		fl.u.ip4.daddr = tun->dst_v4.s_addr;
>  		fl.u.ip4.saddr = tun->src_v4.s_addr;
>  		fl.u.ip4.flowi4_iif = nft_in(pkt)->ifindex;
> -		fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb));
> +		if (nft_pf(pkt) == NFPROTO_IPV4)
> +			fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb));
>  		fl.u.ip4.flowi4_mark = pkt->skb->mark;
>  		fl.u.ip4.flowi4_flags = FLOWI_FLAG_ANYSRC;
>  		break;
> @@ -229,13 +230,14 @@ static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt,
>  		fl.u.ip6.daddr = tun->dst_v6;
>  		fl.u.ip6.saddr = tun->src_v6;
>  		fl.u.ip6.flowi6_iif = nft_in(pkt)->ifindex;
> -		fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb));
> +		if (nft_pf(pkt) == NFPROTO_IPV6)
> +			fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb));
>  		fl.u.ip6.flowi6_mark = pkt->skb->mark;
>  		fl.u.ip6.flowi6_flags = FLOWI_FLAG_ANYSRC;
>  		break;
>  	}
>  
> -	nf_route(nft_net(pkt), &tun_dst, &fl, false, nft_pf(pkt));
> +	nf_route(nft_net(pkt), &tun_dst, &fl, false, tun->encap_proto);
>  	if (!tun_dst)
>  		return -ENOENT;
>  
> 
> -- 
> 2.55.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* [PATCH net] net: stmmac: resume PHY before reopening the interface on MTU change
From: Stefan Agner @ 2026-07-07 16:21 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn
  Cc: Russell King (Oracle), Maxime Chevallier, Ovidiu Panait,
	Maxime Coquelin, Alexandre Torgue, netdev, linux-stm32,
	linux-arm-kernel, regressions, Stefan Agner

Since the referenced commit, changing the MTU on a running interface no
longer disconnects and reconnects the PHY; __stmmac_release() merely
stops phylink, which also suspends the PHY (BMCR power-down) when WoL
is not enabled. __stmmac_open() then performs the DMA software reset in
stmmac_hw_setup() before phylink_start() resumes the PHY again.

IEEE 802.3 22.2.4.1.5 allows a PHY to stop its receive clock while
powered down, and stmmac requires a running receive clock for the DMA
software reset to complete (the phylink config sets mac_requires_rxc).
On such setups, e.g. the RK3566-based Home Assistant Green with an
RTL8211F-VD PHY in RGMII mode, any runtime MTU change now times out and
leaves the interface dead:

  rk_gmac-dwmac fe010000.ethernet end0: Failed to reset the dma
  rk_gmac-dwmac fe010000.ethernet end0: stmmac_hw_setup: DMA engine initialization failed
  rk_gmac-dwmac fe010000.ethernet end0: __stmmac_open: Hw setup failed
  rk_gmac-dwmac fe010000.ethernet end0: failed reopening the interface after MTU change

In the field this is triggered by NetworkManager applying an MTU while
activating the connection, breaking networking entirely.

Resume the PHY before reopening the interface, like stmmac_resume()
does, to ensure the receive clock is running for the DMA software
reset.

Fixes: db299a0c09e9 ("net: stmmac: move PHY handling out of __stmmac_open()/release()")
Link: https://github.com/home-assistant/operating-system/issues/4858
Assisted-by: Claude:claude-fable-5
Tested-by: Stefan Agner <stefan@agner.ch>
Signed-off-by: Stefan Agner <stefan@agner.ch>
---
Note: phylink_prepare_resume()'s kernel-doc says it is to be called
prior to phylink_resume(); here it is paired with phylink_start()
(called from __stmmac_open()) instead, which phylink_resume() itself
uses to restart the machinery. If preferred, I can extend the
kernel-doc or introduce a more generically named helper.

 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -5884,6 +5884,15 @@
 
 		__stmmac_release(dev);
 
+		/* phylink_stop() in __stmmac_release() suspends the PHY.
+		 * IEEE 802.3 allows PHYs to stop their receive clock while
+		 * powered down, but the DMA software reset performed by
+		 * stmmac_hw_setup() requires a running receive clock.
+		 * Resume the PHY, as on system resume, to ensure its clocks
+		 * are running before reopening the interface.
+		 */
+		phylink_prepare_resume(priv->phylink);
+
 		ret = __stmmac_open(dev, dma_conf);
 		if (ret) {
 			free_dma_desc_resources(priv, dma_conf);
-- 
2.49.0

^ permalink raw reply

* Re: [PATCH 1/3] arm64: dts: qcom: sm8450: Add IPA support
From: Esteban Urrutia @ 2026-07-07 16:34 UTC (permalink / raw)
  To: Konrad Dybcio, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Alex Elder
  Cc: linux-arm-msm, devicetree, linux-kernel, netdev
In-Reply-To: <4afa8797-1cb1-4cf1-bdbd-9cb5725377b8@oss.qualcomm.com>

On 7/7/26 6:35 AM, Konrad Dybcio wrote:
> Yes

On SM8475 I get this warning though:

[    9.551813] ipa 3f40000.ipa: limiting IPA memory size to 0x00005000

Both SM8450 and SM8475 use the same IPA version, so that shouldn't be an
issue.
On SM8550 and SM8650 this area is set to 0x5000 as well.
What should the proper solution be?

Regards,
Esteban


^ permalink raw reply

* Re: [PATCH net] tipc: serialize udp bearer replicast list updates
From: Weiming Shi @ 2026-07-07 16:34 UTC (permalink / raw)
  To: Tung Quang Nguyen
  Cc: Jon Maloy, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, tipc-discussion@lists.sourceforge.net, Xiang Mei,
	netdev@vger.kernel.org
In-Reply-To: <GV1P189MB1988C48DB0AA286E9DF62789C6F02@GV1P189MB1988.EURP189.PROD.OUTLOOK.COM>

Tung Quang Nguyen <tung.quang.nguyen@est.tech> 于2026年7月7日周二 21:21写道:
>
> >Subject: [PATCH net] tipc: serialize udp bearer replicast list updates
> >
> >tipc_udp_rcast_add() and cleanup_bearer() both update ub->rcast.list with
> >list_add_rcu() / list_del_rcu(), but nothing serializes them. The add runs from
> >the encap receive softirq (via tipc_udp_rcast_disc()) without rtnl_lock(), so it
> >can race the cleanup delete and corrupt the
> >list:
> >
> > list_del corruption. prev->next should be ffff8880298d7ab8,
> >   but was ffff88802449ad38. (prev=ffff888027e3ec98)  kernel BUG at
> >lib/list_debug.c:62!
> > RIP: __list_del_entry_valid_or_report+0x17a/0x200
> > Workqueue: events cleanup_bearer
> > Call Trace:
> >  cleanup_bearer (net/tipc/udp_media.c:811)
> >  process_one_work (kernel/workqueue.c:3302)
> >  worker_thread (kernel/workqueue.c:3466)
> >
> >The bearer can be enabled from an unprivileged user namespace, as the
> >TIPCv2 generic-netlink ops carry no GENL_ADMIN_PERM.
> >
> >Add a spinlock and take it around both list updates. Re-check for the peer
> >under the lock in the add path so the check-then-add in
> >tipc_udp_rcast_disc() cannot insert a duplicate.
> >
> >Fixes: ef20cd4dd163 ("tipc: introduce UDP replicast")
> >Reported-by: Xiang Mei <xmei5@asu.edu>
> >Assisted-by: Claude:claude-opus-4-8
> >Signed-off-by: Weiming Shi <bestswngs@gmail.com>
> >---
> > net/tipc/udp_media.c | 19 ++++++++++++++++++-
> > 1 file changed, 18 insertions(+), 1 deletion(-)
> >
> >diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index
> >62ae7f5b5840..601174297a16 100644
> >--- a/net/tipc/udp_media.c
> >+++ b/net/tipc/udp_media.c
> >@@ -94,6 +94,7 @@ struct udp_replicast {
> >  * @ifindex:  local address scope
> >  * @work:     used to schedule deferred work on a bearer
> >  * @rcast:    associated udp_replicast container
> >+ * @rcast_lock:       serializes updates to @rcast.list
> >  */
> > struct udp_bearer {
> >       struct tipc_bearer __rcu *bearer;
> >@@ -101,6 +102,7 @@ struct udp_bearer {
> >       u32 ifindex;
> >       struct work_struct work;
> >       struct udp_replicast rcast;
> >+      spinlock_t rcast_lock; /* protects rcast.list */
> > };
> >
> > static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr) @@ -301,7
> >+303,7 @@ static bool tipc_udp_is_known_peer(struct tipc_bearer *b,  static
> >int tipc_udp_rcast_add(struct tipc_bearer *b,
> >                             struct udp_media_addr *addr)
> > {
> >-      struct udp_replicast *rcast;
> >+      struct udp_replicast *rcast, *tmp;
> >       struct udp_bearer *ub;
> >
> >       ub = rcu_dereference_rtnl(b->media_ptr);
> >@@ -319,6 +321,17 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
> >
> >       memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr));
> >
> >+      /* tipc_udp_rcast_disc() adds from softirq without rtnl_lock(). */
>
> Remove above comment because it is vague. Caller of tipc_udp_rcv() already holds rcu_read_lock().
>
> >+      spin_lock_bh(&ub->rcast_lock);
>
> Move 'spin_lock_bh(&ub->rcast_lock)' to above 'list_add_rcu(&rcast->list, &ub->rcast.list);' because only 'ub->rcast.list' needs protection.
>
> >+      list_for_each_entry(tmp, &ub->rcast.list, list) {
> >+              if (!memcmp(&tmp->addr, addr, sizeof(struct
> >udp_media_addr))) {
> >+                      spin_unlock_bh(&ub->rcast_lock);
> >+                      dst_cache_destroy(&rcast->dst_cache);
> >+                      kfree(rcast);
> >+                      return 0;
> >+              }
> >+      }
>
> This is wrong because the code in the loop is never executed. tipc_udp_is_known_peer() already verifies address duplication.
> Please remove above code and fix tipc_udp_is_known_peer() as below:
>
> static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
>                                    struct udp_media_addr *addr)
>  {
> -       struct udp_replicast *rcast, *tmp;
> +       struct udp_replicast *rcast;
>         struct udp_bearer *ub;
>
>         ub = rcu_dereference_rtnl(b->media_ptr);
> @@ -292,7 +292,7 @@ static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
>                 return false;
>         }
>
> -       list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
> +       list_for_each_entry_rcu(rcast, &ub->rcast.list, list) {
>                 if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
>                         return true;
>         }
>
> >+
> >       if (ntohs(addr->proto) == ETH_P_IP)
> >               pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4);  #if
> >IS_ENABLED(CONFIG_IPV6) @@ -327,6 +340,7 @@ static int
> >tipc_udp_rcast_add(struct tipc_bearer *b,  #endif
> >       b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
> >       list_add_rcu(&rcast->list, &ub->rcast.list);
> >+      spin_unlock_bh(&ub->rcast_lock);
> >       return 0;
> > }
> >
> >@@ -679,6 +693,7 @@ static int tipc_udp_enable(struct net *net, struct
> >tipc_bearer *b,
> >               return -ENOMEM;
> >
> >       INIT_LIST_HEAD(&ub->rcast.list);
> >+      spin_lock_init(&ub->rcast_lock);
> >
> >       if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
> >               goto err;
> >@@ -819,10 +834,12 @@ static void cleanup_bearer(struct work_struct
> >*work)
> >       struct udp_replicast *rcast, *tmp;
> >       struct tipc_net *tn;
> >
> >+      spin_lock_bh(&ub->rcast_lock);
> >       list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
> >               list_del_rcu(&rcast->list);
> >               call_rcu_hurry(&rcast->rcu, rcast_free_rcu);
> >       }
> >+      spin_unlock_bh(&ub->rcast_lock);
> >
> >       tn = tipc_net(sock_net(ub->sk));
> >
> >--
> >2.43.0
> >
>

Thank you for the review. I will look into your suggestions and send out v2.

^ permalink raw reply

* Re: [PATCH net v2] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
From: Paul E. McKenney @ 2026-07-07 16:39 UTC (permalink / raw)
  To: Petr Pavlu
  Cc: Sebastian Andrzej Siewior, Qingfang Deng, Breno Leitao,
	Norbert Szetei, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Taegu Ha, Kees Cook, linux-ppp,
	linux-kernel, Guillaume Nault, netdev, Luis Chamberlain,
	Daniel Gomez, Sami Tolvanen, Aaron Tomlin, linux-modules
In-Reply-To: <0dfe59c2-bf60-40fe-90e6-d6e1003709d0@suse.com>

On Tue, Jul 07, 2026 at 05:32:10PM +0200, Petr Pavlu wrote:
> On 7/6/26 11:29 AM, Sebastian Andrzej Siewior wrote:
> > + MODULE maintainer
> 
> + Paul E. McKenney
> 
> > 
> > On 2026-07-05 10:57:44 [+0800], Qingfang Deng wrote:
> >> On 7/4/2026 at 12:32 AM, Breno Leitao wrote:
> >>> On Fri, Jul 03, 2026 at 03:27:00PM +0800, Qingfang Deng wrote:
> >>>> AI-review found an issue: https://sashiko.dev/#/patchset/D9C0245B-608B-4884-8A09-F55BA4A9F948%40doyensec.com
> >>>>
> >>>> An rcu_barrier() call is needed at the end of ppp_cleanup().
> >>>
> >>> I was initially unclear why rcu_barrier() would be necessary on a kfree path,
> >>> but it appears to be required during module unload to ensure that
> >>> ppp_release_channel_free() completes before the module's struct rcu_head is
> >>> destroyed. Is that the correct understanding?
> >>
> >> It's required to ensure that all ppp_release_channel_free() callback
> >> complete before the text segment of the module is unloaded.
> > 
> > So either a rcu_barrier() in ppp's module_exit() callback or a
> > synchronize_rcu() instead of the call_rcu(). And all this because the
> > module RCU callbacks pending which can be invoked after the module has
> > been removed. There is a synchronize_rcu() during module exit but this
> > is after the module code is gone.
> > 
> > I'm curious how many modules have a call_rcu() within their code but
> > don't have anything to enforce its completion before module removal is
> > complete? Wouldn't something like
> > 
> > 
> > diff --git a/kernel/module/main.c b/kernel/module/main.c
> > index 46dd8d25a6058..8eae1ea2d6eb4 100644
> > --- a/kernel/module/main.c
> > +++ b/kernel/module/main.c
> > @@ -858,6 +858,9 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
> >  		goto out;
> >  
> >  	mutex_unlock(&module_mutex);
> > +
> > +	/* Ensure all rcu callbacks issued by the module have completed */
> > +	rcu_barrier();
> >  	/* Final destruction now no one is using it. */
> >  	if (mod->exit != NULL)
> >  		mod->exit();
> > 
> > make sense?

There was some discussion of doing exactly this back in the day, but
at that time there were many modules that didn't do call_rcu() at all,
let alone call_rcu() with a function defined in that module.  And yes,
there were performance concerns.

Now rcu_barrier() has seen some performance work in the meantime, but
careful benchmarking would be required to justify the above patch.

That said, some automation would be very good, given that this sort of
bug happens from time to time.

> This is discussed in Documentation/RCU/rcubarrier.rst and
> Documentation/RCU/Design/Requirements/Requirements.rst. The latter
> contains:
> 
> | Loadable Modules
> | ~~~~~~~~~~~~~~~~
> | 
> | The Linux kernel has loadable modules, and these modules can also be
> | unloaded. After a given module has been unloaded, any attempt to call
> | one of its functions results in a segmentation fault. The module-unload
> | functions must therefore cancel any delayed calls to loadable-module
> | functions, for example, any outstanding mod_timer() must be dealt
> | with via timer_shutdown_sync() or similar.
> | 
> | Unfortunately, there is no way to cancel an RCU callback; once you
> | invoke call_rcu(), the callback function is eventually going to be
> | invoked, unless the system goes down first. Because it is normally
> | considered socially irresponsible to crash the system in response to a
> | module unload request, we need some other way to deal with in-flight RCU
> | callbacks.
> | 
> | RCU therefore provides rcu_barrier(), which waits until all
> | in-flight RCU callbacks have been invoked. If a module uses
> | call_rcu(), its exit function should therefore prevent any future
> | invocation of call_rcu(), then invoke rcu_barrier(). In theory,
> | the underlying module-unload code could invoke rcu_barrier()
> | unconditionally, but in practice this would incur unacceptable
> | latencies.
> 
> I don't know if the last part about unacceptable latencies is still
> relevant. I haven't done any measurements myself.

Actual measurements would most definitely be needed!

Alternatives include:

o	Provide a patch like that above, but only execute the
	rcu_barrier() in some debug mode.  If your code works when
	that debug is enabled but does not otherwise, you add the
	rcu_barrier().

o	If debug is enabled, make rcu_do_batch() check the function
	before invoking it.  If the function is not mapped, issue a
	diagnostic, and don't try to invoke the function.  (But is
	there a sufficiently cheap way to check for the function not
	being mapped?)

o	Make the page-fault code check this possibility.  (But it would
	need to know that rcu_do_batch() was involved, which could no
	doubt be arranged.)

o	Make call_rcu() keep track of the fact that it was passed a
	function defined in a module, and set a flag that caused the
	module-exit code for that module to do rcu_barrier().  The
	trick here would be doing this without unacceptable increases
	to call_rcu() overheads.

o	Some sort of static analysis that determines that call_rcu()
	was passed a function defined in a module and either issues
	needed diagnostics or (somehow) letting the module-unload
	code know that rcu_barrier() is needed.

o	One challenge for many of these alternatives is that the module is
	already gone.  Maybe a KASAN-like trick that tracks the module's
	old memory for some time afterwards?  Or maybe the user usually
	knows which module was just now unloaded?  (Except for modules
	being dependent on each other...)

o	Your ideas here!!!

							Thanx, Paul

^ permalink raw reply

* Re: [PATCH net-next v8 9/9] selftests: net: Add a test for BIG TCP in UDP tunnels
From: Alice Mikityanska @ 2026-07-07 16:40 UTC (permalink / raw)
  To: Matthieu Baerts
  Cc: Shuah Khan, Stanislav Fomichev, Andrew Lunn, Simon Horman,
	Florian Westphal, netdev, Alice Mikityanska, Paolo Abeni,
	Daniel Borkmann, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Xin Long, Willem de Bruijn, Willem de Bruijn, David Ahern,
	Nikolay Aleksandrov
In-Reply-To: <f9aac06f-25ca-40fb-9e47-d12398262c61@kernel.org>

On Tue, Jul 7, 2026, at 17:12, Matthieu Baerts wrote:
> Hi Alice,
>
> On 07/07/2026 10:40, Alice Mikityanska wrote:
>> On Tue, Jul 7, 2026, at 11:06, Paolo Abeni wrote:
>>> On 7/6/26 8:19 PM, Alice Mikityanska wrote:
>
> (...)
>
>>>> +
>>>> +cleanup_tunnel() {
>>>> +	ip -netns "$CLIENT_NS" link del tun0
>>>> +	ip -netns "$SERVER_NS" link del tun1
>>>> +}
>>>> +
>>>> +cleanup() {
>>>> +	ip netns pids "$SERVER_NS" | xargs -r kill
>>>> +	ip netns pids "$CLIENT_NS" | xargs -r kill
>>>> +	ip netns del "$SERVER_NS"
>>>> +	ip netns del "$CLIENT_NS"
>>>> +	rm -rf "$WORKDIR"
>>>> +}
>>>> +
>>>> +do_test() {
>>>> +	# When tx csum offload is off, software GSO is performed before passing the
>>>> +	# packet to veth. Check BIG TCP packets inside the VXLAN tunnel to verify
>>>> +	# the software checksum path: if the checksum code is broken, these packets
>>>> +	# will be dropped.
>>>> +	if [ "$2" = on ]; then
>>>> +		CAPTURE_IFACE='link'
>>>> +	else
>>>> +		CAPTURE_IFACE='tun'
>>>> +	fi
>>>> +
>>>> +	ip netns exec "$SERVER_NS" tcpdump -nn -s 256 -i "${CAPTURE_IFACE}1" greater 65536 -w "$WORKDIR/server.pcap" 2> /dev/null &
>>>> +	TCPDUMP_SERVER_PID="$!"
>>>> +	ip netns exec "$CLIENT_NS" tcpdump -nn -s 256 -i "${CAPTURE_IFACE}0" greater 65536 -w "$WORKDIR/client.pcap" 2> /dev/null &
>>>> +	TCPDUMP_CLIENT_PID="$!"
>>>> +
>>>> +	# This filter doesn't capture all possible variants of SACK, but it's aimed
>>>> +	# at the typical one where SACK follows after [nop, nop, timestamp, nop,
>>>> +	# nop] (14 bytes after the 20-byte TCP header). IPv6 needs a separate match,
>>>> +	# because man tcpdump says:
>>>> +	# > Arithmetic expression against transport layer headers, like tcp[0], does
>>>> +	# > not work against IPv6 packets.  It only looks at IPv4 packets.
>>>> +	ip netns exec "$SERVER_NS" tcpdump -nn -s 256 -i "tun1" '(tcp[tcpflags] & (tcp-syn|tcp-ack) = tcp-ack and tcp[34:2] & 0xffc3 = 0x0502) or (ip6[6] = 0x06 and ip6[53] & 0x12 = 0x10 and ip6[74:2] & 0xffc3 = 0x0502)' -w "$WORKDIR/sack.pcap" 2> /dev/null &
>
> The tcpdump commands might need to be used with ...
>
>   --immediate-mode --packet-buffered
>
> ... but that's maybe not needed, see below.

Thanks! I was aware of these options, but I was afraid they'd slow down
tcpdump and let it drop some packets. Well, now that I actually tested
them, it seems that my concern was not valid. I'll add these and drop
the second sleep (if I stay with tcpdump).

>>>> +	TCPDUMP_SACK_PID="$!"
>>>> +
>>>> +	if [ "$1" = 4 ]; then
>>>> +		SERVER_IP="$SERVER_IP4_TUN"
>>>> +		echo "Running IPv4 traffic in the tunnel"
>>>> +	else
>>>> +		SERVER_IP="$SERVER_IP6_TUN"
>>>> +		echo "Running IPv6 traffic in the tunnel"
>>>> +	fi
>>>> +
>>>> +	sleep 1 # Give tcpdump a second to spin up.
>
> This is possibly not needed, see below.
>
>>>> +	ip netns exec "$CLIENT_NS" netperf -t TCP_STREAM -l 5 -H "$SERVER_IP" -- \
>>>> +		-m 80000 > /dev/null
>>>> +	sleep 1 # Give tcpdump a second to process buffered packets.
>>>> +	kill "$TCPDUMP_SERVER_PID" "$TCPDUMP_CLIENT_PID" "$TCPDUMP_SACK_PID"
>>>> +	wait "$TCPDUMP_SERVER_PID" "$TCPDUMP_CLIENT_PID" "$TCPDUMP_SACK_PID"
>>>> +	PACKETS_SERVER=$(tcpdump --count -r "$WORKDIR/server.pcap" 2> /dev/null | cut -d ' ' -f 1)
>>>> +	PACKETS_CLIENT=$(tcpdump --count -r "$WORKDIR/client.pcap" 2> /dev/null | cut -d ' ' -f 1)
>>>> +	PACKETS_SACK=$(tcpdump --count -r "$WORKDIR/sack.pcap" 2> /dev/null | cut -d ' ' -f 1)
>>>> +
>>>> +	echo "Captured BIG TCP RX packets: $PACKETS_SERVER"
>>>> +	echo "Captured BIG TCP TX packets: $PACKETS_CLIENT"
>>>> +	echo "Captured TCP SACK packets: $PACKETS_SACK"
>>>> +	[ "$PACKETS_SERVER" -gt "$PACKETS_THRESHOLD" ] || return 1
>>>> +	[ "$PACKETS_CLIENT" -gt "$PACKETS_THRESHOLD" ] || return 1
>>>> +	[ "$PACKETS_SACK" -lt "$(( PACKETS_CLIENT / 2 ))" ] || return 1
>>>
>>> KCI fails here, see:
>>>
>>> https://netdev-ctrl.bots.linux.dev/logs/vmksft/net/results/722863/156-big-tcp-tunnels-sh/stdout
>>>
>>> possibly the above parsing is a bit fragile/tcpdump version's dependent?!?
>> 
>> TL/DR: I've seen this when I ran out of space in /tmp before adding -s 256.
>> 
>> Hmm, the changes I made in this iteration were aimed at addressing version-
>> dependent behavior of tcpdump... I used to count the lines of its
>> output. Newer tcpdump prints two lines per encapsulated packet. Older
>> tcpdump can't parse BIG TCP in this test and prints one line per packet.
>> To make it more robust, I decided to store the pcap and count the
>> packets there (--count doesn't work with live capture when tcpdump is
>> interrupted). The pitfall is that now it takes a few hundred megabytes
>> in /tmp to store those pcaps.
>
> Wow :)
>
> If you need to keep tcpdump -- see below -- you can probably reduce even
> more the packet size (-s 256), I don't know what's the minimum. And
> purge the workdir in cleanup_tunnel().

Apparently, the overflow in /tmp happens during one single test, because
the files are not accumulated: the next test overwrites the same files.

>> Now, seeing empty stdout from tcpdump could be if the pcap file is empty
>> (doesn't contain the pcap header), because we ran out of space in /tmp
>> (first two tcpdumps took all space, the third didn't write the header -
>> I observed this behavior before I added -s 256). We don't see the
>> corresponding error messages, because tcpdump starts with 2> /dev/null
>> (otherwise it floods the screen with statistics). Could this be the
>> reason? How much space is allocated for /tmp in the CI runners?
>> 
>> I can probably address it by limiting the overall number of captured
>> packets with -c, but then I won't be able to compare $PACKETS_SACK
>> relative to $PACKETS_CLIENT.
>
> If you only need to count some packets matching a filter, what about
> using Netfilter rules with a counter, instead of using pcap files?
>
> You can have counters not attached to rules altering packets:
>
>   https://wiki.nftables.org/wiki-nftables/index.php/Counters

Thanks for the suggestion! Yes, I agree, I think, at this point it will
be cleaner to use netfilter counters. By the way, the other test
big_tcp.sh uses an iptables rule, that comes with a counter by default,
to count BIG TCP packets.

> It might even be easier to match the SACK packets, with the 'tcp option
> sack' filter from nft [1].

Great, it will match all sorts of SACK packets, not only the specific
one that my filter does.

Thank you, I'll explore this direction, it looks more promising.

> And for the size, I guess you can use "meta
> length > 65536" [2], or checking other fields from IP/TCP headers.
>
> If you want to keep your cBPF filter, you can also use nfbpf_compile for
> the translation, and use that with 'iptables -m bpf --bytecode <...>',
> but it might not be needed.
>
> [1] 
> https://www.mankier.com/8/nft#Payload_Expressions-Extension_Header_Expressions
> [2] https://www.mankier.com/8/nft#Primary_Expressions-Meta_Expressions
>
> Cheers,
> Matt
> -- 
> Sponsored by the NGI0 Core fund.

^ permalink raw reply

* [PATCH net v2] tipc: serialize udp bearer replicast list updates
From: Weiming Shi @ 2026-07-07 16:42 UTC (permalink / raw)
  To: Jon Maloy, Tung Nguyen, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman
  Cc: netdev, tipc-discussion, linux-kernel, Xiang Mei, Weiming Shi

tipc_udp_rcast_add() and cleanup_bearer() both update ub->rcast.list
with list_add_rcu() / list_del_rcu(), but nothing serializes them. The
add runs from the encap receive softirq (via tipc_udp_rcast_disc())
without rtnl_lock(), so it can race the cleanup delete and corrupt the
list:

 list_del corruption. prev->next should be ffff8880298d7ab8,
   but was ffff88802449ad38. (prev=ffff888027e3ec98)
 kernel BUG at lib/list_debug.c:62!
 RIP: __list_del_entry_valid_or_report+0x17a/0x200
 Workqueue: events cleanup_bearer
 Call Trace:
  cleanup_bearer (net/tipc/udp_media.c:811)
  process_one_work (kernel/workqueue.c:3302)
  worker_thread (kernel/workqueue.c:3466)

The bearer can be enabled from an unprivileged user namespace, as the
TIPCv2 generic-netlink ops carry no GENL_ADMIN_PERM.

Add a spinlock to struct udp_bearer and take it around the
list_add_rcu() in tipc_udp_rcast_add() and the list_del_rcu() loop in
cleanup_bearer() so the two writers can no longer corrupt the list.

While here, switch the read-only walk in tipc_udp_is_known_peer() to
list_for_each_entry_rcu(); it never deletes, so list_for_each_entry_safe()
was misleading.

Fixes: ef20cd4dd163 ("tipc: introduce UDP replicast")
Reported-by: Xiang Mei <xmei5@asu.edu>
Suggested-by: Tung Nguyen <tung.quang.nguyen@est.tech>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
v2: (per Tung's review)
 - Narrow the lock to just list_add_rcu().
 - Drop the under-lock dup re-check; serializing the writers is enough.
 - Use list_for_each_entry_rcu() in tipc_udp_is_known_peer().

 net/tipc/udp_media.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 62ae7f5b5840..c6aa8c3c54ce 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -94,6 +94,7 @@ struct udp_replicast {
  * @ifindex:	local address scope
  * @work:	used to schedule deferred work on a bearer
  * @rcast:	associated udp_replicast container
+ * @rcast_lock:	serializes updates to @rcast.list
  */
 struct udp_bearer {
 	struct tipc_bearer __rcu *bearer;
@@ -101,6 +102,7 @@ struct udp_bearer {
 	u32 ifindex;
 	struct work_struct work;
 	struct udp_replicast rcast;
+	spinlock_t rcast_lock; /* protects rcast.list */
 };
 
 static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
@@ -281,7 +283,7 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
 static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
 				   struct udp_media_addr *addr)
 {
-	struct udp_replicast *rcast, *tmp;
+	struct udp_replicast *rcast;
 	struct udp_bearer *ub;
 
 	ub = rcu_dereference_rtnl(b->media_ptr);
@@ -290,7 +292,7 @@ static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
 		return false;
 	}
 
-	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
+	list_for_each_entry_rcu(rcast, &ub->rcast.list, list) {
 		if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
 			return true;
 	}
@@ -326,7 +328,9 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
 		pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
 #endif
 	b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
+	spin_lock_bh(&ub->rcast_lock);
 	list_add_rcu(&rcast->list, &ub->rcast.list);
+	spin_unlock_bh(&ub->rcast_lock);
 	return 0;
 }
 
@@ -679,6 +683,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
 		return -ENOMEM;
 
 	INIT_LIST_HEAD(&ub->rcast.list);
+	spin_lock_init(&ub->rcast_lock);
 
 	if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
 		goto err;
@@ -819,10 +824,12 @@ static void cleanup_bearer(struct work_struct *work)
 	struct udp_replicast *rcast, *tmp;
 	struct tipc_net *tn;
 
+	spin_lock_bh(&ub->rcast_lock);
 	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
 		list_del_rcu(&rcast->list);
 		call_rcu_hurry(&rcast->rcu, rcast_free_rcu);
 	}
+	spin_unlock_bh(&ub->rcast_lock);
 
 	tn = tipc_net(sock_net(ub->sk));
 
-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox