[PATCH net v6 4/6] can: rockchip_canfd: prevent timestamp work from rearming after bus-off
From: Cunhao Lu via B4 Relay
Date: Sat Oct 17 2026 - 10:53:05 EST
From: Cunhao Lu <1579567540@xxxxxx>
The bus-off IRQ cancels timestamp work asynchronously. If the worker
is already running, it can requeue itself after cancellation and keep
polling while the controller remains bus-off.
Add a dedicated lock and an active flag, initialized once at probe.
Serialize the worker's active check, counter read and self-requeue with
start and stop. Clear the flag under the lock before cancelling work
so no worker can read the counter or rearm after deactivation. Enable
the flag and schedule the initial work under the same lock at start.
Keep both cancellation calls outside the lock so synchronous stop can
wait for the worker to finish.
Fixes: 4e1a18bab124 ("can: rockchip_canfd: add hardware timestamping support")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Cunhao Lu <1579567540@xxxxxx>
---
.../net/can/rockchip/rockchip_canfd-timestamp.c | 34 +++++++++++++++++++---
drivers/net/can/rockchip/rockchip_canfd.h | 4 +++
2 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/net/can/rockchip/rockchip_canfd-timestamp.c b/drivers/net/can/rockchip/rockchip_canfd-timestamp.c
index ad582afbbd68..0fa791849c95 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-timestamp.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-timestamp.c
@@ -30,18 +30,27 @@ static void rkcanfd_timestamp_work(struct work_struct *work)
{
const struct delayed_work *delayed_work = to_delayed_work(work);
struct rkcanfd_priv *priv;
+ unsigned long flags;
priv = container_of(delayed_work, struct rkcanfd_priv, timestamp);
- timecounter_read(&priv->tc);
-
- schedule_delayed_work(&priv->timestamp, priv->work_delay_jiffies);
+ spin_lock_irqsave(&priv->timestamp_lock, flags);
+ if (priv->timestamp_active) {
+ timecounter_read(&priv->tc);
+
+ /* Serialize requeueing with timestamp_stop(). */
+ schedule_delayed_work(&priv->timestamp, priv->work_delay_jiffies);
+ }
+ spin_unlock_irqrestore(&priv->timestamp_lock, flags);
}
void rkcanfd_timestamp_work_init(struct rkcanfd_priv *priv)
{
+ spin_lock_init(&priv->timestamp_lock);
+ priv->timestamp_active = false;
INIT_DELAYED_WORK(&priv->timestamp, rkcanfd_timestamp_work);
}
+/* The caller must quiesce IRQs and timestamp work before reconfiguration. */
void rkcanfd_timestamp_init(struct rkcanfd_priv *priv)
{
const struct can_bittiming *dbt = &priv->can.fd.data_bittiming;
@@ -93,17 +102,34 @@ void rkcanfd_timestamp_init(struct rkcanfd_priv *priv)
void rkcanfd_timestamp_start(struct rkcanfd_priv *priv)
{
- timecounter_init(&priv->tc, &priv->cc, ktime_get_real_ns());
+ unsigned long flags;
+ spin_lock_irqsave(&priv->timestamp_lock, flags);
+ timecounter_init(&priv->tc, &priv->cc, ktime_get_real_ns());
+ priv->timestamp_active = true;
schedule_delayed_work(&priv->timestamp, priv->work_delay_jiffies);
+ spin_unlock_irqrestore(&priv->timestamp_lock, flags);
}
void rkcanfd_timestamp_stop(struct rkcanfd_priv *priv)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->timestamp_lock, flags);
+ priv->timestamp_active = false;
+ spin_unlock_irqrestore(&priv->timestamp_lock, flags);
+
cancel_delayed_work(&priv->timestamp);
}
void rkcanfd_timestamp_stop_sync(struct rkcanfd_priv *priv)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->timestamp_lock, flags);
+ priv->timestamp_active = false;
+ spin_unlock_irqrestore(&priv->timestamp_lock, flags);
+
+ /* The worker needs timestamp_lock to finish. */
cancel_delayed_work_sync(&priv->timestamp);
}
diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h
index e1debab54534..2743c96ed42f 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>
@@ -485,8 +486,11 @@ struct rkcanfd_priv {
struct cyclecounter cc;
struct timecounter tc;
+ /* Protects timestamp_active and worker reads/requeueing. */
+ spinlock_t timestamp_lock;
struct delayed_work timestamp;
unsigned long work_delay_jiffies;
+ bool timestamp_active;
struct can_berr_counter bec;
--
2.34.1