* [PATCH] ppp: require CAP_NET_ADMIN in target netns for unattached ioctls
From: Taegu Ha @ 2026-04-08 4:23 UTC (permalink / raw)
To: netdev
Cc: linux-ppp, qingfang.deng, gnault, jaco, richardbgobert, ericwouds,
Taegu Ha
/dev/ppp open is currently authorized against file->f_cred->user_ns,
while unattached administrative ioctls operate on current->nsproxy->net_ns.
As a result, a local unprivileged user can create a new user namespace
with CLONE_NEWUSER, gain CAP_NET_ADMIN only in that new user namespace,
and still issue PPPIOCNEWUNIT, PPPIOCATTACH, or PPPIOCATTCHAN against
an inherited network namespace.
Require CAP_NET_ADMIN in the user namespace that owns the target network
namespace before handling these unattached PPP administrative ioctls.
This preserves normal pppd operation in the network namespace it is
actually privileged in, while rejecting the userns-only inherited-netns
case.
Fixes: 273ec51dd7ce ("net: ppp_generic - introduce net-namespace functionality v2")
Reported-by: Taegu Ha <hataegu0826@gmail.com>
Signed-off-by: Taegu Ha <hataegu0826@gmail.com>
---
drivers/net/ppp/ppp_generic.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index e9b41777be80..99a8557188c0 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -1059,6 +1059,9 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
switch (cmd) {
case PPPIOCNEWUNIT:
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
+
/* Create a new ppp unit */
if (get_user(unit, p))
break;
@@ -1073,6 +1076,9 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
break;
case PPPIOCATTACH:
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
+
/* Attach to an existing ppp unit */
if (get_user(unit, p))
break;
@@ -1089,6 +1095,9 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
break;
case PPPIOCATTCHAN:
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
+
if (get_user(unit, p))
break;
err = -ENXIO;
--
2.43.0
^ permalink raw reply related
* Re: [PATCH bpf v6 1/2] bpf: tcp: Reject non-TCP skb in bpf_sk_assign_tcp_reqsk()
From: Kuniyuki Iwashima @ 2026-04-08 4:25 UTC (permalink / raw)
To: Jiayuan Chen
Cc: Martin KaFai Lau, Eric Dumazet, bpf, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Song Liu, Yonghong Song, Jiri Olsa,
John Fastabend, Stanislav Fomichev, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan, netdev,
linux-kernel, linux-kselftest
In-Reply-To: <4820d99b-f2c4-4b24-b90f-87e6444284c4@linux.dev>
On Mon, Apr 6, 2026 at 10:22 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>
>
> On 4/7/26 3:53 AM, Martin KaFai Lau wrote:
> > On Fri, Apr 03, 2026 at 09:58:27AM +0800, Jiayuan Chen wrote:
> >> bpf_sk_assign_tcp_reqsk() only validates skb->protocol (L3) but does not
> >> check the L4 protocol in the IP header. A BPF program can call this kfunc
> >> on a UDP skb with a valid TCP listener socket, which will succeed and
> >> attach a TCP reqsk to the UDP skb.
> >>
> >> When the UDP skb enters the UDP receive path, skb_steal_sock() returns
> >> the TCP listener from the reqsk. The UDP code then passes this TCP socket
> >> to udp_unicast_rcv_skb() -> __udp_enqueue_schedule_skb(), which casts
> >> it to udp_sock and accesses UDP-specific fields at invalid offsets,
> >> causing a null pointer dereference and kernel panic:
> >>
> >> BUG: KASAN: null-ptr-deref in __udp_enqueue_schedule_skb+0x19d/0x1df0
> >> Read of size 4 at addr 0000000000000008 by task test_progs/537
> >>
> >> CPU: 1 UID: 0 PID: 537 Comm: test_progs Not tainted 7.0.0-rc4+ #46 PREEMPT
> >> Call Trace:
> >> <IRQ>
> >> dump_stack_lvl (lib/dump_stack.c:123)
> >> print_report (mm/kasan/report.c:487)
> >> kasan_report (mm/kasan/report.c:597)
> >> __kasan_check_read (mm/kasan/shadow.c:32)
> >> __udp_enqueue_schedule_skb (net/ipv4/udp.c:1719)
> >> udp_queue_rcv_one_skb (net/ipv4/udp.c:2370 net/ipv4/udp.c:2500)
> >> udp_queue_rcv_skb (net/ipv4/udp.c:2532)
> >> udp_unicast_rcv_skb (net/ipv4/udp.c:2684)
> >> __udp4_lib_rcv (net/ipv4/udp.c:2742)
> >> udp_rcv (net/ipv4/udp.c:2937)
> >> ip_protocol_deliver_rcu (net/ipv4/ip_input.c:209)
> >> ip_local_deliver_finish (./include/linux/rcupdate.h:879 net/ipv4/ip_input.c:242)
> >> ip_local_deliver (net/ipv4/ip_input.c:265)
> >> __netif_receive_skb_one_core (net/core/dev.c:6164 (discriminator 4))
> >> __netif_receive_skb (net/core/dev.c:6280)
> >>
> >> Fix this by checking the IP header's protocol field in
> >> bpf_sk_assign_tcp_reqsk() and rejecting non-TCP skbs with -EINVAL.
> >>
> >> Note that for IPv6, the nexthdr check does not walk extension headers.
> >> This is uncommon for TCP SYN packets in practice, and keeping it simple
> >> was agreed upon by Kuniyuki Iwashima.
> > sashiko has flagged a similar issue with larger scope.
> > Please take a look. Thanks.
> >
> > https://sashiko.dev/#/patchset/20260403015851.148209-1-jiayuan.chen%40linux.dev
>
>
> Thanks a lot Martin, sashiko actually dug into a deeper issue here.
>
> Eric and Kuniyuki,
>
> I think the AI review has a point. Since BPF can modify skb fields, the
> following sequence still bypasses the protocol check in
> bpf_sk_assign_tcp_reqsk():
>
> // for a UDP skb
> iph->protocol = TCP
> bpf_sk_assign_tcp_reqsk()
> iph->protocol = UDP
>
> On top of that, bpf_sk_assign() already has the same problem — it doesn't
> validate L4 protocol at all.
Sigh... honestly it does not make sense to me to add changes
in the common fast path to protect someone with bpf capability
shooting oneself in the foot.
On top of L4 validation in bpf_sk_assign() and bpf_sk_assign_tcp_reqsk(),
can't we mark such an skb immutable after the helpers and catch
subsequent writes to skb->data on the verifier ?
>
> So I think we should add a check matching skb against sk in
> skb_steal_sock() instead of adding check in bpf helper.
> (Also, we probably need some MIB counters for this.)
No one increments the counter in the real life.
>
>
>
> Below is my diff:
>
> //The core reason for passing protocol as a parameter rather than
> reading it from
> //the skb is that IPv6 extension headers make it non-trivial to get the
> actual nexthdr.
> //It's much simpler to just let the caller tell us what protocol it expects.
>
> diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h
> index c16de5b7963fd..c1032bb9ea864 100644
> --- a/include/net/inet6_hashtables.h
> +++ b/include/net/inet6_hashtables.h
> @@ -104,12 +104,13 @@ static inline
> struct sock *inet6_steal_sock(struct net *net, struct sk_buff *skb,
> int doff,
> const struct in6_addr *saddr, const
> __be16 sport,
> const struct in6_addr *daddr, const
> __be16 dport,
> - bool *refcounted, inet6_ehashfn_t *ehashfn)
> + bool *refcounted, inet6_ehashfn_t *ehashfn,
> + int protocol)
> {
> struct sock *sk, *reuse_sk;
> bool prefetched;
>
> - sk = skb_steal_sock(skb, refcounted, &prefetched);
> + sk = skb_steal_sock(skb, refcounted, &prefetched, protocol);
> if (!sk)
> return NULL;
>
> @@ -151,7 +152,7 @@ static inline struct sock *__inet6_lookup_skb(struct
> sk_buff *skb, int doff,
> struct sock *sk;
>
> sk = inet6_steal_sock(net, skb, doff, &ip6h->saddr, sport,
> &ip6h->daddr, dport,
> - refcounted, inet6_ehashfn);
> + refcounted, inet6_ehashfn, IPPROTO_TCP);
> if (IS_ERR(sk))
> return NULL;
> if (sk)
> diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
> index 5a979dcab5383..0ad73135ece7d 100644
> --- a/include/net/inet_hashtables.h
> +++ b/include/net/inet_hashtables.h
> @@ -433,12 +433,13 @@ static inline
> struct sock *inet_steal_sock(struct net *net, struct sk_buff *skb, int
> doff,
> const __be32 saddr, const __be16 sport,
> const __be32 daddr, const __be16 dport,
> - bool *refcounted, inet_ehashfn_t *ehashfn)
> + bool *refcounted, inet_ehashfn_t *ehashfn,
> + int protocol)
> {
> struct sock *sk, *reuse_sk;
> bool prefetched;
>
> - sk = skb_steal_sock(skb, refcounted, &prefetched);
> + sk = skb_steal_sock(skb, refcounted, &prefetched, protocol);
> if (!sk)
> return NULL;
>
> @@ -469,7 +470,7 @@ struct sock *inet_steal_sock(struct net *net, struct
> sk_buff *skb, int doff,
> return reuse_sk;
> }
>
> -static inline struct sock *__inet_lookup_skb(struct sk_buff *skb,
> +static inline struct sock *__tcp_lookup_skb(struct sk_buff *skb,
> int doff,
> const __be16 sport,
> const __be16 dport,
> @@ -481,7 +482,7 @@ static inline struct sock *__inet_lookup_skb(struct
> sk_buff *skb,
> struct sock *sk;
>
> sk = inet_steal_sock(net, skb, doff, iph->saddr, sport,
> iph->daddr, dport,
> - refcounted, inet_ehashfn);
> + refcounted, inet_ehashfn, IPPROTO_TCP);
> if (IS_ERR(sk))
> return NULL;
> if (sk)
> diff --git a/include/net/request_sock.h b/include/net/request_sock.h
> index 5a9c826a7092d..80bd209b2323b 100644
> --- a/include/net/request_sock.h
> +++ b/include/net/request_sock.h
> @@ -91,7 +91,8 @@ static inline struct sock *req_to_sk(struct
> request_sock *req)
> * @prefetched: is set to true if the socket was assigned from bpf
> */
> static inline struct sock *skb_steal_sock(struct sk_buff *skb,
> - bool *refcounted, bool
> *prefetched)
> + bool *refcounted, bool
> *prefetched,
> + int protocol)
> {
> struct sock *sk = skb->sk;
>
> @@ -103,6 +104,24 @@ static inline struct sock *skb_steal_sock(struct
> sk_buff *skb,
>
> *prefetched = skb_sk_is_prefetched(skb);
> if (*prefetched) {
> + /* Validate that the stolen socket matches the expected L4
> + * protocol. BPF (bpf_sk_assign) can mistakenly or
> maliciously
> + * assign a socket of the wrong type. If the protocols
> don't
> + * match, clean up the assignment and return NULL so the
> caller
> + * falls through to the normal hash table lookup.
> + *
> + * For full sockets, check sk_protocol directly.
> + * For request sockets (only created by
> bpf_sk_assign_tcp_reqsk),
> + * sk_protocol is not in sock_common and cannot be accessed.
> + * Request sockets are always TCP, so assume IPPROTO_TCP.
> + */
> + if ((sk_fullsock(sk) ? sk->sk_protocol : IPPROTO_TCP) !=
> protocol) {
> + skb_orphan(skb);
> + *prefetched = false;
> + *refcounted = false;
> + return NULL;
> + }
> +
> #if IS_ENABLED(CONFIG_SYN_COOKIES)
> if (sk->sk_state == TCP_NEW_SYN_RECV &&
> inet_reqsk(sk)->syncookie) {
> struct request_sock *req = inet_reqsk(sk);
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index c7b2463c2e254..442a9379aefe6 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -2188,7 +2188,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
> th = (const struct tcphdr *)skb->data;
> iph = ip_hdr(skb);
> lookup:
> - sk = __inet_lookup_skb(skb, __tcp_hdrlen(th), th->source,
> + sk = __tcp_lookup_skb(skb, __tcp_hdrlen(th), th->source,
> th->dest, sdif, &refcounted);
> if (!sk)
> goto no_tcp_socket;
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index b60fad393e182..61f7fbf5ffd9c 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -2728,7 +2728,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct
> udp_table *udptable,
> goto csum_error;
>
> sk = inet_steal_sock(net, skb, sizeof(struct udphdr), saddr,
> uh->source, daddr, uh->dest,
> - &refcounted, udp_ehashfn);
> + &refcounted, udp_ehashfn, IPPROTO_UDP);
> if (IS_ERR(sk))
> goto no_sk;
>
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index 010b909275dd0..9c3c25c938ec8 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -1115,7 +1115,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct
> udp_table *udptable,
>
> /* Check if the socket is already available, e.g. due to early
> demux */
> sk = inet6_steal_sock(net, skb, sizeof(struct udphdr), saddr,
> uh->source, daddr, uh->dest,
> - &refcounted, udp6_ehashfn);
> + &refcounted, udp6_ehashfn, IPPROTO_UDP);
> if (IS_ERR(sk))
> goto no_sk;
>
>
>
>
> ---
> This fix touches the net core path (skb_steal_sock / inet_steal_sock),
> so it probably makes more sense to review it on the net side rather than
> the bpf side.
>
^ permalink raw reply
* RE: [PATCH v2 net 2/6] net: enetc: linearize PTP event packets with one-step TX timestamping
From: Wei Fang @ 2026-04-08 4:44 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Zefir Kurtisi, Claudiu Manoil, Clark Wang, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ioana Ciornei, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Simon Horman, bpf@vger.kernel.org, imx@lists.linux.dev,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org
In-Reply-To: <20260406204122.167237-3-vladimir.oltean@nxp.com>
> Sashiko reports that enetc_ptp_parse() uses ptp_parse_header(), which
> does not handle fragmented headers, and expects the entire area between
> skb_mac_header() and the end of the PTP header to be linear.
I think we should correct the check in ptp_parse_header().
@@ -130,7 +130,7 @@ struct ptp_header *ptp_parse_header(struct sk_buff *skb, unsigned int type)
ptr += ETH_HLEN;
/* Ensure that the entire header is present in this packet. */
- if (ptr + sizeof(struct ptp_header) > skb->data + skb->len)
+ if (ptr + sizeof(struct ptp_header) > skb->data + skb_headlen(skb))
return NULL;
^ permalink raw reply
* RE: [PATCH v2 net 3/6] net: enetc: ensure enetc_xdp_xmit() calls enetc_update_tx_ring_tail()
From: Wei Fang @ 2026-04-08 5:06 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Zefir Kurtisi, Claudiu Manoil, Clark Wang, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ioana Ciornei, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Simon Horman, bpf@vger.kernel.org, imx@lists.linux.dev,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org
In-Reply-To: <20260406204122.167237-4-vladimir.oltean@nxp.com>
> enetc_xdp_xmit() keeps track of 3 numbers:
> - num_frames: total length of passed struct xdp_frame **frames array
> - xdp_tx_frm_cnt: number of frames successfully sent
> - k: index of currently sent frame from array
>
> With "k != xdp_tx_frm_cnt", the intention was to detect an early break
> due to an inability to send a frame, and to trigger a TX doorbell
> anyway.
>
> However, that doesn't work because every time when the loop breaks,
> k and xdp_tx_frm_cnt are mathematically equal.
Since k is always equal to xdp_tx_frm_cnt, so we only need one of them,
there is no need to use two variables.
^ permalink raw reply
* RE: [PATCH v2 net 4/6] net: enetc: fix bogus TX ring consumer index after reinitialization
From: Wei Fang @ 2026-04-08 5:09 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Zefir Kurtisi, Claudiu Manoil, Clark Wang, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ioana Ciornei, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Simon Horman, bpf@vger.kernel.org, imx@lists.linux.dev,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org
In-Reply-To: <20260406204122.167237-5-vladimir.oltean@nxp.com>
> The TBCIR (Transmit Buffer Descriptor Ring Consumer Index) register has
> the BD index as the lower 16 bits, but the upper 16 bits contain this
> field:
>
> STAT_ID: Status identifier. Incremented each time the BDR_INDEX is
> updated and an error status bit was set for one of the processed BDs.
> Clears on read.
>
> If there was any transmit error prior to the ring reinitialization and
> this is the first time we re-read the TBCIR register, reading it will
> give us a value with non-zero upper bits, which is saved in
> bdr->next_to_clean.
>
> If subsequently NAPI gets invoked and enetc_clean_tx_ring() runs, this
> will dereference the &tx_ring->tx_swbd[] for the bogus (and huge)
> next_to_clean index, and will result in an out-of-bounds memory access.
>
> Other places like enetc_bd_ready_count() do mask out the upper bits, so
> let's do that here as well.
>
> Fixes: d4fd0404c1c9 ("enetc: Introduce basic PF and VF ENETC ethernet
> drivers")
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> v1->v2: none
> ---
> drivers/net/ethernet/freescale/enetc/enetc.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c
> b/drivers/net/ethernet/freescale/enetc/enetc.c
> index 5b97f9e668ba..a48ee3040b80 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
> @@ -2604,7 +2604,8 @@ static void enetc_setup_txbdr(struct enetc_hw *hw,
> struct enetc_bdr *tx_ring)
> * adjust sw indexes
> */
> tx_ring->next_to_use = enetc_txbdr_rd(hw, idx, ENETC_TBPIR);
> - tx_ring->next_to_clean = enetc_txbdr_rd(hw, idx, ENETC_TBCIR);
> + tx_ring->next_to_clean = enetc_txbdr_rd(hw, idx, ENETC_TBCIR) &
> + ENETC_TBCIR_IDX_MASK;
>
> if (tx_ring->next_to_use != tx_ring->next_to_clean &&
> !is_enetc_rev1(si)) {
> --
> 2.43.0
Reviewed-by: Wei Fang <wei.fang@nxp.com>
^ permalink raw reply
* RE: [PATCH v2 net 5/6] net: enetc: pad short frames in software
From: Wei Fang @ 2026-04-08 5:17 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Zefir Kurtisi, Claudiu Manoil, Clark Wang, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ioana Ciornei, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Simon Horman, bpf@vger.kernel.org, imx@lists.linux.dev,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org
In-Reply-To: <20260406204122.167237-6-vladimir.oltean@nxp.com>
> The ENETC does not support BUF_LEN or FRM_LEN in TX buffer descriptors
> less than 16. This is written in the reference manual of all SoCs
> supported by the driver: LS1028A, i.MX943, i.MX95 etc.
>
> Frames must not have a FRM_LEN that is less than 16 bytes. Frames of
> 0-15 bytes are not supported.
> (...)
> The first descriptor in a chain must not have a BUFF_LEN that is less
> than 16 bytes.
>
> I don't think proper attention was paid to this during development, we
> found the text at the end of a bug investigation. Therefore, the driver
> does not enforce this.
>
> But the frame length is out of the driver's control, and the network
> stack can actually send packets with skb->len smaller than that. The
> result is unpleasant, as will be explained below, so for simplicity
> sake, we just pad anything shorter than ETH_ZLEN.
>
> Zefir Kurtisi found a case where transmitting L2 WNM keep-alive frames
> through ENETC would soft-lockup the host through an IRQ storm. He later
> distilled this into a small enetc-killer.c user space program which
> sends a packet with MAC DA, MAC SA and EtherType IPv4 (14 octets in
> length) through an AF_PACKET raw socket.
>
> The IRQ storm is actually a curious effect of a chain of events.
>
> The hardware behaviour, when an invalid BD is put in its TX ring, is
> that it would transmit the packet as normal, update counters, raise
> completion interrupt as normal, but it would just not advance the
> consumer index of the ring (TBaCIR) to signify that the BD has been
> consumed and is available for software to free. The ring will also get
> its TBaSR[BUSY] bit persistently set to 1 afterwards.
>
> It deserves an explanation why the behaviour above would lead to an
> IRQ storm, since ENETC interrupts are message-based (MSI-X), and an
> unhandled interrupt would typically just be lost rather than retrigger
> itself as a wired interrupt would.
>
> NAPI processing in ENETC has 3 steps:
>
> I. the enetc_msix() hardirq handler disables RBaIER, TBaIER and sets
> softirq processing to the 'pending' state.
>
> II. the enetc_poll() softirq handler for the IRQ vector walks through
> the TX rings affine to that vector, checks which ones have a TBCIR
> updated since last time - enetc_bd_ready_count() - processes those
> completed frames, and clears pending interrupts in these updated TX
> rings by writing to TBaIDR. (I've excluded RX processing due to it
> being irrelevant).
>
> III. After the softirq handler does its round of checking all RX and TX
> rings for updates, it re-enables all interrupts in RBaIER and
> TBaIER that were previously disabled by the hardirq handler, and
> exits.
>
> Because the TX ring with the short frame is skipped at step II (TBCIR
> wasn't updated as part of HW malfunction), its pending IRQ is not
> cleared in TBaIDR by enetc_clean_tx_ring().
>
> But because enetc_msix() disables TBaIER at step I and re-enables it at
> step III, another MSI will be fired upon re-enabling it. This is what
> completes the cycle and the driver goes back to step I.
>
> So the driver misinterprets the mixed signals it's getting from the
> hardware, and ends up causing a software-amplified IRQ storm.
>
> Fixes: d4fd0404c1c9 ("enetc: Introduce basic PF and VF ENETC ethernet
> drivers")
> Reported-by: Zefir Kurtisi <zefir.kurtisi@westermo.com>
> Closes:
> https://lore.kernel.org/netdev/b3d9136c-2803-4203-b1ea-1f9e62de80a1@gma
> il.com/
> Tested-by: Zefir Kurtisi <zefir.kurtisi@westermo.com>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> v1->v2: handle the BUF_LEN limitation too, independently of FRM_LEN
> ---
> drivers/net/ethernet/freescale/enetc/enetc.c | 13 +++++++++++++
> drivers/net/ethernet/freescale/enetc/enetc.h | 2 ++
> 2 files changed, 15 insertions(+)
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c
> b/drivers/net/ethernet/freescale/enetc/enetc.c
> index a48ee3040b80..c70df45422e0 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
> @@ -1050,6 +1050,19 @@ netdev_tx_t enetc_xmit(struct sk_buff *skb, struct
> net_device *ndev)
> u8 udp, msgtype, twostep;
> u16 offset1, offset2;
>
> + /* Hardware does not support transmit buffer descriptors with a total
> + * length of less than 16 bytes, or a first buffer size of less than
> + * 16 bytes.
> + */
> + if (unlikely(skb_headlen(skb) < ENETC_MIN_BUFF_SIZE &&
> + skb_linearize(skb))) {
> + dev_kfree_skb_any(skb);
> + return NETDEV_TX_OK;
> + }
> +
> + if (eth_skb_pad(skb))
> + return NETDEV_TX_OK;
> +
> /* Mark tx timestamp type on enetc_cb->flag if requires */
> if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
> (priv->active_offloads & ENETC_F_TX_TSTAMP_MASK))
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h
> b/drivers/net/ethernet/freescale/enetc/enetc.h
> index aecd40aeef9c..819e643538b1 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
> @@ -21,6 +21,8 @@
> #define ENETC_MAX_MTU (ENETC_MAC_MAXFRM_SIZE - \
> (ETH_FCS_LEN + ETH_HLEN + VLAN_HLEN))
>
> +#define ENETC_MIN_BUFF_SIZE 16
> +
> #define ENETC_CBD_DATA_MEM_ALIGN 64
>
> #define ENETC_MADDR_HASH_TBL_SZ 64
> --
> 2.43.0
Reviewed-by: Wei Fang <wei.fang@nxp.com>
^ permalink raw reply
* [PATCH v2 0/2] mfd: nct6694: Refactor transport layer and add HIF (eSPI) support
From: a0282524688 @ 2026-04-08 5:30 UTC (permalink / raw)
To: tmyu0, linusw, brgl, linux, andi.shyti, lee, mkl, mailhol,
alexandre.belloni, wim
Cc: linux-kernel, linux-gpio, linux-i2c, linux-can, netdev,
linux-watchdog, linux-hwmon, linux-rtc, linux-usb, Ming Yu
From: Ming Yu <a0282524688@gmail.com>
The Nuvoton NCT6694 is a peripheral expander that provides GPIO, I2C,
CAN-FD, Watchdog, HWMON, PWM, and RTC sub-devices. Currently, the
driver only supports USB as the host transport interface.
This series refactors the NCT6694 MFD core to support multiple transport
backends and adds a new Host Interface (HIF) transport driver that
communicates over eSPI using Super-I/O shared memory.
Changes since version 1:
- Reworked the Super-I/O access helpers.
Ming Yu (2):
mfd: nct6694: Switch to devm_mfd_add_devices() and drop IDA
mfd: Add Host Interface (HIF) support for Nuvoton NCT6694
MAINTAINERS | 1 +
drivers/gpio/gpio-nct6694.c | 26 +-
drivers/hwmon/nct6694-hwmon.c | 21 -
drivers/i2c/busses/i2c-nct6694.c | 26 +-
drivers/mfd/Kconfig | 47 ++-
drivers/mfd/Makefile | 3 +-
drivers/mfd/nct6694-hif.c | 634 ++++++++++++++++++++++++++++
drivers/mfd/nct6694.c | 180 ++++----
drivers/net/can/usb/nct6694_canfd.c | 18 +-
drivers/rtc/rtc-nct6694.c | 7 -
drivers/watchdog/nct6694_wdt.c | 27 +-
include/linux/mfd/nct6694.h | 57 ++-
12 files changed, 814 insertions(+), 233 deletions(-)
create mode 100644 drivers/mfd/nct6694-hif.c
--
2.34.1
^ permalink raw reply
* [PATCH v2 1/2] mfd: nct6694: Switch to devm_mfd_add_devices() and drop IDA
From: a0282524688 @ 2026-04-08 5:30 UTC (permalink / raw)
To: tmyu0, linusw, brgl, linux, andi.shyti, lee, mkl, mailhol,
alexandre.belloni, wim
Cc: linux-kernel, linux-gpio, linux-i2c, linux-can, netdev,
linux-watchdog, linux-hwmon, linux-rtc, linux-usb, Ming Yu
In-Reply-To: <20260408053037.1867092-1-a0282524688@gmail.com>
From: Ming Yu <a0282524688@gmail.com>
Currently, the nct6694 core driver uses mfd_add_hotplug_devices()
and an IDA to manage subdevice IDs.
Switch the core implementation to use the managed
devm_mfd_add_devices() API, which simplifies the error handling and
device lifecycle management. Concurrently, drop the custom IDA
implementation and transition to using pdev->id.
Signed-off-by: Ming Yu <a0282524688@gmail.com>
---
drivers/gpio/gpio-nct6694.c | 19 +------
drivers/i2c/busses/i2c-nct6694.c | 19 +------
drivers/mfd/nct6694.c | 83 ++++++++++++-----------------
drivers/net/can/usb/nct6694_canfd.c | 12 +----
drivers/watchdog/nct6694_wdt.c | 20 +------
include/linux/mfd/nct6694.h | 8 +--
6 files changed, 43 insertions(+), 118 deletions(-)
diff --git a/drivers/gpio/gpio-nct6694.c b/drivers/gpio/gpio-nct6694.c
index a8607f0d9915..3703a61209e6 100644
--- a/drivers/gpio/gpio-nct6694.c
+++ b/drivers/gpio/gpio-nct6694.c
@@ -7,7 +7,6 @@
#include <linux/bits.h>
#include <linux/gpio/driver.h>
-#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
@@ -381,14 +380,6 @@ static void nct6694_irq_dispose_mapping(void *d)
irq_dispose_mapping(data->irq);
}
-static void nct6694_gpio_ida_free(void *d)
-{
- struct nct6694_gpio_data *data = d;
- struct nct6694 *nct6694 = data->nct6694;
-
- ida_free(&nct6694->gpio_ida, data->group);
-}
-
static int nct6694_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -403,15 +394,7 @@ static int nct6694_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
data->nct6694 = nct6694;
-
- ret = ida_alloc(&nct6694->gpio_ida, GFP_KERNEL);
- if (ret < 0)
- return ret;
- data->group = ret;
-
- ret = devm_add_action_or_reset(dev, nct6694_gpio_ida_free, data);
- if (ret)
- return ret;
+ data->group = pdev->id;
names = devm_kcalloc(dev, NCT6694_NR_GPIO, sizeof(char *),
GFP_KERNEL);
diff --git a/drivers/i2c/busses/i2c-nct6694.c b/drivers/i2c/busses/i2c-nct6694.c
index 1413ab6f9462..7d8ad997f6d2 100644
--- a/drivers/i2c/busses/i2c-nct6694.c
+++ b/drivers/i2c/busses/i2c-nct6694.c
@@ -6,7 +6,6 @@
*/
#include <linux/i2c.h>
-#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
@@ -134,14 +133,6 @@ static int nct6694_i2c_set_baudrate(struct nct6694_i2c_data *data)
return 0;
}
-static void nct6694_i2c_ida_free(void *d)
-{
- struct nct6694_i2c_data *data = d;
- struct nct6694 *nct6694 = data->nct6694;
-
- ida_free(&nct6694->i2c_ida, data->port);
-}
-
static int nct6694_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -155,15 +146,7 @@ static int nct6694_i2c_probe(struct platform_device *pdev)
data->dev = dev;
data->nct6694 = nct6694;
-
- ret = ida_alloc(&nct6694->i2c_ida, GFP_KERNEL);
- if (ret < 0)
- return ret;
- data->port = ret;
-
- ret = devm_add_action_or_reset(dev, nct6694_i2c_ida_free, data);
- if (ret)
- return ret;
+ data->port = pdev->id;
ret = nct6694_i2c_set_baudrate(data);
if (ret)
diff --git a/drivers/mfd/nct6694.c b/drivers/mfd/nct6694.c
index 308b2fda3055..8ce2c4985aab 100644
--- a/drivers/mfd/nct6694.c
+++ b/drivers/mfd/nct6694.c
@@ -11,7 +11,6 @@
#include <linux/bits.h>
#include <linux/interrupt.h>
-#include <linux/idr.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
@@ -23,35 +22,35 @@
#include <linux/usb.h>
static const struct mfd_cell nct6694_devs[] = {
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
-
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
-
- MFD_CELL_NAME("nct6694-canfd"),
- MFD_CELL_NAME("nct6694-canfd"),
-
- MFD_CELL_NAME("nct6694-wdt"),
- MFD_CELL_NAME("nct6694-wdt"),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 0),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 1),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 2),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 3),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 4),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 5),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 6),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 7),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 8),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 9),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 10),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 11),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 12),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 13),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 14),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 15),
+
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 0),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 1),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 2),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 3),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 4),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 5),
+
+ MFD_CELL_BASIC("nct6694-canfd", NULL, NULL, 0, 0),
+ MFD_CELL_BASIC("nct6694-canfd", NULL, NULL, 0, 1),
+
+ MFD_CELL_BASIC("nct6694-wdt", NULL, NULL, 0, 0),
+ MFD_CELL_BASIC("nct6694-wdt", NULL, NULL, 0, 1),
MFD_CELL_NAME("nct6694-hwmon"),
@@ -307,23 +306,18 @@ static int nct6694_usb_probe(struct usb_interface *iface,
nct6694->dev = dev;
nct6694->udev = udev;
- ida_init(&nct6694->gpio_ida);
- ida_init(&nct6694->i2c_ida);
- ida_init(&nct6694->canfd_ida);
- ida_init(&nct6694->wdt_ida);
-
spin_lock_init(&nct6694->irq_lock);
ret = devm_mutex_init(dev, &nct6694->access_lock);
if (ret)
- goto err_ida;
+ goto err_irq_domain;
interface = iface->cur_altsetting;
int_endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(int_endpoint)) {
ret = -ENODEV;
- goto err_ida;
+ goto err_irq_domain;
}
usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
@@ -332,11 +326,11 @@ static int nct6694_usb_probe(struct usb_interface *iface,
ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);
if (ret)
- goto err_ida;
+ goto err_irq_domain;
usb_set_intfdata(iface, nct6694);
- ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs));
+ ret = devm_mfd_add_devices(dev, 0, nct6694_devs, ARRAY_SIZE(nct6694_devs), NULL, 0, NULL);
if (ret)
goto err_mfd;
@@ -344,11 +338,7 @@ static int nct6694_usb_probe(struct usb_interface *iface,
err_mfd:
usb_kill_urb(nct6694->int_in_urb);
-err_ida:
- ida_destroy(&nct6694->wdt_ida);
- ida_destroy(&nct6694->canfd_ida);
- ida_destroy(&nct6694->i2c_ida);
- ida_destroy(&nct6694->gpio_ida);
+err_irq_domain:
irq_domain_remove(nct6694->domain);
err_urb:
usb_free_urb(nct6694->int_in_urb);
@@ -359,12 +349,7 @@ static void nct6694_usb_disconnect(struct usb_interface *iface)
{
struct nct6694 *nct6694 = usb_get_intfdata(iface);
- mfd_remove_devices(nct6694->dev);
usb_kill_urb(nct6694->int_in_urb);
- ida_destroy(&nct6694->wdt_ida);
- ida_destroy(&nct6694->canfd_ida);
- ida_destroy(&nct6694->i2c_ida);
- ida_destroy(&nct6694->gpio_ida);
irq_domain_remove(nct6694->domain);
usb_free_urb(nct6694->int_in_urb);
}
diff --git a/drivers/net/can/usb/nct6694_canfd.c b/drivers/net/can/usb/nct6694_canfd.c
index e5f7f8849a73..29282c56430f 100644
--- a/drivers/net/can/usb/nct6694_canfd.c
+++ b/drivers/net/can/usb/nct6694_canfd.c
@@ -8,7 +8,6 @@
#include <linux/can/dev.h>
#include <linux/can/rx-offload.h>
#include <linux/ethtool.h>
-#include <linux/idr.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/mfd/nct6694.h>
@@ -725,15 +724,13 @@ static int nct6694_canfd_probe(struct platform_device *pdev)
struct net_device *ndev;
int port, irq, ret, can_clk;
- port = ida_alloc(&nct6694->canfd_ida, GFP_KERNEL);
- if (port < 0)
- return port;
+ port = pdev->id;
irq = irq_create_mapping(nct6694->domain,
NCT6694_IRQ_CAN0 + port);
if (!irq) {
ret = -EINVAL;
- goto free_ida;
+ return ret;
}
ndev = alloc_candev(sizeof(struct nct6694_canfd_priv), 1);
@@ -796,24 +793,19 @@ static int nct6694_canfd_probe(struct platform_device *pdev)
free_candev(ndev);
dispose_irq:
irq_dispose_mapping(irq);
-free_ida:
- ida_free(&nct6694->canfd_ida, port);
return ret;
}
static void nct6694_canfd_remove(struct platform_device *pdev)
{
struct nct6694_canfd_priv *priv = platform_get_drvdata(pdev);
- struct nct6694 *nct6694 = priv->nct6694;
struct net_device *ndev = priv->ndev;
- int port = ndev->dev_port;
int irq = ndev->irq;
unregister_candev(ndev);
can_rx_offload_del(&priv->offload);
free_candev(ndev);
irq_dispose_mapping(irq);
- ida_free(&nct6694->canfd_ida, port);
}
static struct platform_driver nct6694_canfd_driver = {
diff --git a/drivers/watchdog/nct6694_wdt.c b/drivers/watchdog/nct6694_wdt.c
index bc3689bd4b6b..2b4b804a1739 100644
--- a/drivers/watchdog/nct6694_wdt.c
+++ b/drivers/watchdog/nct6694_wdt.c
@@ -5,7 +5,6 @@
* Copyright (C) 2025 Nuvoton Technology Corp.
*/
-#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
@@ -233,21 +232,12 @@ static const struct watchdog_ops nct6694_wdt_ops = {
.ping = nct6694_wdt_ping,
};
-static void nct6694_wdt_ida_free(void *d)
-{
- struct nct6694_wdt_data *data = d;
- struct nct6694 *nct6694 = data->nct6694;
-
- ida_free(&nct6694->wdt_ida, data->wdev_idx);
-}
-
static int nct6694_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct nct6694 *nct6694 = dev_get_drvdata(dev->parent);
struct nct6694_wdt_data *data;
struct watchdog_device *wdev;
- int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -260,15 +250,7 @@ static int nct6694_wdt_probe(struct platform_device *pdev)
data->dev = dev;
data->nct6694 = nct6694;
-
- ret = ida_alloc(&nct6694->wdt_ida, GFP_KERNEL);
- if (ret < 0)
- return ret;
- data->wdev_idx = ret;
-
- ret = devm_add_action_or_reset(dev, nct6694_wdt_ida_free, data);
- if (ret)
- return ret;
+ data->wdev_idx = pdev->id;
wdev = &data->wdev;
wdev->info = &nct6694_wdt_info;
diff --git a/include/linux/mfd/nct6694.h b/include/linux/mfd/nct6694.h
index 6eb9be2cd4a0..496da72949d9 100644
--- a/include/linux/mfd/nct6694.h
+++ b/include/linux/mfd/nct6694.h
@@ -8,6 +8,10 @@
#ifndef __MFD_NCT6694_H
#define __MFD_NCT6694_H
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
#define NCT6694_VENDOR_ID 0x0416
#define NCT6694_PRODUCT_ID 0x200B
#define NCT6694_INT_IN_EP 0x81
@@ -82,10 +86,6 @@ union __packed nct6694_usb_msg {
struct nct6694 {
struct device *dev;
- struct ida gpio_ida;
- struct ida i2c_ida;
- struct ida canfd_ida;
- struct ida wdt_ida;
struct irq_domain *domain;
struct mutex access_lock;
spinlock_t irq_lock;
--
2.34.1
^ permalink raw reply related
* [PATCH v2 2/2] mfd: Add Host Interface (HIF) support for Nuvoton NCT6694
From: a0282524688 @ 2026-04-08 5:30 UTC (permalink / raw)
To: tmyu0, linusw, brgl, linux, andi.shyti, lee, mkl, mailhol,
alexandre.belloni, wim
Cc: linux-kernel, linux-gpio, linux-i2c, linux-can, netdev,
linux-watchdog, linux-hwmon, linux-rtc, linux-usb, Ming Yu
In-Reply-To: <20260408053037.1867092-1-a0282524688@gmail.com>
From: Ming Yu <a0282524688@gmail.com>
The Nuvoton NCT6694 also provides a Host Interface (HIF) via eSPI
to the host to access its features.
Sub-devices can use the common functions nct6694_read_msg() and
nct6694_write_msg() to issue a command. They can also request
interrupts that will be called when the HIF device triggers a
shared memory interrupt.
To support multiple transports, the driver configuration is
updated to allow selecting between the USB and HIF interfaces.
Signed-off-by: Ming Yu <a0282524688@gmail.com>
---
Changes since version 1:
- Drop function pointers from Super-I/O access and use static inline
helpers with proper types.
MAINTAINERS | 1 +
drivers/gpio/gpio-nct6694.c | 7 -
drivers/hwmon/nct6694-hwmon.c | 21 -
drivers/i2c/busses/i2c-nct6694.c | 7 -
drivers/mfd/Kconfig | 47 ++-
drivers/mfd/Makefile | 3 +-
drivers/mfd/nct6694-hif.c | 634 ++++++++++++++++++++++++++++
drivers/mfd/nct6694.c | 97 +++--
drivers/net/can/usb/nct6694_canfd.c | 6 -
drivers/rtc/rtc-nct6694.c | 7 -
drivers/watchdog/nct6694_wdt.c | 7 -
include/linux/mfd/nct6694.h | 51 ++-
12 files changed, 772 insertions(+), 116 deletions(-)
create mode 100644 drivers/mfd/nct6694-hif.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c3fe46d7c4bc..7b6241faa6df 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18899,6 +18899,7 @@ S: Supported
F: drivers/gpio/gpio-nct6694.c
F: drivers/hwmon/nct6694-hwmon.c
F: drivers/i2c/busses/i2c-nct6694.c
+F: drivers/mfd/nct6694-hif.c
F: drivers/mfd/nct6694.c
F: drivers/net/can/usb/nct6694_canfd.c
F: drivers/rtc/rtc-nct6694.c
diff --git a/drivers/gpio/gpio-nct6694.c b/drivers/gpio/gpio-nct6694.c
index 3703a61209e6..a279510ece89 100644
--- a/drivers/gpio/gpio-nct6694.c
+++ b/drivers/gpio/gpio-nct6694.c
@@ -12,13 +12,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-/*
- * USB command module type for NCT6694 GPIO controller.
- * This defines the module type used for communication with the NCT6694
- * GPIO controller over the USB interface.
- */
-#define NCT6694_GPIO_MOD 0xFF
-
#define NCT6694_GPIO_VER 0x90
#define NCT6694_GPIO_VALID 0x110
#define NCT6694_GPI_DATA 0x120
diff --git a/drivers/hwmon/nct6694-hwmon.c b/drivers/hwmon/nct6694-hwmon.c
index 6dcf22ca5018..581451875f2c 100644
--- a/drivers/hwmon/nct6694-hwmon.c
+++ b/drivers/hwmon/nct6694-hwmon.c
@@ -15,13 +15,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
-/*
- * USB command module type for NCT6694 report channel
- * This defines the module type used for communication with the NCT6694
- * report channel over the USB interface.
- */
-#define NCT6694_RPT_MOD 0xFF
-
/* Report channel */
/*
* The report channel is used to report the status of the hardware monitor
@@ -38,13 +31,6 @@
#define NCT6694_TIN_STS(x) (0x6A + (x))
#define NCT6694_FIN_STS(x) (0x6E + (x))
-/*
- * USB command module type for NCT6694 HWMON controller.
- * This defines the module type used for communication with the NCT6694
- * HWMON controller over the USB interface.
- */
-#define NCT6694_HWMON_MOD 0x00
-
/* Command 00h - Hardware Monitor Control */
#define NCT6694_HWMON_CONTROL 0x00
#define NCT6694_HWMON_CONTROL_SEL 0x00
@@ -53,13 +39,6 @@
#define NCT6694_HWMON_ALARM 0x02
#define NCT6694_HWMON_ALARM_SEL 0x00
-/*
- * USB command module type for NCT6694 PWM controller.
- * This defines the module type used for communication with the NCT6694
- * PWM controller over the USB interface.
- */
-#define NCT6694_PWM_MOD 0x01
-
/* PWM Command - Manual Control */
#define NCT6694_PWM_CONTROL 0x01
#define NCT6694_PWM_CONTROL_SEL 0x00
diff --git a/drivers/i2c/busses/i2c-nct6694.c b/drivers/i2c/busses/i2c-nct6694.c
index 7d8ad997f6d2..7ee209a04d16 100644
--- a/drivers/i2c/busses/i2c-nct6694.c
+++ b/drivers/i2c/busses/i2c-nct6694.c
@@ -11,13 +11,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-/*
- * USB command module type for NCT6694 I2C controller.
- * This defines the module type used for communication with the NCT6694
- * I2C controller over the USB interface.
- */
-#define NCT6694_I2C_MOD 0x03
-
/* Command 00h - I2C Deliver */
#define NCT6694_I2C_DELIVER 0x00
#define NCT6694_I2C_DELIVER_SEL 0x00
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7192c9d1d268..8a715ec2f79f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1164,19 +1164,46 @@ config MFD_MENF21BMC
will be called menf21bmc.
config MFD_NCT6694
- tristate "Nuvoton NCT6694 support"
+ tristate
select MFD_CORE
+ help
+ Core MFD support for the Nuvoton NCT6694 peripheral expander.
+ This provides the common APIs and shared structures used by all
+ interfaces (USB, HIF) to access the NCT6694 hardware features
+ such as GPIO, I2C, CAN-FD, Watchdog, ADC, PWM, and RTC.
+
+ It is selected automatically by the transport interface drivers.
+
+config MFD_NCT6694_HIF
+ tristate "Nuvoton NCT6694 HIF (eSPI) interface support"
+ depends on HAS_IOPORT && ACPI
+ select MFD_NCT6694
+ select REGMAP_MMIO
+ help
+ This enables support for the Nuvoton NCT6694 peripheral expander
+ connected via the Host Interface (HIF) using eSPI transport.
+
+ The transport driver uses Super-I/O mapping and shared memory to
+ communicate with the NCT6694 firmware. Enable this option if you
+ are using the NCT6694 over an eSPI interface on an ACPI platform.
+
+ To compile this driver as a module, choose M here: the module
+ will be called nct6694-hif.
+
+config MFD_NCT6694_USB
+ tristate "Nuvoton NCT6694 USB interface support"
+ select MFD_NCT6694
depends on USB
help
- This enables support for the Nuvoton USB device NCT6694, which shares
- peripherals.
- The Nuvoton NCT6694 is a peripheral expander with 16 GPIO chips,
- 6 I2C controllers, 2 CANfd controllers, 2 Watchdog timers, ADC,
- PWM, and RTC.
- This driver provides core APIs to access the NCT6694 hardware
- monitoring and control features.
- Additional drivers must be enabled to utilize the specific
- functionalities of the device.
+ This enables support for the Nuvoton NCT6694 peripheral expander
+ connected via the USB interface.
+
+ The transport driver uses USB bulk and interrupt transfers to
+ communicate with the NCT6694 firmware. Enable this option if you
+ are using the NCT6694 via a USB connection.
+
+ To compile this driver as a module, choose M here: the module
+ will be called nct6694.
config MFD_OCELOT
tristate "Microsemi Ocelot External Control Support"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e75e8045c28a..4cee9b74978c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -124,7 +124,8 @@ obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
obj-$(CONFIG_MFD_PF1550) += pf1550.o
-obj-$(CONFIG_MFD_NCT6694) += nct6694.o
+obj-$(CONFIG_MFD_NCT6694_HIF) += nct6694-hif.o
+obj-$(CONFIG_MFD_NCT6694_USB) += nct6694.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
diff --git a/drivers/mfd/nct6694-hif.c b/drivers/mfd/nct6694-hif.c
new file mode 100644
index 000000000000..7560754e7481
--- /dev/null
+++ b/drivers/mfd/nct6694-hif.c
@@ -0,0 +1,634 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Nuvoton Technology Corp.
+ *
+ * Nuvoton NCT6694 host-interface (eSPI) transport driver.
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/nct6694.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/unaligned.h>
+
+#define DRVNAME "nct6694-hif"
+
+#define NCT6694_POLL_INTERVAL_US 10
+#define NCT6694_POLL_TIMEOUT_US 10000
+
+/*
+ * Super-I/O registers
+ */
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_LD_SHM 0x0F /* Logical device shared memory control */
+
+#define SIO_REG_SHM_ENABLE 0x30 /* Enable shared memory */
+#define SIO_REG_SHM_BASE_ADDR 0x60 /* Shared memory base address (2 bytes) */
+#define SIO_REG_SHM_IRQ_NR 0x70 /* Shared memory interrupt number */
+
+#define SIO_REG_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+#define SIO_REG_LOCK_KEY 0xAA /* Key to disable Super-I/O */
+
+#define SIO_NCT6694B_ID 0xD029
+#define SIO_NCT6694D_ID 0x5832
+
+/*
+ * Super-I/O Shared Memory Logical Device registers
+ */
+#define NCT6694_SHM_COFS_STS 0x2E
+#define NCT6694_SHM_COFS_STS_COFS4W BIT(7)
+
+#define NCT6694_SHM_COFS_CTL2 0x3B
+#define NCT6694_SHM_COFS_CTL2_COFS4W_IE BIT(3)
+
+#define NCT6694_SHM_INTR_STATUS 0x9C /* Interrupt status register (4 bytes) */
+
+enum nct6694_chips {
+ NCT6694B = 0,
+ NCT6694D,
+};
+
+enum nct6694_module_id {
+ NCT6694_GPIO0 = 0,
+ NCT6694_GPIO1,
+ NCT6694_GPIO2,
+ NCT6694_GPIO3,
+ NCT6694_GPIO4,
+ NCT6694_GPIO5,
+ NCT6694_GPIO6,
+ NCT6694_GPIO7,
+ NCT6694_GPIO8,
+ NCT6694_GPIO9,
+ NCT6694_GPIOA,
+ NCT6694_GPIOB,
+ NCT6694_GPIOC,
+ NCT6694_GPIOD,
+ NCT6694_GPIOE,
+ NCT6694_GPIOF,
+ NCT6694_I2C0,
+ NCT6694_I2C1,
+ NCT6694_I2C2,
+ NCT6694_I2C3,
+ NCT6694_I2C4,
+ NCT6694_I2C5,
+ NCT6694_CAN0,
+ NCT6694_CAN1,
+};
+
+struct __packed nct6694_msg {
+ struct nct6694_cmd_header cmd_header;
+ struct nct6694_response_header response_header;
+ unsigned char *data;
+};
+
+struct nct6694_sio_data {
+ enum nct6694_chips chip;
+ int sioreg; /* Super-I/O index port */
+};
+
+struct nct6694_hif_data {
+ struct regmap *regmap;
+ struct mutex msg_lock;
+ struct nct6694_sio_data *sio_data;
+ void __iomem *msg_base;
+ unsigned int shm_base;
+};
+
+static const char * const nct6694_chip_names[] = {
+ "NCT6694D",
+ "NCT6694B"
+};
+
+/*
+ * Super-I/O functions.
+ */
+static inline int superio_enter(struct nct6694_sio_data *sio_data)
+{
+ int ioreg = sio_data->sioreg;
+
+ /*
+ * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
+ */
+ if (!request_muxed_region(ioreg, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(SIO_REG_UNLOCK_KEY, ioreg);
+ outb(SIO_REG_UNLOCK_KEY, ioreg);
+
+ return 0;
+}
+
+static inline void superio_exit(struct nct6694_sio_data *sio_data)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(SIO_REG_LOCK_KEY, ioreg);
+
+ release_region(ioreg, 2);
+}
+
+static inline void superio_select(struct nct6694_sio_data *sio_data, int ld)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(SIO_REG_LDSEL, ioreg);
+ outb(ld, ioreg + 1);
+}
+
+static inline int superio_inb(struct nct6694_sio_data *sio_data, int reg)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(reg, ioreg);
+ return inb(ioreg + 1);
+}
+
+static inline int superio_inw(struct nct6694_sio_data *sio_data, int reg)
+{
+ int ioreg = sio_data->sioreg;
+ int val;
+
+ outb(reg++, ioreg);
+ val = inb(ioreg + 1) << 8;
+ outb(reg, ioreg);
+ val |= inb(ioreg + 1);
+
+ return val;
+}
+
+static inline void superio_outb(struct nct6694_sio_data *sio_data, int reg, u8 val)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(reg, ioreg);
+ outb(val, ioreg + 1);
+}
+
+static int nct6694_sio_find(struct nct6694_sio_data *sio_data, u8 sioreg)
+{
+ int ret;
+ u16 devid;
+
+ sio_data->sioreg = sioreg;
+
+ ret = superio_enter(sio_data);
+ if (ret)
+ return ret;
+
+ /* Check Chip ID */
+ devid = superio_inw(sio_data, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_NCT6694B_ID:
+ sio_data->chip = NCT6694B;
+ break;
+ case SIO_NCT6694D_ID:
+ sio_data->chip = NCT6694D;
+ break;
+ default:
+ pr_err("Unsupported device 0x%04x\n", devid);
+ goto err;
+ }
+
+ pr_info("Found %s at %#x\n", nct6694_chip_names[sio_data->chip], sio_data->sioreg);
+
+ superio_exit(sio_data);
+
+ return 0;
+
+err:
+ superio_exit(sio_data);
+ return -ENODEV;
+}
+
+static const struct mfd_cell_acpi_match nct6694_acpi_match_gpio[] = {
+ { .adr = NCT6694_GPIO0 },
+ { .adr = NCT6694_GPIO1 },
+ { .adr = NCT6694_GPIO2 },
+ { .adr = NCT6694_GPIO3 },
+ { .adr = NCT6694_GPIO4 },
+ { .adr = NCT6694_GPIO5 },
+ { .adr = NCT6694_GPIO6 },
+ { .adr = NCT6694_GPIO7 },
+ { .adr = NCT6694_GPIO8 },
+ { .adr = NCT6694_GPIO9 },
+ { .adr = NCT6694_GPIOA },
+ { .adr = NCT6694_GPIOB },
+ { .adr = NCT6694_GPIOC },
+ { .adr = NCT6694_GPIOD },
+ { .adr = NCT6694_GPIOE },
+ { .adr = NCT6694_GPIOF },
+};
+
+static const struct mfd_cell_acpi_match nct6694_acpi_match_i2c[] = {
+ { .adr = NCT6694_I2C0 },
+ { .adr = NCT6694_I2C1 },
+ { .adr = NCT6694_I2C2 },
+ { .adr = NCT6694_I2C3 },
+ { .adr = NCT6694_I2C4 },
+ { .adr = NCT6694_I2C5 },
+};
+
+static const struct mfd_cell_acpi_match nct6694_acpi_match_can[] = {
+ { .adr = NCT6694_CAN0 },
+ { .adr = NCT6694_CAN1 },
+};
+
+static const struct mfd_cell nct6694_devs[] = {
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 0, &nct6694_acpi_match_gpio[0]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 1, &nct6694_acpi_match_gpio[1]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 2, &nct6694_acpi_match_gpio[2]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 3, &nct6694_acpi_match_gpio[3]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 4, &nct6694_acpi_match_gpio[4]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 5, &nct6694_acpi_match_gpio[5]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 6, &nct6694_acpi_match_gpio[6]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 7, &nct6694_acpi_match_gpio[7]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 8, &nct6694_acpi_match_gpio[8]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 9, &nct6694_acpi_match_gpio[9]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 10, &nct6694_acpi_match_gpio[10]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 11, &nct6694_acpi_match_gpio[11]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 12, &nct6694_acpi_match_gpio[12]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 13, &nct6694_acpi_match_gpio[13]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 14, &nct6694_acpi_match_gpio[14]),
+ MFD_CELL_ACPI("nct6694-gpio", NULL, NULL, 0, 15, &nct6694_acpi_match_gpio[15]),
+
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 0, &nct6694_acpi_match_i2c[0]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 1, &nct6694_acpi_match_i2c[1]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 2, &nct6694_acpi_match_i2c[2]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 3, &nct6694_acpi_match_i2c[3]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 4, &nct6694_acpi_match_i2c[4]),
+ MFD_CELL_ACPI("nct6694-i2c", NULL, NULL, 0, 5, &nct6694_acpi_match_i2c[5]),
+
+ MFD_CELL_ACPI("nct6694-canfd", NULL, NULL, 0, 0, &nct6694_acpi_match_can[0]),
+ MFD_CELL_ACPI("nct6694-canfd", NULL, NULL, 0, 1, &nct6694_acpi_match_can[1]),
+};
+
+static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)
+{
+ switch (err_status) {
+ case NCT6694_NO_ERROR:
+ return 0;
+ case NCT6694_NOT_SUPPORT_ERROR:
+ dev_err(nct6694->dev, "Command is not supported!\n");
+ break;
+ case NCT6694_NO_RESPONSE_ERROR:
+ dev_warn(nct6694->dev, "Command received no response!\n");
+ break;
+ case NCT6694_TIMEOUT_ERROR:
+ dev_warn(nct6694->dev, "Command timed out!\n");
+ break;
+ case NCT6694_PENDING:
+ dev_err(nct6694->dev, "Command is pending!\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return -EIO;
+}
+
+static int nct6694_xfer_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ u8 hctrl, void *buf)
+{
+ struct nct6694_hif_data *hdata = nct6694->priv;
+ void __iomem *hdr = hdata->msg_base + offsetof(struct nct6694_msg, cmd_header);
+ struct nct6694_cmd_header cmd = *cmd_hd;
+ struct nct6694_response_header resp;
+ u16 len = le16_to_cpu(cmd.len);
+ u8 status;
+ int ret;
+
+ guard(mutex)(&hdata->msg_lock);
+
+ /* Wait until the previous command is completed */
+ ret = readb_poll_timeout(hdr + offsetof(struct nct6694_cmd_header, hctrl),
+ status, status == 0, NCT6694_POLL_INTERVAL_US,
+ NCT6694_POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ /*
+ * Write cmd header fields, but skip hctrl — writing to it triggers
+ * firmware command processing and must be deferred until data is ready.
+ */
+ memcpy_toio(hdr, &cmd, offsetof(struct nct6694_cmd_header, hctrl));
+ memcpy_toio(hdr + offsetof(struct nct6694_cmd_header, rsv2), &cmd.rsv2,
+ sizeof(cmd) - offsetof(struct nct6694_cmd_header, rsv2));
+
+ if (hctrl == NCT6694_HCTRL_SET && len)
+ memcpy_toio(hdata->msg_base + offsetof(struct nct6694_msg, data),
+ buf, len);
+
+ /* Write hctrl last to trigger command processing */
+ writeb(hctrl, hdr + offsetof(struct nct6694_cmd_header, hctrl));
+
+ ret = readb_poll_timeout(hdr + offsetof(struct nct6694_cmd_header, hctrl),
+ status, status == 0, NCT6694_POLL_INTERVAL_US,
+ NCT6694_POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ memcpy_fromio(&resp, hdata->msg_base + offsetof(struct nct6694_msg, response_header),
+ sizeof(resp));
+
+ ret = nct6694_response_err_handling(nct6694, resp.sts);
+ if (ret)
+ return ret;
+
+ if (le16_to_cpu(resp.len))
+ memcpy_fromio(buf, hdata->msg_base + offsetof(struct nct6694_msg, data),
+ min(len, le16_to_cpu(resp.len)));
+
+ return 0;
+}
+
+/**
+ * nct6694_hif_read_msg() - Send a command and read response data via HIF
+ * @nct6694: NCT6694 device data
+ * @cmd_hd: command header
+ * @buf: buffer to store response data
+ *
+ * Return: 0 on success or negative errno on failure.
+ */
+static int nct6694_hif_read_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
+{
+ struct nct6694_hif_data *hdata = nct6694->priv;
+
+ if (cmd_hd->mod == NCT6694_RPT_MOD)
+ return regmap_bulk_read(hdata->regmap,
+ le16_to_cpu(cmd_hd->offset),
+ buf, le16_to_cpu(cmd_hd->len));
+ return nct6694_xfer_msg(nct6694, cmd_hd, NCT6694_HCTRL_GET, buf);
+}
+
+/**
+ * nct6694_hif_write_msg() - Send a command with data payload via HIF
+ * @nct6694: NCT6694 device data
+ * @cmd_hd: command header
+ * @buf: buffer containing data to send
+ *
+ * Return: 0 on success or negative errno on failure.
+ */
+static int nct6694_hif_write_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
+{
+ struct nct6694_hif_data *hdata = nct6694->priv;
+
+ if (cmd_hd->mod == NCT6694_RPT_MOD)
+ return regmap_bulk_write(hdata->regmap,
+ le16_to_cpu(cmd_hd->offset),
+ buf, le16_to_cpu(cmd_hd->len));
+ return nct6694_xfer_msg(nct6694, cmd_hd, NCT6694_HCTRL_SET, buf);
+}
+
+static const struct regmap_config nct6694_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_stride = 1,
+};
+
+static irqreturn_t nct6694_irq_handler(int irq, void *data)
+{
+ struct nct6694 *nct6694 = data;
+ struct nct6694_hif_data *hdata = nct6694->priv;
+ u8 reg_data[4];
+ u32 intr_status;
+ int ret;
+
+ /* Check interrupt status is set */
+ if (!(inb(hdata->shm_base + NCT6694_SHM_COFS_STS) & NCT6694_SHM_COFS_STS_COFS4W))
+ return IRQ_NONE;
+
+ /* Clear interrupt status */
+ outb(NCT6694_SHM_COFS_STS_COFS4W, hdata->shm_base + NCT6694_SHM_COFS_STS);
+
+ ret = regmap_bulk_read(hdata->regmap, NCT6694_SHM_INTR_STATUS,
+ reg_data, ARRAY_SIZE(reg_data));
+ if (ret)
+ return IRQ_NONE;
+
+ intr_status = get_unaligned_le32(reg_data);
+
+ while (intr_status) {
+ int irq = __ffs(intr_status);
+
+ generic_handle_irq_safe(irq_find_mapping(nct6694->domain, irq));
+ intr_status &= ~BIT(irq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void nct6694_irq_release(void *data)
+{
+ struct nct6694 *nct6694 = data;
+ struct nct6694_hif_data *hdata = nct6694->priv;
+ unsigned char cofs_ctl2;
+
+ /* Disable SIRQ interrupt */
+ cofs_ctl2 = inb(hdata->shm_base + NCT6694_SHM_COFS_CTL2);
+ cofs_ctl2 &= ~NCT6694_SHM_COFS_CTL2_COFS4W_IE;
+ outb(cofs_ctl2, hdata->shm_base + NCT6694_SHM_COFS_CTL2);
+}
+
+static int nct6694_irq_init(struct nct6694 *nct6694, int irq)
+{
+ struct nct6694_hif_data *hdata = nct6694->priv;
+ struct nct6694_sio_data *sio_data = hdata->sio_data;
+ unsigned char cofs_ctl2;
+
+ /* Set SIRQ number */
+ superio_enter(sio_data);
+ superio_select(sio_data, SIO_REG_LD_SHM);
+ if (!superio_inb(sio_data, SIO_REG_SHM_ENABLE)) {
+ superio_exit(sio_data);
+ return -EIO;
+ }
+ hdata->shm_base = superio_inw(sio_data, SIO_REG_SHM_BASE_ADDR);
+
+ superio_outb(sio_data, SIO_REG_SHM_IRQ_NR, irq);
+
+ superio_exit(sio_data);
+
+ /* Enable SIRQ interrupt */
+ cofs_ctl2 = inb(hdata->shm_base + NCT6694_SHM_COFS_CTL2);
+ cofs_ctl2 |= NCT6694_SHM_COFS_CTL2_COFS4W_IE;
+ outb(cofs_ctl2, hdata->shm_base + NCT6694_SHM_COFS_CTL2);
+
+ return 0;
+}
+
+static void nct6694_irq_enable(struct irq_data *data)
+{
+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ guard(spinlock_irqsave)(&nct6694->irq_lock);
+
+ nct6694->irq_enable |= BIT(hwirq);
+}
+
+static void nct6694_irq_disable(struct irq_data *data)
+{
+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ guard(spinlock_irqsave)(&nct6694->irq_lock);
+
+ nct6694->irq_enable &= ~BIT(hwirq);
+}
+
+static const struct irq_chip nct6694_irq_chip = {
+ .name = "nct6694-irq",
+ .flags = IRQCHIP_SKIP_SET_WAKE,
+ .irq_enable = nct6694_irq_enable,
+ .irq_disable = nct6694_irq_disable,
+};
+
+static void nct6694_irq_domain_remove(void *data)
+{
+ struct nct6694 *nct6694 = data;
+
+ irq_domain_remove(nct6694->domain);
+}
+
+static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+ struct nct6694 *nct6694 = d->host_data;
+
+ irq_set_chip_data(irq, nct6694);
+ irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops nct6694_irq_domain_ops = {
+ .map = nct6694_irq_domain_map,
+ .unmap = nct6694_irq_domain_unmap,
+};
+
+static const u8 sio_addrs[] = { 0x2e, 0x4e };
+
+static int nct6694_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nct6694_sio_data *sio_data;
+ struct nct6694_hif_data *hdata;
+ struct nct6694 *data;
+ void __iomem *rpt_base, *msg_base;
+ int ret, i, irq;
+
+ sio_data = devm_kzalloc(dev, sizeof(*sio_data), GFP_KERNEL);
+ if (!sio_data)
+ return -ENOMEM;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
+ if (!hdata)
+ return -ENOMEM;
+
+ rpt_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rpt_base))
+ return PTR_ERR(rpt_base);
+ msg_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(msg_base))
+ return PTR_ERR(msg_base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ for (i = 0; i < ARRAY_SIZE(sio_addrs); i++) {
+ ret = nct6694_sio_find(sio_data, sio_addrs[i]);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return ret;
+
+ hdata->sio_data = sio_data;
+ hdata->msg_base = msg_base;
+ hdata->regmap = devm_regmap_init_mmio(dev, rpt_base,
+ &nct6694_regmap_config);
+ if (IS_ERR(hdata->regmap))
+ return PTR_ERR(hdata->regmap);
+
+ data->dev = dev;
+ data->priv = hdata;
+ data->read_msg = nct6694_hif_read_msg;
+ data->write_msg = nct6694_hif_write_msg;
+
+ spin_lock_init(&data->irq_lock);
+
+ data->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
+ &nct6694_irq_domain_ops,
+ data);
+ if (!data->domain)
+ return -ENODEV;
+
+ ret = devm_add_action_or_reset(dev, nct6694_irq_domain_remove, data);
+ if (ret)
+ return ret;
+
+ ret = nct6694_irq_init(data, irq);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, nct6694_irq_release, data);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, nct6694_irq_handler,
+ IRQF_ONESHOT | IRQF_SHARED,
+ dev_name(dev), data);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(dev, &hdata->msg_lock);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, data);
+
+ return devm_mfd_add_devices(dev, 0, nct6694_devs, ARRAY_SIZE(nct6694_devs), NULL, 0, NULL);
+}
+
+static const struct acpi_device_id nct6694_acpi_ids[] = {
+ { "NTN0538", 0 },
+ {}
+};
+
+static struct platform_driver nct6694_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .acpi_match_table = nct6694_acpi_ids,
+ },
+ .probe = nct6694_probe,
+};
+module_platform_driver(nct6694_driver);
+
+MODULE_DESCRIPTION("Nuvoton NCT6694 host-interface transport driver");
+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/nct6694.c b/drivers/mfd/nct6694.c
index 8ce2c4985aab..903a0a7f0694 100644
--- a/drivers/mfd/nct6694.c
+++ b/drivers/mfd/nct6694.c
@@ -21,6 +21,27 @@
#include <linux/spinlock.h>
#include <linux/usb.h>
+#define NCT6694_VENDOR_ID 0x0416
+#define NCT6694_PRODUCT_ID 0x200B
+#define NCT6694_INT_IN_EP 0x81
+#define NCT6694_BULK_IN_EP 0x02
+#define NCT6694_BULK_OUT_EP 0x03
+
+#define NCT6694_URB_TIMEOUT 1000
+
+union __packed nct6694_usb_msg {
+ struct nct6694_cmd_header cmd_header;
+ struct nct6694_response_header response_header;
+};
+
+struct nct6694_usb_data {
+ struct mutex access_lock;
+ struct urb *int_in_urb;
+ struct usb_device *udev;
+ union nct6694_usb_msg *usb_msg;
+ __le32 *int_buffer;
+};
+
static const struct mfd_cell nct6694_devs[] = {
MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 0),
MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 1),
@@ -57,7 +78,8 @@ static const struct mfd_cell nct6694_devs[] = {
MFD_CELL_NAME("nct6694-rtc"),
};
-static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)
+static int nct6694_usb_err_handling(struct nct6694 *nct6694,
+ unsigned char err_status)
{
switch (err_status) {
case NCT6694_NO_ERROR:
@@ -82,7 +104,7 @@ static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char
}
/**
- * nct6694_read_msg() - Read message from NCT6694 device
+ * nct6694_usb_read_msg() - Read message from NCT6694 device via USB
* @nct6694: NCT6694 device pointer
* @cmd_hd: command header structure
* @buf: buffer to store the response data
@@ -93,13 +115,16 @@ static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char
*
* Return: Negative value on error or 0 on success.
*/
-int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
+static int nct6694_usb_read_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
{
- union nct6694_usb_msg *msg = nct6694->usb_msg;
- struct usb_device *udev = nct6694->udev;
+ struct nct6694_usb_data *udata = nct6694->priv;
+ union nct6694_usb_msg *msg = udata->usb_msg;
+ struct usb_device *udev = udata->udev;
int tx_len, rx_len, ret;
- guard(mutex)(&nct6694->access_lock);
+ guard(mutex)(&udata->access_lock);
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
msg->cmd_header.hctrl = NCT6694_HCTRL_GET;
@@ -128,12 +153,11 @@ int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *c
return -EIO;
}
- return nct6694_response_err_handling(nct6694, msg->response_header.sts);
+ return nct6694_usb_err_handling(nct6694, msg->response_header.sts);
}
-EXPORT_SYMBOL_GPL(nct6694_read_msg);
/**
- * nct6694_write_msg() - Write message to NCT6694 device
+ * nct6694_usb_write_msg() - Write message to NCT6694 device via USB
* @nct6694: NCT6694 device pointer
* @cmd_hd: command header structure
* @buf: buffer containing the data to be sent
@@ -143,13 +167,16 @@ EXPORT_SYMBOL_GPL(nct6694_read_msg);
*
* Return: Negative value on error or 0 on success.
*/
-int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
+static int nct6694_usb_write_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
{
- union nct6694_usb_msg *msg = nct6694->usb_msg;
- struct usb_device *udev = nct6694->udev;
+ struct nct6694_usb_data *udata = nct6694->priv;
+ union nct6694_usb_msg *msg = udata->usb_msg;
+ struct usb_device *udev = udata->udev;
int tx_len, rx_len, ret;
- guard(mutex)(&nct6694->access_lock);
+ guard(mutex)(&udata->access_lock);
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
msg->cmd_header.hctrl = NCT6694_HCTRL_SET;
@@ -184,9 +211,8 @@ int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *
return -EIO;
}
- return nct6694_response_err_handling(nct6694, msg->response_header.sts);
+ return nct6694_usb_err_handling(nct6694, msg->response_header.sts);
}
-EXPORT_SYMBOL_GPL(nct6694_write_msg);
static void usb_int_callback(struct urb *urb)
{
@@ -276,6 +302,7 @@ static int nct6694_usb_probe(struct usb_interface *iface,
struct usb_endpoint_descriptor *int_endpoint;
struct usb_host_interface *interface;
struct device *dev = &iface->dev;
+ struct nct6694_usb_data *udata;
struct nct6694 *nct6694;
int ret;
@@ -283,18 +310,28 @@ static int nct6694_usb_probe(struct usb_interface *iface,
if (!nct6694)
return -ENOMEM;
- nct6694->usb_msg = devm_kzalloc(dev, sizeof(union nct6694_usb_msg), GFP_KERNEL);
- if (!nct6694->usb_msg)
+ udata = devm_kzalloc(dev, sizeof(*udata), GFP_KERNEL);
+ if (!udata)
+ return -ENOMEM;
+
+ udata->usb_msg = devm_kzalloc(dev, sizeof(*udata->usb_msg), GFP_KERNEL);
+ if (!udata->usb_msg)
return -ENOMEM;
- nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL);
- if (!nct6694->int_buffer)
+ udata->int_buffer = devm_kzalloc(dev, sizeof(*udata->int_buffer), GFP_KERNEL);
+ if (!udata->int_buffer)
return -ENOMEM;
- nct6694->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!nct6694->int_in_urb)
+ udata->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!udata->int_in_urb)
return -ENOMEM;
+ udata->udev = udev;
+
+ nct6694->priv = udata;
+ nct6694->read_msg = nct6694_usb_read_msg;
+ nct6694->write_msg = nct6694_usb_write_msg;
+
nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
&nct6694_irq_domain_ops,
nct6694);
@@ -304,11 +341,10 @@ static int nct6694_usb_probe(struct usb_interface *iface,
}
nct6694->dev = dev;
- nct6694->udev = udev;
spin_lock_init(&nct6694->irq_lock);
- ret = devm_mutex_init(dev, &nct6694->access_lock);
+ ret = devm_mutex_init(dev, &udata->access_lock);
if (ret)
goto err_irq_domain;
@@ -320,11 +356,11 @@ static int nct6694_usb_probe(struct usb_interface *iface,
goto err_irq_domain;
}
- usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
- nct6694->int_buffer, sizeof(*nct6694->int_buffer), usb_int_callback,
+ usb_fill_int_urb(udata->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
+ udata->int_buffer, sizeof(*udata->int_buffer), usb_int_callback,
nct6694, int_endpoint->bInterval);
- ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);
+ ret = usb_submit_urb(udata->int_in_urb, GFP_KERNEL);
if (ret)
goto err_irq_domain;
@@ -337,21 +373,22 @@ static int nct6694_usb_probe(struct usb_interface *iface,
return 0;
err_mfd:
- usb_kill_urb(nct6694->int_in_urb);
+ usb_kill_urb(udata->int_in_urb);
err_irq_domain:
irq_domain_remove(nct6694->domain);
err_urb:
- usb_free_urb(nct6694->int_in_urb);
+ usb_free_urb(udata->int_in_urb);
return ret;
}
static void nct6694_usb_disconnect(struct usb_interface *iface)
{
struct nct6694 *nct6694 = usb_get_intfdata(iface);
+ struct nct6694_usb_data *udata = nct6694->priv;
- usb_kill_urb(nct6694->int_in_urb);
+ usb_kill_urb(udata->int_in_urb);
irq_domain_remove(nct6694->domain);
- usb_free_urb(nct6694->int_in_urb);
+ usb_free_urb(udata->int_in_urb);
}
static const struct usb_device_id nct6694_ids[] = {
diff --git a/drivers/net/can/usb/nct6694_canfd.c b/drivers/net/can/usb/nct6694_canfd.c
index 29282c56430f..05db00455f63 100644
--- a/drivers/net/can/usb/nct6694_canfd.c
+++ b/drivers/net/can/usb/nct6694_canfd.c
@@ -17,12 +17,6 @@
#define DEVICE_NAME "nct6694-canfd"
-/* USB command module type for NCT6694 CANfd controller.
- * This defines the module type used for communication with the NCT6694
- * CANfd controller over the USB interface.
- */
-#define NCT6694_CANFD_MOD 0x05
-
/* Command 00h - CAN Setting and Initialization */
#define NCT6694_CANFD_SETTING 0x00
#define NCT6694_CANFD_SETTING_ACTIVE_CTRL1 BIT(0)
diff --git a/drivers/rtc/rtc-nct6694.c b/drivers/rtc/rtc-nct6694.c
index 35401a0d9cf5..c06902f150c9 100644
--- a/drivers/rtc/rtc-nct6694.c
+++ b/drivers/rtc/rtc-nct6694.c
@@ -14,13 +14,6 @@
#include <linux/rtc.h>
#include <linux/slab.h>
-/*
- * USB command module type for NCT6694 RTC controller.
- * This defines the module type used for communication with the NCT6694
- * RTC controller over the USB interface.
- */
-#define NCT6694_RTC_MOD 0x08
-
/* Command 00h - RTC Time */
#define NCT6694_RTC_TIME 0x0000
#define NCT6694_RTC_TIME_SEL 0x00
diff --git a/drivers/watchdog/nct6694_wdt.c b/drivers/watchdog/nct6694_wdt.c
index 2b4b804a1739..847d8f1d1830 100644
--- a/drivers/watchdog/nct6694_wdt.c
+++ b/drivers/watchdog/nct6694_wdt.c
@@ -19,13 +19,6 @@
#define NCT6694_WDT_MAX_DEVS 2
-/*
- * USB command module type for NCT6694 WDT controller.
- * This defines the module type used for communication with the NCT6694
- * WDT controller over the USB interface.
- */
-#define NCT6694_WDT_MOD 0x07
-
/* Command 00h - WDT Setup */
#define NCT6694_WDT_SETUP 0x00
#define NCT6694_WDT_SETUP_SEL(idx) (idx ? 0x01 : 0x00)
diff --git a/include/linux/mfd/nct6694.h b/include/linux/mfd/nct6694.h
index 496da72949d9..ff0814dc82d4 100644
--- a/include/linux/mfd/nct6694.h
+++ b/include/linux/mfd/nct6694.h
@@ -2,7 +2,8 @@
/*
* Copyright (C) 2025 Nuvoton Technology Corp.
*
- * Nuvoton NCT6694 USB transaction and data structure.
+ * Nuvoton NCT6694 core definitions shared by all transport drivers
+ * and sub-device drivers.
*/
#ifndef __MFD_NCT6694_H
@@ -12,16 +13,17 @@
#include <linux/spinlock.h>
#include <linux/types.h>
-#define NCT6694_VENDOR_ID 0x0416
-#define NCT6694_PRODUCT_ID 0x200B
-#define NCT6694_INT_IN_EP 0x81
-#define NCT6694_BULK_IN_EP 0x02
-#define NCT6694_BULK_OUT_EP 0x03
-
#define NCT6694_HCTRL_SET 0x40
#define NCT6694_HCTRL_GET 0x80
-#define NCT6694_URB_TIMEOUT 1000
+#define NCT6694_HWMON_MOD 0x00
+#define NCT6694_PWM_MOD 0x01
+#define NCT6694_I2C_MOD 0x03
+#define NCT6694_CANFD_MOD 0x05
+#define NCT6694_WDT_MOD 0x07
+#define NCT6694_RTC_MOD 0x08
+#define NCT6694_RPT_MOD 0xFF
+#define NCT6694_GPIO_MOD NCT6694_RPT_MOD
enum nct6694_irq_id {
NCT6694_IRQ_GPIO0 = 0,
@@ -79,24 +81,33 @@ struct __packed nct6694_response_header {
__le16 len;
};
-union __packed nct6694_usb_msg {
- struct nct6694_cmd_header cmd_header;
- struct nct6694_response_header response_header;
-};
-
struct nct6694 {
struct device *dev;
struct irq_domain *domain;
- struct mutex access_lock;
spinlock_t irq_lock;
- struct urb *int_in_urb;
- struct usb_device *udev;
- union nct6694_usb_msg *usb_msg;
- __le32 *int_buffer;
unsigned int irq_enable;
+
+ void *priv;
+ int (*read_msg)(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf);
+ int (*write_msg)(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf);
};
-int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf);
-int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf);
+static inline int nct6694_read_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
+{
+ return nct6694->read_msg(nct6694, cmd_hd, buf);
+}
+
+static inline int nct6694_write_msg(struct nct6694 *nct6694,
+ const struct nct6694_cmd_header *cmd_hd,
+ void *buf)
+{
+ return nct6694->write_msg(nct6694, cmd_hd, buf);
+}
#endif
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net] xfrm_user: fix info leak in build_mapping()
From: Greg Kroah-Hartman @ 2026-04-08 5:42 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Steffen Klassert, netdev, linux-kernel, Herbert Xu,
David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman
In-Reply-To: <20260407181211.5419f2cc@kernel.org>
On Tue, Apr 07, 2026 at 06:12:11PM -0700, Jakub Kicinski wrote:
> On Tue, 7 Apr 2026 07:51:15 +0200 Greg Kroah-Hartman wrote:
> > > > I guess nlmsg_append() would work? It tries to do some zeroing out for
> > > > alignment for some reason...
> > > >
> > > > Want me to do that? I don't have a way to test any of this, I just
> > > > found it using some static code analysis tools that looked at holes in
> > > > structures.
> > >
> > > Do you have any more Netlink leaks in the queue? If you do let's do it,
> > > if you don't we can wait until the next victi^w patch to arrive.
> >
> > I do not have any more, sorry. So is it worth it for just these 2?
> > Your call :)
>
> These are fine. I would have applied but I think Steffen will take them
> via the ipsec tree first (LMK if that's not the plan, Steffen)
Thanks!
^ permalink raw reply
* Re: [Intel-wired-lan] [PATCH net] ice: fix VF queue configuration with low MTU values
From: Michal Swiatkowski @ 2026-04-08 5:42 UTC (permalink / raw)
To: Jacob Keller
Cc: Jose Ignacio Tornos Martinez, intel-wired-lan, netdev,
Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Aleksandr Loktionov,
Michal Swiatkowski, Dave Ertman, Michal Kubiak, stable
In-Reply-To: <88c37486-70aa-4109-a5b1-ee74cce23bc5@intel.com>
On Tue, Apr 07, 2026 at 03:50:32PM -0700, Jacob Keller wrote:
> On 4/6/2026 7:56 AM, Jose Ignacio Tornos Martinez wrote:
> > The ice driver's VF queue configuration validation rejects
> > databuffer_size values below 1024 bytes, which prevents VFs from
> > using MTU values below 871 bytes.
> >
> > The iavf driver calculates databuffer_size based on the MTU using:
> > databuffer_size = ALIGN(MTU + LIBETH_RX_LL_LEN, 128)
> >
> > where LIBETH_RX_LL_LEN = 26 (ETH_HLEN + 2*VLAN_HLEN + ETH_FCS_LEN).
> >
> > For MTU values below 871:
> > MTU 870: 870 + 26 = 896, aligned to 128 = 896 (< 1024, rejected)
> > MTU 871: 871 + 26 = 897, aligned to 128 = 1024 (>= 1024, accepted)
> >
> > The 1024-byte minimum seems unnecessarily restrictive, because the hardware
> > supports databuffer_size as low as 128 bytes (the alignment boundary),
> > which should allow MTU values down to the standard minimum of 68 bytes.
> >
> > I haven't found the reason why the limit was configured in the commit
> > 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message"), so
> > with no more information and since it is working, change the minimum
> > databuffer_size validation from 1024 to 128 bytes to allow standard low
> > MTU values while still preventing invalid configurations.
> >
>
> I dug through some of our internal history and found that there was no
> justification on why 1024 was chosen.
I can't remember why I choose 1024 and didn't mention it in commit
message. I agree that it is unnecessarily restrictive.
Reviewed-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
>
> I agree with your assessment that the value of 128 makes the most sense
> as it is the actual hardware minimum. I wonder if we used to always use
> data buffer sizes of 1024 before the conversion to libeth. Either way, I
> think it makes sense to allow smaller buffers since the modern iAVF will
> request them.
>
> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
>
> > Fixes: 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message")
> > cc: stable@vger.kernel.org
> > Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> > ---
> > drivers/net/ethernet/intel/ice/virt/queues.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c
> > index f73d5a3e83d4..31be2f76181c 100644
> > --- a/drivers/net/ethernet/intel/ice/virt/queues.c
> > +++ b/drivers/net/ethernet/intel/ice/virt/queues.c
> > @@ -840,7 +840,7 @@ int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
> >
> > if (qpi->rxq.databuffer_size != 0 &&
> > (qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
> > - qpi->rxq.databuffer_size < 1024))
> > + qpi->rxq.databuffer_size < 128))
> > goto error_param;
> >
> > ring->rx_buf_len = qpi->rxq.databuffer_size;
^ permalink raw reply
* Re: [PATCH net-next v6 08/14] bnxt: use snapshot in bnxt_cfg_rx_mode
From: Michael Chan @ 2026-04-08 5:48 UTC (permalink / raw)
To: Stanislav Fomichev; +Cc: netdev, davem, edumazet, kuba, pabeni, Pavan Chebbi
In-Reply-To: <20260407153101.3694714-9-sdf@fomichev.me>
[-- Attachment #1: Type: text/plain, Size: 778 bytes --]
On Tue, Apr 7, 2026 at 8:31 AM Stanislav Fomichev <sdf.kernel@gmail.com> wrote:
>
> With the introduction of ndo_set_rx_mode_async (as discussed in [1])
> we can call bnxt_cfg_rx_mode directly. Convert bnxt_cfg_rx_mode to
> use uc/mc snapshots and move its call in bnxt_sp_task to the
> section that resets BNXT_STATE_IN_SP_TASK. Switch to direct call in
> bnxt_set_rx_mode.
>
> Link: https://lore.kernel.org/netdev/CACKFLi=5vj8hPqEUKDd8RTw3au5G+zRgQEqjF+6NZnyoNm90KA@mail.gmail.com/ [1]
>
> Cc: Michael Chan <michael.chan@broadcom.com>
> Cc: Pavan Chebbi <pavan.chebbi@broadcom.com>
> Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
This version with fixes for AI reported issues looks good to me.
Reviewed-by: Michael Chan <michael.chan@broadcom.com>
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply
* [GIT PULL v2 net-next] netfilter: updates for net-next
From: Florian Westphal @ 2026-04-08 6:04 UTC (permalink / raw)
To: netdev
Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
netfilter-devel, pablo
No changes since v1, I only dropped the last patch (13/13). This is also
why I am not resending the individual patches again.
The following PR contains Netfilter updates for *net-next*:
1) Fix ancient sparse warnings in nf conntrack nat modules, from
Sun Jian.
2) Fix typo in enum description, from Jelle van der Waa.
3) remove redundant refetch of netns pointer in nf_conntrack_sip.
4) add a deprecation warning for dccp match.
We can extend the deadline later if needed, but plan atm is to
remove the feature.
5) remove nf_conntrack_h323 debug code that can read out-of-bounds
with malformed messages. This code was commented out, but better
remove this.
6+7) add more netlink policy validations in netfilter.
This could theoretically cause issues when a client sends e.g.
unsupported feature flags that were previously ignored, so we
may have to relax some changes. For now, try to be stricter and
reject upfront.
8+9) minor code cleanup in nft_set_pipapo (an nftables set backend).
10) Add nftables matching support fro double-tagged vlan and pppoe
frames, from Pablo Neira Ayuso.
11) Fix up indentation of debug messages in nf_conntrack_h323 conntrack
helper, from David Laight.
12) Add a helper to iterate to next flow action and bail out if the
maximum number of actions is reached, also from Pablo.
Please, pull these changes from:
The following changes since commit b3e69fc3196fc421e26196e7792f17b0463edc6f:
Merge branch 'net-pull-gso-packet-headers-in-core-stack' (2026-04-07 19:02:18 -0700)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next.git tags/nf-next-26-04-08
for you to fetch changes up to c6f85577584b5f8414141ae389e974b8ca6a698b:
netfilter: nf_tables_offload: add nft_flow_action_entry_next() and use it (2026-04-08 07:51:31 +0200)
----------------------------------------------------------------
netfilter pull request nf-next-26-04-08
----------------------------------------------------------------
David Laight (1):
netfilter: nf_conntrack_h323: Correct indentation when H323_TRACE defined
Florian Westphal (7):
netfilter: nf_conntrack_sip: remove net variable shadowing
netfilter: add deprecation warning for dccp support
netfilter: nf_conntrack_h323: remove unreliable debug code in decode_octstr
netfilter: add more netlink-based policy range checks
netfilter: nf_tables: add netlink policy based cap on registers
netfilter: nft_set_pipapo: increment data in one step
netfilter: nft_set_pipapo_avx2: remove redundant loop in lookup_slow
Jelle van der Waa (1):
netfilter: nf_tables: Fix typo in enum description
Pablo Neira Ayuso (2):
netfilter: nft_meta: add double-tagged vlan and pppoe support
netfilter: nf_tables_offload: add nft_flow_action_entry_next() and use
it
Sun Jian (1):
netfilter: use function typedefs for __rcu NAT helper hook pointers
include/linux/netfilter/nf_conntrack_amanda.h | 15 +++--
include/linux/netfilter/nf_conntrack_ftp.h | 17 +++---
include/linux/netfilter/nf_conntrack_irc.h | 15 +++--
include/linux/netfilter/nf_conntrack_snmp.h | 11 ++--
include/linux/netfilter/nf_conntrack_tftp.h | 9 ++-
include/net/netfilter/nf_tables.h | 4 ++
include/net/netfilter/nf_tables_ipv4.h | 17 ++++--
include/net/netfilter/nf_tables_ipv6.h | 16 +++--
include/net/netfilter/nf_tables_offload.h | 10 ++++
include/uapi/linux/netfilter/nf_tables.h | 6 +-
net/netfilter/ipset/ip_set_core.c | 2 +-
net/netfilter/nf_conntrack_amanda.c | 10 +---
net/netfilter/nf_conntrack_ftp.c | 10 +---
net/netfilter/nf_conntrack_h323_asn1.c | 45 ++++++--------
net/netfilter/nf_conntrack_irc.c | 10 +---
net/netfilter/nf_conntrack_sip.c | 3 +-
net/netfilter/nf_conntrack_snmp.c | 7 +--
net/netfilter/nf_conntrack_tftp.c | 7 +--
net/netfilter/nf_dup_netdev.c | 5 +-
net/netfilter/nf_tables_api.c | 20 +++++--
net/netfilter/nf_tables_core.c | 2 +-
net/netfilter/nfnetlink_acct.c | 2 +-
net/netfilter/nfnetlink_cthelper.c | 2 +-
net/netfilter/nfnetlink_hook.c | 2 +-
net/netfilter/nfnetlink_log.c | 4 +-
net/netfilter/nfnetlink_osf.c | 2 +-
net/netfilter/nfnetlink_queue.c | 2 +-
net/netfilter/nft_bitwise.c | 6 +-
net/netfilter/nft_byteorder.c | 4 +-
net/netfilter/nft_cmp.c | 2 +-
net/netfilter/nft_compat.c | 2 +-
net/netfilter/nft_connlimit.c | 2 +-
net/netfilter/nft_ct.c | 6 +-
net/netfilter/nft_dynset.c | 3 +-
net/netfilter/nft_exthdr.c | 9 ++-
net/netfilter/nft_fib.c | 2 +-
net/netfilter/nft_hash.c | 4 +-
net/netfilter/nft_immediate.c | 6 +-
net/netfilter/nft_inner.c | 2 +-
net/netfilter/nft_limit.c | 2 +-
net/netfilter/nft_log.c | 2 +-
net/netfilter/nft_lookup.c | 4 +-
net/netfilter/nft_meta.c | 58 ++++++++++++++++++-
net/netfilter/nft_numgen.c | 2 +-
net/netfilter/nft_objref.c | 2 +-
net/netfilter/nft_osf.c | 4 +-
net/netfilter/nft_payload.c | 8 +--
net/netfilter/nft_queue.c | 2 +-
net/netfilter/nft_quota.c | 2 +-
net/netfilter/nft_range.c | 2 +-
net/netfilter/nft_rt.c | 2 +-
net/netfilter/nft_set_pipapo.c | 4 +-
net/netfilter/nft_set_pipapo.h | 3 -
net/netfilter/nft_set_pipapo_avx2.c | 32 +++-------
net/netfilter/nft_socket.c | 2 +-
net/netfilter/nft_synproxy.c | 4 +-
net/netfilter/nft_tunnel.c | 6 +-
net/netfilter/nft_xfrm.c | 6 +-
net/netfilter/xt_dccp.c | 3 +
59 files changed, 262 insertions(+), 191 deletions(-)
--
2.52.0
^ permalink raw reply
* [PATCH net-next 0/5] net: enetc: improve statistics for v1 and add statistics for v4
From: Wei Fang @ 2026-04-08 5:58 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, imx
For ENETC v1, some standardized statistics were redundantly included in
the unstructured statistics, so remove these duplicated entries.
Previously, the unstructured statistics only contained eMAC data and
did not include pMAC data; add pMAC statistics to ensure completeness.
For ENETC v4, the driver previously reported MAC statistics only for the
internal ENETC (Pseudo MAC). Extend the implementation to provide
additional statistics for both the internal ENETC and the standalone
ENETC.
Wei Fang (5):
net: enetc: add support for the standardized counters
net: enetc: show RX drop counters only for assigned RX rings
net: enetc: remove standardized counters from enetc_pm_counters
net: enetc: add unstructured pMAC counters for ENETC v1
net: enetc: add unstructured counters for ENETC v4
drivers/net/ethernet/freescale/enetc/enetc.h | 2 +
.../net/ethernet/freescale/enetc/enetc4_hw.h | 192 ++++++++
.../ethernet/freescale/enetc/enetc_ethtool.c | 427 +++++++++++++-----
3 files changed, 519 insertions(+), 102 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH net-next 1/5] net: enetc: add support for the standardized counters
From: Wei Fang @ 2026-04-08 5:58 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, imx
In-Reply-To: <20260408055849.1314033-1-wei.fang@nxp.com>
ENETC v4 provides 64-bit counters for IEEE 802.3 basic and mandatory
managed objects, the IETF Management Information Database (MIB) package
(RFC2665), and Remote Network Monitoring (RMON) statistics. In addition,
some ENETCs support preemption, so these ENETCs have two MACs: MAC 0 is
the express MAC (eMAC), MAC 1 is the preemptible MAC (pMAC). Both MACs
support these statistics.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/enetc.h | 2 +
.../net/ethernet/freescale/enetc/enetc4_hw.h | 120 +++++++++++++++
.../ethernet/freescale/enetc/enetc_ethtool.c | 141 +++++++++++++++---
3 files changed, 245 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index aecd40aeef9c..e663bb5e614e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -264,6 +264,8 @@ struct enetc_msg_swbd {
};
#define ENETC_REV1 0x1
+#define ENETC_REV4 0x4
+
enum enetc_errata {
ENETC_ERR_VLAN_ISOL = BIT(0),
ENETC_ERR_UCMCSWP = BIT(1),
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
index 719c88ceb801..392992a646fb 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
@@ -196,6 +196,126 @@
#define PM_SINGLE_STEP_OFFSET_SET(o) FIELD_PREP(PM_SINGLE_STEP_OFFSET, o)
#define PM_SINGLE_STEP_EN BIT(31)
+/* Port MAC 0/1 Receive Ethernet Octets Counter */
+#define ENETC4_PM_REOCT(mac) (0x5100 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Alignment Error Counter Register */
+#define ENETC4_PM_RALN(mac) (0x5110 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Valid Pause Frame Counter */
+#define ENETC4_PM_RXPF(mac) (0x5118 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Frame Counter */
+#define ENETC4_PM_RFRM(mac) (0x5120 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Frame Check Sequence Error Counter */
+#define ENETC4_PM_RFCS(mac) (0x5128 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Multicast Frame Counter */
+#define ENETC4_PM_RMCA(mac) (0x5148 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Broadcast Frame Counter */
+#define ENETC4_PM_RBCA(mac) (0x5150 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Undersized Packet Counter */
+#define ENETC4_PM_RUND(mac) (0x5168 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive 64-Octet Packet Counter */
+#define ENETC4_PM_R64(mac) (0x5170 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive 65 to 127-Octet Packet Counter */
+#define ENETC4_PM_R127(mac) (0x5178 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive 128 to 255-Octet Packet Counter */
+#define ENETC4_PM_R255(mac) (0x5180 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive 256 to 511-Octet Packet Counter */
+#define ENETC4_PM_R511(mac) (0x5188 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive 512 to 1023-Octet Packet Counter */
+#define ENETC4_PM_R1023(mac) (0x5190 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive 1024 to 1522-Octet Packet Counter */
+#define ENETC4_PM_R1522(mac) (0x5198 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive 1523 to Max-Octet Packet Counter */
+#define ENETC4_PM_R1523X(mac) (0x51a0 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Oversized Packet Counter */
+#define ENETC4_PM_ROVR(mac) (0x51a8 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Jabber Packet Counter */
+#define ENETC4_PM_RJBR(mac) (0x51b0 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Fragment Packet Counter */
+#define ENETC4_PM_RFRG(mac) (0x51b8 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Control Packet Counter */
+#define ENETC4_PM_RCNP(mac) (0x51c0 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Dropped Not Truncated Packets Counter */
+#define ENETC4_PM_RDRNTP(mac) (0x51c8 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Ethernet Octets Counter */
+#define ENETC4_PM_TEOCT(mac) (0x5200 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Valid Pause Frame Counter */
+#define ENETC4_PM_TXPF(mac) (0x5218 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Frame Counter */
+#define ENETC4_PM_TFRM(mac) (0x5220 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Frame Error Counter */
+#define ENETC4_PM_TERR(mac) (0x5238 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Multicast Frame Counter */
+#define ENETC4_PM_TMCA(mac) (0x5248 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Broadcast Frame Counter */
+#define ENETC4_PM_TBCA(mac) (0x5250 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Undersized Packet Counter */
+#define ENETC4_PM_TUND(mac) (0x5268 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit 64-Octet Packet Counter */
+#define ENETC4_PM_T64(mac) (0x5270 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit 65 to 127-Octet Packet Counter */
+#define ENETC4_PM_T127(mac) (0x5278 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit 128 to 255-Octet Packet Counter */
+#define ENETC4_PM_T255(mac) (0x5280 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit 256 to 511-Octet Packet Counter */
+#define ENETC4_PM_T511(mac) (0x5288 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit 512 to 1023-Octet Packet Counter */
+#define ENETC4_PM_T1023(mac) (0x5290 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit 1024 to 1522-Octet Packet Counter */
+#define ENETC4_PM_T1522(mac) (0x5298 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit 1523 to TX_MTU-Octet Packet Counter */
+#define ENETC4_PM_T1523X(mac) (0x52a0 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Control Packet Counter */
+#define ENETC4_PM_TCNP(mac) (0x52c0 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Deferred Packet Counter */
+#define ENETC4_PM_TDFR(mac) (0x52d0 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Multiple Collisions Counter */
+#define ENETC4_PM_TMCOL(mac) (0x52d8 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Single Collision */
+#define ENETC4_PM_TSCOL(mac) (0x52e0 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Late Collision Counter */
+#define ENETC4_PM_TLCOL(mac) (0x52e8 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit Excessive Collisions Counter */
+#define ENETC4_PM_TECOL(mac) (0x52f0 + (mac) * 0x400)
+
/* Port MAC 0 Interface Mode Control Register */
#define ENETC4_PM_IF_MODE(mac) (0x5300 + (mac) * 0x400)
#define PM_IF_MODE_IFMODE GENMASK(2, 0)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 7c17acaf7a38..c30a119e9142 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -320,27 +320,38 @@ static void enetc_get_ethtool_stats(struct net_device *ndev,
data[o++] = enetc_port_rd64(hw, enetc_pm_counters[i].reg);
}
-static void enetc_pause_stats(struct enetc_hw *hw, int mac,
+static void enetc_pause_stats(struct enetc_si *si, int mac,
struct ethtool_pause_stats *pause_stats)
{
- pause_stats->tx_pause_frames = enetc_port_rd64(hw, ENETC_PM_TXPF(mac));
- pause_stats->rx_pause_frames = enetc_port_rd64(hw, ENETC_PM_RXPF(mac));
+ struct enetc_hw *hw = &si->hw;
+
+ switch (si->pdev->revision) {
+ case ENETC_REV1:
+ pause_stats->tx_pause_frames = enetc_port_rd64(hw, ENETC_PM_TXPF(mac));
+ pause_stats->rx_pause_frames = enetc_port_rd64(hw, ENETC_PM_RXPF(mac));
+ break;
+ case ENETC_REV4:
+ pause_stats->tx_pause_frames = enetc_port_rd64(hw, ENETC4_PM_TXPF(mac));
+ pause_stats->rx_pause_frames = enetc_port_rd64(hw, ENETC4_PM_RXPF(mac));
+ break;
+ default:
+ break;
+ }
}
static void enetc_get_pause_stats(struct net_device *ndev,
struct ethtool_pause_stats *pause_stats)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
- struct enetc_hw *hw = &priv->si->hw;
struct enetc_si *si = priv->si;
switch (pause_stats->src) {
case ETHTOOL_MAC_STATS_SRC_EMAC:
- enetc_pause_stats(hw, 0, pause_stats);
+ enetc_pause_stats(si, 0, pause_stats);
break;
case ETHTOOL_MAC_STATS_SRC_PMAC:
if (si->hw_features & ENETC_SI_F_QBU)
- enetc_pause_stats(hw, 1, pause_stats);
+ enetc_pause_stats(si, 1, pause_stats);
break;
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
ethtool_aggregate_pause_stats(ndev, pause_stats);
@@ -371,11 +382,45 @@ static void enetc_mac_stats(struct enetc_hw *hw, int mac,
s->BroadcastFramesReceivedOK = enetc_port_rd64(hw, ENETC_PM_RBCA(mac));
}
-static void enetc_ctrl_stats(struct enetc_hw *hw, int mac,
+static void enetc4_mac_stats(struct enetc_hw *hw, int mac,
+ struct ethtool_eth_mac_stats *s)
+{
+ s->FramesTransmittedOK = enetc_port_rd64(hw, ENETC4_PM_TFRM(mac));
+ s->SingleCollisionFrames = enetc_port_rd64(hw, ENETC4_PM_TSCOL(mac));
+ s->MultipleCollisionFrames = enetc_port_rd64(hw, ENETC4_PM_TMCOL(mac));
+ s->FramesReceivedOK = enetc_port_rd64(hw, ENETC4_PM_RFRM(mac));
+ s->FrameCheckSequenceErrors = enetc_port_rd64(hw, ENETC4_PM_RFCS(mac));
+ s->AlignmentErrors = enetc_port_rd64(hw, ENETC4_PM_RALN(mac));
+ s->OctetsTransmittedOK = enetc_port_rd64(hw, ENETC4_PM_TEOCT(mac));
+ s->FramesWithDeferredXmissions = enetc_port_rd64(hw, ENETC4_PM_TDFR(mac));
+ s->LateCollisions = enetc_port_rd64(hw, ENETC4_PM_TLCOL(mac));
+ s->FramesAbortedDueToXSColls = enetc_port_rd64(hw, ENETC4_PM_TECOL(mac));
+ s->FramesLostDueToIntMACXmitError = enetc_port_rd64(hw, ENETC4_PM_TERR(mac));
+ s->OctetsReceivedOK = enetc_port_rd64(hw, ENETC4_PM_REOCT(mac));
+ s->FramesLostDueToIntMACRcvError = enetc_port_rd64(hw, ENETC4_PM_RDRNTP(mac));
+ s->MulticastFramesXmittedOK = enetc_port_rd64(hw, ENETC4_PM_TMCA(mac));
+ s->BroadcastFramesXmittedOK = enetc_port_rd64(hw, ENETC4_PM_TBCA(mac));
+ s->MulticastFramesReceivedOK = enetc_port_rd64(hw, ENETC4_PM_RMCA(mac));
+ s->BroadcastFramesReceivedOK = enetc_port_rd64(hw, ENETC4_PM_RBCA(mac));
+}
+
+static void enetc_ctrl_stats(struct enetc_si *si, int mac,
struct ethtool_eth_ctrl_stats *s)
{
- s->MACControlFramesTransmitted = enetc_port_rd64(hw, ENETC_PM_TCNP(mac));
- s->MACControlFramesReceived = enetc_port_rd64(hw, ENETC_PM_RCNP(mac));
+ struct enetc_hw *hw = &si->hw;
+
+ switch (si->pdev->revision) {
+ case ENETC_REV1:
+ s->MACControlFramesTransmitted = enetc_port_rd64(hw, ENETC_PM_TCNP(mac));
+ s->MACControlFramesReceived = enetc_port_rd64(hw, ENETC_PM_RCNP(mac));
+ break;
+ case ENETC_REV4:
+ s->MACControlFramesTransmitted = enetc_port_rd64(hw, ENETC4_PM_TCNP(mac));
+ s->MACControlFramesReceived = enetc_port_rd64(hw, ENETC4_PM_RCNP(mac));
+ break;
+ default:
+ break;
+ }
}
static const struct ethtool_rmon_hist_range enetc_rmon_ranges[] = {
@@ -414,20 +459,61 @@ static void enetc_rmon_stats(struct enetc_hw *hw, int mac,
s->hist_tx[6] = enetc_port_rd64(hw, ENETC_PM_T1523X(mac));
}
+static void enetc4_rmon_stats(struct enetc_hw *hw, int mac,
+ struct ethtool_rmon_stats *s)
+{
+ s->undersize_pkts = enetc_port_rd64(hw, ENETC4_PM_RUND(mac));
+ s->oversize_pkts = enetc_port_rd64(hw, ENETC4_PM_ROVR(mac));
+ s->fragments = enetc_port_rd64(hw, ENETC4_PM_RFRG(mac));
+ s->jabbers = enetc_port_rd64(hw, ENETC4_PM_RJBR(mac));
+
+ s->hist[0] = enetc_port_rd64(hw, ENETC4_PM_R64(mac));
+ s->hist[1] = enetc_port_rd64(hw, ENETC4_PM_R127(mac));
+ s->hist[2] = enetc_port_rd64(hw, ENETC4_PM_R255(mac));
+ s->hist[3] = enetc_port_rd64(hw, ENETC4_PM_R511(mac));
+ s->hist[4] = enetc_port_rd64(hw, ENETC4_PM_R1023(mac));
+ s->hist[5] = enetc_port_rd64(hw, ENETC4_PM_R1522(mac));
+ s->hist[6] = enetc_port_rd64(hw, ENETC4_PM_R1523X(mac));
+
+ s->hist_tx[0] = enetc_port_rd64(hw, ENETC4_PM_T64(mac));
+ s->hist_tx[1] = enetc_port_rd64(hw, ENETC4_PM_T127(mac));
+ s->hist_tx[2] = enetc_port_rd64(hw, ENETC4_PM_T255(mac));
+ s->hist_tx[3] = enetc_port_rd64(hw, ENETC4_PM_T511(mac));
+ s->hist_tx[4] = enetc_port_rd64(hw, ENETC4_PM_T1023(mac));
+ s->hist_tx[5] = enetc_port_rd64(hw, ENETC4_PM_T1522(mac));
+ s->hist_tx[6] = enetc_port_rd64(hw, ENETC4_PM_T1523X(mac));
+}
+
+static void enetc_get_mac_stats(struct enetc_si *si, int mac,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct enetc_hw *hw = &si->hw;
+
+ switch (si->pdev->revision) {
+ case ENETC_REV1:
+ enetc_mac_stats(hw, mac, mac_stats);
+ break;
+ case ENETC_REV4:
+ enetc4_mac_stats(hw, mac, mac_stats);
+ break;
+ default:
+ break;
+ }
+}
+
static void enetc_get_eth_mac_stats(struct net_device *ndev,
struct ethtool_eth_mac_stats *mac_stats)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
- struct enetc_hw *hw = &priv->si->hw;
struct enetc_si *si = priv->si;
switch (mac_stats->src) {
case ETHTOOL_MAC_STATS_SRC_EMAC:
- enetc_mac_stats(hw, 0, mac_stats);
+ enetc_get_mac_stats(si, 0, mac_stats);
break;
case ETHTOOL_MAC_STATS_SRC_PMAC:
if (si->hw_features & ENETC_SI_F_QBU)
- enetc_mac_stats(hw, 1, mac_stats);
+ enetc_get_mac_stats(si, 1, mac_stats);
break;
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
ethtool_aggregate_mac_stats(ndev, mac_stats);
@@ -481,16 +567,15 @@ static void enetc_get_eth_ctrl_stats(struct net_device *ndev,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
- struct enetc_hw *hw = &priv->si->hw;
struct enetc_si *si = priv->si;
switch (ctrl_stats->src) {
case ETHTOOL_MAC_STATS_SRC_EMAC:
- enetc_ctrl_stats(hw, 0, ctrl_stats);
+ enetc_ctrl_stats(si, 0, ctrl_stats);
break;
case ETHTOOL_MAC_STATS_SRC_PMAC:
if (si->hw_features & ENETC_SI_F_QBU)
- enetc_ctrl_stats(hw, 1, ctrl_stats);
+ enetc_ctrl_stats(si, 1, ctrl_stats);
break;
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
ethtool_aggregate_ctrl_stats(ndev, ctrl_stats);
@@ -498,23 +583,39 @@ static void enetc_get_eth_ctrl_stats(struct net_device *ndev,
}
}
+static void enetc_get_mac_rmon_stats(struct enetc_si *si, int mac,
+ struct ethtool_rmon_stats *rmon_stats)
+{
+ struct enetc_hw *hw = &si->hw;
+
+ switch (si->pdev->revision) {
+ case ENETC_REV1:
+ enetc_rmon_stats(hw, mac, rmon_stats);
+ break;
+ case ENETC_REV4:
+ enetc4_rmon_stats(hw, mac, rmon_stats);
+ break;
+ default:
+ break;
+ }
+}
+
static void enetc_get_rmon_stats(struct net_device *ndev,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
- struct enetc_hw *hw = &priv->si->hw;
struct enetc_si *si = priv->si;
*ranges = enetc_rmon_ranges;
switch (rmon_stats->src) {
case ETHTOOL_MAC_STATS_SRC_EMAC:
- enetc_rmon_stats(hw, 0, rmon_stats);
+ enetc_get_mac_rmon_stats(si, 0, rmon_stats);
break;
case ETHTOOL_MAC_STATS_SRC_PMAC:
if (si->hw_features & ENETC_SI_F_QBU)
- enetc_rmon_stats(hw, 1, rmon_stats);
+ enetc_get_mac_rmon_stats(si, 1, rmon_stats);
break;
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
ethtool_aggregate_rmon_stats(ndev, rmon_stats);
@@ -1398,6 +1499,10 @@ const struct ethtool_ops enetc4_pf_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES |
ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
+ .get_pause_stats = enetc_get_pause_stats,
+ .get_rmon_stats = enetc_get_rmon_stats,
+ .get_eth_ctrl_stats = enetc_get_eth_ctrl_stats,
+ .get_eth_mac_stats = enetc_get_eth_mac_stats,
.get_ringparam = enetc_get_ringparam,
.get_coalesce = enetc_get_coalesce,
.set_coalesce = enetc_set_coalesce,
--
2.34.1
^ permalink raw reply related
* [PATCH net-next 2/5] net: enetc: show RX drop counters only for assigned RX rings
From: Wei Fang @ 2026-04-08 5:58 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, imx
In-Reply-To: <20260408055849.1314033-1-wei.fang@nxp.com>
For ENETC v1, each SI provides 16 RBDCR registers for RX ring drop
counters, but this does not imply that an SI actually owns 16 RX rings.
The ENETC hardware supports a total of 16 RX rings, which are assigned
to 3 SIs (1 PSI and 2 VSIs), so each SI is assigned fewer than 16 RX
rings.
The current implementation always reports 16 RX drop counters per SI,
leading to redundant output for SIs with fewer RX rings. Update the
logic to display drop counters only for the RX rings that are actually
assigned to the SI.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
.../ethernet/freescale/enetc/enetc_ethtool.c | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index c30a119e9142..36d1a2b810c2 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -124,22 +124,6 @@ static const struct {
{ ENETC_SITFRM, "SI tx frames" },
{ ENETC_SITUCA, "SI tx u-cast frames" },
{ ENETC_SITMCA, "SI tx m-cast frames" },
- { ENETC_RBDCR(0), "Rx ring 0 discarded frames" },
- { ENETC_RBDCR(1), "Rx ring 1 discarded frames" },
- { ENETC_RBDCR(2), "Rx ring 2 discarded frames" },
- { ENETC_RBDCR(3), "Rx ring 3 discarded frames" },
- { ENETC_RBDCR(4), "Rx ring 4 discarded frames" },
- { ENETC_RBDCR(5), "Rx ring 5 discarded frames" },
- { ENETC_RBDCR(6), "Rx ring 6 discarded frames" },
- { ENETC_RBDCR(7), "Rx ring 7 discarded frames" },
- { ENETC_RBDCR(8), "Rx ring 8 discarded frames" },
- { ENETC_RBDCR(9), "Rx ring 9 discarded frames" },
- { ENETC_RBDCR(10), "Rx ring 10 discarded frames" },
- { ENETC_RBDCR(11), "Rx ring 11 discarded frames" },
- { ENETC_RBDCR(12), "Rx ring 12 discarded frames" },
- { ENETC_RBDCR(13), "Rx ring 13 discarded frames" },
- { ENETC_RBDCR(14), "Rx ring 14 discarded frames" },
- { ENETC_RBDCR(15), "Rx ring 15 discarded frames" },
};
static const struct {
@@ -224,6 +208,7 @@ static const char rx_ring_stats[][ETH_GSTRING_LEN] = {
"Rx ring %2d recycle failures",
"Rx ring %2d redirects",
"Rx ring %2d redirect failures",
+ "Rx ring %2d discarded frames",
};
static const char tx_ring_stats[][ETH_GSTRING_LEN] = {
@@ -308,6 +293,7 @@ static void enetc_get_ethtool_stats(struct net_device *ndev,
data[o++] = priv->rx_ring[i]->stats.recycle_failures;
data[o++] = priv->rx_ring[i]->stats.xdp_redirect;
data[o++] = priv->rx_ring[i]->stats.xdp_redirect_failures;
+ data[o++] = enetc_rd(hw, ENETC_RBDCR(i));
}
if (!enetc_si_is_pf(priv->si))
--
2.34.1
^ permalink raw reply related
* [PATCH net-next 3/5] net: enetc: remove standardized counters from enetc_pm_counters
From: Wei Fang @ 2026-04-08 5:58 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, imx
In-Reply-To: <20260408055849.1314033-1-wei.fang@nxp.com>
The standardized counters are already exposed via the get_pause_stats(),
get_rmon_stats(), get_eth_ctrl_stats() and get_eth_mac_stats()
interfaces. Keeping the same counters in enetc_pm_counters results in
redundant output.
Remove these standardized counters from enetc_pm_counters and rely on
the existing statistics interfaces to report them.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
.../ethernet/freescale/enetc/enetc_ethtool.c | 40 -------------------
1 file changed, 40 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 36d1a2b810c2..504def405489 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -130,57 +130,17 @@ static const struct {
int reg;
char name[ETH_GSTRING_LEN] __nonstring;
} enetc_pm_counters[] = {
- { ENETC_PM_REOCT(0), "MAC rx ethernet octets" },
- { ENETC_PM_RALN(0), "MAC rx alignment errors" },
- { ENETC_PM_RXPF(0), "MAC rx valid pause frames" },
- { ENETC_PM_RFRM(0), "MAC rx valid frames" },
- { ENETC_PM_RFCS(0), "MAC rx fcs errors" },
{ ENETC_PM_RVLAN(0), "MAC rx VLAN frames" },
{ ENETC_PM_RERR(0), "MAC rx frame errors" },
{ ENETC_PM_RUCA(0), "MAC rx unicast frames" },
- { ENETC_PM_RMCA(0), "MAC rx multicast frames" },
- { ENETC_PM_RBCA(0), "MAC rx broadcast frames" },
{ ENETC_PM_RDRP(0), "MAC rx dropped packets" },
{ ENETC_PM_RPKT(0), "MAC rx packets" },
- { ENETC_PM_RUND(0), "MAC rx undersized packets" },
- { ENETC_PM_R64(0), "MAC rx 64 byte packets" },
- { ENETC_PM_R127(0), "MAC rx 65-127 byte packets" },
- { ENETC_PM_R255(0), "MAC rx 128-255 byte packets" },
- { ENETC_PM_R511(0), "MAC rx 256-511 byte packets" },
- { ENETC_PM_R1023(0), "MAC rx 512-1023 byte packets" },
- { ENETC_PM_R1522(0), "MAC rx 1024-1522 byte packets" },
- { ENETC_PM_R1523X(0), "MAC rx 1523 to max-octet packets" },
- { ENETC_PM_ROVR(0), "MAC rx oversized packets" },
- { ENETC_PM_RJBR(0), "MAC rx jabber packets" },
- { ENETC_PM_RFRG(0), "MAC rx fragment packets" },
- { ENETC_PM_RCNP(0), "MAC rx control packets" },
- { ENETC_PM_RDRNTP(0), "MAC rx fifo drop" },
- { ENETC_PM_TEOCT(0), "MAC tx ethernet octets" },
{ ENETC_PM_TOCT(0), "MAC tx octets" },
- { ENETC_PM_TCRSE(0), "MAC tx carrier sense errors" },
- { ENETC_PM_TXPF(0), "MAC tx valid pause frames" },
- { ENETC_PM_TFRM(0), "MAC tx frames" },
{ ENETC_PM_TFCS(0), "MAC tx fcs errors" },
{ ENETC_PM_TVLAN(0), "MAC tx VLAN frames" },
- { ENETC_PM_TERR(0), "MAC tx frame errors" },
{ ENETC_PM_TUCA(0), "MAC tx unicast frames" },
- { ENETC_PM_TMCA(0), "MAC tx multicast frames" },
- { ENETC_PM_TBCA(0), "MAC tx broadcast frames" },
{ ENETC_PM_TPKT(0), "MAC tx packets" },
{ ENETC_PM_TUND(0), "MAC tx undersized packets" },
- { ENETC_PM_T64(0), "MAC tx 64 byte packets" },
- { ENETC_PM_T127(0), "MAC tx 65-127 byte packets" },
- { ENETC_PM_T255(0), "MAC tx 128-255 byte packets" },
- { ENETC_PM_T511(0), "MAC tx 256-511 byte packets" },
- { ENETC_PM_T1023(0), "MAC tx 512-1023 byte packets" },
- { ENETC_PM_T1522(0), "MAC tx 1024-1522 byte packets" },
- { ENETC_PM_T1523X(0), "MAC tx 1523 to max-octet packets" },
- { ENETC_PM_TCNP(0), "MAC tx control packets" },
- { ENETC_PM_TDFR(0), "MAC tx deferred packets" },
- { ENETC_PM_TMCOL(0), "MAC tx multiple collisions" },
- { ENETC_PM_TSCOL(0), "MAC tx single collisions" },
- { ENETC_PM_TLCOL(0), "MAC tx late collisions" },
- { ENETC_PM_TECOL(0), "MAC tx excessive collisions" },
};
static const struct {
--
2.34.1
^ permalink raw reply related
* [PATCH net-next 4/5] net: enetc: add unstructured pMAC counters for ENETC v1
From: Wei Fang @ 2026-04-08 5:58 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, imx
In-Reply-To: <20260408055849.1314033-1-wei.fang@nxp.com>
The ENETC v1 has two MACs (eMAC and pMAC) to support preemption. The
existing unstructured counters include the eMAC counters, but not the
pMAC counters. So add pMAC counters to improve statistical coverage.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
.../ethernet/freescale/enetc/enetc_ethtool.c | 77 ++++++++++++++-----
1 file changed, 56 insertions(+), 21 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 504def405489..bdc5916e4400 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -129,18 +129,35 @@ static const struct {
static const struct {
int reg;
char name[ETH_GSTRING_LEN] __nonstring;
-} enetc_pm_counters[] = {
- { ENETC_PM_RVLAN(0), "MAC rx VLAN frames" },
- { ENETC_PM_RERR(0), "MAC rx frame errors" },
- { ENETC_PM_RUCA(0), "MAC rx unicast frames" },
- { ENETC_PM_RDRP(0), "MAC rx dropped packets" },
- { ENETC_PM_RPKT(0), "MAC rx packets" },
- { ENETC_PM_TOCT(0), "MAC tx octets" },
- { ENETC_PM_TFCS(0), "MAC tx fcs errors" },
- { ENETC_PM_TVLAN(0), "MAC tx VLAN frames" },
- { ENETC_PM_TUCA(0), "MAC tx unicast frames" },
- { ENETC_PM_TPKT(0), "MAC tx packets" },
- { ENETC_PM_TUND(0), "MAC tx undersized packets" },
+} enetc_emac_counters[] = {
+ { ENETC_PM_RVLAN(0), "eMAC rx VLAN frames" },
+ { ENETC_PM_RERR(0), "eMAC rx frame errors" },
+ { ENETC_PM_RUCA(0), "eMAC rx unicast frames" },
+ { ENETC_PM_RDRP(0), "eMAC rx dropped packets" },
+ { ENETC_PM_RPKT(0), "eMAC rx packets" },
+ { ENETC_PM_TOCT(0), "eMAC tx octets" },
+ { ENETC_PM_TFCS(0), "eMAC tx fcs errors" },
+ { ENETC_PM_TVLAN(0), "eMAC tx VLAN frames" },
+ { ENETC_PM_TUCA(0), "eMAC tx unicast frames" },
+ { ENETC_PM_TPKT(0), "eMAC tx packets" },
+ { ENETC_PM_TUND(0), "eMAC tx undersized packets" },
+};
+
+static const struct {
+ int reg;
+ char name[ETH_GSTRING_LEN] __nonstring;
+} enetc_pmac_counters[] = {
+ { ENETC_PM_RVLAN(1), "pMAC rx VLAN frames" },
+ { ENETC_PM_RERR(1), "pMAC rx frame errors" },
+ { ENETC_PM_RUCA(1), "pMAC rx unicast frames" },
+ { ENETC_PM_RDRP(1), "pMAC rx dropped packets" },
+ { ENETC_PM_RPKT(1), "pMAC rx packets" },
+ { ENETC_PM_TOCT(1), "pMAC tx octets" },
+ { ENETC_PM_TFCS(1), "pMAC tx fcs errors" },
+ { ENETC_PM_TVLAN(1), "pMAC tx VLAN frames" },
+ { ENETC_PM_TUCA(1), "pMAC tx unicast frames" },
+ { ENETC_PM_TPKT(1), "pMAC tx packets" },
+ { ENETC_PM_TUND(1), "pMAC tx undersized packets" },
};
static const struct {
@@ -181,6 +198,7 @@ static const char tx_ring_stats[][ETH_GSTRING_LEN] = {
static int enetc_get_sset_count(struct net_device *ndev, int sset)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct enetc_si *si = priv->si;
int len;
if (sset != ETH_SS_STATS)
@@ -190,11 +208,14 @@ static int enetc_get_sset_count(struct net_device *ndev, int sset)
ARRAY_SIZE(tx_ring_stats) * priv->num_tx_rings +
ARRAY_SIZE(rx_ring_stats) * priv->num_rx_rings;
- if (!enetc_si_is_pf(priv->si))
+ if (!enetc_si_is_pf(si))
return len;
len += ARRAY_SIZE(enetc_port_counters);
- len += ARRAY_SIZE(enetc_pm_counters);
+ len += ARRAY_SIZE(enetc_emac_counters);
+
+ if (si->hw_features & ENETC_SI_F_QBU)
+ len += ARRAY_SIZE(enetc_pmac_counters);
return len;
}
@@ -202,6 +223,7 @@ static int enetc_get_sset_count(struct net_device *ndev, int sset)
static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct enetc_si *si = priv->si;
int i, j;
switch (stringset) {
@@ -215,14 +237,20 @@ static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
for (j = 0; j < ARRAY_SIZE(rx_ring_stats); j++)
ethtool_sprintf(&data, rx_ring_stats[j], i);
- if (!enetc_si_is_pf(priv->si))
+ if (!enetc_si_is_pf(si))
break;
for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
ethtool_cpy(&data, enetc_port_counters[i].name);
- for (i = 0; i < ARRAY_SIZE(enetc_pm_counters); i++)
- ethtool_cpy(&data, enetc_pm_counters[i].name);
+ for (i = 0; i < ARRAY_SIZE(enetc_emac_counters); i++)
+ ethtool_cpy(&data, enetc_emac_counters[i].name);
+
+ if (!(si->hw_features & ENETC_SI_F_QBU))
+ break;
+
+ for (i = 0; i < ARRAY_SIZE(enetc_pmac_counters); i++)
+ ethtool_cpy(&data, enetc_pmac_counters[i].name);
break;
}
@@ -232,7 +260,8 @@ static void enetc_get_ethtool_stats(struct net_device *ndev,
struct ethtool_stats *stats, u64 *data)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
- struct enetc_hw *hw = &priv->si->hw;
+ struct enetc_si *si = priv->si;
+ struct enetc_hw *hw = &si->hw;
int i, o = 0;
for (i = 0; i < ARRAY_SIZE(enetc_si_counters); i++)
@@ -256,14 +285,20 @@ static void enetc_get_ethtool_stats(struct net_device *ndev,
data[o++] = enetc_rd(hw, ENETC_RBDCR(i));
}
- if (!enetc_si_is_pf(priv->si))
+ if (!enetc_si_is_pf(si))
return;
for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
data[o++] = enetc_port_rd(hw, enetc_port_counters[i].reg);
- for (i = 0; i < ARRAY_SIZE(enetc_pm_counters); i++)
- data[o++] = enetc_port_rd64(hw, enetc_pm_counters[i].reg);
+ for (i = 0; i < ARRAY_SIZE(enetc_emac_counters); i++)
+ data[o++] = enetc_port_rd64(hw, enetc_emac_counters[i].reg);
+
+ if (!(si->hw_features & ENETC_SI_F_QBU))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(enetc_pmac_counters); i++)
+ data[o++] = enetc_port_rd64(hw, enetc_pmac_counters[i].reg);
}
static void enetc_pause_stats(struct enetc_si *si, int mac,
--
2.34.1
^ permalink raw reply related
* [PATCH net-next 5/5] net: enetc: add unstructured counters for ENETC v4
From: Wei Fang @ 2026-04-08 5:58 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, imx
In-Reply-To: <20260408055849.1314033-1-wei.fang@nxp.com>
Like ENETC v1, ENETC v4 also has many non-standard counters, so these
counters are added to improve statistical coverage.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
.../net/ethernet/freescale/enetc/enetc4_hw.h | 72 +++++++
.../ethernet/freescale/enetc/enetc_ethtool.c | 187 +++++++++++++++---
2 files changed, 234 insertions(+), 25 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
index 392992a646fb..f18437556a0e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
@@ -64,6 +64,9 @@
#define ENETC4_PPAUONTR 0x108
#define ENETC4_PPAUOFFTR 0x10c
+/* Port ingress congestion DRa (a=0,1,2,3) discard count register */
+#define ENETC4_PICDRDCR(a) ((a) * 0x10 + 0x140)
+
/* Port Station interface promiscuous MAC mode register */
#define ENETC4_PSIPMMR 0x200
#define PSIPMMR_SI_MAC_UP(a) BIT(a) /* a = SI index */
@@ -72,6 +75,12 @@
/* Port Station interface promiscuous VLAN mode register */
#define ENETC4_PSIPVMR 0x204
+/* Port broadcast frames dropped due to MAC filtering register */
+#define ENETC4_PBFDSIR 0x208
+
+/* Port frame drop MAC source address pruning register */
+#define ENETC4_PFDMSAPR 0x20c
+
/* Port RSS key register n. n = 0,1,2,...,9 */
#define ENETC4_PRSSKR(n) ((n) * 0x4 + 0x250)
@@ -79,6 +88,12 @@
#define ENETC4_PSIMAFCAPR 0x280
#define PSIMAFCAPR_NUM_MAC_AFTE GENMASK(11, 0)
+/* Port unicast frames dropped due to MAC filtering register */
+#define ENETC4_PUFDMFR 0x284
+
+/* Port multicast frames dropped due to MAC filtering register */
+#define ENETC4_PMFDMFR 0x288
+
/* Port station interface VLAN filtering capability register */
#define ENETC4_PSIVLANFCAPR 0x2c0
#define PSIVLANFCAPR_NUM_VLAN_FTE GENMASK(11, 0)
@@ -87,6 +102,15 @@
#define ENETC4_PSIVLANFMR 0x2c4
#define PSIVLANFMR_VS BIT(0)
+/* Port unicast frames dropped VLAN filtering register */
+#define ENETC4_PUFDVFR 0x2d0
+
+/* Port multicast frames dropped VLAN filtering register */
+#define ENETC4_PMFDVFR 0x2d4
+
+/* Port broadcast frames dropped VLAN filtering register */
+#define ENETC4_PBFDVFR 0x2d8
+
/* Port Station interface a primary MAC address registers */
#define ENETC4_PSIPMAR0(a) ((a) * 0x80 + 0x2000)
#define ENETC4_PSIPMAR1(a) ((a) * 0x80 + 0x2004)
@@ -141,6 +165,18 @@
#define ENETC4_PSR 0x4104
#define PSR_RX_BUSY BIT(1)
+/* Port Rx discard count register */
+#define ENETC4_PRXDCR 0x41c0
+
+/* Port Rx discard count read-reset register */
+#define ENETC4_PRXDCRRR 0x41c4
+
+/* Port Rx discard count reason register 0 */
+#define ENETC4_PRXDCRR0 0x41c8
+
+/* Port Rx discard count reason register 1 */
+#define ENETC4_PRXDCRR1 0x41cc
+
/* Port traffic class a transmit maximum SDU register */
#define ENETC4_PTCTMSDUR(a) ((a) * 0x20 + 0x4208)
#define PTCTMSDUR_MAXSDU GENMASK(15, 0)
@@ -199,6 +235,9 @@
/* Port MAC 0/1 Receive Ethernet Octets Counter */
#define ENETC4_PM_REOCT(mac) (0x5100 + (mac) * 0x400)
+/* Port MAC 0/1 Receive Octets Counter */
+#define ENETC4_PM_ROCT(mac) (0x5108 + (mac) * 0x400)
+
/* Port MAC 0/1 Receive Alignment Error Counter Register */
#define ENETC4_PM_RALN(mac) (0x5110 + (mac) * 0x400)
@@ -211,12 +250,27 @@
/* Port MAC 0/1 Receive Frame Check Sequence Error Counter */
#define ENETC4_PM_RFCS(mac) (0x5128 + (mac) * 0x400)
+/* Port MAC 0/1 Receive VLAN Frame Counter */
+#define ENETC4_PM_RVLAN(mac) (0x5130 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Frame Error Counter */
+#define ENETC4_PM_RERR(mac) (0x5138 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Unicast Frame Counter */
+#define ENETC4_PM_RUCA(mac) (0x5140 + (mac) * 0x400)
+
/* Port MAC 0/1 Receive Multicast Frame Counter */
#define ENETC4_PM_RMCA(mac) (0x5148 + (mac) * 0x400)
/* Port MAC 0/1 Receive Broadcast Frame Counter */
#define ENETC4_PM_RBCA(mac) (0x5150 + (mac) * 0x400)
+/* Port MAC 0/1 Receive Dropped Packets Counter */
+#define ENETC4_PM_RDRP(mac) (0x5158 + (mac) * 0x400)
+
+/* Port MAC 0/1 Receive Packets Counter */
+#define ENETC4_PM_RPKT(mac) (0x5160 + (mac) * 0x400)
+
/* Port MAC 0/1 Receive Undersized Packet Counter */
#define ENETC4_PM_RUND(mac) (0x5168 + (mac) * 0x400)
@@ -259,21 +313,36 @@
/* Port MAC 0/1 Transmit Ethernet Octets Counter */
#define ENETC4_PM_TEOCT(mac) (0x5200 + (mac) * 0x400)
+/* Port MAC 0/1 Transmit Octets Counter */
+#define ENETC4_PM_TOCT(mac) (0x5208 + (mac) * 0x400)
+
/* Port MAC 0/1 Transmit Valid Pause Frame Counter */
#define ENETC4_PM_TXPF(mac) (0x5218 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Frame Counter */
#define ENETC4_PM_TFRM(mac) (0x5220 + (mac) * 0x400)
+/* Port MAC 0/1 Transmit Frame Check Sequence Error Counter */
+#define ENETC4_PM_TFCS(mac) (0x5228 + (mac) * 0x400)
+
+/* Port MAC 0/1 Transmit VLAN Frame Counter */
+#define ENETC4_PM_TVLAN(mac) (0x5230 + (mac) * 0x400)
+
/* Port MAC 0/1 Transmit Frame Error Counter */
#define ENETC4_PM_TERR(mac) (0x5238 + (mac) * 0x400)
+/* Port MAC 0/1 Transmit Unicast Frame Counter */
+#define ENETC4_PM_TUCA(mac) (0x5240 + (mac) * 0x400)
+
/* Port MAC 0/1 Transmit Multicast Frame Counter */
#define ENETC4_PM_TMCA(mac) (0x5248 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Broadcast Frame Counter */
#define ENETC4_PM_TBCA(mac) (0x5250 + (mac) * 0x400)
+/* Port MAC 0/1 Transmit Packets Counter */
+#define ENETC4_PM_TPKT(mac) (0x5260 + (mac) * 0x400)
+
/* Port MAC 0/1 Transmit Undersized Packet Counter */
#define ENETC4_PM_TUND(mac) (0x5268 + (mac) * 0x400)
@@ -316,6 +385,9 @@
/* Port MAC 0/1 Transmit Excessive Collisions Counter */
#define ENETC4_PM_TECOL(mac) (0x52f0 + (mac) * 0x400)
+/* Port MAC 0/1 Transmit Invalid Octets Counter */
+#define ENETC4_PM_TIOCT(mac) (0x52f8 + (mac) * 0x400)
+
/* Port MAC 0 Interface Mode Control Register */
#define ENETC4_PM_IF_MODE(mac) (0x5300 + (mac) * 0x400)
#define PM_IF_MODE_IFMODE GENMASK(2, 0)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index bdc5916e4400..71f376ef1be1 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -177,6 +177,65 @@ static const struct {
{ ENETC_PICDR(3), "ICM DR3 discarded frames" },
};
+static const struct {
+ int reg;
+ char name[ETH_GSTRING_LEN] __nonstring;
+} enetc4_emac_counters[] = {
+ { ENETC4_PM_ROCT(0), "eMAC rx octets" },
+ { ENETC4_PM_RVLAN(0), "eMAC rx VLAN frames" },
+ { ENETC4_PM_RERR(0), "eMAC rx frame errors" },
+ { ENETC4_PM_RUCA(0), "eMAC rx unicast frames" },
+ { ENETC4_PM_RDRP(0), "eMAC rx dropped packets" },
+ { ENETC4_PM_RPKT(0), "eMAC rx packets" },
+ { ENETC4_PM_TOCT(0), "eMAC tx octets" },
+ { ENETC4_PM_TVLAN(0), "eMAC tx VLAN frames" },
+ { ENETC4_PM_TFCS(0), "eMAC tx fcs errors" },
+ { ENETC4_PM_TUCA(0), "eMAC tx unicast frames" },
+ { ENETC4_PM_TPKT(0), "eMAC tx packets" },
+ { ENETC4_PM_TUND(0), "eMAC tx undersized packets" },
+ { ENETC4_PM_TIOCT(0), "eMAC tx invalid octets" },
+};
+
+static const struct {
+ int reg;
+ char name[ETH_GSTRING_LEN] __nonstring;
+} enetc4_pmac_counters[] = {
+ { ENETC4_PM_ROCT(1), "pMAC rx octets" },
+ { ENETC4_PM_RVLAN(1), "pMAC rx VLAN frames" },
+ { ENETC4_PM_RERR(1), "pMAC rx frame errors" },
+ { ENETC4_PM_RUCA(1), "pMAC rx unicast frames" },
+ { ENETC4_PM_RDRP(1), "pMAC rx dropped packets" },
+ { ENETC4_PM_RPKT(1), "pMAC rx packets" },
+ { ENETC4_PM_TOCT(1), "pMAC tx octets" },
+ { ENETC4_PM_TVLAN(1), "pMAC tx VLAN frames" },
+ { ENETC4_PM_TFCS(1), "pMAC tx fcs errors" },
+ { ENETC4_PM_TUCA(1), "pMAC tx unicast frames" },
+ { ENETC4_PM_TPKT(1), "pMAC tx packets" },
+ { ENETC4_PM_TUND(1), "pMAC tx undersized packets" },
+ { ENETC4_PM_TIOCT(1), "pMAC tx invalid octets" },
+};
+
+static const struct {
+ int reg;
+ char name[ETH_GSTRING_LEN] __nonstring;
+} enetc4_port_counters[] = {
+ { ENETC4_PICDRDCR(0), "ICM DR0 discarded frames" },
+ { ENETC4_PICDRDCR(1), "ICM DR1 discarded frames" },
+ { ENETC4_PICDRDCR(2), "ICM DR2 discarded frames" },
+ { ENETC4_PICDRDCR(3), "ICM DR3 discarded frames" },
+ { ENETC4_PUFDMFR, "MAC filter discarded unicast" },
+ { ENETC4_PMFDMFR, "MAC filter discarded multicast" },
+ { ENETC4_PBFDSIR, "MAC filter discarded broadcast" },
+ { ENETC4_PFDMSAPR, "MAC SA pruning discarded frames" },
+ { ENETC4_PUFDVFR, "VLAN filter discarded unicast" },
+ { ENETC4_PMFDVFR, "VLAN filter discarded multicast" },
+ { ENETC4_PBFDVFR, "VLAN filter discarded broadcast" },
+ { ENETC4_PRXDCR, "MAC rx discarded frames" },
+ { ENETC4_PRXDCRRR, "MAC rx discard read-reset" },
+ { ENETC4_PRXDCRR0, "MAC rx discard reason 0" },
+ { ENETC4_PRXDCRR1, "MAC rx discard reason 1" },
+};
+
static const char rx_ring_stats[][ETH_GSTRING_LEN] = {
"Rx ring %2d frames",
"Rx ring %2d alloc errors",
@@ -211,15 +270,62 @@ static int enetc_get_sset_count(struct net_device *ndev, int sset)
if (!enetc_si_is_pf(si))
return len;
- len += ARRAY_SIZE(enetc_port_counters);
- len += ARRAY_SIZE(enetc_emac_counters);
+ if (is_enetc_rev1(si)) {
+ len += ARRAY_SIZE(enetc_port_counters);
+ len += ARRAY_SIZE(enetc_emac_counters);
+ if (si->hw_features & ENETC_SI_F_QBU)
+ len += ARRAY_SIZE(enetc_pmac_counters);
+ } else {
+ len += ARRAY_SIZE(enetc4_port_counters);
- if (si->hw_features & ENETC_SI_F_QBU)
- len += ARRAY_SIZE(enetc_pmac_counters);
+ if (enetc_is_pseudo_mac(si))
+ return len;
+
+ len += ARRAY_SIZE(enetc4_emac_counters);
+ if (si->hw_features & ENETC_SI_F_QBU)
+ len += ARRAY_SIZE(enetc4_pmac_counters);
+ }
return len;
}
+static void enetc_get_pf_strings(struct enetc_si *si, u8 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
+ ethtool_cpy(&data, enetc_port_counters[i].name);
+
+ for (i = 0; i < ARRAY_SIZE(enetc_emac_counters); i++)
+ ethtool_cpy(&data, enetc_emac_counters[i].name);
+
+ if (!(si->hw_features & ENETC_SI_F_QBU))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(enetc_pmac_counters); i++)
+ ethtool_cpy(&data, enetc_pmac_counters[i].name);
+}
+
+static void enetc4_get_pf_strings(struct enetc_si *si, u8 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(enetc4_port_counters); i++)
+ ethtool_cpy(&data, enetc4_port_counters[i].name);
+
+ if (enetc_is_pseudo_mac(si))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(enetc4_emac_counters); i++)
+ ethtool_cpy(&data, enetc4_emac_counters[i].name);
+
+ if (!(si->hw_features & ENETC_SI_F_QBU))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(enetc4_pmac_counters); i++)
+ ethtool_cpy(&data, enetc4_pmac_counters[i].name);
+}
+
static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
@@ -240,20 +346,52 @@ static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
if (!enetc_si_is_pf(si))
break;
- for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
- ethtool_cpy(&data, enetc_port_counters[i].name);
+ if (is_enetc_rev1(si))
+ enetc_get_pf_strings(si, data);
+ else
+ enetc4_get_pf_strings(si, data);
- for (i = 0; i < ARRAY_SIZE(enetc_emac_counters); i++)
- ethtool_cpy(&data, enetc_emac_counters[i].name);
+ break;
+ }
+}
- if (!(si->hw_features & ENETC_SI_F_QBU))
- break;
+static void enetc_pf_get_ethtool_stats(struct enetc_si *si, int *o, u64 *data)
+{
+ struct enetc_hw *hw = &si->hw;
+ int i;
- for (i = 0; i < ARRAY_SIZE(enetc_pmac_counters); i++)
- ethtool_cpy(&data, enetc_pmac_counters[i].name);
+ for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
+ data[(*o)++] = enetc_port_rd(hw, enetc_port_counters[i].reg);
- break;
- }
+ for (i = 0; i < ARRAY_SIZE(enetc_emac_counters); i++)
+ data[(*o)++] = enetc_port_rd64(hw, enetc_emac_counters[i].reg);
+
+ if (!(si->hw_features & ENETC_SI_F_QBU))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(enetc_pmac_counters); i++)
+ data[(*o)++] = enetc_port_rd64(hw, enetc_pmac_counters[i].reg);
+}
+
+static void enetc4_pf_get_ethtool_stats(struct enetc_si *si, int *o, u64 *data)
+{
+ struct enetc_hw *hw = &si->hw;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(enetc4_port_counters); i++)
+ data[(*o)++] = enetc_port_rd(hw, enetc4_port_counters[i].reg);
+
+ if (enetc_is_pseudo_mac(si))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(enetc4_emac_counters); i++)
+ data[(*o)++] = enetc_port_rd64(hw, enetc4_emac_counters[i].reg);
+
+ if (!(si->hw_features & ENETC_SI_F_QBU))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(enetc4_pmac_counters); i++)
+ data[(*o)++] = enetc_port_rd64(hw, enetc4_pmac_counters[i].reg);
}
static void enetc_get_ethtool_stats(struct net_device *ndev,
@@ -288,17 +426,10 @@ static void enetc_get_ethtool_stats(struct net_device *ndev,
if (!enetc_si_is_pf(si))
return;
- for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
- data[o++] = enetc_port_rd(hw, enetc_port_counters[i].reg);
-
- for (i = 0; i < ARRAY_SIZE(enetc_emac_counters); i++)
- data[o++] = enetc_port_rd64(hw, enetc_emac_counters[i].reg);
-
- if (!(si->hw_features & ENETC_SI_F_QBU))
- return;
-
- for (i = 0; i < ARRAY_SIZE(enetc_pmac_counters); i++)
- data[o++] = enetc_port_rd64(hw, enetc_pmac_counters[i].reg);
+ if (is_enetc_rev1(si))
+ enetc_pf_get_ethtool_stats(si, &o, data);
+ else
+ enetc4_pf_get_ethtool_stats(si, &o, data);
}
static void enetc_pause_stats(struct enetc_si *si, int mac,
@@ -1438,6 +1569,9 @@ const struct ethtool_ops enetc4_ppm_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES |
ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
+ .get_sset_count = enetc_get_sset_count,
+ .get_strings = enetc_get_strings,
+ .get_ethtool_stats = enetc_get_ethtool_stats,
.get_eth_mac_stats = enetc_ppm_get_eth_mac_stats,
.get_rx_ring_count = enetc_get_rx_ring_count,
.get_rxfh_key_size = enetc_get_rxfh_key_size,
@@ -1480,6 +1614,9 @@ const struct ethtool_ops enetc4_pf_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES |
ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
+ .get_sset_count = enetc_get_sset_count,
+ .get_strings = enetc_get_strings,
+ .get_ethtool_stats = enetc_get_ethtool_stats,
.get_pause_stats = enetc_get_pause_stats,
.get_rmon_stats = enetc_get_rmon_stats,
.get_eth_ctrl_stats = enetc_get_eth_ctrl_stats,
--
2.34.1
^ permalink raw reply related
* Re: [Intel-wired-lan] [PATCH net] ice: fix VF queue configuration with low MTU values
From: Paul Menzel @ 2026-04-08 6:24 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez
Cc: intel-wired-lan, netdev, Tony Nguyen, Przemek Kitszel,
Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Jacob Keller, Aleksandr Loktionov,
Michal Swiatkowski, Dave Ertman, Michal Kubiak, stable
In-Reply-To: <20260406145641.1020623-1-jtornosm@redhat.com>
Dear Jose,
Thank you for the patch.
Am 06.04.26 um 16:56 schrieb Jose Ignacio Tornos Martinez:
> The ice driver's VF queue configuration validation rejects
> databuffer_size values below 1024 bytes, which prevents VFs from
> using MTU values below 871 bytes.
>
> The iavf driver calculates databuffer_size based on the MTU using:
> databuffer_size = ALIGN(MTU + LIBETH_RX_LL_LEN, 128)
>
> where LIBETH_RX_LL_LEN = 26 (ETH_HLEN + 2*VLAN_HLEN + ETH_FCS_LEN).
>
> For MTU values below 871:
> MTU 870: 870 + 26 = 896, aligned to 128 = 896 (< 1024, rejected)
> MTU 871: 871 + 26 = 897, aligned to 128 = 1024 (>= 1024, accepted)
>
> The 1024-byte minimum seems unnecessarily restrictive, because the hardware
> supports databuffer_size as low as 128 bytes (the alignment boundary),
> which should allow MTU values down to the standard minimum of 68 bytes.
>
> I haven't found the reason why the limit was configured in the commit
> 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message"), so
> with no more information and since it is working, change the minimum
> databuffer_size validation from 1024 to 128 bytes to allow standard low
> MTU values while still preventing invalid configurations.
Should you resend, having the reproducer script would be nice to have.
> Fixes: 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message")
> cc: stable@vger.kernel.org
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> ---
> drivers/net/ethernet/intel/ice/virt/queues.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c
> index f73d5a3e83d4..31be2f76181c 100644
> --- a/drivers/net/ethernet/intel/ice/virt/queues.c
> +++ b/drivers/net/ethernet/intel/ice/virt/queues.c
> @@ -840,7 +840,7 @@ int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
>
> if (qpi->rxq.databuffer_size != 0 &&
> (qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
> - qpi->rxq.databuffer_size < 1024))
> + qpi->rxq.databuffer_size < 128))
> goto error_param;
>
> ring->rx_buf_len = qpi->rxq.databuffer_size;
Either way:
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Kind regards,
Paul
^ permalink raw reply
* Re: [PATCH] ppp: require CAP_NET_ADMIN in target netns for unattached ioctls
From: Qingfang Deng @ 2026-04-08 6:28 UTC (permalink / raw)
To: Taegu Ha, netdev
Cc: linux-ppp, gnault, jaco, richardbgobert, ericwouds,
Cyrill Gorcunov, Matteo Croce
In-Reply-To: <20260408042345.1011-1-hataegu0826@gmail.com>
Hi,
Added Cc: Cyrill, and Matteo
On 2026/4/8 12:23, Taegu Ha wrote:
> /dev/ppp open is currently authorized against file->f_cred->user_ns,
> while unattached administrative ioctls operate on current->nsproxy->net_ns.
>
> As a result, a local unprivileged user can create a new user namespace
> with CLONE_NEWUSER, gain CAP_NET_ADMIN only in that new user namespace,
> and still issue PPPIOCNEWUNIT, PPPIOCATTACH, or PPPIOCATTCHAN against
> an inherited network namespace.
>
> Require CAP_NET_ADMIN in the user namespace that owns the target network
> namespace before handling these unattached PPP administrative ioctls.
>
> This preserves normal pppd operation in the network namespace it is
> actually privileged in, while rejecting the userns-only inherited-netns
> case.
>
> Fixes: 273ec51dd7ce ("net: ppp_generic - introduce net-namespace functionality v2")
For fixes, you should set the target tree to "net" in the patch subject,
using:
git format-patch --subject-prefix="PATCH net"
> Reported-by: Taegu Ha <hataegu0826@gmail.com>
Minor nit: Reported-by tag is not needed if it's the same user as Signed-off-by.
> Signed-off-by: Taegu Ha <hataegu0826@gmail.com>
> ---
> drivers/net/ppp/ppp_generic.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
> index e9b41777be80..99a8557188c0 100644
> --- a/drivers/net/ppp/ppp_generic.c
> +++ b/drivers/net/ppp/ppp_generic.c
> @@ -1059,6 +1059,9 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
>
> switch (cmd) {
> case PPPIOCNEWUNIT:
> + if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
> + return -EPERM;
> +
These three identical checks can be combined into one and placed before
the switch statement.
> /* Create a new ppp unit */
> if (get_user(unit, p))
> break;
> @@ -1073,6 +1076,9 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
> break;
>
> case PPPIOCATTACH:
> + if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
> + return -EPERM;
> +
> /* Attach to an existing ppp unit */
> if (get_user(unit, p))
> break;
> @@ -1089,6 +1095,9 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
> break;
>
> case PPPIOCATTCHAN:
> + if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
> + return -EPERM;
> +
> if (get_user(unit, p))
> break;
> err = -ENXIO;
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-net 2/2] idpf: fix skb datapath queue based scheduling crashes and timeouts
From: Loktionov, Aleksandr @ 2026-04-08 6:37 UTC (permalink / raw)
To: Hay, Joshua A, intel-wired-lan@lists.osuosl.org; +Cc: netdev@vger.kernel.org
In-Reply-To: <20260406233236.3585504-3-joshua.a.hay@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Joshua Hay
> Sent: Tuesday, April 7, 2026 1:33 AM
> To: intel-wired-lan@lists.osuosl.org
> Cc: netdev@vger.kernel.org
> Subject: [Intel-wired-lan] [PATCH iwl-net 2/2] idpf: fix skb datapath
> queue based scheduling crashes and timeouts
>
> The splitq Tx resource checks were assuming that the queues were using
> flow based scheduling and checking the refillqs for free buffers.
> However, the Tx refillqs are not allocated when using queue based
> scheduling resulting in a NULL ptr dereference. Adjust the Tx resource
> checks to only check available descriptor resources when using queue
> based scheduling. Because queue based scheduling does not have any
> notion of descriptor only completions, there cannot be any packets in
> flight, meaning there is no need to check for pending completions.
>
> The driver also only supported 8 byte completion descriptors in the
> skb datapath previously. However, currently the FW only supports 4
> byte completion descriptors when using queue based scheduling. This
> meant we were skipping over completions, resulting in Tx timeouts.
> Add support to process both 4 and 8 byte completion descriptors,
> depending on the scheduling mode. Cache the next_to_clean completion
> descriptor in the completion queue struct, and fetch this descriptor
> before the start of each cleaning loop. Access the next descriptor in
> the loop by calculating the index based on raw byte count.
>
> Fixes: 0c3f135e840d ("idpf: stop Tx if there are insufficient buffer
> resources")
> Fixes: 1c325aac10a8 ("idpf: configure resources for TX queues")
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Reviewed-by: Madhu Chittim <madhu.chittim@intel.com>
> ---
> drivers/net/ethernet/intel/idpf/idpf_txrx.c | 49 +++++++++++++-------
> - drivers/net/ethernet/intel/idpf/idpf_txrx.h | 6 ++-
> 2 files changed, 36 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
> b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
> index f6b3b15364ff..4fc0bb14c5b1 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
> @@ -270,11 +270,9 @@ static int idpf_tx_desc_alloc(const struct
> idpf_vport *vport, static int idpf_compl_desc_alloc(const struct
> idpf_vport *vport,
> struct idpf_compl_queue *complq)
> {
...
>
> /**
> * struct idpf_sw_queue
> --
> 2.39.2
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
^ permalink raw reply
* [syzbot ci] Re: net_sched: fix skb memory leak in deferred qdisc drops
From: syzbot ci @ 2026-04-08 6:45 UTC (permalink / raw)
To: damilola, davem, edumazet, fmancera, horms, kuba, netdev, pabeni
Cc: syzbot, syzkaller-bugs
In-Reply-To: <20260407170214.4157-2-fmancera@suse.de>
syzbot ci has tested the following series
[v1] net_sched: fix skb memory leak in deferred qdisc drops
https://lore.kernel.org/all/20260407170214.4157-2-fmancera@suse.de
* [PATCH net] net_sched: fix skb memory leak in deferred qdisc drops
and found the following issue:
WARNING: suspicious RCU usage in cake_dequeue
Full report is available here:
https://ci.syzbot.org/series/90acbb62-3e93-4bb7-af7a-51d555f5956b
***
WARNING: suspicious RCU usage in cake_dequeue
tree: net
URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net.git
base: a9b8b18364fffce4c451e6f6fd218fa4ab646705
arch: amd64
compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
config: https://ci.syzbot.org/builds/a5e0fad0-c9c8-4102-b2af-b4d1c7947ee3/config
syz repro: https://ci.syzbot.org/findings/b1dd239a-f59f-414f-8bde-578258899e94/syz_repro
=============================
WARNING: suspicious RCU usage
syzkaller #0 Not tainted
-----------------------------
./include/net/sch_generic.h:581 suspicious rcu_dereference_check() usage!
other info that might help us debug this:
rcu_scheduler_active = 2, debug_locks = 1
2 locks held by syz.0.51/6016:
#0: ffffffff8e75e640 (rcu_read_lock_bh){....}-{1:3}, at: local_bh_disable include/linux/bottom_half.h:20 [inline]
#0: ffffffff8e75e640 (rcu_read_lock_bh){....}-{1:3}, at: rcu_read_lock_bh include/linux/rcupdate.h:903 [inline]
#0: ffffffff8e75e640 (rcu_read_lock_bh){....}-{1:3}, at: __dev_queue_xmit+0x277/0x3890 net/core/dev.c:4778
#1: ffff8881ad9e8170 (&sch->root_lock_key#31){+...}-{3:3}, at: spin_lock include/linux/spinlock.h:341 [inline]
#1: ffff8881ad9e8170 (&sch->root_lock_key#31){+...}-{3:3}, at: __dev_xmit_skb net/core/dev.c:4241 [inline]
#1: ffff8881ad9e8170 (&sch->root_lock_key#31){+...}-{3:3}, at: __dev_queue_xmit+0xbf2/0x3890 net/core/dev.c:4819
stack backtrace:
CPU: 1 UID: 0 PID: 6016 Comm: syz.0.51 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
lockdep_rcu_suspicious+0x13f/0x1d0 kernel/locking/lockdep.c:6876
qdisc_root_sleeping include/net/sch_generic.h:581 [inline]
qdisc_dequeue_drop include/net/sch_generic.h:1173 [inline]
cake_dequeue+0x23a7/0x4e50 net/sched/sch_cake.c:2240
dequeue_skb net/sched/sch_generic.c:297 [inline]
qdisc_restart net/sched/sch_generic.c:402 [inline]
__qdisc_run+0x26c/0x15d0 net/sched/sch_generic.c:420
qdisc_run include/net/pkt_sched.h:120 [inline]
__dev_xmit_skb net/core/dev.c:4286 [inline]
__dev_queue_xmit+0x1bad/0x3890 net/core/dev.c:4819
packet_snd net/packet/af_packet.c:3076 [inline]
packet_sendmsg+0x3eb6/0x50f0 net/packet/af_packet.c:3108
sock_sendmsg_nosec net/socket.c:727 [inline]
__sock_sendmsg net/socket.c:742 [inline]
__sys_sendto+0x672/0x710 net/socket.c:2206
__do_sys_sendto net/socket.c:2213 [inline]
__se_sys_sendto net/socket.c:2209 [inline]
__x64_sys_sendto+0xde/0x100 net/socket.c:2209
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f077b39c819
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f077c28c028 EFLAGS: 00000246 ORIG_RAX: 000000000000002c
RAX: ffffffffffffffda RBX: 00007f077b615fa0 RCX: 00007f077b39c819
RDX: 000000000000fc13 RSI: 0000200000000800 RDI: 0000000000000003
RBP: 00007f077b432c91 R08: 0000000000000000 R09: fffffffffffffef0
R10: 0000000000000880 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f077b616038 R14: 00007f077b615fa0 R15: 00007fffceb20038
</TASK>
***
If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
Tested-by: syzbot@syzkaller.appspotmail.com
---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.
To test a patch for this bug, please reply with `#syz test`
(should be on a separate line).
The patch should be attached to the email.
Note: arguments like custom git repos and branches are not supported.
^ permalink raw reply
* [PATCH net v2] ppp: require CAP_NET_ADMIN in target netns for unattached ioctls
From: Taegu Ha @ 2026-04-08 6:51 UTC (permalink / raw)
To: netdev
Cc: linux-ppp, qingfang.deng, gnault, jaco, richardbgobert, ericwouds,
Taegu Ha
/dev/ppp open is currently authorized against file->f_cred->user_ns,
while unattached administrative ioctls operate on current->nsproxy->net_ns.
As a result, a local unprivileged user can create a new user namespace
with CLONE_NEWUSER, gain CAP_NET_ADMIN only in that new user namespace,
and still issue PPPIOCNEWUNIT, PPPIOCATTACH, or PPPIOCATTCHAN against
an inherited network namespace.
Require CAP_NET_ADMIN in the user namespace that owns the target network
namespace before handling these unattached PPP administrative ioctls.
This preserves normal pppd operation in the network namespace it is
actually privileged in, while rejecting the userns-only inherited-netns
case.
Fixes: 273ec51dd7ce ("net: ppp_generic - introduce net-namespace functionality v2")
Signed-off-by: Taegu Ha <hataegu0826@gmail.com>
---
drivers/net/ppp/ppp_generic.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index e9b41777be80..4435c594f587 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -1057,6 +1057,12 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
struct ppp_net *pn;
int __user *p = (int __user *)arg;
+ if ((cmd == PPPIOCNEWUNIT ||
+ cmd == PPPIOCATTACH ||
+ cmd == PPPIOCATTCHAN) &&
+ !ns_capable(net->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
+
switch (cmd) {
case PPPIOCNEWUNIT:
/* Create a new ppp unit */
--
2.43.0
^ permalink raw reply related
* [PATCH 0/3] io_uring: fix resource leak issues
From: KobaK @ 2026-04-08 6:54 UTC (permalink / raw)
To: Jens Axboe, Pavel Begunkov
Cc: Keith Busch, Ming Lei, io-uring, netdev, linux-kernel, Koba Ko
From: Koba Ko <kobak@nvidia.com>
Three resource leak fixes found by code audit:
1. memmap: pinned pages and pages array leak on WARN_ON path in
io_region_pin_pages() — mr->pages is never assigned so the caller's
cleanup is a no-op.
2. rsrc: kfree() used instead of io_cache_free() in
io_buffer_register_bvec() error path — bypasses cache return.
3. zcrx: io_import_umem() leaves live pinned pages in a partially
initialized struct on io_account_mem() failure, and
io_release_area_mem() is not idempotent (missing pages = NULL),
creating a double-free hazard.
Koba Ko (3):
io_uring: fix pinned pages and pages array leak in
io_region_pin_pages()
io_uring/rsrc: use io_cache_free for node in io_buffer_register_bvec
error path
io_uring/zcrx: fix resource leak and double-free hazard in
io_import_umem
io_uring/memmap.c | 5 ++++-
io_uring/rsrc.c | 2 +-
io_uring/zcrx.c | 19 +++++++++++++------
3 files changed, 18 insertions(+), 8 deletions(-)
--
2.43.0
^ permalink raw reply
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