iwlwifi: pcie: set STATUS_RFKILL immediately after interrupt
authorGolan Ben Ami <golan.ben.ami@intel.com>
Sun, 11 Dec 2016 15:12:47 +0000 (17:12 +0200)
committerLuca Coelho <luciano.coelho@intel.com>
Wed, 8 Feb 2017 15:54:21 +0000 (17:54 +0200)
Currently, when getting a RFKILL interrupt, the transport enters a flow
in which it stops the device, disables other interrupts, etc. After
stopping the device, the transport resets the hw, and sleeps. During
the sleep, a context switch occurs and host commands are sent by upper
layers (e.g. mvm) to the fw. This is possible since the op_mode layer
and the transport layer hold different mutexes.

Since the STATUS_RFKILL bit isn't set, the transport layer doesn't
recognize that RFKILL was toggled on, and no commands can actually be
sent, so it enqueues the command to the tx queue and sets a timer on
the queue.

After switching context back to stopping the device, STATUS_RFKILL is
set, and then the transport can't send the command to the fw.
This eventually results in a queue hang.

Fix this by setting STATUS_RFKILL immediately when
the interrupt is fired.

Signed-off-by: Golan Ben-Ami <golan.ben.ami@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/pcie/rx.c

index e1bf6da209093e7175bb1ffa26034997427768d2..de94dfdf2ec9972ee7c78e695b26f04f3a022154 100644 (file)
@@ -1609,6 +1609,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
                mutex_lock(&trans_pcie->mutex);
                hw_rfkill = iwl_is_rfkill_set(trans);
+               if (hw_rfkill)
+                       set_bit(STATUS_RFKILL, &trans->status);
+
                IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
                         hw_rfkill ? "disable radio" : "enable radio");
 
@@ -1617,7 +1620,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                iwl_trans_pcie_rf_kill(trans, hw_rfkill);
                mutex_unlock(&trans_pcie->mutex);
                if (hw_rfkill) {
-                       set_bit(STATUS_RFKILL, &trans->status);
                        if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
                                               &trans->status))
                                IWL_DEBUG_RF_KILL(trans,
@@ -1954,6 +1956,9 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
 
                mutex_lock(&trans_pcie->mutex);
                hw_rfkill = iwl_is_rfkill_set(trans);
+               if (hw_rfkill)
+                       set_bit(STATUS_RFKILL, &trans->status);
+
                IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
                         hw_rfkill ? "disable radio" : "enable radio");
 
@@ -1962,7 +1967,6 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
                iwl_trans_pcie_rf_kill(trans, hw_rfkill);
                mutex_unlock(&trans_pcie->mutex);
                if (hw_rfkill) {
-                       set_bit(STATUS_RFKILL, &trans->status);
                        if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
                                               &trans->status))
                                IWL_DEBUG_RF_KILL(trans,