From: Petr Oros <[email protected]>

When the link goes down the processing loop drops every outstanding
request, and a request whose timestamp is not ready yet is freed
without reading the PHY slot. The hardware completes the capture a
moment later, the orphaned ready bit blocks the port interrupt until
the next link-up sweep, and the freed index can meanwhile be reused
by a new request whose slot the hardware then overwrites. Captured on
a reproducer as ready bits with no in_use owner right after a link
bounce.

Stop dropping on link down. Mark the outstanding requests stale so
their completions are read and discarded, reject new requests while
the link is down, and free a not yet ready slot only after the two
second timeout. This way an index is never reused while the hardware
can still write it and never left untracked while a completion can
still arrive.

To avoid an IRQ storm in the event that we really do have a stale packet
that is not timestamped, modify ice_ptp_tx_tstamps_pending() to ignore
stale timestamps when checking for whether to re-arm the IRQ from the
miscellaneous thread function. Instead, only check for stale packets in the
auxiliary work thread. This way we do not check in a tight loop waiting for
a timestamp that may never come.

This effectively reverts commit fcc2cef37fed ("ice/ptp: fix the PTP worker
retrying indefinitely if the link went down"), which tried to release an
index before this 2 second wait period.

Fixes: fcc2cef37fed ("ice/ptp: fix the PTP worker retrying indefinitely if the 
link went down")
Suggested-by: Jacob Keller <[email protected]>
Signed-off-by: Petr Oros <[email protected]>
Signed-off-by: Jacob Keller <[email protected]>
Reviewed-by: Maciek Machnikowski <[email protected]>
---
 drivers/net/ethernet/intel/ice/ice_ptp.h  |  8 ++++--
 drivers/net/ethernet/intel/ice/ice_main.c |  2 +-
 drivers/net/ethernet/intel/ice/ice_ptp.c  | 44 +++++++++++++++----------------
 3 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h 
b/drivers/net/ethernet/intel/ice/ice_ptp.h
index da2003ba3bb0..13158a9319fb 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.h
@@ -132,6 +132,9 @@ struct ice_ptp_tx {
 #define INDEX_PER_PORT_E82X            16
 #define INDEX_PER_PORT                 64
 
+/* Maximum number of timestamp indexes across all devices */
+#define INDEX_PER_PORT_MAX              INDEX_PER_PORT
+
 /**
  * struct ice_ptp_port - data used to initialize an external port for PTP
  *
@@ -316,7 +319,7 @@ void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 
idx);
 void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx);
 void ice_ptp_process_ts(struct ice_pf *pf);
 irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf);
-bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf);
+bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf, bool in_irq);
 u64 ice_ptp_read_src_clk_reg(struct ice_pf *pf,
                             struct ptp_system_timestamp *sts);
 
@@ -364,7 +367,8 @@ static inline irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf)
        return IRQ_HANDLED;
 }
 
-static inline bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
+static inline bool
+ice_ptp_tx_tstamps_pending(struct ice_pf *pf, bool in_irq)
 {
        return false;
 }
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c 
b/drivers/net/ethernet/intel/ice/ice_main.c
index bb631ae9e67d..c84d63b8d261 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -3248,7 +3248,7 @@ static irqreturn_t ice_misc_intr_thread_fn(int 
__always_unused irq, void *data)
        ice_irq_dynamic_ena(hw, NULL, NULL);
        ice_flush(hw);
 
-       if (ice_ptp_tx_tstamps_pending(pf)) {
+       if (ice_ptp_tx_tstamps_pending(pf, true)) {
                /* If any new Tx timestamps happened while in interrupt,
                 * re-arm the interrupt to trigger it again.
                 */
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c 
b/drivers/net/ethernet/intel/ice/ice_ptp.c
index a049dc7a2241..1a9bf8839404 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -364,9 +364,12 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 
in_tstamp)
 static bool
 ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx)
 {
+       struct ice_ptp_port *ptp_port =
+               container_of(tx, struct ice_ptp_port, tx);
+
        lockdep_assert_held(&tx->lock);
 
-       return tx->init && !tx->calibrating;
+       return tx->init && !tx->calibrating && ptp_port->link_up;
 }
 
 /**
@@ -563,7 +566,6 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
        struct ice_pf *pf;
        struct ice_hw *hw;
        u64 tstamp_ready;
-       bool link_up;
        int err;
        u8 idx;
 
@@ -581,14 +583,11 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx 
*tx)
                        return;
        }
 
-       /* Drop packets if the link went down */
-       link_up = ptp_port->link_up;
-
        for_each_set_bit(idx, tx->in_use, tx->len) {
                struct skb_shared_hwtstamps shhwtstamps = {};
                u8 phy_idx = idx + tx->offset;
                u64 raw_tstamp = 0, tstamp;
-               bool drop_ts = !link_up;
+               bool drop_ts = false;
                struct sk_buff *skb;
 
                /* Drop packets which have waited for more than 2 seconds */
@@ -1372,6 +1371,9 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
        if (pf->hw.reset_ongoing)
                return;
 
+       if (!linkup)
+               ice_ptp_mark_tx_tracker_stale(&ptp_port->tx);
+
        if (hw->mac_type == ICE_MAC_GENERIC_3K_E825 &&
            test_bit(ICE_FLAG_DPLL, pf->flags)) {
                int pin, err;
@@ -2763,28 +2765,29 @@ void ice_ptp_process_ts(struct ice_pf *pf)
        }
 }
 
-static bool ice_port_has_timestamps(struct ice_ptp_tx *tx)
+static bool ice_port_has_timestamps(struct ice_ptp_tx *tx, bool in_irq)
 {
-       bool more_timestamps;
+       DECLARE_BITMAP(tstamps, INDEX_PER_PORT_MAX) = {};
 
        scoped_guard(spinlock_irqsave, &tx->lock) {
                if (!tx->init)
                        return false;
 
-               more_timestamps = !bitmap_empty(tx->in_use, tx->len);
+               if (in_irq)
+                       return bitmap_andnot(tstamps, tx->in_use, tx->stale, 
tx->len);
+               else
+                       return !bitmap_empty(tx->in_use, tx->len);
        }
-
-       return more_timestamps;
 }
 
-static bool ice_any_port_has_timestamps(struct ice_pf *pf)
+static bool ice_any_port_has_timestamps(struct ice_pf *pf, bool in_irq)
 {
        bool have_tstamps = false;
        struct ice_ptp_port *port;
 
        rcu_read_lock();
        list_for_each_entry_rcu(port, &pf->adapter->ports.list, list_node) {
-               if (ice_port_has_timestamps(&port->tx)) {
+               if (ice_port_has_timestamps(&port->tx, in_irq)) {
                        have_tstamps = true;
                        break;
                }
@@ -2794,7 +2797,7 @@ static bool ice_any_port_has_timestamps(struct ice_pf *pf)
        return have_tstamps;
 }
 
-bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
+bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf, bool in_irq)
 {
        struct ice_hw *hw = &pf->hw;
        int ret;
@@ -2804,11 +2807,11 @@ bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
        case ICE_PTP_TX_INTERRUPT_NONE:
                return false;
        case ICE_PTP_TX_INTERRUPT_SELF:
-               if (ice_port_has_timestamps(&pf->ptp.port.tx))
+               if (ice_port_has_timestamps(&pf->ptp.port.tx, in_irq))
                        return true;
                break;
        case ICE_PTP_TX_INTERRUPT_ALL:
-               if (ice_any_port_has_timestamps(pf))
+               if (ice_any_port_has_timestamps(pf, in_irq))
                        return true;
                break;
        default:
@@ -2884,7 +2887,7 @@ irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf)
                /* E830 can read timestamps in the top half using rd32() */
                ice_ptp_process_ts(pf);
 
-               if (ice_ptp_tx_tstamps_pending(pf)) {
+               if (ice_ptp_tx_tstamps_pending(pf, true)) {
                        /* Process outstanding Tx timestamps. If there
                         * is more work, re-arm the interrupt to trigger again.
                         */
@@ -2914,7 +2917,6 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct 
ice_pf *pf)
 {
        struct device *dev = ice_pf_to_dev(pf);
        struct ice_hw *hw = &pf->hw;
-       int ret;
 
        if (!pf->ptp.port.tx.has_ready_bitmap)
                return;
@@ -2922,11 +2924,7 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct 
ice_pf *pf)
        if (!ice_pf_src_tmr_owned(pf))
                return;
 
-       ret = ice_check_phy_tx_tstamp_ready(hw);
-       if (ret < 0) {
-               dev_dbg(dev, "PTP periodic task unable to read PHY timestamp 
ready bitmap, err %d\n",
-                       ret);
-       } else if (ret) {
+       if (ice_ptp_tx_tstamps_pending(pf, false)) {
                dev_dbg(dev, "PTP periodic task detected waiting timestamps. 
Triggering Tx timestamp interrupt now.\n");
 
                wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);

-- 
2.55.0.814.gc42f45431d0f

Reply via email to