]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: wangxun: Add periodic checks for overflow and errors
authorJiawen Wu <jiawenwu@trustnetic.com>
Tue, 18 Feb 2025 02:34:31 +0000 (10:34 +0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 20 Feb 2025 22:59:37 +0000 (14:59 -0800)
Implement watchdog task to detect SYSTIME overflow and error cases of
Rx/Tx timestamp.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://patch.msgid.link/20250218023432.146536-4-jiawenwu@trustnetic.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/wangxun/libwx/wx_ptp.c
drivers/net/ethernet/wangxun/libwx/wx_type.h
drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c

index e56288a18614b2118a54892462dee861bf8e71c6..76986e41afe00647c35febfaec8228a51c42f5f1 100644 (file)
@@ -212,6 +212,102 @@ static int wx_ptp_tx_hwtstamp_work(struct wx *wx)
        return -1;
 }
 
+/**
+ * wx_ptp_overflow_check - watchdog task to detect SYSTIME overflow
+ * @wx: pointer to wx struct
+ *
+ * this watchdog task periodically reads the timecounter
+ * in order to prevent missing when the system time registers wrap
+ * around. This needs to be run approximately twice a minute for the fastest
+ * overflowing hardware. We run it for all hardware since it shouldn't have a
+ * large impact.
+ */
+static void wx_ptp_overflow_check(struct wx *wx)
+{
+       bool timeout = time_is_before_jiffies(wx->last_overflow_check +
+                                             WX_OVERFLOW_PERIOD);
+       unsigned long flags;
+
+       if (timeout) {
+               /* Update the timecounter */
+               write_seqlock_irqsave(&wx->hw_tc_lock, flags);
+               timecounter_read(&wx->hw_tc);
+               write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
+
+               wx->last_overflow_check = jiffies;
+       }
+}
+
+/**
+ * wx_ptp_rx_hang - detect error case when Rx timestamp registers latched
+ * @wx: pointer to wx struct
+ *
+ * this watchdog task is scheduled to detect error case where hardware has
+ * dropped an Rx packet that was timestamped when the ring is full. The
+ * particular error is rare but leaves the device in a state unable to
+ * timestamp any future packets.
+ */
+static void wx_ptp_rx_hang(struct wx *wx)
+{
+       struct wx_ring *rx_ring;
+       unsigned long rx_event;
+       u32 tsyncrxctl;
+       int n;
+
+       tsyncrxctl = rd32(wx, WX_PSR_1588_CTL);
+
+       /* if we don't have a valid timestamp in the registers, just update the
+        * timeout counter and exit
+        */
+       if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID)) {
+               wx->last_rx_ptp_check = jiffies;
+               return;
+       }
+
+       /* determine the most recent watchdog or rx_timestamp event */
+       rx_event = wx->last_rx_ptp_check;
+       for (n = 0; n < wx->num_rx_queues; n++) {
+               rx_ring = wx->rx_ring[n];
+               if (time_after(rx_ring->last_rx_timestamp, rx_event))
+                       rx_event = rx_ring->last_rx_timestamp;
+       }
+
+       /* only need to read the high RXSTMP register to clear the lock */
+       if (time_is_before_jiffies(rx_event + 5 * HZ)) {
+               rd32(wx, WX_PSR_1588_STMPH);
+               wx->last_rx_ptp_check = jiffies;
+
+               wx->rx_hwtstamp_cleared++;
+               dev_warn(&wx->pdev->dev, "clearing RX Timestamp hang");
+       }
+}
+
+/**
+ * wx_ptp_tx_hang - detect error case where Tx timestamp never finishes
+ * @wx: private network wx structure
+ */
+static void wx_ptp_tx_hang(struct wx *wx)
+{
+       bool timeout = time_is_before_jiffies(wx->ptp_tx_start +
+                                             WX_PTP_TX_TIMEOUT);
+
+       if (!wx->ptp_tx_skb)
+               return;
+
+       if (!test_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state))
+               return;
+
+       /* If we haven't received a timestamp within the timeout, it is
+        * reasonable to assume that it will never occur, so we can unlock the
+        * timestamp bit when this occurs.
+        */
+       if (timeout) {
+               wx_ptp_clear_tx_timestamp(wx);
+               wx->tx_hwtstamp_timeouts++;
+               dev_warn(&wx->pdev->dev, "clearing Tx timestamp hang\n");
+       }
+}
+
 static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
 {
        struct wx *wx = container_of(ptp, struct wx, ptp_caps);
@@ -219,6 +315,12 @@ static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
 
        ts_done = wx_ptp_tx_hwtstamp_work(wx);
 
+       wx_ptp_overflow_check(wx);
+       if (unlikely(test_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
+                             wx->flags)))
+               wx_ptp_rx_hang(wx);
+       wx_ptp_tx_hang(wx);
+
        return ts_done ? 1 : HZ;
 }
 
@@ -475,6 +577,9 @@ void wx_ptp_reset(struct wx *wx)
        timecounter_init(&wx->hw_tc, &wx->hw_cc,
                         ktime_to_ns(ktime_get_real()));
        write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
+
+       wx->last_overflow_check = jiffies;
+       ptp_schedule_worker(wx->ptp_clock, HZ);
 }
 EXPORT_SYMBOL(wx_ptp_reset);
 
index f83c54d4657c96bde3fa379c1abb046dc002e12a..0fabfa90d4e71087b8d2ad895ddb1e478e60b48f 100644 (file)
@@ -1175,6 +1175,8 @@ struct wx {
        u32 tx_hwtstamp_skipped;
        u32 tx_hwtstamp_errors;
        u32 rx_hwtstamp_cleared;
+       unsigned long last_overflow_check;
+       unsigned long last_rx_ptp_check;
        unsigned long ptp_tx_start;
        seqlock_t hw_tc_lock; /* seqlock for ptp */
        struct cyclecounter hw_cc;
index c7944e62838ae0ee2845db726cbdb21f84f39f11..ea1d7e9a91f3deadcebcf3a2745c40b98ab6d64b 100644 (file)
@@ -111,6 +111,7 @@ static void ngbe_mac_link_up(struct phylink_config *config,
        wr32(wx, WX_MAC_WDG_TIMEOUT, reg);
 
        wx->speed = speed;
+       wx->last_rx_ptp_check = jiffies;
        if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
                wx_ptp_reset_cyclecounter(wx);
 }
index 60e5f3288ad8afc44008b39c0792f125b9e55a6b..7e17d727c2bad7b7fc77d427044e16f95f08cddc 100644 (file)
@@ -222,6 +222,7 @@ static void txgbe_mac_link_up(struct phylink_config *config,
        wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
 
        wx->speed = speed;
+       wx->last_rx_ptp_check = jiffies;
        if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
                wx_ptp_reset_cyclecounter(wx);
 }