* [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure [not found] <20260730-master-v2-0-572974052400@qq.com> @ 2026-07-30 10:11 ` Cunhao Lu 2026-07-30 10:26 ` sashiko-bot 2026-07-30 11:38 ` Marc Kleine-Budde 2026-07-30 10:11 ` [PATCH v2 2/3] can: rockchip_canfd: retry the outstanding TX buffer Cunhao Lu 2026-07-30 10:11 ` [PATCH v2 3/3] can: rockchip_canfd: serialize TX state and command writes Cunhao Lu 2 siblings, 2 replies; 9+ messages in thread From: Cunhao Lu @ 2026-07-30 10:11 UTC (permalink / raw) To: Marc Kleine-Budde, kernel, Vincent Mailhol, Heiko Stuebner Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel, stable, Cunhao Lu rkcanfd_start_xmit() advances tx_head and requests transmission even when can_put_echo_skb() fails. This creates a pending TX entry without the echo skb that the RXSTX completion path needs to match the self-received frame. The entry cannot be completed, and the netdev TX queue can remain stopped after the two-entry software FIFO fills. Do not advance tx_head or request transmission if the echo skb cannot be installed. Account the frame as dropped and report the error. The -ENOMEM and -EBUSY paths have already consumed the skb; explicitly free it on -EINVAL, where ownership remains with the caller. Fixes: b6661d73290c ("can: rockchip_canfd: add TX PATH") Cc: stable@vger.kernel.org Signed-off-by: Cunhao Lu <1579567540@qq.com> --- drivers/net/can/rockchip/rockchip_canfd-tx.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c index 12200dcfd338..d2cbe25c46d6 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c @@ -125,8 +125,18 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) frame_len = can_skb_get_frame_len(skb); err = can_put_echo_skb(skb, ndev, tx_head, frame_len); - if (!err) - netdev_sent_queue(priv->ndev, frame_len); + if (err) { + if (err == -EINVAL) + dev_kfree_skb_any(skb); + + ndev->stats.tx_dropped++; + if (net_ratelimit()) + netdev_err(ndev, "%s: failed to put echo skb: %pe\n", + __func__, ERR_PTR(err)); + + return NETDEV_TX_OK; + } + netdev_sent_queue(priv->ndev, frame_len); WRITE_ONCE(priv->tx_head, priv->tx_head + 1); -- 2.34.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure 2026-07-30 10:11 ` [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure Cunhao Lu @ 2026-07-30 10:26 ` sashiko-bot 2026-07-30 11:38 ` Marc Kleine-Budde 1 sibling, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-07-30 10:26 UTC (permalink / raw) To: Cunhao Lu Cc: Marc Kleine-Budde, Vincent Mailhol, Oleksij Rempel, linux-can, Oliver Hartkopp Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] The `rkcanfd_xmit_retry()` function retransmits the wrong hardware buffer due to using `tx_head` instead of `tx_tail`. -- --- Patch [1]: [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure --- commit f51df37d6dddee0c506b68b904fa428c63fce520 Author: Cunhao Lu <1579567540@qq.com> can: rockchip_canfd: prevent TX stall on echo skb failure This commit prevents a TX stall when can_put_echo_skb() fails by not advancing tx_head or requesting transmission. It ensures the skb is properly freed on -EINVAL and accounts the frame as dropped. > diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c > index 12200dcfd3389..d2cbe25c46d6c 100644 > --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c > +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c [ ... ] [Severity: High] This isn't a bug introduced by this patch, but while reviewing this file I noticed a pre-existing issue in rkcanfd_xmit_retry(). When a hardware erratum occurs and rkcanfd_rxstx_filter() detects a mangled self-received frame, it calls rkcanfd_xmit_retry() to retransmit it. The failed frame is located at the software queue's tail. Does this code request the correct buffer for retransmission? drivers/net/can/rockchip/rockchip_canfd-tx.c:rkcanfd_xmit_retry() { ... const unsigned int tx_head = rkcanfd_get_tx_head(priv); const u32 reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_head); rkcanfd_start_xmit_write_cmd(priv, reg_cmd); ... } Because rkcanfd_start_xmit() already incremented tx_head, it points to the next available, uninitialized slot. Will this instruct the hardware to transmit an empty or uninitialized buffer onto the CAN bus instead of recovering the valid frame at tx_tail? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260730-master-v2-0-572974052400@qq.com?part=1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure 2026-07-30 10:11 ` [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure Cunhao Lu 2026-07-30 10:26 ` sashiko-bot @ 2026-07-30 11:38 ` Marc Kleine-Budde 2026-07-30 12:39 ` Cunhao Lu 1 sibling, 1 reply; 9+ messages in thread From: Marc Kleine-Budde @ 2026-07-30 11:38 UTC (permalink / raw) To: Cunhao Lu Cc: kernel, Vincent Mailhol, Heiko Stuebner, linux-can, linux-arm-kernel, linux-rockchip, linux-kernel, stable [-- Attachment #1: Type: text/plain, Size: 2395 bytes --] On 30.07.2026 18:11:46, Cunhao Lu wrote: > rkcanfd_start_xmit() advances tx_head and requests transmission even when > can_put_echo_skb() fails. This creates a pending TX entry without the echo > skb that the RXSTX completion path needs to match the self-received frame. > The entry cannot be completed, and the netdev TX queue can remain stopped > after the two-entry software FIFO fills. > > Do not advance tx_head or request transmission if the echo skb cannot be > installed. Account the frame as dropped and report the error. The > -ENOMEM and -EBUSY paths have already consumed the skb; explicitly free > it on -EINVAL, where ownership remains with the caller. Can you update can_put_echo_skb() to always free the skb in the error case? > Fixes: b6661d73290c ("can: rockchip_canfd: add TX PATH") > Cc: stable@vger.kernel.org > Signed-off-by: Cunhao Lu <1579567540@qq.com> > --- > drivers/net/can/rockchip/rockchip_canfd-tx.c | 14 ++++++++++++-- > 1 file changed, 12 insertions(+), 2 deletions(-) > > diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c > index 12200dcfd338..d2cbe25c46d6 100644 > --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c > +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c > @@ -125,8 +125,18 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) > > frame_len = can_skb_get_frame_len(skb); > err = can_put_echo_skb(skb, ndev, tx_head, frame_len); > - if (!err) > - netdev_sent_queue(priv->ndev, frame_len); > + if (err) { > + if (err == -EINVAL) > + dev_kfree_skb_any(skb); > + > + ndev->stats.tx_dropped++; > + if (net_ratelimit()) > + netdev_err(ndev, "%s: failed to put echo skb: %pe\n", > + __func__, ERR_PTR(err)); Is this error message needed? In the -EINVAL case can_put_echo_skb() already emits an error. If skb allocation I think in -ENOMEM case the kernel already throws an error, right? > + > + return NETDEV_TX_OK; > + } > + netdev_sent_queue(priv->ndev, frame_len); > > WRITE_ONCE(priv->tx_head, priv->tx_head + 1); Marc -- Pengutronix e.K. | Marc Kleine-Budde | Embedded Linux | https://www.pengutronix.de | Vertretung Nürnberg | Phone: +49-5121-206917-129 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 | [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure 2026-07-30 11:38 ` Marc Kleine-Budde @ 2026-07-30 12:39 ` Cunhao Lu 0 siblings, 0 replies; 9+ messages in thread From: Cunhao Lu @ 2026-07-30 12:39 UTC (permalink / raw) To: Marc Kleine-Budde Cc: kernel, Vincent Mailhol, Heiko Stuebner, linux-can, linux-arm-kernel, linux-rockchip, linux-kernel, stable Hi Marc, On 30.07.2026, Marc Kleine-Budde wrote: > Can you update can_put_echo_skb() to always free the skb in the error case? Yes. In v3, I will update can_put_echo_skb() to also free the skb on -EINVAL, so that it consistently consumes the skb on every error path. I will remove the corresponding -EINVAL special case from the Rockchip CAN-FD driver. > Is this error message needed? In the -EINVAL case can_put_echo_skb() > already emits an error. If skb allocation I think in -ENOMEM case the > kernel already throws an error, right? Agreed. The additional driver error message is redundant. I will remove it in v3 and retain only the dropped-frame accounting and error handling. Thanks for the review. Best regards, Cunhao ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 2/3] can: rockchip_canfd: retry the outstanding TX buffer [not found] <20260730-master-v2-0-572974052400@qq.com> 2026-07-30 10:11 ` [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure Cunhao Lu @ 2026-07-30 10:11 ` Cunhao Lu 2026-07-30 10:34 ` sashiko-bot 2026-07-30 10:11 ` [PATCH v2 3/3] can: rockchip_canfd: serialize TX state and command writes Cunhao Lu 2 siblings, 1 reply; 9+ messages in thread From: Cunhao Lu @ 2026-07-30 10:11 UTC (permalink / raw) To: Marc Kleine-Budde, kernel, Vincent Mailhol, Heiko Stuebner Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel, stable, Cunhao Lu rkcanfd_xmit_retry() originally operated with a TX FIFO depth of one. At that depth, the masked head and tail indices both select buffer 0, so using tx_head happened to select the correct buffer. After the FIFO depth was increased to two, tx_head instead identifies the next free buffer when one frame is outstanding. The erratum 6 workaround therefore requests transmission from the wrong buffer, leaving the outstanding echo entry incomplete and the netdev TX queue stopped. Use tx_tail to select the outstanding buffer for retransmission. Fixes: a5605d61c7dd ("can: rockchip_canfd: enable full TX-FIFO depth of 2") Cc: stable@vger.kernel.org Signed-off-by: Cunhao Lu <1579567540@qq.com> --- drivers/net/can/rockchip/rockchip_canfd-tx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c index d2cbe25c46d6..c4ecdc9411cc 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c @@ -57,8 +57,8 @@ static void rkcanfd_start_xmit_write_cmd(const struct rkcanfd_priv *priv, void rkcanfd_xmit_retry(struct rkcanfd_priv *priv) { - const unsigned int tx_head = rkcanfd_get_tx_head(priv); - const u32 reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_head); + const unsigned int tx_tail = rkcanfd_get_tx_tail(priv); + const u32 reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_tail); rkcanfd_start_xmit_write_cmd(priv, reg_cmd); } -- 2.34.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/3] can: rockchip_canfd: retry the outstanding TX buffer 2026-07-30 10:11 ` [PATCH v2 2/3] can: rockchip_canfd: retry the outstanding TX buffer Cunhao Lu @ 2026-07-30 10:34 ` sashiko-bot 0 siblings, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-07-30 10:34 UTC (permalink / raw) To: Cunhao Lu Cc: Vincent Mailhol, Oliver Hartkopp, linux-can, Marc Kleine-Budde, Oleksij Rempel Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] TOCTOU race in `rkcanfd_tx_tail_is_eff()` causes spurious NULL pointer errors in the kernel log and a Use-After-Free vulnerability. -- --- Patch [2]: [PATCH v2 2/3] can: rockchip_canfd: retry the outstanding TX buffer --- commit 5e421e7397d8c64fe895d386c02cbed045a1e541 Author: Cunhao Lu <1579567540@qq.com> can: rockchip_canfd: retry the outstanding TX buffer rkcanfd_xmit_retry() originally operated with a TX FIFO depth of one. At that depth, the masked head and tail indices both select buffer 0, so using tx_head happened to select the correct buffer. After the FIFO depth was increased to two, tx_head instead identifies the next free buffer when one frame is outstanding. The erratum 6 workaround therefore requests transmission from the wrong buffer, leaving the outstanding echo entry incomplete and the netdev TX queue stopped. Use tx_tail to select the outstanding buffer for retransmission. > diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c > index d2cbe25c46d6c..c4ecdc9411cc6 100644 > --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c > +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c > @@ -57,8 +57,8 @@ static void rkcanfd_start_xmit_write_cmd(const struct rkcanfd_priv *priv, > > void rkcanfd_xmit_retry(struct rkcanfd_priv *priv) > { > - const unsigned int tx_head = rkcanfd_get_tx_head(priv); > - const u32 reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_head); > + const unsigned int tx_tail = rkcanfd_get_tx_tail(priv); > + const u32 reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_tail); > > rkcanfd_start_xmit_write_cmd(priv, reg_cmd); > } [Severity: High] This is a pre-existing issue, but does the lockless access to tx_tail and echo_skb in rkcanfd_tx_tail_is_eff() introduce a race condition? drivers/net/can/rockchip/rockchip_canfd-tx.c:rkcanfd_tx_tail_is_eff() { ... tx_tail = rkcanfd_get_tx_tail(priv); skb = priv->can.echo_skb[tx_tail]; if (!skb) { netdev_err(priv->ndev, "%s: echo_skb[%u]=NULL tx_head=0x%08x tx_tail=0x%08x\n", __func__, tx_tail, priv->tx_head, priv->tx_tail); return false; } cfd = (struct canfd_frame *)skb->data; return cfd->can_id & CAN_EFF_FLAG; } The transmit path holds netif_tx_lock, which disables softirqs but leaves hardirqs enabled. If a hardware RX interrupt fires concurrently, it can process the loopback frame, free the echo_skb (setting it to NULL), and increment tx_tail. If the interrupt fires between reading tx_tail and accessing the array, could this read NULL and spuriously flood the kernel log with netdev_err? If the interrupt fires after the skb pointer is read but before skb->data is accessed, could the SKB be freed before it is used, leading to a use-after-free when evaluating cfd->can_id & CAN_EFF_FLAG? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260730-master-v2-0-572974052400@qq.com?part=2 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 3/3] can: rockchip_canfd: serialize TX state and command writes [not found] <20260730-master-v2-0-572974052400@qq.com> 2026-07-30 10:11 ` [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure Cunhao Lu 2026-07-30 10:11 ` [PATCH v2 2/3] can: rockchip_canfd: retry the outstanding TX buffer Cunhao Lu @ 2026-07-30 10:11 ` Cunhao Lu 2026-07-30 10:50 ` sashiko-bot 2 siblings, 1 reply; 9+ messages in thread From: Cunhao Lu @ 2026-07-30 10:11 UTC (permalink / raw) To: Marc Kleine-Budde, kernel, Vincent Mailhol, Heiko Stuebner Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel, stable, Cunhao Lu The TX completion path removes an echo skb before advancing tx_tail. In parallel, the transmit path reads tx_head, tx_tail and the tail echo slot when applying the erratum 6 queue restriction. There is no synchronization between these operations. On SMP, the transmit path can consequently observe a pending frame with an empty echo slot: rockchip_canfd fea60000.can can0: rkcanfd_tx_tail_is_eff: echo_skb[0]=NULL tx_head=0x00060f7d tx_tail=0x00060f7c It can also keep a pointer to an echo skb while the completion path removes and queues it for NAPI, where it can be freed on another CPU. The inconsistent snapshot can stop the netdev TX queue when there is no later completion to wake it. There is a second race in the command submission sequence. rkcanfd_start_xmit() runs in softirq context while rkcanfd_xmit_retry() is called from the RX interrupt handler. On controllers affected by erratum 12, both execute a MODE/CMD/MODE register sequence. The interrupt handler can restore the default MODE between the softirq writes, causing the resumed softirq to issue CMD without SPACE_RX_MODE and bypass the erratum 12 workaround. Add a TX state lock and use it to protect tx_head, tx_tail and the echo skb ring as one state. The same lock serializes the MODE/CMD/MODE sequence between the transmit and interrupt paths. Keep the queue completion and wake operation outside the lock to avoid nesting the TX state lock with the netdev TX queue lock. Tested on an RK3588 rev2.2 at 1 Mbit/s with 100,000 extended CAN frames. The run triggered 138 erratum 6 retries and completed without drops, queue stalls or driver warnings. RK3588 does not enable erratum 12, so this test does not exercise that hardware workaround. Fixes: ae002cc32ec4 ("can: rockchip_canfd: prepare to use full TX-FIFO depth") Fixes: 83f9bd6bf39d ("can: rockchip_canfd: implement workaround for erratum 12") Cc: stable@vger.kernel.org Signed-off-by: Cunhao Lu <1579567540@qq.com> --- drivers/net/can/rockchip/rockchip_canfd-core.c | 1 + drivers/net/can/rockchip/rockchip_canfd-rx.c | 31 +++++++++++++++++++------- drivers/net/can/rockchip/rockchip_canfd-tx.c | 26 ++++++++++++++++++--- drivers/net/can/rockchip/rockchip_canfd.h | 4 +++- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c index 29de0c01e4ed..c8257858b452 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-core.c +++ b/drivers/net/can/rockchip/rockchip_canfd-core.c @@ -904,6 +904,7 @@ static int rkcanfd_probe(struct platform_device *pdev) priv->can.do_set_mode = rkcanfd_set_mode; priv->can.do_get_berr_counter = rkcanfd_get_berr_counter; priv->ndev = ndev; + spin_lock_init(&priv->tx_lock); match = device_get_match_data(&pdev->dev); if (match) { diff --git a/drivers/net/can/rockchip/rockchip_canfd-rx.c b/drivers/net/can/rockchip/rockchip_canfd-rx.c index 475c0409e215..9f57a35672ed 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-rx.c +++ b/drivers/net/can/rockchip/rockchip_canfd-rx.c @@ -99,15 +99,26 @@ static int rkcanfd_rxstx_filter(struct rkcanfd_priv *priv, struct rkcanfd_stats *rkcanfd_stats = &priv->stats; const struct canfd_frame *cfd_nominal; const struct sk_buff *skb; + unsigned long flags; unsigned int tx_tail; + spin_lock_irqsave(&priv->tx_lock, flags); + + if (!rkcanfd_get_tx_pending(priv)) + goto out_unlock; + tx_tail = rkcanfd_get_tx_tail(priv); skb = priv->can.echo_skb[tx_tail]; if (!skb) { + const unsigned int tx_head = priv->tx_head; + const unsigned int tx_tail_full = priv->tx_tail; + + spin_unlock_irqrestore(&priv->tx_lock, flags); + netdev_err(priv->ndev, "%s: echo_skb[%u]=NULL tx_head=0x%08x tx_tail=0x%08x\n", __func__, tx_tail, - priv->tx_head, priv->tx_tail); + tx_head, tx_tail_full); return -ENOMSG; } @@ -123,17 +134,18 @@ static int rkcanfd_rxstx_filter(struct rkcanfd_priv *priv, rkcanfd_handle_tx_done_one(priv, ts, &frame_len); WRITE_ONCE(priv->tx_tail, priv->tx_tail + 1); + *tx_done = true; + + spin_unlock_irqrestore(&priv->tx_lock, flags); netif_subqueue_completed_wake(priv->ndev, 0, 1, frame_len, rkcanfd_get_effective_tx_free(priv), RKCANFD_TX_START_THRESHOLD); - *tx_done = true; - return 0; } if (!(priv->devtype_data.quirks & RKCANFD_QUIRK_RK3568_ERRATUM_6)) - return 0; + goto out_unlock; /* Erratum 6: Extended frames may be send as standard frames. * @@ -143,7 +155,7 @@ static int rkcanfd_rxstx_filter(struct rkcanfd_priv *priv, */ if (!(cfd_nominal->can_id & CAN_EFF_FLAG) || (cfd_rx->can_id & CAN_EFF_FLAG)) - return 0; + goto out_unlock; /* Not affected if: * - standard part and RTR flag of the TX'ed frame @@ -151,20 +163,20 @@ static int rkcanfd_rxstx_filter(struct rkcanfd_priv *priv, */ if ((cfd_nominal->can_id & (CAN_RTR_FLAG | CAN_SFF_MASK)) != (cfd_rx->can_id & (CAN_RTR_FLAG | CAN_SFF_MASK))) - return 0; + goto out_unlock; /* Not affected if: * - length is not the same */ if (cfd_nominal->len != cfd_rx->len) - return 0; + goto out_unlock; /* Not affected if: * - the data of non RTR frames is different */ if (!(cfd_nominal->can_id & CAN_RTR_FLAG) && memcmp(cfd_nominal->data, cfd_rx->data, cfd_nominal->len)) - return 0; + goto out_unlock; /* Affected by Erratum 6 */ u64_stats_update_begin(&rkcanfd_stats->syncp); @@ -185,6 +197,9 @@ static int rkcanfd_rxstx_filter(struct rkcanfd_priv *priv, rkcanfd_xmit_retry(priv); +out_unlock: + spin_unlock_irqrestore(&priv->tx_lock, flags); + return 0; } diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c index c4ecdc9411cc..c3811d3179b6 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c @@ -14,6 +14,8 @@ static bool rkcanfd_tx_tail_is_eff(const struct rkcanfd_priv *priv) const struct sk_buff *skb; unsigned int tx_tail; + lockdep_assert_held(&priv->tx_lock); + if (!rkcanfd_get_tx_pending(priv)) return false; @@ -33,13 +35,22 @@ static bool rkcanfd_tx_tail_is_eff(const struct rkcanfd_priv *priv) return cfd->can_id & CAN_EFF_FLAG; } -unsigned int rkcanfd_get_effective_tx_free(const struct rkcanfd_priv *priv) +unsigned int rkcanfd_get_effective_tx_free(struct rkcanfd_priv *priv) { + unsigned long flags; + unsigned int tx_free; + + spin_lock_irqsave(&priv->tx_lock, flags); + if (priv->devtype_data.quirks & RKCANFD_QUIRK_RK3568_ERRATUM_6 && rkcanfd_tx_tail_is_eff(priv)) - return 0; + tx_free = 0; + else + tx_free = rkcanfd_get_tx_free(priv); + + spin_unlock_irqrestore(&priv->tx_lock, flags); - return rkcanfd_get_tx_free(priv); + return tx_free; } static void rkcanfd_start_xmit_write_cmd(const struct rkcanfd_priv *priv, @@ -60,6 +71,8 @@ void rkcanfd_xmit_retry(struct rkcanfd_priv *priv) const unsigned int tx_tail = rkcanfd_get_tx_tail(priv); const u32 reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_tail); + lockdep_assert_held(&priv->tx_lock); + rkcanfd_start_xmit_write_cmd(priv, reg_cmd); } @@ -69,6 +82,7 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) u32 reg_frameinfo, reg_id, reg_cmd; unsigned int tx_head, frame_len; const struct canfd_frame *cfd; + unsigned long flags; int err; u8 i; @@ -124,8 +138,11 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) *(u32 *)(cfd->data + i)); frame_len = can_skb_get_frame_len(skb); + spin_lock_irqsave(&priv->tx_lock, flags); err = can_put_echo_skb(skb, ndev, tx_head, frame_len); if (err) { + spin_unlock_irqrestore(&priv->tx_lock, flags); + if (err == -EINVAL) dev_kfree_skb_any(skb); @@ -141,6 +158,7 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) WRITE_ONCE(priv->tx_head, priv->tx_head + 1); rkcanfd_start_xmit_write_cmd(priv, reg_cmd); + spin_unlock_irqrestore(&priv->tx_lock, flags); netif_subqueue_maybe_stop(priv->ndev, 0, rkcanfd_get_effective_tx_free(priv), @@ -157,6 +175,8 @@ void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts, unsigned int tx_tail; struct sk_buff *skb; + lockdep_assert_held(&priv->tx_lock); + tx_tail = rkcanfd_get_tx_tail(priv); skb = priv->can.echo_skb[tx_tail]; diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h index 93131c7d7f54..3cf283a7b380 100644 --- a/drivers/net/can/rockchip/rockchip_canfd.h +++ b/drivers/net/can/rockchip/rockchip_canfd.h @@ -15,6 +15,7 @@ #include <linux/netdevice.h> #include <linux/reset.h> #include <linux/skbuff.h> +#include <linux/spinlock.h> #include <linux/timecounter.h> #include <linux/types.h> #include <linux/u64_stats_sync.h> @@ -462,6 +463,7 @@ struct rkcanfd_priv { struct can_rx_offload offload; struct net_device *ndev; + spinlock_t tx_lock; /* protects tx_head, tx_tail and echo_skb */ void __iomem *regs; unsigned int tx_head; unsigned int tx_tail; @@ -544,7 +546,7 @@ void rkcanfd_timestamp_start(struct rkcanfd_priv *priv); void rkcanfd_timestamp_stop(struct rkcanfd_priv *priv); void rkcanfd_timestamp_stop_sync(struct rkcanfd_priv *priv); -unsigned int rkcanfd_get_effective_tx_free(const struct rkcanfd_priv *priv); +unsigned int rkcanfd_get_effective_tx_free(struct rkcanfd_priv *priv); void rkcanfd_xmit_retry(struct rkcanfd_priv *priv); netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev); void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts, -- 2.34.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 3/3] can: rockchip_canfd: serialize TX state and command writes 2026-07-30 10:11 ` [PATCH v2 3/3] can: rockchip_canfd: serialize TX state and command writes Cunhao Lu @ 2026-07-30 10:50 ` sashiko-bot 0 siblings, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-07-30 10:50 UTC (permalink / raw) To: Cunhao Lu Cc: Vincent Mailhol, Marc Kleine-Budde, Oleksij Rempel, linux-can, Oliver Hartkopp Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] The `tx_lock` fails to fully serialize `RKCANFD_REG_MODE` writes against the interrupt path, leaving a race that can break the bus-off state. -- --- Patch [3]: [PATCH v2 3/3] can: rockchip_canfd: serialize TX state and command writes --- commit 2a5b1a8235e0f295b950d48868b11a6ee290c521 Author: Cunhao Lu <1579567540@qq.com> can: rockchip_canfd: serialize TX state and command writes This patch adds a TX state lock to protect tx_head, tx_tail, and the echo skb ring. It also serializes the MODE/CMD/MODE sequence between the transmit and interrupt paths to address errata 6 and 12. Fixes: ae002cc32ec4 ("can: rockchip_canfd: prepare to use full TX-FIFO depth") Fixes: 83f9bd6bf39d ("can: rockchip_canfd: implement workaround for erratum 12") Cc: stable@vger.kernel.org Signed-off-by: Cunhao Lu <1579567540@qq.com> > diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c > index c4ecdc9411cc6..c3811d3179b61 100644 > --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c > +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c [ ... ] > @@ -141,6 +158,7 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) > WRITE_ONCE(priv->tx_head, priv->tx_head + 1); > > rkcanfd_start_xmit_write_cmd(priv, reg_cmd); > + spin_unlock_irqrestore(&priv->tx_lock, flags); [Severity: High] This is a pre-existing issue and was not introduced by this patch, but since the commit message mentions using tx_lock to serialize the MODE/CMD/MODE sequence between the transmit and interrupt paths, does this leave a race with the bus-off error handler? Looking at rkcanfd_chip_set_reset_mode() in drivers/net/can/rockchip/rockchip_canfd-core.c, it places the controller in reset during a bus-off event by writing 0x0 to RKCANFD_REG_MODE, but it does so without holding tx_lock. If a bus-off event occurs concurrently with a packet transmission, could the following sequence occur? CPU1 (Interrupt path): rkcanfd_irq() -> rkcanfd_handle_state_error_int() -> rkcanfd_chip_stop() -> rkcanfd_chip_set_reset_mode() rkcanfd_write(priv, RKCANFD_REG_MODE, 0x0); CPU2 (TX path, holding tx_lock): rkcanfd_start_xmit_write_cmd() rkcanfd_write(priv, RKCANFD_REG_MODE, priv->reg_mode_default); Could this overwrite the bus-off reset mode and bring the controller back out of reset prematurely, potentially causing the node to continuously disrupt the CAN bus? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260730-master-v2-0-572974052400@qq.com?part=3 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 0/3] can: rockchip_canfd: fix TX stalls and races
@ 2026-07-30 10:11 Cunhao Lu
0 siblings, 0 replies; 9+ messages in thread
From: Cunhao Lu @ 2026-07-30 10:11 UTC (permalink / raw)
To: Marc Kleine-Budde, kernel, Vincent Mailhol, Heiko Stuebner
Cc: linux-can, linux-arm-kernel, linux-rockchip, linux-kernel, stable,
Cunhao Lu
While running CAN traffic on an RK3588 system, the driver repeatedly
reported an inconsistent TX echo state:
rockchip_canfd fea60000.can can0: rkcanfd_tx_tail_is_eff:
echo_skb[0]=NULL tx_head=0x00060f7d tx_tail=0x00060f7c
The Rockchip CAN-FD driver completes transmissions by matching
self-received frames with entries in the echo skb ring. The warning above
is emitted when the driver sees a pending transmission but finds no skb in
the corresponding echo slot.
The immediate cause is a race between the transmit and completion paths.
The completion path removes an echo skb before advancing tx_tail, while
the transmit path reads tx_head, tx_tail and that echo slot without common
synchronization. It can therefore observe a partially updated state,
dereference an skb which has been queued for NAPI, or stop the TX queue
without a later completion to wake it.
Two additional TX stall paths were found. The transmit path submits a
frame and advances tx_head even if installing its echo skb fails. In
addition, the erratum 6 retry path selects tx_head, which denotes the next
free buffer with a two-entry FIFO, instead of retrying the outstanding
buffer at tx_tail.
Review also identified that the erratum 12 MODE/CMD/MODE sequence can be
interleaved between the transmit softirq and the RX interrupt retry path.
The interrupt can restore the default MODE before the softirq issues CMD,
bypassing the SPACE_RX_MODE workaround.
This series makes echo skb setup failure abort the transmission, retries
the correct hardware buffer, and uses a spinlock to serialize the TX head,
tail, echo ring and command register sequence.
The final series was tested on an RK3588 rev2.2 at 1 Mbit/s with 100,000
extended CAN frames. The run triggered 138 erratum 6 retries and completed
without drops, queue stalls or driver warnings. RK3588 does not enable
erratum 12, so this test does not exercise that hardware workaround.
All three patches pass scripts/checkpatch.pl --strict. The Rockchip CAN-FD
module also builds cleanly with W=1.
---
Changes in v2:
- Document that the TX lock also serializes the erratum 12 MODE/CMD/MODE
sequence.
- Add the erratum 12 commit to the Fixes tags of patch 3.
- Add RK3588 extended-frame stress-test results.
- Link to v1:
https://lore.kernel.org/linux-can/tencent_0E5C2332F801372DD0D4CB4C25B88C18040A@qq.com
---
Cunhao Lu (3):
can: rockchip_canfd: prevent TX stall on echo skb failure
can: rockchip_canfd: retry the outstanding TX buffer
can: rockchip_canfd: serialize TX state and command writes
drivers/net/can/rockchip/rockchip_canfd-core.c | 1 +
drivers/net/can/rockchip/rockchip_canfd-rx.c | 31 +++++++++++++-----
drivers/net/can/rockchip/rockchip_canfd-tx.c | 44 ++++++++++++++++++++++----
drivers/net/can/rockchip/rockchip_canfd.h | 4 ++-
4 files changed, 64 insertions(+), 16 deletions(-)
---
base-commit: 11028ab62899e4191e074ee364c712b77823a9c4
change-id: 20260730-master-a1b54b3cf5a0
Best regards,
--
Cunhao Lu <1579567540@qq.com>
^ permalink raw reply [flat|nested] 9+ messages in threadend of thread, other threads:[~2026-07-30 12:39 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260730-master-v2-0-572974052400@qq.com>
2026-07-30 10:11 ` [PATCH v2 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure Cunhao Lu
2026-07-30 10:26 ` sashiko-bot
2026-07-30 11:38 ` Marc Kleine-Budde
2026-07-30 12:39 ` Cunhao Lu
2026-07-30 10:11 ` [PATCH v2 2/3] can: rockchip_canfd: retry the outstanding TX buffer Cunhao Lu
2026-07-30 10:34 ` sashiko-bot
2026-07-30 10:11 ` [PATCH v2 3/3] can: rockchip_canfd: serialize TX state and command writes Cunhao Lu
2026-07-30 10:50 ` sashiko-bot
2026-07-30 10:11 [PATCH v2 0/3] can: rockchip_canfd: fix TX stalls and races Cunhao Lu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox