From 75332c410c8190d61499001c5516af977ca426c3 Mon Sep 17 00:00:00 2001 From: ChunYuZhiQiang Date: Tue, 27 Oct 2026 14:50:11 +0800 Subject: [PATCH 1/2] can: dev: can_fill_info(): don't wake up controller if interface is down If the interface is down, the CAN controller might be powered down and its clocks gated. Don't call do_get_berr_counter() to read the bus error counters if the interface is down, and adjust can_get_size() to match. This removes one trigger for a synchronous external abort seen on i.MX8QXP when "ip link show" is run on a flexcan interface that was never brought up. The underlying cause is platform-specific: on i.MX8QXP the CAN clocks are gated through the SCU LPCG and the subsystem shares the IMX_SC_R_CAN_0 power domain, and there is a window after pm_runtime_resume_and_get() returns where the clock has not reached the module yet. This patch avoids the netlink query path while the interface is down, but does not root-cause the LPCG window itself. As a result, IFLA_CAN_BERR_COUNTER is no longer reported for down interfaces. Userspace must treat the attribute as optional. The issue was triggered on an i.MX8QXP board by running "ip link show" without ever bringing the interface up: Internal error: synchronous external abort: 0000000096000210 pc : flexcan_read_le+0x0/0x18 lr : flexcan_get_berr_counter+0x4c/0x8c Call trace: flexcan_read_le+0x0/0x18 flexcan_get_berr_counter+0x4c/0x8c can_fill_info+0x1f8/0x434 rtnl_fill_ifinfo+0x8fc/0xbb4 rtnl_dump_ifinfo+0x364/0x448 This is the same class of problem fixed for m_can in commit 91a55c72a821 ("can: m_can: m_can_get_berr_counter(): don't wake up controller if interface is down"). Fixes: ec56acfef2af1 ("can: flexcan: flexcan_get_berr_counter(): switch on clocks before accessing ecr register") Cc: stable@vger.kernel.org Signed-off-by: ChunYuZhiQiang Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/20261027065012.191348-1-chunyuzhiqiang@yinhe.ht Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev/netlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c index 766d455950f5..b958c04d53db 100644 --- a/drivers/net/can/dev/netlink.c +++ b/drivers/net/can/dev/netlink.c @@ -812,7 +812,7 @@ static size_t can_get_size(const struct net_device *dev) size += nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ - if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ + if (priv->do_get_berr_counter && netif_running(dev)) /* IFLA_CAN_BERR_COUNTER */ size += nla_total_size(sizeof(struct can_berr_counter)); if (priv->termination_const) { size += nla_total_size(sizeof(priv->termination)); /* IFLA_CAN_TERMINATION */ @@ -1006,7 +1006,7 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) || nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) || - (priv->do_get_berr_counter && + (priv->do_get_berr_counter && netif_running(dev) && !priv->do_get_berr_counter(dev, &bec) && nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) || From f5128a76726105f398d7ee70ee523dd4f68d62f4 Mon Sep 17 00:00:00 2001 From: ChunYuZhiQiang Date: Tue, 27 Oct 2026 14:50:12 +0800 Subject: [PATCH 2/2] can: dev: can_tdc_fill_info(): don't read automatic TDCV if interface is down The automatic TDCV readback calls into the driver via do_get_auto_tdcv(). Some drivers, such as xcan_get_auto_tdcv(), read controller registers without taking a runtime PM reference, so calling this while the interface is down can touch registers whose clocks are gated. Gate do_get_auto_tdcv() on netif_running() in can_tdc_fill_info() and adjust can_tdc_get_size() and can_data_bittiming_get_size() so that only the automatic TDCV size is skipped when the interface is down. The other TDC parameters are still reported, as they are meaningful while the interface is down. This is a defensive hardening based on code inspection; the automatic TDCV path has not been triggered on hardware. Signed-off-by: ChunYuZhiQiang Reviewed-by: Vincent Mailhol Fixes: e8060f08cd69 ("can: netlink: add can_priv::do_get_auto_tdcv() to retrieve tdcv from device") Link: https://patch.msgid.link/20261027065012.191348-2-chunyuzhiqiang@yinhe.ht [mkl: fix open parenthesis alignment] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev/netlink.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c index b958c04d53db..081dfddda000 100644 --- a/drivers/net/can/dev/netlink.c +++ b/drivers/net/can/dev/netlink.c @@ -720,7 +720,8 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[], return 0; } -static size_t can_tdc_get_size(struct data_bittiming_params *dbt_params, +static size_t can_tdc_get_size(const struct net_device *dev, + struct data_bittiming_params *dbt_params, u32 tdc_flags) { bool tdc_manual = tdc_flags & CAN_CTRLMODE_TDC_MANUAL_MASK; @@ -742,7 +743,8 @@ static size_t can_tdc_get_size(struct data_bittiming_params *dbt_params, } if (tdc_flags) { - if (tdc_manual || dbt_params->do_get_auto_tdcv) + if (tdc_manual || + (dbt_params->do_get_auto_tdcv && netif_running(dev))) size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV */ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO */ if (dbt_params->tdc_const->tdcf_max) @@ -752,7 +754,8 @@ static size_t can_tdc_get_size(struct data_bittiming_params *dbt_params, return size; } -static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_params, +static size_t can_data_bittiming_get_size(const struct net_device *dev, + struct data_bittiming_params *dbt_params, u32 tdc_flags) { size_t size = 0; @@ -764,7 +767,7 @@ static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_para if (dbt_params->data_bitrate_const) /* IFLA_CAN_{,XL}_DATA_BITRATE_CONST */ size += nla_total_size(sizeof(*dbt_params->data_bitrate_const) * dbt_params->data_bitrate_const_cnt); - size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */ + size += can_tdc_get_size(dev, dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */ return size; } @@ -825,10 +828,10 @@ static size_t can_get_size(const struct net_device *dev) size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */ size += can_ctrlmode_ext_get_size(); /* IFLA_CAN_CTRLMODE_EXT */ - size += can_data_bittiming_get_size(&priv->fd, + size += can_data_bittiming_get_size(dev, &priv->fd, priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK); - size += can_data_bittiming_get_size(&priv->xl, + size += can_data_bittiming_get_size(dev, &priv->xl, priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK); size += can_pwm_get_size(priv->xl.pwm_const, /* IFLA_CAN_XL_PWM */ priv->ctrlmode & CAN_CTRLMODE_XL_TMS); @@ -910,7 +913,7 @@ static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev, if (tdc_manual) { tdcv = tdc->tdcv; err = 0; - } else if (dbt_params->do_get_auto_tdcv) { + } else if (dbt_params->do_get_auto_tdcv && netif_running(dev)) { err = dbt_params->do_get_auto_tdcv(dev, &tdcv); } if (!err && nla_put_u32(skb, IFLA_CAN_TDC_TDCV, tdcv))