Commit 704145a8 authored by Jiawen Wu's avatar Jiawen Wu Committed by Jakub Kicinski
Browse files

net: wangxun: Add periodic checks for overflow and errors



Implement watchdog task to detect SYSTIME overflow and error cases of
Rx/Tx timestamp.

Signed-off-by: default avatarJiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: default avatarVadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://patch.msgid.link/20250218023432.146536-4-jiawenwu@trustnetic.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent ce114069
Loading
Loading
Loading
Loading
+105 −0
Original line number Diff line number Diff line
@@ -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);

+2 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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);
}
+1 −0
Original line number Diff line number Diff line
@@ -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);
}