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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 10 Jan 2024 16:16:47 +0000 (17:16 +0100)
[ Upstream commit 400f6ebbc175286576c7f7fddf3c347d09d12310 ]

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
Signed-off-by: Sasha Levin <sashal@kernel.org>
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 0f6493d..5602441 100644 (file)
@@ -749,7 +749,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)
 {
@@ -796,7 +796,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 4614ace..a9415d3 100644 (file)
@@ -1785,7 +1785,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;
@@ -1809,7 +1809,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) {
@@ -1949,7 +1949,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;
        }
 
@@ -2366,7 +2366,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 2e23ccd..1bc4a00 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;
 }
@@ -1236,7 +1236,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);
 
@@ -1263,7 +1263,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);
@@ -1453,7 +1454,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)
@@ -1468,12 +1469,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);
@@ -1486,7 +1487,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);
        }
 }
 
@@ -2869,7 +2870,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;
 }