Commit fa9f3281 authored by Emmanuel Grumbach's avatar Emmanuel Grumbach

iwlwifi: pcie: lock start_hw / start_fw / stop_device

This allows to ensure that we don't have races between them.
A user reported that stop_device was called twice upon
rfkill interrupt after suspend. When the interrupts are
enabled, and right after when we directly check the rfkill
state.
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 012c02c1
...@@ -613,6 +613,7 @@ static int iwl_pci_resume(struct device *device) ...@@ -613,6 +613,7 @@ static int iwl_pci_resume(struct device *device)
{ {
struct pci_dev *pdev = to_pci_dev(device); struct pci_dev *pdev = to_pci_dev(device);
struct iwl_trans *trans = pci_get_drvdata(pdev); struct iwl_trans *trans = pci_get_drvdata(pdev);
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill; bool hw_rfkill;
/* Before you put code here, think about WoWLAN. You cannot check here /* Before you put code here, think about WoWLAN. You cannot check here
...@@ -643,7 +644,10 @@ static int iwl_pci_resume(struct device *device) ...@@ -643,7 +644,10 @@ static int iwl_pci_resume(struct device *device)
} }
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);
return 0; return 0;
} }
......
...@@ -301,6 +301,7 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) ...@@ -301,6 +301,7 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
* @scd_set_active: should the transport configure the SCD for HCMD queue * @scd_set_active: should the transport configure the SCD for HCMD queue
* @rx_page_order: page order for receive buffer size * @rx_page_order: page order for receive buffer size
* @reg_lock: protect hw register access * @reg_lock: protect hw register access
* @mutex: to protect stop_device / start_fw / start_hw
* @cmd_in_flight: true when we have a host command in flight * @cmd_in_flight: true when we have a host command in flight
* @fw_mon_phys: physical address of the buffer for the firmware monitor * @fw_mon_phys: physical address of the buffer for the firmware monitor
* @fw_mon_page: points to the first page of the buffer for the firmware monitor * @fw_mon_page: points to the first page of the buffer for the firmware monitor
...@@ -320,9 +321,11 @@ struct iwl_trans_pcie { ...@@ -320,9 +321,11 @@ struct iwl_trans_pcie {
dma_addr_t ict_tbl_dma; dma_addr_t ict_tbl_dma;
int ict_index; int ict_index;
bool use_ict; bool use_ict;
bool is_down;
struct isr_statistics isr_stats; struct isr_statistics isr_stats;
spinlock_t irq_lock; spinlock_t irq_lock;
struct mutex mutex;
u32 inta_mask; u32 inta_mask;
u32 scd_base_addr; u32 scd_base_addr;
struct iwl_dma_ptr scd_bc_tbls; struct iwl_dma_ptr scd_bc_tbls;
......
...@@ -1251,7 +1251,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) ...@@ -1251,7 +1251,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
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);
if (hw_rfkill) { if (hw_rfkill) {
set_bit(STATUS_RFKILL, &trans->status); set_bit(STATUS_RFKILL, &trans->status);
if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE, if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
......
...@@ -982,13 +982,25 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, ...@@ -982,13 +982,25 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
const struct fw_img *fw, bool run_in_rfkill) const struct fw_img *fw, bool run_in_rfkill)
{ {
int ret; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill; bool hw_rfkill;
int ret;
mutex_lock(&trans_pcie->mutex);
/* Someone called stop_device, don't try to start_fw */
if (trans_pcie->is_down) {
IWL_WARN(trans,
"Can't start_fw since the HW hasn't been started\n");
ret = EIO;
goto out;
}
/* This may fail if AMT took ownership of the device */ /* This may fail if AMT took ownership of the device */
if (iwl_pcie_prepare_card_hw(trans)) { if (iwl_pcie_prepare_card_hw(trans)) {
IWL_WARN(trans, "Exit HW not ready\n"); IWL_WARN(trans, "Exit HW not ready\n");
return -EIO; ret = -EIO;
goto out;
} }
iwl_enable_rfkill_int(trans); iwl_enable_rfkill_int(trans);
...@@ -1000,15 +1012,17 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, ...@@ -1000,15 +1012,17 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
else else
clear_bit(STATUS_RFKILL, &trans->status); clear_bit(STATUS_RFKILL, &trans->status);
iwl_trans_pcie_rf_kill(trans, hw_rfkill); iwl_trans_pcie_rf_kill(trans, hw_rfkill);
if (hw_rfkill && !run_in_rfkill) if (hw_rfkill && !run_in_rfkill) {
return -ERFKILL; ret = -ERFKILL;
goto out;
}
iwl_write32(trans, CSR_INT, 0xFFFFFFFF); iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
ret = iwl_pcie_nic_init(trans); ret = iwl_pcie_nic_init(trans);
if (ret) { if (ret) {
IWL_ERR(trans, "Unable to init nic\n"); IWL_ERR(trans, "Unable to init nic\n");
return ret; goto out;
} }
/* make sure rfkill handshake bits are cleared */ /* make sure rfkill handshake bits are cleared */
...@@ -1026,9 +1040,13 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, ...@@ -1026,9 +1040,13 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
/* Load the given image to the HW */ /* Load the given image to the HW */
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
return iwl_pcie_load_given_ucode_8000(trans, fw); ret = iwl_pcie_load_given_ucode_8000(trans, fw);
else else
return iwl_pcie_load_given_ucode(trans, fw); ret = iwl_pcie_load_given_ucode(trans, fw);
out:
mutex_unlock(&trans_pcie->mutex);
return ret;
} }
static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
...@@ -1037,11 +1055,18 @@ static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) ...@@ -1037,11 +1055,18 @@ static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
iwl_pcie_tx_start(trans, scd_addr); iwl_pcie_tx_start(trans, scd_addr);
} }
static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill, was_hw_rfkill; bool hw_rfkill, was_hw_rfkill;
lockdep_assert_held(&trans_pcie->mutex);
if (trans_pcie->is_down)
return;
trans_pcie->is_down = true;
was_hw_rfkill = iwl_is_rfkill_set(trans); was_hw_rfkill = iwl_is_rfkill_set(trans);
/* tell the device to stop sending interrupts */ /* tell the device to stop sending interrupts */
...@@ -1131,10 +1156,24 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) ...@@ -1131,10 +1156,24 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
iwl_pcie_prepare_card_hw(trans); iwl_pcie_prepare_card_hw(trans);
} }
static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
mutex_lock(&trans_pcie->mutex);
_iwl_trans_pcie_stop_device(trans, low_power);
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)
{ {
struct iwl_trans_pcie __maybe_unused *trans_pcie =
IWL_TRANS_GET_PCIE_TRANS(trans);
lockdep_assert_held(&trans_pcie->mutex);
if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) if (iwl_op_mode_hw_rf_kill(trans->op_mode, state))
iwl_trans_pcie_stop_device(trans, true); _iwl_trans_pcie_stop_device(trans, true);
} }
static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
...@@ -1219,11 +1258,14 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, ...@@ -1219,11 +1258,14 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
return 0; return 0;
} }
static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill; bool hw_rfkill;
int err; int err;
lockdep_assert_held(&trans_pcie->mutex);
err = iwl_pcie_prepare_card_hw(trans); err = iwl_pcie_prepare_card_hw(trans);
if (err) { if (err) {
IWL_ERR(trans, "Error while preparing HW: %d\n", err); IWL_ERR(trans, "Error while preparing HW: %d\n", err);
...@@ -1240,20 +1282,38 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) ...@@ -1240,20 +1282,38 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
/* From now on, the op_mode will be kept updated about RF kill state */ /* From now on, the op_mode will be kept updated about RF kill state */
iwl_enable_rfkill_int(trans); iwl_enable_rfkill_int(trans);
/* Set is_down to false here so that...*/
trans_pcie->is_down = false;
hw_rfkill = iwl_is_rfkill_set(trans); hw_rfkill = iwl_is_rfkill_set(trans);
if (hw_rfkill) if (hw_rfkill)
set_bit(STATUS_RFKILL, &trans->status); set_bit(STATUS_RFKILL, &trans->status);
else else
clear_bit(STATUS_RFKILL, &trans->status); clear_bit(STATUS_RFKILL, &trans->status);
/* ... rfkill can call stop_device and set it false if needed */
iwl_trans_pcie_rf_kill(trans, hw_rfkill); iwl_trans_pcie_rf_kill(trans, hw_rfkill);
return 0; return 0;
} }
static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
mutex_lock(&trans_pcie->mutex);
ret = _iwl_trans_pcie_start_hw(trans, low_power);
mutex_unlock(&trans_pcie->mutex);
return ret;
}
static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
mutex_lock(&trans_pcie->mutex);
/* disable interrupts - don't enable HW RF kill interrupt */ /* disable interrupts - don't enable HW RF kill interrupt */
spin_lock(&trans_pcie->irq_lock); spin_lock(&trans_pcie->irq_lock);
iwl_disable_interrupts(trans); iwl_disable_interrupts(trans);
...@@ -1266,6 +1326,7 @@ static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) ...@@ -1266,6 +1326,7 @@ static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
spin_unlock(&trans_pcie->irq_lock); spin_unlock(&trans_pcie->irq_lock);
iwl_pcie_disable_ict(trans); iwl_pcie_disable_ict(trans);
mutex_unlock(&trans_pcie->mutex);
} }
static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
...@@ -2472,6 +2533,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, ...@@ -2472,6 +2533,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
spin_lock_init(&trans_pcie->irq_lock); spin_lock_init(&trans_pcie->irq_lock);
spin_lock_init(&trans_pcie->reg_lock); spin_lock_init(&trans_pcie->reg_lock);
spin_lock_init(&trans_pcie->ref_lock); spin_lock_init(&trans_pcie->ref_lock);
mutex_init(&trans_pcie->mutex);
init_waitqueue_head(&trans_pcie->ucode_write_waitq); init_waitqueue_head(&trans_pcie->ucode_write_waitq);
err = pci_enable_device(pdev); err = pci_enable_device(pdev);
......
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