* [PATCH 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure
[not found] <20260730-master-v1-0-6945fdd4f0ff@qq.com>
@ 2026-07-30 6:11 ` Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
2 siblings, 0 replies; 10+ messages in thread
From: Cunhao Lu @ 2026-07-30 6: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] 10+ messages in thread
* [PATCH 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure
@ 2026-07-30 6:11 ` Cunhao Lu
0 siblings, 0 replies; 10+ messages in thread
From: Cunhao Lu @ 2026-07-30 6: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
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/3] can: rockchip_canfd: retry the outstanding TX buffer
[not found] <20260730-master-v1-0-6945fdd4f0ff@qq.com>
@ 2026-07-30 6:11 ` Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
2 siblings, 0 replies; 10+ messages in thread
From: Cunhao Lu @ 2026-07-30 6: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] 10+ messages in thread
* [PATCH 2/3] can: rockchip_canfd: retry the outstanding TX buffer
@ 2026-07-30 6:11 ` Cunhao Lu
0 siblings, 0 replies; 10+ messages in thread
From: Cunhao Lu @ 2026-07-30 6: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
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/3] can: rockchip_canfd: fix TX echo skb race
[not found] <20260730-master-v1-0-6945fdd4f0ff@qq.com>
@ 2026-07-30 6:11 ` Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
2 siblings, 0 replies; 10+ messages in thread
From: Cunhao Lu @ 2026-07-30 6: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 frees it. The inconsistent snapshot can stop the netdev TX queue when
there is no later completion to wake it.
Add a TX state lock and use it to protect tx_head, tx_tail and the echo skb
ring as one state. Keep the queue completion and wake operation outside
the lock to avoid nesting the TX state lock with the netdev TX queue lock.
Fixes: ae002cc32ec4 ("can: rockchip_canfd: prepare to use full TX-FIFO depth")
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] 10+ messages in thread
* [PATCH 3/3] can: rockchip_canfd: fix TX echo skb race
@ 2026-07-30 6:11 ` Cunhao Lu
0 siblings, 0 replies; 10+ messages in thread
From: Cunhao Lu @ 2026-07-30 6: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 frees it. The inconsistent snapshot can stop the netdev TX queue when
there is no later completion to wake it.
Add a TX state lock and use it to protect tx_head, tx_tail and the echo skb
ring as one state. Keep the queue completion and wake operation outside
the lock to avoid nesting the TX state lock with the netdev TX queue lock.
Fixes: ae002cc32ec4 ("can: rockchip_canfd: prepare to use full TX-FIFO depth")
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
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure
2026-07-30 6:11 ` Cunhao Lu
(?)
@ 2026-07-30 6:25 ` sashiko-bot
2026-07-30 7:04 ` Cunhao Lu
-1 siblings, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-07-30 6:25 UTC (permalink / raw)
To: Cunhao Lu
Cc: Oliver Hartkopp, Vincent Mailhol, Oleksij Rempel,
Marc Kleine-Budde, linux-can
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Concurrent access to `echo_skb` in `rkcanfd_tx_tail_is_eff()` without locking leads to a Use-After-Free when the IRQ handler frees the SKB.
--
--- Patch [1]: [PATCH 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure ---
commit dae5727dde941410ecb97f7ed728561d5c939a22
Author: Cunhao Lu <1579567540@qq.com>
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.
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.
> 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
> @@ -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);
[Severity: High]
This is a pre-existing issue, but I noticed a potential use-after-free
related to echo_skb accesses in rkcanfd_tx_tail_is_eff() when called from
rkcanfd_start_xmit() via rkcanfd_get_effective_tx_free().
Does rkcanfd_tx_tail_is_eff() need locking to safely access echo_skb?
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) {
...
}
cfd = (struct canfd_frame *)skb->data;
...
}
If a concurrent TX completion interrupt on another CPU executes
rkcanfd_rxstx_filter() and rkcanfd_handle_tx_done_one(), wouldn't it
consume this same SKB and set echo_skb[tx_tail] to NULL?
If the SKB is then queued to the NAPI offload subsystem and immediately
processed and freed on the remote CPU, could the original thread continue
and dereference the now-freed SKB when accessing skb->data?
>
> WRITE_ONCE(priv->tx_head, priv->tx_head + 1);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260730-master-v1-0-6945fdd4f0ff@qq.com?part=1
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] can: rockchip_canfd: retry the outstanding TX buffer
2026-07-30 6:11 ` Cunhao Lu
(?)
@ 2026-07-30 6:26 ` sashiko-bot
2026-07-30 8:45 ` Cunhao Lu
-1 siblings, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-07-30 6:26 UTC (permalink / raw)
To: Cunhao Lu
Cc: Marc Kleine-Budde, Vincent Mailhol, Oleksij Rempel,
Oliver Hartkopp, linux-can
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but there is a data race on hardware registers RKCANFD_REG_MODE and RKCANFD_REG_CMD in rkcanfd_start_xmit_write_cmd() between softirq and hard IRQ contexts.
--
--- Patch [2]: [PATCH 2/3] can: rockchip_canfd: retry the outstanding TX buffer ---
commit 008079a4d397736133d0b75cae9351b6c5be751d
Author: Cunhao Lu <1579567540@qq.com>
can: rockchip_canfd: retry the outstanding TX buffer
After the FIFO depth was increased to two, tx_head selects the next
free buffer instead of the outstanding one, breaking the erratum 6
workaround. This patch fixes the issue by using 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 isn't a bug introduced by this patch, but does this call expose a race
condition on the hardware registers between softirq and hardirq contexts?
rkcanfd_start_xmit() runs in softirq context and calls
rkcanfd_start_xmit_write_cmd(), which performs non-atomic register writes
to RKCANFD_REG_MODE. If a hardware RX interrupt preempts execution between
these writes, rkcanfd_irq() (hardirq) will call rkcanfd_xmit_retry()
which also calls rkcanfd_start_xmit_write_cmd().
The hardirq will restore priv->reg_mode_default, so when the softirq
resumes, RKCANFD_REG_CMD is written without the required SPACE_RX_MODE
bit set, bypassing the RK3568 Erratum 12 workaround. Could this lead to
hardware misbehavior under heavy load?
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260730-master-v1-0-6945fdd4f0ff@qq.com?part=2
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure
2026-07-30 6:25 ` sashiko-bot
@ 2026-07-30 7:04 ` Cunhao Lu
0 siblings, 0 replies; 10+ messages in thread
From: Cunhao Lu @ 2026-07-30 7:04 UTC (permalink / raw)
To: sashiko-reviews
Cc: Oliver Hartkopp, Vincent Mailhol, Oleksij Rempel,
Marc Kleine-Budde, linux-can
Hi,
Thank you for the review.
> > 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
> > @@ -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);
>
> [Severity: High]
> This is a pre-existing issue, but I noticed a potential use-after-free
> related to echo_skb accesses in rkcanfd_tx_tail_is_eff() when called from
> rkcanfd_start_xmit() via rkcanfd_get_effective_tx_free().
>
> Does rkcanfd_tx_tail_is_eff() need locking to safely access echo_skb?
>
> 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) {
> ...
> }
>
> cfd = (struct canfd_frame *)skb->data;
> ...
> }
>
> If a concurrent TX completion interrupt on another CPU executes
> rkcanfd_rxstx_filter() and rkcanfd_handle_tx_done_one(), wouldn't it
> consume this same SKB and set echo_skb[tx_tail] to NULL?
>
> If the SKB is then queued to the NAPI offload subsystem and immediately
> processed and freed on the remote CPU, could the original thread continue
> and dereference the now-freed SKB when accessing skb->data?
Yes, this is a real pre-existing race. The TX completion path removes the
echo skb from `can.echo_skb[]` and queues it to the RX offload path, while
`rkcanfd_tx_tail_is_eff()` can concurrently retain the old pointer and
dereference `skb->data`. It can therefore observe inconsistent TX state or
access an skb after it has been consumed by the completion path.
This is addressed by PATCH 3/3. It adds `tx_lock` and uses
`spin_lock_irqsave()` to protect `tx_head`, `tx_tail`, and the echo skb ring as
one state. Both the lookup and dereference in
`rkcanfd_tx_tail_is_eff()` and the echo removal plus `tx_tail` update in the
completion path are performed while holding this lock.
I will make this relationship clearer in the v2 cover letter and PATCH 3/3
description.
Regards,
Cunhao
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] can: rockchip_canfd: retry the outstanding TX buffer
2026-07-30 6:26 ` sashiko-bot
@ 2026-07-30 8:45 ` Cunhao Lu
0 siblings, 0 replies; 10+ messages in thread
From: Cunhao Lu @ 2026-07-30 8:45 UTC (permalink / raw)
To: sashiko-reviews
Cc: Marc Kleine-Budde, Vincent Mailhol, Oleksij Rempel,
Oliver Hartkopp, linux-can
Hi,
Thank you for pointing this out.
> > 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 isn't a bug introduced by this patch, but does this call expose a race
> condition on the hardware registers between softirq and hardirq contexts?
>
> rkcanfd_start_xmit() runs in softirq context and calls
> rkcanfd_start_xmit_write_cmd(), which performs non-atomic register writes
> to RKCANFD_REG_MODE. If a hardware RX interrupt preempts execution between
> these writes, rkcanfd_irq() (hardirq) will call rkcanfd_xmit_retry()
> which also calls rkcanfd_start_xmit_write_cmd().
>
> The hardirq will restore priv->reg_mode_default, so when the softirq
> resumes, RKCANFD_REG_CMD is written without the required SPACE_RX_MODE
> bit set, bypassing the RK3568 Erratum 12 workaround. Could this lead to
> hardware misbehavior under heavy load?
Yes, the MODE/CMD sequence has a pre-existing serialization problem. On a
device affected by both erratum 6 and erratum 12, `rkcanfd_start_xmit()` can
be interrupted after enabling `SPACE_RX_MODE`. The retry path can then write
its own MODE/CMD/MODE sequence and restore the default mode before the
original path writes CMD. That CMD would consequently be issued without
`SPACE_RX_MODE` enabled.
With the current quirk table this combination applies to RK3568v2. RK3588 is
affected by erratum 6 but not erratum 12, so this specific hardware failure
cannot be reproduced on my RK3588 test board.
PATCH 3/3 already prevents the interleaving: both
`rkcanfd_start_xmit()` and the IRQ retry path call
`rkcanfd_start_xmit_write_cmd()` while holding the same `tx_lock` with IRQs
saved. I will update PATCH 3/3 in v2 to explicitly document that the lock also
protects the MODE/CMD transaction, add a lockdep assertion to
`rkcanfd_start_xmit_write_cmd()`, and include the erratum 12 commit in the
fix description.
Regards,
Cunhao
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-07-30 8:45 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260730-master-v1-0-6945fdd4f0ff@qq.com>
2026-07-30 6:11 ` [PATCH 1/3] can: rockchip_canfd: prevent TX stall on echo skb failure Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
2026-07-30 6:25 ` sashiko-bot
2026-07-30 7:04 ` Cunhao Lu
2026-07-30 6:11 ` [PATCH 2/3] can: rockchip_canfd: retry the outstanding TX buffer Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
2026-07-30 6:26 ` sashiko-bot
2026-07-30 8:45 ` Cunhao Lu
2026-07-30 6:11 ` [PATCH 3/3] can: rockchip_canfd: fix TX echo skb race Cunhao Lu
2026-07-30 6:11 ` Cunhao Lu
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.