[PATCH] can: flexcan: fix synchronous external abort in flexcan_get_berr_counter()
From: chunyuzhiqiang <hidden>
Date: 2026-09-15 06:38:06
Also in:
linux-can
Subsystem:
can network drivers, the rest · Maintainers:
Marc Kleine-Budde, Vincent Mailhol, Linus Torvalds
From: ChunYuZhiQiang <redacted>
When the FlexCAN interface is down, can_fill_info() still calls
flexcan_get_berr_counter() to fill the error counters for a netlink
dump. This function calls pm_runtime_resume_and_get(), which only
enables the clocks via flexcan_runtime_resume(), but does not clear
the MCR[MDIS] bit. Since the interface has never been opened, the
FlexCAN module is still disabled (MDIS=1) from register_flexcandev().
Accessing the ECR register then triggers a synchronous external abort.
This can be reproduced on an i.MX8QXP board by simply running
`ip link show` without ever bringing the CAN interface up:
Internal error: synchronous external abort: 0000000096000210 [#1] PREEMPT SMP
pc : flexcan_read_le+0x0/0x18
lr : flexcan_get_berr_counter+0x4c/0x8c
Call trace:
flexcan_read_le+0x0/0x18
can_fill_info+0x1f8/0x434
rtnl_fill_ifinfo+0x8fc/0xbb4
rtnl_dump_ifinfo+0x364/0x448
...
ftrace shows the exact path:
can_fill_info() {
flexcan_get_berr_counter() {
__pm_runtime_resume() {
rpm_resume()
rpm_callback()
__rpm_callback()
pm_generic_runtime_resume()
flexcan_runtime_resume()
flexcan_clks_enable() {
clk_prepare(); clk_enable();
...
}
}
do_mem_abort() {
do_sea()
...
}
}
}
Fix this by returning early if the interface is not running.
Tested on i.MX8QXP: after the patch, `ip link show` no longer triggers
the abort.
Cc: stable@vger.kernel.org
Signed-off-by: ChunYuZhiQiang <redacted>
---
drivers/net/can/flexcan/flexcan-core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
index 06d5d35fc..b08779ee2 100644
--- a/drivers/net/can/flexcan/flexcan-core.c
+++ b/drivers/net/can/flexcan/flexcan-core.c@@ -765,6 +765,9 @@ static int flexcan_get_berr_counter(const struct net_device *dev, const struct flexcan_priv *priv = netdev_priv(dev); int err; + if (!netif_running(dev)) + return 0; + err = pm_runtime_resume_and_get(priv->dev); if (err < 0) return err;
--
2.47.3