Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net v4 0/2] amt: fix use-after-free of the skb head across pulls
From: Taehee Yoo @ 2026-07-11 14:48 UTC (permalink / raw)
  To: Michael Bommarito
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, netdev, linux-kernel
In-Reply-To: <20260707193243.3448201-1-michael.bommarito@gmail.com>

On Wed, Jul 8, 2026 at 4:32 AM Michael Bommarito
<michael.bommarito@gmail.com> wrote:
>
> Several AMT receive and transmit paths cache a pointer into the skb head
> and then call a helper that can reallocate that head before the cached
> pointer is used again, so the later access reads or writes freed memory.
>
> v3 addressed only the source-address reads in a subset of the handlers
> and described amt_membership_query_handler() and
> amt_multicast_data_handler() as unaffected.  As the review pointed out,
> that was incomplete: those handlers keep stale eth_hdr() and AMT-header
> pointers across later pulls, the record loops in the IGMPv3 and MLDv2
> report handlers read the record count and the group record across the
> *_mc_may_pull() calls, and amt_update_handler() and amt_dev_xmit() read
> the destination address after further pulls.
>
> Patch 1 walks every AMT path and, for each pointer used after a
> reallocating call, either snapshots the value before the first pull or
> re-derives the pointer after the last one.  This uses the re-derive
> approach rather than the per-value snapshot of v3, because the write
> sites cannot be expressed as a snapshot and re-derivation is already the
> idiom used elsewhere in the file.
>
> Patch 2 is a smaller, separable hardening change: the three handlers
> that rewrite the ethernet header do so in place without making the head
> private, which corrupts a cloned skb (for example one held by a packet
> tap).  It adds skb_cow_head() before the rewrite, split out so the
> use-after-free fix is not held up by discussion of the clone case.
>
> Both patches build cleanly (x86_64, CONFIG_AMT=m, W=1) and are
> checkpatch --strict clean.
>
> Changes since v3:
>  - Rework from the per-value source-address snapshot to re-deriving the
>    header pointers after the last reallocating pull, and cover every
>    affected handler (amt_dev_xmit, amt_multicast_data_handler,
>    amt_membership_query_handler, the IGMPv3 and MLDv2 record loops, and
>    the remaining reads in amt_update_handler), not just the
>    source-address reads.
>  - Correct the v3 commit-message claim that the query and multicast-data
>    handlers were unaffected.
>  - Add patch 2 (skb_cow_head() before the L2 rewrite).
>  - Drop the v2 Acked-by from Taehee Yoo: this series is materially larger
>    than what was acked.
>
> v3: https://lore.kernel.org/all/20260626111917.802243-1-michael.bommarito@gmail.com/
> v2: https://lore.kernel.org/all/20260617123443.3586930-1-michael.bommarito@gmail.com/
>

Hi Michael,

Thanks a lot for this work!

Sashiko flagged one more issue, please take a look:
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260707193243.3448201-1-michael.bommarito%40gmail.com

Also, please remove the unnecessary comments in the patch.
AI-generated code tends to include too many comments.

One more thing: please follow the reverse Christmas tree order
(longest to shortest) for local variable declarations.

Thanks,
Taehee

> Michael Bommarito (2):
>   amt: re-read skb header pointers after every pull
>   amt: make the head writable before rewriting the L2 header
>
>  drivers/net/amt.c | 117 +++++++++++++++++++++++++++++++++++++---------
>  1 file changed, 96 insertions(+), 21 deletions(-)
>
>
> base-commit: 5200f5f493f79f14bbdc349e402a40dfb32f23c8
> --
> 2.53.0
>

^ permalink raw reply

* Re: [PATCH nf] ipvs: make destination flags atomic
From: Yizhou Zhao @ 2026-07-11 14:09 UTC (permalink / raw)
  To: Julian Anastasov
  Cc: Simon Horman, David Ahern, Ido Schimmel, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Pablo Neira Ayuso,
	Florian Westphal, Phil Sutter, Alexander Frolkin, netdev,
	lvs-devel, linux-kernel, netfilter-devel, coreteam, stable,
	Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li, Ke Xu
In-Reply-To: <afcdb34c-ec10-de8e-083c-624bcedca90e@ssi.bg>

Hello Julian,

Thank you for the detailed proposal. Yes, I am happy to handle the
follow-up conversion of the overload flag to bitops.


