diff options
-rw-r--r-- | drivers/net/ethernet/intel/igb/igb.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igb/igb_ethtool.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igb/igb_main.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igb/igb_ptp.c | 45 |
4 files changed, 51 insertions, 0 deletions
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 8c6f59d95b2..ad317ab3a34 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -221,6 +221,7 @@ struct igb_ring { struct igb_tx_buffer *tx_buffer_info; struct igb_rx_buffer *rx_buffer_info; }; + unsigned long last_rx_timestamp; void *desc; /* descriptor ring memory */ unsigned long flags; /* ring specific flags */ void __iomem *tail; /* pointer to ring tail register */ @@ -415,10 +416,12 @@ struct igb_adapter { struct work_struct ptp_tx_work; struct sk_buff *ptp_tx_skb; unsigned long ptp_tx_start; + unsigned long last_rx_ptp_check; spinlock_t tmreg_lock; struct cyclecounter cc; struct timecounter tc; u32 tx_hwtstamp_timeouts; + u32 rx_hwtstamp_cleared; char fw_version[32]; #ifdef CONFIG_IGB_HWMON @@ -486,6 +489,7 @@ extern void igb_ptp_init(struct igb_adapter *adapter); extern void igb_ptp_stop(struct igb_adapter *adapter); extern void igb_ptp_reset(struct igb_adapter *adapter); extern void igb_ptp_tx_work(struct work_struct *work); +extern void igb_ptp_rx_hang(struct igb_adapter *adapter); extern void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter); extern void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, struct sk_buff *skb); diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 6f2579c50d8..3ff37946f89 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -93,6 +93,7 @@ static const struct igb_stats igb_gstrings_stats[] = { IGB_STAT("os2bmc_tx_by_host", stats.o2bspc), IGB_STAT("os2bmc_rx_by_host", stats.b2ogprc), IGB_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts), + IGB_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared), }; #define IGB_NETDEV_STAT(_net_stat) { \ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 9540a814a22..c9e438ba1ec 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3991,6 +3991,7 @@ static void igb_watchdog_task(struct work_struct *work) } igb_spoof_check(adapter); + igb_ptp_rx_hang(adapter); /* Reset the timer */ if (!test_bit(__IGB_DOWN, &adapter->state)) diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 086af468354..2ec53d7ca1b 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -429,6 +429,51 @@ static void igb_ptp_overflow_check(struct work_struct *work) } /** + * igb_ptp_rx_hang - detect error case when Rx timestamp registers latched + * @adapter: private network adapter structure + * + * 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. + */ +void igb_ptp_rx_hang(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + struct igb_ring *rx_ring; + u32 tsyncrxctl = rd32(E1000_TSYNCRXCTL); + unsigned long rx_event; + int n; + + if (hw->mac.type != e1000_82576) + return; + + /* If we don't have a valid timestamp in the registers, just update the + * timeout counter and exit + */ + if (!(tsyncrxctl & E1000_TSYNCRXCTL_VALID)) { + adapter->last_rx_ptp_check = jiffies; + return; + } + + /* Determine the most recent watchdog or rx_timestamp event */ + rx_event = adapter->last_rx_ptp_check; + for (n = 0; n < adapter->num_rx_queues; n++) { + rx_ring = adapter->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(E1000_RXSTMPH); + adapter->last_rx_ptp_check = jiffies; + adapter->rx_hwtstamp_cleared++; + dev_warn(&adapter->pdev->dev, "clearing Rx timestamp hang"); + } +} + +/** * igb_ptp_tx_hwtstamp - utility function which checks for TX time stamp * @adapter: Board private structure. * |