From: Cunhao Lu <1579567540@qq.com> The timestamp worker advances the timecounter with timecounter_read() while RX, TX completion and error-frame paths use timecounter_cyc2time() to convert hardware timestamps without holding timestamp_lock. timecounter_read() updates frac, cycle_last and nsec in separate steps. A concurrent conversion can observe the new cycle_last with the old nsec and produce an incorrect timestamp. Hold timestamp_lock across timecounter_cyc2time(), using the same lock as timecounter_read() and timecounter_init(). Keep the SKB timestamp assignment outside the critical section. TX completion nests timestamp_lock inside tx_lock; no timestamp-lock holder acquires tx_lock. Fixes: 4e1a18bab124 ("can: rockchip_canfd: add hardware timestamping support") Cc: stable@vger.kernel.org Signed-off-by: Cunhao Lu <1579567540@qq.com> --- drivers/net/can/rockchip/rockchip_canfd-timestamp.c | 5 ++++- drivers/net/can/rockchip/rockchip_canfd.h | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/rockchip/rockchip_canfd-timestamp.c b/drivers/net/can/rockchip/rockchip_canfd-timestamp.c index 1d2a122770dc..e255bb33a20d 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-timestamp.c +++ b/drivers/net/can/rockchip/rockchip_canfd-timestamp.c @@ -15,13 +15,16 @@ static u64 rkcanfd_timestamp_read(struct cyclecounter *cc) return rkcanfd_get_timestamp(priv); } -void rkcanfd_skb_set_timestamp(const struct rkcanfd_priv *priv, +void rkcanfd_skb_set_timestamp(struct rkcanfd_priv *priv, struct sk_buff *skb, const u32 timestamp) { struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); + unsigned long flags; u64 ns; + spin_lock_irqsave(&priv->timestamp_lock, flags); ns = timecounter_cyc2time(&priv->tc, timestamp); + spin_unlock_irqrestore(&priv->timestamp_lock, flags); hwtstamps->hwtstamp = ns_to_ktime(ns); } diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h index 078876769a05..ecb9e2d6aa19 100644 --- a/drivers/net/can/rockchip/rockchip_canfd.h +++ b/drivers/net/can/rockchip/rockchip_canfd.h @@ -477,7 +477,7 @@ struct rkcanfd_priv { /* Serialize TX submission/completion with bus-off and restart. */ spinlock_t tx_lock; - /* Protects timestamp_active and worker reads/requeueing. */ + /* Protects tc, timestamp_active and worker requeueing. */ spinlock_t timestamp_lock; void __iomem *regs; unsigned int tx_head; @@ -557,7 +557,7 @@ void rkcanfd_ethtool_init(struct rkcanfd_priv *priv); int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv); -void rkcanfd_skb_set_timestamp(const struct rkcanfd_priv *priv, +void rkcanfd_skb_set_timestamp(struct rkcanfd_priv *priv, struct sk_buff *skb, const u32 timestamp); void rkcanfd_timestamp_work_init(struct rkcanfd_priv *priv); void rkcanfd_timestamp_init(struct rkcanfd_priv *priv); -- 2.34.1