> On Jul 8, 2026, at 23:53, Julian Anastasov <ja@ssi.bg> wrote:
> 
> 
> Hello,
> 
> On Wed, 8 Jul 2026, Yizhou Zhao wrote:
> 
>>> On Jul 8, 2026, at 03:18, Julian Anastasov <ja@ssi.bg> wrote:
>>> 
>>> On Tue, 7 Jul 2026, Yizhou Zhao wrote:
>>> 
>> 
>> We have posted a v2 patch at:
>> https://lore.kernel.org/netfilter-devel/20260708060454.20534-1-zhaoyz24@mails.tsinghua.edu.cn/
>> 
>> The v2 patch updates the commit message with more conservative
>> wording, and fixes the checkpatch logical-continuation warnings.
> 
> After looking again at the code, I think we can
> do it in different way:
> 
> - IP_VS_DEST_F_AVAILABLE and IP_VS_DEST_F_OVERLOAD are defined
> in include/uapi/linux/ip_vs.h but we never export them to user
> space. So, we are free to change them. We can move them to 
> include/net/ip_vs.h, see below...
> 
> - IP_VS_DEST_F_AVAILABLE is changed only under service_mutex,
> so we can keep its usage
> 
> - IP_VS_DEST_F_OVERLOAD needs different access methods.
> We can add 'unsigned long flags2;', may be after l_threshold.
> And to switch to such usage (F_OVERLOAD -> FL_OVERLOAD):
> 
> - test_bit(IP_VS_DEST_FL_OVERLOAD, &dest->flags2)
> - set_bit(IP_VS_DEST_FL_OVERLOAD, &dest->flags2)
> 
> Sometimes if (test_bit()) clear_bit() can avoid
> full memory barrier in ip_vs_dest_update_overload()
> 
> - clear_bit(IP_VS_DEST_FL_OVERLOAD, &dest->flags2)
> test_bit() guard can help here too
> 
> As there are other races involved, something like
> this can be a starting point for such change. It tries harder
> to update the overload flag on dest edit/add but it does not
> include the proposed bitops:
> 
> diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
> index 49297fec448a..b34631270e24 100644
> --- a/include/net/ip_vs.h
> +++ b/include/net/ip_vs.h
> @@ -1906,6 +1906,8 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
> kfree(dest);
> }
> 
> +void ip_vs_dest_update_overload(struct ip_vs_dest *dest);
> +
> /* IPVS sync daemon data and function prototypes
>  * (from ip_vs_sync.c)
>  */
> diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
> index d19caf66afeb..3fd221996e6e 100644
> --- a/net/netfilter/ipvs/ip_vs_conn.c
> +++ b/net/netfilter/ipvs/ip_vs_conn.c
> @@ -1087,6 +1087,26 @@ static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest)
> + atomic_read(&dest->inactconns);
> }
> 
> +__always_inline void ip_vs_dest_update_overload(struct ip_vs_dest *dest)
> +{
> + int conns, l, u;
> +
> + u = READ_ONCE(dest->u_threshold);
> + if (!u)
> + goto unset;
> + conns = ip_vs_dest_totalconns(dest);
> + if (conns >= u) {
> + dest->flags |= IP_VS_DEST_F_OVERLOAD;
> + return;
> + }
> + l = READ_ONCE(dest->l_threshold) ? : (u * 3 / 4);
> + if (conns >= l && l)
> + return;
> +

I noticed one integer-rounding detail in the proposed helper. The
existing default lower-threshold check:

ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3

clears OVERLOAD when conns is below ceil(3 * u / 4), assuming the
multiplications do not overflow. In the proposed helper:

l = u * 3 / 4;
if (conns >= l && l)
return;

the division rounds down, so the boundary is different when u is not a
multiple of four. For example, with u == 2 and conns == 1, the existing
code clears OVERLOAD while the helper keeps it set. Would using

l = u - u / 4;

for the default lower threshold preserve the existing behavior while
also avoiding the multiplication overflow?

> +unset:
> + dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
> +}
> +
> /*
>  * Bind a connection entry with a virtual service destination
>  * Called just after a new connection entry is created.
> @@ -1161,9 +1181,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
> atomic_inc(&dest->persistconns);
> }
> 
> - if (dest->u_threshold != 0 &&
> -    ip_vs_dest_totalconns(dest) >= dest->u_threshold)
> - dest->flags |= IP_VS_DEST_F_OVERLOAD;
> + ip_vs_dest_update_overload(dest);
> }
> 
> 
> @@ -1257,16 +1275,8 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
> atomic_dec(&dest->persistconns);
> }
> 
> - if (dest->l_threshold != 0) {
> - if (ip_vs_dest_totalconns(dest) < dest->l_threshold)
> - dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
> - } else if (dest->u_threshold != 0) {
> - if (ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3)
> - dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
> - } else {
> - if (dest->flags & IP_VS_DEST_F_OVERLOAD)
> - dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
> - }
> + if (dest->flags & IP_VS_DEST_F_OVERLOAD)
> + ip_vs_dest_update_overload(dest);
> 
> ip_vs_dest_put(dest);
> }
> diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
> index bcf40b8c41cf..2871116e46ec 100644
> --- a/net/netfilter/ipvs/ip_vs_ctl.c
> +++ b/net/netfilter/ipvs/ip_vs_ctl.c
> @@ -1315,6 +1315,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
> struct ip_vs_service *old_svc;
> struct ip_vs_scheduler *sched;
> int conn_flags;
> + bool upd_thresh;
> 
> /* We cannot modify an address and change the address family */
> BUG_ON(!add && udest->af != dest->af);
> @@ -1370,10 +1371,12 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
> /* set the dest status flags */
> dest->flags |= IP_VS_DEST_F_AVAILABLE;
> 
> - if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold)
> - dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
> - dest->u_threshold = udest->u_threshold;
> - dest->l_threshold = udest->l_threshold;
> + upd_thresh = READ_ONCE(dest->u_threshold) != udest->u_threshold ||
> +     READ_ONCE(dest->l_threshold) != udest->l_threshold;
> + WRITE_ONCE(dest->u_threshold, udest->u_threshold);
> + WRITE_ONCE(dest->l_threshold, udest->l_threshold);
> + if (upd_thresh)
> + ip_vs_dest_update_overload(dest);
> 
> dest->af = udest->af;
> 
> @@ -3667,8 +3670,8 @@ __ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests *
> entry.port = dest->port;
> entry.conn_flags = atomic_read(&dest->conn_flags);
> entry.weight = atomic_read(&dest->weight);
> - entry.u_threshold = dest->u_threshold;
> - entry.l_threshold = dest->l_threshold;
> + entry.u_threshold = READ_ONCE(dest->u_threshold);
> + entry.l_threshold = READ_ONCE(dest->l_threshold);
> entry.activeconns = atomic_read(&dest->activeconns);
> entry.inactconns = atomic_read(&dest->inactconns);
> entry.persistconns = atomic_read(&dest->persistconns);
> @@ -4277,8 +4280,10 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
> dest->tun_port) ||
>    nla_put_u16(skb, IPVS_DEST_ATTR_TUN_FLAGS,
> dest->tun_flags) ||
> -    nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold) ||
> -    nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold) ||
> +    nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH,
> + READ_ONCE(dest->u_threshold)) ||
> +    nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH,
> + READ_ONCE(dest->l_threshold)) ||
>    nla_put_u32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
> atomic_read(&dest->activeconns)) ||
>    nla_put_u32(skb, IPVS_DEST_ATTR_INACT_CONNS,
> 
> Regards
> 
> --
> Julian Anastasov <ja@ssi.bg>

Please go ahead with the ip_vs_dest_update_overload() patch. I will base
the follow-up on the posted version and check the resulting struct
ip_vs_dest layout as Vadim suggested.

Regards,
Yizhou


^ permalink raw reply

* RE: [PATCH net-next 04/11] igb: prepare for RSS key get/set support
From: Takashi Kozu @ 2026-07-11 14:10 UTC (permalink / raw)
  To: anthony.l.nguyen
  Cc: aleksandr.loktionov, andrew+netdev, davem, edumazet, enjuk, horms,
	kohei.enju, kuba, netdev, pabeni, piotr.kwapulinski, sx.rinitha,
	takkozu
In-Reply-To: <70999e07-114e-47af-a8b1-912a30eccb8a@intel.com>

On 7/1/2026 2:02 PM, Tony Nguyen wrote:
> > From: Takashi Kozu <takkozu@amazon.com>
> ...
> 
> Hi Takashi-san,
> 
> Sashiko flags this for the power management path.
> 
> "
> Will the new ASSERT_RTNL() in igb_write_rss_key() trigger a false-positive
> kernel warning during runtime power management resume?
> When the device undergoes runtime PM suspend and then wakes up,
> igb_runtime_resume() is called. This passes rpm=true to __igb_resume(),
> which
> explicitly skips acquiring the rtnl_lock() to avoid deadlocks.
> __igb_resume() then calls __igb_open(), which cascades to igb_configure(),
> igb_setup_mrqc(), and finally igb_write_rss_key(). Because the lock is
> intentionally not held on this path, the ASSERT_RTNL() will fail and
> trigger a
> WARN_ONCE.
> Is it possible to remove this assertion or adjust it to account for the
> runtime PM resume path?
> "
> 
> Kohei-san's version in igc did not have this and it does share similar
> flows with igc. I'm not sure if you added this for a specific reason but
> would you be able to send a follow up patch to adjust or remove this?
> 
> Thanks,
> Tony

Thanks for the suggestion. Sent a follow-up patch as requested:

https://lore.kernel.org/all/20260711133239.29270-2-takkozu@amazon.com/

Thanks,
Takashi

^ permalink raw reply

* Re: [PATCH net] ipvlan: add xmit recursion protection
From: Eric Dumazet @ 2026-07-11 14:00 UTC (permalink / raw)
  To: Tristan Madani
  Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Mahesh Bandewar,
	Andrew Lunn, netdev, linux-kernel, stable, Tristan Madani
In-Reply-To: <20260711134732.1385563-1-tristmd@gmail.com>

On Sat, Jul 11, 2026 at 3:47 PM Tristan Madani <tristmd@gmail.com> wrote:
>
> From: Tristan Madani <tristan@talencesecurity.com>
>
> ipvlan devices can enter infinite transmit recursion when combined with
> packet forwarding configurations (such as IPVS) that route traffic back
> through the same ipvlan interface.
>
> The recursion path is:
>
>   ipvlan_start_xmit -> ipvlan_queue_xmit -> ipvlan_xmit_mode_l3
>   -> ipvlan_process_outbound -> ip_local_out -> netfilter hooks
>   -> dev_queue_xmit -> ipvlan_start_xmit (recurse)
>
> The existing per-CPU xmit recursion counter in __dev_queue_xmit()
> (XMIT_RECURSION_LIMIT = 8) does detect the loop, but fires too late:
> each recursion level consumes roughly 2KB of stack space through
> ip_local_out and netfilter, and at 8 levels the cumulative usage
> exceeds the 16KB kernel stack on x86_64. The resulting stack overflow
> hits the VMAP_STACK guard page and causes a kernel panic.
>

Please fix the existing mechanism. 2KB of stack space seems excessive.

If this is caused by some DEBUG option, it will be just fine to limit
XMIT_RECURSION_LIMIT to 3 for such debug kernels.

Adding a workaround in every virtual driver is not an option.

^ permalink raw reply

* [PATCH nf] selftests: netfilter: nft_flowtable.sh: check offload counters for IP6IP6 tunnel test
From: Lorenzo Bianconi @ 2026-07-11 13:55 UTC (permalink / raw)
  To: Pablo Neira Ayuso, Florian Westphal, Phil Sutter, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Shuah Khan
  Cc: netfilter-devel, coreteam, netdev, linux-kselftest,
	Lorenzo Bianconi

The IP6IP6 tunnel test uses test_tcp_forwarding() which only verifies
TCP data integrity but does not verify that flowtable offload actually
took place. Unlike the non-tunnel and IPIP tests, it omits the
check_counters() call.
The reply direction for IP6IP6 tunnel traffic goes through the physical
interface (oif=veth0), so it is already counted by the existing
routed_repl counter rule. Add check_counters() after the IP6IP6 test to
detect offload failures.

Fixes: 5e5180352193 ("selftests: netfilter: nft_flowtable.sh: Add IP6IP6 flowtable selftest")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 tools/testing/selftests/net/netfilter/nft_flowtable.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
index 08ad07500e8a..0ea01a876c8b 100755
--- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh
+++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
@@ -629,7 +629,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel"; then
 fi
 
 if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
-	echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel"
+	check_counters "flow offload for ns1/ns2 IP6IP6 tunnel"
 else
 	echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel" 1>&2
 	ip netns exec "$nsr1" nft list ruleset
@@ -683,7 +683,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel over vlan"; then
 fi
 
 if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
-	echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel over vlan"
+	check_counters "flow offload for ns1/ns2 IP6IP6 tunnel over vlan"
 else
 	echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel over vlan" 1>&2
 	ip netns exec "$nsr1" nft list ruleset

---
base-commit: 3f1f755366687d051174739fb99f7d560202f60b
change-id: 20260711-flowtable-selftest-ip6ip6-fix-4d8a623247a3

Best regards,
-- 
Lorenzo Bianconi <lorenzo@kernel.org>


^ permalink raw reply related

* [PATCH] dt-bindings: net: microchip: fix bracket
From: Manuel Ebner @ 2026-07-11 13:42 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Oleksij Rempel
  Cc: Manuel Ebner, netdev, devicetree, linux-kernel

Remove needless ')'

Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
---
I send this in a seperate patch, because there's a change the maintainers
wants a bracked added instead of removed.
---
 Documentation/devicetree/bindings/net/microchip,lan95xx.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml b/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml
index b9c394009040..f4d0929616a5 100644
--- a/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml
+++ b/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml
@@ -35,7 +35,7 @@ properties:
           - usb424,9906   # SMSC9505A USB Ethernet Device (HAL)
           - usb424,9907   # SMSC9500 USB Ethernet Device (Alternate ID)
           - usb424,9908   # SMSC9500A USB Ethernet Device (Alternate ID)
-          - usb424,9909   # SMSC9512/9514 USB Hub & Ethernet Device  ID)
+          - usb424,9909   # SMSC9512/9514 USB Hub & Ethernet Device ID
           - usb424,9e00   # SMSC9500A USB Ethernet Device
           - usb424,9e01   # SMSC9505A USB Ethernet Device
           - usb424,9e08   # SMSC LAN89530 USB Ethernet Device
-- 
2.54.0


^ permalink raw reply related

* [PATCH net] ipvlan: add xmit recursion protection
From: Tristan Madani @ 2026-07-11 13:47 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Mahesh Bandewar, Andrew Lunn, netdev, linux-kernel, stable,
	Tristan Madani

From: Tristan Madani <tristan@talencesecurity.com>

ipvlan devices can enter infinite transmit recursion when combined with
packet forwarding configurations (such as IPVS) that route traffic back
through the same ipvlan interface.

The recursion path is:

  ipvlan_start_xmit -> ipvlan_queue_xmit -> ipvlan_xmit_mode_l3
  -> ipvlan_process_outbound -> ip_local_out -> netfilter hooks
  -> dev_queue_xmit -> ipvlan_start_xmit (recurse)

The existing per-CPU xmit recursion counter in __dev_queue_xmit()
(XMIT_RECURSION_LIMIT = 8) does detect the loop, but fires too late:
each recursion level consumes roughly 2KB of stack space through
ip_local_out and netfilter, and at 8 levels the cumulative usage
exceeds the 16KB kernel stack on x86_64. The resulting stack overflow
hits the VMAP_STACK guard page and causes a kernel panic.

Add a per-CPU counter that prevents any re-entry into
ipvlan_queue_xmit() while it is already executing on the same CPU.
This mirrors the approach used by tunnel devices (see
IP_TUNNEL_RECURSION_LIMIT in ip_tunnels.h) but with a stricter limit
appropriate for ipvlan.

Fixes: 2ad7bf363841 ("ipvlan: Initial check-in of the IPVLAN driver.")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
 drivers/net/ipvlan/ipvlan_core.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index 835e04835..ab99eb624 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -9,6 +9,8 @@
 
 static u32 ipvlan_jhash_secret __read_mostly;
 
+static DEFINE_PER_CPU(int, ipvlan_xmit_depth);
+
 void ipvlan_init_secret(void)
 {
 	net_get_random_once(&ipvlan_jhash_secret, sizeof(ipvlan_jhash_secret));
@@ -676,6 +678,7 @@ int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct ipvl_dev *ipvlan = netdev_priv(dev);
 	struct ipvl_port *port = ipvlan_port_get_rcu_bh(ipvlan->phy_dev);
+	int ret = NET_XMIT_DROP;
 
 	if (!port)
 		goto out;
@@ -683,18 +686,32 @@ int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
 	if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))
 		goto out;
 
+	if (this_cpu_read(ipvlan_xmit_depth)) {
+		net_crit_ratelimited("ipvlan: xmit recursion detected on dev %s\n",
+				     dev->name);
+		goto out;
+	}
+
+	this_cpu_inc(ipvlan_xmit_depth);
 	switch(port->mode) {
 	case IPVLAN_MODE_L2:
-		return ipvlan_xmit_mode_l2(skb, dev);
+		ret = ipvlan_xmit_mode_l2(skb, dev);
+		break;
 	case IPVLAN_MODE_L3:
 #ifdef CONFIG_IPVLAN_L3S
 	case IPVLAN_MODE_L3S:
 #endif
-		return ipvlan_xmit_mode_l3(skb, dev);
+		ret = ipvlan_xmit_mode_l3(skb, dev);
+		break;
+	default:
+		WARN_ONCE(true, "%s called for mode = [%x]\n",
+			  __func__, port->mode);
+		kfree_skb(skb);
+		break;
 	}
