| From dd447319895d0c0af423e483d9b63f84f3f8869a Mon Sep 17 00:00:00 2001 |
| From: Stanislav Yakovlev <stas.yakovlev@gmail.com> |
| Date: Thu, 19 Apr 2012 15:55:09 -0400 |
| Subject: ipw2200: Fix race condition in the command completion acknowledge |
| |
| From: Stanislav Yakovlev <stas.yakovlev@gmail.com> |
| |
| 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> |
| |
| --- |
| drivers/net/wireless/ipw2x00/ipw2200.c | 13 ++++++++++++- |
| 1 file changed, 12 insertions(+), 1 deletion(-) |
| |
| --- 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_pri |
| { |
| int rc = 0; |
| unsigned long flags; |
| + unsigned long now, end; |
| |
| spin_lock_irqsave(&priv->lock, flags); |
| if (priv->status & STATUS_HCMD_ACTIVE) { |
| @@ -2223,10 +2224,20 @@ static int __ipw_send_cmd(struct ipw_pri |
| } |
| 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) { |