* [PATCH v2 0/3] can: enable NAPI before starting the controller in three drivers
@ 2026-10-30 23:33 Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 1/3] can: ctucanfd: enable NAPI before starting the controller Patrick Zhang
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Patrick Zhang @ 2026-10-30 23:33 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, linux-can
Cc: Pavel Pisa, Ondrej Ille, Harini T, Michal Simek,
linux-arm-kernel, linux-kernel
Hi,
Three CAN drivers open the device in the order "start the controller
and unmask its RX interrupt, then napi_enable()". Each of them has an
ISR that masks the RX interrupt before calling napi_schedule(), and a
poll that is the only place the RX interrupt is unmasked again. A frame
that arrives between the controller start and napi_enable() is handled
by the ISR, which masks RX and calls napi_schedule() on a NAPI instance
that is still disabled. napi_schedule_prep() sees NAPI_STATE_SCHED set
and drops the schedule. napi_enable() clears SCHED without scheduling.
Nothing ever polls, so RX stays masked and the interface receives no
data frames until the next close. In ctucanfd and xilinx_can, transmit
keeps working. ctucanfd counts no errors, so the failure is silent
there. xilinx_can counts RX FIFO overflows once the FIFO fills. In
ifi_canfd the TX wake-up interrupt is masked too, so transmit also
stops.
The affected drivers are ctucanfd, xilinx_can and ifi_canfd. The fix is
the same in each: call napi_enable() before the controller start, so
that an early RX interrupt can schedule the poll, and call
napi_disable() on the error path if the start can fail. ifi_canfd has
no reset in open() outside its start function, so that patch also
splits the reset out of ifi_canfd_start() and enables NAPI between the
reset and the start.
Patch 1 (ctucanfd) was tested on QEMU with the ctucan_pci device.
Patch 2 (xilinx_can) was tested on a Versal board. Patch 3 (ifi_canfd)
is from code inspection only. I do not have the hardware, so it is
compile tested only. The sequence is the same as in the other two
drivers, and I would appreciate a test from someone who has an IFI CAN
FD core.
The bug has been present since each driver was added, so each patch
has a Fixes: tag pointing at the commit that added the driver.
Patches 1 and 2 are marked for stable backport. Patch 3 is not,
because it is untested. The patches are independent and can be
applied separately.
I looked at the other NAPI users under drivers/net/can. at91_can had
the same persistent-mask pattern in 6.6 but no longer masks RX in its
ISR since the conversion to rx-offload, so I left it alone; ti_hecc and
virtio_can start the device before napi_enable() but do not leave RX
masked, so a dropped first schedule only delays the first frame. All
the others enable NAPI before the device can interrupt.
Changes in v2:
- Patch 1: add Acked-by and Tested-by from Pavel
- Patch 2: correct the commit message, which said no error counter
moves during the stall; the RX overflow interrupt stays enabled and
counts rx_over_errors
- Patch 3: split the reset out of ifi_canfd_start() and enable NAPI
after it, with synchronize_irq() in between, so the poll cannot race
with the reset (reported by the netdev AI review). This replaces the
ifi_canfd_stop() call proposed in my reply to that review; see the
patch notes
- Patches 2 and 3: name the related fixes by subject
- Rebased on current linux-can main
- v1: https://lore.kernel.org/r/20261021205924.2538511-1-patrzhan@amazon.com
Patrick Zhang (3):
can: ctucanfd: enable NAPI before starting the controller
can: xilinx_can: enable NAPI before starting the controller
can: ifi_canfd: enable NAPI before starting the controller
drivers/net/can/ctucanfd/ctucanfd_base.c | 6 +++++-
drivers/net/can/ifi_canfd/ifi_canfd.c | 17 ++++++++++++++---
drivers/net/can/xilinx_can.c | 6 +++++-
3 files changed, 24 insertions(+), 5 deletions(-)
base-commit: abeacca61eea4f6803df4a595b656ea45c2d3249
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 1/3] can: ctucanfd: enable NAPI before starting the controller
2026-10-30 23:33 [PATCH v2 0/3] can: enable NAPI before starting the controller in three drivers Patrick Zhang
@ 2026-10-30 23:33 ` Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 2/3] can: xilinx_can: " Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 3/3] can: ifi_canfd: " Patrick Zhang
2 siblings, 0 replies; 4+ messages in thread
From: Patrick Zhang @ 2026-10-30 23:33 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, linux-can
Cc: Pavel Pisa, Ondrej Ille, Harini T, Michal Simek,
linux-arm-kernel, linux-kernel, stable, Pavel Pisa
ctucan_open() starts the controller and unmasks RBNEI in
ctucan_chip_start(), prints "ctu_can_fd device registered", and only
then calls napi_enable(). A frame that arrives in that window raises
RBNEI. ctucan_interrupt() masks RBNEI and calls napi_schedule(). NAPI is
still disabled, so napi_schedule_prep() finds NAPI_STATE_SCHED set and
the schedule is dropped. napi_enable() then clears SCHED without
scheduling anything. Only ctucan_rx_poll() unmasks RBNEI again, and it
never runs. The interface receives nothing until the next close.
Transmit still works. The RX FIFO fills up and the core sets DOR, but
the overrun is only accounted in ctucan_rx_poll(), so rx_errors and
rx_over_errors stay at zero and nothing is logged. Every re-open (ip
link down/up, a driver rebind) runs the same race again.
The netdev_info() in the window makes it much wider on a slow console.
This can be reproduced by sending continuous traffic with cangen from
another node and repeating "ip link set down/up" on the test device.
Enable NAPI before the controller starts, so that an early RX interrupt
can schedule the poll. NAPI can be enabled while the controller is in
reset, because nothing schedules it until an interrupt arrives. If
ctucan_chip_start() fails, disable NAPI again. Otherwise the next open
would call napi_enable() on an already enabled instance and trip the
BUG_ON() in napi_enable_locked().
Tested on QEMU with the ctucan_pci device.
Fixes: 2dcb8e8782d8 ("can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part.")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Patrick Zhang <patrzhan@amazon.com>
Acked-by: Pavel Pisa <pisa@fel.cvut.cz>
Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
---
Notes:
v2:
- Add Acked-by and Tested-by from Pavel
- v1: https://lore.kernel.org/r/20261021205924.2538511-2-patrzhan@amazon.com
drivers/net/can/ctucanfd/ctucanfd_base.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c
index 07d4aa43c..82bf6c645 100644
--- a/drivers/net/can/ctucanfd/ctucanfd_base.c
+++ b/drivers/net/can/ctucanfd/ctucanfd_base.c
@@ -1231,6 +1231,10 @@ static int ctucan_open(struct net_device *ndev)
goto err_irq;
}
+ /* Keep before ctucan_chip_start(): the ISR masks RBNEI and only the
+ * poll unmasks it.
+ */
+ napi_enable(&priv->napi);
ret = ctucan_chip_start(ndev);
if (ret < 0) {
netdev_err(ndev, "ctucan_chip_start failed!\n");
@@ -1238,12 +1242,12 @@ static int ctucan_open(struct net_device *ndev)
}
netdev_info(ndev, "ctu_can_fd device registered\n");
- napi_enable(&priv->napi);
netif_start_queue(ndev);
return 0;
err_chip_start:
+ napi_disable(&priv->napi);
free_irq(ndev->irq, ndev);
err_irq:
close_candev(ndev);
base-commit: abeacca61eea4f6803df4a595b656ea45c2d3249
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 2/3] can: xilinx_can: enable NAPI before starting the controller
2026-10-30 23:33 [PATCH v2 0/3] can: enable NAPI before starting the controller in three drivers Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 1/3] can: ctucanfd: enable NAPI before starting the controller Patrick Zhang
@ 2026-10-30 23:33 ` Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 3/3] can: ifi_canfd: " Patrick Zhang
2 siblings, 0 replies; 4+ messages in thread
From: Patrick Zhang @ 2026-10-30 23:33 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, linux-can
Cc: Pavel Pisa, Ondrej Ille, Harini T, Michal Simek,
linux-arm-kernel, linux-kernel, stable
xcan_open() calls xcan_chip_start(), which writes XCAN_IER_OFFSET to
unmask the RX interrupts and then sets XCAN_SRR_CEN_MASK to enable the
controller, and only then calls napi_enable(). A frame that arrives in
that window raises the RX interrupt. xcan_interrupt() clears the RX
bits in XCAN_IER_OFFSET and calls napi_schedule(). NAPI is still
disabled, so napi_schedule_prep() finds NAPI_STATE_SCHED set and the
schedule is dropped. Only xcan_rx_poll() unmasks RX again, and it never
runs. The interface receives no data frames until the next close.
Transmit still works. The RX overflow interrupt stays enabled, so once
the RX FIFO fills, xcan_err_interrupt() counts rx_over_errors and
reports overflow error frames, but nothing reads the FIFO. Every
re-open (ip link down/up, a driver rebind) runs the same race again.
The window is only the tail of xcan_chip_start(), so the failure is
rare. Adding an mdelay(50) at the end of xcan_chip_start() makes it
reproducible with continuous traffic from another node and a single
"ip link set down/up" on the test device.
Enable NAPI before the controller starts, so that an early RX interrupt
can schedule the poll. NAPI can be enabled while the controller is in
reset, because nothing schedules it until an interrupt arrives. If
xcan_chip_start() fails, disable NAPI again. Otherwise the next open
would call napi_enable() on an already enabled instance and trip the
BUG_ON() in napi_enable_locked().
This is the same race as in "can: ctucanfd: enable NAPI before starting
the controller".
Tested on a Versal board.
Fixes: b1201e44f50b ("can: xilinx CAN controller support")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Patrick Zhang <patrzhan@amazon.com>
---
Notes:
v2:
- Correct the commit message: the RX overflow interrupt stays enabled
during the stall, so rx_over_errors is counted once the FIFO fills
- Name the ctucanfd fix by subject instead of "the previous patch"
- v1: https://lore.kernel.org/r/20261021205924.2538511-3-patrzhan@amazon.com
drivers/net/can/xilinx_can.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
index 43d7f2282..5c2412bf9 100644
--- a/drivers/net/can/xilinx_can.c
+++ b/drivers/net/can/xilinx_can.c
@@ -1570,18 +1570,22 @@ static int xcan_open(struct net_device *ndev)
if (ret)
goto err_irq;
+ /* Keep before xcan_chip_start(): the ISR masks RX interrupts and only
+ * the poll unmasks them.
+ */
+ napi_enable(&priv->napi);
ret = xcan_chip_start(ndev);
if (ret < 0) {
netdev_err(ndev, "xcan_chip_start failed!\n");
goto err_candev;
}
- napi_enable(&priv->napi);
netif_start_queue(ndev);
return 0;
err_candev:
+ napi_disable(&priv->napi);
close_candev(ndev);
err_irq:
free_irq(ndev->irq, ndev);
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 3/3] can: ifi_canfd: enable NAPI before starting the controller
2026-10-30 23:33 [PATCH v2 0/3] can: enable NAPI before starting the controller in three drivers Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 1/3] can: ctucanfd: enable NAPI before starting the controller Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 2/3] can: xilinx_can: " Patrick Zhang
@ 2026-10-30 23:33 ` Patrick Zhang
2 siblings, 0 replies; 4+ messages in thread
From: Patrick Zhang @ 2026-10-30 23:33 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, linux-can
Cc: Pavel Pisa, Ondrej Ille, Harini T, Michal Simek,
linux-arm-kernel, linux-kernel
ifi_canfd_open() calls ifi_canfd_start(), which unmasks the interrupts
in IFI_CANFD_IRQMASK and then enables the controller through
IFI_CANFD_STCMD, and only then calls napi_enable(). A frame that
arrives in that window raises RXFIFO_NEMPTY. ifi_canfd_isr() masks all
interrupts with ifi_canfd_irq_enable(ndev, 0) and calls
napi_schedule(). NAPI is still disabled, so napi_schedule_prep() finds
NAPI_STATE_SCHED set and the schedule is dropped. Only ifi_canfd_poll()
unmasks the interrupts again, and it never runs. From then on the
interface receives nothing. ifi_canfd_start_xmit() stops the queue
after every frame and relies on the TX FIFO interrupt to wake it, and
that interrupt is masked too, so transmit stops after the next frame.
The stall lasts until the next close. Every re-open runs the same race
again.
Enable NAPI before the controller starts, so that an early RX interrupt
can schedule the poll. ifi_canfd_start() cannot fail, so no error path
changes.
NAPI must not be enabled before the IP has been reset, though. The
handler is registered with IRQF_SHARED and acts on the raw
IFI_CANFD_INTERRUPT status, and nothing resets the IP before the first
open, so stale status could schedule a poll that runs while
ifi_canfd_start() resets and programs the IP. Split the reset,
configuration and status clear out of ifi_canfd_start() into
ifi_canfd_reset_config(), and enable NAPI between the two. Before
enabling NAPI, wait with synchronize_irq() for any handler that read
the stale status before the reset, so that its schedule is still
dropped, as before. The controller stays disabled until
ifi_canfd_start() unmasks the interrupts and enables it. The bus-off
restart path calls both halves in sequence, so its register writes do
not change.
This is the same race as in "can: ctucanfd: enable NAPI before starting
the controller" and "can: xilinx_can: enable NAPI before starting the
controller". I do not have IFI CAN FD hardware, so this change is
compile tested only and the failure sequence is taken from the code.
Fixes: 0c4d9c94a130 ("can: ifi: Add IFI CANFD IP support")
Assisted-by: LLM
Signed-off-by: Patrick Zhang <patrzhan@amazon.com>
---
Notes:
v2:
- Split the reset, configuration and status clear out of
ifi_canfd_start() into ifi_canfd_reset_config(), and enable NAPI
between the two, so the poll cannot race with the reset when the
shared handler sees stale status (reported by the netdev AI review)
- This replaces the ifi_canfd_stop() call I proposed in reply to the
review: ifi_canfd_stop() writes ~0 to IFI_CANFD_IRQMASK, and I cannot
confirm from the driver alone that this masks the interrupts, while
the split does not depend on it
- Call synchronize_irq() before napi_enable(), so a handler that read
stale status before the reset cannot schedule the poll afterwards
- Explain the ordering in the commit message, and name the ctucanfd
and xilinx_can fixes by subject instead of "the previous patches"
- A poll can still overlap the interrupt unmask and error-counter writes
at the end of ifi_canfd_start(), the same as on the existing bus-off
restart path. I have not reordered those writes, because I cannot
test the effect on hardware.
- v1: https://lore.kernel.org/r/20261021205924.2538511-4-patrzhan@amazon.com
drivers/net/can/ifi_canfd/ifi_canfd.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c
index 0f83335e4..0ff766c7e 100644
--- a/drivers/net/can/ifi_canfd/ifi_canfd.c
+++ b/drivers/net/can/ifi_canfd/ifi_canfd.c
@@ -734,10 +734,9 @@ static void ifi_canfd_set_filters(struct net_device *ndev)
IFI_CANFD_FILTER_IDENT_IDE);
}
-static void ifi_canfd_start(struct net_device *ndev)
+static void ifi_canfd_reset_config(struct net_device *ndev)
{
struct ifi_canfd_priv *priv = netdev_priv(ndev);
- u32 stcmd;
/* Reset the IP */
writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD);
@@ -760,6 +759,12 @@ static void ifi_canfd_start(struct net_device *ndev)
/* Clear all pending interrupts */
writel((u32)(~IFI_CANFD_INTERRUPT_SET_IRQ),
priv->base + IFI_CANFD_INTERRUPT);
+}
+
+static void ifi_canfd_start(struct net_device *ndev)
+{
+ struct ifi_canfd_priv *priv = netdev_priv(ndev);
+ u32 stcmd;
stcmd = IFI_CANFD_STCMD_ENABLE | IFI_CANFD_STCMD_NORMAL_MODE |
IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING;
@@ -817,6 +822,7 @@ static int ifi_canfd_set_mode(struct net_device *ndev, enum can_mode mode)
{
switch (mode) {
case CAN_MODE_START:
+ ifi_canfd_reset_config(ndev);
ifi_canfd_start(ndev);
netif_wake_queue(ndev);
break;
@@ -846,9 +852,14 @@ static int ifi_canfd_open(struct net_device *ndev)
goto err_irq;
}
+ ifi_canfd_reset_config(ndev);
+ synchronize_irq(ndev->irq);
+ /* Keep between the reset and ifi_canfd_start(): the ISR masks the
+ * interrupts and only the poll unmasks them.
+ */
+ napi_enable(&priv->napi);
ifi_canfd_start(ndev);
- napi_enable(&priv->napi);
netif_start_queue(ndev);
return 0;
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-30 23:36 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-30 23:33 [PATCH v2 0/3] can: enable NAPI before starting the controller in three drivers Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 1/3] can: ctucanfd: enable NAPI before starting the controller Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 2/3] can: xilinx_can: " Patrick Zhang
2026-10-30 23:33 ` [PATCH v2 3/3] can: ifi_canfd: " Patrick Zhang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®