summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ipw2x00
diff options
context:
space:
mode:
authorStanislav Yakovlev <stas.yakovlev@gmail.com>2012-04-19 15:55:09 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-23 14:57:49 -0400
commitdd447319895d0c0af423e483d9b63f84f3f8869a (patch)
tree21c7c322adb4b9117c89331a9e61c0e47603b601 /drivers/net/wireless/ipw2x00
parent8db4c7e25d153fb049e81715d72fa3be3a0c3b69 (diff)
ipw2200: Fix race condition in the command completion acknowledge
Driver incorrectly validates command completion: instead of waiting for a command to be acknowledged it continues execution. Most of the time driver gets acknowledge of the command completion in a tasklet before it executes the next one. But sometimes it sends the next command before it gets acknowledge for the previous one. In such a case one of the following error messages appear in the log: Failed to send SYSTEM_CONFIG: Already sending a command. Failed to send ASSOCIATE: Already sending a command. Failed to send TX_POWER: Already sending a command. After that you need to reload the driver to get it working again. This bug occurs during roaming (reported by Sam Varshavchik) https://bugzilla.redhat.com/show_bug.cgi?id=738508 and machine booting (reported by Tom Gundersen and Mads Kiilerich) https://bugs.archlinux.org/task/28097 https://bugzilla.redhat.com/show_bug.cgi?id=802106 This patch doesn't fix the delay issue during firmware load. But at least device now works as usual after boot. Cc: stable@kernel.org Signed-off-by: Stanislav Yakovlev <stas.yakovlev@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ipw2x00')
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2200.c13
1 files changed, 12 insertions, 1 deletions
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
index 2b022571a859..1779db3aa2b0 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -2191,6 +2191,7 @@ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
{
int rc = 0;
unsigned long flags;
+ unsigned long now, end;
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_HCMD_ACTIVE) {
@@ -2232,10 +2233,20 @@ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
}
spin_unlock_irqrestore(&priv->lock, flags);
+ now = jiffies;
+ end = now + HOST_COMPLETE_TIMEOUT;
+again:
rc = wait_event_interruptible_timeout(priv->wait_command_queue,
!(priv->
status & STATUS_HCMD_ACTIVE),
- HOST_COMPLETE_TIMEOUT);
+ end - now);
+ if (rc < 0) {
+ now = jiffies;
+ if (time_before(now, end))
+ goto again;
+ rc = 0;
+ }
+
if (rc == 0) {
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_HCMD_ACTIVE) {