* [PATCH net-next v6 0/3] net: dsa: motorcomm: Add LED support
From: David Yang @ 2026-07-09 1:47 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
v5: https://lore.kernel.org/r/20260707064752.1030345-1-mmyangfl@gmail.com
- improve trigger selection
v4: https://lore.kernel.org/r/20260703165241.542195-1-mmyangfl@gmail.com
- fix some reg op typos
v3: https://lore.kernel.org/r/20260701155519.273212-1-mmyangfl@gmail.com
- fix null pointer dereference
- support polarity auto-configuration
v2: https://lore.kernel.org/r/20260629183137.541341-1-mmyangfl@gmail.com
- allocate LED structures only
- eliminate double locking
v1: https://lore.kernel.org/r/20260618202716.2166450-1-mmyangfl@gmail.com
- set up polarity correctly
- do not set up .brightness_get() to prevent dead lock
David Yang (3):
net: dsa: motorcomm: Move to subdirectory
net: dsa: motorcomm: Split SMI module
net: dsa: motorcomm: Add LED support
MAINTAINERS | 2 +-
drivers/net/dsa/Kconfig | 10 +-
drivers/net/dsa/Makefile | 2 +-
drivers/net/dsa/motorcomm/Kconfig | 17 +
drivers/net/dsa/motorcomm/Makefile | 5 +
.../net/dsa/{yt921x.c => motorcomm/chip.c} | 222 +-----
.../net/dsa/{yt921x.h => motorcomm/chip.h} | 14 +-
drivers/net/dsa/motorcomm/leds.c | 630 ++++++++++++++++++
drivers/net/dsa/motorcomm/leds.h | 121 ++++
drivers/net/dsa/motorcomm/smi.c | 157 +++++
drivers/net/dsa/motorcomm/smi.h | 88 +++
11 files changed, 1048 insertions(+), 220 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/Kconfig
create mode 100644 drivers/net/dsa/motorcomm/Makefile
rename drivers/net/dsa/{yt921x.c => motorcomm/chip.c} (96%)
rename drivers/net/dsa/{yt921x.h => motorcomm/chip.h} (99%)
create mode 100644 drivers/net/dsa/motorcomm/leds.c
create mode 100644 drivers/net/dsa/motorcomm/leds.h
create mode 100644 drivers/net/dsa/motorcomm/smi.c
create mode 100644 drivers/net/dsa/motorcomm/smi.h
--
2.53.0
^ permalink raw reply
* [PATCH v7] net: gro: fix double aggregation of flush-marked skbs
From: Shiming Cheng @ 2026-07-09 1:46 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, horms, matthias.bgg,
angelogioacchino.delregno, willemb, daniel.zahka, alice, sd,
eilaimemedsnaimel, imv4bel, nbd, dsahern, netdev, linux-kernel,
linux-arm-kernel, linux-mediatek
Cc: stable, steffen.klassert, lena.wang, shiming.cheng
Commit 0ab03f353d36 ("net-gro: Fix GRO flush when receiving a GSO
packet.") added a flush check to skb_gro_receive(), but
skb_gro_receive_list() lacks the same validation.
As a result, packets marked with NAPI_GRO_CB(skb)->flush may still be
re-aggregated.
This allows already-GRO'd packets with existing frag_list to be
re-aggregated into a new GRO session, corrupting the frag_list chain
structure. When skb_segment() attempts to unpack these malformed packets,
it encounters invalid state and triggers a kernel panic.
Scenario (Tethering/Device forwarding):
1. Driver: Generated aggregated packet P1 via LRO with frag_list
2. Dev A: Receives aggregated fraglist packet and flush flag set
3. Dev A: Re-enters GRO, skb_gro_receive_list() is called
4. Missing flush check allows re-aggregation despite flush flag
5. Frag_list chain becomes corrupted (loops or dangling refs)
6. Dev B: TX path calls skb_segment(), crashes on corrupted frag_list
Root cause in skb_segment():
The check at line ~4891:
if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) &&
(skb_headlen(list_skb) == len || sg)) {
When frag_list is corrupted by double aggregation, when list_skb is
a NULL pointer from skb->next, skb_headlen(list_skb) dereference
NULL/corrupted pointers occurs.
Call Trace:
skb_headlen(NULL skb)
skb_segment
tcp_gso_segment
tcp4_gso_segment
inet_gso_segment
skb_mac_gso_segment
__skb_gso_segment
skb_gso_segment
validate_xmit_skb
validate_xmit_skb_list
sch_direct_xmit
qdisc_restart
__qdisc_run
qdisc_run
net_tx_action
Fix: Add NAPI_GRO_CB(skb)->flush validation to the early-return check in
skb_gro_receive_list(), matching the defensive programming pattern of
skb_gro_receive().
Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.")
Cc: stable@vger.kernel.org
Signed-off-by: Shiming Cheng <shiming.cheng@mediatek.com>
---
net/core/gro.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/net/core/gro.c b/net/core/gro.c
index 35f2f708f010..b413f4a6462b 100644
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -229,7 +229,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
{
- if (unlikely(p->len + skb->len >= 65536))
+ /* make sure to check flush flag and to not merge */
+ if (unlikely(p->len + skb->len >= 65536 ||
+ NAPI_GRO_CB(skb)->flush))
return -E2BIG;
if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
--
2.45.2
^ permalink raw reply related
* Re: [PATCH net-next v2] net: skbuff: optimization of net_zcopy_get() call in pskb_carve helpers
From: Willem de Bruijn @ 2026-07-09 1:26 UTC (permalink / raw)
To: Yun Lu, davem, edumazet, kuba, pabeni, horms, kerneljasonxing,
kuniyu, willemdebruijn.kernel
Cc: mhal, bjorn, jiayuan.chen, netdev
In-Reply-To: <20260708055454.9167-1-luyun_611@163.com>
Yun Lu wrote:
> From: Yun Lu <luyun@kylinos.cn>
>
> Commit 98d0912e9f84 ("net: skbuff: fix missing zerocopy reference in
> pskb_carve helpers") introduced two calls of net_zcopy_get(skb_zcopy(skb)).
> In fact, skb_zcopy() has already been executed once before. When calling
> net_zcopy_get(), skb_zcopy() always returns skb_uarg(skb), which results
> in adding some unnecessary instructions in skb_zcopy. So, change these
> two calls to directly use skb_uarg(skb) instead of skb_zcopy.
>
> In addition, also use net_zcopy_get() instead of refcount_inc() in
> pskb_expand_head() for code consistency.
>
> No functional change intended.
>
> Signed-off-by: Yun Lu <luyun@kylinos.cn>
Reviewed-by: Willem de Bruijn <willemb@google.com>
^ permalink raw reply
* Re: [PATCH v6] net: gro: fix double aggregation of flush-marked skbs
From: Willem de Bruijn @ 2026-07-09 1:24 UTC (permalink / raw)
To: Shiming Cheng (成诗明),
linux-kernel@vger.kernel.org, dsahern@kernel.org,
imv4bel@gmail.com, linux-mediatek@lists.infradead.org,
alice@isovalent.com, daniel.zahka@gmail.com,
eilaimemedsnaimel@gmail.com, nbd@nbd.name, horms@kernel.org,
kuba@kernel.org, pabeni@redhat.com, edumazet@google.com,
willemdebruijn.kernel@gmail.com, willemb@google.com,
netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
matthias.bgg@gmail.com, davem@davemloft.net,
AngeloGioacchino Del Regno, sd@queasysnail.net
Cc: steffen.klassert@secunet.com, stable@vger.kernel.org,
Lena Wang (王娜)
In-Reply-To: <2d71af40897d73dbd9e243ce5e25bbd3f99acc5d.camel@mediatek.com>
Shiming Cheng (成诗明) wrote:
> On Tue, 2026-07-07 at 11:16 -0400, Willem de Bruijn wrote:
> > External email : Please do not click links or open attachments until
> > you have verified the sender or the content.
> >
> >
> > Shiming Cheng wrote:
> > > The skb_gro_receive_list() function is missing a critical safety
> > > check
> > > that exists in the skb_gro_receive() implementation. Specifically,
> > > it
> > > does not validate NAPI_GRO_CB(skb)->flush before allowing packet
> > > aggregation, as of commit 0ab03f353d36 ("net-gro: Fix GRO flush
> > > when receiving a GSO packet.").
> >
> > It does not check .. as of commit .. ?
> >
> > No, skb_gro_receive checkos NAP_GRO_CB(skb)->flush as of that commit.
> >
>
> Is this wording okay?
>
> Commit 0ab03f353d36 ("net-gro: Fix GRO flush when receiving a GSO
> packet.") added a flush check to skb_gro_receive(), but
> skb_gro_receive_list() lacks the same validation.
>
> As a result, packets marked with NAPI_GRO_CB(skb)->flush may still be
> re-aggregated.
That sounds good to me, thanks.
^ permalink raw reply
* Re: [PATCH net-next v6 1/2] udp: fix encapsulation packet resubmit in multicast deliver
From: Willem de Bruijn @ 2026-07-09 1:24 UTC (permalink / raw)
To: Anton Danilov, netdev
Cc: Willem de Bruijn, David S . Miller, David Ahern, Eric Dumazet,
Kuniyuki Iwashima, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan, linux-kselftest
In-Reply-To: <5372ccac062193147e02b991d5328a5c3fa3a85a.1783372173.git.littlesmilingcloud@gmail.com>
Anton Danilov wrote:
> When a UDP encapsulation socket (e.g., FOU) receives a multicast
> packet, __udp4_lib_mcast_deliver() and __udp6_lib_mcast_deliver()
> call consume_skb() when udp_queue_rcv_skb() returns a positive value.
> A positive return value from udp_queue_rcv_skb() indicates that the
> encap_rcv handler (e.g., fou_udp_recv) has consumed the UDP header
> and wants the packet to be resubmitted to the IP protocol handler
> for further processing (e.g., as a GRE packet).
>
> The unicast paths handle this correctly by propagating the return
> value up to ip_protocol_deliver_rcu() / ip6_protocol_deliver_rcu()
> for resubmission. However, the multicast paths destroy the packet
> via consume_skb() instead of resubmitting it, causing silent packet
> loss.
>
> This affects any UDP encapsulation (FOU, GUE) combined with multicast
> destination addresses.
>
> Fix this by returning the value from udp_queue_rcv_skb() when it is
> positive, matching the behavior of the corresponding unicast paths.
> Note the sign difference between IPv4 and IPv6:
>
> - IPv4: udp_unicast_rcv_skb() returns -ret, and
> ip_protocol_deliver_rcu() resubmits when ret < 0
> (using -ret as the protocol number).
> - IPv6: udp6_unicast_rcv_skb() returns ret, and
> ip6_protocol_deliver_rcu() resubmits when ret > 0
> (using ret as the nexthdr).
>
> Both mcast paths now follow the same convention as their respective
> unicast paths.
>
> Suggested-by: Kuniyuki Iwashima <kuniyu@google.com>
> Signed-off-by: Anton Danilov <littlesmilingcloud@gmail.com>
> Assisted-by: Claude:claude-opus-4-6
> Reviewed-by: Willem de Bruijn <willemb@google.com>
> ---
> net/ipv4/udp.c | 6 ++++--
> net/ipv6/udp.c | 6 ++++--
> 2 files changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index 59248a59358c..d3ddcbfc8477 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -2476,6 +2476,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
> struct udp_hslot *hslot;
> struct sk_buff *nskb;
> bool use_hash2;
> + int ret;
>
> hash2_any = 0;
> hash2 = 0;
> @@ -2520,8 +2521,9 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
> }
>
> if (first) {
> - if (udp_queue_rcv_skb(first, skb) > 0)
> - consume_skb(skb);
> + ret = udp_queue_rcv_skb(first, skb);
> + if (ret > 0)
> + return -ret;
This helps the case of one encap_rcv socket in the multicast receiver
group, so is a useful fix on its own.
But is Sashiko correct that this would still leave the same issue for
other sockets in the group? If so, something to address in this series
or leave for later?
Might be worthwhile to extend the test to capture that case too.
^ permalink raw reply
* Re: [PATCH net-next v6 2/2] selftests: net: add FOU multicast encapsulation resubmit test
From: Willem de Bruijn @ 2026-07-09 1:22 UTC (permalink / raw)
To: Anton Danilov, netdev
Cc: Willem de Bruijn, David S . Miller, David Ahern, Eric Dumazet,
Kuniyuki Iwashima, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan, linux-kselftest
In-Reply-To: <a5b65f092d22a12b52fc536c0565b948cd8ecae3.1783372173.git.littlesmilingcloud@gmail.com>
Anton Danilov wrote:
> Add a selftest to verify that FOU-encapsulated packets addressed to a
> multicast destination are correctly resubmitted to the inner protocol
> handler (GRE) via the UDP multicast delivery path. Both IPv4 and IPv6
> paths are tested.
>
> The test creates two network namespaces connected by a veth pair with
> a FOU/GRETAP (IPv4) and FOU/ip6gretap (IPv6) tunnel using multicast
> remote addresses (239.0.0.1 and ff0e::1). Ping is sent through each
> tunnel and received packets are counted on the receiver's tunnel
> interface.
>
> The veth pair is created directly inside the namespaces to avoid
> possible name collisions with devices in the root namespace.
>
> Static neighbor entries are configured on the sender because ARP/ND
> replies from the receiver cannot traverse the unidirectional multicast
> tunnel back to the sender.
>
> The early demux optimization (net.ipv4.ip_early_demux, which controls
> both IPv4 and IPv6) is disabled on the receiver to force packets
> through __udp4_lib_mcast_deliver() / __udp6_lib_mcast_deliver(), which
> is the code path being tested.
>
> Signed-off-by: Anton Danilov <littlesmilingcloud@gmail.com>
> Assisted-by: Claude:claude-opus-4-6
Reviewed-by: Willem de Bruijn <willemb@google.com>
> +setup_ipv4() {
> + # IPv4 FOU (CONFIG_NET_FOU) is built in on kernels configured for
Instead of this distinction and modprobe for IPv6, also set those new
CONFIGs to =Y, so that the two are equivalent?
Not important enough to respin.
^ permalink raw reply
* Re: Ethtool : PRBS feature
From: Lee Trager @ 2026-07-09 0:58 UTC (permalink / raw)
To: Das, Shubham, Srinivasan, Vijay, Andrew Lunn
Cc: Alexander Duyck, Maxime Chevallier, netdev@vger.kernel.org,
mkubecek@suse.cz, D H, Siddaraju, Chintalapalle, Balaji,
Lindberg, Magnus, niklas.damberg@ericsson.com, Wirandi, Jonas
In-Reply-To: <SN7PR11MB8109F7A3F491E20701BDC0DFFFF02@SN7PR11MB8109.namprd11.prod.outlook.com>
On 7/7/26 2:06 AM, Das, Shubham wrote:
> Thanks Andrew, Lee for the feedback.
>
> Lee,
> I don't see prbs11.0, prbs11.1, prbs11.2, prbs11.3, prbs13.0, prbs13.1, prbs13.2, prbs13.3, prbs16 and prbs32 in IEEE 802.3 2022 standard.
> Is this specific to fnic based on base PRBS pattern or it is mentioned in some other standard ?
Those were from the fbnic spec, I'm not sure where they originate from.
I suppose we can drop those for now but its good to keep in mind more
tests may be added in the future.
>
> Each lane and each direction is a completely separate test with its own test of
>> statistics. The test is actually verified on the Rx side, Tx is your generator so you
>> won't have data to collect. So when you run PRBS testing on a 2 lane NIC you are
>> actually running 4 independent tests.
>> While its fine to have a shortcut to run the same test on all lanes we absolutely
>> need a way to run tests per lane and the ability to choose Rx, Tx, or both.
> - Agree, we need lane parameter in commands, Updated command.
>
>
>> I wouldn't consider stats a phy-test action. It shouldn't change the state of the
>> NIC at all. I would just add phy-test-stats as set of standard ethtool statistics.
> Yes moved under separate command.
>
> Below are the updated UAPI, data structures, and Netlink messages to support PRBS/BERT and test pattern configuration.
>
> diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
> index 5e9135e3774f..113005a5f80a 100644
> --- a/Documentation/netlink/specs/ethtool.yaml
> +++ b/Documentation/netlink/specs/ethtool.yaml
> @@ -30,6 +30,35 @@ definitions:
> + -
> + name: phy-test-pattern
> + enum-name: phy-test-pattern
> + type: enum
> + name-prefix: phy-test-pattern-
> + doc: PRBS and other PHY test patterns
> + entries:
> + - off
> + - prbs7
> + - prbs9
> + - prbs11
> + - prbs13
> + - prbs15
> + - prbs23
> + - prbs31
> + - ssprq
> + - prbs13q
> + - prbs31q
> + - square8
>
> + name: phy-test-action
> + enum-name: phy-test-action
> + type: enum
> + name-prefix: phy-test-action-
> + doc: Actions for PHY BERT test control
> + entries:
> + - none
> + - start
> + - stop
>
> + name: phy-test
> + attr-cnt-name: __ethtool-a-phy-test-cnt
> + doc: |
> + PHY test configuration for pattern generation/checking,
> + BERT (Bit Error Rate Test), and statistics.
> + attributes:
> + -
> + name: unspec
> + type: unused
> + value: 0
> + -
> + name: header
> + type: nest
> + nested-attributes: header
> + -
> + name: lane
> + type: u32
> + doc: PHY lane index to target for the test operation
This could be a mask to allow starting and stopping the same test on
multiple lanes at once.
> + -
> + name: tx-pattern
> + type: u32
> + doc: TX test pattern type (PRBS or square8 wave)
> + enum: phy-test-pattern
> + -
> + name: rx-pattern
> + type: u32
> + doc: RX checker pattern type (PRBS or square8 wave)
> + enum: phy-test-pattern
> + -
> + name: bert-action
> + type: u32
> + doc: BERT test start/stop
> + enum: phy-test-action
> + -
> + name: inject-error-count
> + type: u32
> + doc: |
> + Inject a specified number of bit errors into the PHY transmit data
> + stream for diagnostic verification purposes.
> +
> + Context and Purpose:
> + When performing Bit Error Ratio Testing (BERT), the receiving side
> + runs a PRBS checker that monitors for bit errors. Before relying
> + on a zero-error BERT result, operators need to confirm Checker
> + state and configuration to qualify result as TRUE.
> + A checker that is broken or misconfigured would also report
> + zero errors, giving a FALSE pass. Error injection provides this
> + confirmation by deliberately introducing a known number of errors
> + on the transmit side and verifying they appear on the receive side.
> +
> + Note:
> + Receiver under test maybe in the same port as the transmitter
> + (loopback mode) or a different port in the same device or another
> + device connected to the transmitting port (non-loopback mode).
> +
> + Layer and Mechanism:
> + Error injection operates at the PMA/PMD boundary. This is
> + bit-level injection in the serial data stream, not frame-level.
> + The SerDes Built-In Self Test (BIST) block inverts the specified
> + number of bits in the outgoing serial stream. The injection does
> + not distinguish between data frames and test patterns; it
> + corrupts raw bits at the physical layer regardless of what the
> + stream carries.
> +
> + Mode of Operation:
> + This command implements "one-shot" injection: a single burst of
> + N bit errors injected immediately. The PHY inverts exactly N
> + consecutive bits in the serial transmit stream at the PMA/PMD
> + layer, then resumes clean transmission. No continuous/fixed-rate
> + injection mode is provided.
> +
> + Prerequisites:
> + - A PRBS test pattern must be active on the transmitting port
> + (tx-pattern != off).
> + - The receiving port must have the matching rx-pattern configured
> + and ber-lock-status must be "locked" (indicating the checker
> + has synchronized to the incoming pattern).
> + - BERT must be running on the receiving port (bert start issued).
> +
> + Note:
> + Availability of BIST mode test pattern generator, checker and
> + lock indication is IP dependent.
> +
> + Semantics:
> + - Fire-and-forget: the command completes immediately. No
> + persistent state is created. Each invocation is an independent
> + injection event.
> + - The command may be issued multiple times. Each invocation
> + injects an additional burst of errors (counts accumulate on
> + the receiver's ber-error-count across invocations).
> + - If no test pattern is active, the behaviour is
> + implementation-defined (hardware may silently ignore the
> + request or return an error).
> +
> + Expected Outcome:
> + After injecting N errors on the TX port, the far-end receiver's
> + ber-error-count (read via --show-phy-test) should increment by
> + exactly N (within hardware counter precision). This confirms:
> + 1. The PRBS checker is locked and actively counting errors.
> + 2. The data path between TX and RX is intact.
> + 3. The BERT counters are functioning correctly.
> +
> + Example Workflow:
> + # Configure TX pattern on port A
> + ethtool --phy-test eth1 lane 0 tx-pattern prbs31
> + # Configure RX checker on port B, start BERT
> + ethtool --phy-test eth2 lane 0 rx-pattern prbs31
> + ethtool --phy-test eth2 lane 0 bert start
> + # Verify lock
> + ethtool --show-phy-test eth2 lane 0
> + # -> ber-lock-status: locked, ber-error-count: 0
> + # Inject 5 errors from TX side
> + ethtool --phy-test eth1 lane 0 inject-errors 5
> + # Confirm errors were detected
> + ethtool --show-phy-test eth2 lane 0
> + # -> ber-error-count: 5
> + -
> + name: ber-lock-status
> + type: u8
> + doc: PRBS lock status (1=locked, 0=not locked)
> + -
> + name: ber-error-count
> + type: u64
> + doc: BERT bit error count
> + -
> + name: ber-total-bits-sent
> + type: u64
> + doc: BERT total bits sent
nit: Hardware engineers I spoke to expect BER to be a floating point
number which is calculated as errors / total. I dropped "BER" from
everything kernel related to avoid confusion. I provided an awk script
which did the calculation and provided a floating point.
> + -
> + name: supported-test-patterns
> + type: u32
> + doc: Bitmask of supported test patterns
>
> + -
> + name: phy-test-act
> + doc: |
> + Configure PHY test parameters. Each attribute is optional and only
> + specified attributes are applied. TX/RX patterns are set on the
> + local port. BERT and error injection operate on the receiver port.
> + Typical workflow:
> + ethtool --phy-test eth1 lane 0 tx-pattern prbs7 (TX side)
> + ethtool --phy-test eth2 lane 0 rx-pattern prbs7 (RX side)
> + ethtool --phy-test eth2 lane 0 bert start (start BERT on RX)
> + ethtool --phy-test eth1 lane 0 inject-errors 10 (inject 10 errors on TX)
> + ethtool --show-phy-test eth2 lane 0 (read counters, expect +10)
> + ethtool --phy-test eth2 lane 0 bert stop (stop BERT)
I think it would be good to follow what existing tools do. Lane is
optional when not given all lanes are assumed. When specified it can be
one or more comma separated lanes.
https://networking-docs.nvidia.com/mftswum/43018lts/mlxlink-utility
> +
> + attribute-set: phy-test
> +
> + do:
> + request:
> + attributes:
> + - header
> + - lane
> + - tx-pattern
> + - rx-pattern
> + - bert-action
> + - inject-error-count
> + -
> + name: phy-test-get
> + doc: |
> + Get PHY test configuration status, supported patterns, and BERT
> + statistics (lock status, error count, total bits).
> +
> + attribute-set: phy-test
> +
> + do:
> + request:
> + attributes:
> + - header
> + - lane
Lane should be a mask. This way userspace can poll stats while testing
is running which would only require one call.
> + reply:
> + attributes:
> + - header
> + - lane
> + - tx-pattern
> + - rx-pattern
> + - supported-test-patterns
> + - ber-lock-status
> + - ber-error-count
> + - ber-total-bits-sent
>
> mcast-groups:
> list:
>
> - Shubham D
>
> From: Srinivasan, Vijay <vijay.srinivasan@intel.com>
> Sent: 02 July 2026 05:19
> To: Lee Trager <lee@trager.us>; Andrew Lunn <andrew@lunn.ch>
> Cc: Das, Shubham <shubham.das@intel.com>; Alexander Duyck <alexander.duyck@gmail.com>; Maxime Chevallier <maxime.chevallier@bootlin.com>; netdev@vger.kernel.org; mkubecek@suse.cz; D H, Siddaraju <siddaraju.dh@intel.com>; Chintalapalle, Balaji <balaji.chintalapalle@intel.com>; Lindberg, Magnus <magnus.k.lindberg@ericsson.com>; niklas.damberg@ericsson.com; Wirandi, Jonas <jonas.wirandi@ericsson.com>
> Subject: Re: Ethtool : PRBS feature
>
> All good points and noted.
> Will write the specification in general terms with full description of context, usage, configuration, expected outcome etc.
>
> Vijay
>
> ________________________________________
> From: Lee Trager <mailto:lee@trager.us>
> Sent: Wednesday, July 1, 2026 4:28 PM
> To: Andrew Lunn <mailto:andrew@lunn.ch>; Srinivasan, Vijay <mailto:vijay.srinivasan@intel.com>
> Cc: Das, Shubham <mailto:shubham.das@intel.com>; Alexander Duyck <mailto:alexander.duyck@gmail.com>; Maxime Chevallier <mailto:maxime.chevallier@bootlin.com>; mailto:netdev@vger.kernel.org <mailto:netdev@vger.kernel.org>; mailto:mkubecek@suse.cz <mailto:mkubecek@suse.cz>; D H, Siddaraju <mailto:siddaraju.dh@intel.com>; Chintalapalle, Balaji <mailto:balaji.chintalapalle@intel.com>; Lindberg, Magnus <mailto:magnus.k.lindberg@ericsson.com>; mailto:niklas.damberg@ericsson.com <mailto:niklas.damberg@ericsson.com>; Wirandi, Jonas <mailto:jonas.wirandi@ericsson.com>
> Subject: Re: Ethtool : PRBS feature
>
> On 7/1/26 3:02 PM, Andrew Lunn wrote:
>
>> On Wed, Jul 01, 2026 at 09:38:08PM +0000, Srinivasan, Vijay wrote:
>>> Hi Andrew,
>>> I think there is a disconnect here.
>> Which proves my point. The specification is not sufficient if you have
>> to keep correcting me.
>>
>> The kAPI should be understandable by somebody who has a general
>> networking background. Please write a specification with that
>> assumption in mind. Don't assume the reader is a test engineer who has
>> used PRBS for half his life. Assume it is a brand new test engineer
>> who is hearing PRBS for the first time. That is what most engineers on
>> the netdev list are. Me included.
> I think part of the disconnect is that PRBS testing is a signal
> integrity test, not a network test. In this case the phy happens to be
> Ethernet but it could just as easily be PCIE or USB. That is why it was
> heavily suggested to me at netdev 0x19 that this should be done on the
> generic phy layer, not netdev.
>
> Lee
^ permalink raw reply
* Re: [PATCH net-next 2/5] net: phy: mediatek: move MTK GE SoC registers define to dedicated header
From: Andrew Lunn @ 2026-07-08 23:44 UTC (permalink / raw)
To: Christian Marangi
Cc: Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Daniel Golle, Qingfang Deng,
SkyLake Huang, Matthias Brugger, AngeloGioacchino Del Regno,
linux-kernel, netdev, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260708102341.53919-3-ansuelsmth@gmail.com>
On Wed, Jul 08, 2026 at 12:23:28PM +0200, Christian Marangi wrote:
> In preparation for support of special Software Calibration for Airoha
> PHY, move the MTK GE SoC registers define to a dedicated header.
>
> It's also needed to generalize the cal_cycle function as Airoha needs
> only part of its logic (the wait logic) to complete a calibration cycle.
Can this be two patches?
Andrew
^ permalink raw reply
* Re: [PATCH net-next 3/5] net: phy: mediatek: split Airoha code to dedicated source
From: Wayen Yan @ 2026-07-08 16:32 UTC (permalink / raw)
To: netdev
Cc: lorenzo, horms, pabeni, kuba, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Daniel Golle, Qingfang Deng, SkyLake Huang,
linux-kernel
In-Reply-To: <20260708102341.53919-4-ansuelsmth@gmail.com>
Hi Christian,
One minor typo in the new Kconfig help text:
In drivers/net/phy/mediatek/Kconfig, the help text for
AIROHA_GE_SOC_PHY has a leftover "d":
Include support for built-in Ethernet PHYs which are present in
the AN7581 and AN7583 SoCs. These PHYs d will dynamically
^
calibrate during startup.
Should be: "These PHYs will dynamically calibrate during startup."
Otherwise the code split looks clean.
Best,
Wayen
^ permalink raw reply
* Re: [PATCH net-next 5/5] net: phy: mediatek: add calibration logic for AN7583
From: Wayen Yan @ 2026-07-08 16:31 UTC (permalink / raw)
To: netdev
Cc: lorenzo, horms, pabeni, kuba, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Daniel Golle, Qingfang Deng, SkyLake Huang,
linux-kernel
In-Reply-To: <20260708102341.53919-6-ansuelsmth@gmail.com>
Hi Christian,
A few issues in an7583_phy_config_init():
1) Redundant double assignment of mdi_resister_type
shared->mdi_resister_type = MDI_5R;
shared->mdi_resister_type = MDI_5R; /* duplicate */
if (shared->mdi_resister_type == MDI_0R)
shared->r50_cal_tbl = an7583_zcal_to_r50ohm_0R;
if (shared->mdi_resister_type == MDI_5R)
shared->r50_cal_tbl = an7583_zcal_to_r50ohm_5R;
The first assignment is immediately overwritten by the second
identical one. Also the two `if` blocks should be `else if` --
currently both conditions are checked independently and the
MDI_0R branch is dead code (since the value is hardcoded to
MDI_5R).
If the intent is to default to MDI_5R and later read from SoC,
the structure should be:
shared->mdi_resister_type = MDI_5R; /* FIXME: read from SCU */
if (shared->mdi_resister_type == MDI_0R)
shared->r50_cal_tbl = an7583_zcal_to_r50ohm_0R;
else
shared->r50_cal_tbl = an7583_zcal_to_r50ohm_5R;
2) Typo: mdi_resister_type -> mdi_resistor_type (same as patch 4/5)
Also the FIXME comment says "MDI Resister Type" -- should be
"Resistor Type".
3) Dependency on patch 4/5 probe fix
an7583_phy_config_init() uses shared->phydev_p0 which is set in
an7581_phy_probe(). If the probe function from patch 4/5 is not
fixed (missing shared = phy_package_get_priv(phydev)), this will
deref NULL/garbage:
phydev_p0 = shared->phydev_p0;
phy_offset = phydev->mdio.addr - phydev_p0->mdio.addr;
This highlights that patch 4/5's probe fix is a blocker for both
AN7581 and AN7583.
Best,
Wayen
^ permalink raw reply
* Re: [PATCH net-next 4/5] net: phy: mediatek: add calibration logic for AN7581
From: Wayen Yan @ 2026-07-08 16:30 UTC (permalink / raw)
To: netdev
Cc: lorenzo, horms, pabeni, kuba, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Daniel Golle, Qingfang Deng, SkyLake Huang,
linux-kernel
In-Reply-To: <20260708102341.53919-5-ansuelsmth@gmail.com>
Hi Christian,
Thanks for working on this. One critical bug found that will crash
on probe, plus a couple of minor issues.
1) Uninitialized shared pointer in an7581_phy_probe()
The local variable `shared` is declared but never assigned before
use:
static int an7581_phy_probe(struct phy_device *phydev)
{
struct airoha_socphy_shared *shared; /* not initialized */
...
ret = devm_phy_package_join(&phydev->mdio.dev, phydev, 0,
sizeof(struct airoha_socphy_shared));
if (ret)
return ret;
...
if (phydev->mdio.addr == AIROHA_DEFAULT_PORT0_ADDR)
shared->phydev_p0 = phydev; /* writing to uninitialized pointer */
devm_phy_package_join() allocates the shared priv data internally
(accessible via phydev->shared->priv), but the local variable
`shared` itself is never populated. You need to call
phy_package_get_priv(phydev) after the join succeeds before
accessing any shared fields.
Fix:
shared = phy_package_get_priv(phydev);
should be added right after devm_phy_package_join() succeeds.
Without this fix, the first PHY to probe (addr == 0x9) will crash,
and all subsequent PHYs' config_init will dereference an
uninitialized phydev_p0 in every calibration function.
2) Typo: mdi_resister_type -> mdi_resistor_type
The field name "resister" appears in multiple places (enum, struct
field, config_init, FIXME comments). It should be "resistor".
This will be baked into the ABI once merged so worth fixing now:
- enum airoha_mdi_resister_type -> airoha_mdi_resistor_type
- shared->mdi_resister_type -> shared->mdi_resistor_type
- FIXME comment: "MDI Resister Type" -> "MDI Resistor Type"
3) Observation: mdi_resister_type is always MDI_5R but tables have
MDI_0R data
Currently the code hardcodes mdi_resister_type = MDI_5R, so the
MDI_0R entries in an7581_tx_amp_compensation_tbl[] are dead data.
The FIXME suggests this should be read from SCU registers
eventually. Consider adding the MDI_0R branch now (or at minimum
an else) so the code structure is ready when the SCU read is
implemented, and avoid shipping dead table data.
Best,
Wayen
^ permalink raw reply
* Re: [PATCH iproute] ss: Don't re-print an SCTP listen socket as an assoc
From: Xin Long @ 2026-07-08 23:33 UTC (permalink / raw)
To: Jamie Bainbridge; +Cc: netdev, Stephen Hemminger, Phil Sutter, Rustam Kovhaev
In-Reply-To: <4d56e7ac2502cd92d4837d9fb16b9228aa17d599.1783406660.git.jamie.bainbridge@gmail.com>
On Tue, Jul 7, 2026 at 2:48 AM Jamie Bainbridge
<jamie.bainbridge@gmail.com> wrote:
>
> "ss -Sa" prints a LISTEN-state SCTP sock once as a non-assoc when
> is_sctp_assoc() correctly returns false, then again when we have cached
> the inode in sctp_ino, so is_sctp_assoc() returns true.
>
> On the second pass, we try to print the socket state with
> sctp_sstate_name[s->state], but s->state == 10 (SCTP_SS_LISTENING)
> but sctp_sstate_name[] only has 8 entries, so we illegally access beyond
> the end of the name array.
>
> If you are lucky, the space beyond the array is NULL and the argument to
> the print format is printed as "(null)" by the C library:
>
> $ ./misc/ss -San
> State Recv-Q Send-Q Local Address:Port Peer Address:Port
> LISTEN 0 5 192.0.2.10:9001 0.0.0.0:*
> `- (null) 0 5 192.0.2.10:9001 0.0.0.0:*
> `- ESTAB 0 0 192.0.2.10%net1:9001 192.0.2.9:11111
>
> If you are unlucky, the space beyond the array is non-NULL and you
> pass an invalid address to the print format:
>
> $ ./misc/ss -San
> Segmentation fault (core dumped)
>
> The inode is correctly cached at the bottom of inet_show_sock(), so
> check if we've already processed this SCTP LISTEN inode and exit early.
>
> $ ./misc/ss -San
> State Recv-Q Send-Q Local Address:Port Peer Address:Port
> LISTEN 0 5 192.0.2.10:9001 0.0.0.0:*
> `- ESTAB 0 0 192.0.2.10%net1:9001 192.0.2.9:11111
>
> We cannot exit later than this (eg: in sock_state_print()) because by
> then we're already halfway through a new line in inet_stats_print().
>
> Keep the existing check in is_sctp_assoc() because that is needed to
> differentiate related assocs from new endpoints.
>
> Note: there is still a chance of double-printing a listen socket (now
> with the correct format if netlink delivers us listen sockets and their
> assocs with something else in between:
>
> $ ./misc/ss -Sane
> State Recv-Q Send-Q Local Address:Port Peer Address:Port
> > LISTEN 0 5 192.0.2.10:9001 0.0.0.0:* ino:36518
> LISTEN 0 5 192.0.2.10:9002 0.0.0.0:* ino:44485
> `- ESTAB 0 0 192.0.2.10%net1:9002 192.0.2.9:22222 ino:44485
> > LISTEN 0 5 192.0.2.10:9001 0.0.0.0:* ino:36518
> `- ESTAB 0 0 192.0.2.10%net1:9001 122.0.2.9:11111 ino:36518
>
> This behaviour existed before this commit. I don't see a way around this
> except to pre-sort the results from netlink which seems unrealistic.
>
> Fixes: f89d46ad63f6f ("ss: Add support for SCTP protocol")
> Reported-by: Rustam Kovhaev <rkovhaev@gmail.com>
> Signed-off-by: Jamie Bainbridge <jamie.bainbridge@gmail.com>
> ---
> misc/ss.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/misc/ss.c b/misc/ss.c
> index 14e9f27a75321556240a5f290b8bcf51c605a4c2..7e94160f7590c35aa187fe00902be8def7593608 100644
> --- a/misc/ss.c
> +++ b/misc/ss.c
> @@ -3764,6 +3764,14 @@ static bool bpf_map_opts_is_enabled(void)
> static int inet_show_sock(struct nlmsghdr *nlh,
> struct sockstat *s)
> {
> + /* SCTP assocs share the same inode number with their parent endpoint.
> + * If we've seen a LISTEN socket inode before, we've already printed it
> + * and cached the inode at the bottom of this function. We don't want
> + * to re-print the LISTEN socket again. so exit early.
> + */
> + if (s->type == IPPROTO_SCTP && s->state == SS_LISTEN && sctp_ino == s->ino)
> + return 0;
> +
> struct rtattr *tb[INET_DIAG_MAX+1];
> struct inet_diag_msg *r = NLMSG_DATA(nlh);
> unsigned char v6only = 0;
> --
> 2.47.3
>
Hi, Jamie, thanks for identifying the issue.
The same listen was dumped twice continuously, which is abnormal.
I think the issue was introduced in kernel by:
1ba8d77f410d ("sctp_diag: Respect ss adding TCPF_CLOSE to idiag_states")
where the check against TCPF_LISTEN in sctp_ep_dump() is incorrect.
and got fixed (unintentionally) by:
7d8297e26b4e ("sctp: hold socket lock when dumping endpoints in sctp_diag")
Thanks.
^ permalink raw reply
* Re: [PATCH RFC net-next 3/3] net: dsa: mxl862xx: add devlink flash_update and info_get
From: Andrew Lunn @ 2026-07-08 23:24 UTC (permalink / raw)
To: Daniel Golle
Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, linux-kernel, netdev
In-Reply-To: <ak6s363rweBPZhQZ@makrotopia.org>
On Wed, Jul 08, 2026 at 10:02:39PM +0200, Daniel Golle wrote:
> On Wed, Jul 08, 2026 at 07:27:21PM +0200, Andrew Lunn wrote:
> > > + * The flash process takes approximately 15 minutes. Progress is
> > > + * reported via devlink status notifications. After a successful (or
> > > + * failed) flash the driver reprobes the device automatically.
> >
> > Have you tested the failed use case?
> >
> > I assume if the firmware in the flash is invalid, the bootloader does
> > not boot it, and it remains in the bootloader waiting for another
> > attempt. Does this DSA driver still load, so devlink can be used to
> > try again?
>
> No. Without a running the firmware the driver doesn't probe and only
> a special rescue tool allows to recover the hardware.
> Having the DSA driver detect the presence of the switch stuck in
> mcuboot mode and probe without registering any user or CPU ports
> also isn't straight forward.
And that special rescue tool exists?
Why not wrap it in a script which unloads the DSA driver, let it do
its thing, and then reload the DSA driver?
All the other users of devlink flash that i know of can operate while
the device is still running. So it makes sense for it to be part of
the driver, it is something just going on in the background. This
device is different, so i don't really see the advantage of making it
part of the driver.
Andrew
^ permalink raw reply
* Re: [PATCH RFC net-next 2/3] net: dsa: mxl862xx: add SMDIO clause-22 register access
From: Andrew Lunn @ 2026-07-08 23:15 UTC (permalink / raw)
To: Daniel Golle
Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, linux-kernel, netdev
In-Reply-To: <ak6o7Ovekjb_evPs@makrotopia.org>
On Wed, Jul 08, 2026 at 09:45:48PM +0200, Daniel Golle wrote:
> On Wed, Jul 08, 2026 at 07:22:49PM +0200, Andrew Lunn wrote:
> > On Tue, Jul 07, 2026 at 04:16:07PM +0200, Daniel Golle wrote:
> > > Add mxl862xx_smdio_read() and mxl862xx_smdio_write() for clause-22
> > > SMDIO register access. MCUboot rescue mode only exposes clause-22
> > > registers; the existing clause-45 MMD interface is unavailable during
> > > firmware transfer. The MDIO bus lock is held per-transaction (not
> > > across polls) so that SB PDI polling during flash erase does not
> > > starve other MDIO users.
> >
> > What other MDIO users are there? It sounds like once the switch is in
> > rescue mode, switch management is dead. So how can there be users?
>
> The MDIO bus lock refers to the host bus which is used to connect
> the switch management interface. The same bus can also be used to
> connect other unrelated PHYs (eg. to provide a WAN or management
> interface independent of the switch).
Thanks for the explanation. Maybe 'does not starve other non-switch
MDIO users'?
I've not got to the rest of the patch yet, but i wondered if phylib
might still be trying to poll the switches PHYs.
Andrew
^ permalink raw reply
* [PATCH net-next v5 3/3] selftests/net: devmem.py: add check_rx_large_niov
From: Bobby Eshleman @ 2026-07-08 22:55 UTC (permalink / raw)
To: Donald Hunter, Jakub Kicinski, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, Gerd Hoffmann,
Vivek Kasireddy, Sumit Semwal, Christian König, Shuah Khan
Cc: netdev, linux-kernel, dri-devel, linux-media, linaro-mm-sig,
linux-kselftest, sdf, razor, daniel, almasrymina, matttbe,
skhawaja, dw, Joe Damato, Bobby Eshleman
In-Reply-To: <20260708-tcpdm-large-niovs-v5-0-34bf6fac941b@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Add a new devmem test case for binding the dmabuf with rx-buf-size=16K.
The test sweeps RX payload sizes straddling the niov boundary to cover
the sub-niov, exact-niov, and multi-niov RX paths.
Silence pylint invalid-name (`with open() as f`) and too-many-arguments
(ncdevmem_rx grew to 6 args) at file scope.
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
---
tools/testing/selftests/drivers/net/hw/devmem.py | 12 ++++-
.../testing/selftests/drivers/net/hw/devmem_lib.py | 59 +++++++++++++++++++++-
.../testing/selftests/drivers/net/hw/nk_devmem.py | 11 +++-
3 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/devmem.py b/tools/testing/selftests/drivers/net/hw/devmem.py
index 031cf9905f65..47b54e18e7a6 100755
--- a/tools/testing/selftests/drivers/net/hw/devmem.py
+++ b/tools/testing/selftests/drivers/net/hw/devmem.py
@@ -2,7 +2,8 @@
# SPDX-License-Identifier: GPL-2.0
from os import path
-from devmem_lib import setup_test, run_rx, run_tx, run_tx_chunks, run_rx_hds
+from devmem_lib import (setup_test, run_rx, run_tx, run_tx_chunks, run_rx_hds,
+ run_rx_large_niov)
from lib.py import ksft_run, ksft_exit, ksft_disruptive
from lib.py import NetDrvEpEnv
@@ -30,11 +31,18 @@ def check_rx_hds(cfg) -> None:
run_rx_hds(cfg)
+@ksft_disruptive
+def check_rx_large_niov(cfg) -> None:
+ """Run the devmem RX test with rx-buf-size = 16 KiB."""
+ run_rx_large_niov(cfg)
+
+
def main() -> None:
"""Run the devmem test cases."""
with NetDrvEpEnv(__file__) as cfg:
setup_test(cfg, path.abspath(path.dirname(__file__) + "/ncdevmem"))
- ksft_run([check_rx, check_tx, check_tx_chunks, check_rx_hds],
+ ksft_run([check_rx, check_tx, check_tx_chunks, check_rx_hds,
+ check_rx_large_niov],
args=(cfg,))
ksft_exit()
diff --git a/tools/testing/selftests/drivers/net/hw/devmem_lib.py b/tools/testing/selftests/drivers/net/hw/devmem_lib.py
index 0921ff03eb81..7b8557959c40 100644
--- a/tools/testing/selftests/drivers/net/hw/devmem_lib.py
+++ b/tools/testing/selftests/drivers/net/hw/devmem_lib.py
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+# pylint: disable=invalid-name,too-many-arguments
"""Shared helpers for devmem TCP selftests."""
import re
@@ -8,7 +9,7 @@ from lib.py import (bkg, cmd, defer, ethtool, rand_port, wait_port_listen,
NetdevFamily)
-def require_devmem(cfg):
+def require_devmem(cfg, rx_buf_size=0):
"""Probe ncdevmem on cfg.ifname and SKIP the test if devmem isn't supported."""
if not hasattr(cfg, "devmem_probed"):
probe_command = f"{cfg.bin_local} -f {cfg.ifname}"
@@ -18,6 +19,19 @@ def require_devmem(cfg):
if not cfg.devmem_supported:
raise KsftSkipEx("Test requires devmem support")
+ if rx_buf_size > 0:
+ if not hasattr(cfg, "devmem_rx_buf_size_probed"):
+ cfg.devmem_rx_buf_size_probed = {}
+
+ if rx_buf_size not in cfg.devmem_rx_buf_size_probed:
+ probe_command = f"{cfg.bin_local} -f {cfg.ifname} -b {rx_buf_size}"
+ cfg.devmem_rx_buf_size_probed[rx_buf_size] = \
+ cmd(probe_command, fail=False, shell=True).ret == 0
+
+ if not cfg.devmem_rx_buf_size_probed[rx_buf_size]:
+ raise KsftSkipEx(
+ f"Test requires devmem rx-buf-size={rx_buf_size} support")
+
def configure_nic(cfg):
"""Channels, rings, RSS, queue lease for netkit devmem."""
@@ -76,7 +90,8 @@ def set_flow_rule(cfg, port):
return int(re.search(r'ID (\d+)', output).group(1))
-def ncdevmem_rx(cfg, port, verify=True, fail_on_linear=False, flow_steer=False):
+def ncdevmem_rx(cfg, port, verify=True, fail_on_linear=False, flow_steer=False,
+ rx_buf_size=0):
"""Build the ncdevmem RX listener command."""
if hasattr(cfg, 'netns'):
flow_rule_id = set_flow_rule(cfg, port)
@@ -96,6 +111,8 @@ def ncdevmem_rx(cfg, port, verify=True, fail_on_linear=False, flow_steer=False):
extras.append("-v 7")
if fail_on_linear:
extras.append("-L")
+ if rx_buf_size > 0:
+ extras.append(f"-b {rx_buf_size}")
parts = [cfg.bin_local, "-l", f"-f {ifname}", f"-s {addr}",
f"-p {port}", *extras]
@@ -202,6 +219,44 @@ def run_tx_chunks(cfg):
ksft_eq(socat.stdout.strip(), "hello\nworld")
+def _restore_nr_hugepages(hp_file, nr_hugepages):
+ with open(hp_file, 'w', encoding='utf-8') as f:
+ f.write(str(nr_hugepages))
+
+
+def run_rx_large_niov(cfg):
+ """Run the devmem RX test with a large niov (rx-buf-size > PAGE_SIZE).
+
+ Sweep payload sizes that straddle the niov boundary: below, equal to,
+ and above rx_buf_size, to exercise sub-niov, exact-niov, and multi-niov
+ RX paths.
+ """
+ hp_file = "/proc/sys/vm/nr_hugepages"
+ with open(hp_file, 'r+', encoding='utf-8') as f:
+ nr_hugepages = int(f.read().strip())
+ if nr_hugepages < 64:
+ f.seek(0)
+ f.write("64")
+ defer(_restore_nr_hugepages, hp_file, nr_hugepages)
+ require_devmem(cfg, rx_buf_size=16384)
+ configure_nic(cfg)
+ netns = getattr(cfg, "netns", None)
+
+ for size in [1024, 4096, 8192, 16384, 32768, 65536]:
+ port = rand_port()
+ socat = socat_send(cfg, port)
+ listen_cmd = ncdevmem_rx(cfg, port,
+ flow_steer=not netns,
+ rx_buf_size=16384)
+ data_pipe = (f"yes $(echo -e \x01\x02\x03\x04\x05\x06) | "
+ f"head -c {size} | {socat}")
+ with bkg(listen_cmd, exit_wait=True, ns=netns) as ncdevmem:
+ wait_port_listen(port, proto="tcp", ns=netns)
+ cmd(data_pipe, host=cfg.remote, shell=True)
+ ksft_eq(ncdevmem.ret, 0,
+ f"large-niov failed for payload size {size}")
+
+
def run_rx_hds(cfg):
"""Run the HDS test by running devmem RX across a segment size sweep."""
require_devmem(cfg)
diff --git a/tools/testing/selftests/drivers/net/hw/nk_devmem.py b/tools/testing/selftests/drivers/net/hw/nk_devmem.py
index 300ed2a70ab4..7f1867e4ff32 100755
--- a/tools/testing/selftests/drivers/net/hw/nk_devmem.py
+++ b/tools/testing/selftests/drivers/net/hw/nk_devmem.py
@@ -3,7 +3,8 @@
"""Test devmem TCP with netkit."""
import os
-from devmem_lib import setup_test, run_rx, run_tx, run_tx_chunks, run_rx_hds
+from devmem_lib import (setup_test, run_rx, run_tx, run_tx_chunks, run_rx_hds,
+ run_rx_large_niov)
from lib.py import ksft_run, ksft_exit, ksft_disruptive
from lib.py import NetDrvContEnv
@@ -31,6 +32,12 @@ def check_nk_rx_hds(cfg) -> None:
run_rx_hds(cfg)
+@ksft_disruptive
+def check_nk_rx_large_niov(cfg) -> None:
+ """Run the devmem RX large-niov test through netkit."""
+ run_rx_large_niov(cfg)
+
+
def main() -> None:
"""Run the netkit devmem test cases."""
with NetDrvContEnv(__file__, rxqueues=2, primary_rx_redirect=True) as cfg:
@@ -38,7 +45,7 @@ def main() -> None:
os.path.join(os.path.dirname(os.path.abspath(__file__)),
"ncdevmem"))
ksft_run([check_nk_rx, check_nk_tx, check_nk_tx_chunks,
- check_nk_rx_hds], args=(cfg,))
+ check_nk_rx_hds, check_nk_rx_large_niov], args=(cfg,))
ksft_exit()
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v5 2/3] selftests/net: ncdevmem: add -b option to set rx-buf-size on bind
From: Bobby Eshleman @ 2026-07-08 22:55 UTC (permalink / raw)
To: Donald Hunter, Jakub Kicinski, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, Gerd Hoffmann,
Vivek Kasireddy, Sumit Semwal, Christian König, Shuah Khan
Cc: netdev, linux-kernel, dri-devel, linux-media, linaro-mm-sig,
linux-kselftest, sdf, razor, daniel, almasrymina, matttbe,
skhawaja, dw, Joe Damato, Bobby Eshleman
In-Reply-To: <20260708-tcpdm-large-niovs-v5-0-34bf6fac941b@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Add -b <bytes> to request a non-default niov size via
NETDEV_A_DMABUF_RX_BUF_SIZE. When the value exceeds PAGE_SIZE,
udmabuf_alloc() switches to an MFD_HUGETLB-backed memfd so each 2 MB
hugepage produces one naturally-aligned sg entry.
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
---
tools/testing/selftests/drivers/net/hw/ncdevmem.c | 36 +++++++++++++++++++++--
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
index d96e8a3b5a65..a16e55af51ee 100644
--- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c
+++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
@@ -40,6 +40,7 @@
#include <linux/uio.h>
#include <stdarg.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -61,6 +62,7 @@
#include <sys/time.h>
#include <linux/memfd.h>
+#include <sys/param.h>
#include <linux/dma-buf.h>
#include <linux/errqueue.h>
#include <linux/udmabuf.h>
@@ -79,6 +81,7 @@
#define PAGE_SHIFT 12
#define TEST_PREFIX "ncdevmem"
#define NUM_PAGES 16000
+#define MB(x) ((x) << 20)
#ifndef MSG_SOCK_DEVMEM
#define MSG_SOCK_DEVMEM 0x2000000
@@ -100,6 +103,7 @@ static unsigned int dmabuf_id;
static uint32_t tx_dmabuf_id;
static int waittime_ms = 500;
static bool fail_on_linear;
+static uint32_t rx_buf_size;
/* System state loaded by current_config_load() */
#define MAX_FLOWS 8
@@ -142,6 +146,7 @@ static struct memory_buffer *udmabuf_alloc(size_t size)
{
struct udmabuf_create create;
struct memory_buffer *ctx;
+ unsigned int memfd_flags;
int ret;
ctx = malloc(sizeof(*ctx));
@@ -156,9 +161,14 @@ static struct memory_buffer *udmabuf_alloc(size_t size)
goto err_free_ctx;
}
- ctx->memfd = memfd_create("udmabuf-test", MFD_ALLOW_SEALING);
+ memfd_flags = MFD_ALLOW_SEALING;
+ if (rx_buf_size > getpagesize())
+ memfd_flags |= MFD_HUGETLB | MFD_HUGE_2MB;
+
+ ctx->memfd = memfd_create("udmabuf-test", memfd_flags);
if (ctx->memfd < 0) {
- pr_err("[skip,no-memfd]");
+ pr_err("[skip,no-memfd%s]",
+ (memfd_flags & MFD_HUGETLB) ? " (need hugepages)" : "");
goto err_close_dev;
}
@@ -168,6 +178,11 @@ static struct memory_buffer *udmabuf_alloc(size_t size)
goto err_close_memfd;
}
+ if (memfd_flags & MFD_HUGETLB) {
+ size = roundup(size, MB(2));
+ ctx->size = size;
+ }
+
ret = ftruncate(ctx->memfd, size);
if (ret == -1) {
pr_err("[FAIL,memfd-truncate]");
@@ -699,6 +714,8 @@ static int bind_rx_queue(unsigned int ifindex, unsigned int dmabuf_fd,
netdev_bind_rx_req_set_ifindex(req, ifindex);
netdev_bind_rx_req_set_fd(req, dmabuf_fd);
__netdev_bind_rx_req_set_queues(req, queues, n_queue_index);
+ if (rx_buf_size)
+ netdev_bind_rx_req_set_rx_buf_size(req, rx_buf_size);
rsp = netdev_bind_rx(*ys, req);
if (!rsp) {
@@ -1411,7 +1428,7 @@ int main(int argc, char *argv[])
int is_server = 0, opt;
int ret, err = 1;
- while ((opt = getopt(argc, argv, "Lls:c:p:v:q:t:f:z:n")) != -1) {
+ while ((opt = getopt(argc, argv, "Lls:c:p:v:q:t:f:z:nb:")) != -1) {
switch (opt) {
case 'L':
fail_on_linear = true;
@@ -1446,6 +1463,19 @@ int main(int argc, char *argv[])
case 'n':
skip_config = 1;
break;
+ case 'b': {
+ unsigned long val;
+
+ errno = 0;
+ val = strtoul(optarg, NULL, 0);
+ if ((val == ULONG_MAX && errno == ERANGE) ||
+ val > UINT32_MAX) {
+ pr_err("invalid rx_buf_size: %s", optarg);
+ return 1;
+ }
+ rx_buf_size = val;
+ break;
+ }
case '?':
fprintf(stderr, "unknown option: %c\n", optopt);
break;
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v5 1/3] net: devmem: allow rx-buf-size > PAGE_SIZE per dmabuf binding
From: Bobby Eshleman @ 2026-07-08 22:55 UTC (permalink / raw)
To: Donald Hunter, Jakub Kicinski, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, Gerd Hoffmann,
Vivek Kasireddy, Sumit Semwal, Christian König, Shuah Khan
Cc: netdev, linux-kernel, dri-devel, linux-media, linaro-mm-sig,
linux-kselftest, sdf, razor, daniel, almasrymina, matttbe,
skhawaja, dw, Joe Damato, Bobby Eshleman
In-Reply-To: <20260708-tcpdm-large-niovs-v5-0-34bf6fac941b@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Every devmem dmabuf binding today hands the page_pool PAGE_SIZE niovs.
This caps a single RX descriptor at PAGE_SIZE, burning CPU on buffer
churn for large flows.
Add a bind-time netlink attribute, NETDEV_A_DMABUF_RX_BUF_SIZE, that
lets userspace request a larger niov size. The value must be a power of
two >= PAGE_SIZE.
Measurements:
Setup: kperf in devmem RX/TX cuda mode, 4 flows, 64 MB messages, 60s,
dctcp, num-rx-queues=4, dmabuf-rx/tx-size-mb=2048, 10 runs per niov
size, mlx5.
CPU Util:
niov net sirq % net idle % app sys % app idle %
----- ---------------- ---------------- ---------------- ----------------
4K 62.38 +/- 8.27 33.40 +/- 7.51 54.15 +/- 10.23 43.67 +/- 10.53
16K 58.91 +/- 5.35 35.23 +/- 5.88 41.05 +/- 8.87 56.42 +/- 9.24
32K 64.12 +/- 0.68 31.09 +/- 1.48 44.54 +/- 3.51 52.63 +/- 3.65
64K 54.69 +/- 5.54 39.67 +/- 5.81 35.47 +/- 3.11 61.97 +/- 3.27
RX app sys % drops ~19% from 4K to 64K.
Throughput:
niov RX dev Gbps RX flow avg Gbps
----- ---------------- -----------------
4K 300.63 +/- 53.21 75.16 +/- 13.30
16K 321.35 +/- 28.20 80.34 +/- 7.05
32K 347.63 +/- 2.20 86.91 +/- 0.55
64K 332.11 +/- 14.26 83.03 +/- 3.56
Throughput seems to increase, but the stdev is pretty wide so could just
be noise.
kperf support (not yet merged):
https://github.com/facebookexperimental/kperf/commit/8837577f920876bce6986ec18869ac04439ebcd2
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Reviewed-by: Mina Almasry <almasrymina@google.com>
---
Documentation/netlink/specs/netdev.yaml | 8 ++++++
include/uapi/linux/netdev.h | 1 +
net/core/devmem.c | 51 +++++++++++++++++++--------------
net/core/devmem.h | 13 ++++++---
net/core/netdev-genl-gen.c | 5 ++--
net/core/netdev-genl.c | 19 ++++++++++--
tools/include/uapi/linux/netdev.h | 1 +
7 files changed, 69 insertions(+), 29 deletions(-)
diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
index 5f143da7458c..70b902008bd3 100644
--- a/Documentation/netlink/specs/netdev.yaml
+++ b/Documentation/netlink/specs/netdev.yaml
@@ -598,6 +598,13 @@ attribute-sets:
type: u32
checks:
min: 1
+ -
+ name: rx-buf-size
+ doc: |
+ Size in bytes of each RX buffer the NIC writes into from the bound
+ dmabuf. Must be a power of two and >= PAGE_SIZE; defaults to
+ PAGE_SIZE.
+ type: u32
operations:
list:
@@ -812,6 +819,7 @@ operations:
- ifindex
- fd
- queues
+ - rx-buf-size
reply:
attributes:
- id
diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h
index 2f3ab75e8cc0..85e1d20c6268 100644
--- a/include/uapi/linux/netdev.h
+++ b/include/uapi/linux/netdev.h
@@ -219,6 +219,7 @@ enum {
NETDEV_A_DMABUF_QUEUES,
NETDEV_A_DMABUF_FD,
NETDEV_A_DMABUF_ID,
+ NETDEV_A_DMABUF_RX_BUF_SIZE,
__NETDEV_A_DMABUF_MAX,
NETDEV_A_DMABUF_MAX = (__NETDEV_A_DMABUF_MAX - 1)
diff --git a/net/core/devmem.c b/net/core/devmem.c
index 957d6b96216b..3ce3cc14bec0 100644
--- a/net/core/devmem.c
+++ b/net/core/devmem.c
@@ -46,7 +46,7 @@ static dma_addr_t net_devmem_get_dma_addr(const struct net_iov *niov)
owner = net_devmem_iov_to_chunk_owner(niov);
return owner->base_dma_addr +
- ((dma_addr_t)net_iov_idx(niov) << PAGE_SHIFT);
+ ((dma_addr_t)net_iov_idx(niov) << owner->binding->niov_shift);
}
static void net_devmem_dmabuf_binding_release(struct percpu_ref *ref)
@@ -93,13 +93,14 @@ net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding)
ssize_t offset;
ssize_t index;
- dma_addr = gen_pool_alloc_owner(binding->chunk_pool, PAGE_SIZE,
+ dma_addr = gen_pool_alloc_owner(binding->chunk_pool,
+ 1UL << binding->niov_shift,
(void **)&owner);
if (!dma_addr)
return NULL;
offset = dma_addr - owner->base_dma_addr;
- index = offset / PAGE_SIZE;
+ index = offset >> binding->niov_shift;
niov = &owner->area.niovs[index];
niov->desc.pp_magic = 0;
@@ -113,12 +114,13 @@ void net_devmem_free_dmabuf(struct net_iov *niov)
{
struct net_devmem_dmabuf_binding *binding = net_devmem_iov_binding(niov);
unsigned long dma_addr = net_devmem_get_dma_addr(niov);
+ size_t niov_size = 1UL << binding->niov_shift;
if (WARN_ON(!gen_pool_has_addr(binding->chunk_pool, dma_addr,
- PAGE_SIZE)))
+ niov_size)))
return;
- gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE);
+ gen_pool_free(binding->chunk_pool, dma_addr, niov_size);
}
void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
@@ -163,6 +165,9 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
u32 xa_idx;
int err;
+ if (binding->niov_shift != PAGE_SHIFT)
+ mp_params.rx_page_size = 1U << binding->niov_shift;
+
err = netif_mp_open_rxq(dev, rxq_idx, &mp_params, extack);
if (err)
return err;
@@ -184,14 +189,16 @@ struct net_devmem_dmabuf_binding *
net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
- unsigned int dmabuf_fd, struct netdev_nl_sock *priv,
+ unsigned int dmabuf_fd, unsigned int niov_shift,
+ struct netdev_nl_sock *priv,
struct netlink_ext_ack *extack)
{
struct net_devmem_dmabuf_binding *binding;
+ size_t niov_size = 1UL << niov_shift;
static u32 id_alloc_next;
+ unsigned int sg_idx, i;
struct scatterlist *sg;
struct dma_buf *dmabuf;
- unsigned int sg_idx, i;
unsigned long virtual;
int err;
@@ -213,6 +220,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
binding->dev = dev;
binding->vdev = vdev;
+ binding->niov_shift = niov_shift;
xa_init_flags(&binding->bound_rxqs, XA_FLAGS_ALLOC);
err = percpu_ref_init(&binding->ref,
@@ -248,18 +256,14 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
goto err_unmap;
}
binding->tx_vec = kvmalloc_objs(struct net_iov *,
- dmabuf->size / PAGE_SIZE);
+ dmabuf->size >> niov_shift);
if (!binding->tx_vec) {
err = -ENOMEM;
goto err_unmap;
}
}
- /* For simplicity we expect to make PAGE_SIZE allocations, but the
- * binding can be much more flexible than that. We may be able to
- * allocate MTU sized chunks here. Leave that for future work...
- */
- binding->chunk_pool = gen_pool_create(PAGE_SHIFT,
+ binding->chunk_pool = gen_pool_create(niov_shift,
dev_to_node(&dev->dev));
if (!binding->chunk_pool) {
err = -ENOMEM;
@@ -273,9 +277,12 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
size_t len = sg_dma_len(sg);
struct net_iov *niov;
- if (!IS_ALIGNED(len, PAGE_SIZE)) {
+ if (!IS_ALIGNED(dma_addr, niov_size) ||
+ !IS_ALIGNED(len, niov_size)) {
err = -EINVAL;
- NL_SET_ERR_MSG(extack, "dma-buf SG length must be PAGE_SIZE aligned");
+ NL_SET_ERR_MSG_FMT(extack,
+ "dmabuf sg entry (addr=%pad, len=%zu) not aligned to niov size %zu",
+ &dma_addr, len, niov_size);
goto err_free_chunks;
}
@@ -288,7 +295,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
owner->area.base_virtual = virtual;
owner->base_dma_addr = dma_addr;
- owner->area.num_niovs = len / PAGE_SIZE;
+ owner->area.num_niovs = len >> niov_shift;
owner->binding = binding;
err = gen_pool_add_owner(binding->chunk_pool, dma_addr,
@@ -313,7 +320,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
page_pool_set_dma_addr_netmem(net_iov_to_netmem(niov),
net_devmem_get_dma_addr(niov));
if (direction == DMA_TO_DEVICE)
- binding->tx_vec[owner->area.base_virtual / PAGE_SIZE + i] = niov;
+ binding->tx_vec[(owner->area.base_virtual >> niov_shift) + i] = niov;
}
virtual += len;
@@ -430,13 +437,15 @@ struct net_iov *
net_devmem_get_niov_at(struct net_devmem_dmabuf_binding *binding,
size_t virt_addr, size_t *off, size_t *size)
{
+ size_t niov_size = 1UL << binding->niov_shift;
+
if (virt_addr >= binding->dmabuf->size)
return NULL;
- *off = virt_addr % PAGE_SIZE;
- *size = PAGE_SIZE - *off;
+ *off = virt_addr & (niov_size - 1);
+ *size = niov_size - *off;
- return binding->tx_vec[virt_addr / PAGE_SIZE];
+ return binding->tx_vec[virt_addr >> binding->niov_shift];
}
/*** "Dmabuf devmem memory provider" ***/
@@ -454,7 +463,7 @@ int mp_dmabuf_devmem_init(struct page_pool *pool)
pool->dma_sync = false;
pool->dma_sync_for_cpu = false;
- if (pool->p.order != 0)
+ if (pool->p.order != binding->niov_shift - PAGE_SHIFT)
return -E2BIG;
net_devmem_dmabuf_binding_get(binding);
diff --git a/net/core/devmem.h b/net/core/devmem.h
index 3852a56036cb..4a293a7d1149 100644
--- a/net/core/devmem.h
+++ b/net/core/devmem.h
@@ -71,6 +71,8 @@ struct net_devmem_dmabuf_binding {
*/
struct net_iov **tx_vec;
+ unsigned int niov_shift;
+
struct work_struct unbind_w;
};
@@ -93,7 +95,8 @@ struct net_devmem_dmabuf_binding *
net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
- unsigned int dmabuf_fd, struct netdev_nl_sock *priv,
+ unsigned int dmabuf_fd, unsigned int niov_shift,
+ struct netdev_nl_sock *priv,
struct netlink_ext_ack *extack);
struct net_devmem_dmabuf_binding *net_devmem_lookup_dmabuf(u32 id);
void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding);
@@ -122,10 +125,11 @@ static inline u32 net_devmem_iov_binding_id(const struct net_iov *niov)
static inline unsigned long net_iov_virtual_addr(const struct net_iov *niov)
{
- struct net_iov_area *owner = net_iov_owner(niov);
+ struct dmabuf_genpool_chunk_owner *co =
+ net_devmem_iov_to_chunk_owner(niov);
- return owner->base_virtual +
- ((unsigned long)net_iov_idx(niov) << PAGE_SHIFT);
+ return net_iov_owner(niov)->base_virtual +
+ ((unsigned long)net_iov_idx(niov) << co->binding->niov_shift);
}
static inline bool
@@ -175,6 +179,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
unsigned int dmabuf_fd,
+ unsigned int niov_shift,
struct netdev_nl_sock *priv,
struct netlink_ext_ack *extack)
{
diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c
index d18c89b5a6c7..447ed06d8c74 100644
--- a/net/core/netdev-genl-gen.c
+++ b/net/core/netdev-genl-gen.c
@@ -106,10 +106,11 @@ static const struct nla_policy netdev_qstats_get_nl_policy[NETDEV_A_QSTATS_SCOPE
};
/* NETDEV_CMD_BIND_RX - do */
-static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_FD + 1] = {
+static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_RX_BUF_SIZE + 1] = {
[NETDEV_A_DMABUF_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),
[NETDEV_A_DMABUF_FD] = { .type = NLA_U32, },
[NETDEV_A_DMABUF_QUEUES] = NLA_POLICY_NESTED(netdev_queue_id_nl_policy),
+ [NETDEV_A_DMABUF_RX_BUF_SIZE] = { .type = NLA_U32, },
};
/* NETDEV_CMD_NAPI_SET - do */
@@ -219,7 +220,7 @@ static const struct genl_split_ops netdev_nl_ops[] = {
.cmd = NETDEV_CMD_BIND_RX,
.doit = netdev_nl_bind_rx_doit,
.policy = netdev_bind_rx_nl_policy,
- .maxattr = NETDEV_A_DMABUF_FD,
+ .maxattr = NETDEV_A_DMABUF_RX_BUF_SIZE,
.flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c
index c15d8d4ca1f8..82089dac000f 100644
--- a/net/core/netdev-genl.c
+++ b/net/core/netdev-genl.c
@@ -1013,6 +1013,7 @@ netdev_nl_get_dma_dev(struct net_device *netdev, unsigned long *rxq_bitmap,
int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net_devmem_dmabuf_binding *binding;
+ unsigned int niov_shift = PAGE_SHIFT;
u32 ifindex, dmabuf_fd, rxq_idx;
struct netdev_nl_sock *priv;
struct net_device *netdev;
@@ -1030,6 +1031,19 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]);
dmabuf_fd = nla_get_u32(info->attrs[NETDEV_A_DMABUF_FD]);
+ if (info->attrs[NETDEV_A_DMABUF_RX_BUF_SIZE]) {
+ u32 rx_buf_size = nla_get_u32(info->attrs[NETDEV_A_DMABUF_RX_BUF_SIZE]);
+
+ if (!rx_buf_size || !is_power_of_2(rx_buf_size) ||
+ rx_buf_size < PAGE_SIZE) {
+ NL_SET_ERR_MSG_FMT(info->extack,
+ "rx_buf_size %u must be a power of 2 >= page size (%lu)",
+ rx_buf_size, PAGE_SIZE);
+ return -EINVAL;
+ }
+ niov_shift = ilog2(rx_buf_size);
+ }
+
priv = genl_sk_priv_get(&netdev_nl_family, NETLINK_CB(skb).sk);
if (IS_ERR(priv))
return PTR_ERR(priv);
@@ -1080,7 +1094,8 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
}
binding = net_devmem_bind_dmabuf(netdev, NULL, dma_dev, DMA_FROM_DEVICE,
- dmabuf_fd, priv, info->extack);
+ dmabuf_fd, niov_shift, priv,
+ info->extack);
if (IS_ERR(binding)) {
err = PTR_ERR(binding);
goto err_rxq_bitmap;
@@ -1221,7 +1236,7 @@ int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info)
binding = net_devmem_bind_dmabuf(bind_dev,
bind_dev != netdev ? netdev : NULL,
dma_dev, DMA_TO_DEVICE, dmabuf_fd,
- priv, info->extack);
+ PAGE_SHIFT, priv, info->extack);
if (IS_ERR(binding)) {
err = PTR_ERR(binding);
goto err_unlock_bind_dev;
diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h
index 2f3ab75e8cc0..85e1d20c6268 100644
--- a/tools/include/uapi/linux/netdev.h
+++ b/tools/include/uapi/linux/netdev.h
@@ -219,6 +219,7 @@ enum {
NETDEV_A_DMABUF_QUEUES,
NETDEV_A_DMABUF_FD,
NETDEV_A_DMABUF_ID,
+ NETDEV_A_DMABUF_RX_BUF_SIZE,
__NETDEV_A_DMABUF_MAX,
NETDEV_A_DMABUF_MAX = (__NETDEV_A_DMABUF_MAX - 1)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v5 0/3] net: devmem: allow rx-buf-size > PAGE_SIZE per binding
From: Bobby Eshleman @ 2026-07-08 22:55 UTC (permalink / raw)
To: Donald Hunter, Jakub Kicinski, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, Gerd Hoffmann,
Vivek Kasireddy, Sumit Semwal, Christian König, Shuah Khan
Cc: netdev, linux-kernel, dri-devel, linux-media, linaro-mm-sig,
linux-kselftest, sdf, razor, daniel, almasrymina, matttbe,
skhawaja, dw, Joe Damato, Bobby Eshleman
Every devmem dmabuf binding hands the page_pool PAGE_SIZE niovs today.
On NICs that consume one descriptor per netmem, this caps a single RX
descriptor at PAGE_SIZE and burns CPU on buffer churn.
In this series, we add a bind-time netlink attribute,
NETDEV_A_DMABUF_RX_BUF_SIZE, that lets userspace request a larger niov
size (power of two >= PAGE_SIZE). Drivers must opt in via
queue_mgmt_ops.QCFG_RX_PAGE_SIZE.
Measurements:
Setup: kperf devmem RX/TX cuda, 4 flows, 64 MB messages, 60s, dctcp,
num-rx-queues=4, dmabuf-rx/tx-size-mb=2048, 10 runs per niov size,
mlx5.
niov RX dev Gbps RX flow avg Gbps app sys %
----- ---------------- ----------------- ----------------
4K 300.63 +/- 53.21 75.16 +/- 13.30 54.15 +/- 10.23
16K 321.35 +/- 28.20 80.34 +/- 7.05 41.05 +/- 8.87
32K 347.63 +/- 2.20 86.91 +/- 0.55 44.54 +/- 3.51
64K 332.11 +/- 14.26 83.03 +/- 3.56 35.47 +/- 3.11
RX app sys % drops ~19% from 4K to 64K.
kperf support (not yet merged):
https://github.com/facebookexperimental/kperf/commit/8837577f920876bce6986ec18869ac04439ebcd2
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Changes in v5:
- removed unnecessary change from ssize_t to size_t (Mina)
- removed '--------' lines in the commit message (Paolo)
- removed commit msg about CONFIG_HUGETLB since that change was already
merged
- Link to v4: https://lore.kernel.org/r/20260701-tcpdm-large-niovs-v4-0-ca4654f37570@meta.com
Changes in v4:
- ncdevmem: fix the possible overflow in ncdevmem (Sashiko)
- drop the udmabuf patch because the fix is now already in net-next
- silenced two pylint complaints in devmem_lib.py
- Link to v3: https://lore.kernel.org/r/20260612-tcpdm-large-niovs-v3-0-a3b693e76fcb@meta.com
Changes in v3:
- fix a bunch of non-reverse christmas tree declarations (Stan)
- remove extra uint32 cast for getpagesize() (Stan)
- remove overzealous strtoul checking (Stan)
- remove value checks that the kernel already performs on rx_buf_size
(Stan)
- Link to v2: https://lore.kernel.org/r/20260611-tcpdm-large-niovs-v2-0-ee2bf15e7523@meta.com
Changes in v2:
- Use NL_SET_ERR_MSG_FMT for sg alignment failure details (Stan)
- Keep -E2BIG (not a direct ask, but seemed preferred, Stan)
- Update udmabuf commit message and comments explaining why
"one sg ent per folio" is useful (Christian)
- Set/restore nr_hugepages in py harness (Stan)
- Link to v1: https://lore.kernel.org/r/20260603-tcpdm-large-niovs-v1-0-f37a4ac6726c@meta.com
---
Bobby Eshleman (3):
net: devmem: allow rx-buf-size > PAGE_SIZE per dmabuf binding
selftests/net: ncdevmem: add -b option to set rx-buf-size on bind
selftests/net: devmem.py: add check_rx_large_niov
Documentation/netlink/specs/netdev.yaml | 8 +++
include/uapi/linux/netdev.h | 1 +
net/core/devmem.c | 51 +++++++++++--------
net/core/devmem.h | 13 +++--
net/core/netdev-genl-gen.c | 5 +-
net/core/netdev-genl.c | 19 ++++++-
tools/include/uapi/linux/netdev.h | 1 +
tools/testing/selftests/drivers/net/hw/devmem.py | 12 ++++-
.../testing/selftests/drivers/net/hw/devmem_lib.py | 59 +++++++++++++++++++++-
tools/testing/selftests/drivers/net/hw/ncdevmem.c | 36 +++++++++++--
.../testing/selftests/drivers/net/hw/nk_devmem.py | 11 +++-
11 files changed, 178 insertions(+), 38 deletions(-)
---
base-commit: 474cff6868129755cf889edf40d7f491729fc588
change-id: 20260602-tcpdm-large-niovs-56523a3a1077
Best regards,
--
Bobby Eshleman <bobbyeshleman@meta.com>
^ permalink raw reply
* [PATCH] ovpn: prevent UAF re-add to by_transp_addr on float-vs-delete race
From: Ibrahim Hashimov @ 2026-07-08 22:46 UTC (permalink / raw)
To: Antonio Quartulli, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: Sabrina Dubroca, netdev, linux-kernel, stable
ovpn_peer_endpoints_update() reacts to a data-channel "float" (a
peer's packets arriving from a new source transport address) by
first committing the new endpoint under peer->lock, then dropping
peer->lock, and only afterwards re-acquiring peer->ovpn->lock and
peer->lock to rehash the peer into peers->by_transp_addr:
spin_unlock_bh(&peer->lock);
ovpn_nl_peer_float_notify(peer, &ss);
if (peer->ovpn->mode == OVPN_MODE_MP) {
spin_lock_bh(&peer->ovpn->lock);
spin_lock_bh(&peer->lock);
bind = rcu_dereference_protected(peer->bind, ...);
if (unlikely(!bind)) {
... return;
}
...
hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr);
nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, ...);
hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead);
...
}
Between the spin_unlock_bh(&peer->lock) and the re-acquire of
peer->ovpn->lock, this thread holds *no* lock on the peer at all. If
an OVPN_CMD_PEER_DEL arrives in that window, ovpn_peer_remove()
(which only requires peer->ovpn->lock) runs to completion: it
unhashes the peer from every table, including by_transp_addr, and
queues it on the release list. peer->bind is only cleared much
later, when the peer is actually released, so the
"if (unlikely(!bind))" check performed after re-acquiring the locks
does *not* observe that the peer has already been removed.
ovpn_peer_endpoints_update() then proceeds to unconditionally re-add
hash_entry_transp_addr, resurrecting the already-removed peer in the
by_transp_addr hash table. Because ovpn_peer_remove() itself guards
against a double remove with
"if (hlist_unhashed(&peer->hash_entry_id)) return;", nothing ever
unhashes the peer a second time. Once the in-flight RX packet that
triggered the float drops its reference and the refcount reaches
zero, the peer is kfree()'d via RCU while still linked in
by_transp_addr. The next matching lookup in
ovpn_peer_get_by_transp_addr() walks that bucket and calls
ovpn_peer_transp_match(), dereferencing the freed peer's ->bind
*before* ovpn_peer_hold() is attempted -- a slab-use-after-free read
on the RX softirq path, runtime-confirmed under KASAN (715
independent "slab-use-after-free in ovpn_peer_get_by_transp_addr"
reports, kmalloc-1k / struct ovpn_peer, freed by the RCU callback,
read from udp_queue_rcv_one_skb -> ovpn_udp_encap_recv ->
ovpn_peer_get_by_transp_addr).
Fix it the same way ovpn_peer_remove() protects itself against a
racing double-remove: after re-acquiring peer->ovpn->lock, check
hlist_unhashed(&peer->hash_entry_id) before touching
hash_entry_transp_addr. ovpn_peer_remove() only mutates the peer's
hashtable membership while holding peer->ovpn->lock, and this check
is performed while we hold that same lock, so the observation is
race-free: either the remove has already happened and hash_entry_id
is unhashed (in which case we must not resurrect the peer and simply
return), or it has not happened yet and cannot happen until we
release peer->ovpn->lock (by which point the rehash under this lock
has already completed). This mirrors the existing double-remove
idiom in ovpn_peer_remove() (drivers/net/ovpn/peer.c) rather than
introducing a new locking primitive.
This is a minimal, targeted fix for the float-vs-delete race; it
does not attempt to shrink the lock-free window itself (peer->lock
is still dropped around ovpn_nl_peer_float_notify()), only to stop
the rehash path from acting on a peer it can no longer safely assume
is still part of the peer tables.
Runtime-verified on a v6.19 KASAN-instrumented kernel: a reproducer
that races authenticated-peer float traffic against a concurrent
OVPN_CMD_PEER_DEL reliably trips a KASAN slab-use-after-free read in
ovpn_peer_get_by_transp_addr() before this fix, and the same
reproducer no longer triggers it once this fix is applied.
Fixes: f0281c1d3732 ("ovpn: add support for updating local or remote UDP endpoint")
Cc: stable@vger.kernel.org
Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
Assisted-by: AuditCode-AI:2026.07
---
drivers/net/ovpn/peer.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index a09d61296425..aeb69f0b06fa 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -307,6 +307,28 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
return;
}
+ /* Guard against a peer that was concurrently removed (e.g.
+ * OVPN_CMD_PEER_DEL -> ovpn_peer_remove()) while we held neither
+ * peer->lock nor ovpn->lock, i.e. in the window opened by the
+ * spin_unlock_bh(&peer->lock) above. ovpn_peer_remove() only
+ * unhashes the peer and queues it for release: peer->bind is
+ * not cleared until the peer is actually released, so the
+ * !bind check we just did above does not catch this case.
+ * Blindly re-adding hash_entry_transp_addr below would
+ * resurrect an already-removed (and soon to be freed) peer in
+ * the by_transp_addr table, causing a use-after-free the next
+ * time that table is walked. Reuse the same
+ * hlist_unhashed(&peer->hash_entry_id) test ovpn_peer_remove()
+ * itself uses to detect a duplicate removal: ovpn->lock is
+ * held here too, so this observation is race-free with any
+ * in-flight or future removal.
+ */
+ if (unlikely(hlist_unhashed(&peer->hash_entry_id))) {
+ spin_unlock_bh(&peer->lock);
+ spin_unlock_bh(&peer->ovpn->lock);
+ return;
+ }
+
/* This function may be invoked concurrently, therefore another
* float may have happened in parallel: perform rehashing
* using the peer->bind->remote directly as key
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* Re: [PATCH] net: usb: sr9700: validate receive packet extent
From: Ethan Nelson-Moore @ 2026-07-08 22:44 UTC (permalink / raw)
To: Pengpeng Hou
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Peter Korsgaard, Simon Horman, linux-usb, netdev,
linux-kernel
In-Reply-To: <20260705083724.24494-1-pengpeng@iscas.ac.cn>
On Sun, Jul 5, 2026 at 1:37 AM Pengpeng Hou <pengpeng@iscas.ac.cn> wrote:
>
> sr9700_rx_fixup() copies len bytes from skb->data + SR_RX_OVERHEAD when
> a URB contains multiple packets. The old check compared len against
> skb->len, but the source pointer has already skipped SR_RX_OVERHEAD
> bytes.
>
> Validate len against the remaining bytes after SR_RX_OVERHEAD before the
> copy and cursor advance.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c
> --- a/drivers/net/usb/sr9700.c
> +++ b/drivers/net/usb/sr9700.c
> @@ -355,7 +355,8 @@
> /* ignore the CRC length */
> len = (skb->data[1] | (skb->data[2] << 8)) - 4;
>
> - if (len > ETH_FRAME_LEN || len > skb->len || len < 0)
> + if (len > ETH_FRAME_LEN || len < 0 ||
> + len > skb->len - SR_RX_OVERHEAD)
> return 0;
>
> /* the last packet of current skb */
>
Hi, Pengpeng,
Other than the subject needing to be clarified, as Andrew mentioned,
the patch looks good to me. Thank you for noticing this issue.
Reviewed-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
Tested-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
Ethan
^ permalink raw reply
* Re: [PATCH net v3] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Cen Zhang (Microsoft) @ 2026-07-08 22:41 UTC (permalink / raw)
To: vadim.fedorenko
Cc: AutonomousCodeSecurity, blbllhy, davem, edumazet, horms, jmaloy,
kuba, kys, linux-kernel, netdev, pabeni, tgopinath,
tipc-discussion, tung.quang.nguyen
In-Reply-To: <7a6d7de8-7c33-42fd-a0f3-68bc1911b0e5@linux.dev>
Sadly NLA_POLICY_MAX() cannot be used here -- its .max field
is s16 instead of u16 in struct nla_policy, which will
overflow to -1 during my testing.
Please let me know if we have any other better choices.
Otherwise, I'll prepare a patch adding .min check (
TIPC_MIN_BEARER_MTU).
^ permalink raw reply
* Re: [PATCH net] macsec: fix promiscuity refcount leak in macsec_dev_open()
From: Sabrina Dubroca @ 2026-07-08 21:47 UTC (permalink / raw)
To: James Raphael Tiovalen
Cc: netdev, stable, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Antoine Tenart, linux-kernel
In-Reply-To: <20260705113629.187490-1-jamestiotio@gmail.com>
2026-07-05, 19:36:29 +0800, James Raphael Tiovalen wrote:
> When a MACsec interface with IFF_PROMISC set is brought up on top of a
> device that has hardware offload enabled, macsec_dev_open() first calls
> dev_set_promiscuity(real_dev, 1) and then propagates the open to the
> offload device. If that propagation fails, the error path jumps to the
> clear_allmulti label, which only reverts allmulti and the unicast
> address. The promiscuity taken on the lower device is never dropped, so
> real_dev is left permanently stuck in promiscuous mode. Its promiscuity
> count can no longer be balanced from software.
>
> Add a clear_promisc label that drops the promiscuity reference and
> route the two offload failure paths to it. The dev_set_promiscuity()
> failure itself still jumps to clear_allmulti, since on that failure the
> count was not incremented.
>
> Fixes: 3cf3227a21d1 ("net: macsec: hardware offloading infrastructure")
> Cc: stable@vger.kernel.org
> Signed-off-by: James Raphael Tiovalen <jamestiotio@gmail.com>
> ---
> drivers/net/macsec.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
--
Sabrina
^ permalink raw reply
* Re: [PATCH 3/3] net: ipa: Add IPA v5.1 data
From: Esteban Urrutia @ 2026-07-08 21:35 UTC (permalink / raw)
To: Alex Elder, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Alex Elder
Cc: linux-arm-msm, devicetree, linux-kernel, netdev
In-Reply-To: <b810c574-0f60-4d3d-ad5a-4205a119fe00@ieee.org>
On 7/8/26 4:06 PM, Alex Elder wrote:
> I think the DDR values might be wrong, but it's difficult to be
> sure. In some cases, in arrays like this in the downstream code,
> if there is no entry found in an array, the *earlier* version
> values should be used. (Unless someone better informed states
> that this is wrong, I think it's fine as-is.)
>
> This information is found in the ipa3_qmb_outstanding[IPA_5_1][]
> array in the downstream code. However there is no entry for that
> version. Given that, all zeroes (as you have it) makes sense.
> But it's possible this applies instead:
>
> [IPA_5_0][IPA_QMB_INSTANCE_DDR] = {12, 12, 0},
> [IPA_5_0][IPA_QMB_INSTANCE_PCIE] = {0, 0, 0},
>
> I have no way of knowing; perhaps someone from Qualcomm can
> get confirmation that all zeroes is correct.
>
> (Note the order of values presented in the downstream code
> differs from upstream.)
In downstream ipa_utils.c there's a function called ipa3_cfg_qsb() which
is in charge of reading these values from ipa3_qmb_outstanding.
Since these values are not present for IPA v5.1, one would assume
they're not set and that, since this is the first time such behavior is
found, additional changes would need to be made to the IPA driver.
However, looking at this function, which I'll leave as a snippet below
given its shortness:
static void ipa3_cfg_qsb(void)
{
u8 hw_type_idx;
const struct ipa_qmb_outstanding *qmb_ot;
struct ipahal_reg_qsb_max_reads max_reads = { 0 };
struct ipahal_reg_qsb_max_writes max_writes = { 0 };
hw_type_idx = ipa3_ctx->hw_type_index;
/*
* Read the register values before writing to them to ensure
* other values are not overwritten
*/
ipahal_read_reg_fields(IPA_QSB_MAX_WRITES, &max_writes);
ipahal_read_reg_fields(IPA_QSB_MAX_READS, &max_reads);
qmb_ot = &(ipa3_qmb_outstanding[hw_type_idx][IPA_QMB_INSTANCE_DDR]);
max_reads.qmb_0_max_reads = qmb_ot->ot_reads;
max_writes.qmb_0_max_writes = qmb_ot->ot_writes;
max_reads.qmb_0_max_read_beats = qmb_ot->ot_read_beats;
qmb_ot = &(ipa3_qmb_outstanding[hw_type_idx][IPA_QMB_INSTANCE_PCIE]);
max_reads.qmb_1_max_reads = qmb_ot->ot_reads;
max_writes.qmb_1_max_writes = qmb_ot->ot_writes;
ipahal_write_reg_fields(IPA_QSB_MAX_WRITES, &max_writes);
ipahal_write_reg_fields(IPA_QSB_MAX_READS, &max_reads);
}
There are no conditions for writing ot_reads, ot_writes and
ot_read_beats, which would correspond to max_reads, max_writes and
max_reads_beats in upstream.
Since hw_type_idx is set to IPA_5_1, this should give a null pointer.
With this info, there are two possibilities:
1. Null pointer dereference, resulting in a kernel oops downstream.
2. qmb_ot is set with all values to 0.
I have never seen a null pointer dereference downstream, so I'm more
inclined to believe option 2 is what's actually happening.
IPA v5.0 also zeroes these values, but I wasn't able to actually confirm
this.
This because in downstream, IPA versions are split into subversions,
which are:
1. Normal IPA (IPA_X_Y)
2. MHI IPA (IPA_X_Y_MHI)
3. APQ IPA (IPA_X_Y_APQ)
Data for IPA v5.0 seems to be of the MHI type, since it's only used in
the SDX65 SoC, which I believe is mostly used in 5G modems.
I realized this while cross-checking since some values didn't really
match, so I had to cross-check with different ipa_data-vX.Y.c files
because of this.
> And although ipa_gsi_ep_config is not defined in this code
> base, here is what it looks like:
>
> struct ipa_gsi_ep_config {
> int ipa_ep_num;
> int ipa_gsi_chan_num;
> int ipa_if_tlv;
> int ipa_if_aos;
> int ee;
> enum gsi_prefetch_mode prefetch_mode;
> uint8_t prefetch_threshold;
> };
>
> This might not be current; I'm using code found here:
> https://git.codelinaro.org/clo/la/kernel/msm-5.15.git
Many thanks for providing the declaration for this struct.
> In the downstream code--confusingly--ipa_gsi_setup_channel()
> doubles the desc_fifo_sz value (for GSI, versus the older BAM
> interface). So the ring size becomes 4096 bytes, and that
> works out to 256 16-byte GSI TRE entries. I'm not sure why
> 512 is used for IPA v3.5.1, but it probably just means it's
> bigger than it needs to be.
>
> The event_count should be the same as the tre_count. Again
> I no longer know why that's not the case for IPA v3.5.1.
For the record (since this is off-topic). I tried to get the modem up in
a device whose SoC was using IPA v3.5.1 and I was experimenting weird
behavior, such as IPA crashing the SoC when removing the module or when
it automatically loaded at boot.
The reason I mention this is because a warning similar to "channel 4
limited to 256 TREs" appeared whenever IPA was loaded. That may be the
reason I was experimenting those issues.
>> +static const struct ipa_mem ipa_mem_local_data[] = {
>
> IPA has local memory that is partitioned as defined by this
> array. The regions are used by IPA/GSI firmware and/or
> hardware. The configuration defined here is sent to
> the modem in an ipa_init_modem_driver_req QMI message
> so both the modem and AP have a consistent view of
> how the memory is used.
>
> Many memory regions are preceded by 0-2 "canaries", which
> are 32-byte values initialized to IPA_MEM_CANARY_VAL.
>
> In the downstream code there is structure ipa3_mem_partition
> that defines these things, and structures of this type are
> defined in "ipa_utils.c". For IPA v5.1, ipa_5_1_mem_part
> defines them all. The mapping between downstream and
> upstream is not trivial and direct, but it should be
> obvious how they get translated.
>
>
> With two exceptions, what I see here looks like you
> correctly transferred everything. (The two exceptions
> are entries that from what I can tell, should not be
> present.)
[...]
> The next two entries look wrong to me. Can you explain where
> you got these offsets and sizes? Is it from "ipa_data-v5.0.c"?
>
> Here are the relevant entries I see in ipa_5_1_mem_part
> in the downstream code:
> .stats_flt_v4_ofst = 0,
> .stats_flt_v4_size = 0,
> .stats_flt_v6_ofst = 0,
> .stats_flt_v6_size = 0,
> .stats_rt_v4_ofst = 0,
> .stats_rt_v4_size = 0,
> .stats_rt_v6_ofst = 0,
> .stats_rt_v6_size = 0,
> (Since their size is zero, their entries can be omitted.)
>
>> + {
>> + .id = IPA_MEM_AP_V4_FILTER,
>> + .offset = 0x29b8,
>> + .size = 0x0188,
>> + .canary_count = 2,
>> + },
>> + {
>> + .id = IPA_MEM_AP_V6_FILTER,
>> + .offset = 0x2b40,
>> + .size = 0x0228,
>> + .canary_count = 0,
>> + },
>
> The remaining entries (below) look good.
While cross-referencing IPA data files upstream, I found these two
regions to be present in data for IPA v5.5, even though they aren't
defined in ipa_5_5_mem_part. At the start I assumed these were correct
since they were present upstream, but I took a closer look at that file
and I believe data for this version was directly added without going
through a proper review process.
The reason why I used IPA v5.5 memory regions in IPA v5.1 is because
their memory partitions are identical.
Now, with this information, I would like to ask whether both of these
memory regions are correct for both IPA v5.1 and v5.5 data files.
>> +/* Memory configuration data for an SoC having IPA v5.1 */
>> +static const struct ipa_mem_data ipa_mem_data = {
>> + .local_count = ARRAY_SIZE(ipa_mem_local_data),
>> + .local = ipa_mem_local_data,
>> + .imem_addr = 0x146a8000,
>
> I think I needed to look up the imem offset value
> in Qualcomm documentation I no longer have access
> to. Perhaps someone from there could confirm you
> are using the right values here.
After cross-checking I believe this may be the qcom,additional-mapping
property downstream, which specifies the IMEM starting address and size.
I'll leave (2) and (3) for reference.
(2) corresponds to SM8450, while (3) corresponds to SM8475.
>
>> + .imem_size = 0x00002000,
>> + /*
>> + * While this value is 0xb000 on SM8450 and 0x9000 on SM8475,
>> + * it has been left set to 0x9000 for compatibility with SM8475
>> + */
>
> As I said earlier, I'm not completely sure this will still
> work on the SM8450. Someone should confirm this, and it
> really ought to be tested somehow.
I have clarified this in my previous email (4), so I'll skip this part.
>
>> + .smem_size = 0x00009000,
>> +};
>> +
>> +/* Interconnect rates are in 1000 byte/second units */
>> +static const struct ipa_interconnect_data ipa_interconnect_data[] = {
>> + {
>> + .name = "memory",
>> + .peak_bandwidth = 1900000, /* 1.9 GBps */
>> + .average_bandwidth = 590000, /* 590 MBps */
>
> I no longer recall where to get these bandwidth values
> for the interconnects. Perhaps someone from Qualcomm
> can find this out/confirm what you have.
This was a tricky part. These seem to come from the qcom,svs2 property
which seems to be mapped to the interconnects specified downstream.
Since the IPA interconnects declared in device trees are different in
both downstream and upstream I had to make some adjustments, such as
using the minimum value between both ipa_to_llcc and llcc_to_ebi1
interconnects.
An example of this can be seen in (5), which corresponds to the SM8350
SoC using IPA v4.9.
Again, thanks for taking the time to properly explain things.
(1) https://github.com/LineageOS/android_kernel_qcom_sm8450-modules/blob/lineage-20/qcom/opensource/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c#L7881
https://github.com/LineageOS/android_kernel_qcom_sm8450-devicetrees/blob/lineage-20/qcom/waipio.dtsi#L3404
https://github.com/LineageOS/android_kernel_qcom_sm8450-devicetrees/blob/lineage-20/qcom/cape.dtsi#L2723
(4) https://lore.kernel.org/all/3e70d77e-6bec-4e16-ae88-a4f5161f182e@proton.me/
(5) https://github.com/LineageOS/android_kernel_motorola_sm7325/blob/lineage-23.2/arch/arm64/boot/dts/vendor/qcom/lahaina.dtsi#L4683
Regards,
Esteban
^ permalink raw reply
* Re: [PATCH 02/15] dt-bindings: clock: mediatek: regroup MT8188 dt-bindings into MT8186
From: Rob Herring @ 2026-07-08 21:34 UTC (permalink / raw)
To: Louis-Alexis Eyraud
Cc: Michael Turquette, Stephen Boyd, Brian Masney,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chun-Jie Chen, Philipp Zabel,
Edward-JW Yang, Richard Cochran, kernel, linux-clk, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek, netdev
In-Reply-To: <d1e37bd4f2a05fed6c7bfdc5d9a0fa90c892d608.camel@collabora.com>
On Wed, Jul 8, 2026 at 8:45 AM Louis-Alexis Eyraud
<louisalexis.eyraud@collabora.com> wrote:
>
> Hello Rob,
>
> On Wed, 2026-07-01 at 14:33 -0500, Rob Herring wrote:
> > On Wed, Jul 01, 2026 at 03:11:07PM +0200, Louis-Alexis Eyraud wrote:
> > > Regroup the MT8188 clock and system clock dt-bindings into MT8186
> > > ones
> > > to ease maintainability and have common files for several currently
> > > supported SoC or new future ones, that have the same kind of clock
> > > controller design.
> > >
> > > Note:
> > > The `#clock-cells` property is a required property for all
> > > compatibles
> > > declared in MT8188 clock and system clock dt-bindings but not in
> > > MT8186
> > > ones.
> > > To avoid ABI breakage, conditional blocks to check this requirement
> > > for MT8188 compatibles are added, rather than enforcing it for
> > > MT8186
> > > compatibles.
> >
> > If the existing DTs are just wrong, then I would just make #clock-
> > cells
> > required. But please update the .dts files so the warnings don't
> > grow.
> >
> I've tested to make the #clock-cells required for the MT8186, MT8192
> and MT8195 system and functional clock controllers.
> I did not see new warnings, so no extra dts patches would be needed.
>
> I'll add new patches (one per SoC) in the next revision of the series
> for this, as it simplifies the grouping patches (no more if/then to
> require #clock-cells for the MT8188/MT8189 clock controllers) and the
> note in commit message could be removed.
>
> > The grouping I would do here is:
> >
> > - clock controller only
> > - reset controller only
> > - both clock and reset controller
> >
> > That should avoid any if/then schemas.
> >
>
> By this grouping, I understand you suggest having separate dt-bindings
> files, that could look like:
> - mediatek,mt8186-clock.yaml: clock controllers
> - <name to be found>: reset controllers
> - <name to be found>: clock controllers with reset controller
> - mediatek,mt8186-sys-clock.yaml: system clock controllers.
> - <name to be found>: system clock controllers with reset controller
>
> Is that what you meant?
I think so, but not sure I understand the distinction with clock
controllers and system clock controllers.
> There is no pure reset controllers for those SoC so no dedicated file
> would needed at the moment.
> The system clock controllers all have reset-controllers, even they may
> currently be not all implemented, so no separate files for system clock
> controllers would needed as well.
>
> Also, from what I see the current dt-bindings, the system clocks
> controllers for the MT8186/MT8188/MT8192/MT8195 SoC have the #reset-
> cells property but it is not required for them (examples:
> mediatek,mt8188-infracfg-ao or mediatek,mt8195-infracfg_ao).
>
> With the patches to make the #clock-cells property required, I already
> removed the biggest if/else block in mediatek,mt8186-clock.yaml, so
> only the one regarding #reset-cells property remains.
>
> So, should I create separate files, following the grouping suggestion,
> for the v2 of this patch?
Shrug. There's no hard rule here, it's a judgment call. With one
if/then block dropped, it's a bit more tolerable to keep it as-is. If
the if/then schemas are as long as the rest of the schema (minus any
example), then I would say to split the schemas.
Rob
^ permalink raw reply
* [PATCH 4/6] pds_core: add PLDM component info display
From: Nikhil P. Rao @ 2026-07-08 21:22 UTC (permalink / raw)
To: netdev
Cc: kuba, brett.creeley, eric.joyner, andrew+netdev, davem, edumazet,
pabeni, jacob.e.keller
In-Reply-To: <20260708212222.296202-1-nikhil.rao@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
Add detailed component information display via devlink info. This
allows users to see individual firmware components and their versions.
Components are reported as fixed, running, or stored based on their
firmware-provided flags.
Example output:
$ devlink dev info pci/0000:00:05.0
versions:
fixed:
asic.id 0x0
asic.rev 0x0
running:
fw.bootloader 1.2.3
fw.uboot 1.60.0-73
fw 1.60.0-73
fw.cpld 3.18
stored:
fw.bootloader 1.2.3
fw.uboot 1.60.0-73
fw.uboot.gold 1.50.0-22
fw.gold 1.50.0-22
fw 1.60.0-73
fw.cpld 3.18
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
---
drivers/net/ethernet/amd/pds_core/core.c | 2 +
drivers/net/ethernet/amd/pds_core/core.h | 1 +
drivers/net/ethernet/amd/pds_core/devlink.c | 145 +++++++++++++++++++-
drivers/net/ethernet/amd/pds_core/fw.c | 12 +-
4 files changed, 153 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c
index 6c62ff7a32f0..a7a0bcf98ed3 100644
--- a/drivers/net/ethernet/amd/pds_core/core.c
+++ b/drivers/net/ethernet/amd/pds_core/core.c
@@ -589,6 +589,8 @@ void pdsc_fw_up(struct pdsc *pdsc)
return;
}
+ pdsc_fw_components_invalidate(pdsc);
+
err = pdsc_setup(pdsc, PDSC_SETUP_RECOVERY);
if (err)
goto err_out;
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index c686f0bbbaeb..73356c74bb9f 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -340,6 +340,7 @@ int pdsc_firmware_update(struct pdsc *pdsc,
struct netlink_ext_ack *extack);
int pdsc_get_component_info(struct pdsc *pdsc);
const char *pdsc_fw_type_to_name(u8 type);
+void pdsc_fw_components_invalidate(struct pdsc *pdsc);
void pdsc_fw_down(struct pdsc *pdsc);
void pdsc_fw_up(struct pdsc *pdsc);
diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
index 3b763ee1715e..63fe45e91f71 100644
--- a/drivers/net/ethernet/amd/pds_core/devlink.c
+++ b/drivers/net/ethernet/amd/pds_core/devlink.c
@@ -93,14 +93,120 @@ int pdsc_dl_flash_update(struct devlink *dl,
return pdsc_firmware_update(pdsc, params, extack);
}
+static int pdsc_dl_report_component(struct devlink_info_req *req,
+ struct pds_core_fw_component_info *info)
+{
+ enum devlink_info_version_type ver_type;
+ u16 flags = le16_to_cpu(info->flags);
+ char *ver = info->version;
+ const char *name;
+ char buf[32];
+
+ /* Main firmware is reported as generic "fw" */
+ if (info->component_type == PDS_CORE_FW_TYPE_MAIN) {
+ if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
+ snprintf(buf, sizeof(buf), "fw.gold");
+ else
+ snprintf(buf, sizeof(buf), "fw");
+ } else {
+ name = pdsc_fw_type_to_name(info->component_type);
+ if (!name)
+ return 0;
+
+ if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
+ snprintf(buf, sizeof(buf), "fw.%s.gold", name);
+ else
+ snprintf(buf, sizeof(buf), "fw.%s", name);
+ }
+
+ ver_type = DEVLINK_INFO_VERSION_TYPE_NONE;
+ if (flags & PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME)
+ ver_type = DEVLINK_INFO_VERSION_TYPE_COMPONENT;
+
+ if (flags & PDS_CORE_FW_COMPONENT_INFO_F_FIXED) {
+ int err;
+
+ err = devlink_info_version_fixed_put(req, buf, ver);
+ if (err)
+ return err;
+ }
+
+ if (flags & PDS_CORE_FW_COMPONENT_INFO_F_RUNNING) {
+ int err;
+
+ err = devlink_info_version_running_put_ext(req, buf,
+ ver, ver_type);
+ if (err)
+ return err;
+ }
+
+ if (flags & PDS_CORE_FW_COMPONENT_INFO_F_STARTUP) {
+ int err;
+
+ err = devlink_info_version_stored_put_ext(req, buf,
+ ver, ver_type);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int pdsc_dl_report_fw_ver(struct devlink_info_req *req, char *fw_ver)
+{
+ return devlink_info_version_running_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_FW,
+ fw_ver);
+}
+
+static int pdsc_dl_component_info_get(struct devlink *dl,
+ struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ struct pdsc *pdsc = devlink_priv(dl);
+ u8 num_components;
+ int err;
+ int i;
+
+ /* Pairs with WRITE_ONCE in pdsc_fw_components_invalidate().
+ * Use READ_ONCE to get a consistent snapshot of num_components.
+ * pdsc_fw_components_invalidate() can zero it concurrently during
+ * firmware recovery; using the local copy avoids iterating zero
+ * times when we already decided the cache was valid.
+ */
+ num_components = READ_ONCE(pdsc->fw_components.num_components);
+ if (!num_components) {
+ err = pdsc_get_component_info(pdsc);
+ if (err)
+ return pdsc_dl_report_fw_ver(req,
+ pdsc->dev_info.fw_version);
+ num_components = READ_ONCE(pdsc->fw_components.num_components);
+ if (!num_components)
+ return pdsc_dl_report_fw_ver(req,
+ pdsc->dev_info.fw_version);
+ }
+
+ num_components = min_t(u16, num_components,
+ le16_to_cpu(pdsc->dev_ident.max_fw_slots));
+ for (i = 0; i < num_components; i++) {
+ err = pdsc_dl_report_component(req,
+ &pdsc->fw_components.info[i]);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static char *fw_slotnames[] = {
"fw.goldfw",
"fw.mainfwa",
"fw.mainfwb",
};
-int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
- struct netlink_ext_ack *extack)
+static int pdsc_dl_fw_list_info_get(struct devlink *dl,
+ struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
{
union pds_core_dev_cmd cmd = {
.fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
@@ -134,12 +240,41 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
return err;
}
- err = devlink_info_version_running_put(req,
- DEVLINK_INFO_VERSION_GENERIC_FW,
- pdsc->dev_info.fw_version);
+ return 0;
+}
+
+static int pdsc_dl_info_get_v1(struct devlink *dl,
+ struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ struct pdsc *pdsc = devlink_priv(dl);
+ int err;
+
+ err = pdsc_dl_fw_list_info_get(dl, req, extack);
if (err)
return err;
+ /* Version 1: report fw from dev_info (running only) */
+ return pdsc_dl_report_fw_ver(req, pdsc->dev_info.fw_version);
+}
+
+int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ struct pdsc *pdsc = devlink_priv(dl);
+ char buf[32];
+ int err;
+
+ if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2) {
+ err = pdsc_dl_component_info_get(dl, req, extack);
+ if (err)
+ return err;
+ } else {
+ err = pdsc_dl_info_get_v1(dl, req, extack);
+ if (err)
+ return err;
+ }
+
snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_type);
err = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c
index dc793005ec70..ae39f684c3b8 100644
--- a/drivers/net/ethernet/amd/pds_core/fw.c
+++ b/drivers/net/ethernet/amd/pds_core/fw.c
@@ -42,6 +42,12 @@ const char *pdsc_fw_type_to_name(u8 type)
return NULL;
}
+void pdsc_fw_components_invalidate(struct pdsc *pdsc)
+{
+ /* Pairs with READ_ONCE in pdsc_dl_component_info_get() */
+ WRITE_ONCE(pdsc->fw_components.num_components, 0);
+}
+
static u8 pdsc_name_to_fw_type(const char *name)
{
size_t prefix_len;
@@ -765,7 +771,9 @@ static int pdsc_flash_component(struct pldmfw *context,
if (component_type) {
const char *type_name = pdsc_fw_type_to_name(component_type);
- if (type_name) {
+ if (component_type == PDS_CORE_FW_TYPE_MAIN) {
+ component_name = "fw";
+ } else if (type_name) {
snprintf(component_name_buf, sizeof(component_name_buf),
"%s%s", PDSC_FW_COMPONENT_PREFIX, type_name);
component_name = component_name_buf;
@@ -966,7 +974,7 @@ int pdsc_firmware_update(struct pdsc *pdsc,
err = pdsc_legacy_firmware_update(pdsc, params, extack);
/* Invalidate cached component info so next info_get refreshes */
- pdsc->fw_components.num_components = 0;
+ pdsc_fw_components_invalidate(pdsc);
return err;
}
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox