summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVinicius Costa Gomes <vinicius.gomes@intel.com>2023-06-07 14:32:32 -0700
committerTony Nguyen <anthony.l.nguyen@intel.com>2023-06-22 08:22:35 -0700
commitc789ad7cbebcac5d5f417296c140a1252c689524 (patch)
tree2e1f8e7f12b946233e912b9254cb0371e8912854
parentafa141583d82725f682b2fa762cb36a07f58b3f3 (diff)
igc: Work around HW bug causing missing timestamps
There's an hardware issue that can cause missing timestamps. The bug is that the interrupt is only cleared if the IGC_TXSTMPH_0 register is read. The bug can cause a race condition if a timestamp is captured at the wrong time, and we will miss that timestamp. To reduce the time window that the problem is able to happen, in case no timestamp was ready, we read the "previous" value of the timestamp registers, and we compare with the "current" one, if it didn't change we can be reasonably sure that no timestamp was captured. If they are different, we use the new value as the captured timestamp. The HW bug is not easy to reproduce, got to reproduce it when smashing the NIC with timestamping requests from multiple applications (e.g. multiple ntpperf instances + ptp4l), after 10s of minutes. This workaround has more impact when multiple timestamp registers are used, and the IGC_TXSTMPH_0 register always need to be read, so the interrupt is cleared. Fixes: 2c344ae24501 ("igc: Add support for TX timestamping") Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com> Tested-by: Naama Meir <naamax.meir@linux.intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ptp.c48
1 files changed, 37 insertions, 11 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c
index cf963a12a92f..32ef112f8291 100644
--- a/drivers/net/ethernet/intel/igc/igc_ptp.c
+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c
@@ -685,14 +685,49 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
struct sk_buff *skb = adapter->ptp_tx_skb;
struct skb_shared_hwtstamps shhwtstamps;
struct igc_hw *hw = &adapter->hw;
+ u32 tsynctxctl;
int adjust = 0;
u64 regval;
if (WARN_ON_ONCE(!skb))
return;
- regval = rd32(IGC_TXSTMPL);
- regval |= (u64)rd32(IGC_TXSTMPH) << 32;
+ tsynctxctl = rd32(IGC_TSYNCTXCTL);
+ tsynctxctl &= IGC_TSYNCTXCTL_TXTT_0;
+ if (tsynctxctl) {
+ regval = rd32(IGC_TXSTMPL);
+ regval |= (u64)rd32(IGC_TXSTMPH) << 32;
+ } else {
+ /* There's a bug in the hardware that could cause
+ * missing interrupts for TX timestamping. The issue
+ * is that for new interrupts to be triggered, the
+ * IGC_TXSTMPH_0 register must be read.
+ *
+ * To avoid discarding a valid timestamp that just
+ * happened at the "wrong" time, we need to confirm
+ * that there was no timestamp captured, we do that by
+ * assuming that no two timestamps in sequence have
+ * the same nanosecond value.
+ *
+ * So, we read the "low" register, read the "high"
+ * register (to latch a new timestamp) and read the
+ * "low" register again, if "old" and "new" versions
+ * of the "low" register are different, a valid
+ * timestamp was captured, we can read the "high"
+ * register again.
+ */
+ u32 txstmpl_old, txstmpl_new;
+
+ txstmpl_old = rd32(IGC_TXSTMPL);
+ rd32(IGC_TXSTMPH);
+ txstmpl_new = rd32(IGC_TXSTMPL);
+
+ if (txstmpl_old == txstmpl_new)
+ return;
+
+ regval = txstmpl_new;
+ regval |= (u64)rd32(IGC_TXSTMPH) << 32;
+ }
if (igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval))
return;
@@ -730,22 +765,13 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
*/
void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter)
{
- struct igc_hw *hw = &adapter->hw;
unsigned long flags;
- u32 tsynctxctl;
spin_lock_irqsave(&adapter->ptp_tx_lock, flags);
if (!adapter->ptp_tx_skb)
goto unlock;
- tsynctxctl = rd32(IGC_TSYNCTXCTL);
- tsynctxctl &= IGC_TSYNCTXCTL_TXTT_0;
- if (!tsynctxctl) {
- WARN_ONCE(1, "Received a TSTAMP interrupt but no TSTAMP is ready.\n");
- goto unlock;
- }
-
igc_ptp_tx_hwtstamp(adapter);
unlock: