Skip to content

Commit a6bd005

Browse files
committed
iwlwifi: pcie: fix RF-Kill vs. firmware load race
When we load the firmware, we hold trans_pcie->mutex to avoid nested flows. We also rely on the ISR to wake up the thread when the DMA has finished copying a chunk. During this flow, we enable the RF-Kill interrupt. The problem is that the RF-Kill interrupt handler can take the mutex and bring the device down. This means that if we load the firmware while the RF-Kill switch is enabled (which will happen when we load the INIT firmware to read the device's capabilities and register to mac80211), we may get an RF-Kill interrupt immediately and the ISR will be waiting for the mutex held by the thread that is currently loading the firmware. At this stage, the ISR won't be able to service the DMA's interrupt needed to wake up the thread that load the firmware. We are in a deadlock situation which ends when the thread that loads the firmware fails on timeout and releases the mutex. To fix this, take the mutex later in the flow, disable the interrupts and synchronize_irq() to give a chance to the RF-Kill interrupt to run and complete. After that, mask all the interrupts besides the DMA interrupt and proceed with firmware load. Make sure to check that there was no RF-Kill interrupt when the interrupts were disabled. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=111361 Signed-off-by: Emmanuel Grumbach <[email protected]>
1 parent 5e56276 commit a6bd005

File tree

3 files changed

+123
-82
lines changed

3 files changed

+123
-82
lines changed

drivers/net/wireless/intel/iwlwifi/pcie/internal.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,15 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans)
490490
iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
491491
}
492492

493+
static inline void iwl_enable_fw_load_int(struct iwl_trans *trans)
494+
{
495+
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
496+
497+
IWL_DEBUG_ISR(trans, "Enabling FW load interrupt\n");
498+
trans_pcie->inta_mask = CSR_INT_BIT_FH_TX;
499+
iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
500+
}
501+
493502
static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
494503
{
495504
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);

drivers/net/wireless/intel/iwlwifi/pcie/rx.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,9 +1438,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
14381438
inta & ~trans_pcie->inta_mask);
14391439
}
14401440

1441-
/* Re-enable all interrupts */
1442-
/* only Re-enable if disabled by irq */
1443-
if (test_bit(STATUS_INT_ENABLED, &trans->status))
1441+
/* we are loading the firmware, enable FH_TX interrupt only */
1442+
if (handled & CSR_INT_BIT_FH_TX)
1443+
iwl_enable_fw_load_int(trans);
1444+
/* only Re-enable all interrupt if disabled by irq */
1445+
else if (test_bit(STATUS_INT_ENABLED, &trans->status))
14441446
iwl_enable_interrupts(trans);
14451447
/* Re-enable RF_KILL if it occurred */
14461448
else if (handled & CSR_INT_BIT_RF_KILL)

drivers/net/wireless/intel/iwlwifi/pcie/trans.c

Lines changed: 109 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,82 +1021,6 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
10211021
&first_ucode_section);
10221022
}
10231023

1024-
static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
1025-
const struct fw_img *fw, bool run_in_rfkill)
1026-
{
1027-
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
1028-
bool hw_rfkill;
1029-
int ret;
1030-
1031-
mutex_lock(&trans_pcie->mutex);
1032-
1033-
/* Someone called stop_device, don't try to start_fw */
1034-
if (trans_pcie->is_down) {
1035-
IWL_WARN(trans,
1036-
"Can't start_fw since the HW hasn't been started\n");
1037-
ret = EIO;
1038-
goto out;
1039-
}
1040-
1041-
/* This may fail if AMT took ownership of the device */
1042-
if (iwl_pcie_prepare_card_hw(trans)) {
1043-
IWL_WARN(trans, "Exit HW not ready\n");
1044-
ret = -EIO;
1045-
goto out;
1046-
}
1047-
1048-
iwl_enable_rfkill_int(trans);
1049-
1050-
/* If platform's RF_KILL switch is NOT set to KILL */
1051-
hw_rfkill = iwl_is_rfkill_set(trans);
1052-
if (hw_rfkill)
1053-
set_bit(STATUS_RFKILL, &trans->status);
1054-
else
1055-
clear_bit(STATUS_RFKILL, &trans->status);
1056-
iwl_trans_pcie_rf_kill(trans, hw_rfkill);
1057-
if (hw_rfkill && !run_in_rfkill) {
1058-
ret = -ERFKILL;
1059-
goto out;
1060-
}
1061-
1062-
iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
1063-
1064-
ret = iwl_pcie_nic_init(trans);
1065-
if (ret) {
1066-
IWL_ERR(trans, "Unable to init nic\n");
1067-
goto out;
1068-
}
1069-
1070-
/* make sure rfkill handshake bits are cleared */
1071-
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
1072-
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
1073-
CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
1074-
1075-
/* clear (again), then enable host interrupts */
1076-
iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
1077-
iwl_enable_interrupts(trans);
1078-
1079-
/* really make sure rfkill handshake bits are cleared */
1080-
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
1081-
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
1082-
1083-
/* Load the given image to the HW */
1084-
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
1085-
ret = iwl_pcie_load_given_ucode_8000(trans, fw);
1086-
else
1087-
ret = iwl_pcie_load_given_ucode(trans, fw);
1088-
1089-
out:
1090-
mutex_unlock(&trans_pcie->mutex);
1091-
return ret;
1092-
}
1093-
1094-
static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
1095-
{
1096-
iwl_pcie_reset_ict(trans);
1097-
iwl_pcie_tx_start(trans, scd_addr);
1098-
}
1099-
11001024
static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
11011025
{
11021026
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -1127,7 +1051,8 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
11271051
* already dead.
11281052
*/
11291053
if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
1130-
IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
1054+
IWL_DEBUG_INFO(trans,
1055+
"DEVICE_ENABLED bit was set and is now cleared\n");
11311056
iwl_pcie_tx_stop(trans);
11321057
iwl_pcie_rx_stop(trans);
11331058

@@ -1161,7 +1086,6 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
11611086
iwl_disable_interrupts(trans);
11621087
spin_unlock(&trans_pcie->irq_lock);
11631088

1164-
11651089
/* clear all status bits */
11661090
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
11671091
clear_bit(STATUS_INT_ENABLED, &trans->status);
@@ -1194,10 +1118,116 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
11941118
if (hw_rfkill != was_hw_rfkill)
11951119
iwl_trans_pcie_rf_kill(trans, hw_rfkill);
11961120

1197-
/* re-take ownership to prevent other users from stealing the deivce */
1121+
/* re-take ownership to prevent other users from stealing the device */
11981122
iwl_pcie_prepare_card_hw(trans);
11991123
}
12001124

