iwlwifi: pcie: fix a race in firmware loading flow
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Mon, 13 Jun 2016 05:28:26 +0000 (08:28 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Wed, 6 Jul 2016 07:16:12 +0000 (10:16 +0300)
commitf16c3ebfa64fdf0e2dc88e6baa72da95ab70ffd7
tree567536a45f9a61b173ed1cfb08cd14a37d6bc413
parent21cb3222fe569bd09a42771ffc8da9fb4666bd8a
iwlwifi: pcie: fix a race in firmware loading flow

Upon firmware load interrupt (FH_TX), the ISR re-enables the
firmware load interrupt only to avoid races with other
flows as described in the commit below. When the firmware
is completely loaded, the thread that is loading the
firmware will enable all the interrupts to make sure that
the driver gets the ALIVE interrupt.
The problem with that is that the thread that is loading
the firmware is actually racing against the ISR and we can
get to the following situation:

CPU0 CPU1
iwl_pcie_load_given_ucode
...
iwl_pcie_load_firmware_chunk
wait_for_interrupt
<interrupt>
ISR handles CSR_INT_BIT_FH_TX
ISR wakes up the thread on CPU0
/* enable all the interrupts
 * to get the ALIVE interrupt
 */
iwl_enable_interrupts
ISR re-enables CSR_INT_BIT_FH_TX only
/* start the firmware */
iwl_write32(trans, CSR_RESET, 0);

BUG! ALIVE interrupt will never arrive since it has been
masked by CPU1.

In order to fix that, change the ISR to first check if
STATUS_INT_ENABLED is set. If so, re-enable all the
interrupts. If STATUS_INT_ENABLED is clear, then we can
check what specific interrupt happened and re-enable only
that specific interrupt (RFKILL or FH_TX).

All the credit for the analysis goes to Kirtika who did the
actual debugging work.

Cc: <stable@vger.kernel.org> [4.5+]
Fixes: a6bd005fe92 ("iwlwifi: pcie: fix RF-Kill vs. firmware load race")
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/rx.c
drivers/net/wireless/intel/iwlwifi/pcie/trans.c