ixgbe: Add ptp work item to poll for the Tx timestamp
[deliverable/linux.git] / drivers / net / ethernet / intel / ixgbe / ixgbe_ptp.c
index 1a751c9d09c47f319e45d76a9c2bbad8573016d3..33cb8c62935412601e2b39583dcdb118f0b477be 100644 (file)
 #define IXGBE_MAX_TIMEADJ_VALUE  0x7FFFFFFFFFFFFFFFULL
 
 #define IXGBE_OVERFLOW_PERIOD    (HZ * 30)
+#define IXGBE_PTP_TX_TIMEOUT     (HZ * 15)
 
 #ifndef NSECS_PER_SEC
 #define NSECS_PER_SEC 1000000000ULL
 #endif
 
-static struct sock_filter ptp_filter[] = {
-       PTP_FILTER
-};
-
 /**
  * ixgbe_ptp_setup_sdp
  * @hw: the hardware private structure
@@ -405,134 +402,132 @@ void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr)
        }
 }
 
-
 /**
- * ixgbe_ptp_overflow_check - delayed work to detect SYSTIME overflow
- * @work: structure containing information about this work task
+ * ixgbe_ptp_overflow_check - watchdog task to detect SYSTIME overflow
+ * @adapter: private adapter struct
  *
- * this work function is scheduled to continue reading the timecounter
+ * this watchdog task periodically reads the timecounter
  * in order to prevent missing when the system time registers wrap
- * around. This needs to be run approximately twice a minute when no
- * PTP activity is occurring.
+ * around. This needs to be run approximately twice a minute.
  */
 void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter)
 {
-       unsigned long elapsed_jiffies = adapter->last_overflow_check - jiffies;
+       bool timeout = time_is_before_jiffies(adapter->last_overflow_check +
+                                            IXGBE_OVERFLOW_PERIOD);
        struct timespec ts;
 
-       if ((adapter->flags2 & IXGBE_FLAG2_PTP_ENABLED) &&
-           (elapsed_jiffies >= IXGBE_OVERFLOW_PERIOD)) {
+       if (timeout) {
                ixgbe_ptp_gettime(&adapter->ptp_caps, &ts);
                adapter->last_overflow_check = jiffies;
        }
 }
 
 /**
- * ixgbe_ptp_match - determine if this skb matches a ptp packet
- * @skb: pointer to the skb
- * @hwtstamp: pointer to the hwtstamp_config to check
- *
- * Determine whether the skb should have been timestamped, assuming the
- * hwtstamp was set via the hwtstamp ioctl. Returns non-zero when the packet
- * should have a timestamp waiting in the registers, and 0 otherwise.
+ * ixgbe_ptp_rx_hang - detect error case when Rx timestamp registers latched
+ * @adapter: private network adapter structure
  *
- * V1 packets have to check the version type to determine whether they are
- * correct. However, we can't directly access the data because it might be
- * fragmented in the SKB, in paged memory. In order to work around this, we
- * use skb_copy_bits which will properly copy the data whether it is in the
- * paged memory fragments or not. We have to copy the IP header as well as the
- * message type.
+ * 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.
  */
-static int ixgbe_ptp_match(struct sk_buff *skb, int rx_filter)
+void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter)
 {
-       struct iphdr iph;
-       u8 msgtype;
-       unsigned int type, offset;
-
-       if (rx_filter == HWTSTAMP_FILTER_NONE)
-               return 0;
-
-       type = sk_run_filter(skb, ptp_filter);
-
-       if (likely(rx_filter == HWTSTAMP_FILTER_PTP_V2_EVENT))
-               return type & PTP_CLASS_V2;
+       struct ixgbe_hw *hw = &adapter->hw;
+       struct ixgbe_ring *rx_ring;
+       u32 tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
+       unsigned long rx_event;
+       int n;
 
-       /* For the remaining cases actually check message type */
-       switch (type) {
-       case PTP_CLASS_V1_IPV4:
-               skb_copy_bits(skb, OFF_IHL, &iph, sizeof(iph));
-               offset = ETH_HLEN + (iph.ihl << 2) + UDP_HLEN + OFF_PTP_CONTROL;
-               break;
-       case PTP_CLASS_V1_IPV6:
-               offset = OFF_PTP6 + OFF_PTP_CONTROL;
-               break;
-       default:
-               /* other cases invalid or handled above */
-               return 0;
+       /* if we don't have a valid timestamp in the registers, just update the
+        * timeout counter and exit
+        */
+       if (!(tsyncrxctl & IXGBE_TSYNCRXCTL_VALID)) {
+               adapter->last_rx_ptp_check = jiffies;
+               return;
        }
 
-       /* Make sure our buffer is long enough */
-       if (skb->len < offset)
-               return 0;
+       /* 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;
+       }
 
-       skb_copy_bits(skb, offset, &msgtype, sizeof(msgtype));
+       /* only need to read the high RXSTMP register to clear the lock */
+       if (time_is_before_jiffies(rx_event + 5*HZ)) {
+               IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+               adapter->last_rx_ptp_check = jiffies;
 
-       switch (rx_filter) {
-       case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
-               return (msgtype == IXGBE_RXMTRL_V1_SYNC_MSG);
-               break;
-       case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
-               return (msgtype == IXGBE_RXMTRL_V1_DELAY_REQ_MSG);
-               break;
-       default:
-               return 0;
+               e_warn(drv, "clearing RX Timestamp hang");
        }
 }
 
 /**
  * ixgbe_ptp_tx_hwtstamp - utility function which checks for TX time stamp
- * @q_vector: structure containing interrupt and ring information
- * @skb: particular skb to send timestamp with
+ * @adapter: the private adapter struct
  *
  * if the timestamp is valid, we convert it into the timecounter ns
  * value, then store that result into the shhwtstamps structure which
  * is passed up the network stack
  */
-void ixgbe_ptp_tx_hwtstamp(struct ixgbe_q_vector *q_vector,
-                          struct sk_buff *skb)
+static void ixgbe_ptp_tx_hwtstamp(struct ixgbe_adapter *adapter)
 {
-       struct ixgbe_adapter *adapter;
-       struct ixgbe_hw *hw;
+       struct ixgbe_hw *hw = &adapter->hw;
        struct skb_shared_hwtstamps shhwtstamps;
        u64 regval = 0, ns;
-       u32 tsynctxctl;
        unsigned long flags;
 
-       /* we cannot process timestamps on a ring without a q_vector */
-       if (!q_vector || !q_vector->adapter)
-               return;
-
-       adapter = q_vector->adapter;
-       hw = &adapter->hw;
-
-       tsynctxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
        regval |= (u64)IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
        regval |= (u64)IXGBE_READ_REG(hw, IXGBE_TXSTMPH) << 32;
 
-       /*
-        * if TX timestamp is not valid, exit after clearing the
-        * timestamp registers
-        */
-       if (!(tsynctxctl & IXGBE_TSYNCTXCTL_VALID))
-               return;
-
        spin_lock_irqsave(&adapter->tmreg_lock, flags);
        ns = timecounter_cyc2time(&adapter->tc, regval);
        spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
 
        memset(&shhwtstamps, 0, sizeof(shhwtstamps));
        shhwtstamps.hwtstamp = ns_to_ktime(ns);
-       skb_tstamp_tx(skb, &shhwtstamps);
+       skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps);
+
+       dev_kfree_skb_any(adapter->ptp_tx_skb);
+       adapter->ptp_tx_skb = NULL;
+}
+
+/**
+ * ixgbe_ptp_tx_hwtstamp_work
+ * @work: pointer to the work struct
+ *
+ * This work item polls TSYNCTXCTL valid bit to determine when a Tx hardware
+ * timestamp has been taken for the current skb. It is necesary, because the
+ * descriptor's "done" bit does not correlate with the timestamp event.
+ */
+static void ixgbe_ptp_tx_hwtstamp_work(struct work_struct *work)
+{
+       struct ixgbe_adapter *adapter = container_of(work, struct ixgbe_adapter,
+                                                    ptp_tx_work);
+       struct ixgbe_hw *hw = &adapter->hw;
+       bool timeout = time_is_before_jiffies(adapter->ptp_tx_start +
+                                             IXGBE_PTP_TX_TIMEOUT);
+       u32 tsynctxctl;
+
+       /* we have to have a valid skb */
+       if (!adapter->ptp_tx_skb)
+               return;
+
+       if (timeout) {
+               dev_kfree_skb_any(adapter->ptp_tx_skb);
+               adapter->ptp_tx_skb = NULL;
+               e_warn(drv, "clearing Tx Timestamp hang");
+               return;
+       }
+
+       tsynctxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
+       if (tsynctxctl & IXGBE_TSYNCTXCTL_VALID)
+               ixgbe_ptp_tx_hwtstamp(adapter);
+       else
+               /* reschedule to keep checking if it's not available yet */
+               schedule_work(&adapter->ptp_tx_work);
 }
 
 /**
@@ -545,7 +540,7 @@ void ixgbe_ptp_tx_hwtstamp(struct ixgbe_q_vector *q_vector,
  * value, then store that result into the shhwtstamps structure which
  * is passed up the network stack
  */
-void ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector,
+void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring,
                           union ixgbe_adv_rx_desc *rx_desc,
                           struct sk_buff *skb)
 {
@@ -557,43 +552,32 @@ void ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector,
        unsigned long flags;
 
        /* we cannot process timestamps on a ring without a q_vector */
-       if (!q_vector || !q_vector->adapter)
+       if (!rx_ring->q_vector || !rx_ring->q_vector->adapter)
                return;
 
-       adapter = q_vector->adapter;
+       adapter = rx_ring->q_vector->adapter;
        hw = &adapter->hw;
 
-       if (likely(!ixgbe_ptp_match(skb, adapter->rx_hwtstamp_filter)))
+       if (unlikely(!ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS)))
                return;
 
+       /*
+        * Read the tsyncrxctl register afterwards in order to prevent taking an
+        * I/O hit on every packet.
+        */
        tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
-
-       /* Check if we have a valid timestamp and make sure the skb should
-        * have been timestamped */
        if (!(tsyncrxctl & IXGBE_TSYNCRXCTL_VALID))
                return;
 
        /*
-        * Always read the registers, in order to clear a possible fault
-        * because of stagnant RX timestamp values for a packet that never
-        * reached the queue.
+        * Update the last_rx_timestamp timer in order to enable watchdog check
+        * for error case of latched timestamp on a dropped packet.
         */
+       rx_ring->last_rx_timestamp = jiffies;
+
        regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
        regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPH) << 32;
 
-       /*
-        * If the timestamp bit is set in the packet's descriptor, we know the
-        * timestamp belongs to this packet. No other packet can be
-        * timestamped until the registers for timestamping have been read.
-        * Therefor only one packet with this bit can be in the queue at a
-        * time, and the rx timestamp values that were in the registers belong
-        * to this packet.
-        *
-        * If nothing went wrong, then it should have a skb_shared_tx that we
-        * can turn into a skb_shared_hwtstamps.
-        */
-       if (unlikely(!ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS)))
-               return;
 
        spin_lock_irqsave(&adapter->tmreg_lock, flags);
        ns = timecounter_cyc2time(&adapter->tc, regval);
@@ -698,9 +682,6 @@ int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter,
                return 0;
        }
 
-       /* Store filter value for later use */
-       adapter->rx_hwtstamp_filter = config.rx_filter;
-
        /* define ethertype filter for timestamping L2 packets */
        if (is_l2)
                IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -902,11 +883,8 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
                return;
        }
 
-       /* initialize the ptp filter */
-       if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter)))
-               e_dev_warn("ptp_filter_init failed\n");
-
        spin_lock_init(&adapter->tmreg_lock);
+       INIT_WORK(&adapter->ptp_tx_work, ixgbe_ptp_tx_hwtstamp_work);
 
        adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
                                                &adapter->pdev->dev);
@@ -938,6 +916,12 @@ void ixgbe_ptp_stop(struct ixgbe_adapter *adapter)
 
        ixgbe_ptp_setup_sdp(adapter);
 
+       cancel_work_sync(&adapter->ptp_tx_work);
+       if (adapter->ptp_tx_skb) {
+               dev_kfree_skb_any(adapter->ptp_tx_skb);
+               adapter->ptp_tx_skb = NULL;
+       }
+
        if (adapter->ptp_clock) {
                ptp_clock_unregister(adapter->ptp_clock);
                adapter->ptp_clock = NULL;
This page took 0.052927 seconds and 5 git commands to generate.