* [PATCH net-next v4 0/3] ptp: Add driver for R-Car Gen4 gPTP timer
From: Niklas Söderlund @ 2026-07-02 12:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
Cc: Niklas Söderlund
Hello,
This series is the first part cleaning up how PTP timer support is
implemented on R-Car Gen4. Currently there is partial support for it in
some of the Ethernet devices that can use it, but not all.
The partial support have been implemented by hacking the gPTP module
directly into the first Ethernet device driver that used it, RTSN for
V4H and RSWITCH for S4. This is understandable as earlier R-Car
generations had a dedicated gPTP timer for each Ethernet device, but on
Gen4 there is a single system-wide PTP timer shared by all.
The current implementation makes it impossible for other Ethernet
devices on the platform to use the PTP timer without messing around with
other Ethernet device drivers.
The effort to clean this up starts with this series which adds the
system-wide gPTP timer as its own driver and device tree node.
This series will then be followed by work to add proper PTP support to
the R-Car RAVB Gen4 driver, which currently advertises to user-space it
supports PTP but which implementation is broken and does not work.
This will in turn be followed by work to the RTSN and RSWITCH drivers
will be be switched from its current partial support by mapping the gPTP
address space directly to instead use this driver.
Having both this and RTSN/RSWITCH described and enabled (!) in device
tree will not work as they will try to use the same memory region. For
this reason this new solution will only be enabled on platforms
after all user's of the gPTP clock have moved to only use the new
centralized timer. But in the interim both devices will be described
(but not enabled) in the platforms base dtsi file.
For some platforms this is straight forward, such as V4H Sparrow Hawk,
which only have the RAVB Ethernet interface. This platform currently
have no users of the PTP timer, but still advertise it supports it. This
and the soon to be posted RAVB patches solves that.
As the RAVB patches depends on this series the device tree node for the
gPTP clock is added in this series but will be enabled and linked to
consumers in the RAVB gPTP series for platforms where it will not
conflict with RTSN and RSWITCH. And further enabled as more of this is
cleaned up.
The gPTP driver itself is heavily influence by the existing partial
support for gPTP in the RTSN and RSWITCH drivers and the Renesas BSP.
Niklas Söderlund (3):
dt-bindings: ptp: renesas,rcar-gen4-gptp: Add R-Car Gen4
ptp: Add driver for R-Car Gen4
arm64: dts: renesas: r8a779g0: Add gPTP node
.../bindings/ptp/renesas,rcar-gen4-gptp.yaml | 64 +++++
MAINTAINERS | 7 +
arch/arm64/boot/dts/renesas/r8a779g0.dtsi | 9 +
drivers/ptp/Kconfig | 12 +
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_rcar_gen4.c | 232 ++++++++++++++++++
6 files changed, 325 insertions(+)
create mode 100644 Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
create mode 100644 drivers/ptp/ptp_rcar_gen4.c
--
2.55.0
^ permalink raw reply
* [PATCH net-next v4 1/3] dt-bindings: ptp: renesas,rcar-gen4-gptp: Add R-Car Gen4
From: Niklas Söderlund @ 2026-07-02 12:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
Cc: Niklas Söderlund, Krzysztof Kozlowski
In-Reply-To: <20260702125525.2230427-1-niklas.soderlund+renesas@ragnatech.se>
Add bindings for the R-Car Gen4 gPTP timer. The timer enables accurate
synchronization of the clock in the control system. The timer is
system-wide and used by different Ethernet devices on each Gen4 platform.
- On R-Car S4 it is shared between RSWITCH and RAVB.
- On R-Car V4H it is shared between RTSN and RAVB.
- On R-Car V4M it is only used by RAVB.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
* Changes since v1
- Drop 'binding for' for patch subject.
- Drop comment for renesas,rcar-gen4-gptp compatible to match other
Renesas bindings.
- Drop unused label in example.
- Rename node ptp in example.
---
.../bindings/ptp/renesas,rcar-gen4-gptp.yaml | 64 +++++++++++++++++++
MAINTAINERS | 6 ++
2 files changed, 70 insertions(+)
create mode 100644 Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
diff --git a/Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml b/Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
new file mode 100644
index 000000000000..3edd64d40038
--- /dev/null
+++ b/Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+# Copyright (C) 2026 Renesas Electronics Corp.
+# Copyright (C) 2026 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/ptp/renesas,rcar-gen4-gptp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas R-Car Gen4 gPTP timer
+
+maintainers:
+ - Niklas Söderlund <niklas.soderlund@ragnatech.se>
+
+description:
+ The R-Car Gen4 gPTP timer enables accurate synchronization of the clock in
+ the control system. The timer is system-wide and used by different Ethernet
+ devices on each Gen4 platform.
+
+ - On R-Car S4 it is shared between RSWITCH and RAVB.
+ - On R-Car V4H it is shared between RTSN and RAVB.
+ - On R-Car V4M it is only used by RAVB.
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - renesas,r8a779f0-gptp # S4-8
+ - renesas,r8a779g0-gptp # V4H
+ - renesas,r8a779h0-gptp # V4M
+ - const: renesas,rcar-gen4-gptp
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - power-domains
+ - resets
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/r8a779g0-cpg-mssr.h>
+ #include <dt-bindings/power/r8a779g0-sysc.h>
+
+ ptp@e6449000 {
+ compatible = "renesas,r8a779g0-gptp", "renesas,rcar-gen4-gptp";
+ reg = <0xe6449000 0x500>;
+ clocks = <&cpg CPG_MOD 2723>;
+ power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
+ resets = <&cpg 2723>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..ef17128d6f3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22901,6 +22901,12 @@ S: Maintained
F: Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
F: drivers/mtd/nand/raw/renesas-nand-controller.c
+RENESAS R-CAR GEN4 GPTP DRIVER
+M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
+L: linux-renesas-soc@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
+
RENESAS R-CAR GYROADC DRIVER
M: Marek Vasut <marek.vasut+renesas@mailbox.org>
L: linux-iio@vger.kernel.org
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v4 2/3] ptp: Add driver for R-Car Gen4
From: Niklas Söderlund @ 2026-07-02 12:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
Cc: Niklas Söderlund
In-Reply-To: <20260702125525.2230427-1-niklas.soderlund+renesas@ragnatech.se>
Add driver for the gPTP timer found on R-Car Gen4 devices. The timer is
system-wide and shared by different Ethernet devices on each Gen4
platform. The operation of the timer is however not completely in
depended of the systems Ethernet devices.
- On R-Car S4 is gated by the RSWITCH Ethernet module clock.
- On R-Car V4H is gated by the RTSN Ethernet module clock.
- On R-Car V4M is gated by its own module clock, the system have
neither RTSN or RSWITCH device. But the module clock is the same as
RTSN on V4H and the documentation referees to it as tsn (EtherTSN).
The gPTP device do have its own register space on all three platforms.
But on S4 and V4H it will share its clock and reset property with
RSWITCH or RTSN, respectively.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
* Changes since v3
- Clamp increment calculated to register limitations.
- Check return value of clk_get_rate().
- Disable PM if ptp_clock_register() fails.
---
MAINTAINERS | 1 +
drivers/ptp/Kconfig | 12 ++
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_rcar_gen4.c | 232 ++++++++++++++++++++++++++++++++++++
4 files changed, 246 insertions(+)
create mode 100644 drivers/ptp/ptp_rcar_gen4.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ef17128d6f3f..4a387623409b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22906,6 +22906,7 @@ M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
L: linux-renesas-soc@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
+F: drivers/ptp/ptp_rcar_gen4.c
RENESAS R-CAR GYROADC DRIVER
M: Marek Vasut <marek.vasut+renesas@mailbox.org>
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index b93640ca08b7..3593fd9da92a 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -263,4 +263,16 @@ config PTP_NETC_V4_TIMER
synchronization. It also supports periodic output signal (e.g. PPS)
and external trigger timestamping.
+config PTP_RCAR_GEN4
+ tristate "Renesas R-Car Gen4 PTP Driver"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on PTP_1588_CLOCK
+ help
+ This driver adds support for using the Renesas R-Car Gen4 gPTP timer
+ as a PTP clock, the clock can then be used by Gen4 Ethernet drivers
+ for PTP time synchronization.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ptp_rcar_gen4.
+
endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index bdc47e284f14..0464a586bed2 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_OCP) += ptp_ocp.o
obj-$(CONFIG_PTP_DFL_TOD) += ptp_dfl_tod.o
obj-$(CONFIG_PTP_S390) += ptp_s390.o
obj-$(CONFIG_PTP_NETC_V4_TIMER) += ptp_netc.o
+obj-$(CONFIG_PTP_RCAR_GEN4) += ptp_rcar_gen4.o
diff --git a/drivers/ptp/ptp_rcar_gen4.c b/drivers/ptp/ptp_rcar_gen4.c
new file mode 100644
index 000000000000..0d862849cd4c
--- /dev/null
+++ b/drivers/ptp/ptp_rcar_gen4.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Renesas R-Car Gen4 gPTP device driver
+ *
+ * Copyright (C) 2026 Renesas Electronics Corporation
+ * Copyright (C) 2026 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/types.h>
+
+#define PTPTMEC_REG 0x0010
+#define PTPTMDC_REG 0x0014
+#define PTPTIVC0_REG 0x0020
+#define PTPTOVC00_REG 0x0030
+#define PTPTOVC10_REG 0x0034
+#define PTPTOVC20_REG 0x0038
+#define PTPGPTPTM00_REG 0x0050
+#define PTPGPTPTM10_REG 0x0054
+#define PTPGPTPTM20_REG 0x0058
+
+struct ptp_rcar_gen4_priv {
+ void __iomem *base;
+ struct clk *clk;
+
+ struct ptp_clock *clock;
+ struct ptp_clock_info info;
+
+ spinlock_t lock; /* Registers access. */
+ s64 default_addend;
+};
+
+#define ptp_to_priv(ptp) container_of(ptp, struct ptp_rcar_gen4_priv, info)
+
+static int ptp_rcar_gen4_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+ s64 addend = priv->default_addend;
+ bool neg_adj = scaled_ppm < 0;
+ unsigned long flags;
+ s64 diff;
+
+ if (neg_adj)
+ scaled_ppm = -scaled_ppm;
+ diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC);
+ addend = neg_adj ? addend - diff : addend + diff;
+
+ /* Clamp value to register limits, defined as in nanoseconds.
+ * bit[31:27] - integer
+ * bit[26:0] - decimal
+ */
+ addend = clamp_val(addend, 0, UINT_MAX);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ iowrite32(addend, priv->base + PTPTIVC0_REG);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void _ptp_rcar_gen4_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+
+ lockdep_assert_held(&priv->lock);
+
+ ts->tv_nsec = ioread32(priv->base + PTPGPTPTM00_REG);
+ ts->tv_sec = ioread32(priv->base + PTPGPTPTM10_REG) |
+ ((s64)ioread32(priv->base + PTPGPTPTM20_REG) << 32);
+}
+
+static int ptp_rcar_gen4_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ _ptp_rcar_gen4_gettime(ptp, ts);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void _ptp_rcar_gen4_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+
+ lockdep_assert_held(&priv->lock);
+
+ iowrite32(1, priv->base + PTPTMDC_REG);
+ iowrite32(0, priv->base + PTPTOVC20_REG);
+ iowrite32(0, priv->base + PTPTOVC10_REG);
+ iowrite32(0, priv->base + PTPTOVC00_REG);
+ iowrite32(1, priv->base + PTPTMEC_REG);
+ iowrite32(ts->tv_sec >> 32, priv->base + PTPTOVC20_REG);
+ iowrite32(ts->tv_sec, priv->base + PTPTOVC10_REG);
+ iowrite32(ts->tv_nsec, priv->base + PTPTOVC00_REG);
+}
+
+static int ptp_rcar_gen4_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ _ptp_rcar_gen4_settime(ptp, ts);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int ptp_rcar_gen4_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+ struct timespec64 ts;
+ unsigned long flags;
+ s64 now;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ _ptp_rcar_gen4_gettime(ptp, &ts);
+ now = ktime_to_ns(timespec64_to_ktime(ts));
+ ts = ns_to_timespec64(now + delta);
+ _ptp_rcar_gen4_settime(ptp, &ts);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static struct ptp_clock_info ptp_rcar_gen4_info = {
+ .owner = THIS_MODULE,
+ .name = "R-Car Gen4 gPTP",
+ .max_adj = 50000000,
+ .adjfine = ptp_rcar_gen4_adjfine,
+ .adjtime = ptp_rcar_gen4_adjtime,
+ .gettime64 = ptp_rcar_gen4_gettime,
+ .settime64 = ptp_rcar_gen4_settime,
+};
+
+static int ptp_rcar_gen4_probe(struct platform_device *pdev)
+{
+ struct ptp_rcar_gen4_priv *priv;
+ struct device *dev = &pdev->dev;
+ unsigned long rate;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ rate = clk_get_rate(priv->clk);
+ if (!rate)
+ return -ENODEV;
+
+ spin_lock_init(&priv->lock);
+
+ priv->info = ptp_rcar_gen4_info;
+
+ /* Default timer increment in ns.
+ * bit[31:27] - integer
+ * bit[26:0] - decimal
+ * increment[ns] = perid[ns] * 2^27 => (1ns * 2^27) / rate[hz]
+ */
+
+ priv->default_addend = div_s64(1000000000LL << 27, rate);
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ iowrite32(priv->default_addend, priv->base + PTPTIVC0_REG);
+ iowrite32(1, priv->base + PTPTMEC_REG);
+
+ priv->clock = ptp_clock_register(&priv->info, dev);
+ if (IS_ERR(priv->clock)) {
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ return PTR_ERR(priv->clock);
+ }
+
+ return 0;
+}
+
+static void ptp_rcar_gen4_remove(struct platform_device *pdev)
+{
+ struct ptp_rcar_gen4_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ ptp_clock_unregister(priv->clock);
+
+ iowrite32(1, priv->base + PTPTMDC_REG);
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+}
+
+static const struct of_device_id ptp_rcar_gen4_of_match[] = {
+ { .compatible = "renesas,rcar-gen4-gptp", },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ptp_rcar_gen4_of_match);
+
+static struct platform_driver ptp_rcar_gen4_driver = {
+ .driver = {
+ .name = "ptp-rcar-gen4",
+ .of_match_table = ptp_rcar_gen4_of_match,
+ },
+ .probe = ptp_rcar_gen4_probe,
+ .remove = ptp_rcar_gen4_remove,
+};
+module_platform_driver(ptp_rcar_gen4_driver);
+
+MODULE_AUTHOR("Niklas Söderlund");
+MODULE_DESCRIPTION("Renesas R-Car Gen4 gPTP driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v4 3/3] arm64: dts: renesas: r8a779g0: Add gPTP node
From: Niklas Söderlund @ 2026-07-02 12:55 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
Cc: Niklas Söderlund
In-Reply-To: <20260702125525.2230427-1-niklas.soderlund+renesas@ragnatech.se>
The gPTP module is shared between the RAVB and RTSN Ethernet devices on
the SoC.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
* Changes since v2
- Preserve sort order by unit-address.
* Changes since v1
- Rename node ptp.
---
arch/arm64/boot/dts/renesas/r8a779g0.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
index 82a7278836e5..b9b860ef7035 100644
--- a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
@@ -589,6 +589,15 @@ tmu4: timer@ffc00000 {
status = "disabled";
};
+ gptp: ptp@e6449000 {
+ compatible = "renesas,r8a779g0-gptp", "renesas,rcar-gen4-gptp";
+ reg = <0 0xe6449000 0 0x500>;
+ clocks = <&cpg CPG_MOD 2723>;
+ power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
+ resets = <&cpg 2723>;
+ status = "disabled";
+ };
+
tsn0: ethernet@e6460000 {
compatible = "renesas,r8a779g0-ethertsn", "renesas,rcar-gen4-ethertsn";
reg = <0 0xe6460000 0 0x7000>,
--
2.55.0
^ permalink raw reply related
* Re: [PATCH v6] net: mvneta_bm: add suspend/resume support to prevent crash after resume
From: Paolo Abeni @ 2026-07-02 13:00 UTC (permalink / raw)
To: Yun Zhou, marcin.s.wojtas, andrew+netdev, davem, edumazet, kuba
Cc: netdev, linux-kernel
In-Reply-To: <20260630060311.4072140-1-yun.zhou@windriver.com>
On 6/30/26 8:03 AM, Yun Zhou wrote:
> The mvneta driver uses the hardware Buffer Manager (BM) for RX buffer
> allocation. During suspend, mvneta disables its clock, causing BM to
> lose all buffer address state. On resume, mvneta_bm_port_init() re-
> attaches the BM pool to the NIC, but BM hardware returns stale/garbage
> buffer addresses. When NAPI poll processes these buffers, DMA cache
> sync hits an invalid virtual address causing a kernel panic:
>
> Unable to handle kernel paging request at virtual address b0000080
> PC is at v7_dma_inv_range
> Call trace:
> v7_dma_inv_range from arch_sync_dma_for_cpu+0x94/0x158
> arch_sync_dma_for_cpu from __dma_sync_single_for_cpu+0xc4/0x15c
> __dma_sync_single_for_cpu from mvneta_rx_swbm+0x6c8/0xf48
> mvneta_rx_swbm from mvneta_poll+0x6fc/0x70c
> mvneta_poll from __napi_poll.constprop.0+0x2c/0x1e0
> __napi_poll.constprop.0 from net_rx_action+0x160/0x2c4
> net_rx_action from handle_softirqs+0xd8/0x2b8
> handle_softirqs from run_ksoftirqd+0x30/0x94
> run_ksoftirqd from smpboot_thread_fn+0x100/0x204
> smpboot_thread_fn from kthread+0xf4/0x110
> kthread from ret_from_fork+0x14/0x28
>
> Fix by adding suspend/resume callbacks to the BM driver:
>
> - suspend: drain all buffers (with DMA unmapping), free the BPPE
> regions, and reset pool state to FREE before stopping BM and gating
> the clock.
>
> - resume: enable the clock, reinitialize BM defaults, and restore pool
> read/write pointers and size registers. Pool allocation and buffer
> refill are handled by mvneta_resume() through the normal
> mvneta_bm_port_init() path, which sees pools as FREE and performs
> full initialization identical to probe.
>
> Add a device_link (DL_FLAG_AUTOREMOVE_CONSUMER) in mvneta_probe to
> guarantee BM resumes before mvneta and suspends after mvneta. If the
> link cannot be created, fall back to SW buffer management to avoid a
> potential crash on resume due to unordered PM transitions.
>
> Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
Sashiko gemini has found a bunch of pre-existing issues; it would be
nice if you could follow-up on them:
https://sashiko.dev/#/patchset/20260630060311.4072140-1-yun.zhou%40windriver.com
/P
^ permalink raw reply
* Re: [PATCH v6] net: mvneta_bm: add suspend/resume support to prevent crash after resume
From: patchwork-bot+netdevbpf @ 2026-07-02 13:10 UTC (permalink / raw)
To: Zhou, Yun
Cc: marcin.s.wojtas, andrew+netdev, davem, edumazet, kuba, pabeni,
netdev, linux-kernel
In-Reply-To: <20260630060311.4072140-1-yun.zhou@windriver.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 30 Jun 2026 14:03:11 +0800 you wrote:
> The mvneta driver uses the hardware Buffer Manager (BM) for RX buffer
> allocation. During suspend, mvneta disables its clock, causing BM to
> lose all buffer address state. On resume, mvneta_bm_port_init() re-
> attaches the BM pool to the NIC, but BM hardware returns stale/garbage
> buffer addresses. When NAPI poll processes these buffers, DMA cache
> sync hits an invalid virtual address causing a kernel panic:
>
> [...]
Here is the summary with links:
- [v6] net: mvneta_bm: add suspend/resume support to prevent crash after resume
https://git.kernel.org/netdev/net-next/c/140be217df57
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v2 1/2] octeontx2-af: reserve 4 PKINDs for skip-size custom use
From: Paolo Abeni @ 2026-07-02 13:11 UTC (permalink / raw)
To: nshettyj, netdev, linux-kernel
Cc: sgoutham, lcherian, gakula, hkelam, sbhatta, andrew+netdev, davem,
edumazet, kuba, Kiran Kumar K
In-Reply-To: <20260630062145.2533816-2-nshettyj@marvell.com>
On 6/30/26 8:21 AM, nshettyj@marvell.com wrote:
> @@ -4218,6 +4248,12 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
> shift_dir);
> if (rc)
> return rc;
> + } else if (pkind >= NPC_RX_SKIP_SIZE_PKIND &&
> + pkind <= NPC_RX_SKIP_SIZE_PKIND + 3) {
> + rc = npc_set_skip_size_pkind(rvu, pcifunc, pkind,
> + skip_size);
Both sashikos noted that the driver allows unprivileged VFs to modify
global configuration.
Please follow-up on that issue
/P
^ permalink raw reply
* Re: [External Mail] Re: [PATCH v3 2/7] net: wwan: t9xx: Add control plane transaction layer
From: Andrew Lunn @ 2026-07-02 13:17 UTC (permalink / raw)
To: Wu. JackBB (GSM)
Cc: Loic Poulain, Sergey Ryazanov, Johannes Berg, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Wen-Zhi Huang, Shi-Wei Yeh, Minano Tseng, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, Jonathan Corbet,
Shuah Khan, linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <4ec081f8df234cc584702abc67213965@compal.com>
> We will also remove all unnecessary devm_kfree() calls from probe
> error paths and remove paths, keeping them only where resources
> are freed and re-allocated at runtime (e.g., CLDMA queue lifecycle
> during modem reset cycles).
There is no point using devm_ if you are going to manually manage
their release. Anything which has a shorter lifetime than the device
should use kzalloc()/kfree().
Andrew
^ permalink raw reply
* Re: [PATCH net-next] net/mlx5e: MACsec: annotate context list traversals
From: Tariq Toukan @ 2026-07-02 13:19 UTC (permalink / raw)
To: Runyu Xiao, borisp, saeedm, leon, tariqt, mbloch
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sd, dtatulea,
cjubran, horms, jianbol, netdev, linux-rdma, linux-kernel,
jianhao.xu
In-Reply-To: <20260701124030.3208833-1-runyu.xiao@seu.edu.cn>
On 01/07/2026 15:40, Runyu Xiao wrote:
> The MACsec offload control paths take macsec->lock before looking up
> MACsec device and RX SC contexts. The lookup helpers walk RCU lists, but
> the iterators do not currently pass the mutex condition, so
> CONFIG_PROVE_RCU_LIST cannot see the existing writer/control-path
> protection.
>
> Pass lockdep_is_held(&macsec->lock) to the list iterators in the MACsec
> lookup helpers. The RX SC helper does not otherwise need the MACsec
> context, so pass it in only to express the existing lockdep condition.
>
> This was found by our static analysis tool and then manually reviewed
> against the current tree. The dynamic triage evidence is a
> target-matched CONFIG_PROVE_RCU_LIST warning; the change is limited
> to documenting the existing protection contract.
>
> This is a lockdep annotation cleanup. It does not change MACsec context
> lifetime or list updates.
>
> Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
> ---
> .../mellanox/mlx5/core/en_accel/macsec.c | 23 +++++++++++--------
> 1 file changed, 13 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
> index 528b04d4de41..3028e327e36d 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
> @@ -405,11 +405,13 @@ static int mlx5e_macsec_init_sa(struct macsec_context *ctx,
> }
>
> static struct mlx5e_macsec_rx_sc *
> -mlx5e_macsec_get_rx_sc_from_sc_list(const struct list_head *list, sci_t sci)
> +mlx5e_macsec_get_rx_sc_from_sc_list(struct mlx5e_macsec *macsec,
> + const struct list_head *list, sci_t sci)
> {
> struct mlx5e_macsec_rx_sc *iter;
>
> - list_for_each_entry_rcu(iter, list, rx_sc_list_element) {
> + list_for_each_entry_rcu(iter, list, rx_sc_list_element,
> + lockdep_is_held(&macsec->lock)) {
> if (iter->sci == sci)
> return iter;
> }
> @@ -473,14 +475,15 @@ static bool mlx5e_macsec_secy_features_validate(struct macsec_context *ctx)
> }
>
> static struct mlx5e_macsec_device *
> -mlx5e_macsec_get_macsec_device_context(const struct mlx5e_macsec *macsec,
> +mlx5e_macsec_get_macsec_device_context(struct mlx5e_macsec *macsec,
Looking at the changes below, I don't find the const removal necessary.
> const struct macsec_context *ctx)
> {
> struct mlx5e_macsec_device *iter;
> const struct list_head *list;
>
> list = &macsec->macsec_device_list_head;
> - list_for_each_entry_rcu(iter, list, macsec_device_list_element) {
> + list_for_each_entry_rcu(iter, list, macsec_device_list_element,
> + lockdep_is_held(&macsec->lock)) {
> if (iter->netdev == ctx->secy->netdev)
> return iter;
> }
> @@ -692,7 +695,7 @@ static int mlx5e_macsec_add_rxsc(struct macsec_context *ctx)
> }
>
> rx_sc_list = &macsec_device->macsec_rx_sc_list_head;
> - rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(rx_sc_list, ctx_rx_sc->sci);
> + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(macsec, rx_sc_list, ctx_rx_sc->sci);
> if (rx_sc) {
> netdev_err(ctx->netdev, "MACsec offload: rx_sc (sci %lld) already exists\n",
> ctx_rx_sc->sci);
> @@ -775,7 +778,7 @@ static int mlx5e_macsec_upd_rxsc(struct macsec_context *ctx)
> }
>
> list = &macsec_device->macsec_rx_sc_list_head;
> - rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, ctx_rx_sc->sci);
> + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(macsec, list, ctx_rx_sc->sci);
> if (!rx_sc) {
> err = -EINVAL;
> goto out;
> @@ -853,7 +856,7 @@ static int mlx5e_macsec_del_rxsc(struct macsec_context *ctx)
> }
>
> list = &macsec_device->macsec_rx_sc_list_head;
> - rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, ctx->rx_sc->sci);
> + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(macsec, list, ctx->rx_sc->sci);
> if (!rx_sc) {
> netdev_err(ctx->netdev,
> "MACsec offload rx_sc sci %lld doesn't exist\n",
> @@ -894,7 +897,7 @@ static int mlx5e_macsec_add_rxsa(struct macsec_context *ctx)
> }
>
> list = &macsec_device->macsec_rx_sc_list_head;
> - rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, sci);
> + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(macsec, list, sci);
> if (!rx_sc) {
> netdev_err(ctx->netdev,
> "MACsec offload rx_sc sci %lld doesn't exist\n",
> @@ -978,7 +981,7 @@ static int mlx5e_macsec_upd_rxsa(struct macsec_context *ctx)
> }
>
> list = &macsec_device->macsec_rx_sc_list_head;
> - rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, sci);
> + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(macsec, list, sci);
> if (!rx_sc) {
> netdev_err(ctx->netdev,
> "MACsec offload rx_sc sci %lld doesn't exist\n",
> @@ -1035,7 +1038,7 @@ static int mlx5e_macsec_del_rxsa(struct macsec_context *ctx)
> }
>
> list = &macsec_device->macsec_rx_sc_list_head;
> - rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, sci);
> + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(macsec, list, sci);
> if (!rx_sc) {
> netdev_err(ctx->netdev,
> "MACsec offload rx_sc sci %lld doesn't exist\n",
^ permalink raw reply
* Re: [PATCH net-next 0/2] octeontx2-af: NPC parser and RSS improvements
From: patchwork-bot+netdevbpf @ 2026-07-02 13:20 UTC (permalink / raw)
To: Nitin Shetty J
Cc: netdev, linux-kernel, sgoutham, lcherian, gakula, hkelam, sbhatta,
andrew+netdev, davem, edumazet, kuba, pabeni, kirankumark
In-Reply-To: <20260630062145.2533816-1-nshettyj@marvell.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 30 Jun 2026 11:51:43 +0530 you wrote:
> From: Kiran Kumar K <kirankumark@marvell.com>
>
> This series extends the Marvell OcteonTX2 admin-function driver with two
> improvements to the NPC (Network Parser CAM) block. The NPC parses packets
> received by or transmitted from the NIX, and its matching CAM (MCAM)
> selects which VFs, queues, or output ports handle each packet.
>
> [...]
Here is the summary with links:
- [v2,1/2] octeontx2-af: reserve 4 PKINDs for skip-size custom use
https://git.kernel.org/netdev/net-next/c/5ba5611ef946
- [v2,2/2] octeontx2-af: Add RSS hashing support based on RoCEv2 header
https://git.kernel.org/netdev/net-next/c/961db18e28d0
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net] net: mdio: select REGMAP_MMIO instead of depending on it
From: Andrew Lunn @ 2026-07-02 13:26 UTC (permalink / raw)
To: Rosen Penev
Cc: netdev, Heiner Kallweit, Russell King, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Tianchen Ding,
open list
In-Reply-To: <20260702032653.1580616-1-rosenp@gmail.com>
On Wed, Jul 01, 2026 at 08:26:52PM -0700, Rosen Penev wrote:
> REGMAP_MMIO is a hidden (non-user-visible) tristate symbol. Using
> depends on it is incorrect because there is no way for the user to
> enable it directly. Change to select, which is the convention used
> by every other driver in the tree that needs REGMAP_MMIO.
>
> Fixes: 8057cbb8335c ("net: mdio: mscc-miim: Add depend of REGMAP_MMIO on MDIO_MSCC_MIIM")
> Assisted-by: opencode:big-pickle
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* Re: [PATCH net-next v7 2/3] net: airoha: fix ETS QoS stats counter underflow and cross-channel corruption
From: Lorenzo Bianconi @ 2026-07-02 13:31 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev
In-Reply-To: <20260701-airoha-ethtool-priv_flags-v7-2-b4153bd44428@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 4323 bytes --]
> airoha_qdma_get_tx_ets_stats() has two bugs:
> - The hardware counters read via airoha_qdma_rr() are 32-bit values
> but are stored in u64 locals and subtracted from u64 baselines. When
> a 32-bit hardware counter wraps around, the subtraction produces a
> large underflow value passed to _bstats_update().
> - The baseline counters (cpu_tx_packets, fwd_tx_packets) are stored as
> single per-device fields, but airoha_qdma_get_tx_ets_stats() is
> called with different channel values (0-3). Each call reads a
> different channel's hardware counter but overwrites the same
> baseline, corrupting the delta computation for other channels.
>
> Fix both by:
> - Narrowing the counter locals and baselines to u32 so that 32-bit
> unsigned subtraction handles wrap-around naturally.
> - Grouping the baselines into a per-channel qos_stats array so each
> channel tracks its own previous counter value independently.
>
> Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support")
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
commenting on sashiko's report:
https://sashiko.dev/#/patchset/20260701-airoha-ethtool-priv_flags-v7-0-b4153bd44428%40kernel.org
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 18 +++++++++++-------
> drivers/net/ethernet/airoha/airoha_eth.h | 7 ++++---
> 2 files changed, 15 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 8bba54ebcf07..2c9ceb9f16f8 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -2491,16 +2491,20 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> struct airoha_qdma *qdma = dev->qdma;
> + u32 cpu_tx_packets, fwd_tx_packets;
> + u64 tx_packets;
>
> - u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
> - u64 fwd_tx_packets = airoha_qdma_rr(qdma,
> - REG_CNTR_VAL((channel << 1) + 1));
> - u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
> - (fwd_tx_packets - dev->fwd_tx_packets);
> + cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
> + fwd_tx_packets = airoha_qdma_rr(qdma,
> + REG_CNTR_VAL((channel << 1) + 1));
> + tx_packets = (u32)(cpu_tx_packets -
> + dev->qos_stats[channel].cpu_tx_packets) +
> + (u32)(fwd_tx_packets -
> + dev->qos_stats[channel].fwd_tx_packets);
- Will this addition overflow in 32-bit space before the result is assigned to
the 64-bit tx_packets?
- I do not think this is a problem since we are just considering the delta
betwen cpu_tx_packets/fwd_tx_packets and the previous value. Moreover, the
u32 cast will take care of possible wrap-around.
>
> _bstats_update(opt->stats.bstats, 0, tx_packets);
- This isn't a bug introduced by this patch, but does calling _bstats_update()
here directly from process context race with the software datapath?
- Sashiko is right here. This is a pre-existing (theoretical) issue not
introduced by this patch. However, since the Airoha EN7581/EN7583 is
ARM64-only, u64_stats_update_begin/end are NOPs on this platform and
there is no actual race. IIUC the seqcount corruption scenario only
applies to 32-bit architectures. I guess we can
Regards,
Lorenzo
> - dev->cpu_tx_packets = cpu_tx_packets;
> - dev->fwd_tx_packets = fwd_tx_packets;
> + dev->qos_stats[channel].cpu_tx_packets = cpu_tx_packets;
> + dev->qos_stats[channel].fwd_tx_packets = fwd_tx_packets;
>
> return 0;
> }
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index 87ab3ea10664..ac5f571f3e53 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -545,9 +545,10 @@ struct airoha_gdm_dev {
> struct airoha_eth *eth;
>
> DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
> - /* qos stats counters */
> - u64 cpu_tx_packets;
> - u64 fwd_tx_packets;
> + struct {
> + u32 cpu_tx_packets;
> + u32 fwd_tx_packets;
> + } qos_stats[AIROHA_NUM_QOS_CHANNELS];
>
> u32 flags;
> int nbq;
>
> --
> 2.54.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH RFC v2 0/9] leds: Add support for hardware-initiated hardware control trigger transition
From: Lee Jones @ 2026-07-02 13:41 UTC (permalink / raw)
To: Rong Zhang
Cc: Pavel Machek, Jonathan Corbet, Shuah Khan, Thomas Weißschuh,
Benson Leung, Guenter Roeck, Marek Behún, Mark Pearson,
Derek J. Clark, Hans de Goede, Ilpo Järvinen, Ike Panhc,
Andrew Lunn, Jakub Kicinski, Vishnu Sankar, Vishnu Sankar,
linux-leds, netdev, linux-doc, linux-kernel, chrome-platform,
platform-driver-x86
In-Reply-To: <20260618-leds-trigger-hw-changed-v2-0-c28c44053cf3@rong.moe>
On Thu, 18 Jun 2026, Rong Zhang wrote:
> Some laptops can tune their keyboard backlight according to ambient
> light sensors (auto mode). This capability is essentially a hardware
> control trigger. Meanwhile, such laptops also offer a shrotcut for
> cycling through brightness levels and auto mode. For example, on
> ThinkBook, pressing Fn+Space cycles keyboard backlight levels in the
> following sequence:
So we're effectively lifting something out of netdev and making it more
generic. I don't generally have an issue with this, but the idea will
need more eyes on before I feel confident enough to review (for my
own little quirks) and merge it.
--
Lee Jones
^ permalink raw reply
* Re: [PATCH 2/4] ice: use kzalloc() to allocate staging buffer for reading from GNSS
From: Przemek Kitszel @ 2026-07-02 13:49 UTC (permalink / raw)
To: Mike Rapoport (Microsoft), Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Manish Chopra, Paolo Abeni
Cc: Edward Cree, Sudarsana Kalluru, Tony Nguyen, intel-wired-lan,
linux-kernel, linux-mm, linux-net-drivers, netdev
In-Reply-To: <20260701-b4-drivers-ethernet-v1-2-58776615db6e@kernel.org>
On 7/1/26 15:57, Mike Rapoport (Microsoft) wrote:
> ice_gnss_read() uses get_zeroed_page() to allocate a staging buffer for
> reading GNSS module data via I2C bus.
>
> This buffer can be allocated with kmalloc() as there's nothing special
> about it to go directly to the page allocator.
>
> kmalloc() provides a better API that does not require ugly casts and
> kfree() does not need to know the size of the freed object.
>
> Performance difference between kmalloc() and __get_free_pages() is not
> measurable as both allocators take an object/page from a per-CPU list for
> fast path allocations.
>
> For the slow path the performance is anyway determined by the amount of
> reclaim involved rather than by what allocator is used.
>
> Replace use of get_zeroed_page() with kzalloc() and free_page() with
> kfree().
>
> Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> ---
> drivers/net/ethernet/intel/ice/ice_gnss.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_gnss.c b/drivers/net/ethernet/intel/ice/ice_gnss.c
> index 8fd954f1ebd6..7d21c3417b0b 100644
> --- a/drivers/net/ethernet/intel/ice/ice_gnss.c
> +++ b/drivers/net/ethernet/intel/ice/ice_gnss.c
> @@ -2,6 +2,7 @@
> /* Copyright (C) 2021-2022, Intel Corporation. */
>
> #include "ice.h"
> +#include <linux/slab.h>
> #include "ice_lib.h"
>
> /**
> @@ -124,7 +125,7 @@ static void ice_gnss_read(struct kthread_work *work)
>
> data_len = min_t(typeof(data_len), data_len, PAGE_SIZE);
>
> - buf = (char *)get_zeroed_page(GFP_KERNEL);
> + buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
nit:
from the code it is clear that we read at most a page, and @data_len
stores the actual amount needed
comment:
I don't know why we limit to a page, it's outside of the scope of this
series, but likely you have removed the limit (which will go into the
loop - single AQ call is likely limited by a PAGE too).
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
> if (!buf) {
> err = -ENOMEM;
> goto requeue;
> @@ -151,7 +152,7 @@ static void ice_gnss_read(struct kthread_work *work)
> count, i);
> delay = ICE_GNSS_TIMER_DELAY_TIME;
> free_buf:
> - free_page((unsigned long)buf);
> + kfree(buf);
> requeue:
> kthread_queue_delayed_work(gnss->kworker, &gnss->read_work, delay);
> if (err)
>
^ permalink raw reply
* Re: [PATCH 1/4] bnx2x: use kzalloc() to allocate mac filtering list
From: Przemek Kitszel @ 2026-07-02 13:52 UTC (permalink / raw)
To: Mike Rapoport (Microsoft), Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Manish Chopra, Paolo Abeni
Cc: Edward Cree, Sudarsana Kalluru, Tony Nguyen, intel-wired-lan,
linux-kernel, linux-mm, linux-net-drivers, netdev
In-Reply-To: <20260701-b4-drivers-ethernet-v1-1-58776615db6e@kernel.org>
> @@ -2713,8 +2714,7 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
> total_elems = BNX2X_MCAST_BINS_NUM;
> }
> while (total_elems > 0) {
> - elem_group = (struct bnx2x_mcast_elem_group *)
> - __get_free_page(GFP_ATOMIC | __GFP_ZERO);
> + elem_group = kzalloc(PAGE_SIZE, GFP_ATOMIC);
what is the current rule of thumb for kzalloc vs kvzalloc size under
GFP_ATOMIC?
> if (!elem_group) {
> bnx2x_free_groups(&new_cmd->group_head);
> kfree(new_cmd);
>
^ permalink raw reply
* Re: [PATCH net-next v7 3/3] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload
From: Lorenzo Bianconi @ 2026-07-02 13:51 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev, Madhur Agrawal
In-Reply-To: <20260701-airoha-ethtool-priv_flags-v7-3-b4153bd44428@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 21222 bytes --]
> GDM3 and GDM4 ports require GDM2 loopback to be enabled for hardware
> QoS offload to function. Without it, HTB and ETS offload on these ports
> do not work.
> Previously, GDM3/GDM4 ports were automatically configured as WAN with
> GDM2 loopback enabled during ndo_init(). Add the capability to configure
> GDM3/GDM4 as WAN/LAN on demand when QoS offload is created or destroyed.
> Hook airoha_enable_qos_for_gdm34() into TC_HTB_CREATE so that requesting
> HTB offload on a GDM3/GDM4 LAN port switches it to WAN mode and enables
> GDM2 loopback, with proper rollback on failure. Introduce the
> AIROHA_DEV_F_QOS flag to track whether a device has an active HTB
> qdisc; clear it on TC_HTB_DESTROY. The device keeps its WAN role after
> qdisc teardown so that its configuration is preserved until another
> device explicitly needs the WAN role for QoS offload.
> If another GDM3/GDM4 device already holds the WAN role without an active
> QoS qdisc, demote it to LAN before promoting the requesting device. Skip
> the demotion when the requesting device is itself already the WAN device.
> Since airoha_dev_set_qdma() can now be called on a running device to
> migrate between QDMA blocks, make dev->qdma an RCU pointer so the TX
> path can safely dereference it without holding RTNL.
> Hold flow_offload_mutex in airoha_enable_qos_for_gdm34() and
> airoha_disable_qos_for_gdm34() around the dev->flags update,
> airoha_dev_set_qdma() and GDM2 loopback configuration, serializing
> against concurrent airoha_ppe_hw_init() in the TC_SETUP_CLSFLOWER
> offload path.
> Introduce airoha_qdma_deref() helper that wraps rcu_dereference_protected()
> with a lockdep condition accepting either rtnl_lock or flow_offload_mutex,
> and use it across all control-path dereferences of the RCU-protected
> dev->qdma pointer.
> Add airoha_disable_gdm2_loopback() to disable GDM2 hw loopback.
>
> Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Reviewed-by: Simon Horman <horms@kernel.org>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
commenting on Sashiko's report:
https://sashiko.dev/#/patchset/20260701-airoha-ethtool-priv_flags-v7-0-b4153bd44428%40kernel.org
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 219 ++++++++++++++++++++++++++----
> drivers/net/ethernet/airoha/airoha_eth.h | 13 +-
> drivers/net/ethernet/airoha/airoha_ppe.c | 9 +-
> drivers/net/ethernet/airoha/airoha_regs.h | 1 +
> 4 files changed, 214 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 2c9ceb9f16f8..609a5ea67fb7 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -929,7 +929,7 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
> if (!dev)
> continue;
>
> - if (dev->qdma != qdma)
> + if (rcu_access_pointer(dev->qdma) != qdma)
> continue;
>
> netdev = netdev_from_priv(dev);
> @@ -1837,13 +1837,14 @@ static int airoha_dev_open(struct net_device *netdev)
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> struct airoha_gdm_port *port = dev->port;
> u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
> - struct airoha_qdma *qdma = dev->qdma;
> + struct airoha_qdma *qdma;
>
> netif_tx_start_all_queues(netdev);
> err = airoha_set_vip_for_gdm_port(dev, true);
> if (err)
> return err;
>
> + qdma = airoha_qdma_deref(dev);
> if (netdev_uses_dsa(netdev))
> airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
> GDM_STAG_EN_MASK);
> @@ -1903,7 +1904,6 @@ static int airoha_dev_stop(struct net_device *netdev)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> struct airoha_gdm_port *port = dev->port;
> - struct airoha_qdma *qdma = dev->qdma;
>
> netif_tx_disable(netdev);
> airoha_set_vip_for_gdm_port(dev, false);
> @@ -1911,7 +1911,7 @@ static int airoha_dev_stop(struct net_device *netdev)
> if (--port->users)
> airoha_set_port_mtu(dev->eth, port);
> else
> - airoha_set_gdm_port_fwd_cfg(qdma->eth,
> + airoha_set_gdm_port_fwd_cfg(dev->eth,
> REG_GDM_FWD_CFG(port->id),
> FE_PSE_PORT_DROP);
> return 0;
> @@ -1998,6 +1998,53 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
> return 0;
> }
>
> +static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)
> +{
> + struct airoha_gdm_port *port = dev->port;
> + struct airoha_eth *eth = dev->eth;
> + int i, src_port;
> + u32 pse_port;
> +
> + src_port = eth->soc->ops.get_sport(dev->port, dev->nbq);
> + if (src_port < 0)
> + return src_port;
> +
> + airoha_fe_clear(eth,
> + REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
> + SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK));
> +
> + airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
> + GDM_STRIP_CRC_MASK);
> + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
> + FE_PSE_PORT_DROP);
> + airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
> + LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);
> + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
> + : FE_PSE_PORT_PPE1;
> + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
> + pse_port);
> +
> + airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,
> + FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));
> +
> + for (i = 0; i < eth->soc->num_ppe; i++)
> + airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),
> + DFT_CPORT_MASK(AIROHA_GDM2_IDX));
> +
> + /* Enable VIP and IFC for GDM2 */
> + airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
> + airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
> +
> + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
> + u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
> +
> + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
> + FC_MAP6_DEF_VALUE & mask);
> + }
> +
> + return 0;
> +}
> +
> static struct airoha_gdm_dev *
> airoha_get_wan_gdm_dev(struct airoha_eth *eth)
> {
> @@ -2024,15 +2071,26 @@ airoha_get_wan_gdm_dev(struct airoha_eth *eth)
> static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
> {
> struct net_device *netdev = netdev_from_priv(dev);
> + struct airoha_qdma *cur_qdma, *qdma;
> struct airoha_eth *eth = dev->eth;
> int ppe_id;
>
> /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
> - dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
> - netdev->irq = dev->qdma->irq_banks[0].irq;
> + qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
> + cur_qdma = airoha_qdma_deref(dev);
> +
> + rcu_assign_pointer(dev->qdma, qdma);
> + netdev->irq = qdma->irq_banks[0].irq;
>
> ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
> airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
> +
> + if (!cur_qdma)
> + return;
> +
> + memset(dev->qos_stats, 0, sizeof(dev->qos_stats));
- Will zeroing dev->qos_stats without resetting the free-running hardware counters
cause a massive artificial spike in reported QoS statistics?
- I do not think this issue can occur since we can't enable hw ETS QoS for
GDM3 and GDM4 at the same time. Moreover, this would be just a 'cosmetic'
issue since in the next iteration the driver properly takes care of the
delta.
> + synchronize_rcu();
> + netif_tx_wake_all_queues(netdev);
> }
>
> static int airoha_dev_init(struct net_device *netdev)
> @@ -2187,9 +2245,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> struct net_device *netdev)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_qdma *qdma = dev->qdma;
> u32 nr_frags, tag, msg0, msg1, len;
> struct airoha_queue_entry *e;
> + struct airoha_qdma *qdma;
> struct netdev_queue *txq;
> struct airoha_queue *q;
> LIST_HEAD(tx_list);
> @@ -2198,6 +2256,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> u16 index;
> u8 fport;
- This is a pre-existing issue, but does this function have an out-of-bounds
write to the TCP header for GSO packets?
- This issue has been already reported in the past and we already decided it
can't occur.
>
> + rcu_read_lock();
> + qdma = rcu_dereference(dev->qdma);
> qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
> tag = airoha_get_dsa_tag(skb, netdev);
- This isn't a bug introduced by this patch, but does the hardware QoS offload
use the wrong channel due to incorrect queue ID mapping?
- This issue has been already reported in the past but it can't occur since
the driver implement ndo_select_queue() callback setting skb queue in the range [0,32[.
Regards,
Lorenzo
>
> @@ -2247,6 +2307,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> netif_tx_stop_queue(txq);
> q->txq_stopped = true;
> spin_unlock_bh(&q->lock);
> + rcu_read_unlock();
> +
> return NETDEV_TX_BUSY;
> }
>
> @@ -2309,6 +2371,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
>
> spin_unlock_bh(&q->lock);
> + rcu_read_unlock();
>
> return NETDEV_TX_OK;
>
> @@ -2324,6 +2387,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> error:
> dev_kfree_skb_any(skb);
> netdev->stats.tx_dropped++;
> + rcu_read_unlock();
>
> return NETDEV_TX_OK;
> }
> @@ -2403,17 +2467,19 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
> const u16 *weights, u8 n_weights)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_qdma *qdma;
> int i;
>
> + qdma = airoha_qdma_deref(dev);
> for (i = 0; i < AIROHA_NUM_QOS_QUEUES; i++)
> - airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
> + airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel),
> TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
>
> for (i = 0; i < n_weights; i++) {
> u32 status;
> int err;
>
> - airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG,
> + airoha_qdma_wr(qdma, REG_TXWRR_WEIGHT_CFG,
> TWRR_RW_CMD_MASK |
> FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
> FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
> @@ -2421,12 +2487,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
> err = read_poll_timeout(airoha_qdma_rr, status,
> status & TWRR_RW_CMD_DONE,
> USEC_PER_MSEC, 10 * USEC_PER_MSEC,
> - true, dev->qdma, REG_TXWRR_WEIGHT_CFG);
> + true, qdma, REG_TXWRR_WEIGHT_CFG);
> if (err)
> return err;
> }
>
> - airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3),
> + airoha_qdma_rmw(qdma, REG_CHAN_QOS_MODE(channel >> 3),
> CHAN_QOS_MODE_MASK(channel),
> __field_prep(CHAN_QOS_MODE_MASK(channel), mode));
>
> @@ -2490,10 +2556,11 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
> struct tc_ets_qopt_offload *opt)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_qdma *qdma = dev->qdma;
> u32 cpu_tx_packets, fwd_tx_packets;
> + struct airoha_qdma *qdma;
> u64 tx_packets;
>
> + qdma = airoha_qdma_deref(dev);
> cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
> fwd_tx_packets = airoha_qdma_rr(qdma,
> REG_CNTR_VAL((channel << 1) + 1));
> @@ -2760,16 +2827,18 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
> u32 bucket_size)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_qdma *qdma;
> int i, err;
>
> + qdma = airoha_qdma_deref(dev);
> for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
> - err = airoha_qdma_set_trtcm_config(dev->qdma, channel,
> + err = airoha_qdma_set_trtcm_config(qdma, channel,
> REG_EGRESS_TRTCM_CFG, i,
> !!rate, TRTCM_METER_MODE);
> if (err)
> return err;
>
> - err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel,
> + err = airoha_qdma_set_trtcm_token_bucket(qdma, channel,
> REG_EGRESS_TRTCM_CFG,
> i, rate, bucket_size);
> if (err)
> @@ -2805,11 +2874,12 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
> u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> int err, num_tx_queues = AIROHA_NUM_TX_RING + channel + 1;
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_qdma *qdma = dev->qdma;
> + struct airoha_qdma *qdma;
>
> /* Here we need to check the requested QDMA channel is not already
> * in use by another net_device running on the same QDMA block.
> */
> + qdma = airoha_qdma_deref(dev);
> if (test_and_set_bit(channel, qdma->qos_channel_map)) {
> NL_SET_ERR_MSG_MOD(opt->extack,
> "qdma qos channel already in use");
> @@ -2845,7 +2915,7 @@ static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
> u32 rate, u32 bucket_size,
> enum trtcm_unit_type unit_type)
> {
> - struct airoha_qdma *qdma = dev->qdma;
> + struct airoha_qdma *qdma = airoha_qdma_deref(dev);
> int i;
>
> for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
> @@ -3020,10 +3090,11 @@ static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> int num_tx_queues = AIROHA_NUM_TX_RING;
> - struct airoha_qdma *qdma = dev->qdma;
> + struct airoha_qdma *qdma;
>
> airoha_qdma_set_tx_rate_limit(netdev, queue, 0, 0);
>
> + qdma = airoha_qdma_deref(dev);
> clear_bit(queue, qdma->qos_channel_map);
> clear_bit(queue, dev->qos_sq_bmap);
>
> @@ -3049,6 +3120,95 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
> return 0;
> }
>
> +static void airoha_disable_qos_for_gdm34(struct net_device *netdev)
> +{
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> + int err;
> +
> + if (port->id != AIROHA_GDM3_IDX &&
> + port->id != AIROHA_GDM4_IDX)
> + return;
> +
> + err = airoha_disable_gdm2_loopback(dev);
> + if (err)
> + netdev_warn(netdev,
> + "failed disabling GDM2 loopback: %d\n", err);
> +
> + dev->flags &= ~AIROHA_DEV_F_WAN;
> + airoha_dev_set_qdma(dev);
> +
> + airoha_set_macaddr(dev, netdev->dev_addr);
> + if (netif_running(netdev))
> + airoha_set_gdm_port_fwd_cfg(dev->eth,
> + REG_GDM_FWD_CFG(port->id),
> + FE_PSE_PORT_PPE1);
> +}
> +
> +static int airoha_enable_qos_for_gdm34(struct net_device *netdev,
> + struct netlink_ext_ack *extack)
> +{
> + struct airoha_gdm_dev *wan_dev, *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> + struct airoha_eth *eth = dev->eth;
> + int err = -EBUSY;
> +
> + if (port->id != AIROHA_GDM3_IDX &&
> + port->id != AIROHA_GDM4_IDX) {
> + /* HW QoS is always supported by GDM1 and GDM2 */
> + return 0;
> + }
> +
> + if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */
> + return 0;
> +
> + mutex_lock(&flow_offload_mutex);
> +
> + wan_dev = airoha_get_wan_gdm_dev(eth);
> + if (wan_dev) {
> + if ((wan_dev->flags & AIROHA_DEV_F_QOS) ||
> + wan_dev->port->id == AIROHA_GDM2_IDX) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "QoS configured for WAN device");
> + goto error_unlock;
> + }
> + airoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev));
> + }
> +
> + dev->flags |= AIROHA_DEV_F_WAN;
> + airoha_dev_set_qdma(dev);
> + err = airoha_enable_gdm2_loopback(dev);
> + if (err)
> + goto error_disable_wan;
> +
> + err = airoha_set_macaddr(dev, netdev->dev_addr);
> + if (err)
> + goto error_disable_loopback;
> +
> + if (netif_running(netdev)) {
> + u32 pse_port;
> +
> + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
> + : FE_PSE_PORT_PPE1;
> + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
> + pse_port);
> + }
> +
> + mutex_unlock(&flow_offload_mutex);
> +
> + return 0;
> +
> +error_disable_loopback:
> + airoha_disable_gdm2_loopback(dev);
> +error_disable_wan:
> + dev->flags &= ~AIROHA_DEV_F_WAN;
> + airoha_dev_set_qdma(dev);
> +error_unlock:
> + mutex_unlock(&flow_offload_mutex);
> +
> + return err;
> +}
> +
> static int airoha_tc_htb_destroy(struct net_device *netdev)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> @@ -3057,6 +3217,8 @@ static int airoha_tc_htb_destroy(struct net_device *netdev)
> for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
> airoha_tc_remove_htb_queue(netdev, q);
>
> + dev->flags &= ~AIROHA_DEV_F_QOS;
> +
> return 0;
> }
>
> @@ -3076,24 +3238,33 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
> return 0;
> }
>
> -static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
> +static int airoha_tc_setup_qdisc_htb(struct net_device *netdev,
> struct tc_htb_qopt_offload *opt)
> {
> switch (opt->command) {
> - case TC_HTB_CREATE:
> + case TC_HTB_CREATE: {
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + int err;
> +
> + err = airoha_enable_qos_for_gdm34(netdev, opt->extack);
> + if (err)
> + return err;
> +
> + dev->flags |= AIROHA_DEV_F_QOS;
> break;
> + }
> case TC_HTB_DESTROY:
> - return airoha_tc_htb_destroy(dev);
> + return airoha_tc_htb_destroy(netdev);
> case TC_HTB_NODE_MODIFY:
> - return airoha_tc_htb_modify_queue(dev, opt);
> + return airoha_tc_htb_modify_queue(netdev, opt);
> case TC_HTB_LEAF_ALLOC_QUEUE:
> - return airoha_tc_htb_alloc_leaf_queue(dev, opt);
> + return airoha_tc_htb_alloc_leaf_queue(netdev, opt);
> case TC_HTB_LEAF_DEL:
> case TC_HTB_LEAF_DEL_LAST:
> case TC_HTB_LEAF_DEL_LAST_FORCE:
> - return airoha_tc_htb_delete_leaf_queue(dev, opt);
> + return airoha_tc_htb_delete_leaf_queue(netdev, opt);
> case TC_HTB_LEAF_QUERY_QUEUE:
> - return airoha_tc_get_htb_get_leaf_queue(dev, opt);
> + return airoha_tc_get_htb_get_leaf_queue(netdev, opt);
> default:
> return -EOPNOTSUPP;
> }
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index ac5f571f3e53..a314330fcd48 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -537,11 +537,12 @@ struct airoha_qdma {
>
> enum airoha_dev_flags {
> AIROHA_DEV_F_WAN = BIT(0),
> + AIROHA_DEV_F_QOS = BIT(1),
> };
>
> struct airoha_gdm_dev {
> + struct airoha_qdma __rcu *qdma;
> struct airoha_gdm_port *port;
> - struct airoha_qdma *qdma;
> struct airoha_eth *eth;
>
> DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
> @@ -677,6 +678,16 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev);
> bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
> struct airoha_gdm_dev *dev);
>
> +extern struct mutex flow_offload_mutex;
> +
> +static inline struct airoha_qdma *
> +airoha_qdma_deref(struct airoha_gdm_dev *dev)
> +{
> + return rcu_dereference_protected(dev->qdma,
> + lockdep_rtnl_is_held() ||
> + lockdep_is_held(&flow_offload_mutex));
> +}
> +
> void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
> bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
> void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
> diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
> index 42f4b0f21d17..0f260c50ac3c 100644
> --- a/drivers/net/ethernet/airoha/airoha_ppe.c
> +++ b/drivers/net/ethernet/airoha/airoha_ppe.c
> @@ -15,7 +15,10 @@
> #include "airoha_regs.h"
> #include "airoha_eth.h"
>
> -static DEFINE_MUTEX(flow_offload_mutex);
> +/* Serialize airoha_gdm_dev flags, QDMA pointer and PPE CPU port
> + * configuration.
> + */
> +DEFINE_MUTEX(flow_offload_mutex);
> static DEFINE_SPINLOCK(ppe_lock);
>
> static const struct rhashtable_params airoha_flow_table_params = {
> @@ -86,8 +89,8 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
>
> void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
> {
> - struct airoha_qdma *qdma = dev->qdma;
> - struct airoha_eth *eth = qdma->eth;
> + struct airoha_qdma *qdma = airoha_qdma_deref(dev);
> + struct airoha_eth *eth = dev->eth;
> u8 qdma_id = qdma - ð->qdma[0];
> u32 fe_cpu_port;
>
> diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
> index 436f3c8779c1..4e17dfbcf2b8 100644
> --- a/drivers/net/ethernet/airoha/airoha_regs.h
> +++ b/drivers/net/ethernet/airoha/airoha_regs.h
> @@ -376,6 +376,7 @@
>
> #define REG_SRC_PORT_FC_MAP6 0x2298
> #define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
> +#define FC_MAP6_DEF_VALUE 0x1b1a1918
>
> #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4
>
>
> --
> 2.54.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH net 0/2] bpf, sockmap: fix copied_seq after partial TCP read
From: Dong Chenchen @ 2026-07-02 14:09 UTC (permalink / raw)
To: daniel, edumazet, ncardwell, kuniyu, john.fastabend, jakub,
jiayuan.chen
Cc: davem, kuba, pabeni, horms, zhangchangzhong, netdev, bpf,
Dong Chenchen
tcp_eat_skb() assumes that an skb dequeued by the sockmap verdict path
has not previously been consumed. However, a socket can be inserted
into a sockmap after userspace has partially read the skb at the head of
its receive queue.
When new data invokes the verdict path, tcp_eat_skb() advances
copied_seq by the full skb length. This counts the already-read prefix
twice, moves copied_seq beyond rcv_nxt, and makes later native TCP reads
fail. TCP_ZEROCOPY_RECEIVE then triggers the tcp_recvmsg_locked()
sequence warning reported by syzbot.
TCP recvmsg seq # bug 2: copied AA28C633, seq AA28C601, rcvnxt AA28C602
WARNING: net/ipv4/tcp.c:2745 at tcp_recvmsg_locked
RIP: 0010:tcp_recvmsg_locked (net/ipv4/tcp.c:2745)
Call Trace:
<TASK>
tcp_zerocopy_receive (net/ipv4/tcp.c:1995 net/ipv4/tcp.c:2227)
do_tcp_getsockopt (net/ipv4/tcp.c:4771)
tcp_getsockopt (net/ipv4/tcp.c:4869)
do_sock_getsockopt (net/socket.c:2487)
__sys_getsockopt (net/socket.c:2518)
__x64_sys_getsockopt (net/socket.c:2525 net/socket.c:2522)
do_syscall_64 (arch/x86/entry/syscall_64.c:63)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
Patch 1 advances copied_seq to the skb end sequence and accounts only
the unread sequence-space delta during receive-buffer cleanup.
Patch 2 adds a deterministic regression test which reproduces the
tcp_recvmsg_locked() warning on the unpatched kernel.
Dong Chenchen (2):
bpf, sockmap: account only unread data in tcp_eat_skb
selftests/bpf: cover sockmap drop after partial TCP read
net/ipv4/tcp_bpf.c | 9 ++-
.../selftests/bpf/prog_tests/sockmap_basic.c | 73 +++++++++++++++++++
2 files changed, 78 insertions(+), 4 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH net 1/2] bpf, sockmap: account only unread data in tcp_eat_skb
From: Dong Chenchen @ 2026-07-02 14:09 UTC (permalink / raw)
To: daniel, edumazet, ncardwell, kuniyu, john.fastabend, jakub,
jiayuan.chen
Cc: davem, kuba, pabeni, horms, zhangchangzhong, netdev, bpf,
Dong Chenchen, syzbot+06dbd397158ec0ea4983
In-Reply-To: <20260702140959.1806754-1-dongchenchen2@huawei.com>
tcp_eat_skb() advances copied_seq by the full skb length when a sockmap
verdict drops or redirects an skb. This assumes none of the skb has
previously been consumed.
That assumption does not hold when userspace partially reads an skb
before adding the socket to a sockmap. A later packet invokes the
verdict path, which dequeues the partially consumed skb. Adding its full
length counts the consumed prefix twice and can move copied_seq beyond
rcv_nxt, causing subsequent native TCP reads to fail.
The following sequence reproduces the corruption:
1. TCP receives a 200-byte segment; skb sits on sk_receive_queue.
2. Userspace reads 50 bytes //copied_seq = 50, rcv_nxt = 200
3. Socket is inserted into a sockmap with an SK_DROP verdict.
4. A 1-byte segment arrives and tcp_try_coalesce() merges it with the
existing skb. //skb->len = 201, copied_seq = 50, rcv_nxt = 201
5. The verdict path calls tcp_eat_skb(), which does:
copied_seq += skb->len; // copied_seq = 251, rcv_nxt = 201
This counts the 50 already-read bytes again.
6. After removing the socket from the map, native receive triggers the
sequence warning:
TCP recvmsg seq # bug 2: copied AA28C633, seq AA28C601,
rcvnxt AA28C602, fl 40
WARNING: net/ipv4/tcp.c:2745 at tcp_recvmsg_locked+0x45e/0x9f0
Fix tcp_eat_skb() to advance copied_seq to the skb TCP end sequence and
pass only the distance from the old copied_seq to end_seq to
__tcp_cleanup_rbuf().
Fixes: e5c6de5fa025 ("bpf, sockmap: Incorrectly handling copied_seq")
Reported-by: syzbot+06dbd397158ec0ea4983@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=06dbd397158ec0ea4983
Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com>
---
net/ipv4/tcp_bpf.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index cc0bd73f36b6..d640f8e06529 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -15,7 +15,7 @@
void tcp_eat_skb(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tcp;
- int copied;
+ u32 end_seq, delta;
if (!skb || !skb->len || !sk_is_tcp(sk))
return;
@@ -24,10 +24,11 @@ void tcp_eat_skb(struct sock *sk, struct sk_buff *skb)
return;
tcp = tcp_sk(sk);
- copied = tcp->copied_seq + skb->len;
- WRITE_ONCE(tcp->copied_seq, copied);
+ end_seq = TCP_SKB_CB(skb)->end_seq;
+ delta = end_seq - tcp->copied_seq;
+ WRITE_ONCE(tcp->copied_seq, end_seq);
tcp_rcv_space_adjust(sk);
- __tcp_cleanup_rbuf(sk, skb->len);
+ __tcp_cleanup_rbuf(sk, delta);
}
static int bpf_tcp_ingress(struct sock *sk, struct sk_psock *psock,
--
2.43.0
^ permalink raw reply related
* [PATCH net 2/2] selftests/bpf: cover sockmap drop after partial TCP read
From: Dong Chenchen @ 2026-07-02 14:09 UTC (permalink / raw)
To: daniel, edumazet, ncardwell, kuniyu, john.fastabend, jakub,
jiayuan.chen
Cc: davem, kuba, pabeni, horms, zhangchangzhong, netdev, bpf,
Dong Chenchen
In-Reply-To: <20260702140959.1806754-1-dongchenchen2@huawei.com>
Add a regression test for a TCP socket that is partially read before it
is inserted into a sockmap with an SK_DROP verdict.
The test leaves part of the original skb on the receive queue, adds the
socket to the map, and sends another byte to drive the verdict path.
After removing the socket from the map, it invokes TCP_ZEROCOPY_RECEIVE
on newly arrived native data.
Without the tcp_eat_skb() fix, copied_seq includes the already consumed
prefix, TCP_ZEROCOPY_RECEIVE triggers the sequence warning, and no data
is copied. With the fix, the new byte is copied without a warning.
Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com>
---
.../selftests/bpf/prog_tests/sockmap_basic.c | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
index cb3229711f93..106dd03cde84 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
@@ -1275,6 +1275,77 @@ static void test_sockmap_copied_seq(bool strp)
test_sockmap_pass_prog__destroy(skel);
}
+static void test_sockmap_drop_after_partial_read(void)
+{
+ int map, err, sent, recvd, zero = 0, on = 1;
+ struct test_sockmap_drop_prog *skel;
+ int c0 = -1, p0 = -1, c1 = -1, p1 = -1;
+ struct tcp_zerocopy_receive zc = {};
+ socklen_t zc_len = sizeof(zc);
+ char buf[200] = {}, rcv[50], addr[100];
+ struct bpf_program *prog;
+
+ skel = test_sockmap_drop_prog__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ if (create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1))
+ goto end;
+
+ sent = xsend(c0, buf, sizeof(buf), 0);
+ if (!ASSERT_EQ(sent, sizeof(buf), "xsend(native)"))
+ goto end;
+
+ recvd = recv_timeout(p0, rcv, sizeof(rcv), MSG_DONTWAIT, 1);
+ if (!ASSERT_EQ(recvd, sizeof(rcv), "recv_timeout(partial)"))
+ goto end;
+
+ prog = skel->progs.prog_skb_verdict;
+ map = bpf_map__fd(skel->maps.sock_map_rx);
+ err = bpf_prog_attach(bpf_program__fd(prog), map,
+ BPF_SK_SKB_STREAM_VERDICT, 0);
+ if (!ASSERT_OK(err, "bpf_prog_attach"))
+ goto end;
+
+ err = bpf_map_update_elem(map, &zero, &p0, BPF_NOEXIST);
+ if (!ASSERT_OK(err, "bpf_map_update_elem"))
+ goto end;
+
+ sent = xsend(c0, buf, 1, 0);
+ if (!ASSERT_EQ(sent, 1, "xsend(drop)"))
+ goto end;
+
+ err = bpf_map_delete_elem(map, &zero);
+ if (!ASSERT_OK(err, "bpf_map_delete_elem"))
+ goto end;
+
+ sent = xsend(c0, buf, 1, 0);
+ if (!ASSERT_EQ(sent, 1, "xsend(native again)"))
+ goto end;
+
+ err = setsockopt(p0, SOL_SOCKET, SO_ZEROCOPY, &on, sizeof(on));
+ if (!ASSERT_OK(err, "setsockopt(SO_ZEROCOPY)"))
+ goto end;
+
+ zc.copybuf_address = (__u64)(unsigned long)addr;
+ zc.copybuf_len = sizeof(addr);
+ err = getsockopt(p0, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, &zc, &zc_len);
+ if (!ASSERT_OK(err, "getsockopt(TCP_ZEROCOPY_RECEIVE)"))
+ goto end;
+
+ ASSERT_EQ(zc.copybuf_len, 1, "TCP_ZEROCOPY_RECEIVE copied");
+end:
+ if (c0 >= 0)
+ close(c0);
+ if (p0 >= 0)
+ close(p0);
+ if (c1 >= 0)
+ close(c1);
+ if (p1 >= 0)
+ close(p1);
+ test_sockmap_drop_prog__destroy(skel);
+}
+
/* Wait until FIONREAD returns the expected value or timeout */
static int wait_for_fionread(int fd, int expected, unsigned int timeout_ms)
{
@@ -1447,6 +1518,8 @@ void test_sockmap_basic(void)
test_sockmap_copied_seq(false);
if (test__start_subtest("sockmap recover with strp"))
test_sockmap_copied_seq(true);
+ if (test__start_subtest("sockmap drop after partial read"))
+ test_sockmap_drop_after_partial_read();
if (test__start_subtest("sockmap tcp multi channels"))
test_sockmap_multi_channels(SOCK_STREAM);
if (test__start_subtest("sockmap udp multi channels"))
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net-next v7 3/3] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload
From: Lorenzo Bianconi @ 2026-07-02 14:02 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev, Madhur Agrawal
In-Reply-To: <20260701-airoha-ethtool-priv_flags-v7-3-b4153bd44428@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 12784 bytes --]
On Jul 01, Lorenzo Bianconi wrote:
> GDM3 and GDM4 ports require GDM2 loopback to be enabled for hardware
> QoS offload to function. Without it, HTB and ETS offload on these ports
> do not work.
> Previously, GDM3/GDM4 ports were automatically configured as WAN with
> GDM2 loopback enabled during ndo_init(). Add the capability to configure
> GDM3/GDM4 as WAN/LAN on demand when QoS offload is created or destroyed.
> Hook airoha_enable_qos_for_gdm34() into TC_HTB_CREATE so that requesting
> HTB offload on a GDM3/GDM4 LAN port switches it to WAN mode and enables
> GDM2 loopback, with proper rollback on failure. Introduce the
> AIROHA_DEV_F_QOS flag to track whether a device has an active HTB
> qdisc; clear it on TC_HTB_DESTROY. The device keeps its WAN role after
> qdisc teardown so that its configuration is preserved until another
> device explicitly needs the WAN role for QoS offload.
> If another GDM3/GDM4 device already holds the WAN role without an active
> QoS qdisc, demote it to LAN before promoting the requesting device. Skip
> the demotion when the requesting device is itself already the WAN device.
> Since airoha_dev_set_qdma() can now be called on a running device to
> migrate between QDMA blocks, make dev->qdma an RCU pointer so the TX
> path can safely dereference it without holding RTNL.
> Hold flow_offload_mutex in airoha_enable_qos_for_gdm34() and
> airoha_disable_qos_for_gdm34() around the dev->flags update,
> airoha_dev_set_qdma() and GDM2 loopback configuration, serializing
> against concurrent airoha_ppe_hw_init() in the TC_SETUP_CLSFLOWER
> offload path.
> Introduce airoha_qdma_deref() helper that wraps rcu_dereference_protected()
> with a lockdep condition accepting either rtnl_lock or flow_offload_mutex,
> and use it across all control-path dereferences of the RCU-protected
> dev->qdma pointer.
> Add airoha_disable_gdm2_loopback() to disable GDM2 hw loopback.
>
> Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Reviewed-by: Simon Horman <horms@kernel.org>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Commenting on Sashiko's report:
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260701-airoha-ethtool-priv_flags-v7-0-b4153bd44428%40kernel.org
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 219 ++++++++++++++++++++++++++----
> drivers/net/ethernet/airoha/airoha_eth.h | 13 +-
> drivers/net/ethernet/airoha/airoha_ppe.c | 9 +-
> drivers/net/ethernet/airoha/airoha_regs.h | 1 +
> 4 files changed, 214 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 2c9ceb9f16f8..609a5ea67fb7 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
[...]
> +static void airoha_disable_qos_for_gdm34(struct net_device *netdev)
> +{
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> + int err;
> +
> + if (port->id != AIROHA_GDM3_IDX &&
> + port->id != AIROHA_GDM4_IDX)
> + return;
> +
> + err = airoha_disable_gdm2_loopback(dev);
> + if (err)
> + netdev_warn(netdev,
> + "failed disabling GDM2 loopback: %d\n", err);
> +
> + dev->flags &= ~AIROHA_DEV_F_WAN;
> + airoha_dev_set_qdma(dev);
> +
> + airoha_set_macaddr(dev, netdev->dev_addr);
- Should the return value of airoha_set_macaddr() be checked here?
airoha_set_macaddr() can return -EINVAL when the device MAC MSBs do
not match other same-role sibling devices:
if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
...
return -EINVAL;
}
Since AIROHA_DEV_F_WAN has just been cleared and dev->qdma has been
switched to QDMA0, the scan now iterates LAN-role peers. If the
ex-WAN MAC's top three bytes disagree with the LAN peers, the call
returns -EINVAL and the HW MAC registers are left unwritten while
the software state has already been flipped to LAN.
- The asymmetry is intentional. airoha_disable_qos_for_gdm34() is a
teardown path that returns void, the demotion has to happen regardless.
Moreover, airoha_set_macaddr() can fail just if the device is
misconfigured.
> + if (netif_running(netdev))
> + airoha_set_gdm_port_fwd_cfg(dev->eth,
> + REG_GDM_FWD_CFG(port->id),
> + FE_PSE_PORT_PPE1);
> +}
> +
> +static int airoha_enable_qos_for_gdm34(struct net_device *netdev,
> + struct netlink_ext_ack *extack)
> +{
> + struct airoha_gdm_dev *wan_dev, *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> + struct airoha_eth *eth = dev->eth;
> + int err = -EBUSY;
> +
> + if (port->id != AIROHA_GDM3_IDX &&
> + port->id != AIROHA_GDM4_IDX) {
> + /* HW QoS is always supported by GDM1 and GDM2 */
> + return 0;
> + }
> +
> + if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */
> + return 0;
> +
> + mutex_lock(&flow_offload_mutex);
> +
> + wan_dev = airoha_get_wan_gdm_dev(eth);
> + if (wan_dev) {
> + if ((wan_dev->flags & AIROHA_DEV_F_QOS) ||
> + wan_dev->port->id == AIROHA_GDM2_IDX) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "QoS configured for WAN device");
> + goto error_unlock;
> + }
> + airoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev));
> + }
> +
> + dev->flags |= AIROHA_DEV_F_WAN;
> + airoha_dev_set_qdma(dev);
> + err = airoha_enable_gdm2_loopback(dev);
> + if (err)
> + goto error_disable_wan;
> +
> + err = airoha_set_macaddr(dev, netdev->dev_addr);
> + if (err)
> + goto error_disable_loopback;
> +
> + if (netif_running(netdev)) {
> + u32 pse_port;
> +
> + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
> + : FE_PSE_PORT_PPE1;
> + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
> + pse_port);
> + }
> +
> + mutex_unlock(&flow_offload_mutex);
> +
> + return 0;
> +
> +error_disable_loopback:
> + airoha_disable_gdm2_loopback(dev);
> +error_disable_wan:
> + dev->flags &= ~AIROHA_DEV_F_WAN;
> + airoha_dev_set_qdma(dev);
> +error_unlock:
> + mutex_unlock(&flow_offload_mutex);
> +
> + return err;
- Does this failure path fully implement the "proper rollback on failure"
described in the commit message?
Earlier in the function, if a peer wan_dev exists it is demoted via:
airoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev));
That helper clears AIROHA_DEV_F_WAN on wan_dev, swaps its RCU qdma
pointer to QDMA0, disables GDM2 loopback, rewrites its MAC to the LAN
registers, and (if wan_dev was running) sets its REG_GDM_FWD_CFG to
FE_PSE_PORT_PPE1.
If a later step for the requesting device fails, the error labels
error_disable_loopback / error_disable_wan / error_unlock only unwind
the requesting device. The demoted wan_dev is never re-promoted.
For example, airoha_set_macaddr(dev, netdev->dev_addr) can return
-EINVAL when the requesting device's MAC MSBs disagree with the other
WAN-role peers:
if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
...
return -EINVAL;
}
In that case the failed tc qdisc add returns to userspace, the
requesting device is rolled back to LAN, and no GDM3/GDM4 device holds
the WAN role anymore. The previously working WAN device silently loses
its WAN role and QDMA1 binding.
Should the error paths re-promote wan_dev, or should the preconditions
that can fail (MAC MSB check, GDM2 loopback setup) be validated before
mutating the peer's state?
- This issue has been already reported in the past. If the configuration
fails, I think there is no point to move back the previous device as WAN.
The user will be able to log again and re-apply the configuration.
Moreover, airoha_enable_qos_for_gdm34() can fail just if the device is
misconfigured (failures in airoha_enable_gdm2_loopback() or
airoha_set_macaddr()).
Regards,
Lorenzo
> +}
> +
> static int airoha_tc_htb_destroy(struct net_device *netdev)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> @@ -3057,6 +3217,8 @@ static int airoha_tc_htb_destroy(struct net_device *netdev)
> for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
> airoha_tc_remove_htb_queue(netdev, q);
>
> + dev->flags &= ~AIROHA_DEV_F_QOS;
> +
> return 0;
> }
>
> @@ -3076,24 +3238,33 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
> return 0;
> }
>
> -static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
> +static int airoha_tc_setup_qdisc_htb(struct net_device *netdev,
> struct tc_htb_qopt_offload *opt)
> {
> switch (opt->command) {
> - case TC_HTB_CREATE:
> + case TC_HTB_CREATE: {
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + int err;
> +
> + err = airoha_enable_qos_for_gdm34(netdev, opt->extack);
> + if (err)
> + return err;
> +
> + dev->flags |= AIROHA_DEV_F_QOS;
> break;
> + }
> case TC_HTB_DESTROY:
> - return airoha_tc_htb_destroy(dev);
> + return airoha_tc_htb_destroy(netdev);
> case TC_HTB_NODE_MODIFY:
> - return airoha_tc_htb_modify_queue(dev, opt);
> + return airoha_tc_htb_modify_queue(netdev, opt);
> case TC_HTB_LEAF_ALLOC_QUEUE:
> - return airoha_tc_htb_alloc_leaf_queue(dev, opt);
> + return airoha_tc_htb_alloc_leaf_queue(netdev, opt);
> case TC_HTB_LEAF_DEL:
> case TC_HTB_LEAF_DEL_LAST:
> case TC_HTB_LEAF_DEL_LAST_FORCE:
> - return airoha_tc_htb_delete_leaf_queue(dev, opt);
> + return airoha_tc_htb_delete_leaf_queue(netdev, opt);
> case TC_HTB_LEAF_QUERY_QUEUE:
> - return airoha_tc_get_htb_get_leaf_queue(dev, opt);
> + return airoha_tc_get_htb_get_leaf_queue(netdev, opt);
> default:
> return -EOPNOTSUPP;
> }
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index ac5f571f3e53..a314330fcd48 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -537,11 +537,12 @@ struct airoha_qdma {
>
> enum airoha_dev_flags {
> AIROHA_DEV_F_WAN = BIT(0),
> + AIROHA_DEV_F_QOS = BIT(1),
> };
>
> struct airoha_gdm_dev {
> + struct airoha_qdma __rcu *qdma;
> struct airoha_gdm_port *port;
> - struct airoha_qdma *qdma;
> struct airoha_eth *eth;
>
> DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
> @@ -677,6 +678,16 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev);
> bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
> struct airoha_gdm_dev *dev);
>
> +extern struct mutex flow_offload_mutex;
> +
> +static inline struct airoha_qdma *
> +airoha_qdma_deref(struct airoha_gdm_dev *dev)
> +{
> + return rcu_dereference_protected(dev->qdma,
> + lockdep_rtnl_is_held() ||
> + lockdep_is_held(&flow_offload_mutex));
> +}
> +
> void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
> bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
> void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
> diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
> index 42f4b0f21d17..0f260c50ac3c 100644
> --- a/drivers/net/ethernet/airoha/airoha_ppe.c
> +++ b/drivers/net/ethernet/airoha/airoha_ppe.c
> @@ -15,7 +15,10 @@
> #include "airoha_regs.h"
> #include "airoha_eth.h"
>
> -static DEFINE_MUTEX(flow_offload_mutex);
> +/* Serialize airoha_gdm_dev flags, QDMA pointer and PPE CPU port
> + * configuration.
> + */
> +DEFINE_MUTEX(flow_offload_mutex);
> static DEFINE_SPINLOCK(ppe_lock);
>
> static const struct rhashtable_params airoha_flow_table_params = {
> @@ -86,8 +89,8 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
>
> void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
> {
> - struct airoha_qdma *qdma = dev->qdma;
> - struct airoha_eth *eth = qdma->eth;
> + struct airoha_qdma *qdma = airoha_qdma_deref(dev);
> + struct airoha_eth *eth = dev->eth;
> u8 qdma_id = qdma - ð->qdma[0];
> u32 fe_cpu_port;
>
> diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
> index 436f3c8779c1..4e17dfbcf2b8 100644
> --- a/drivers/net/ethernet/airoha/airoha_regs.h
> +++ b/drivers/net/ethernet/airoha/airoha_regs.h
> @@ -376,6 +376,7 @@
>
> #define REG_SRC_PORT_FC_MAP6 0x2298
> #define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
> +#define FC_MAP6_DEF_VALUE 0x1b1a1918
>
> #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4
>
>
> --
> 2.54.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH iwl-net 2/2] ice: fix stats array overflow via proper realloc
From: Przemek Kitszel @ 2026-07-02 14:12 UTC (permalink / raw)
To: intel-wired-lan, Michal Schmidt, Jakub Kicinski
Cc: netdev, Tony Nguyen, Aleksandr Loktionov, Andrew Lunn,
David S. Miller, Eric Dumazet, Paolo Abeni, Jedrzej Jagielski,
Piotr Kwapulinski
In-Reply-To: <20260701104141.9740-2-przemyslaw.kitszel@intel.com>
> /**
> * ice_vsi_alloc_def - set default values for already allocated VSI
> * @vsi: ptr to VSI
> @@ -2319,7 +2274,17 @@ static int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
> u16 prev_txq = vsi->alloc_txq;
> u16 prev_rxq = vsi->alloc_rxq;
>
> + if (vsi->type == ICE_VSI_CHNL)
> + return 0;
> +
> vsi_stat = pf->vsi_stats[vsi->idx];
> + if (!vsi_stat) {
> + vsi_stat = kzalloc_obj(*vsi_stat);
> + if (!vsi_stat)
> + return -ENOMEM;
> +
> + pf->vsi_stats[vsi->idx] = vsi_stat;
> + }
sashiko [1] points out that if there will be allocation error
later we will end up with, say, vsi_stat->tx_ring_stats == NULL,
but ice_vsi_free_stats() will try to dereference it
will post v2 with a fix
[1]
https://sashiko.dev/#/patchset/20260701104141.9740-1-przemyslaw.kitszel%40intel.com
>
> if (req_txq < prev_txq) {
> for (int i = req_txq; i < prev_txq; i++) {
^ permalink raw reply
* Re: [PATCH 6.6.y] net: add missing ns_capable check for peer netns
From: Greg KH @ 2026-07-02 14:14 UTC (permalink / raw)
To: Maximilian Heyne
Cc: stable, Wolfgang Grandegger, Marc Kleine-Budde, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Eric W. Biederman,
linux-can, netdev, linux-kernel
In-Reply-To: <20260617-sprain-dye-86c242ac@mheyne-amazon>
On Wed, Jun 17, 2026 at 08:26:39AM +0000, Maximilian Heyne wrote:
> The upstream commit 7b735ef81286 ("rtnetlink: add missing
> netlink_ns_capable() check for peer netns") doesn't apply on older
> stable kernels due to refactoring. Therefore, this patch is an attempt
> to implement the same capability check just directly in the respective
> interface types.
Why not just take the upstream commits instead? That's simpler over
time than a one-off patch.
thanks,
greg k-h
^ permalink raw reply
* [PATCH net-next 0/4] net: usb: move exported code to usbnet
From: Oliver Neukum @ 2026-07-02 14:25 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, shaoxul, netdev,
linux-usb, linux-kernel
Some drivers are reusing common code originating in other drivers.
This means that two drivers need to be loaded for one device.
Also maintainability is reduced if changes in one driver affect
another driver.
Shift common code to usbnet.
^ permalink raw reply
* [PATCH net-next 3/4] net: usb: usbnet: add cdc_state to struct usbnet
From: Oliver Neukum @ 2026-07-02 14:25 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, shaoxul, netdev,
linux-usb, linux-kernel
Cc: Oliver Neukum
In-Reply-To: <20260702143142.890654-1-oneukum@suse.com>
This allows centralisation of code using cdc_state in usbnet, reducing
code duplication. No functional change intended.
Signed-off-by: Oliver Neukum <oneukum@suse.com>
---
include/linux/usb/usbnet.h | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index bbf799ccf3b3..79f48eb388ee 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -16,6 +16,14 @@
#include <linux/usb.h>
#include <linux/spinlock.h>
+struct cdc_state {
+ struct usb_cdc_header_desc *header;
+ struct usb_cdc_union_desc *u;
+ struct usb_cdc_ether_desc *ether;
+ struct usb_interface *control;
+ struct usb_interface *data;
+};
+
/* interface from usbnet core to each USB networking link we handle */
struct usbnet {
/* housekeeping */
@@ -41,6 +49,7 @@ struct usbnet {
/* protocol/interface state */
struct net_device *net;
int msg_enable;
+ struct cdc_state cdc; /* too common to leave out*/
unsigned long data[5];
u32 xid;
u32 hard_mtu; /* count any extra framing */
@@ -211,13 +220,6 @@ extern int usbnet_write_cmd_async(struct usbnet *dev, u8 cmd, u8 reqtype,
* (notably, using multiple interfaces according to the CDC
* union descriptor) get some helper code.
*/
-struct cdc_state {
- struct usb_cdc_header_desc *header;
- struct usb_cdc_union_desc *u;
- struct usb_cdc_ether_desc *ether;
- struct usb_interface *control;
- struct usb_interface *data;
-};
extern void usbnet_cdc_update_filter(struct usbnet *dev);
extern int usbnet_generic_cdc_bind(struct usbnet *, struct usb_interface *);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 1/4] net: usb: move updating filter and status from cdc to usbnet
From: Oliver Neukum @ 2026-07-02 14:25 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, shaoxul, netdev,
linux-usb, linux-kernel
Cc: Oliver Neukum
In-Reply-To: <20260702143142.890654-1-oneukum@suse.com>
These helpers are used by multiple drivers and do not depend
on the rest of cdc. Leavin them in a cdc driver means that
more drivers are loaded just for infrastructure, not hardware
support.
Signed-off-by: Oliver Neukum <oneukum@suse.com>
---
drivers/net/usb/cdc_ether.c | 75 ------------------------------------
drivers/net/usb/usbnet.c | 76 +++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+), 75 deletions(-)
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index a0a5740590b9..b4df32e18461 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -63,35 +63,6 @@ static const u8 mbm_guid[16] = {
0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a,
};
-void usbnet_cdc_update_filter(struct usbnet *dev)
-{
- struct net_device *net = dev->net;
-
- u16 cdc_filter = USB_CDC_PACKET_TYPE_DIRECTED
- | USB_CDC_PACKET_TYPE_BROADCAST;
-
- /* filtering on the device is an optional feature and not worth
- * the hassle so we just roughly care about snooping and if any
- * multicast is requested, we take every multicast
- */
- if (net->flags & IFF_PROMISC)
- cdc_filter |= USB_CDC_PACKET_TYPE_PROMISCUOUS;
- if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI))
- cdc_filter |= USB_CDC_PACKET_TYPE_ALL_MULTICAST;
-
- usb_control_msg(dev->udev,
- usb_sndctrlpipe(dev->udev, 0),
- USB_CDC_SET_ETHERNET_PACKET_FILTER,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- cdc_filter,
- dev->intf->cur_altsetting->desc.bInterfaceNumber,
- NULL,
- 0,
- USB_CTRL_SET_TIMEOUT
- );
-}
-EXPORT_SYMBOL_GPL(usbnet_cdc_update_filter);
-
/* We need to override usbnet_*_link_ksettings in bind() */
static const struct ethtool_ops cdc_ether_ethtool_ops = {
.get_link = usbnet_get_link,
@@ -400,52 +371,6 @@ EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
* (by Brad Hards) talked with, with more functionality.
*/
-static void speed_change(struct usbnet *dev, __le32 *speeds)
-{
- dev->tx_speed = __le32_to_cpu(speeds[0]);
- dev->rx_speed = __le32_to_cpu(speeds[1]);
-}
-
-void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
-{
- struct usb_cdc_notification *event;
-
- if (urb->actual_length < sizeof(*event))
- return;
-
- /* SPEED_CHANGE can get split into two 8-byte packets */
- if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
- speed_change(dev, (__le32 *) urb->transfer_buffer);
- return;
- }
-
- event = urb->transfer_buffer;
- switch (event->bNotificationType) {
- case USB_CDC_NOTIFY_NETWORK_CONNECTION:
- netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
- event->wValue ? "on" : "off");
- if (netif_carrier_ok(dev->net) != !!event->wValue)
- usbnet_link_change(dev, !!event->wValue, 0);
- break;
- case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
- netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n",
- urb->actual_length);
- if (urb->actual_length != (sizeof(*event) + 8))
- set_bit(EVENT_STS_SPLIT, &dev->flags);
- else
- speed_change(dev, (__le32 *) &event[1]);
- break;
- /* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS),
- * but there are no standard formats for the response data.
- */
- default:
- netdev_err(dev->net, "CDC: unexpected notification %02x!\n",
- event->bNotificationType);
- break;
- }
-}
-EXPORT_SYMBOL_GPL(usbnet_cdc_status);
-
int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
{
int status;
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 25518635b7b7..5544af1f4aa5 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -22,6 +22,7 @@
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/usb/cdc.h>
#include <linux/ctype.h>
#include <linux/ethtool.h>
#include <linux/workqueue.h>
@@ -2271,6 +2272,81 @@ int usbnet_write_cmd_async(struct usbnet *dev, u8 cmd, u8 reqtype,
}
EXPORT_SYMBOL_GPL(usbnet_write_cmd_async);
+
+void usbnet_cdc_update_filter(struct usbnet *dev)
+{
+ struct net_device *net = dev->net;
+
+ u16 cdc_filter = USB_CDC_PACKET_TYPE_DIRECTED
+ | USB_CDC_PACKET_TYPE_BROADCAST;
+
+ /* filtering on the device is an optional feature and not worth
+ * the hassle so we just roughly care about snooping and if any
+ * multicast is requested, we take every multicast
+ */
+ if (net->flags & IFF_PROMISC)
+ cdc_filter |= USB_CDC_PACKET_TYPE_PROMISCUOUS;
+ if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI))
+ cdc_filter |= USB_CDC_PACKET_TYPE_ALL_MULTICAST;
+
+ usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ USB_CDC_SET_ETHERNET_PACKET_FILTER,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ cdc_filter,
+ dev->intf->cur_altsetting->desc.bInterfaceNumber,
+ NULL,
+ 0,
+ USB_CTRL_SET_TIMEOUT
+ );
+}
+EXPORT_SYMBOL_GPL(usbnet_cdc_update_filter);
+
+static void speed_change(struct usbnet *dev, __le32 *speeds)
+{
+ dev->tx_speed = __le32_to_cpu(speeds[0]);
+ dev->rx_speed = __le32_to_cpu(speeds[1]);
+}
+
+void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
+{
+ struct usb_cdc_notification *event;
+
+ if (urb->actual_length < sizeof(*event))
+ return;
+
+ /* SPEED_CHANGE can get split into two 8-byte packets */
+ if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
+ speed_change(dev, (__le32 *)urb->transfer_buffer);
+ return;
+ }
+
+ event = urb->transfer_buffer;
+ switch (event->bNotificationType) {
+ case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+ netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
+ event->wValue ? "on" : "off");
+ if (netif_carrier_ok(dev->net) != !!event->wValue)
+ usbnet_link_change(dev, !!event->wValue, 0);
+ break;
+ case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
+ netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n",
+ urb->actual_length);
+ if (urb->actual_length != (sizeof(*event) + 8))
+ set_bit(EVENT_STS_SPLIT, &dev->flags);
+ else
+ speed_change(dev, (__le32 *)&event[1]);
+ break;
+ /* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS),
+ * but there are no standard formats for the response data.
+ */
+ default:
+ netdev_err(dev->net, "CDC: unexpected notification %02x!\n",
+ event->bNotificationType);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(usbnet_cdc_status);
/*-------------------------------------------------------------------------*/
static int __init usbnet_init(void)
--
2.54.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