Commit 23aeea94 authored by Johannes Berg's avatar Johannes Berg Committed by Luca Coelho

iwlwifi: pcie: fix another RF-kill race

When resuming, it's possible for the following scenario to occur:

 * iwl_pci_resume() enables the RF-kill interrupt
 * iwl_pci_resume() reads the RF-kill state (e.g. to 'radio enabled')
 * RF_KILL interrupt triggers, and iwl_pcie_irq_handler() reads the
   state, now 'radio disabled', and acquires the &trans_pcie->mutex.
 * iwl_pcie_irq_handler() further calls iwl_trans_pcie_rf_kill() to
   indicate to the higher layers that the radio is now disabled (and
   stops the device while at it)
 * iwl_pcie_irq_handler() drops the mutex
 * iwl_pci_resume() continues, acquires the mutex and calls the higher
   layers to indicate that the radio is enabled.

At this point, the device is stopped but the higher layers think it's
available, and can call deeply into the driver to try to enable it.
However, this will fail since the device is actually disabled.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 5594d80e
...@@ -784,13 +784,14 @@ static int iwl_pci_resume(struct device *device) ...@@ -784,13 +784,14 @@ static int iwl_pci_resume(struct device *device)
/* /*
* Enable rfkill interrupt (in order to keep track of * Enable rfkill interrupt (in order to keep track of
* the rfkill status) * the rfkill status). Must be locked to avoid processing
* a possible rfkill interrupt between reading the state
* and calling iwl_trans_pcie_rf_kill() with it.
*/ */
mutex_lock(&trans_pcie->mutex);
iwl_enable_rfkill_int(trans); iwl_enable_rfkill_int(trans);
hw_rfkill = iwl_is_rfkill_set(trans); hw_rfkill = iwl_is_rfkill_set(trans);
mutex_lock(&trans_pcie->mutex);
iwl_trans_pcie_rf_kill(trans, hw_rfkill); iwl_trans_pcie_rf_kill(trans, hw_rfkill);
mutex_unlock(&trans_pcie->mutex); mutex_unlock(&trans_pcie->mutex);
......
...@@ -670,6 +670,8 @@ static inline u8 get_cmd_index(struct iwl_txq *q, u32 index) ...@@ -670,6 +670,8 @@ static inline u8 get_cmd_index(struct iwl_txq *q, u32 index)
static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
{ {
lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->mutex);
return !(iwl_read32(trans, CSR_GP_CNTRL) & return !(iwl_read32(trans, CSR_GP_CNTRL) &
CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW);
} }
......
...@@ -1607,13 +1607,13 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) ...@@ -1607,13 +1607,13 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
if (inta & CSR_INT_BIT_RF_KILL) { if (inta & CSR_INT_BIT_RF_KILL) {
bool hw_rfkill; bool hw_rfkill;
mutex_lock(&trans_pcie->mutex);
hw_rfkill = iwl_is_rfkill_set(trans); hw_rfkill = iwl_is_rfkill_set(trans);
IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
hw_rfkill ? "disable radio" : "enable radio"); hw_rfkill ? "disable radio" : "enable radio");
isr_stats->rfkill++; isr_stats->rfkill++;
mutex_lock(&trans_pcie->mutex);
iwl_trans_pcie_rf_kill(trans, hw_rfkill); iwl_trans_pcie_rf_kill(trans, hw_rfkill);
mutex_unlock(&trans_pcie->mutex); mutex_unlock(&trans_pcie->mutex);
if (hw_rfkill) { if (hw_rfkill) {
...@@ -1952,13 +1952,13 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) ...@@ -1952,13 +1952,13 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
if (inta_hw & MSIX_HW_INT_CAUSES_REG_RF_KILL) { if (inta_hw & MSIX_HW_INT_CAUSES_REG_RF_KILL) {
bool hw_rfkill; bool hw_rfkill;
mutex_lock(&trans_pcie->mutex);
hw_rfkill = iwl_is_rfkill_set(trans); hw_rfkill = iwl_is_rfkill_set(trans);
IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
hw_rfkill ? "disable radio" : "enable radio"); hw_rfkill ? "disable radio" : "enable radio");
isr_stats->rfkill++; isr_stats->rfkill++;
mutex_lock(&trans_pcie->mutex);
iwl_trans_pcie_rf_kill(trans, hw_rfkill); iwl_trans_pcie_rf_kill(trans, hw_rfkill);
mutex_unlock(&trans_pcie->mutex); mutex_unlock(&trans_pcie->mutex);
if (hw_rfkill) { if (hw_rfkill) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment