* Re: [PATCH v2 net-next] ipv4: fib: fix route re-dump in inet_dump_fib() on multi-batch dump
From: patchwork-bot+netdevbpf @ 2026-07-03 6:50 UTC (permalink / raw)
To: Pengfei Zhang
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, netdev,
linux-kernel, chenzhangqi, baohua, zhangpengfei16
In-Reply-To: <20260630084220.2711025-1-zhangfeionline@gmail.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 30 Jun 2026 16:42:20 +0800 you wrote:
> inet_dump_fib() saves its progress in cb->args[1] as a positional
> index within the current hash chain. Between batches, a concurrent
> fib_new_table() can insert a new table at the chain head, shifting
> all existing entries. On resume the saved index lands on a different
> table, causing already-dumped tables to be re-dumped and the
> originally suspended table to restart from the beginning.
>
> [...]
Here is the summary with links:
- [v2,net-next] ipv4: fib: fix route re-dump in inet_dump_fib() on multi-batch dump
https://git.kernel.org/netdev/net-next/c/2ed8d5c72488
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next 0/3] net: report multicast group user count
From: Paolo Abeni @ 2026-07-03 6:50 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, Andrew Lunn, David Ahern, Donald Hunter,
Eric Dumazet, Ido Schimmel, Jakub Kicinski, Shuah Khan,
Simon Horman, linux-kernel, linux-kselftest, netdev
In-Reply-To: <20260630110207.37841-1-sigefriedhyy@gmail.com>
On 6/30/26 1:02 PM, Yuyang Huang wrote:
> RTM_GETMULTICAST reports IPv4 and IPv6 multicast group membership, but
> does not include the per-group user count. Userspace therefore still has
> to parse /proc/net/igmp and /proc/net/igmp6 to obtain the Users column.
> In particular, this prevents iproute2 from moving "ip maddr show"
> entirely from procfs to rtnetlink.
>
> Add IFA_MC_USERS to carry the user count in RTM_GETMULTICAST dumps and
> RTM_NEWMULTICAST / RTM_DELMULTICAST notifications for both address
> families. Update the rt-addr YNL specification and extend the rtnetlink
> selftest to verify that two joins increase the reported count by two.
Will you take care of implementing the iproute2 bits, too?
Thanks,
Paolo
^ permalink raw reply
* [PATCH net-next 1/2] net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages
From: Luke Howard @ 2026-07-03 6:41 UTC (permalink / raw)
To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
Richard Cochran
Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
Christoph Mellauner, Simon Gapp, netdev, linux-kernel,
Luke Howard
In-Reply-To: <20260703-mv88e6xxx-ptp-fixes-v1-0-0138581889a9@padl.com>
mv88e6xxx switches have two arrival timestamp counters for timestamping
PTP event messages. This permits more than one arriving event message's
timestamp to be captured. This is useful for the case where event
messages from a grandmaster arrive at the same time as PDelayReq/PDelayResp
messages from a peer.
Previously only PDelayResp messages were assigned to the second arrival
counter; this patch does so for PDelayReq messages as well.
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/hwtstamp.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
index 6e6472a3b75ad..57ff77496864f 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
@@ -319,9 +319,16 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip,
&ps->rx_queue2);
}
-static int is_pdelay_resp(const struct ptp_header *hdr)
+static bool is_pdelay_msg(const struct ptp_header *hdr)
{
- return (hdr->tsmt & 0xf) == 3;
+ switch (ptp_get_msgtype(hdr, PTP_CLASS_V2)) {
+ case PTP_MSGTYPE_PDELAY_REQ:
+ fallthrough;
+ case PTP_MSGTYPE_PDELAY_RESP:
+ return true;
+ default:
+ return false;
+ }
}
bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
@@ -343,7 +350,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
SKB_PTP_TYPE(skb) = type;
- if (is_pdelay_resp(hdr))
+ if (is_pdelay_msg(hdr))
skb_queue_tail(&ps->rx_queue2, skb);
else
skb_queue_tail(&ps->rx_queue, skb);
@@ -584,8 +591,9 @@ int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip)
if (err)
return err;
- /* Use ARRIVAL1 for peer delay response messages. */
+ /* Use ARRIVAL1 for peer delay messages. */
err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR,
+ MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ |
MV88E6XXX_PTP_MSGTYPE_PDLAY_RES);
if (err)
return err;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 0/2] net: dsa: mv88e6xxx: various hwstamp fixes
From: Luke Howard @ 2026-07-03 6:41 UTC (permalink / raw)
To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
Richard Cochran
Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
Christoph Mellauner, Simon Gapp, netdev, linux-kernel,
Luke Howard
Two fixes for improving the reliably of hardware timestamp acquisition
on Marvell switches. In our tests this eliminated missed timestamps in
ptp4l.
Signed-off-by: Luke Howard <lukeh@padl.com>
---
Luke Howard (2):
net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages
net: dsa: mv88e6xxx: embedded PTP timestamp support
drivers/net/dsa/mv88e6xxx/chip.c | 3 +
drivers/net/dsa/mv88e6xxx/chip.h | 8 +++
drivers/net/dsa/mv88e6xxx/hwtstamp.c | 132 +++++++++++++++++++++++++++++++----
drivers/net/dsa/mv88e6xxx/hwtstamp.h | 14 ++++
4 files changed, 144 insertions(+), 13 deletions(-)
---
base-commit: b8ea7da314c2efcb9c2f559ed65b7a36c869d68e
change-id: 20260630-mv88e6xxx-ptp-fixes-1732570b8829
Best regards,
--
Luke Howard <lukeh@padl.com>
^ permalink raw reply
* Re: [PATCH net-next 0/3] net: report multicast group user count
From: Yuyang Huang @ 2026-07-03 6:52 UTC (permalink / raw)
To: Paolo Abeni
Cc: David S. Miller, Andrew Lunn, David Ahern, Donald Hunter,
Eric Dumazet, Ido Schimmel, Jakub Kicinski, Shuah Khan,
Simon Horman, linux-kernel, linux-kselftest, netdev
In-Reply-To: <2a0033b6-9a54-4de3-b1a8-78d4bc815f56@redhat.com>
On Fri, Jul 3, 2026 at 3:50 PM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On 6/30/26 1:02 PM, Yuyang Huang wrote:
> > RTM_GETMULTICAST reports IPv4 and IPv6 multicast group membership, but
> > does not include the per-group user count. Userspace therefore still has
> > to parse /proc/net/igmp and /proc/net/igmp6 to obtain the Users column.
> > In particular, this prevents iproute2 from moving "ip maddr show"
> > entirely from procfs to rtnetlink.
> >
> > Add IFA_MC_USERS to carry the user count in RTM_GETMULTICAST dumps and
> > RTM_NEWMULTICAST / RTM_DELMULTICAST notifications for both address
> > families. Update the rt-addr YNL specification and extend the rtnetlink
> > selftest to verify that two joins increase the reported count by two.
>
> Will you take care of implementing the iproute2 bits, too?
Yes, I actually have something WIP for iproute2 and realized it cannot
be done due to the missing fields. That's why I sent out this patch
series first.
^ permalink raw reply
* [PATCH RFC net-next] net: bridge: add BRIDGE_VLAN_INFO_DYNAMIC flag
From: Luke Howard @ 2026-07-03 6:54 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Nikolay Aleksandrov, Ido Schimmel, Simon Horman
Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
Christoph Mellauner, Simon Gapp, netdev, bridge, linux-kernel,
Luke Howard
Dynamic VLAN entries as specified in IEEE Std 802.1Q-2022,
Clause 8.8.5. These allow a user-space VLAN registration
protocol such as MVRP to mark a bridge's VLAN entries as
dynamic, so they can be distinguished from administratively
configured static entries.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Luke Howard <lukeh@padl.com>
---
This patch adds support for Dynamic VLAN Entries, as specified
in 802.1Q. I have marked it as RFC as a similar patch to add
support for Dynamic Reservation Entries to the FDB was rejected
on the argument that the dynamic bit should be stored separately.
Our MVRP implementation [1] will use this flag if available.
[1] https://github.com/PADL/OpenSRP
---
include/uapi/linux/if_bridge.h | 1 +
net/bridge/br_netlink.c | 6 ++++++
net/bridge/br_vlan.c | 18 ++++++++++++++----
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 21a700c02ef76..a3abbfe79694b 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -134,6 +134,7 @@ enum {
#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */
#define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */
#define BRIDGE_VLAN_INFO_ONLY_OPTS (1<<6) /* Skip create/delete/flags */
+#define BRIDGE_VLAN_INFO_DYNAMIC (1<<7) /* 802.1Q Dynamic VLAN Registration Entry */
struct bridge_vlan_info {
__u16 flags;
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index b2cd4e39326d0..90f2e103f53d5 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -388,6 +388,9 @@ static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+ if (v->flags & BRIDGE_VLAN_INFO_DYNAMIC)
+ flags |= BRIDGE_VLAN_INFO_DYNAMIC;
+
if (vid_range_start == 0) {
goto initvars;
} else if ((v->vid - vid_range_end) == 1 &&
@@ -440,6 +443,9 @@ static int br_fill_ifvlaninfo(struct sk_buff *skb,
if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+ if (v->flags & BRIDGE_VLAN_INFO_DYNAMIC)
+ vinfo.flags |= BRIDGE_VLAN_INFO_DYNAMIC;
+
if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
sizeof(vinfo), &vinfo))
goto nla_put_failure;
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 5560afcaaca32..e6bc59e3a4c4f 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -54,9 +54,11 @@ static void __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid)
vg->pvid = 0;
}
-/* Update the BRIDGE_VLAN_INFO_PVID and BRIDGE_VLAN_INFO_UNTAGGED flags of @v.
- * If @commit is false, return just whether the BRIDGE_VLAN_INFO_PVID and
- * BRIDGE_VLAN_INFO_UNTAGGED bits of @flags would produce any change onto @v.
+/* Update the BRIDGE_VLAN_INFO_PVID, BRIDGE_VLAN_INFO_UNTAGGED and
+ * BRIDGE_VLAN_INFO_DYNAMIC flags of @v.
+ * If @commit is false, return just whether the BRIDGE_VLAN_INFO_PVID,
+ * BRIDGE_VLAN_INFO_UNTAGGED and BRIDGE_VLAN_INFO_DYNAMIC bits of @flags
+ * would produce any change onto @v.
*/
static bool __vlan_flags_update(struct net_bridge_vlan *v, u16 flags,
bool commit)
@@ -71,7 +73,8 @@ static bool __vlan_flags_update(struct net_bridge_vlan *v, u16 flags,
/* check if anything would be changed on commit */
change = !!(flags & BRIDGE_VLAN_INFO_PVID) == !!(vg->pvid != v->vid) ||
- ((flags ^ v->flags) & BRIDGE_VLAN_INFO_UNTAGGED);
+ ((flags ^ v->flags) & (BRIDGE_VLAN_INFO_UNTAGGED |
+ BRIDGE_VLAN_INFO_DYNAMIC));
if (!commit)
goto out;
@@ -86,6 +89,11 @@ static bool __vlan_flags_update(struct net_bridge_vlan *v, u16 flags,
else
v->flags &= ~BRIDGE_VLAN_INFO_UNTAGGED;
+ if (flags & BRIDGE_VLAN_INFO_DYNAMIC)
+ v->flags |= BRIDGE_VLAN_INFO_DYNAMIC;
+ else
+ v->flags &= ~BRIDGE_VLAN_INFO_DYNAMIC;
+
out:
return change;
}
@@ -1874,6 +1882,8 @@ static bool br_vlan_fill_vids(struct sk_buff *skb, u16 vid, u16 vid_range,
info.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
if (flags & BRIDGE_VLAN_INFO_PVID)
info.flags |= BRIDGE_VLAN_INFO_PVID;
+ if (flags & BRIDGE_VLAN_INFO_DYNAMIC)
+ info.flags |= BRIDGE_VLAN_INFO_DYNAMIC;
if (nla_put(skb, BRIDGE_VLANDB_ENTRY_INFO, sizeof(info), &info))
goto out_err;
---
base-commit: 2bb62a85aff6d4c14a62a476dfabaada3c1cc014
change-id: 20260703-vlan-dynamic-entry-56a028e8990e
Best regards,
--
Luke Howard <lukeh@padl.com>
^ permalink raw reply related
* Re: [PATCH net-next v2 0/2] tools: ynl: pyynl: pull the --family resolution logic into the lib
From: patchwork-bot+netdevbpf @ 2026-07-03 7:00 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
donald.hunter, sdf, gal, jstancek, ast
In-Reply-To: <20260701021751.3234681-1-kuba@kernel.org>
Hello:
This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 30 Jun 2026 19:17:49 -0700 you wrote:
> When packaging YNL as a system level utility we added a --family
> argument which auto-resolves the full spec path from a well known
> path in /usr/share. Spelling out full YAML spec files is at this
> point only done in-tree, for example in the selftests which need
> the very latest YAML. But the selftests have their own wrapping
> classes for each family so test authors aren't really bothered
> by having to spell the paths out.
>
> [...]
Here is the summary with links:
- [net-next,v2,1/2] tools: ynl: pyynl: re-export the library API from the package root
https://git.kernel.org/netdev/net-next/c/df87e5c4e94e
- [net-next,v2,2/2] tools: ynl: pyynl: pull the --family resolution logic into the lib
https://git.kernel.org/netdev/net-next/c/10c90f1bba3a
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next 0/3] net: report multicast group user count
From: patchwork-bot+netdevbpf @ 2026-07-03 7:00 UTC (permalink / raw)
To: Yuyang Huang
Cc: davem, andrew+netdev, dsahern, donald.hunter, edumazet, idosch,
kuba, pabeni, shuah, horms, linux-kernel, linux-kselftest, netdev
In-Reply-To: <20260630110207.37841-1-sigefriedhyy@gmail.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 30 Jun 2026 20:02:04 +0900 you wrote:
> RTM_GETMULTICAST reports IPv4 and IPv6 multicast group membership, but
> does not include the per-group user count. Userspace therefore still has
> to parse /proc/net/igmp and /proc/net/igmp6 to obtain the Users column.
> In particular, this prevents iproute2 from moving "ip maddr show"
> entirely from procfs to rtnetlink.
>
> Add IFA_MC_USERS to carry the user count in RTM_GETMULTICAST dumps and
> RTM_NEWMULTICAST / RTM_DELMULTICAST notifications for both address
> families. Update the rt-addr YNL specification and extend the rtnetlink
> selftest to verify that two joins increase the reported count by two.
>
> [...]
Here is the summary with links:
- [net-next,1/3] net: ipv4: report multicast group user count
https://git.kernel.org/netdev/net-next/c/7cb8198761e6
- [net-next,2/3] net: ipv6: report multicast group user count
https://git.kernel.org/netdev/net-next/c/e1d0f3f08391
- [net-next,3/3] selftests: net: check multicast group user count
https://git.kernel.org/netdev/net-next/c/3373cb099e66
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* RE: [PATCH v2 net-next 02/14] net: enetc: extract common helpers for MAC hash filter configuration
From: Wei Fang (OSS) @ 2026-07-03 7:02 UTC (permalink / raw)
To: sashiko-reviews@lists.linux.dev
Cc: imx@lists.linux.dev, Frank.Li@kernel.org, network dev
In-Reply-To: <20260703032702.231E51F000E9@smtp.kernel.org>
> commit 0373b31ab4e7a0beb8461d5ceb05e09d7d5b61ab
> Author: Wei Fang <wei.fang@nxp.com>
> Date: Thu Jul 2 02:57:02 2026 +0000
>
> net: enetc: extract common helpers for MAC hash filter configuration
>
> The patch extracts enetc_set_si_uc_hash_filter() and
> enetc_set_si_mc_hash_filter() into enetc_pf_common.c. This allows sharing
> the MAC hash filter configuration between ENETC v1 and v4 drivers, and
> removes redundant hardware-specific functions.
>
> > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> > index a97d2e2dd07b7..db2a800a7aaf0 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> [ ... ]
> > @@ -135,7 +108,7 @@ static void enetc_sync_mac_filters(struct enetc_pf
> *pf)
> > if (em) {
> > int err;
> >
> > - enetc_clear_mac_ht_flt(si, 0, UC);
> > + enetc_set_si_uc_hash_filter(si, 0, 0);
> >
> > err = enetc_set_mac_flt_entry(si, pos, f->mac_addr,
> > BIT(0));
> > @@ -147,11 +120,15 @@ static void enetc_sync_mac_filters(struct enetc_pf
> *pf)
> > err);
> > }
> >
> > + bitmap_to_arr64(&hash, f->mac_hash_table,
> > + ENETC_MADDR_HASH_TBL_SZ);
> > /* hash table filter, clear EM filter for UC entries */
> > - if (i == UC)
> > + if (i == UC) {
> > enetc_clear_mac_flt_entry(si, pos);
> > -
> > - enetc_set_mac_ht_flt(si, 0, i, *f->mac_hash_table);
> > + enetc_set_si_uc_hash_filter(si, 0, hash);
>
> [Severity: High]
> This is a pre-existing issue, but does this fallback path actually work for
> exact match MAC addresses?
>
> In enetc_pf_set_rx_mode(), when configuring a single unicast MAC address,
> enetc_add_mac_addr_em_filter() is called, but the hash table population is
> explicitly skipped:
>
> enetc_pf_set_rx_mode() {
> ...
> em = (netdev_uc_count(ndev) == 1);
> netdev_for_each_uc_addr(ha, ndev) {
> if (em) {
> enetc_add_mac_addr_em_filter(filter, ha->addr);
> break;
> }
> enetc_add_mac_addr_ht_filter(filter, ha->addr);
> }
> ...
> }
>
> Because of this, mac_hash_table remains empty for the EM filter.
>
> If enetc_set_mac_flt_entry() fails in enetc_sync_mac_filters(), the driver
> warns "fallback to HT filt" and falls through to this block. However, since
> the hash table was never populated, the derived hash is 0.
>
> Doesn't this result in programming an empty hash filter, which will silently
> drop all packets for this intended MAC address instead of falling back to
> hash-based filtering?
>
Yes, the code behavior is inconsistent with the comments, and this issue
will be resolved by adding a fix patch. The current patch does not require
modification.
^ permalink raw reply
* [syzbot] [mm?] INFO: rcu detected stall in unmap_region
From: syzbot @ 2026-07-03 7:03 UTC (permalink / raw)
To: akpm, jannh, liam, linux-kernel, linux-mm, ljs, netdev, pfalcato,
syzkaller-bugs, vbabka
Hello,
syzbot found the following issue on:
HEAD commit: 32f1c2bbb26a net: airoha: dma map xmit frags with skb_frag..
git tree: net
console output: https://syzkaller.appspot.com/x/log.txt?x=116c2c0a580000
kernel config: https://syzkaller.appspot.com/x/.config?x=86ba763b42fa66a
dashboard link: https://syzkaller.appspot.com/bug?extid=2ad5ec205a38c46522b3
compiler: Debian clang version 22.1.8 (++20260613092233+e80beda6e255-1~exp1~20260613092250.77), Debian LLD 22.1.8
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=132f5861580000
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/7b7c3a22a8ed/disk-32f1c2bb.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/168b43c87305/vmlinux-32f1c2bb.xz
kernel image: https://storage.googleapis.com/syzbot-assets/70704720d284/bzImage-32f1c2bb.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+2ad5ec205a38c46522b3@syzkaller.appspotmail.com
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: 0-...!: (1 GPs behind) idle=6664/1/0x4000000000000000 softirq=17730/17732 fqs=2
rcu: (detected by 1, t=10502 jiffies, g=17101, q=1895 ncpus=2)
Sending NMI from CPU 1 to CPUs 0:
NMI backtrace for cpu 0
CPU: 0 UID: 0 PID: 6010 Comm: modprobe Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026
RIP: 0010:lock_release+0x5/0x3c0 kernel/locking/lockdep.c:5876
Code: e9 4f fe ff ff 41 bf 2f 00 00 00 e9 06 ff ff ff 0f 1f 44 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 55 <41> 57 41 56 41 55 41 54 53 48 83 ec 30 49 89 f5 49 89 fe 65 48 8b
RSP: 0018:ffffc90000007db0 EFLAGS: 00000082
RAX: 932ea09fa7cced00 RBX: 0000000000000092 RCX: 0000000000000002
RDX: 0000000000000001 RSI: ffffffff81b30e19 RDI: ffffffff9a73d4c8
RBP: ffff88807e29b300 R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: fffff52000000f98 R12: ffff8880b86281c0
R13: dffffc0000000000 R14: ffffffff9a73d4b0 R15: ffff8880b8628350
FS: 0000000000000000(0000) GS:ffff888125226000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffae81e84e8 CR3: 000000007ee52000 CR4: 00000000003526f0
Call Trace:
<IRQ>
__raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:176 [inline]
_raw_spin_unlock_irqrestore+0x1b/0x80 kernel/locking/spinlock.c:198
debug_hrtimer_deactivate kernel/time/hrtimer.c:490 [inline]
__run_hrtimer kernel/time/hrtimer.c:2000 [inline]
__hrtimer_run_queues+0x239/0xa10 kernel/time/hrtimer.c:2096
hrtimer_interrupt+0x448/0x910 kernel/time/hrtimer.c:2215
local_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1051 [inline]
__sysvec_apic_timer_interrupt+0x102/0x430 arch/x86/kernel/apic/apic.c:1068
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:do_zap_pte_range mm/memory.c:1819 [inline]
RIP: 0010:zap_pte_range mm/memory.c:1934 [inline]
RIP: 0010:zap_pmd_range mm/memory.c:2020 [inline]
RIP: 0010:zap_pud_range mm/memory.c:2048 [inline]
RIP: 0010:zap_p4d_range mm/memory.c:2069 [inline]
RIP: 0010:__zap_vma_range+0xfbd/0x4f10 mm/memory.c:2109
Code: 4c 89 64 24 30 4c 89 e6 48 83 e6 9f 31 ff e8 3a 4a ad ff 4d 89 e7 49 83 e4 9f 75 2f 48 8b 44 24 10 4c 01 e8 48 83 f8 01 74 28 <e8> 3e 45 ad ff 48 83 c3 08 49 ff c5 4d 89 f4 eb a3 4d 89 ef e8 2a
RSP: 0018:ffffc90003287000 EFLAGS: 00000286
RAX: ffffffffffffff73 RBX: ffff888034e9d190 RCX: ffff88802dcf1f00
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: ffffc900032872f0 R08: ffff888078979803 R09: 1ffff1100f12f300
R10: dffffc0000000000 R11: ffffed100f12f301 R12: 0000000000000000
R13: 0000000000000032 R14: dffffc0000000000 R15: 0000000000000000
unmap_vmas+0x390/0x550 mm/memory.c:2178
unmap_region+0x208/0x330 mm/vma.c:488
vms_clear_ptes mm/vma.c:1303 [inline]
vms_clean_up_area mm/vma.c:1315 [inline]
__mmap_setup mm/vma.c:2476 [inline]
__mmap_region mm/vma.c:2756 [inline]
mmap_region+0xc62/0x2310 mm/vma.c:2860
do_mmap+0xc3b/0x10c0 mm/mmap.c:560
vm_mmap_pgoff+0x272/0x4e0 mm/util.c:581
ksys_mmap_pgoff+0x4dc/0x760 mm/mmap.c:606
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ffae8219242
Code: 08 00 04 00 00 eb e2 90 41 f7 c1 ff 0f 00 00 75 27 55 89 cd 53 48 89 fb 48 85 ff 74 33 41 89 ea 48 89 df b8 09 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 5e 5b 5d c3 0f 1f 00 c7 05 46 40 01 00 16 00
RSP: 002b:00007ffec64cd428 EFLAGS: 00000206 ORIG_RAX: 0000000000000009
RAX: ffffffffffffffda RBX: 00007ffae7f73000 RCX: 00007ffae8219242
RDX: 0000000000000005 RSI: 000000000014e000 RDI: 00007ffae7f73000
RBP: 0000000000000812 R08: 0000000000000000 R09: 0000000000028000
R10: 0000000000000812 R11: 0000000000000206 R12: 00007ffec64cd478
R13: 00007ffae81ed5f0 R14: 00007ffec64cdc60 R15: 00000fffd8c99a88
</TASK>
rcu: rcu_preempt kthread starved for 10477 jiffies! g17101 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x0 ->cpu=1
rcu: Unless rcu_preempt kthread gets sufficient CPU time, OOM is now expected behavior.
rcu: RCU grace-period kthread stack dump:
task:rcu_preempt state:R running task stack:28192 pid:16 tgid:16 ppid:2 task_flags:0x208040 flags:0x00080000
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5510 [inline]
__schedule+0x17d9/0x56c0 kernel/sched/core.c:7234
__schedule_loop kernel/sched/core.c:7311 [inline]
schedule+0x164/0x2b0 kernel/sched/core.c:7326
schedule_timeout+0x152/0x2c0 kernel/time/sleep_timeout.c:99
rcu_gp_fqs_loop+0x30c/0x11f0 kernel/rcu/tree.c:2123
rcu_gp_kthread+0x9e/0x2b0 kernel/rcu/tree.c:2325
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
rcu: Stack dump where RCU GP kthread last ran:
CPU: 1 UID: 0 PID: 4992 Comm: udevd Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026
RIP: 0010:csd_lock_wait kernel/smp.c:342 [inline]
RIP: 0010:smp_call_function_many_cond+0x10b0/0x14b0 kernel/smp.c:892
Code: c0 75 73 41 8b 1e 89 de 83 e6 01 31 ff e8 d8 1e 0c 00 83 e3 01 48 bb 00 00 00 00 00 fc ff df 75 07 e8 84 1a 0c 00 eb 37 f3 90 <41> 0f b6 04 1c 84 c0 75 10 41 f7 06 01 00 00 00 74 1e e8 69 1a 0c
RSP: 0018:ffffc90003837580 EFLAGS: 00000293
RAX: ffffffff81ba5717 RBX: dffffc0000000000 RCX: ffff88807e5a0000
RDX: 0000000000000000 RSI: 0000000000000001 RDI: 0000000000000000
RBP: ffffc900038376a8 R08: ffffffff9032d2f7 R09: 1ffffffff2065a5e
R10: dffffc0000000000 R11: fffffbfff2065a5f R12: 1ffff110170c85fd
R13: ffff8880b873c448 R14: ffff8880b8642fe8 R15: 0000000000000000
FS: 00007fdd3255d880(0000) GS:ffff888125326000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00002c919703e000 CR3: 000000007e7f2000 CR4: 00000000003526f0
Call Trace:
<TASK>
on_each_cpu_cond_mask+0x3f/0x80 kernel/smp.c:1057
__flush_tlb_multi arch/x86/include/asm/paravirt.h:46 [inline]
flush_tlb_multi arch/x86/mm/tlb.c:1361 [inline]
flush_tlb_mm_range+0x5c4/0x1090 arch/x86/mm/tlb.c:1451
dup_mmap+0x1786/0x1d90 mm/mmap.c:1905
dup_mm kernel/fork.c:1538 [inline]
copy_mm+0x11a/0x480 kernel/fork.c:1590
copy_process+0x1e99/0x43f0 kernel/fork.c:2288
kernel_clone+0x2d7/0x940 kernel/fork.c:2747
__do_sys_clone kernel/fork.c:2888 [inline]
__se_sys_clone kernel/fork.c:2872 [inline]
__x64_sys_clone+0x1b6/0x230 kernel/fork.c:2872
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fdd31ef1636
Code: 89 df e8 6d e8 f6 ff 45 31 c0 31 d2 31 f6 64 48 8b 04 25 10 00 00 00 bf 11 00 20 01 4c 8d 90 d0 02 00 00 b8 38 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 52 89 c5 85 c0 75 31 64 48 8b 04 25 10 00 00
RSP: 002b:00007ffcd95f6670 EFLAGS: 00000246 ORIG_RAX: 0000000000000038
RAX: ffffffffffffffda RBX: 00007ffcd95f6678 RCX: 00007fdd31ef1636
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000001200011
RBP: 0000564712dd5910 R08: 0000000000000000 R09: 0000564712ddd8e0
R10: 00007fdd3255db50 R11: 0000000000000246 R12: 00007ffcd95f6a30
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
</TASK>
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ permalink raw reply
* Re: [PATCH 2/3] arm64: dts: socfpga: agilex5: Add SoCDK TSN Config2 board
From: Nazle Asmade, Muhammad Nazim Amirul @ 2026-07-03 7:04 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: <d70a6795-e1a2-43e2-b523-0fc668324674@lunn.ch>
On 1/7/2026 8:47 pm, Andrew Lunn wrote:
>>> # There are a small number of cases where the MAC has hard coded
>>> # delays which cannot be disabled. The 'phy-mode' only describes the
>>> # PCB. The inability to disable the delays in the MAC does not change
>>> # the meaning of 'phy-mode'. It does however mean that a 'phy-mode' of
>>> # 'rgmii' is now invalid, it cannot be supported, since both the PCB
>>> # and the MAC and PHY adding delays cannot result in a functional
>>> # link. Thus the MAC should report a fatal error for any modes which
>>> # cannot be supported. When the MAC implements the delay, it must
>>> # ensure that the PHY does not also implement the same delay. So it
>>> # must modify the phy-mode it passes to the PHY, removing the delay it
>>> # has added. Failure to remove the delay will result in a
>>> # non-functioning link.
>>>
>>> Andrew
>>>
>>> ---
>>> pw-bot: cr
>> Hi Andrew,
>>
>> The delays are provided by the FPGA GMII-to-RGMII converter soft IP,
>> which is hardcoded in the FPGA bitstream and cannot be disabled or
>> modified from the driver side.
>>
>> Using phy-mode = "rgmii" is intentional here — it prevents the PHY from
>> adding its own internal delays on top, since the FPGA converter already
>> provides the full required delay. This is consistent with how all other
>> Agilex5 SoCDK board variants are described, as seen in commit
>> c5637e5ceb4b ("arm64: dts: socfpga: agilex5: Fix phy-mode to rgmii as HW
>> provides clock delay") already in Dinh Nguyen's tree, which applies the
>> same rationale across all Agilex5 boards.
>
> I've become more insistent that designs get this correct. So i don't
> care too much about past systems. Many vendors are having to fix up
> their drivers and DT in order to make new boards consistent.
>
> You can look at your system as the FPGA being the MAC, and the PHY is
> the PHY. The PCB is not providing the delay, the MAC is. This exactly
> fits the description above.
>
> Andrew
Hi Andrew,
Thank you for the clarification. We agree with your framework in
principle, but would like to explain why phy-mode = "rgmii" is the
appropriate description for this specific case.
After getting more information from hw team, for Agilex specific device,
the RGMII timing delays on this board are provided by an FPGA delay
chain (Input/Output Delay Chain primitives in the FPGA fabric). The
reason for using the FPGA rather than the PHY is that the Marvell PHY on
this board only supports 0ns or 2ns delay steps — too coarse to meet the
RGMII timing requirements. The FPGA delay chain provides up to 63 steps
of ~0.1ns precision, which the hardware team has tuned at design time to
achieve correct signal timing.
This delay is fixed in the FPGA bitstream and is invisible to the Linux
driver — the driver cannot read, modify, or even detect the delay value
that was programmed. The driver has no way to know "the MAC is providing
X nanoseconds of delay."
In this situation, the only meaningful action the driver can take is to
instruct the PHY not to add its own delays — which phy-mode = "rgmii"
already achieves correctly. Changing to phy-mode = "rgmii-id" and having
the driver strip the delay before passing to the PHY would produce the
same hardware behaviour (PHY adds zero delay), but would add driver
complexity with no practical benefit, and would misrepresent the FPGA
delay as a driver-managed MAC delay when it is actually a fixed,
board-level hardware calibration.
Could you advise if you still prefer the rgmii-id approach given this
constraint?
BR,
Nazim
^ permalink raw reply
* Re: [PATCH net-next v4 3/3] net: phy: own phydev->psec via PSE notifier and remove fwnode_mdio hook
From: Paolo Abeni @ 2026-07-03 7:10 UTC (permalink / raw)
To: github
Cc: o.rempel, kory.maincent, andrew+netdev, hkallweit1, linux, davem,
edumazet, kuba, pabeni, corey, jelonek.jonas, horms, netdev,
linux-kernel
In-Reply-To: <20260630091125.3162481-4-github@szelinsky.de>
From: AI Reviewer <ai@example.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
Now I have all the information I need. Let me produce the report.
commit da23b745f9e416f319d1edba4054506a8dabbe37
Author: Corey Leavitt <corey@leavitt.info>
Subject: net: phy: own phydev->psec via PSE notifier and remove fwnode_mdio hook
This patch moves ownership of phydev->psec from fwnode_mdio into the phy
subsystem via a PSE notifier, defers the final pse_control_put() from
phy_device_remove() to phy_device_release(), and adds a
phy_device_register_locked() variant for callers that already hold rtnl.
Link: https://lore.kernel.org/netdev/e00048dd-1ed3-40c3-9912-59bccf015ad5@gmail.com/
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index 0615228459ef..f5febff4b00b 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -223,8 +223,19 @@ static void phy_mdio_device_free(struct mdio_device *mdiodev)
>
> static void phy_device_release(struct device *dev)
> {
> + struct phy_device *phydev = to_phy_device(dev);
> +
> + /* bus_for_each_dev() holds get_device() across each iteration
> + * step, deferring this release callback until any in-flight PSE
> + * notifier walk has advanced past this phy. pse_control_put()
> + * takes pse_list_mutex, so this path must run in sleepable
> + * context.
> + */
> + might_sleep();
> + pse_control_put(phydev->psec);
> +
> fwnode_handle_put(dev->fwnode);
> - kfree(to_phy_device(dev));
> + kfree(phydev);
> }
[High]
Can this combine with the PSE_UNREGISTERED walk to leave a use-after-free
window for phys that have already been device_del()'d but still have an
outstanding reference?
The notifier walks the bus klist:
case PSE_UNREGISTERED:
rtnl_lock();
bus_for_each_dev(&mdio_bus_type, NULL, data,
phy_pse_detach_one);
rtnl_unlock();
bus_for_each_dev() iterates subsys_private's klist_devices, and
device_del() -> bus_remove_device() -> klist_del() unlinks the device
from that klist. A phy that has been removed from the bus but is still
pinned (for example by a netdev that took get_device() via
phy_attach_direct()) is no longer reachable from this walk, so
phy_pse_detach_one() will not clear its phydev->psec.
> @@ -1152,8 +1299,6 @@ EXPORT_SYMBOL(phy_device_register);
> void phy_device_remove(struct phy_device *phydev)
> {
> unregister_mii_timestamper(phydev->mii_ts);
> - pse_control_put(phydev->psec);
> -
> device_del(&phydev->mdio.dev);
>
> /* Assert the reset signal */
[High]
Previously phy_device_remove() performed pse_control_put() synchronously
before device_del(), so the put always ran while phydev->psec still
pointed at a live pcdev. After this change the put is deferred to
phy_device_release(), which only runs when the last device reference is
dropped.
Consider this sequence on two CPUs:
CPU A (phy teardown) CPU B (rmmod PSE driver)
-------------------------------- -----------------------------------
phy_detach()
/* netdev still pins phydev */
phy_device_remove()
device_del() /* off klist */
pse_controller_unregister()
blocking_notifier_call_chain(
PSE_UNREGISTERED)
phy_pse_notifier_event()
bus_for_each_dev(mdio_bus_type,
..., phy_pse_detach_one)
/* phydev not on klist,
* phydev->psec not cleared */
pse_release_pis()
kfree(pcdev->pi)
/* later, last ref dropped */
phy_device_release()
pse_control_put(phydev->psec)
__pse_control_release()
pcdev->pi[psec->id].admin_state_enabled
/* UAF: pcdev->pi already freed */
module_put(psec->pcdev->owner)
Does the commit message paragraph
"The final pse_control_put() of phydev->psec moves from
phy_device_remove() to phy_device_release(), so it runs only after
every reference on the device -- including the bus-iterator
references taken by bus_for_each_dev() in the notifier walk -- has
been dropped."
cover the case where a phy is already off the bus klist before the
notifier fires? bus_for_each_dev() only holds get_device() on devices
it can reach via the klist, so a phy that has been device_del()'d but
is still pinned by some other holder (an attached netdev being the
typical case) is invisible to phy_pse_detach_one().
Similarly, when an MDIO bus is torn down (mdiobus_unregister() walks
its phys and calls phy_device_remove() on each), is there any guarantee
that no consumer is still holding a reference that would carry the
phy past device_del() and into a later phy_device_release()?
If this race is reachable, would it be safer to keep the
pse_control_put() in phy_device_remove() (or somewhere else that runs
before device_del() returns), so the put cannot outlive any
pcdev->pi[] that PSE_UNREGISTERED is about to free?
--
This is an AI-generated review.
^ permalink raw reply
* [PATCH net-next v9 1/4] net: phy: c45: add genphy_c45_pma_soft_reset()
From: javen @ 2026-07-03 7:13 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb, maxime.chevallier
Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu
In-Reply-To: <20260703071330.1707-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
Add a generic Clause 45 software reset helper. The helper sets the reset
bit in the PMA/PMD control register and waits until the bit is cleared by
hardware.
Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes, new file
Changes in v3:
- re-order function according to the order in phy-c45.c
Changes in v4:
- no changes
Changes in v5:
- no changes
Changes in v6:
- increase timeout to 600ms
Changes in v7:
- no changes
Changes in v8:
- no changes
Changes in v9:
- rename genphy_c45_soft_reset to genphy_c45_pma_soft_reset
---
drivers/net/phy/phy-c45.c | 22 ++++++++++++++++++++++
include/linux/phy.h | 1 +
2 files changed, 23 insertions(+)
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 126951741428..c1f817a59739 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -384,6 +384,28 @@ int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart)
}
EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg);
+/**
+ * genphy_c45_pma_soft_reset - software reset the PHY via Clause 45 PMA/PMD control register
+ * @phydev: target phy_device struct
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int genphy_c45_pma_soft_reset(struct phy_device *phydev)
+{
+ int ret, val;
+
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,
+ MDIO_CTRL1_RESET);
+ if (ret < 0)
+ return ret;
+
+ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PMAPMD,
+ MDIO_CTRL1, val,
+ !(val & MDIO_CTRL1_RESET),
+ 5000, 600000, true);
+}
+EXPORT_SYMBOL_GPL(genphy_c45_pma_soft_reset);
+
/**
* genphy_c45_aneg_done - return auto-negotiation complete status
* @phydev: target phy_device struct
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 199a7aaa341b..d6bc19931fb8 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2309,6 +2309,7 @@ int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
/* Clause 45 PHY */
int genphy_c45_restart_aneg(struct phy_device *phydev);
int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart);
+int genphy_c45_pma_soft_reset(struct phy_device *phydev);
int genphy_c45_aneg_done(struct phy_device *phydev);
int genphy_c45_read_link(struct phy_device *phydev);
int genphy_c45_read_lpa(struct phy_device *phydev);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v9 3/4] net: phy: realtek: add support for RTL8261C_CG
From: javen @ 2026-07-03 7:13 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb, maxime.chevallier
Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu
In-Reply-To: <20260703071330.1707-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds support for Realtek phy chip RTL8261C_CG. Its PHY ID is
0x001cc898.
This patch introduces a distinct family of handlers (probe, get_features,
config_aneg, read_status, config_intr, handle_interrupt).
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes, new file
Changes in v3:
- re-order function according to the order in phy-c45.c
- add kernel-doc about return value
- add MASTER_SLAVE_CFG_MASTER_PREFERRED,
MASTER_SLAVE_CFG_SLAVE_PREFERRED, MASTER_SLAVE_CFG_UNKNOWN,
MASTER_SLAVE_CFG_UNSUPPORTED, MASTER_SLAVE_CFG_SLAVE_PREFERRED cfg
Changes in v4:
- no changes
Changes in v5:
- remove genphy_c45_pma_setup_forced() for this is already done when
calling genphy_c45_config_aneg()
Changes in v6:
- when PHY_INTERRUPT_DISABLE, clear IMR and ISR
- if AUTONEG_DISABLE, nothing need to do in rtl8261x_config_aneg
- add rtl8261x_read_status, support 1G speed
Changes in v7:
- remove RTL8261X_IMR and RTL8261X_ISR, duplicated definition
- modify commit message
- continue with default behavior when meet unknown sub_phy_id
- change the internal order of rtl8261x_read_status
- expand RTL8261X_INT_MASK_DEFAULT
- add the handle for ADVERTISE_1000HALF in rtl8261x_config_aneg
Changes in v8:
- no changes
Changes in v9:
- get an status from genphy_c45_aneg_done()
---
drivers/net/phy/realtek/realtek_main.c | 192 +++++++++++++++++++++++++
1 file changed, 192 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 27268811f564..bd12f10843d9 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -141,6 +141,10 @@
#define RTL8211F_PHYSICAL_ADDR_WORD1 17
#define RTL8211F_PHYSICAL_ADDR_WORD2 18
+#define RTL8261X_EXT_ADDR_REG 0xa436
+#define RTL8261X_EXT_DATA_REG 0xa438
+#define RTL_8261X_SUB_PHY_ID_ADDR 0x801d
+
#define RTL822X_VND1_SERDES_OPTION 0x697a
#define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0)
#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0
@@ -251,6 +255,32 @@
#define RTL_8221B_VM_CG 0x001cc84a
#define RTL_8251B 0x001cc862
#define RTL_8261C 0x001cc890
+#define RTL_8261C_CG 0x001cc898
+
+#define RTL8261C_CE_MODEL 0x00
+#define RTL8261X_INT_AUTONEG_ERROR BIT(0)
+#define RTL8261X_INT_PAGE_RECV BIT(2)
+#define RTL8261X_INT_AUTONEG_DONE BIT(3)
+#define RTL8261X_INT_LINK_CHG BIT(4)
+#define RTL8261X_INT_PHY_REG_ACCESS BIT(5)
+#define RTL8261X_INT_PME BIT(7)
+#define RTL8261X_INT_ALDPS_CHG BIT(9)
+#define RTL8261X_INT_JABBER BIT(10)
+
+#define RTL8261X_INT_MASK_DEFAULT (RTL8261X_INT_AUTONEG_DONE | \
+ RTL8261X_INT_LINK_CHG | \
+ RTL8261X_INT_AUTONEG_ERROR | \
+ RTL8261X_INT_JABBER)
+
+#define RTL8261X_INT_MASK_ALL (RTL8261X_INT_AUTONEG_ERROR | \
+ RTL8261X_INT_PAGE_RECV | \
+ RTL8261X_INT_AUTONEG_DONE | \
+ RTL8261X_INT_LINK_CHG | \
+ RTL8261X_INT_PHY_REG_ACCESS | \
+ RTL8261X_INT_PME | \
+ RTL8261X_INT_ALDPS_CHG | \
+ RTL8261X_INT_JABBER)
+
/* RTL8211E and RTL8211F support up to three LEDs */
#define RTL8211x_LED_COUNT 3
@@ -310,6 +340,156 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
return phy_restore_page(phydev, oldpage, ret);
}
+static int rtl8261x_probe(struct phy_device *phydev)
+{
+ int sub_phy_id, ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
+ RTL_8261X_SUB_PHY_ID_ADDR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_DATA_REG);
+ if (ret < 0)
+ return ret;
+
+ sub_phy_id = (ret >> 8) & 0xff;
+
+ switch (sub_phy_id) {
+ case RTL8261C_CE_MODEL:
+ phydev_info(phydev, "RTL8261C detected (sub_id 0x%02x)\n", sub_phy_id);
+ break;
+
+ default:
+ phydev_warn(phydev, "Unknown sub_id 0x%02x, default behavior\n", sub_phy_id);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rtl8261x_get_features(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_c45_pma_read_abilities(phydev);
+ if (ret)
+ return ret;
+ /*
+ * Supplement Multi-Gig speeds that may not be automatically detected
+ * RTL8261X supports 2.5G/5G in addition to standard 10G
+ */
+ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ phydev->supported);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+ phydev->supported);
+
+ return 0;
+}
+
+static int rtl8261x_read_status(struct phy_device *phydev)
+{
+ int ret, val = 0;
+
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ ret = genphy_c45_aneg_done(phydev);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(MII_STAT1000));
+ if (val < 0)
+ return val;
+ }
+ }
+
+ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
+
+ ret = genphy_c45_read_status(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rtl8261x_config_intr(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
+ RTL8261X_INT_MASK_DEFAULT);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rtl8261x_handle_interrupt(struct phy_device *phydev)
+{
+ int irq_status;
+
+ irq_status = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
+ if (irq_status < 0) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ if (!(irq_status & RTL8261X_INT_MASK_ALL))
+ return IRQ_NONE;
+
+ if (irq_status & (RTL8261X_INT_LINK_CHG | RTL8261X_INT_AUTONEG_DONE |
+ RTL8261X_INT_AUTONEG_ERROR | RTL8261X_INT_JABBER))
+ phy_trigger_machine(phydev);
+
+ return IRQ_HANDLED;
+}
+
+static int rtl8261x_config_aneg(struct phy_device *phydev)
+{
+ u16 adv_1g = 0;
+ int ret;
+
+ ret = genphy_c45_config_aneg(phydev);
+ if (ret < 0)
+ return ret;
+
+ if (phydev->autoneg == AUTONEG_DISABLE)
+ return 0;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->advertising))
+ adv_1g = ADVERTISE_1000FULL;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ phydev->advertising))
+ adv_1g |= ADVERTISE_1000HALF;
+
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(MII_CTRL1000),
+ ADVERTISE_1000FULL | ADVERTISE_1000HALF,
+ adv_1g);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ return genphy_c45_restart_aneg(phydev);
+
+ return 0;
+}
+
static int rtl821x_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -3001,6 +3181,18 @@ static struct phy_driver realtek_drvs[] = {
.resume = genphy_resume,
.read_mmd = genphy_read_mmd_unsupported,
.write_mmd = genphy_write_mmd_unsupported,
+ }, {
+ PHY_ID_MATCH_EXACT(RTL_8261C_CG),
+ .name = "Realtek RTL8261C 10Gbps PHY",
+ .probe = rtl8261x_probe,
+ .get_features = rtl8261x_get_features,
+ .config_aneg = rtl8261x_config_aneg,
+ .read_status = rtl8261x_read_status,
+ .config_intr = rtl8261x_config_intr,
+ .handle_interrupt = rtl8261x_handle_interrupt,
+ .soft_reset = genphy_c45_pma_soft_reset,
+ .suspend = genphy_c45_pma_suspend,
+ .resume = genphy_c45_pma_resume,
},
};
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v9 4/4] net: phy: realtek: load firmware for RTL8261C_CG
From: javen @ 2026-07-03 7:13 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb, maxime.chevallier
Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu
In-Reply-To: <20260703071330.1707-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds support for loading firmware. Download some parameters
for RTL8261C_CG.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- remove __pack, struct rtl8261x_fw_header and rtl8261x_fw_entry will not pad
- reverse xmas tree for some definition
- add explanation on rtl_phy_write_mmd_bits()
Changes in v3:
- add struct rtl8261x_priv
Changes in v4:
- add struct device *dev
Changes in v5:
- no changes
Changes in v6:
- replace rtl_phy_write_mmd_bits with phy_modify_mmd, keep mdio lock
- check msb and lsb at the beginning of rtl8261x_fw_execute_entry()
- add comments on rtl8261x_config_init()
Changes in v7:
- no changes
Changes in v8:
- remove some phydev_err message in rtl8261x_fw_execute_entry() and
rtl8261x_config_init()
Changes in v9:
- no changes
---
drivers/net/phy/realtek/realtek_main.c | 214 +++++++++++++++++++++++++
1 file changed, 214 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index bd12f10843d9..68a2de0b2bd3 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -8,7 +8,9 @@
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*/
#include <linux/bitops.h>
+#include <linux/crc32.h>
#include <linux/ethtool_netlink.h>
+#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/pm_wakeirq.h>
@@ -281,6 +283,43 @@
RTL8261X_INT_ALDPS_CHG | \
RTL8261X_INT_JABBER)
+#define FW_MAIN_MAGIC 0x52544C38
+#define FW_SUB_MAGIC_8261C 0x32363143
+#define RTL8261X_POLL_TIMEOUT_MS 100
+#define RTL8261X_MAX_MMD_DEV 31
+
+#define RTL8261C_CE_FW_NAME "rtl_nic/rtl8261c.bin"
+MODULE_FIRMWARE(RTL8261C_CE_FW_NAME);
+
+enum rtl8261x_fw_op {
+ OP_WRITE = 0x00, /* Write */
+ OP_POLL = 0x02, /* Polling */
+};
+
+struct rtl8261x_fw_header {
+ __le32 main_magic; /* Main magic number */
+ __le32 sub_magic; /* Sub magic number */
+ __le16 version_major; /* Major version */
+ __le16 version_minor; /* Minor version */
+ __le16 num_entries; /* Number of entries */
+ __le16 reserved; /* Reserved */
+ __le32 crc32; /* CRC32 checksum */
+};
+
+struct rtl8261x_fw_entry {
+ __u8 type; /* Operation type (OP_*) */
+ __u8 dev; /* MMD device */
+ __le16 addr; /* Register address */
+ __u8 msb; /* MSB bit position */
+ __u8 lsb; /* LSB bit position */
+ __le16 value; /* Value to write/compare */
+ __le16 timeout_ms; /* Poll timeout in milliseconds */
+ __u8 poll_set; /* Poll until equal (1) or not equal (0) */
+ __u8 reserved; /* Reserved */
+};
+
+#define FW_HEADER_SIZE sizeof(struct rtl8261x_fw_header)
+#define FW_ENTRY_SIZE sizeof(struct rtl8261x_fw_entry)
/* RTL8211E and RTL8211F support up to three LEDs */
#define RTL8211x_LED_COUNT 3
@@ -300,6 +339,11 @@ struct rtl821x_priv {
u16 iner;
};
+struct rtl8261x_priv {
+ const char *fw_name;
+ bool fw_loaded;
+};
+
static int rtl821x_read_page(struct phy_device *phydev)
{
return __phy_read(phydev, RTL821x_PAGE_SELECT);
@@ -342,8 +386,16 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
static int rtl8261x_probe(struct phy_device *phydev)
{
+ struct device *dev = &phydev->mdio.dev;
+ struct rtl8261x_priv *priv;
int sub_phy_id, ret;
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
RTL_8261X_SUB_PHY_ID_ADDR);
if (ret < 0)
@@ -357,6 +409,7 @@ static int rtl8261x_probe(struct phy_device *phydev)
switch (sub_phy_id) {
case RTL8261C_CE_MODEL:
+ priv->fw_name = RTL8261C_CE_FW_NAME;
phydev_info(phydev, "RTL8261C detected (sub_id 0x%02x)\n", sub_phy_id);
break;
@@ -413,6 +466,152 @@ static int rtl8261x_read_status(struct phy_device *phydev)
return 0;
}
+static int rtl8261x_verify_firmware(struct phy_device *phydev, const struct firmware *fw)
+{
+ const struct rtl8261x_fw_header *hdr;
+ u32 main_magic, sub_magic;
+ u32 calc_crc, file_crc;
+ size_t data_len;
+ u16 num_entries;
+
+ if (fw->size < FW_HEADER_SIZE) {
+ phydev_err(phydev, "Firmware too small: %zu bytes\n", fw->size);
+ return -EINVAL;
+ }
+
+ hdr = (const struct rtl8261x_fw_header *)fw->data;
+
+ main_magic = le32_to_cpu(hdr->main_magic);
+ if (main_magic != FW_MAIN_MAGIC) {
+ phydev_err(phydev, "Invalid firmware magic: 0x%08x\n", main_magic);
+ return -EINVAL;
+ }
+
+ sub_magic = le32_to_cpu(hdr->sub_magic);
+ if (sub_magic != FW_SUB_MAGIC_8261C) {
+ phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
+ return -EINVAL;
+ }
+
+ num_entries = le16_to_cpu(hdr->num_entries);
+ data_len = num_entries * FW_ENTRY_SIZE;
+
+ if (fw->size != sizeof(*hdr) + data_len) {
+ phydev_err(phydev, "Firmware size mismatch\n");
+ return -EINVAL;
+ }
+
+ calc_crc = crc32(~0, fw->data + FW_HEADER_SIZE, data_len) ^ ~0;
+ file_crc = le32_to_cpu(hdr->crc32);
+
+ if (calc_crc != file_crc) {
+ phydev_err(phydev, "CRC32 mismatch: calculated=0x%08x file=0x%08x\n",
+ calc_crc, file_crc);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtl8261x_fw_execute_entry(struct phy_device *phydev,
+ const struct rtl8261x_fw_entry *entry)
+{
+ u16 addr, value, timeout_ms;
+ u8 dev, msb, lsb, poll_set;
+ u32 bits, expect_val;
+ int ret, val;
+
+ dev = entry->dev;
+ addr = le16_to_cpu(entry->addr);
+ msb = entry->msb;
+ lsb = entry->lsb;
+ value = le16_to_cpu(entry->value);
+ timeout_ms = le16_to_cpu(entry->timeout_ms);
+ poll_set = entry->poll_set;
+
+ if (timeout_ms == 0)
+ timeout_ms = RTL8261X_POLL_TIMEOUT_MS;
+
+ if (dev > RTL8261X_MAX_MMD_DEV) {
+ phydev_err(phydev, "invalid firmware MMD device: dev=%u\n", dev);
+ return -EINVAL;
+ }
+
+ if (msb > 15 || lsb > msb) {
+ phydev_err(phydev, "invalid firmware bits: msb=%u, lsb=%u\n", msb, lsb);
+ return -EINVAL;
+ }
+
+ switch (entry->type) {
+ case OP_WRITE:
+ ret = phy_modify_mmd(phydev, dev, addr,
+ GENMASK(msb, lsb), (value << lsb) & GENMASK(msb, lsb));
+ if (ret)
+ return ret;
+ break;
+
+ case OP_POLL:
+ bits = GENMASK(msb, lsb);
+ expect_val = (value << lsb) & bits;
+
+ if (poll_set)
+ ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
+ (val & bits) == expect_val,
+ 1000, timeout_ms * 1000, false);
+ else
+ ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
+ (val & bits) != expect_val,
+ 1000, timeout_ms * 1000, false);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtl8261x_fw_load(struct phy_device *phydev)
+{
+ struct rtl8261x_priv *priv = phydev->priv;
+ const struct rtl8261x_fw_entry *entry;
+ const struct rtl8261x_fw_header *hdr;
+ const struct firmware *fw;
+ int ret, i;
+
+ if (!priv->fw_name)
+ return 0;
+
+ ret = request_firmware(&fw, priv->fw_name, &phydev->mdio.dev);
+ if (ret) {
+ phydev_err(phydev, "Failed to load firmware %s: %d\n", priv->fw_name, ret);
+ return ret;
+ }
+
+ ret = rtl8261x_verify_firmware(phydev, fw);
+ if (ret)
+ goto release_fw;
+
+ hdr = (const struct rtl8261x_fw_header *)fw->data;
+
+ entry = (const struct rtl8261x_fw_entry *)(fw->data + FW_HEADER_SIZE);
+ for (i = 0; i < le16_to_cpu(hdr->num_entries); i++, entry++) {
+ ret = rtl8261x_fw_execute_entry(phydev, entry);
+ if (ret) {
+ phydev_err(phydev, "Entry %d failed: %d\n", i, ret);
+ goto release_fw;
+ }
+ }
+
+ priv->fw_loaded = true;
+
+release_fw:
+ release_firmware(fw);
+ return ret;
+}
+
static int rtl8261x_config_intr(struct phy_device *phydev)
{
int ret;
@@ -490,6 +689,20 @@ static int rtl8261x_config_aneg(struct phy_device *phydev)
return 0;
}
+static int rtl8261x_config_init(struct phy_device *phydev)
+{
+ struct rtl8261x_priv *priv = phydev->priv;
+
+ /* The firmware parameters are preserved across IEEE soft resets and
+ * suspend/resume cycles. Reloading is only necessary after a power
+ * cycle or hard reset.
+ */
+ if (priv->fw_name && !priv->fw_loaded)
+ return rtl8261x_fw_load(phydev);
+
+ return 0;
+}
+
static int rtl821x_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -3185,6 +3398,7 @@ static struct phy_driver realtek_drvs[] = {
PHY_ID_MATCH_EXACT(RTL_8261C_CG),
.name = "Realtek RTL8261C 10Gbps PHY",
.probe = rtl8261x_probe,
+ .config_init = rtl8261x_config_init,
.get_features = rtl8261x_get_features,
.config_aneg = rtl8261x_config_aneg,
.read_status = rtl8261x_read_status,
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v9 2/4] net: phy: c45: add setup and read master/slave helpers
From: javen @ 2026-07-03 7:13 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb, maxime.chevallier
Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu
In-Reply-To: <20260703071330.1707-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds two static helpers in drivers/net/phy/phy-c45.c to
configure and read back master-slave roles for non BASE-T1 Clause 45
PHYs via the 10GBASE-T AN control/status registers.
These helpers are wired into genphy_c45_config_aneg() and
genphy_c45_read_status(). This changes the observable ethtool output
for drivers using the generic c45 read path.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes, new file
Changes in v3:
- re-order function according to the order in phy-c45.c
- add kernel-doc about return value
- add MASTER_SLAVE_CFG_MASTER_PREFERRED,
MASTER_SLAVE_CFG_SLAVE_PREFERRED, MASTER_SLAVE_CFG_UNKNOWN,
MASTER_SLAVE_CFG_UNSUPPORTED, MASTER_SLAVE_CFG_SLAVE_PREFERRED cfg
Changes in v4:
- no changes
Changes in v5:
- move genphy_c45_an_setup_master_slave() to genphy_c45_config_aneg(),
as that C22 does.
Changes in v6:
- add colon in the function description
- add genphy_c45_read_master_slave in read function
Changes in v7:
- when phydev->link is down, just return UNKNOWN
- modify commit message
Changes in v8:
- no changes
Changes in v9:
- no changes
---
drivers/net/phy/phy-c45.c | 103 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/mdio.h | 5 ++
2 files changed, 108 insertions(+)
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index c1f817a59739..870920311f9a 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -406,6 +406,97 @@ int genphy_c45_pma_soft_reset(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(genphy_c45_pma_soft_reset);
+/**
+ * genphy_c45_an_setup_master_slave - Configure Master/Slave setting for C45 PHYs
+ * @phydev: target phy_device struct
+ *
+ * Description: Configure the forced or preferred Master/Slave role
+ * 10GBASE-T control register (MMD 7, Register 0x0020) according to
+ * IEEE 802.3 standards.
+ *
+ * Return: negative errno code on failure, 0 if Master/Slave didn't change,
+ * or 1 if Master/Slave modes changed.
+ */
+static int genphy_c45_an_setup_master_slave(struct phy_device *phydev)
+{
+ u16 ctl = 0;
+
+ switch (phydev->master_slave_set) {
+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+ ctl = MDIO_AN_10GBT_CTRL_MS_PORT_TYPE;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+ break;
+ case MASTER_SLAVE_CFG_MASTER_FORCE:
+ ctl = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
+ ctl = MDIO_AN_10GBT_CTRL_MS_ENABLE;
+ break;
+ case MASTER_SLAVE_CFG_UNKNOWN:
+ case MASTER_SLAVE_CFG_UNSUPPORTED:
+ return 0;
+ default:
+ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ return phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+ MDIO_AN_10GBT_CTRL_MS_ENABLE |
+ MDIO_AN_10GBT_CTRL_MS_VALUE |
+ MDIO_AN_10GBT_CTRL_MS_PORT_TYPE, ctl);
+}
+
+/**
+ * genphy_c45_read_master_slave - read master/slave status
+ * @phydev: target phy_device struct
+ *
+ * Description: Read the Master/Slave configuration and status
+ * from 10GBASE-T control/status registers (MMD 7, Reg 0x0020 and 0x0021).
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int genphy_c45_read_master_slave(struct phy_device *phydev)
+{
+ int val;
+
+ phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_10GBT_CTRL_MS_ENABLE) {
+ if (val & MDIO_AN_10GBT_CTRL_MS_VALUE)
+ phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+ else
+ phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
+ } else {
+ if (val & MDIO_AN_10GBT_CTRL_MS_PORT_TYPE)
+ phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+ else
+ phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+ }
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_10GBT_STAT_MS_FAULT) {
+ phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
+ } else if (phydev->link) {
+ if (val & MDIO_AN_10GBT_STAT_MS_RES)
+ phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
+ else
+ phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
+ } else {
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+ }
+
+ return 0;
+}
+
/**
* genphy_c45_aneg_done - return auto-negotiation complete status
* @phydev: target phy_device struct
@@ -1214,6 +1305,10 @@ int genphy_c45_read_status(struct phy_device *phydev)
ret = genphy_c45_baset1_read_status(phydev);
if (ret < 0)
return ret;
+ } else {
+ ret = genphy_c45_read_master_slave(phydev);
+ if (ret < 0)
+ return ret;
}
phy_resolve_aneg_linkmode(phydev);
@@ -1247,6 +1342,14 @@ int genphy_c45_config_aneg(struct phy_device *phydev)
if (ret > 0)
changed = true;
+ if (!genphy_c45_baset1_able(phydev)) {
+ ret = genphy_c45_an_setup_master_slave(phydev);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+ }
+
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
EXPORT_SYMBOL_GPL(genphy_c45_config_aneg);
diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h
index b2541c948fc1..06f4bc3c20c7 100644
--- a/include/uapi/linux/mdio.h
+++ b/include/uapi/linux/mdio.h
@@ -332,8 +332,13 @@
#define MDIO_AN_10GBT_CTRL_ADV2_5G 0x0080 /* Advertise 2.5GBASE-T */
#define MDIO_AN_10GBT_CTRL_ADV5G 0x0100 /* Advertise 5GBASE-T */
#define MDIO_AN_10GBT_CTRL_ADV10G 0x1000 /* Advertise 10GBASE-T */
+#define MDIO_AN_10GBT_CTRL_MS_ENABLE 0x8000 /* Master/slave manual config enable */
+#define MDIO_AN_10GBT_CTRL_MS_VALUE 0x4000 /* Master/slave config value (1=Master) */
+#define MDIO_AN_10GBT_CTRL_MS_PORT_TYPE 0x2000 /* Master Preferred Type */
/* AN 10GBASE-T status register. */
+#define MDIO_AN_10GBT_STAT_MS_FAULT 0x8000 /* Master/slave fault */
+#define MDIO_AN_10GBT_STAT_MS_RES 0x4000 /* Master/slave resolution (1=Master) */
#define MDIO_AN_10GBT_STAT_LP2_5G 0x0020 /* LP is 2.5GBT capable */
#define MDIO_AN_10GBT_STAT_LP5G 0x0040 /* LP is 5GBT capable */
#define MDIO_AN_10GBT_STAT_LPTRR 0x0200 /* LP training reset req. */
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v9 0/4] Add support for RTL8261C_CG
From: javen @ 2026-07-03 7:13 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb, maxime.chevallier
Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
Add support for RTL8261C_CG and add support for loading firmware.
Javen Xu (4):
net: phy: c45: add genphy_c45_pma_soft_reset()
net: phy: c45: add setup and read master/slave helpers
net: phy: realtek: add support for RTL8261C_CG
net: phy: realtek: load firmware for RTL8261C_CG
drivers/net/phy/phy-c45.c | 125 ++++++++
drivers/net/phy/realtek/realtek_main.c | 406 +++++++++++++++++++++++++
include/linux/phy.h | 1 +
include/uapi/linux/mdio.h | 5 +
4 files changed, 537 insertions(+)
--
2.43.0
^ permalink raw reply
* [PATCH net 1/1] net: sctp: fix AUTH HMAC list overflow into auth_chunks
From: Ren Wei @ 2026-07-03 7:19 UTC (permalink / raw)
To: linux-sctp, netdev
Cc: marcelo.leitner, lucien.xin, davem, edumazet, pabeni, horms,
vladislav.yasevich, yuantan098, dstsmallbird, xizh2024,
enjou1224z
In-Reply-To: <cover.1782798905.git.xizh2024@lzu.edu.cn>
From: Zihan Xi <xizh2024@lzu.edu.cn>
sctp_auth_ep_set_hmacs() may advertise a 12-byte HMAC-ALGO parameter when
four identifiers are configured, but the association only stores ten bytes
in c.auth_hmacs. sctp_association_init() copies the advertised length and
overwrites the adjacent auth_chunks field, so sctp_auth_asoc_verify_hmac_id()
accepts forged HMAC identifiers and sctp_auth_get_hmac() indexes past
sctp_hmac_list.
Clamp the stored parameter length to the association buffer, copy only that
many bytes when initializing an association, and reject out-of-range HMAC
identifiers in sctp_auth_get_hmac().
Fixes: 65b07e5d0d09 ("[SCTP]: API updates to suport SCTP-AUTH extensions.")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Xin Liu <dstsmallbird@foxmail.com>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zihan Xi <xizh2024@lzu.edu.cn>
Reviewed-by: Ren Wei <enjou1224z@gmail.com>
---
net/sctp/associola.c | 10 +++++++---
net/sctp/auth.c | 10 ++++++++--
net/sctp/sm_statefuns.c | 2 ++
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 62d3cc1558..760457def6 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -260,9 +260,13 @@ static struct sctp_association *sctp_association_init(
asoc->strreset_enable = ep->strreset_enable;
/* Save the hmacs and chunks list into this association */
- if (ep->auth_hmacs_list)
- memcpy(asoc->c.auth_hmacs, ep->auth_hmacs_list,
- ntohs(ep->auth_hmacs_list->param_hdr.length));
+ if (ep->auth_hmacs_list) {
+ size_t hmac_len = min_t(size_t,
+ ntohs(ep->auth_hmacs_list->param_hdr.length),
+ sizeof(asoc->c.auth_hmacs));
+
+ memcpy(asoc->c.auth_hmacs, ep->auth_hmacs_list, hmac_len);
+ }
if (ep->auth_chunk_list)
memcpy(asoc->c.auth_chunks, ep->auth_chunk_list,
ntohs(ep->auth_chunk_list->param_hdr.length));
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
index be9782760f..4d14bd6185 100644
--- a/net/sctp/auth.c
+++ b/net/sctp/auth.c
@@ -447,6 +447,8 @@ struct sctp_shared_key *sctp_auth_get_shkey(
const struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id)
{
+ if (hmac_id >= SCTP_AUTH_NUM_HMACS)
+ return NULL;
return &sctp_hmac_list[hmac_id];
}
@@ -510,6 +512,9 @@ int sctp_auth_asoc_verify_hmac_id(const struct sctp_association *asoc,
hmacs = (struct sctp_hmac_algo_param *)asoc->c.auth_hmacs;
n_elt = (ntohs(hmacs->param_hdr.length) -
sizeof(struct sctp_paramhdr)) >> 1;
+ n_elt = min_t(__u16, n_elt,
+ (sizeof(asoc->c.auth_hmacs) -
+ sizeof(struct sctp_paramhdr)) / sizeof(__u16));
return __sctp_auth_find_hmacid(hmacs->hmac_ids, n_elt, hmac_id);
}
@@ -708,8 +713,9 @@ int sctp_auth_ep_set_hmacs(struct sctp_endpoint *ep,
ep->auth_hmacs_list->hmac_ids[i] =
htons(hmacs->shmac_idents[i]);
ep->auth_hmacs_list->param_hdr.length =
- htons(sizeof(struct sctp_paramhdr) +
- hmacs->shmac_num_idents * sizeof(__u16));
+ htons(min_t(__u16, sizeof(struct sctp_paramhdr) +
+ hmacs->shmac_num_idents * sizeof(__u16),
+ SCTP_AUTH_NUM_HMACS * sizeof(__u16) + 2));
return 0;
}
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index d23d935e12..21cda509a0 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -4431,6 +4431,8 @@ static enum sctp_ierror sctp_sf_authenticate(
sig_len = ntohs(chunk->chunk_hdr->length) -
sizeof(struct sctp_auth_chunk);
hmac = sctp_auth_get_hmac(ntohs(auth_hdr->hmac_id));
+ if (!hmac)
+ return SCTP_IERROR_AUTH_BAD_HMAC;
if (sig_len != hmac->hmac_len)
return SCTP_IERROR_PROTO_VIOLATION;
--
2.43.0
^ permalink raw reply related
* [PATCH RFC net-next] net: dsa: mv88e6xxx: honor resolved flow control in mac_link_up
From: Luke Howard @ 2026-07-03 7:26 UTC (permalink / raw)
To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn
Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
Christoph Mellauner, Simon Gapp, netdev, linux-kernel,
Luke Howard
Program the pause status resolved by phylink into the Port MAC
Control register when forcing the link up, and return flow control
to hardware resolution when forcing the link down, mirroring the
existing treatment of speed and duplex.
Flow control is programmed before port_sync_link() so that the
port's MAC control is not modified while the link is forced up.
The MAC has a single flow control enable covering both directions;
as the driver only advertises MAC_SYM_PAUSE, rx_pause and tx_pause
are always equal here.
A fixed-link port whose device tree node lacks the "pause" property
now has flow control explicitly disabled while the link is forced
up, rather than being left at the chip's default.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 9 +++++++++
drivers/net/dsa/mv88e6xxx/port.c | 20 ++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 2 ++
3 files changed, 31 insertions(+)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 80b877c74513d..73c28bf2bf3cb 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -974,6 +974,10 @@ static void mv88e6xxx_mac_link_down(struct phylink_config *config,
mode == MLO_AN_FIXED) && ops->port_sync_link)
err = ops->port_sync_link(chip, port, mode, false);
+ if (!err)
+ err = mv88e6xxx_port_set_force_flow_ctl(chip, port, false,
+ false);
+
if (!err && ops->port_set_speed_duplex)
err = ops->port_set_speed_duplex(chip, port, SPEED_UNFORCED,
DUPLEX_UNFORCED);
@@ -1012,6 +1016,11 @@ static void mv88e6xxx_mac_link_up(struct phylink_config *config,
goto error;
}
+ err = mv88e6xxx_port_set_force_flow_ctl(chip, port, true,
+ rx_pause);
+ if (err)
+ goto error;
+
if (ops->port_sync_link)
err = ops->port_sync_link(chip, port, mode, true);
}
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index ea1fab71968a0..7ee802cf3412c 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -182,6 +182,26 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
return 0;
}
+int mv88e6xxx_port_set_force_flow_ctl(struct mv88e6xxx_chip *chip, int port,
+ bool force, bool value)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®);
+ if (err)
+ return err;
+
+ reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_FC | MV88E6XXX_PORT_MAC_CTL_FC);
+ if (force) {
+ reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_FC;
+ if (value)
+ reg |= MV88E6XXX_PORT_MAC_CTL_FC;
+ }
+
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+}
+
int mv88e6xxx_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup)
{
const struct mv88e6xxx_ops *ops = chip->info->ops;
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 5b6cde9f7406f..f98df6351f5b7 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -485,6 +485,8 @@ int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link);
+int mv88e6xxx_port_set_force_flow_ctl(struct mv88e6xxx_chip *chip, int port,
+ bool force, bool value);
int mv88e6xxx_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup);
int mv88e6185_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup);
---
base-commit: 2bb62a85aff6d4c14a62a476dfabaada3c1cc014
change-id: 20260701-mv88e6xxx-flow-control-cafe32352712
Best regards,
--
Luke Howard <lukeh@padl.com>
^ permalink raw reply related
* Re: [PATCH net v2] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
From: Qingfang Deng @ 2026-07-03 7:27 UTC (permalink / raw)
To: Norbert Szetei
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Sebastian Andrzej Siewior, Breno Leitao, Taegu Ha,
Kees Cook, linux-ppp, linux-kernel, Guillaume Nault, netdev
In-Reply-To: <D9C0245B-608B-4884-8A09-F55BA4A9F948@doyensec.com>
Hi,
On 2026/7/2 2:12, Norbert Szetei wrote:
> +/* Purge after the grace period: a late ppp_input() may still queue an
> + * skb on pch->file.rq before the last RCU reader drains.
> + */
> +static void ppp_release_channel_free(struct rcu_head *rcu)
> +{
> + struct channel *pch = container_of(rcu, struct channel, rcu);
> +
> + skb_queue_purge(&pch->file.xq);
> + skb_queue_purge(&pch->file.rq);
> + kfree(pch);
> +}
> +
> /*
> * Drop a reference to a ppp channel and free its memory if the refcount reaches
> * zero.
> @@ -3581,9 +3594,7 @@ static void ppp_release_channel(struct channel *pch)
> pr_err("ppp: destroying undead channel %p !\n", pch);
> return;
> }
> - skb_queue_purge(&pch->file.xq);
> - skb_queue_purge(&pch->file.rq);
> - kfree(pch);
> + call_rcu(&pch->rcu, ppp_release_channel_free);
> }
>
> static void __exit ppp_cleanup(void)
AI-review found an issue:
https://sashiko.dev/#/patchset/D9C0245B-608B-4884-8A09-F55BA4A9F948%40doyensec.com
An rcu_barrier() call is needed at the end of ppp_cleanup().
Regards,
Qingfang
^ permalink raw reply
* Re: [PATCH net-next v4 1/2] net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
From: Maxime Chevallier @ 2026-07-03 7:29 UTC (permalink / raw)
To: Johan Alvarado, linusw, alsi, andrew, olteanv, kuba, davem,
edumazet, pabeni, linux
Cc: luizluca, namiltd, netdev, linux-kernel
In-Reply-To: <0100019f2496091c-0bf7417f-aa27-4465-972b-f9a9b156506a-000000@email.amazonses.com>
Hi Johan,
On 7/2/26 22:47, Johan Alvarado wrote:
> The RTL8367S can mux its embedded SerDes to external interface 1,
> which is typically used to connect the switch to a CPU port. The chip
> info table already declares SGMII as a supported interface mode for
> this chip, but the driver only implements RGMII so far.
>
> Implement SGMII support as a phylink PCS, with the configuration
> sequence derived from the GPL-licensed Realtek rtl8367c vendor driver
> as distributed in the Mercusys MR80X GPL code drop:
>
> - Add accessors for the SerDes indirect access registers (SDS_INDACS),
> through which the SerDes internal registers are reached.
>
> - Register a phylink_pcs for the SerDes, selected from mac_select_pcs
> for the SGMII interface, so the SerDes handling lives in the PCS
> operations rather than in the MAC operations.
>
> - Probe the SerDes tuning variant from the chip option register once
> at setup. The vendor driver keeps two sets of SerDes tuning
> parameters and selects between them based on this option; only the
> variant for a non-zero option (which all RTL8367S parts seen so far
> report) has been validated on hardware, so the SerDes interface
> modes are only advertised in that case. An unsupported variant thus
> fails at phylink validation time instead of at link configuration
> time.
>
> - Keep the embedded DW8051 microcontroller in reset and disabled. The
> vendor driver loads firmware into it to manage the SerDes link, but
> analysis of that firmware shows it only duplicates the link
> management phylink already performs: it polls the port status and
> writes the external interface force registers behind the driver's
> back.
>
> - Clear the line rate bypass bit for the external interface, tune the
> SerDes with the vendor-prescribed parameters, mux the SerDes to MAC8
> in SGMII mode and only then take the SerDes out of reset, as the
> vendor driver does.
>
> - After deasserting the SerDes reset, reset the SerDes data path via
> the SerDes BMCR register to flush the FIFOs and resync the PLL.
> This mirrors what the vendor firmware does right after deasserting
> the SerDes reset, and ensures a clean link state from cold boot.
>
> - Force the SGMII link parameters (link, speed, duplex) in the SDS_MISC
> register from pcs_link_up(). SGMII in-band autonegotiation is not
> implemented, so only fixed-link and conventional PHY setups are
> supported, just like RGMII. This is reported to phylink through
> pcs_inband_caps() returning LINK_INBAND_DISABLE, so phylink never
> selects an in-band-enabled negotiation mode for this PCS.
>
> - Program the SerDes pause controls in SDS_MISC from the resolved
> pause modes when forcing the MAC external interface in mac_link_up,
> as the vendor driver does, rather than leaving whatever state the
> boot firmware left there. This is done in the MAC layer because
> pcs_link_up() carries no pause information.
>
> - Implement pcs_get_state() by reading the link status from the
> SerDes, with the forced speed and duplex read back from SDS_MISC.
> Although the supported fixed-link and conventional PHY setups do not
> use it, the PCS owns the SerDes link state, and phylink consults
> pcs_get_state() to track the physical link when operating in in-band
> mode with autonegotiation disabled. The SerDes has no link interrupt
> wired up, so the PCS sets its poll flag.
>
> Tested on a Mercusys MR80X v2.20, where the RTL8367S is connected to
> the SoC over SGMII.
Ah nice conversion to phylink PCS :) I have a few comments below
>
> Suggested-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
> Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
> Suggested-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
> Signed-off-by: Johan Alvarado <contact@c127.dev>
> ---
[...]
> +static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
> + phy_interface_t interface,
> + const unsigned long *advertising,
> + bool permit_pause_to_mac)
> +{
> + const int id = RTL8365MB_SDS_EXT_INTERFACE_ID;
> + struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
> + struct realtek_priv *priv;
> + u16 val;
> + int ret;
> + int i;
> +
> + priv = mb->priv;
> +
> + /* This driver does not implement SGMII in-band autonegotiation yet, so
> + * the link parameters are forced from rtl8365mb_pcs_link_up() instead.
> + * rtl8365mb_pcs_inband_caps() reports this to phylink, which should
> + * therefore never select an in-band-enabled negotiation mode.
> + */
> + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
> + return -EOPNOTSUPP;
As you implement the .pcs_inband_caps() method, phylink will pass you a valid
mode, no need for that check :)
[...]
> @@ -1218,14 +1680,46 @@ static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config,
> p = &mb->ports[port];
> schedule_delayed_work(&p->mib_work, 0);
>
> - if (phy_interface_mode_is_rgmii(interface)) {
> + /* The SerDes forced link state is programmed by the PCS in
> + * rtl8365mb_pcs_link_up(); here only the MAC external interface force
> + * is configured, for both RGMII and SerDes.
> + */
> + if (phy_interface_mode_is_rgmii(interface) ||
> + rtl8365mb_interface_is_serdes(interface)) {
> ret = rtl8365mb_ext_config_forcemode(priv, port, true, speed,
> duplex, tx_pause,
> rx_pause);
> - if (ret)
> + if (ret) {
> dev_err(priv->dev,
> "failed to force mode on port %d: %pe\n", port,
> ERR_PTR(ret));
> + return;
> + }
> +
> + /* The SerDes has its own pause controls; program them from
> + * the resolved pause modes, as the vendor driver does when
> + * forcing the link on a SerDes external interface. This is
> + * done here rather than in rtl8365mb_pcs_link_up() because
> + * pcs_link_up() carries no pause information.
> + */
> + if (rtl8365mb_interface_is_serdes(interface)) {
> + u32 val = 0;
> +
> + if (tx_pause)
> + val |= RTL8365MB_SDS_MISC_SGMII_TXFC_MASK;
> + if (rx_pause)
> + val |= RTL8365MB_SDS_MISC_SGMII_RXFC_MASK;
Do you know what this does in HW ? Is this so that the PCS lets the Pause frames through
in either directions ?
I suspect this is something that would be only used for inband advertising
of pause settings (in such case, you don't even need that), but ofc I'm not sure :)
You already configure the MAC pause settings, can you test that these bits actually do
anything by exercising a bit flow control and checking if these registers are used ?
Otherwise, this is looking pretty good :)
Maxime
^ permalink raw reply
* Re: [PATCH v4 3/5] drm/xe/ras: Add support for error threshold
From: Tauro, Riana @ 2026-07-03 7:30 UTC (permalink / raw)
To: Raag Jadav, intel-xe, dri-devel, netdev
Cc: simona.vetter, airlied, kuba, lijo.lazar, Hawking.Zhang, davem,
pabeni, edumazet, dev, zachary.mckevitt, rodrigo.vivi,
michal.wajdeczko, matthew.d.roper, mallesh.koujalagi
In-Reply-To: <20260623101043.255897-4-raag.jadav@intel.com>
On 23-06-2026 15:39, Raag Jadav wrote:
> System controller allows getting/setting per counter threshold for
> correctable errors, which it uses to raise error events to the driver.
> Get/set it using the respective mailbox command.
>
> Signed-off-by: Raag Jadav <raag.jadav@intel.com>
Reviewed-by: Riana Tauro <riana.tauro@intel.com>
> ---
> v2: Add RAS operation status codes (Riana)
> v3: Reuse status codes and uapi mapping from counter series (Riana)
> Access request/response counter using local pointer (Riana)
> Mark unused field as reserved (Riana)
> v4: Make debug logs consistent (Riana)
> Update kdoc (Riana)
> ---
> drivers/gpu/drm/xe/xe_ras.c | 105 ++++++++++++++++++
> drivers/gpu/drm/xe/xe_ras.h | 2 +
> drivers/gpu/drm/xe/xe_ras_types.h | 51 +++++++++
> drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h | 4 +
> 4 files changed, 162 insertions(+)
>
> diff --git a/drivers/gpu/drm/xe/xe_ras.c b/drivers/gpu/drm/xe/xe_ras.c
> index 44f4e1a3455b..afee8202d24e 100644
> --- a/drivers/gpu/drm/xe/xe_ras.c
> +++ b/drivers/gpu/drm/xe/xe_ras.c
> @@ -270,6 +270,111 @@ int xe_ras_clear_counter(struct xe_device *xe, u8 severity, u8 component)
> return 0;
> }
>
> +/**
> + * xe_ras_get_threshold() - Get error counter threshold
> + * @xe: Xe device instance
> + * @severity: Error severity to be queried (&enum drm_xe_ras_error_severity)
> + * @component: Error component to be queried (&enum drm_xe_ras_error_component)
> + * @threshold: Counter threshold
> + *
> + * This function retrieves the error threshold of a specific counter based on
> + * severity and component.
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int xe_ras_get_threshold(struct xe_device *xe, u8 severity, u8 component, u32 *threshold)
> +{
> + struct xe_ras_get_threshold_response response = {};
> + struct xe_ras_get_threshold_request request = {};
> + struct xe_sysctrl_mailbox_command command = {};
> + struct xe_ras_error_class *counter;
> + size_t len;
> + int ret;
> +
> + counter = &request.counter;
> + counter->common.severity = drm_to_xe_ras_severity(severity);
> + counter->common.component = drm_to_xe_ras_component(component);
> +
> + xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_GET_THRESHOLD,
> + &request, sizeof(request), &response, sizeof(response));
> +
> + guard(xe_pm_runtime)(xe);
> + ret = xe_sysctrl_send_command(&xe->sc, &command, &len);
> + if (ret) {
> + xe_err(xe, "sysctrl: failed to get threshold %d\n", ret);
> + return ret;
> + }
> +
> + if (len != sizeof(response)) {
> + xe_err(xe, "sysctrl: unexpected get threshold response length %zu (expected %zu)\n",
> + len, sizeof(response));
> + return -EIO;
> + }
> +
> + counter = &response.counter;
> + *threshold = response.threshold;
> +
> + xe_dbg(xe, "[RAS]: get threshold %u for %s %s\n", *threshold,
> + comp_to_str(counter->common.component), sev_to_str(counter->common.severity));
> + return 0;
> +}
> +
> +/**
> + * xe_ras_set_threshold() - Set error counter threshold
> + * @xe: Xe device instance
> + * @severity: Error severity to be set (&enum drm_xe_ras_error_severity)
> + * @component: Error component to be set (&enum drm_xe_ras_error_component)
> + * @threshold: Counter threshold
> + *
> + * This function sets the error threshold of a specific counter based on
> + * severity and component.
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int xe_ras_set_threshold(struct xe_device *xe, u8 severity, u8 component, u32 threshold)
> +{
> + struct xe_ras_set_threshold_response response = {};
> + struct xe_ras_set_threshold_request request = {};
> + struct xe_sysctrl_mailbox_command command = {};
> + struct xe_ras_error_class *counter;
> + size_t len;
> + int ret;
> +
> + counter = &request.counter;
> + counter->common.severity = drm_to_xe_ras_severity(severity);
> + counter->common.component = drm_to_xe_ras_component(component);
> + request.threshold = threshold;
> +
> + xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_SET_THRESHOLD,
> + &request, sizeof(request), &response, sizeof(response));
> +
> + guard(xe_pm_runtime)(xe);
> + ret = xe_sysctrl_send_command(&xe->sc, &command, &len);
> + if (ret) {
> + xe_err(xe, "sysctrl: failed to set threshold %d\n", ret);
> + return ret;
> + }
> +
> + if (len != sizeof(response)) {
> + xe_err(xe, "sysctrl: unexpected set threshold response length %zu (expected %zu)\n",
> + len, sizeof(response));
> + return -EIO;
> + }
> +
> + ret = ras_status_to_errno(response.status);
> + if (ret) {
> + xe_err(xe, "sysctrl: set threshold command failed with status %#x\n",
> + response.status);
> + return ret;
> + }
> +
> + counter = &response.counter;
> +
> + xe_dbg(xe, "[RAS]: set threshold %u for %s %s\n", response.threshold,
> + comp_to_str(counter->common.component), sev_to_str(counter->common.severity));
> + return 0;
> +}
> +
> /**
> * xe_ras_init - Initialize Xe RAS
> * @xe: xe device instance
> diff --git a/drivers/gpu/drm/xe/xe_ras.h b/drivers/gpu/drm/xe/xe_ras.h
> index ba0b0224df23..1aa43c54b710 100644
> --- a/drivers/gpu/drm/xe/xe_ras.h
> +++ b/drivers/gpu/drm/xe/xe_ras.h
> @@ -15,6 +15,8 @@ void xe_ras_counter_threshold_crossed(struct xe_device *xe,
> struct xe_sysctrl_event_response *response);
> int xe_ras_get_counter(struct xe_device *xe, u8 severity, u8 component, u32 *value);
> int xe_ras_clear_counter(struct xe_device *xe, u8 severity, u8 component);
> +int xe_ras_get_threshold(struct xe_device *xe, u8 severity, u8 component, u32 *threshold);
> +int xe_ras_set_threshold(struct xe_device *xe, u8 severity, u8 component, u32 threshold);
> void xe_ras_init(struct xe_device *xe);
>
> #endif
> diff --git a/drivers/gpu/drm/xe/xe_ras_types.h b/drivers/gpu/drm/xe/xe_ras_types.h
> index 6688e11f57a8..747b651880cd 100644
> --- a/drivers/gpu/drm/xe/xe_ras_types.h
> +++ b/drivers/gpu/drm/xe/xe_ras_types.h
> @@ -121,4 +121,55 @@ struct xe_ras_clear_counter_response {
> /** @reserved1: Reserved for future use */
> u32 reserved1[3];
> } __packed;
> +
> +/**
> + * struct xe_ras_get_threshold_request - Request structure for get threshold
> + */
> +struct xe_ras_get_threshold_request {
> + /** @counter: Counter to get threshold for */
> + struct xe_ras_error_class counter;
> + /** @reserved: Reserved for future use */
> + u32 reserved;
> +} __packed;
> +
> +/**
> + * struct xe_ras_get_threshold_response - Response structure for get threshold
> + */
> +struct xe_ras_get_threshold_response {
> + /** @counter: Counter ID */
> + struct xe_ras_error_class counter;
> + /** @threshold: Current threshold of the counter */
> + u32 threshold;
> + /** @reserved: Reserved for future use */
> + u32 reserved[4];
> +} __packed;
> +
> +/**
> + * struct xe_ras_set_threshold_request - Request structure for set threshold
> + */
> +struct xe_ras_set_threshold_request {
> + /** @counter: Counter to set threshold for */
> + struct xe_ras_error_class counter;
> + /** @threshold: Threshold to be set */
> + u32 threshold;
> + /** @reserved: Reserved for future use */
> + u32 reserved;
> +} __packed;
> +
> +/**
> + * struct xe_ras_set_threshold_response - Response structure for set threshold
> + */
> +struct xe_ras_set_threshold_response {
> + /** @counter: Counter ID */
> + struct xe_ras_error_class counter;
> + /** @reserved: Reserved */
> + u32 reserved;
> + /** @threshold: Updated threshold */
> + u32 threshold;
> + /** @status: Operation status */
> + u32 status;
> + /** @reserved1: Reserved for future use */
> + u32 reserved1[2];
> +} __packed;
> +
> #endif
> diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h b/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
> index 6e3753554510..10f06aa5c4b5 100644
> --- a/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
> +++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
> @@ -24,11 +24,15 @@ enum xe_sysctrl_group {
> *
> * @XE_SYSCTRL_CMD_GET_COUNTER: Get error counter value
> * @XE_SYSCTRL_CMD_CLEAR_COUNTER: Clear error counter value
> + * @XE_SYSCTRL_CMD_GET_THRESHOLD: Retrieve error threshold
> + * @XE_SYSCTRL_CMD_SET_THRESHOLD: Set error threshold
> * @XE_SYSCTRL_CMD_GET_PENDING_EVENT: Retrieve pending event
> */
> enum xe_sysctrl_gfsp_cmd {
> XE_SYSCTRL_CMD_GET_COUNTER = 0x03,
> XE_SYSCTRL_CMD_CLEAR_COUNTER = 0x04,
> + XE_SYSCTRL_CMD_GET_THRESHOLD = 0x05,
> + XE_SYSCTRL_CMD_SET_THRESHOLD = 0x06,
> XE_SYSCTRL_CMD_GET_PENDING_EVENT = 0x07,
> };
>
^ permalink raw reply
* [PATCH net-next 00/12] net: dsa: support for inband management of switches
From: Luke Howard @ 2026-07-03 7:30 UTC (permalink / raw)
To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
Simon Horman, Florian Fainelli, Tobias Waldekranz
Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
Christoph Mellauner, Simon Gapp, Ryan Wilkins, Mattias Forsblad,
netdev, linux-kernel, Luke Howard, Christian Marangi
This patch set shephards a series of commits made by Andrew Lunn
and others to support inband management of DSA switches. [1]
Specifically, it allows the switch itself to be managed using
DSA tagged frames instead of MDIO. MDIO is typically required
to enable inband management. An implementation is included for
the qca8k driver.
This is patch set one of four: the remaining sets add support
for the Remote Management Unit (RMU) in the mv88e6xxx, and then
general and mv88e6xxx support for "inband-only" mode where MDIO
is unavailable on the hardware.
Note: the patch to validate the source trunk against lags_len
was previously submitted and rejected, but I am including it as
the issue has been triggered when running in inband mode.
[1] https://github.com/lunn/linux/tree/v6.15-rc2-next-next-inband
Signed-off-by: Luke Howard <lukeh@padl.com>
---
Andrew Lunn (10):
net: dsa: qca8k: Move register access completion into DSA core
net: dsa: qca8K: Move queuing for request frame into the core
net: dsa: qca8k: dsa_inband_request: More normal return values
net: dsa: qca8k: Drop replies with wrong sequence numbers
net: dsa: qca8k: Move request sequence number handling into core
net: dsa: qca8k: Refactor sequence number mismatch to use error code
net: dsa: qca8k: Pass error code from reply decoder to requester
net: dsa: qca8k: Update error handling
net: dsa: qca8k: Move inband mutex into DSA core
net: dsa: Add helper to find ds_switch by index
Christian Marangi (1):
net: dsa: qca8k: drop redundant mgmt_eth_data
Luke Howard (1):
net: dsa: validate source trunk against lags_len
drivers/net/dsa/qca/qca8k-8xxx.c | 238 +++++++++----------------------------
drivers/net/dsa/qca/qca8k-common.c | 2 +-
drivers/net/dsa/qca/qca8k.h | 12 +-
include/net/dsa.h | 36 ++++++
net/dsa/dsa.c | 109 +++++++++++++++++
net/dsa/tag.h | 14 +++
6 files changed, 220 insertions(+), 191 deletions(-)
---
base-commit: 2bb62a85aff6d4c14a62a476dfabaada3c1cc014
change-id: 20260612-net-next-dsa-rmu-5cbb0f10ab8b
Best regards,
--
Luke Howard <lukeh@padl.com>
^ permalink raw reply
* [PATCH net-next 01/12] net: dsa: qca8k: Move register access completion into DSA core
From: Luke Howard @ 2026-07-03 7:30 UTC (permalink / raw)
To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
Simon Horman, Florian Fainelli, Tobias Waldekranz
Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
Christoph Mellauner, Simon Gapp, Ryan Wilkins, Mattias Forsblad,
netdev, linux-kernel, Luke Howard
In-Reply-To: <20260703-net-next-dsa-rmu-v1-0-a03febf20bb4@padl.com>
From: Andrew Lunn <andrew@lunn.ch>
When performing operations on a remote switch using Ethernet frames, a
completion is used between the sender of the request and the code
which receives the reply.
Move this completion into the DSA core, simplifying the driver. The
initialisation and reinitialisation of the completion is now performed
in the core. Also, the conversion of milliseconds to jiffies is also
in the core.
The MIB access via left untouched because it is not a simple
request/reply protocol.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 43 +++++++++++++++-------------------------
drivers/net/dsa/qca/qca8k.h | 4 ++--
include/net/dsa.h | 12 +++++++++++
net/dsa/dsa.c | 22 ++++++++++++++++++++
4 files changed, 52 insertions(+), 29 deletions(-)
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index 4c928983b8623..fe6fb69b6f92f 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -212,7 +212,7 @@ static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
}
}
- complete(&mgmt_eth_data->rw_done);
+ dsa_inband_complete(&mgmt_eth_data->inband);
}
static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val,
@@ -332,8 +332,6 @@ static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
skb->dev = priv->mgmt_conduit;
- reinit_completion(&mgmt_eth_data->rw_done);
-
/* Increment seq_num and set it in the mdio pkt */
mgmt_eth_data->seq++;
qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
@@ -341,8 +339,8 @@ static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
dev_queue_xmit(skb);
- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
+ QCA8K_ETHERNET_TIMEOUT);
*val = mgmt_eth_data->data[0];
if (len > QCA_HDR_MGMT_DATA1_LEN)
@@ -384,8 +382,6 @@ static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
skb->dev = priv->mgmt_conduit;
- reinit_completion(&mgmt_eth_data->rw_done);
-
/* Increment seq_num and set it in the mdio pkt */
mgmt_eth_data->seq++;
qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
@@ -393,8 +389,8 @@ static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
dev_queue_xmit(skb);
- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
+ QCA8K_ETHERNET_TIMEOUT);
ack = mgmt_eth_data->ack;
@@ -594,8 +590,6 @@ qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
if (!skb)
return -ENOMEM;
- reinit_completion(&mgmt_eth_data->rw_done);
-
/* Increment seq_num and set it in the copy pkt */
mgmt_eth_data->seq++;
qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
@@ -603,8 +597,8 @@ qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
dev_queue_xmit(skb);
- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
+ QCA8K_ETHERNET_TIMEOUT);
ack = mgmt_eth_data->ack;
@@ -696,8 +690,6 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
clear_skb->dev = mgmt_conduit;
write_skb->dev = mgmt_conduit;
- reinit_completion(&mgmt_eth_data->rw_done);
-
/* Increment seq_num and set it in the write pkt */
mgmt_eth_data->seq++;
qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq);
@@ -705,8 +697,8 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
dev_queue_xmit(write_skb);
- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
+ QCA8K_ETHERNET_TIMEOUT);
ack = mgmt_eth_data->ack;
@@ -733,8 +725,6 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
}
if (read) {
- reinit_completion(&mgmt_eth_data->rw_done);
-
/* Increment seq_num and set it in the read pkt */
mgmt_eth_data->seq++;
qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq);
@@ -742,8 +732,8 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
dev_queue_xmit(read_skb);
- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
+ QCA8K_ETHERNET_TIMEOUT);
ack = mgmt_eth_data->ack;
@@ -762,8 +752,6 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
kfree_skb(read_skb);
}
exit:
- reinit_completion(&mgmt_eth_data->rw_done);
-
/* Increment seq_num and set it in the clear pkt */
mgmt_eth_data->seq++;
qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq);
@@ -771,8 +759,8 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
dev_queue_xmit(clear_skb);
- wait_for_completion_timeout(&mgmt_eth_data->rw_done,
- QCA8K_ETHERNET_TIMEOUT);
+ dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
+ QCA8K_ETHERNET_TIMEOUT);
mutex_unlock(&mgmt_eth_data->mutex);
mutex_unlock(&priv->bus->mdio_lock);
@@ -1728,7 +1716,8 @@ qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data)
if (ret)
goto exit;
- ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT);
+ ret = wait_for_completion_timeout(&mib_eth_data->rw_done,
+ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
exit:
mutex_unlock(&mib_eth_data->mutex);
@@ -2105,7 +2094,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
return -ENOMEM;
mutex_init(&priv->mgmt_eth_data.mutex);
- init_completion(&priv->mgmt_eth_data.rw_done);
+ dsa_inband_init(&priv->mgmt_eth_data.inband);
mutex_init(&priv->mib_eth_data.mutex);
init_completion(&priv->mib_eth_data.rw_done);
diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h
index 9563388930325..938f2c9ff0cac 100644
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -16,7 +16,7 @@
#define QCA8K_ETHERNET_MDIO_PRIORITY 7
#define QCA8K_ETHERNET_PHY_PRIORITY 6
-#define QCA8K_ETHERNET_TIMEOUT msecs_to_jiffies(5)
+#define QCA8K_ETHERNET_TIMEOUT 5
#define QCA8K_NUM_PORTS 7
#define QCA8K_NUM_CPU_PORTS 2
@@ -392,7 +392,7 @@ enum {
};
struct qca8k_mgmt_eth_data {
- struct completion rw_done;
+ struct dsa_inband inband;
struct mutex mutex; /* Enforce one mdio read/write at time */
bool ack;
u32 seq;
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8c16ef23cc102..9b0c109b3058a 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -7,6 +7,7 @@
#ifndef __LINUX_NET_DSA_H
#define __LINUX_NET_DSA_H
+#include <linux/completion.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/list.h>
@@ -1347,6 +1348,17 @@ int dsa_port_simple_hsr_join(struct dsa_switch *ds, int port,
int dsa_port_simple_hsr_leave(struct dsa_switch *ds, int port,
struct net_device *hsr);
+/* Perform operations on a switch by sending it request in Ethernet
+ * frames and expecting a response in a frame.
+ */
+struct dsa_inband {
+ struct completion completion;
+};
+
+void dsa_inband_init(struct dsa_inband *inband);
+void dsa_inband_complete(struct dsa_inband *inband);
+int dsa_inband_wait_for_completion(struct dsa_inband *inband, int timeout_ms);
+
/* Keep inline for faster access in hot path */
static inline bool netdev_uses_dsa(const struct net_device *dev)
{
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 9cb732f6b1e3e..dc4e5cdb2f5b2 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -1834,6 +1834,28 @@ int dsa_port_simple_hsr_leave(struct dsa_switch *ds, int port,
}
EXPORT_SYMBOL_GPL(dsa_port_simple_hsr_leave);
+void dsa_inband_init(struct dsa_inband *inband)
+{
+ init_completion(&inband->completion);
+}
+EXPORT_SYMBOL_GPL(dsa_inband_init);
+
+void dsa_inband_complete(struct dsa_inband *inband)
+{
+ complete(&inband->completion);
+}
+EXPORT_SYMBOL_GPL(dsa_inband_complete);
+
+int dsa_inband_wait_for_completion(struct dsa_inband *inband, int timeout_ms)
+{
+ unsigned long jiffies = msecs_to_jiffies(timeout_ms);
+
+ reinit_completion(&inband->completion);
+
+ return wait_for_completion_timeout(&inband->completion, jiffies);
+}
+EXPORT_SYMBOL_GPL(dsa_inband_wait_for_completion);
+
static const struct dsa_stubs __dsa_stubs = {
.conduit_hwtstamp_validate = __dsa_conduit_hwtstamp_validate,
};
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 02/12] net: dsa: qca8K: Move queuing for request frame into the core
From: Luke Howard @ 2026-07-03 7:30 UTC (permalink / raw)
To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
Simon Horman, Florian Fainelli, Tobias Waldekranz
Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
Christoph Mellauner, Simon Gapp, Ryan Wilkins, Mattias Forsblad,
netdev, linux-kernel, Luke Howard
In-Reply-To: <20260703-net-next-dsa-rmu-v1-0-a03febf20bb4@padl.com>
From: Andrew Lunn <andrew@lunn.ch>
Combine the queuing of the request and waiting for the completion into
one core helper. Add the function dsa_rmu_request() to perform this.
Access to statistics is not a strict request/reply, so the
dsa_rmu_wait_for_completion needs to be kept.
It is also no possible to combine dsa_rmu_request() and
dsa_rmu_wait_for_completion() since we need to avoid the race of
sending the request, receiving a reply, and the completion has not
been reinitialised because the schedule at decided to do other things.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 32 ++++++++++----------------------
include/net/dsa.h | 2 ++
net/dsa/dsa.c | 31 +++++++++++++++++++++++++++++++
3 files changed, 43 insertions(+), 22 deletions(-)
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index fe6fb69b6f92f..4da6094552f1e 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -337,10 +337,8 @@ static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
mgmt_eth_data->ack = false;
- dev_queue_xmit(skb);
-
- ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_request(&mgmt_eth_data->inband, skb,
+ QCA8K_ETHERNET_TIMEOUT);
*val = mgmt_eth_data->data[0];
if (len > QCA_HDR_MGMT_DATA1_LEN)
@@ -387,10 +385,8 @@ static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
mgmt_eth_data->ack = false;
- dev_queue_xmit(skb);
-
- ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_request(&mgmt_eth_data->inband, skb,
+ QCA8K_ETHERNET_TIMEOUT);
ack = mgmt_eth_data->ack;
@@ -595,10 +591,8 @@ qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
mgmt_eth_data->ack = false;
- dev_queue_xmit(skb);
-
- ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_request(&mgmt_eth_data->inband, skb,
+ QCA8K_ETHERNET_TIMEOUT);
ack = mgmt_eth_data->ack;
@@ -695,10 +689,8 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq);
mgmt_eth_data->ack = false;
- dev_queue_xmit(write_skb);
-
- ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_request(&mgmt_eth_data->inband, write_skb,
+ QCA8K_ETHERNET_TIMEOUT);
ack = mgmt_eth_data->ack;
@@ -730,10 +722,8 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq);
mgmt_eth_data->ack = false;
- dev_queue_xmit(read_skb);
-
- ret = dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
- QCA8K_ETHERNET_TIMEOUT);
+ ret = dsa_inband_request(&mgmt_eth_data->inband, read_skb,
+ QCA8K_ETHERNET_TIMEOUT);
ack = mgmt_eth_data->ack;
@@ -757,8 +747,6 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq);
mgmt_eth_data->ack = false;
- dev_queue_xmit(clear_skb);
-
dsa_inband_wait_for_completion(&mgmt_eth_data->inband,
QCA8K_ETHERNET_TIMEOUT);
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 9b0c109b3058a..6b5aeb99ec3bb 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -1357,6 +1357,8 @@ struct dsa_inband {
void dsa_inband_init(struct dsa_inband *inband);
void dsa_inband_complete(struct dsa_inband *inband);
+int dsa_inband_request(struct dsa_inband *inband, struct sk_buff *skb,
+ int timeout_ms);
int dsa_inband_wait_for_completion(struct dsa_inband *inband, int timeout_ms);
/* Keep inline for faster access in hot path */
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index dc4e5cdb2f5b2..534e391fac7b5 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -1856,6 +1856,37 @@ int dsa_inband_wait_for_completion(struct dsa_inband *inband, int timeout_ms)
}
EXPORT_SYMBOL_GPL(dsa_inband_wait_for_completion);
+/* dsa_inband_request - send an inband request frame and wait for the reply.
+ * @inband: inband state for the switch
+ * @skb: request frame; ownership is transferred to this function
+ * @insert_seqno: optional callback to stamp the sequence number into @skb
+ * @resp: buffer to receive the reply payload, or NULL if none is expected
+ * @resp_len: size of @resp in bytes
+ * @timeout_ms: how long to wait for the reply, in milliseconds
+ *
+ * Serialise against other inband operations, transmit @skb and wait for the
+ * matching reply handed back via dsa_inband_complete().
+ *
+ * Return the number of response bytes copied into @resp (0 when no response
+ * is expected) on success, or a negative errno (-EOPNOTSUPP if the conduit
+ * is down, -ETIMEDOUT if no reply arrived, or an error from the completer).
+ *
+ * Cannot use dsa_inband_wait_for_completion() since the completion needs to
+ * be reinitialised before the skb is queued, to avoid races.
+ */
+int dsa_inband_request(struct dsa_inband *inband, struct sk_buff *skb,
+ int timeout_ms)
+{
+ unsigned long jiffies = msecs_to_jiffies(timeout_ms);
+
+ reinit_completion(&inband->completion);
+
+ dev_queue_xmit(skb);
+
+ return wait_for_completion_timeout(&inband->completion, jiffies);
+}
+EXPORT_SYMBOL_GPL(dsa_inband_request);
+
static const struct dsa_stubs __dsa_stubs = {
.conduit_hwtstamp_validate = __dsa_conduit_hwtstamp_validate,
};
--
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