aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStanislav Yakovlev <stas.yakovlev@gmail.com>2012-04-19 15:55:09 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-07 11:56:40 -0400
commitb00c5b8d8590a0fe2713a4bee24e9562480ccb9e (patch)
tree18b0ccebf6db040babda8beebedcfb719d20e17a
parentb476e58a834f099aa0f7b4d0a71853e6c61ee6d8 (diff)
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 <stas.yakovlev@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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 87813c33bdc..b2707d733e9 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -2182,6 +2182,7 @@ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
2182{ 2182{
2183 int rc = 0; 2183 int rc = 0;
2184 unsigned long flags; 2184 unsigned long flags;
2185 unsigned long now, end;
2185 2186
2186 spin_lock_irqsave(&priv->lock, flags); 2187 spin_lock_irqsave(&priv->lock, flags);
2187 if (priv->status & STATUS_HCMD_ACTIVE) { 2188 if (priv->status & STATUS_HCMD_ACTIVE) {
@@ -2223,10 +2224,20 @@ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
2223 } 2224 }
2224 spin_unlock_irqrestore(&priv->lock, flags); 2225 spin_unlock_irqrestore(&priv->lock, flags);
2225 2226
2227 now = jiffies;
2228 end = now + HOST_COMPLETE_TIMEOUT;
2229again:
2226 rc = wait_event_interruptible_timeout(priv->wait_command_queue, 2230 rc = wait_event_interruptible_timeout(priv->wait_command_queue,
2227 !(priv-> 2231 !(priv->
2228 status & STATUS_HCMD_ACTIVE), 2232 status & STATUS_HCMD_ACTIVE),
2229 HOST_COMPLETE_TIMEOUT); 2233 end - now);
2234 if (rc < 0) {
2235 now = jiffies;
2236 if (time_before(now, end))
2237 goto again;
2238 rc = 0;
2239 }
2240
2230 if (rc == 0) { 2241 if (rc == 0) {
2231 spin_lock_irqsave(&priv->lock, flags); 2242 spin_lock_irqsave(&priv->lock, flags);
2232 if (priv->status & STATUS_HCMD_ACTIVE) { 2243 if (priv->status & STATUS_HCMD_ACTIVE) {