* Re: [PATCH v2] net: mvneta: free/request IRQ across suspend/resume
From: Maxime Chevallier @ 2026-06-17 12:49 UTC (permalink / raw)
To: Yun Zhou, marcin.s.wojtas, andrew+netdev, davem, edumazet, kuba,
pabeni, bigeasy, clrkwllms, rostedt
Cc: netdev, linux-kernel, linux-rt-devel
In-Reply-To: <20260617092028.1722407-1-yun.zhou@windriver.com>
Hi,
On 6/17/26 11:20, Yun Zhou wrote:
> On PREEMPT_RT, the mvneta IRQ handler is force-threaded. Under high
> network traffic, the IRQ can enter suspend with desc->depth == 1
> (masked by the oneshot mechanism between handler invocations).
>
> During suspend, the kernel increments depth to 2 and masks the
> interrupt at the MPIC level (clearing the SRC_CTL CPU routing bit,
> due to IRQCHIP_MASK_ON_SUSPEND). On resume, depth is decremented
> back to 1, but since it does not reach 0, the unmask is never
> called. The MPIC CPU routing remains cleared, permanently disabling
> interrupt delivery.
>
> Fix by freeing the IRQ in suspend and re-requesting it in resume.
> This ensures a clean IRQ state (depth=0, proper hardware routing)
> on every resume cycle, regardless of the pre-suspend depth. This
> follows the approach used by other drivers (e.g. igb).
This description makes it sound like it's not really a mvneta problem,
but rather a broader effect from preempt-rt / irq management / suspend
interactions.
Is this the expected way to deal with that ?
Maxime
^ permalink raw reply
* [PATCH v4 0/3] describe RTL8125 PCIe NICs on Rockchip boards (and add DT binding)
From: Ricardo Pardini via B4 Relay @ 2026-06-17 12:58 UTC (permalink / raw)
To: Heiner Kallweit, nic_swsd, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner
Cc: Sebastian Reichel, netdev, devicetree, linux-kernel,
linux-arm-kernel, linux-rockchip, Ricardo Pardini
Several Rockchip rk35xx boards carry on-board Realtek RTL8125 2.5GbE
NICs whose PCI function nodes are not described in the DT. Describing
them allows for stable ethernetN aliases (matching the GMAC alias
convention on these boards) and lets U-Boot's fdt_fixup_ethernet()
inject mac-address properties from its ethaddr/ethNaddr env, so MACs
stay stable across boots and U-Boot and kernel MAC match.
Patch 1 adds a DT binding for Realtek RTL8125 family PCIe Ethernet
controllers.
Patch 2 describes the on-board RTL8125 function nodes on the
FriendlyElec NanoPC-T6 (and variants).
Patch 3 describes the on-board RTL8125 function nodes on the Radxa
ROCK 5B / 5B+ / 5T family done based on lspci output provided by
helpful Armbian folks.
---
Changes in v4:
- binding: simplify the binding YAML ref Sashiko's and Krzysztof's
reviews
- binding: describe only the RTL8125 + rename to match ref Heiner's
review.
- dt: fix the bus-range according to Sashiko's review.
- Link to v3: https://patch.msgid.link/20260605-rk3588-dts-rtl-eth-describe-dt-alias-v3-0-8a8857b39daf@pardini.net
Changes in v3:
- new patch: add a DT binding for Realtek r8169 family PCIe Ethernet
controllers, per Sebastian Reichel's review (the "pciVVVV,DDDD" OF
spelling still needs a binding when used in a board DT).
- new patch for Rock5 series, and include a brief rationale in each.
- retitle the series, since it now covers a few boards and a binding
rather than just DeviceTree changes for the NanoPC-T6.
- drop the v2 "rename vcc3v3_pcie2x1l0 regulator" patch from this
series; it will be sent separately as it is not relevant to this.
- Link to v2: https://patch.msgid.link/20260529-rk3588-dts-rtl-eth-describe-dt-alias-v2-0-49700248143f@pardini.net
Changes in v2:
- fix: pcie2x1l0, not pcie2x1l1; indirectly caught by Sashiko's review [1]
- while-at-it: rename regulator vcc3v3_pcie2x1l0 to l1
- Link to v1: https://patch.msgid.link/20260525-rk3588-dts-rtl-eth-describe-dt-alias-v1-1-a6fcda563ac7@pardini.net
[1] https://sashiko.dev/#/patchset/20260525-rk3588-dts-rtl-eth-describe-dt-alias-v1-1-a6fcda563ac7%40pardini.net
To: Heiner Kallweit <hkallweit1@gmail.com>
To: nic_swsd@realtek.com
To: Andrew Lunn <andrew+netdev@lunn.ch>
To: "David S. Miller" <davem@davemloft.net>
To: Eric Dumazet <edumazet@google.com>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Heiko Stuebner <heiko@sntech.de>
Cc: Sebastian Reichel <sebastian.reichel@collabora.com>
Cc: netdev@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-rockchip@lists.infradead.org
Signed-off-by: Ricardo Pardini <ricardo@pardini.net>
---
Ricardo Pardini (3):
dt-bindings: net: add Realtek RTL8125 PCIe Ethernet
arm64: dts: rockchip: describe PCIe RTL8125 Ethernet on NanoPC-T6
arm64: dts: rockchip: describe PCIe RTL8125 Ethernet on Radxa ROCK 5 family
.../devicetree/bindings/net/realtek,rtl8125.yaml | 43 ++++++++++++++++++++++
MAINTAINERS | 1 +
arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi | 30 +++++++++++++++
.../boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi | 15 ++++++++
arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts | 18 +++++++++
5 files changed, 107 insertions(+)
---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260524-rk3588-dts-rtl-eth-describe-dt-alias-c1ed187b7c50
Best regards,
--
Ricardo Pardini <ricardo@pardini.net>
^ permalink raw reply
* [PATCH v4 1/3] dt-bindings: net: add Realtek RTL8125 PCIe Ethernet
From: Ricardo Pardini via B4 Relay @ 2026-06-17 12:58 UTC (permalink / raw)
To: Heiner Kallweit, nic_swsd, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner
Cc: Sebastian Reichel, netdev, devicetree, linux-kernel,
linux-arm-kernel, linux-rockchip, Ricardo Pardini
In-Reply-To: <20260617-rk3588-dts-rtl-eth-describe-dt-alias-v4-0-2bd38922d129@pardini.net>
From: Ricardo Pardini <ricardo@pardini.net>
Add a binding for fixed/soldered Realtek RTL8125 PCIe Ethernet
controller.
The "pciVVVV,DDDD" compatibles are the Open Firmware PCI Bus Binding
spelling, auto-derived from PCI-SIG vendor/device IDs, but they still
need a binding when used in a board DT - analogous to "usbVVVV,PPPP"
compatibles documented in their own bindings (e.g. microchip,lan95xx)
so board DTs attaching properties (fixed MAC, nvmem cell, ...) to
these PCI function nodes can be validated.
Suggested-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Ricardo Pardini <ricardo@pardini.net>
---
.../devicetree/bindings/net/realtek,rtl8125.yaml | 43 ++++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 44 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/realtek,rtl8125.yaml b/Documentation/devicetree/bindings/net/realtek,rtl8125.yaml
new file mode 100644
index 0000000000000..eee13fbc1e6a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/realtek,rtl8125.yaml
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/realtek,rtl8125.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RTL8125 2.5 Gigabit PCIe Ethernet Controller
+
+maintainers:
+ - Heiner Kallweit <hkallweit1@gmail.com>
+
+description:
+ The Realtek RTL8125 is a 2.5GBASE-T Ethernet controller with a PCIe host
+ interface.
+
+allOf:
+ - $ref: ethernet-controller.yaml#
+
+properties:
+ compatible:
+ const: pci10ec,8125
+
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ pcie {
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ ethernet@0,0 {
+ compatible = "pci10ec,8125";
+ reg = <0x10000 0 0 0 0>;
+ local-mac-address = [00 00 00 00 00 00];
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index c8d4b913f26c1..e5fbd82946aec 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -134,6 +134,7 @@ M: Heiner Kallweit <hkallweit1@gmail.com>
M: nic_swsd@realtek.com
L: netdev@vger.kernel.org
S: Maintained
+F: Documentation/devicetree/bindings/net/realtek,rtl8125.yaml
F: drivers/net/ethernet/realtek/r8169*
8250/16?50 (AND CLONE UARTS) SERIAL DRIVER
--
2.54.0
^ permalink raw reply related
* [PATCH v4 2/3] arm64: dts: rockchip: describe PCIe RTL8125 Ethernet on NanoPC-T6
From: Ricardo Pardini via B4 Relay @ 2026-06-17 12:58 UTC (permalink / raw)
To: Heiner Kallweit, nic_swsd, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner
Cc: Sebastian Reichel, netdev, devicetree, linux-kernel,
linux-arm-kernel, linux-rockchip, Ricardo Pardini
In-Reply-To: <20260617-rk3588-dts-rtl-eth-describe-dt-alias-v4-0-2bd38922d129@pardini.net>
From: Ricardo Pardini <ricardo@pardini.net>
The FriendlyElec NanoPC-T6 carries two on-board Realtek RTL8125 NICs
behind pcie2x1l0 and pcie2x1l2.
Describe the fixed function nodes and attach ethernet0/ethernet1
aliases, so that U-Boot's fdt_fixup_ethernet() can inject mac-address
properties from its ethaddr/eth1addr env. The on-NIC EEPROMs on this
board are not pre-programmed with a unique MAC, so this gives a
stable MAC across boots that both U-Boot and the kernel agree on.
Signed-off-by: Ricardo Pardini <ricardo@pardini.net>
---
arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi | 30 ++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi
index 84b6b53f016ab..0c11033f9d8e4 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi
@@ -20,6 +20,8 @@ / {
compatible = "friendlyarm,nanopc-t6", "rockchip,rk3588";
aliases {
+ ethernet0 = &rtl_eth0;
+ ethernet1 = &rtl_eth1;
mmc0 = &sdhci;
mmc1 = &sdmmc;
};
@@ -635,6 +637,20 @@ &pcie2x1l0 {
pinctrl-names = "default";
pinctrl-0 = <&pcie2_0_rst>;
status = "okay";
+
+ pcie@0,0 {
+ reg = <0x200000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ device_type = "pci";
+ bus-range = <0x21 0x2f>;
+
+ rtl_eth0: ethernet@0,0 {
+ compatible = "pci10ec,8125";
+ reg = <0x210000 0 0 0 0>;
+ };
+ };
};
&pcie2x1l1 {
@@ -651,6 +667,20 @@ &pcie2x1l2 {
pinctrl-names = "default";
pinctrl-0 = <&pcie2_2_rst>;
status = "okay";
+
+ pcie@0,0 {
+ reg = <0x400000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ device_type = "pci";
+ bus-range = <0x41 0x4f>;
+
+ rtl_eth1: ethernet@0,0 {
+ compatible = "pci10ec,8125";
+ reg = <0x410000 0 0 0 0>;
+ };
+ };
};
&pcie30phy {
--
2.54.0
^ permalink raw reply related
* [PATCH v4 3/3] arm64: dts: rockchip: describe PCIe RTL8125 Ethernet on Radxa ROCK 5 family
From: Ricardo Pardini via B4 Relay @ 2026-06-17 12:58 UTC (permalink / raw)
To: Heiner Kallweit, nic_swsd, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner
Cc: Sebastian Reichel, netdev, devicetree, linux-kernel,
linux-arm-kernel, linux-rockchip, Ricardo Pardini
In-Reply-To: <20260617-rk3588-dts-rtl-eth-describe-dt-alias-v4-0-2bd38922d129@pardini.net>
From: Ricardo Pardini <ricardo@pardini.net>
The Radxa ROCK 5B / 5B+ / 5T all carry on-board Realtek RTL8125 NICs.
Describe the fixed function nodes and attach ethernet0/ethernet1
aliases, so that U-Boot's fdt_fixup_ethernet() can inject mac-address
properties from its ethaddr/eth1addr env, for stable MACs across
boots that both U-Boot and the kernel agree on.
The RTL8125 on pcie2x1l2 is shared by all three variants. The ROCK 5T
additionally describes pcie2x1l1 with its second RTL8125.
Signed-off-by: Ricardo Pardini <ricardo@pardini.net>
---
.../arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi | 15 +++++++++++++++
arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts | 18 ++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi
index bf4a1d2e55ca3..b53dfe6848cce 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi
@@ -10,6 +10,7 @@
/ {
aliases {
+ ethernet0 = &rtl_eth0;
mmc0 = &sdhci;
mmc1 = &sdmmc;
mmc2 = &sdio;
@@ -482,6 +483,20 @@ &pcie2x1l2 {
reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>;
vpcie3v3-supply = <&vcc3v3_pcie2x1l2>;
status = "okay";
+
+ pcie@0,0 {
+ reg = <0x400000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ device_type = "pci";
+ bus-range = <0x41 0x4f>;
+
+ rtl_eth0: ethernet@0,0 {
+ compatible = "pci10ec,8125";
+ reg = <0x410000 0 0 0 0>;
+ };
+ };
};
&pcie30phy {
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts
index 425036146b6d9..b1a3e4b2165f9 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5t.dts
@@ -8,6 +8,10 @@ / {
model = "Radxa ROCK 5T";
compatible = "radxa,rock-5t", "rockchip,rk3588";
+ aliases {
+ ethernet1 = &rtl_eth1;
+ };
+
analog-sound {
compatible = "audio-graph-card";
label = "rk3588-es8316";
@@ -76,6 +80,20 @@ &pcie2x1l1 {
reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>;
vpcie3v3-supply = <&vcc3v3_pcie2x1l1>;
status = "okay";
+
+ pcie@0,0 {
+ reg = <0x300000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ device_type = "pci";
+ bus-range = <0x31 0x3f>;
+
+ rtl_eth1: ethernet@0,0 {
+ compatible = "pci10ec,8125";
+ reg = <0x310000 0 0 0 0>;
+ };
+ };
};
&pcie30phy {
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net] net: thunderbolt: Fix frags[] overflow by bounding frame_count
From: Mika Westerberg @ 2026-06-17 13:00 UTC (permalink / raw)
To: Maoyi Xie
Cc: Yehezkel Bernat, Andrew Lunn, Jakub Kicinski, Paolo Abeni,
David S. Miller, Eric Dumazet, netdev, linux-kernel
In-Reply-To: <178163152194.2486768.14724194232649760778@maoyixie.com>
On Wed, Jun 17, 2026 at 01:38:41AM +0800, Maoyi Xie wrote:
> tbnet_poll() assembles a multi-frame ThunderboltIP packet into one skb. The
> first frame goes into the skb linear area and every further frame is added as
> a page fragment.
>
> skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
> page, hdr_size, frame_size,
> TBNET_RX_PAGE_SIZE - hdr_size);
>
> A packet of frame_count frames therefore ends up with frame_count - 1
> fragments. tbnet_check_frame() only bounds the peer supplied frame_count to
> TBNET_RING_SIZE / 4 (64), which is far above MAX_SKB_FRAGS (17 by default). A
> peer that sends a packet of 19 or more small frames pushes nr_frags past
> MAX_SKB_FRAGS, so skb_add_rx_frag() writes past skb_shinfo()->frags[] and
> corrupts memory after the shared info.
>
> Tighten the start of packet bound to MAX_SKB_FRAGS + 1 so a packet can never
> produce more fragments than frags[] can hold. This matches the recent skb
> frags overflow fixes in other receive paths, for example f0813bcd2d9d ("net:
> wwan: t7xx: fix potential skb->frags overflow in RX path") and 600dc40554dc
> ("net: usb: cdc-phonet: fix skb frags[] overflow in rx_complete()").
>
> Fixes: e69b6c02b4c3 ("net: Add support for networking over Thunderbolt cable")
> Cc: stable@vger.kernel.org
> Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
> ---
> Mika preferred the bound in tbnet_check_frame() over the nr_frags <
> MAX_SKB_FRAGS guard in tbnet_poll() that I first floated on the list, so this
> rejects the oversized packet up front. Reproduced under KASAN with a harness
> that mirrors the per-frame skb_add_rx_frag() loop.
Yeah the maximum size of "jumbo" packet over USB4NET is 64k == 16 frames,
so this should be fine. Thanks!
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
^ permalink raw reply
* Re: [PATCH net-next v2] net: dsa: Fix skb ownership in taggers
From: Vladimir Oltean @ 2026-06-17 13:03 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Florian Fainelli, Jonas Gorski,
Hauke Mehrtens, Kurt Kanzenbach, Woojung Huh, UNGLinuxDriver,
Chester A. Unal, Daniel Golle, Matthias Brugger,
AngeloGioacchino Del Regno, Wei Fang, Clark Wang,
Clément Léger, George McCollister, David Yang, netdev,
Sashiko AI Review
In-Reply-To: <CAD++jLm7tvbB2RBV5FEo=mAkPpmUJEMemXV0+mhTvZ0oX8O=+Q@mail.gmail.com>
On Wed, Jun 17, 2026 at 02:23:40PM +0200, Linus Walleij wrote:
> > - Has anyone proven that a real problem exists? Because dsa_user_xmit()
> > -> skb_ensure_writable_head_tail() has run successfully at this stage,
> > so we know that dev->needed_headroom bytes are available for writing.
> > Because DSA uses VLAN as a tag, dsa_user_setup_tagger() will increase
> > dev->needed_headroom by VLAN_HLEN for the tag_8021q protocols, so
> > vlan_insert_tag() should not fail. I've looked at this function at it
> > seems not to be coded up to fail for any other reason.
>
> I guess what you're saying is that vlan_insert_tag() will never fail in
> ->xmit()?
Yes, I may be wrong, but this is what I think. The central idea of the
generic TX reallocation series
(https://lore.kernel.org/netdev/20201030014910.2738809-1-vladimir.oltean@nxp.com/)
was to simplify taggers by moving the failure point in case of
reallocation somewhere else. I'm just saying I don't see a strong reason
to complicate the tagger responsibility again.
^ permalink raw reply
* [PATCH iproute2-next v4] ip/bond: add lacp_strict support
From: Louis Scalbert @ 2026-06-17 13:03 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, jv, edumazet, kuba, pabeni, fbl, andy, shemminger,
maheshb, jonas.gorski, horms, stephen, Louis Scalbert
lacp_strict defines the behavior of a LACP bonding interface
when no slaves are in Collecting_Distributing state while at least
'min_links' slaves have carrier.
In the default (off) mode, the bonding master remains up and a
single slave is selected for TX/RX, while traffic received on other
slaves is dropped. This preserves the existing behavior.
In lacp_strict mode, the bonding master reports carrier down in this
situation.
Link: https://lore.kernel.org/netdev/20260603150331.1919611-1-louis.scalbert@6wind.com/
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
---
include/uapi/linux/if_link.h | 1 +
ip/iplink_bond.c | 25 +++++++++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 70aee114..d3a21fba 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -1601,6 +1601,7 @@ enum {
IFLA_BOND_NS_IP6_TARGET,
IFLA_BOND_COUPLED_CONTROL,
IFLA_BOND_BROADCAST_NEIGH,
+ IFLA_BOND_LACP_STRICT,
__IFLA_BOND_MAX,
};
diff --git a/ip/iplink_bond.c b/ip/iplink_bond.c
index 714fe7bd..d2d822cd 100644
--- a/ip/iplink_bond.c
+++ b/ip/iplink_bond.c
@@ -87,6 +87,12 @@ static const char *lacp_rate_tbl[] = {
NULL,
};
+static const char *lacp_strict_tbl[] = {
+ "off",
+ "on",
+ NULL,
+};
+
static const char *ad_select_tbl[] = {
"stable",
"bandwidth",
@@ -155,6 +161,7 @@ static void print_explain(FILE *f)
" [ ad_user_port_key PORTKEY ]\n"
" [ ad_actor_sys_prio SYSPRIO ]\n"
" [ ad_actor_system LLADDR ]\n"
+ " [ lacp_strict LACP_STRICT ]\n"
" [ arp_missed_max MISSED_MAX ]\n"
"\n"
"BONDMODE := balance-rr|active-backup|balance-xor|broadcast|802.3ad|balance-tlb|balance-alb\n"
@@ -168,6 +175,7 @@ static void print_explain(FILE *f)
"AD_SELECT := stable|bandwidth|count\n"
"COUPLED_CONTROL := off|on\n"
"BROADCAST_NEIGHBOR := off|on\n"
+ "LACP_STRICT := off|on\n"
);
}
@@ -188,6 +196,7 @@ static int bond_parse_opt(struct link_util *lu, int argc, char **argv,
__u32 packets_per_slave;
__u8 missed_max;
__u8 broadcast_neighbor;
+ __u8 lacp_strict;
unsigned int ifindex;
int ret;
@@ -417,6 +426,13 @@ static int bond_parse_opt(struct link_util *lu, int argc, char **argv,
return -1;
addattr_l(n, 1024, IFLA_BOND_AD_ACTOR_SYSTEM,
abuf, len);
+ } else if (matches(*argv, "lacp_strict") == 0) {
+ NEXT_ARG();
+ if (get_index(lacp_strict_tbl, *argv) < 0)
+ invarg("invalid lacp_strict", *argv);
+
+ lacp_strict = get_index(lacp_strict_tbl, *argv);
+ addattr8(n, 1024, IFLA_BOND_LACP_STRICT, lacp_strict);
} else if (matches(*argv, "tlb_dynamic_lb") == 0) {
NEXT_ARG();
if (get_u8(&tlb_dynamic_lb, *argv, 0)) {
@@ -642,6 +658,15 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
"all_slaves_active %u ",
rta_getattr_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE]));
+ if (tb[IFLA_BOND_LACP_STRICT]) {
+ __u8 lacp_strict = rta_getattr_u8(tb[IFLA_BOND_LACP_STRICT]);
+ print_string(PRINT_FP,
+ "lacp_strict",
+ "lacp_strict %s ",
+ get_name(lacp_strict_tbl, lacp_strict));
+ print_bool(PRINT_JSON, "lacp_strict", NULL, lacp_strict);
+ }
+
if (tb[IFLA_BOND_MIN_LINKS])
print_uint(PRINT_ANY,
"min_links",
--
2.39.2
^ permalink raw reply related
* [for-next v3 0/3] Add Reorder Completion Queue (RCQ) support
From: Abhijit Gangurde @ 2026-06-17 13:26 UTC (permalink / raw)
To: jgg, leon, brett.creeley, andrew+netdev, davem, edumazet, kuba,
pabeni
Cc: allen.hubbe, nikhil.agarwal, linux-rdma, netdev, linux-kernel,
Abhijit Gangurde
This series adds userspace support for the Reorder Completion Queue (RCQ)
feature in the ionic RDMA driver.
Patch 1 extends the net/ionic firmware identity structure to expose the
rcq_sign_bit field from the RDMA LIF identity.
Patch 2 adds robust udata support to extend the qp creation udata
request
Patch 3 plumbs the RCQ sign bit through the RDMA driver's LIF configuration,
exposes it to userspace via the ucontext response, and allows userspace to
specify ionic specific QP flags during QP creation. This enables rdma-core to
discover RCQ capability at context allocation time and configure QPs with
RCQ support.
PR: https://github.com/linux-rdma/rdma-core/pull/1733
v3:
- Added robust udata compatibility checks
v2:
- Dropped QP transport mode selection
- https://lore.kernel.org/linux-rdma/20260611092544.783731-1-abhijit.gangurde@amd.com/
v1:
- https://lore.kernel.org/linux-rdma/20260430123931.3256130-1-abhijit.gangurde@amd.com/
Abhijit Gangurde (3):
net: ionic: Fetch RCQ sign bit from firmware
RDMA/ionic: Add robust udata compatibility checks to all uapi verbs
RDMA/ionic: Add RCQ userspace support
.../infiniband/hw/ionic/ionic_controlpath.c | 72 ++++++++++++++++---
drivers/infiniband/hw/ionic/ionic_fw.h | 2 +
drivers/infiniband/hw/ionic/ionic_ibdev.c | 1 +
drivers/infiniband/hw/ionic/ionic_lif_cfg.c | 1 +
drivers/infiniband/hw/ionic/ionic_lif_cfg.h | 1 +
.../net/ethernet/pensando/ionic/ionic_if.h | 6 +-
include/uapi/rdma/ionic-abi.h | 4 +-
7 files changed, 77 insertions(+), 10 deletions(-)
--
2.43.0
^ permalink raw reply
* [for-next v3 1/3] net: ionic: Fetch RCQ sign bit from firmware
From: Abhijit Gangurde @ 2026-06-17 13:26 UTC (permalink / raw)
To: jgg, leon, brett.creeley, andrew+netdev, davem, edumazet, kuba,
pabeni
Cc: allen.hubbe, nikhil.agarwal, linux-rdma, netdev, linux-kernel,
Abhijit Gangurde
In-Reply-To: <20260617132605.1888205-1-abhijit.gangurde@amd.com>
Read the rcq_sign_bit from the RDMA LIF identity reported by firmware.
Signed-off-by: Abhijit Gangurde <abhijit.gangurde@amd.com>
---
drivers/net/ethernet/pensando/ionic/ionic_if.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h
index 23d6e2b4791e..b97de96f78c4 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_if.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h
@@ -553,6 +553,8 @@ enum ionic_lif_rdma_cap_stats {
* @rdma.eq_qtype: RDMA Event Qtype
* @rdma.stats_type: Supported statistics type
* (enum ionic_lif_rdma_cap_stats)
+ * @rdma.rsvd: Reserved byte
+ * @rdma.rcq_sign_bit: RCQ sign bit
* @rdma.rsvd1: Reserved byte(s)
* @words: word access to struct contents
*/
@@ -598,7 +600,9 @@ union ionic_lif_identity {
struct ionic_lif_logical_qtype cq_qtype;
struct ionic_lif_logical_qtype eq_qtype;
__le16 stats_type;
- u8 rsvd1[162];
+ u8 rsvd;
+ u8 rcq_sign_bit;
+ u8 rsvd1[160];
} __packed rdma;
} __packed;
__le32 words[478];
--
2.43.0
^ permalink raw reply related
* [for-next v3 3/3] RDMA/ionic: Add RCQ userspace support
From: Abhijit Gangurde @ 2026-06-17 13:26 UTC (permalink / raw)
To: jgg, leon, brett.creeley, andrew+netdev, davem, edumazet, kuba,
pabeni
Cc: allen.hubbe, nikhil.agarwal, linux-rdma, netdev, linux-kernel,
Abhijit Gangurde
In-Reply-To: <20260617132605.1888205-1-abhijit.gangurde@amd.com>
Expose the Reorder Completion Queue (RCQ) capability to userspace via
ucontext response and allow userspace to specify ionic specific QP
flags during QP creation.
Co-developed-by: Allen Hubbe <allen.hubbe@amd.com>
Signed-off-by: Allen Hubbe <allen.hubbe@amd.com>
Signed-off-by: Abhijit Gangurde <abhijit.gangurde@amd.com>
---
drivers/infiniband/hw/ionic/ionic_controlpath.c | 9 ++++++---
drivers/infiniband/hw/ionic/ionic_fw.h | 2 ++
drivers/infiniband/hw/ionic/ionic_lif_cfg.c | 1 +
drivers/infiniband/hw/ionic/ionic_lif_cfg.h | 1 +
include/uapi/rdma/ionic-abi.h | 4 +++-
5 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/drivers/infiniband/hw/ionic/ionic_controlpath.c b/drivers/infiniband/hw/ionic/ionic_controlpath.c
index 79570da3e6a6..27058473d845 100644
--- a/drivers/infiniband/hw/ionic/ionic_controlpath.c
+++ b/drivers/infiniband/hw/ionic/ionic_controlpath.c
@@ -408,6 +408,7 @@ int ionic_alloc_ucontext(struct ib_ucontext *ibctx, struct ib_udata *udata)
resp.udma_count = dev->lif_cfg.udma_count;
resp.expdb_mask = dev->lif_cfg.expdb_mask;
+ resp.rcq_sign_bit = dev->lif_cfg.rcq_sign_bit;
if (dev->lif_cfg.sq_expdb)
resp.expdb_qtypes |= IONIC_EXPDB_SQ;
@@ -1369,7 +1370,8 @@ static int ionic_create_qp_cmd(struct ionic_ibdev *dev,
struct ionic_qp *qp,
struct ionic_tbl_buf *sq_buf,
struct ionic_tbl_buf *rq_buf,
- struct ib_qp_init_attr *attr)
+ struct ib_qp_init_attr *attr,
+ u32 ionic_flags)
{
const u16 dbid = ionic_obj_dbid(dev, pd->ibpd.uobject);
const u32 flags = to_ionic_qp_flags(0, 0,
@@ -1385,7 +1387,8 @@ static int ionic_create_qp_cmd(struct ionic_ibdev *dev,
.len = cpu_to_le16(IONIC_ADMIN_CREATE_QP_IN_V1_LEN),
.cmd.create_qp = {
.pd_id = cpu_to_le32(pd->pdid),
- .priv_flags = cpu_to_be32(flags),
+ .priv_flags = cpu_to_be32(flags |
+ (ionic_flags & IONIC_QP_USER_FLAGS_MASK)),
.type_state = to_ionic_qp_type(attr->qp_type),
.dbid_flags = cpu_to_le16(dbid),
.id_ver = cpu_to_le32(qp->qpid),
@@ -2282,7 +2285,7 @@ int ionic_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attr,
rc = ionic_create_qp_cmd(dev, pd,
to_ionic_vcq_cq(attr->send_cq, qp->udma_idx),
to_ionic_vcq_cq(attr->recv_cq, qp->udma_idx),
- qp, &sq_buf, &rq_buf, attr);
+ qp, &sq_buf, &rq_buf, attr, req.ionic_flags);
if (rc)
goto err_cmd;
diff --git a/drivers/infiniband/hw/ionic/ionic_fw.h b/drivers/infiniband/hw/ionic/ionic_fw.h
index adfbb89d856c..4c6752bfb1de 100644
--- a/drivers/infiniband/hw/ionic/ionic_fw.h
+++ b/drivers/infiniband/hw/ionic/ionic_fw.h
@@ -105,6 +105,8 @@ enum ionic_qp_flags {
IONIC_QPF_SQ_CMB = BIT(13),
IONIC_QPF_RQ_CMB = BIT(14),
IONIC_QPF_PRIVILEGED = BIT(15),
+
+ IONIC_QP_USER_FLAGS_MASK = GENMASK(31, 16),
};
static inline int from_ionic_qp_flags(int flags)
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
index f3cd281c3a2f..a9044f47c913 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
@@ -84,6 +84,7 @@ void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg)
cfg->udma_count = 2;
cfg->max_stride = ident->rdma.max_stride;
+ cfg->rcq_sign_bit = ident->rdma.rcq_sign_bit;
cfg->expdb_mask = ionic_get_expdb(lif);
cfg->sq_expdb =
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
index 20853429f623..e6b17055147f 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
@@ -56,6 +56,7 @@ struct ionic_lif_cfg {
bool sq_expdb;
bool rq_expdb;
u8 expdb_mask;
+ u8 rcq_sign_bit;
};
void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg);
diff --git a/include/uapi/rdma/ionic-abi.h b/include/uapi/rdma/ionic-abi.h
index 7b589d3e9728..729cea3ccd56 100644
--- a/include/uapi/rdma/ionic-abi.h
+++ b/include/uapi/rdma/ionic-abi.h
@@ -46,8 +46,9 @@ struct ionic_ctx_resp {
__u8 udma_count;
__u8 expdb_mask;
__u8 expdb_qtypes;
+ __u8 rcq_sign_bit;
- __u8 rsvd2[3];
+ __u8 rsvd2[2];
};
struct ionic_qdesc {
@@ -84,6 +85,7 @@ struct ionic_qp_req {
__u8 rq_cmb;
__u8 udma_mask;
__u8 rsvd[3];
+ __u32 ionic_flags;
};
struct ionic_qp_resp {
--
2.43.0
^ permalink raw reply related
* [for-next v3 2/3] RDMA/ionic: Add robust udata compatibility checks to all uapi verbs
From: Abhijit Gangurde @ 2026-06-17 13:26 UTC (permalink / raw)
To: jgg, leon, brett.creeley, andrew+netdev, davem, edumazet, kuba,
pabeni
Cc: allen.hubbe, nikhil.agarwal, linux-rdma, netdev, linux-kernel,
Abhijit Gangurde
In-Reply-To: <20260617132605.1888205-1-abhijit.gangurde@amd.com>
Enable the robust udata contract by setting uverbs_robust_udata and
adding proper input validation and output handling to all verbs that
accept struct ib_udata.
For verbs with no driver request struct, add ib_is_udata_in_empty()
to reject unknown non-zero input with -EOPNOTSUPP. For verbs with no
driver response struct, add ib_respond_empty_udata() to zero-fill the
userspace output buffer. For create_ah, which already responds with
ionic_ah_resp, add the missing input validation.
This ensures the kernel correctly advertises
IB_UVERBS_CORE_SUPPORT_ROBUST_UDATA and upholds the forward/backward
compatibility rules for all verbs: create_ah, alloc_pd, dealloc_pd,
reg_user_mr, dereg_mr, alloc_mw, destroy_cq, modify_qp, and
destroy_qp.
Signed-off-by: Abhijit Gangurde <abhijit.gangurde@amd.com>
---
.../infiniband/hw/ionic/ionic_controlpath.c | 63 +++++++++++++++++--
drivers/infiniband/hw/ionic/ionic_ibdev.c | 1 +
2 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/drivers/infiniband/hw/ionic/ionic_controlpath.c b/drivers/infiniband/hw/ionic/ionic_controlpath.c
index 9d91f7667d4f..79570da3e6a6 100644
--- a/drivers/infiniband/hw/ionic/ionic_controlpath.c
+++ b/drivers/infiniband/hw/ionic/ionic_controlpath.c
@@ -487,6 +487,15 @@ int ionic_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
{
struct ionic_ibdev *dev = to_ionic_ibdev(ibpd->device);
struct ionic_pd *pd = to_ionic_pd(ibpd);
+ int rc;
+
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return rc;
+
+ rc = ib_respond_empty_udata(udata);
+ if (rc)
+ return rc;
return ionic_get_pdid(dev, &pd->pdid);
}
@@ -495,10 +504,15 @@ int ionic_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
{
struct ionic_ibdev *dev = to_ionic_ibdev(ibpd->device);
struct ionic_pd *pd = to_ionic_pd(ibpd);
+ int rc;
+
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return rc;
ionic_put_pdid(dev, pd->pdid);
- return 0;
+ return ib_respond_empty_udata(udata);
}
static int ionic_build_hdr(struct ionic_ibdev *dev,
@@ -741,6 +755,10 @@ int ionic_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,
u32 flags = init_attr->flags;
int rc;
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return rc;
+
rc = ionic_get_ahid(dev, &ah->ahid);
if (rc)
return rc;
@@ -877,6 +895,14 @@ struct ib_mr *ionic_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 length,
unsigned long pg_sz;
int rc;
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return ERR_PTR(rc);
+
+ rc = ib_respond_empty_udata(udata);
+ if (rc)
+ return ERR_PTR(rc);
+
if (dmah)
return ERR_PTR(-EOPNOTSUPP);
@@ -1008,6 +1034,10 @@ int ionic_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata)
struct ionic_mr *mr = to_ionic_mr(ibmr);
int rc;
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return rc;
+
if (!mr->ibmr.lkey)
goto out;
@@ -1027,7 +1057,7 @@ int ionic_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata)
out:
kfree(mr);
- return 0;
+ return ib_respond_empty_udata(udata);
}
struct ib_mr *ionic_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type type,
@@ -1120,6 +1150,14 @@ int ionic_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata)
struct ionic_mr *mr = to_ionic_mw(ibmw);
int rc;
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return rc;
+
+ rc = ib_respond_empty_udata(udata);
+ if (rc)
+ return rc;
+
rc = ionic_get_mrid(dev, &mr->mrid);
if (rc)
return rc;
@@ -1292,6 +1330,10 @@ int ionic_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
struct ionic_vcq *vcq = to_ionic_vcq(ibcq);
int udma_idx, rc_tmp, rc = 0;
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return rc;
+
for (udma_idx = dev->lif_cfg.udma_count; udma_idx; ) {
--udma_idx;
@@ -1309,7 +1351,10 @@ int ionic_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
ionic_destroy_cq_common(dev, &vcq->cq[udma_idx]);
}
- return rc;
+ if (rc)
+ return rc;
+
+ return ib_respond_empty_udata(udata);
}
static bool pd_remote_privileged(struct ib_pd *pd)
@@ -2585,6 +2630,10 @@ int ionic_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int mask,
struct ionic_qp *qp = to_ionic_qp(ibqp);
int rc;
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return rc;
+
rc = ionic_check_modify_qp(qp, attr, mask);
if (rc)
return rc;
@@ -2607,7 +2656,7 @@ int ionic_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int mask,
}
}
- return 0;
+ return ib_respond_empty_udata(udata);
}
int ionic_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
@@ -2658,6 +2707,10 @@ int ionic_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
struct ionic_cq *cq;
int rc;
+ rc = ib_is_udata_in_empty(udata);
+ if (rc)
+ return rc;
+
rc = ionic_destroy_qp_cmd(dev, qp->qpid);
if (rc)
return rc;
@@ -2692,5 +2745,5 @@ int ionic_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
}
ionic_put_qpid(dev, qp->qpid);
- return 0;
+ return ib_respond_empty_udata(udata);
}
diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.c b/drivers/infiniband/hw/ionic/ionic_ibdev.c
index b0449c75f893..ad4e9abb5bf4 100644
--- a/drivers/infiniband/hw/ionic/ionic_ibdev.c
+++ b/drivers/infiniband/hw/ionic/ionic_ibdev.c
@@ -216,6 +216,7 @@ static const struct ib_device_ops ionic_dev_ops = {
.owner = THIS_MODULE,
.driver_id = RDMA_DRIVER_IONIC,
.uverbs_abi_ver = IONIC_ABI_VERSION,
+ .uverbs_robust_udata = true,
.alloc_ucontext = ionic_alloc_ucontext,
.dealloc_ucontext = ionic_dealloc_ucontext,
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net] ipv6: ndisc: fix NULL deref in accept_untracked_na()
From: Weiming Shi @ 2026-06-17 13:38 UTC (permalink / raw)
To: Jiayuan Chen, Weiming Shi, David S . Miller, David Ahern,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, netdev, linux-kernel, Xiang Mei
In-Reply-To: <e8ace4ba-31cb-40d7-b288-eeb411f8d0ef@linux.dev>
On Wed Jun 17, 2026 at 4:32 PM CST, Jiayuan Chen wrote:
>
> On 6/17/26 2:55 PM, Weiming Shi wrote:
>> accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev)
>> and dereferences idev->cnf.accept_untracked_na without a NULL check,
>
>
> Does ipv6_rpl_srh_rcv have same problem?
Hi,
Yes, ipv6_rpl_srh_rcv() has the same missing check. It reads
idev->cnf.rpl_seg_enabled right after __in6_dev_get(skb->dev) with no
NULL check, while seg6 and ioam6 in the same file both check it.
But I tried to trigger it and couldn't. With a guard added as an instrument,
idev never came back NULL over tens of millions of RPL packets while
flapping the MTU, so I can't say it's actually reachable.
Still, it's the only one of the three without the check. Want me to send
a patch adding it there too, for consistency?
Thanks,
Weiming Shi
^ permalink raw reply
* [PATCH net v3] tipc: fix use-after-free of the discoverer in tipc_disc_rcv()
From: Weiming Shi @ 2026-06-17 13:57 UTC (permalink / raw)
To: Jon Maloy, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Ying Xue, netdev, tipc-discussion, linux-kernel,
Xiang Mei, Weiming Shi
bearer_disable() frees b->disc with tipc_disc_delete()'s plain kfree(),
but tipc_disc_rcv() still dereferences b->disc in RX softirq under
rcu_read_lock() (tipc_udp_recv -> tipc_rcv -> tipc_disc_rcv).
L2 bearers are safe thanks to the synchronize_net() in
tipc_disable_l2_media(), but the UDP bearer defers that call to the
cleanup_bearer() workqueue, so the discoverer is freed with no grace
period:
BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149)
Read of size 8 at addr ffff88802348b728 by task poc_tipc/184
<IRQ>
tipc_disc_rcv (net/tipc/discover.c:149)
tipc_rcv (net/tipc/node.c:2126)
tipc_udp_recv (net/tipc/udp_media.c:391)
udp_rcv (net/ipv4/udp.c:2643)
ip_local_deliver_finish (net/ipv4/ip_input.c:241)
</IRQ>
Freed by task 181:
kfree (mm/slub.c:6565)
bearer_disable (net/tipc/bearer.c:418)
tipc_nl_bearer_disable (net/tipc/bearer.c:1001)
The bearer is freed with kfree_rcu(); free the discoverer the same way.
Add an rcu_head to struct tipc_discoverer and free it and its skb from an
RCU callback.
Because the RCU callback (tipc_disc_free_rcu) lives in module text, a
call_rcu() that is still pending when the tipc module is unloaded would
invoke a freed function. Add an rcu_barrier() to tipc_exit() after the
bearer subsystem has been torn down, so all pending discoverer callbacks
have run before the module text goes away.
Reachable from an unprivileged user namespace: the TIPCv2 genl family is
netnsok and its bearer commands have no GENL_ADMIN_PERM. Needs CONFIG_TIPC
and CONFIG_TIPC_MEDIA_UDP.
Fixes: 25b0b9c4e835 ("tipc: handle collisions of 32-bit node address hash values")
Reported-by: Xiang Mei <xmei5@asu.edu>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
v3:
- Reword the rcu_barrier() comment as a TODO (Tung Quang Nguyen).
v2:
- split the over-80-column container_of() line (Tung Quang Nguyen)
- add rcu_barrier() to tipc_exit() so a pending call_rcu() cannot fire
into freed module text after rmmod (Eric Dumazet)
net/tipc/core.c | 5 +++++
net/tipc/discover.c | 14 ++++++++++++--
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/net/tipc/core.c b/net/tipc/core.c
index 434e70eabe08..1ddecea1df6e 100644
--- a/net/tipc/core.c
+++ b/net/tipc/core.c
@@ -218,6 +218,11 @@ static void __exit tipc_exit(void)
unregister_pernet_device(&tipc_net_ops);
tipc_unregister_sysctl();
+ /* TODO: Wait for all timers that called call_rcu() to finish before
+ * calling rcu_barrier().
+ */
+ rcu_barrier();
+
pr_info("Deactivated\n");
}
diff --git a/net/tipc/discover.c b/net/tipc/discover.c
index 3e54d2df5683..b9d06595b067 100644
--- a/net/tipc/discover.c
+++ b/net/tipc/discover.c
@@ -58,6 +58,7 @@
* @skb: request message to be (repeatedly) sent
* @timer: timer governing period between requests
* @timer_intv: current interval between requests (in ms)
+ * @rcu: RCU head for deferred freeing
*/
struct tipc_discoverer {
u32 bearer_id;
@@ -69,6 +70,7 @@ struct tipc_discoverer {
struct sk_buff *skb;
struct timer_list timer;
unsigned long timer_intv;
+ struct rcu_head rcu;
};
/**
@@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,
return 0;
}
+static void tipc_disc_free_rcu(struct rcu_head *rp)
+{
+ struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer,
+ rcu);
+
+ kfree_skb(d->skb);
+ kfree(d);
+}
+
/**
* tipc_disc_delete - destroy object sending periodic link setup requests
* @d: ptr to link dest structure
@@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,
void tipc_disc_delete(struct tipc_discoverer *d)
{
timer_shutdown_sync(&d->timer);
- kfree_skb(d->skb);
- kfree(d);
+ call_rcu(&d->rcu, tipc_disc_free_rcu);
}
/**
--
2.43.0
^ permalink raw reply related
* [PATCH net V2 0/3] net/mlx5e: Fix crashes in dynamic per-channel stats and HV VHCA agent
From: Tariq Toukan @ 2026-06-17 14:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Cosmin Ratiu, Eran Ben Elisha, Feng Liu, Haiyang Zhang,
Lama Kayal, Leon Romanovsky, linux-kernel, linux-rdma, Mark Bloch,
Nimrod Oren, Saeed Mahameed, Tariq Toukan
Hi,
Since per-channel stats were converted to be allocated and published
lazily at first channel open in commit fa691d0c9c08 ("net/mlx5e:
Allocate per-channel stats dynamically at first usage"),
priv->channel_stats[] and priv->stats_nch are filled in
incrementally during interface bring-up. This opened a window in
which the various stats readers - most of them reachable from
userspace via netlink/netdev stats queries - can race with
mlx5e_open_channel() on another CPU and observe partially
initialized state. The HV VHCA stats agent, which is created
before the channels are opened, hits related problems of its own.
This series by Feng fixes the resulting crashes.
Regards,
Tariq
V2:
- Drop "Bounds-check stats_nch in mlx5e_get_queue_stats_rx()" (Jakub).
V1:
https://lore.kernel.org/all/20260604135041.455754-1-tariqt@nvidia.com
Feng Liu (3):
net/mlx5e: Fix HV VHCA stats zero-sized buffer allocation
net/mlx5e: Fix HV VHCA stats agent registration race
net/mlx5e: Fix publication race for priv->channel_stats[]
drivers/net/ethernet/mellanox/mlx5/core/en.h | 12 ++++++
.../mellanox/mlx5/core/en/hv_vhca_stats.c | 38 +++++++++++++------
.../net/ethernet/mellanox/mlx5/core/en_main.c | 14 ++++---
.../ethernet/mellanox/mlx5/core/en_stats.c | 9 +++--
.../ethernet/mellanox/mlx5/core/ipoib/ipoib.c | 3 +-
.../ethernet/mellanox/mlx5/core/lib/hv_vhca.c | 8 +++-
.../ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 6 ++-
7 files changed, 63 insertions(+), 27 deletions(-)
base-commit: 406e8a651a7b854c41fecd5117bb282b3a6c2c6b
--
2.44.0
^ permalink raw reply
* [PATCH net V2 1/3] net/mlx5e: Fix HV VHCA stats zero-sized buffer allocation
From: Tariq Toukan @ 2026-06-17 14:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Cosmin Ratiu, Eran Ben Elisha, Feng Liu, Haiyang Zhang,
Lama Kayal, Leon Romanovsky, linux-kernel, linux-rdma, Mark Bloch,
Nimrod Oren, Saeed Mahameed, Tariq Toukan
In-Reply-To: <20260617140127.573117-1-tariqt@nvidia.com>
From: Feng Liu <feliu@nvidia.com>
mlx5e_hv_vhca_stats_create() is called from mlx5e_nic_enable(),
before mlx5e_open(). At that point priv->stats_nch is still zero,
because it is only ever incremented in mlx5e_channel_stats_alloc(),
which is reached only from mlx5e_open_channel().
mlx5e_hv_vhca_stats_buf_size() therefore returns 0, and
kvzalloc(0, GFP_KERNEL) returns ZERO_SIZE_PTR ((void *)16) rather
than NULL. The "if (!buf)" guard does not catch this, and
mlx5e_hv_vhca_stats_create() completes "successfully" with
priv->stats_agent.buf set to ZERO_SIZE_PTR.
Once channels are opened (priv->stats_nch > 0) and the hypervisor
enables stats reporting, mlx5e_hv_vhca_stats_work() recomputes
buf_len using the new non-zero stats_nch and calls
memset(buf, 0, buf_len) on ZERO_SIZE_PTR, faulting at address 0x10.
Allocate the buffer based on priv->max_nch, which is set in
mlx5e_priv_init() and is the upper bound on stats_nch:
- Add a separate helper mlx5e_hv_vhca_stats_buf_max_size() that
returns sizeof(per_ring_stats) * max(max_nch, stats_nch), and
use it for the kvzalloc() in mlx5e_hv_vhca_stats_create().
- Keep mlx5e_hv_vhca_stats_buf_size() (which returns based on
stats_nch) for the worker's active payload size, so the wire
format (block->rings = stats_nch) and the amount of data filled
by mlx5e_hv_vhca_fill_stats() are unchanged.
The max(max_nch, stats_nch) guard handles the rare case where
mlx5e_attach_netdev() recomputes max_nch downward across a
detach/resume cycle while priv->stats_nch persists (mlx5e_detach_netdev
does not call mlx5e_priv_cleanup, so stats_nch is only reset when
the netdev is destroyed). Without the guard, the worker could compute
buf_len from stats_nch and overrun the smaller buffer allocated based
on the reduced max_nch.
This mirrors the existing mlx5e pattern of preallocating arrays of
size max_nch (e.g. priv->channel_stats) and lazily populating
entries up to stats_nch on demand.
Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage")
Signed-off-by: Feng Liu <feliu@nvidia.com>
Reviewed-by: Eran Ben Elisha <eranbe@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
index 195863b2c013..06cbd49d4e98 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
@@ -54,6 +54,12 @@ static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
priv->stats_nch);
}
+static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv)
+{
+ return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
+ max(priv->max_nch, priv->stats_nch));
+}
+
static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
{
struct mlx5e_hv_vhca_stats_agent *sagent;
@@ -122,7 +128,7 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
{
- int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
+ int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv);
struct mlx5_hv_vhca_agent *agent;
priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
--
2.44.0
^ permalink raw reply related
* [PATCH net V2 2/3] net/mlx5e: Fix HV VHCA stats agent registration race
From: Tariq Toukan @ 2026-06-17 14:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Cosmin Ratiu, Eran Ben Elisha, Feng Liu, Haiyang Zhang,
Lama Kayal, Leon Romanovsky, linux-kernel, linux-rdma, Mark Bloch,
Nimrod Oren, Saeed Mahameed, Tariq Toukan
In-Reply-To: <20260617140127.573117-1-tariqt@nvidia.com>
From: Feng Liu <feliu@nvidia.com>
mlx5e_hv_vhca_stats_create() registers the stats agent through
mlx5_hv_vhca_agent_create(). The helper publishes the agent in
hv_vhca->agents[type] under agents_lock and immediately schedules an
asynchronous control invalidation on the HV VHCA workqueue before
returning to mlx5e.
The asynchronous invalidation invokes the control agent's invalidate
callback, which reads the hypervisor control block and forwards the
command to mlx5e_hv_vhca_stats_control(). That callback may either:
- call cancel_delayed_work_sync(&priv->stats_agent.work), or
- call queue_delayed_work(priv->wq, &sagent->work, sagent->delay).
However, the delayed_work and priv->stats_agent.agent are only
initialized after mlx5_hv_vhca_agent_create() returns to mlx5e:
agent = mlx5_hv_vhca_agent_create(...); /* publish + invalidate */
...
priv->stats_agent.agent = agent; /* too late */
INIT_DELAYED_WORK(&priv->stats_agent.work, ...); /* too late */
If the asynchronous control path runs before the two assignments
above, it can:
- Operate on an uninitialized delayed_work whose timer.function is
NULL. queue_delayed_work() calls add_timer() unconditionally, so
when the timer expires the timer softirq invokes a NULL function
pointer.
- Re-initialize the timer later through INIT_DELAYED_WORK() while
the timer is already enqueued in the timer wheel, corrupting the
hlist (entry.pprev cleared while the previous bucket node still
points at this entry).
- When the worker eventually runs, mlx5e_hv_vhca_stats_work() reads
sagent->agent (NULL) and dereferences it inside
mlx5_hv_vhca_agent_write().
Fix this by:
- Initializing priv->stats_agent.work before invoking
mlx5_hv_vhca_agent_create(), so the work is always in a valid
state when the control callback observes it.
- Adding a struct mlx5_hv_vhca_agent **ctx_update out-parameter
to mlx5_hv_vhca_agent_create(). The helper writes the agent
pointer to *ctx_update before publishing into hv_vhca->agents[]
and triggering the agents_update flow, so any callback
subsequently invoked from that flow already sees a valid
priv->stats_agent.agent. This avoids having the control
callback participate in agent initialization.
While at it, clear priv->stats_agent.{agent,buf} after teardown and
on the agent_create() failure path. Without this, an enable/disable
cycle hitting an early-return in create can lead to a UAF or
double-destroy of stale pointers from the previous cycle.
Fixes: cef35af34d6d ("net/mlx5e: Add mlx5e HV VHCA stats agent")
Signed-off-by: Feng Liu <feliu@nvidia.com>
Reviewed-by: Eran Ben Elisha <eranbe@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../mellanox/mlx5/core/en/hv_vhca_stats.c | 22 ++++++++++++-------
.../ethernet/mellanox/mlx5/core/lib/hv_vhca.c | 8 +++++--
.../ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 6 +++--
3 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
index 06cbd49d4e98..2e495442a547 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
@@ -73,7 +73,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
priv = container_of(sagent, struct mlx5e_priv, stats_agent);
buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
- agent = sagent->agent;
+ agent = READ_ONCE(sagent->agent);
buf = sagent->buf;
memset(buf, 0, buf_len);
@@ -135,11 +135,14 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
if (!priv->stats_agent.buf)
return;
+ INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
+
agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
MLX5_HV_VHCA_AGENT_STATS,
mlx5e_hv_vhca_stats_control, NULL,
mlx5e_hv_vhca_stats_cleanup,
- priv);
+ priv,
+ &priv->stats_agent.agent);
if (IS_ERR_OR_NULL(agent)) {
if (IS_ERR(agent))
@@ -148,18 +151,21 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
agent);
kvfree(priv->stats_agent.buf);
- return;
+ priv->stats_agent.buf = NULL;
}
-
- priv->stats_agent.agent = agent;
- INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
}
void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
{
- if (IS_ERR_OR_NULL(priv->stats_agent.agent))
+ struct mlx5_hv_vhca_agent *agent;
+
+ agent = READ_ONCE(priv->stats_agent.agent);
+ if (IS_ERR_OR_NULL(agent))
return;
- mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
+ mlx5_hv_vhca_agent_destroy(agent);
kvfree(priv->stats_agent.buf);
+
+ WRITE_ONCE(priv->stats_agent.agent, NULL);
+ priv->stats_agent.buf = NULL;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
index d6dc7bce855e..305752dab7bd 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
@@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca)
return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL,
NULL,
mlx5_hv_vhca_control_agent_invalidate,
- NULL, NULL);
+ NULL, NULL, NULL);
}
static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent)
@@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
void (*invalidate)(struct mlx5_hv_vhca_agent*,
u64 block_mask),
void (*cleaup)(struct mlx5_hv_vhca_agent *agent),
- void *priv)
+ void *priv,
+ struct mlx5_hv_vhca_agent **ctx_update)
{
struct mlx5_hv_vhca_agent *agent;
@@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
agent->invalidate = invalidate;
agent->cleanup = cleaup;
+ if (ctx_update)
+ WRITE_ONCE(*ctx_update, agent);
+
mutex_lock(&hv_vhca->agents_lock);
hv_vhca->agents[type] = agent;
mutex_unlock(&hv_vhca->agents_lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
index f240ffe5116c..8b3974cf0ee4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
@@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
void (*invalidate)(struct mlx5_hv_vhca_agent*,
u64 block_mask),
void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
- void *context);
+ void *context,
+ struct mlx5_hv_vhca_agent **ctx_update);
void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent);
int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent,
@@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
void (*invalidate)(struct mlx5_hv_vhca_agent*,
u64 block_mask),
void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
- void *context)
+ void *context,
+ struct mlx5_hv_vhca_agent **ctx_update)
{
return NULL;
}
--
2.44.0
^ permalink raw reply related
* Re: [PATCH net 0/2] devlink: Fix a couple parent ref leaks
From: Simon Horman @ 2026-06-17 14:02 UTC (permalink / raw)
To: Cosmin Ratiu
Cc: netdev, Jiri Pirko, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Michal Wilczynski, Carolina Jubran, Mark Bloch,
Tariq Toukan
In-Reply-To: <20260616110633.1449432-1-cratiu@nvidia.com>
On Tue, Jun 16, 2026 at 02:06:31PM +0300, Cosmin Ratiu wrote:
> These two patches fix parent ref leaks on errors.
>
> Cosmin Ratiu (2):
> devlink: Fix parent ref leak in devl_rate_node_create()
> devlink: Fix parent ref leak on tc-bw failure
Thanks Cosmin,
For the series:
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* [PATCH net V2 3/3] net/mlx5e: Fix publication race for priv->channel_stats[]
From: Tariq Toukan @ 2026-06-17 14:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Cosmin Ratiu, Eran Ben Elisha, Feng Liu, Haiyang Zhang,
Lama Kayal, Leon Romanovsky, linux-kernel, linux-rdma, Mark Bloch,
Nimrod Oren, Saeed Mahameed, Tariq Toukan
In-Reply-To: <20260617140127.573117-1-tariqt@nvidia.com>
From: Feng Liu <feliu@nvidia.com>
mlx5e_channel_stats_alloc() publishes a new entry to
priv->channel_stats[] and then increments priv->stats_nch as a
publication token, but neither store carries any memory barrier:
priv->channel_stats[ix] = kvzalloc_node(...);
if (!priv->channel_stats[ix])
return -ENOMEM;
priv->stats_nch++;
Concurrent readers compute the loop bound from priv->stats_nch and
then dereference priv->channel_stats[i] using plain accesses, e.g.
for (i = 0; i < priv->stats_nch; i++) {
struct mlx5e_channel_stats *cs = priv->channel_stats[i];
... cs->rq.packets ...
}
On weakly-ordered architectures (ARM, PowerPC, RISC-V) the writes to
channel_stats[ix] and stats_nch may become visible to other CPUs out
of program order. A reader can observe stats_nch == N while still
seeing channel_stats[N-1] == NULL, leading to a NULL pointer
dereference in the channel_stats loop.
This has been observed in production on BlueField-3 DPUs (arm64),
where ovs-vswitchd queries netdev statistics over netlink during NIC
bringup, racing mlx5e_open_channel() -> mlx5e_channel_stats_alloc()
on another CPU:
Unable to handle kernel NULL pointer dereference at virtual address 0x840
Hardware name: BlueField-3 DPU
pc : mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core]
Call trace:
mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core]
dev_get_stats+0x50/0xc0
ovs_vport_get_stats+0x38/0xac [openvswitch]
ovs_vport_cmd_fill_info+0x194/0x290 [openvswitch]
ovs_vport_cmd_get+0xbc/0x10c [openvswitch]
genl_family_rcv_msg_doit+0xd0/0x160
genl_rcv_msg+0xec/0x1f0
netlink_rcv_skb+0x64/0x130
genl_rcv+0x40/0x60
netlink_unicast+0x2fc/0x370
netlink_sendmsg+0x1dc/0x454
...
__arm64_sys_sendmsg+0x2c/0x40
Add mlx5e_stats_nch_write() and mlx5e_stats_nch_read() helpers in en.h
that wrap the smp_store_release()/smp_load_acquire() pair on stats_nch.
The release/acquire pair establishes the contract:
stats_nch == N => channel_stats[0..N-1] are visible and non-NULL.
Publish the stats_nch increment via mlx5e_stats_nch_write() in the
writer (mlx5e_channel_stats_alloc()), and read stats_nch via
mlx5e_stats_nch_read() in all readers: mlx5e RX/TX queue stats,
mlx5e_get_base_stats(), ethtool channels stats, IPoIB stats, the
sw_stats fold and the HV VHCA stats agent.
Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage")
Signed-off-by: Feng Liu <feliu@nvidia.com>
Reviewed-by: Eran Ben Elisha <eranbe@nvidia.com>
Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Nimrod Oren <noren@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/ethernet/mellanox/mlx5/core/en.h | 12 ++++++++++++
.../ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 ++++++----
drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 14 ++++++++------
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c | 9 +++++----
.../net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c | 3 ++-
5 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 2270e2e550dd..d507289096c2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -987,6 +987,18 @@ struct mlx5e_priv {
struct ethtool_fec_hist_range *fec_ranges;
};
+static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv)
+{
+ /* Pairs with smp_store_release in mlx5e_stats_nch_write(). */
+ return smp_load_acquire(&priv->stats_nch);
+}
+
+static inline void mlx5e_stats_nch_write(struct mlx5e_priv *priv, u16 n)
+{
+ /* Pairs with smp_load_acquire in mlx5e_stats_nch_read(). */
+ smp_store_release(&priv->stats_nch, n);
+}
+
struct mlx5e_dev {
struct net_device *netdev;
struct devlink_port dl_port;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
index 2e495442a547..9747d7736d37 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
@@ -33,9 +33,10 @@ mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch,
static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
int buf_len)
{
+ u16 nch = mlx5e_stats_nch_read(priv);
int ch, i = 0;
- for (ch = 0; ch < priv->stats_nch; ch++) {
+ for (ch = 0; ch < nch; ch++) {
void *buf = data + i;
if (WARN_ON_ONCE(buf +
@@ -50,8 +51,9 @@ static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
{
- return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
- priv->stats_nch);
+ u16 nch = mlx5e_stats_nch_read(priv);
+
+ return sizeof(struct mlx5e_hv_vhca_per_ring_stats) * nch;
}
static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv)
@@ -106,7 +108,7 @@ static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent,
sagent = &priv->stats_agent;
block->version = MLX5_HV_VHCA_STATS_VERSION;
- block->rings = priv->stats_nch;
+ block->rings = mlx5e_stats_nch_read(priv);
if (!block->command) {
cancel_delayed_work_sync(&priv->stats_agent.work);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 8f2b3abe0092..94e5352a246c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -2773,7 +2773,7 @@ static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu)
GFP_KERNEL, cpu_to_node(cpu));
if (!priv->channel_stats[ix])
return -ENOMEM;
- priv->stats_nch++;
+ mlx5e_stats_nch_write(priv, priv->stats_nch + 1);
return 0;
}
@@ -4043,9 +4043,10 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s)
{
+ u16 nch = mlx5e_stats_nch_read(priv);
int i;
- for (i = 0; i < priv->stats_nch; i++) {
+ for (i = 0; i < nch; i++) {
struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq;
struct mlx5e_rq_stats *rq_stats = &channel_stats->rq;
@@ -5489,7 +5490,7 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i,
struct mlx5e_rq_stats *xskrq_stats;
struct mlx5e_rq_stats *rq_stats;
- if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch)
+ if (mlx5e_is_uplink_rep(priv) || !mlx5e_stats_nch_read(priv))
return;
channel_stats = priv->channel_stats[i];
@@ -5508,7 +5509,7 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i,
struct mlx5e_priv *priv = netdev_priv(dev);
struct mlx5e_sq_stats *sq_stats;
- if (!priv->stats_nch)
+ if (!mlx5e_stats_nch_read(priv))
return;
/* no special case needed for ptp htb etc since txq2sq_stats is kept up
@@ -5525,6 +5526,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
struct netdev_queue_stats_tx *tx)
{
struct mlx5e_priv *priv = netdev_priv(dev);
+ u16 nch = mlx5e_stats_nch_read(priv);
struct mlx5e_ptp *ptp_channel;
int i, tc;
@@ -5533,7 +5535,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
rx->bytes = 0;
rx->alloc_fail = 0;
- for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) {
+ for (i = priv->channels.params.num_channels; i < nch; i++) {
struct netdev_queue_stats_rx rx_i = {0};
mlx5e_get_queue_stats_rx(dev, i, &rx_i);
@@ -5558,7 +5560,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
tx->packets = 0;
tx->bytes = 0;
- for (i = 0; i < priv->stats_nch; i++) {
+ for (i = 0; i < nch; i++) {
struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
/* handle two cases:
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
index 1a3ecf073913..8632b73179cb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
@@ -516,6 +516,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c)
static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
{
struct mlx5e_sw_stats *s = &priv->stats.sw;
+ u16 nch = mlx5e_stats_nch_read(priv);
int i;
memset(s, 0, sizeof(*s));
@@ -523,7 +524,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
for (i = 0; i < priv->channels.num; i++) /* for active channels only */
mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]);
- for (i = 0; i < priv->stats_nch; i++) {
+ for (i = 0; i < nch; i++) {
struct mlx5e_channel_stats *channel_stats =
priv->channel_stats[i];
@@ -2615,7 +2616,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ptp) { return; }
static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels)
{
- int max_nch = priv->stats_nch;
+ int max_nch = mlx5e_stats_nch_read(priv);
return (NUM_RQ_STATS * max_nch) +
(NUM_CH_STATS * max_nch) +
@@ -2628,8 +2629,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels)
static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels)
{
+ int max_nch = mlx5e_stats_nch_read(priv);
bool is_xsk = priv->xsk.ever_used;
- int max_nch = priv->stats_nch;
int i, j, tc;
for (i = 0; i < max_nch; i++)
@@ -2661,8 +2662,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels)
static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(channels)
{
+ int max_nch = mlx5e_stats_nch_read(priv);
bool is_xsk = priv->xsk.ever_used;
- int max_nch = priv->stats_nch;
int i, j, tc;
for (i = 0; i < max_nch; i++)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
index 0a6003fe60e9..674bed721e63 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
@@ -135,10 +135,11 @@ void mlx5i_cleanup(struct mlx5e_priv *priv)
static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv)
{
+ u16 nch = mlx5e_stats_nch_read(priv);
struct rtnl_link_stats64 s = {};
int i, j;
- for (i = 0; i < priv->stats_nch; i++) {
+ for (i = 0; i < nch; i++) {
struct mlx5e_channel_stats *channel_stats;
struct mlx5e_rq_stats *rq_stats;
--
2.44.0
^ permalink raw reply related
* Re: [PATCH net v2] tipc: fix use-after-free of the discoverer in tipc_disc_rcv()
From: Weiming Shi @ 2026-06-17 14:05 UTC (permalink / raw)
To: Tung Quang Nguyen
Cc: jmaloy@redhat.com, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, davem@davemloft.net,
xmei5@asu.edu, netdev@vger.kernel.org,
tipc-discussion@lists.sourceforge.net,
linux-kernel@vger.kernel.org
In-Reply-To: <GV1P189MB1988614C09148258A9844D0CC6E42@GV1P189MB1988.EURP189.PROD.OUTLOOK.COM>
Tung Quang Nguyen <tung.quang.nguyen@est.tech> 于2026年6月17日周三 16:47写道:
>
> >Subject: [PATCH net v2] tipc: fix use-after-free of the discoverer in
> >tipc_disc_rcv()
> >
> >bearer_disable() frees b->disc with tipc_disc_delete()'s plain kfree(), but
> >tipc_disc_rcv() still dereferences b->disc in RX softirq under
> >rcu_read_lock() (tipc_udp_recv -> tipc_rcv -> tipc_disc_rcv).
> >
> >L2 bearers are safe thanks to the synchronize_net() in tipc_disable_l2_media(),
> >but the UDP bearer defers that call to the
> >cleanup_bearer() workqueue, so the discoverer is freed with no grace
> >period:
> >
> > BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149)
> >Read of size 8 at addr ffff88802348b728 by task poc_tipc/184 <IRQ>
> > tipc_disc_rcv (net/tipc/discover.c:149)
> > tipc_rcv (net/tipc/node.c:2126)
> > tipc_udp_recv (net/tipc/udp_media.c:391)
> > udp_rcv (net/ipv4/udp.c:2643)
> > ip_local_deliver_finish (net/ipv4/ip_input.c:241) </IRQ> Freed by task 181:
> > kfree (mm/slub.c:6565)
> > bearer_disable (net/tipc/bearer.c:418)
> > tipc_nl_bearer_disable (net/tipc/bearer.c:1001)
> >
> >The bearer is freed with kfree_rcu(); free the discoverer the same way.
> >Add an rcu_head to struct tipc_discoverer and free it and its skb from an RCU
> >callback.
> >
> >Because the RCU callback (tipc_disc_free_rcu) lives in module text, a
> >call_rcu() that is still pending when the tipc module is unloaded would invoke a
> >freed function. Add an rcu_barrier() to tipc_exit() after the bearer subsystem
> >has been torn down, so all pending discoverer callbacks have run before the
> >module text goes away.
> >
> >Reachable from an unprivileged user namespace: the TIPCv2 genl family is
> >netnsok and its bearer commands have no GENL_ADMIN_PERM. Needs
> >CONFIG_TIPC and CONFIG_TIPC_MEDIA_UDP.
> >
> >Fixes: 25b0b9c4e835 ("tipc: handle collisions of 32-bit node address hash
> >values")
> >Reported-by: Xiang Mei <xmei5@asu.edu>
> >Assisted-by: Claude:claude-opus-4-8
> >Signed-off-by: Weiming Shi <bestswngs@gmail.com>
> >---
> >v2:
> > - split the over-80-column container_of() line (Tung Quang Nguyen)
> > - add rcu_barrier() to tipc_exit() so a pending call_rcu() cannot fire
> > into freed module text after rmmod (Eric Dumazet)
> >
> > net/tipc/core.c | 3 +++
> > net/tipc/discover.c | 14 ++++++++++++--
> > 2 files changed, 15 insertions(+), 2 deletions(-)
> >
> >diff --git a/net/tipc/core.c b/net/tipc/core.c index
> >434e70eabe08..747328e58d30 100644
> >--- a/net/tipc/core.c
> >+++ b/net/tipc/core.c
> >@@ -218,6 +218,9 @@ static void __exit tipc_exit(void)
> > unregister_pernet_device(&tipc_net_ops);
> > tipc_unregister_sysctl();
> >
> >+ /* Wait for tipc_disc_free_rcu() callbacks queued from module text. */
>
> Please change above comment to: /* TODO: Wait for all timers that called call_rcu() to finish before calling rcu_barrier() */
>
> Note that call_rcu() are used in discover.c and node.c. So, the TODO comment helps we add more checking code later in another patch.
>
> >+ rcu_barrier();
> >+
> > pr_info("Deactivated\n");
> > }
> >
> >diff --git a/net/tipc/discover.c b/net/tipc/discover.c index
> >3e54d2df5683..696b7a8ed54d 100644
> >--- a/net/tipc/discover.c
> >+++ b/net/tipc/discover.c
> >@@ -58,6 +58,7 @@
> > * @skb: request message to be (repeatedly) sent
> > * @timer: timer governing period between requests
> > * @timer_intv: current interval between requests (in ms)
> >+ * @rcu: RCU head for deferred freeing
> > */
> > struct tipc_discoverer {
> > u32 bearer_id;
> >@@ -69,6 +70,7 @@ struct tipc_discoverer {
> > struct sk_buff *skb;
> > struct timer_list timer;
> > unsigned long timer_intv;
> >+ struct rcu_head rcu;
> > };
> >
> > /**
> >@@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer
> >*b,
> > return 0;
> > }
> >
> >+static void tipc_disc_free_rcu(struct rcu_head *rp) {
> >+ struct tipc_discoverer *d =
> >+ container_of(rp, struct tipc_discoverer, rcu);
> >+
> >+ kfree_skb(d->skb);
> >+ kfree(d);
> >+}
> >+
> > /**
> > * tipc_disc_delete - destroy object sending periodic link setup requests
> > * @d: ptr to link dest structure
> >@@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer
> >*b, void tipc_disc_delete(struct tipc_discoverer *d) {
> > timer_shutdown_sync(&d->timer);
> >- kfree_skb(d->skb);
> >- kfree(d);
> >+ call_rcu(&d->rcu, tipc_disc_free_rcu);
> > }
> >
> > /**
> >--
> >2.43.0
>
Hi
Sent v3. Thanks for the review.
Best,
Weiming Shi
^ permalink raw reply
* Re: [PATCH net] net: rnpgbe: fix mailbox endianness handling
From: Yibo Dong @ 2026-06-17 14:05 UTC (permalink / raw)
To: Andrew Lunn
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, vadim.fedorenko,
netdev, linux-kernel, yaojun
In-Reply-To: <26517b8f-33b7-4de7-8fe8-c7dca5fa7a4b@lunn.ch>
On Wed, Jun 17, 2026 at 02:09:00PM +0200, Andrew Lunn wrote:
> > My understanding is as follows:
> > The firmware structures are defined with__le16 / __le32 for wire format,
> > but the original code cast these struct pointers to u32 * before passing
> > them to the mailbox read/write routines:
> > - Send path: (u32 *)&req -> msg buffer -> writel()
> > - Receive path: readl() -> msg buffer -> (u32 *)&reply
> > Sparse only sees pure u32 = u32 assignments here, so no type mismatch is
> > reported.
>
> Can the code be changed so that it does not need the cast? Casts are
> bad, as you have just shown. This is something i try to push back on,
> it makes you think about types and avoid issues like this.
>
> Andrew
>
Thinking... Yes. A few possibilities:
1. Make all fields __le32, then extract via shifts:
struct mbx_fw_cmd_req {
__le32 word0; // [15:0]=flags [31:16]=opcode
__le32 word1; // [15:0]=datalen [31:16]=ret_value
...
};
But that's painful — le32_to_cpu(req.word0) >> 16 vs req.opcode.
2. Use a union to keep named fields while also exposing __le32[] access:
union mbx_fw_cmd_req_u {
struct mbx_fw_cmd_req req;
__le32 dwords[sizeof(struct mbx_fw_cmd_req) / sizeof(__le32)];
};
union mbx_fw_cmd_reply_u {
struct mbx_fw_cmd_reply reply;
__le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)];
};
The transport interface becomes:
int mucse_write_mbx_pf(struct mucse_hw *hw, const __le32 *msg, u16 size);
int mucse_read_mbx_pf(struct mucse_hw *hw, __le32 *msg, u16 size);
Callers would use:
union mbx_fw_cmd_req_u cmd = {};
cmd.req.opcode = cpu_to_le16(...);
cmd.req.flags = cpu_to_le16(...);
mucse_write_mbx_pf(hw, cmd.dwords, sizeof(cmd.req));
If the transport layer forgets le32_to_cpu(), sparse would catch it
because msg is __le32 * and mbx_data_rd32() returns u32.
The downside is an extra union wrapper and an extra level in field
access (cmd.req.opcode vs req.opcode) — a minor inconvenience.
Do you have a preference between these, or another approach?
Thanks for the feedback.
^ permalink raw reply
* Re: [PATCH bpf v3 2/2] selftests/bpf: Cover partial copy of non-linear test_run output
From: sun jian @ 2026-06-17 14:19 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, netdev, linux-kselftest, linux-kernel, ast, daniel, andrii,
martin.lau, eddyz87, memxor, song, yonghong.song, jolsa, davem,
edumazet, kuba, pabeni, horms, shuah, hawk, john.fastabend, sdf,
toke, lorenzo, paul.chaignon, martin.lau, clm, ihor.solodrai
In-Reply-To: <2dad9b5c184fa101d7cffa1d8f5eea5b5df60f53533d98c68175c9e3ec5ee6ac@mail.kernel.org>
On Wed, Jun 17, 2026 at 6:31 PM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/tools/testing/selftests/bpf/prog_tests/prog_run_opts.c b/tools/testing/selftests/bpf/prog_tests/prog_run_opts.c
> > index 01f1d1b6715a..9cc898e6a9f7 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/prog_run_opts.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/prog_run_opts.c
> > @@ -4,6 +4,10 @@
> >
> > #include "test_pkt_access.skel.h"
> >
> > +#define NONLINEAR_PKT_LEN 9000
> > +#define NONLINEAR_LINEAR_DATA_LEN 64
> > +#define SHORT_OUT_LEN 100
> > +
>
> [ ... ]
>
> > @@ -20,6 +24,69 @@ static void check_run_cnt(int prog_fd, __u64 run_cnt)
> > "incorrect number of repetitions, want %llu have %llu\n", run_cnt, info.run_cnt);
> > }
> >
> > +static void init_pkt(__u8 *pkt, size_t len)
> > +{
> > + size_t i;
> > +
> > + for (i = 0; i < len; i++)
> > + pkt[i] = i & 0xff;
> > +}
>
> A question was raised on v2 about whether pkt_v4 could be reused by
> reducing the linear area to ETH_HLEN, rather than introducing a custom
> init_pkt() with a 9000-byte stack packet.
>
> Can't we reuse pkt_v4 here by reducing the linear area to ETH_HLEN?
> The v3 still adds init_pkt() and the NONLINEAR_PKT_LEN packet, so this
> doesn't seem to have been picked up.
>
> > +
> > +static void test_skb_nonlinear_data_out_partial(struct test_pkt_access *skel)
> > +{
> > + LIBBPF_OPTS(bpf_test_run_opts, topts);
> > + __u8 pkt[NONLINEAR_PKT_LEN];
> > + __u8 out[SHORT_OUT_LEN];
> > + struct __sk_buff skb = {};
> > + int prog_fd, err;
> > +
> > + init_pkt(pkt, sizeof(pkt));
> > +
> > + skb.data_end = NONLINEAR_LINEAR_DATA_LEN;
> > +
> > + topts.data_in = pkt;
> > + topts.data_size_in = sizeof(pkt);
> > + topts.data_out = out;
> > + topts.data_size_out = sizeof(out);
> > + topts.ctx_in = &skb;
> > + topts.ctx_size_in = sizeof(skb);
> > +
> > + prog_fd = bpf_program__fd(skel->progs.tc_pass_prog);
>
> [ ... ]
>
> > diff --git a/tools/testing/selftests/bpf/progs/test_pkt_access.c b/tools/testing/selftests/bpf/progs/test_pkt_access.c
> > index bce7173152c6..cd284401eebd 100644
> > --- a/tools/testing/selftests/bpf/progs/test_pkt_access.c
> > +++ b/tools/testing/selftests/bpf/progs/test_pkt_access.c
> > @@ -150,3 +150,15 @@ int test_pkt_access(struct __sk_buff *skb)
> >
> > return TC_ACT_UNSPEC;
> > }
> > +
> > +SEC("tc")
> > +int tc_pass_prog(struct __sk_buff *skb)
> > +{
> > + return TC_ACT_OK;
> > +}
> > +
> > +SEC("xdp.frags")
> > +int xdp_frags_pass_prog(struct xdp_md *ctx)
> > +{
> > + return XDP_PASS;
> > +}
>
> A related suggestion on v2 was that, once pkt_v4 is reused, the existing
> BPF program could be reused instead of adding new pass-through programs.
>
> Could tc_pass_prog and xdp_frags_pass_prog be dropped in favour of the
> existing program? The v3 still adds both of these, so this point also
> seems to be open.
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27680511802
Hi,
Thanks for checking this.
I tried reusing pkt_v4 and the existing TC program, but they do not fit
the skb case this test is trying to cover.
For skb test_run, IPv4/IPv6 inputs with a too-short L3 header in the
linear area are rejected before bpf_test_finish(). With pkt_v4 and a
linear area of ETH_HLEN, the test fails with -EINVAL before reaching the
partial copy-out path. If the linear area is increased enough to pass the
IPv4 check, pkt_v4 is too small to both trigger the old
copy_size - frag_size path and verify that the copied prefix spans the
linear data and the first fragment. pkt_v6 has the same issue: after
making the IPv6 header linear, only 20 bytes remain in frags.
The existing test_pkt_access program has its own packet-access coverage
goals and is not just a pass-through carrier. With such a short linear
area or small packet fixture, it can fail before the test hits the
bpf_test_finish()'s partial copy-out path. A pass-through TC program is
therefore a better fit, because it keeps the test focused on the
bpf_test_finish() copy-out semantics.
For XDP, this object does not have an existing xdp.frags pass-through
program, so the small XDP frags program is needed to cover the other
caller of the shared bpf_test_finish() path.
Thanks,
Sun Jian
^ permalink raw reply
* Re: Landlock: LANDLOCK_ACCESS_NET_CONNECT_TCP bypass via TCP Fast Open
From: Mickaël Salaün @ 2026-06-17 14:22 UTC (permalink / raw)
To: Bryam Vargas
Cc: Günther Noack, Matthieu Buffet, Paul Moore, Eric Dumazet,
Neal Cardwell, linux-security-module, netdev, linux-kernel,
Mikhail Ivanov
In-Reply-To: <20260616201615.275032-1-hexlabsecurity@proton.me>
Hi,
Thanks for the report. This was previously identified by Mikhail and
Matthieu, see the related issue:
https://github.com/landlock-lsm/linux/issues/41
On Tue, Jun 16, 2026 at 08:16:22PM +0000, Bryam Vargas wrote:
> Hello Mickaël, and Landlock folks,
>
> A task confined by a Landlock ruleset that handles
> LANDLOCK_ACCESS_NET_CONNECT_TCP and is denied connecting to a given port can
> still establish a TCP connection to that port by using TCP Fast Open, i.e.
> sendto(fd, ..., MSG_FASTOPEN, &dst, dstlen) on a fresh stream socket. The
> network-egress confinement for TCP connect is silently bypassed.
>
> Affected
> --------
> Any kernel with CONFIG_SECURITY_LANDLOCK=y and Landlock enabled that supports
> the TCP network access rights (Landlock ABI >= 4, since Linux 6.7). Confirmed by
> source inspection on mainline (v7.1-rc7) and reproduced on Linux 7.0.11
> (Landlock ABI 8). No CONFIG beyond Landlock + IPv4/IPv6 TCP; TCP Fast Open client
> is enabled by the per-netns default (net.ipv4.tcp_fastopen has TFO_CLIENT_ENABLE
> set), so no sysctl change and no setsockopt are required.
>
> Root cause
> ----------
> LANDLOCK_ACCESS_NET_CONNECT_TCP is enforced only by the socket_connect LSM hook
> (hook_socket_connect -> current_check_access_socket). security_socket_connect()
> has exactly one call site in the tree, net/socket.c (the connect(2) syscall).
>
> TCP Fast Open performs an implicit connect inside sendmsg:
>
> tcp_sendmsg_locked() net/ipv4/tcp.c (MSG_FASTOPEN branch)
> -> tcp_sendmsg_fastopen() net/ipv4/tcp.c
> -> __inet_stream_connect(..., is_sendmsg=1) net/ipv4/af_inet.c
> -> sk->sk_prot->connect() net/ipv4/af_inet.c -> tcp_v4_connect()
>
> This path establishes the connection to the address taken from msg_name but
> never calls security_socket_connect(). The only LSM hook fired on the sendmsg
> path is security_socket_sendmsg(), and Landlock registers no socket_sendmsg
> hook, so LANDLOCK_ACCESS_NET_CONNECT_TCP is never re-checked. __inet_stream_connect()
> itself carries no LSM hook (only the cgroup-BPF pre_connect, a different
> mechanism).
>
> Notably the kernel already mediates the analogous AF_UNIX implicit-connect on the
> send path via the unix_may_send hook, which Landlock does register
> (hook_unix_may_send) -- so the sendmsg-implies-connect pattern is recognized, but
> the TCP Fast Open case has no equivalent coverage. The MPTCP fast-open path
> (mptcp_sendmsg_fastopen -> __inet_stream_connect) is a second producer of the
> same unmediated connect (by source inspection; not separately reproduced).
>
> Reproducer
> ----------
> A self-contained, fully unprivileged PoC is available on request. It forks an
> unconfined TFO-capable loopback listener, then in a child applies a Landlock
> ruleset handling LANDLOCK_ACCESS_NET_CONNECT_TCP with no allow rule
> (landlock_create_ruleset() with handled_access_net =
> LANDLOCK_ACCESS_NET_CONNECT_TCP, no landlock_add_rule(), then
> landlock_restrict_self(); every TCP connect is denied) and tries the forbidden
> port two ways:
>
> (1) connect(fd, &dst) -> -EACCES (Landlock enforces CONNECT_TCP)
> (2) sendto(fd2, buf, len, MSG_FASTOPEN, &dst, dstlen)
> -> succeeds; the listener accepts the
> connection and reads the payload.
>
> Observed on Linux 7.0.11 (Landlock ABI 8):
>
> [1] connect(2) -> ret=-1 errno=13 (Permission denied)
> [2] sendto(MSG_FASTOPEN) -> ret=14 errno=0 (OK/queued)
> [+] listener ACCEPTED the confined child's connection; payload="..."
>
> connect(2) to the port is denied while sendto(MSG_FASTOPEN) reaches the identical
> port and delivers data.
>
> Impact
> ------
> A sandbox that uses LANDLOCK_ACCESS_NET_CONNECT_TCP to restrict outbound TCP
> (e.g. to keep a confined component from reaching an internal service or a
> metadata endpoint) can be escaped by an unprivileged, self-confined task with no
> CAP and no namespace transition -- for any destination port, since the
> implicit-connect path never consults the connect hook regardless of address (the
> run above shows one port). It is an integrity
> bypass of the network-confinement property; no memory safety is involved.
> I score it CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N (6.5 Medium) -- the
> confined task escapes the policy authority that defined its sandbox, a scope
> change; 5.5 if you treat the Landlock boundary as the same authority (S:U).
>
> Note on the in-flight UDP series
> --------------------------------
> The "landlock: Add UDP access control support" series (v5, Matthieu Buffet,
> https://lore.kernel.org/r/20260611162107.49278-3-matthieu@buffet.re) adds a
> socket_sendmsg hook, hook_socket_sendmsg(), but it returns 0 for non-UDP
> sockets:
>
> if (sk_is_udp(sock->sk))
> access_request = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
> else
> return 0;
>
> so a TCP socket using MSG_FASTOPEN still bypasses LANDLOCK_ACCESS_NET_CONNECT_TCP
> even after that series lands. It may be most convenient to fix this there.
>
> Suggested direction
> -------------------
> Re-check LANDLOCK_ACCESS_NET_CONNECT_TCP on the implicit-connect path: either have
> the socket_sendmsg hook evaluate CONNECT_TCP for stream sockets when the call
> performs an implicit connect (mirroring the AF_UNIX unix_may_send handling), or
> place the check inside __inet_stream_connect() so a single chokepoint covers
> connect(2), TCP Fast Open, and the MPTCP fast-open sibling.
>
> I am happy to send a patch for this if you would like me to.
Yes please.
>
> Best regards,
>
> Bryam Vargas
> Independent security researcher, HEXLAB S.A.S., Cali, Colombia
> hexlabsecurity@proton.me
>
>
^ permalink raw reply
* Re: [PATCH] net/sched: dualpi2: fix GSO backlog accounting
From: Jamal Hadi Salim @ 2026-06-17 14:23 UTC (permalink / raw)
To: Xingquan Liu
Cc: netdev, Jiri Pirko, Victor Nogueira, stable,
Chia-Yu Chang (Nokia)
In-Reply-To: <CAM0EoM=o+kBQNND8ViMe8bZQmFAtATav+CFMmtp1udzu+tpTzA@mail.gmail.com>
On Wed, Jun 17, 2026 at 6:23 AM Jamal Hadi Salim <jhs@mojatatu.com> wrote:
>
> On Tue, Jun 16, 2026 at 6:03 PM Xingquan Liu <b1n@b1n.io> wrote:
> >
> > When DualPI2 splits a GSO skb into N segments, it propagates N
> > additional packets to its parent before returning NET_XMIT_SUCCESS.
> > The parent then accounts for the original skb once more, leaving its
> > qlen one larger than the number of packets actually queued.
> >
> > With QFQ as the parent, after all real packets are dequeued, QFQ still
> > has a non-zero qlen while its in-service aggregate has no active
> > classes. qfq_choose_next_agg() returns NULL and qfq_dequeue() passes
> > the result to qfq_peek_skb(), causing a NULL pointer dereference.
> >
> > Count only successfully queued segments and propagate the difference
> > between the original skb and those segments. Return success whenever
> > at least one segment was queued.
> >
> > Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Xingquan Liu <b1n@b1n.io>
> > ---
> > net/sched/sch_dualpi2.c | 11 +++++------
> > 1 file changed, 5 insertions(+), 6 deletions(-)
> >
> > diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c
> > index dfec3c99eb45..37d6a8960310 100644
> > --- a/net/sched/sch_dualpi2.c
> > +++ b/net/sched/sch_dualpi2.c
> > @@ -461,7 +461,7 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> > if (IS_ERR_OR_NULL(nskb))
> > return qdisc_drop(skb, sch, to_free);
> >
> > - cnt = 1;
> > + cnt = 0;
> > byte_len = 0;
> > orig_len = qdisc_pkt_len(skb);
> > skb_list_walk_safe(nskb, nskb, next) {
> > @@ -488,16 +488,15 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> > byte_len += nskb->len;
> > }
> > }
> > - if (cnt > 1) {
> > + if (cnt > 0) {
> > /* The caller will add the original skb stats to its
> > * backlog, compensate this if any nskb is enqueued.
> > */
> > - --cnt;
> > - byte_len -= orig_len;
> > + qdisc_tree_reduce_backlog(sch, 1 - cnt,
> > + orig_len - byte_len);
> > }
> > - qdisc_tree_reduce_backlog(sch, -cnt, -byte_len);
> > consume_skb(skb);
> > - return err;
> > + return cnt > 0 ? NET_XMIT_SUCCESS : err;
> > }
>
> This looks like a behavior change?
> Ex: If the last segment failed you will return XMIT_SUCCESS whereas
> before it could be with __NET_XMIT_BYPASS, NET_XMIT_CN, etc.
> I am not sure what the best answer is and maybe it doesnt matter. Did
> you look at what other qdiscs do? I dont have time right now but will
> later - or you can before i get to it.
> Also, you didnt add the owner of this qdisc on your to: - maybe he
> has some thoughts..
>
After looking at what other qdiscs do, your patch is fine. But please
fixup the commit to something like:
---
When DualPI2 splits a GSO skb into N segments, it propagates N
additional packets to its parent before returning NET_XMIT_SUCCESS.
The parent then accounts for the original skb once more, leaving its
qlen one larger than the number of packets actually queued.
With QFQ as the parent, after all real packets are dequeued, QFQ still
has a non-zero qlen while its in-service aggregate has no active
classes. qfq_choose_next_agg() returns NULL and qfq_dequeue() passes
the result to qfq_peek_skb(), causing a NULL pointer dereference.
Follow the same pattern used by tbf_segment() and taprio: count only
successfully queued segments, propagate the difference between the
original skb and those segments, and return NET_XMIT_SUCCESS whenever
at least one segment was queued.
Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc")
Cc: stable@vger.kernel.org
Signed-off-by: Xingquan Liu <b1n@b1n.io>
-----
Do you know how to create a tdc test that will recreate this? If not
either Victor or myself can help you create one.
cheers,
jamal
> cheers,
> jamal
>
>
> > return dualpi2_enqueue_skb(skb, sch, to_free);
> > }
> >
> > base-commit: fbc6a80cb5d3fd4ac4b56e8c9d791dd17be890c4
> > --
> > Xingquan Liu
> >
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-next v1] ixgbe: Implement PCI reset handler
From: Temerkhanov, Sergey @ 2026-06-17 14:36 UTC (permalink / raw)
To: Paul Menzel
Cc: intel-wired-lan@lists.osuosl.org, netdev@vger.kernel.org,
Loktionov, Aleksandr, Bjorn Helgaas, linux-pci@vger.kernel.org
In-Reply-To: <bd5ab9e3-ab93-43ad-a2ce-03d56e2d2ecf@molgen.mpg.de>
> -----Original Message-----
> From: Paul Menzel <pmenzel@molgen.mpg.de>
> Sent: Wednesday, June 17, 2026 11:03 AM
> To: Temerkhanov, Sergey <sergey.temerkhanov@intel.com>
> Cc: intel-wired-lan@lists.osuosl.org; netdev@vger.kernel.org; Loktionov,
> Aleksandr <aleksandr.loktionov@intel.com>; Bjorn Helgaas
> <bhelgaas@google.com>; linux-pci@vger.kernel.org
> Subject: Re: [Intel-wired-lan] [PATCH iwl-next v1] ixgbe: Implement PCI reset
> handler
>
> [Cc: +Aleksandr (as in Reviewed-by:), +PCI subsystem]
>
> Dear Sergey,
>
>
> Thank you for your patch.
>
> Am 17.06.26 um 10:43 schrieb Sergey Temerkhanov:
> > Implement PCI device reset handler to allow the network device to get
> > re-initialized and function after a PCI-level reset.
>
> Please describe the problem in more detail. When does PCI-level reset occur,
> and what is the current problematic situation?
The actual scenario is when a reset is invoked via sysfs during the operation and the adapter is not
properly restoring its state thereafter resulting in a TX queue timeout.
>
> Also, what is ixgbe specific compared to a general PCIe implementation?
>
> Please share details how to test it, and how you tested it.
The test is simple:
- Run traffic on the adapter
- Initiate reset via sysfs
- Observe TX queue WDT timeout w/o this change
> > +#define IXGBE_PCIE_RESET_RETRIES 1000
>
> Why 1000? Isn’t there a generic PCIe macro? Please extend the commit
> message.
This is going to be replaced in v2
> > + if (test_bit(__IXGBE_SERVICE_INITED, &adapter->state)) {
> > + timer_delete_sync(&adapter->service_timer);
> > + /* Prevent the service task from running while we're resetting.
> */
>
> One of the two comments seems redundant.
I am clarifying this in v2. Essentially, the timer callback may queue a work, cancel_work_sync()
cancels any instance that may have been already pending.
>
> > + cancel_work_sync(&adapter->service_task);
> > + }
> progress\n");
>
> How can this happen? What should the user reading this error do?
Under the normal circumstances we should never get here, I am adding a comment in v2
> > static DEFINE_SIMPLE_DEV_PM_OPS(ixgbe_pm_ops, ixgbe_suspend,
> > ixgbe_resume);
>
> Kind regards,
>
> Paul
Regards,
Sergey
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox