73 lines
2.8 KiB
Diff
73 lines
2.8 KiB
Diff
From 08956ae250aabf15d728a7502ff86986f540be55 Mon Sep 17 00:00:00 2001
|
|
From: Cunhao Lu <1579567540@qq.com>
|
|
Date: Thu, 30 Jul 2026 23:16:17 +0800
|
|
Subject: [PATCH 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure
|
|
|
|
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.
|
|
|
|
Install the echo skb before loading the hardware TX buffer. If installation
|
|
fails, account the frame as dropped and leave both the hardware FIFO and
|
|
software TX state unchanged. After the echo skb is installed, use the
|
|
stored echo skb as the source for the hardware frame data.
|
|
|
|
This depends on the standalone can_put_echo_skb() ownership fix. It makes
|
|
the remaining -EINVAL path consume the skb and was posted at:
|
|
|
|
Link: https://lore.kernel.org/linux-can/tencent_944DADCC4B42C8484EC01DA2B15F42132906@qq.com
|
|
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 | 17 +++++++++++------
|
|
1 file changed, 11 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c
|
|
index 12200dcfd338..86fa8f2e1c8b 100644
|
|
--- a/drivers/net/can/rockchip/rockchip_canfd-tx.c
|
|
+++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c
|
|
@@ -88,7 +88,16 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
|
return NETDEV_TX_BUSY;
|
|
}
|
|
|
|
- cfd = (struct canfd_frame *)skb->data;
|
|
+ tx_head = rkcanfd_get_tx_head(priv);
|
|
+ frame_len = can_skb_get_frame_len(skb);
|
|
+ err = can_put_echo_skb(skb, ndev, tx_head, frame_len);
|
|
+ if (err) {
|
|
+ ndev->stats.tx_dropped++;
|
|
+ return NETDEV_TX_OK;
|
|
+ }
|
|
+
|
|
+ skb = priv->can.echo_skb[tx_head];
|
|
+ cfd = (const struct canfd_frame *)skb->data;
|
|
|
|
if (cfd->can_id & CAN_EFF_FLAG) {
|
|
reg_frameinfo = RKCANFD_REG_FD_FRAMEINFO_FRAME_FORMAT;
|
|
@@ -114,7 +123,6 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
|
cfd->len);
|
|
}
|
|
|
|
- tx_head = rkcanfd_get_tx_head(priv);
|
|
reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_head);
|
|
|
|
rkcanfd_write(priv, RKCANFD_REG_FD_TXFRAMEINFO, reg_frameinfo);
|
|
@@ -123,10 +131,7 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
|
rkcanfd_write(priv, RKCANFD_REG_FD_TXDATA0 + i,
|
|
*(u32 *)(cfd->data + i));
|
|
|
|
- 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);
|
|
+ netdev_sent_queue(priv->ndev, frame_len);
|
|
|
|
WRITE_ONCE(priv->tx_head, priv->tx_head + 1);
|
|
|
|
--
|
|
2.34.1
|
|
|