+	this_cpu_dec(ipvlan_xmit_depth);
+	return ret;
 
-	/* Should not reach here */
-	WARN_ONCE(true, "%s called for mode = [%x]\n", __func__, port->mode);
 out:
 	kfree_skb(skb);
 	return NET_XMIT_DROP;
-- 
2.47.3


^ permalink raw reply related

* Re: [PATCH net] nfc: llcp: bound the remaining LLCP TLV parsers to their buffers
From: Doruk (0sec) @ 2026-07-11 13:42 UTC (permalink / raw)
  To: horms; +Cc: david, oe-linux-nfc, netdev, linux-kernel, david.laight.linux
In-Reply-To: <20260710134850.1615092-1-horms@kernel.org>

> Would it make sense to either guard skb->len >= LLCP_HEADER_SIZE
> before the two call sites, or take the same skb_tail_pointer() based
> approach used in nfc_llcp_connect_sn()?

Thanks for the review Simon! I went with the first option in a separate patch,
since I believe the missing minimum-length check affects every PDU type the
dispatcher hands off, not just CONNECT/CC:

[PATCH net] nfc: llcp: guard against short PDUs in nfc_llcp_rx_skb()

It adds "if (skb->len < LLCP_HEADER_SIZE) return;" at the top of
nfc_llcp_rx_skb(), so no handler can see a sub-header PDU and the
"skb->len - LLCP_HEADER_SIZE" subtraction can no longer wrap. With that
in place the connection-TLV parser's bound is safe. Happy to respin
either patch if you'd prefer both changes squashed into one.

Best,
Doruk

