* Re: [PATCH] net/ftgmac100: fix ring allocation unwind leaks on open failure
From: Andrew Lunn @ 2026-03-28 14:37 UTC (permalink / raw)
To: Yufan Chen
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-kernel, Yufan Chen
In-Reply-To: <20260328092428.75430-1-yufan.chen@linux.dev>
On Sat, Mar 28, 2026 at 05:24:28PM +0800, Yufan Chen wrote:
> From: Yufan Chen <ericterminal@gmail.com>
>
> ftgmac100_alloc_rings() allocated rx_skbs, tx_skbs, rxdes, txdes, and rx_scratch in stages but returned directly on any intermediate allocation failure. This left previously allocated objects unreleased and could accumulate leaks across repeated ifup retries under memory pressure.
Please take a read of
https://docs.kernel.org/process/submitting-patches.html
and
https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
You have a number of process issues.
> static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
> {
> + int err = -ENOMEM;
> +
> /* Allocate skb arrays */
> priv->rx_skbs = kcalloc(MAX_RX_QUEUE_ENTRIES, sizeof(void *),
> GFP_KERNEL);
> if (!priv->rx_skbs)
> - return -ENOMEM;
> + goto out;
> priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *),
> GFP_KERNEL);
> if (!priv->tx_skbs)
> - return -ENOMEM;
> + goto out;
>
> /* Allocate descriptors */
> priv->rxdes = dma_alloc_coherent(priv->dev,
> MAX_RX_QUEUE_ENTRIES * sizeof(struct ftgmac100_rxdes),
> &priv->rxdes_dma, GFP_KERNEL);
> if (!priv->rxdes)
> - return -ENOMEM;
> + goto out;
> priv->txdes = dma_alloc_coherent(priv->dev,
> MAX_TX_QUEUE_ENTRIES * sizeof(struct ftgmac100_txdes),
> &priv->txdes_dma, GFP_KERNEL);
> if (!priv->txdes)
> - return -ENOMEM;
> + goto out;
>
> /* Allocate scratch packet buffer */
> priv->rx_scratch = dma_alloc_coherent(priv->dev,
> @@ -997,9 +1002,13 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
> &priv->rx_scratch_dma,
> GFP_KERNEL);
> if (!priv->rx_scratch)
> - return -ENOMEM;
> + goto out;
>
> return 0;
> +
> +out:
> + ftgmac100_free_rings(priv);
> + return err;
If you look at other drivers, you will notice that functions that do
allocations pretty much always do their own cleanup, rather than
calling a helper. You generally see multiple labels, one per
allocation, and gotos jumping to the needed code.
Andrew
---
pw-bot: cr
^ permalink raw reply
* Re: [PATCH net v3 09/11] rxrpc: Fix keyring reference count leak in rxrpc_setsockopt()
From: Anderson Nascimento @ 2026-03-28 14:53 UTC (permalink / raw)
To: David Howells
Cc: netdev, Marc Dionne, Jakub Kicinski, David S. Miller,
Eric Dumazet, Paolo Abeni, linux-afs, linux-kernel,
Jeffrey Altman, Simon Horman, stable
In-Reply-To: <754861.1774596280@warthog.procyon.org.uk>
On Fri, Mar 27, 2026 at 4:24 AM David Howells <dhowells@redhat.com> wrote:
>
> Anderson Nascimento <anderson@allelesecurity.com> wrote:
>
> > To make the logic more coherent, what if we check if (rx->key ||
> > rx->securities) in both options and remove the rx->securities check from
> > rxrpc_request_key()?
>
> You're allowed to have both a keyring (server) and a key (client). You can
> issue client calls on a server socket. The in-kernel kafs filesystem does
> this, for example - though it normally sets the outgoing key on individual
> calls.
>
Understood, thanks.
> To parallel the kernel example, it might be worth my while adding a CMSG tag
> to take a key ID or key description so the rxrpc_sendmsg() can do a
> request_key() when setting up a call (the AF_RXRPC socket allows a different
> key with each call dispatched), though the AFS command line tools tend only to
> talk to a single cell at a time (you only need one key for comms with an
> entire cell).
>
> Davod
>
--
Anderson Nascimento
Allele Security Intelligence
https://www.allelesecurity.com
^ permalink raw reply
* Re: [PATCH RFC 3/8] clk: sunxi-ng: a733: Add PRCM CCU
From: Chen-Yu Tsai @ 2026-03-28 15:04 UTC (permalink / raw)
To: Junhui Liu
Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jernej Skrabec, Samuel Holland, Philipp Zabel,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Richard Cochran, linux-clk, devicetree, linux-arm-kernel,
linux-sunxi, linux-kernel, linux-riscv, netdev
In-Reply-To: <20260310-a733-clk-v1-3-36b4e9b24457@pigmoral.tech>
On Tue, Mar 10, 2026 at 4:42 PM Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
> Add support for the Power Reset Clock Management (PRCM) module found in
> the Allwinner A733 SoC. This clock controller manages the clock control
> and reset functions for device modules within the CPUS domain.
>
> The PRCM module includes the management of three primary buses: r-ahb,
> r-apb0, and r-apb1. It also provides clocking for several key
> peripherals, such as R-UART, R-I2C, R-SPI, and the R-RISCV subsystem.
> Additionally, the reset lines for these modules are integrated.
>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
> drivers/clk/sunxi-ng/Kconfig | 5 +
> drivers/clk/sunxi-ng/Makefile | 2 +
> drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c | 276 +++++++++++++++++++++++++++++++
> 3 files changed, 283 insertions(+)
>
> diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
> index 6af2d020e03e..202e793dc754 100644
> --- a/drivers/clk/sunxi-ng/Kconfig
> +++ b/drivers/clk/sunxi-ng/Kconfig
> @@ -67,6 +67,11 @@ config SUN55I_A523_R_CCU
> default ARCH_SUNXI
> depends on ARM64 || COMPILE_TEST
>
> +config SUN60I_A733_R_CCU
> + tristate "Support for the Allwinner A733 PRCM CCU"
> + default ARCH_SUNXI
> + depends on ARM64 || COMPILE_TEST
> +
> config SUN4I_A10_CCU
> tristate "Support for the Allwinner A10/A20 CCU"
> default ARCH_SUNXI
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index a1c4087d7241..d3702bdb7a23 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
> obj-$(CONFIG_SUN55I_A523_CCU) += sun55i-a523-ccu.o
> obj-$(CONFIG_SUN55I_A523_MCU_CCU) += sun55i-a523-mcu-ccu.o
> obj-$(CONFIG_SUN55I_A523_R_CCU) += sun55i-a523-r-ccu.o
> +obj-$(CONFIG_SUN60I_A733_R_CCU) += sun60i-a733-r-ccu.o
> obj-$(CONFIG_SUN4I_A10_CCU) += sun4i-a10-ccu.o
> obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o
> obj-$(CONFIG_SUN6I_A31_CCU) += sun6i-a31-ccu.o
> @@ -64,6 +65,7 @@ sun50i-h616-ccu-y += ccu-sun50i-h616.o
> sun55i-a523-ccu-y += ccu-sun55i-a523.o
> sun55i-a523-mcu-ccu-y += ccu-sun55i-a523-mcu.o
> sun55i-a523-r-ccu-y += ccu-sun55i-a523-r.o
> +sun60i-a733-r-ccu-y += ccu-sun60i-a733-r.o
> sun4i-a10-ccu-y += ccu-sun4i-a10.o
> sun5i-ccu-y += ccu-sun5i.o
> sun6i-a31-ccu-y += ccu-sun6i-a31.o
> diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c
> new file mode 100644
> index 000000000000..06679be1eaae
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c
> @@ -0,0 +1,276 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 rengaomin@allwinnertech.com
> + * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
> + * Based on the A523 CCU driver:
> + * Copyright (C) 2024 Arm Ltd.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include <dt-bindings/clock/sun60i-a733-r-ccu.h>
> +#include <dt-bindings/reset/sun60i-a733-r-ccu.h>
> +
> +#include "ccu_common.h"
> +#include "ccu_reset.h"
> +
> +#include "ccu_gate.h"
> +#include "ccu_mp.h"
> +
> +static const struct clk_parent_data r_ahb_parents[] = {
> + { .fw_name = "hosc" },
> + { .fw_name = "losc" },
> + { .fw_name = "iosc" },
> + { .fw_name = "pll-periph0-200m" },
> + { .fw_name = "pll-periph0-300m" },
> +};
> +static SUNXI_CCU_M_DATA_WITH_MUX(r_ahb_clk, "r-ahb", r_ahb_parents, 0x000,
> + 0, 5, /* M */
> + 24, 3, /* mux */
> + 0);
> +
> +static const struct clk_parent_data r_apb_parents[] = {
> + { .fw_name = "hosc" },
> + { .fw_name = "losc" },
> + { .fw_name = "iosc" },
> + { .fw_name = "pll-periph0-200m" },
> + { .fw_name = "sys-24m" },
> +};
> +
> +static SUNXI_CCU_M_DATA_WITH_MUX(r_apb0_clk, "r-apb0", r_apb_parents, 0x00c,
> + 0, 5, /* M */
> + 24, 3, /* mux */
> + 0);
> +
> +static SUNXI_CCU_M_DATA_WITH_MUX(r_apb1_clk, "r-apb1", r_apb_parents, 0x010,
> + 0, 5, /* M */
> + 24, 3, /* mux */
> + 0);
> +
> +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer0, "r-timer0", r_apb_parents, 0x100,
> + 0, 0, /* no M */
> + 1, 3, /* P */
> + 4, 3, /* mux */
> + BIT(0), /* gate */
> + 0);
Use SUNXI_CCU_P_DATA_WITH_MUX_GATE(). Same for the other ones.
> +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer1, "r-timer1", r_apb_parents, 0x104,
> + 0, 0, /* no M */
> + 1, 3, /* P */
> + 4, 3, /* mux */
> + BIT(0), /* gate */
> + 0);
> +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer2, "r-timer2", r_apb_parents, 0x108,
> + 0, 0, /* no M */
> + 1, 3, /* P */
> + 4, 3, /* mux */
> + BIT(0), /* gate */
> + 0);
> +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_cpu_timer3, "r-timer3", r_apb_parents, 0x10c,
> + 0, 0, /* no M */
> + 1, 3, /* P */
> + 4, 3, /* mux */
> + BIT(0), /* gate */
> + 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_timer_clk, "bus-r-timer", &r_ahb_clk.common.hw, 0x11c, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_twd_clk, "bus-r-twd", &r_apb0_clk.common.hw, 0x12c, BIT(0), 0);
> +
> +static const struct clk_parent_data r_pwmctrl_parents[] = {
> + { .fw_name = "hosc" },
> + { .fw_name = "losc" },
> + { .fw_name = "iosc" },
> + { .fw_name = "sys-24m" },
> +};
> +static SUNXI_CCU_MUX_DATA_WITH_GATE(r_pwmctrl_clk, "r-pwmctrl", r_pwmctrl_parents, 0x130,
r_pwm_clk, "r-pwm", ...
> + 24, 2, /* mux */
> + BIT(31), /* gate */
> + 0);
> +static SUNXI_CCU_GATE_HW(bus_r_pwmctrl_clk, "bus-r-pwmctrl",
bus_r_pwm_clk, "bus-r-pwm".
> + &r_apb0_clk.common.hw, 0x13c, BIT(0), 0);
> +
> +static const struct clk_parent_data r_spi_parents[] = {
> + { .fw_name = "hosc" },
> + { .fw_name = "pll-periph0-200m" },
> + { .fw_name = "pll-periph0-300m" },
> + { .fw_name = "pll-periph1-300m" },
> + { .fw_name = "sys-24m" },
> +};
> +static SUNXI_CCU_DUALDIV_MUX_GATE(r_spi_clk, "r-spi", r_spi_parents, 0x150,
> + 0, 5, /* M */
> + 8, 5, /* N */
> + 24, 3, /* mux */
> + BIT(31), /* gate */
> + 0);
> +static SUNXI_CCU_GATE_HW(bus_r_spi_clk, "bus-r-spi", &r_ahb_clk.common.hw, 0x15c, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_msgbox_clk, "bus-r-msgbox", &r_ahb_clk.common.hw, 0x17c, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_uart0_clk, "bus-r-uart0", &r_apb1_clk.common.hw, 0x18c, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_uart1_clk, "bus-r-uart1", &r_apb1_clk.common.hw, 0x18c, BIT(1), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_i2c0_clk, "bus-r-i2c0", &r_apb1_clk.common.hw, 0x19c, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_i2c1_clk, "bus-r-i2c1", &r_apb1_clk.common.hw, 0x19c, BIT(1), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_i2c2_clk, "bus-r-i2c2", &r_apb1_clk.common.hw, 0x19c, BIT(2), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_ppu_clk, "bus-r-ppu", &r_apb0_clk.common.hw, 0x1ac, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_tzma_clk, "bus-r-tzma", &r_apb0_clk.common.hw, 0x1b0, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_cpu_bist_clk, "bus-r-cpu-bist", &r_apb0_clk.common.hw,
> + 0x1bc, BIT(0), 0);
> +
> +static const struct clk_parent_data r_ir_rx_parents[] = {
> + { .fw_name = "losc" },
> + { .fw_name = "hosc" },
> + { .fw_name = "sys-24m" },
> +};
> +static SUNXI_CCU_M_DATA_WITH_MUX_GATE(r_ir_rx_clk, "r-ir-rx", r_ir_rx_parents, 0x1c0,
> + 0, 5, /* M */
> + 24, 2, /* mux */
> + BIT(31), /* gate */
> + 0);
> +static SUNXI_CCU_GATE_HW(bus_r_ir_rx_clk, "bus-r-ir-rx", &r_apb0_clk.common.hw, 0x1cc, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_rtc_clk, "bus-r-rtc", &r_ahb_clk.common.hw, 0x20c, BIT(0), 0);
> +
> +static const struct clk_parent_data r_riscv_parents[] = {
> + { .fw_name = "hosc" },
> + { .fw_name = "losc" },
> + { .fw_name = "iosc" },
> +};
> +static SUNXI_CCU_MUX_DATA_WITH_GATE(r_riscv_clk, "r-riscv", r_riscv_parents, 0x210,
> + 24, 2, /* mux */
> + BIT(31), /* gate */
> + 0);
> +static SUNXI_CCU_GATE_HW(bus_r_riscv_clk, "bus-r-riscv", &r_apb0_clk.common.hw,
> + 0x21c, BIT(0), 0);
> +static SUNXI_CCU_GATE_HW(bus_r_riscv_cfg_clk, "bus-r-riscv-cfg", &r_apb0_clk.common.hw,
> + 0x21c, BIT(1), 0);
> +
> +static SUNXI_CCU_GATE_HW(bus_r_cpucfg_clk, "bus-r-cpucfg", &r_apb0_clk.common.hw,
> + 0x22c, BIT(0), CLK_IS_CRITICAL);
> +
> +static struct ccu_common *sun60i_a733_r_ccu_clks[] = {
> + &r_ahb_clk.common,
> + &r_apb0_clk.common,
> + &r_apb1_clk.common,
> + &r_cpu_timer0.common,
> + &r_cpu_timer1.common,
> + &r_cpu_timer2.common,
> + &r_cpu_timer3.common,
> + &bus_r_timer_clk.common,
> + &bus_r_twd_clk.common,
> + &r_pwmctrl_clk.common,
> + &bus_r_pwmctrl_clk.common,
> + &r_spi_clk.common,
> + &bus_r_spi_clk.common,
> + &bus_r_msgbox_clk.common,
> + &bus_r_uart0_clk.common,
> + &bus_r_uart1_clk.common,
> + &bus_r_i2c0_clk.common,
> + &bus_r_i2c1_clk.common,
> + &bus_r_i2c2_clk.common,
> + &bus_r_ppu_clk.common,
> + &bus_r_tzma_clk.common,
> + &bus_r_cpu_bist_clk.common,
> + &r_ir_rx_clk.common,
> + &bus_r_ir_rx_clk.common,
> + &bus_r_rtc_clk.common,
> + &r_riscv_clk.common,
> + &bus_r_riscv_clk.common,
> + &bus_r_riscv_cfg_clk.common,
> + &bus_r_cpucfg_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sun60i_a733_r_hw_clks = {
> + .hws = {
> + [CLK_R_AHB] = &r_ahb_clk.common.hw,
> + [CLK_R_APB0] = &r_apb0_clk.common.hw,
> + [CLK_R_APB1] = &r_apb1_clk.common.hw,
> + [CLK_R_TIMER0] = &r_cpu_timer0.common.hw,
> + [CLK_R_TIMER1] = &r_cpu_timer1.common.hw,
> + [CLK_R_TIMER2] = &r_cpu_timer2.common.hw,
> + [CLK_R_TIMER3] = &r_cpu_timer3.common.hw,
> + [CLK_BUS_R_TIMER] = &bus_r_timer_clk.common.hw,
> + [CLK_BUS_R_TWD] = &bus_r_twd_clk.common.hw,
> + [CLK_R_PWMCTRL] = &r_pwmctrl_clk.common.hw,
> + [CLK_BUS_R_PWMCTRL] = &bus_r_pwmctrl_clk.common.hw,
> + [CLK_R_SPI] = &r_spi_clk.common.hw,
> + [CLK_BUS_R_SPI] = &bus_r_spi_clk.common.hw,
> + [CLK_BUS_R_MSGBOX] = &bus_r_msgbox_clk.common.hw,
> + [CLK_BUS_R_UART0] = &bus_r_uart0_clk.common.hw,
> + [CLK_BUS_R_UART1] = &bus_r_uart1_clk.common.hw,
> + [CLK_BUS_R_I2C0] = &bus_r_i2c0_clk.common.hw,
> + [CLK_BUS_R_I2C1] = &bus_r_i2c1_clk.common.hw,
> + [CLK_BUS_R_I2C2] = &bus_r_i2c2_clk.common.hw,
> + [CLK_BUS_R_PPU] = &bus_r_ppu_clk.common.hw,
> + [CLK_BUS_R_TZMA] = &bus_r_tzma_clk.common.hw,
> + [CLK_BUS_R_CPU_BIST] = &bus_r_cpu_bist_clk.common.hw,
> + [CLK_R_IR_RX] = &r_ir_rx_clk.common.hw,
> + [CLK_BUS_R_IR_RX] = &bus_r_ir_rx_clk.common.hw,
> + [CLK_BUS_R_RTC] = &bus_r_rtc_clk.common.hw,
> + [CLK_R_RISCV] = &r_riscv_clk.common.hw,
> + [CLK_BUS_R_RISCV] = &bus_r_riscv_clk.common.hw,
> + [CLK_BUS_R_RISCV_CFG] = &bus_r_riscv_cfg_clk.common.hw,
> + [CLK_BUS_R_CPUCFG] = &bus_r_cpucfg_clk.common.hw,
> + },
> + .num = CLK_BUS_R_CPUCFG + 1,
> +};
> +
> +static struct ccu_reset_map sun60i_a733_r_ccu_resets[] = {
> + [RST_BUS_R_TIMER] = { 0x11c, BIT(16) },
> + [RST_BUS_R_PWM] = { 0x13c, BIT(16) },
> + [RST_BUS_R_SPI] = { 0x15c, BIT(16) },
> + [RST_BUS_R_MSGBOX] = { 0x17c, BIT(16) },
> + [RST_BUS_R_UART0] = { 0x18c, BIT(16) },
> + [RST_BUS_R_UART1] = { 0x18c, BIT(17) },
> + [RST_BUS_R_I2C0] = { 0x19c, BIT(16) },
> + [RST_BUS_R_I2C1] = { 0x19c, BIT(17) },
> + [RST_BUS_R_I2C2] = { 0x19c, BIT(18) },
> + [RST_BUS_R_IR_RX] = { 0x1cc, BIT(16) },
> + [RST_BUS_R_RTC] = { 0x20c, BIT(16) },
> + [RST_BUS_R_RISCV_CFG] = { 0x21c, BIT(16) },
> + [RST_BUS_R_CPUCFG] = { 0x22c, BIT(16) },
> +};
> +
> +static const struct sunxi_ccu_desc sun60i_a733_r_ccu_desc = {
> + .ccu_clks = sun60i_a733_r_ccu_clks,
> + .num_ccu_clks = ARRAY_SIZE(sun60i_a733_r_ccu_clks),
> +
> + .hw_clks = &sun60i_a733_r_hw_clks,
> +
> + .resets = sun60i_a733_r_ccu_resets,
> + .num_resets = ARRAY_SIZE(sun60i_a733_r_ccu_resets),
> +};
> +
> +static int sun60i_a733_r_ccu_probe(struct platform_device *pdev)
> +{
> + void __iomem *reg;
> +
> + reg = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(reg))
> + return PTR_ERR(reg);
> +
> + return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun60i_a733_r_ccu_desc);
> +}
> +
> +static const struct of_device_id sun60i_a733_r_ccu_ids[] = {
> + { .compatible = "allwinner,sun60i-a733-r-ccu" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, sun60i_a733_r_ccu_ids);
> +
> +static struct platform_driver sun60i_a733_r_ccu_driver = {
> + .probe = sun60i_a733_r_ccu_probe,
> + .driver = {
> + .name = "sun60i-a733-r-ccu",
> + .suppress_bind_attrs = true,
> + .of_match_table = sun60i_a733_r_ccu_ids,
> + },
> +};
> +module_platform_driver(sun60i_a733_r_ccu_driver);
> +
> +MODULE_IMPORT_NS("SUNXI_CCU");
> +MODULE_DESCRIPTION("Support for the Allwinner A733 PRCM CCU");
> +MODULE_LICENSE("GPL");
The rest look OK.
ChenYu
^ permalink raw reply
* [PATCH net-next v7 0/5] TLS read_sock performance scalability
From: Chuck Lever @ 2026-03-28 15:17 UTC (permalink / raw)
To: john.fastabend, kuba, sd
Cc: netdev, kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
Alistair Francis
I'd like to encourage in-kernel kTLS consumers (i.e., NFS and
NVMe/TCP) to coalesce on the use of read_sock. When I suggested
this to Hannes, he reported a number of nagging performance
scalability issues with read_sock. This series is an attempt to
run these issues down and get them fixed before we convert the
above sock_recvmsg consumers over to read_sock.
Batch async decryption and its submit/deliver scaffolding were
dropped from this series because async_capable is always false
for TLS 1.3, which NFS and NVMe/TCP both require. Async crypto
support for TLS 1.3 is a prerequisite for revisiting that work.
---
Changes since v6:
- Rebased on net-next, v5's 1/6 was merged upstream
Changes since v5:
- Patch 6: Set released = true when sk_flush_backlog() returns
true, so tls_strp_msg_load() knows the socket lock was
released (Sabrina)
- Patch 6: Drop Fixes tag; submit bug fix separately via net
if warranted (Sabrina)
- Patch 6: Note redundant flush on cold path in commit message
(Sabrina)
Changes since v4:
- Drop batch async decryption and submit/deliver restructure:
async_capable is always false for TLS 1.3, so the new code
was unreachable for NFS and NVMe/TCP
- Purge async_hold directly in tls_decrypt_async_wait() and drop
the tls_decrypt_async_drain() wrapper
- Merge tls_strp_check_rcv_quiet() into tls_strp_check_rcv() with
a bool wake parameter; fix lost wakeup on the recvmsg exit path
Changes since v3:
- Clarify why tls_decrypt_async_drain() is separate from _wait()
- Fold tls_err_abort() into tls_rx_one_record(), drop tls_rx_decrypt_record()
- Move backlog flush into tls_rx_rec_wait() so all RX paths benefit
Changes since v2:
- Fix short read self tests
Changes since v1:
- Add C11 reference
- Extend data_ready reduction to recvmsg and splice
- Restructure read_sock and recvmsg using shared helpers
---
Chuck Lever (5):
tls: Abort the connection on decrypt failure
tls: Fix dangling skb pointer in tls_sw_read_sock()
tls: Factor tls_strp_msg_release() from tls_strp_msg_done()
tls: Suppress spurious saved_data_ready on all receive paths
tls: Flush backlog before waiting for a new record
net/tls/tls.h | 4 ++--
net/tls/tls_main.c | 2 +-
net/tls/tls_strp.c | 42 +++++++++++++++++++++++++++++++-----------
net/tls/tls_sw.c | 50 ++++++++++++++++++++++++++++++--------------------
4 files changed, 64 insertions(+), 34 deletions(-)
---
base-commit: ced629dc8e5c51ff2b5d847adeeb1035cd655d58
change-id: 20260317-tls-read-sock-a0022c9df265
Best regards,
--
Chuck Lever
^ permalink raw reply
* [PATCH net-next v7 1/5] tls: Abort the connection on decrypt failure
From: Chuck Lever @ 2026-03-28 15:17 UTC (permalink / raw)
To: john.fastabend, kuba, sd
Cc: netdev, kernel-tls-handshake, Chuck Lever, Hannes Reinecke
In-Reply-To: <20260328-tls-read-sock-v7-0-15678415dfc1@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
recvmsg, read_sock, and splice_read each open-code a
tls_err_abort() call after tls_rx_one_record() fails. Move
the abort into tls_rx_one_record() so each receive path
shares a single decrypt-and-abort sequence.
A tls_check_pending_rekey() failure after successful
decryption no longer triggers tls_err_abort(). That path
fires only when skb_copy_bits() fails on a valid skb,
which is not a realistic scenario.
Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls_sw.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 20f8fc84c5f5..5626fdd4ea0a 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1799,6 +1799,9 @@ static int tls_check_pending_rekey(struct sock *sk, struct tls_context *ctx,
return 0;
}
+/* Decrypt and return one TLS record. On decrypt failure the connection is
+ * aborted (sk_err set) before returning a negative errno.
+ */
static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
struct tls_decrypt_arg *darg)
{
@@ -1810,8 +1813,10 @@ static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
err = tls_decrypt_device(sk, msg, tls_ctx, darg);
if (!err)
err = tls_decrypt_sw(sk, tls_ctx, msg, darg);
- if (err < 0)
+ if (err < 0) {
+ tls_err_abort(sk, -EBADMSG);
return err;
+ }
rxm = strp_msg(darg->skb);
rxm->offset += prot->prepend_size;
@@ -2122,10 +2127,8 @@ int tls_sw_recvmsg(struct sock *sk,
darg.async = false;
err = tls_rx_one_record(sk, msg, &darg);
- if (err < 0) {
- tls_err_abort(sk, -EBADMSG);
+ if (err < 0)
goto recv_end;
- }
async |= darg.async;
@@ -2284,10 +2287,8 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
memset(&darg.inargs, 0, sizeof(darg.inargs));
err = tls_rx_one_record(sk, NULL, &darg);
- if (err < 0) {
- tls_err_abort(sk, -EBADMSG);
+ if (err < 0)
goto splice_read_end;
- }
tls_rx_rec_done(ctx);
skb = darg.skb;
@@ -2370,10 +2371,8 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
memset(&darg.inargs, 0, sizeof(darg.inargs));
err = tls_rx_one_record(sk, NULL, &darg);
- if (err < 0) {
- tls_err_abort(sk, -EBADMSG);
+ if (err < 0)
goto read_sock_end;
- }
released = tls_read_flush_backlog(sk, prot, INT_MAX,
0, decrypted,
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v7 2/5] tls: Fix dangling skb pointer in tls_sw_read_sock()
From: Chuck Lever @ 2026-03-28 15:17 UTC (permalink / raw)
To: john.fastabend, kuba, sd
Cc: netdev, kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
Alistair Francis
In-Reply-To: <20260328-tls-read-sock-v7-0-15678415dfc1@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
Per ISO/IEC 9899:2011 section 6.2.4p2, a pointer value becomes
indeterminate when the object it points to reaches the end of its
lifetime; Annex J.2 classifies the use of such a value as undefined
behavior. In tls_sw_read_sock(), consume_skb(skb) in the
fully-consumed path frees the skb, but the "do { } while (skb)"
loop condition then evaluates that freed pointer. Although the
value is never dereferenced -- the loop either continues and
overwrites skb, or exits -- any future change that adds a
dereference between consume_skb() and the loop condition would
produce a silent use-after-free.
Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()")
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls_sw.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 5626fdd4ea0a..5fdd43a55f1e 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2356,7 +2356,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_end;
decrypted = 0;
- do {
+ for (;;) {
if (!skb_queue_empty(&ctx->rx_list)) {
skb = __skb_dequeue(&ctx->rx_list);
rxm = strp_msg(skb);
@@ -2405,10 +2405,11 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_requeue;
} else {
consume_skb(skb);
+ skb = NULL;
if (!desc->count)
- skb = NULL;
+ break;
}
- } while (skb);
+ }
read_sock_end:
tls_rx_reader_release(sk, ctx);
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v7 3/5] tls: Factor tls_strp_msg_release() from tls_strp_msg_done()
From: Chuck Lever @ 2026-03-28 15:17 UTC (permalink / raw)
To: john.fastabend, kuba, sd
Cc: netdev, kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
Alistair Francis
In-Reply-To: <20260328-tls-read-sock-v7-0-15678415dfc1@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
tls_strp_msg_done() conflates releasing the current record with
checking for the next one via tls_strp_check_rcv(). Batch
processing requires releasing a record without immediately
triggering that check, so the release step is separated into
tls_strp_msg_release(). tls_strp_msg_done() is preserved as a
wrapper for existing callers.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls.h | 1 +
net/tls/tls_strp.c | 15 ++++++++++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/net/tls/tls.h b/net/tls/tls.h
index e8f81a006520..a97f1acef31d 100644
--- a/net/tls/tls.h
+++ b/net/tls/tls.h
@@ -193,6 +193,7 @@ int tls_strp_init(struct tls_strparser *strp, struct sock *sk);
void tls_strp_data_ready(struct tls_strparser *strp);
void tls_strp_check_rcv(struct tls_strparser *strp);
+void tls_strp_msg_release(struct tls_strparser *strp);
void tls_strp_msg_done(struct tls_strparser *strp);
int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb);
diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c
index 98e12f0ff57e..a7648ebde162 100644
--- a/net/tls/tls_strp.c
+++ b/net/tls/tls_strp.c
@@ -581,7 +581,16 @@ static void tls_strp_work(struct work_struct *w)
release_sock(strp->sk);
}
-void tls_strp_msg_done(struct tls_strparser *strp)
+/**
+ * tls_strp_msg_release - release the current strparser message
+ * @strp: TLS stream parser instance
+ *
+ * Release the current record without triggering a check for the
+ * next record. Callers must invoke tls_strp_check_rcv() before
+ * releasing the socket lock, or queued data will stall until
+ * the next tls_strp_data_ready() event.
+ */
+void tls_strp_msg_release(struct tls_strparser *strp)
{
WARN_ON(!strp->stm.full_len);
@@ -592,7 +601,11 @@ void tls_strp_msg_done(struct tls_strparser *strp)
WRITE_ONCE(strp->msg_ready, 0);
memset(&strp->stm, 0, sizeof(strp->stm));
+}
+void tls_strp_msg_done(struct tls_strparser *strp)
+{
+ tls_strp_msg_release(strp);
tls_strp_check_rcv(strp);
}
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v7 4/5] tls: Suppress spurious saved_data_ready on all receive paths
From: Chuck Lever @ 2026-03-28 15:17 UTC (permalink / raw)
To: john.fastabend, kuba, sd
Cc: netdev, kernel-tls-handshake, Chuck Lever, Alistair Francis,
Hannes Reinecke
In-Reply-To: <20260328-tls-read-sock-v7-0-15678415dfc1@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
Each record release via tls_strp_msg_done() triggers
tls_strp_check_rcv(), which calls tls_rx_msg_ready() and
fires saved_data_ready(). During a multi-record receive,
the first N-1 wakeups are pure overhead: the caller is
already running and will pick up subsequent records on
the next loop iteration. On the splice_read path the
per-record wakeup is similarly unnecessary because the
caller still holds the socket lock.
Replace tls_strp_msg_done() with tls_strp_msg_release()
in all three receive paths (read_sock, recvmsg,
splice_read), deferring the tls_strp_check_rcv() call
to each path's exit point. Factor tls_rx_msg_ready()
out of tls_strp_read_sock() so that parsing a record
no longer fires the callback directly, and add a @wake
parameter to tls_strp_check_rcv() so callers can parse
queued data without notifying.
With no remaining callers, tls_strp_msg_done() and its
wrapper tls_rx_rec_done() are removed.
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls.h | 3 +--
net/tls/tls_main.c | 2 +-
net/tls/tls_strp.c | 35 +++++++++++++++++++++--------------
net/tls/tls_sw.c | 22 +++++++++++++++-------
4 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/net/tls/tls.h b/net/tls/tls.h
index a97f1acef31d..f41dac6305f4 100644
--- a/net/tls/tls.h
+++ b/net/tls/tls.h
@@ -192,9 +192,8 @@ void tls_strp_stop(struct tls_strparser *strp);
int tls_strp_init(struct tls_strparser *strp, struct sock *sk);
void tls_strp_data_ready(struct tls_strparser *strp);
-void tls_strp_check_rcv(struct tls_strparser *strp);
+void tls_strp_check_rcv(struct tls_strparser *strp, bool wake);
void tls_strp_msg_release(struct tls_strparser *strp);
-void tls_strp_msg_done(struct tls_strparser *strp);
int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb);
void tls_rx_msg_ready(struct tls_strparser *strp);
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index fd39acf41a61..c10a3fd7fc17 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -769,7 +769,7 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
} else {
struct tls_sw_context_rx *rx_ctx = tls_sw_ctx_rx(ctx);
- tls_strp_check_rcv(&rx_ctx->strp);
+ tls_strp_check_rcv(&rx_ctx->strp, true);
}
return 0;
diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c
index a7648ebde162..b0b2ca92fa99 100644
--- a/net/tls/tls_strp.c
+++ b/net/tls/tls_strp.c
@@ -368,7 +368,6 @@ static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb,
desc->count = 0;
WRITE_ONCE(strp->msg_ready, 1);
- tls_rx_msg_ready(strp);
}
return ret;
@@ -539,18 +538,32 @@ static int tls_strp_read_sock(struct tls_strparser *strp)
return tls_strp_read_copy(strp, false);
WRITE_ONCE(strp->msg_ready, 1);
- tls_rx_msg_ready(strp);
return 0;
}
-void tls_strp_check_rcv(struct tls_strparser *strp)
+/**
+ * tls_strp_check_rcv - parse queued data and optionally notify
+ * @strp: TLS stream parser instance
+ * @wake: if true, fire consumer notification when a message is ready
+ *
+ * When @wake is false, queued data is parsed without consumer
+ * notification. A subsequent call with @wake set to true is
+ * required before the socket lock is released; otherwise queued
+ * data stalls until the next tls_strp_data_ready() event.
+ */
+void tls_strp_check_rcv(struct tls_strparser *strp, bool wake)
{
- if (unlikely(strp->stopped) || strp->msg_ready)
+ if (unlikely(strp->stopped))
return;
- if (tls_strp_read_sock(strp) == -ENOMEM)
- queue_work(tls_strp_wq, &strp->work);
+ if (!strp->msg_ready) {
+ if (tls_strp_read_sock(strp) == -ENOMEM)
+ queue_work(tls_strp_wq, &strp->work);
+ }
+
+ if (wake && strp->msg_ready)
+ tls_rx_msg_ready(strp);
}
/* Lower sock lock held */
@@ -568,7 +581,7 @@ void tls_strp_data_ready(struct tls_strparser *strp)
return;
}
- tls_strp_check_rcv(strp);
+ tls_strp_check_rcv(strp, true);
}
static void tls_strp_work(struct work_struct *w)
@@ -577,7 +590,7 @@ static void tls_strp_work(struct work_struct *w)
container_of(w, struct tls_strparser, work);
lock_sock(strp->sk);
- tls_strp_check_rcv(strp);
+ tls_strp_check_rcv(strp, true);
release_sock(strp->sk);
}
@@ -603,12 +616,6 @@ void tls_strp_msg_release(struct tls_strparser *strp)
memset(&strp->stm, 0, sizeof(strp->stm));
}
-void tls_strp_msg_done(struct tls_strparser *strp)
-{
- tls_strp_msg_release(strp);
- tls_strp_check_rcv(strp);
-}
-
void tls_strp_stop(struct tls_strparser *strp)
{
strp->stopped = 1;
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 5fdd43a55f1e..8fb2f2a93846 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1373,7 +1373,11 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
return ret;
if (!skb_queue_empty(&sk->sk_receive_queue)) {
- tls_strp_check_rcv(&ctx->strp);
+ /* Defer notification to the exit point;
+ * this thread will consume the record
+ * directly.
+ */
+ tls_strp_check_rcv(&ctx->strp, false);
if (tls_strp_msg_ready(ctx))
break;
}
@@ -1859,9 +1863,9 @@ static int tls_record_content_type(struct msghdr *msg, struct tls_msg *tlm,
return 1;
}
-static void tls_rx_rec_done(struct tls_sw_context_rx *ctx)
+static void tls_rx_rec_release(struct tls_sw_context_rx *ctx)
{
- tls_strp_msg_done(&ctx->strp);
+ tls_strp_msg_release(&ctx->strp);
}
/* This function traverses the rx_list in tls receive context to copies the
@@ -2142,7 +2146,7 @@ int tls_sw_recvmsg(struct sock *sk,
err = tls_record_content_type(msg, tls_msg(darg.skb), &control);
if (err <= 0) {
DEBUG_NET_WARN_ON_ONCE(darg.zc);
- tls_rx_rec_done(ctx);
+ tls_rx_rec_release(ctx);
put_on_rx_list_err:
__skb_queue_tail(&ctx->rx_list, darg.skb);
goto recv_end;
@@ -2156,7 +2160,8 @@ int tls_sw_recvmsg(struct sock *sk,
/* TLS 1.3 may have updated the length by more than overhead */
rxm = strp_msg(darg.skb);
chunk = rxm->full_len;
- tls_rx_rec_done(ctx);
+ tls_rx_rec_release(ctx);
+ tls_strp_check_rcv(&ctx->strp, false);
if (!darg.zc) {
bool partially_consumed = chunk > len;
@@ -2250,6 +2255,7 @@ int tls_sw_recvmsg(struct sock *sk,
copied += decrypted;
end:
+ tls_strp_check_rcv(&ctx->strp, true);
tls_rx_reader_unlock(sk, ctx);
if (psock)
sk_psock_put(sk, psock);
@@ -2290,7 +2296,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
if (err < 0)
goto splice_read_end;
- tls_rx_rec_done(ctx);
+ tls_rx_rec_release(ctx);
skb = darg.skb;
}
@@ -2317,6 +2323,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
consume_skb(skb);
splice_read_end:
+ tls_strp_check_rcv(&ctx->strp, true);
tls_rx_reader_unlock(sk, ctx);
return copied ? : err;
@@ -2382,7 +2389,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
tlm = tls_msg(skb);
decrypted += rxm->full_len;
- tls_rx_rec_done(ctx);
+ tls_rx_rec_release(ctx);
}
/* read_sock does not support reading control messages */
@@ -2412,6 +2419,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
}
read_sock_end:
+ tls_strp_check_rcv(&ctx->strp, true);
tls_rx_reader_release(sk, ctx);
return copied ? : err;
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v7 5/5] tls: Flush backlog before waiting for a new record
From: Chuck Lever @ 2026-03-28 15:17 UTC (permalink / raw)
To: john.fastabend, kuba, sd
Cc: netdev, kernel-tls-handshake, Chuck Lever, Hannes Reinecke
In-Reply-To: <20260328-tls-read-sock-v7-0-15678415dfc1@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
While lock_sock is held, incoming TCP segments land on
sk->sk_backlog rather than sk->sk_receive_queue.
tls_rx_rec_wait() inspects only sk_receive_queue, so
backlog data remains invisible. For non-blocking callers
(read_sock, and recvmsg or splice_read with MSG_DONTWAIT)
this causes a spurious -EAGAIN. For blocking callers it
forces an unnecessary sleep/wakeup cycle.
Flush the backlog inside tls_rx_rec_wait() before checking
sk_receive_queue so the strparser can parse newly-arrived
segments immediately. On the next loop iteration
tls_read_flush_backlog() may redundantly flush, but this
path is cold and the cost is negligible.
Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls_sw.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 8fb2f2a93846..e15adf250d78 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1372,6 +1372,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
if (ret < 0)
return ret;
+ if (sk_flush_backlog(sk))
+ released = true;
if (!skb_queue_empty(&sk->sk_receive_queue)) {
/* Defer notification to the exit point;
* this thread will consume the record
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net-next 5/5] selftests: net: add veth BQL stress test
From: Simon Schippers @ 2026-03-28 15:19 UTC (permalink / raw)
To: hawk
Cc: andrew+netdev, davem, edumazet, horms, j.koeppeler, jhs, jiri,
kernel-team, kuba, netdev, pabeni
In-Reply-To: <20260324174719.1224337-7-hawk@kernel.org>
Hi, thanks for your work! I am really interested in this patchset.
I am planning to submit a similar patch set (see [1]) for the tun/tap
driver, where I am currently implementing qdisc backpressure similar
to that used in veth.
Can you run pktgen [2] to see if there is a regression?
I think that there might be a slowdown due to BQL not choosing a big
enough queue size.
Thanks!
[1] Link: https://lore.kernel.org/all/20260312130639.138988-1-simon.schippers@tu-dortmund.de/
[2] Link: https://www.kernel.org/doc/html/latest/networking/pktgen.html
^ permalink raw reply
* Re: [PATCH v8 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: David Laight @ 2026-03-28 10:08 UTC (permalink / raw)
To: Pawan Gupta
Cc: Borislav Petkov, x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin,
Josh Poimboeuf, David Kaplan, Sean Christopherson, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
Andy Lutomirski, Thomas Gleixner, Ingo Molnar, David Ahern,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, Stanislav Fomichev, Hao Luo, Paolo Bonzini,
Jonathan Corbet, linux-kernel, kvm, Asit Mallick, Tao Zhang, bpf,
netdev, linux-doc
In-Reply-To: <20260328004256.mm2ttj5iwvu5kdpa@desk>
On Fri, 27 Mar 2026 17:42:56 -0700
Pawan Gupta <pawan.kumar.gupta@linux.intel.com> wrote:
> On Thu, Mar 26, 2026 at 01:29:31PM -0700, Pawan Gupta wrote:
> > On Thu, Mar 26, 2026 at 10:45:57AM +0000, David Laight wrote:
> > > On Thu, 26 Mar 2026 11:01:20 +0100
> > > Borislav Petkov <bp@alien8.de> wrote:
> > >
> > > > On Thu, Mar 26, 2026 at 01:39:34AM -0700, Pawan Gupta wrote:
> > > > > I believe the equivalent for cpu_feature_enabled() in asm is the
> > > > > ALTERNATIVE. Please let me know if I am missing something.
> > > >
> > > > Yes, you are.
> > > >
> > > > The point is that you don't want to stick those alternative calls inside some
> > > > magic bhb_loop function but hand them in from the outside, as function
> > > > arguments.
> > > >
> > > > Basically what I did.
> > > >
> > > > Then you were worried about this being C code and it had to be noinstr... So
> > > > that outer function can be rewritten in asm, I think, and still keep it well
> > > > separate.
> > > >
> > > > I'll try to rewrite it once I get a free minute, and see how it looks.
> > > >
> > >
> > > I think someone tried getting C code to write the values to global data
> > > and getting the asm to read them.
> > > That got discounted because it spilt things between two largely unrelated files.
> >
> >
> > The implementation with global variables wasn't that bad, let me revive it.
> >
> > This part which ties sequence to BHI mitigation, which is not ideal,
> > (because VMSCAPE also uses it) it does seems a cleaner option.
> >
> > --- a/arch/x86/kernel/cpu/bugs.c
> > +++ b/arch/x86/kernel/cpu/bugs.c
> > @@ -2095,6 +2095,11 @@ static void __init bhi_select_mitigation(void)
> >
> > static void __init bhi_update_mitigation(void)
> > {
> > + if (!cpu_feature_enabled(X86_FEATURE_BHI_CTRL)) {
> > + bhi_seq_outer_loop = 5;
> > + bhi_seq_inner_loop = 5;
> > + }
> > +
> >
> > I believe this can be moved to somewhere common to all mitigations.
> >
> > > I think the BPF code would need significant refactoring to call a C function.
> >
> > Ya, true. Will use globals and keep clear_bhb_loop() in asm.
>
> While testing this approach, I noticed that syscalls were suffering an 8%
> regression on ICX for Native BHI mitigation:
>
> $ perf bench syscall basic -l 100000000
>
> Bisection pointed to the change for using 8-bit registers (al/ah replacing
> eax/ecx) as the main contributor to the regression. (Global variables added
> a bit, but within noise).
>
> Further digging revealed a strange behavior, using %ah for the inner loop
> was causing the regression, interchanging %al and %ah in the loops
> (for movb and sub) eliminated the regression.
>
> <clear_bhb_loop_nofence>:
>
> movb bhb_seq_outer_loop(%rip), %al
>
> call 1f
> jmp 5f
> 1: call 2f
> .Lret1: RET
> 2: movb bhb_seq_inner_loop(%rip), %ah
> 3: jmp 4f
> nop
> 4: sub $1, %ah <---- No regression with %al here
> jnz 3b
> sub $1, %al
> jnz 1b
>
> My guess is, "sub $1, %al" is faster than "sub $1, %ah". Using %al in the
> inner loop, which is executed more number of times is likely making the
> difference. A perf profile is needed to confirm this.
I bet it is also CPU dependant - it is quite likely that there isn't
any special hardware to support partial writes of %ah so it ends up taking
a slow path (possibly even a microcoded one to get an 8% regression).
As well as swapping %al <-> %ah try changing the outer loop decrement to
sub $0x100, %ax
since %al is zero that will set the z flag the same.
I've just hacked a test into some test code I've got.
I'm not seeing an unexpected costs on either zen-5 or haswell.
So it may be more subtle.
David
>
> Never imagined a register selection can make an 8% difference in
> performance! Anyways, will update the patch with this finding.
^ permalink raw reply
* [PATCH net v3] rtnetlink: add missing netlink_ns_capable() check for peer netns
From: Nikolaos Gkarlis @ 2026-03-28 13:52 UTC (permalink / raw)
To: netdev; +Cc: kuba, nickgarlis, kuniyu
rtnl_newlink() lacks a CAP_NET_ADMIN capability check on the peer
network namespace when creating paired devices (veth, vxcan,
netkit). This allows an unprivileged user with a user namespace
to create interfaces in arbitrary network namespaces, including
init_net.
Add a netlink_ns_capable() check for CAP_NET_ADMIN in the peer
namespace before allowing device creation to proceed.
Fixes: 81adee47dfb6 ("net: Support specifying the network namespace upon device creation.")
Signed-off-by: Nikolaos Gkarlis <nickgarlis@gmail.com>
---
v3:
- Move netlink_ns_capable() check from rtnl_newlink() into
rtnl_get_peer_net(), after the last rtnl_link_get_net_ifla(tb)
call. The tbp path is already covered by rtnl_link_get_net_capable()
in the caller. (suggested by Kuniyuki)
- Pass skb to rtnl_get_peer_net() for the capability check.
- Add IS_ERR() check on rtnl_link_get_net_ifla(tb) return value.
v2:
- Removed "Reported-by" tag
- Fixed "Fixes" tag with the help of Kuniyuki Iwashima (thanks !)
net/core/rtnetlink.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index fae8034efbf..4be7fc6e23a 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3894,12 +3894,14 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
goto out;
}
-static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops,
+static struct net *rtnl_get_peer_net(struct sk_buff *skb,
+ const struct rtnl_link_ops *ops,
struct nlattr *tbp[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct nlattr *tb[IFLA_MAX + 1];
+ struct net *net;
int err;
if (!data || !data[ops->peer_type])
@@ -3915,7 +3917,19 @@ static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops,
return ERR_PTR(err);
}
- return rtnl_link_get_net_ifla(tb);
+ net = rtnl_link_get_net_ifla(tb);
+ if (IS_ERR(net))
+ return net;
+
+ if (!net)
+ return NULL;
+
+ if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
+ put_net(net);
+ return ERR_PTR(-EPERM);
+ }
+
+ return net;
}
static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -4054,7 +4068,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
}
if (ops->peer_type) {
- peer_net = rtnl_get_peer_net(ops, tb, data, extack);
+ peer_net = rtnl_get_peer_net(skb, ops, tb, data, extack);
if (IS_ERR(peer_net)) {
ret = PTR_ERR(peer_net);
goto put_ops;
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v2] net: phy: air_en8811h: add AN8811HB MCU assert/deassert support
From: Eric Woudstra @ 2026-03-28 10:21 UTC (permalink / raw)
To: Lucien.Jheng, andrew, hkallweit1, linux, davem, edumazet, kuba,
pabeni, netdev, linux-kernel, bjorn
Cc: frank-w, daniel, lucien.jheng
In-Reply-To: <edeee8e5-0de3-48ea-aeb5-cc705fdcd469@gmail.com>
On 3/27/26 5:41 AM, Eric Woudstra wrote:
> static int __air_pbus_reg_write(struct mdio_device *mdio, u32 pbus_address,
> u32 pbus_data)
> {
> int ret;
>
> ret = __mdiobus_write(mdio->bus, mdio->addr, AIR_EXT_PAGE_ACCESS,
> (u16)(pbus_address >> 6));
> if (ret < 0)
> return ret;
>
> ret = __mdiobus_write(mdio->bus, mdio->addr, (pbus_address >> 2) & 0xf,
> (u16)pbus_data);
> if (ret < 0)
> return ret;
>
> ret = __mdiobus_write(mdio->bus, mdio->addr, AIR_PBUS_DATA_HIGH,
> (u16)(pbus_data >> 16));
> if (ret < 0)
> return ret;
>
> return 0;
> }
>
> static int air_pbus_reg_write(struct mdio_device *mdio, u32 pbus_address,
> u32 pbus_data)
> {
> int ret;
>
> mutex_lock(&mdio->bus->mdio_lock);
>
> ret = __air_pbus_reg_write(mdio, pbus_address, pbus_data);
> if (ret < 0)
> dev_err(&mdio->dev, "%s 0x%08x failed: %d\n", __func__,
> pbus_address, ret);
>
> mutex_unlock(&mdio->bus->mdio_lock);
>
> return ret;
> }
>
> static int __air_pbus_reg_read(struct mdio_device *mdio, u32 pbus_address,
> u32 *pbus_data)
> {
> int ret, pbus_data_low;
>
> ret = __mdiobus_write(mdio->bus, mdio->addr, AIR_EXT_PAGE_ACCESS,
> (u16)(pbus_address >> 6));
> if (ret < 0)
> return ret;
>
> ret = __mdiobus_read(mdio->bus, mdio->addr, (pbus_address >> 2) & 0xf);
> if (ret < 0)
> return ret;
> pbus_data_low = ret;
>
> ret = __mdiobus_read(mdio->bus, mdio->addr, AIR_PBUS_DATA_HIGH);
> if (ret < 0)
> return ret;
>
> *pbus_data = (ret << 16) + pbus_data_low;
>
> return 0;
> }
>
> static int air_pbus_reg_read(struct mdio_device *mdio, u32 pbus_address,
> u32 *pbus_data)
> {
> int ret;
>
> mutex_lock(&mdio->bus->mdio_lock);
>
> ret = __air_pbus_reg_read(mdio, pbus_address, pbus_data);
> if (ret < 0)
> dev_err(&mdio->dev, "%s 0x%08x failed: %d\n", __func__,
> pbus_address, ret);
>
> mutex_unlock(&mdio->bus->mdio_lock);
>
> return ret;
> }
>
> With:
>
> #define AIR_PBUS_DATA_HIGH 0x10
Small correction using upper_16_bits() and lower_16_bits():
static int __air_pbus_reg_write(struct mdio_device *mdio, u32 pbus_address,
u32 pbus_data)
{
int ret;
ret = __mdiobus_write(mdio->bus, mdio->addr, AIR_EXT_PAGE_ACCESS,
(u16)(pbus_address >> 6));
if (ret < 0)
return ret;
ret = __mdiobus_write(mdio->bus, mdio->addr, (pbus_address >> 2) & 0xf,
lower_16_bits(pbus_data));
if (ret < 0)
return ret;
ret = __mdiobus_write(mdio->bus, mdio->addr, AIR_PBUS_DATA_HIGH,
upper_16_bits(pbus_data));
if (ret < 0)
return ret;
return 0;
}
static int air_pbus_reg_write(struct mdio_device *mdio, u32 pbus_address,
u32 pbus_data)
{
int ret;
mutex_lock(&mdio->bus->mdio_lock);
ret = __air_pbus_reg_write(mdio, pbus_address, pbus_data);
if (ret < 0)
dev_err(&mdio->dev, "%s 0x%08x failed: %d\n", __func__,
pbus_address, ret);
mutex_unlock(&mdio->bus->mdio_lock);
return ret;
}
^ permalink raw reply
* Re: [PATCH iproute2-next] dpll: Send object per event in JSON monitor mode
From: Vitaly Grinberg @ 2026-03-28 16:17 UTC (permalink / raw)
To: vgrinber, netdev; +Cc: stephen
In-Reply-To: <CACLnSDidL2kgT4z6bdHuhPZ+Y8Vb0dgj2tNNj9VDPJ31-gCsoQ@mail.gmail.com>
Hello,
Please ignore this version; I found an indentation problem and will be sending a v2 shortly.
Best regards,
Vitaly
^ permalink raw reply
* [PATCH net v2] net: ftgmac100: fix ring allocation unwind on open failure
From: Yufan Chen @ 2026-03-28 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-kernel
Cc: yufan.chen
ftgmac100_alloc_rings() allocates rx_skbs, tx_skbs, rxdes, txdes, and
rx_scratch in stages. On intermediate failures it returned -ENOMEM
directly, leaking resources allocated earlier in the function.
Rework the failure path to use staged local unwind labels and free
allocated resources in reverse order before returning -ENOMEM. This
matches common netdev allocation cleanup style.
Fixes: d72e01a0430f ("ftgmac100: Use a scratch buffer for failed RX allocations")
Cc: stable@vger.kernel.org
Signed-off-by: Yufan Chen <yufan.chen@linux.dev>
---
drivers/net/ethernet/faraday/ftgmac100.c | 28 ++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 1e91e79c8..6d2fe5c2f 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -977,19 +977,19 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *),
GFP_KERNEL);
if (!priv->tx_skbs)
- return -ENOMEM;
+ goto err_free_rx_skbs;
/* Allocate descriptors */
priv->rxdes = dma_alloc_coherent(priv->dev,
MAX_RX_QUEUE_ENTRIES * sizeof(struct ftgmac100_rxdes),
&priv->rxdes_dma, GFP_KERNEL);
if (!priv->rxdes)
- return -ENOMEM;
+ goto err_free_tx_skbs;
priv->txdes = dma_alloc_coherent(priv->dev,
MAX_TX_QUEUE_ENTRIES * sizeof(struct ftgmac100_txdes),
&priv->txdes_dma, GFP_KERNEL);
if (!priv->txdes)
- return -ENOMEM;
+ goto err_free_rxdes;
/* Allocate scratch packet buffer */
priv->rx_scratch = dma_alloc_coherent(priv->dev,
@@ -997,9 +997,29 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
&priv->rx_scratch_dma,
GFP_KERNEL);
if (!priv->rx_scratch)
- return -ENOMEM;
+ goto err_free_txdes;
return 0;
+
+err_free_txdes:
+ dma_free_coherent(priv->dev,
+ MAX_TX_QUEUE_ENTRIES *
+ sizeof(struct ftgmac100_txdes),
+ priv->txdes, priv->txdes_dma);
+ priv->txdes = NULL;
+err_free_rxdes:
+ dma_free_coherent(priv->dev,
+ MAX_RX_QUEUE_ENTRIES *
+ sizeof(struct ftgmac100_rxdes),
+ priv->rxdes, priv->rxdes_dma);
+ priv->rxdes = NULL;
+err_free_tx_skbs:
+ kfree(priv->tx_skbs);
+ priv->tx_skbs = NULL;
+err_free_rx_skbs:
+ kfree(priv->rx_skbs);
+ priv->rx_skbs = NULL;
+ return -ENOMEM;
}
static void ftgmac100_init_rings(struct ftgmac100 *priv)
--
2.47.3
^ permalink raw reply related
* [PATCH] xfrm6: fix slab-out-of-bounds write in xfrm6_input_addr()
From: nicholas @ 2026-03-28 16:35 UTC (permalink / raw)
To: netdev
Cc: Steffen Klassert, Herbert Xu, David S . Miller, Milad Nasr,
Nicholas Carlini
From: Nicholas Carlini <nicholas@carlini.com>
The bounds check guarding sp->xvec[sp->len++] uses == where >= is
required. When sp->len has already reached XFRM_MAX_DEPTH via prior
ESP processing in xfrm_input(), the check (1 + 6 == 6) is false and
the write goes out of bounds into the adjacent skbuff_ext_cache slab
object.
An unprivileged local user can trigger this by entering a
user+network namespace, configuring six transport-mode ESP SAs plus
one MIP6 routing SA, and injecting an IPv6 packet with six ESP
layers followed by multiple Routing Header Type 2 extensions.
The check was correct (>) when the function was introduced, but
was changed to == during a refactor in 2007.
Fixes: 9473e1f631de ("[XFRM] MIPv6: Fix to input RO state correctly.")
Reported-by: Milad Nasr <srxzr@anthropic.com>
Signed-off-by: Nicholas Carlini <nicholas@carlini.com>
---
net/ipv6/xfrm6_input.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index 9005fc156a20..a958c08589d6 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -246,7 +246,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
goto drop;
}
- if (1 + sp->len == XFRM_MAX_DEPTH) {
+ if (1 + sp->len >= XFRM_MAX_DEPTH) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
goto drop;
}
--
2.39.5
^ permalink raw reply related
* Re: [PATCH net-next 2/2] net: stmmac: simplify GSO/TSO test in stmmac_xmit()
From: Russell King (Oracle) @ 2026-03-28 17:25 UTC (permalink / raw)
To: Andrew Lunn, Ong Boon Leong
Cc: Alexandre Torgue, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, linux-arm-kernel, linux-stm32, netdev,
Paolo Abeni
In-Reply-To: <acegAqUb-Dzy87d8@shell.armlinux.org.uk>
On Sat, Mar 28, 2026 at 09:31:46AM +0000, Russell King (Oracle) wrote:
> On Sat, Mar 28, 2026 at 08:24:37AM +0000, Russell King (Oracle) wrote:
> > On Fri, Mar 27, 2026 at 09:40:09AM +0000, Russell King (Oracle) wrote:
> > > The test in stmmac_xmit() to see whether we should pass the skbuff to
> > > stmmac_tso_xmit() is more complex than it needs to be. This test can
> > > be simplified by storing the mask of GSO types that we will pass, and
> > > setting it according to the enabled features.
> > >
> > > Note that "tso" is a mis-nomer since commit b776620651a1 ("net:
> > > stmmac: Implement UDP Segmentation Offload"). Also note that this
> > > commit controls both via the TSO feature. We preserve this behaviour
> > > in this commit.
> > >
> > > Also, this commit unconditionally accessed skb_shinfo(skb)->gso_type
> > > for all frames, even when skb_is_gso() was false. This access is
> > > eliminated.
> > >
> > > Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
> >
> > AI review of this patch regurgitates Jakub's point that was discussed.
> >
> > > @@ -3700,7 +3700,7 @@ static int stmmac_hw_setup(struct net_device *dev)
> > > stmmac_set_rings_length(priv);
> > >
> > > /* Enable TSO */
> > > - if (priv->tso) {
> > > + if (priv->gso_enabled_types) {
> > > for (chan = 0; chan < tx_cnt; chan++) {
> > > struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
> > >
> >
> > ...
> >
> > > @@ -7828,7 +7834,7 @@ static int __stmmac_dvr_probe(struct device *device,
> > > ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
> > > if (priv->plat->core_type == DWMAC_CORE_GMAC4)
> > > ndev->hw_features |= NETIF_F_GSO_UDP_L4;
> > > - priv->tso = true;
> > > + stmmac_set_gso_types(priv, true);
> >
> > Clearly, the issue it is regurgitating has been there for a long time
> > and isn't a new issue introduced by this patch.
> >
> > AI needs to stop doing this, because it is encouraging multiple changes
> > in a single patch, which is against the normal kernel process.
> >
> > As already pointed out, there are multiple issues with stmmac TSO
> > support, particularly with glue drivers that enable TSO on some
> > queues/channels and not others, since netdev core TSO support is
> > global across all channels.
> >
> > So, won't the AI response in this patch - it's just another pre-
> > existing issue that needs fixing in a separate patch.
>
> Looking at the TSO vs TBS issue (which precludes the use of TSO on a
> channel in stmmac) I can't find an obvious reason for this in the
> available documentation. However, unfortunately, iMX8MP doesn't support
> TSO, so the TSO bits are elided there, but does support TBS (needing
> enhanced descriptors to be enabled). STM32MP151 on the other hand
> supports TSO but not TBS, and thus fails to mention anything about
> enhanced descriptors or TBS.
>
> When stmmac_enable_tbs() enables TBS, it isn't actually enabling a
> feature specific bit, but switching the channel to use enhanced
> descriptor format. This format extends the basic descriptors by
> placing four extra 32-bit words before the basic descriptor.
>
> Looking at the enhanced normal descriptor format for TDES3, it
> indicates that the format includes bit 18 in the control field, which
> is the TSE bit (TCP segmentation enable for this packet.) So, it seems
> it's not a limitation of the descriptor format.
>
> So, either "TSO and TBS cannot co-exist" is incorrect, or there is a
> hardware limitation that isn't documented between these two manuals.
>
> One other interesting point is that stmmac_tso_xmit() seems to
> handle the case where TSO and TBS are enabled on the channel:
>
> if (tx_q->tbs & STMMAC_TBS_AVAIL)
> mss_desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
> else
> mss_desc = &tx_q->dma_tx[tx_q->cur_tx];
>
> stmmac_set_mss(priv, mss_desc, mss);
> ...
> if (tx_q->tbs & STMMAC_TBS_AVAIL)
> desc = &tx_q->dma_entx[first_entry].basic;
> else
> desc = &tx_q->dma_tx[first_entry];
> first = desc;
>
> etc.
>
> Avoiding enabling TSO for a TBS channel was added by this commit:
>
> commit 5e6038b88a5718910dd74b949946d9d9cee9a041
> Author: Ong Boon Leong <boon.leong.ong@intel.com>
> Date: Wed Apr 21 17:11:49 2021 +0800
>
> net: stmmac: fix TSO and TBS feature enabling during driver open
>
> TSO and TBS cannot co-exist and current implementation requires two
> fixes:
>
> 1) stmmac_open() does not need to call stmmac_enable_tbs() because
> the MAC is reset in stmmac_init_dma_engine() anyway.
> 2) Inside stmmac_hw_setup(), we should call stmmac_enable_tso() for
> TX Q that is _not_ configured for TBS.
>
> Fixes: 579a25a854d4 ("net: stmmac: Initial support for TBS")
> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com>
> Signed-off-by: David S. Miller <davem@davemloft.net>
>
> which doesn't really explain the background, and leaves all the TBS
> cruft in the TSO transmit path (nothing like properly updating the
> driver, eh? No wonder stmmac is such a mess!)
The more I look at this, the more I'm convinced this commit is
incorrect, even if it is the case that the hardware doesn't support
TSO and TBS together.
When TSO is enabled (NETIF_F_TSO set in the netif's features) then
the core net layer can submit skbuffs that need to be processed using
TSO.
If such a skbuff hits a channel that has TSO disabled (because the
above commit caused:
stmmac_enable_tso(priv, priv->ioaddr, 1, chan);
not to be called) then the TSE bit in the transmit control register
will not be set, thereby disabling TSO on this particular channel.
However, stmmac_xmit() will still call through to stmmac_tso_xmit()
which will dutifully populate the transmit ring with descriptors that
assume TSE has been set in the transmit control register.
It seems to me _that_ is even more broken than "the hardware doesn't
support TSO and TBS together" - I have no idea what the stmmac hardware
does if it encounters descriptors with TSE set but TSE is disabled in
the transmit control register. My guess would be it would ignore the
TSE bit in the descriptor and assume that it's one very large packet
to be sent - and either error out because it's longer than the
maximum the hardware can support or it will just try to transmit it
anyway.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH net v6] bnxt_en: validate firmware backing store types
From: Michael Chan @ 2026-03-28 18:15 UTC (permalink / raw)
To: Pengpeng Hou
Cc: pavan.chebbi, andrew+netdev, davem, edumazet, kuba, pabeni,
netdev, linux-kernel
In-Reply-To: <20260328060824.8383-1-pengpeng@iscas.ac.cn>
[-- Attachment #1: Type: text/plain, Size: 1433 bytes --]
On Fri, Mar 27, 2026 at 11:08 PM Pengpeng Hou <pengpeng@iscas.ac.cn> wrote:
>
> bnxt_hwrm_func_backing_store_qcaps_v2() stores resp->type from the
> firmware response in ctxm->type and later uses that value to index
> fixed backing-store metadata arrays such as ctx_arr[] and
> bnxt_bstore_to_trace[].
>
> ctxm->type is fixed by the current backing-store query type and matches
> the array index of ctx->ctx_arr. Avoid depending on resp->type and assign
> ctxm->type from the current loop variable instead. Keep next_valid_type in
> a dedicated variable so loop control stays clear for non-valid or
> unchanged entries.
Please change the title of the commit since you are not validating the
type in the FW response anymore.
> @@ -8700,22 +8701,24 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp)
> if (rc)
> goto ctx_done;
> flags = le32_to_cpu(resp->flags);
> - type = le16_to_cpu(resp->next_valid_type);
> + next_type = le16_to_cpu(resp->next_valid_type);
> if (!(flags & BNXT_CTX_MEM_TYPE_VALID)) {
> bnxt_free_one_ctx_mem(bp, ctxm, true);
> + type = next_type;
You can just update type = next_type in the for loop statement instead
of updating it in 3 different places:
for (type = 0; type < BNXT_CTX_V2_MAX; type = next_type)
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply
* [PATCH net 0/5] net/sched: netem: bug fixes found during AI-assisted review
From: Stephen Hemminger @ 2026-03-28 18:21 UTC (permalink / raw)
To: netdev; +Cc: Stephen Hemminger
These bugs were identified while using AI-assisted code review of
sch_netem.c to analyze the packet duplication re-entrancy problem
(CVE-2025-37890, CVE-2025-38001), which are addressed in a separate
series.
The review uncovered several additional issues:
- probability gaps in the 4-state Markov loss model where
boundary values produce no state transition
- queue limit check not accounting for reordered packets
- PRNG reseeded on every tc change, breaking reproducibility
- the core dequeue re-entrancy issue with child qdiscs
causing HFSC eltree corruption and DRR class stalls
- missing NULL termination on the tfifo linear list tail
Stephen Hemminger (5):
net/sched: netem: fix probability gaps in 4-state loss model
net/sched: netem: fix queue limit check to include reordered packets
net/sched: netem: only reseed PRNG when seed is explicitly provided
net/sched: netem: restructure dequeue to avoid re-entrancy with child
qdisc
net/sched: netem: null-terminate tfifo linear queue tail
net/sched/sch_netem.c | 222 ++++++++++++++++++++++++++----------------
1 file changed, 140 insertions(+), 82 deletions(-)
--
2.53.0
^ permalink raw reply
* [PATCH net 1/5] net/sched: netem: fix probability gaps in 4-state loss model
From: Stephen Hemminger @ 2026-03-28 18:21 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
open list
In-Reply-To: <20260328182336.392817-1-stephen@networkplumber.org>
The 4-state Markov chain in loss_4state() has gaps at the boundaries
between transition probability ranges. The comparisons use:
if (rnd < a4)
else if (a4 < rnd && rnd < a1 + a4)
When rnd equals a boundary value exactly, neither branch matches and
no state transition occurs. The redundant lower-bound check (a4 < rnd)
is already implied by being in the else branch.
Remove the unnecessary lower-bound comparisons so the ranges are
contiguous and every random value produces a transition, matching
the GI (General and Intuitive) loss model specification.
This bug goes back to original implementation of this model.
Fixes: 661b79725fea ("netem: revised correlated loss generator")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 5de1c932944a..2cc3acaa4068 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -227,10 +227,10 @@ static bool loss_4state(struct netem_sched_data *q)
if (rnd < clg->a4) {
clg->state = LOST_IN_GAP_PERIOD;
return true;
- } else if (clg->a4 < rnd && rnd < clg->a1 + clg->a4) {
+ } else if (rnd < clg->a1 + clg->a4) {
clg->state = LOST_IN_BURST_PERIOD;
return true;
- } else if (clg->a1 + clg->a4 < rnd) {
+ } else {
clg->state = TX_IN_GAP_PERIOD;
}
@@ -247,9 +247,9 @@ static bool loss_4state(struct netem_sched_data *q)
case LOST_IN_BURST_PERIOD:
if (rnd < clg->a3)
clg->state = TX_IN_BURST_PERIOD;
- else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) {
+ else if (rnd < clg->a2 + clg->a3) {
clg->state = TX_IN_GAP_PERIOD;
- } else if (clg->a2 + clg->a3 < rnd) {
+ } else {
clg->state = LOST_IN_BURST_PERIOD;
return true;
}
--
2.53.0
^ permalink raw reply related
* [PATCH net 2/5] net/sched: netem: fix queue limit check to include reordered packets
From: Stephen Hemminger @ 2026-03-28 18:21 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
open list
In-Reply-To: <20260328182336.392817-1-stephen@networkplumber.org>
The queue limit check in netem_enqueue() uses q->t_len which only
counts packets in the internal tfifo. Packets placed in sch->q by
the reorder path (__qdisc_enqueue_head) are not counted, allowing
the total queue occupancy to exceed sch->limit under reordering.
Include sch->q.qlen in the limit check.
Fixes: 50612537e9ab ("netem: fix classful handling")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 2cc3acaa4068..6cc48b698e48 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -523,7 +523,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
1<<get_random_u32_below(8);
}
- if (unlikely(q->t_len >= sch->limit)) {
+ if (unlikely(sch->q.qlen >= sch->limit)) {
/* re-link segs, so that qdisc_drop_all() frees them all */
skb->next = segs;
qdisc_drop_all(skb, sch, to_free);
--
2.53.0
^ permalink raw reply related
* [PATCH net 3/5] net/sched: netem: only reseed PRNG when seed is explicitly provided
From: Stephen Hemminger @ 2026-03-28 18:21 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
François Michel, open list
In-Reply-To: <20260328182336.392817-1-stephen@networkplumber.org>
netem_change() unconditionally reseeds the PRNG on every tc change
command. If TCA_NETEM_PRNG_SEED is not specified, a new random seed
is generated, destroying reproducibility for users who set a
deterministic seed on a previous change.
Move the initial random seed generation to netem_init() and only
reseed in netem_change() when TCA_NETEM_PRNG_SEED is explicitly
provided by the user.
Fixes: 4072d97ddc44 ("netem: add prng attribute to netem_sched_data")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 6cc48b698e48..448097fc88a4 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -1111,11 +1111,10 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
/* capping jitter to the range acceptable by tabledist() */
q->jitter = min_t(s64, abs(q->jitter), INT_MAX);
- if (tb[TCA_NETEM_PRNG_SEED])
+ if (tb[TCA_NETEM_PRNG_SEED]) {
q->prng.seed = nla_get_u64(tb[TCA_NETEM_PRNG_SEED]);
- else
- q->prng.seed = get_random_u64();
- prandom_seed_state(&q->prng.prng_state, q->prng.seed);
+ prandom_seed_state(&q->prng.prng_state, q->prng.seed);
+ }
unlock:
sch_tree_unlock(sch);
@@ -1138,6 +1137,9 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt,
return -EINVAL;
q->loss_model = CLG_RANDOM;
+ q->prng.seed = get_random_u64();
+ prandom_seed_state(&q->prng.prng_state, q->prng.seed);
+
ret = netem_change(sch, opt, extack);
if (ret)
pr_info("netem: change failed\n");
--
2.53.0
^ permalink raw reply related
* [PATCH net 4/5] net/sched: netem: restructure dequeue to avoid re-entrancy with child qdisc
From: Stephen Hemminger @ 2026-03-28 18:21 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
open list
In-Reply-To: <20260328182336.392817-1-stephen@networkplumber.org>
netem_dequeue() enqueues packets into its child qdisc while being
called from the parent's dequeue path. This causes two problems:
- HFSC tracks class active/inactive state on qlen transitions.
A child enqueue during dequeue causes double-insertion into
the eltree (CVE-2025-37890, CVE-2025-38001).
- Non-work-conserving children like TBF may refuse to dequeue
packets just enqueued, causing netem to return NULL despite
having backlog. Parents like DRR then incorrectly deactivate
the class.
Split the dequeue into helpers:
netem_pull_tfifo() - remove head packet from tfifo
netem_slot_account() - update slot pacing counters
netem_dequeue_child() - batch-transfer ready packets to the
child, then dequeue from the child
netem_dequeue_direct()- dequeue from tfifo when no child
When a child qdisc is present, all time-ready packets are moved
into the child before calling its dequeue. This separates the
enqueue and dequeue phases so the parent sees consistent qlen
transitions.
Fixes: 50612537e9ab ("netem: fix classful handling")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 201 +++++++++++++++++++++++++++---------------
1 file changed, 128 insertions(+), 73 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 448097fc88a4..ce12b64603b2 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -688,99 +688,154 @@ static struct sk_buff *netem_peek(struct netem_sched_data *q)
return q->t_head;
}
-static void netem_erase_head(struct netem_sched_data *q, struct sk_buff *skb)
+/*
+ * Pop the head packet from the tfifo and prepare it for delivery.
+ * skb->dev shares the rbnode area and must be restored after removal.
+ */
+static struct sk_buff *netem_pull_tfifo(struct netem_sched_data *q,
+ struct Qdisc *sch)
{
- if (skb == q->t_head) {
+ struct sk_buff *skb;
+
+ if (q->t_head) {
+ skb = q->t_head;
q->t_head = skb->next;
if (!q->t_head)
q->t_tail = NULL;
} else {
- rb_erase(&skb->rbnode, &q->t_root);
+ struct rb_node *p = rb_first(&q->t_root);
+
+ if (!p)
+ return NULL;
+ skb = rb_to_skb(p);
+ rb_erase(p, &q->t_root);
}
+
+ q->t_len--;
+ skb->next = NULL;
+ skb->prev = NULL;
+ skb->dev = qdisc_dev(sch);
+
+ return skb;
}
-static struct sk_buff *netem_dequeue(struct Qdisc *sch)
+/* Update slot pacing counters after releasing a packet */
+static void netem_slot_account(struct netem_sched_data *q,
+ const struct sk_buff *skb, u64 now)
+{
+ if (!q->slot.slot_next)
+ return;
+
+ q->slot.packets_left--;
+ q->slot.bytes_left -= qdisc_pkt_len(skb);
+ if (q->slot.packets_left <= 0 || q->slot.bytes_left <= 0)
+ get_slot_next(q, now);
+}
+
+/*
+ * Transfer all time-ready packets from the tfifo into the child qdisc,
+ * then dequeue from the child. Batching the transfers avoids calling
+ * qdisc_enqueue() inside the parent's dequeue path, which confuses
+ * parents that track active/inactive state on qlen transitions (HFSC).
+ */
+static struct sk_buff *netem_dequeue_child(struct Qdisc *sch)
{
struct netem_sched_data *q = qdisc_priv(sch);
+ u64 now = ktime_get_ns();
struct sk_buff *skb;
-tfifo_dequeue:
- skb = __qdisc_dequeue_head(&sch->q);
- if (skb) {
-deliver:
- qdisc_qstats_backlog_dec(sch, skb);
- qdisc_bstats_update(sch, skb);
- return skb;
- }
- skb = netem_peek(q);
- if (skb) {
- u64 time_to_send;
- u64 now = ktime_get_ns();
-
- /* if more time remaining? */
- time_to_send = netem_skb_cb(skb)->time_to_send;
- if (q->slot.slot_next && q->slot.slot_next < time_to_send)
- get_slot_next(q, now);
-
- if (time_to_send <= now && q->slot.slot_next <= now) {
- netem_erase_head(q, skb);
- q->t_len--;
- skb->next = NULL;
- skb->prev = NULL;
- /* skb->dev shares skb->rbnode area,
- * we need to restore its value.
- */
- skb->dev = qdisc_dev(sch);
-
- if (q->slot.slot_next) {
- q->slot.packets_left--;
- q->slot.bytes_left -= qdisc_pkt_len(skb);
- if (q->slot.packets_left <= 0 ||
- q->slot.bytes_left <= 0)
- get_slot_next(q, now);
- }
+ while ((skb = netem_peek(q)) != NULL) {
+ struct sk_buff *to_free = NULL;
+ unsigned int pkt_len;
+ int err;
- if (q->qdisc) {
- unsigned int pkt_len = qdisc_pkt_len(skb);
- struct sk_buff *to_free = NULL;
- int err;
-
- err = qdisc_enqueue(skb, q->qdisc, &to_free);
- kfree_skb_list(to_free);
- if (err != NET_XMIT_SUCCESS) {
- if (net_xmit_drop_count(err))
- qdisc_qstats_drop(sch);
- sch->qstats.backlog -= pkt_len;
- sch->q.qlen--;
- qdisc_tree_reduce_backlog(sch, 1, pkt_len);
- }
- goto tfifo_dequeue;
- }
+ if (netem_skb_cb(skb)->time_to_send > now)
+ break;
+ if (q->slot.slot_next && q->slot.slot_next > now)
+ break;
+
+ skb = netem_pull_tfifo(q, sch);
+ netem_slot_account(q, skb, now);
+
+ pkt_len = qdisc_pkt_len(skb);
+ err = qdisc_enqueue(skb, q->qdisc, &to_free);
+ kfree_skb_list(to_free);
+ if (unlikely(err != NET_XMIT_SUCCESS)) {
+ if (net_xmit_drop_count(err))
+ qdisc_qstats_drop(sch);
+ sch->qstats.backlog -= pkt_len;
sch->q.qlen--;
- goto deliver;
+ qdisc_tree_reduce_backlog(sch, 1, pkt_len);
}
+ }
- if (q->qdisc) {
- skb = q->qdisc->ops->dequeue(q->qdisc);
- if (skb) {
- sch->q.qlen--;
- goto deliver;
- }
- }
+ skb = q->qdisc->ops->dequeue(q->qdisc);
+ if (skb)
+ sch->q.qlen--;
- qdisc_watchdog_schedule_ns(&q->watchdog,
- max(time_to_send,
- q->slot.slot_next));
- }
+ return skb;
+}
- if (q->qdisc) {
- skb = q->qdisc->ops->dequeue(q->qdisc);
- if (skb) {
- sch->q.qlen--;
- goto deliver;
- }
+/* Dequeue directly from the tfifo when no child qdisc is configured. */
+static struct sk_buff *netem_dequeue_direct(struct Qdisc *sch)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+ struct sk_buff *skb;
+ u64 time_to_send;
+ u64 now;
+
+ skb = netem_peek(q);
+ if (!skb)
+ return NULL;
+
+ now = ktime_get_ns();
+ time_to_send = netem_skb_cb(skb)->time_to_send;
+
+ if (q->slot.slot_next && q->slot.slot_next < time_to_send)
+ get_slot_next(q, now);
+
+ if (time_to_send > now || q->slot.slot_next > now)
+ return NULL;
+
+ skb = netem_pull_tfifo(q, sch);
+ netem_slot_account(q, skb, now);
+ sch->q.qlen--;
+
+ return skb;
+}
+
+static struct sk_buff *netem_dequeue(struct Qdisc *sch)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+ struct sk_buff *skb;
+
+ /* First check the reorder queue */
+ skb = __qdisc_dequeue_head(&sch->q);
+ if (skb)
+ goto deliver;
+
+ if (q->qdisc)
+ skb = netem_dequeue_child(sch);
+ else
+ skb = netem_dequeue_direct(sch);
+
+ if (skb)
+ goto deliver;
+
+ /* Nothing ready — schedule watchdog for next packet */
+ skb = netem_peek(q);
+ if (skb) {
+ u64 time_to_send = netem_skb_cb(skb)->time_to_send;
+
+ qdisc_watchdog_schedule_ns(&q->watchdog,
+ max(time_to_send, q->slot.slot_next));
}
return NULL;
+
+deliver:
+ qdisc_qstats_backlog_dec(sch, skb);
+ qdisc_bstats_update(sch, skb);
+ return skb;
}
static void netem_reset(struct Qdisc *sch)
--
2.53.0
^ permalink raw reply related
* [PATCH net 5/5] net/sched: netem: null-terminate tfifo linear queue tail
From: Stephen Hemminger @ 2026-03-28 18:21 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Peter Oskolkov, open list
In-Reply-To: <20260328182336.392817-1-stephen@networkplumber.org>
When tfifo_enqueue() appends a packet to the linear queue tail,
nskb->next is never set to NULL. The list terminates correctly
only by accident if the skb arrived with next already NULL.
Explicitly null-terminate the tail to prevent list corruption.
Fixes: d66280b12bd7 ("net: netem: use a list in addition to rbtree")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index ce12b64603b2..4b27fab72fef 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -398,6 +398,7 @@ static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
q->t_tail->next = nskb;
else
q->t_head = nskb;
+ nskb->next = NULL;
q->t_tail = nskb;
} else {
struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
--
2.53.0
^ permalink raw reply related
* [PATCH net-next 0/6] net/sched: netem: cleanups and improvements
From: Stephen Hemminger @ 2026-03-28 18:26 UTC (permalink / raw)
To: netdev; +Cc: Stephen Hemminger
Cleanup and improvement patches for netem, done as follow-on
to the bug fix series found during AI-assisted review.
- replace pr_info() with netlink extack error reporting
- validate slot min/max delay range on configuration
- fix slot delay calculation overflow for large ranges
- remove unused loss model struct fields
- remove stale VERSION string
- add per-impairment extended statistics (delayed, dropped,
corrupted, duplicated, reordered, ecn_marked)
The xstats patch requires a corresponding iproute2 change
to display the new counters in tc -s qdisc show. This will go
as separate patch after this.
Stephen Hemminger (6):
net/sched: netem: replace pr_info with netlink extack error messages
net/sched: netem: check for invalid slot range
net/sched: netem: fix slot delay calculation overflow
net/sched: netem: remove unused loss model fields
net/sched: netem: remove useless VERSION
net/sched: netem: add per-impairment extended statistics
include/uapi/linux/pkt_sched.h | 9 +++
net/sched/sch_netem.c | 120 ++++++++++++++++++++++-----------
2 files changed, 89 insertions(+), 40 deletions(-)
--
2.53.0
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox