Commit 9439eac7 authored by Johannes Berg's avatar Johannes Berg

iwlwifi: pcie: poke device when commands don't complete quickly

In certain corner cases in the firmware implementation, powersave
transitions can cause the firmware to miss the fact that commands
were added to the queue/FIFO and thus never processes them. Since
the commands really are in the queue, try to poke the firmware in
such cases (by grabbing NIC access, which wakes up the NIC) so it
notices the new command and processes it.
Reviewed-by: default avatarAlexander Bondar <alexander.bondar@intel.com>
Reviewed-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 5a3e9f7f
...@@ -1465,7 +1465,8 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, ...@@ -1465,7 +1465,8 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
spin_unlock_bh(&txq->lock); spin_unlock_bh(&txq->lock);
} }
#define HOST_COMPLETE_TIMEOUT (2 * HZ) #define HOST_COMPLETE_TIMEOUT (2 * HZ)
#define COMMAND_POKE_TIMEOUT (HZ / 10)
static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans,
struct iwl_host_cmd *cmd) struct iwl_host_cmd *cmd)
...@@ -1493,6 +1494,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, ...@@ -1493,6 +1494,7 @@ static int iwl_pcie_send_hcmd_sync(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);
int cmd_idx; int cmd_idx;
int ret; int ret;
int timeout = HOST_COMPLETE_TIMEOUT;
IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
get_cmd_string(trans_pcie, cmd->id)); get_cmd_string(trans_pcie, cmd->id));
...@@ -1517,10 +1519,29 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, ...@@ -1517,10 +1519,29 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
return ret; return ret;
} }
ret = wait_event_timeout(trans_pcie->wait_command_queue, while (timeout > 0) {
!test_bit(STATUS_HCMD_ACTIVE, unsigned long flags;
&trans_pcie->status),
HOST_COMPLETE_TIMEOUT); timeout -= COMMAND_POKE_TIMEOUT;
ret = wait_event_timeout(trans_pcie->wait_command_queue,
!test_bit(STATUS_HCMD_ACTIVE,
&trans_pcie->status),
COMMAND_POKE_TIMEOUT);
if (ret)
break;
/* poke the device - it may have lost the command */
if (iwl_trans_grab_nic_access(trans, true, &flags)) {
iwl_trans_release_nic_access(trans, &flags);
IWL_DEBUG_INFO(trans,
"Tried to wake NIC for command %s\n",
get_cmd_string(trans_pcie, cmd->id));
} else {
IWL_ERR(trans, "Failed to poke NIC for command %s\n",
get_cmd_string(trans_pcie, cmd->id));
break;
}
}
if (!ret) { if (!ret) {
if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) {
struct iwl_txq *txq = struct iwl_txq *txq =
......
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