From 12427fbf470faaf535915f4efc878eb46cb74c8c Mon Sep 17 00:00:00 2001 From: Stanislav Yakovlev Date: Thu, 19 Apr 2012 15:55:09 -0400 Subject: [PATCH] ipw2200: Fix race condition in the command completion acknowledge commit dd447319895d0c0af423e483d9b63f84f3f8869a upstream. 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. Signed-off-by: Stanislav Yakovlev Signed-off-by: John W. Linville Signed-off-by: Ben Hutchings --- drivers/net/wireless/ipw2x00/ipw2200.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 99a710dfe771..827889bb05ab 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -2183,6 +2183,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) { @@ -2224,10 +2225,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) { -- 2.39.5