* RE: [PATCH net-next v10 5/5] net: wangxun: add pcie error handler
From: Jiawen Wu @ 2026-07-16 6:55 UTC (permalink / raw)
To: 'Simon Horman'
Cc: netdev, mengyuanlou, andrew+netdev, davem, edumazet, kuba, pabeni,
richardcochran, linux, aleksandr.loktionov, jacob.e.keller,
michal.swiatkowski, kees, joe, leitao, shaojijie, u.kleine-koenig,
weirongguang, larysa.zaremba, fabio.baltieri
In-Reply-To: <20260714091129.1815871-1-horms@kernel.org>
On Tue, Jul 14, 2026 5:11 PM, Simon Horman wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> ---
> --- Patch [5]: [PATCH net-next v10 5/5] net: wangxun: add pcie error handler ---
> commit 91baa4e77d07ab6772569a7d37ed86cfda625c00
> Author: Jiawen Wu <jiawenwu@trustnetic.com>
> Subject: net: wangxun: add pcie error handler
>
> This commit adds PCIe error handling support (AER) to the wangxun drivers
> (ngbe and txgbe). It detects hardware errors, attempts to soft quiesce,
> and registers standard PCI error handler callbacks.
>
> > diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
> > index ee27f96735dc7..0cb99e456a1c9 100644
> > --- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
> > +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
>
> [ ... ]
>
> > +static void wx_io_resume(struct pci_dev *pdev)
> > +{
> > + struct wx *wx = pci_get_drvdata(pdev);
> > + struct net_device *netdev;
> > + int err;
> > +
> > + netdev = wx->netdev;
> > + rtnl_lock();
> > + if (netif_running(netdev)) {
> > + err = netdev->netdev_ops->ndo_open(netdev);
>
> [Severity: High]
> Does this sequence risk corrupting the hw_tc_lock seqlock?
>
> During AER recovery, wx_io_slot_reset() calls down_suspend(), which bypasses
> wx_ptp_stop(). This leaves the PTP clock registered and potentially in use by
> userspace.
>
> When wx_io_resume() then calls ndo_open(), this triggers txgbe_up() or
> ngbe_up(), which in turn calls wx_ptp_init(). Since wx_ptp_init()
> unconditionally calls seqlock_init(&wx->hw_tc_lock), won't this corrupt the
> lock if userspace is actively issuing PTP ioctls?
PTP clock was destroyed in wx_ptp_quiesce() before V8 patch set.
The piece of code was removed based on the Sashiko's comment.
I think it should be added back because the order of the calls in wx_soft_quiesce()
has been changed.
>
> > + if (err) {
> > + wx_err(wx, "Failed to open netdev after reset\n");
>
> [ ... ]
>
> > diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> > index 92895f5035118..56d4b63387fd6 100644
> > --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> > +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
>
> [ ... ]
>
> > @@ -413,6 +428,9 @@ static void ngbe_disable_device(struct wx *wx)
> >
> > static void ngbe_reset(struct wx *wx)
> > {
> > + if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
> > + return;
> > +
> > wx_flush_sw_mac_table(wx);
>
> [Severity: High]
> Is the hardware properly reinitialized here after an AER slot reset?
>
> In wx_io_slot_reset(), the hardware needs to be restarted from scratch.
> While txgbe_reset() calls txgbe_reset_hw(), ngbe_reset() only flushes the
> software MAC tables and skips calling ngbe_reset_hw().
>
> Because this patch registers wx_err_handler for the ngbe driver, won't this
> leave the hardware uninitialized and the interface broken after a PCIe slot
> reset?
I think I have answered this question many times and also described ngbe's lack
of functionality in the commit and cover-letter messages.
>
> [ ... ]
>
> > @@ -529,12 +548,16 @@ static int ngbe_close(struct net_device *netdev)
> > {
> > struct wx *wx = netdev_priv(netdev);
> >
> > + if (test_bit(WX_STATE_RES_FREED, wx->state))
> > + goto out;
> > +
> > wx_ptp_stop(wx);
>
> [Severity: High]
> Does this early return cause a use-after-free for the PTP clock?
>
> If a PCI error occurs, down_suspend() sets WX_STATE_RES_FREED but
> intentionally skips wx_ptp_stop(). If the interface is subsequently closed or
> unregistered, ngbe_close() will return early here and never call
> wx_ptp_stop().
>
> Because the PTP clock device is never unregistered, it remains exposed to
> userspace after the driver's memory is freed. Can this lead to a
> use-after-free if userspace interacts with the clock later?
>
> [ ... ]
>
> > diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> > index a7bde03a98feb..d85ee83192e4f 100644
> > --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> > +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
>
> [ ... ]
>
> > @@ -530,12 +549,16 @@ static int txgbe_close(struct net_device *netdev)
> > {
> > struct wx *wx = netdev_priv(netdev);
> >
> > + if (test_bit(WX_STATE_RES_FREED, wx->state))
> > + goto out;
> > +
> > wx_ptp_stop(wx);
>
> [Severity: High]
> Similar to ngbe_close() above, does this early return skip wx_ptp_stop() and
> lead to a use-after-free if the txgbe device is closed after an AER suspend?
>
^ permalink raw reply
* Re: [PATCH net] net: rnpgbe: Pass an expression directly in rnpgbe_rm_adapter()
From: Dan Carpenter @ 2026-07-16 6:57 UTC (permalink / raw)
To: Markus Elfring
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, MD Danish Anwar, Michael Grzeschik, Paolo Abeni,
Uwe Kleine-König, Vadim Fedorenko, Yibo Dong, LKML,
kernel-janitors
In-Reply-To: <8110bb3f-70d9-4f0f-82f2-ffe1262b2962@web.de>
On Mon, Jul 13, 2026 at 10:22:06AM +0200, Markus Elfring wrote:
> >> The address of a data structure member was determined before
> >> a corresponding null pointer check in the implementation of
> >> the function “rnpgbe_rm_adapter”.
> >>
> >> Thus avoid the risk for undefined behaviour by omitting the variable “hw”.
> >> Pass the required address directly to a function call.
> >>
> >> This issue was detected by using the Coccinelle software.
> >>
> >> Fixes: 2ee95ec17e97c58b65e978a08b75fa8cb6424e4e ("net: rnpgbe: Add register_netdev")
> >
> > There is no NULL dereference here. It's just pointer math.
> > No need for a Fixes tag.
>
> How does your view fit to information in an article like “Fun with NULL pointers, part 1”(by Jonathan Corbet from 2009-07-20)?
> https://lwn.net/Articles/342330/
>
Of course you can't have NULL pointer dereferences but this is not a
dereference, it's just pointer math.
regards,
dan carpenter
^ permalink raw reply
* Re: [PATCH net] net/mlx5: Use unique names for software steering caches
From: Christian Borntraeger @ 2026-07-16 6:57 UTC (permalink / raw)
To: Leon Romanovsky, Saeed Mahameed, Tariq Toukan, Mark Bloch,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Alex Vesker, Yevgeny Kliteynik
Cc: netdev, linux-rdma, linux-kernel
In-Reply-To: <20260715-kmem-dupliate-name-v1-1-85551c328155@nvidia.com>
Am 15.07.26 um 10:22 schrieb Leon Romanovsky:
> From: Leon Romanovsky <leonro@nvidia.com>
>
> Each software steering domain creates its own slab caches, but all
> domains use the same names. When domains for two devices are alive at
> once, the second kmem_cache_create() triggers the following splat:
[...]
>
> Prefix each cache name with the device name to make it unique.
>
> Fixes: fd785e5213f0 ("net/mlx5: DR, Allocate icm_chunks from their own slab allocator")
> Fixes: fb628b71fb2a ("net/mlx5: DR, Allocate htbl from its own slab allocator")
> Reported-by: Christian Borntraeger <borntraeger@linux.ibm.com>
> Closes: https://lore.kernel.org/all/a3cea501-4d1f-47d5-b6d0-fcda9a0aab16@linux.ibm.com/
> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Tested-by: Christian Borntraeger <borntraeger@linux.ibm.com>
error is gone in our CI when the patch is applied.
^ permalink raw reply
* Re: [PATCH net-next v2 12/14] gpio: tc956x: add TC956x/QPS615 support
From: Manivannan Sadhasivam @ 2026-07-16 7:02 UTC (permalink / raw)
To: Alex Elder
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, maxime.chevallier,
rmk+kernel, andersson, konradybcio, robh, krzk+dt, conor+dt,
linusw, brgl, arnd, gregkh, daniel, mohd.anwar, a0987203069,
alexandre.torgue, ast, boon.khai.ng, chenchuangyu, chenhuacai,
daniel, hawk, hkallweit1, inochiama, john.fastabend, julianbraha,
livelycarpet87, mcoquelin.stm32, me, prabhakar.mahadev-lad.rj,
richardcochran, rohan.g.thomas, sdf, siyanteng, weishangjuan,
wens, netdev, bpf, linux-arm-msm, devicetree, linux-gpio,
linux-stm32, linux-arm-kernel, linux-kernel
In-Reply-To: <20260605010022.968612-13-elder@riscstar.com>
On Thu, Jun 04, 2026 at 08:00:19PM -0500, Alex Elder wrote:
> Toshiba TC956x is an Ethernet-AVB/TSN bridge and is essentially
> a small and highly-specialized SoC. TC956x includes a GPIO block that
> can be accessed, alongside several other peripherals, via two PCIe
> endpoint functions. The PCIe function driver creates an auxiliary
> device for the GPIO block, and that device gets bound to this auxiliary
> device driver.
>
> This driver is implemented using the generic regmap-based GPIO driver.
>
While the regmap over the switch BAR works for GPIO access post-enumeration,
there is a blocker in using these GPIOs to control the power to endpoints.
We have a design [1] where the GPIOs from the switch are used to control power
and PERST# signals to the EP. With this current design, during the initial PCI
bus scan, the switch will get enumerated, but the EP will not. Because, power to
the EP depends on the GPIO Aux driver that will get probed only after the switch
enumeration.
But this creates a chicken-and-egg problem with the PCI Pwrctrl design.
pcie-qcom driver uses the Pwrctrl framework to power on the PCI endpoints before
the initial PCI bus scan. It calls pci_pwrctrl_create_devices() and
pci_pwrctrl_power_on_devices() APIs to create the platform device for all PCI
devices defined in DT (that require pwrctrl support), waits for their respective
pwrctrl drivers to get probed and then power ON all of them. This step is
required because in DT platforms, many Root Ports are not hotplug capable and
also BIOS doesn't assign bridge windows during boot. So all the devices has to
appear during the initial PCI bus scan so that the PCI core can allocate the
resources properly.
Now the issue with this Aux driver design is that, if an EP makes use of the
switch GPIO for PERST# or power, like:
tc9563: pcie@0,0 {
compatible = "pci1179,0623";
reg = <0x10000 0x0 0x0 0x0 0x0>;
...
pcie@1,0 {
compatible = "pciclass,0604";
reg = <0x20800 0x0 0x0 0x0 0x0>;
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
ranges;
bus-range = <0x3 0xff>;
reset-gpios = <&tc9563 5 GPIO_ACTIVE_LOW>;
};
...
};
Then the pwrctrl driver (pci/pwrctrl/generic.c) will try to acquire the GPIO
controller during its probe, but will fail with -EPROBE_DEFER as the GPIO
controller won't be available at that time. So the whole PCIe instance will
probe defer as the driver requires all pwrctrl drivers to be probed before
starting the initial bus scan.
So we need to make sure that the GPIO controller driver is available before
enumerating the switch device. One way to achieve is by creating the GPIO Aux
device in the pci/pwrctrl/pci-pwrctrl-tc9563.c driver and let the GPIO
controller driver use I2C communication for setting up the GPIOs. Thankfully,
the switch allows both I2C and BAR MMIO configurations for internal GPIOs.
With this change, the GPIO driver will get probed by the time the pwrctrl
generic driver acquires the 'reset-gpios' and will turn ON the EP.
Let me know your thoughts!
- Mani
[1] https://lore.kernel.org/linux-pci/e2inl7k5gsjj6oomv2k5ximuzpb3gfiz66ufet3b4hvov7zqt4@qz4pifbos7yf/
> Co-developed-by: Daniel Thompson <daniel@riscstar.com>
> Signed-off-by: Daniel Thompson <daniel@riscstar.com>
> Signed-off-by: Alex Elder <elder@riscstar.com>
> ---
> MAINTAINERS | 1 +
> drivers/gpio/Kconfig | 12 ++++
> drivers/gpio/Makefile | 1 +
> drivers/gpio/gpio-tc956x.c | 130 +++++++++++++++++++++++++++++++++++++
> 4 files changed, 144 insertions(+)
> create mode 100644 drivers/gpio/gpio-tc956x.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0924f7ec43cb0..0439607d1155f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -27057,6 +27057,7 @@ M: Alex Elder <elder@kernel.org>
> M: Daniel Thompson <danielt@kernel.org>
> S: Maintained
> F: Documentation/devicetree/bindings/net/toshiba,tc956x-dwmac.yaml
> +F: drivers/gpio/gpio-tc956x.c
> F: drivers/misc/tc956x_pci.c
>
> TOSHIBA WMI HOTKEYS DRIVER
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 020e51e30317a..36631ca722fa3 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -743,6 +743,18 @@ config GPIO_TB10X
> select GPIO_GENERIC
> select GENERIC_IRQ_CHIP
>
> +config GPIO_TC956X
> + tristate "Toshiba TC956X GPIO support"
> + depends on TOSHIBA_TC956X_PCI
> + select GPIO_REGMAP
> + default m
> + help
> + This enables support for the GPIO controller embedded in the Toshiba
> + TC956X (and Qualcomm QPS615). This device connects to the host
> + via PCIe port, which is the upstream port on an internal PCIe
> + switch. On some platforms, a few of the GPIO lines are used to
> + manage external resets.
> +
> config GPIO_TEGRA
> tristate "NVIDIA Tegra GPIO support"
> default ARCH_TEGRA
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index b267598b517de..c3584e7cba9b4 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -178,6 +178,7 @@ obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
> obj-$(CONFIG_GPIO_TANGIER) += gpio-tangier.o
> obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
> obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
> +obj-$(CONFIG_GPIO_TC956X) += gpio-tc956x.o
> obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
> obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
> obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o
> diff --git a/drivers/gpio/gpio-tc956x.c b/drivers/gpio/gpio-tc956x.c
> new file mode 100644
> index 0000000000000..0dc6b1028d970
> --- /dev/null
> +++ b/drivers/gpio/gpio-tc956x.c
> @@ -0,0 +1,130 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright (C) 2026 by RISCstar Solutions Corporation. All rights reserved.
> + */
> +
> +/*
> + * The Toshiba TC956X implements a PCIe Gen 3 switch that connects an
> + * upstream x4 port to two downstream PCIe x2 ports. It incorporates
> + * an internal endpoint on a internal PCIe port that implements two
> + * Synopsys XGMAC Ethernet interfaces.
> + *
> + * 35 GPIOs are also implemented by an embedded GPIO controller. Three
> + * registers control the first 32 GPIOs (other than 20 and 21, which are
> + * reserved). Three other registers control GPIOs 32 through 36. GPIOs
> + * 22-24, 27-28, 31, and 34 are treated as "input only".
> + *
> + * There is a TC956X PCI power controller driver that accesses the
> + * direction and output value registers for GPIOs 2 and 3. These
> + * GPIOs control the reset signal for the two downstream PCIe ports.
> + * Their values will never change during operation of this driver, and
> + * this driver reserves these two GPIOS.
> + */
> +
> +#include <linux/auxiliary_bus.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/gpio/regmap.h>
> +
> +#define DRIVER_NAME "tc956x-gpio"
> +
> +#define TC956X_GPIO_COUNT 37 /* Number of GPIOs (20-21 reserved) */
> +
> +/* The GPIO offsets are relative to 0x1200 in TC956X SFR space. */
> +#define GPIO_IN0_OFFSET 0x00 /* Input value (0-31) */
> +#define GPIO_EN0_OFFSET 0x08 /* 0: out; 1: in (0-31) */
> +#define GPIO_OUT0_OFFSET 0x10 /* Output value (0-31) */
> +
> +/*
> + * There are two sets of registers, each representing (up to) 32 GPIOs with a
> + * stride of 4 bytes (IN1 is 4 bytes past IN0, EN1 is 4 bytes past EN0, etc.).
> + */
> +#define GPIO_PER_REG 32
> +#define GPIO_REG_STRIDE 4
> +
> +static int tc956x_gpio_init_valid_mask(struct gpio_chip *gc,
> + unsigned long *valid_mask,
> + unsigned int ngpios)
> +{
> + /*
> + * GPIOs 2 and 3 are used by the PCI power control driver, and
> + * we don't allow them to be used. GPIOs 20 and 21 are reserved
> + * (and not usable).
> + */
> + bitmap_fill(valid_mask, ngpios);
> + bitmap_clear(valid_mask, 2, 2);
> + bitmap_clear(valid_mask, 20, 2);
> +
> + return 0;
> +}
> +
> +static int tc956x_gpio_probe(struct auxiliary_device *adev,
> + const struct auxiliary_device_id *id)
> +{
> + DECLARE_BITMAP(zeroes, TC956X_GPIO_COUNT);
> + DECLARE_BITMAP(fixed, TC956X_GPIO_COUNT);
> + struct gpio_regmap_config config = { };
> + struct gpio_regmap *gpio_regmap;
> + struct device *dev = &adev->dev;
> +
> + /* We need the regmap pointer, stored in our platform data */
> + if (!dev->platform_data)
> + return -EINVAL;
> +
> + /*
> + * Only some of our GPIOs are fixed direction:
> + * 22, 23, 24, 27, 28, 31, and 34 (all input-only)
> + * Set up the fixed bitmap to indicate which are fixed.
> + */
> + bitmap_zero(fixed, TC956X_GPIO_COUNT);
> + bitmap_set(fixed, 22, 3);
> + bitmap_set(fixed, 27, 2);
> + set_bit(31, fixed);
> + set_bit(34, fixed);
> +
> + /* All fixed GPIOs are input; the zeroes bitmap indicates that. */
> + bitmap_zero(zeroes, TC956X_GPIO_COUNT);
> +
> + config.parent = dev;
> + config.regmap = dev->platform_data;
> + config.label = DRIVER_NAME;
> + config.ngpio = TC956X_GPIO_COUNT;
> + config.reg_dat_base = GPIO_REGMAP_ADDR(GPIO_IN0_OFFSET);
> + config.reg_set_base = GPIO_REGMAP_ADDR(GPIO_OUT0_OFFSET);
> + config.reg_dir_in_base = GPIO_REGMAP_ADDR(GPIO_EN0_OFFSET);
> + config.reg_stride = GPIO_REG_STRIDE;
> + config.ngpio_per_reg = GPIO_PER_REG;
> + config.init_valid_mask = tc956x_gpio_init_valid_mask;
> + config.fixed_direction_mask = fixed;
> + config.fixed_direction_output = zeroes;
> +
> + gpio_regmap = devm_gpio_regmap_register(dev, &config);
> + if (IS_ERR(gpio_regmap))
> + return PTR_ERR(gpio_regmap);
> +
> + return 0;
> +}
> +
> +static const struct auxiliary_device_id tc956x_gpio_ids[] = {
> + { .name = "tc956x_pci.tc9564-gpio", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(auxiliary, tc956x_gpio_ids);
> +
> +static struct auxiliary_driver tc956x_gpio_driver = {
> + .name = DRIVER_NAME,
> + .probe = tc956x_gpio_probe,
> + .id_table = tc956x_gpio_ids,
> + .driver = {
> + .name = DRIVER_NAME,
> + .owner = THIS_MODULE,
> + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> + },
> +};
> +module_auxiliary_driver(tc956x_gpio_driver);
> +
> +MODULE_DESCRIPTION("Toshiba TC956X PCIe GPIO Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("auxiliary:" DRIVER_NAME);
> --
> 2.51.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH net v3 2/3] net: dsa: mv88e6xxx: flush the ATU when the hash algorithm changes
From: Luke Howard @ 2026-07-16 7:09 UTC (permalink / raw)
To: Jonas Gorski
Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Vivien Didelot, Gregory CLEMENT,
Richard Cochran, Cedric Jehasse, Kieran Tyrrell, Max Holtmann,
Max Hunter, Christoph Mellauner, Simon Gapp, netdev, linux-kernel
In-Reply-To: <CAOiHx=mZeS1dZ59grQrf9U2QP3OKEJDahhYpLx5_tKCEUty1yQ@mail.gmail.com>
> On 16 Jul 2026, at 4:52 am, Jonas Gorski <jonas.gorski@gmail.com> wrote:
>
> Probably also needs to be not attached to any bridge, right? To make
> sure that no additional static entries can exist (because I think
> these are hard to recreate).
Thank you, I have fixed and tested locally and will include in the next revision.
Luke
^ permalink raw reply
* Re: [PATCH 1/6] pds_core: add support for quiet devcmd failures
From: Simon Horman @ 2026-07-16 7:17 UTC (permalink / raw)
To: nikhil.rao
Cc: Simon Horman, netdev, kuba, brett.creeley, eric.joyner,
andrew+netdev, davem, edumazet, pabeni, jacob.e.keller
In-Reply-To: <20260708212222.296202-2-nikhil.rao@amd.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
--- Patch [1]: [PATCH 1/6] pds_core: add support for quiet devcmd failures ---
commit 2618e77f5d1aae31c9817f09b5056a69e237162a
Author: Brett Creeley <brett.creeley@amd.com>
pds_core: add support for quiet devcmd failures
This commit adds support to allow devcmd failure messages to be suppressed.
This will be used when adding support to negotiate
PDS_CORE_IDENTITY_VERSION_2.
> diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
> index bded6b33289ce..dd9989cfe6b3f 100644
> --- a/drivers/net/ethernet/amd/pds_core/dev.c
> +++ b/drivers/net/ethernet/amd/pds_core/dev.c
[ ... ]
> @@ -197,7 +199,7 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
>
> memcpy_toio(&pdsc->cmd_regs->cmd, cmd, sizeof(*cmd));
> pdsc_devcmd_dbell(pdsc);
> - err = pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds);
> + err = __pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds, do_msg);
>
> if ((err == -ENXIO || err == -ETIMEDOUT) && pdsc->wq)
> queue_work(pdsc->wq, &pdsc->health_work);
[Severity: High]
This is a pre-existing issue, but does this error path trigger a
use-after-free during device removal?
During device removal, pdsc_remove() destroys the workqueue without clearing
the pointer before invoking pdsc_teardown():
pdsc_remove() {
...
if (pdsc->wq)
destroy_workqueue(pdsc->wq);
mutex_lock(&pdsc->config_lock);
set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state);
pdsc_stop(pdsc);
pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING);
...
}
The teardown function issues a hardware reset command via pdsc_devcmd_reset().
If the device is unresponsive and the command times out, the error path here in
__pdsc_devcmd_locked() catches the -ETIMEDOUT error.
Because pdsc->wq was left as a dangling pointer, the condition evaluates to true
and execution falls through to queue_work(), which will dereference the already
destroyed workqueue pointer.
Could the pdsc->wq pointer be set to NULL immediately after destruction to
prevent this, or should the teardown sequence be reordered?
> else
> memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp));
>
> if (err != -ETIMEDOUT && err != -EAGAIN)
> pdsc_deferred_dma_free(pdsc);
>
> return err;
> }
^ permalink raw reply
* Re: [PATCH 3/6] pds_core: add PLDM firmware update support via devlink flash
From: Simon Horman @ 2026-07-16 7:20 UTC (permalink / raw)
To: nikhil.rao
Cc: Simon Horman, netdev, kuba, brett.creeley, eric.joyner,
andrew+netdev, davem, edumazet, pabeni, jacob.e.keller
In-Reply-To: <20260708212222.296202-4-nikhil.rao@amd.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
--- Patch [3]: [PATCH 3/6] pds_core: add PLDM firmware update support via devlink flash ---
commit 865af5a5208d8bbf12e2bc16faaacfc3c3239978
Author: Brett Creeley <brett.creeley@amd.com>
pds_core: add PLDM firmware update support via devlink flash
This commit implements PLDM firmware update support in the pds_core driver
using the upstream pldmfw API. It allows updating an entire PLDM FW package
at once or updating specific firmware components by name.
> diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
> index 84ea502ecb128..b149d29bd2560 100644
> --- a/drivers/net/ethernet/amd/pds_core/dev.c
> +++ b/drivers/net/ethernet/amd/pds_core/dev.c
> @@ -206,15 +206,56 @@ static int __pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
> else
> memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp));
>
> + if (err != -ETIMEDOUT && err != -EAGAIN)
> + pdsc_deferred_dma_free(pdsc);
> +
[Severity: High]
Can pdsc_deferred_dma_free() prematurely free DMA memory that the device
firmware is still actively accessing?
If a DMA-based devcmd like pdsc_devcmd_send_component() times out after
polling and returns -EAGAIN, the DMA mapping is added to
pdsc->deferred_dma_list in pdsc_flash_component_chunk(). The firmware update
aborts on the host, but the device might still be processing the command in
the background.
If an unrelated devcmd (e.g., from a periodic health check) executes and
succeeds shortly after, this logic will unconditionally unmap and free all
deferred DMA memory. Does this lead to memory corruption or IOMMU faults if
the firmware continues to access the freed memory?
> return err;
> }
>
> +void pdsc_deferred_dma_add(struct pdsc *pdsc, struct pdsc_deferred_dma *entry,
> + dma_addr_t dma_addr, void *va, size_t size,
> + enum dma_data_direction dir)
> +{
[ ... ]
> @@ -227,6 +268,47 @@ int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
> return err;
> }
>
> +static int __pdsc_devcmd_with_data(struct pdsc *pdsc,
> + union pds_core_dev_cmd *cmd,
> + const void *data, size_t data_len,
> + union pds_core_dev_comp *comp,
> + int max_seconds, bool do_msg)
> +{
> + int err;
> +
> + mutex_lock(&pdsc->devcmd_lock);
> + if (!pdsc->cmd_regs) {
> + err = -ENXIO;
> + goto unlock;
> + }
> + if (data_len > sizeof(pdsc->cmd_regs->data)) {
> + err = -ENOSPC;
> + goto unlock;
> + }
> + memcpy_toio(&pdsc->cmd_regs->data, data, data_len);
[Severity: High]
This is a pre-existing issue, but can a concurrent PCI reset unmap the device
BARs while the firmware update is actively copying data to MMIO here?
When devlink flash initiates a firmware update, __pdsc_devcmd_with_data()
acquires devcmd_lock and begins copying up to 2KB of data via memcpy_toio().
Concurrently, a PCI reset (e.g., via sysfs or AER) triggers
pdsc_reset_prepare(), which calls pdsc_unmap_bars() without acquiring
devcmd_lock. pdsc_unmap_bars() sets pdsc->cmd_regs = NULL and calls
pci_iounmap().
Could this race cause a kernel panic if the firmware update thread dereferences
the now-NULL pdsc->cmd_regs pointer or accesses unmapped MMIO memory? The long
polling firmware update that repeatedly copies large data blocks might
exacerbate this race window.
> + err = __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, do_msg);
> +unlock:
> + mutex_unlock(&pdsc->devcmd_lock);
> +
> + return err;
> +}
^ permalink raw reply
* Re: [PATCH 4/6] pds_core: add PLDM component info display
From: Simon Horman @ 2026-07-16 7:22 UTC (permalink / raw)
To: nikhil.rao
Cc: Simon Horman, netdev, kuba, brett.creeley, eric.joyner,
andrew+netdev, davem, edumazet, pabeni, jacob.e.keller
In-Reply-To: <20260708212222.296202-5-nikhil.rao@amd.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
--- Patch [4]: [PATCH 4/6] pds_core: add PLDM component info display ---
commit 4d0cc8aef76c7e2e30f98535495312200941b646
Author: Brett Creeley <brett.creeley@amd.com>
pds_core: add PLDM component info display
Add detailed component information display via devlink info. This
allows users to see individual firmware components and their versions.
Components are reported as fixed, running, or stored based on their
firmware-provided flags.
> diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
> index 3b763ee1715ee..63fe45e91f710 100644
> --- a/drivers/net/ethernet/amd/pds_core/devlink.c
> +++ b/drivers/net/ethernet/amd/pds_core/devlink.c
[ ... ]
> +static int pdsc_dl_component_info_get(struct devlink *dl,
> + struct devlink_info_req *req,
> + struct netlink_ext_ack *extack)
> +{
> + struct pdsc *pdsc = devlink_priv(dl);
> + u8 num_components;
> + int err;
> + int i;
> +
> + /* Pairs with WRITE_ONCE in pdsc_fw_components_invalidate().
> + * Use READ_ONCE to get a consistent snapshot of num_components.
> + * pdsc_fw_components_invalidate() can zero it concurrently during
> + * firmware recovery; using the local copy avoids iterating zero
> + * times when we already decided the cache was valid.
> + */
> + num_components = READ_ONCE(pdsc->fw_components.num_components);
> + if (!num_components) {
> + err = pdsc_get_component_info(pdsc);
[Severity: High]
Does this code expose uninitialized memory to concurrent readers?
If two threads execute this concurrently after the firmware component list
has been invalidated, one thread could enter pdsc_get_component_info(),
which updates the count before the array is populated:
drivers/net/ethernet/amd/pds_core/fw.c:pdsc_get_component_info() {
...
pdsc->fw_components.num_components = num_components;
for (i = 0; i < num_components; i++) {
struct pds_core_fw_component_info *info =
&pdsc->fw_components.info[i];
memcpy(info, &list_info->info[i], sizeof(*info));
...
}
A second thread could concurrently execute pdsc_dl_component_info_get(), see
num_components > 0 via READ_ONCE(), and proceed to iterate over the array
while the first thread is still executing memcpy() into the array.
Could this be addressed by moving the update of num_components to after the
array population is complete, perhaps using smp_store_release() and
smp_load_acquire() to ensure proper ordering?
> + if (err)
> + return pdsc_dl_report_fw_ver(req,
> + pdsc->dev_info.fw_version);
> + num_components = READ_ONCE(pdsc->fw_components.num_components);
> + if (!num_components)
> + return pdsc_dl_report_fw_ver(req,
> + pdsc->dev_info.fw_version);
> + }
> +
> + num_components = min_t(u16, num_components,
> + le16_to_cpu(pdsc->dev_ident.max_fw_slots));
> + for (i = 0; i < num_components; i++) {
> + err = pdsc_dl_report_component(req,
> + &pdsc->fw_components.info[i]);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
^ permalink raw reply
* Re: [PATCH 5/6] pds_core: add host backed memory support for firmware
From: Simon Horman @ 2026-07-16 7:23 UTC (permalink / raw)
To: nikhil.rao
Cc: Simon Horman, netdev, kuba, brett.creeley, eric.joyner,
andrew+netdev, davem, edumazet, pabeni, jacob.e.keller
In-Reply-To: <20260708212222.296202-6-nikhil.rao@amd.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
--- Patch [5]: [PATCH 5/6] pds_core: add host backed memory support for firmware ---
commit 0970ce1c8fa02a37c722e99472916638286b6ad9
Author: Nikhil P. Rao <nikhil.rao@amd.com>
pds_core: add host backed memory support for firmware
Some newer AMD/Pensando cards have minimal memory and there are cases
where components, specifically in the control plane, need more memory.
This series adds support for host backed DMA memory that can be used
by the firmware for the previously mentioned cases.
> diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c
> index a7a0bcf98ed3f..356a69c895b63 100644
> --- a/drivers/net/ethernet/amd/pds_core/core.c
> +++ b/drivers/net/ethernet/amd/pds_core/core.c
[ ... ]
> @@ -673,3 +675,161 @@ void pdsc_health_thread(struct work_struct *work)
[ ... ]
> +static int pdsc_host_mem_add_one(struct pdsc *pdsc, int index)
> +{
> + struct pdsc_host_mem *hm = &pdsc->host_mem_reqs[index];
> + union pds_core_dev_comp comp = {};
> + union pds_core_dev_cmd cmd = {};
> + int err;
[ ... ]
> + /* Track this allocation so pdsc_host_mem_free() can clean it up */
> + pdsc->num_host_mem_reqs++;
> +
> + memset(&cmd, 0, sizeof(cmd));
> + memset(&comp, 0, sizeof(comp));
> + cmd.host_mem.opcode = PDS_CORE_CMD_HOST_MEM;
> + cmd.host_mem.oper = PDS_CORE_HOST_MEM_ADD;
> + cmd.host_mem.tag = cpu_to_le16(hm->tag);
> + cmd.host_mem.size = cpu_to_le32(hm->size);
> + cmd.host_mem.buf_pa = cpu_to_le64(hm->pa);
> +
> + dev_dbg(pdsc->dev, "Sending devcmd for mem add tag %d size %d pa %pad\n",
> + hm->tag, hm->size, &hm->pa);
> + err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
> + if (err || comp.status != PDS_RC_SUCCESS) {
> + dev_err(pdsc->dev, "mem add failed err %d status %d for tag %d\n",
> + err, comp.status, hm->tag);
> + err = err ? err : -EIO;
> + goto err_del;
> + }
> + dev_dbg(pdsc->dev, "mem add completed for tag %d\n", hm->tag);
> +
> + return 0;
> +
> +err_del:
> + /* After MEM_QUERY succeeds, firmware expects MEM_ADD or MEM_DEL */
> + pdsc_host_mem_del_one(pdsc, hm->tag, PDS_RC_ENOMEM);
> + return err;
> +}
[Severity: Medium]
When the MEM_ADD device command fails, does this error path leave the memory
allocated and DMA mapped?
Since pdsc->num_host_mem_reqs was incremented before the command, the memory
will eventually be freed when pdsc_host_mem_free() runs at driver teardown.
However, it appears up to 4MB of memory per failed request could be held
uselessly for the entire operational runtime of the driver.
Should the driver explicitly unmap and free the memory in this error path?
[ ... ]
> +void pdsc_host_mem_free(struct pdsc *pdsc)
> +{
> + int i;
> +
> + if (!pdsc->host_mem_reqs)
> + return;
> +
> + for (i = 0; i < pdsc->num_host_mem_reqs; i++) {
> + dma_unmap_page(pdsc->dev, pdsc->host_mem_reqs[i].pa,
> + pdsc->host_mem_reqs[i].size,
> + DMA_BIDIRECTIONAL);
> + __free_pages(pdsc->host_mem_reqs[i].pg,
> + pdsc->host_mem_reqs[i].order);
> + }
> +
> + kfree(pdsc->host_mem_reqs);
> + pdsc->host_mem_reqs = NULL;
> + pdsc->num_host_mem_reqs = 0;
> +}
[Severity: High]
Does this code risk leaving the firmware with dangling DMA addresses?
The loop unmaps and frees the provisioned host memory pages, but it doesn't
appear to send a corresponding PDS_CORE_HOST_MEM_DEL device command to notify
the firmware that the memory has been revoked.
While pdsc_teardown() sends an explicit reset command for Physical Functions,
it skips this step for Virtual Functions. If a VF frees memory without sending
a MEM_DEL command, could the firmware still attempt to access the freed memory,
resulting in IOMMU faults or use-after-free corruption?
^ permalink raw reply
* Re: [PATCH 5/6] pds_core: add host backed memory support for firmware
From: Simon Horman @ 2026-07-16 7:23 UTC (permalink / raw)
To: nikhil.rao
Cc: Simon Horman, netdev, kuba, brett.creeley, eric.joyner,
andrew+netdev, davem, edumazet, pabeni, jacob.e.keller
In-Reply-To: <20260708212222.296202-6-nikhil.rao@amd.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
--- Patch [5]: [PATCH 5/6] pds_core: add host backed memory support for firmware ---
commit 0970ce1c8fa02a37c722e99472916638286b6ad9
Author: Nikhil P. Rao <nikhil.rao@amd.com>
pds_core: add host backed memory support for firmware
Some newer AMD/Pensando cards have minimal memory and there are cases
where components, specifically in the control plane, need more memory.
This series adds support for host backed DMA memory that can be used
by the firmware for the previously mentioned cases.
> diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c
> index a7a0bcf98ed3f..356a69c895b63 100644
> --- a/drivers/net/ethernet/amd/pds_core/core.c
> +++ b/drivers/net/ethernet/amd/pds_core/core.c
[ ... ]
> @@ -673,3 +675,161 @@ void pdsc_health_thread(struct work_struct *work)
[ ... ]
> +static int pdsc_host_mem_add_one(struct pdsc *pdsc, int index)
> +{
> + struct pdsc_host_mem *hm = &pdsc->host_mem_reqs[index];
> + union pds_core_dev_comp comp = {};
> + union pds_core_dev_cmd cmd = {};
> + int err;
[ ... ]
> + /* Track this allocation so pdsc_host_mem_free() can clean it up */
> + pdsc->num_host_mem_reqs++;
> +
> + memset(&cmd, 0, sizeof(cmd));
> + memset(&comp, 0, sizeof(comp));
> + cmd.host_mem.opcode = PDS_CORE_CMD_HOST_MEM;
> + cmd.host_mem.oper = PDS_CORE_HOST_MEM_ADD;
> + cmd.host_mem.tag = cpu_to_le16(hm->tag);
> + cmd.host_mem.size = cpu_to_le32(hm->size);
> + cmd.host_mem.buf_pa = cpu_to_le64(hm->pa);
> +
> + dev_dbg(pdsc->dev, "Sending devcmd for mem add tag %d size %d pa %pad\n",
> + hm->tag, hm->size, &hm->pa);
> + err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
> + if (err || comp.status != PDS_RC_SUCCESS) {
> + dev_err(pdsc->dev, "mem add failed err %d status %d for tag %d\n",
> + err, comp.status, hm->tag);
> + err = err ? err : -EIO;
> + goto err_del;
> + }
> + dev_dbg(pdsc->dev, "mem add completed for tag %d\n", hm->tag);
> +
> + return 0;
> +
> +err_del:
> + /* After MEM_QUERY succeeds, firmware expects MEM_ADD or MEM_DEL */
> + pdsc_host_mem_del_one(pdsc, hm->tag, PDS_RC_ENOMEM);
> + return err;
> +}
[Severity: Medium]
When the MEM_ADD device command fails, does this error path leave the memory
allocated and DMA mapped?
Since pdsc->num_host_mem_reqs was incremented before the command, the memory
will eventually be freed when pdsc_host_mem_free() runs at driver teardown.
However, it appears up to 4MB of memory per failed request could be held
uselessly for the entire operational runtime of the driver.
Should the driver explicitly unmap and free the memory in this error path?
[ ... ]
> +void pdsc_host_mem_free(struct pdsc *pdsc)
> +{
> + int i;
> +
> + if (!pdsc->host_mem_reqs)
> + return;
> +
> + for (i = 0; i < pdsc->num_host_mem_reqs; i++) {
> + dma_unmap_page(pdsc->dev, pdsc->host_mem_reqs[i].pa,
> + pdsc->host_mem_reqs[i].size,
> + DMA_BIDIRECTIONAL);
> + __free_pages(pdsc->host_mem_reqs[i].pg,
> + pdsc->host_mem_reqs[i].order);
> + }
> +
> + kfree(pdsc->host_mem_reqs);
> + pdsc->host_mem_reqs = NULL;
> + pdsc->num_host_mem_reqs = 0;
> +}
[Severity: High]
Does this code risk leaving the firmware with dangling DMA addresses?
The loop unmaps and frees the provisioned host memory pages, but it doesn't
appear to send a corresponding PDS_CORE_HOST_MEM_DEL device command to notify
the firmware that the memory has been revoked.
While pdsc_teardown() sends an explicit reset command for Physical Functions,
it skips this step for Virtual Functions. If a VF frees memory without sending
a MEM_DEL command, could the firmware still attempt to access the freed memory,
resulting in IOMMU faults or use-after-free corruption?
^ permalink raw reply
* Re: [PATCH net v3 2/3] net: dsa: mv88e6xxx: flush the ATU when the hash algorithm changes
From: Jonas Gorski @ 2026-07-16 7:24 UTC (permalink / raw)
To: Luke Howard
Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Vivien Didelot, Gregory CLEMENT,
Richard Cochran, Cedric Jehasse, Kieran Tyrrell, Max Holtmann,
Max Hunter, Christoph Mellauner, Simon Gapp, netdev, linux-kernel
In-Reply-To: <540B5427-A5BE-486A-818F-5D512E1BBE4E@padl.com>
On Thu, Jul 16, 2026 at 9:09 AM Luke Howard <lukeh@padl.com> wrote:
>
>
>
> > On 16 Jul 2026, at 4:52 am, Jonas Gorski <jonas.gorski@gmail.com> wrote:
> >
> > Probably also needs to be not attached to any bridge, right? To make
> > sure that no additional static entries can exist (because I think
> > these are hard to recreate).
>
> Thank you, I have fixed and tested locally and will include in the next revision.
The more extensive alternative would be adding a way to replay all
static fdb (and mdb) entries to re-add them after flushing the ATU,
then you can allow that any time. This would be also something
interesting for e.g. b53, where disabling (the global) vlan filtering
in hardware means you cannot add or remove (static) entries with VID
!= 0.
But either option is fine for me; my main concern is avoiding a desync
of hardware and software configuration.
Best regards,
Jonas
^ permalink raw reply
* Re: [PATCH net-next] net: stmmac: dwmac-rk: enable the reference clock for output mode
From: Maxime Chevallier @ 2026-07-16 7:30 UTC (permalink / raw)
To: Jiaxing Hu, andrew+netdev, davem, edumazet, kuba, pabeni, heiko,
mcoquelin.stm32, alexandre.torgue
Cc: netdev, linux-rockchip, linux-arm-kernel, linux-stm32,
linux-kernel
In-Reply-To: <02ef3409-4078-4c3a-8c17-cb1a7f1f07e4@smtp-relay.sendinblue.com>
Hi,
On 7/16/26 08:56, Jiaxing Hu wrote:
>
> rk_gmac_clk_init() only requests the refout clock group for RMII. The
> ArmSoM CM5 has an on-module YT8531 RGMII PHY with no crystal that needs
> the SoC 25 MHz reference (clk_mac_refout), so in RGMII the clock was
> never enabled and the PHY did not respond on MDIO.
>
> Request the group whenever the SoC drives the clock (clock_in_out =
> "output"), not just for RMII. The clocks are optional, so other boards
> are unaffected.
DT binding for rockchip-dwmac says :
clock_in_out:
description:
For RGMII, it must be "input", means main clock(125MHz)
is not sourced from SoC's PLL, but input from PHY.
For RMII, "input" means PHY provides the reference clock(50MHz),
"output" means GMAC provides the reference clock.
So the statement "For RGMII, it must be input [...]" is now incorrect :
- it can be output for RGMII
- it will be 25Hz if set to output, from what I get from the commit log.
Can you update it ?
Maxime
^ permalink raw reply
* [PATCH v2 2/3] net/9p: prepare fd transports for asynchronous aborts
From: Ze Tan @ 2026-07-16 7:30 UTC (permalink / raw)
To: ericvh, lucho, asmadeus, linux_oss, davem, edumazet, kuba, v9fs,
netdev
Cc: linux-kernel, tanze
In-Reply-To: <cover.1784186316.git.tanze@kylinos.cn>
The fd transports keep their request reference while a sent request is on
the receive list. That reference is released when a reply is consumed or
when p9_conn_cancel() tears the connection down, so the caller can stop
waiting without freeing the request or reusing its tag.
Add an explicit transport capability and teach the fd reply path to
consume replies for requests whose caller has aborted. Leave the capability
disabled for transports with different request or DMA lifetime rules.
Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
include/net/9p/client.h | 3 +++
include/net/9p/transport.h | 4 ++++
net/9p/trans_fd.c | 10 +++++++++-
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/include/net/9p/client.h b/include/net/9p/client.h
index 55c6cb54bd25..700dcb37c1dc 100644
--- a/include/net/9p/client.h
+++ b/include/net/9p/client.h
@@ -58,6 +58,8 @@ enum p9_trans_status {
* @REQ_STATUS_UNSENT: request waiting to be sent
* @REQ_STATUS_SENT: request sent to server
* @REQ_STATUS_RCVD: response received from server
+ * @REQ_STATUS_ABORTED: caller stopped waiting, but the request keeps its tag
+ * reserved until a reply arrives or the transport closes
* @REQ_STATUS_FLSHD: request has been flushed
* @REQ_STATUS_ERROR: request encountered an error on the client side
*/
@@ -67,6 +69,7 @@ enum p9_req_status_t {
REQ_STATUS_UNSENT,
REQ_STATUS_SENT,
REQ_STATUS_RCVD,
+ REQ_STATUS_ABORTED,
REQ_STATUS_FLSHD,
REQ_STATUS_ERROR,
};
diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h
index a912bbaa862f..93349fe33dff 100644
--- a/include/net/9p/transport.h
+++ b/include/net/9p/transport.h
@@ -34,6 +34,9 @@
* @supports_vmalloc: set if this transport can work with vmalloc'd buffers
* (non-physically contiguous memory). Transports requiring
* DMA should leave this as false.
+ * @supports_async_abort: set if a sent request remains referenced by the
+ * transport until its reply is consumed or the
+ * transport is closed
* @create: member function to create a new connection on this transport
* @close: member function to discard a connection on this transport
* @request: member function to issue a request to the transport
@@ -55,6 +58,7 @@ struct p9_trans_module {
bool pooled_rbuffers;
bool def; /* this transport should be default */
bool supports_vmalloc; /* can work with vmalloc'd buffers */
+ bool supports_async_abort; /* keeps sent requests alive after caller exits */
struct module *owner;
int (*create)(struct p9_client *client,
struct fs_context *fc);
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index eb685b52aeb2..d5cff8c2c88c 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -293,7 +293,9 @@ static void p9_read_work(struct work_struct *work)
m, m->rc.size, m->rc.tag);
m->rreq = p9_tag_lookup(m->client, m->rc.tag);
- if (!m->rreq || (m->rreq->status != REQ_STATUS_SENT)) {
+ if (!m->rreq ||
+ (m->rreq->status != REQ_STATUS_SENT &&
+ m->rreq->status != REQ_STATUS_ABORTED)) {
p9_debug(P9_DEBUG_ERROR, "Unexpected packet tag %d\n",
m->rc.tag);
err = -EIO;
@@ -332,6 +334,9 @@ static void p9_read_work(struct work_struct *work)
if (m->rreq->status == REQ_STATUS_SENT) {
list_del(&m->rreq->req_list);
p9_client_cb(m->client, m->rreq, REQ_STATUS_RCVD);
+ } else if (m->rreq->status == REQ_STATUS_ABORTED) {
+ list_del(&m->rreq->req_list);
+ p9_client_cb(m->client, m->rreq, REQ_STATUS_ABORTED);
} else if (m->rreq->status == REQ_STATUS_FLSHD) {
/* Ignore replies associated with a cancelled request. */
p9_debug(P9_DEBUG_TRANS,
@@ -996,6 +1001,7 @@ static struct p9_trans_module p9_tcp_trans = {
.pooled_rbuffers = false,
.def = false,
.supports_vmalloc = true,
+ .supports_async_abort = true,
.create = p9_fd_create_tcp,
.close = p9_fd_close,
.request = p9_fd_request,
@@ -1011,6 +1017,7 @@ static struct p9_trans_module p9_unix_trans = {
.maxsize = MAX_SOCK_BUF,
.def = false,
.supports_vmalloc = true,
+ .supports_async_abort = true,
.create = p9_fd_create_unix,
.close = p9_fd_close,
.request = p9_fd_request,
@@ -1026,6 +1033,7 @@ static struct p9_trans_module p9_fd_trans = {
.maxsize = MAX_SOCK_BUF,
.def = false,
.supports_vmalloc = true,
+ .supports_async_abort = true,
.create = p9_fd_create,
.close = p9_fd_close,
.request = p9_fd_request,
--
2.43.0
^ permalink raw reply related
* [PATCH v2 1/3] net/9p: handle replies racing with interrupted RPCs
From: Ze Tan @ 2026-07-16 7:30 UTC (permalink / raw)
To: ericvh, lucho, asmadeus, linux_oss, davem, edumazet, kuba, v9fs,
netdev
Cc: linux-kernel, tanze
In-Reply-To: <cover.1784186316.git.tanze@kylinos.cn>
A reply can arrive after io_wait_event_killable() reports an interruption
but before the client starts cancellation. In that case the request is
already complete and must not be flushed or returned as an error.
Recheck REQ_STATUS_RCVD before entering the flush path in both regular and
zero-copy RPCs.
Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
net/9p/client.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/net/9p/client.c b/net/9p/client.c
index ef64546c6d52..b9860ccb224b 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -612,6 +612,11 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
err = req->t_err;
}
if (err == -ERESTARTSYS && c->status == Connected) {
+ if (READ_ONCE(req->status) == REQ_STATUS_RCVD) {
+ err = 0;
+ goto recalc_sigpending;
+ }
+
p9_debug(P9_DEBUG_MUX, "flushing\n");
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
@@ -697,6 +702,11 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type,
err = req->t_err;
}
if (err == -ERESTARTSYS && c->status == Connected) {
+ if (READ_ONCE(req->status) == REQ_STATUS_RCVD) {
+ err = 0;
+ goto recalc_sigpending;
+ }
+
p9_debug(P9_DEBUG_MUX, "flushing\n");
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/3] net/9p: safely abort fd RPCs on fatal signals
From: Ze Tan @ 2026-07-16 7:30 UTC (permalink / raw)
To: ericvh, lucho, asmadeus, linux_oss, davem, edumazet, kuba, v9fs,
netdev
Cc: linux-kernel, tanze
Hi,
This series fixes a hung task reported by Syzkaller when a thread blocked
in a 9p fd RPC receives a fatal signal while another thread in the same
group is waiting in coredump_wait(). The interrupted RPC currently enters
the TFLUSH path and can wait indefinitely if the 9p server is no longer
responding, preventing the thread from exiting and the coredump from
completing.
Ze Tan (3):
net/9p: handle replies racing with interrupted RPCs
net/9p: prepare fd transports for asynchronous aborts
net/9p: abort interrupted fd RPCs on fatal signals
include/net/9p/client.h | 3 +++
include/net/9p/transport.h | 4 ++++
net/9p/client.c | 35 +++++++++++++++++++++++++++++++++++
net/9p/trans_fd.c | 10 +++++++++-
4 files changed, 51 insertions(+), 1 deletion(-)
base-commit: 58717b2a1365d06c8c64b72aa948541b53fe31eb
--
2.43.0
^ permalink raw reply
* [PATCH v2 3/3] net/9p: abort interrupted fd RPCs on fatal signals
From: Ze Tan @ 2026-07-16 7:30 UTC (permalink / raw)
To: ericvh, lucho, asmadeus, linux_oss, davem, edumazet, kuba, v9fs,
netdev
Cc: linux-kernel, tanze
In-Reply-To: <cover.1784186316.git.tanze@kylinos.cn>
Syzkaller reported a hung task while a thread group was dumping core. One
thread was blocked in p9_client_rpc() on a 9p fd mount while another
thread entered coredump_wait(). The coredump path sent a fatal signal to
the blocked thread and waited for it to exit, but the 9p client sent TFLUSH
and waited for an unresponsive server to acknowledge it.
INFO: task syz.1.4497:22259 blocked for more than 143 seconds.
Call trace:
__switch_to
__schedule
schedule
schedule_timeout
__wait_for_common
wait_for_completion_state
vfs_coredump
get_signal
do_notify_resume
Commit 6b4f48728faa ("net/9p: fix infinite loop in p9_client_rpc on fatal
signal") stopped retrying an interrupted TFLUSH when a fatal signal is
pending. The original non-flush RPC can still enter the TFLUSH path first,
which needlessly depends on the server while the task is trying to exit.
For transports that explicitly retain sent requests across an
asynchronous abort, cancel unsent requests as usual and mark sent requests
aborted without issuing TFLUSH. The fd receive or teardown path keeps the
request and tag alive until it releases the transport reference.
Do not enable this path for zero-copy RPCs or transports without the
required lifetime guarantee.
Fixes: 91b8534fa8f5 ("9p: make rpc code common and rework flush code")
Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
net/9p/client.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/net/9p/client.c b/net/9p/client.c
index b9860ccb224b..47353cf0750d 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -538,6 +538,22 @@ static struct p9_req_t *p9_client_prepare_req(struct p9_client *c,
return ERR_PTR(err);
}
+static void p9_client_abort(struct p9_client *c, struct p9_req_t *req)
+{
+ /*
+ * A fatal signal cannot wait for TFLUSH, but a sent request must keep
+ * its tag until a late reply arrives or the transport is torn down.
+ */
+ if (!c->trans_mod->supports_async_abort ||
+ READ_ONCE(req->status) >= REQ_STATUS_RCVD)
+ return;
+
+ if (!c->trans_mod->cancel(c, req))
+ return;
+
+ cmpxchg(&req->status, REQ_STATUS_SENT, REQ_STATUS_ABORTED);
+}
+
/**
* p9_client_rpc - issue a request and wait for a response
* @c: client session
@@ -617,6 +633,15 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
goto recalc_sigpending;
}
+ if (fatal_signal_pending(current) &&
+ c->trans_mod->supports_async_abort) {
+ p9_debug(P9_DEBUG_MUX, "fatal signal: skip flush\n");
+ p9_client_abort(c, req);
+ if (READ_ONCE(req->status) == REQ_STATUS_RCVD)
+ err = 0;
+ goto recalc_sigpending;
+ }
+
p9_debug(P9_DEBUG_MUX, "flushing\n");
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net v2] mctp: check register_netdevice_notifier() error in mctp_device_init()
From: Kuniyuki Iwashima @ 2026-07-16 7:32 UTC (permalink / raw)
To: Minhong He; +Cc: jk, matt, netdev, davem, edumazet, kuba, pabeni, horms
In-Reply-To: <20260716063534.116793-1-heminhong@kylinos.cn>
On Thu, Jul 16, 2026 at 8:35 AM Minhong He <heminhong@kylinos.cn> wrote:
>
> mctp_device_init() handles errors from rtnl_af_register() and
> rtnl_register_many(), but ignores the return value of
> register_netdevice_notifier(). If notifier registration fails, init can
> still return success while the module is only partially initialized.
>
> Check the notifier registration error and fail module init early.
>
> Fixes: d51705614f66 ("mctp: Handle error of rtnl_register_module().")
The bug predates the commit:
$ git blame -L:mctp_device_init net/mctp/device.c d51705614f668~
> Signed-off-by: Minhong He <heminhong@kylinos.cn>
> ---
> net/mctp/device.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/net/mctp/device.c b/net/mctp/device.c
> index 2c84df674669..822120e860c8 100644
> --- a/net/mctp/device.c
> +++ b/net/mctp/device.c
> @@ -536,7 +536,9 @@ int __init mctp_device_init(void)
> {
> int err;
>
> - register_netdevice_notifier(&mctp_dev_nb);
> + err = register_netdevice_notifier(&mctp_dev_nb);
> + if (err)
> + return err;
>
> err = rtnl_af_register(&mctp_af_ops);
> if (err)
> --
> 2.25.1
>
^ permalink raw reply
* Re: [PATCH v7] net: gro: fix double aggregation of flush-marked skbs
From: Jakub Raczynski @ 2026-07-16 7:35 UTC (permalink / raw)
To: Shiming Cheng (成诗明)
Cc: linux-kernel@vger.kernel.org, dsahern@kernel.org,
imv4bel@gmail.com, linux-mediatek@lists.infradead.org,
alice@isovalent.com, daniel.zahka@gmail.com,
eilaimemedsnaimel@gmail.com, nbd@nbd.name, horms@kernel.org,
kuba@kernel.org, pabeni@redhat.com, edumazet@google.com,
willemdebruijn.kernel@gmail.com, willemb@google.com,
netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
matthias.bgg@gmail.com, davem@davemloft.net,
AngeloGioacchino Del Regno, sd@queasysnail.net,
steffen.klassert@secunet.com, stable@vger.kernel.org,
Lena Wang (王娜)
In-Reply-To: <99ce187af1495ffac592bfcf7ef97fc61a7f780b.camel@mediatek.com>
[-- Attachment #1: Type: text/plain, Size: 492 bytes --]
On Thu, Jul 16, 2026 at 01:41:12AM +0000, Shiming Cheng (成诗明) wrote:
> Hello everyone,
>
> it looks like this has been peeding for a while.
> Is there anything else I need to do or follow up on ?
>
> Thanks
>
Hi,
There was an announcement
https://lore.kernel.org/netdev/c0cd72bd-6298-47a4-82a3-c43811c98063@redhat.com/
and currently there are over 1000 patches waiting for processing
https://patchwork.kernel.org/project/netdevbpf/list/
So need to be patient.
BR
Jakub Raczynski
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* [PATCH net-next v11 5/5] net: wangxun: add pcie error handler
From: Jiawen Wu @ 2026-07-16 7:38 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Aleksandr Loktionov, Jacob Keller, Michal Swiatkowski, Kees Cook,
Larysa Zaremba, Breno Leitao, Joe Damato,
Uwe Kleine-König (The Capable Hub), Rongguang Wei,
Fabio Baltieri, Jiawen Wu
In-Reply-To: <20260716073822.24356-1-jiawenwu@trustnetic.com>
Support AER driver to handle the PCIe errors. Sometimes netdev watchdog
Tx timeout happens before the AER error report when a PCIe error occurs,
CPU blocking would be caused by MMIO during the reset process. To
prevent it, check PCIe error status in .ndo_tx_timeout. The current
function of ngbe is not yet fully developed, it will be completed in the
future.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_err.c | 144 +++++++++++++++++-
drivers/net/ethernet/wangxun/libwx/wx_err.h | 2 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 4 +
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 32 +++-
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 31 +++-
5 files changed, 208 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
index ee27f96735dc..0cb99e456a1c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
@@ -4,11 +4,120 @@
#include <linux/netdevice.h>
#include <linux/pci.h>
+#include <linux/aer.h>
#include "wx_type.h"
#include "wx_lib.h"
#include "wx_err.h"
+/**
+ * wx_io_error_detected - called when PCI error is detected
+ * @pdev: Pointer to PCI device
+ * @state: The current pci connection state
+ *
+ * Return: pci_ers_result_t.
+ *
+ * This function is called after a PCI bus error affecting
+ * this device has been detected.
+ */
+static pci_ers_result_t wx_io_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ struct wx *wx = pci_get_drvdata(pdev);
+ struct net_device *netdev;
+
+ if (!wx)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ netdev = wx->netdev;
+ if (!netif_device_present(netdev))
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ if (state == pci_channel_io_perm_failure)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ rtnl_lock();
+ netif_device_detach(netdev);
+ set_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
+ wx_soft_quiesce(wx);
+
+ if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+ pci_disable_device(pdev);
+ rtnl_unlock();
+
+ /* Request a slot reset. */
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * wx_io_slot_reset - called after the pci bus has been reset.
+ * @pdev: Pointer to PCI device
+ *
+ * Return: pci_ers_result_t.
+ *
+ * Restart the card from scratch, as if from a cold-boot.
+ */
+static pci_ers_result_t wx_io_slot_reset(struct pci_dev *pdev)
+{
+ struct wx *wx = pci_get_drvdata(pdev);
+
+ if (pci_enable_device_mem(pdev)) {
+ wx_err(wx, "Cannot re-enable PCI device after reset.\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ /* make all memory operations done before clearing the flag */
+ smp_mb__before_atomic();
+ clear_bit(WX_STATE_DISABLED, wx->state);
+ clear_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
+ pci_set_master(pdev);
+ pci_restore_state(pdev);
+ pci_wake_from_d3(pdev, false);
+
+ rtnl_lock();
+ if (netif_running(wx->netdev) && wx->down_suspend)
+ wx->down_suspend(wx);
+ if (wx->do_reset)
+ wx->do_reset(wx->netdev, false);
+ rtnl_unlock();
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * wx_io_resume - called when traffic can start flowing again.
+ * @pdev: Pointer to PCI device
+ *
+ * This callback is called when the error recovery driver tells us that
+ * its OK to resume normal operation.
+ */
+static void wx_io_resume(struct pci_dev *pdev)
+{
+ struct wx *wx = pci_get_drvdata(pdev);
+ struct net_device *netdev;
+ int err;
+
+ netdev = wx->netdev;
+ rtnl_lock();
+ if (netif_running(netdev)) {
+ err = netdev->netdev_ops->ndo_open(netdev);
+ if (err) {
+ wx_err(wx, "Failed to open netdev after reset\n");
+ goto out;
+ }
+ }
+ netif_device_attach(netdev);
+out:
+ rtnl_unlock();
+}
+
+const struct pci_error_handlers wx_err_handler = {
+ .error_detected = wx_io_error_detected,
+ .slot_reset = wx_io_slot_reset,
+ .resume = wx_io_resume,
+};
+EXPORT_SYMBOL(wx_err_handler);
+
static void wx_pf_reset_subtask(struct wx *wx)
{
if (!test_and_clear_bit(WX_FLAG_NEED_PF_RESET, wx->flags))
@@ -25,6 +134,9 @@ static void wx_reset_task(struct work_struct *work)
rtnl_lock();
+ if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
+ wx_soft_quiesce(wx);
+
if (test_bit(WX_STATE_DOWN, wx->state) ||
test_bit(WX_STATE_RESETTING, wx->state))
goto out;
@@ -139,6 +251,33 @@ void wx_check_hang_subtask(struct wx *wx)
}
EXPORT_SYMBOL(wx_check_hang_subtask);
+static bool wx_check_pcie_error(struct wx *wx)
+{
+ u16 vid, pci_cmd;
+
+ pci_read_config_word(wx->pdev, PCI_VENDOR_ID, &vid);
+ pci_read_config_word(wx->pdev, PCI_COMMAND, &pci_cmd);
+
+ /* PCIe link loss or memory space can't access */
+ if (vid == U16_MAX || !(pci_cmd & PCI_COMMAND_MEMORY))
+ return true;
+
+ return false;
+}
+
+static void wx_tx_timeout_recovery(struct wx *wx)
+{
+ /*
+ * When a PCIe hardware error occurs, the driver should initiate a PCIe
+ * recovery mechanism. However, this recovery flow relies on the AER
+ * driver for current kernel policy. Therefore, a self-contained
+ * recovery mechanism is not implemented yet.
+ */
+ set_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
+ wx_err(wx, "PCIe error detected during tx timeout\n");
+ queue_work(wx->reset_wq, &wx->reset_task);
+}
+
static void wx_tx_timeout_reset(struct wx *wx)
{
if (test_bit(WX_STATE_DOWN, wx->state))
@@ -153,7 +292,10 @@ void wx_tx_timeout(struct net_device *netdev, unsigned int __always_unused txque
{
struct wx *wx = netdev_priv(netdev);
- wx_tx_timeout_reset(wx);
+ if (wx_check_pcie_error(wx))
+ wx_tx_timeout_recovery(wx);
+ else
+ wx_tx_timeout_reset(wx);
}
EXPORT_SYMBOL(wx_tx_timeout);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h
index 1eed13e48095..a6a82a263528 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_err.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h
@@ -7,6 +7,8 @@
#ifndef _WX_ERR_H_
#define _WX_ERR_H_
+extern const struct pci_error_handlers wx_err_handler;
+
void wx_check_err_subtask(struct wx *wx);
int wx_init_err_task(struct wx *wx);
void wx_check_hang_subtask(struct wx *wx);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index edcb8d121b86..aa357e260c4c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -1222,6 +1222,8 @@ enum wx_state {
WX_STATE_PTP_RUNNING,
WX_STATE_PTP_TX_IN_PROGRESS,
WX_STATE_SERVICE_SCHED,
+ WX_STATE_DISABLED,
+ WX_STATE_RES_FREED,
WX_STATE_NBITS /* must be last */
};
@@ -1289,6 +1291,7 @@ enum wx_pf_flags {
WX_FLAG_RX_MERGE_ENABLED,
WX_FLAG_TXHEAD_WB_ENABLED,
WX_FLAG_NEED_PF_RESET,
+ WX_FLAG_NEED_PCIE_RECOVERY,
WX_PF_FLAGS_NBITS /* must be last */
};
@@ -1410,6 +1413,7 @@ struct wx {
void (*configure_fdir)(struct wx *wx);
int (*setup_tc)(struct net_device *netdev, u8 tc);
void (*do_reset)(struct net_device *netdev, bool reinit);
+ void (*down_suspend)(struct wx *wx);
int (*ptp_setup_sdp)(struct wx *wx);
void (*set_num_queues)(struct wx *wx);
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 92895f503511..56d4b63387fd 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -47,6 +47,20 @@ static const struct pci_device_id ngbe_pci_tbl[] = {
{ }
};
+static void ngbe_down_suspend(struct wx *wx)
+{
+ if (test_and_set_bit(WX_STATE_RES_FREED, wx->state))
+ return;
+
+ phylink_stop(wx->phylink);
+ phylink_disconnect_phy(wx->phylink);
+ wx_clean_all_tx_rings(wx);
+ wx_clean_all_rx_rings(wx);
+ wx_free_irq(wx);
+ wx_free_isb_resources(wx);
+ wx_free_resources(wx);
+}
+
/**
* ngbe_init_type_code - Initialize the shared code
* @wx: pointer to hardware structure
@@ -135,6 +149,7 @@ static int ngbe_sw_init(struct wx *wx)
wx->mbx.size = WX_VXMAILBOX_SIZE;
wx->setup_tc = ngbe_setup_tc;
wx->do_reset = ngbe_do_reset;
+ wx->down_suspend = ngbe_down_suspend;
set_bit(0, &wx->fwd_bitmask);
return 0;
@@ -413,6 +428,9 @@ static void ngbe_disable_device(struct wx *wx)
static void ngbe_reset(struct wx *wx)
{
+ if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
+ return;
+
wx_flush_sw_mac_table(wx);
wx_mac_set_default_filter(wx, wx->mac.addr);
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
@@ -435,6 +453,7 @@ static void ngbe_up_complete(struct wx *wx)
/* make sure to complete pre-operations */
smp_mb__before_atomic();
clear_bit(WX_STATE_DOWN, wx->state);
+ clear_bit(WX_STATE_RES_FREED, wx->state);
wx_napi_enable_all(wx);
/* enable transmits */
netif_tx_start_all_queues(wx->netdev);
@@ -529,12 +548,16 @@ static int ngbe_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
+ if (test_bit(WX_STATE_RES_FREED, wx->state))
+ goto out;
+
wx_ptp_stop(wx);
ngbe_down(wx);
wx_free_irq(wx);
wx_free_isb_resources(wx);
wx_free_resources(wx);
phylink_disconnect_phy(wx->phylink);
+out:
wx_control_hw(wx, false);
return 0;
@@ -566,7 +589,8 @@ static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
*enable_wake = !!wufc;
wx_control_hw(wx, false);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+ pci_disable_device(pdev);
}
static void ngbe_shutdown(struct pci_dev *pdev)
@@ -855,6 +879,7 @@ static int ngbe_probe(struct pci_dev *pdev,
goto err_register;
pci_set_drvdata(pdev, wx);
+ pci_save_state(pdev);
return 0;
@@ -910,7 +935,8 @@ static void ngbe_remove(struct pci_dev *pdev)
kfree(wx->mac_table);
wx_clear_interrupt_scheme(wx);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+ pci_disable_device(pdev);
}
static int ngbe_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -937,6 +963,7 @@ static int ngbe_resume(struct pci_dev *pdev)
wx_err(wx, "Cannot enable PCI device from suspend\n");
return err;
}
+ clear_bit(WX_STATE_DISABLED, wx->state);
pci_set_master(pdev);
device_wakeup_disable(&pdev->dev);
@@ -961,6 +988,7 @@ static struct pci_driver ngbe_driver = {
.resume = ngbe_resume,
.shutdown = ngbe_shutdown,
.sriov_configure = wx_pci_sriov_configure,
+ .err_handler = &wx_err_handler,
};
module_pci_driver(ngbe_driver);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 7a05661b1d80..499f48619f00 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -163,6 +163,7 @@ static void txgbe_up_complete(struct wx *wx)
/* make sure to complete pre-operations */
smp_mb__before_atomic();
clear_bit(WX_STATE_DOWN, wx->state);
+ clear_bit(WX_STATE_RES_FREED, wx->state);
wx_napi_enable_all(wx);
switch (wx->mac.type) {
@@ -206,6 +207,9 @@ static void txgbe_reset(struct wx *wx)
u8 old_addr[ETH_ALEN];
int err;
+ if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
+ return;
+
err = txgbe_reset_hw(wx);
if (err != 0)
wx_err(wx, "Hardware Error: %d\n", err);
@@ -312,6 +316,20 @@ void txgbe_up(struct wx *wx)
txgbe_up_complete(wx);
}
+static void txgbe_down_suspend(struct wx *wx)
+{
+ if (test_and_set_bit(WX_STATE_RES_FREED, wx->state))
+ return;
+
+ phylink_stop(wx->phylink);
+ wx_clean_all_tx_rings(wx);
+ wx_clean_all_rx_rings(wx);
+ wx_free_irq(wx);
+ txgbe_free_misc_irq(wx->priv);
+ wx_free_resources(wx);
+ txgbe_fdir_filter_exit(wx);
+}
+
/**
* txgbe_init_type_code - Initialize the shared code
* @wx: pointer to hardware structure
@@ -428,6 +446,7 @@ static int txgbe_sw_init(struct wx *wx)
wx->setup_tc = txgbe_setup_tc;
wx->do_reset = txgbe_do_reset;
+ wx->down_suspend = txgbe_down_suspend;
set_bit(0, &wx->fwd_bitmask);
switch (wx->mac.type) {
@@ -538,12 +557,16 @@ static int txgbe_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
+ if (test_bit(WX_STATE_RES_FREED, wx->state))
+ goto out;
+
wx_ptp_stop(wx);
txgbe_down(wx);
wx_free_irq(wx);
txgbe_free_misc_irq(wx->priv);
wx_free_resources(wx);
txgbe_fdir_filter_exit(wx);
+out:
wx_control_hw(wx, false);
return 0;
@@ -564,7 +587,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev)
wx_control_hw(wx, false);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+ pci_disable_device(pdev);
}
static void txgbe_shutdown(struct pci_dev *pdev)
@@ -915,6 +939,7 @@ static int txgbe_probe(struct pci_dev *pdev,
goto err_remove_phy;
pci_set_drvdata(pdev, wx);
+ pci_save_state(pdev);
netif_tx_stop_all_queues(netdev);
@@ -989,7 +1014,8 @@ static void txgbe_remove(struct pci_dev *pdev)
kfree(wx->mac_table);
wx_clear_interrupt_scheme(wx);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
+ pci_disable_device(pdev);
}
static struct pci_driver txgbe_driver = {
@@ -999,6 +1025,7 @@ static struct pci_driver txgbe_driver = {
.remove = txgbe_remove,
.shutdown = txgbe_shutdown,
.sriov_configure = wx_pci_sriov_configure,
+ .err_handler = &wx_err_handler,
};
module_pci_driver(txgbe_driver);
--
2.51.0
^ permalink raw reply related
* [PATCH net-next v11 3/5] net: wangxun: add reinit parameter to wx->do_reset callback
From: Jiawen Wu @ 2026-07-16 7:38 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Aleksandr Loktionov, Jacob Keller, Michal Swiatkowski, Kees Cook,
Larysa Zaremba, Breno Leitao, Joe Damato,
Uwe Kleine-König (The Capable Hub), Rongguang Wei,
Fabio Baltieri, Jiawen Wu
In-Reply-To: <20260716073822.24356-1-jiawenwu@trustnetic.com>
To implement a simple hardware reset without tearing down the network
interface state, introduce a boolean 'reinit' parameter to wx->do_reset
callback.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/wangxun/libwx/wx_err.c | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_ethtool.c | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 4 ++--
drivers/net/ethernet/wangxun/libwx/wx_type.h | 2 +-
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 4 ++--
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 2 +-
drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 4 ++--
drivers/net/ethernet/wangxun/txgbe/txgbe_type.h | 2 +-
8 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
index b6e2d16d4a16..ee27f96735dc 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
@@ -16,7 +16,7 @@ static void wx_pf_reset_subtask(struct wx *wx)
wx_warn(wx, "Reset adapter.\n");
if (wx->do_reset)
- wx->do_reset(wx->netdev);
+ wx->do_reset(wx->netdev, true);
}
static void wx_reset_task(struct work_struct *work)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index 5df971aca9e3..d1356ff5d69b 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -395,7 +395,7 @@ static void wx_update_rsc(struct wx *wx)
/* reset the device to apply the new RSC setting */
if (need_reset && wx->do_reset)
- wx->do_reset(netdev);
+ wx->do_reset(netdev, true);
}
int wx_set_coalesce(struct net_device *netdev,
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 3a6db657c685..c8a4822d3819 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -3149,7 +3149,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
netdev->features = features;
if (changed & NETIF_F_HW_VLAN_CTAG_RX && wx->do_reset)
- wx->do_reset(netdev);
+ wx->do_reset(netdev, true);
else if (changed & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER))
wx_set_rx_mode(netdev);
@@ -3199,7 +3199,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
out:
if (need_reset && wx->do_reset)
- wx->do_reset(netdev);
+ wx->do_reset(netdev, true);
return 0;
}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index e12a59305193..edcb8d121b86 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -1409,7 +1409,7 @@ struct wx {
void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
void (*configure_fdir)(struct wx *wx);
int (*setup_tc)(struct net_device *netdev, u8 tc);
- void (*do_reset)(struct net_device *netdev);
+ void (*do_reset)(struct net_device *netdev, bool reinit);
int (*ptp_setup_sdp)(struct wx *wx);
void (*set_num_queues)(struct wx *wx);
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 996c48da52d7..92895f503511 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -633,11 +633,11 @@ static void ngbe_reinit_locked(struct wx *wx)
mutex_unlock(&wx->reset_lock);
}
-void ngbe_do_reset(struct net_device *netdev)
+void ngbe_do_reset(struct net_device *netdev, bool reinit)
{
struct wx *wx = netdev_priv(netdev);
- if (netif_running(netdev))
+ if (netif_running(netdev) && reinit)
ngbe_reinit_locked(wx);
else
ngbe_reset(wx);
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index 4f648f272c08..c9233dc7ae50 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -125,6 +125,6 @@ extern char ngbe_driver_name[];
void ngbe_down(struct wx *wx);
void ngbe_up(struct wx *wx);
int ngbe_setup_tc(struct net_device *dev, u8 tc);
-void ngbe_do_reset(struct net_device *netdev);
+void ngbe_do_reset(struct net_device *netdev, bool reinit);
#endif /* _NGBE_TYPE_H_ */
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index b1615f82a265..a8773712cff8 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -610,11 +610,11 @@ static void txgbe_reinit_locked(struct wx *wx)
mutex_unlock(&wx->reset_lock);
}
-void txgbe_do_reset(struct net_device *netdev)
+void txgbe_do_reset(struct net_device *netdev, bool reinit)
{
struct wx *wx = netdev_priv(netdev);
- if (netif_running(netdev))
+ if (netif_running(netdev) && reinit)
txgbe_reinit_locked(wx);
else
txgbe_reset(wx);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 877234e3fdc2..3e93a3f309c1 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -313,7 +313,7 @@ extern char txgbe_driver_name[];
void txgbe_down(struct wx *wx);
void txgbe_up(struct wx *wx);
int txgbe_setup_tc(struct net_device *dev, u8 tc);
-void txgbe_do_reset(struct net_device *netdev);
+void txgbe_do_reset(struct net_device *netdev, bool reinit);
#define DECLARE_PHY_INTERFACE_MASK_ZERO(name) \
unsigned long name[PHY_INTERFACE_MODE_MAX] = { 0, }
--
2.51.0
^ permalink raw reply related
* [PATCH net-next v11 2/5] net: wangxun: add Tx timeout process
From: Jiawen Wu @ 2026-07-16 7:38 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Aleksandr Loktionov, Jacob Keller, Michal Swiatkowski, Kees Cook,
Larysa Zaremba, Breno Leitao, Joe Damato,
Uwe Kleine-König (The Capable Hub), Rongguang Wei,
Fabio Baltieri, Jiawen Wu
In-Reply-To: <20260716073822.24356-1-jiawenwu@trustnetic.com>
Implement .ndo_tx_timeout to handle Tx side timeout event. When a Tx
timeout event occur, it will trigger driver into reset process. And
allocate a separate work queue for reset process.
The WX_HANG_CHECK_ARMED bit is set to indicate a potential hang. It will
be cleared if a pause frame is received to avoid false hang detection
caused by pause frames.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/Makefile | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_err.c | 175 ++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_err.h | 16 ++
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 17 +-
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 37 ++++
drivers/net/ethernet/wangxun/libwx/wx_type.h | 19 +-
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 14 ++
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 14 ++
8 files changed, 289 insertions(+), 5 deletions(-)
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.c
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.h
diff --git a/drivers/net/ethernet/wangxun/libwx/Makefile b/drivers/net/ethernet/wangxun/libwx/Makefile
index a71b0ad77de3..c8724bb129aa 100644
--- a/drivers/net/ethernet/wangxun/libwx/Makefile
+++ b/drivers/net/ethernet/wangxun/libwx/Makefile
@@ -4,5 +4,5 @@
obj-$(CONFIG_LIBWX) += libwx.o
-libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o
+libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o wx_err.o
libwx-objs += wx_vf.o wx_vf_lib.o wx_vf_common.o
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
new file mode 100644
index 000000000000..b6e2d16d4a16
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd. */
+/* Copyright (c) 1999 - 2026 Intel Corporation. */
+
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+
+#include "wx_type.h"
+#include "wx_lib.h"
+#include "wx_err.h"
+
+static void wx_pf_reset_subtask(struct wx *wx)
+{
+ if (!test_and_clear_bit(WX_FLAG_NEED_PF_RESET, wx->flags))
+ return;
+
+ wx_warn(wx, "Reset adapter.\n");
+ if (wx->do_reset)
+ wx->do_reset(wx->netdev);
+}
+
+static void wx_reset_task(struct work_struct *work)
+{
+ struct wx *wx = container_of(work, struct wx, reset_task);
+
+ rtnl_lock();
+
+ if (test_bit(WX_STATE_DOWN, wx->state) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ goto out;
+
+ wx_pf_reset_subtask(wx);
+
+out:
+ rtnl_unlock();
+}
+
+void wx_check_err_subtask(struct wx *wx)
+{
+ if (test_bit(WX_FLAG_NEED_PF_RESET, wx->flags))
+ queue_work(wx->reset_wq, &wx->reset_task);
+}
+EXPORT_SYMBOL(wx_check_err_subtask);
+
+int wx_init_err_task(struct wx *wx)
+{
+ wx->reset_wq = alloc_workqueue("%s_reset_wq_%x", WQ_UNBOUND | WQ_HIGHPRI,
+ 1, wx->driver_name, pci_dev_id(wx->pdev));
+ if (!wx->reset_wq) {
+ wx_err(wx, "Failed to create wx_reset_wq workqueue\n");
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&wx->reset_task, wx_reset_task);
+ return 0;
+}
+EXPORT_SYMBOL(wx_init_err_task);
+
+static bool wx_ring_tx_pending(struct wx *wx)
+{
+ int i;
+
+ for (i = 0; i < wx->num_tx_queues; i++) {
+ struct wx_ring *tx_ring = wx->tx_ring[i];
+
+ if (tx_ring->next_to_use != tx_ring->next_to_clean)
+ return true;
+ }
+
+ return false;
+}
+
+static bool wx_vf_tx_pending(struct wx *wx)
+{
+ struct wx_ring_feature *vmdq = &wx->ring_feature[RING_F_VMDQ];
+ u32 q_per_pool = __ALIGN_MASK(1, ~vmdq->mask);
+ u32 i, j;
+
+ if (!wx->num_vfs)
+ return false;
+
+ for (i = 0; i < wx->num_vfs; i++) {
+ for (j = 0; j < q_per_pool; j++) {
+ u32 h, t;
+
+ h = rd32(wx, WX_PX_TR_RP_PV(q_per_pool, i, j));
+ t = rd32(wx, WX_PX_TR_WP_PV(q_per_pool, i, j));
+
+ if (h != t)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void wx_watchdog_flush_tx(struct wx *wx)
+{
+ if (!netif_running(wx->netdev))
+ return;
+ if (netif_carrier_ok(wx->netdev))
+ return;
+
+ if (wx_ring_tx_pending(wx) || wx_vf_tx_pending(wx)) {
+ /* We've lost link, so the controller stops DMA,
+ * but we've got queued Tx work that's never going
+ * to get done, so reset controller to flush Tx.
+ * (Do the reset outside of interrupt context).
+ */
+ wx_warn(wx, "initiating reset due to lost link with pending Tx work\n");
+ set_bit(WX_FLAG_NEED_PF_RESET, wx->flags);
+ }
+}
+
+static void wx_detect_tx_hang(struct wx *wx)
+{
+ int i;
+
+ /* If we're down or resetting, just bail */
+ if (!netif_running(wx->netdev) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ return;
+
+ /* Force detection of hung controller */
+ if (netif_carrier_ok(wx->netdev)) {
+ for (i = 0; i < wx->num_tx_queues; i++)
+ set_bit(WX_TX_DETECT_HANG, wx->tx_ring[i]->state);
+ }
+}
+
+void wx_check_hang_subtask(struct wx *wx)
+{
+ if (test_bit(WX_STATE_DOWN, wx->state) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ return;
+
+ wx_watchdog_flush_tx(wx);
+ wx_detect_tx_hang(wx);
+}
+EXPORT_SYMBOL(wx_check_hang_subtask);
+
+static void wx_tx_timeout_reset(struct wx *wx)
+{
+ if (test_bit(WX_STATE_DOWN, wx->state))
+ return;
+
+ set_bit(WX_FLAG_NEED_PF_RESET, wx->flags);
+ wx_warn(wx, "initiating reset due to tx timeout\n");
+ wx_service_event_schedule(wx);
+}
+
+void wx_tx_timeout(struct net_device *netdev, unsigned int __always_unused txqueue)
+{
+ struct wx *wx = netdev_priv(netdev);
+
+ wx_tx_timeout_reset(wx);
+}
+EXPORT_SYMBOL(wx_tx_timeout);
+
+void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next)
+{
+ struct wx *wx = netdev_priv(tx_ring->netdev);
+
+ wx_warn(wx,
+ "Detected Tx Unit Hang: Queue %d, TDH %x, TDT %x, ntu %x, ntc %x, ntc.time_stamp %lx, jiffies %lx\n",
+ tx_ring->queue_index,
+ rd32(wx, WX_PX_TR_RP(tx_ring->reg_idx)),
+ rd32(wx, WX_PX_TR_WP(tx_ring->reg_idx)),
+ tx_ring->next_to_use, next,
+ tx_ring->tx_buffer_info[next].time_stamp, jiffies);
+
+ netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
+
+ wx_tx_timeout_reset(wx);
+}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h
new file mode 100644
index 000000000000..1eed13e48095
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * WangXun Gigabit PCI Express Linux driver
+ * Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd.
+ */
+
+#ifndef _WX_ERR_H_
+#define _WX_ERR_H_
+
+void wx_check_err_subtask(struct wx *wx);
+int wx_init_err_task(struct wx *wx);
+void wx_check_hang_subtask(struct wx *wx);
+void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue);
+void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next);
+
+#endif /* _WX_ERR_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 260e14d5d541..122c4952d203 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -1932,6 +1932,7 @@ static void wx_configure_tx_ring(struct wx *wx,
else
ring->atr_sample_rate = 0;
+ bitmap_zero(ring->state, WX_RING_STATE_NBITS);
/* reinitialize tx_buffer_info */
memset(ring->tx_buffer_info, 0,
sizeof(struct wx_tx_buffer) * ring->count);
@@ -2851,16 +2852,26 @@ EXPORT_SYMBOL(wx_fc_enable);
static void wx_update_xoff_rx_lfc(struct wx *wx)
{
struct wx_hw_stats *hwstats = &wx->stats;
+ u64 data;
+ int i;
if (wx->fc.mode != wx_fc_full &&
wx->fc.mode != wx_fc_rx_pause)
return;
if (wx->mac.type >= wx_mac_aml)
- hwstats->lxoffrxc += rd32_wrap(wx, WX_MAC_LXOFFRXC_AML,
- &wx->last_stats.lxoffrxc);
+ data = rd32_wrap(wx, WX_MAC_LXOFFRXC_AML,
+ &wx->last_stats.lxoffrxc);
else
- hwstats->lxoffrxc += rd64(wx, WX_MAC_LXOFFRXC);
+ data = rd64(wx, WX_MAC_LXOFFRXC);
+ hwstats->lxoffrxc += data;
+
+ /* refill credits (no tx hang) if we received xoff */
+ if (!data)
+ return;
+
+ for (i = 0; i < wx->num_tx_queues; i++)
+ clear_bit(WX_HANG_CHECK_ARMED, wx->tx_ring[i]->state);
}
/**
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 814d88d2aee4..3a6db657c685 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -14,6 +14,7 @@
#include "wx_type.h"
#include "wx_lib.h"
+#include "wx_err.h"
#include "wx_ptp.h"
#include "wx_hw.h"
#include "wx_vf_lib.h"
@@ -742,6 +743,37 @@ static struct netdev_queue *wx_txring_txq(const struct wx_ring *ring)
return netdev_get_tx_queue(ring->netdev, ring->queue_index);
}
+static u32 wx_get_tx_pending(struct wx_ring *ring)
+{
+ unsigned int head, tail;
+
+ head = ring->next_to_clean;
+ tail = ring->next_to_use;
+
+ return ((head <= tail) ? tail : tail + ring->count) - head;
+}
+
+static bool wx_check_tx_hang(struct wx_ring *ring)
+{
+ u32 tx_done_old = ring->tx_stats.tx_done_old;
+ u32 tx_pending = wx_get_tx_pending(ring);
+ u32 tx_done = ring->stats.packets;
+
+ if (!test_and_clear_bit(WX_TX_DETECT_HANG, ring->state))
+ return false;
+
+ if (tx_done_old == tx_done && tx_pending)
+ /* make sure it is true for two checks in a row */
+ return test_and_set_bit(WX_HANG_CHECK_ARMED, ring->state);
+
+ /* update completed stats and continue */
+ ring->tx_stats.tx_done_old = tx_done;
+ /* reset the countdown */
+ clear_bit(WX_HANG_CHECK_ARMED, ring->state);
+
+ return false;
+}
+
/**
* wx_clean_tx_irq - Reclaim resources after transmit completes
* @q_vector: structure containing interrupt and ring information
@@ -866,6 +898,11 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector,
netdev_tx_completed_queue(wx_txring_txq(tx_ring),
total_packets, total_bytes);
+ if (wx_check_tx_hang(tx_ring)) {
+ wx_handle_tx_hang(tx_ring, i);
+ return true;
+ }
+
#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) &&
(wx_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) {
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 65e3e55db1cf..e12a59305193 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -450,6 +450,11 @@ enum WX_MSCA_CMD_value {
#define WX_PX_TR_CFG_THRE_SHIFT 8
#define WX_PX_TR_CFG_HEAD_WB BIT(27)
+#define WX_PX_TR_RP_PV(q_per_pool, vf_number, vf_q_index) \
+ (WX_PX_TR_RP((q_per_pool) * (vf_number) + (vf_q_index)))
+#define WX_PX_TR_WP_PV(q_per_pool, vf_number, vf_q_index) \
+ (WX_PX_TR_WP((q_per_pool) * (vf_number) + (vf_q_index)))
+
/* Receive DMA Registers */
#define WX_PX_RR_BAL(_i) (0x01000 + ((_i) * 0x40))
#define WX_PX_RR_BAH(_i) (0x01004 + ((_i) * 0x40))
@@ -1040,6 +1045,7 @@ struct wx_queue_stats {
struct wx_tx_queue_stats {
u64 restart_queue;
u64 tx_busy;
+ u32 tx_done_old;
};
struct wx_rx_queue_stats {
@@ -1055,6 +1061,12 @@ struct wx_rx_queue_stats {
#define wx_for_each_ring(posm, headm) \
for (posm = (headm).ring; posm; posm = posm->next)
+enum wx_ring_state {
+ WX_TX_DETECT_HANG,
+ WX_HANG_CHECK_ARMED,
+ WX_RING_STATE_NBITS
+};
+
struct wx_ring_container {
struct wx_ring *ring; /* pointer to linked list of rings */
unsigned int total_bytes; /* total bytes processed this int */
@@ -1074,6 +1086,7 @@ struct wx_ring {
struct wx_tx_buffer *tx_buffer_info;
struct wx_rx_buffer *rx_buffer_info;
};
+ DECLARE_BITMAP(state, WX_RING_STATE_NBITS);
u8 __iomem *tail;
dma_addr_t dma; /* phys. address of descriptor ring */
dma_addr_t headwb_dma;
@@ -1275,6 +1288,7 @@ enum wx_pf_flags {
WX_FLAG_NEED_DO_RESET,
WX_FLAG_RX_MERGE_ENABLED,
WX_FLAG_TXHEAD_WB_ENABLED,
+ WX_FLAG_NEED_PF_RESET,
WX_PF_FLAGS_NBITS /* must be last */
};
@@ -1423,6 +1437,8 @@ struct wx {
struct timer_list service_timer;
struct work_struct service_task;
+ struct work_struct reset_task;
+ struct workqueue_struct *reset_wq;
struct mutex reset_lock; /* mutex for reset */
};
@@ -1505,7 +1521,8 @@ rd32_wrap(struct wx *wx, u32 reg, u32 *last)
#define wx_err(wx, fmt, arg...) \
dev_err(&(wx)->pdev->dev, fmt, ##arg)
-
+#define wx_warn(wx, fmt, arg...) \
+ dev_warn(&(wx)->pdev->dev, fmt, ##arg)
#define wx_dbg(wx, fmt, arg...) \
dev_dbg(&(wx)->pdev->dev, fmt, ##arg)
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index bbbec9b43bc2..996c48da52d7 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -14,6 +14,7 @@
#include "../libwx/wx_type.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_lib.h"
+#include "../libwx/wx_err.h"
#include "../libwx/wx_ptp.h"
#include "../libwx/wx_mbx.h"
#include "../libwx/wx_sriov.h"
@@ -148,6 +149,8 @@ static void ngbe_service_task(struct work_struct *work)
struct wx *wx = container_of(work, struct wx, service_task);
wx_update_stats(wx);
+ wx_check_hang_subtask(wx);
+ wx_check_err_subtask(wx);
wx_service_event_complete(wx);
}
@@ -393,6 +396,7 @@ static void ngbe_disable_device(struct wx *wx)
netif_tx_stop_all_queues(netdev);
netif_tx_disable(netdev);
+ clear_bit(WX_FLAG_NEED_PF_RESET, wx->flags);
timer_delete_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
@@ -644,6 +648,7 @@ static const struct net_device_ops ngbe_netdev_ops = {
.ndo_stop = ngbe_close,
.ndo_change_mtu = wx_change_mtu,
.ndo_start_xmit = wx_xmit_frame,
+ .ndo_tx_timeout = wx_tx_timeout,
.ndo_set_rx_mode = wx_set_rx_mode,
.ndo_set_features = wx_set_features,
.ndo_fix_features = wx_fix_features,
@@ -733,6 +738,7 @@ static int ngbe_probe(struct pci_dev *pdev,
wx->driver_name = ngbe_driver_name;
ngbe_set_ethtool_ops(netdev);
netdev->netdev_ops = &ngbe_netdev_ops;
+ netdev->watchdog_timeo = 5 * HZ;
netdev->features = NETIF_F_SG | NETIF_F_IP_CSUM |
NETIF_F_TSO | NETIF_F_TSO6 |
@@ -829,6 +835,10 @@ static int ngbe_probe(struct pci_dev *pdev,
eth_hw_addr_set(netdev, wx->mac.perm_addr);
wx_mac_set_default_filter(wx, wx->mac.perm_addr);
+ err = wx_init_err_task(wx);
+ if (err)
+ goto err_free_mac_table;
+
ngbe_init_service(wx);
err = wx_init_interrupt_scheme(wx);
@@ -856,6 +866,8 @@ static int ngbe_probe(struct pci_dev *pdev,
err_cancel_service:
timer_delete_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
+ cancel_work_sync(&wx->reset_task);
+ destroy_workqueue(wx->reset_wq);
err_free_mac_table:
kfree(wx->rss_key);
kfree(wx->mac_table);
@@ -887,6 +899,8 @@ static void ngbe_remove(struct pci_dev *pdev)
timer_shutdown_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
+ cancel_work_sync(&wx->reset_task);
+ destroy_workqueue(wx->reset_wq);
phylink_destroy(wx->phylink);
pci_release_selected_regions(pdev,
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 20c5a295c6c2..b1615f82a265 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -14,6 +14,7 @@
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
+#include "../libwx/wx_err.h"
#include "../libwx/wx_ptp.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_mbx.h"
@@ -123,6 +124,8 @@ static void txgbe_service_task(struct work_struct *work)
txgbe_module_detection_subtask(wx);
txgbe_link_config_subtask(wx);
wx_update_stats(wx);
+ wx_check_hang_subtask(wx);
+ wx_check_err_subtask(wx);
wx_service_event_complete(wx);
}
@@ -224,6 +227,7 @@ static void txgbe_disable_device(struct wx *wx)
wx_irq_disable(wx);
wx_napi_disable_all(wx);
+ clear_bit(WX_FLAG_NEED_PF_RESET, wx->flags);
timer_delete_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
@@ -654,6 +658,7 @@ static const struct net_device_ops txgbe_netdev_ops = {
.ndo_stop = txgbe_close,
.ndo_change_mtu = wx_change_mtu,
.ndo_start_xmit = wx_xmit_frame,
+ .ndo_tx_timeout = wx_tx_timeout,
.ndo_set_rx_mode = wx_set_rx_mode,
.ndo_set_features = wx_set_features,
.ndo_fix_features = wx_fix_features,
@@ -745,6 +750,7 @@ static int txgbe_probe(struct pci_dev *pdev,
wx->driver_name = txgbe_driver_name;
txgbe_set_ethtool_ops(netdev);
netdev->netdev_ops = &txgbe_netdev_ops;
+ netdev->watchdog_timeo = 5 * HZ;
netdev->udp_tunnel_nic_info = &txgbe_udp_tunnels;
/* setup the private structure */
@@ -814,6 +820,10 @@ static int txgbe_probe(struct pci_dev *pdev,
eth_hw_addr_set(netdev, wx->mac.perm_addr);
wx_mac_set_default_filter(wx, wx->mac.perm_addr);
+ err = wx_init_err_task(wx);
+ if (err)
+ goto err_free_mac_table;
+
txgbe_init_service(wx);
err = wx_init_interrupt_scheme(wx);
@@ -916,6 +926,8 @@ static int txgbe_probe(struct pci_dev *pdev,
err_cancel_service:
timer_delete_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
+ cancel_work_sync(&wx->reset_task);
+ destroy_workqueue(wx->reset_wq);
err_free_mac_table:
kfree(wx->rss_key);
kfree(wx->mac_table);
@@ -948,6 +960,8 @@ static void txgbe_remove(struct pci_dev *pdev)
timer_shutdown_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
+ cancel_work_sync(&wx->reset_task);
+ destroy_workqueue(wx->reset_wq);
txgbe_remove_phy(txgbe);
wx_free_isb_resources(wx);
--
2.51.0
^ permalink raw reply related
* [PATCH net-next v11 4/5] net: wangxun: implement soft quiesce for PCIe error recovery
From: Jiawen Wu @ 2026-07-16 7:38 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Aleksandr Loktionov, Jacob Keller, Michal Swiatkowski, Kees Cook,
Larysa Zaremba, Breno Leitao, Joe Damato,
Uwe Kleine-König (The Capable Hub), Rongguang Wei,
Fabio Baltieri, Jiawen Wu
In-Reply-To: <20260716073822.24356-1-jiawenwu@trustnetic.com>
Function wx_soft_quiesce() provide a lightweight shutdown path during
PCIe error recovery. It avoids MMIO-dependent operations in PCIe error
status.
Waiting for the service task to complete may unnecessarily delay PCIe
error recovery, especially if the work item is already blocked by the
hardware failure that triggered AER. So the service task is not
explicitly cancelled in quiesce path. As a measure to block the service
task, the checking of WX_STATE_DOWN and WX_STATE_RESETTING is added at
the entry of relevant work item.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 18 +++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 1 +
drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 27 +++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 1 +
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 16 +++++++++++
5 files changed, 63 insertions(+)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index c8a4822d3819..a65312c8078f 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -3383,5 +3383,23 @@ void wx_service_timer(struct timer_list *t)
}
EXPORT_SYMBOL(wx_service_timer);
+void wx_soft_quiesce(struct wx *wx)
+{
+ if (!netif_running(wx->netdev) ||
+ test_and_set_bit(WX_STATE_DOWN, wx->state))
+ return;
+
+ pci_clear_master(wx->pdev);
+ netif_tx_stop_all_queues(wx->netdev);
+ netif_carrier_off(wx->netdev);
+ netif_tx_disable(wx->netdev);
+ wx_napi_disable_all(wx);
+ wx_ptp_quiesce(wx);
+
+ clear_bit(WX_FLAG_NEED_PF_RESET, wx->flags);
+ timer_delete_sync(&wx->service_timer);
+}
+EXPORT_SYMBOL(wx_soft_quiesce);
+
MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
index aed6ea8cf0d6..11bd79985e17 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
@@ -41,5 +41,6 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
void wx_service_event_schedule(struct wx *wx);
void wx_service_event_complete(struct wx *wx);
void wx_service_timer(struct timer_list *t);
+void wx_soft_quiesce(struct wx *wx);
#endif /* _WX_LIB_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
index 44f3e6505246..3eea647c4742 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
@@ -321,6 +321,9 @@ static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
int ts_done;
+ if (!test_bit(WX_STATE_PTP_RUNNING, wx->state))
+ return HZ;
+
ts_done = wx_ptp_tx_hwtstamp_work(wx);
wx_ptp_overflow_check(wx);
@@ -842,6 +845,30 @@ void wx_ptp_stop(struct wx *wx)
}
EXPORT_SYMBOL(wx_ptp_stop);
+void wx_ptp_quiesce(struct wx *wx)
+{
+ if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
+ return;
+
+ clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
+
+ if (wx->ptp_clock)
+ ptp_cancel_worker_sync(wx->ptp_clock);
+
+ if (wx->ptp_tx_skb) {
+ dev_kfree_skb_any(wx->ptp_tx_skb);
+ wx->ptp_tx_skb = NULL;
+ }
+ clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
+
+ if (wx->ptp_clock) {
+ ptp_clock_unregister(wx->ptp_clock);
+ wx->ptp_clock = NULL;
+ dev_info(&wx->pdev->dev, "removed PHC on %s\n", wx->netdev->name);
+ }
+}
+EXPORT_SYMBOL(wx_ptp_quiesce);
+
/**
* wx_ptp_rx_hwtstamp - utility function which checks for RX time stamp
* @wx: pointer to wx struct
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
index 50db90a6e3ee..ad2f824875d5 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
@@ -10,6 +10,7 @@ void wx_ptp_reset(struct wx *wx);
void wx_ptp_init(struct wx *wx);
void wx_ptp_suspend(struct wx *wx);
void wx_ptp_stop(struct wx *wx);
+void wx_ptp_quiesce(struct wx *wx);
void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);
int wx_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index a8773712cff8..7a05661b1d80 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -94,12 +94,24 @@ static void txgbe_module_detection_subtask(struct wx *wx)
{
int err;
+ if (test_bit(WX_STATE_DOWN, wx->state) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ return;
+
if (!test_and_clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags))
return;
/* wait for SFF module ready */
msleep(200);
+ /* Re-check state to avoid racing with down/reset paths.
+ * Module identification is deferred to the next up event,
+ * so it is safe to bail out here.
+ */
+ if (test_bit(WX_STATE_DOWN, wx->state) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ return;
+
err = txgbe_identify_module(wx);
if (err == -ENODEV)
set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
@@ -107,6 +119,10 @@ static void txgbe_module_detection_subtask(struct wx *wx)
static void txgbe_link_config_subtask(struct wx *wx)
{
+ if (test_bit(WX_STATE_DOWN, wx->state) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ return;
+
if (!test_and_clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags))
return;
--
2.51.0
^ permalink raw reply related
* [PATCH net-next v11 0/5] net: wangxun: timeout and error
From: Jiawen Wu @ 2026-07-16 7:38 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Aleksandr Loktionov, Jacob Keller, Michal Swiatkowski, Kees Cook,
Larysa Zaremba, Breno Leitao, Joe Damato,
Uwe Kleine-König (The Capable Hub), Rongguang Wei,
Fabio Baltieri, Jiawen Wu
It is about adding the Tx timeout process and pci_error_handlers.
When a PCIe error occurs, the txgbe device is able to recover on platform
that support AER interrupt. And for Tx timeout, the txgbe driver can
recover the device by reset process.
For ngbe devices, due to the absence of the current function, it cannot
br fully recovered once there is a PCIe error or Tx timeout. Its
function will be completed in the future.
---
Changes log:
v11:
- Destroy ptp_clock in wx_ptp_quiesce().
- Re-check down/resetting state flag after time sleep in module
detection subtask.
v10: https://lore.kernel.org/all/20260707061455.39564-1-jiawenwu@trustnetic.com
- Remove pci_aer_clear_nonfatal_status().
v9: https://lore.kernel.org/all/20260701072357.33984-1-jiawenwu@trustnetic.com
- Add calling ptp_cancel_worker_sync() in wx_ptp_quiesce().
- Fix the typo of 'out' for wx_control_hw().
v8: https://lore.kernel.org/all/20260630031016.19820-1-jiawenwu@trustnetic.com
- Not destroying PTP clock in wx_soft_quiesce(), and keeping the PTP worker
alive but idle during PCIe recovery.
- Move wx_soft_quiesce() after wx_napi_disable_all().
- Use PCI_COMMAND_MEMORY and U16_MAX instead of magic number.
- Fix the leak of wx_control_hw() when WX_STATE_RES_FREED is set.
v7: https://lore.kernel.org/all/20260615065016.21672-1-jiawenwu@trustnetic.com
- Move ptp_clock_unregister() to be executed before free wx->ptp_tx_skb.
v6: https://lore.kernel.org/all/20260610060917.23980-1-jiawenwu@trustnetic.com
- Move the check of device status inside wx_soft_quiesce().
- Reverse the error return of txgbe_disable_device().
- Add PCIe error check in tx_timeout.
- Add WX_STATE_RES_FREED flag to avoid double-free of resources.
v5: https://lore.kernel.org/all/20260604085631.12720-1-jiawenwu@trustnetic.com
- Avoid the same name on two functions.
- Encode the device identity into the name of reset work queue.
- Change pr_err() to wx_err().
- Check WX_STATE_DOWN and WX_STATE_RESETTING at the entry of every work item.
- Implement wx_ptp_quiesce().
- Add netif_carrier_off() and netif_tx_disable() in soft_quiesce.
- Move resource free operations after PCIe recovery.
- Return error code in down path.
v4: https://lore.kernel.org/all/20260601072221.2952-1-jiawenwu@trustnetic.com
- Create a separate work queue for the reset task.
- Gate wx_watchdog_flush_tx() on netif_running().
- Add rtnl_lock() around wx->do_reset() in wx_io_slot_reset().
- Change .close_suspend() to .soft_quiesce() to avoid MMIO when PCI
channel is frozen.
v3: https://lore.kernel.org/all/20260509100540.32612-1-jiawenwu@trustnetic.com
- Merge the multiple string line into one in wx_handle_tx_hang().
- Remove the redundant warn messages.
- Use test_and_clear_bit() instead of checking the flag bit then clear it.
- Drop the Tx hang check in tx_timeout.
- Call wx_update_stats() before wx_check_tx_hang().
- Add Tx flush when link lost.
- Move wx_ptp_stop() into wx->close_suspend().
- Drop V2 patch 5/6 because WOL packets are handled before DMA ring.
- Check wx NULL pointer in wx_io_error_detected().
- Check perm failure before hardware teardown.
v2: https://lore.kernel.org/all/20260430082517.19612-1-jiawenwu@trustnetic.com
- Add the missing rtnl_unlock() at early return in wx_reset_subtask().
- Replace ngbe_close() with ngbe_close_suspend() in ngbe_dev_shutdown().
- Add a patch to clear stored DMA addresses.
v1: https://lore.kernel.org/r/20260428021156.13564-1-jiawenwu@trustnetic.com
---
Jiawen Wu (5):
net: ngbe: implement libwx reset ops
net: wangxun: add Tx timeout process
net: wangxun: add reinit parameter to wx->do_reset callback
net: wangxun: implement soft quiesce for PCIe error recovery
net: wangxun: add pcie error handler
drivers/net/ethernet/wangxun/libwx/Makefile | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_err.c | 317 ++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_err.h | 18 +
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 17 +-
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 59 +++-
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 1 +
drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 27 ++
drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 1 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 25 +-
.../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 1 -
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 83 ++++-
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 1 +
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 65 +++-
.../net/ethernet/wangxun/txgbe/txgbe_type.h | 2 +-
15 files changed, 602 insertions(+), 19 deletions(-)
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.c
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.h
--
2.51.0
^ permalink raw reply
* [PATCH net-next v11 1/5] net: ngbe: implement libwx reset ops
From: Jiawen Wu @ 2026-07-16 7:38 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Aleksandr Loktionov, Jacob Keller, Michal Swiatkowski, Kees Cook,
Larysa Zaremba, Breno Leitao, Joe Damato,
Uwe Kleine-König (The Capable Hub), Rongguang Wei,
Fabio Baltieri, Jiawen Wu
In-Reply-To: <20260716073822.24356-1-jiawenwu@trustnetic.com>
Implement wx->do_reset() for library module calling.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
.../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 1 -
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 37 ++++++++++++++++++-
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 1 +
3 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
index b2e191982803..1960f7154151 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
@@ -59,7 +59,6 @@ static int ngbe_set_ringparam(struct net_device *netdev,
wx_set_ring(wx, new_tx_count, new_rx_count, temp_ring);
kvfree(temp_ring);
- wx_configure(wx);
ngbe_up(wx);
clear_reset:
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index a16221995909..bbbec9b43bc2 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -133,6 +133,7 @@ static int ngbe_sw_init(struct wx *wx)
wx->mbx.size = WX_VXMAILBOX_SIZE;
wx->setup_tc = ngbe_setup_tc;
+ wx->do_reset = ngbe_do_reset;
set_bit(0, &wx->fwd_bitmask);
return 0;
@@ -423,7 +424,7 @@ void ngbe_down(struct wx *wx)
wx_clean_all_rx_rings(wx);
}
-void ngbe_up(struct wx *wx)
+static void ngbe_up_complete(struct wx *wx)
{
wx_configure_vectors(wx);
@@ -490,7 +491,7 @@ static int ngbe_open(struct net_device *netdev)
wx_ptp_init(wx);
- ngbe_up(wx);
+ ngbe_up_complete(wx);
return 0;
err_dis_phy:
@@ -503,6 +504,12 @@ static int ngbe_open(struct net_device *netdev)
return err;
}
+void ngbe_up(struct wx *wx)
+{
+ wx_configure(wx);
+ ngbe_up_complete(wx);
+}
+
/**
* ngbe_close - Disables a network interface
* @netdev: network interface device structure
@@ -590,6 +597,8 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc)
*/
if (netif_running(dev))
ngbe_close(dev);
+ else
+ ngbe_reset(wx);
wx_clear_interrupt_scheme(wx);
@@ -606,6 +615,30 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc)
return 0;
}
+static void ngbe_reinit_locked(struct wx *wx)
+{
+ netif_trans_update(wx->netdev);
+
+ mutex_lock(&wx->reset_lock);
+ set_bit(WX_STATE_RESETTING, wx->state);
+
+ ngbe_down(wx);
+ ngbe_up(wx);
+
+ clear_bit(WX_STATE_RESETTING, wx->state);
+ mutex_unlock(&wx->reset_lock);
+}
+
+void ngbe_do_reset(struct net_device *netdev)
+{
+ struct wx *wx = netdev_priv(netdev);
+
+ if (netif_running(netdev))
+ ngbe_reinit_locked(wx);
+ else
+ ngbe_reset(wx);
+}
+
static const struct net_device_ops ngbe_netdev_ops = {
.ndo_open = ngbe_open,
.ndo_stop = ngbe_close,
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index 7077a0da4c98..4f648f272c08 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -125,5 +125,6 @@ extern char ngbe_driver_name[];
void ngbe_down(struct wx *wx);
void ngbe_up(struct wx *wx);
int ngbe_setup_tc(struct net_device *dev, u8 tc);
+void ngbe_do_reset(struct net_device *netdev);
#endif /* _NGBE_TYPE_H_ */
--
2.51.0
^ permalink raw reply related
* RE: [PATCH 1/2] sctp: socket: Fix uninitialized error on socket shutdown
From: Jagielski, Jedrzej @ 2026-07-16 7:42 UTC (permalink / raw)
To: luoqing, marcelo.leitner@gmail.com, lucien.xin@gmail.com,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com
Cc: horms@kernel.org, linux-sctp@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
luoqing@kylinos.cn
In-Reply-To: <20260716065259.872235-1-l1138897701@163.com>
From: luoqing <l1138897701@163.com>
Sent: Thursday, July 16, 2026 8:53 AM
>From: luoqing <luoqing@kylinos.cn>
>
>When sctp_skb_recv_datagram() detects sk->sk_shutdown & RCV_SHUTDOWN,
>it breaks out of the loop and returns NULL without setting *err.
>This leaves the error pointer uninitialized or with a stale value,
>which can confuse callers expecting a clean shutdown indication.
>
>Compare with the generic __skb_wait_for_more_packets() in
>net/core/datagram.c which properly handles shutdown by setting *err = 0.
>
>Fix this by setting *err = 0 before breaking when the socket is shut down,
>indicating an orderly shutdown rather than an error condition.
Hi luoqing
patch title should contain target tree
fix patches should contain 'fixes' tag and be targeted to net tree
please put Name + Surname for the signed-off-by tag
>
>Signed-off-by: luoqing <luoqing@kylinos.cn>
>---
> net/sctp/socket.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
>diff --git a/net/sctp/socket.c b/net/sctp/socket.c
>index c7b9e325ec1c..ea7050b27715 100644
>--- a/net/sctp/socket.c
>+++ b/net/sctp/socket.c
>@@ -9117,8 +9117,10 @@ struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int *err)
> if (error)
> goto no_packet;
>
>- if (sk->sk_shutdown & RCV_SHUTDOWN)
>+ if (sk->sk_shutdown & RCV_SHUTDOWN) {
>+ *err = 0;
from what i see any of the sctp_skb_recv_datagram() callers doesn't use
@err - so at this point that fix doesn't affect any code
i'm wondering if we could just drop @err
> break;
>+ }
>
>
> /* User doesn't want to wait. */
>--
>2.25.1
^ 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