wifi: iwlwifi: pcie: don't synchronize IRQs from IRQ
authorJohannes Berg <johannes.berg@intel.com>
Fri, 15 Dec 2023 10:13:34 +0000 (11:13 +0100)
committerKalle Valo <kvalo@kernel.org>
Mon, 18 Dec 2023 16:41:48 +0000 (18:41 +0200)
On older devices (before unified image!) we can end up calling
stop_device from an rfkill interrupt. However, in stop_device
we attempt to synchronize IRQs, which then of course deadlocks.

Avoid this by checking the context, if running from the IRQ
thread then don't synchronize. This wouldn't be correct on a
new device since RSS is supported, but older devices only have
a single interrupt/queue.

Fixes: 37fb29bd1f90 ("wifi: iwlwifi: pcie: synchronize IRQs before NAPI")
Reviewed-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://msgid.link/20231215111335.59aab00baed7.Iadfe154d6248e7f9dfd69522e5429dbbd72925d7@changeid
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/rx.c
drivers/net/wireless/intel/iwlwifi/pcie/trans.c

index 56def20374f30cc29eafbeeb2d3a8c43c11d7368..7805a42948afe9992687ab4244bc44d626f51317 100644 (file)
@@ -770,7 +770,7 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
        }
 }
 
-void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans);
+void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq);
 
 static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
 {
@@ -817,7 +817,7 @@ static inline bool iwl_pcie_dbg_on(struct iwl_trans *trans)
        return (trans->dbg.dest_tlv || iwl_trans_dbg_ini_valid(trans));
 }
 
-void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state);
+void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq);
 void iwl_trans_pcie_dump_regs(struct iwl_trans *trans);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
index bc6a9f861711fdf9b2b4c3f57c46d82902b522e3..07931c2db494317f704aba7cbebde62edd61e0d8 100644 (file)
@@ -1783,7 +1783,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
        return inta;
 }
 
-void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans)
+void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
@@ -1807,7 +1807,7 @@ void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans)
        isr_stats->rfkill++;
 
        if (prev != report)
-               iwl_trans_pcie_rf_kill(trans, report);
+               iwl_trans_pcie_rf_kill(trans, report, from_irq);
        mutex_unlock(&trans_pcie->mutex);
 
        if (hw_rfkill) {
@@ -1947,7 +1947,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
        /* HW RF KILL switch toggled */
        if (inta & CSR_INT_BIT_RF_KILL) {
-               iwl_pcie_handle_rfkill_irq(trans);
+               iwl_pcie_handle_rfkill_irq(trans, true);
                handled |= CSR_INT_BIT_RF_KILL;
        }
 
@@ -2370,7 +2370,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
 
        /* HW RF KILL switch toggled */
        if (inta_hw & MSIX_HW_INT_CAUSES_REG_RF_KILL)
-               iwl_pcie_handle_rfkill_irq(trans);
+               iwl_pcie_handle_rfkill_irq(trans, true);
 
        if (inta_hw & MSIX_HW_INT_CAUSES_REG_HW_ERR) {
                IWL_ERR(trans,
index 92253260f56832678cc149f562c6b18910a20655..d10208075ae55fff07a1fe4f1df3e7d88d5a5165 100644 (file)
@@ -1082,7 +1082,7 @@ bool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans)
        report = test_bit(STATUS_RFKILL_OPMODE, &trans->status);
 
        if (prev != report)
-               iwl_trans_pcie_rf_kill(trans, report);
+               iwl_trans_pcie_rf_kill(trans, report, false);
 
        return hw_rfkill;
 }
@@ -1237,7 +1237,7 @@ static void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie)
        trans_pcie->hw_mask = trans_pcie->hw_init_mask;
 }
 
-static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans)
+static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
@@ -1264,7 +1264,8 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
                IWL_DEBUG_INFO(trans,
                               "DEVICE_ENABLED bit was set and is now cleared\n");
-               iwl_pcie_synchronize_irqs(trans);
+               if (!from_irq)
+                       iwl_pcie_synchronize_irqs(trans);
                iwl_pcie_rx_napi_sync(trans);
                iwl_pcie_tx_stop(trans);
                iwl_pcie_rx_stop(trans);
@@ -1454,7 +1455,7 @@ void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans,
                clear_bit(STATUS_RFKILL_OPMODE, &trans->status);
        }
        if (hw_rfkill != was_in_rfkill)
-               iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+               iwl_trans_pcie_rf_kill(trans, hw_rfkill, false);
 }
 
 static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
@@ -1469,12 +1470,12 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        mutex_lock(&trans_pcie->mutex);
        trans_pcie->opmode_down = true;
        was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status);
-       _iwl_trans_pcie_stop_device(trans);
+       _iwl_trans_pcie_stop_device(trans, false);
        iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill);
        mutex_unlock(&trans_pcie->mutex);
 }
 
-void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
+void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq)
 {
        struct iwl_trans_pcie __maybe_unused *trans_pcie =
                IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -1487,7 +1488,7 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
                if (trans->trans_cfg->gen2)
                        _iwl_trans_pcie_gen2_stop_device(trans);
                else
-                       _iwl_trans_pcie_stop_device(trans);
+                       _iwl_trans_pcie_stop_device(trans, from_irq);
        }
 }
 
@@ -2887,7 +2888,7 @@ static ssize_t iwl_dbgfs_rfkill_write(struct file *file,
        IWL_WARN(trans, "changing debug rfkill %d->%d\n",
                 trans_pcie->debug_rfkill, new_value);
        trans_pcie->debug_rfkill = new_value;
-       iwl_pcie_handle_rfkill_irq(trans);
+       iwl_pcie_handle_rfkill_irq(trans, false);
 
        return count;
 }