Merge branch '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next...
authorDavid S. Miller <davem@davemloft.net>
Mon, 12 Jun 2023 07:52:09 +0000 (08:52 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 12 Jun 2023 07:52:09 +0000 (08:52 +0100)
Tony Nguyen says:

====================
ice: Improve miscellaneous interrupt code

Jacob Keller says:

This series improves the driver's use of the threaded IRQ and the
communication between ice_misc_intr() and the ice_misc_intr_thread_fn()
which was previously introduced by commit 1229b33973c7 ("ice: Add low
latency Tx timestamp read").

First, a new custom enumerated return value is used instead of a boolean for
ice_ptp_process_ts(). This significantly reduces the cognitive burden when
reviewing the logic for this function, as the expected action is clear from
the return value name.

Second, the unconditional loop in ice_misc_intr_thread_fn() is removed,
replacing it with a write to the Other Interrupt Cause register. This causes
the MAC to trigger the Tx timestamp interrupt again. This makes it possible
to safely use the ice_misc_intr_thread_fn() to handle other tasks beyond
just the Tx timestamps. It is also easier to reason about since the thread
function will exit cleanly if we do something like disable the interrupt and
call synchronize_irq().

Third, refactor the handling for external timestamp events to use the
miscellaneous thread function. This resolves an issue with the external
time stamps getting blocked while processing the periodic work function
task.

Fourth, a simplification of the ice_misc_intr() function to always return
IRQ_WAKE_THREAD, and schedule the ice service task in the
ice_misc_intr_thread_fn() instead.

Finally, the Other Interrupt Cause is kept disabled over the thread function
processing, rather than immediately re-enabled.

Special thanks to Michal Schmidt for the careful review of the series and
pointing out my misunderstandings of the kernel IRQ code. It has been
determined that the race outlined as being fixed in previous series was
actually introduced by this series itself, which I've since corrected.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_ptp.c
drivers/net/ethernet/intel/ice/ice_ptp.h

index b4bca1d964a949a6a6553e8585ea8b4c13a4a123..4ba3d99439a0ab56e4afa1fd35430247b5fb3892 100644 (file)
@@ -508,6 +508,12 @@ enum ice_pf_flags {
        ICE_PF_FLAGS_NBITS              /* must be last */
 };
 
+enum ice_misc_thread_tasks {
+       ICE_MISC_THREAD_EXTTS_EVENT,
+       ICE_MISC_THREAD_TX_TSTAMP,
+       ICE_MISC_THREAD_NBITS           /* must be last */
+};
+
 struct ice_switchdev_info {
        struct ice_vsi *control_vsi;
        struct ice_vsi *uplink_vsi;
@@ -550,6 +556,7 @@ struct ice_pf {
        DECLARE_BITMAP(features, ICE_F_MAX);
        DECLARE_BITMAP(state, ICE_STATE_NBITS);
        DECLARE_BITMAP(flags, ICE_PF_FLAGS_NBITS);
+       DECLARE_BITMAP(misc_thread, ICE_MISC_THREAD_NBITS);
        unsigned long *avail_txqs;      /* bitmap to track PF Tx queue usage */
        unsigned long *avail_rxqs;      /* bitmap to track PF Rx queue usage */
        unsigned long serv_tmr_period;
index 62e91512aeabcac01612aeb41e7a5c374a0ae086..aa57d26a0ac7d5ef3240df2adddb4dfe46f186b3 100644 (file)
@@ -3058,7 +3058,6 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data)
 {
        struct ice_pf *pf = (struct ice_pf *)data;
        struct ice_hw *hw = &pf->hw;
-       irqreturn_t ret = IRQ_NONE;
        struct device *dev;
        u32 oicr, ena_mask;
 
@@ -3140,19 +3139,24 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data)
        if (oicr & PFINT_OICR_TSYN_TX_M) {
                ena_mask &= ~PFINT_OICR_TSYN_TX_M;
                if (!hw->reset_ongoing)
-                       ret = IRQ_WAKE_THREAD;
+                       set_bit(ICE_MISC_THREAD_TX_TSTAMP, pf->misc_thread);
        }
 
        if (oicr & PFINT_OICR_TSYN_EVNT_M) {
                u8 tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
                u32 gltsyn_stat = rd32(hw, GLTSYN_STAT(tmr_idx));
 
-               /* Save EVENTs from GTSYN register */
-               pf->ptp.ext_ts_irq |= gltsyn_stat & (GLTSYN_STAT_EVENT0_M |
-                                                    GLTSYN_STAT_EVENT1_M |
-                                                    GLTSYN_STAT_EVENT2_M);
                ena_mask &= ~PFINT_OICR_TSYN_EVNT_M;
-               kthread_queue_work(pf->ptp.kworker, &pf->ptp.extts_work);
+
+               if (hw->func_caps.ts_func_info.src_tmr_owned) {
+                       /* Save EVENTs from GLTSYN register */
+                       pf->ptp.ext_ts_irq |= gltsyn_stat &
+                                             (GLTSYN_STAT_EVENT0_M |
+                                              GLTSYN_STAT_EVENT1_M |
+                                              GLTSYN_STAT_EVENT2_M);
+
+                       set_bit(ICE_MISC_THREAD_EXTTS_EVENT, pf->misc_thread);
+               }
        }
 
 #define ICE_AUX_CRIT_ERR (PFINT_OICR_PE_CRITERR_M | PFINT_OICR_HMC_ERR_M | PFINT_OICR_PE_PUSH_M)
@@ -3172,16 +3176,10 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data)
                if (oicr & (PFINT_OICR_PCI_EXCEPTION_M |
                            PFINT_OICR_ECC_ERR_M)) {
                        set_bit(ICE_PFR_REQ, pf->state);
-                       ice_service_task_schedule(pf);
                }
        }
-       if (!ret)
-               ret = IRQ_HANDLED;
 
-       ice_service_task_schedule(pf);
-       ice_irq_dynamic_ena(hw, NULL, NULL);
-
-       return ret;
+       return IRQ_WAKE_THREAD;
 }
 
 /**
@@ -3192,12 +3190,29 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data)
 static irqreturn_t ice_misc_intr_thread_fn(int __always_unused irq, void *data)
 {
        struct ice_pf *pf = data;
+       struct ice_hw *hw;
+
+       hw = &pf->hw;
 
        if (ice_is_reset_in_progress(pf->state))
                return IRQ_HANDLED;
 
-       while (!ice_ptp_process_ts(pf))
-               usleep_range(50, 100);
+       ice_service_task_schedule(pf);
+
+       if (test_and_clear_bit(ICE_MISC_THREAD_EXTTS_EVENT, pf->misc_thread))
+               ice_ptp_extts_event(pf);
+
+       if (test_and_clear_bit(ICE_MISC_THREAD_TX_TSTAMP, pf->misc_thread)) {
+               /* Process outstanding Tx timestamps. If there is more work,
+                * re-arm the interrupt to trigger again.
+                */
+               if (ice_ptp_process_ts(pf) == ICE_TX_TSTAMP_WORK_PENDING) {
+                       wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
+                       ice_flush(hw);
+               }
+       }
+
+       ice_irq_dynamic_ena(hw, NULL, NULL);
 
        return IRQ_HANDLED;
 }
index d4b6c997141dd79a9917888294601e151aac467e..81d96a40d5a74306372b594128eb39c7a049bba6 100644 (file)
@@ -617,7 +617,7 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx)
 }
 
 /**
- * ice_ptp_tx_tstamp - Process Tx timestamps for a port
+ * ice_ptp_process_tx_tstamp - Process Tx timestamps for a port
  * @tx: the PTP Tx timestamp tracker
  *
  * Process timestamps captured by the PHY associated with this port. To do
@@ -633,15 +633,6 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx)
  * 6) extend the 40 bit timestamp value to get a 64 bit timestamp value
  * 7) send this 64 bit timestamp to the stack
  *
- * Returns true if all timestamps were handled, and false if any slots remain
- * without a timestamp.
- *
- * After looping, if we still have waiting SKBs, return false. This may cause
- * us effectively poll even when not strictly necessary. We do this because
- * it's possible a new timestamp was requested around the same time as the
- * interrupt. In some cases hardware might not interrupt us again when the
- * timestamp is captured.
- *
  * Note that we do not hold the tracking lock while reading the Tx timestamp.
  * This is because reading the timestamp requires taking a mutex that might
  * sleep.
@@ -673,10 +664,9 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx)
  * 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)
+static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
 {
        struct ice_ptp_port *ptp_port;
-       bool more_timestamps;
        struct ice_pf *pf;
        struct ice_hw *hw;
        u64 tstamp_ready;
@@ -685,7 +675,7 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
        u8 idx;
 
        if (!tx->init)
-               return true;
+               return;
 
        ptp_port = container_of(tx, struct ice_ptp_port, tx);
        pf = ptp_port_to_pf(ptp_port);
@@ -694,7 +684,7 @@ static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
        /* Read the Tx ready status first */
        err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready);
        if (err)
-               return false;
+               return;
 
        /* Drop packets if the link went down */
        link_up = ptp_port->link_up;
@@ -782,15 +772,34 @@ skip_ts_read:
                skb_tstamp_tx(skb, &shhwtstamps);
                dev_kfree_skb_any(skb);
        }
+}
 
-       /* Check if we still have work to do. If so, re-queue this task to
-        * poll for remaining timestamps.
-        */
+/**
+ * ice_ptp_tx_tstamp - Process Tx timestamps for this function.
+ * @tx: Tx tracking structure to initialize
+ *
+ * Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding incomplete
+ * Tx timestamps, or ICE_TX_TSTAMP_WORK_DONE otherwise.
+ */
+static enum ice_tx_tstamp_work ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
+{
+       bool more_timestamps;
+
+       if (!tx->init)
+               return ICE_TX_TSTAMP_WORK_DONE;
+
+       /* Process the Tx timestamp tracker */
+       ice_ptp_process_tx_tstamp(tx);
+
+       /* Check if there are outstanding Tx timestamps */
        spin_lock(&tx->lock);
        more_timestamps = tx->init && !bitmap_empty(tx->in_use, tx->len);
        spin_unlock(&tx->lock);
 
-       return !more_timestamps;
+       if (more_timestamps)
+               return ICE_TX_TSTAMP_WORK_PENDING;
+
+       return ICE_TX_TSTAMP_WORK_DONE;
 }
 
 /**
@@ -1458,15 +1467,11 @@ static int ice_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
 }
 
 /**
- * ice_ptp_extts_work - Workqueue task function
- * @work: external timestamp work structure
- *
- * Service for PTP external clock event
+ * ice_ptp_extts_event - Process PTP external clock event
+ * @pf: Board private structure
  */
-static void ice_ptp_extts_work(struct kthread_work *work)
+void ice_ptp_extts_event(struct ice_pf *pf)
 {
-       struct ice_ptp *ptp = container_of(work, struct ice_ptp, extts_work);
-       struct ice_pf *pf = container_of(ptp, struct ice_pf, ptp);
        struct ptp_clock_event event;
        struct ice_hw *hw = &pf->hw;
        u8 chan, tmr_idx;
@@ -2430,9 +2435,10 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb)
  * ice_ptp_process_ts - Process the PTP Tx timestamps
  * @pf: Board private structure
  *
- * Returns true if timestamps are processed.
+ * Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding Tx
+ * timestamps that need processing, and ICE_TX_TSTAMP_WORK_DONE otherwise.
  */
-bool ice_ptp_process_ts(struct ice_pf *pf)
+enum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf)
 {
        return ice_ptp_tx_tstamp(&pf->ptp.port.tx);
 }
@@ -2558,7 +2564,6 @@ void ice_ptp_prepare_for_reset(struct ice_pf *pf)
        ice_ptp_cfg_timestamp(pf, false);
 
        kthread_cancel_delayed_work_sync(&ptp->work);
-       kthread_cancel_work_sync(&ptp->extts_work);
 
        if (test_bit(ICE_PFR_REQ, pf->state))
                return;
@@ -2656,7 +2661,6 @@ static int ice_ptp_init_work(struct ice_pf *pf, struct ice_ptp *ptp)
 
        /* Initialize work functions */
        kthread_init_delayed_work(&ptp->work, ice_ptp_periodic_work);
-       kthread_init_work(&ptp->extts_work, ice_ptp_extts_work);
 
        /* Allocate a kworker for handling work required for the ports
         * connected to the PTP hardware clock.
index 9cda2f43e0e562c89291d803d4f43859c67a7394..995a57019ba767ce9349c182a51019308d3afb84 100644 (file)
@@ -108,6 +108,16 @@ struct ice_tx_tstamp {
        u64 cached_tstamp;
 };
 
+/**
+ * enum ice_tx_tstamp_work - Status of Tx timestamp work function
+ * @ICE_TX_TSTAMP_WORK_DONE: Tx timestamp processing is complete
+ * @ICE_TX_TSTAMP_WORK_PENDING: More Tx timestamps are pending
+ */
+enum ice_tx_tstamp_work {
+       ICE_TX_TSTAMP_WORK_DONE = 0,
+       ICE_TX_TSTAMP_WORK_PENDING,
+};
+
 /**
  * struct ice_ptp_tx - Tracking structure for all Tx timestamp requests on a port
  * @lock: lock to prevent concurrent access to fields of this struct
@@ -169,7 +179,6 @@ struct ice_ptp_port {
  * struct ice_ptp - data used for integrating with CONFIG_PTP_1588_CLOCK
  * @port: data for the PHY port initialization procedure
  * @work: delayed work function for periodic tasks
- * @extts_work: work function for handling external Tx timestamps
  * @cached_phc_time: a cached copy of the PHC time for timestamp extension
  * @cached_phc_jiffies: jiffies when cached_phc_time was last updated
  * @ext_ts_chan: the external timestamp channel in use
@@ -190,7 +199,6 @@ struct ice_ptp_port {
 struct ice_ptp {
        struct ice_ptp_port port;
        struct kthread_delayed_work work;
-       struct kthread_work extts_work;
        u64 cached_phc_time;
        unsigned long cached_phc_jiffies;
        u8 ext_ts_chan;
@@ -256,8 +264,9 @@ int ice_ptp_get_ts_config(struct ice_pf *pf, struct ifreq *ifr);
 void ice_ptp_cfg_timestamp(struct ice_pf *pf, bool ena);
 int ice_get_ptp_clock_index(struct ice_pf *pf);
 
+void ice_ptp_extts_event(struct ice_pf *pf);
 s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb);
-bool ice_ptp_process_ts(struct ice_pf *pf);
+enum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf);
 
 void
 ice_ptp_rx_hwtstamp(struct ice_rx_ring *rx_ring,
@@ -284,6 +293,7 @@ static inline int ice_get_ptp_clock_index(struct ice_pf *pf)
        return -1;
 }
 
+static inline void ice_ptp_extts_event(struct ice_pf *pf) { }
 static inline s8
 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb)
 {