* [PATCH net-next v8 0/3] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand
From: Lorenzo Bianconi @ 2026-07-03 9:19 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev, Madhur Agrawal
Add the capability to configure GDM3/GDM4 as WAN/LAN on demand when QoS
offload is created or destroyed.
Make dev->qdma an RCU pointer so the TX path can safely dereference it
without holding RTNL.
Introduce airoha_qdma_start() and airoha_qdma_stop() helpers.
---
Changes in v8:
- Rebase on top of next-next to fix conflicts.
- Link to v7: https://lore.kernel.org/r/20260701-airoha-ethtool-priv_flags-v7-0-b4153bd44428@kernel.org
Changes in v7:
- Fix ETS stats accounting in patch 2/3
- Reset ETS stats accounting in airoha_dev_set_qdma().
- Link to v6: https://lore.kernel.org/r/20260629-airoha-ethtool-priv_flags-v6-0-86bc600d31bc@kernel.org
Changes in v6:
- Rebase on top of next-next
- Add patch 1/3: "rename airoha_priv_flags to airoha_dev_flags"
- Drop patch 2/3: "refactor QDMA start/stop into reusable helpers"
- Link to v5: https://lore.kernel.org/r/20260611-airoha-ethtool-priv_flags-v5-0-c11de08486d1@kernel.org
Changes in v5:
- Add patch 1/3: use int instead of atomic_t for qdma users counter
- Protect dev->flags with flow_offload_mutex mutex.
- Introduce AIROHA_PRIV_F_QOS in order to handle better WAN/LAN
switching.
- Link to v4: https://lore.kernel.org/r/20260610-airoha-ethtool-priv_flags-v4-0-60e89cf28fea@kernel.org
Changes in v4:
- Move back QDMA TX/RX DMA enable to airoha_dev_open()/airoha_dev_stop().
- Configure GDM3/4 as WAN if GDM2 is not available in ndo_init()
callback.
- Protect qdma pointer in airoha_gdm_dev struct using RCU.
- Rely on rtnl_dereference() to access qdma pointer in the control path.
- Add airoha_qdma_start() and airoha_qdma_stop() utility routines in
patch 1/2
- Link to v3: https://lore.kernel.org/r/20260608-airoha-ethtool-priv_flags-v3-1-3e8e3dc3f715@kernel.org
Changes in v3:
- Do not introduce ethtool private flags support to configure LAN/WAN
for GDM3/4 and rely on tc qdisc offload for it instead.
- Set GDM3/4 ports as LAN by default.
- Move QDMA TX/RX DMA enable from airoha_dev_open() to airoha_probe()
and the corresponding disable from airoha_dev_stop() to airoha_qdma_cleanup().
- Link to v2: https://lore.kernel.org/r/20260607-airoha-ethtool-priv_flags-v2-1-742c7aa1e182@kernel.org
Changes in v2:
- Rework airoha_dev_set_wan_flag routine
- Enable GDM_STRIP_CRC_MASK in airoha_disable_gdm2_loopback()
- Do not always reset REG_SRC_PORT_FC_MAP6 in
airoha_disable_gdm2_loopback() but use the same condition used in
airoha_enable_gdm2_loopback().
- Link to v1: https://lore.kernel.org/r/20260606-airoha-ethtool-priv_flags-v1-1-401b2c9fe9f1@kernel.org
---
Lorenzo Bianconi (3):
net: airoha: rename airoha_priv_flags to airoha_dev_flags
net: airoha: fix ETS QoS stats counter underflow and cross-channel corruption
net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload
drivers/net/ethernet/airoha/airoha_eth.c | 253 ++++++++++++++++++++++++++----
drivers/net/ethernet/airoha/airoha_eth.h | 26 ++-
drivers/net/ethernet/airoha/airoha_ppe.c | 9 +-
drivers/net/ethernet/airoha/airoha_regs.h | 1 +
4 files changed, 246 insertions(+), 43 deletions(-)
---
base-commit: 6fb33632323a396c9dc2bb9bea483e013e547d57
change-id: 20260606-airoha-ethtool-priv_flags-b6aa70caa780
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply
* Re: [RFC PATCH 3/3] coredump, net: remove `SOCK_COREDUMP`
From: John Ericson @ 2026-07-03 9:08 UTC (permalink / raw)
To: Christian Brauner, John Ericson
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Cong Wang, Kuniyuki Iwashima, Simon Horman, David Rheinsberg,
Andy Lutomirski, Sergei Zimmerman, network dev, linux-fsdevel,
Mickaël Salaün, Günther Noack, Paul Moore,
linux-security-module, LKML
In-Reply-To: <20260703-umgehen-auftun-anlagen-826bf59dfcb5@brauner>
On Fri, Jul 3, 2026, at 4:11 AM, Christian Brauner wrote:
> I don't think dragging the bowels of net/ into fs/coredump.c just for
> the sake of getting a purely internal SOCK_COREDUMP flag out of the way
> is the right way to go.
>
> I suspect the saner thing to do would be to introduce a primitive for
> connecting to an AF_UNIX path-based socket directly instead of this
> weird dance here, no?
>
> int kernel_unix_connect(const char *path, struct socket *socket, unsigned int o_flags, int type)
>
> then my kthread changes would collapse this into:
>
> scoped_with_init_fs()
> retval = kernel_unix_connect(path, socket, O_NONBLOCK, SOCK_STREAM);
That works for this case, but the destination I am trying to eventually
reach is being allowed (in userspace) to connect to unbound sockets by
their fd --- no path, no abstract socket name.
unix_connectat(int fd, const char *path, struct socket *socket,
unsigned int at_flags, unsigned int o_flags,
int type)
is a mouthful, but it would work. Still, there is a subtlety about the
retry logic. When one does something like:
> connect(..."/dev/fd/N",...)
it will repeatedly re-lookup "/dev/fd/N" until the timeout is reached. I
consider that pretty terrible --- the rest of the program could race to
change what that file descriptor (number) refers to. Therefore, I think
table stakes to make a good `unix_connectat` is to make the
`AT_EMPTY_PATH` case not re-lookup the socket.
(making a sockfs file descriptor work is separate, I already have the
patch for that, I can include it.)
> The two helpers also make no sense to me and force a bunch of cleanup on
> the caller as well.
I am fine with `unix_connectat`, but just for reference, me breaking up
the steps is because I generally like the decomposition of `fverb`
rather than `verbat` system calls, since the latter would typically be
used with `openat` or so. It seems the `kernel_*` "syscalls" generally
take `struct path` rather than strings, which seems good and in the
spirit of that decomposition, but then even `struct path` is overkill to
refer to a socket. So putting all that together, the final composition I
had was:
1. string path (from /proc/...) -> `struct path`
2. `struct path` -> `struct sock *`
3. connect to `struct sock *`
So I quite liked those 3 orthogonal knobs, vs an all-singing-all-dancing
`unix_connectat`. (I suppose making it `struct socket *` would make it
slightly less internals-y?) But again, anything that puts us on track to
being able to connect to an unbound socket without procfs is good enough
for me.
> I'm not sure why we would go on altering all kinds of LSM hooks as well.
That's in the commit message, it is because without `SOCK_COREDUMP`
those are all dead code. Instead of removing them in this commit, I can
just keep those flags, or remove them in a separate commit. Fine
with any of those.
John
^ permalink raw reply
* Re: [PATCH net] bnge/bng_re: fix ring ID widths
From: Vikas Gupta @ 2026-07-03 8:59 UTC (permalink / raw)
To: Paolo Abeni
Cc: davem, edumazet, kuba, andrew+netdev, horms, netdev, linux-kernel,
linux-rdma, leonro, jgg, bhargava.marreddy, rahul-rg.gupta,
vsrama-krishna.nemani, rajashekar.hudumula, ajit.khaparde,
Siva Reddy Kallam, Dharmender Garg,
Yendapally Reddy Dhananjaya Reddy
In-Reply-To: <55e4bc6f-f393-4e76-9220-a818b28c585b@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 1256 bytes --]
On Fri, Jul 3, 2026 at 1:03 PM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On 6/30/26 12:15 PM, Vikas Gupta wrote:
> > diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h
> > index 341c7f81ed09..bb0c79a1ee60 100644
> > --- a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h
> > +++ b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h
> > @@ -184,7 +184,7 @@ struct bnge_ctx_mem_info {
> > struct bnge_ring_struct {
> > struct bnge_ring_mem_info ring_mem;
> >
> > - u16 fw_ring_id;
> > + u32 fw_ring_id;
>
> Sashiko gemini has a few concerns about the id size increases:
>
> https://sashiko.dev/#/patchset/20260630101554.1221733-1-vikas.gupta%40broadcom.com
>
> please have a look.
Sashiko's concern about backward compatibility is valid, however, the
driver is not expected to work with older firmware because the Thor
Ultra device has not been deployed yet.
The concern about overlapping bits is also valid, but the firmware
uses a ring ID with a maximum width of 18 bits, so no overlap occurs.
Nevertheless, I will add a mask for xid to improve code clarity and
readability.
Thanks,
Vikas
>
>
> /P
>
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5465 bytes --]
^ permalink raw reply
* Re: [PATCH net] gtp: parse extension headers before reading inner protocol
From: Pablo Neira Ayuso @ 2026-07-03 8:54 UTC (permalink / raw)
To: Zhixing Chen
Cc: Harald Welte, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, osmocom-net-gprs, netdev
In-Reply-To: <20260703084244.59077-1-running910@gmail.com>
On Fri, Jul 03, 2026 at 04:42:44PM +0800, Zhixing Chen wrote:
> GTPv1-U packets may carry a chain of extension headers before the inner
> IP packet. The receive path already parses and skips these extension
> headers, but it currently reads the inner protocol before doing so.
>
> As a result, the first extension header byte is interpreted as the inner
> IP version. Packets with extension headers are then dropped before PDP
> lookup.
>
> Parse the extension header chain before calling gtp_inner_proto(), so the
> inner protocol is read from the actual inner IP header.
Missing Fixes: tag.
> Signed-off-by: Zhixing Chen <running910@gmail.com>
> ---
>
> I noticed this while running a few GTP tunnel tests with a veth pair and a
> peer network namespace.
>
> The commands below only set up a small GTPv1-U demo and verify that the
> plain tunnel works as expected:
>
> ip link add vroot type veth peer name vpeer
> ip a a 172.0.0.1/24 dev vroot
> ip link set vroot up
> ip a a 172.99.0.1/32 dev lo
> gtp-link add gtp0 ip 172.0.0.1 &
> gtp-tunnel add gtp0 v1 200 100 172.99.0.2 172.0.0.2
> ip r a 172.99.0.2/32 dev gtp0
> ip link set gtp0 mtu 1500
>
> ip netns add nspeer
> ip link set vpeer netns nspeer
> ip netns exec nspeer ip a a 172.0.0.2/24 dev vpeer
> ip netns exec nspeer ip link set vpeer up
> ip netns exec nspeer ip a a 172.99.0.2/32 dev lo
> ip netns exec nspeer ip link set lo up
>
> ip netns exec nspeer gtp-link add gtp1 ip 172.0.0.2 &
> ip netns exec nspeer gtp-tunnel add gtp1 v1 100 200 172.99.0.1 172.0.0.1
> ip netns exec nspeer ip r a 172.99.0.1/32 dev gtp1
> ip netns exec nspeer ip link set gtp1 mtu 1500
>
> With this setup, plain traffic between 172.99.0.1 and 172.99.0.2 goes
> through the GTP tunnel.
>
> After that, I used a small sender in the peer namespace to build two
> UDP/2152 packets for the root namespace GTP endpoint. Both packets use TEID
> 200 and carry an inner UDP packet from 172.99.0.2:12345 to
> 172.99.0.1:9999. The first packet is a plain GTPv1-U T-PDU. The second
> packet carries the same inner UDP packet after a GTP extension header.
>
> Before this fix, a receiver bound to 172.99.0.1:9999 only receives the
> plain packet:
>
> root@vm:/# python gtp_nsroot_recv.py
> listening on 172.99.0.1:9999
> received #1 from ('172.99.0.2', 12345): b'plain'
>
> After this fix, it receives both packets:
>
> root@vm:/# python gtp_nsroot_recv.py
> listening on 172.99.0.1:9999
> received #1 from ('172.99.0.2', 12345): b'plain'
> received #2 from ('172.99.0.2', 12345): b'extension'
>
> ---
> drivers/net/gtp.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
> index a60ef32b35b8..4a8b00548673 100644
> --- a/drivers/net/gtp.c
> +++ b/drivers/net/gtp.c
> @@ -826,6 +826,10 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
> if (!pskb_may_pull(skb, hdrlen))
> return -1;
>
> + if (gtp1->flags & GTP1_F_EXTHDR &&
> + gtp_parse_exthdrs(skb, &hdrlen) < 0)
> + return -1;
> +
> if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
> netdev_dbg(gtp->dev, "GTP packet does not encapsulate an IP packet\n");
> return -1;
> @@ -840,10 +844,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
> return 1;
> }
>
> - if (gtp1->flags & GTP1_F_EXTHDR &&
> - gtp_parse_exthdrs(skb, &hdrlen) < 0)
> - return -1;
> -
> return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto);
> }
>
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH net] net/stmmac: Set Rx queue page_pool to NULL when freeing DMA resources
From: Jakub Raczynski @ 2026-07-03 8:43 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, andrew+netdev, davem, edumazet, kuba, mcoquelin.stm32,
linux-kernel, k.tegowski, k.domagalski, yashwant.v
In-Reply-To: <20260703072651.110614-1-pabeni@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 2140 bytes --]
For starters I would like to comment that I am sending this fix because this
actually happened in our environment, so this is not some fake AI report,
which I would credit with Reported-by if it was.
Sadly, no panic logs available since it happened long ago and this fix has
been for a while in our code.
Comments for AI review below
On Fri, Jul 03, 2026 at 09:26:51AM +0200, Paolo Abeni wrote:
> From: AI Reviewer <ai@example.com>
>
> - read rx_q->buf_pool[i] in dma_free_rx_skbufs() ->
> stmmac_free_rx_buffer() from already-freed slab memory:
>
> static void stmmac_free_rx_buffer(...)
> {
> struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
> if (buf->page)
> page_pool_put_full_page(rx_q->page_pool, buf->page, false);
> ...
> }
Comment invalid, I love how AI ignored literally next line, a bit more code:
>> static void stmmac_free_rx_buffer(struct stmmac_priv *priv,
>> struct stmmac_rx_queue *rx_q,
>> int i)
>>{
>> struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
>>
>> if (buf->page)
>> page_pool_put_full_page(rx_q->page_pool, buf->page, false);
>> buf->page = NULL;
>
> - call dma_free_coherent() a second time on the stale
> rx_q->dma_rx/dma_erx and rx_q->dma_rx_phy?
That is completely separate issue and fix is not what AI suggets.
Because if we zero rx_q->dma_erx or rx_q->dma_rx, dma_free_coherent() will run
regardless on NULL pointer then. We probably should lock dma_free_coherent()
behind simple NULL check and zero it, but this will be separate patch if so.
>
> - call kfree() a second time on the stale rx_q->buf_pool?
Invalid, kfree() does have 'if (unlikely(ZERO_OR_NULL_PTR(object)))' check
>
> Would also nulling rx_q->buf_pool, rx_q->dma_rx, rx_q->dma_erx, and
> rx_q->dma_rx_phy in __free_dma_rx_desc_resources() (or having the alloc
> path defensively reset them before any early return) be appropriate to
> cover the same failure mode the commit message describes?
"Would this be appropriate to cover same failures in same commit"
- Nah
"[...] in same patchset
- Maybe
BR
Jakub Raczynski
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* [PATCH net] gtp: parse extension headers before reading inner protocol
From: Zhixing Chen @ 2026-07-03 8:42 UTC (permalink / raw)
To: Pablo Neira Ayuso, Harald Welte
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, osmocom-net-gprs, netdev, Zhixing Chen
GTPv1-U packets may carry a chain of extension headers before the inner
IP packet. The receive path already parses and skips these extension
headers, but it currently reads the inner protocol before doing so.
As a result, the first extension header byte is interpreted as the inner
IP version. Packets with extension headers are then dropped before PDP
lookup.
Parse the extension header chain before calling gtp_inner_proto(), so the
inner protocol is read from the actual inner IP header.
Signed-off-by: Zhixing Chen <running910@gmail.com>
---
I noticed this while running a few GTP tunnel tests with a veth pair and a
peer network namespace.
The commands below only set up a small GTPv1-U demo and verify that the
plain tunnel works as expected:
ip link add vroot type veth peer name vpeer
ip a a 172.0.0.1/24 dev vroot
ip link set vroot up
ip a a 172.99.0.1/32 dev lo
gtp-link add gtp0 ip 172.0.0.1 &
gtp-tunnel add gtp0 v1 200 100 172.99.0.2 172.0.0.2
ip r a 172.99.0.2/32 dev gtp0
ip link set gtp0 mtu 1500
ip netns add nspeer
ip link set vpeer netns nspeer
ip netns exec nspeer ip a a 172.0.0.2/24 dev vpeer
ip netns exec nspeer ip link set vpeer up
ip netns exec nspeer ip a a 172.99.0.2/32 dev lo
ip netns exec nspeer ip link set lo up
ip netns exec nspeer gtp-link add gtp1 ip 172.0.0.2 &
ip netns exec nspeer gtp-tunnel add gtp1 v1 100 200 172.99.0.1 172.0.0.1
ip netns exec nspeer ip r a 172.99.0.1/32 dev gtp1
ip netns exec nspeer ip link set gtp1 mtu 1500
With this setup, plain traffic between 172.99.0.1 and 172.99.0.2 goes
through the GTP tunnel.
After that, I used a small sender in the peer namespace to build two
UDP/2152 packets for the root namespace GTP endpoint. Both packets use TEID
200 and carry an inner UDP packet from 172.99.0.2:12345 to
172.99.0.1:9999. The first packet is a plain GTPv1-U T-PDU. The second
packet carries the same inner UDP packet after a GTP extension header.
Before this fix, a receiver bound to 172.99.0.1:9999 only receives the
plain packet:
root@vm:/# python gtp_nsroot_recv.py
listening on 172.99.0.1:9999
received #1 from ('172.99.0.2', 12345): b'plain'
After this fix, it receives both packets:
root@vm:/# python gtp_nsroot_recv.py
listening on 172.99.0.1:9999
received #1 from ('172.99.0.2', 12345): b'plain'
received #2 from ('172.99.0.2', 12345): b'extension'
---
drivers/net/gtp.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index a60ef32b35b8..4a8b00548673 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -826,6 +826,10 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
if (!pskb_may_pull(skb, hdrlen))
return -1;
+ if (gtp1->flags & GTP1_F_EXTHDR &&
+ gtp_parse_exthdrs(skb, &hdrlen) < 0)
+ return -1;
+
if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
netdev_dbg(gtp->dev, "GTP packet does not encapsulate an IP packet\n");
return -1;
@@ -840,10 +844,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
return 1;
}
- if (gtp1->flags & GTP1_F_EXTHDR &&
- gtp_parse_exthdrs(skb, &hdrlen) < 0)
- return -1;
-
return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto);
}
--
2.34.1
^ permalink raw reply related
* [PATCH net] macsec: don't read an unset MAC header in macsec_encrypt()
From: Daehyeon Ko @ 2026-07-03 8:36 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel, Daehyeon Ko, stable
macsec_encrypt() reads the Ethernet header via eth_hdr(skb)
(skb->head + skb->mac_header) to memmove() the 12 source/destination MAC
bytes forward and make room for the SecTAG.
On the AF_PACKET SOCK_RAW + PACKET_QDISC_BYPASS transmit path the skb
reaches the macsec ndo_start_xmit() with the MAC header unset, so
eth_hdr(skb) resolves to skb->head + (u16)~0 and the read is out of
bounds: a 12-byte heap over-read that is also emitted on the wire as the
frame's outer source/destination MAC. KASAN reports a slab-out-of-bounds
read in macsec_start_xmit() on 6.0; on current mainline a CONFIG_DEBUG_NET
build flags it as an unset mac header in skb_mac_header().
On the TX path the L2 header is at skb->data, so use skb_eth_hdr(), added
by commit 96cc4b69581d ("macvlan: do not assume mac_header is set in
macvlan_broadcast()") for exactly this purpose.
Fixes: c09440f7dcb3 ("macsec: introduce IEEE 802.1AE driver")
Cc: stable@vger.kernel.org
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
drivers/net/macsec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index fb009120a924..dd89282f0179 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -646,7 +646,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
}
unprotected_len = skb->len;
- eth = eth_hdr(skb);
+ eth = skb_eth_hdr(skb);
sci_present = macsec_send_sci(secy);
hh = skb_push(skb, macsec_extra_len(sci_present));
memmove(hh, eth, 2 * ETH_ALEN);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net] gue: validate REMCSUM private option length
From: patchwork-bot+netdevbpf @ 2026-07-03 8:35 UTC (permalink / raw)
To: Qihang; +Cc: netdev, edumazet, kuba, davem, pabeni
In-Reply-To: <20260701022617.53171-1-q.h.hack.winter@gmail.com>
Hello:
This patch was applied to netdev/net.git (main)
by David S. Miller <davem@davemloft.net>:
On Wed, 1 Jul 2026 10:26:17 +0800 you wrote:
> GUE private flags can indicate that remote checksum offload metadata is
> present. The private flags field itself is accounted for by
> guehdr_flags_len(), but guehdr_priv_flags_len() currently returns 0 even
> when GUE_PFLAG_REMCSUM is set.
>
> This lets a packet with only the private flags field pass
> validate_gue_flags(), after which gue_remcsum() and gue_gro_remcsum()
> read the missing REMCSUM start/offset fields from the following bytes.
>
> [...]
Here is the summary with links:
- [net] gue: validate REMCSUM private option length
https://git.kernel.org/netdev/net/c/d335dcc6f521
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH] wanxl: Remove pci_map_single_debug()
From: Bence Csokas @ 2026-07-03 8:21 UTC (permalink / raw)
To: Krzysztof Halasa, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel
Cc: Bence Csokas
Since commit 24dd377a76b0 ("wan: wanxl: switch from 'pci_' to 'dma_' API")
this has been dead code anyways. The pci_map_single() function it attempts
to redefine has been removed in 7968778914e5 ("PCI: Remove the deprecated
"pci-dma-compat.h" API").
Signed-off-by: Bence Csokas <bence.csokas@arm.com>
---
drivers/net/wan/wanxl.c | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c
index d4da88c77112..1540ce0a499d 100644
--- a/drivers/net/wan/wanxl.c
+++ b/drivers/net/wan/wanxl.c
@@ -88,22 +88,6 @@ static inline port_status_t *get_status(struct port *port)
return &port->card->status->port_status[port->node];
}
-#ifdef DEBUG_PCI
-static inline dma_addr_t pci_map_single_debug(struct pci_dev *pdev, void *ptr,
- size_t size, int direction)
-{
- dma_addr_t addr = dma_map_single(&pdev->dev, ptr, size, direction);
-
- if (addr + size > 0x100000000LL)
- pr_crit("%s: pci_map_single() returned memory at 0x%llx!\n",
- pci_name(pdev), (unsigned long long)addr);
- return addr;
-}
-
-#undef pci_map_single
-#define pci_map_single pci_map_single_debug
-#endif
-
/* Cable and/or personality module change interrupt service */
static inline void wanxl_cable_intr(struct port *port)
{
---
base-commit: 51512e22efe813d8223de27f6fd02a8a48ea2323
change-id: 20260427-wanxl-cleanup-7e59607dc4f9
Best regards,
--
Bence Csokas <bence.csokas@arm.com>
^ permalink raw reply related
* [PATCH net-next v2 3/3] af_unix: Clean up error handling in unix_listen()
From: John Ericson @ 2026-07-03 8:14 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Kuniyuki Iwashima
Cc: John Ericson, Simon Horman, Christian Brauner, David Rheinsberg,
Cong Wang, John Ericson, Sergei Zimmerman, netdev, linux-kernel
In-Reply-To: <20260703081416.2583118-1-John.Ericson@Obsidian.Systems>
From: John Ericson <mail@johnericson.me>
To avoid the sort of bug that was just fixed going forward, switch to a
style where `err = -E...;` instead happens right before the `goto`.
(`err = other_function(...);` is not changed.) Then there is no
spooky-action-at-a-distance between the `err` initialization and the
`goto`, something which is easier to slip by code review.
Assisted-by: Claude:claude-fable-5
Signed-off-by: John Ericson <mail@johnericson.me>
---
net/unix/af_unix.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 10ed9421e43a..7878b27bbaf8 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -813,19 +813,22 @@ static int unix_listen(struct socket *sock, int backlog)
struct unix_sock *u = unix_sk(sk);
struct unix_peercred peercred = {};
- err = -EOPNOTSUPP;
- if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
+ if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET) {
+ err = -EOPNOTSUPP;
goto out; /* Only stream/seqpacket sockets accept */
- err = -EINVAL;
- if (!READ_ONCE(u->addr))
+ }
+ if (!READ_ONCE(u->addr)) {
+ err = -EINVAL;
goto out; /* No listens on an unbound socket */
+ }
err = prepare_peercred(&peercred);
if (err)
goto out;
unix_state_lock(sk);
- err = -EINVAL;
- if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
+ if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) {
+ err = -EINVAL;
goto out_unlock;
+ }
if (backlog > sk->sk_max_ack_backlog)
wake_up_interruptible_all(&u->peer_wait);
sk->sk_max_ack_backlog = backlog;
--
2.54.0
^ permalink raw reply related
* [PATCH net v2 2/3] selftests/net/af_unix: test listen() rejects wrong socket states
From: John Ericson @ 2026-07-03 8:14 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Kuniyuki Iwashima
Cc: John Ericson, Simon Horman, Christian Brauner, David Rheinsberg,
Cong Wang, John Ericson, Sergei Zimmerman, netdev, linux-kernel
In-Reply-To: <20260703081416.2583118-1-John.Ericson@Obsidian.Systems>
From: John Ericson <mail@johnericson.me>
Add a regression test for the `unix_listen()` state check. The key case
is `listen()` on a bound socket that has already been connected: it is
no longer in `TCP_CLOSE` or `TCP_LISTEN`, so it must fail with `EINVAL`.
A `prepare_peercred()` call slipped in ahead of that check once left
`err` at 0 and made `listen()` silently succeed there instead; this
guards against a repeat.
The neighbouring outcomes are covered too so they cannot regress the
same way: a bound socket in `TCP_CLOSE` listens fine, re-`listen()`ing a
socket already in `TCP_LISTEN` is allowed, and an unbound socket fails
with `EINVAL`.
Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: John Ericson <mail@johnericson.me>
---
.../testing/selftests/net/af_unix/.gitignore | 1 +
tools/testing/selftests/net/af_unix/Makefile | 1 +
.../selftests/net/af_unix/unix_listen.c | 126 ++++++++++++++++++
3 files changed, 128 insertions(+)
create mode 100644 tools/testing/selftests/net/af_unix/unix_listen.c
diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore
index 240b26740c9e..973176644103 100644
--- a/tools/testing/selftests/net/af_unix/.gitignore
+++ b/tools/testing/selftests/net/af_unix/.gitignore
@@ -6,3 +6,4 @@ scm_rights
so_peek_off
unix_connect
unix_connreset
+unix_listen
diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile
index 4c0375e28bbe..57d159803a3a 100644
--- a/tools/testing/selftests/net/af_unix/Makefile
+++ b/tools/testing/selftests/net/af_unix/Makefile
@@ -14,6 +14,7 @@ TEST_GEN_PROGS := \
so_peek_off \
unix_connect \
unix_connreset \
+ unix_listen \
# end of TEST_GEN_PROGS
include ../../lib.mk
diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c
new file mode 100644
index 000000000000..7b5264c97f52
--- /dev/null
+++ b/tools/testing/selftests/net/af_unix/unix_listen.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Tests for the state checks in AF_UNIX listen().
+ *
+ * The central case is a regression test: listen() on a bound socket that
+ * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must
+ * fail with EINVAL. A prior change accidentally let it return success
+ * without doing anything, because a helper called in between reset the
+ * error code to 0. The neighbouring checks (unbound, already listening)
+ * are tested too so they cannot silently regress the same way.
+ */
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "kselftest_harness.h"
+
+/* Fill @addr with an abstract-namespace address named @name. */
+static socklen_t unix_abstract(struct sockaddr_un *addr, const char *name)
+{
+ size_t len = strlen(name);
+
+ memset(addr, 0, sizeof(*addr));
+ addr->sun_family = AF_UNIX;
+ /* Leading NUL selects the abstract namespace (no filesystem entry). */
+ memcpy(addr->sun_path + 1, name, len);
+
+ return offsetof(struct sockaddr_un, sun_path) + 1 + len;
+}
+
+FIXTURE(unix_listen)
+{
+ int sk; /* socket under test */
+ int server; /* a listening peer, when a test needs one */
+ struct sockaddr_un addr, srv_addr;
+ socklen_t addrlen, srv_addrlen;
+};
+
+FIXTURE_SETUP(unix_listen)
+{
+ self->sk = -1;
+ self->server = -1;
+ self->addrlen = unix_abstract(&self->addr, "unix_listen.sk");
+ self->srv_addrlen = unix_abstract(&self->srv_addr, "unix_listen.srv");
+}
+
+FIXTURE_TEARDOWN(unix_listen)
+{
+ if (self->sk >= 0)
+ close(self->sk);
+ if (self->server >= 0)
+ close(self->server);
+ /* Abstract addresses are released automatically on close. */
+}
+
+/* A bound socket in TCP_CLOSE is the normal, allowed case. */
+TEST_F(unix_listen, bound_is_ok)
+{
+ self->sk = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, self->sk);
+ ASSERT_EQ(0, bind(self->sk, (struct sockaddr *)&self->addr,
+ self->addrlen));
+
+ EXPECT_EQ(0, listen(self->sk, 8));
+}
+
+/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */
+TEST_F(unix_listen, relisten_is_ok)
+{
+ self->sk = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, self->sk);
+ ASSERT_EQ(0, bind(self->sk, (struct sockaddr *)&self->addr,
+ self->addrlen));
+ ASSERT_EQ(0, listen(self->sk, 8));
+
+ EXPECT_EQ(0, listen(self->sk, 16));
+}
+
+/* listen() on an unbound socket fails: there is nothing to listen on. */
+TEST_F(unix_listen, unbound_is_einval)
+{
+ int ret;
+
+ self->sk = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, self->sk);
+
+ ret = listen(self->sk, 8);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+}
+
+/*
+ * The regression: a bound socket that has already been connected is not in
+ * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather
+ * than quietly succeeding.
+ */
+TEST_F(unix_listen, connected_is_einval)
+{
+ int ret;
+
+ self->server = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, self->server);
+ ASSERT_EQ(0, bind(self->server, (struct sockaddr *)&self->srv_addr,
+ self->srv_addrlen));
+ ASSERT_EQ(0, listen(self->server, 8));
+
+ self->sk = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, self->sk);
+ /* Bind first so the unbound check does not mask the state check. */
+ ASSERT_EQ(0, bind(self->sk, (struct sockaddr *)&self->addr,
+ self->addrlen));
+ ASSERT_EQ(0, connect(self->sk, (struct sockaddr *)&self->srv_addr,
+ self->srv_addrlen));
+
+ ret = listen(self->sk, 8);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+}
+
+TEST_HARNESS_MAIN
--
2.54.0
^ permalink raw reply related
* [PATCH net v2 1/3] af_unix: fix listen() succeeding on sockets in the wrong state
From: John Ericson @ 2026-07-03 8:14 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Kuniyuki Iwashima
Cc: John Ericson, Simon Horman, Christian Brauner, David Rheinsberg,
Cong Wang, John Ericson, Sergei Zimmerman, netdev, linux-kernel
From: John Ericson <mail@johnericson.me>
Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for
reaped sk->sk_peer_pid") inserted a `prepare_peercred()` call between
`err = -EINVAL` and the socket-state check in `unix_listen()`. Since
`prepare_peercred()` leaves `err` at 0 on success, `listen()` on an
AF_UNIX socket that is not in `TCP_CLOSE` or `TCP_LISTEN` state (e.g.
one that is already connected) now silently returns success without
doing anything, instead of failing with `EINVAL` as it did before.
Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
Assisted-by: Claude:claude-fable-5
Signed-off-by: John Ericson <mail@johnericson.me>
---
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..10ed9421e43a 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -823,6 +823,7 @@ static int unix_listen(struct socket *sock, int backlog)
if (err)
goto out;
unix_state_lock(sk);
+ err = -EINVAL;
if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
goto out_unlock;
if (backlog > sk->sk_max_ack_backlog)
--
2.54.0
^ permalink raw reply related
* Re: [RFC PATCH 3/3] coredump, net: remove `SOCK_COREDUMP`
From: Christian Brauner @ 2026-07-03 8:11 UTC (permalink / raw)
To: John Ericson
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
John Ericson, Cong Wang, Kuniyuki Iwashima, Simon Horman,
Christian Brauner, David Rheinsberg, Andy Lutomirski,
Sergei Zimmerman, netdev, linux-fsdevel, Mickaël Salaün,
Günther Noack, Paul Moore, linux-security-module,
linux-kernel
In-Reply-To: <20260703073948.2541875-4-John.Ericson@Obsidian.Systems>
> The utility of the refactors of the last two commits is demonstrated by
> fixing this glaring layer violation: the unix socket implementation
> knowing about the coredump socket caller.
>
> Before, when the only way to connect to a socket was via the UAPI
> `struct sockaddr_un`, the only way to implement the proper logic that
> the kernel needs to resolve the coredump socket path was via hacking it
> into the socket implementation.
>
> In addition to being quite ugly, this layer violation is not great for
> security. The intent is that `SOCK_COREDUMP` can only be used by the
> kernel, and to be clear, I have no reason to believe this is not
> correctly enforced today. But because of the many functions with flags
> arguments, this is not a locally-enforced invariant. Some change, at
> some point, could mess up, and allow user-provided flags to sneak in,
> and this strikes me as a mistake waiting to happen. At that point, a
> user could exploit this to connect to any socket it likes, bypassing
> permission checks.
>
> Now, with the two functions we've just previously factored out, we can
> fix the layering. The custom path lookup logic lives with the coredump
> caller, where it belongs. Once the right `struct path` is found
> (actually just the inode is needed), the coredump caller can resolve a
> `struct sock *` from it, and then directly connect to it. With this
> change, `SOCK_COREDUMP` is no longer needed at all, and can be deleted.
> The layer violation is gone, and the security footgun is gone with it.
>
> As an added bonus, remove `flags` parameters from a number of internal
> functions that no longer need them. They were just taking flags
> parameters for the sake of `SOCK_COREDUMP`, and so with that gone, those
> flag parameters are also no longer needed. If or when there is a new
> flag that motivates them, they can be added back.
>
> Tested that `coredump_socket_protocol_test` still passes.
>
> Signed-off-by: John Ericson <mail@JohnEricson.me>
>
> diff --git a/fs/coredump.c b/fs/coredump.c
> index e68a76ff92a3..e1452021218e 100644
> --- a/fs/coredump.c
> +++ b/fs/coredump.c
> @@ -14,6 +14,7 @@
> #include <linux/perf_event.h>
> #include <linux/highmem.h>
> #include <linux/spinlock.h>
> +#include <linux/cred.h>
> #include <linux/key.h>
> #include <linux/personality.h>
> #include <linux/binfmts.h>
> @@ -21,6 +22,7 @@
> #include <linux/sort.h>
> #include <linux/sched/coredump.h>
> #include <linux/sched/signal.h>
> +#include <linux/sched/task.h>
> #include <linux/sched/task_stack.h>
> #include <linux/utsname.h>
> #include <linux/pid_namespace.h>
> @@ -50,7 +52,6 @@
> #include <net/net_namespace.h>
> #include <net/sock.h>
> #include <uapi/linux/pidfd.h>
> -#include <uapi/linux/un.h>
> #include <uapi/linux/coredump.h>
>
> #include <linux/uaccess.h>
> @@ -668,17 +669,10 @@ static int umh_coredump_setup(struct subprocess_info *info, struct cred *new)
> static bool coredump_sock_connect(struct core_name *cn, struct coredump_params *cprm)
> {
> struct file *file __free(fput) = NULL;
> - struct sockaddr_un addr = {
> - .sun_family = AF_UNIX,
> - };
> - ssize_t addr_len;
> - int retval;
> + struct path root, path;
> struct socket *socket;
> -
> - addr_len = strscpy(addr.sun_path, cn->corename);
> - if (addr_len < 0)
> - return false;
> - addr_len += offsetof(struct sockaddr_un, sun_path) + 1;
> + struct sock *sk;
> + int retval;
>
> /*
> * It is possible that the userspace process which is supposed
> @@ -710,14 +704,37 @@ static bool coredump_sock_connect(struct core_name *cn, struct coredump_params *
> */
> pidfs_coredump(cprm);
>
> - retval = kernel_connect(socket, (struct sockaddr_unsized *)(&addr), addr_len,
> - O_NONBLOCK | SOCK_COREDUMP);
> + /*
> + * Resolve the socket path relative to init's root and with kernel
> + * credentials, and with symlinks, magic links and escaping the
> + * root all forbidden, so the dumping process cannot use its own
> + * filesystem view to redirect its core to an arbitrary socket.
> + */
> + task_lock(&init_task);
> + get_fs_root(init_task.fs, &root);
> + task_unlock(&init_task);
> +
> + scoped_with_kernel_creds()
> + retval = vfs_path_lookup(root.dentry, root.mnt, cn->corename,
> + LOOKUP_BENEATH | LOOKUP_NO_SYMLINKS |
> + LOOKUP_NO_MAGICLINKS, &path);
> + path_put(&root);
> + if (retval)
> + return false;
> +
> + /* Connect directly to the socket bound there, by fd not by name. */
> + sk = unix_lookup_bsd_path(&path, SOCK_STREAM);
> + path_put(&path);
> + if (IS_ERR(sk))
> + return false;
>
> + retval = kernel_unix_connect_direct(sk, socket, O_NONBLOCK);
I don't think dragging the bowels of net/ into fs/coredump.c just for
the sake of getting a purely internal SOCK_COREDUMP flag out of the way
is the right way to go.
The two helpers also make no sense to me and force a bunch of cleanup on
the caller as well.
I suspect the saner thing to do would be to introduce a primitive for
connecting to an AF_UNIX path-based socket directly instead of this
weird dance here, no?
int kernel_unix_connect(const char *path, struct socket *socket, unsigned int o_flags, int type)
then my kthread changes would collapse this into:
scoped_with_init_fs()
retval = kernel_unix_connect(path, socket, O_NONBLOCK, SOCK_STREAM);
> + sock_put(sk);
> if (retval) {
> if (retval == -EAGAIN)
> - coredump_report_failure("Coredump socket %s receive queue full", addr.sun_path);
> + coredump_report_failure("Coredump socket %s receive queue full", cn->corename);
> else
> - coredump_report_failure("Coredump socket connection %s failed %d", addr.sun_path, retval);
> + coredump_report_failure("Coredump socket connection %s failed %d", cn->corename, retval);
> return false;
> }
>
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index 65c9609ec207..3d6fbb6d2628 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -323,8 +323,7 @@ LSM_HOOK(int, 0, watch_key, struct key *key)
> #endif /* CONFIG_SECURITY && CONFIG_KEY_NOTIFICATIONS */
>
> #if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
> -LSM_HOOK(int, 0, unix_find, const struct path *path, struct sock *other,
> - int flags)
> +LSM_HOOK(int, 0, unix_find, const struct path *path, struct sock *other)
I'm not sure why we would go on altering all kinds of LSM hooks as well.
--
Christian Brauner <brauner@kernel.org>
^ permalink raw reply
* Re: [PATCH 3/3] net: stmmac: dwmac-socfpga: Add mac-mode DT property support
From: Nazle Asmade, Muhammad Nazim Amirul @ 2026-07-03 8:10 UTC (permalink / raw)
To: Andrew Lunn
Cc: dinguyen@kernel.org, maxime.chevallier@bootlin.com,
rmk+kernel@armlinux.org.uk, krzk+dt@kernel.org,
conor+dt@kernel.org, robh@kernel.org, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
andrew+netdev@lunn.ch, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <e489be4a-5940-46e5-ae06-b78f1c919352@lunn.ch>
On 1/7/2026 10:43 pm, Andrew Lunn wrote:
> On Tue, Jun 30, 2026 at 06:31:08AM -0700, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
>> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
>>
>> Russell King's commit de696c63c1dc ("net: stmmac: socfpga: convert to
>> use phy_interface") replaced mac_interface with phy_interface in
>> socfpga_get_plat_phymode(), noting that no upstream DTS files set the
>> "mac-mode" property, making the two values identical.
>>
>> The Agilex5 SoCDK TSN Config2 board is an exception: its gmac1 TSN
>> port uses GMII internally in the MAC while the PHY-side interface is
>> RGMII, so mac-mode and phy-mode differ. Without restoring mac_interface
>> support, the MAC is configured with RGMII instead of GMII, causing
>> connectivity failures on this board.
>>
>> Add socfpga_of_get_mac_mode() to read the optional "mac-mode" DT
>> property and store it in a new mac_interface field. When the property
>> is absent, mac_interface falls back to phy_interface, preserving
>> the existing behaviour for all other boards.
>
> I don't actually see a need for mac-mode. From what you are saying,
> there is no choice. The MAC is hard wired to the converter block. So
> you can just look at the compatible. You are going to need to use the
> compatible anyway, to mask the phy-mode to handle the "MAC" doing the
> RGMII delays.
>
> Andrew
>
Hi Maxime, Andrew
Thanks for the reviews!
Hi Andrew,
The challenge with using compatible is that the TSN Config2 board has
two ports — only gmac1 uses the GMII-to-RGMII converter, while gmac2
connects directly to its PHY. A board-level compatible check would need
additional logic to know which port to apply the GMII override to.
With mac-mode, the configuration is per-port in the DTS — consistent
with how altr,emac-splitter works today. I also addressing Maxime's
feedback in v2 by unifying the splitter and mac-mode code paths.
BR,
Nazim
^ permalink raw reply
* Re: [PATCH net-next 1/2] net: ethernet: qualcomm: Unconstify function arguments passed by value
From: Jie Luo @ 2026-07-03 8:07 UTC (permalink / raw)
To: Krzysztof Kozlowski, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel
In-Reply-To: <20260702094908.79859-3-krzysztof.kozlowski@oss.qualcomm.com>
On 7/2/2026 5:49 PM, Krzysztof Kozlowski wrote:
> There is no benefit in marking "const" a pass-by-value (not a pointer)
> function argument, because it is passed as a copy on the stack. No code
> readability improvements, no additional compiler-time safety for misuse.
> Drop such redundant "const".
Reviewed-by: Luo Jie <jie.luo@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH net-next 2/2] net: ethernet: qualcomm: Constify "queue_map" in ppe_ring_queue_map_set()
From: Jie Luo @ 2026-07-03 8:06 UTC (permalink / raw)
To: Krzysztof Kozlowski, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel
In-Reply-To: <20260702094908.79859-4-krzysztof.kozlowski@oss.qualcomm.com>
On 7/2/2026 5:49 PM, Krzysztof Kozlowski wrote:
> "queue_map" is a pointer to "u32" and is not modified by the
> ppe_ring_queue_map_set() function, thus can be made a pointer to const to
> indicate that function is treating the pointed value read-only. This in
> general makes the code easier to follow and a bit safer.
Thanks for this improvement.
Reviewed-by: Luo Jie <jie.luo@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH net-next 00/17] net: dsa: mv88e6xxx: support for Remote Management Unit
From: Paolo Abeni @ 2026-07-03 8:05 UTC (permalink / raw)
To: Luke Howard, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Simon Horman, Russell King,
Richard Cochran, Florian Fainelli
Cc: Cedric Jehasse, Max Holtmann, Max Hunter, Kieran Tyrrell,
Ryan Wilkins, Mattias Forsblad, netdev, linux-kernel
In-Reply-To: <20260703-net-next-mv88e6xxx-rmu-v1-0-991a27d78bca@padl.com>
On 7/3/26 9:46 AM, Luke Howard wrote:
> This patch set shephards a series of commits made by Andrew Lunn
> and others to support remotely managing Marvell switches over
> Ethernet (as opposed to MDIO) using the Remote Management Unit
> (RMU).
>
> This is the second of four patch sets: the remaining two add
> support for remote management when MDIO is unavailable.
>
> Signed-off-by: Luke Howard <lukeh@padl.com>
You have already 35 patches pending. Please read carefully:
https://elixir.bootlin.com/linux/v7.1.2/source/Documentation/process/maintainer-netdev.rst
and specifically `Limit patches outstanding on mailing list`
Also note that the limit is supposed to be approached/reached by
submitters with a well known history of positive contributions (i.e.
reviews).
I'm deferring (at least) this series. Please repost only after that the
other pending ones have been processed.
/P
^ permalink raw reply
* Re: [PATCH net] gve: fix Rx queue stall on alloc failure
From: Eddie Phillips @ 2026-07-03 8:03 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: Harshitha Ramamurthy, netdev, joshwash, andrew+netdev, davem,
edumazet, kuba, pabeni, ast, daniel, hawk, john.fastabend, bpf,
sdf, willemb, jordanrhee, nktgrg, maolson, jacob.e.keller,
thostet, csully, bcf, linux-kernel, stable
In-Reply-To: <akUUXT6UwTTD2yOs@boxer>
> I think this deserves to be pulled out of the timer logic?
If by this you mean pull the stats into a separate patch, I agree.
> - couldn't you detect this case within napi poll loop?
It can only be detected after attempting to refill the queue and finding
that we are still below the critical threshold.
> - if not, does it have to be per-q timer? wouldn't one global per pf timer
> satisfy your needs?
There are a few ways a global timer could be implemented,
- The global timer could queue napi for *all* queues, which would
result in a lot of unnecessary work.
- The global timer could iterate over each queue and try to detect
the critical low buffer condition, however this would require
introducing synchronization between the timer and the napis, which
would introduce expensive locking into the hot path.
- The global timer could be paired with a bitmap that stores which
queues need to be serviced.
A `struct timer_list` is only 40 bytes, so the current implemention is
not expensive. Though a global timer is valid, it's not strictly better.
That said, I agree that we can clean up the structure—I will move the
timer state from the individual RX rings to the `gve_priv` structure.
On Wed, Jul 1, 2026 at 6:22 AM Maciej Fijalkowski
<maciej.fijalkowski@intel.com> wrote:
>
> On Wed, Jul 01, 2026 at 12:53:41AM +0000, Harshitha Ramamurthy wrote:
> > From: Eddie Phillips <eddiephillips@google.com>
> >
> > When the system is under extreme memory pressure, page allocations can
> > fail during the Rx buffer refill loop. If the number of buffers posted
> > to hardware falls below a critical low threshold and the refill loop
> > exits due to allocation failures, the queue can stall:
> >
> > 1. The device drops incoming packets because there are no descriptors.
> > 2. Since no packets are processed, no Rx completions are generated.
> > 3. Because no completions occur, NAPI is never scheduled, preventing
> > the refill loop from running again even after memory is freed.
> >
> > This results in a permanent queue stall.
> >
> > Resolve this by introducing a starvation recovery timer for each Rx queue.
> > If the number of buffers posted to hardware falls below a critical low
> > threshold, start a timer to periodically reschedule NAPI. Once NAPI runs
> > and successfully refills the queue above the threshold, the timer is
> > not rescheduled.
> >
> > Also add a new ethtool statistic "rx_critical_low_bufs" to track the
> > number of times the starvation recovery timer is triggered.
>
> I think this deserves to be pulled out of the timer logic?
>
> Two questions tho:
> - couldn't you detect this case within napi poll loop?
> - if not, does it have to be per-q timer? wouldn't one global per pf timer
> satisfy your needs?
>
> >
> > Cc: stable@vger.kernel.org
> > Fixes: 9b8dd5e5ea48 ("gve: DQO: Add RX path")
> > Reviewed-by: Jordan Rhee <jordanrhee@google.com>
> > Signed-off-by: Eddie Phillips <eddiephillips@google.com>
> > Signed-off-by: Harshitha Ramamurthy <hramamurthy@google.com>
> > ---
> > drivers/net/ethernet/google/gve/gve.h | 4 ++++
> > drivers/net/ethernet/google/gve/gve_ethtool.c | 14 +++++++++++++-
> > drivers/net/ethernet/google/gve/gve_rx_dqo.c | 32 ++++++++++++++++++++++++++++++++
> > 3 files changed, 49 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h
> > index 2f7bd330..8378bef2 100644
> > --- a/drivers/net/ethernet/google/gve/gve.h
> > +++ b/drivers/net/ethernet/google/gve/gve.h
> > @@ -13,6 +13,7 @@
> > #include <linux/netdevice.h>
> > #include <linux/net_tstamp.h>
> > #include <linux/pci.h>
> > +#include <linux/timer.h>
> > #include <linux/ptp_clock_kernel.h>
> > #include <linux/u64_stats_sync.h>
> > #include <net/page_pool/helpers.h>
> > @@ -41,6 +42,7 @@
> >
> > /* Interval to schedule a stats report update, 20000ms. */
> > #define GVE_STATS_REPORT_TIMER_PERIOD 20000
> > +#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */
> >
> > /* Numbers of NIC tx/rx stats in stats report. */
> > #define NIC_TX_STATS_REPORT_NUM 0
> > @@ -318,6 +320,7 @@ struct gve_rx_ring {
> > u64 rx_copied_pkt; /* free-running total number of copied packets */
> > u64 rx_skb_alloc_fail; /* free-running count of skb alloc fails */
> > u64 rx_buf_alloc_fail; /* free-running count of buffer alloc fails */
> > + u64 rx_critical_low_bufs; /* count of critical low buffer events */
> > u64 rx_desc_err_dropped_pkt; /* free-running count of packets dropped by descriptor error */
> > /* free-running count of unsplit packets due to header buffer overflow or hdr_len is 0 */
> > u64 rx_hsplit_unsplit_pkt;
> > @@ -334,6 +337,7 @@ struct gve_rx_ring {
> > struct gve_queue_resources *q_resources; /* head and tail pointer idx */
> > dma_addr_t q_resources_bus; /* dma address for the queue resources */
> > struct u64_stats_sync statss; /* sync stats for 32bit archs */
> > + struct timer_list starvation_timer; /* for queue starvation recovery */
> >
> > struct gve_rx_ctx ctx; /* Info for packet currently being processed in this ring. */
> >
> > diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c
> > index a0e0472b..71b6efbf 100644
> > --- a/drivers/net/ethernet/google/gve/gve_ethtool.c
> > +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
> > @@ -46,6 +46,7 @@ static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = {
> > "rx_hsplit_unsplit_pkt",
> > "interface_up_cnt", "interface_down_cnt", "reset_cnt",
> > "page_alloc_fail", "dma_mapping_error", "stats_report_trigger_cnt",
> > + "rx_critical_low_bufs",
> > };
> >
> > static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = {
> > @@ -58,6 +59,7 @@ static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = {
> > "rx_xdp_aborted[%u]", "rx_xdp_drop[%u]", "rx_xdp_pass[%u]",
> > "rx_xdp_tx[%u]", "rx_xdp_redirect[%u]",
> > "rx_xdp_tx_errors[%u]", "rx_xdp_redirect_errors[%u]", "rx_xdp_alloc_fails[%u]",
> > + "rx_critical_low_bufs[%u]",
> > };
> >
> > static const char gve_gstrings_tx_stats[][ETH_GSTRING_LEN] = {
> > @@ -151,12 +153,14 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > {
> > u64 tmp_rx_pkts, tmp_rx_hsplit_pkt, tmp_rx_bytes, tmp_rx_hsplit_bytes,
> > tmp_rx_skb_alloc_fail, tmp_rx_buf_alloc_fail,
> > + tmp_rx_critical_low_bufs,
> > tmp_rx_desc_err_dropped_pkt, tmp_rx_hsplit_unsplit_pkt,
> > tmp_tx_pkts, tmp_tx_bytes,
> > tmp_xdp_tx_errors, tmp_xdp_redirect_errors;
> > u64 rx_buf_alloc_fail, rx_desc_err_dropped_pkt, rx_hsplit_unsplit_pkt,
> > rx_pkts, rx_hsplit_pkt, rx_skb_alloc_fail, rx_bytes, tx_pkts, tx_bytes,
> > - tx_dropped, xdp_tx_errors, xdp_redirect_errors;
> > + rx_critical_low_bufs, tx_dropped, xdp_tx_errors,
> > + xdp_redirect_errors;
> > int rx_base_stats_idx, max_rx_stats_idx, max_tx_stats_idx;
> > int stats_idx, stats_region_len, nic_stats_len;
> > struct stats *report_stats;
> > @@ -197,6 +201,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
> >
> > for (rx_pkts = 0, rx_bytes = 0, rx_hsplit_pkt = 0,
> > rx_skb_alloc_fail = 0, rx_buf_alloc_fail = 0,
> > + rx_critical_low_bufs = 0,
> > rx_desc_err_dropped_pkt = 0, rx_hsplit_unsplit_pkt = 0,
> > xdp_tx_errors = 0, xdp_redirect_errors = 0,
> > ring = 0;
> > @@ -212,6 +217,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > tmp_rx_bytes = rx->rbytes;
> > tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail;
> > tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail;
> > + tmp_rx_critical_low_bufs =
> > + rx->rx_critical_low_bufs;
> > tmp_rx_desc_err_dropped_pkt =
> > rx->rx_desc_err_dropped_pkt;
> > tmp_rx_hsplit_unsplit_pkt =
> > @@ -226,6 +233,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > rx_bytes += tmp_rx_bytes;
> > rx_skb_alloc_fail += tmp_rx_skb_alloc_fail;
> > rx_buf_alloc_fail += tmp_rx_buf_alloc_fail;
> > + rx_critical_low_bufs += tmp_rx_critical_low_bufs;
> > rx_desc_err_dropped_pkt += tmp_rx_desc_err_dropped_pkt;
> > rx_hsplit_unsplit_pkt += tmp_rx_hsplit_unsplit_pkt;
> > xdp_tx_errors += tmp_xdp_tx_errors;
> > @@ -269,6 +277,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > data[i++] = priv->page_alloc_fail;
> > data[i++] = priv->dma_mapping_error;
> > data[i++] = priv->stats_report_trigger_cnt;
> > + data[i++] = rx_critical_low_bufs;
> > i = GVE_MAIN_STATS_LEN;
> >
> > rx_base_stats_idx = 0;
> > @@ -337,6 +346,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > tmp_rx_hsplit_bytes = rx->rx_hsplit_bytes;
> > tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail;
> > tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail;
> > + tmp_rx_critical_low_bufs =
> > + rx->rx_critical_low_bufs;
> > tmp_rx_desc_err_dropped_pkt =
> > rx->rx_desc_err_dropped_pkt;
> > tmp_xdp_tx_errors = rx->xdp_tx_errors;
> > @@ -381,6 +392,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > } while (u64_stats_fetch_retry(&priv->rx[ring].statss,
> > start));
> > i += GVE_XDP_ACTIONS + 3; /* XDP rx counters */
> > + data[i++] = tmp_rx_critical_low_bufs;
> > }
> > } else {
> > i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS;
> > diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
> > index 02cba280..303db4fa 100644
> > --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c
> > +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
> > @@ -18,6 +18,16 @@
> > #include <net/tcp.h>
> > #include <net/xdp_sock_drv.h>
> >
> > +static void gve_rx_starvation_timer(struct timer_list *t)
> > +{
> > + struct gve_rx_ring *rx = timer_container_of(rx, t, starvation_timer);
> > + struct gve_priv *priv = rx->gve;
> > + struct gve_notify_block *block;
> > +
> > + block = &priv->ntfy_blocks[rx->ntfy_id];
> > + napi_schedule(&block->napi);
> > +}
> > +
> > static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx)
> > {
> > struct device *hdev = &priv->pdev->dev;
> > @@ -120,6 +130,7 @@ void gve_rx_stop_ring_dqo(struct gve_priv *priv, int idx)
> >
> > if (rx->dqo.page_pool)
> > page_pool_disable_direct_recycling(rx->dqo.page_pool);
> > + timer_delete_sync(&rx->starvation_timer);
> > gve_remove_napi(priv, ntfy_idx);
> > gve_rx_remove_from_block(priv, idx);
> > gve_rx_reset_ring_dqo(priv, idx);
> > @@ -136,6 +147,8 @@ void gve_rx_free_ring_dqo(struct gve_priv *priv, struct gve_rx_ring *rx,
> > u32 qpl_id;
> > int i;
> >
> > + timer_shutdown_sync(&rx->starvation_timer);
> > +
> > completion_queue_slots = rx->dqo.complq.mask + 1;
> > buffer_queue_slots = rx->dqo.bufq.mask + 1;
> >
> > @@ -232,6 +245,7 @@ int gve_rx_alloc_ring_dqo(struct gve_priv *priv,
> > rx->gve = priv;
> > rx->q_num = idx;
> > rx->packet_buffer_size = cfg->packet_buffer_size;
> > + timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0);
> >
> > if (cfg->xdp) {
> > rx->packet_buffer_truesize = GVE_XDP_RX_BUFFER_SIZE_DQO;
> > @@ -365,6 +379,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)
> > struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq;
> > struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq;
> > struct gve_priv *priv = rx->gve;
> > + u32 num_bufs_avail_to_hw;
> > u32 num_avail_slots;
> > u32 num_full_slots;
> > u32 num_posted = 0;
> > @@ -400,6 +415,23 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)
> > }
> >
> > rx->fill_cnt += num_posted;
> > +
> > + /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors
> > + * visible to the hardware, and no doorbell was written, the hardware
> > + * is in danger of starving and cannot trigger interrupts. Start the
> > + * timer to periodically reschedule NAPI and recover from starvation.
> > + */
> > + num_bufs_avail_to_hw =
> > + ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) -
> > + bufq->head) & bufq->mask;
> > +
> > + if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) {
> > + u64_stats_update_begin(&rx->statss);
> > + rx->rx_critical_low_bufs++;
> > + u64_stats_update_end(&rx->statss);
> > + mod_timer(&rx->starvation_timer,
> > + jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS));
> > + }
> > }
> >
> > static void gve_rx_skb_csum(struct sk_buff *skb,
> > --
> > 2.55.0.rc2.803.g1fd1e6609c-goog
> >
> >
^ permalink raw reply
* Re: [PATCH] net: phylink: reject unsupported speed/duplex in ksettings_set() with PHY
From: Nazle Asmade, Muhammad Nazim Amirul @ 2026-07-03 8:01 UTC (permalink / raw)
To: Andrew Lunn, Maxime Chevallier
Cc: linux@armlinux.org.uk, hkallweit1@gmail.com, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <c3a0e35b-a633-487c-bc08-d62f809047a6@lunn.ch>
On 1/7/2026 10:27 pm, Andrew Lunn wrote:
>> I think rejecting these settings makes sense, I'm however wondering
>> wether this is a fix or not, as this will change user-visible behaviour.
>> I'd err to the side of caution and send that to net-next, but maybe
>> Andrew will have more insight :)
>
> net-next seems reasonable.
>
> Andrew
Hi Maxime, Andrew
Sure, will repost it.
BR,
Nazim
^ permalink raw reply
* Re: [PATCH net-next v4 2/2] net: pse-pd: add Realtek/Broadcom PSE MCU driver
From: Paolo Abeni @ 2026-07-03 7:55 UTC (permalink / raw)
To: Jonas Jelonek, Oleksij Rempel, Kory Maincent, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: netdev, devicetree, linux-kernel, Daniel Golle, Bjørn Mork
In-Reply-To: <20260630105651.756058-3-jelonek.jonas@gmail.com>
On 6/30/26 12:56 PM, Jonas Jelonek wrote:
> A range of PoE switches use a small microcontroller on the PCB to front
> the actual PSE silicon. The host CPU talks to that MCU over I2C/SMBus or
> UART using a fixed 12-byte request/response protocol with a trailing
> checksum; the PSE chips are managed by the MCU and are not accessed
> directly. The same protocol family is spoken by Realtek and Broadcom PSE
> MCUs, diverging in opcode numbering and a few response layouts, which the
> driver abstracts behind a per-dialect opcode table and parser hooks
> selected by the compatible. The specific PSE chip behind the MCU is
> detected at runtime and only influences per-chip constants (power scaling
> and the per-port cap).
>
> The driver is split into a shared core and two transport modules:
>
> - PSE_REALTEK_MCU: protocol, message framing, dialect machinery, and the
> pse_controller_ops glue.
> - PSE_REALTEK_MCU_I2C / PSE_REALTEK_MCU_UART: transport modules
> registering the MCU on an I2C bus or a serdev port respectively.
>
> The realtek-pse-mcu-* files and PSE_REALTEK_MCU* symbols match the
> realtek,pse-mcu-rtk / realtek,pse-mcu-brcm compatibles: all name the
> Realtek PSE-MCU front-end, not the MCU silicon or the PSE chip behind
> it (see the binding for the prefix rationale). Broadcom PSE MCUs speak
> the same protocol family and are handled by the same shared core
> through the dialect abstraction selected by the '-brcm' compatible.
>
> Power budgeting is left to the MCU firmware; the driver advertises
> PSE_BUDGET_EVAL_STRAT_DYNAMIC (controller-managed budget) accordingly.
>
> Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
> ---
> MAINTAINERS | 7 +
> drivers/net/pse-pd/Kconfig | 28 +
> drivers/net/pse-pd/Makefile | 3 +
> drivers/net/pse-pd/realtek-pse-mcu-core.c | 1019 +++++++++++++++++++++
> drivers/net/pse-pd/realtek-pse-mcu-i2c.c | 163 ++++
> drivers/net/pse-pd/realtek-pse-mcu-uart.c | 156 ++++
> drivers/net/pse-pd/realtek-pse-mcu.h | 87 ++
> 7 files changed, 1463 insertions(+)
> create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-core.c
> create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-i2c.c
> create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-uart.c
> create mode 100644 drivers/net/pse-pd/realtek-pse-mcu.h
This is quite large, and shouls be split in smaller patches to help
reviewers.
[...]
> +struct rtpse_mcu_dialect {
> + struct rtpse_mcu_opcode opcode[RTPSE_MCU_NUM_CMDS];
> +
> + /*
> + * Response parsers. Each dialect must supply its own; the core calls
> + * these unconditionally rather than carrying a default that would
> + * silently mis-decode bytes from a dialect that forgot to set them.
> + */
> + int (*parse_system_info)(const u8 *payload, struct rtpse_mcu_info *info);
The 2 existing implementation always return 0; you may consider change
it to a void function.
> +static int rtpse_mcu_port_get_voltage(struct pse_controller_dev *pcdev, int id)
> +{
> + struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
> + struct rtpse_mcu_port_measurement measurement;
> + int ret;
> + u32 uV;
> +
> + ret = rtpse_mcu_port_get_measurement(pse, id, &measurement);
> + if (ret)
> + return ret;
> +
> + /* 64.45mV per LSB */
> + uV = (u32)measurement.voltage_raw * 64450U;
This cast ^^^^^ should be unneeded.
> + return min_t(u32, uV, INT_MAX);
> +}
> +
> +static int rtpse_mcu_port_enable(struct pse_controller_dev *pcdev, int id)
> +{
> + return rtpse_mcu_port_set_state(to_rtpse_mcu_ctrl(pcdev), id, true);
> +}
> +
> +static int rtpse_mcu_port_disable(struct pse_controller_dev *pcdev, int id)
> +{
> + return rtpse_mcu_port_set_state(to_rtpse_mcu_ctrl(pcdev), id, false);
> +}
> +
> +static int rtpse_mcu_port_get_pw_limit(struct pse_controller_dev *pcdev, int id)
> +{
> + struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
> + struct rtpse_mcu_port_ext_config config;
> + int ret;
> +
> + ret = rtpse_mcu_port_get_ext_config(pse, id, &config);
> + if (ret)
> + return ret;
> +
> + return config.max_power * pse->chip->pw_read_lsb_mW;
> +}
> +
> +static int rtpse_mcu_port_set_pw_limit(struct pse_controller_dev *pcdev, int id, int max_mW)
> +{
> + const struct rtpse_mcu_opcode *type_opc, *val_opc;
> + struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
> + const struct rtpse_mcu_chip_info *chip = pse->chip;
> + unsigned int prg_val;
> + int ret;
> +
> + if (max_mW < 0 || max_mW > chip->max_mW_per_port)
> + return -ERANGE;
> +
> + type_opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_TYPE];
> + val_opc = &pse->dialect->opcode[chip->pw_set_cmd];
> + if (!type_opc->valid || !val_opc->valid)
> + return -EOPNOTSUPP;
> +
> + /*
> + * Switch the port to user-defined limit mode first, then program the
> + * limit value. If the second cmd fails, the port is left in
> + * user-defined mode but with the previous limit value; the next
> + * successful set_pw_limit call recovers it.
> + */
> + ret = rtpse_mcu_port_cmd(pse, id, type_opc->op, RTPSE_MCU_PORT_PW_LIMIT_TYPE_USER);
> + if (ret)
> + return ret;
> +
> + prg_val = min_t(unsigned int, max_mW / chip->pw_set_lsb_mW, 0xff);
> +
> + return rtpse_mcu_port_cmd(pse, id, val_opc->op, prg_val);
> +}
> +
> +static int rtpse_mcu_port_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,
> + struct pse_pw_limit_ranges *out)
> +{
> + struct ethtool_c33_pse_pw_limit_range *range;
> + struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
> +
> + range = kzalloc_obj(*range, GFP_KERNEL);
or just:
range = kzalloc_obj(*range);
> +static int rtpse_mcu_discover(struct rtpse_mcu_ctrl *pse, struct rtpse_mcu_info *info)
> +{
> + struct rtpse_mcu_ext_config ext_config;
> + unsigned long deadline;
> + int ret;
> +
> + /*
> + * The MCU may not answer on the bus yet right after power-up or
> + * enable-gpios assertion: depending on the transport it either stays
> + * silent (-ETIMEDOUT) or does not ACK its address at all (-ENXIO /
> + * -EREMOTEIO). Retry within a bounded wall-time window so a slow boot
> + * still probes, while a genuinely unresponsive MCU fails with its real
> + * error instead of deferring forever and masking it.
> + */
> + deadline = jiffies + msecs_to_jiffies(RTPSE_MCU_BOOT_TIMEOUT_MS);
> + do {
> + ret = rtpse_mcu_get_info(pse, info);
> + if (ret != -ETIMEDOUT && ret != -ENXIO && ret != -EREMOTEIO &&
> + ret != -EAGAIN)
> + break;
> + msleep(RTPSE_MCU_BOOT_RETRY_MS);
> + } while (time_before(jiffies, deadline));
> + if (ret)
> + return dev_err_probe(pse->dev, ret, "failed to read MCU info\n");
> +
> + switch (info->device_id) {
> + case RTPSE_MCU_DEVICE_ID_RTL8238B:
> + pse->chip = &rtl8238b_info;
> + break;
> + case RTPSE_MCU_DEVICE_ID_RTL8239:
> + pse->chip = &rtl8239_info;
> + break;
> + case RTPSE_MCU_DEVICE_ID_RTL8239C:
> + pse->chip = &rtl8239c_info;
> + break;
> + case RTPSE_MCU_DEVICE_ID_BCM59111:
> + pse->chip = &bcm59111_info;
> + break;
> + case RTPSE_MCU_DEVICE_ID_BCM59121:
> + pse->chip = &bcm59121_info;
> + break;
> + default:
> + return dev_err_probe(pse->dev, -EINVAL, "unknown PSE id 0x%x\n",
> + info->device_id);
> + }
> +
> + if (!info->max_ports || info->max_ports > RTPSE_MCU_MAX_PORTS)
> + return dev_err_probe(pse->dev, -EINVAL,
> + "MCU reports invalid port count %u\n", info->max_ports);
> +
> + ret = rtpse_mcu_get_ext_config(pse, &ext_config);
> + if (ret)
> + return dev_err_probe(pse->dev, ret, "failed to read MCU ext config\n");
> +
> + dev_info(pse->dev, "%s MCU, %s (id 0x%04x), %u ports across %u PSE chip(s)\n",
> + pse->dialect->mcu_type_str(info->mcu_type), pse->chip->name,
> + info->device_id, info->max_ports, ext_config.num_of_pses);
The general guidance is to try to avoid unneeded print on dmsg, as they
tend to scary admins, but I personally agree on message on modprobe.
No strong opionion either ways.
/P
^ permalink raw reply
* Re: [PATCH v2 1/2] net: fman: move IRQ registration after init to prevent NULL deref and UAF
From: 赵金明 @ 2026-07-03 7:54 UTC (permalink / raw)
To: Andrew Lunn
Cc: pabeni, andrew+netdev, davem, edumazet, horms, kuba, linux-kernel,
madalin.bucur, netdev, sean.anderson
In-Reply-To: <19877366-38df-4598-9892-3b295a5238f1@lunn.ch>
>On Thu, Jul 02, 2026 at 05:28:15PM +0800, ZhaoJinming wrote:
>> read_dts_node() registers shared interrupt handlers via
>> devm_request_irq() with fman as dev_id. Two bugs exist in the
>> current code:
>
>Please start a new thread with a new version of the patch. The CI
>system just thinks this is part of the discussion, not something it
>must test.
>
>Please also read:
>
>https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
>
>and set the Subject: line correctly, etc.
>
>???? Andrew
>
Started a new thread, https://lore.kernel.org/netdev/20260703074324.907294-1-zhaojinming@uniontech.com/T/#t
Thanks,
Jinming
^ permalink raw reply
* [PATCH net-next 17/17] net: dsa: mv88e6xxx: RMU DUMP_ATU support in devlink
From: Luke Howard @ 2026-07-03 7:46 UTC (permalink / raw)
To: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Russell King,
Richard Cochran, Florian Fainelli
Cc: Cedric Jehasse, Max Holtmann, Max Hunter, Kieran Tyrrell,
Ryan Wilkins, Mattias Forsblad, netdev, linux-kernel, Luke Howard
In-Reply-To: <20260703-net-next-mv88e6xxx-rmu-v1-0-991a27d78bca@padl.com>
Support reading an ATU snapshot in a single RMU request.
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/chip.h | 10 +++++
drivers/net/dsa/mv88e6xxx/devlink.c | 67 ++++++++++++++++++++++---------
drivers/net/dsa/mv88e6xxx/rmu.c | 78 +++++++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/rmu.h | 35 +++++++++++++++++
4 files changed, 171 insertions(+), 19 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index fdc87d02469b5..7bbb649330d55 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -205,6 +205,16 @@ struct mv88e6xxx_stu_entry {
u8 state[DSA_MAX_PORTS];
};
+struct mv88e6xxx_devlink_atu_entry {
+ /* The FID is scattered over multiple registers. */
+ u16 fid;
+ u16 atu_op;
+ u16 atu_data;
+ u16 atu_01;
+ u16 atu_23;
+ u16 atu_45;
+};
+
struct mv88e6xxx_bus_ops;
struct mv88e6xxx_irq_ops;
struct mv88e6xxx_gpio_ops;
diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c
index 6d7da381f9f38..5c2a59d1145b8 100644
--- a/drivers/net/dsa/mv88e6xxx/devlink.c
+++ b/drivers/net/dsa/mv88e6xxx/devlink.c
@@ -6,6 +6,7 @@
#include "global1.h"
#include "global2.h"
#include "port.h"
+#include "rmu.h"
static int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash)
{
@@ -307,16 +308,6 @@ static int mv88e6xxx_region_global_snapshot(struct devlink *dl,
* mv88e6xxx generations
*/
-struct mv88e6xxx_devlink_atu_entry {
- /* The FID is scattered over multiple registers. */
- u16 fid;
- u16 atu_op;
- u16 atu_data;
- u16 atu_01;
- u16 atu_23;
- u16 atu_45;
-};
-
static int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip,
int fid,
struct mv88e6xxx_devlink_atu_entry *table,
@@ -369,6 +360,33 @@ static int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip,
return 0;
}
+static int mv88e6xxx_region_atu_snapshot_rmu(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_devlink_atu_entry *table,
+ int *count)
+{
+ unsigned int num_macs = mv88e6xxx_num_macs(chip);
+ u16 cont_code = 0;
+ int err;
+
+ do {
+ if (*count + MV88E6XXX_RMU_MAX_ATU_ENTRIES > num_macs) {
+ err = -ERANGE;
+ break;
+ }
+
+ err = mv88e6xxx_rmu_dump_atu(chip, &table[*count], &cont_code);
+ if (err < 0)
+ break;
+
+ *count += err;
+ } while (cont_code != 0);
+
+ if (err < 0)
+ *count = 0;
+
+ return err;
+}
+
static int mv88e6xxx_region_atu_snapshot(struct devlink *dl,
const struct devlink_region_ops *ops,
struct netlink_ext_ack *extack,
@@ -386,18 +404,29 @@ static int mv88e6xxx_region_atu_snapshot(struct devlink *dl,
mv88e6xxx_reg_lock(chip);
- while (1) {
- fid = find_next_bit(chip->fid_bitmap, MV88E6XXX_N_FID, fid + 1);
- if (fid == MV88E6XXX_N_FID)
- break;
+ err = mv88e6xxx_region_atu_snapshot_rmu(chip, table, &count);
+ if (err >= 0) {
+ /* RMU succeeded, use its results */
+ err = 0;
+ } else if (!mv88e6xxx_rmu_can_mdio_fallback(chip, err)) {
+ kfree(table);
+ goto out;
+ } else {
+ while (1) {
+ fid = find_next_bit(chip->fid_bitmap, MV88E6XXX_N_FID,
+ fid + 1);
+ if (fid == MV88E6XXX_N_FID)
+ break;
- err = mv88e6xxx_region_atu_snapshot_fid(chip, fid, table,
- &count);
- if (err) {
- kfree(table);
- goto out;
+ err = mv88e6xxx_region_atu_snapshot_fid(chip, fid,
+ table, &count);
+ if (err) {
+ kfree(table);
+ goto out;
+ }
}
}
+
*data = (u8 *)table;
out:
mv88e6xxx_reg_unlock(chip);
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.c b/drivers/net/dsa/mv88e6xxx/rmu.c
index 5660372f41ad7..36422039030cd 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.c
+++ b/drivers/net/dsa/mv88e6xxx/rmu.c
@@ -106,6 +106,84 @@ static int mv88e6xxx_rmu_request(struct mv88e6xxx_chip *chip,
resp, resp_len, timeout_ms);
}
+int mv88e6xxx_rmu_dump_atu(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_devlink_atu_entry *entries,
+ u16 *cont_code)
+{
+ __be16 req[] = {
+ MV88E6XXX_RMU_REQ_FORMAT_SOHO,
+ MV88E6XXX_RMU_REQ_PAD,
+ MV88E6XXX_RMU_REQ_CODE_ATU,
+ htons(*cont_code),
+ };
+ struct mv88e6xxx_rmu_atu_resp resp;
+ int resp_len, count;
+ int ret, i;
+
+ if (chip->rmu_state == MV88E6XXX_RMU_DISABLED)
+ return -EOPNOTSUPP;
+
+ resp_len = sizeof(resp);
+ ret = mv88e6xxx_rmu_request(chip, req, sizeof(req),
+ &resp, resp_len,
+ MV88E6XXX_RMU_REQUEST_TIMEOUT_MS);
+ if (ret < 0) {
+ dev_dbg(chip->dev, "RMU: error for command DUMP_ATU %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ /* minimum packet length */
+ resp_len = sizeof(struct mv88e6xxx_rmu_header) + sizeof(__be16); /* cont_code */
+
+ if (ret < resp_len || (ret - resp_len) % sizeof(struct mv88e6xxx_rmu_atu_entry)) {
+ dev_err(chip->dev, "RMU: DUMP_ATU returned wrong length: rx %d expecting %zd\n",
+ ret, sizeof(resp));
+ return -EPROTO;
+ }
+
+ count = (ret - resp_len) / sizeof(struct mv88e6xxx_rmu_atu_entry);
+
+ if (resp.rmu_header.code != MV88E6XXX_RMU_RESP_CODE_ATU) {
+ dev_err(chip->dev, "RMU: DUMP_ATU returned wrong code %d\n",
+ be16_to_cpu(resp.rmu_header.code));
+ return -EPROTO;
+ }
+
+ for (i = 0; i < count; i++) {
+ const struct mv88e6xxx_rmu_atu_entry *src = &resp.entries[i];
+ struct mv88e6xxx_devlink_atu_entry *dst = &entries[i];
+ u16 tmp;
+
+ tmp = ntohs(src->state_trunk_dpv);
+ if (!MV88E6XXX_RMU_ATU_ENTRY_STATE_GET(tmp)) {
+ *cont_code = 0;
+ return i;
+ }
+
+ dst->atu_data = MV88E6XXX_RMU_ATU_ENTRY_STATE_GET(tmp) & 0xf;
+ if (tmp & MV88E6XXX_RMU_ATU_TRUNK)
+ dst->atu_data |= MV88E6XXX_G1_ATU_DATA_TRUNK;
+ dst->atu_data |= (MV88E6XXX_RMU_ATU_DPV_GET(tmp) &
+ mv88e6xxx_port_mask(chip)) << 4;
+
+ tmp = ntohs(src->pri_fid);
+ dst->fid = MV88E6XXX_RMU_ATU_FID_GET(tmp);
+ dst->atu_op = MV88E6XXX_G1_ATU_OP_GET_NEXT_DB;
+
+ dst->atu_01 = ntohs(src->atu_01);
+ dst->atu_23 = ntohs(src->atu_23);
+ dst->atu_45 = ntohs(src->atu_45);
+ }
+
+ if (i == MV88E6XXX_RMU_MAX_ATU_ENTRIES)
+ *cont_code = ntohs(resp.cont_code);
+ else
+ *cont_code = 0;
+
+ return i;
+}
+
int mv88e6xxx_rmu_stats(struct mv88e6xxx_chip *chip, int port,
u64 *data,
const struct mv88e6xxx_hw_stat *hw_stats,
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.h b/drivers/net/dsa/mv88e6xxx/rmu.h
index 0865e899cb340..3a288ff3b113f 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.h
+++ b/drivers/net/dsa/mv88e6xxx/rmu.h
@@ -17,6 +17,7 @@
#define MV88E6XXX_RMU_REQ_FORMAT_SOHO htons(0x0001)
#define MV88E6XXX_RMU_REQ_PAD htons(0x0000)
#define MV88E6XXX_RMU_REQ_CODE_GET_ID htons(0x0000)
+#define MV88E6XXX_RMU_REQ_CODE_ATU htons(0x1000)
#define MV88E6XXX_RMU_REQ_CODE_MIB htons(0x1020)
#define MV88E6XXX_RMU_REQ_CODE_REG_RW htons(0x2000)
#define MV88E6XXX_RMU_REQ_DATA htons(0x0000)
@@ -50,6 +51,7 @@
#define MV88E6XXX_RMU_RESP_FORMAT_1 htons(0x0001)
#define MV88E6XXX_RMU_RESP_FORMAT_2 htons(0x0002)
#define MV88E6XXX_RMU_RESP_CODE_GOT_ID htons(0x0000)
+#define MV88E6XXX_RMU_RESP_CODE_ATU htons(0x1000)
#define MV88E6XXX_RMU_RESP_CODE_MIB htons(0x1020)
#define MV88E6XXX_RMU_RESP_CODE_REG_RW htons(0x2000)
@@ -67,6 +69,34 @@ struct mv88e6xxx_rmu_rw_resp {
__be16 end1;
} __packed;
+struct mv88e6xxx_rmu_atu_entry {
+ __be16 state_trunk_dpv;
+ __be16 atu_01;
+ __be16 atu_23;
+ __be16 atu_45;
+ __be16 pri_fid;
+} __packed;
+
+#define MV88E6XXX_RMU_MAX_ATU_ENTRIES 48
+
+#define MV88E6XXX_RMU_ATU_ENTRY_STATE_MASK GENMASK(15, 12)
+#define MV88E6XXX_RMU_ATU_ENTRY_STATE_GET(p) FIELD_GET(MV88E6XXX_RMU_ATU_ENTRY_STATE_MASK, p)
+#define MV88E6XXX_RMU_ATU_TRUNK 0x0800
+#define MV88E6XXX_RMU_ATU_DPV_MASK GENMASK(10, 0)
+#define MV88E6XXX_RMU_ATU_DPV_GET(p) FIELD_GET(MV88E6XXX_RMU_ATU_DPV_MASK, p)
+
+#define MV88E6XXX_RMU_ATU_PRI_MASK GENMASK(14, 12)
+#define MV88E6XXX_RMU_ATU_PRI_GET(p) FIELD_GET(MV88E6XXX_RMU_ATU_PRI_MASK, p)
+
+#define MV88E6XXX_RMU_ATU_FID_MASK GENMASK(11, 0)
+#define MV88E6XXX_RMU_ATU_FID_GET(p) FIELD_GET(MV88E6XXX_RMU_ATU_FID_MASK, p)
+
+struct mv88e6xxx_rmu_atu_resp {
+ struct mv88e6xxx_rmu_header rmu_header;
+ struct mv88e6xxx_rmu_atu_entry entries[MV88E6XXX_RMU_MAX_ATU_ENTRIES];
+ __be16 cont_code;
+} __packed;
+
/* number of RMU MIB statistics, by type, in octets */
#define MV88E6XXX_RMU_STATS_TYPE_DATA0_LEN 128
#define MV88E6XXX_RMU_STATS_TYPE_PORT_LEN 12
@@ -81,6 +111,11 @@ struct mv88e6xxx_rmu_mib_resp {
__be32 data[MV88E6XXX_RMU_STATS_TYPE_MAX_LEN / 4];
} __packed;
+struct mv88e6xxx_devlink_atu_entry;
+
+int mv88e6xxx_rmu_dump_atu(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_devlink_atu_entry *entries,
+ u16 *cont_code);
int mv88e6xxx_rmu_stats(struct mv88e6xxx_chip *chip, int port,
u64 *data,
const struct mv88e6xxx_hw_stat *hw_stats,
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 16/17] net: dsa: mv88e6xxx: size ATU snapshot buffer and region by num_macs
From: Luke Howard @ 2026-07-03 7:46 UTC (permalink / raw)
To: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Russell King,
Richard Cochran, Florian Fainelli
Cc: Cedric Jehasse, Max Holtmann, Max Hunter, Kieran Tyrrell,
Ryan Wilkins, Mattias Forsblad, netdev, linux-kernel, Luke Howard
In-Reply-To: <20260703-net-next-mv88e6xxx-rmu-v1-0-991a27d78bca@padl.com>
The ATU devlink region and its snapshot buffer are both sized by the
number of forwarding databases, but the ATU can hold up to
mv88e6xxx_num_macs() entries (which is 8192 on the 6352 series).
Fixes: bfb255428966 ("net: dsa: mv88e6xxx: Add devlink regions")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/devlink.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c
index 6f034841883c6..6d7da381f9f38 100644
--- a/drivers/net/dsa/mv88e6xxx/devlink.c
+++ b/drivers/net/dsa/mv88e6xxx/devlink.c
@@ -380,7 +380,7 @@ static int mv88e6xxx_region_atu_snapshot(struct devlink *dl,
int fid = -1, err = 0, count = 0;
table = kzalloc_objs(struct mv88e6xxx_devlink_atu_entry,
- mv88e6xxx_num_databases(chip));
+ mv88e6xxx_num_macs(chip));
if (!table)
return -ENOMEM;
@@ -776,7 +776,7 @@ int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds)
switch (i) {
case MV88E6XXX_REGION_ATU:
- size = mv88e6xxx_num_databases(chip) *
+ size = mv88e6xxx_num_macs(chip) *
sizeof(struct mv88e6xxx_devlink_atu_entry);
break;
case MV88E6XXX_REGION_VTU:
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 15/17] net: dsa: mv88e6xxx: cancel PTP polling work on .shutdown
From: Luke Howard @ 2026-07-03 7:46 UTC (permalink / raw)
To: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Russell King,
Richard Cochran, Florian Fainelli
Cc: Cedric Jehasse, Max Holtmann, Max Hunter, Kieran Tyrrell,
Ryan Wilkins, Mattias Forsblad, netdev, linux-kernel, Luke Howard
In-Reply-To: <20260703-net-next-mv88e6xxx-rmu-v1-0-991a27d78bca@padl.com>
DSA core's dsa_switch_shutdown() only closes CPU port conduits and
unlinks DSA uppers; it does not invoke ds->ops->teardown(). On
.shutdown (reboot), the chip's mv88e6xxx_teardown() therefore never
runs, and the PTP overflow / TAI event delayed works are not
cancelled. They continue to fire as the conduit netdev is torn down
by its own driver, racing the teardown and crashing in the RMU
request path when conduit->dev_addr is released.
Factor the work cancellation out of mv88e6xxx_ptp_free() into a new
mv88e6xxx_ptp_shutdown() helper that leaves the PTP clock registered,
and call it from mv88e6xxx_shutdown_common() before
dsa_switch_shutdown(). mv88e6xxx_ptp_free() now calls the helper
itself, so the regular .remove path is unchanged.
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 6 ++++++
drivers/net/dsa/mv88e6xxx/ptp.c | 14 ++++++++++++--
drivers/net/dsa/mv88e6xxx/ptp.h | 5 +++++
3 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 531a59093ef97..3d92c59de7b5f 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -7543,6 +7543,12 @@ static void mv88e6xxx_shutdown(struct mdio_device *mdiodev)
if (!ds)
return;
+ /* DSA core does not call ->teardown on .shutdown, so stop PTP
+ * polling here before the conduit goes away. Otherwise the work
+ * can race teardown and dereference a stale conduit->dev_addr.
+ */
+ mv88e6xxx_ptp_shutdown(ds->priv);
+
dsa_switch_shutdown(ds);
dev_set_drvdata(&mdiodev->dev, NULL);
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c
index f7603573d3a98..f6ce3232d10ff 100644
--- a/drivers/net/dsa/mv88e6xxx/ptp.c
+++ b/drivers/net/dsa/mv88e6xxx/ptp.c
@@ -551,14 +551,24 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
return 0;
}
-/* This must never be called holding the register lock */
-void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
+/* Cancel any pending PTP polling work. Safe to call from .shutdown
+ * paths where the PTP clock itself stays registered.
+ * This must never be called holding the register lock.
+ */
+void mv88e6xxx_ptp_shutdown(struct mv88e6xxx_chip *chip)
{
if (chip->ptp_clock) {
cancel_delayed_work_sync(&chip->overflow_work);
if (chip->info->ops->ptp_ops->event_work)
cancel_delayed_work_sync(&chip->tai_event_work);
+ }
+}
+/* This must never be called holding the register lock */
+void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
+{
+ if (chip->ptp_clock) {
+ mv88e6xxx_ptp_shutdown(chip);
ptp_clock_unregister(chip->ptp_clock);
chip->ptp_clock = NULL;
}
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/ptp.h
index 95bdddb0bf39f..90031b05d2514 100644
--- a/drivers/net/dsa/mv88e6xxx/ptp.h
+++ b/drivers/net/dsa/mv88e6xxx/ptp.h
@@ -68,6 +68,7 @@
int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip);
void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip);
+void mv88e6xxx_ptp_shutdown(struct mv88e6xxx_chip *chip);
#define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip, \
ptp_clock_info)
@@ -87,6 +88,10 @@ static inline void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
{
}
+static inline void mv88e6xxx_ptp_shutdown(struct mv88e6xxx_chip *chip)
+{
+}
+
static const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = {};
static const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {};
static const struct mv88e6xxx_ptp_ops mv88e6390_ptp_ops = {};
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 14/17] net: dsa: mv88e6xxx: Time RMU operations and disable if slow
From: Luke Howard @ 2026-07-03 7:46 UTC (permalink / raw)
To: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Russell King,
Richard Cochran, Florian Fainelli
Cc: Cedric Jehasse, Max Holtmann, Max Hunter, Kieran Tyrrell,
Ryan Wilkins, Mattias Forsblad, netdev, linux-kernel, Luke Howard
In-Reply-To: <20260703-net-next-mv88e6xxx-rmu-v1-0-991a27d78bca@padl.com>
From: Andrew Lunn <andrew@lunn.ch>
MII access to the switch generally has consistent latency. Sending and
receiving frames on the other hand has a big variance in
latency. Receiver interrupt coalescence can result in delays of
100us. As a result, the RMU can be slower than MDIO.
Measure the latency of the first 16 read requests performed using the
RMU, and compare it to the latency on an MDIO read. If the RMU is
slower than MDIO, do not use if for single register reads, writes and
waiting for bits to be set. Do however use it for retrieving MIB
values since a single RMU requests replaces a large number of MIDO
transactions.
Testing on ZII Devel C, which uses a FEC, and Marvel 370RD using
mvneta has show RMU is slower than MDIO. ZII RAP however, makes use of
bit banging MDIO and an IGB. IGB by default has a short interrupt
coalesce period, making RMU much faster than bit banging, by around 20
times.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/chip.h | 6 +++++
drivers/net/dsa/mv88e6xxx/rmu.c | 58 ++++++++++++++++++++++++++++++++++++----
2 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 7e483ba8c58d9..fdc87d02469b5 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -355,6 +355,8 @@ enum mv88e6xxx_rmu_state {
*/
};
+#define MV88E6XXX_RMU_IS_SLOW BIT(0)
+
struct mv88e6xxx_chip {
const struct mv88e6xxx_info *info;
@@ -471,6 +473,10 @@ struct mv88e6xxx_chip {
struct net_device *rmu_conduit;
struct dsa_inband rmu_inband;
enum mv88e6xxx_rmu_state rmu_state;
+ u8 rmu_flags;
+ ktime_t rmu_read_latencies[16];
+ u32 rmu_samples;
+ ktime_t smi_read_latency;
};
#define TCAM_MATCH_SIZE 96
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.c b/drivers/net/dsa/mv88e6xxx/rmu.c
index 88b02030a774f..5660372f41ad7 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.c
+++ b/drivers/net/dsa/mv88e6xxx/rmu.c
@@ -221,7 +221,8 @@ int mv88e6xxx_rmu_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
int resp_len;
int ret = -1;
- if (chip->rmu_state == MV88E6XXX_RMU_DISABLED)
+ if (chip->rmu_state == MV88E6XXX_RMU_DISABLED ||
+ (chip->rmu_flags & MV88E6XXX_RMU_IS_SLOW))
return -EOPNOTSUPP;
resp_len = sizeof(resp);
@@ -249,6 +250,33 @@ int mv88e6xxx_rmu_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
return 0;
}
+static void mv88e6xxx_rmu_read_latency(struct mv88e6xxx_chip *chip,
+ ktime_t latency)
+{
+ ktime_t average = 0;
+ int i;
+
+ if (chip->rmu_samples >= ARRAY_SIZE(chip->rmu_read_latencies))
+ return;
+
+ chip->rmu_read_latencies[chip->rmu_samples++] = latency;
+
+ if (chip->rmu_samples == ARRAY_SIZE(chip->rmu_read_latencies)) {
+ for (i = 0; i < ARRAY_SIZE(chip->rmu_read_latencies); i++)
+ average += chip->rmu_read_latencies[i];
+ average = average / ARRAY_SIZE(chip->rmu_read_latencies);
+
+ dev_dbg(chip->dev, "RMU %lldus, smi %lldus\n",
+ div_u64(average, 1000),
+ div_u64(chip->smi_read_latency, 1000));
+
+ if (chip->smi_read_latency < average)
+ chip->rmu_flags |= MV88E6XXX_RMU_IS_SLOW;
+
+ chip->rmu_samples = U32_MAX;
+ }
+}
+
int mv88e6xxx_rmu_read(struct mv88e6xxx_chip *chip, int addr, int reg,
u16 *val)
{
@@ -263,11 +291,15 @@ int mv88e6xxx_rmu_read(struct mv88e6xxx_chip *chip, int addr, int reg,
};
struct mv88e6xxx_rmu_rw_resp resp;
int resp_len;
- int ret = -1;
+ ktime_t start;
+ int ret;
- if (chip->rmu_state == MV88E6XXX_RMU_DISABLED)
+ if (chip->rmu_state == MV88E6XXX_RMU_DISABLED ||
+ (chip->rmu_flags & MV88E6XXX_RMU_IS_SLOW))
return -EOPNOTSUPP;
+ start = ktime_get();
+
resp_len = sizeof(resp);
ret = mv88e6xxx_rmu_request(chip, req, sizeof(req),
&resp, resp_len,
@@ -290,6 +322,8 @@ int mv88e6xxx_rmu_read(struct mv88e6xxx_chip *chip, int addr, int reg,
return -EPROTO;
}
+ mv88e6xxx_rmu_read_latency(chip, ktime_get() - start);
+
*val = ntohs(resp.value);
return 0;
@@ -312,7 +346,8 @@ int mv88e6xxx_rmu_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg,
int resp_len;
int ret = -1;
- if (chip->rmu_state == MV88E6XXX_RMU_DISABLED)
+ if (chip->rmu_state == MV88E6XXX_RMU_DISABLED ||
+ (chip->rmu_flags & MV88E6XXX_RMU_IS_SLOW))
return -EOPNOTSUPP;
resp_len = sizeof(resp);
@@ -389,8 +424,10 @@ void mv88e6xxx_rmu_conduit_state_change(struct dsa_switch *ds,
{
struct dsa_port *cpu_dp = conduit->dsa_ptr;
struct mv88e6xxx_chip *chip = ds->priv;
+ ktime_t start;
int port;
int ret;
+ u16 id;
port = dsa_towards_port(ds, cpu_dp->ds->index, cpu_dp->index);
@@ -414,13 +451,24 @@ void mv88e6xxx_rmu_conduit_state_change(struct dsa_switch *ds,
if (ret < 0) {
dev_err(chip->dev, "RMU: initialization check failed %pe",
ERR_PTR(ret));
- goto out;
+ goto out_disable;
}
+
+ start = ktime_get();
+ ret = mv88e6xxx_port_read(chip, 0, MV88E6XXX_PORT_SWITCH_ID,
+ &id);
+ if (ret < 0) {
+ dev_err(chip->dev, "RMU: SMI latency read failed %pe",
+ ERR_PTR(ret));
+ goto out_disable;
+ }
+ chip->smi_read_latency = ktime_get() - start;
chip->rmu_state = MV88E6XXX_RMU_ENABLED;
dev_info(chip->dev, "RMU: enabled on port %d via conduit device %s",
port, chip->rmu_conduit->name);
} else {
+out_disable:
if (chip->info->ops->rmu_disable)
chip->info->ops->rmu_disable(chip);
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox