iwlwifi: wake from runtime suspend before sending sync commands
authorLuca Coelho <luciano.coelho@intel.com>
Fri, 11 Mar 2016 10:12:16 +0000 (12:12 +0200)
committerLuca Coelho <luciano.coelho@intel.com>
Tue, 10 May 2016 19:14:48 +0000 (22:14 +0300)
If a host command was queued while in runtime suspend, it would go out
before the D0I3_END_CMD was sent.  Sometimes it works, but sometimes
it fails, and it is obviously the wrong thing to do.

To fix this, have the opmode take a reference before sending a SYNC
command and make the pcie trans wait for the runtime state to become
active before actually queueing the command.

Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/utils.c
drivers/net/wireless/intel/iwlwifi/pcie/tx.c

index 362a54601a80505a1d39d8b3521817f7308c8945..513a85403924dcb25ddc8c97d83ea58cbc75af49 100644 (file)
@@ -1309,6 +1309,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
        PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA);
        PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT);
        PRINT_MVM_REF(IWL_MVM_REF_INIT_UCODE);
+       PRINT_MVM_REF(IWL_MVM_REF_SENDING_CMD);
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
index 72c04672143ffa1c7d1efb6ee6c909e429dc0190..23d7539edf179d112e4a19cdece3b236658ad002 100644 (file)
@@ -301,6 +301,7 @@ enum iwl_mvm_ref_type {
        IWL_MVM_REF_PROTECT_CSA,
        IWL_MVM_REF_FW_DBG_COLLECT,
        IWL_MVM_REF_INIT_UCODE,
+       IWL_MVM_REF_SENDING_CMD,
 
        /* update debugfs.c when changing this */
 
index f0ffd62f02d331b820dd5bb11ec05cceba6f87de..eb41d3bd8059f42c23951c303753ae39465f0fad 100644 (file)
@@ -90,11 +90,17 @@ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
         * the mutex, this ensures we don't try to send two
         * (or more) synchronous commands at a time.
         */
-       if (!(cmd->flags & CMD_ASYNC))
+       if (!(cmd->flags & CMD_ASYNC)) {
                lockdep_assert_held(&mvm->mutex);
+               if (!(cmd->flags & CMD_SEND_IN_IDLE))
+                       iwl_mvm_ref(mvm, IWL_MVM_REF_SENDING_CMD);
+       }
 
        ret = iwl_trans_send_cmd(mvm->trans, cmd);
 
+       if (!(cmd->flags & (CMD_ASYNC | CMD_SEND_IN_IDLE)))
+               iwl_mvm_unref(mvm, IWL_MVM_REF_SENDING_CMD);
+
        /*
         * If the caller wants the SKB, then don't hide any problems, the
         * caller might access the response buffer which will be NULL if
index e2eb130dae9818a5cbb7f78c4628a47568f62321..d6beac9af029a8f6b2be7061888d3dedcbd66ce6 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/ieee80211.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/pm_runtime.h>
 #include <net/ip6_checksum.h>
 #include <net/tso.h>
 
@@ -1799,6 +1800,16 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n",
                       iwl_get_cmd_string(trans, cmd->id));
 
+       if (pm_runtime_suspended(&trans_pcie->pci_dev->dev)) {
+               ret = wait_event_timeout(trans_pcie->d0i3_waitq,
+                                pm_runtime_active(&trans_pcie->pci_dev->dev),
+                                msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT));
+               if (!ret) {
+                       IWL_ERR(trans, "Timeout exiting D0i3 before hcmd\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
        cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
        if (cmd_idx < 0) {
                ret = cmd_idx;