Commit 12490501 authored by Jacob Keller's avatar Jacob Keller Committed by Jeff Kirsher

i40e: replace PTP Rx timestamp hang logic

The current Rx timestamp hang logic is not very robust because it does
not notice a register is hung until all four timestamps have been
latched and we wait a full 5 seconds. Replace this logic with a newer Rx
hang detection based on storing the jiffies when we first notice
a receive timestamp event. We store each register's time separately,
along with a flag indicating if it is currently latched. Upon first
transitioning to latch, we will update the latch_events[i] jiffies
value. This indicates the time we first noticed this event. The watchdog
routine will simply check that the either the flag has been cleared, or
we have passed at least one second. In this case, it is able to clear
the Rx timestamp register under the assumption that it was for a dropped
frame. The benefit if this strategy is that we should be able to
detect and clear out stalled RXTIME_H registers before we exhaust the
supply of 4, and avoid complete stall of Rx timestamp events.

Change-ID: Id55458c0cd7a5dd0c951ff2b8ac0b2509364131f
Signed-off-by: default avatarJacob Keller <jacob.e.keller@intel.com>
Tested-by: default avatarAndrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 19551262
...@@ -429,11 +429,13 @@ struct i40e_pf { ...@@ -429,11 +429,13 @@ struct i40e_pf {
struct ptp_clock_info ptp_caps; struct ptp_clock_info ptp_caps;
struct sk_buff *ptp_tx_skb; struct sk_buff *ptp_tx_skb;
struct hwtstamp_config tstamp_config; struct hwtstamp_config tstamp_config;
unsigned long last_rx_ptp_check;
struct mutex tmreg_lock; /* Used to protect the SYSTIME registers. */ struct mutex tmreg_lock; /* Used to protect the SYSTIME registers. */
u64 ptp_base_adj; u64 ptp_base_adj;
u32 tx_hwtstamp_timeouts; u32 tx_hwtstamp_timeouts;
u32 rx_hwtstamp_cleared; u32 rx_hwtstamp_cleared;
u32 latch_event_flags;
spinlock_t ptp_rx_lock; /* Used to protect Rx timestamp registers. */
unsigned long latch_events[4];
bool ptp_tx; bool ptp_tx;
bool ptp_rx; bool ptp_rx;
u16 rss_table_size; /* HW RSS table size */ u16 rss_table_size; /* HW RSS table size */
......
...@@ -226,6 +226,47 @@ static int i40e_ptp_feature_enable(struct ptp_clock_info *ptp, ...@@ -226,6 +226,47 @@ static int i40e_ptp_feature_enable(struct ptp_clock_info *ptp,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
/**
* i40e_ptp_update_latch_events - Read I40E_PRTTSYN_STAT_1 and latch events
* @pf: the PF data structure
*
* This function reads I40E_PRTTSYN_STAT_1 and updates the corresponding timers
* for noticed latch events. This allows the driver to keep track of the first
* time a latch event was noticed which will be used to help clear out Rx
* timestamps for packets that got dropped or lost.
*
* This function will return the current value of I40E_PRTTSYN_STAT_1 and is
* expected to be called only while under the ptp_rx_lock.
**/
static u32 i40e_ptp_get_rx_events(struct i40e_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
u32 prttsyn_stat, new_latch_events;
int i;
prttsyn_stat = rd32(hw, I40E_PRTTSYN_STAT_1);
new_latch_events = prttsyn_stat & ~pf->latch_event_flags;
/* Update the jiffies time for any newly latched timestamp. This
* ensures that we store the time that we first discovered a timestamp
* was latched by the hardware. The service task will later determine
* if we should free the latch and drop that timestamp should too much
* time pass. This flow ensures that we only update jiffies for new
* events latched since the last time we checked, and not all events
* currently latched, so that the service task accounting remains
* accurate.
*/
for (i = 0; i < 4; i++) {
if (new_latch_events & BIT(i))
pf->latch_events[i] = jiffies;
}
/* Finally, we store the current status of the Rx timestamp latches */
pf->latch_event_flags = prttsyn_stat;
return prttsyn_stat;
}
/** /**
* i40e_ptp_rx_hang - Detect error case when Rx timestamp registers are hung * i40e_ptp_rx_hang - Detect error case when Rx timestamp registers are hung
* @vsi: The VSI with the rings relevant to 1588 * @vsi: The VSI with the rings relevant to 1588
...@@ -239,10 +280,7 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi) ...@@ -239,10 +280,7 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi)
{ {
struct i40e_pf *pf = vsi->back; struct i40e_pf *pf = vsi->back;
struct i40e_hw *hw = &pf->hw; struct i40e_hw *hw = &pf->hw;
struct i40e_ring *rx_ring; int i;
unsigned long rx_event;
u32 prttsyn_stat;
int n;
/* Since we cannot turn off the Rx timestamp logic if the device is /* Since we cannot turn off the Rx timestamp logic if the device is
* configured for Tx timestamping, we check if Rx timestamping is * configured for Tx timestamping, we check if Rx timestamping is
...@@ -252,42 +290,30 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi) ...@@ -252,42 +290,30 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi)
if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_rx) if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_rx)
return; return;
prttsyn_stat = rd32(hw, I40E_PRTTSYN_STAT_1); spin_lock_bh(&pf->ptp_rx_lock);
/* Unless all four receive timestamp registers are latched, we are not /* Update current latch times for Rx events */
* concerned about a possible PTP Rx hang, so just update the timeout i40e_ptp_get_rx_events(pf);
* counter and exit.
*/
if (!(prttsyn_stat & ((I40E_PRTTSYN_STAT_1_RXT0_MASK <<
I40E_PRTTSYN_STAT_1_RXT0_SHIFT) |
(I40E_PRTTSYN_STAT_1_RXT1_MASK <<
I40E_PRTTSYN_STAT_1_RXT1_SHIFT) |
(I40E_PRTTSYN_STAT_1_RXT2_MASK <<
I40E_PRTTSYN_STAT_1_RXT2_SHIFT) |
(I40E_PRTTSYN_STAT_1_RXT3_MASK <<
I40E_PRTTSYN_STAT_1_RXT3_SHIFT)))) {
pf->last_rx_ptp_check = jiffies;
return;
}
/* Determine the most recent watchdog or rx_timestamp event. */ /* Check all the currently latched Rx events and see whether they have
rx_event = pf->last_rx_ptp_check; * been latched for over a second. It is assumed that any timestamp
for (n = 0; n < vsi->num_queue_pairs; n++) { * should have been cleared within this time, or else it was captured
rx_ring = vsi->rx_rings[n]; * for a dropped frame that the driver never received. Thus, we will
if (time_after(rx_ring->last_rx_timestamp, rx_event)) * clear any timestamp that has been latched for over 1 second.
rx_event = rx_ring->last_rx_timestamp; */
for (i = 0; i < 4; i++) {
if ((pf->latch_event_flags & BIT(i)) &&
time_is_before_jiffies(pf->latch_events[i] + HZ)) {
rd32(hw, I40E_PRTTSYN_RXTIME_H(i));
pf->latch_event_flags &= ~BIT(i);
pf->rx_hwtstamp_cleared++;
dev_warn(&pf->pdev->dev,
"Clearing a missed Rx timestamp event for RXTIME[%d]\n",
i);
}
} }
/* Only need to read the high RXSTMP register to clear the lock */ spin_unlock_bh(&pf->ptp_rx_lock);
if (time_is_before_jiffies(rx_event + 5 * HZ)) {
rd32(hw, I40E_PRTTSYN_RXTIME_H(0));
rd32(hw, I40E_PRTTSYN_RXTIME_H(1));
rd32(hw, I40E_PRTTSYN_RXTIME_H(2));
rd32(hw, I40E_PRTTSYN_RXTIME_H(3));
pf->last_rx_ptp_check = jiffies;
pf->rx_hwtstamp_cleared++;
WARN_ONCE(1, "Detected Rx timestamp register hang\n");
}
} }
/** /**
...@@ -350,14 +376,25 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct sk_buff *skb, u8 index) ...@@ -350,14 +376,25 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct sk_buff *skb, u8 index)
hw = &pf->hw; hw = &pf->hw;
prttsyn_stat = rd32(hw, I40E_PRTTSYN_STAT_1); spin_lock_bh(&pf->ptp_rx_lock);
/* Get current Rx events and update latch times */
prttsyn_stat = i40e_ptp_get_rx_events(pf);
if (!(prttsyn_stat & BIT(index))) /* TODO: Should we warn about missing Rx timestamp event? */
if (!(prttsyn_stat & BIT(index))) {
spin_unlock_bh(&pf->ptp_rx_lock);
return; return;
}
/* Clear the latched event since we're about to read its register */
pf->latch_event_flags &= ~BIT(index);
lo = rd32(hw, I40E_PRTTSYN_RXTIME_L(index)); lo = rd32(hw, I40E_PRTTSYN_RXTIME_L(index));
hi = rd32(hw, I40E_PRTTSYN_RXTIME_H(index)); hi = rd32(hw, I40E_PRTTSYN_RXTIME_H(index));
spin_unlock_bh(&pf->ptp_rx_lock);
ns = (((u64)hi) << 32) | lo; ns = (((u64)hi) << 32) | lo;
i40e_ptp_convert_to_hwtstamp(skb_hwtstamps(skb), ns); i40e_ptp_convert_to_hwtstamp(skb_hwtstamps(skb), ns);
...@@ -511,12 +548,15 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, ...@@ -511,12 +548,15 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf,
} }
/* Clear out all 1588-related registers to clear and unlatch them. */ /* Clear out all 1588-related registers to clear and unlatch them. */
spin_lock_bh(&pf->ptp_rx_lock);
rd32(hw, I40E_PRTTSYN_STAT_0); rd32(hw, I40E_PRTTSYN_STAT_0);
rd32(hw, I40E_PRTTSYN_TXTIME_H); rd32(hw, I40E_PRTTSYN_TXTIME_H);
rd32(hw, I40E_PRTTSYN_RXTIME_H(0)); rd32(hw, I40E_PRTTSYN_RXTIME_H(0));
rd32(hw, I40E_PRTTSYN_RXTIME_H(1)); rd32(hw, I40E_PRTTSYN_RXTIME_H(1));
rd32(hw, I40E_PRTTSYN_RXTIME_H(2)); rd32(hw, I40E_PRTTSYN_RXTIME_H(2));
rd32(hw, I40E_PRTTSYN_RXTIME_H(3)); rd32(hw, I40E_PRTTSYN_RXTIME_H(3));
pf->latch_event_flags = 0;
spin_unlock_bh(&pf->ptp_rx_lock);
/* Enable/disable the Tx timestamp interrupt based on user input. */ /* Enable/disable the Tx timestamp interrupt based on user input. */
regval = rd32(hw, I40E_PRTTSYN_CTL0); regval = rd32(hw, I40E_PRTTSYN_CTL0);
...@@ -656,6 +696,7 @@ void i40e_ptp_init(struct i40e_pf *pf) ...@@ -656,6 +696,7 @@ void i40e_ptp_init(struct i40e_pf *pf)
} }
mutex_init(&pf->tmreg_lock); mutex_init(&pf->tmreg_lock);
spin_lock_init(&pf->ptp_rx_lock);
/* ensure we have a clock device */ /* ensure we have a clock device */
err = i40e_ptp_create_clock(pf); err = i40e_ptp_create_clock(pf);
......
...@@ -1414,10 +1414,8 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring, ...@@ -1414,10 +1414,8 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring,
u32 tsyn = (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> u32 tsyn = (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >>
I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT; I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT;
if (unlikely(tsynvalid)) { if (unlikely(tsynvalid))
i40e_ptp_rx_hwtstamp(rx_ring->vsi->back, skb, tsyn); i40e_ptp_rx_hwtstamp(rx_ring->vsi->back, skb, tsyn);
rx_ring->last_rx_timestamp = jiffies;
}
i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
......
...@@ -307,8 +307,6 @@ struct i40e_ring { ...@@ -307,8 +307,6 @@ struct i40e_ring {
u8 atr_sample_rate; u8 atr_sample_rate;
u8 atr_count; u8 atr_count;
unsigned long last_rx_timestamp;
bool ring_active; /* is ring online or not */ bool ring_active; /* is ring online or not */
bool arm_wb; /* do something to arm write back */ bool arm_wb; /* do something to arm write back */
u8 packet_stride; u8 packet_stride;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment