]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
ice: handle discarding old Tx requests in ice_ptp_tx_tstamp
authorJacob Keller <jacob.e.keller@intel.com>
Mon, 5 Dec 2022 19:52:44 +0000 (11:52 -0800)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Thu, 8 Dec 2022 19:13:21 +0000 (11:13 -0800)
Currently the driver uses the PTP kthread to process handling and
discarding of stale Tx timestamp requests. The function
ice_ptp_tx_tstamp_cleanup is used for this.

A separate thread creates complications for the driver as we now have both
the main Tx timestamp processing IRQ checking timestamps as well as the
kthread.

Rather than using the kthread to handle this, simply check for stale
timestamps within the ice_ptp_tx_tstamp function. This function must
already process the timestamps anyways.

If a Tx timestamp has been waiting for 2 seconds we simply clear the bit
and discard the SKB. This avoids the complication of having separate
threads polling, reducing overall CPU work.

Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Tested-by: Gurucharan G <gurucharanx.g@intel.com> (A Contingent worker at Intel)
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/ice/ice_ptp.c

index 1564c72189bf254adad08fc70b870c41183dee94..726579c0098cd4b3dd7ef552da3b70fe24ace1a1 100644 (file)
@@ -626,15 +626,32 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp)
  * Note that we only take the tracking lock when clearing the bit and when
  * checking if we need to re-queue this task. The only place where bits can be
  * set is the hard xmit routine where an SKB has a request flag set. The only
- * places where we clear bits are this work function, or the periodic cleanup
- * thread. If the cleanup thread clears a bit we're processing we catch it
- * when we lock to clear the bit and then grab the SKB pointer. If a Tx thread
- * starts a new timestamp, we might not begin processing it right away but we
- * will notice it at the end when we re-queue the task. If a Tx thread starts
- * a new timestamp just after this function exits without re-queuing,
- * the interrupt when the timestamp finishes should trigger. Avoiding holding
- * the lock for the entire function is important in order to ensure that Tx
- * threads do not get blocked while waiting for the lock.
+ * places where we clear bits are this work function, or when flushing the Tx
+ * timestamp tracker.
+ *
+ * If the Tx tracker gets flushed while we're processing a packet, we catch
+ * this because we grab the SKB pointer under lock. If the SKB is NULL we know
+ * that another thread already discarded the SKB and we can avoid passing it
+ * up to the stack.
+ *
+ * If a Tx thread starts a new timestamp, we might not begin processing it
+ * right away but we will notice it at the end when we re-queue the task.
+ *
+ * If a Tx thread starts a new timestamp just after this function exits, the
+ * interrupt for that timestamp should re-trigger this function once
+ * a timestamp is ready.
+ *
+ * Note that minimizing the time we hold the lock is important. If we held the
+ * lock for the entire function we would unnecessarily block the Tx hot path
+ * which needs to set the timestamp index. Limiting how long we hold the lock
+ * ensures we do not block Tx threads.
+ *
+ * If a Tx packet has been waiting for more than 2 seconds, it is not possible
+ * to correctly extend the timestamp using the cached PHC time. It is
+ * extremely unlikely that a packet will ever take this long to timestamp. If
+ * we detect a Tx timestamp request that has waited for this long we assume
+ * the packet will never be sent by hardware and discard it without reading
+ * the timestamp register.
  */
 static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
 {
@@ -652,10 +669,21 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
        for_each_set_bit(idx, tx->in_use, tx->len) {
                struct skb_shared_hwtstamps shhwtstamps = {};
                u8 phy_idx = idx + tx->offset;
-               u64 raw_tstamp, tstamp;
+               u64 raw_tstamp = 0, tstamp;
+               bool drop_ts = false;
                struct sk_buff *skb;
                int err;
 
+               /* Drop packets which have waited for more than 2 seconds */
+               if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) {
+                       drop_ts = true;
+
+                       /* Count the number of Tx timestamps that timed out */
+                       pf->ptp.tx_hwtstamp_timeouts++;
+
+                       goto skip_ts_read;
+               }
+
                ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx);
 
                err = ice_read_phy_tstamp(&pf->hw, tx->block, phy_idx,
@@ -670,22 +698,26 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
                    raw_tstamp == tx->tstamps[idx].cached_tstamp)
                        continue;
 
-               /* The timestamp is valid, so we'll go ahead and clear this
-                * index and then send the timestamp up to the stack.
-                */
+skip_ts_read:
                spin_lock(&tx->lock);
-               tx->tstamps[idx].cached_tstamp = raw_tstamp;
+               if (raw_tstamp)
+                       tx->tstamps[idx].cached_tstamp = raw_tstamp;
                clear_bit(idx, tx->in_use);
                skb = tx->tstamps[idx].skb;
                tx->tstamps[idx].skb = NULL;
                spin_unlock(&tx->lock);
 
-               /* it's (unlikely but) possible we raced with the cleanup
-                * thread for discarding old timestamp requests.
+               /* It is unlikely but possible that the SKB will have been
+                * flushed at this point due to link change or teardown.
                 */
                if (!skb)
                        continue;
 
+               if (drop_ts) {
+                       dev_kfree_skb_any(skb);
+                       continue;
+               }
+
                /* Extend the timestamp using cached PHC time */
                tstamp = ice_ptp_extend_40b_ts(pf, raw_tstamp);
                if (tstamp) {
@@ -826,51 +858,6 @@ ice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx)
        return ice_ptp_alloc_tx_tracker(tx);
 }
 
-/**
- * ice_ptp_tx_tstamp_cleanup - Cleanup old timestamp requests that got dropped
- * @pf: pointer to the PF struct
- * @tx: PTP Tx tracker to clean up
- *
- * Loop through the Tx timestamp requests and see if any of them have been
- * waiting for a long time. Discard any SKBs that have been waiting for more
- * than 2 seconds. This is long enough to be reasonably sure that the
- * timestamp will never be captured. This might happen if the packet gets
- * discarded before it reaches the PHY timestamping block.
- */
-static void ice_ptp_tx_tstamp_cleanup(struct ice_pf *pf, struct ice_ptp_tx *tx)
-{
-       struct ice_hw *hw = &pf->hw;
-       u8 idx;
-
-       if (!tx->init)
-               return;
-
-       for_each_set_bit(idx, tx->in_use, tx->len) {
-               struct sk_buff *skb;
-               u64 raw_tstamp;
-
-               /* Check if this SKB has been waiting for too long */
-               if (time_is_after_jiffies(tx->tstamps[idx].start + 2 * HZ))
-                       continue;
-
-               /* Read tstamp to be able to use this register again */
-               ice_read_phy_tstamp(hw, tx->block, idx + tx->offset,
-                                   &raw_tstamp);
-
-               spin_lock(&tx->lock);
-               skb = tx->tstamps[idx].skb;
-               tx->tstamps[idx].skb = NULL;
-               clear_bit(idx, tx->in_use);
-               spin_unlock(&tx->lock);
-
-               /* Count the number of Tx timestamps which have timed out */
-               pf->ptp.tx_hwtstamp_timeouts++;
-
-               /* Free the SKB after we've cleared the bit */
-               dev_kfree_skb_any(skb);
-       }
-}
-
 /**
  * ice_ptp_update_cached_phctime - Update the cached PHC time values
  * @pf: Board specific private structure
@@ -2359,8 +2346,6 @@ static void ice_ptp_periodic_work(struct kthread_work *work)
 
        err = ice_ptp_update_cached_phctime(pf);
 
-       ice_ptp_tx_tstamp_cleanup(pf, &pf->ptp.port.tx);
-
        /* Run twice a second or reschedule if phc update failed */
        kthread_queue_delayed_work(ptp->kworker, &ptp->work,
                                   msecs_to_jiffies(err ? 10 : 500));