1125+
static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
1126+
const struct fw_img *fw, bool run_in_rfkill)
1127+
{
1128+
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
1129+
bool hw_rfkill;
1130+
int ret;
1131+
1132+
/* This may fail if AMT took ownership of the device */
1133+
if (iwl_pcie_prepare_card_hw(trans)) {
1134+
IWL_WARN(trans, "Exit HW not ready\n");
1135+
ret = -EIO;
1136+
goto out;
1137+
}
1138+
1139+
iwl_enable_rfkill_int(trans);
1140+
1141+
iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
1142+
1143+
/*
1144+
* We enabled the RF-Kill interrupt and the handler may very
1145+
* well be running. Disable the interrupts to make sure no other
1146+
* interrupt can be fired.
1147+
*/
1148+
iwl_disable_interrupts(trans);
1149+
1150+
/* Make sure it finished running */
1151+
synchronize_irq(trans_pcie->pci_dev->irq);
1152+
1153+
mutex_lock(&trans_pcie->mutex);
1154+
1155+
/* If platform's RF_KILL switch is NOT set to KILL */
1156+
hw_rfkill = iwl_is_rfkill_set(trans);
1157+
if (hw_rfkill)
1158+
set_bit(STATUS_RFKILL, &trans->status);
1159+
else
1160+
clear_bit(STATUS_RFKILL, &trans->status);
1161+
iwl_trans_pcie_rf_kill(trans, hw_rfkill);
1162+
if (hw_rfkill && !run_in_rfkill) {
1163+
ret = -ERFKILL;
1164+
goto out;
1165+
}
1166+
1167+
/* Someone called stop_device, don't try to start_fw */
1168+
if (trans_pcie->is_down) {
1169+
IWL_WARN(trans,
1170+
"Can't start_fw since the HW hasn't been started\n");
1171+
ret = EIO;
1172+
goto out;
1173+
}
1174+
1175+
/* make sure rfkill handshake bits are cleared */
1176+
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
1177+
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
1178+
CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
1179+
1180+
/* clear (again), then enable host interrupts */
1181+
iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
1182+
1183+
ret = iwl_pcie_nic_init(trans);
1184+
if (ret) {
1185+
IWL_ERR(trans, "Unable to init nic\n");
1186+
goto out;
1187+
}
1188+
1189+
/*
1190+
* Now, we load the firmware and don't want to be interrupted, even
1191+
* by the RF-Kill interrupt (hence mask all the interrupt besides the
1192+
* FH_TX interrupt which is needed to load the firmware). If the
1193+
* RF-Kill switch is toggled, we will find out after having loaded
1194+
* the firmware and return the proper value to the caller.
1195+
*/
1196+
iwl_enable_fw_load_int(trans);
1197+
1198+
/* really make sure rfkill handshake bits are cleared */
1199+
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
1200+
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
1201+
1202+
/* Load the given image to the HW */
1203+
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
1204+
ret = iwl_pcie_load_given_ucode_8000(trans, fw);
1205+
else
1206+
ret = iwl_pcie_load_given_ucode(trans, fw);
1207+
iwl_enable_interrupts(trans);
1208+
1209+
/* re-check RF-Kill state since we may have missed the interrupt */
1210+
hw_rfkill = iwl_is_rfkill_set(trans);
1211+
if (hw_rfkill)
1212+
set_bit(STATUS_RFKILL, &trans->status);
1213+
else
1214+
clear_bit(STATUS_RFKILL, &trans->status);
1215+
1216+
iwl_trans_pcie_rf_kill(trans, hw_rfkill);
1217+
if (hw_rfkill && !run_in_rfkill)
1218+
ret = -ERFKILL;
1219+
1220+
out:
1221+
mutex_unlock(&trans_pcie->mutex);
1222+
return ret;
1223+
}
1224+
1225+
static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
1226+
{
1227+
iwl_pcie_reset_ict(trans);
1228+
iwl_pcie_tx_start(trans, scd_addr);
1229+
}
1230+
12011231
static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
12021232
{
12031233
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);

0 commit comments

Comments
 (0)