Skip to content

Commit c789ad7

Browse files
vcgomesanguy11
authored andcommitted
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: 2c344ae ("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>
1 parent afa1415 commit c789ad7

1 file changed

Lines changed: 37 additions & 11 deletions

File tree

drivers/net/ethernet/intel/igc/igc_ptp.c

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -685,14 +685,49 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
685685
struct sk_buff *skb = adapter->ptp_tx_skb;
686686
struct skb_shared_hwtstamps shhwtstamps;
687687
struct igc_hw *hw = &adapter->hw;
688+
u32 tsynctxctl;
688689
int adjust = 0;
689690
u64 regval;
690691

691692
if (WARN_ON_ONCE(!skb))
692693
return;
693694

694-
regval = rd32(IGC_TXSTMPL);
695-
regval |= (u64)rd32(IGC_TXSTMPH) << 32;
695+
tsynctxctl = rd32(IGC_TSYNCTXCTL);
696+
tsynctxctl &= IGC_TSYNCTXCTL_TXTT_0;
697+
if (tsynctxctl) {
698+
regval = rd32(IGC_TXSTMPL);
699+
regval |= (u64)rd32(IGC_TXSTMPH) << 32;
700+
} else {
701+
/* There's a bug in the hardware that could cause
702+
* missing interrupts for TX timestamping. The issue
703+
* is that for new interrupts to be triggered, the
704+
* IGC_TXSTMPH_0 register must be read.
705+
*
706+
* To avoid discarding a valid timestamp that just
707+
* happened at the "wrong" time, we need to confirm
708+
* that there was no timestamp captured, we do that by
709+
* assuming that no two timestamps in sequence have
710+
* the same nanosecond value.
711+
*
712+
* So, we read the "low" register, read the "high"
713+
* register (to latch a new timestamp) and read the
714+
* "low" register again, if "old" and "new" versions
715+
* of the "low" register are different, a valid
716+
* timestamp was captured, we can read the "high"
717+
* register again.
718+
*/
719+
u32 txstmpl_old, txstmpl_new;
720+
721+
txstmpl_old = rd32(IGC_TXSTMPL);
722+
rd32(IGC_TXSTMPH);
723+
txstmpl_new = rd32(IGC_TXSTMPL);
724+
725+
if (txstmpl_old == txstmpl_new)
726+
return;
727+
728+
regval = txstmpl_new;
729+
regval |= (u64)rd32(IGC_TXSTMPH) << 32;
730+
}
696731
if (igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval))
697732
return;
698733

@@ -730,22 +765,13 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
730765
*/
731766
void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter)
732767
{
733-
struct igc_hw *hw = &adapter->hw;
734768
unsigned long flags;
735-
u32 tsynctxctl;
736769

737770
spin_lock_irqsave(&adapter->ptp_tx_lock, flags);
738771

739772
if (!adapter->ptp_tx_skb)
740773
goto unlock;
741774

742-
tsynctxctl = rd32(IGC_TSYNCTXCTL);
743-
tsynctxctl &= IGC_TSYNCTXCTL_TXTT_0;
744-
if (!tsynctxctl) {
745-
WARN_ONCE(1, "Received a TSTAMP interrupt but no TSTAMP is ready.\n");
746-
goto unlock;
747-
}
748-
749775
igc_ptp_tx_hwtstamp(adapter);
750776

751777
unlock:

0 commit comments

Comments
 (0)