iwlwifi: enable serialization of synchronous commands
Until now it was only possible to have one synchronous command running at
any time. If a synchronous command is in progress when a second request
arrives then the second command will fail. Create a new mutex specific for
this purpose to only allow one synchronous command at a time, but enable
other commands to wait instead of fail if a synchronous command is in
progress.
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 4157c6c..52b6beb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -3364,6 +3364,7 @@
INIT_LIST_HEAD(&priv->free_frames);
mutex_init(&priv->mutex);
+ mutex_init(&priv->sync_cmd_mutex);
/* Clear the driver's (not device's) station table */
iwl_clear_stations_table(priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 530fae8..6347d4b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -603,7 +603,7 @@
/*************** DRIVER STATUS FUNCTIONS *****/
#define STATUS_HCMD_ACTIVE 0 /* host command in progress */
-#define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */
+/* 1 is unused (used to be STATUS_HCMD_SYNC_ACTIVE) */
#define STATUS_INT_ENABLED 2
#define STATUS_RF_KILL_HW 3
#define STATUS_CT_KILL 4
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 78298be..7241fda 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -530,8 +530,6 @@
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_ACTIVE:\t %d\n",
test_bit(STATUS_HCMD_ACTIVE, &priv->status));
- pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_SYNC_ACTIVE: %d\n",
- test_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INT_ENABLED:\t %d\n",
test_bit(STATUS_INT_ENABLED, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n",
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index f81317d..021c686 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -1111,6 +1111,7 @@
spinlock_t hcmd_lock; /* protect hcmd */
spinlock_t reg_lock; /* protect hw register access */
struct mutex mutex;
+ struct mutex sync_cmd_mutex; /* enable serialization of sync commands */
/* basic pci-network driver stuff */
struct pci_dev *pci_dev;
diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c
index 86783c2..73681c4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c
+++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c
@@ -164,15 +164,13 @@
/* A synchronous command can not have a callback set. */
BUG_ON(cmd->callback);
- if (test_and_set_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status)) {
- IWL_ERR(priv,
- "Error sending %s: Already sending a host command\n",
+ IWL_DEBUG_INFO(priv, "Attempting to send sync command %s\n",
get_cmd_string(cmd->id));
- ret = -EBUSY;
- goto out;
- }
+ mutex_lock(&priv->sync_cmd_mutex);
set_bit(STATUS_HCMD_ACTIVE, &priv->status);
+ IWL_DEBUG_INFO(priv, "Setting HCMD_ACTIVE for command %s \n",
+ get_cmd_string(cmd->id));
cmd_idx = iwl_enqueue_hcmd(priv, cmd);
if (cmd_idx < 0) {
@@ -193,6 +191,8 @@
jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
+ IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s \n",
+ get_cmd_string(cmd->id));
ret = -ETIMEDOUT;
goto cancel;
}
@@ -237,7 +237,7 @@
cmd->reply_page = 0;
}
out:
- clear_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status);
+ mutex_unlock(&priv->sync_cmd_mutex);
return ret;
}
EXPORT_SYMBOL(iwl_send_cmd_sync);
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index d8c11f9..38655ad 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -1238,6 +1238,8 @@
if (!(meta->flags & CMD_ASYNC)) {
clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
+ IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s \n",
+ get_cmd_string(cmd->hdr.cmd));
wake_up_interruptible(&priv->wait_command_queue);
}
}
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 0e5a1ca3..54daa38 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -3847,6 +3847,7 @@
INIT_LIST_HEAD(&priv->free_frames);
mutex_init(&priv->mutex);
+ mutex_init(&priv->sync_cmd_mutex);
/* Clear the driver's (not device's) station table */
iwl_clear_stations_table(priv);