aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/libertas
diff options
context:
space:
mode:
authorDaniel Drake <dsd@laptop.org>2011-07-09 10:41:25 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-07-11 15:02:19 -0400
commitdf90d84382b03faf81637db31b6be14f477482c7 (patch)
tree343229cb6b303b4e21aa37bd936ae822b94d0528 /drivers/net/wireless/libertas
parent55d990592f83cbfabfefde6e32bf27d4e7493d0c (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')
-rw-r--r--drivers/net/wireless/libertas/cmd.c42
-rw-r--r--drivers/net/wireless/libertas/cmd.h2
-rw-r--r--drivers/net/wireless/libertas/cmdresp.c6
-rw-r--r--drivers/net/wireless/libertas/main.c12
4 files changed, 45 insertions, 17 deletions
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 3e8123cbb948..a515a8fd02ac 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -1069,16 +1069,34 @@ static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
1069 spin_unlock_irqrestore(&priv->driver_lock, flags); 1069 spin_unlock_irqrestore(&priv->driver_lock, flags);
1070} 1070}
1071 1071
1072void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, 1072void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
1073 int result) 1073 int result)
1074{ 1074{
1075 /*
1076 * Normally, commands are removed from cmdpendingq before being
1077 * submitted. However, we can arrive here on alternative codepaths
1078 * where the command is still pending. Make sure the command really
1079 * isn't part of a list at this point.
1080 */
1081 list_del_init(&cmd->list);
1082
1075 cmd->result = result; 1083 cmd->result = result;
1076 cmd->cmdwaitqwoken = 1; 1084 cmd->cmdwaitqwoken = 1;
1077 wake_up_interruptible(&cmd->cmdwait_q); 1085 wake_up(&cmd->cmdwait_q);
1078 1086
1079 if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) 1087 if (!cmd->callback || cmd->callback == lbs_cmd_async_callback)
1080 __lbs_cleanup_and_insert_cmd(priv, cmd); 1088 __lbs_cleanup_and_insert_cmd(priv, cmd);
1081 priv->cur_cmd = NULL; 1089 priv->cur_cmd = NULL;
1090 wake_up_interruptible(&priv->waitq);
1091}
1092
1093void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
1094 int result)
1095{
1096 unsigned long flags;
1097 spin_lock_irqsave(&priv->driver_lock, flags);
1098 __lbs_complete_command(priv, cmd, result);
1099 spin_unlock_irqrestore(&priv->driver_lock, flags);
1082} 1100}
1083 1101
1084int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) 1102int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
@@ -1250,7 +1268,7 @@ static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv)
1250 if (!list_empty(&priv->cmdfreeq)) { 1268 if (!list_empty(&priv->cmdfreeq)) {
1251 tempnode = list_first_entry(&priv->cmdfreeq, 1269 tempnode = list_first_entry(&priv->cmdfreeq,
1252 struct cmd_ctrl_node, list); 1270 struct cmd_ctrl_node, list);
1253 list_del(&tempnode->list); 1271 list_del_init(&tempnode->list);
1254 } else { 1272 } else {
1255 lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); 1273 lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
1256 tempnode = NULL; 1274 tempnode = NULL;
@@ -1358,10 +1376,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
1358 cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { 1376 cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {
1359 lbs_deb_host( 1377 lbs_deb_host(
1360 "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); 1378 "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");
1361 spin_lock_irqsave(&priv->driver_lock, flags);
1362 list_del(&cmdnode->list);
1363 lbs_complete_command(priv, cmdnode, 0); 1379 lbs_complete_command(priv, cmdnode, 0);
1364 spin_unlock_irqrestore(&priv->driver_lock, flags);
1365 1380
1366 ret = 0; 1381 ret = 0;
1367 goto done; 1382 goto done;
@@ -1371,10 +1386,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
1371 (priv->psstate == PS_STATE_PRE_SLEEP)) { 1386 (priv->psstate == PS_STATE_PRE_SLEEP)) {
1372 lbs_deb_host( 1387 lbs_deb_host(
1373 "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); 1388 "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n");
1374 spin_lock_irqsave(&priv->driver_lock, flags);
1375 list_del(&cmdnode->list);
1376 lbs_complete_command(priv, cmdnode, 0); 1389 lbs_complete_command(priv, cmdnode, 0);
1377 spin_unlock_irqrestore(&priv->driver_lock, flags);
1378 priv->needtowakeup = 1; 1390 priv->needtowakeup = 1;
1379 1391
1380 ret = 0; 1392 ret = 0;
@@ -1386,7 +1398,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
1386 } 1398 }
1387 } 1399 }
1388 spin_lock_irqsave(&priv->driver_lock, flags); 1400 spin_lock_irqsave(&priv->driver_lock, flags);
1389 list_del(&cmdnode->list); 1401 list_del_init(&cmdnode->list);
1390 spin_unlock_irqrestore(&priv->driver_lock, flags); 1402 spin_unlock_irqrestore(&priv->driver_lock, flags);
1391 lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", 1403 lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
1392 le16_to_cpu(cmd->command)); 1404 le16_to_cpu(cmd->command));
@@ -1669,7 +1681,13 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command,
1669 } 1681 }
1670 1682
1671 might_sleep(); 1683 might_sleep();
1672 wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); 1684
1685 /*
1686 * Be careful with signals here. A signal may be received as the system
1687 * goes into suspend or resume. We do not want this to interrupt the
1688 * command, so we perform an uninterruptible sleep.
1689 */
1690 wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
1673 1691
1674 spin_lock_irqsave(&priv->driver_lock, flags); 1692 spin_lock_irqsave(&priv->driver_lock, flags);
1675 ret = cmdnode->result; 1693 ret = cmdnode->result;
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h
index 7109d6b717ea..b280ef7a0aea 100644
--- a/drivers/net/wireless/libertas/cmd.h
+++ b/drivers/net/wireless/libertas/cmd.h
@@ -59,6 +59,8 @@ int lbs_allocate_cmd_buffer(struct lbs_private *priv);
59int lbs_free_cmd_buffer(struct lbs_private *priv); 59int lbs_free_cmd_buffer(struct lbs_private *priv);
60 60
61int lbs_execute_next_command(struct lbs_private *priv); 61int lbs_execute_next_command(struct lbs_private *priv);
62void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
63 int result);
62void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, 64void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
63 int result); 65 int result);
64int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); 66int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len);
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index 207fc361db84..7da8949b0511 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -165,7 +165,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
165 lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); 165 lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
166 } 166 }
167 167
168 lbs_complete_command(priv, priv->cur_cmd, result); 168 __lbs_complete_command(priv, priv->cur_cmd, result);
169 spin_unlock_irqrestore(&priv->driver_lock, flags); 169 spin_unlock_irqrestore(&priv->driver_lock, flags);
170 170
171 ret = 0; 171 ret = 0;
@@ -186,7 +186,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
186 break; 186 break;
187 187
188 } 188 }
189 lbs_complete_command(priv, priv->cur_cmd, result); 189 __lbs_complete_command(priv, priv->cur_cmd, result);
190 spin_unlock_irqrestore(&priv->driver_lock, flags); 190 spin_unlock_irqrestore(&priv->driver_lock, flags);
191 191
192 ret = -1; 192 ret = -1;
@@ -204,7 +204,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
204 204
205 if (priv->cur_cmd) { 205 if (priv->cur_cmd) {
206 /* Clean up and Put current command back to cmdfreeq */ 206 /* Clean up and Put current command back to cmdfreeq */
207 lbs_complete_command(priv, priv->cur_cmd, result); 207 __lbs_complete_command(priv, priv->cur_cmd, result);
208 } 208 }
209 spin_unlock_irqrestore(&priv->driver_lock, flags); 209 spin_unlock_irqrestore(&priv->driver_lock, flags);
210 210
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 8c40949cb076..a839de06fa67 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)
638 le16_to_cpu(priv->cur_cmd->cmdbuf->command)); 638 le16_to_cpu(priv->cur_cmd->cmdbuf->command));
639 639
640 priv->cmd_timed_out = 1; 640 priv->cmd_timed_out = 1;
641
642 /*
643 * If the device didn't even acknowledge the command, reset the state
644 * so that we don't block all future commands due to this one timeout.
645 */
646 if (priv->dnld_sent == DNLD_CMD_SENT)
647 priv->dnld_sent = DNLD_RES_RECEIVED;
648
641 wake_up_interruptible(&priv->waitq); 649 wake_up_interruptible(&priv->waitq);
642out: 650out:
643 spin_unlock_irqrestore(&priv->driver_lock, flags); 651 spin_unlock_irqrestore(&priv->driver_lock, flags);
@@ -994,7 +1002,7 @@ void lbs_stop_card(struct lbs_private *priv)
994 list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { 1002 list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
995 cmdnode->result = -ENOENT; 1003 cmdnode->result = -ENOENT;
996 cmdnode->cmdwaitqwoken = 1; 1004 cmdnode->cmdwaitqwoken = 1;
997 wake_up_interruptible(&cmdnode->cmdwait_q); 1005 wake_up(&cmdnode->cmdwait_q);
998 } 1006 }
999 1007
1000 /* Flush the command the card is currently processing */ 1008 /* Flush the command the card is currently processing */
@@ -1002,7 +1010,7 @@ void lbs_stop_card(struct lbs_private *priv)
1002 lbs_deb_main("clearing current command\n"); 1010 lbs_deb_main("clearing current command\n");
1003 priv->cur_cmd->result = -ENOENT; 1011 priv->cur_cmd->result = -ENOENT;
1004 priv->cur_cmd->cmdwaitqwoken = 1; 1012 priv->cur_cmd->cmdwaitqwoken = 1;
1005 wake_up_interruptible(&priv->cur_cmd->cmdwait_q); 1013 wake_up(&priv->cur_cmd->cmdwait_q);
1006 } 1014 }
1007 lbs_deb_main("done clearing commands\n"); 1015 lbs_deb_main("done clearing commands\n");
1008 spin_unlock_irqrestore(&priv->driver_lock, flags); 1016 spin_unlock_irqrestore(&priv->driver_lock, flags);