iwlwifi: pcie: clear only FH bits handle in the interrupt
authorMordechay Goodstein <mordechay.goodstein@intel.com>
Tue, 30 Mar 2021 13:24:57 +0000 (16:24 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Wed, 14 Apr 2021 09:07:16 +0000 (12:07 +0300)
For simplicity we assume that msix has 2 IRQ lines one used for rx data
called msix_non_share, and another used for one bit flags messages
(alive, hw error, sw error, rx data flag) called msix_share.

Every time the FW has data to send it puts it on the RX queue and HW
turns on the flags in msix_share (inta_fw) indicating about rx data,
and HW sends an interrupt a bit later to the msix_non_share _unless_
the msix_shared RX data bit was cleared.

Currently in the code every time we get an msix_shared we clear all bits
including rx data queue bits.

So we can have a race

----------------------------------------------------
DRIVER        |   HW                |   FW
----------------------------------------------------
- send host cmd to FW  |      |
       |      | - handle message
       |      |   and put a response
       |      |   on the RX queue
       | - RX flag on        |
       |            | - send alive msix
       | - alive flag on     |
       | - interrupt         |
       |   msix_share driver |
- handle msix_shared   |      |
  and clear all flags  |      |
  bits        |      |
       | - don't send an     |
       |   interrupt on      |
       |   msix_non_shared   |
       |   (driver cleared)  |
- driver timeout on    |      |
  waiting for host cmd |      |
  respond        |      |
       |      |
----------------------------------------------------

The change is to clear only the msi_shared flags that are handled in
the msix_shared flow, which will cause the hardware to send an interrupt
on the msix_non_share line as well, when it has data.

Signed-off-by: Mordechay Goodstein <mordechay.goodstein@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20210330162204.a1cdda2fa270.I02a82312679f4541f30bb8db8747a797dbb70ee7@changeid
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/iwl-csr.h
drivers/net/wireless/intel/iwlwifi/pcie/rx.c

index 6ccde7e..db312ab 100644 (file)
@@ -578,6 +578,9 @@ enum msix_fh_int_causes {
        MSIX_FH_INT_CAUSES_FH_ERR               = BIT(21),
 };
 
+/* The low 16 bits are for rx data queue indication */
+#define MSIX_FH_INT_CAUSES_DATA_QUEUE 0xffff
+
 /*
  * Causes for the HW register interrupts
  */
index 2bec971..0cbc799 100644 (file)
@@ -2194,9 +2194,16 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
        struct iwl_trans_pcie *trans_pcie = iwl_pcie_get_trans_pcie(entry);
        struct iwl_trans *trans = trans_pcie->trans;
        struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
+       u32 inta_fh_msk = ~MSIX_FH_INT_CAUSES_DATA_QUEUE;
        u32 inta_fh, inta_hw;
        bool polling = false;
 
+       if (trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_NON_RX)
+               inta_fh_msk |= MSIX_FH_INT_CAUSES_Q0;
+
+       if (trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS)
+               inta_fh_msk |= MSIX_FH_INT_CAUSES_Q1;
+
        lock_map_acquire(&trans->sync_cmd_lockdep_map);
 
        spin_lock_bh(&trans_pcie->irq_lock);
@@ -2205,7 +2212,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
        /*
         * Clear causes registers to avoid being handling the same cause.
         */
-       iwl_write32(trans, CSR_MSIX_FH_INT_CAUSES_AD, inta_fh);
+       iwl_write32(trans, CSR_MSIX_FH_INT_CAUSES_AD, inta_fh & inta_fh_msk);
        iwl_write32(trans, CSR_MSIX_HW_INT_CAUSES_AD, inta_hw);
        spin_unlock_bh(&trans_pcie->irq_lock);