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>
This commit is contained in:
parent
8db4c7e25d
commit
dd44731989
|
@ -2191,6 +2191,7 @@ static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
unsigned long now, end;
|
||||||
|
|
||||||
spin_lock_irqsave(&priv->lock, flags);
|
spin_lock_irqsave(&priv->lock, flags);
|
||||||
if (priv->status & STATUS_HCMD_ACTIVE) {
|
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);
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
|
||||||
|
now = jiffies;
|
||||||
|
end = now + HOST_COMPLETE_TIMEOUT;
|
||||||
|
again:
|
||||||
rc = wait_event_interruptible_timeout(priv->wait_command_queue,
|
rc = wait_event_interruptible_timeout(priv->wait_command_queue,
|
||||||
!(priv->
|
!(priv->
|
||||||
status & STATUS_HCMD_ACTIVE),
|
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) {
|
if (rc == 0) {
|
||||||
spin_lock_irqsave(&priv->lock, flags);
|
spin_lock_irqsave(&priv->lock, flags);
|
||||||
if (priv->status & STATUS_HCMD_ACTIVE) {
|
if (priv->status & STATUS_HCMD_ACTIVE) {
|
||||||
|
|
Loading…
Reference in New Issue