On Fri, Jul 10, 2026 03:49 PM, Simon Horman <horms@kernel.org> wrote:
>
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://netdev-ai.bots.linux.dev/sashiko/
> ---
> nfc: llcp: bound the remaining LLCP TLV parsers to their buffers
>
> The commit adds pointer-based bounds to nfc_llcp_parse_gb_tlv(),
> nfc_llcp_parse_connection_tlv() and nfc_llcp_connect_sn(), replacing
> the previous u8 offset walk which could wrap and produce an unbounded
> TLV loop. It cites the size_t underflow of "skb->len - LLCP_HEADER_SIZE"
> as one of the failure modes it wants to close.
>
> > diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
> > index 291f26facbf3..1a0a2f4aca70 100644
> > --- a/net/nfc/llcp_commands.c
> > +++ b/net/nfc/llcp_commands.c
>
> [ ... ]
>
> > @@ -243,17 +246,21 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
> >                                 const u8 *tlv_array, u16 tlv_array_len)
> >  {
> >       const u8 *tlv = tlv_array;
> > -     u8 type, length, offset = 0;
> > +     const u8 *tlv_end = tlv_array + tlv_array_len;
> > +     u8 type, length;
> >
> >       pr_debug("TLV array length %d\n", tlv_array_len);
> >
> >       if (sock == NULL)
> >               return -ENOTCONN;
> >
> > -     while (offset < tlv_array_len) {
> > +     while (tlv + 2 < tlv_end) {
> >               type = tlv[0];
> >               length = tlv[1];
> >
> > +             if (tlv + 2 + length > tlv_end)
> > +                     break;
> > +
>
> [High]
> Is the size_t underflow the commit message mentions actually addressed
> for nfc_llcp_parse_connection_tlv()?
>
> The two callers, nfc_llcp_recv_connect() and nfc_llcp_recv_cc() in
> net/nfc/llcp_core.c, still compute the length at the call site as
> skb->len - LLCP_HEADER_SIZE and hand it to the parser:
>
>     nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE],
>                                   skb->len - LLCP_HEADER_SIZE);
>
> and
>
>     nfc_llcp_parse_connection_tlv(llcp_sock, &skb->data[LLCP_HEADER_SIZE],
>                                   skb->len - LLCP_HEADER_SIZE);
>
> If skb->len is 0 or 1, that subtraction (size_t at the call site,
> truncated to the u16 tlv_array_len parameter) becomes 0xFFFE or 0xFFFF.
>
> Inside the parser, that value now feeds:
>
>     const u8 *tlv_end = tlv_array + tlv_array_len;
>
>     while (tlv + 2 < tlv_end) {
>             ...
>             if (tlv + 2 + length > tlv_end)
>                     break;
>
> so tlv_end points roughly 64 KiB past &skb->data[LLCP_HEADER_SIZE] and
> neither the loop condition nor the per-TLV check catches the walk
> leaving the skb.
>
> The commit message calls this out as one of the motivations:
>
>     "Dropping the u8 offset also removes the wrap, and for very short
>     connect frames this avoids the size_t underflow of
>     'skb->len - LLCP_HEADER_SIZE'."
>
> That mitigation is applied inside nfc_llcp_connect_sn() by using
> skb_tail_pointer(skb) directly, but the equivalent change is not made
> for the connection TLV path. nfc_llcp_rx_skb() has no minimum-length
> guard, so a received CONNECT or CC PDU with skb->len < LLCP_HEADER_SIZE
> still reaches these callers.
>
> Would it make sense to either guard skb->len >= LLCP_HEADER_SIZE
> before the two call sites, or take the same skb_tail_pointer() based
> approach used in nfc_llcp_connect_sn() so the parser derives its bound
> from the skb rather than from an untrusted subtraction?
>
> > diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
> > index dc65c719f35f..5e1881199238 100644
> > --- a/net/nfc/llcp_core.c
> > +++ b/net/nfc/llcp_core.c
> > @@ -849,13 +849,16 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
> >  static const u8 *nfc_llcp_connect_sn(const struct sk_buff *skb, size_t *sn_len)
> >  {
> >       u8 type, length;
> > -     const u8 *tlv = &skb->data[2];
> > -     size_t tlv_array_len = skb->len - LLCP_HEADER_SIZE, offset = 0;
> > +     const u8 *tlv = &skb->data[LLCP_HEADER_SIZE];
> > +     const u8 *tlv_end = skb_tail_pointer(skb);
> >
> > -     while (offset < tlv_array_len) {
> > +     while (tlv + 2 < tlv_end) {

^ permalink raw reply

* [PATCH iwl-next v1] igb: remove ASSERT_RTNL() from igb_write_rss_key()
From: Takashi Kozu @ 2026-07-11 13:32 UTC (permalink / raw)
  To: anthony.l.nguyen
  Cc: przemyslaw.kitszel, andrew+netdev, davem, edumazet, kuba, pabeni,
	intel-wired-lan, netdev, kohei.enju, Takashi Kozu

igb_runtime_resume() calls __igb_resume() with rpm=true, which
skips rtnl_lock() to avoid deadlocks. On that path __igb_open()
-> igb_configure() -> igb_setup_mrqc() -> igb_write_rss_key()
runs without rtnl held, so ASSERT_RTNL() fires a false-positive
WARN on every runtime resume. Drop it.

Fixes: dfaf57ef99cf ("igb: prepare for RSS key get/set support")
Signed-off-by: Takashi Kozu <takkozu@amazon.com>
---
 drivers/net/ethernet/intel/igb/igb_ethtool.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 65014a54a6d1..4eb586f9af99 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -3031,8 +3031,6 @@ void igb_write_rss_key(struct igb_adapter *adapter)
 {
 	struct e1000_hw *hw = &adapter->hw;
 
-	ASSERT_RTNL();
-
 	for (int i = 0; i < IGB_RSS_KEY_SIZE / 4; i++) {
 		u32 val = get_unaligned_le32(&adapter->rss_key[i * 4]);
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH net] mpls: fix NULL deref in mpls_valid_fib_dump_req() on CONFIG_INET=n
From: Weiming Shi @ 2026-07-11 11:50 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni, horms, dsahern
  Cc: kuniyu, gnault, xmei5, netdev, linux-kernel, Weiming Shi

On CONFIG_INET=n builds, mpls_valid_fib_dump_req() walks the parsed
attribute table itself instead of calling ip_valid_fib_dump_req(). The
RTA_OIF arm passes tb[RTA_OIF] to nla_get_u32() without checking it is
present, so an RTM_GETROUTE dump for AF_MPLS with strict checking and no
RTA_OIF hits a NULL dereference.

RTM_GETROUTE is RTNL_KIND_GET, which rtnetlink_rcv_msg() permits without
CAP_NET_ADMIN, so an unprivileged user can trigger it.

  Oops: general protection fault, probably for non-canonical address
        0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI
  KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
  RIP: 0010:mpls_valid_fib_dump_req (net/mpls/af_mpls.c:2189)
  Call Trace:
   mpls_dump_routes (net/mpls/af_mpls.c:2236)
   netlink_dump (net/netlink/af_netlink.c:2331)
   __netlink_dump_start (net/netlink/af_netlink.c:2446)
   rtnetlink_rcv_msg (net/core/rtnetlink.c:7033)
   netlink_rcv_skb (net/netlink/af_netlink.c:2556)
   netlink_unicast (net/netlink/af_netlink.c:1345)
   netlink_sendmsg (net/netlink/af_netlink.c:1900)
   __sock_sendmsg (net/socket.c:790)
   ____sys_sendmsg (net/socket.c:2684)
   ___sys_sendmsg (net/socket.c:2738)
   __sys_sendmsg (net/socket.c:2770)
   do_syscall_64 (arch/x86/entry/syscall_64.c:94)
   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)

Skip unset attributes, as ip_valid_fib_dump_req() does.

Fixes: 196cfebf8972 ("net/mpls: Handle kernel side filtering of route dumps")
Assisted-by: Claude:claude-opus-4-8
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
 net/mpls/af_mpls.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index ca504d9626cf..318cb7e2ac5f 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -2186,6 +2186,9 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
 		int ifindex;
 
 		if (i == RTA_OIF) {
+			if (!tb[i])
+				continue;
+
 			ifindex = nla_get_u32(tb[i]);
 			filter->dev = dev_get_by_index_rcu(net, ifindex);
 			if (!filter->dev)
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH net] net: sock: prevent integer overflow in sock_reserve_memory()
From: Kuniyuki Iwashima @ 2026-07-11 13:13 UTC (permalink / raw)
  To: Xiang Mei (Microsoft)
  Cc: Eric Dumazet, Paolo Abeni, Willem de Bruijn, David S . Miller,
	Jakub Kicinski, Simon Horman, Wei Wang, netdev, linux-kernel,
	AutonomousCodeSecurity, tgopinath, kys
In-Reply-To: <20260711005955.1467140-1-xmei5@asu.edu>

On Fri, Jul 10, 2026 at 6:00 PM Xiang Mei (Microsoft) <xmei5@asu.edu> wrote:
>
> sock_reserve_memory() adds 'pages << PAGE_SHIFT' (a plain int) to the int
> sk->sk_forward_alloc. An unprivileged SO_RESERVE_MEM caller can drive the
> accumulating counter past INT_MAX and wrap it negative, either in one
> INT_MAX request (0x80000000) or across several smaller ones.
> The corrupted sk_forward_alloc then trips WARN_ON_ONCE() in
> inet_sock_destruct() on close (a panic under panic_on_warn/oops=panic).
>
> Bound the field that overflows: compute the new sk_forward_alloc in u64 and
> reject with -EINVAL anything exceeding INT_MAX.
>
>   Kernel panic - not syncing: kernel: panic_on_warn set ...
>    __warn (kernel/panic.c:1054)
>    ...
>   RIP: 0010:inet_sock_destruct (net/ipv4/af_inet.c:161)
>    __sk_destruct (net/core/sock.c:2357)
>    inet_release (net/ipv4/af_inet.c:442)
>    sock_close (net/socket.c:1501)
>    __fput (fs/file_table.c:512)
>    __x64_sys_close (fs/open.c:1511)
>    do_syscall_64 (arch/x86/entry/syscall_64.c:94)
>
> Fixes: 2bb2f5fb21b0 ("net: add new socket option SO_RESERVE_MEM")
> Reported-by: AutonomousCodeSecurity@microsoft.com
> Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>

Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>

^ permalink raw reply

* Re: [PATCH] af_unix: add cond_resched() when flushing receive queue on close
From: Kuniyuki Iwashima @ 2026-07-11 12:41 UTC (permalink / raw)
  To: albin_yang
  Cc: davem, edumazet, kuba, pabeni, horms, netdev, linux-kernel,
	albinwyang
In-Reply-To: <20260711095147.1972419-1-albin_yang@163.com>

On Sat, Jul 11, 2026 at 2:52 AM <albin_yang@163.com> wrote:
>
> From: Wei Yang <albinwyang@tencent.com>
>
> unix_release_sock() drains sk_receive_queue in a tight loop with no
> rescheduling point.  Each iteration may do extra work depending on
> the socket type: kfree_skb() runs unix_destruct_scm()->fput() for
> SCM_RIGHTS fds on data sockets, and a LISTEN socket recurses into
> unix_release_sock() for each embryonic child.
>
> A long receive queue can hog the CPU and trip the softlockup watchdog
> on CONFIG_PREEMPT_NONE kernels.

AFAIU, CONFIG_PREEMPT_NONE is no longer supported on
modern arch since 7dadeaa6e851, and it also mentions how bad
cond_resched() is.

I think you need to keep this patch downstream.


>  We have reproduced this multiple
> times under stress-ng stress testing.  Add cond_resched() as done in
> similar teardown loops (inet_csk_listen_stop, inet_twsk_purge,
> close_files).
>
> Signed-off-by: Wei Yang <albinwyang@tencent.com>
> ---
>  net/unix/af_unix.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
> index f7a9d55eee8a..8b4624ce4178 100644
> --- a/net/unix/af_unix.c
> +++ b/net/unix/af_unix.c
> @@ -724,6 +724,7 @@ static void unix_release_sock(struct sock *sk, int embrion)
>
>                 /* passed fds are erased in the kfree_skb hook */
>                 kfree_skb_reason(skb, SKB_DROP_REASON_SOCKET_CLOSE);
> +               cond_resched();
>         }
>
>         if (path.dentry)
> --
> 2.43.5
>

^ permalink raw reply

* [PATCH net] nfc: port100: reject frames whose declared length exceeds the received data
From: Doruk Tan Ozturk @ 2026-07-11 12:36 UTC (permalink / raw)
  To: david; +Cc: oe-linux-nfc, netdev, linux-kernel, stable

port100_recv_response() passes the URB transfer buffer to
port100_rx_frame_is_valid(), which checksums le16_to_cpu(frame->datalen)
bytes of frame->data. datalen is a 16-bit field supplied by the device
and is never checked against the number of bytes actually received
(urb->actual_length), so a device reporting a datalen larger than the
received frame makes port100_data_checksum() read out of bounds past the
transfer buffer.

Reject a response whose declared frame size does not fit the received
length before validating it.

Found by 0sec (https://0sec.ai) using automated source analysis; the
missing bound is evident from source. Compile-tested.

Fixes: 562d4d59b8a1 ("NFC: Sony Port-100 Series driver")
Cc: stable@vger.kernel.org
Assisted-by: 0sec:claude-opus-4-8
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
 drivers/nfc/port100.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
index 5ae61d7ebcfe..30a4e09875d3 100644
--- a/drivers/nfc/port100.c
+++ b/drivers/nfc/port100.c
@@ -636,6 +636,13 @@ static void port100_recv_response(struct urb *urb)
 
 	in_frame = dev->in_urb->transfer_buffer;
 
+	if (urb->actual_length < PORT100_FRAME_HEADER_LEN ||
+	    urb->actual_length < port100_rx_frame_size(in_frame)) {
+		nfc_err(&dev->interface->dev, "Received a truncated frame\n");
+		cmd->status = -EIO;
+		goto sched_wq;
+	}
+
 	if (!port100_rx_frame_is_valid(in_frame)) {
 		nfc_err(&dev->interface->dev, "Received an invalid frame\n");
 		cmd->status = -EIO;
-- 
2.43.0


^ permalink raw reply related

* [PATCH net] nfc: fdp: bound the device-supplied read size in fdp_nci_i2c_read()
From: Doruk Tan Ozturk @ 2026-07-11 12:36 UTC (permalink / raw)
  To: david; +Cc: oe-linux-nfc, netdev, linux-kernel, stable

fdp_nci_i2c_read() reads a "length packet" from the FDP I2C controller and
computes the size of the next I2C transfer from two device-supplied bytes:

	phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3;

next_read_size is a u16 (up to 65535) and is never bounded. On the next
loop iteration it is used directly as the length passed to

	i2c_master_recv(client, tmp, len);

which reads into the fixed 261-byte stack buffer
tmp[FDP_NCI_I2C_MAX_PAYLOAD]. A malicious or malfunctioning controller
that reports a large length thus overflows the stack buffer -- the
r != len check runs only after the read has already happened.

Reject a next-read size larger than the buffer and resynchronize.

Found by 0sec (https://0sec.ai) using automated source analysis; the
missing bound is evident from source. Compile-tested.

Fixes: a06347c04c13 ("NFC: Add Intel Fields Peak NFC solution driver")
Cc: stable@vger.kernel.org
Assisted-by: 0sec:claude-opus-4-8
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
 drivers/nfc/fdp/i2c.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
index c1896a1d978c..581f85f0dfa8 100644
--- a/drivers/nfc/fdp/i2c.c
+++ b/drivers/nfc/fdp/i2c.c
@@ -128,7 +128,7 @@ static const struct nfc_phy_ops i2c_phy_ops = {
 
 static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb)
 {
-	int r, len;
+	int r = -EREMOTEIO, len;
 	u8 tmp[FDP_NCI_I2C_MAX_PAYLOAD], lrc, k;
 	u16 i;
 	struct i2c_client *client = phy->i2c_dev;
@@ -140,6 +140,13 @@ static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb)
 
 		len = phy->next_read_size;
 
+		if (len > FDP_NCI_I2C_MAX_PAYLOAD) {
+			dev_dbg(&client->dev, "%s: read size %d too large\n",
+				__func__, len);
+			phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
+			goto flush;
+		}
+
 		r = i2c_master_recv(client, tmp, len);
 		if (r != len) {
 			dev_dbg(&client->dev, "%s: i2c recv err: %d\n",
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH bpf] bpf: tcp: Fix use-after-free in bpf_iter_tcp_established_batch()
From: Kuniyuki Iwashima @ 2026-07-11 12:36 UTC (permalink / raw)
  To: Jose Fernandez (Anthropic)
  Cc: Eric Dumazet, Neal Cardwell, David S. Miller, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Andrii Nakryiko, Yonghong Song,
	Martin KaFai Lau, netdev, linux-kernel, bpf, Ben Cressey
In-Reply-To: <20260620-bpf-iter-tcp-refcnt-v1-1-883bf9e69495@linux.dev>

On Fri, Jun 19, 2026 at 5:33 PM Jose Fernandez (Anthropic)
<jose.fernandez@linux.dev> wrote:
>
> reqsk_queue_hash_req() publishes a TCP_NEW_SYN_RECV request_sock onto
> the ehash chain (via inet_ehash_insert(), which drops the bucket lock on
> return) and only afterwards refcount_set()s rsk_refcnt to 3.
>
> Lockless readers such as __inet_lookup_established() account for this by
> using refcount_inc_not_zero(), but bpf_iter_tcp_established_batch() uses
> plain sock_hold() while holding the bucket lock, on the assumption that
> the lock guarantees sk_refcnt > 0. That assumption does not hold for
> request_sock:
>
>   CPU 0                                CPU 1
>   -----                                -----
>   tcp_conn_request()
>    reqsk_queue_hash_req()
>     inet_ehash_insert(req)
>      spin_lock(bucket)
>      __sk_nulls_add_node_rcu(req)      // rsk_refcnt == 0
>      spin_unlock(bucket)
>                                        bpf_iter_tcp_established_batch()
>                                         spin_lock(bucket)
>                                         sock_hold(req)   <-- addition on 0
>                                         spin_unlock(bucket)
>     refcount_set(&req->rsk_refcnt, 3)  // clobbers saturated value
>
> which surfaces as:
>
>   refcount_t: addition on 0; use-after-free.
>   WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x48/0x90, CPU#1
>   Call Trace:
>    bpf_iter_tcp_established_batch+0x14e/0x170
>    bpf_iter_tcp_batch+0x53/0x200
>    bpf_iter_tcp_seq_next+0x27/0x70
>    bpf_seq_read+0x107/0x410
>    vfs_read+0xb9/0x380
>
> refcount_warn_saturate() then saturates the count, the publishing CPU's
> refcount_set() clobbers it, and the socket is left one reference short.
> When the last legitimate owner drops its reference the reqsk is freed
> while still reachable, leading to use-after-free panics in e.g.
> inet_csk_accept() or inet_csk_listen_stop().
>
> This reproduces in seconds with tcp_syncookies=0, a handful of threads
> doing connect()/close() to a local listener while others read an
> iter/tcp link in a tight loop.
>
> Use refcount_inc_not_zero() and skip the socket on failure, the same way
> every other ehash walker does. The listening hash is unaffected as
> listeners are always inserted into lhash2 with sk_refcnt >= 1, so
> bpf_iter_tcp_listening_batch() is left as-is.
>
> If every matching socket in a bucket is mid-init, end_sk can stay at 0;
> advance to the next bucket in that case rather than terminating the
> whole iteration on a stale batch[0].
>
> Fixes: 04c7820b776f ("bpf: tcp: Bpf iter batching and lock_sock")
> Reviewed-by: Ben Cressey <ben@cressey.dev>
> Assisted-by: Claude:unspecified
> Signed-off-by: Jose Fernandez (Anthropic) <jose.fernandez@linux.dev>
> ---
>  net/ipv4/tcp_ipv4.c | 35 ++++++++++++++++++++---------------
>  1 file changed, 20 insertions(+), 15 deletions(-)
>
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index fdc81150ff6c..92342dcc6892 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -3074,25 +3074,25 @@ static unsigned int bpf_iter_tcp_established_batch(struct seq_file *seq,
>  {
>         struct bpf_tcp_iter_state *iter = seq->private;
>         struct hlist_nulls_node *node;
> -       unsigned int expected = 1;
> -       struct sock *sk;
> +       unsigned int expected = 0;
> +       struct sock *sk = *start_sk;
>
> -       sock_hold(*start_sk);
> -       iter->batch[iter->end_sk++].sk = *start_sk;
> -
> -       sk = sk_nulls_next(*start_sk);
>         *start_sk = NULL;
>         sk_nulls_for_each_from(sk, node) {
> -               if (seq_sk_match(seq, sk)) {
> -                       if (iter->end_sk < iter->max_sk) {
> -                               sock_hold(sk);
> -                               iter->batch[iter->end_sk++].sk = sk;
> -                       } else if (!*start_sk) {
> -                               /* Remember where we left off. */
> -                               *start_sk = sk;
> -                       }
> -                       expected++;
> +               if (!seq_sk_match(seq, sk))
> +                       continue;
> +               if (iter->end_sk < iter->max_sk) {
> +                       /* reqsk_queue_hash_req() inserts with sk_refcnt == 0
> +                        * and refcount_set()s it after the bucket lock drops.
> +                        */
> +                       if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
> +                               continue;
> +                       iter->batch[iter->end_sk++].sk = sk;
> +               } else if (!*start_sk) {
> +                       /* Remember where we left off. */
> +                       *start_sk = sk;
>                 }
> +               expected++;

This should be incremented just after seq_sk_match()
(see below)


>         }
>
>         return expected;
> @@ -3129,6 +3129,7 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
>         struct sock *sk;
>         int err;
>
> +again:
>         sk = bpf_iter_tcp_resume(seq);
>         if (!sk)
>                 return NULL; /* Done */
> @@ -3167,6 +3168,10 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
>         WARN_ON_ONCE(iter->end_sk != expected);

Let's say the batch array was smaller than the hash chain length
and we reallocate the array based on "expected" w/ the bucket lock.

What happens if refcount_set(..., 3) is called during reallocation ?
bpf_iter_fill_batch() will see the larger "expected", and WARN_ON_ONCE()
will be triggered.


>  done:
>         bpf_iter_tcp_unlock_bucket(seq);
> +       if (unlikely(!iter->end_sk)) {
> +               ++iter->state.bucket;
> +               goto again;
> +       }
>         return iter->batch[0].sk;
>  }
>
>
> ---
> base-commit: 4549871118cf616eecdd2d939f78e3b9e1dddc48
> change-id: 20260619-bpf-iter-tcp-refcnt-107d52b238da
>
> Best regards,
> --
> Jose Fernandez (Anthropic) <jose.fernandez@linux.dev>
>

^ permalink raw reply

* [PATCH net v2 0/2] net: pse-pd: fix use-after-free of PI array on controller unregister
From: Carlo Szelinsky @ 2026-07-11 12:16 UTC (permalink / raw)
  To: Oleksij Rempel, Kory Maincent, Andrew Lunn, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, netdev, linux-kernel, Carlo Szelinsky

This series fixes use-after-free bugs in the PSE core teardown path.

When a PSE controller is unregistered, pse_controller_unregister() frees
the PI array (pcdev->pi) and the power domain supplies. But the IRQ
handler, the notification worker, and the regulator disable path can
still run at that point and touch this freed memory.

Patch 1 reorders pse_controller_unregister() so the IRQ and the worker
are stopped before anything they use is freed.

Patch 2 clears pcdev->pi under the lock after freeing it, and makes the
three regulator ops that read pcdev->pi return early when it is NULL.

This series closes the use-after-free reachable from the regulator core
and the controller's own IRQ and worker during unregister. The wider
case - a pse_control consumer (the ethtool path) whose handle outlives
the controller - is a separate lifetime problem handled by the PSE
notifier decoupling work for net-next, not this fix.

Both bugs are pre-existing. They are teardown races, so there is no easy
way to trigger them on purpose and no simple reproducer. The fix is based
on code review. It is compile tested and checkpatch clean.

v1: https://lore.kernel.org/all/20260524223306.2570676-1-github@szelinsky.de/

Changes in v2:
- Patch 1: also stop the IRQ before pse_flush_pw_ds(), and cancel the
  notification worker before pse_release_pis(). v1 only moved
  disable_irq() ahead of pse_release_pis(). Also fix the commit message,
  which wrongly said the worker does not touch pcdev->pi.
- Patch 2: take pcdev->lock around the kfree() and the pcdev->pi = NULL
  store, so a reader sees an authoritative NULL. Add the same NULL guard
  to pse_pi_enable() and pse_pi_is_enabled(), not just pse_pi_disable().
- Thanks to Simon Horman for the review.

Carlo Szelinsky (2):
  net: pse-pd: stop async event sources before freeing PI data in
    unregister
  net: pse-pd: guard regulator ops against freed PI data during
    unregister

 drivers/net/pse-pd/pse_core.c | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

-- 
2.43.0


^ permalink raw reply

* [PATCH net v2 2/2] net: pse-pd: guard regulator ops against freed PI data during unregister
From: Carlo Szelinsky @ 2026-07-11 12:16 UTC (permalink / raw)
  To: Oleksij Rempel, Kory Maincent, Andrew Lunn, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, netdev, linux-kernel, Carlo Szelinsky
In-Reply-To: <20260711121611.1639086-1-github@szelinsky.de>

The PSE PI regulators are devm-registered inside pse_controller_register(),
which runs before devres_add() arms the controller's own release in
devm_pse_controller_register(). On driver detach devres unwinds in LIFO
order, so pse_controller_unregister() runs first and frees pcdev->pi via
pse_release_pis(); the regulators are torn down afterwards.

When regulator_unregister() flushes a pending disable, the regulator core
invokes pse_pi_disable(), which dereferences pcdev->pi[id] (directly and
via _pse_pi_disable() -> pse_pi_deallocate_pw_budget()). At that point the
PI array is already freed, so this is a use-after-free. pse_pi_enable()
and pse_pi_is_enabled() dereference pcdev->pi[id] the same way and are
reachable by any regulator consumer that keeps a handle across the
teardown window.

Clear pcdev->pi after freeing it and bail out of the three regulator ops
that dereference it when it is NULL. Perform the kfree() and NULL store in
pse_release_pis() under pcdev->lock, and read pcdev->pi under the same lock
in the ops, so the NULL an op observes is authoritative even when the free
runs concurrently on another CPU: the op either sees the live array or
returns without touching freed memory.

The other three regulator ops (pse_pi_get_voltage(),
pse_pi_get_current_limit(), pse_pi_set_current_limit()) do not dereference
pcdev->pi and need no guard.

Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
 drivers/net/pse-pd/pse_core.c | 27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 6045b6c399c2..21ccb5146616 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -144,7 +144,13 @@ static void pse_release_pis(struct pse_controller_dev *pcdev)
 		of_node_put(pcdev->pi[i].pairset[1].np);
 		of_node_put(pcdev->pi[i].np);
 	}
+	/* Free under the lock so the NULL store is authoritative against
+	 * the regulator ops that read pcdev->pi under pcdev->lock.
+	 */
+	mutex_lock(&pcdev->lock);
 	kfree(pcdev->pi);
+	pcdev->pi = NULL;
+	mutex_unlock(&pcdev->lock);
 }
 
 /**
@@ -421,6 +427,11 @@ static int pse_pi_is_enabled(struct regulator_dev *rdev)
 
 	id = rdev_get_id(rdev);
 	mutex_lock(&pcdev->lock);
+	/* Controller may be unregistered (pcdev->pi freed) mid-teardown. */
+	if (!pcdev->pi) {
+		ret = -ENODEV;
+		goto out;
+	}
 	if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) {
 		ret = pcdev->pi[id].admin_state_enabled;
 		goto out;
@@ -674,6 +685,11 @@ static int pse_pi_enable(struct regulator_dev *rdev)
 
 	id = rdev_get_id(rdev);
 	mutex_lock(&pcdev->lock);
+	/* Controller may be unregistered (pcdev->pi freed) mid-teardown. */
+	if (!pcdev->pi) {
+		mutex_unlock(&pcdev->lock);
+		return -ENODEV;
+	}
 	if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) {
 		/* Manage enabled status by software.
 		 * Real enable process will happen if a port is connected.
@@ -702,15 +718,20 @@ static int pse_pi_enable(struct regulator_dev *rdev)
 static int pse_pi_disable(struct regulator_dev *rdev)
 {
 	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
-	struct pse_pi *pi;
 	int id, ret;
 
 	id = rdev_get_id(rdev);
-	pi = &pcdev->pi[id];
 	mutex_lock(&pcdev->lock);
+	/* Reached via the regulator core's deferred-disable flush after
+	 * pcdev->pi is freed on unregister.
+	 */
+	if (!pcdev->pi) {
+		mutex_unlock(&pcdev->lock);
+		return 0;
+	}
 	ret = _pse_pi_disable(pcdev, id);
 	if (!ret)
-		pi->admin_state_enabled = 0;
+		pcdev->pi[id].admin_state_enabled = 0;
 
 	mutex_unlock(&pcdev->lock);
 	return 0;
-- 
2.43.0


^ permalink raw reply related

* [PATCH net v2 1/2] net: pse-pd: stop async event sources before freeing PI data in unregister
From: Carlo Szelinsky @ 2026-07-11 12:16 UTC (permalink / raw)
  To: Oleksij Rempel, Kory Maincent, Andrew Lunn, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, netdev, linux-kernel, Carlo Szelinsky
In-Reply-To: <20260711121611.1639086-1-github@szelinsky.de>

pse_controller_unregister() frees resources that its own asynchronous
event sources are still using:

 * The PI array (pcdev->pi) is freed by pse_release_pis() while the
   threaded IRQ handler pse_isr() can still be running. pse_isr() walks
   pcdev->pi[] (via pse_set_config_isr() and
   regulator_notifier_call_chain() on pcdev->pi[i].rdev), so an interrupt
   arriving before disable_irq() dereferences freed memory.

 * pse_flush_pw_ds() runs before disable_irq() and drops the power domain
   references, which can free pw_d->supply via __pse_pw_d_release().
   A concurrent interrupt reaches that supply through
   _pse_pi_disable() -> pse_pw_d_retry_power_delivery() ->
   regulator_request_power_budget(pw_d->supply), another use-after-free.

 * cancel_work_sync(&pcdev->ntf_work) runs after pse_release_pis(), but
   the notification worker reaches pcdev->pi too: pse_send_ntf_worker()
   -> pse_control_put() -> __pse_control_release() dereferences
   psec->pcdev->pi[psec->id].admin_state_enabled. Draining the worker
   after the PI array is freed is therefore also a use-after-free.

Reorder teardown so every asynchronous accessor is stopped first:
disable the IRQ, drain the notification worker, and only then flush the
power domains and release the PI array.

Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
 drivers/net/pse-pd/pse_core.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index a5e6d7b26b9f..6045b6c399c2 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -1114,11 +1114,15 @@ EXPORT_SYMBOL_GPL(pse_controller_register);
  */
 void pse_controller_unregister(struct pse_controller_dev *pcdev)
 {
-	pse_flush_pw_ds(pcdev);
-	pse_release_pis(pcdev);
+	/* Stop the IRQ and notification worker before freeing what they
+	 * reach: both touch pcdev->pi, and the IRQ also uses pw_d->supply
+	 * that pse_flush_pw_ds() drops.
+	 */
 	if (pcdev->irq)
 		disable_irq(pcdev->irq);
 	cancel_work_sync(&pcdev->ntf_work);
+	pse_flush_pw_ds(pcdev);
+	pse_release_pis(pcdev);
 	kfifo_free(&pcdev->ntf_fifo);
 	mutex_lock(&pse_list_mutex);
 	list_del(&pcdev->list);
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v19 39/40] rust: completion: Add __rust_helper to rust_helper_wait_for_completion()
From: Miguel Ojeda @ 2026-07-11 12:13 UTC (permalink / raw)
  To: Byungchul Park, Gary Guo
  Cc: linux-kernel, max.byungchul.park, kernel_team, torvalds,
	damien.lemoal, linux-ide, adilger.kernel, linux-ext4, mingo,
	peterz, will, tglx, rostedt, joel, sashal, daniel.vetter,
	duyuyang, johannes.berg, tj, tytso, willy, david, amir73il,
	gregkh, kernel-team, linux-mm, akpm, mhocko, minchan, hannes,
	vdavydov.dev, sj, jglisse, dennis, cl, penberg, rientjes, vbabka,
	ngupta, linux-block, josef, linux-fsdevel, jack, jlayton,
	dan.j.williams, hch, djwong, dri-devel, rodrigosiqueiramelo,
	melissa.srw, hamohammed.sa, harry.yoo, chris.p.wilson,
	gwan-gyeong.mun, boqun.feng, longman, yunseong.kim, ysk,
	yeoreum.yun, netdev, matthew.brost, her0gyugyu, corbet,
	catalin.marinas, bp, x86, hpa, luto, sumit.semwal, gustavo,
	christian.koenig, andi.shyti, arnd, lorenzo.stoakes, Liam.Howlett,
	rppt, surenb, mcgrof, petr.pavlu, da.gomez, samitolvanen, paulmck,
	frederic, neeraj.upadhyay, joelagnelf, josh, urezki,
	mathieu.desnoyers, jiangshanlai, qiang.zhang, juri.lelli,
	vincent.guittot, dietmar.eggemann, bsegall, mgorman, vschneid,
	chuck.lever, neil, okorniev, Dai.Ngo, tom, trondmy, anna, kees,
	bigeasy, clrkwllms, mark.rutland, ada.coupriediaz,
	kristina.martsenko, wangkefeng.wang, broonie, kevin.brodsky, dwmw,
	shakeel.butt, ast, ziy, yuzhao, baolin.wang, usamaarif642,
	joel.granados, richard.weiyang, geert+renesas, tim.c.chen, linux,
	alexander.shishkin, lillian, chenhuacai, francesco,
	guoweikang.kernel, link, jpoimboe, masahiroy, brauner,
	thomas.weissschuh, oleg, mjguzik, andrii, wangfushuai, linux-doc,
	linux-arm-kernel, linux-media, linaro-mm-sig, linux-i2c,
	linux-arch, linux-modules, rcu, linux-nfs, linux-rt-devel,
	2407018371, dakr, neilb, bagasdotme, wsa+renesas, dave.hansen,
	geert, ojeda, alex.gaynor, bjorn3_gh, lossin, a.hindborg,
	aliceryhl, tmgross, rust-for-linux
In-Reply-To: <20260706061928.66713-40-byungchul@sk.com>

On Mon, Jul 6, 2026 at 8:22 AM Byungchul Park <byungchul@sk.com> wrote:
>
> This is needed to inline these helpers into Rust code, which is required
> for DEPT to play with wait_for_completion().
>
> Signed-off-by: Byungchul Park <byungchul@sk.com>

Apart from what Gary said -- why did you need to do this in a separate
patch in the same series?

Cheers,
Miguel

^ permalink raw reply

* [PATCH v2] net/rps: consolidate RPS dispatch into helpers
From: Jemmy Wong @ 2026-07-11 12:10 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: netdev, linux-kernel

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, sharing a common __netif_rps() inner
helper that runs the get_rps_cpu()/enqueue_to_backlog() step under RCU.

Both wrappers keep the early static_branch_unlikely(&rps_needed) check
and only enter an RCU read-side critical section when RPS is actually
enabled at runtime, so no extra rcu_read_lock()/unlock() is executed on
the hot netif_rx_internal() path when RPS is compiled in but disabled.

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.

No functional change intended.

Signed-off-by: Jemmy Wong <jemmywong512@gmail.com>
---
Changes since v1 [1]:
  - Move rcu_read_lock()/unlock() inside the
    static_branch_unlikely(&rps_needed) check, so no RCU read-side section
    is entered when RPS is compiled in but disabled at runtime. Addresses
    Paolo's concern that netif_rx_internal() is performance critical and
    rcu_read_lock() is not a no-op.
  - Drop the guard(rcu) / scoped_guard(rcu) conversion; the RCU section
    is now scoped inside netif_rps() / netif_rps_list() themselves.
  - Split __netif_rps() out of netif_rps() so that netif_rps_list() can
    reuse the per-skb steering step without re-testing the static branch
    or dropping and reacquiring the RCU lock per skb.
  - netif_rps_list() now returns int; when RPS drains the entire list,
    netif_receive_skb_list_internal() skips the __netif_receive_skb_list()
    fall-through instead of walking an empty list. The result is decided
    directly from list_empty(&undo_list): drop the splice on the drained
    path and only splice back when there is something to fall through.

Verified generated code with objdump on aarch64 with CONFIG_RPS=y,
CONFIG_XPS=y, CONFIG_SMP=y, CONFIG_PREEMPT=y (so rcu_read_lock()
expands to a real preempt_disable(), i.e. not a no-op -- the
specific scenario Paolo asked about [2]):

  scripts/bloat-o-meter net/core/dev.o (before -> after):
    add/remove: 2/2 grow/shrink: 1/2 up/down: 720/-716 (4)
    Function                        old     new   delta
    __netif_rps                       -     660    +660
    netif_receive_skb_list_internal 640     700     +60
    netif_rx_internal               236     200     -36
    netif_receive_skb               204     136     -68
    get_rps_cpu                     608       -    -608
    Total: Before=335841, After=335849, chg +0.00%

  netif_rx_internal fast path (!rps_needed, i.e. static branch not
  taken -- the path Paolo asked about [2]):
    - identical instruction sequence to base, no rcu_read_lock/unlock
    - stack frame 16 bytes smaller (0x50 -> 0x40)
    - static_branch NOP is preserved
  netif_receive_skb_internal: inlined in both builds; caller
    netif_receive_skb shrinks by 68 bytes.
  netif_receive_skb_list_internal: !rps_needed fast path unchanged;
    rps_needed=true path grows +60 bytes for LIST_HEAD(undo_list)
    setup, the explicit list_empty(&undo_list)/list_splice_init()
    branch and its early NET_RX_SUCCESS return.

[1] https://lore.kernel.org/all/20260702152830.39065-1-jemmywong512@gmail.com/
[2] https://lore.kernel.org/all/e53482b8-4e1d-4a04-929c-f5cb443ba59d@redhat.com/

 include/linux/netdevice.h |   5 +-
 net/core/dev.c            | 118 +++++++++++++++++++++++---------------
 2 files changed, 74 insertions(+), 49 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..1a05ac8fdd50 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5426,6 +5426,65 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
 	return NET_RX_DROP;
 }

+#ifdef CONFIG_RPS
+static inline int __netif_rps(struct sk_buff *skb)
+{
+	int cpu;
+	int ret = NET_RX_UNHANDLED;
+	struct rps_dev_flow voidflow, *rflow = &voidflow;
+
+	cpu = get_rps_cpu(skb->dev, skb, &rflow);
+	if (cpu >= 0)
+		ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
+
+	return ret;
+}
+#endif
+
+static inline int netif_rps(struct sk_buff *skb)
+{
+	int ret = NET_RX_UNHANDLED;
+
+#ifdef CONFIG_RPS
+	if (!static_branch_unlikely(&rps_needed))
+		return ret;
+
+	rcu_read_lock();
+	ret = __netif_rps(skb);
+	rcu_read_unlock();
+#endif
+
+	return ret;
+}
+
+static inline int netif_rps_list(struct list_head *head)
+{
+	int ret = NET_RX_UNHANDLED;
+
+#ifdef CONFIG_RPS
+	LIST_HEAD(undo_list);
+	struct sk_buff *skb, *next;
+
+	if (!static_branch_unlikely(&rps_needed))
+		return ret;
+
+	rcu_read_lock();
+	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);
+	}
+	rcu_read_unlock();
+
+	if (list_empty(&undo_list))
+		ret = NET_RX_SUCCESS;
+	else
+		list_splice_init(&undo_list, head);
+#endif
+
+	return ret;
+}
+
 static struct netdev_rx_queue *netif_get_rxqueue(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
@@ -5695,34 +5754,18 @@ EXPORT_SYMBOL_GPL(do_xdp_generic);

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

 	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();
-
-		ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
-
-		rcu_read_unlock();
-	} else
-#endif
-	{
-		unsigned int qtail;
+	ret = netif_rps(skb);
+	if (ret != NET_RX_UNHANDLED)
+		return ret;

-		ret = enqueue_to_backlog(skb, smp_processor_id(), &qtail);
-	}
-	return ret;
+	return enqueue_to_backlog(skb, smp_processor_id(), &qtail);
 }

 /**
@@ -6389,19 +6432,11 @@ 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);
+	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
+	rcu_read_lock();
 	ret = __netif_receive_skb(skb);
 	rcu_read_unlock();
 	return ret;
@@ -6421,21 +6456,10 @@ 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 (netif_rps_list(head) == NET_RX_SUCCESS)
+		return;

-			if (cpu >= 0) {
-				/* Will be handled, remove from list */
-				skb_list_del_init(skb);
-				enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
-			}
-		}
-	}
-#endif
+	rcu_read_lock();
 	__netif_receive_skb_list(head);
 	rcu_read_unlock();
 }
--
2.54.0 (Apple Git-157)

^ permalink raw reply related

* [PATCH net-next v2 2/2] net: dsa: mt7530: add EN7528 support
From: Ahmed Naseef @ 2026-07-11 11:41 UTC (permalink / raw)
  To: netdev
  Cc: Arınç ÜNAL, Chester A. Unal, David S. Miller,
	Andrew Lunn, AngeloGioacchino Del Regno, Conor Dooley,
	DENG Qingfang, Daniel Golle, Eric Dumazet, Jakub Kicinski,
	Krzysztof Kozlowski, Landen Chao, Matthias Brugger, Paolo Abeni,
	Rob Herring, Russell King, Sean Wang, Vladimir Oltean, devicetree,
	linux-arm-kernel, linux-kernel, linux-mediatek, Ahmed Naseef
In-Reply-To: <cover.1783770059.git.naseefkm@gmail.com>

The EcoNet EN7528 SoC integrates an MT7530 switch (the chip revision
register reads 0x7530), memory-mapped in the SoC register space and
reached through the same MMIO glue used for the built-in switches of the
MediaTek MT7988 and Airoha EN7581/AN7583 SoCs. Its reset sequence and its
PHY indirect access registers are the same as on those switches, so add
an ID_EN7528 variant bound with the "econet,en7528-switch" compatible,
reusing mt7988_setup() and the indirect PHY accessors.

