diff options
author | Stanislav Yakovlev <stas.yakovlev@gmail.com> | 2012-04-19 15:55:09 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-04-23 14:57:49 -0400 |
commit | dd447319895d0c0af423e483d9b63f84f3f8869a (patch) | |
tree | 21c7c322adb4b9117c89331a9e61c0e47603b601 /drivers/net/wireless/ipw2x00 | |
parent | 8db4c7e25d153fb049e81715d72fa3be3a0c3b69 (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.c | 13 |
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) | |||
2191 | { | 2191 | { |
2192 | int rc = 0; | 2192 | int rc = 0; |
2193 | unsigned long flags; | 2193 | unsigned long flags; |
2194 | unsigned long now, end; | ||
2194 | 2195 | ||
2195 | spin_lock_irqsave(&priv->lock, flags); | 2196 | spin_lock_irqsave(&priv->lock, flags); |
2196 | if (priv->status & STATUS_HCMD_ACTIVE) { | 2197 | if (priv->status & STATUS_HCMD_ACTIVE) { |
@@ -2232,10 +2233,20 @@ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) | |||
2232 | } | 2233 | } |
2233 | spin_unlock_irqrestore(&priv->lock, flags); | 2234 | spin_unlock_irqrestore(&priv->lock, flags); |
2234 | 2235 | ||
2236 | now = jiffies; | ||
2237 | end = now + HOST_COMPLETE_TIMEOUT; | ||
2238 | again: | ||
2235 | rc = wait_event_interruptible_timeout(priv->wait_command_queue, | 2239 | rc = wait_event_interruptible_timeout(priv->wait_command_queue, |
2236 | !(priv-> | 2240 | !(priv-> |
2237 | status & STATUS_HCMD_ACTIVE), | 2241 | status & STATUS_HCMD_ACTIVE), |
2238 | HOST_COMPLETE_TIMEOUT); | 2242 | end - now); |
2243 | if (rc < 0) { | ||
2244 | now = jiffies; | ||
2245 | if (time_before(now, end)) | ||
2246 | goto again; | ||
2247 | rc = 0; | ||
2248 | } | ||
2249 | |||
2239 | if (rc == 0) { | 2250 | if (rc == 0) { |
2240 | spin_lock_irqsave(&priv->lock, flags); | 2251 | spin_lock_irqsave(&priv->lock, flags); |
2241 | if (priv->status & STATUS_HCMD_ACTIVE) { | 2252 | if (priv->status & STATUS_HCMD_ACTIVE) { |