diff options
Diffstat (limited to 'drivers/net/ethernet/intel/i40e/i40e_ptp.c')
| -rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_ptp.c | 49 | 
1 files changed, 44 insertions, 5 deletions
| diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 18c1cc08da97..1a0be835fa06 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -269,6 +269,7 @@ static u32 i40e_ptp_get_rx_events(struct i40e_pf *pf)  /**   * i40e_ptp_rx_hang - Detect error case when Rx timestamp registers are hung + * @pf: The PF private data structure   * @vsi: The VSI with the rings relevant to 1588   *   * This watchdog task is scheduled to detect error case where hardware has @@ -276,9 +277,8 @@ static u32 i40e_ptp_get_rx_events(struct i40e_pf *pf)   * particular error is rare but leaves the device in a state unable to timestamp   * any future packets.   **/ -void i40e_ptp_rx_hang(struct i40e_vsi *vsi) +void i40e_ptp_rx_hang(struct i40e_pf *pf)  { -	struct i40e_pf *pf = vsi->back;  	struct i40e_hw *hw = &pf->hw;  	unsigned int i, cleared = 0; @@ -328,6 +328,36 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi)  }  /** + * i40e_ptp_tx_hang - Detect error case when Tx timestamp register is hung + * @pf: The PF private data structure + * + * This watchdog task is run periodically to make sure that we clear the Tx + * timestamp logic if we don't obtain a timestamp in a reasonable amount of + * time. It is unexpected in the normal case but if it occurs it results in + * permanently prevent timestamps of future packets + **/ +void i40e_ptp_tx_hang(struct i40e_pf *pf) +{ +	if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_tx) +		return; + +	/* Nothing to do if we're not already waiting for a timestamp */ +	if (!test_bit(__I40E_PTP_TX_IN_PROGRESS, pf->state)) +		return; + +	/* We already have a handler routine which is run when we are notified +	 * of a Tx timestamp in the hardware. If we don't get an interrupt +	 * within a second it is reasonable to assume that we never will. +	 */ +	if (time_is_before_jiffies(pf->ptp_tx_start + HZ)) { +		dev_kfree_skb_any(pf->ptp_tx_skb); +		pf->ptp_tx_skb = NULL; +		clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state); +		pf->tx_hwtstamp_timeouts++; +	} +} + +/**   * i40e_ptp_tx_hwtstamp - Utility function which returns the Tx timestamp   * @pf: Board private structure   * @@ -338,6 +368,7 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi)  void i40e_ptp_tx_hwtstamp(struct i40e_pf *pf)  {  	struct skb_shared_hwtstamps shhwtstamps; +	struct sk_buff *skb = pf->ptp_tx_skb;  	struct i40e_hw *hw = &pf->hw;  	u32 hi, lo;  	u64 ns; @@ -353,12 +384,19 @@ void i40e_ptp_tx_hwtstamp(struct i40e_pf *pf)  	hi = rd32(hw, I40E_PRTTSYN_TXTIME_H);  	ns = (((u64)hi) << 32) | lo; -  	i40e_ptp_convert_to_hwtstamp(&shhwtstamps, ns); -	skb_tstamp_tx(pf->ptp_tx_skb, &shhwtstamps); -	dev_kfree_skb_any(pf->ptp_tx_skb); + +	/* Clear the bit lock as soon as possible after reading the register, +	 * and prior to notifying the stack via skb_tstamp_tx(). Otherwise +	 * applications might wake up and attempt to request another transmit +	 * timestamp prior to the bit lock being cleared. +	 */  	pf->ptp_tx_skb = NULL;  	clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state); + +	/* Notify the stack and free the skb after we've unlocked */ +	skb_tstamp_tx(skb, &shhwtstamps); +	dev_kfree_skb_any(skb);  }  /** @@ -562,6 +600,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf,  			config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;  		}  		break; +	case HWTSTAMP_FILTER_NTP_ALL:  	case HWTSTAMP_FILTER_ALL:  	default:  		return -ERANGE; | 