The switch core, however, is an MT7530 and not an MT7531 derivative: the
CPU port to trap frames to is set through the MT7530-style CPU_EN /
CPU_PORT fields of the MFC register rather than the MT7531 CFC register,
so add it to the MT7530 handling in mt753x_conduit_state_change(). For the
same reason the MT7530 mirror and force-mode register layouts already
apply to it as the default of the MT753X_*() macros.

The four user ports (1-4) are connected to integrated Gigabit PHYs at
MDIO addresses 9-12 of the switch internal MDIO bus. The CPU port (port
6) is connected to the SoC Ethernet MAC at a fixed 1000 Mbps full duplex
link, so the port capabilities cannot be shared with the MT7988 and
EN7581 switches, whose CPU ports run at 10 Gbps.

The LAN GPHYs advertise EEE by default, but negotiating EEE with some
link partners results in an unstable link with dropped frames. Leave the
LPI capabilities empty for the EN7528 so that phylink disables EEE on
these PHYs and refuses to enable it from userspace.

Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
---
 drivers/net/dsa/mt7530-mmio.c |  1 +
 drivers/net/dsa/mt7530.c      | 58 ++++++++++++++++++++++++++++++-----
 drivers/net/dsa/mt7530.h      |  1 +
 3 files changed, 52 insertions(+), 8 deletions(-)

diff --git a/drivers/net/dsa/mt7530-mmio.c b/drivers/net/dsa/mt7530-mmio.c
index 1dc8b93fb51a..1065671bcdd1 100644
--- a/drivers/net/dsa/mt7530-mmio.c
+++ b/drivers/net/dsa/mt7530-mmio.c
@@ -13,6 +13,7 @@
 static const struct of_device_id mt7988_of_match[] = {
 	{ .compatible = "airoha,an7583-switch", .data = &mt753x_table[ID_AN7583], },
 	{ .compatible = "airoha,en7581-switch", .data = &mt753x_table[ID_EN7581], },
+	{ .compatible = "econet,en7528-switch", .data = &mt753x_table[ID_EN7528], },
 	{ .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], },
 	{ /* sentinel */ },
 };
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 3c2a3029b10c..6c8ed00ee9e7 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -2912,6 +2912,30 @@ static void en7581_mac_port_get_caps(struct dsa_switch *ds, int port,
 	}
 }
 
+static void en7528_mac_port_get_caps(struct dsa_switch *ds, int port,
+				     struct phylink_config *config)
+{
+	switch (port) {
+	/* Ports which are connected to switch PHYs. There is no MII pinout. */
+	case 1 ... 4:
+		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
+			  config->supported_interfaces);
+
+		config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD;
+		break;
+
+	/* Port 6 is connected to SoC's GMAC at 1000 Mbps full duplex. There
+	 * is no MII pinout.
+	 */
+	case 6:
+		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
+			  config->supported_interfaces);
+
+		config->mac_capabilities |= MAC_1000FD;
+		break;
+	}
+}
+
 static void
 mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
 		  phy_interface_t interface)
@@ -3101,17 +3125,24 @@ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
 				    struct phylink_config *config)
 {
 	struct mt7530_priv *priv = ds->priv;
-	u32 eeecr;
 
 	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
 
-	config->lpi_capabilities = MAC_100FD | MAC_1000FD | MAC_2500FD;
-
-	eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
-	/* tx_lpi_timer should be in microseconds. The time units for
-	 * LPI threshold are unspecified.
+	/* The EN7528 GPHYs report EEE capability, but negotiating EEE with
+	 * common link partners (e.g. Realtek GbE NICs) results in an unstable
+	 * link with dropped frames. Leave the LPI capabilities empty so that
+	 * phylink disables EEE on these PHYs and refuses to enable it from
+	 * userspace.
 	 */
-	config->lpi_timer_default = FIELD_GET(LPI_THRESH_MASK, eeecr);
+	if (priv->id != ID_EN7528) {
+		u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
+
+		config->lpi_capabilities = MAC_100FD | MAC_1000FD | MAC_2500FD;
+		/* tx_lpi_timer should be in microseconds. The time units for
+		 * LPI threshold are unspecified.
+		 */
+		config->lpi_timer_default = FIELD_GET(LPI_THRESH_MASK, eeecr);
+	}
 
 	priv->info->mac_port_get_caps(ds, port, config);
 }
@@ -3254,7 +3285,8 @@ mt753x_conduit_state_change(struct dsa_switch *ds,
 	 * forwarded to the numerically smallest CPU port whose conduit
 	 * interface is up.
 	 */
-	if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
+	if (priv->id != ID_MT7530 && priv->id != ID_MT7621 &&
+	    priv->id != ID_EN7528)
 		return;
 
 	mask = BIT(cpu_dp->index);
@@ -3459,6 +3491,16 @@ const struct mt753x_info mt753x_table[] = {
 		.phy_write_c45 = mt7531_ind_c45_phy_write,
 		.mac_port_get_caps = en7581_mac_port_get_caps,
 	},
+	[ID_EN7528] = {
+		.id = ID_EN7528,
+		.pcs_ops = &mt7530_pcs_ops,
+		.sw_setup = mt7988_setup,
+		.phy_read_c22 = mt7531_ind_c22_phy_read,
+		.phy_write_c22 = mt7531_ind_c22_phy_write,
+		.phy_read_c45 = mt7531_ind_c45_phy_read,
+		.phy_write_c45 = mt7531_ind_c45_phy_write,
+		.mac_port_get_caps = en7528_mac_port_get_caps,
+	},
 };
 EXPORT_SYMBOL_GPL(mt753x_table);
 
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
index dd33b0df3419..5f1e841f42c0 100644
--- a/drivers/net/dsa/mt7530.h
+++ b/drivers/net/dsa/mt7530.h
@@ -21,6 +21,7 @@ enum mt753x_id {
 	ID_MT7988 = 3,
 	ID_EN7581 = 4,
 	ID_AN7583 = 5,
+	ID_EN7528 = 6,
 };
 
 #define	NUM_TRGMII_CTRL			5
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next v2 1/2] dt-bindings: net: dsa: mediatek,mt7530: add econet,en7528-switch
From: Ahmed Naseef @ 2026-07-11 11:41 UTC (permalink / raw)
  To: netdev
  Cc: Arınç ÜNAL, Chester A. Unal, David S. Miller,
	Andrew Lunn, AngeloGioacchino Del Regno, Conor Dooley,
	DENG Qingfang, Daniel Golle, Eric Dumazet, Jakub Kicinski,
	Krzysztof Kozlowski, Landen Chao, Matthias Brugger, Paolo Abeni,
	Rob Herring, Russell King, Sean Wang, Vladimir Oltean, devicetree,
	linux-arm-kernel, linux-kernel, linux-mediatek, Ahmed Naseef
In-Reply-To: <cover.1783770059.git.naseefkm@gmail.com>

The EcoNet EN7528 MIPS SoC integrates an MT7530 Gigabit switch,
memory-mapped in the SoC register space like the built-in switches of
the MediaTek MT7988 and Airoha EN7581/AN7583 SoCs. Its four user ports
are connected to integrated Gigabit PHYs and its CPU port is connected
internally to the SoC Ethernet MAC.

Those three switches are MT7531-based, whereas the EN7528 has a genuine
MT7530 switch core (its chip revision register reads 0x7530). The two
generations differ in their register programming - for example the CPU
port is selected through the MT7530-style MFC register rather than the
MT7531 CFC register - so the EN7528 is not compatible with the existing
switch compatibles and cannot fall back to one of them.

Add the econet,en7528-switch compatible, with the same constraints as
the other built-in switches.

Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
---
 .../devicetree/bindings/net/dsa/mediatek,mt7530.yaml         | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/dsa/mediatek,mt7530.yaml b/Documentation/devicetree/bindings/net/dsa/mediatek,mt7530.yaml
index 815a90808901..90b3582b7619 100644
--- a/Documentation/devicetree/bindings/net/dsa/mediatek,mt7530.yaml
+++ b/Documentation/devicetree/bindings/net/dsa/mediatek,mt7530.yaml
@@ -100,6 +100,10 @@ properties:
           Built-in switch of the Airoha AN7583 SoC
         const: airoha,an7583-switch
 
+      - description:
+          Built-in switch of the EcoNet EN7528 SoC
+        const: econet,en7528-switch
+
   reg:
     maxItems: 1
 
@@ -318,6 +322,7 @@ allOf:
             - mediatek,mt7988-switch
             - airoha,en7581-switch
             - airoha,an7583-switch
+            - econet,en7528-switch
     then:
       $ref: "#/$defs/builtin-dsa-port"
       properties:
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next v2 0/2] net: dsa: mt7530: add EcoNet EN7528 built-in switch support
From: Ahmed Naseef @ 2026-07-11 11:40 UTC (permalink / raw)
  To: netdev
  Cc: Arınç ÜNAL, Chester A. Unal, David S. Miller,
	Andrew Lunn, AngeloGioacchino Del Regno, Conor Dooley,
	DENG Qingfang, Daniel Golle, Eric Dumazet, Jakub Kicinski,
	Krzysztof Kozlowski, Landen Chao, Matthias Brugger, Paolo Abeni,
	Rob Herring, Russell King, Sean Wang, Vladimir Oltean, devicetree,
	linux-arm-kernel, linux-kernel, linux-mediatek, Ahmed Naseef

The EcoNet EN7528 is a MIPS SoC whose platform support is already
upstream. It integrates an MT7530 switch, memory-mapped like the built-in
switches of the MediaTek MT7988 and Airoha EN7581/AN7583 SoCs, but with a
true MT7530 core, four Gigabit PHYs on ports 1-4 and a CPU port at a fixed
1000 Mbps full duplex link.

Patch 1 documents the compatible, patch 2 adds the driver support.

Changes in v2:
- patch 1: reword the commit message to explain what distinguishes the
  EN7528 from the other built-in switches and why it cannot fall back to
  one of their compatibles (Conor Dooley).
- patch 2: drop the setup-time EEE advertisement register writes. Instead
  leave lpi_capabilities empty for the EN7528, so phylink disables EEE on
  its PHYs via phy_disable_eee() and rejects enabling it from userspace
  (Andrew Lunn).
- Link to v1:
  https://lore.kernel.org/netdev/cover.1783680864.git.naseefkm@gmail.com/

Ahmed Naseef (2):
  dt-bindings: net: dsa: mediatek,mt7530: add econet,en7528-switch
  net: dsa: mt7530: add EN7528 support

 .../bindings/net/dsa/mediatek,mt7530.yaml     |  5 ++
 drivers/net/dsa/mt7530-mmio.c                 |  1 +
 drivers/net/dsa/mt7530.c                      | 58 ++++++++++++++++---
 drivers/net/dsa/mt7530.h                      |  1 +
 4 files changed, 57 insertions(+), 8 deletions(-)

Range-diff against v1:
1:  68e85666b6df ! 1:  2133035bb22e dt-bindings: net: dsa: mediatek,mt7530: add econet,en7528-switch
    @@ Commit message
         are connected to integrated Gigabit PHYs and its CPU port is connected
         internally to the SoC Ethernet MAC.
     
    +    Those three switches are MT7531-based, whereas the EN7528 has a genuine
    +    MT7530 switch core (its chip revision register reads 0x7530). The two
    +    generations differ in their register programming - for example the CPU
    +    port is selected through the MT7530-style MFC register rather than the
    +    MT7531 CFC register - so the EN7528 is not compatible with the existing
    +    switch compatibles and cannot fall back to one of them.
    +
         Add the econet,en7528-switch compatible, with the same constraints as
         the other built-in switches.
     
2:  1865eaedef97 ! 2:  8c7dfabd860a net: dsa: mt7530: add EN7528 support
    @@ Commit message
         an ID_EN7528 variant bound with the "econet,en7528-switch" compatible,
         reusing mt7988_setup() and the indirect PHY accessors.
     
    -    The switch core, however, is an MT7530 and not an MT7531 derivative: it
    -    has no MT7531 CFC register, and the CPU port to trap frames to is set
    -    through the MT7530-style CPU_EN / CPU_PORT fields of the MFC register, so
    -    add it to the MT7530 handling in mt753x_conduit_state_change(). For the
    +    The switch core, however, is an MT7530 and not an MT7531 derivative: the
    +    CPU port to trap frames to is set through the MT7530-style CPU_EN /
    +    CPU_PORT fields of the MFC register rather than the MT7531 CFC register,
    +    so add it to the MT7530 handling in mt753x_conduit_state_change(). For the
         same reason the MT7530 mirror and force-mode register layouts already
         apply to it as the default of the MT753X_*() macros.
     
    @@ Commit message
         EN7581 switches, whose CPU ports run at 10 Gbps.
     
         The LAN GPHYs advertise EEE by default, but negotiating EEE with some
    -    link partners results in an unstable link with dropped frames. Disable
    -    EEE advertisement on them at setup time, like mt7531_setup() does for the
    -    MT7531 switch PHYs.
    +    link partners results in an unstable link with dropped frames. Leave the
    +    LPI capabilities empty for the EN7528 so that phylink disables EEE on
    +    these PHYs and refuses to enable it from userspace.
     
         Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
     
    @@ drivers/net/dsa/mt7530.c: static void en7581_mac_port_get_caps(struct dsa_switch
      static void
      mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
      		  phy_interface_t interface)
    +@@ drivers/net/dsa/mt7530.c: static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
    + 				    struct phylink_config *config)
    + {
    + 	struct mt7530_priv *priv = ds->priv;
    +-	u32 eeecr;
    + 
    + 	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
    + 
    +-	config->lpi_capabilities = MAC_100FD | MAC_1000FD | MAC_2500FD;
    +-
    +-	eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
    +-	/* tx_lpi_timer should be in microseconds. The time units for
    +-	 * LPI threshold are unspecified.
    ++	/* The EN7528 GPHYs report EEE capability, but negotiating EEE with
    ++	 * common link partners (e.g. Realtek GbE NICs) results in an unstable
    ++	 * link with dropped frames. Leave the LPI capabilities empty so that
    ++	 * phylink disables EEE on these PHYs and refuses to enable it from
    ++	 * userspace.
    + 	 */
    +-	config->lpi_timer_default = FIELD_GET(LPI_THRESH_MASK, eeecr);
    ++	if (priv->id != ID_EN7528) {
    ++		u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
    ++
    ++		config->lpi_capabilities = MAC_100FD | MAC_1000FD | MAC_2500FD;
    ++		/* tx_lpi_timer should be in microseconds. The time units for
    ++		 * LPI threshold are unspecified.
    ++		 */
    ++		config->lpi_timer_default = FIELD_GET(LPI_THRESH_MASK, eeecr);
    ++	}
    + 
    + 	priv->info->mac_port_get_caps(ds, port, config);
    + }
     @@ drivers/net/dsa/mt7530.c: mt753x_conduit_state_change(struct dsa_switch *ds,
      	 * forwarded to the numerically smallest CPU port whose conduit
      	 * interface is up.
    @@ drivers/net/dsa/mt7530.c: mt753x_conduit_state_change(struct dsa_switch *ds,
      		return;
      
      	mask = BIT(cpu_dp->index);
    -@@ drivers/net/dsa/mt7530.c: static int mt753x_setup_tc(struct dsa_switch *ds, int port,
    - 	}
    - }
    - 
    -+/* The EN7528 LAN ports are integrated GPHYs at MDIO addresses 9..12 (switch
    -+ * ports 1..4) on the switch internal MDIO bus, reachable only through the PHY
    -+ * indirect access registers. There is no mdiodev to derive the addresses from.
    -+ */
    -+#define EN7528_GPHY_BASE		9
    -+#define EN7528_NUM_GPHYS		4
    -+
    - static int mt7988_setup(struct dsa_switch *ds)
    - {
    - 	struct mt7530_priv *priv = ds->priv;
    -+	int i;
    - 
    - 	/* Reset the switch */
    - 	reset_control_assert(priv->rstc);
    -@@ drivers/net/dsa/mt7530.c: static int mt7988_setup(struct dsa_switch *ds)
    - 	/* Reset the switch PHYs */
    - 	mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST);
    - 
    -+	/* The EN7528 LAN GPHYs advertise EEE by default, but negotiating EEE
    -+	 * with common link partners (e.g. Realtek GbE NICs) results in an
    -+	 * unstable link with dropped frames. Disable EEE advertisement on
    -+	 * them.
    -+	 */
    -+	if (priv->id == ID_EN7528)
    -+		for (i = EN7528_GPHY_BASE;
    -+		     i < EN7528_GPHY_BASE + EN7528_NUM_GPHYS; i++)
    -+			mt7531_ind_c45_phy_write(priv, i, MDIO_MMD_AN,
    -+						 MDIO_AN_EEE_ADV, 0);
    -+
    - 	return mt7531_setup_common(ds);
    - }
    - 
     @@ drivers/net/dsa/mt7530.c: const struct mt753x_info mt753x_table[] = {
      		.phy_write_c45 = mt7531_ind_c45_phy_write,
      		.mac_port_get_caps = en7581_mac_port_get_caps,

base-commit: fe3e786ef4eb6e47d2901f568a27bd920477bbe9
-- 
2.34.1


^ permalink raw reply

* [PATCH net v2] net:ethernet: mtk_wed: setup WDMA_RING_TX(0) for non-DBDC MT7986
From: Zhi-Jun You @ 2026-07-11 11:21 UTC (permalink / raw)
  To: Felix Fietkau, Lorenzo Bianconi
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Matthias Brugger, AngeloGioacchino Del Regno,
	Sujuan Chen, netdev, linux-kernel, linux-arm-kernel,
	linux-mediatek, Zhi-Jun You

WDMA_RING_TX(0) is required to set MTK_WED_WDMA_RING_TX for WED RX
but on a non-DBDC MT7986 it is never setup because idx is 1.

Setting MTK_WED_WDMA_RING_TX with WDMA_RING_TX(1) is not feasible because
WED still tries to send through WDMA_RING_TX(0). This is verified with
register dump.

Fix this by calling mtk_wed_wdma_tx_ring_setup if wed is v2 and
rx_ring[0] is not allocated and reset tx_wdma[0] if it's already
allocated.

Fixes: 4c5de09eb0d0 ("net: ethernet: mtk_wed: add configure wed wo support")
Signed-off-by: Zhi-Jun You <hujy652@gmail.com>
---
Changes in v2:
- Address warm reset concern raised by AI review.
- Return value concern is skipped until further comment from reviewers.
- Added a comment to explain this special case.
- Link to v1: https://lore.kernel.org/netdev/20260630144831.1109-1-hujy652@gmail.com/
---
 drivers/net/ethernet/mediatek/mtk_wed.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
index 10d9beaae372..3fd704cac02f 100644
--- a/drivers/net/ethernet/mediatek/mtk_wed.c
+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
@@ -2334,6 +2334,13 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
 		if (!dev->rx_wdma[i].desc)
 			mtk_wed_wdma_rx_ring_setup(dev, i, 16, false);
 
+	/*
+	 * non-DBDC MT7986 allocates only rx_ring[1] and tx_wdma[1] during setup
+	 * but tx_wdma[0] is also needed for WED to function.
+	 */
+	if (mtk_wed_is_v2(dev->hw) && !dev->rx_ring[0].desc)
+		mtk_wed_wdma_tx_ring_setup(dev, 0, MTK_WED_WDMA_RING_SIZE, !!dev->tx_wdma[0].desc);
+
 	if (dev->wlan.hw_rro) {
 		for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) {
 			u32 addr = MTK_WED_RRO_MSDU_PG_CTRL0(i) +
-- 
2.47.3


^ permalink raw reply related

* Re: [PATCH net] net: openvswitch: reject oversized nested action attrs
From: patchwork-bot+netdevbpf @ 2026-07-11 11:20 UTC (permalink / raw)
  To: Asim Viladi Oglu Manizada
  Cc: netdev, dev, aconole, echaudro, i.maximets, davem, edumazet, kuba,
	pabeni, horms, stable
In-Reply-To: <20260706094336.38639-1-manizada@pm.me>

Hello:

This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Mon, 06 Jul 2026 09:44:10 +0000 you wrote:
> Open vSwitch stores generated flow actions as nlattrs, whose nla_len
> field is u16. Commit a1e64addf3ff ("net: openvswitch: remove
> misbehaving actions length check") allowed the total sw_flow_actions
> stream to grow beyond 64 KiB, which is valid, but also removed the last
> guard preventing a generated nested action attribute from exceeding
> U16_MAX.
> 
> [...]

Here is the summary with links:
  - [net] net: openvswitch: reject oversized nested action attrs
    https://git.kernel.org/netdev/net/c/3f1f75536668

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply


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