[PATCH] staging: octeon: unwind receive initialization on IRQ failure

From: Tian Xuechen

Date: Thu Nov 05 2026 - 03:37:05 EST


cvm_oct_rx_initialize() returns immediately when request_irq() fails.
The failing receive group has already had its NAPI instance added and
enabled, and earlier selected groups can still have registered IRQ
handlers and active NAPI instances.

When RX initialization fails, cvm_oct_probe() unwinds and returns an
error. The driver core then releases the managed platform data, even
though earlier RX IRQ actions still reference NAPI instances embedded
in that data.

Disable and delete NAPI for the failing group, then unwind the previously
initialized groups in reverse order. Mask their POW interrupts, disable
and delete their NAPI instances, and free their IRQs before returning the
original error.

Fixes: ae4350364008 ("staging: octeon: Propagate rx initialization failures")
Signed-off-by: Tian Xuechen <202300460064@xxxxxxxxxxxxxxx>
---
An AI coding assistant helped analyze the RX error path, draft the
unwind, and edit the changelog.

Cross-built drivers/staging/octeon/ with cavium_octeon_defconfig,
GCC 10.3.0, and W=1.
Not runtime-tested on Octeon hardware.
drivers/staging/octeon/ethernet-rx.c | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c
index 5be43bbf6a1d..a1b5ccdbb6b2 100644
--- a/drivers/staging/octeon/ethernet-rx.c
+++ b/drivers/staging/octeon/ethernet-rx.c
@@ -450,6 +450,7 @@ void cvm_oct_poll_controller(struct net_device *dev)
int cvm_oct_rx_initialize(struct platform_device *pdev)
{
int i;
+ int ret;
struct net_device *dev_for_napi = NULL;
struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev);
struct oct_rx_group *rx_group = plat->rx_group;
@@ -467,8 +468,6 @@ int cvm_oct_rx_initialize(struct platform_device *pdev)
}

for (i = 0; i < ARRAY_SIZE(plat->rx_group); i++) {
- int ret;
-
if (!(pow_receive_groups & BIT(i)))
continue;

@@ -485,7 +484,9 @@ int cvm_oct_rx_initialize(struct platform_device *pdev)
"Ethernet", &rx_group[i].napi);
if (ret) {
dev_err(&pdev->dev, "Could not acquire Ethernet IRQ %d\n", rx_group[i].irq);
- return ret;
+ napi_disable(&rx_group[i].napi);
+ netif_napi_del(&rx_group[i].napi);
+ goto err_irq;
}

disable_irq_nosync(rx_group[i].irq);
@@ -524,6 +525,25 @@ int cvm_oct_rx_initialize(struct platform_device *pdev)
}
atomic_inc(&oct_rx_ready);
return 0;
+
+err_irq:
+ while (--i >= 0) {
+ if (!(pow_receive_groups & BIT(i)))
+ continue;
+
+ /* Disable POW interrupt */
+ if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+ cvmx_write_csr(CVMX_SSO_WQ_INT_THRX(i), 0);
+ else
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(i), 0);
+
+ napi_disable(&rx_group[i].napi);
+ /* Wait for the poll callback before freeing its IRQ. */
+ netif_napi_del(&rx_group[i].napi);
+ free_irq(rx_group[i].irq, &rx_group[i].napi);
+ }
+
+ return ret;
}

void cvm_oct_rx_shutdown(struct platform_device *pdev)

base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
--
2.34.1