igb: Add mechanism for detecting latched hardware Rx timestamp
authorMatthew Vick <matthew.vick@intel.com>
Thu, 13 Dec 2012 07:20:35 +0000 (07:20 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Fri, 18 Jan 2013 12:58:09 +0000 (04:58 -0800)
Add a check against possible Rx timestamp freezing in the hardware via
watchdog mechanism. This situation can occur when an Rx timestamp has been
latched, but the packet has been dropped because the Rx ring is full.

Whenever a packet comes in that should be timestamped, the Rx timestamp
gets latched into the hardware registers and we will store the jiffy value
in the rx_ring. The watchdog will keep track of his own jiffy timer
whenever there is no valid timestamp in the registers.

If the watchdog detects a valid timestamp in the registers, meaning that no
Rx packet has consumed it yet, it will check which time is most recent: the
last time in the watchdog or any time in the rx_rings. If the most recent
"event" was more than 5 seconds ago, it will flush the Rx timestamp and
print a warning message to the syslog.

Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: Matthew Vick <matthew.vick@intel.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Acked-by: Jacob Keller <Jacob.e.keller@intel.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igb/igb_ptp.c

index 8c6f59d95b20d3c4303b9c72a9ce7174a999c495..ad317ab3a34cfa0e996b3e1843c025ff6f4fe589 100644 (file)
@@ -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);
index 6f2579c50d8e5e9fdae17c0cfd5091d77d9f2e5b..3ff37946f899e3582a01f552616ba556aae6c056 100644 (file)
@@ -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) { \
index 9540a814a22594d63a1eda702a765ed8a390d339..c9e438ba1ec2333bd72b38ae1a4294a9427970f7 100644 (file)
@@ -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))
index 086af4683544d70ac9f674c545da163e75f09f45..2ec53d7ca1b7d54b364378be7460dadd6209c74d 100644 (file)
@@ -428,6 +428,51 @@ static void igb_ptp_overflow_check(struct work_struct *work)
                              IGB_SYSTIM_OVERFLOW_PERIOD);
 }
 
+/**
+ * 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.