diff options
author | Daniel Drake <dsd@laptop.org> | 2011-07-09 15:41:25 +0100 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-07-11 15:02:19 -0400 |
commit | df90d84382b03faf81637db31b6be14f477482c7 (patch) | |
tree | 343229cb6b303b4e21aa37bd936ae822b94d0528 /drivers/net/wireless/libertas/main.c | |
parent | 55d990592f83cbfabfefde6e32bf27d4e7493d0c (diff) |
libertas: fix handling of command timeout, completion and interruption
When commands time out, corruption ensues. As lbs_complete_command()
is called without locking, the command node is mistakenly freed twice.
Also fixed up locking here in a few other places.
The nature of command timeout may be that the card didn't even
acknowledge receipt of the request. Detect this case and reset dnld_sent
so that other commands don't hang forever.
When cmdnodes are moved between the free list and the pending list,
their list heads should be reinitialized. Fixed this.
Sometimes commands are completed without actually submitting them or
removing them from cmdpendingq. We must remember to remove them from
cmdpendingq in these cases, so handle this in lbs_complete_command().
Harmless signals generated during suspend/resume were interrupting
lbs_cmd. Convert to an uninterruptible sleep to avoid this.
lbs_thread must be woken up every time there is some new work to do.
I found that when 2 commands are queued, ther completion of the first
command would not wake up lbs_thread to submit the second. Poke lbs_thread
at the end of lbs_complete_command() to fix this.
Signed-off-by: Daniel Drake <dsd@laptop.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/main.c')
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 12 |
1 files changed, 10 insertions, 2 deletions
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 8c40949cb07..a839de06fa6 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -638,6 +638,14 @@ static void lbs_cmd_timeout_handler(unsigned long data) le16_to_cpu(priv->cur_cmd->cmdbuf->command)); priv->cmd_timed_out = 1; + + /* + * If the device didn't even acknowledge the command, reset the state + * so that we don't block all future commands due to this one timeout. + */ + if (priv->dnld_sent == DNLD_CMD_SENT) + priv->dnld_sent = DNLD_RES_RECEIVED; + wake_up_interruptible(&priv->waitq); out: spin_unlock_irqrestore(&priv->driver_lock, flags); @@ -994,7 +1002,7 @@ void lbs_stop_card(struct lbs_private *priv) list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { cmdnode->result = -ENOENT; cmdnode->cmdwaitqwoken = 1; - wake_up_interruptible(&cmdnode->cmdwait_q); + wake_up(&cmdnode->cmdwait_q); } /* Flush the command the card is currently processing */ @@ -1002,7 +1010,7 @@ void lbs_stop_card(struct lbs_private *priv) lbs_deb_main("clearing current command\n"); priv->cur_cmd->result = -ENOENT; priv->cur_cmd->cmdwaitqwoken = 1; - wake_up_interruptible(&priv->cur_cmd->cmdwait_q); + wake_up(&priv->cur_cmd->cmdwait_q); } lbs_deb_main("done clearing commands\n"); spin_unlock_irqrestore(&priv->driver_lock, flags); |