* [PATCH net v2] tipc: serialize udp bearer replicast list updates
From: Weiming Shi @ 2026-07-07 16:42 UTC (permalink / raw)
To: Jon Maloy, Tung Nguyen, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: netdev, tipc-discussion, linux-kernel, Xiang Mei, Weiming Shi
tipc_udp_rcast_add() and cleanup_bearer() both update ub->rcast.list
with list_add_rcu() / list_del_rcu(), but nothing serializes them. The
add runs from the encap receive softirq (via tipc_udp_rcast_disc())
without rtnl_lock(), so it can race the cleanup delete and corrupt the
list:
list_del corruption. prev->next should be ffff8880298d7ab8,
but was ffff88802449ad38. (prev=ffff888027e3ec98)
kernel BUG at lib/list_debug.c:62!
RIP: __list_del_entry_valid_or_report+0x17a/0x200
Workqueue: events cleanup_bearer
Call Trace:
cleanup_bearer (net/tipc/udp_media.c:811)
process_one_work (kernel/workqueue.c:3302)
worker_thread (kernel/workqueue.c:3466)
The bearer can be enabled from an unprivileged user namespace, as the
TIPCv2 generic-netlink ops carry no GENL_ADMIN_PERM.
Add a spinlock to struct udp_bearer and take it around the
list_add_rcu() in tipc_udp_rcast_add() and the list_del_rcu() loop in
cleanup_bearer() so the two writers can no longer corrupt the list.
While here, switch the read-only walk in tipc_udp_is_known_peer() to
list_for_each_entry_rcu(); it never deletes, so list_for_each_entry_safe()
was misleading.
Fixes: ef20cd4dd163 ("tipc: introduce UDP replicast")
Reported-by: Xiang Mei <xmei5@asu.edu>
Suggested-by: Tung Nguyen <tung.quang.nguyen@est.tech>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
v2: (per Tung's review)
- Narrow the lock to just list_add_rcu().
- Drop the under-lock dup re-check; serializing the writers is enough.
- Use list_for_each_entry_rcu() in tipc_udp_is_known_peer().
net/tipc/udp_media.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 62ae7f5b5840..c6aa8c3c54ce 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -94,6 +94,7 @@ struct udp_replicast {
* @ifindex: local address scope
* @work: used to schedule deferred work on a bearer
* @rcast: associated udp_replicast container
+ * @rcast_lock: serializes updates to @rcast.list
*/
struct udp_bearer {
struct tipc_bearer __rcu *bearer;
@@ -101,6 +102,7 @@ struct udp_bearer {
u32 ifindex;
struct work_struct work;
struct udp_replicast rcast;
+ spinlock_t rcast_lock; /* protects rcast.list */
};
static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
@@ -281,7 +283,7 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
struct udp_media_addr *addr)
{
- struct udp_replicast *rcast, *tmp;
+ struct udp_replicast *rcast;
struct udp_bearer *ub;
ub = rcu_dereference_rtnl(b->media_ptr);
@@ -290,7 +292,7 @@ static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
return false;
}
- list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
+ list_for_each_entry_rcu(rcast, &ub->rcast.list, list) {
if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
return true;
}
@@ -326,7 +328,9 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
#endif
b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
+ spin_lock_bh(&ub->rcast_lock);
list_add_rcu(&rcast->list, &ub->rcast.list);
+ spin_unlock_bh(&ub->rcast_lock);
return 0;
}
@@ -679,6 +683,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
return -ENOMEM;
INIT_LIST_HEAD(&ub->rcast.list);
+ spin_lock_init(&ub->rcast_lock);
if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
goto err;
@@ -819,10 +824,12 @@ static void cleanup_bearer(struct work_struct *work)
struct udp_replicast *rcast, *tmp;
struct tipc_net *tn;
+ spin_lock_bh(&ub->rcast_lock);
list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
list_del_rcu(&rcast->list);
call_rcu_hurry(&rcast->rcu, rcast_free_rcu);
}
+ spin_unlock_bh(&ub->rcast_lock);
tn = tipc_net(sock_net(ub->sk));
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 0/3] SM8450 IPA support
From: Dmitry Baryshkov @ 2026-07-07 16:45 UTC (permalink / raw)
To: esteuwu
Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Alex Elder, linux-arm-msm,
devicetree, linux-kernel, netdev
In-Reply-To: <20260622-sm8450-ipa-v1-0-532f0299f96e@proton.me>
On Mon, Jun 22, 2026 at 09:44:16PM -0400, Esteban Urrutia via B4 Relay wrote:
> This series adds support for the IPA subsystem found in the SM8450 SoC.
> While IPA v5.0 is very similar to IPA v5.1 (heck, it even managed to
> properly get the modem up and running), it wasn't perfect, since the
> modem would sometimes hang when rebooting or powering the AP off.
> After a thorough investigation, I managed to create the proper data file
> required for IPA v5.1.
>
> Regards,
> Esteban
>
> Signed-off-by: Esteban Urrutia <esteuwu@proton.me>
> ---
> Esteban Urrutia (3):
> arm64: dts: qcom: sm8450: Add IPA support
> dt-bindings: net: qcom,ipa: Add SM8450 compatible string
> net: ipa: Add IPA v5.1 data
Please reoder the patches:
- DT bindings
- driver
- DTS.
It follows the natural way you'd read the patchset.
>
> .../devicetree/bindings/net/qcom,ipa.yaml | 1 +
> arch/arm64/boot/dts/qcom/sm8450.dtsi | 55 ++-
> drivers/net/ipa/Makefile | 2 +-
> drivers/net/ipa/data/ipa_data-v5.1.c | 477 +++++++++++++++++++++
> drivers/net/ipa/gsi_reg.c | 1 +
> drivers/net/ipa/ipa_data.h | 1 +
> drivers/net/ipa/ipa_main.c | 4 +
> drivers/net/ipa/ipa_reg.c | 1 +
> 8 files changed, 536 insertions(+), 6 deletions(-)
> ---
> base-commit: 948efecf22e49aa4bf55bb73ec79a0ddcfd38571
> change-id: 20260622-sm8450-ipa-5da81f67eb65
>
> Best regards,
> --
> Esteban Urrutia <esteuwu@proton.me>
>
>
--
With best wishes
Dmitry
^ permalink raw reply
* [PATCH net] vmxnet3: fix BUG_ON in vmxnet3_get_hdr_len() for Geneve packets
From: Harshaka Narayana @ 2026-07-07 16:52 UTC (permalink / raw)
To: davem, kuba, pabeni, netdev
Cc: ronak.doshi, bcm-kernel-feedback-list, andrew+netdev, edumazet,
linux-kernel, guolin.yang, Harshaka Narayana,
Sankararaman Jayaraman
When rcd->tcp is set based on the inner TCP header of a
Geneve-encapsulated packet that exceeds MTU and is non-LRO, the driver
hits BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP) in vmxnet3_get_hdr_len()
because the outer IPv4 protocol is UDP (Geneve), not TCP.
Convert this BUG_ON to return 0, letting the caller's existing hlen == 0
check skip the LRO path gracefully. Also convert the preceding
BUG_ON(gdesc->rcd.tcp == 0) assertion to return 0 defensively.
Fixes: 45dac1d6ea04 ("vmxnet3: Changes for vmxnet3 adapter version 2 (fwd)")
Signed-off-by: Harshaka Narayana <harshaka.narayana@broadcom.com>
Signed-off-by: Ronak Doshi <ronak.doshi@broadcom.com>
Signed-off-by: Sankararaman Jayaraman <sankararaman.jayaraman@broadcom.com>
---
drivers/net/vmxnet3/vmxnet3_drv.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 40522afc0532..f706cacfa70f 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -1530,7 +1530,8 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
struct ipv6hdr *ipv6;
struct tcphdr *tcp;
} hdr;
- BUG_ON(gdesc->rcd.tcp == 0);
+ if (gdesc->rcd.tcp == 0)
+ return 0;
maplen = skb_headlen(skb);
if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen))
@@ -1547,7 +1548,9 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP) &&
hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP));
hdr.ptr += hlen;
- BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP);
+ if (hdr.ipv4->protocol != IPPROTO_TCP)
+ return 0;
+
hlen = hdr.ipv4->ihl << 2;
hdr.ptr += hdr.ipv4->ihl << 2;
} else if (gdesc->rcd.v6) {
--
2.52.0
^ permalink raw reply related
* [PATCH] dt-bindings: net: convert microchip,lan78xx.txt to YAML schema
From: Mikhail Lukianchikov @ 2026-07-07 16:58 UTC (permalink / raw)
To: Andrew Lunn
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Rengarajan Sundararajan, UNGLinuxDriver, netdev, devicetree,
linux-kernel, Mikhail Lukianchikov
Convert the Microchip LAN78xx family (LAN7800, LAN7801, LAN7850) binding
documentation from plain text to DT schema format using YAML.
The conversion was validated with 'make dt_binding_check'
Signed-off-by: Mikhail Lukianchikov <avermoal@gmail.com>
---
.../bindings/net/microchip,lan78xx.txt | 53 --------
.../bindings/net/microchip,lan78xx.yaml | 113 ++++++++++++++++++
2 files changed, 113 insertions(+), 53 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/net/microchip,lan78xx.txt
create mode 100644 Documentation/devicetree/bindings/net/microchip,lan78xx.yaml
diff --git a/Documentation/devicetree/bindings/net/microchip,lan78xx.txt b/Documentation/devicetree/bindings/net/microchip,lan78xx.txt
deleted file mode 100644
index 11a679530ae6..000000000000
--- a/Documentation/devicetree/bindings/net/microchip,lan78xx.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-Microchip LAN78xx Gigabit Ethernet controller
-
-The LAN78XX devices are usually configured by programming their OTP or with
-an external EEPROM, but some platforms (e.g. Raspberry Pi 3 B+) have neither.
-The Device Tree properties, if present, override the OTP and EEPROM.
-
-Required properties:
-- compatible: Should be one of "usb424,7800", "usb424,7801" or "usb424,7850".
-
-The MAC address will be determined using the optional properties
-defined in ethernet.txt.
-
-Optional properties of the embedded PHY:
-- microchip,led-modes: a 0..4 element vector, with each element configuring
- the operating mode of an LED. Omitted LEDs are turned off. Allowed values
- are defined in "include/dt-bindings/net/microchip-lan78xx.h".
-
-Example:
-
-/* Based on the configuration for a Raspberry Pi 3 B+ */
-&usb {
- usb-port@1 {
- compatible = "usb424,2514";
- reg = <1>;
- #address-cells = <1>;
- #size-cells = <0>;
-
- usb-port@1 {
- compatible = "usb424,2514";
- reg = <1>;
- #address-cells = <1>;
- #size-cells = <0>;
-
- ethernet: ethernet@1 {
- compatible = "usb424,7800";
- reg = <1>;
- local-mac-address = [ 00 11 22 33 44 55 ];
-
- mdio {
- #address-cells = <0x1>;
- #size-cells = <0x0>;
- eth_phy: ethernet-phy@1 {
- reg = <1>;
- microchip,led-modes = <
- LAN78XX_LINK_1000_ACTIVITY
- LAN78XX_LINK_10_100_ACTIVITY
- >;
- };
- };
- };
- };
- };
-};
diff --git a/Documentation/devicetree/bindings/net/microchip,lan78xx.yaml b/Documentation/devicetree/bindings/net/microchip,lan78xx.yaml
new file mode 100644
index 000000000000..743667c1e761
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/microchip,lan78xx.yaml
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/microchip,lan78xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip LAN78xx Gigabit Ethernet controller
+
+maintainers:
+ - Rengarajan Sundararajan <Rengarajan.S@microchip.com>
+ - UNGLinuxDriver <UNGLinuxDriver@microchip.com>
+
+description:
+ The LAN78XX devices are usually configured by programming their OTP or with
+ an external EEPROM, but some platforms (e.g. Raspberry Pi 3 B+) have neither.
+ The Device Tree properties, if present, override the OTP and EEPROM.
+
+allOf:
+ - $ref: /schemas/usb/usb-device.yaml#
+ - $ref: /schemas/net/ethernet-controller.yaml#
+
+properties:
+ compatible:
+ enum:
+ - usb424,7800
+ - usb424,7801
+ - usb424,7850
+ reg:
+ maxItems: 1
+ description: USB port number
+ local-mac-address:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 6
+ maxItems: 6
+ description:
+ MAC address to use if not stored in OTP or EEPROM. If present,
+ overrides OTP/EEPROM.
+ mdio:
+ $ref: /schemas/net/mdio.yaml#
+ unevaluatedProperties: false
+
+patternProperties:
+ "^ethernet-phy(@[0-9a-f]+)?$":
+ type: object
+ description: |
+ PHY node for the embedded or external PHY. The PHY address is
+ given by the 'reg' property.
+ properties:
+ reg:
+ maxItems: 1
+ description: PHY address.
+ microchip,led-modes:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 4
+ description:
+ Array of LED mode values for each of up to 4 LEDs.
+ Omitted LEDs are turned off. Allowed values are defined
+ in include/dt-bindings/net/microchip-lan78xx.h.
+ required:
+ - reg
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/net/microchip-lan78xx.h>
+ / {
+ usb: usb {
+ compatible = "usb-host";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+ &usb {
+ usb-port@1 {
+ compatible = "usb424,2514";
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ usb-port@1 {
+ compatible = "usb424,2514";
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet@1 {
+ compatible = "usb424,7800";
+ reg = <1>;
+ local-mac-address = [00 11 22 33 44 55];
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ethernet-phy@1 {
+ reg = <1>;
+ microchip,led-modes = <
+ LAN78XX_LINK_1000_ACTIVITY
+ LAN78XX_LINK_10_100_ACTIVITY
+ >;
+ };
+ };
+ };
+ };
+ };
+ };
+...
--
2.52.0
^ permalink raw reply related
* [PATCH net-next] net: sparx5: configure TAS port link speed
From: Robert Marko @ 2026-07-07 17:04 UTC (permalink / raw)
To: daniel.machon, UNGLinuxDriver, andrew+netdev, davem, edumazet,
kuba, pabeni, Steen.Hegelund, horms, netdev, linux-arm-kernel,
linux-kernel
Cc: luka.perkov, Robert Marko
On the TSN and RED variants of LAN969x and SparX-5i TAS (Time-Aware Shaper)
is present in the silicon.
Currently, the driver does not use configure it at all, which means that
the TAS_PROFILE_CONFIG.LINK_SPEED[1] value is left at the default of 3
which means that its configured for 1 Gbps.
So, running iperf between two 10G switch ports will result in only 940-ish
Mbps while we should be getting around 9.3 Gbps.
Correctly populating the TAS_PROFILE_CONFIG.LINK_SPEED[1] with the current
port speed fixes this issue and we achieve around 9.4 Gbps between two 10G
switch ports.
So, port the TAS port link speed setting from the vendor BSP 6.18 kernel[2]
[1] https://microchip-ung.github.io/lan969x-industrial_reginfo/reginfo_LAN969x-Industrial.html?select=hsch,tas_profile_cfg,tas_profile_config,link_speed
[2] https://github.com/microchip-ung/linux/tree/bsp-6.18-2026
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
---
.../microchip/sparx5/lan969x/lan969x_regs.c | 3 ++
.../microchip/sparx5/sparx5_main_regs.h | 12 +++++
.../ethernet/microchip/sparx5/sparx5_port.c | 4 ++
.../ethernet/microchip/sparx5/sparx5_qos.c | 49 +++++++++++++++++++
.../ethernet/microchip/sparx5/sparx5_qos.h | 1 +
.../ethernet/microchip/sparx5/sparx5_regs.c | 3 ++
.../ethernet/microchip/sparx5/sparx5_regs.h | 3 ++
7 files changed, 75 insertions(+)
diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c
index ace4ba21eec4..3fc2c006ba12 100644
--- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c
+++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c
@@ -95,6 +95,7 @@ const unsigned int lan969x_gaddr[GADDR_LAST] = {
[GA_HSCH_SYSTEM] = 37384,
[GA_HSCH_MMGT] = 36260,
[GA_HSCH_TAS_CONFIG] = 37696,
+ [GA_HSCH_TAS_PROFILE_CFG] = 37712,
[GA_PTP_PTP_CFG] = 512,
[GA_PTP_PTP_TOD_DOMAINS] = 528,
[GA_PTP_PHASE_DETECTOR_CTRL] = 628,
@@ -129,6 +130,7 @@ const unsigned int lan969x_gcnt[GCNT_LAST] = {
[GC_GCB_SIO_CTRL] = 1,
[GC_HSCH_HSCH_CFG] = 1120,
[GC_HSCH_HSCH_DWRR] = 32,
+ [GC_HSCH_TAS_PROFILE_CFG] = 30,
[GC_PTP_PTP_PINS] = 8,
[GC_PTP_PHASE_DETECTOR_CTRL] = 8,
[GC_REW_PORT] = 35,
@@ -144,6 +146,7 @@ const unsigned int lan969x_gsize[GSIZE_LAST] = {
[GW_FDMA_FDMA] = 448,
[GW_GCB_CHIP_REGS] = 180,
[GW_HSCH_TAS_CONFIG] = 16,
+ [GW_HSCH_TAS_PROFILE_CFG] = 68,
[GW_PTP_PHASE_DETECTOR_CTRL] = 12,
[GW_QSYS_PAUSE_CFG] = 988,
};
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
index 27d02eea7ce1..15fbfa68bc75 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
@@ -5369,6 +5369,18 @@ extern const struct sparx5_regs *regs;
#define HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_GET(x)\
FIELD_GET(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY, x)
+/* HSCH:TAS_PROFILE_CFG:TAS_PROFILE_CONFIG */
+#define HSCH_TAS_PROFILE_CONFIG(g) \
+ __REG(TARGET_HSCH, 0, 1, regs->gaddr[GA_HSCH_TAS_PROFILE_CFG], g, \
+ regs->gcnt[GC_HSCH_TAS_PROFILE_CFG], \
+ regs->gsize[GW_HSCH_TAS_PROFILE_CFG], 32, 0, 1, 4)
+
+#define HSCH_TAS_PROFILE_CONFIG_LINK_SPEED GENMASK(10, 8)
+#define HSCH_TAS_PROFILE_CONFIG_LINK_SPEED_SET(x)\
+ FIELD_PREP(HSCH_TAS_PROFILE_CONFIG_LINK_SPEED, x)
+#define HSCH_TAS_PROFILE_CONFIG_LINK_SPEED_GET(x)\
+ FIELD_GET(HSCH_TAS_PROFILE_CONFIG_LINK_SPEED, x)
+
/* LAN969X ONLY */
/* HSIOWRAP:XMII_CFG:XMII_CFG */
#define HSIO_WRAP_XMII_CFG(g) \
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
index 62c49893de3c..ef06bed3a9cc 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
@@ -11,6 +11,7 @@
#include "sparx5_main_regs.h"
#include "sparx5_main.h"
#include "sparx5_port.h"
+#include "sparx5_qos.h"
#define SPX5_ETYPE_TAG_C 0x8100
#define SPX5_ETYPE_TAG_S 0x88a8
@@ -1050,6 +1051,9 @@ int sparx5_port_config(struct sparx5 *sparx5,
sparx5,
QFWD_SWITCH_PORT_MODE(port->portno));
+ /* Notify TAS about the speed. */
+ sparx5_tas_speed(port, conf->speed);
+
/* Save the new values */
port->conf = *conf;
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c
index e580670f3992..972da8a71f5a 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c
@@ -9,6 +9,17 @@
#include "sparx5_main.h"
#include "sparx5_qos.h"
+enum sparx5_tas_link_speed {
+ TAS_SPEED_NO_GB,
+ TAS_SPEED_10,
+ TAS_SPEED_100,
+ TAS_SPEED_1000,
+ TAS_SPEED_2500,
+ TAS_SPEED_5000,
+ TAS_SPEED_10000,
+ TAS_SPEED_25000,
+};
+
/* Calculate new base_time based on cycle_time.
*
* The hardware requires a base_time that is always in the future.
@@ -581,3 +592,41 @@ int sparx5_tc_ets_del(struct sparx5_port *port)
return sparx5_dwrr_conf_set(port, &dwrr);
}
+
+void sparx5_tas_speed(struct sparx5_port *port, int speed)
+{
+ struct sparx5 *sparx5 = port->sparx5;
+ u8 spd;
+
+ switch (speed) {
+ case SPEED_10:
+ spd = TAS_SPEED_10;
+ break;
+ case SPEED_100:
+ spd = TAS_SPEED_100;
+ break;
+ case SPEED_1000:
+ spd = TAS_SPEED_1000;
+ break;
+ case SPEED_2500:
+ spd = TAS_SPEED_2500;
+ break;
+ case SPEED_5000:
+ spd = TAS_SPEED_5000;
+ break;
+ case SPEED_10000:
+ spd = TAS_SPEED_10000;
+ break;
+ case SPEED_25000:
+ spd = TAS_SPEED_25000;
+ break;
+ default:
+ netdev_err(port->ndev, "TAS: Unsupported speed: %d\n", speed);
+ return;
+ }
+
+ spx5_rmw(HSCH_TAS_PROFILE_CONFIG_LINK_SPEED_SET(spd),
+ HSCH_TAS_PROFILE_CONFIG_LINK_SPEED,
+ sparx5,
+ HSCH_TAS_PROFILE_CONFIG(port->portno));
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h
index 04f76f1e23f6..a92a699c551f 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h
@@ -60,6 +60,7 @@ struct sparx5_dwrr {
};
int sparx5_qos_init(struct sparx5 *sparx5);
+void sparx5_tas_speed(struct sparx5_port *port, int speed);
/* Multi-Queue Priority */
int sparx5_tc_mqprio_add(struct net_device *ndev, u8 num_tc);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c
index 220e81b714d4..3863f954bd83 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c
@@ -95,6 +95,7 @@ const unsigned int sparx5_gaddr[GADDR_LAST] = {
[GA_HSCH_SYSTEM] = 184000,
[GA_HSCH_MMGT] = 162368,
[GA_HSCH_TAS_CONFIG] = 162384,
+ [GA_HSCH_TAS_PROFILE_CFG] = 188416,
[GA_PTP_PTP_CFG] = 320,
[GA_PTP_PTP_TOD_DOMAINS] = 336,
[GA_PTP_PHASE_DETECTOR_CTRL] = 420,
@@ -129,6 +130,7 @@ const unsigned int sparx5_gcnt[GCNT_LAST] = {
[GC_GCB_SIO_CTRL] = 3,
[GC_HSCH_HSCH_CFG] = 5040,
[GC_HSCH_HSCH_DWRR] = 72,
+ [GC_HSCH_TAS_PROFILE_CFG] = 100,
[GC_PTP_PTP_PINS] = 5,
[GC_PTP_PHASE_DETECTOR_CTRL] = 5,
[GC_REW_PORT] = 70,
@@ -144,6 +146,7 @@ const unsigned int sparx5_gsize[GSIZE_LAST] = {
[GW_FDMA_FDMA] = 428,
[GW_GCB_CHIP_REGS] = 424,
[GW_HSCH_TAS_CONFIG] = 12,
+ [GW_HSCH_TAS_PROFILE_CFG] = 64,
[GW_PTP_PHASE_DETECTOR_CTRL] = 8,
[GW_QSYS_PAUSE_CFG] = 1128,
};
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h
index ea28130c2341..585589a31e90 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h
@@ -104,6 +104,7 @@ enum sparx5_gaddr_enum {
GA_HSCH_SYSTEM,
GA_HSCH_MMGT,
GA_HSCH_TAS_CONFIG,
+ GA_HSCH_TAS_PROFILE_CFG,
GA_PTP_PTP_CFG,
GA_PTP_PTP_TOD_DOMAINS,
GA_PTP_PHASE_DETECTOR_CTRL,
@@ -139,6 +140,7 @@ enum sparx5_gcnt_enum {
GC_GCB_SIO_CTRL,
GC_HSCH_HSCH_CFG,
GC_HSCH_HSCH_DWRR,
+ GC_HSCH_TAS_PROFILE_CFG,
GC_PTP_PTP_PINS,
GC_PTP_PHASE_DETECTOR_CTRL,
GC_REW_PORT,
@@ -155,6 +157,7 @@ enum sparx5_gsize_enum {
GW_FDMA_FDMA,
GW_GCB_CHIP_REGS,
GW_HSCH_TAS_CONFIG,
+ GW_HSCH_TAS_PROFILE_CFG,
GW_PTP_PHASE_DETECTOR_CTRL,
GW_QSYS_PAUSE_CFG,
GSIZE_LAST,
--
2.55.0
^ permalink raw reply related
* RE: [Intel-wired-lan] [PATCH iwl-next v4] libie: log more info when virtchnl fails
From: Salin, Samuel @ 2026-07-07 17:06 UTC (permalink / raw)
To: Loktionov, Aleksandr, Li Li, Nguyen, Anthony L,
Kitszel, Przemyslaw, David S. Miller, Jakub Kicinski,
Eric Dumazet, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
David Decotigny, Singhai, Anjali, Samudrala, Sridhar,
Brian Vazquez, Tantilov, Emil S
In-Reply-To: <IA3PR11MB8986AF8D9E84A7861B14B3F1E5382@IA3PR11MB8986.namprd11.prod.outlook.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of
> Loktionov, Aleksandr
> Sent: Monday, May 11, 2026 3:10 AM
> To: Li Li <boolli@google.com>; Nguyen, Anthony L
> <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
> <przemyslaw.kitszel@intel.com>; David S. Miller <davem@davemloft.net>;
> Jakub Kicinski <kuba@kernel.org>; Eric Dumazet <edumazet@google.com>;
> intel-wired-lan@lists.osuosl.org
> Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; David Decotigny
> <decot@google.com>; Singhai, Anjali <anjali.singhai@intel.com>; Samudrala,
> Sridhar <sridhar.samudrala@intel.com>; Brian Vazquez
> <brianvv@google.com>; Tantilov, Emil S <emil.s.tantilov@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH iwl-next v4] libie: log more info when
> virtchnl fails
>
>
>
> > -----Original Message-----
> > From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> > Of Li Li via Intel-wired-lan
> > Sent: Saturday, May 9, 2026 1:07 AM
> > To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
> > Przemyslaw <przemyslaw.kitszel@intel.com>; David S. Miller
> > <davem@davemloft.net>; Jakub Kicinski <kuba@kernel.org>; Eric Dumazet
> > <edumazet@google.com>; intel-wired-lan@lists.osuosl.org
> > Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; David
> > Decotigny <decot@google.com>; Singhai, Anjali
> > <anjali.singhai@intel.com>; Samudrala, Sridhar
> > <sridhar.samudrala@intel.com>; Brian Vazquez <brianvv@google.com>;
> > Tantilov, Emil S <emil.s.tantilov@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH iwl-next v4] libie: log more
> > info when virtchnl fails
> >
> > Friendly ping on this patch.
> >
> > Please let me know if you have any questions regarding this patch,
> > thank you!
> >
> > Li
> >
> > On Thu, Apr 30, 2026 at 6:25 PM Li Li <boolli@google.com> wrote:
> > >
> > > Virtchnl failures can be hard to debug without logs. Logging the
> > > details of virtchnl transactions can be useful for debugging
> > > virtchnl-related issues.
> > >
> > > Tested: Built & booted on a test machine and synthetically produced
> > a
> > > virtual failure to produce the following log:
> > >
> > > idpf 0000:01:00.0: Non-zero virtchnl ret val 6 (msg op: 1, data_len:
> > > 8); xn id: 0, cookie: 0 idpf 0000:01:00.0: Transaction failed (op 1,
> > > xn state:
> > > 3, id: 0, cookie: 0, size: 8)
> > >
> > > Signed-off-by: Li Li <boolli@google.com>
> > > ---
> > > v4:
> > > - Simplify logging to reduce redundant "ret val"s.
> > > - Use %u for xn->state.
> > > v3:
> > > - Use dev_err_ratelimited in both logs.
> > > - Move log placement to after virtchnl field validation.
> > > - Remove redundant op/cookie fields since they were validated.
> > > v2:
> > > - Use dev_warn_ratelimited instead of dev_notice_ratelimited based
> > on
> > > reviewer feedback.
> > > drivers/net/ethernet/intel/libie/controlq.c | 13 +++++++++++++
> > > 1 file changed, 13 insertions(+)
> > >
> > > diff --git a/drivers/net/ethernet/intel/libie/controlq.c
> > > b/drivers/net/ethernet/intel/libie/controlq.c
> > > index ebc05355e39d..e1bc19d6cdbf 100644
> > > --- a/drivers/net/ethernet/intel/libie/controlq.c
> > > +++ b/drivers/net/ethernet/intel/libie/controlq.c
> > > @@ -766,6 +766,14 @@ libie_ctlq_xn_process_recv(struct
> > libie_ctlq_xn_recv_params *params,
> > > msg_cookie != xn->cookie)
> > > return false;
> > >
> > > + if (ctlq_msg->chnl_retval) {
> > > + dev_err_ratelimited(
> > > + params->ctlq->dev,
> > > + "Non-zero virtchnl ret val %u (msg op: %u,
> > data_len: %u); xn id: %u, cookie: %u\n",
> > > + ctlq_msg->chnl_retval, ctlq_msg-
> > >chnl_opcode,
> > > + ctlq_msg->data_len, xn->index, xn->cookie);
> > > + }
> > > +
> > > spin_lock(&xn->xn_lock);
> > > if (xn->state != LIBIE_CTLQ_XN_ASYNC &&
> > > xn->state != LIBIE_CTLQ_XN_WAITING) { @@ -1011,6
> > +1019,11
> > > @@ int libie_ctlq_xn_send(struct libie_ctlq_xn_send_params *params)
> > > params->recv_mem = xn->recv_mem;
> > > break;
> > > default:
> > > + dev_err_ratelimited(
> > > + params->ctlq->dev,
> > > + "Transaction failed (op %u, xn state: %u,
> > id: %u, cookie: %u, size: %zu)\n",
> > > + params->chnl_opcode, xn->state, xn->index,
> > xn->cookie,
> > > + xn->recv_mem.iov_len);
> > > ret = -EBADMSG;
> > > break;
> > > }
> > > --
> > > 2.54.0.545.g6539524ca2-goog
> > >
>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
^ permalink raw reply
* [PATCH v2] mptcp: only set DATA_FIN when a mapping is present
From: Michael Bommarito @ 2026-07-07 17:17 UTC (permalink / raw)
To: Matthieu Baerts, Mat Martineau
Cc: Geliang Tang, Paolo Abeni, Eric Dumazet, Jakub Kicinski, mptcp,
netdev, linux-kernel
In-Reply-To: <2edcb1cd-f2db-4d0c-a0e3-fd2b4dd820fe@redhat.com>
mptcp_get_options() clears only the status group of struct
mptcp_options_received; data_seq, subflow_seq and data_len are filled in
by mptcp_parse_option() exclusively inside the DSS mapping block, which
runs only when the DSS M (mapping present) bit is set.
A peer can send a DSS option with the DATA_FIN flag set but the mapping
bit clear. The parser then records mp_opt->data_fin while leaving
data_len and data_seq uninitialized. For a zero-length segment
mptcp_incoming_options() evaluates
if (mp_opt.data_fin && mp_opt.data_len == 1 &&
mptcp_update_rcv_data_fin(msk, mp_opt.data_seq, mp_opt.dsn64))
which reads the uninitialized data_len and data_seq; KMSAN reports an
uninit-value in mptcp_incoming_options(). The stale data_seq can also be
fed into the receive-side DATA_FIN sequence tracking.
Record the DATA_FIN flag only when the DSS option carries a mapping, so
data_fin is never set without data_seq and data_len also being present.
data_fin is part of the status group that mptcp_get_options() clears up
front, so on the no-map path it stays zero and the zero-length DATA_FIN
branch is simply skipped. A DATA_FIN is always transmitted together with
a mapping (mptcp_write_data_fin() sets use_map along with data_seq and
data_len), so legitimate DATA_FIN handling is unaffected.
Fixes: 43b54c6ee382 ("mptcp: Use full MPTCP-level disconnect state machine")
Suggested-by: Paolo Abeni <pabeni@redhat.com>
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
v2: adopt Paolo Abeni's suggested approach - do not set mp_opt->data_fin
at all unless a mapping is present, rather than gating the consumer in
mptcp_incoming_options() (v1). data_fin then defaults to the value
mptcp_get_options() already clears it to (0) on the no-map path, so
the uninitialized data_len/data_seq are never read.
net/mptcp/options.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index dff3fd5d3b559..6d003b24b969f 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -157,7 +157,6 @@ static void mptcp_parse_option(const struct sk_buff *skb,
ptr++;
flags = (*ptr++) & MPTCP_DSS_FLAG_MASK;
- mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
mp_opt->dsn64 = (flags & MPTCP_DSS_DSN64) != 0;
mp_opt->use_map = (flags & MPTCP_DSS_HAS_MAP) != 0;
mp_opt->ack64 = (flags & MPTCP_DSS_ACK64) != 0;
@@ -178,6 +177,7 @@ static void mptcp_parse_option(const struct sk_buff *skb,
}
if (mp_opt->use_map) {
+ mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
if (mp_opt->dsn64)
expected_opsize += TCPOLEN_MPTCP_DSS_MAP64;
else
--
2.53.0
^ permalink raw reply related
* Re: [PATCH bpf] bpf, sockmap: Account for receive queue in FIONREAD without a verdict program
From: Emil Tsalapatis @ 2026-07-07 17:19 UTC (permalink / raw)
To: mattia.meleleo, John Fastabend, Jakub Sitnicki, Jiayuan Chen; +Cc: netdev, bpf
In-Reply-To: <20260707-fionread-no-verdict-v1-1-ce94a72357ec@coralogix.com>
On Tue Jul 7, 2026 at 12:15 PM EDT, Mattia Meleleo via B4 Relay wrote:
> From: Mattia Meleleo <mattia.meleleo@coralogix.com>
>
> tcp_bpf_ioctl() answers SIOCINQ from psock->msg_tot_len, which only
> counts bytes in ingress_msg. Without a stream/skb verdict program
> nothing is diverted there: data stays in sk_receive_queue, so FIONREAD
> returns 0 even though read() returns data.
>
> Add tcp_inq() to the reported value when the psock has no verdict
> program. The two queues are disjoint, so bytes redirected into
> ingress_msg from other sockets stay correctly accounted through
> msg_tot_len.
>
> Add a selftest covering FIONREAD without a verdict program.
>
> Fixes: 929e30f93125 ("bpf, sockmap: Fix FIONREAD for sockmap")
> Signed-off-by: Mattia Meleleo <mattia.meleleo@coralogix.com>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
I don't think the Sashiko READ_ONCE() recommendation is that important,
we're just checking for the pointers' existence and I don't see how the
reads can be moved/optimized out/merged in a way that breaks this code.
> ---
> net/ipv4/tcp_bpf.c | 16 ++++++++-
> .../selftests/bpf/prog_tests/sockmap_basic.c | 39 ++++++++++++++++++++++
> 2 files changed, 54 insertions(+), 1 deletion(-)
>
> diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
> index cc0bd73f3..a001b1fff 100644
> --- a/net/ipv4/tcp_bpf.c
> +++ b/net/ipv4/tcp_bpf.c
> @@ -334,6 +334,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
>
> static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
> {
> + struct sk_psock *psock;
> bool slow;
>
> if (cmd != SIOCINQ)
> @@ -344,7 +345,20 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
> return -EINVAL;
>
> slow = lock_sock_fast(sk);
> - *karg = sk_psock_msg_inq(sk);
> + psock = sk_psock_get(sk);
> + if (unlikely(!psock)) {
> + unlock_sock_fast(sk, slow);
> + return tcp_ioctl(sk, cmd, karg);
> + }
> + *karg = sk_psock_get_msg_len_nolock(psock);
> + /* Without a verdict program, ingress data is never diverted to
> + * ingress_msg: it stays in sk_receive_queue and is read through
> + * the fallback to tcp_recvmsg(), so account for it like
> + * tcp_ioctl() does.
> + */
> + if (!psock->progs.stream_verdict && !psock->progs.skb_verdict)
> + *karg += tcp_inq(sk);
> + sk_psock_put(sk, psock);
> unlock_sock_fast(sk, slow);
>
> return 0;
> diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> index cb3229711..f0f368201 100644
> --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> @@ -1373,6 +1373,43 @@ static void test_sockmap_multi_channels(int sotype)
> test_sockmap_pass_prog__destroy(skel);
> }
>
> +/* A socket in a sockmap without a verdict program keeps its ingress data
> + * in sk_receive_queue: FIONREAD must account for it.
> + */
> +static void test_sockmap_no_verdict_fionread(void)
> +{
> + int err, map, zero = 0, sent, avail;
> + int c0 = -1, c1 = -1, p0 = -1, p1 = -1;
> + struct test_sockmap_pass_prog *skel;
> + char buf[256] = "0123456789";
> +
> + skel = test_sockmap_pass_prog__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "open_and_load"))
> + return;
> + map = bpf_map__fd(skel->maps.sock_map_rx);
> +
> + err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1);
> + if (!ASSERT_OK(err, "create_socket_pairs()"))
> + goto out;
> +
> + err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST);
> + if (!ASSERT_OK(err, "bpf_map_update_elem(c1)"))
> + goto out_close;
> +
> + sent = xsend(p1, &buf, sizeof(buf), 0);
> + ASSERT_EQ(sent, sizeof(buf), "xsend(p1)");
> + avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC);
> + ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)");
> +
> +out_close:
> + close(c0);
> + close(p0);
> + close(c1);
> + close(p1);
> +out:
> + test_sockmap_pass_prog__destroy(skel);
> +}
> +
> void test_sockmap_basic(void)
> {
> if (test__start_subtest("sockmap create_update_free"))
> @@ -1415,6 +1452,8 @@ void test_sockmap_basic(void)
> test_sockmap_skb_verdict_shutdown();
> if (test__start_subtest("sockmap skb_verdict fionread"))
> test_sockmap_skb_verdict_fionread(true);
> + if (test__start_subtest("sockmap no_verdict fionread"))
> + test_sockmap_no_verdict_fionread();
> if (test__start_subtest("sockmap skb_verdict fionread on drop"))
> test_sockmap_skb_verdict_fionread(false);
> if (test__start_subtest("sockmap skb_verdict change tail"))
>
> ---
> base-commit: d2c9a99135da931377240942d44f3dea104cedb8
> change-id: 20260707-fionread-no-verdict-a4f8697ac9f9
>
> Best regards,
> --
> Mattia Meleleo <mattia.meleleo@coralogix.com>
^ permalink raw reply
* Re: [PATCH net] net: stmmac: resume PHY before reopening the interface on MTU change
From: Jakub Raczynski @ 2026-07-07 17:20 UTC (permalink / raw)
To: Stefan Agner
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, Russell King (Oracle), Maxime Chevallier,
Ovidiu Panait, Maxime Coquelin, Alexandre Torgue, netdev,
linux-stm32, linux-arm-kernel, regressions
In-Reply-To: <20260707162146.73823-1-stefan@agner.ch>
[-- Attachment #1: Type: text/plain, Size: 926 bytes --]
On Tue, Jul 07, 2026 at 06:21:46PM +0200, Stefan Agner wrote:
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -5884,6 +5884,15 @@
>
> __stmmac_release(dev);
>
> + /* phylink_stop() in __stmmac_release() suspends the PHY.
> + * IEEE 802.3 allows PHYs to stop their receive clock while
> + * powered down, but the DMA software reset performed by
> + * stmmac_hw_setup() requires a running receive clock.
> + * Resume the PHY, as on system resume, to ensure its clocks
> + * are running before reopening the interface.
> + */
> + phylink_prepare_resume(priv->phylink);
Does it work without warnings? Nothing in dmesg?
phylink_prepare_resume() does have ASSERT_RTNL() which is not called anywhere.
BR
Jakub Raczynski
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* Re: [PATCH net-next v5 1/4] dt-bindings: net: pse-pd: add bindings for Realtek/Broadcom PSE MCU
From: Conor Dooley @ 2026-07-07 17:25 UTC (permalink / raw)
To: Jonas Jelonek
Cc: Oleksij Rempel, Kory Maincent, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, netdev, devicetree,
linux-kernel, Daniel Golle, Bjørn Mork
In-Reply-To: <c19e563a-8931-4f31-b05a-ff8def4a5161@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 7547 bytes --]
On Mon, Jul 06, 2026 at 10:30:00PM +0200, Jonas Jelonek wrote:
> Hi Conor,
>
> On 06.07.26 19:35, Conor Dooley wrote:
> > On Mon, Jul 06, 2026 at 11:24:21AM +0000, Jonas Jelonek wrote:
> >> [...]
> >> +
> >> +description: |
> >> + Microcontroller (MCU) that fronts the PSE hardware on switches using
> >> + Realtek (RTL8238B, RTL8239, RTL8239C) or Broadcom (BCM59111, BCM59121)
> >> + PSE chips. The MCU exposes a small message-based protocol over either
> >> + I2C/SMBus or UART; the actual PSE silicon is not accessed directly. The
> >> + Realtek and Broadcom variants share this device tree contract but use
> >> + different protocol opcodes, selected by the compatible.
> >> +
> >> + The compatible identifies the PSE-MCU protocol dialect, not a specific
> >> + part. The device here is the MCU: it presents a stable message protocol
> >> + documented by Realtek, with the PSE silicon behind it - Broadcom on
> >> + older boards, Realtek on newer - detected at runtime and not described
> >> + here. The MCU's own silicon is general-purpose and varies across
> >> + boards, so the 'realtek' vendor prefix names the protocol front-end
> >> + (following the google,cros-ec pattern); the '-rtk'/'-brcm' suffix
> >> + selects the Realtek or Broadcom dialect.
> >> +
> >> + A single compatible per dialect covers both the I2C/SMBus and UART
> >> + attachments: the wire protocol is identical across them and the
> >> + transport is already expressed by the node's parent bus, so it is not
> >> + encoded in the compatible. Transport-specific properties differ
> >> + accordingly - the I2C attachment carries 'reg' (and, for Realtek,
> >> + 'realtek,i2c-protocol'), while the UART attachment carries the serial
> >> + peripheral properties such as 'current-speed'.
> > I'm not really convinced by the arguments here.
> >
> > If the switch vendors are running different software on their MCUs to
> > the point that they behave differently, then yes it makes sense to have
> > different compatibles.
> >
> > The first thing I don't understand is why realtek is considered the
> > main vendor here? Is it their MCU that broadcom are re-using with some
> > protocol tweaks?
>
> The protocol and firmware on the MCU, most likely the whole "solution",
> is from Realtek. The setup is always the same on most Realtek-based
> switches (saying most because a few counterexamples use completely
> different setups, not even Broadcom or Realtek PSE silicon). The host
> interface is always the same (except for I2C vs. SMBus vs. UART, which
> is likely just a config in the MCU firmware). Therefore "realtek," is the
> right prefix for all of these.
>
> Broadcom is not really involved here except for their PSE silicon being
> used. Maybe Realtek modeled their MCU host protocol after the one that
> Broadcom PSE silicon uses as host interface, but this is rather guessing.
>
> Maybe a historical view might help. Older RTL83xx-based switches with
> PoE shipped with this setup using Broadcom PSE silicon. From what I know,
> at this point Realtek didn't design their own PSE silicon. They used the
> Broadcom silicon, put a MCU as a manager in front of it with their firmware
> and a host protocol based on what Broadcom PSE itself uses. At some
> point Realtek started to design their own PSE silicon which then was
> used in newer switches instead of Broadcom PSE.
Right, in that case it does make sense to use a realtek prefix, since
the software and mcu solution is all theirs.
> > If it is, then having the vendor as a suffix like wheel reinvention to
> > me, and if the MCU and/or protocol aren't something that broadcom
> > borrowed from realtek then having a realtek vendor prefix is strange
> > altogether. The mention of old boards being broadcom while the protocol
> > is documented by realtek is confusing me.
> >
> > Either way, encoding the vendor without using the vendor prefix seems
> > very odd me to.
>
> I'm open for suggestions here. This has been the hardest issue in this
> whole series, to be honest. It basically boils down to differentiating
> these two protocol generations. I cannot say why Realtek did that on
> the transition to their own PSE silicon but vendors doing weird stuff
> shouldn't be surprising and this is the reality now.
>
> Is something like "-gen1" and "-gen2" better, with a clear description
> somewhere in the bindings how that maps to actual device setups
> (gen1 = older/MCU fronts Broadcom PSE, gen2 = newer/MCU fronts
> Realtek PSE)? This would drop Broadcom and the confusion around it
> here. I admit, using a vendor suffix isn't great and still attributes
> Broadcom too much in this context here.
>
> > Secondly, the compatibles you do provide seem too generic. Is it really
> > possible for a given board to use smbus AND i2c, or do specific boards
> > only ever use i2c OR smbus (or uart for that matter).
> > I find it more believable that a board would support i2c and uart than
> > supporting both i2c and smbus fwiw.
>
> Only one at a time is used, but not combined in any way. All switches
> I've seen so far always have a single management MCU for PoE, not
> multiple. Thus, only a single variant is used. Which variant is used
> likely depends on the board vendor which then tells Realtek "I want your
> PoE solution, I can attach it via (I2C/SMBus/UART)". At least for UART vs.
> I2C/SMBus there are sometimes valid reasons to use UART over the other.
>
> There is only a single switch (from Linksys) where the MCU expects raw
> I2C messages. SMBus transaction fail actually. But I don't see the reason
> why Linksys did it that way. The reason can't be that the MCU is attached
> on a bit-banged I2C because another switch uses SMBus transaction on
> a bit-banged I2C.
Reading this, it feels like you "should" have compatibles that uniquely
identify the protocol used. Looking at the devices below, it seems like it
would be possible to use compatibles based on the switches themselves, e.g.
zyxel,xs1930-pse etc. If there are other devices that use the same
protocol, they could fall back to the ones below.
It'd be good to have the net developers weigh in though, as to whether
using compatibles based on the switches is suitable.
> > Can you provide a link to the actual devices somewhere? It is
> > completely non-obvious to me what the binding actually represents.
>
> I hope I get your request correctly. Find some links to devices and/or
Ye, this is what I was looking for, thanks.
Conor.
> to my commits wiring that up in actual DTS (WIP, hashes may change).
>
> Zyxel XMG1915-10EP (UART with Realtek PSE silicon):
> https://svanheule.net/switches/xmg1915-10ep
> https://github.com/jonasjelonek/openwrt/commit/d173e64730a511e04b68271289be23ae4e98a02f
>
> Zyxel XS1930-12HP (SMBus with Realtek PSE silicon):
> https://www.zyxel.com/de/de/products/switch/10-12-port-10g-multi-gigabit-lite-l3-smart-managed-switch-xs1930-series
> https://github.com/jonasjelonek/openwrt/commit/74339c9a002032fc204b6b1fe07af259d8f51787
>
> Zyxel GS1900-10HPv1 (UART with Broadcom PSE silicon):
> https://svanheule.net/switches/gs1900-10hp
>
> Linksys LGS328MPC (I2C raw, not SMBus, with Realtek PSE silicon):
> https://support.linksys.com/kb/article/5133-en/
>
> (mostly Zyxel devices but that's just the main devices I work with)
>
> > Cheers,
> > Conor.
> >
> >
> >> [...]
> >>
>
> Best regards,
> Jonas
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH net] gve: fix Rx queue stall on alloc failure
From: Eddie Phillips @ 2026-07-07 17:28 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: Harshitha Ramamurthy, netdev, joshwash, andrew+netdev, davem,
edumazet, kuba, pabeni, ast, daniel, hawk, john.fastabend, bpf,
sdf, willemb, jordanrhee, nktgrg, maolson, jacob.e.keller,
thostet, csully, bcf, linux-kernel, stable
In-Reply-To: <akfd5abdGbxFl5o9@boxer>
On Fri, Jul 3, 2026 at 9:06 AM Maciej Fijalkowski
<maciej.fijalkowski@intel.com> wrote:
>
> On Fri, Jul 03, 2026 at 01:03:20AM -0700, Eddie Phillips wrote:
> > > I think this deserves to be pulled out of the timer logic?
> >
> > If by this you mean pull the stats into a separate patch, I agree.
>
> Hi Eddie,
>
> instead of forming a response at the top of the mail, please have your
> answers inlined; it is preferred way of communication on mailing lists.
>
> >
> > > - couldn't you detect this case within napi poll loop?
> >
> > It can only be detected after attempting to refill the queue and finding
> > that we are still below the critical threshold.
> >
> > > - if not, does it have to be per-q timer? wouldn't one global per pf timer
> > > satisfy your needs?
> >
> > There are a few ways a global timer could be implemented,
> > - The global timer could queue napi for *all* queues, which would
> > result in a lot of unnecessary work.
> > - The global timer could iterate over each queue and try to detect
> > the critical low buffer condition, however this would require
> > introducing synchronization between the timer and the napis, which
> > would introduce expensive locking into the hot path.
> > - The global timer could be paired with a bitmap that stores which
> > queues need to be serviced.
>
> bitmap would probably do the job but i won't insist here tho.
>
> One more question/idea:
> Before arming the starvation timer, could we first try to make a smaller batch
> of already-posted buffers visible to HW?
The maximum number of descriptors that a single RSC packet can consume
is 19, so 8 descriptors isn't enough to receive a maximum-sized RSC
packet. If the hardware runs out of buffers, it is supposed to close
the RSC window and flush the descriptors, but operating this close to
the hardware's limits could be risky in case there are HW bugs or edge
cases we're unaware of. I think building in a safety margin would be more
robust.
> It seems the HW can accept RX buffer tail doorbell updates at a granularity
> lower than the normal `GVE_RX_BUF_THRESH_DQO` batching threshold, apparently as
> low as 8 descriptors. If that is the case, could we first use this as an
> emergency low-watermark path: when refill posts at least 8 descriptors but does
> not reach the normal 32-descriptor threshold, ring the doorbell immediately and
> only arm the starvation timer if even that lower threshold cannot be reached?
>
> >
> > A `struct timer_list` is only 40 bytes, so the current implemention is
> > not expensive. Though a global timer is valid, it's not strictly better.
> >
> > That said, I agree that we can clean up the structure—I will move the
> > timer state from the individual RX rings to the `gve_priv` structure.
> >
> > On Wed, Jul 1, 2026 at 6:22 AM Maciej Fijalkowski
> > <maciej.fijalkowski@intel.com> wrote:
> > >
> > > On Wed, Jul 01, 2026 at 12:53:41AM +0000, Harshitha Ramamurthy wrote:
> > > > From: Eddie Phillips <eddiephillips@google.com>
> > > >
> > > > When the system is under extreme memory pressure, page allocations can
> > > > fail during the Rx buffer refill loop. If the number of buffers posted
> > > > to hardware falls below a critical low threshold and the refill loop
> > > > exits due to allocation failures, the queue can stall:
> > > >
> > > > 1. The device drops incoming packets because there are no descriptors.
> > > > 2. Since no packets are processed, no Rx completions are generated.
> > > > 3. Because no completions occur, NAPI is never scheduled, preventing
> > > > the refill loop from running again even after memory is freed.
> > > >
> > > > This results in a permanent queue stall.
> > > >
> > > > Resolve this by introducing a starvation recovery timer for each Rx queue.
> > > > If the number of buffers posted to hardware falls below a critical low
> > > > threshold, start a timer to periodically reschedule NAPI. Once NAPI runs
> > > > and successfully refills the queue above the threshold, the timer is
> > > > not rescheduled.
> > > >
> > > > Also add a new ethtool statistic "rx_critical_low_bufs" to track the
> > > > number of times the starvation recovery timer is triggered.
> > >
> > > I think this deserves to be pulled out of the timer logic?
> > >
> > > Two questions tho:
> > > - couldn't you detect this case within napi poll loop?
> > > - if not, does it have to be per-q timer? wouldn't one global per pf timer
> > > satisfy your needs?
> > >
> > > >
> > > > Cc: stable@vger.kernel.org
> > > > Fixes: 9b8dd5e5ea48 ("gve: DQO: Add RX path")
> > > > Reviewed-by: Jordan Rhee <jordanrhee@google.com>
> > > > Signed-off-by: Eddie Phillips <eddiephillips@google.com>
> > > > Signed-off-by: Harshitha Ramamurthy <hramamurthy@google.com>
> > > > ---
> > > > drivers/net/ethernet/google/gve/gve.h | 4 ++++
> > > > drivers/net/ethernet/google/gve/gve_ethtool.c | 14 +++++++++++++-
> > > > drivers/net/ethernet/google/gve/gve_rx_dqo.c | 32 ++++++++++++++++++++++++++++++++
> > > > 3 files changed, 49 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h
> > > > index 2f7bd330..8378bef2 100644
> > > > --- a/drivers/net/ethernet/google/gve/gve.h
> > > > +++ b/drivers/net/ethernet/google/gve/gve.h
> > > > @@ -13,6 +13,7 @@
> > > > #include <linux/netdevice.h>
> > > > #include <linux/net_tstamp.h>
> > > > #include <linux/pci.h>
> > > > +#include <linux/timer.h>
> > > > #include <linux/ptp_clock_kernel.h>
> > > > #include <linux/u64_stats_sync.h>
> > > > #include <net/page_pool/helpers.h>
> > > > @@ -41,6 +42,7 @@
> > > >
> > > > /* Interval to schedule a stats report update, 20000ms. */
> > > > #define GVE_STATS_REPORT_TIMER_PERIOD 20000
> > > > +#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */
> > > >
> > > > /* Numbers of NIC tx/rx stats in stats report. */
> > > > #define NIC_TX_STATS_REPORT_NUM 0
> > > > @@ -318,6 +320,7 @@ struct gve_rx_ring {
> > > > u64 rx_copied_pkt; /* free-running total number of copied packets */
> > > > u64 rx_skb_alloc_fail; /* free-running count of skb alloc fails */
> > > > u64 rx_buf_alloc_fail; /* free-running count of buffer alloc fails */
> > > > + u64 rx_critical_low_bufs; /* count of critical low buffer events */
> > > > u64 rx_desc_err_dropped_pkt; /* free-running count of packets dropped by descriptor error */
> > > > /* free-running count of unsplit packets due to header buffer overflow or hdr_len is 0 */
> > > > u64 rx_hsplit_unsplit_pkt;
> > > > @@ -334,6 +337,7 @@ struct gve_rx_ring {
> > > > struct gve_queue_resources *q_resources; /* head and tail pointer idx */
> > > > dma_addr_t q_resources_bus; /* dma address for the queue resources */
> > > > struct u64_stats_sync statss; /* sync stats for 32bit archs */
> > > > + struct timer_list starvation_timer; /* for queue starvation recovery */
> > > >
> > > > struct gve_rx_ctx ctx; /* Info for packet currently being processed in this ring. */
> > > >
> > > > diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c
> > > > index a0e0472b..71b6efbf 100644
> > > > --- a/drivers/net/ethernet/google/gve/gve_ethtool.c
> > > > +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
> > > > @@ -46,6 +46,7 @@ static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = {
> > > > "rx_hsplit_unsplit_pkt",
> > > > "interface_up_cnt", "interface_down_cnt", "reset_cnt",
> > > > "page_alloc_fail", "dma_mapping_error", "stats_report_trigger_cnt",
> > > > + "rx_critical_low_bufs",
> > > > };
> > > >
> > > > static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = {
> > > > @@ -58,6 +59,7 @@ static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = {
> > > > "rx_xdp_aborted[%u]", "rx_xdp_drop[%u]", "rx_xdp_pass[%u]",
> > > > "rx_xdp_tx[%u]", "rx_xdp_redirect[%u]",
> > > > "rx_xdp_tx_errors[%u]", "rx_xdp_redirect_errors[%u]", "rx_xdp_alloc_fails[%u]",
> > > > + "rx_critical_low_bufs[%u]",
> > > > };
> > > >
> > > > static const char gve_gstrings_tx_stats[][ETH_GSTRING_LEN] = {
> > > > @@ -151,12 +153,14 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > > > {
> > > > u64 tmp_rx_pkts, tmp_rx_hsplit_pkt, tmp_rx_bytes, tmp_rx_hsplit_bytes,
> > > > tmp_rx_skb_alloc_fail, tmp_rx_buf_alloc_fail,
> > > > + tmp_rx_critical_low_bufs,
> > > > tmp_rx_desc_err_dropped_pkt, tmp_rx_hsplit_unsplit_pkt,
> > > > tmp_tx_pkts, tmp_tx_bytes,
> > > > tmp_xdp_tx_errors, tmp_xdp_redirect_errors;
> > > > u64 rx_buf_alloc_fail, rx_desc_err_dropped_pkt, rx_hsplit_unsplit_pkt,
> > > > rx_pkts, rx_hsplit_pkt, rx_skb_alloc_fail, rx_bytes, tx_pkts, tx_bytes,
> > > > - tx_dropped, xdp_tx_errors, xdp_redirect_errors;
> > > > + rx_critical_low_bufs, tx_dropped, xdp_tx_errors,
> > > > + xdp_redirect_errors;
> > > > int rx_base_stats_idx, max_rx_stats_idx, max_tx_stats_idx;
> > > > int stats_idx, stats_region_len, nic_stats_len;
> > > > struct stats *report_stats;
> > > > @@ -197,6 +201,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > > >
> > > > for (rx_pkts = 0, rx_bytes = 0, rx_hsplit_pkt = 0,
> > > > rx_skb_alloc_fail = 0, rx_buf_alloc_fail = 0,
> > > > + rx_critical_low_bufs = 0,
> > > > rx_desc_err_dropped_pkt = 0, rx_hsplit_unsplit_pkt = 0,
> > > > xdp_tx_errors = 0, xdp_redirect_errors = 0,
> > > > ring = 0;
> > > > @@ -212,6 +217,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > > > tmp_rx_bytes = rx->rbytes;
> > > > tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail;
> > > > tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail;
> > > > + tmp_rx_critical_low_bufs =
> > > > + rx->rx_critical_low_bufs;
> > > > tmp_rx_desc_err_dropped_pkt =
> > > > rx->rx_desc_err_dropped_pkt;
> > > > tmp_rx_hsplit_unsplit_pkt =
> > > > @@ -226,6 +233,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > > > rx_bytes += tmp_rx_bytes;
> > > > rx_skb_alloc_fail += tmp_rx_skb_alloc_fail;
> > > > rx_buf_alloc_fail += tmp_rx_buf_alloc_fail;
> > > > + rx_critical_low_bufs += tmp_rx_critical_low_bufs;
> > > > rx_desc_err_dropped_pkt += tmp_rx_desc_err_dropped_pkt;
> > > > rx_hsplit_unsplit_pkt += tmp_rx_hsplit_unsplit_pkt;
> > > > xdp_tx_errors += tmp_xdp_tx_errors;
> > > > @@ -269,6 +277,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > > > data[i++] = priv->page_alloc_fail;
> > > > data[i++] = priv->dma_mapping_error;
> > > > data[i++] = priv->stats_report_trigger_cnt;
> > > > + data[i++] = rx_critical_low_bufs;
> > > > i = GVE_MAIN_STATS_LEN;
> > > >
> > > > rx_base_stats_idx = 0;
> > > > @@ -337,6 +346,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > > > tmp_rx_hsplit_bytes = rx->rx_hsplit_bytes;
> > > > tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail;
> > > > tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail;
> > > > + tmp_rx_critical_low_bufs =
> > > > + rx->rx_critical_low_bufs;
> > > > tmp_rx_desc_err_dropped_pkt =
> > > > rx->rx_desc_err_dropped_pkt;
> > > > tmp_xdp_tx_errors = rx->xdp_tx_errors;
> > > > @@ -381,6 +392,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
> > > > } while (u64_stats_fetch_retry(&priv->rx[ring].statss,
> > > > start));
> > > > i += GVE_XDP_ACTIONS + 3; /* XDP rx counters */
> > > > + data[i++] = tmp_rx_critical_low_bufs;
> > > > }
> > > > } else {
> > > > i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS;
> > > > diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
> > > > index 02cba280..303db4fa 100644
> > > > --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c
> > > > +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
> > > > @@ -18,6 +18,16 @@
> > > > #include <net/tcp.h>
> > > > #include <net/xdp_sock_drv.h>
> > > >
> > > > +static void gve_rx_starvation_timer(struct timer_list *t)
> > > > +{
> > > > + struct gve_rx_ring *rx = timer_container_of(rx, t, starvation_timer);
> > > > + struct gve_priv *priv = rx->gve;
> > > > + struct gve_notify_block *block;
> > > > +
> > > > + block = &priv->ntfy_blocks[rx->ntfy_id];
> > > > + napi_schedule(&block->napi);
> > > > +}
> > > > +
> > > > static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx)
> > > > {
> > > > struct device *hdev = &priv->pdev->dev;
> > > > @@ -120,6 +130,7 @@ void gve_rx_stop_ring_dqo(struct gve_priv *priv, int idx)
> > > >
> > > > if (rx->dqo.page_pool)
> > > > page_pool_disable_direct_recycling(rx->dqo.page_pool);
> > > > + timer_delete_sync(&rx->starvation_timer);
> > > > gve_remove_napi(priv, ntfy_idx);
> > > > gve_rx_remove_from_block(priv, idx);
> > > > gve_rx_reset_ring_dqo(priv, idx);
> > > > @@ -136,6 +147,8 @@ void gve_rx_free_ring_dqo(struct gve_priv *priv, struct gve_rx_ring *rx,
> > > > u32 qpl_id;
> > > > int i;
> > > >
> > > > + timer_shutdown_sync(&rx->starvation_timer);
> > > > +
> > > > completion_queue_slots = rx->dqo.complq.mask + 1;
> > > > buffer_queue_slots = rx->dqo.bufq.mask + 1;
> > > >
> > > > @@ -232,6 +245,7 @@ int gve_rx_alloc_ring_dqo(struct gve_priv *priv,
> > > > rx->gve = priv;
> > > > rx->q_num = idx;
> > > > rx->packet_buffer_size = cfg->packet_buffer_size;
> > > > + timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0);
> > > >
> > > > if (cfg->xdp) {
> > > > rx->packet_buffer_truesize = GVE_XDP_RX_BUFFER_SIZE_DQO;
> > > > @@ -365,6 +379,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)
> > > > struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq;
> > > > struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq;
> > > > struct gve_priv *priv = rx->gve;
> > > > + u32 num_bufs_avail_to_hw;
> > > > u32 num_avail_slots;
> > > > u32 num_full_slots;
> > > > u32 num_posted = 0;
> > > > @@ -400,6 +415,23 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)
> > > > }
> > > >
> > > > rx->fill_cnt += num_posted;
> > > > +
> > > > + /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors
> > > > + * visible to the hardware, and no doorbell was written, the hardware
> > > > + * is in danger of starving and cannot trigger interrupts. Start the
> > > > + * timer to periodically reschedule NAPI and recover from starvation.
> > > > + */
> > > > + num_bufs_avail_to_hw =
> > > > + ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) -
> > > > + bufq->head) & bufq->mask;
> > > > +
> > > > + if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) {
> > > > + u64_stats_update_begin(&rx->statss);
> > > > + rx->rx_critical_low_bufs++;
> > > > + u64_stats_update_end(&rx->statss);
> > > > + mod_timer(&rx->starvation_timer,
> > > > + jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS));
> > > > + }
> > > > }
> > > >
> > > > static void gve_rx_skb_csum(struct sk_buff *skb,
> > > > --
> > > > 2.55.0.rc2.803.g1fd1e6609c-goog
> > > >
> > > >
^ permalink raw reply
* Re: [PATCH net-next] net: dpaa_eth: convert to napi_gro_receive
From: Vladimir Oltean @ 2026-07-07 17:34 UTC (permalink / raw)
To: Rosen Penev
Cc: netdev, Madalin Bucur, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, open list
In-Reply-To: <CAKxU2N-6Msn+WciepQ3rnP64fUE6s_jviWfy8no_-qorPoyJEw@mail.gmail.com>
On Mon, Jul 06, 2026 at 04:25:31PM -0700, Rosen Penev wrote:
> On Mon, Jul 6, 2026 at 1:39 AM Vladimir Oltean <olteanv@gmail.com> wrote:
> > Have you tested traffic in mixed scenarios, where flows from multiple
> > interfaces land on the same CPU?
> Just iperf3 --bidir.
Just this would not have reproduced the problem I pointed out.
> > Until further evidence comes in:
> Maybe netif_receive_skb_list is a better fit here.
Maybe or maybe not. I don't know how netif_receive_skb_list() is
implemented and whether it shares the same pain points as
napi_gro_receive() on DPAA1, or what performance it has to offer.
Testing in a wide variety of circumstances should show that.
I currently have very limited access to email for the rest of this week.
I did not mean to completely dismiss napi_gro_receive(), just point out
what kind of complexity is required for proper operation when the QMan
portal mixes frames from different interfaces in the same NAPI poll
cycle.
^ permalink raw reply
* [PATCH net v2 1/2] net/stmmac: Set Rx queue page_pool to NULL when freeing DMA resources
From: Jakub Raczynski @ 2026-07-07 17:41 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski,
Maxime Chevallier
In-Reply-To: <20260707174115.1264466-1-j.raczynski@samsung.com>
When freeing RX descriptor resources, there is standard clearing of
descriptor page_pool via page_pool_destroy() which does destroy
page but does not set its pointer to NULL, which must be done by driver
calling this function.
It is not done in __free_dma_rx_desc_resources() when stopping interface,
which is generally not an issue, because __alloc_dma_rx_desc_resources() does
setup this regardless of previous state.
But above is true assuming reinitialization is successful.
In case of failure of page_pool_create() in __alloc_dma_rx_desc_resources(),
all non-NULL pages will be freed, including those already cleared.
So there is possible kernel panic due to wrong paging request at address.
Fix this by assigning NULL to page_pool pointer on free.
Also remove NULL check as page_pool_destroy() does check for NULL param.
Fixes: da5ec7f22a0f1 ("net: stmmac: refactor stmmac_init_rx_buffers for stmmac_reinit_rx_buffers")
Signed-off-by: Yashwant Varur <yashwant.v@samsung.com>
Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 2a0d7eff88d3..3098971e0b66 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2172,8 +2172,8 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
xdp_rxq_info_unreg(&rx_q->xdp_rxq);
kfree(rx_q->buf_pool);
- if (rx_q->page_pool)
- page_pool_destroy(rx_q->page_pool);
+ page_pool_destroy(rx_q->page_pool);
+ rx_q->page_pool = NULL;
}
static void free_dma_rx_desc_resources(struct stmmac_priv *priv,
--
2.34.1
^ permalink raw reply related
* [PATCH net 0/2] net/stmmac: Secure against failures of DMA memory allocation
From: Jakub Raczynski @ 2026-07-07 17:41 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski
In-Reply-To: <CGME20260707174134eucas1p2d88b2b6007e833f02ef6497b388374ef@eucas1p2.samsung.com>
This series fixing two possible issues related to fails of
__alloc_dma_rx_desc_resources(). Original issue from 1st patch is related to
page_pool that has happened in testing env, while second was requested by
Sashiko to have similar change for DMA allocation.
To have complete fix for all failures of __alloc_dma_rx_desc_resources(),
merge two fixes into series.
---
Note:
1st patch "Set Rx queue page_pool to NULL when freeing DMA resources" is
set to v2 while this series is v1. I know this is inconsistent, but this
series is not v2 and would be even more confusing.
Hopefully that doesn't break some CI, as second patch is v1.
Link to original:
https://lore.kernel.org/netdev/20260630100953.747868-1-j.raczynski@samsung.com/
Changes in v2 (in first patch):
- Added reviewed by Maxime
- Dropped null check as page_pool_destroy() does provide that
- Modified comment to reflect that
Jakub Raczynski (2):
net/stmmac: Set Rx queue page_pool to NULL when freeing DMA resources
net/stmmac: Prevent dma queue NULL free on allocation failure
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 20 +++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH net 2/2] net/stmmac: Prevent dma queue NULL free on allocation failure
From: Jakub Raczynski @ 2026-07-07 17:41 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski, Sashiko AI
In-Reply-To: <20260707174115.1264466-1-j.raczynski@samsung.com>
During allocation of RX/TX descriptor resources and its DMA,
there is verification of failed dma_alloc_coherent() due to lack of memory.
In case of that failure, all allocated resources are freed instantly after,
but there are no checks for dma_free_coherent() whether previous step has
failed.
This will generally result in panic due to freeing NULL address.
Fix it by adding NULL verification of memory that is to be freed.
Theoretically code should also set address of pointed memory to zero when
freeing, but currently the only path of invalid address is non intialized zero,
and there is no case possible of double-free of same memory.
Fixes: e73b19baa3b1c ("net: stmmac: simplify DMA descriptor allocation/init/freeing")
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3098971e0b66..187d9bbc61d9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2146,7 +2146,6 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
u32 queue)
{
struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
- size_t size;
void *addr;
/* Release the DMA RX socket buffers */
@@ -2164,9 +2163,12 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
else
addr = rx_q->dma_rx;
- size = stmmac_get_rx_desc_size(priv) * dma_conf->dma_rx_size;
+ if (!IS_ERR_OR_NULL(addr)) {
+ size_t size;
+ size = stmmac_get_rx_desc_size(priv) * dma_conf->dma_rx_size;
- dma_free_coherent(priv->device, size, addr, rx_q->dma_rx_phy);
+ dma_free_coherent(priv->device, size, addr, rx_q->dma_rx_phy);
+ }
if (xdp_rxq_info_is_reg(&rx_q->xdp_rxq))
xdp_rxq_info_unreg(&rx_q->xdp_rxq);
@@ -2198,7 +2200,6 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv,
u32 queue)
{
struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
- size_t size;
void *addr;
/* Release the DMA TX socket buffers */
@@ -2212,9 +2213,12 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv,
addr = tx_q->dma_tx;
}
- size = stmmac_get_tx_desc_size(priv, tx_q) * dma_conf->dma_tx_size;
+ if (!IS_ERR_OR_NULL(addr)) {
+ size_t size;
+ size = stmmac_get_tx_desc_size(priv, tx_q) * dma_conf->dma_tx_size;
- dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
+ dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
+ }
kfree(tx_q->tx_skbuff_dma);
kfree(tx_q->tx_skbuff);
--
2.34.1
^ permalink raw reply related
* [PATCH net 0/2] net/stmmac: Verify more wrong DTS configuration
From: Jakub Raczynski @ 2026-07-07 17:44 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski
In-Reply-To: <CGME20260707174437eucas1p2cedaa017eea6cd5696535b17925a3b53@eucas1p2.samsung.com>
Commit 8a7bca6de6de protected against possible wrong number of TX/RX queues,
which could happen with new XGMAC hardware that has more supported queues than
currently available in driver.
Sashiko mentioned that there should also be protection against zero input
and some AXI related config, as these are possible kernel panics too.
While this series has lower value than original patch, since this
misconfiguration should not happen by pure mistake, there is no reason not
to fix it.
Jakub Raczynski (2):
net/stmmac: Protect against zero queue DTS config
net/stmmac: Verify provided DTS AXI setup
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH net 1/2] net/stmmac: Protect against zero queue DTS config
From: Jakub Raczynski @ 2026-07-07 17:44 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski
In-Reply-To: <20260707174431.1264520-1-j.raczynski@samsung.com>
Commit 8a7bca6de6de protected against inputing number of tx/rx_queues_to_use
over kernel supported limit in DTS config. AI review mentioned that we also
should protect against zero queue input, because this would cause issues
down the line. Missing config is not an issue as stmmac_plat_dat_alloc()
does apply '1' by default.
Fix this by adding check for zero queues input during DTS parsing
Fixes: 8a7bca6de6de ("net/stmmac: Apply MTL_MAX queue limit if config missing")
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index dc5f951a311d..9112cd69b9b1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -158,6 +158,8 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
if (!of_property_read_u32(rx_node, "snps,rx-queues-to-use", &value)) {
if (value > MTL_MAX_RX_QUEUES)
value = MTL_MAX_RX_QUEUES;
+ else if (value == 0)
+ value = 1;
plat->rx_queues_to_use = value;
}
@@ -212,6 +214,8 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
if (!of_property_read_u32(tx_node, "snps,tx-queues-to-use", &value)) {
if (value > MTL_MAX_TX_QUEUES)
value = MTL_MAX_TX_QUEUES;
+ else if (value == 0)
+ value = 1;
plat->tx_queues_to_use = value;
}
--
2.34.1
^ permalink raw reply related
* [PATCH net 2/2] net/stmmac: Verify provided DTS AXI setup
From: Jakub Raczynski @ 2026-07-07 17:44 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski
In-Reply-To: <20260707174431.1264520-1-j.raczynski@samsung.com>
During parsing of AXI setup, there are few issues:
- 'axi_blen' array is uninitialized value on stack without zero-init stack
configured. This can result in random AXI burst length config if
DTS config provides shorter array than AXI_BLEN.
Fix this by initializing it to zero, which allows to provide shorter configs,
which sometimes happens where only one value is used.
- stmmac_axi_blen_to_mask() is executed regardless of return of
of_property_read_u32_array(). This is not issue after axi_blen is
initialized to zero, as zero blen values are skipped in
stmmac_axi_blen_to_mask(), but that would be useless operation.
Fix it by checking return value of of_property_read_u32_array() and
parse any legit config.
- In case of failed memory allocation for AXI and error, there is no handling
of that. Fix it by checking if AXI config is error and return if so,
as this can only lack of memory. No AXI config, although is probably
wrong in most cases, is not treated as error, as generic config is mostly
provided in drivers.
Fixes: afea03656add ("stmmac: rework DMA bus setting and introduce new platform AXI structure")
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 9112cd69b9b1..0acc61a98292 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -95,7 +95,7 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
{
struct device_node *np;
struct stmmac_axi *axi;
- u32 axi_blen[AXI_BLEN];
+ u32 axi_blen[AXI_BLEN] = { 0 };
np = of_parse_phandle(pdev->dev.of_node, "snps,axi-config", 0);
if (!np)
@@ -115,8 +115,8 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
axi->axi_wr_osr_lmt = 1;
if (of_property_read_u32(np, "snps,rd_osr_lmt", &axi->axi_rd_osr_lmt))
axi->axi_rd_osr_lmt = 1;
- of_property_read_u32_array(np, "snps,blen", axi_blen, AXI_BLEN);
- stmmac_axi_blen_to_mask(&axi->axi_blen_regval, axi_blen, AXI_BLEN);
+ if (!of_property_read_u32_array(np, "snps,blen", axi_blen, AXI_BLEN))
+ stmmac_axi_blen_to_mask(&axi->axi_blen_regval, axi_blen, AXI_BLEN);
of_node_put(np);
return axi;
@@ -580,6 +580,10 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
of_property_read_u32(np, "snps,ps-speed", &plat->mac_port_sel_speed);
plat->axi = stmmac_axi_setup(pdev);
+ if (IS_ERR(plat->axi)) {
+ ret = plat->axi;
+ goto error_put_mdio;
+ }
rc = stmmac_mtl_setup(pdev, plat);
if (rc) {
--
2.34.1
^ permalink raw reply related
* [PATCH net-next V5 0/6] devlink: Add boot-time eswitch mode defaults
From: Mark Bloch @ 2026-07-07 17:45 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
This series adds a devlink_eswitch_mode= kernel command line parameter
for setting a default devlink eswitch mode during boot.
Following the discussion with Jakub[1] and the feedback on the RFC
postings, this version keeps the scope limited to a boot-time devlink
eswitch mode default only.
The option selects either all devlink handles or an explicit
comma-separated handle list:
devlink_eswitch_mode=*=switchdev
devlink_eswitch_mode=pci/0000:08:00.0,pci/0000:09:00.1=switchdev_inactive
The supported modes are legacy, switchdev and switchdev_inactive. The
selected mode is applied through the existing eswitch_mode_set() devlink
operation, the same operation used by the devlink eswitch mode command.
Registration may happen before a driver is ready to change eswitch mode,
so devlink core marks the default as pending and queues async work from
devl_unlock() once the instance is registered. The worker takes the
devlink instance lock before calling into the driver.
After a successful reload that performed DRIVER_REINIT, devlink core
already holds the devlink instance lock and the driver completed
reload_up(), so the default is applied directly from the reload path.
Drivers that know exactly when the device is ready can call
devl_apply_default_esw_mode() directly. mlx5 uses this after initial
probe, when the device is initialized and the devlink lock is already
held.
Patch 1 clears the mlx5 FW reset-in-progress bit before reload.
Patch 2 factors the common eswitch mode set validation into a helper.
Patch 3 adds the devlink_eswitch_mode= parser and documentation.
Patch 4 applies parsed defaults from devlink core.
Patch 5 adds devl_apply_default_esw_mode() for drivers.
Patch 6 wires mlx5 to apply the default after initial probe.
Changelog:
v4 -> v5:
- Moved the default eswitch mode code into a separate file, per Jiri's
comment.
- Dropped the delayed workqueue and switched to regular work triggered
via devl_unlock(), per Jiri's comment.
- Renamed some functions to better align with devlink code.
v3 -> v4:
- Rework registration time apply to use per devlink delayed work instead
of calling eswitch_mode_set() directly from devl_register().
- Apply the default directly after successful DRIVER_REINIT devlink reload,
where the devlink lock is already held and reload_up() has completed.
- Add devl_apply_default_esw_mode() for drivers that know their exact ready
point.
- Drop the driver registration-ordering preparation patches that are no
longer needed with the async registration apply path.
v2 -> v3:
- Change the devlink_eswitch_mode= API syntax to use <selector>=<mode>
instead of [<selector>]:<mode>, following a comment from Randy Dunlap.
v1 -> v2:
- Move default eswitch mode application into devlink core. The default is
now applied during devlink registration and after a successful devlink
reload that performed DRIVER_REINIT.
- Remove the exported devl_apply_default_esw_mode() driver API and the mlx5
driver-side call to it.
- Skip devlink health recovery notifications while the devlink instance is
not registered, so drivers can move registration later without early
health work hitting registration assertions.
- Move mlx5 devlink registration after device initialization, including the
lightweight init path, so the core can apply the default through the
normal registration flow.
- Move the matching netdevsim and mlx5 unregister paths before object
teardown, so unregister notifications come from devl_unregister() and the
later object teardown paths run while the devlink instance is no longer
registered.
- Add registration-ordering preparation patches for netdevsim and octeontx2
AF/PF, so their eswitch state is ready before registration-time defaults
may call eswitch_mode_set().
[1] lore.kernel.org/r/20260502184153.4fd8d06f@kernel.org/
RFC v1: lore.kernel.org/r/20260506123739.1959770-1-mbloch@nvidia.com/
RFC v2: lore.kernel.org/r/20260510185424.2041415-1-mbloch@nvidia.com/
v1: lore.kernel.org/r/20260521072434.362624-1-tariqt@nvidia.com/
v2: lore.kernel.org/all/20260603193259.3412464-1-mbloch@nvidia.com/
v3: lore.kernel.org/all/20260605181030.3486619-1-mbloch@nvidia.com/
v4: lore.kernel.org/all/20260629182102.245150-1-mbloch@nvidia.com/
Mark Bloch (6):
net/mlx5: Clear FW reset-in-progress bit before reload
devlink: Factor out eswitch mode setting
devlink: Parse eswitch mode boot defaults
devlink: Apply eswitch mode boot defaults
devlink: Add API to apply eswitch mode boot default
net/mlx5: Apply devlink eswitch mode boot default on probe
.../admin-guide/kernel-parameters.txt | 25 ++
.../networking/devlink/devlink-defaults.rst | 78 ++++
Documentation/networking/devlink/index.rst | 1 +
.../ethernet/mellanox/mlx5/core/fw_reset.c | 28 +-
.../net/ethernet/mellanox/mlx5/core/main.c | 13 +
include/net/devlink.h | 1 +
net/devlink/Makefile | 2 +-
net/devlink/core.c | 13 +
net/devlink/default.c | 364 ++++++++++++++++++
net/devlink/dev.c | 33 +-
net/devlink/devl_internal.h | 12 +
11 files changed, 551 insertions(+), 19 deletions(-)
create mode 100644 Documentation/networking/devlink/devlink-defaults.rst
create mode 100644 net/devlink/default.c
base-commit: 4a13f31a92f35161b499bf29638336885259da78
--
2.43.0
^ permalink raw reply
* [PATCH net-next V5 1/6] net/mlx5: Clear FW reset-in-progress bit before reload
From: Mark Bloch @ 2026-07-07 17:45 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch, Shay Drori, Moshe Shemesh
In-Reply-To: <20260707174527.425134-1-mbloch@nvidia.com>
mlx5 sets MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS when acknowledging a sync
reset request. This bit blocks devlink reload and other devlink operations
while the firmware reset is running, but it was kept set until after the
driver reload finished.
Clear the reset-in-progress bit once the reset unload flow is done and PCI
access is back, before reloading the device. For a reset initiated through
devlink, clear it before completing the reload waiter. For a reset reported
through an asynchronous firmware event, keep the unload flow outside
devl_lock, then take devl_lock before clearing the bit and reloading
through the devl-locked load helper.
Reviewed-by: Shay Drori <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
.../ethernet/mellanox/mlx5/core/fw_reset.c | 28 +++++++++++--------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
index 07440c58713a..7283e5b49eed 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
@@ -238,24 +238,30 @@ static void mlx5_fw_reset_complete_reload(struct mlx5_core_dev *dev)
{
struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset;
struct devlink *devlink = priv_to_devlink(dev);
+ int err;
/* if this is the driver that initiated the fw reset, devlink completed the reload */
if (test_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags)) {
+ clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS,
+ &fw_reset->reset_flags);
complete(&fw_reset->done);
- } else {
- mlx5_sync_reset_unload_flow(dev, false);
- if (mlx5_health_wait_pci_up(dev))
- mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
- else
- mlx5_load_one(dev, true);
- devl_lock(devlink);
- devlink_remote_reload_actions_performed(devlink, 0,
- BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
- BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
- devl_unlock(devlink);
+ return;
}
+ mlx5_sync_reset_unload_flow(dev, false);
+ err = mlx5_health_wait_pci_up(dev);
+
+ devl_lock(devlink);
clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags);
+ if (err)
+ mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
+ else
+ mlx5_load_one_devl_locked(dev, true);
+
+ devlink_remote_reload_actions_performed(devlink, 0,
+ BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
+ BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
+ devl_unlock(devlink);
}
static void mlx5_stop_sync_reset_poll(struct mlx5_core_dev *dev)
--
2.43.0
^ permalink raw reply related
* [PATCH net v3 0/2] net/stmmac: Fix panic during interface shutdown & apply STMMAC_DOWN flag
From: Jakub Raczynski @ 2026-07-07 17:45 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski
In-Reply-To: <CGME20260707174555eucas1p231d122ef4cc791f59dd36cb78378954c@eucas1p2.samsung.com>
When testing interface shutdown during XDP operation using stmmac driver,
we have encountered kernel panics, either poison overwritten or wrong
memory access, caused by XDP processing data after shutting down NAPI.
To fix this problem:
- Add STMMAC_DOWN handling to all XDP paths
- Change location of synchronize_rcu() to proper place
- Apply STMMAC_DOWN flag on interface open()/release().
This flag is used for XDP only and does improve handling in edge cases
Proper order of ensuring proper XDP shutdown is already present in functions
from stmmac_xdp.c during xdp_disable_pool(), since XDP can still have
data with NAPI disabled, as disabling NAPI does not ensure XDP finish.
synchronize_rcu() must be executed after IRQ's & DMA channels are disabled to
flush data.
---
Changes in v3:
- Discard almost all changes from v2.
This was a mistake as napi_disable() does ensure napi_synchronize() and
it didn't fix anything in the end.
- Return STMMAC_DOWN flag set/clear
- Create new function that executes synchronize_rcu() and call it from
proper places
- Remove synchronize_rcu() from stmmac_disable_all_queues()
- Remove barren stmmac_disable_all_queues() and replace it with
__stmmac_disable_all_queues()
- Fix memory leak in STMMAC_DOWN handling in modified XDP paths
Changes in v2:
- Split patch into two: one for XDP paths and second for general fix
- Change commit messages & title
- Fix all cases of NAPI release, not only XDP, via modyfying
stmmac_disable_all_queues() instead of separate later call
- Drop setting/clearing of STMMAC_DOWN flag in release()/open()
Link to v2:
https://lore.kernel.org/all/20260601163258.554300-3-j.raczynski@samsung.com
Link to v1:
https://lore.kernel.org/all/20260511165045.3091475-1-j.raczynski@samsung.com
Jakub Raczynski (2):
net/stmmac: Check for STMMAC_DOWN flag in all XDP paths
net/stmmac: Fix free-after-use panic when interface goes does with XDP
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 40 ++++++++++++++-----
1 file changed, 31 insertions(+), 9 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH net v3 1/2] net/stmmac: Check for STMMAC_DOWN flag in all XDP paths
From: Jakub Raczynski @ 2026-07-07 17:45 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski
In-Reply-To: <20260707174551.1264558-1-j.raczynski@samsung.com>
Currently STMMAC_DOWN flag is only set/cleared by stmmac_reset_subtask(),
to notify driver to stop processing of TX/RX frames. One of these processing
paths is for XDP, but it is only ever checked in stmmac_xdp_xmit(), which
leaves all other XDP paths vulnerable to processing data while interface is
restarting.
Make verification of STMMAC_DOWN flag consistent by applying check to
all XDP paths.
Fixes: 8b278a5b69a22 ("net: stmmac: Add support for XDP_REDIRECT action")
Co-developed-by: Chang-Sub Lee <cs0617.lee@samsung.com>
Signed-off-by: Chang-Sub Lee <cs0617.lee@samsung.com>
Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 2a0d7eff88d3..b9ffff001baf 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -5260,12 +5260,18 @@ static int stmmac_xdp_xmit_back(struct stmmac_priv *priv,
struct xdp_buff *xdp)
{
bool zc = !!(xdp->rxq->mem.type == MEM_TYPE_XSK_BUFF_POOL);
- struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp);
+ struct xdp_frame *xdpf;
int cpu = smp_processor_id();
struct netdev_queue *nq;
int queue;
int res;
+ if (unlikely(test_bit(STMMAC_DOWN, &priv->state))) {
+ xsk_buff_free(xdp);
+ return STMMAC_XSK_CONSUMED;
+ }
+
+ xdpf = xdp_convert_buff_to_frame(xdp);
if (unlikely(!xdpf))
return STMMAC_XDP_CONSUMED;
@@ -5310,7 +5316,9 @@ static int __stmmac_xdp_run_prog(struct stmmac_priv *priv,
res = stmmac_xdp_xmit_back(priv, xdp);
break;
case XDP_REDIRECT:
- if (xdp_do_redirect(priv->dev, xdp, prog) < 0)
+ if (unlikely(test_bit(STMMAC_DOWN, &priv->state)))
+ res = STMMAC_XDP_CONSUMED;
+ else if (xdp_do_redirect(priv->dev, xdp, prog) < 0)
res = STMMAC_XDP_CONSUMED;
else
res = STMMAC_XDP_REDIRECT;
--
2.34.1
^ permalink raw reply related
* [PATCH net v3 2/2] net/stmmac: Fix free-after-use panic when interface goes does with XDP
From: Jakub Raczynski @ 2026-07-07 17:45 UTC (permalink / raw)
To: netdev
Cc: k.tegowski, k.domagalski, andrew+netdev, davem, edumazet, kuba,
pabeni, mcoquelin.stm32, alexandre.torgue, linux-stm32,
linux-arm-kernel, linux-kernel, Jakub Raczynski
In-Reply-To: <20260707174551.1264558-1-j.raczynski@samsung.com>
When running XDP forwarding and interface gets shut down, kernel might panic
or show SLUB "poison overwritten" errors due to a race condition between
NAPI polling and resource freeing.
Observed error is one of following:
- Poison overwrriten
[ 1889.547746] eth1: Link is Down
[ 1889.549940] =============================================================================
[ 1889.549954] BUG kmalloc-4k (Tainted: G B ): Poison overwritten
[ 1889.549959] -----------------------------------------------------------------------------
[ 1889.549963] 0xffffff882dcc4d80-0xffffff882dcc4da7 @offset=19840. First byte 0x0 instead of 0x6b
[ 1889.549969] Allocated in __alloc_dma_tx_desc_resources+0x60/0x10c [stmmac] age=169 cpu=7 pid=27759
[ 1889.550020] __kmem_cache_alloc_node+0x100/0x2e8
[ 1889.550032] __kmalloc+0x58/0x1a0
[ 1889.550039] __alloc_dma_tx_desc_resources+0x60/0x10c [stmmac]
[ 1889.550052] alloc_dma_desc_resources+0xec/0x164 [stmmac]
[ 1889.550064] stmmac_setup_dma_desc+0xec/0x1e4 [stmmac]
[ 1889.550076] stmmac_open+0x28/0x94 [stmmac]
[...]
- Wrong memory address
[ 1901.546692] Unable to handle kernel paging request at virtual address dead000000000122
[...]
[ 1902.964068] Call trace:
[ 1902.967193] free_to_partial_list+0x560/0x600
[ 1902.972227] __slab_free+0x1a8/0x420
[ 1902.976480] __kmem_cache_free+0x204/0x218
[ 1902.981254] kfree+0x6c/0x128
[ 1902.984900] kvfree+0x3c/0x4c
[ 1902.988545] page_pool_release+0x234/0x27c
[ 1902.993320] page_pool_destroy+0xcc/0x190
[ 1902.998006] __free_dma_rx_desc_resources+0x100/0x360 [stmmac]
[ 1903.004516] free_dma_desc_resources+0x8c/0xac [stmmac]
[ 1903.010419] stmmac_release+0x1c0/0x2b4 [stmmac]
[...]
Root cause is stmmac_release() stops DMA and frees TX/RX ring buffers and
page pools while XDP could still be accessing these resources in the
background, because napi_synchronize() from napi_disable() does not ensure
XDP is done.
This makes small window where IRQ is still possible after NAPI has finished.
Problem is that stmmac_release() handles closing XDP in different order than
stmmac_xdp_disable_pool(), which is not affected by this issue, where
synchronize_rcu() is executed after napi_disable() and disable_rx/tx_queue().
Fix this by following:
- Set STMMAC_DOWN flag before stopping DMA to signal XDP to stop and discard
- Call synchronize_rcu() after stopping DMA but before freeing resources to
ensure all ongoing NAPI operations complete, in similar order to
stmmac_xdp_disable_pool()
- Clear STMMAC_DOWN flag in __stmmac_open() to restore normal operation.
This was only done for stmmac_reset_subtask() during abnormal operation,
which is not enough. This flag does not affect normal operation as it is
used only for XDP apps. Usage of such flags is far from optimal and would
be good to rewrite, but it would be quite an effort
Also replace stmmac_disable_all_queues() with __stmmac_disable_all_queues(),
as it is barren after this change.
Fixes: bba2556efad66 ("net: stmmac: Enable RX via AF_XDP zero-copy")
Co-developed-by: Chang-Sub Lee <cs0617.lee@samsung.com>
Signed-off-by: Chang-Sub Lee <cs0617.lee@samsung.com>
Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 28 ++++++++++++++-----
1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index b9ffff001baf..9d971ae35d48 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -262,7 +262,11 @@ static void stmmac_verify_args(void)
pr_warn("stmmac: module parameter 'flow_ctrl' is obsolete - please remove from your module configuration\n");
}
-static void __stmmac_disable_all_queues(struct stmmac_priv *priv)
+/**
+ * stmmac_disable_all_queues - Disable all queues
+ * @priv: driver private structure
+ */
+static void stmmac_disable_all_queues(struct stmmac_priv *priv)
{
u8 rx_queues_cnt = priv->plat->rx_queues_to_use;
u8 tx_queues_cnt = priv->plat->tx_queues_to_use;
@@ -286,16 +290,15 @@ static void __stmmac_disable_all_queues(struct stmmac_priv *priv)
}
/**
- * stmmac_disable_all_queues - Disable all queues
+ * stmmac_drain_xdp - Cleanup for XDP apps
* @priv: driver private structure
*/
-static void stmmac_disable_all_queues(struct stmmac_priv *priv)
+static void stmmac_drain_xdp(struct stmmac_priv *priv)
{
u8 rx_queues_cnt = priv->plat->rx_queues_to_use;
struct stmmac_rx_queue *rx_q;
u8 queue;
- /* synchronize_rcu() needed for pending XDP buffers to drain */
for (queue = 0; queue < rx_queues_cnt; queue++) {
rx_q = &priv->dma_conf.rx_queue[queue];
if (rx_q->xsk_pool) {
@@ -303,8 +306,6 @@ static void stmmac_disable_all_queues(struct stmmac_priv *priv)
break;
}
}
-
- __stmmac_disable_all_queues(priv);
}
/**
@@ -4149,6 +4150,9 @@ static int __stmmac_open(struct net_device *dev,
stmmac_reset_queues_param(priv);
+ /* Clear DOWN flag when opening the interface */
+ clear_bit(STMMAC_DOWN, &priv->state);
+
ret = stmmac_hw_setup(dev);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
@@ -4243,6 +4247,9 @@ static void __stmmac_release(struct net_device *dev)
/* Stop and disconnect the PHY */
phylink_stop(priv->phylink);
+ /* Set DOWN flag to prevent XDP from processing new packets */
+ set_bit(STMMAC_DOWN, &priv->state);
+
stmmac_disable_all_queues(priv);
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
@@ -4256,6 +4263,8 @@ static void __stmmac_release(struct net_device *dev)
/* Stop TX/RX DMA and clear the descriptors */
stmmac_stop_all_dma(priv);
+ stmmac_drain_xdp(priv);
+
/* Release and free the Rx/Tx resources */
free_dma_desc_resources(priv, &priv->dma_conf);
@@ -6412,7 +6421,7 @@ static int stmmac_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
return ret;
- __stmmac_disable_all_queues(priv);
+ stmmac_disable_all_queues(priv);
switch (type) {
case TC_SETUP_CLSU32:
@@ -7122,6 +7131,9 @@ void stmmac_xdp_release(struct net_device *dev)
/* Stop TX/RX DMA channels */
stmmac_stop_all_dma(priv);
+ /* Drain leftover XDP buffers */
+ stmmac_drain_xdp(priv);
+
/* Release and free the Rx/Tx resources */
free_dma_desc_resources(priv, &priv->dma_conf);
@@ -8208,6 +8220,8 @@ int stmmac_suspend(struct device *dev)
/* Stop TX/RX DMA */
stmmac_stop_all_dma(priv);
+ stmmac_drain_xdp(priv);
+
stmmac_legacy_serdes_power_down(priv);
/* Enable Power down mode by programming the PMT regs */
--
2.34.1
^ permalink raw reply related
* [PATCH net-next V5 2/6] devlink: Factor out eswitch mode setting
From: Mark Bloch @ 2026-07-07 17:45 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
In-Reply-To: <20260707174527.425134-1-mbloch@nvidia.com>
Move the common eswitch mode set checks into a small helper and use it
from the netlink eswitch set command. Making the same validation
available to the devlink core path that applies eswitch mode defaults.
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
net/devlink/dev.c | 27 ++++++++++++++++++++-------
net/devlink/devl_internal.h | 3 +++
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index bcf001554e84..119ef105d0a7 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -702,6 +702,25 @@ int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
return genlmsg_reply(msg, info);
}
+int devlink_eswitch_mode_set(struct devlink *devlink,
+ enum devlink_eswitch_mode mode,
+ struct netlink_ext_ack *extack)
+{
+ const struct devlink_ops *ops = devlink->ops;
+ int err;
+
+ devl_assert_locked(devlink);
+
+ if (!ops->eswitch_mode_set)
+ return -EOPNOTSUPP;
+
+ err = devlink_rates_check(devlink, devlink_rate_is_node, extack);
+ if (err)
+ return err;
+
+ return ops->eswitch_mode_set(devlink, mode, extack);
+}
+
int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
@@ -712,14 +731,8 @@ int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
u16 mode;
if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) {
- if (!ops->eswitch_mode_set)
- return -EOPNOTSUPP;
- err = devlink_rates_check(devlink, devlink_rate_is_node,
- info->extack);
- if (err)
- return err;
mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
- err = ops->eswitch_mode_set(devlink, mode, info->extack);
+ err = devlink_eswitch_mode_set(devlink, mode, info->extack);
if (err)
return err;
}
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index cdf894ba5a9d..af43b7163f78 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -348,6 +348,9 @@ bool devlink_rate_is_node(const struct devlink_rate *devlink_rate);
int devlink_rates_check(struct devlink *devlink,
bool (*rate_filter)(const struct devlink_rate *),
struct netlink_ext_ack *extack);
+int devlink_eswitch_mode_set(struct devlink *devlink,
+ enum devlink_eswitch_mode mode,
+ struct netlink_ext_ack *extack);
/* Linecards */
unsigned int devlink_linecard_index(struct devlink_linecard *linecard);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next V5 3/6] devlink: Parse eswitch mode boot defaults
From: Mark Bloch @ 2026-07-07 17:45 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
In-Reply-To: <20260707174527.425134-1-mbloch@nvidia.com>
Add devlink_eswitch_mode= kernel command line parsing for a default
eswitch mode.
The supported syntax selects either all devlink handles or one explicit
comma-separated handle list:
devlink_eswitch_mode=*=<mode>
devlink_eswitch_mode=<handle>[,<handle>...]=<mode>
where <mode> is one of legacy, switchdev or switchdev_inactive. All
selected handles receive the same mode. Assigning different modes to
different handle lists in the same parameter value is not supported.
Store the parsed selector and mode in devlink core so the default can be
applied by a downstream patch.
Document the devlink_eswitch_mode= syntax and duplicate handle handling.
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
.../admin-guide/kernel-parameters.txt | 25 ++
.../networking/devlink/devlink-defaults.rst | 78 ++++++
Documentation/networking/devlink/index.rst | 1 +
net/devlink/Makefile | 2 +-
net/devlink/core.c | 7 +
net/devlink/default.c | 237 ++++++++++++++++++
net/devlink/devl_internal.h | 2 +
7 files changed, 351 insertions(+), 1 deletion(-)
create mode 100644 Documentation/networking/devlink/devlink-defaults.rst
create mode 100644 net/devlink/default.c
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b5493a7f8f22..117300dd589c 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1249,6 +1249,31 @@ Kernel parameters
dell_smm_hwmon.fan_max=
[HW] Maximum configurable fan speed.
+ devlink_eswitch_mode=
+ [NET]
+ Format:
+ <selector>=<mode>
+
+ <selector>:
+ * | <handle>[,<handle>...]
+
+ <handle>:
+ <bus-name>/<dev-name>
+
+ Configure default devlink eswitch mode for matching
+ devlink instances during device initialization.
+
+ <mode>:
+ legacy | switchdev | switchdev_inactive
+
+ Examples:
+ devlink_eswitch_mode=*=switchdev
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev
+ devlink_eswitch_mode=pci/0000:08:00.0,pci/0000:09:00.1=switchdev_inactive
+
+ See Documentation/networking/devlink/devlink-defaults.rst
+ for the full syntax.
+
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
on: s390 zlib hardware support for compression on
diff --git a/Documentation/networking/devlink/devlink-defaults.rst b/Documentation/networking/devlink/devlink-defaults.rst
new file mode 100644
index 000000000000..380c9e99210e
--- /dev/null
+++ b/Documentation/networking/devlink/devlink-defaults.rst
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+Devlink Eswitch Mode Defaults
+==============================
+
+Devlink eswitch mode defaults allow the eswitch mode to be provided on the
+kernel command line and applied to matching devlink instances during device
+initialization.
+
+The devlink device is selected by its devlink handle. For PCI devices this is
+the same handle shown by ``devlink dev show``, for example
+``pci/0000:08:00.0``.
+
+Kernel command line syntax
+==========================
+
+Defaults are specified with the ``devlink_eswitch_mode=`` kernel command line
+parameter.
+
+The general syntax is::
+
+ devlink_eswitch_mode=<selector>=<mode>
+
+``<selector>`` is either ``*`` or one or more devlink handles::
+
+ * | <bus-name>/<dev-name>[,<bus-name>/<dev-name>...]
+
+``*`` applies the mode to every devlink instance. All handles in the same
+selector receive the same eswitch mode.
+
+``<mode>`` is one of ``legacy``, ``switchdev`` or ``switchdev_inactive``.
+
+Syntax rules
+------------
+
+The following syntax rules apply:
+
+* Specify the default in one ``devlink_eswitch_mode=`` parameter. Repeated
+ ``devlink_eswitch_mode=`` parameters are not accumulated.
+* The ``devlink_eswitch_mode=`` value is limited by the kernel command line
+ size.
+* Whitespace is not allowed within the parameter value.
+* ``<selector>`` must be either ``*`` or a handle list. ``*`` cannot be
+ combined with explicit handles.
+* ``<bus-name>`` and ``<dev-name>`` must not be empty.
+* ``<dev-name>`` may contain ``:``. This allows PCI names such as
+ ``0000:08:00.0``.
+* Handles must not contain whitespace, ``*``, ``=`` or more than one ``/``.
+* A comma separates handles.
+* Comma-separated default assignments are not supported.
+* Duplicate handles are rejected and the devlink eswitch mode default is
+ ignored.
+
+The eswitch mode default corresponds to the userspace command::
+
+ devlink dev eswitch set <handle> mode <value>
+
+
+Examples
+========
+
+Set all devlink instances to switchdev mode::
+
+ devlink_eswitch_mode=*=switchdev
+
+Set one PCI devlink instance to switchdev mode::
+
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev
+
+Set two PCI devlink instances to switchdev inactive mode::
+
+ devlink_eswitch_mode=pci/0000:08:00.0,pci/0000:09:00.1=switchdev_inactive
+
+The following is invalid because comma-separated default assignments are not
+supported::
+
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev,pci/0000:09:00.0=switchdev_inactive
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index 4745148fecf4..134d2f319922 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -56,6 +56,7 @@ general.
:maxdepth: 1
devlink-dpipe
+ devlink-defaults
devlink-eswitch-attr
devlink-flash
devlink-health
diff --git a/net/devlink/Makefile b/net/devlink/Makefile
index 8f2adb5e5836..99ca0ef7cf1e 100644
--- a/net/devlink/Makefile
+++ b/net/devlink/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-obj-y := core.o netlink.o netlink_gen.o dev.o port.o sb.o dpipe.o \
+obj-y := core.o netlink.o netlink_gen.o dev.o default.o port.o sb.o dpipe.o \
resource.o param.o region.o health.o trap.o rate.o linecard.o sh_dev.o
diff --git a/net/devlink/core.c b/net/devlink/core.c
index c53a42e17a58..fc14ee5d9dcf 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -598,6 +598,10 @@ static int __init devlink_init(void)
{
int err;
+ err = devlink_default_esw_mode_init();
+ if (err)
+ goto out;
+
err = register_pernet_subsys(&devlink_pernet_ops);
if (err)
goto out;
@@ -613,7 +617,10 @@ static int __init devlink_init(void)
out_unreg_pernet_subsys:
unregister_pernet_subsys(&devlink_pernet_ops);
out:
+ if (err)
+ devlink_default_esw_mode_cleanup();
WARN_ON(err);
+
return err;
}
diff --git a/net/devlink/default.c b/net/devlink/default.c
new file mode 100644
index 000000000000..8434af83ea69
--- /dev/null
+++ b/net/devlink/default.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "devl_internal.h"
+
+static char *devlink_default_esw_mode_param;
+static bool devlink_default_esw_mode_match_all;
+static enum devlink_eswitch_mode devlink_default_esw_mode;
+static LIST_HEAD(devlink_default_esw_mode_nodes);
+
+struct devlink_default_esw_mode_node {
+ struct list_head list;
+ char *bus_name;
+ char *dev_name;
+};
+
+static int __init
+devlink_default_esw_mode_to_value(const char *str,
+ enum devlink_eswitch_mode *mode)
+{
+ if (!strcmp(str, "legacy")) {
+ *mode = DEVLINK_ESWITCH_MODE_LEGACY;
+ return 0;
+ }
+ if (!strcmp(str, "switchdev")) {
+ *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
+ return 0;
+ }
+ if (!strcmp(str, "switchdev_inactive")) {
+ *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int __init
+devlink_default_esw_mode_handle_parse(char *handle, char **bus_name,
+ char **dev_name)
+{
+ char *slash;
+ char *p;
+
+ if (!*handle)
+ return -EINVAL;
+
+ for (p = handle; *p; p++) {
+ if (*p == '*' || *p == '=')
+ return -EINVAL;
+ }
+
+ slash = strchr(handle, '/');
+ if (!slash || slash == handle || !slash[1])
+ return -EINVAL;
+ if (strchr(slash + 1, '/'))
+ return -EINVAL;
+
+ *slash = '\0';
+
+ *bus_name = handle;
+ *dev_name = slash + 1;
+ return 0;
+}
+
+static struct devlink_default_esw_mode_node *
+devlink_default_esw_mode_node_find(const char *bus_name, const char *dev_name)
+{
+ struct devlink_default_esw_mode_node *node;
+
+ list_for_each_entry(node, &devlink_default_esw_mode_nodes, list) {
+ if (!strcmp(node->bus_name, bus_name) &&
+ !strcmp(node->dev_name, dev_name))
+ return node;
+ }
+
+ return NULL;
+}
+
+static int __init
+devlink_default_esw_mode_node_add(const char *bus_name, const char *dev_name)
+{
+ struct devlink_default_esw_mode_node *node;
+
+ if (devlink_default_esw_mode_node_find(bus_name, dev_name))
+ return -EEXIST;
+
+ node = kzalloc_obj(*node);
+ if (!node)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&node->list);
+ node->bus_name = kstrdup(bus_name, GFP_KERNEL);
+ node->dev_name = kstrdup(dev_name, GFP_KERNEL);
+ if (!node->bus_name || !node->dev_name) {
+ kfree(node->bus_name);
+ kfree(node->dev_name);
+ kfree(node);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&node->list, &devlink_default_esw_mode_nodes);
+ return 0;
+}
+
+static int __init devlink_default_esw_mode_handles_parse(char *handles)
+{
+ char *handle;
+ int err;
+
+ if (!strcmp(handles, "*")) {
+ devlink_default_esw_mode_match_all = true;
+ return 0;
+ }
+
+ while ((handle = strsep(&handles, ",")) != NULL) {
+ char *bus_name;
+ char *dev_name;
+
+ err = devlink_default_esw_mode_handle_parse(handle, &bus_name,
+ &dev_name);
+ if (err)
+ return err;
+
+ err = devlink_default_esw_mode_node_add(bus_name, dev_name);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static void __init
+devlink_default_esw_mode_node_free(struct devlink_default_esw_mode_node *node)
+{
+ kfree(node->bus_name);
+ kfree(node->dev_name);
+ kfree(node);
+}
+
+static void __init devlink_default_esw_mode_nodes_clear(void)
+{
+ struct devlink_default_esw_mode_node *node_tmp;
+ struct devlink_default_esw_mode_node *node;
+
+ list_for_each_entry_safe(node, node_tmp,
+ &devlink_default_esw_mode_nodes, list) {
+ list_del(&node->list);
+ devlink_default_esw_mode_node_free(node);
+ }
+
+ devlink_default_esw_mode_match_all = false;
+}
+
+static int __init devlink_default_esw_mode_parse(char *str)
+{
+ enum devlink_eswitch_mode esw_mode;
+ char *separator;
+ char *handles;
+ char *mode;
+ int err;
+
+ if (!*str)
+ return -EINVAL;
+
+ separator = strrchr(str, '=');
+ if (!separator || separator == str || !separator[1])
+ return -EINVAL;
+
+ *separator = '\0';
+ handles = str;
+ mode = separator + 1;
+
+ err = devlink_default_esw_mode_to_value(mode, &esw_mode);
+ if (err)
+ return err;
+
+ err = devlink_default_esw_mode_handles_parse(handles);
+ if (err)
+ devlink_default_esw_mode_nodes_clear();
+ else
+ devlink_default_esw_mode = esw_mode;
+
+ return err;
+}
+
+static int __init devlink_default_esw_mode_setup(char *str)
+{
+ devlink_default_esw_mode_param = str;
+ return 1;
+}
+__setup("devlink_eswitch_mode=", devlink_default_esw_mode_setup);
+
+int __init devlink_default_esw_mode_init(void)
+{
+ char *def;
+ int err;
+
+ if (!devlink_default_esw_mode_param)
+ return 0;
+
+ def = kstrdup(devlink_default_esw_mode_param, GFP_KERNEL);
+ if (!def) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate memory\n");
+ return 0;
+ }
+
+ err = devlink_default_esw_mode_parse(def);
+ kfree(def);
+ if (err == -EEXIST) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: duplicate eswitch mode handles ignored\n");
+ return 0;
+ } else if (err == -EINVAL) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: invalid devlink_eswitch_mode parameter ignored\n");
+ return 0;
+ } else if (err == -ENOMEM) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate memory\n");
+ return 0;
+ } else if (err) {
+ return err;
+ }
+
+ return 0;
+}
+
+void __init devlink_default_esw_mode_cleanup(void)
+{
+ devlink_default_esw_mode_nodes_clear();
+}
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index af43b7163f78..fe9ad58515d4 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -71,6 +71,8 @@ extern struct genl_family devlink_nl_family;
struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
struct net *net, struct device *dev,
const struct device_driver *dev_driver);
+int devlink_default_esw_mode_init(void);
+void devlink_default_esw_mode_cleanup(void);
#define devl_warn(devlink, format, args...) \
do { \
--
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