Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dd447319 authored by Stanislav Yakovlev's avatar Stanislav Yakovlev Committed by John W. Linville
Browse files

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: default avatarStanislav Yakovlev <stas.yakovlev@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 8db4c7e2
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -2191,6 +2191,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) {
@@ -2232,10 +2233,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) {