Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net v3] ipv6: flowlabel: enforce per-netns limit for unprivileged callers
From: Willem de Bruijn @ 2026-04-30 13:42 UTC (permalink / raw)
  To: Maoyi Xie, netdev
  Cc: willemb, edumazet, pabeni, kuba, davem, dsahern, kuznet,
	linux-kernel, stable, security
In-Reply-To: <20260430081608.3137365-1-maoyixie.tju@gmail.com>

Maoyi Xie wrote:
> From: Maoyi Xie <maoyi.xie@ntu.edu.sg>
> 
> fl_size, fl_ht and ip6_fl_lock in net/ipv6/ip6_flowlabel.c are file
> scope and shared across netns. mem_check() reads fl_size to decide
> whether to deny non-CAP_NET_ADMIN callers; capable() runs against
> init_user_ns, so an unprivileged user in any non-init userns can
> push fl_size past FL_MAX_SIZE - FL_MAX_SIZE/4 and starve every
> other unprivileged userns on the host.
> 
> Add struct netns_ipv6::flowlabel_count, bumped and decremented next
> to fl_size in fl_intern, ip6_fl_gc and ip6_fl_purge. Place it near
> ipmr_seq rather than next to flowlabel_has_excl: flowlabel_has_excl
> is read on every flowlabel lookup, and a counter written on every
> alloc would dirty its cacheline.

The cacheline point is more about truly ipv6 hot path fields. This
entire explicit flowlabel mgmt is not that.

Did this new location fill a 4B hole? (on 64b builds)

> 
> mem_check() folds an extra FL_MAX_SIZE/8 ceiling into the existing
> non-CAP_NET_ADMIN conditional.
> 
> Bump FL_MAX_SIZE from 4096 to 8192. It has been 4096 since the file
> was added; machines and connection counts have grown. The new
> per-netns ceiling is then 1024 flowlabels, half of FL_MAX_SIZE/4.
> 
> CAP_NET_ADMIN against init_user_ns still bypasses both caps.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Suggested-by: Willem de Bruijn <willemb@google.com>
> Cc: stable@vger.kernel.org # v5.15+
> Signed-off-by: Maoyi Xie <maoyi.xie@ntu.edu.sg>
> ---
> v3 (this submission, netdev): addressed Willem's review on the
>     private security@ thread:
>     - merged the FL_MAX_SIZE doubling into this patch
>     - dropped the test data block from the commit body
>     - moved flowlabel_count to a 4-byte hole next to ipmr_seq, off
>       the flowlabel_has_excl cacheline
>     - inlined fl->fl_net in ip6_fl_gc (no local var)
> v2: per-netns counter + cap, sent to security@ as a 2-patch series
> v1: fix-shape sketch in original disclosure
> 
>  include/net/netns/ipv6.h |  1 +
>  net/ipv6/ip6_flowlabel.c | 10 ++++++++--
>  2 files changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
> index 34bdb1308..329482373 100644
> --- a/include/net/netns/ipv6.h
> +++ b/include/net/netns/ipv6.h
> @@ -119,6 +119,7 @@ struct netns_ipv6 {
>  	struct fib_notifier_ops	*notifier_ops;
>  	struct fib_notifier_ops	*ip6mr_notifier_ops;
>  	unsigned int ipmr_seq; /* protected by rtnl_mutex */
> +	atomic_t		flowlabel_count;
>  	struct {
>  		struct hlist_head head;
>  		spinlock_t	lock;
> diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
> index c92f98c6f..4a5219356 100644
> --- a/net/ipv6/ip6_flowlabel.c
> +++ b/net/ipv6/ip6_flowlabel.c
> @@ -36,7 +36,7 @@
>  /* FL hash table */
>  
>  #define FL_MAX_PER_SOCK	32
> -#define FL_MAX_SIZE	4096
> +#define FL_MAX_SIZE	8192
>  #define FL_HASH_MASK	255
>  #define FL_HASH(l)	(ntohl(l)&FL_HASH_MASK)
>  
> @@ -162,6 +162,7 @@ static void ip6_fl_gc(struct timer_list *unused)
>  				ttd = fl->expires;
>  				if (time_after_eq(now, ttd)) {
>  					*flp = fl->next;
> +					atomic_dec(&fl->fl_net->ipv6.flowlabel_count);
>  					fl_free(fl);
>  					atomic_dec(&fl_size);

nit: can you place these consistently immediately after the fl_size
operations, to make clear that they are paired.

>  					continue;
> @@ -195,6 +196,7 @@ static void __net_exit ip6_fl_purge(struct net *net)
>  			if (net_eq(fl->fl_net, net) &&
>  			    atomic_read(&fl->users) == 0) {
>  				*flp = fl->next;
> +				atomic_dec(&net->ipv6.flowlabel_count);
>  				fl_free(fl);
>  				atomic_dec(&fl_size);
>  				continue;
> @@ -245,6 +247,7 @@ static struct ip6_flowlabel *fl_intern(struct net *net,
>  	fl->next = fl_ht[FL_HASH(fl->label)];
>  	rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl);
>  	atomic_inc(&fl_size);
> +	atomic_inc(&net->ipv6.flowlabel_count);
>  	spin_unlock_bh(&ip6_fl_lock);
>  	rcu_read_unlock();
>  	return NULL;
> @@ -464,6 +467,7 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
>  
>  static int mem_check(struct sock *sk)
>  {
> +	struct net *net = sock_net(sk);
>  	int room = FL_MAX_SIZE - atomic_read(&fl_size);
>  	struct ipv6_fl_socklist *sfl;
>  	int count = 0;
> @@ -478,7 +482,9 @@ static int mem_check(struct sock *sk)
>  
>  	if (room <= 0 ||
>  	    ((count >= FL_MAX_PER_SOCK ||
> -	      (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
> +	      (count > 0 && room < FL_MAX_SIZE/2) ||
> +	      room < FL_MAX_SIZE/4 ||
> +	      atomic_read(&net->ipv6.flowlabel_count) >= FL_MAX_SIZE/8) &&
>  	     !capable(CAP_NET_ADMIN)))
>  		return -ENOBUFS;
>  
> -- 
> 2.34.1
> 



^ permalink raw reply

* Re: [PATCH net 2/2] ovpn: ensure gro_cells_receive() is invoked with BH disabled
From: Antonio Quartulli @ 2026-04-30 13:40 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, Jakub Kicinski, ralf, Sabrina Dubroca, Paolo Abeni,
	Andrew Lunn, David S. Miller
In-Reply-To: <CANn89i+kGAXWEmgSwrUyRhh7kk6r3FjEza9mjDXq4AjwbC5DZA@mail.gmail.com>

Hi Eric,

On 30/04/2026 15:37, Eric Dumazet wrote:
> On Thu, Apr 30, 2026 at 6:28 AM Antonio Quartulli <antonio@openvpn.net> wrote:
>>
>> Hi Jakub,
>>
>> sashiko came back with an interesting review of the per-cpu stats update
>> in the surrounding code.
>>
>> As far as I can tell its explanation makes sense, but I am no per-cpu
>> expert.
>>
>> IIUC it basically says that if gro_cells_receive() is invoked with
>> bottom halves disabled, the following dev_dstats_rx_add() should be too
>> to avoid deadlocks and corruptions.
>>
>> See below:
>>
>> On 29/04/2026 14:01, Antonio Quartulli wrote:
>>> diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
>>> index d92bb87be2b2..c0fdb9504241 100644
>>> --- a/drivers/net/ovpn/io.c
>>> +++ b/drivers/net/ovpn/io.c
>>> @@ -91,7 +91,13 @@ static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb)
>>>
>>>        /* cause packet to be "received" by the interface */
>>>        pkt_len = skb->len;
>>> +     /* we may get here in process context in case of TCP connections,
>>> +      * therefore we have to disable BHs to ensure gro_cells_receive()
>>> +      * doesn't enter deadlock
>>> +      */
>>> +     local_bh_disable();
>>>        ret = gro_cells_receive(&peer->ovpn->gro_cells, skb);
>>> +     local_bh_enable();
>>>        if (likely(ret == NET_RX_SUCCESS)) {
>>>                /* update RX stats with the size of decrypted packet */
>>>                ovpn_peer_stats_increment_rx(&peer->vpn_stats, pkt_len);
>>   >>             dev_dstats_rx_add(peer->ovpn->dev...) << not visible in the diff,
>> but it's here
>>
>> from sashiko:
>>
>> Can this lead to seqcount deadlocks and stat corruption since
>> dev_dstats_rx_add() is executed with preemption and bottom-halves enabled?
>> dev_dstats_rx_add() operates on per-CPU statistics and protects 64-bit
>> updates with a sequence counter (u64_stats_update_begin()). When called
>> from process context with BH enabled, on 64-bit systems, this_cpu_ptr()
>> is evaluated with preemption enabled. If the task is migrated mid-update,
>> it risks cross-CPU stat corruption.
>> On 32-bit systems, u64_stats_update_begin() disables preemption but not
>> bottom-halves. If a softirq (e.g., a concurrent UDP packet reception)
>> interrupts the process context and calls dev_dstats_rx_add() for the same
>> interface, it will re-enter the seqcount writer lock on the exact same CPU.
>> This corrupts the sequence counter, causing readers to see an unlocked
>> sequence during active writes, leading to torn reads and corrupted stats.
>> Should local_bh_enable() be moved after the statistics updates to ensure
>> the entire per-CPU update is atomic with respect to softirqs?
>>
>>
>> Do you have an opinion?
> 
> Sashiko suggestion seems good to me.

But am I right saying that this bug existed before and it is not 
introduced by this patch?

A concurrent softirq (UDP RX pkt) could already trigger this problem 
before we introduced the local_bh_disable/enable() calls, right?


Regards,


> 
> Proper Fixes: tag would be:
> 
> Fixes: ab66abbc769b ("ovpn: implement basic RX path (UDP)")

-- 
Antonio Quartulli
OpenVPN Inc.


^ permalink raw reply

* Re: [PATCH net 2/2] ovpn: ensure gro_cells_receive() is invoked with BH disabled
From: Eric Dumazet @ 2026-04-30 13:37 UTC (permalink / raw)
  To: Antonio Quartulli
  Cc: netdev, Jakub Kicinski, ralf, Sabrina Dubroca, Paolo Abeni,
	Andrew Lunn, David S. Miller
In-Reply-To: <5b281966-c278-46d5-ade0-9dc24175de6e@openvpn.net>

On Thu, Apr 30, 2026 at 6:28 AM Antonio Quartulli <antonio@openvpn.net> wrote:
>
> Hi Jakub,
>
> sashiko came back with an interesting review of the per-cpu stats update
> in the surrounding code.
>
> As far as I can tell its explanation makes sense, but I am no per-cpu
> expert.
>
> IIUC it basically says that if gro_cells_receive() is invoked with
> bottom halves disabled, the following dev_dstats_rx_add() should be too
> to avoid deadlocks and corruptions.
>
> See below:
>
> On 29/04/2026 14:01, Antonio Quartulli wrote:
> > diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
> > index d92bb87be2b2..c0fdb9504241 100644
> > --- a/drivers/net/ovpn/io.c
> > +++ b/drivers/net/ovpn/io.c
> > @@ -91,7 +91,13 @@ static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb)
> >
> >       /* cause packet to be "received" by the interface */
> >       pkt_len = skb->len;
> > +     /* we may get here in process context in case of TCP connections,
> > +      * therefore we have to disable BHs to ensure gro_cells_receive()
> > +      * doesn't enter deadlock
> > +      */
> > +     local_bh_disable();
> >       ret = gro_cells_receive(&peer->ovpn->gro_cells, skb);
> > +     local_bh_enable();
> >       if (likely(ret == NET_RX_SUCCESS)) {
> >               /* update RX stats with the size of decrypted packet */
> >               ovpn_peer_stats_increment_rx(&peer->vpn_stats, pkt_len);
>  >>             dev_dstats_rx_add(peer->ovpn->dev...) << not visible in the diff,
> but it's here
>
> from sashiko:
>
> Can this lead to seqcount deadlocks and stat corruption since
> dev_dstats_rx_add() is executed with preemption and bottom-halves enabled?
> dev_dstats_rx_add() operates on per-CPU statistics and protects 64-bit
> updates with a sequence counter (u64_stats_update_begin()). When called
> from process context with BH enabled, on 64-bit systems, this_cpu_ptr()
> is evaluated with preemption enabled. If the task is migrated mid-update,
> it risks cross-CPU stat corruption.
> On 32-bit systems, u64_stats_update_begin() disables preemption but not
> bottom-halves. If a softirq (e.g., a concurrent UDP packet reception)
> interrupts the process context and calls dev_dstats_rx_add() for the same
> interface, it will re-enter the seqcount writer lock on the exact same CPU.
> This corrupts the sequence counter, causing readers to see an unlocked
> sequence during active writes, leading to torn reads and corrupted stats.
> Should local_bh_enable() be moved after the statistics updates to ensure
> the entire per-CPU update is atomic with respect to softirqs?
>
>
> Do you have an opinion?

Sashiko suggestion seems good to me.

Proper Fixes: tag would be:

Fixes: ab66abbc769b ("ovpn: implement basic RX path (UDP)")

^ permalink raw reply

* Re: [PATCH net-next] ip6mr: plug drop_reason to ip6mr_cache_report()
From: Ido Schimmel @ 2026-04-30 13:37 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, David Ahern,
	Simon Horman, netdev, eric.dumazet
In-Reply-To: <20260430074004.4133602-1-edumazet@google.com>

On Thu, Apr 30, 2026 at 07:40:04AM +0000, Eric Dumazet wrote:
> - Check mrt->mroute_sk earlier in the function.
> 
> - Use sock_queue_rcv_skb_reason() instead of sock_queue_rcv_skb().
> - Use sk_skb_reason_drop() instead of kfree_skb().
>   Note that we return -ENOMEM if sock_queue_rcv_skb_reason() failed,
>   as the precise error is not really needed for callers.
> 
> - Remove one net_warn_ratelimited().
> 
> Signed-off-by: Eric Dumazet <edumazet@google.com>

Reviewed-by: Ido Schimmel <idosch@nvidia.com>

^ permalink raw reply

* Re: [RFC PATCH] xprtrdma: Move long delayed work on system_dfl_long_wq
From: Chuck Lever @ 2026-04-30 13:35 UTC (permalink / raw)
  To: Marco Crivellari, linux-kernel, linux-nfs, netdev
  Cc: Tejun Heo, Lai Jiangshan, Frederic Weisbecker,
	Sebastian Andrzej Siewior, Michal Hocko, Trond Myklebust,
	Anna Schumaker, Chuck Lever, Jeff Layton, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Tom Talpey, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman
In-Reply-To: <20260430085412.96961-1-marco.crivellari@suse.com>


On Thu, Apr 30, 2026, at 4:54 AM, Marco Crivellari wrote:
> Currently the code enqueue work items using {queue|mod}_delayed_work(),
> using system_long_wq. This workqueue should be used when long works are
> expected, but it is a per-cpu workqueue.
>
> This is important because queue_delayed_work() queue the work using:
>
>    queue_delayed_work_on(WORK_CPU_UNBOUND, ...);
>
> Note that WORK_CPU_UNBOUND = NR_CPUS.
>
> This would end up calling __queue_delayed_work() that does:
>
>     if (housekeeping_enabled(HK_TYPE_TIMER)) {
>     //      [....]
>     } else {
>             if (likely(cpu == WORK_CPU_UNBOUND))
>                     add_timer_global(timer);
>             else
>                     add_timer_on(timer, cpu);
>     }
>
> So when cpu == WORK_CPU_UNBOUND the timer is global and is
> not using a specific CPU. Later, when __queue_work() is called:
>
>     if (req_cpu == WORK_CPU_UNBOUND) {
>             if (wq->flags & WQ_UNBOUND)
>                     cpu = wq_select_unbound_cpu(raw_smp_processor_id());
>             else
>                     cpu = raw_smp_processor_id();
>     }
>
> Because the wq is not unbound, it takes the CPU where the timer
> fired and enqueue the work on that CPU.
> The consequence of all of this is that the work can run anywhere,
> depending on where the timer fired.
>
> Recently, a new unbound workqueue specific for long running work has
> been added:
>
>    c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works")
>
> So change system_long_wq with system_dfl_long_wq so that the work may
> benefit from scheduler task placement.

The patch description confuses me.

The message ends with "the work can run anywhere, depending on where
the timer fired." Read literally, "can run anywhere" sounds like a
feature, not a bug — and the proposed fix (WQ_UNBOUND) also lets it
run anywhere, just via a different selection path. Without a sentence
saying "and that anywhere includes isolated CPUs, which we don't want,"
the reader is left to fill in the gap.                                

So, could the commit message lead with the motivation? My guess is that
this is about respecting HK_TYPE_TIMER housekeeping on isolated systems,
which system_long_wq cannot do because its per-CPU pool ignores the
housekeeping mask once the global timer fires. If that is the case,
please say so directly and the mechanism trace becomes a supporting
argument rather than the whole argument.


-- 
Chuck Lever

^ permalink raw reply

* Re: [PATCH 5.15.y] batman-adv: hold claim backbone gateways by reference
From: Sasha Levin @ 2026-04-30 13:32 UTC (permalink / raw)
  To: Sven Eckelmann
  Cc: stable, Haoze Xie, Robert Garcia, b.a.t.m.a.n, Simon Wunderlich,
	Yifan Wu, Juefei Pu, Yuan Tan, Xin Liu, Ao Zhou, Marek Lindner,
	Antonio Quartulli, David S . Miller, Jakub Kicinski, Andrew Lunn,
	netdev, linux-kernel
In-Reply-To: <3609597.QJadu78ljV@ripper>

On Thu, Apr 30, 2026 at 09:40:34AM +0200, Sven Eckelmann wrote:
>On Thursday, 30 April 2026 09:38:05 CEST Sven Eckelmann wrote:
>> Sasha Levin <sashal@kernel.org> picked it up for 5.15.y (on Sun, 19 Apr 2026
>> 21:13:58 -0400, MsgId 20260419195610.batman-adv-5.15@kernel.org).
>> Yes, it was not yet published or 5.15 - so maybe fell through the cracks.
>
>https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git/commit/?h=queue/5.15&id=6fd37208adf6771125b59e1ae0452561024be4e2

Yup, it's still in the queue.

-- 
Thanks,
Sasha

^ permalink raw reply

* [PATCH net-next v6 3/3] selftests: drv-net: convert so_txtime to drv-net
From: Willem de Bruijn @ 2026-04-30 13:28 UTC (permalink / raw)
  To: netdev
  Cc: davem, kuba, edumazet, pabeni, horms, linux-kselftest, shuah,
	Willem de Bruijn
In-Reply-To: <20260430132820.1944517-1-willemdebruijn.kernel@gmail.com>

From: Willem de Bruijn <willemb@google.com>

In preparation for extending to pacing hardware offload, convert the
so_txtime.sh test to a drv-net test that can be run against netdevsim
and real hardware.

Also update so_txtime.c to not exit on first failure, but run to
completion and report exit code there. This helps with debugging
unexpected results, especially when processing multiple packets,
as happens in the "reverse_order" testcase.

Signed-off-by: Willem de Bruijn <willemb@google.com>

----

v5 -> v6

- fix order in tools/testing/selftests/drivers/net/config

v5: https://lore.kernel.org/netdev/20260427201640.294694-4-willemdebruijn.kernel@gmail.com/

v4 -> v5

- move qdisc setup/restore into each test
- add tc to utils.py (separate patch)
- test expected failure (separate patch)
- fix pylint
- convert fail to pass for timing errors if KSFT_MACHINE_SLOW
  (cmd does not special case KSFT_SKIP process returncode yet)

Responses to sashiko review

- The test converts per packet failure to errors, to continue
  testing other packets, but other error() cases are not in scope.
- The test starts sender and receiver at an absolute future time,
  like the original test. This assumes ~msec scale sync'ed clocks.
- The tc qdisc replace command works fine with noqueue. Tested
  manually.

v4: https://lore.kernel.org/netdev/20260409164238.661091-1-willemdebruijn.kernel@gmail.com/

v3 -> v4

- restore original qdisc after test
- drop unnecessary underscore in tap test names

v3: https://lore.kernel.org/netdev/20260406025020.1636895-1-willemdebruijn.kernel@gmail.com/

v2 -> v3

- Makefile: so_txtime from YNL_GEN_FILES to TEST_GEN_FILES (Sashiko, NIPA)

v2: https://lore.kernel.org/netdev/20260405014458.1038165-1-willemdebruijn.kernel@gmail.com/

v1 -> v2
- move so_txtime.c for net/lib to drivers/net (Jakub)
- fix drivers/net/config order (Jakub)
- detect passing when failure is expected (Jakub, Sashiko)
- pass pylint --disable=R (Jakub)
- only call ksft_run once (Jakub)
- do not sleep if waiting time is negative (Sashiko)
- add \n when converting error() to fprintf() (Sashiko)
- 4 space indentation, instead of 2 space
- increase sync delay from 100 to 200ms, to fix rare vng flakes

v1: https://lore.kernel.org/netdev/20260403175047.152646-1-willemdebruijn.kernel@gmail.com/
---
 .../testing/selftests/drivers/net/.gitignore  |   1 +
 tools/testing/selftests/drivers/net/Makefile  |   2 +
 tools/testing/selftests/drivers/net/config    |   2 +
 .../selftests/{ => drivers}/net/so_txtime.c   |  25 +++-
 .../selftests/drivers/net/so_txtime.py        |  95 +++++++++++++++
 tools/testing/selftests/net/.gitignore        |   1 -
 tools/testing/selftests/net/Makefile          |   2 -
 tools/testing/selftests/net/so_txtime.sh      | 110 ------------------
 8 files changed, 120 insertions(+), 118 deletions(-)
 rename tools/testing/selftests/{ => drivers}/net/so_txtime.c (96%)
 create mode 100755 tools/testing/selftests/drivers/net/so_txtime.py
 delete mode 100755 tools/testing/selftests/net/so_txtime.sh

diff --git a/tools/testing/selftests/drivers/net/.gitignore b/tools/testing/selftests/drivers/net/.gitignore
index 585ecb4d5dc4..e5314ce4bb2d 100644
--- a/tools/testing/selftests/drivers/net/.gitignore
+++ b/tools/testing/selftests/drivers/net/.gitignore
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 napi_id_helper
 psp_responder
+so_txtime
diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index b72080c6d06b..d5bf4cb638a8 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -7,6 +7,7 @@ TEST_INCLUDES := $(wildcard lib/py/*.py) \
 
 TEST_GEN_FILES := \
 	napi_id_helper \
+	so_txtime \
 # end of TEST_GEN_FILES
 
 TEST_PROGS := \
@@ -21,6 +22,7 @@ TEST_PROGS := \
 	queues.py \
 	ring_reconfig.py \
 	shaper.py \
+	so_txtime.py \
 	stats.py \
 	xdp.py \
 # end of TEST_PROGS
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index fd16994366f4..2309109a94ec 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -8,5 +8,7 @@ CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_NETCONSOLE_EXTENDED_LOG=y
 CONFIG_NETDEVSIM=m
+CONFIG_NET_SCH_ETF=m
+CONFIG_NET_SCH_FQ=m
 CONFIG_VLAN_8021Q=m
 CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/net/so_txtime.c b/tools/testing/selftests/drivers/net/so_txtime.c
similarity index 96%
rename from tools/testing/selftests/net/so_txtime.c
rename to tools/testing/selftests/drivers/net/so_txtime.c
index b76df1efc2ef..b6930883569b 100644
--- a/tools/testing/selftests/net/so_txtime.c
+++ b/tools/testing/selftests/drivers/net/so_txtime.c
@@ -33,6 +33,8 @@
 #include <unistd.h>
 #include <poll.h>
 
+#include "kselftest.h"
+
 static int	cfg_clockid	= CLOCK_TAI;
 static uint16_t	cfg_port	= 8000;
 static int	cfg_variance_us	= 4000;
@@ -43,6 +45,8 @@ static bool	cfg_rx;
 static uint64_t glob_tstart;
 static uint64_t tdeliver_max;
 
+static int errors;
+
 /* encode one timed transmission (of a 1B payload) */
 struct timed_send {
 	char	data;
@@ -131,13 +135,15 @@ static void do_recv_one(int fdr, struct timed_send *ts)
 	fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n",
 			rbuf[0], (long long)tstop, (long long)texpect);
 
-	if (rbuf[0] != ts->data)
-		error(1, 0, "payload mismatch. expected %c", ts->data);
+	if (rbuf[0] != ts->data) {
+		fprintf(stderr, "payload mismatch. expected %c\n", ts->data);
+		errors++;
+	}
 
 	if (llabs(tstop - texpect) > cfg_variance_us) {
 		fprintf(stderr, "exceeds variance (%d us)\n", cfg_variance_us);
 		if (!getenv("KSFT_MACHINE_SLOW"))
-			exit(1);
+			errors++;
 	}
 }
 
@@ -255,8 +261,12 @@ static void start_time_wait(void)
 		return;
 
 	now = gettime_ns(CLOCK_REALTIME);
-	if (cfg_start_time_ns < now)
+	if (cfg_start_time_ns < now) {
+		fprintf(stderr, "FAIL: start time already passed\n");
+		if (!getenv("KSFT_MACHINE_SLOW"))
+			errors++;
 		return;
+	}
 
 	err = usleep((cfg_start_time_ns - now) / 1000);
 	if (err)
@@ -513,5 +523,10 @@ int main(int argc, char **argv)
 	else
 		do_test_tx((void *)&cfg_src_addr, cfg_alen);
 
-	return 0;
+	if (errors) {
+		fprintf(stderr, "FAIL: %d errors\n", errors);
+		return KSFT_FAIL;
+	}
+
+	return KSFT_PASS;
 }
diff --git a/tools/testing/selftests/drivers/net/so_txtime.py b/tools/testing/selftests/drivers/net/so_txtime.py
new file mode 100755
index 000000000000..c2bbeea45a61
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/so_txtime.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""Regression tests for the SO_TXTIME interface.
+
+Test delivery time in FQ and ETF qdiscs.
+"""
+
+import time
+
+from lib.py import ksft_exit, ksft_run, ksft_variants
+from lib.py import KsftNamedVariant, KsftSkipEx
+from lib.py import NetDrvEpEnv, bkg, cmd, defer, tc
+
+
+def test_so_txtime(cfg, clockid, ipver, args_tx, args_rx, test_failure):
+    """Main function. Run so_txtime as sender and receiver."""
+    bin_path = cfg.test_dir / "so_txtime"
+
+    tstart = time.time_ns() + 200_000_000
+
+    cmd_addr = f"-S {cfg.addr_v[ipver]} -D {cfg.remote_addr_v[ipver]}"
+    cmd_base = f"{bin_path} -{ipver} -c {clockid} -t {tstart} {cmd_addr}"
+    cmd_rx = f"{cmd_base} {args_rx} -r"
+    cmd_tx = f"{cmd_base} {args_tx}"
+
+    with bkg(cmd_rx, host=cfg.remote, fail=test_failure, exit_wait=True):
+        cmd(cmd_tx)
+
+
+def _qdisc_setup(ifname, qdisc, optargs=""):
+    """Replace root qdisc. Restore the original after the test.
+
+    If the original is mq, children will be of type default_qdisc.
+    """
+    orig = tc(f"qdisc show dev {ifname} root", json=True)[0].get("kind", None)
+    defer(tc, f"qdisc replace dev {ifname} root {orig}")
+    tc(f"qdisc replace dev {ifname} root {qdisc} {optargs}")
+
+
+def _test_variants_mono():
+    for ipver in ["4", "6"]:
+        for testcase in [
+            ["no_delay", "a,-1", "a,-1"],
+            ["zero_delay", "a,0", "a,0"],
+            ["one_pkt", "a,10", "a,10"],
+            ["in_order", "a,10,b,20", "a,10,b,20"],
+            ["reverse_order", "a,20,b,10", "b,20,a,20"],
+        ]:
+            name = f"v{ipver}_{testcase[0]}"
+            yield KsftNamedVariant(name, ipver, testcase[1], testcase[2])
+
+
+@ksft_variants(_test_variants_mono())
+def test_so_txtime_mono(cfg, ipver, args_tx, args_rx):
+    """Run all variants of monotonic (fq) tests."""
+    _qdisc_setup(cfg.ifname, "fq")
+    test_so_txtime(cfg, "mono", ipver, args_tx, args_rx, True)
+
+
+def _test_variants_etf():
+    for ipver in ["4", "6"]:
+        for testcase in [
+            ["no_delay", "a,-1", "a,-1", 'verify_failed'],
+            ["zero_delay", "a,0", "a,0", 'verify_failed'],
+            ["one_pkt", "a,10", "a,10", True],
+            ["in_order", "a,10,b,20", "a,10,b,20", True],
+            ["reverse_order", "a,20,b,10", "b,10,a,20", True],
+        ]:
+            name = f"v{ipver}_{testcase[0]}"
+            yield KsftNamedVariant(
+                name, ipver, testcase[1], testcase[2], testcase[3]
+            )
+
+
+@ksft_variants(_test_variants_etf())
+def test_so_txtime_etf(cfg, ipver, args_tx, args_rx, expect_fail):
+    """Run all variants of etf tests."""
+    try:
+        _qdisc_setup(cfg.ifname, "etf", "clockid CLOCK_TAI delta 400000")
+    except Exception as e:
+        raise KsftSkipEx("tc does not support qdisc etf. skipping") from e
+
+    test_so_txtime(cfg, "tai", ipver, args_tx, args_rx, expect_fail)
+
+
+def main() -> None:
+    """Boilerplate ksft main."""
+    with NetDrvEpEnv(__file__) as cfg:
+        ksft_run([test_so_txtime_mono, test_so_txtime_etf], args=(cfg,))
+    ksft_exit()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 97ad4d551d44..02ad4c99a2b4 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -40,7 +40,6 @@ skf_net_off
 socket
 so_incoming_cpu
 so_netns_cookie
-so_txtime
 so_rcv_listener
 stress_reuseport_listen
 tap
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index a275ed584026..d53670c0d7a2 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -83,7 +83,6 @@ TEST_PROGS := \
 	rxtimestamp.sh \
 	sctp_vrf.sh \
 	skf_net_off.sh \
-	so_txtime.sh \
 	srv6_end_dt46_l3vpn_test.sh \
 	srv6_end_dt4_l3vpn_test.sh \
 	srv6_end_dt6_l3vpn_test.sh \
@@ -157,7 +156,6 @@ TEST_GEN_FILES := \
 	skf_net_off \
 	so_netns_cookie \
 	so_rcv_listener \
-	so_txtime \
 	socket \
 	stress_reuseport_listen \
 	tcp_fastopen_backup_key \
diff --git a/tools/testing/selftests/net/so_txtime.sh b/tools/testing/selftests/net/so_txtime.sh
deleted file mode 100755
index 5e861ad32a42..000000000000
--- a/tools/testing/selftests/net/so_txtime.sh
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-#
-# Regression tests for the SO_TXTIME interface
-
-set -e
-
-readonly ksft_skip=4
-readonly DEV="veth0"
-readonly BIN="./so_txtime"
-
-readonly RAND="$(mktemp -u XXXXXX)"
-readonly NSPREFIX="ns-${RAND}"
-readonly NS1="${NSPREFIX}1"
-readonly NS2="${NSPREFIX}2"
-
-readonly SADDR4='192.168.1.1'
-readonly DADDR4='192.168.1.2'
-readonly SADDR6='fd::1'
-readonly DADDR6='fd::2'
-
-cleanup() {
-	ip netns del "${NS2}"
-	ip netns del "${NS1}"
-}
-
-trap cleanup EXIT
-
-# Create virtual ethernet pair between network namespaces
-ip netns add "${NS1}"
-ip netns add "${NS2}"
-
-ip link add "${DEV}" netns "${NS1}" type veth \
-  peer name "${DEV}" netns "${NS2}"
-
-# Bring the devices up
-ip -netns "${NS1}" link set "${DEV}" up
-ip -netns "${NS2}" link set "${DEV}" up
-
-# Set fixed MAC addresses on the devices
-ip -netns "${NS1}" link set dev "${DEV}" address 02:02:02:02:02:02
-ip -netns "${NS2}" link set dev "${DEV}" address 06:06:06:06:06:06
-
-# Add fixed IP addresses to the devices
-ip -netns "${NS1}" addr add 192.168.1.1/24 dev "${DEV}"
-ip -netns "${NS2}" addr add 192.168.1.2/24 dev "${DEV}"
-ip -netns "${NS1}" addr add       fd::1/64 dev "${DEV}" nodad
-ip -netns "${NS2}" addr add       fd::2/64 dev "${DEV}" nodad
-
-run_test() {
-	local readonly IP="$1"
-	local readonly CLOCK="$2"
-	local readonly TXARGS="$3"
-	local readonly RXARGS="$4"
-
-	if [[ "${IP}" == "4" ]]; then
-		local readonly SADDR="${SADDR4}"
-		local readonly DADDR="${DADDR4}"
-	elif [[ "${IP}" == "6" ]]; then
-		local readonly SADDR="${SADDR6}"
-		local readonly DADDR="${DADDR6}"
-	else
-		echo "Invalid IP version ${IP}"
-		exit 1
-	fi
-
-	local readonly START="$(date +%s%N --date="+ 0.1 seconds")"
-
-	ip netns exec "${NS2}" "${BIN}" -"${IP}" -c "${CLOCK}" -t "${START}" -S "${SADDR}" -D "${DADDR}" "${RXARGS}" -r &
-	ip netns exec "${NS1}" "${BIN}" -"${IP}" -c "${CLOCK}" -t "${START}" -S "${SADDR}" -D "${DADDR}" "${TXARGS}"
-	wait "$!"
-}
-
-do_test() {
-	run_test $@
-	[ $? -ne 0 ] && ret=1
-}
-
-do_fail_test() {
-	run_test $@
-	[ $? -eq 0 ] && ret=1
-}
-
-ip netns exec "${NS1}" tc qdisc add dev "${DEV}" root fq
-set +e
-ret=0
-do_test 4 mono a,-1 a,-1
-do_test 6 mono a,0 a,0
-do_test 6 mono a,10 a,10
-do_test 4 mono a,10,b,20 a,10,b,20
-do_test 6 mono a,20,b,10 b,20,a,20
-
-if ip netns exec "${NS1}" tc qdisc replace dev "${DEV}" root etf clockid CLOCK_TAI delta 400000; then
-	do_fail_test 4 tai a,-1 a,-1
-	do_fail_test 6 tai a,0 a,0
-	do_test 6 tai a,10 a,10
-	do_test 4 tai a,10,b,20 a,10,b,20
-	do_test 6 tai a,20,b,10 b,10,a,20
-else
-	echo "tc ($(tc -V)) does not support qdisc etf. skipping"
-	[ $ret -eq 0 ] && ret=$ksft_skip
-fi
-
-if [ $ret -eq 0 ]; then
-	echo OK. All tests passed
-elif [[ $ret -ne $ksft_skip && -n "$KSFT_MACHINE_SLOW" ]]; then
-	echo "Ignoring errors due to slow environment" 1>&2
-	ret=0
-fi
-exit $ret
-- 
2.54.0.545.g6539524ca2-goog


^ permalink raw reply related

* [PATCH net-next v6 2/3] selftests: net: py: add tc utility
From: Willem de Bruijn @ 2026-04-30 13:28 UTC (permalink / raw)
  To: netdev
  Cc: davem, kuba, edumazet, pabeni, horms, linux-kselftest, shuah,
	Willem de Bruijn
In-Reply-To: <20260430132820.1944517-1-willemdebruijn.kernel@gmail.com>

From: Willem de Bruijn <willemb@google.com>

Add a wrapper similar to existing ip, ethtool, ... commands.

Tc takes a slightly different syntax. Account for that.

The first user is the next patch in this series, converting so_txtime
to drv-net. Pacing offload is supported by selected qdiscs only.

Signed-off-by: Willem de Bruijn <willemb@google.com>
---
 .../testing/selftests/drivers/net/lib/py/__init__.py |  5 +++--
 tools/testing/selftests/net/lib/py/__init__.py       |  4 ++--
 tools/testing/selftests/net/lib/py/utils.py          | 12 +++++++++++-
 3 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/drivers/net/lib/py/__init__.py b/tools/testing/selftests/drivers/net/lib/py/__init__.py
index 2b5ec0505672..09aac4ce67bc 100644
--- a/tools/testing/selftests/drivers/net/lib/py/__init__.py
+++ b/tools/testing/selftests/drivers/net/lib/py/__init__.py
@@ -23,7 +23,8 @@ try:
         NlError, RtnlFamily, DevlinkFamily, PSPFamily, Netlink
     from net.lib.py import CmdExitFailure
     from net.lib.py import bkg, cmd, bpftool, bpftrace, defer, ethtool, \
-        fd_read_timeout, ip, rand_port, rand_ports, wait_port_listen, wait_file
+        fd_read_timeout, ip, rand_port, rand_ports, tc, wait_port_listen, \
+        wait_file
     from net.lib.py import bpf_map_set, bpf_map_dump, bpf_prog_map_ids
     from net.lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx
     from net.lib.py import ksft_disruptive, ksft_exit, ksft_pr, ksft_run, \
@@ -36,7 +37,7 @@ try:
                "NlError", "RtnlFamily", "DevlinkFamily", "PSPFamily", "Netlink",
                "CmdExitFailure",
                "bkg", "cmd", "bpftool", "bpftrace", "defer", "ethtool",
-               "fd_read_timeout", "ip", "rand_port", "rand_ports",
+               "fd_read_timeout", "ip", "rand_port", "rand_ports", "tc",
                "wait_port_listen", "wait_file",
                "bpf_map_set", "bpf_map_dump", "bpf_prog_map_ids",
                "KsftSkipEx", "KsftFailEx", "KsftXfailEx",
diff --git a/tools/testing/selftests/net/lib/py/__init__.py b/tools/testing/selftests/net/lib/py/__init__.py
index 7c81d86a7e97..64a8c1ed4950 100644
--- a/tools/testing/selftests/net/lib/py/__init__.py
+++ b/tools/testing/selftests/net/lib/py/__init__.py
@@ -14,7 +14,7 @@ from .netns import NetNS, NetNSEnter
 from .nsim import NetdevSim, NetdevSimDev
 from .utils import CmdExitFailure, fd_read_timeout, cmd, bkg, defer, \
     bpftool, ip, ethtool, bpftrace, rand_port, rand_ports, wait_port_listen, \
-    wait_file, tool
+    wait_file, tool, tc
 from .bpf import bpf_map_set, bpf_map_dump, bpf_prog_map_ids
 from .ynl import NlError, NlctrlFamily, YnlFamily, \
     EthtoolFamily, NetdevFamily, RtnlFamily, RtnlAddrFamily
@@ -29,7 +29,7 @@ __all__ = ["KSRC",
            "NetNS", "NetNSEnter",
            "CmdExitFailure", "fd_read_timeout", "cmd", "bkg", "defer",
            "bpftool", "ip", "ethtool", "bpftrace", "rand_port", "rand_ports",
-           "wait_port_listen", "wait_file", "tool",
+           "wait_port_listen", "wait_file", "tool", "tc",
            "bpf_map_set", "bpf_map_dump", "bpf_prog_map_ids",
            "NetdevSim", "NetdevSimDev",
            "NetshaperFamily", "DevlinkFamily", "PSPFamily", "NlError",
diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
index ef31c0ba47fc..165adf33e1b5 100644
--- a/tools/testing/selftests/net/lib/py/utils.py
+++ b/tools/testing/selftests/net/lib/py/utils.py
@@ -224,7 +224,10 @@ class defer:
 def tool(name, args, json=None, ns=None, host=None):
     cmd_str = name + ' '
     if json:
-        cmd_str += '--json '
+        if name == 'tc':
+            cmd_str += '-json '
+        else:
+            cmd_str += '--json '
     cmd_str += args
     cmd_obj = cmd(cmd_str, ns=ns, host=host)
     if json:
@@ -242,6 +245,13 @@ def ip(args, json=None, ns=None, host=None):
     return tool('ip', args, json=json, host=host)
 
 
+def tc(args, json=None, ns=None, host=None):
+    """ Helper to call tc with standard set of optional args. """
+    if ns:
+        args = f'-netns {ns} ' + args
+    return tool('tc', args, json=json, host=host)
+
+
 def ethtool(args, json=None, ns=None, host=None):
     return tool('ethtool', args, json=json, ns=ns, host=host)
 
-- 
2.54.0.545.g6539524ca2-goog


^ permalink raw reply related

* [PATCH net-next v6 1/3] selftests: net: py: support cmd verifying expected failure
From: Willem de Bruijn @ 2026-04-30 13:28 UTC (permalink / raw)
  To: netdev
  Cc: davem, kuba, edumazet, pabeni, horms, linux-kselftest, shuah,
	Willem de Bruijn
In-Reply-To: <20260430132820.1944517-1-willemdebruijn.kernel@gmail.com>

From: Willem de Bruijn <willemb@google.com>

Support negative tests, where cmd raises an exception if the command
succeeded.

Existing fail values are

- True:            Pass if returncode == 0, raise Exception otherwise
- False:           Pass unconditionally
- None:            True iff not terminated explicitly

Introduce a variant of True that inverses the condition:

- 'verify_failed': Pass if returncode != 0, raise Exception otherwise

We cannot reuse False for this, because existing tests rely on current
behavior to pass unconditionally.

Only suppress regular test failure. Python subprocess may set a
negative return code on process crash or timeout. Those are not
anticipated failures.

Signed-off-by: Willem de Bruijn <willemb@google.com>
---
 tools/testing/selftests/net/lib/py/utils.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
index 6c44a3d2bbf7..ef31c0ba47fc 100644
--- a/tools/testing/selftests/net/lib/py/utils.py
+++ b/tools/testing/selftests/net/lib/py/utils.py
@@ -111,10 +111,14 @@ class cmd:
 
         stdout, stderr = self._process_terminate(terminate=terminate,
                                                  timeout=timeout)
-        if self.proc.returncode != 0 and fail:
+
+        if (self.proc.returncode != 0 and fail and
+            (self.proc.returncode < 0 or fail != 'verify_failed')):
             if len(stderr) > 0 and stderr[-1] == "\n":
                 stderr = stderr[:-1]
             raise CmdExitFailure("Command failed", self)
+        elif self.proc.returncode == 0 and fail == 'verify_failed':
+            raise CmdExitFailure("Command succeeded while should fail", self)
 
     def __repr__(self):
         def str_fmt(name, s):
-- 
2.54.0.545.g6539524ca2-goog


^ permalink raw reply related

* [PATCH net-next v6 0/3] selftests: drv-net: convert so_txtime to drv-net
From: Willem de Bruijn @ 2026-04-30 13:28 UTC (permalink / raw)
  To: netdev
  Cc: davem, kuba, edumazet, pabeni, horms, linux-kselftest, shuah,
	Willem de Bruijn

From: Willem de Bruijn <willemb@google.com>

In preparation for extending to pacing hardware offload, convert the
so_txtime.sh test to a drv-net test that can be run against netdevsim
and real hardware.

Two preparatory patches
1. support negative tests, where tests are expected to fail
2. add a tc helper 

See individual patches for details.

Willem de Bruijn (3):
  selftests: net: py: support cmd verifying expected failure
  selftests: net: py: add tc utility
  selftests: drv-net: convert so_txtime to drv-net

 .../testing/selftests/drivers/net/.gitignore  |   1 +
 tools/testing/selftests/drivers/net/Makefile  |   2 +
 tools/testing/selftests/drivers/net/config    |   2 +
 .../selftests/drivers/net/lib/py/__init__.py  |   5 +-
 .../selftests/{ => drivers}/net/so_txtime.c   |  25 +++-
 .../selftests/drivers/net/so_txtime.py        |  95 +++++++++++++++
 tools/testing/selftests/net/.gitignore        |   1 -
 tools/testing/selftests/net/Makefile          |   2 -
 .../testing/selftests/net/lib/py/__init__.py  |   4 +-
 tools/testing/selftests/net/lib/py/utils.py   |  18 ++-
 tools/testing/selftests/net/so_txtime.sh      | 110 ------------------
 11 files changed, 141 insertions(+), 124 deletions(-)
 rename tools/testing/selftests/{ => drivers}/net/so_txtime.c (96%)
 create mode 100755 tools/testing/selftests/drivers/net/so_txtime.py
 delete mode 100755 tools/testing/selftests/net/so_txtime.sh

-- 
2.54.0.545.g6539524ca2-goog


^ permalink raw reply

* Re: [PATCH net 2/2] ovpn: ensure gro_cells_receive() is invoked with BH disabled
From: Antonio Quartulli @ 2026-04-30 13:28 UTC (permalink / raw)
  To: netdev, Jakub Kicinski
  Cc: ralf, Sabrina Dubroca, Paolo Abeni, Andrew Lunn, David S. Miller,
	Eric Dumazet
In-Reply-To: <20260429120120.514491-3-antonio@openvpn.net>

Hi Jakub,

sashiko came back with an interesting review of the per-cpu stats update 
in the surrounding code.

As far as I can tell its explanation makes sense, but I am no per-cpu 
expert.

IIUC it basically says that if gro_cells_receive() is invoked with 
bottom halves disabled, the following dev_dstats_rx_add() should be too 
to avoid deadlocks and corruptions.

See below:

On 29/04/2026 14:01, Antonio Quartulli wrote:
> diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
> index d92bb87be2b2..c0fdb9504241 100644
> --- a/drivers/net/ovpn/io.c
> +++ b/drivers/net/ovpn/io.c
> @@ -91,7 +91,13 @@ static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb)
>   
>   	/* cause packet to be "received" by the interface */
>   	pkt_len = skb->len;
> +	/* we may get here in process context in case of TCP connections,
> +	 * therefore we have to disable BHs to ensure gro_cells_receive()
> +	 * doesn't enter deadlock
> +	 */
> +	local_bh_disable();
>   	ret = gro_cells_receive(&peer->ovpn->gro_cells, skb);
> +	local_bh_enable();
>   	if (likely(ret == NET_RX_SUCCESS)) {
>   		/* update RX stats with the size of decrypted packet */
>   		ovpn_peer_stats_increment_rx(&peer->vpn_stats, pkt_len);
 >>		dev_dstats_rx_add(peer->ovpn->dev...) << not visible in the diff, 
but it's here

from sashiko:

Can this lead to seqcount deadlocks and stat corruption since
dev_dstats_rx_add() is executed with preemption and bottom-halves enabled?
dev_dstats_rx_add() operates on per-CPU statistics and protects 64-bit
updates with a sequence counter (u64_stats_update_begin()). When called
from process context with BH enabled, on 64-bit systems, this_cpu_ptr()
is evaluated with preemption enabled. If the task is migrated mid-update,
it risks cross-CPU stat corruption.
On 32-bit systems, u64_stats_update_begin() disables preemption but not
bottom-halves. If a softirq (e.g., a concurrent UDP packet reception)
interrupts the process context and calls dev_dstats_rx_add() for the same
interface, it will re-enter the seqcount writer lock on the exact same CPU.
This corrupts the sequence counter, causing readers to see an unlocked
sequence during active writes, leading to torn reads and corrupted stats.
Should local_bh_enable() be moved after the statistics updates to ensure
the entire per-CPU update is atomic with respect to softirqs?


Do you have an opinion?

Thanks a lot.

Regards,

-- 
Antonio Quartulli
OpenVPN Inc.


^ permalink raw reply

* Re: [PATCH v2 1/2] netfilter: ip_tables: guard ipt_unregister_table_pre_exit against NULL ops
From: Florian Westphal @ 2026-04-30 13:27 UTC (permalink / raw)
  To: Tristan Madani
  Cc: Pablo Neira Ayuso, Phil Sutter, netfilter-devel, netdev, stable,
	linux-kernel
In-Reply-To: <177750474339.3016150.13196470704394042910@talencesecurity.com>

Tristan Madani <tristmd@gmail.com> wrote:
> ipt_register_table() adds the table to the per-netns list via
> xt_register_table() before assigning the per-net ops copy to
> new_table->ops.  If cleanup_net runs during this window,
> ipt_unregister_table_pre_exit() finds the table via xt_find_table()
> and passes the NULL ops pointer to nf_unregister_net_hooks(), causing
> a general protection fault.
> 
> Guard against this by checking table->ops before calling
> nf_unregister_net_hooks().  If ops is NULL the table is still being
> set up; the register path will either complete and register the hooks
> normally, or fail and clean up via __ipt_unregister_table().

Is there a reproducer for this bug?

This explanation makes little sense to me.
If netns is being destroyed, then there should be no more requests
to set/getsockopt.

Is this perhaps about aggressive rmmod + parallel set/getsockopt calls?
That would make more sense, but this needs a different fix.

I'm working on a new unreg scheme to avoid rmmod racing with concurrent
calls into iptables set/getsockopts.

^ permalink raw reply

* Re: [PATCH net v5] ipv6: Implement limits on extension header parsing
From: Eric Dumazet @ 2026-04-30 13:25 UTC (permalink / raw)
  To: Ido Schimmel
  Cc: Daniel Borkmann, kuba, dsahern, tom, willemdebruijn.kernel,
	pabeni, justin.iurman, netdev
In-Reply-To: <20260430124025.GA971154@shredder>

On Thu, Apr 30, 2026 at 5:40 AM Ido Schimmel <idosch@nvidia.com> wrote:
>
> On Wed, Apr 29, 2026 at 05:46:48PM +0200, Daniel Borkmann wrote:
> > ipv6_{skip_exthdr,find_hdr}() and ip6_{tnl_parse_tlv_enc_lim,
> > protocol_deliver_rcu}() iterate over IPv6 extension headers until they
> > find a non-extension-header protocol or run out of packet data. The
> > loops have no iteration counter, relying solely on the packet length
> > to bound them. For a crafted packet with 8-byte extension headers
> > filling a 64KB jumbogram, this means a worst case of up to ~8k
> > iterations with a skb_header_pointer call each. ipv6_skip_exthdr(),
> > for example, is used where it parses the inner quoted packet inside
> > an incoming ICMPv6 error:
> >
> >   - icmpv6_rcv
> >     - checksum validation
> >     - case ICMPV6_DEST_UNREACH
> >       - icmpv6_notify
> >         - pskb_may_pull()       <- pull inner IPv6 header
> >         - ipv6_skip_exthdr()    <- iterates here
> >         - pskb_may_pull()
> >         - ipprot->err_handler() <- sk lookup
> >
> > The per-iteration cost of ipv6_skip_exthdr itself is generally
> > light, but skb_header_pointer becomes more costly on reassembled
> > packets: the first ~1232 bytes of the inner packet are in the skb's
> > linear area, but the remaining ~63KB are in the frag_list where
> > skb_copy_bits is needed to read data.
> >
> > Initially, the idea was to add a configurable limit via a new
> > sysctl knob with default 8, in line with knobs from commit
> > 47d3d7ac656a ("ipv6: Implement limits on Hop-by-Hop and Destination
> > options"), but two reasons eventually argued against it:
> >
> > - It adds to UAPI that needs to be maintained forever, and
> >   upcoming work is restricting extension header ordering anyway,
> >   leaving little reason for another sysctl knob
> > - exthdrs_core.c is always built-in even when CONFIG_IPV6=n,
> >   where struct net has no .ipv6 member, so the read site would
> >   need an ifdef'd fallback to a constant anyway
> >
> > Therefore, just use a constant (IP6_MAX_EXT_HDRS_CNT). All four
> > extension header walking functions are now bound by this limit.
> >
> > Note that the check in ip6_protocol_deliver_rcu() happens right
> > before the goto resubmit, such that we don't have to have a test
> > for ipv6_ext_hdr() in the fast-path.
> >
> > There's an ongoing IETF draft-iurman-6man-eh-occurrences to enforce
> > IPv6 extension headers ordering and occurrence. The latter also
> > discusses security implications. As per RFC8200 section 4.1, the
> > occurrence rules for extension headers provide a practical upper
> > bound which is 8. In order to be conservative, let's define
> > IP6_MAX_EXT_HDRS_CNT as 12 to leave enough room for quirky setups.
> > In the unlikely event that this is still not enough, then we might
> > need to reconsider a sysctl.
> >
> > Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
>
> Reviewed-by: Ido Schimmel <idosch@nvidia.com>

Reviewed-by: Eric Dumazet <edumazet@google.com>

Thanks Daniel!

^ permalink raw reply

* Re: [PATCH net-next] net: Consistently define pci_device_ids using named initializers
From: Uwe Kleine-König (The Capable Hub) @ 2026-04-30 13:15 UTC (permalink / raw)
  To: Markus Schneider-Pargmann
  Cc: Michael Grzeschik, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Marc Kleine-Budde, Vincent Mailhol,
	Krzysztof Halasa, Johannes Berg, Steffen Klassert, David Dillow,
	Ion Badulescu, Mark Einon, Rasesh Mody, GR-Linux-NIC-Dev,
	Manish Chopra, Potnuri Bharat Teja, Denis Kirjanov, Jijie Shao,
	Jian Shen, Cai Huoqing, Fan Gong, Tony Nguyen, Przemek Kitszel,
	Tariq Toukan, Saeed Mahameed, Leon Romanovsky, Mark Bloch,
	Ido Schimmel, Petr Machata, Yibo Dong, Simon Horman,
	Heiner Kallweit, nic_swsd, Jiri Pirko, Francois Romieu,
	Daniele Venzano, Samuel Chessman, Jiawen Wu, Mengyuan Lou,
	Kevin Curtis, Arend van Spriel, Stanislav Yakovlev,
	Richard Cochran, Kees Cook, Thomas Gleixner, Thomas Fourier,
	Ingo Molnar, Kory Maincent, Zilin Guan, Marco Crivellari,
	Vadim Fedorenko, Jacob Keller, Philipp Stanner, Bjorn Helgaas,
	Yeounsu Moon, Denis Benato, Yonglong Liu, Andy Shevchenko,
	Yicong Hui, Randy Dunlap, MD Danish Anwar, Nathan Chancellor,
	Sai Krishna, Ethan Nelson-Moore, Larysa Zaremba, Joe Damato,
	Double Lo, Colin Ian King, netdev, linux-kernel, linux-can,
	linux-parisc, intel-wired-lan, linux-rdma, oss-drivers,
	linux-wireless, brcm80211, brcm80211-dev-list.pdl
In-Reply-To: <DI6D3JVJ6JG6.8V4XUVGJA2D4@baylibre.com>

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

Hello Markus,

On Thu, Apr 30, 2026 at 10:55:14AM +0200, Markus Schneider-Pargmann wrote:
> On Tue Apr 28, 2026 at 7:18 PM CEST, Uwe Kleine-König (The Capable Hub) wrote:
> > diff --git a/drivers/net/can/m_can/m_can_pci.c b/drivers/net/can/m_can/m_can_pci.c
> > index eb31ed1f9644..cb9335c1d3ea 100644
> > --- a/drivers/net/can/m_can/m_can_pci.c
> > +++ b/drivers/net/can/m_can/m_can_pci.c
> > @@ -183,9 +183,9 @@ static SIMPLE_DEV_PM_OPS(m_can_pci_pm_ops,
> >  			 m_can_pci_suspend, m_can_pci_resume);
> >  
> >  static const struct pci_device_id m_can_pci_id_table[] = {
> > -	{ PCI_VDEVICE(INTEL, 0x4bc1), M_CAN_CLOCK_FREQ_EHL, },
> > -	{ PCI_VDEVICE(INTEL, 0x4bc2), M_CAN_CLOCK_FREQ_EHL, },
> > -	{  }	/* Terminating Entry */
> > +	{ PCI_VDEVICE(INTEL, 0x4bc1), .driver_data = M_CAN_CLOCK_FREQ_EHL, },
> > +	{ PCI_VDEVICE(INTEL, 0x4bc2), .driver_data = M_CAN_CLOCK_FREQ_EHL, },
> > +	{ }	/* terminating entry */
> 
> M_CAN_CLOCK_FREQ_EHL is basically hardcoded for all PCI devices since
> 2020. I don't think we need this driver data at all and can just drop it
> and use M_CAN_CLOCK_FREQ_EHL directly in the code for the frequency.
> Once a real new PCI device gets added we can see if and what driver_data
> is needed.

Sounds like a nice *separate* change, right?

Best regards
Uwe

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

^ permalink raw reply

* Re: [PATCH v7 phy-next 17/27] phy: introduce phy_get_max_link_rate() helper for consumers
From: Vladimir Oltean @ 2026-04-30 13:14 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: linux-phy, Vinod Koul, Neil Armstrong, dri-devel, freedreno,
	linux-arm-kernel, linux-arm-msm, linux-can, linux-gpio, linux-ide,
	linux-kernel, linux-media, linux-pci, linux-renesas-soc,
	linux-riscv, linux-rockchip, linux-samsung-soc, linux-scsi,
	linux-sunxi, linux-tegra, linux-usb, netdev, spacemit,
	UNGLinuxDriver, Markus Schneider-Pargmann, Andrzej Hajda,
	Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Andy Yan, Marc Kleine-Budde, Vincent Mailhol,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Geert Uytterhoeven, Magnus Damm
In-Reply-To: <CAMuHMdWbeeRmLf6Ae0Fr0un=-z7z5ONc_hDdjebP=KVkXHPbhw@mail.gmail.com>

On Thu, Apr 30, 2026 at 01:59:53PM +0200, Geert Uytterhoeven wrote:
> Acked-by: Geert Uytterhoeven <geert+renesas@glider.be> # rcar_canfd

Thanks.

> > --- a/include/linux/phy/phy.h
> > +++ b/include/linux/phy/phy.h
> > @@ -57,6 +57,7 @@ int phy_notify_disconnect(struct phy *phy, int port);
> >  int phy_notify_state(struct phy *phy, union phy_notify state);
> >  int phy_get_bus_width(struct phy *phy);
> >  void phy_set_bus_width(struct phy *phy, int bus_width);
> > +u32 phy_get_max_link_rate(struct phy *phy);
> 
> This (and all the existing getters) should take a "const struct phy *".

Yeah... Let's see what other review comments pop up (including Sashiko,
which would be seeing this series for the first time) and decide
afterwards whether to make the argument const for the new getters as
part of a separate set, or in v8.

I don't think that modifying the existing getters is in scope for this
27 patch set.

^ permalink raw reply

* Re: [PATCH 1/9] dt-bindings: mmc: Document support for nvmem-layout
From: Loic Poulain @ 2026-04-30 13:13 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
	Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, linux-mmc, devicetree, linux-kernel, linux-arm-msm,
	linux-block, linux-wireless, ath10k, linux-bluetooth, netdev,
	daniel
In-Reply-To: <20260430-bird-of-sheer-ampleness-744e7f@quoll>

Hi Krzysztof,

On Thu, Apr 30, 2026 at 11:59 AM Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On Tue, Apr 28, 2026 at 04:23:06PM +0200, Loic Poulain wrote:
>
> > +                    compatible = "fixed-layout";
> > +
> > +                    #address-cells = <1>;
> > +                    #size-cells = <1>;
> > +
> > +                    mac-addr@4400 {
> > +                        reg = <0x4400 0x6>;
>
> This looks incomplete. Why isn't this mac-base type of entry? And how do
> you address it from NVMEM consumer?

This indeed falls under the fixed-cell/mac-base type, thanks for
pointing that out.
NVMEM consumers reference these entries using the nvmem-cells
property, via the corresponding label/phandle.

>
> > +                    };
> > +
> > +                    bd-addr@5400 {
> > +                        reg = <0x5400 0x6>;
> > +                    };
> > +                };
> > +            };
> >          };
> >      };
> >
> >
> > --
> > 2.34.1
> >

^ permalink raw reply

* Re: [PATCH net-next] net: Consistently define pci_device_ids using named initializers
From: Uwe Kleine-König (The Capable Hub) @ 2026-04-30 13:13 UTC (permalink / raw)
  To: Jijie Shao
  Cc: Michael Grzeschik, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Marc Kleine-Budde, Vincent Mailhol,
	Krzysztof Halasa, Johannes Berg, Markus Schneider-Pargmann,
	Steffen Klassert, David Dillow, Ion Badulescu, Mark Einon,
	Rasesh Mody, GR-Linux-NIC-Dev, Manish Chopra, Potnuri Bharat Teja,
	Denis Kirjanov, Jian Shen, Cai Huoqing, Fan Gong, Tony Nguyen,
	Przemek Kitszel, Tariq Toukan, Saeed Mahameed, Leon Romanovsky,
	Mark Bloch, Ido Schimmel, Petr Machata, Yibo Dong, Simon Horman,
	Heiner Kallweit, nic_swsd, Jiri Pirko, Francois Romieu,
	Daniele Venzano, Samuel Chessman, Jiawen Wu, Mengyuan Lou,
	Kevin Curtis, Arend van Spriel, Stanislav Yakovlev,
	Richard Cochran, Kees Cook, Thomas Gleixner, Thomas Fourier,
	Ingo Molnar, Kory Maincent, Zilin Guan, Marco Crivellari,
	Vadim Fedorenko, Jacob Keller, Philipp Stanner, Bjorn Helgaas,
	Yeounsu Moon, Denis Benato, Yonglong Liu, Andy Shevchenko,
	Yicong Hui, Randy Dunlap, MD Danish Anwar, Nathan Chancellor,
	Sai Krishna, Ethan Nelson-Moore, Larysa Zaremba, Joe Damato,
	Double Lo, Colin Ian King, netdev, linux-kernel, linux-can,
	linux-parisc, intel-wired-lan, linux-rdma, oss-drivers,
	linux-wireless, brcm80211, brcm80211-dev-list.pdl
In-Reply-To: <814632c8-070b-4b21-adbb-5a01a62d52f2@huawei.com>

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

Hello,

On Thu, Apr 30, 2026 at 02:53:29PM +0800, Jijie Shao wrote:
> on 2026/4/29 1:18, Uwe Kleine-König (The Capable Hub) wrote:
> > ... and PCI device helpers.
> > 
> > The various struct pci_device_id arrays were initialized mostly by one
> > the PCI_DEVICE macros and then list expressions. The latter isn't easily
> > readable if you're not into PCI. Using named initializers is more
> > explicit and thus easier to parse.
> > 
> > Also use PCI_DEVICE* helper macros to assign .vendor, .device,
> > .subvendor and .subdevice where appropriate and skip explicit
> > assignments of 0 (which the compiler takes care of).
> > 
> > The secret plan is to make struct pci_device_id::driver_data an
> > anonymous union (similar to
> > https://lore.kernel.org/all/cover.1776579304.git.u.kleine-koenig@baylibre.com/)
> > and that requires named initializers. But it's also a nice cleanup on
> > its own.
> > 
> > This change doesn't introduce changes to the compiled pci_device_id
> > arrays. Tested on x86 and arm64.
> > 
> > Signed-off-by: Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com>
> 
> ...
> 
> > diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
> > index 068da2fd1fea..b3e01b2f8319 100644
> > --- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
> > +++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
> > @@ -489,7 +489,7 @@ static void hbg_shutdown(struct pci_dev *pdev)
> >   }
> >   static const struct pci_device_id hbg_pci_tbl[] = {
> > -	{PCI_VDEVICE(HUAWEI, 0x3730), 0},
> > +	{ PCI_VDEVICE(HUAWEI, 0x3730) },
> >   	{ }
> >   };
> 
> Reviewed-by: Jijie Shao <shaojijie@huawei.com>

Thanks.

> > +	{
> > +		PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE),
> > +		.driver_data = 0,
> > +	}, {
> > +		PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE),
> > +		.driver_data = 0,
> 
> Thanks for your work.
> 
> If .driver_data = 0, is it possible to delete it to be consistent with other parts, for example:
> 
> { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE) }
> 
> > +	}, {
> > +		PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA),
> > +		.driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS,

However keeping the explicit .driver_data = 0 to have a contrast to
other `pci_device_id`s having a non-zero .driver_data in the same driver
is also a good reason to keep the (technically redundant) assignment.
For other drivers I dropped these assignments if this is possible for
all array members.

Having said that I don't intend to rework the patch for this suggestion.

Best regards
Uwe

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

^ permalink raw reply

* Re: [PATCH net] ipmr: prevent info-leak in pmr_cache_report()
From: Ido Schimmel @ 2026-04-30 13:10 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, David Ahern,
	Simon Horman, netdev, eric.dumazet, Yiming Qian
In-Reply-To: <20260430070611.4004529-1-edumazet@google.com>

Nit: s/pmr_cache_report/ipmr_cache_report/ in subject

On Thu, Apr 30, 2026 at 07:06:11AM +0000, Eric Dumazet wrote:
> Yiming Qian reported:
> 
> <quote>
>  ipmr_cache_report()` allocates a report skb with `alloc_skb(128,
>  GFP_ATOMIC)` and appends a `struct igmphdr` using `skb_put()`. In the
>  non-`IGMPMSG_WHOLEPKT` path it initializes only:
> 
>  - `igmp->type`
>  - `igmp->code`
> 
>  but does not initialize:
> 
>  - `igmp->csum`
>  - `igmp->group`
> 
>  Later, `igmpmsg_netlink_event()` copies the bytes after `sizeof(struct
>  igmpmsg)` into the `IPMRA_CREPORT_PKT` netlink attribute and emits
>  `RTM_NEWCACHEREPORT` on `RTNLGRP_IPV4_MROUTE_R`.
> 
>  As a result, 6 bytes of stale heap data from the skb head are
>  disclosed to userspace.
> </quote>
> 
> Let's use skb_put_zero() instead of skb_put() to fix this bug.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: Yiming Qian <yimingqian591@gmail.com>
> Signed-off-by: Eric Dumazet <edumazet@google.com>

Reviewed-by: Ido Schimmel <idosch@nvidia.com>

FYI, I checked and ip6mr_cache_report() seems OK.

^ permalink raw reply

* Re: [PATCH net] ipvs: fix shift-out-of-bounds in ip_vs_rht_desired_size
From: Julian Anastasov @ 2026-04-30 13:10 UTC (permalink / raw)
  To: Simon Horman; +Cc: horms, pablo, fw, lvs-devel, netfilter-devel, netdev
In-Reply-To: <20260430123917.1628871-2-horms@kernel.org>


	Hello,

On Thu, 30 Apr 2026, Simon Horman wrote:

> From: 'Simon Horman' <horms@kernel.org>
> 
> 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://sashiko.dev
> ---
> ipvs: fix shift-out-of-bounds in ip_vs_rht_desired_size
> 
> This commit addresses a shift-out-of-bounds issue when roundup_pow_of_two()
> is called with 0 in ip_vs_rht_desired_size. It updates the assignment to fall
> back to 1 if n is 0.
> 
> > diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
> > index f5b7a2047291..ea450944465f 100644
> > --- a/net/netfilter/ipvs/ip_vs_core.c
> > +++ b/net/netfilter/ipvs/ip_vs_core.c
> > @@ -237,7 +237,7 @@ int ip_vs_rht_desired_size(struct netns_ipvs *ipvs, struct ip_vs_rht *t, int n,
> >  {
> >  	if (!t)
> >  		return 1 << min_bits;
> > -	n = roundup_pow_of_two(n);
> > +	n = n ? roundup_pow_of_two(n) : 1;

	To overflow it on 64-bit we need 600GB+ memory, the
conns are ~300 bytes.

	In v2 I changed it to n > 0. To solve it completely
we may need to introduce some kind of "conn_max" sysctl,
now per-netns. It can help to limit the conns which are
created GFP_ATOMIC while for other structures we will switch
to GFP_KERNEL_ACCOUNT.

> The parameter n is a signed int. If n happens to be negative, it will evaluate
> to true and be passed to roundup_pow_of_two().
> 
> Because roundup_pow_of_two() casts the input to unsigned long, a negative
> value like -1 becomes ULONG_MAX. This results in a shift exponent of 64,
> triggering a shift-out-of-bounds regression. Would it be safer to check
> for n <= 0 instead?
> 
> Additionally, if n exceeds 1.07 billion (1 << 30) on systems with large
> amounts of RAM, roundup_pow_of_two(n) evaluates to 1UL << 31. Since n is
> a 32-bit signed int, assigning 1UL << 31 back to n overflows and results
> in a negative number.
> 
> Since subsequent bounds checks use signed comparisons:
> 
> >  	if (lfactor < 0) {
> >  		int factor = min(-lfactor, max_bits);
> >  
> >  		n = min(n, 1 << (max_bits - factor));
> 
> These checks will fail to constrain the size correctly.
> 
> [ ... ]
> 
> >  	/* Shrink but keep it n * 2 to prevent frequent resizing */
> >  	return clamp(n << 1, 1 << min_bits, 1 << max_bits);
> >  }
> 
> Eventually, the function returns clamp(n << 1, 1 << min_bits, 1 << max_bits).
> Shifting a negative number causes undefined behavior, and the clamp operation
> will force the hash table to its absolute minimum size.
> 
> Could this cause millions of connections to be placed into a minimally sized
> hash table, causing severe collisions during RCU hash lookups?

	All that is valid without conn/mem limits.

Regards

--
Julian Anastasov <ja@ssi.bg>


^ permalink raw reply

* Re: [PATCH] vsock/virtio: fix vsockmon info leak in non-linear tap copy
From: Luigi Leonardi @ 2026-04-30 13:04 UTC (permalink / raw)
  To: Yiqi Sun
  Cc: kvm, virtualization, netdev, linux-kernel, stefanha, sgarzare,
	mst, jasowang, xuanzhuo, eperezma, davem, edumazet, kuba, pabeni,
	horms
In-Reply-To: <20260430071110.380509-1-sunyiqixm@gmail.com>

On Thu, Apr 30, 2026 at 03:11:10PM +0800, Yiqi Sun wrote:
>vsockmon mirrors packets through virtio_transport_build_skb(), which
>builds a new skb and copies the payload into it. For non-linear skbs,
>this goes through virtio_transport_copy_nonlinear_skb().
>
>Helper manually initializes a iov_iter, but leaves iov_iter.count unset.
>As a result, skb_copy_datagram_iter() sees zero writable bytes
>in the destination iterator and copies no payload data.
>
>This becomes an info leak because virtio_transport_build_skb() has
>already reserved payload_len bytes in the new skb with skb_put(). The
>skb is then returned to the tap path with that payload area still
>uninitialized, so userspace reading from a vsockmon device can observe
>heap contents and potentially kernel address.
>
>Fix it by initializing iov_iter.count to the number of bytes to copy.
>
>Fixes: 4b0bf10eb077 ("vsock/virtio: non-linear skb handling for tap")
>Signed-off-by: Yiqi Sun <sunyiqixm@gmail.com>
>---
> net/vmw_vsock/virtio_transport_common.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
>diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
>index 416d533f493d..6b26ee57ccab 100644
>--- a/net/vmw_vsock/virtio_transport_common.c
>+++ b/net/vmw_vsock/virtio_transport_common.c
>@@ -152,7 +152,7 @@ static void virtio_transport_copy_nonlinear_skb(const struct sk_buff *skb,
> 	iov_iter.nr_segs = 1;
>
> 	to_copy = min_t(size_t, len, skb->len);
>-
>+	iov_iter.count = to_copy;
> 	skb_copy_datagram_iter(skb, VIRTIO_VSOCK_SKB_CB(skb)->offset,
> 			       &iov_iter, to_copy);
> }
>-- 
>2.34.1
>

Thanks for the fix!

Tested using vsock_loopback sending zero-copy packets. Payload is always
zero before the fix.

Reviewed-by: Luigi Leonardi <leonardi@redhat.com>


^ permalink raw reply

* Re: [PATCH net V4 4/4] net/mlx5e: SD, Fix race condition in secondary device probe/remove
From: Shay Drori @ 2026-04-30 13:03 UTC (permalink / raw)
  To: Jakub Kicinski, tariqt
  Cc: edumazet, pabeni, andrew+netdev, davem, saeedm, mbloch, leon,
	horms, phaddad, kees, parav, gal, netdev, linux-rdma,
	linux-kernel, dtatulea
In-Reply-To: <20260430014211.2375751-1-kuba@kernel.org>



On 30/04/2026 4:42, Jakub Kicinski wrote:
> External email: Use caution opening links or attachments
> 
> 
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> net/mlx5e: SD, Fix race condition in secondary device probe/remove
> 
> This commit updates secondary devices to acquire the device lock of the
> primary auxiliary device during probe, suspend, resume, and remove
> operations. It introduces mlx5_sd_get_adev() and mlx5_sd_put_adev() to
> manage this lock ordering and add a second devcom readiness check.
> 
>> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
>> index 7a1787f15320..a43ae482a679 100644
>> --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
>> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
> 
> [ ... ]
> 
>> @@ -563,5 +567,18 @@ struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev,
>>        if (dev == primary)
>>                return adev;
>>
>> +     device_lock(&primary->priv.adev[idx]->adev.dev);
> 
> If mlx5_devcom_comp_is_ready() returned true just before this, but the
> secondary thread is preempted before reaching device_lock(), could a
> concurrent removal of the primary device cause primary->priv.adev[idx]
> to become NULL or freed?
> 
> It looks like the removal path (such as mlx5_unregister_device) frees the
> mlx5_adev memory and explicitly sets primary->priv.adev[idx] to NULL. If
> the secondary thread then resumes, it might dereference a NULL or freed
> pointer when attempting to acquire the lock.
> 
> Since the second readiness check happens after the device_lock() call, does
> this leave a window where memory corruption or a panic can still occur?

This is also correct and will fix in next version.

> 
> Additionally, since the driver core already holds device_lock(&adev->dev)
> for the secondary device during callbacks like probe() and remove(), will
> acquiring the primary device's lock here trigger a lockdep warning for
> possible recursive locking?
> 
> Both the primary and secondary auxiliary devices use the same generic lock
> class assigned in device_initialize(). While the fixed secondary to primary
> ordering avoids an actual deadlock, lockdep might not distinguish between
> the two mutexes without explicit lockdep subclassing.
> 
>> +     /* In case primary finish removing its adev */
>> +     if (!mlx5_devcom_comp_is_ready(sd->devcom)) {
>> +             device_unlock(&primary->priv.adev[idx]->adev.dev);
>> +             return NULL;
>> +     }
>>        return &primary->priv.adev[idx]->adev;
>>   }

I test this code with KASAN and LOCKDEP enable, I didn't get any
splat...


^ permalink raw reply

* Re: [PATCH net V4 3/4] net/mlx5e: SD, Fix missing cleanup on probe error
From: Shay Drori @ 2026-04-30 12:52 UTC (permalink / raw)
  To: Jakub Kicinski, tariqt
  Cc: edumazet, pabeni, andrew+netdev, davem, saeedm, mbloch, leon,
	horms, phaddad, kees, parav, gal, netdev, linux-rdma,
	linux-kernel, dtatulea
In-Reply-To: <20260430014209.2375731-1-kuba@kernel.org>



On 30/04/2026 4:42, Jakub Kicinski wrote:
> External email: Use caution opening links or attachments
> 
> 
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> net/mlx5e: SD, Fix missing cleanup on probe error
> 
> This commit adds a missing mlx5_sd_cleanup() call to the probe error path.
> The commit message notes a similar gap exists on the resume path leaving
> sd == NULL on failure, but defers the fix to a follow-up series.
> 
>> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
>> index 5a46870c4b74..e21affd0ffc4 100644
>> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
>> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> [ ... ]
>> @@ -6912,9 +6912,16 @@ static int mlx5e_probe(struct auxiliary_device *adev,
>>                return err;
>>
>>        actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx);
>> -     if (actual_adev)
>> -             return _mlx5e_probe(actual_adev);
>> +     if (actual_adev) {
>> +             err = _mlx5e_probe(actual_adev);
>> +             if (err)
>> +                     goto sd_cleanup;
>> +     }
>>        return 0;
>> +
>> +sd_cleanup:
>> +     mlx5_sd_cleanup(mdev);
>> +     return err;
>>   }
>>
>>   static void _mlx5e_remove(struct auxiliary_device *adev)
> 
> Will leaving the resume gap unfixed as mentioned in the commit message cause
> a NULL pointer dereference when a secondary device is later removed?
> 
> If a secondary device fails resume, sd becomes NULL. Later during removal,
> mlx5_sd_get_adev() will check !sd and return the secondary adev.
> 
> This causes _mlx5e_remove() to be executed on the secondary adev:
> 
> _mlx5e_remove()
> {
>          struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
>          struct mlx5e_dev *mlx5e_dev = auxiliary_get_drvdata(adev);
>          struct net_device *netdev = mlx5e_dev->netdev;
>          ...
> }
> 
> Since _mlx5e_probe() is never called on secondary devices,
> auxiliary_get_drvdata(adev) returns NULL. Does this mean accessing
> mlx5e_dev->netdev will crash?
> 
> Would it be safer to add a NULL check for mlx5e_dev in _mlx5e_remove()
> until the proper PM lifecycle fixes are implemented?

IMO, a proper fix is needed here. PM lifecycle is implemented, the bug
is in case of error flow...


^ permalink raw reply

* [PATCH net-next v2 2/2] mv88e6xxx: Add SERDES Support for mv88e6321
From: Fidan Aliyeva @ 2026-04-30 12:49 UTC (permalink / raw)
  To: andrew, olteanv, davem, edumazet, kuba, pabeni, netdev
  Cc: linux-kernel, thomas.eckerman.ext, Fidan Aliyeva
In-Reply-To: <20260430124907.3533344-1-fidan.aliyeva.ext@ericsson.com>

Add serdes and pcs_ops functions for mv88e6321. In mv88e6321
2 ports support serdes functionality; port 0 and port 1. These ports are
serdes-only ports.

Changes:

1. Add a function support to return the lane address for the port based on
cmode. Once that in place, reuse mv88e6352's serdes_get_regs_len and
pcs_init functions for mv88e6321.
2. Add mv88e6321_serdes_get_regs function by reusing
mv88e6352_serdes_get_regs_from_lane

Tested on mv88e6321 switch port 0.
Builds were done with allyesconfig and allmodconfig W=1.

Co-developed-by: Thomas Eckerman <thomas.eckerman.ext@ericsson.com>
Signed-off-by: Thomas Eckerman <thomas.eckerman.ext@ericsson.com>
Signed-off-by: Fidan Aliyeva <fidan.aliyeva.ext@ericsson.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c   |  4 ++++
 drivers/net/dsa/mv88e6xxx/serdes.c | 29 +++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/serdes.h |  4 ++++
 3 files changed, 37 insertions(+)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 15a8028a02a8..c1f7fa21d964 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -5259,10 +5259,14 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
 	.stu_getnext = mv88e6352_g1_stu_getnext,
 	.stu_loadpurge = mv88e6352_g1_stu_loadpurge,
+	.serdes_get_lane = mv88e6321_serdes_get_lane,
+	.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
+	.serdes_get_regs = mv88e6321_serdes_get_regs,
 	.gpio_ops = &mv88e6352_gpio_ops,
 	.avb_ops = &mv88e6352_avb_ops,
 	.ptp_ops = &mv88e6352_ptp_ops,
 	.phylink_get_caps = mv88e632x_phylink_get_caps,
+	.pcs_ops = &mv88e6352_pcs_ops,
 };

 static const struct mv88e6xxx_ops mv88e6341_ops = {
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
index 273c3b169f65..3f8c178cf843 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.c
+++ b/drivers/net/dsa/mv88e6xxx/serdes.c
@@ -249,6 +249,35 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
 	mv88e6352_serdes_get_regs_from_lane(chip, MV88E6352_ADDR_SERDES, _p);
 }

+int mv88e6321_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
+{
+	int lane = -ENODEV;
+	u8 cmode;
+
+	if (port != 0 && port != 1)
+		return lane;
+
+	cmode = chip->ports[port].cmode;
+
+	if (cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX ||
+	    cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
+	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)
+		lane = port + MV88E6321_PORT0_LANE;
+
+	return lane;
+}
+
+void mv88e6321_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
+{
+	int lane;
+
+	lane = mv88e6xxx_serdes_get_lane(chip, port);
+	if (lane < 0)
+		return;
+
+	mv88e6352_serdes_get_regs_from_lane(chip, lane, _p);
+}
+
 int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
 {
 	u8 cmode = chip->ports[port].cmode;
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h
index 21e050b328cc..c11ea2118efc 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.h
+++ b/drivers/net/dsa/mv88e6xxx/serdes.h
@@ -14,6 +14,8 @@

 struct phylink_link_state;

+#define MV88E6321_PORT0_LANE		0x0c
+
 #define MV88E6352_ADDR_SERDES		0x0f
 #define MV88E6352_SERDES_PAGE_FIBER	0x01
 #define MV88E6352_SERDES_IRQ		0x0b
@@ -114,6 +116,7 @@ struct phylink_link_state;
 int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa,
 			       u16 status, struct phylink_link_state *state);

+int mv88e6321_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
 int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
 int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
 int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
@@ -134,6 +137,7 @@ int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, int port,
 size_t mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
 				  uint64_t *data);

+void mv88e6321_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p);
 int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port);
 void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p);
 int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port);
--
2.36.0


^ permalink raw reply related

* [PATCH net-next v2 1/2] mv88e6xxx: Refactor 6352's serdes functions
From: Fidan Aliyeva @ 2026-04-30 12:49 UTC (permalink / raw)
  To: andrew, olteanv, davem, edumazet, kuba, pabeni, netdev
  Cc: linux-kernel, thomas.eckerman.ext, Fidan Aliyeva
In-Reply-To: <20260430124907.3533344-1-fidan.aliyeva.ext@ericsson.com>

This is a preparation patch for adding SERDES support for mv88e6321
version of the ethernet switch. The patch aims to make
mv88e6352_pcs_init, as well as some of serdes functions, more generic
so they can be reused for mv88e6321.

Changes:
1. Add mv88e6352_serdes_get_lane function which checks if the port
supports SERDES by reading G2 global scratch register. Then returns
the address of the SERDES lane.
2. Add this function as .serdes_get_lane member to all the chip
versions which use mv88e6352_pcs_init.
3. Replace serdes check by global register in mv88e6352_pcs_init
function by mv88e6xxx_serdes_get_lane function making it more generic.
4. Replace serdes check in mv88e6352_serdes_get_regs_len by
mv88e6xxx_serdes_get_lane. This function is the only other function which
checks the scratch register with lock; thus, can call
mv88e6xxx_serdes_get_lane instead.
5. Add mv88e6352_serdes_get_regs_from_lane helper function and refactor
mv88e6352_serdes_get_regs to call this function with MV88E6352_ADDR_SERDES.
The helper function will later be reused for 6321.
mv88e6352_serdes_get_regs itself is not reused because
mv886exxx_serdes_get_lane cannot be called inside 6352's serdes_get_regs.
The reason is 6352's serdes_get_lane requires register lock which has
already been locked by the serdes_get_regs's caller function.
6. Add lane argument to mv88e6352_serdes_read so it can be reused later for
6321.

Co-developed-by: Thomas Eckerman <thomas.eckerman.ext@ericsson.com>
Signed-off-by: Thomas Eckerman <thomas.eckerman.ext@ericsson.com>
Signed-off-by: Fidan Aliyeva <fidan.aliyeva.ext@ericsson.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c     |  4 ++
 drivers/net/dsa/mv88e6xxx/pcs-6352.c | 12 +++---
 drivers/net/dsa/mv88e6xxx/serdes.c   | 58 +++++++++++++++++++---------
 drivers/net/dsa/mv88e6xxx/serdes.h   |  1 +
 4 files changed, 49 insertions(+), 26 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 8ca5fd40df92..15a8028a02a8 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -4662,6 +4662,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
 	.stu_getnext = mv88e6352_g1_stu_getnext,
 	.stu_loadpurge = mv88e6352_g1_stu_loadpurge,
+	.serdes_get_lane = mv88e6352_serdes_get_lane,
 	.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
 	.serdes_get_regs = mv88e6352_serdes_get_regs,
 	.gpio_ops = &mv88e6352_gpio_ops,
@@ -4765,6 +4766,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
 	.stu_getnext = mv88e6352_g1_stu_getnext,
 	.stu_loadpurge = mv88e6352_g1_stu_loadpurge,
+	.serdes_get_lane = mv88e6352_serdes_get_lane,
 	.serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
 	.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
 	.serdes_get_regs = mv88e6352_serdes_get_regs,
@@ -5040,6 +5042,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
 	.stu_getnext = mv88e6352_g1_stu_getnext,
 	.stu_loadpurge = mv88e6352_g1_stu_loadpurge,
+	.serdes_get_lane = mv88e6352_serdes_get_lane,
 	.serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
 	.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
 	.serdes_get_regs = mv88e6352_serdes_get_regs,
@@ -5475,6 +5478,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
 	.gpio_ops = &mv88e6352_gpio_ops,
 	.avb_ops = &mv88e6352_avb_ops,
 	.ptp_ops = &mv88e6352_ptp_ops,
+	.serdes_get_lane = mv88e6352_serdes_get_lane,
 	.serdes_get_sset_count = mv88e6352_serdes_get_sset_count,
 	.serdes_get_strings = mv88e6352_serdes_get_strings,
 	.serdes_get_stats = mv88e6352_serdes_get_stats,
diff --git a/drivers/net/dsa/mv88e6xxx/pcs-6352.c b/drivers/net/dsa/mv88e6xxx/pcs-6352.c
index 9ebf0f89f817..4228ae5bb9db 100644
--- a/drivers/net/dsa/mv88e6xxx/pcs-6352.c
+++ b/drivers/net/dsa/mv88e6xxx/pcs-6352.c
@@ -324,19 +324,17 @@ static int mv88e6352_pcs_init(struct mv88e6xxx_chip *chip, int port)
 	struct mii_bus *bus;
 	struct device *dev;
 	unsigned int irq;
-	int err;
+	int lane, err;

-	mv88e6xxx_reg_lock(chip);
-	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
-	mv88e6xxx_reg_unlock(chip);
-	if (err <= 0)
-		return err;
+	lane = mv88e6xxx_serdes_get_lane(chip, port);
+	if (lane < 0)
+		return 0;

 	irq = mv88e6xxx_serdes_irq_mapping(chip, port);
 	bus = mv88e6xxx_default_mdio_bus(chip);
 	dev = chip->dev;

-	mpcs = marvell_c22_pcs_alloc(dev, bus, MV88E6352_ADDR_SERDES);
+	mpcs = marvell_c22_pcs_alloc(dev, bus, lane);
 	if (!mpcs)
 		return -ENOMEM;

diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
index a936ee80ce00..273c3b169f65 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.c
+++ b/drivers/net/dsa/mv88e6xxx/serdes.c
@@ -17,10 +17,10 @@
 #include "port.h"
 #include "serdes.h"

-static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg,
-				 u16 *val)
+static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int lane,
+				 int reg, u16 *val)
 {
-	return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
+	return mv88e6xxx_phy_page_read(chip, lane,
 				       MV88E6352_SERDES_PAGE_FIBER,
 				       reg, val);
 }
@@ -102,6 +102,21 @@ int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa,
 	return 0;
 }

+int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
+{
+	int err;
+
+	mv88e6xxx_reg_lock(chip);
+	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
+	mv88e6xxx_reg_unlock(chip);
+	if (err < 0)
+		return err;
+	else if (err == 0)
+		return -ENODEV;
+
+	return MV88E6352_ADDR_SERDES;
+}
+
 struct mv88e6352_serdes_hw_stat {
 	char string[ETH_GSTRING_LEN];
 	int sizeof_stat;
@@ -141,14 +156,14 @@ int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port,
 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
 }

-static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
+static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane,
 					  struct mv88e6352_serdes_hw_stat *stat)
 {
 	u64 val = 0;
 	u16 reg;
 	int err;

-	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
+	err = mv88e6352_serdes_read(chip, lane, stat->reg, &reg);
 	if (err) {
 		dev_err(chip->dev, "failed to read statistic\n");
 		return 0;
@@ -157,7 +172,7 @@ static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
 	val = reg;

 	if (stat->sizeof_stat == 32) {
-		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
+		err = mv88e6352_serdes_read(chip, lane, stat->reg + 1, &reg);
 		if (err) {
 			dev_err(chip->dev, "failed to read statistic\n");
 			return 0;
@@ -185,7 +200,7 @@ size_t mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,

 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
 		stat = &mv88e6352_serdes_hw_stats[i];
-		value = mv88e6352_serdes_get_stat(chip, stat);
+		value = mv88e6352_serdes_get_stat(chip, MV88E6352_ADDR_SERDES, stat);
 		mv88e6xxx_port->serdes_stats[i] += value;
 		data[i] = mv88e6xxx_port->serdes_stats[i];
 	}
@@ -200,35 +215,40 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)

 int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
 {
-	int err;
+	int lane = -ENODEV;

-	mv88e6xxx_reg_lock(chip);
-	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
-	mv88e6xxx_reg_unlock(chip);
-	if (err <= 0)
-		return err;
+	lane = mv88e6xxx_serdes_get_lane(chip, port);
+	if (lane < 0)
+		return 0;

 	return 32 * sizeof(u16);
 }

-void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
+static void mv88e6352_serdes_get_regs_from_lane(struct mv88e6xxx_chip *chip, int lane, void *_p)
 {
 	u16 *p = _p;
 	u16 reg;
 	int err;
 	int i;

-	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
-	if (err <= 0)
-		return;
-
 	for (i = 0 ; i < 32; i++) {
-		err = mv88e6352_serdes_read(chip, i, &reg);
+		err = mv88e6352_serdes_read(chip, lane, i, &reg);
 		if (!err)
 			p[i] = reg;
 	}
 }

+void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
+{
+	int err;
+
+	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
+	if (err <= 0)
+		return;
+
+	mv88e6352_serdes_get_regs_from_lane(chip, MV88E6352_ADDR_SERDES, _p);
+}
+
 int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
 {
 	u8 cmode = chip->ports[port].cmode;
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h
index 17a3e85fabaa..21e050b328cc 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.h
+++ b/drivers/net/dsa/mv88e6xxx/serdes.h
@@ -115,6 +115,7 @@ int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa,
 			       u16 status, struct phylink_link_state *state);

 int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
+int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
 int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
 int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
 int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
--
2.36.0


^ permalink raw reply related

* [PATCH net-next v2 0/2] mv88e6xxx: SERDES on mv88e6321 letter
From: Fidan Aliyeva @ 2026-04-30 12:49 UTC (permalink / raw)
  To: andrew, olteanv, davem, edumazet, kuba, pabeni, netdev
  Cc: linux-kernel, thomas.eckerman.ext, Fidan Aliyeva

This patch series add code support to be able to use SERDES feature of
mv88e6321 version of Marvel mv88e6xxx series. mv88e6321 has 2 ports to
support high speed SERDES but the support is lacking in the driver.

mv88e6321 version has a similar architecture to mv88e6352 version making it
possible to reuse its pcs functions. That's why the patch series consist of
2 parts:
1. Refactor the serdes functions and pcs_init of mv88e6352 to be more
generic
2. Add the SERDES support for mv88e6321 reusing 6352's pcs functions

The final code has been built on top of net-next tree and tested on
mv88e6321 ethernet device directly by ip ping tests, performance tests and
also verifying the switch's expected register values.

Referred document: 88E6321/88E6320 Functional Specification

Code has been built with allmodconfig and allyesconfig. checkpatch.pl was
also run

---
Changes in v2:
  - Removed 6321-specific pcs_init and made 6352's pcs_init more generic
  as suggested by Andrew Lunn
  - Added the correct mailing list

---
Fidan Aliyeva (2):
  mv88e6xxx: Refactor 6352's serdes functions
  mv88e6xxx: Add SERDES Support for mv88e6321

 drivers/net/dsa/mv88e6xxx/chip.c     |  8 +++
 drivers/net/dsa/mv88e6xxx/pcs-6352.c | 12 ++--
 drivers/net/dsa/mv88e6xxx/serdes.c   | 87 ++++++++++++++++++++++------
 drivers/net/dsa/mv88e6xxx/serdes.h   |  5 ++
 4 files changed, 86 insertions(+), 26 deletions(-)

--
2.36.0


^ 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