aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorYogesh Ashok Powar <yogeshp@marvell.com>2011-12-30 06:05:27 -0500
committerJohn W. Linville <linville@tuxdriver.com>2012-01-04 14:31:47 -0500
commit6b6accc3832e5a124eeb144c6b3b1ff65b503d2b (patch)
treeff884be4e816a511b601696ce029977823d0e412 /drivers/net/wireless
parent7f28197560116f08c4c27342974f9e64cab2cbb1 (diff)
mwl8k: Recover from firmware crash
In case of firmware crash, reload the firmware and reconfigure it by triggering ieee80211_hw_restart; mac80211 utility function. V2 Addressed following comments from Lennert: - Stop the queues during reload - Removed atomic_t declaration for hw_restart - Extend the firmware reload support for sta firmware as well - Other misc changes Signed-off-by: Nishant Sarmukadam <nishants@marvell.com> Signed-off-by: Yogesh Ashok Powar <yogeshp@marvell.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/mwl8k.c136
1 files changed, 129 insertions, 7 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 901cd79a061e..cf6927189682 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -198,6 +198,7 @@ struct mwl8k_priv {
198 /* firmware access */ 198 /* firmware access */
199 struct mutex fw_mutex; 199 struct mutex fw_mutex;
200 struct task_struct *fw_mutex_owner; 200 struct task_struct *fw_mutex_owner;
201 struct task_struct *hw_restart_owner;
201 int fw_mutex_depth; 202 int fw_mutex_depth;
202 struct completion *hostcmd_wait; 203 struct completion *hostcmd_wait;
203 204
@@ -262,6 +263,10 @@ struct mwl8k_priv {
262 */ 263 */
263 struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES]; 264 struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES];
264 265
266 /* To perform the task of reloading the firmware */
267 struct work_struct fw_reload;
268 bool hw_restart_in_progress;
269
265 /* async firmware loading state */ 270 /* async firmware loading state */
266 unsigned fw_state; 271 unsigned fw_state;
267 char *fw_pref; 272 char *fw_pref;
@@ -1498,6 +1503,18 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
1498 1503
1499 might_sleep(); 1504 might_sleep();
1500 1505
1506 /* Since fw restart is in progress, allow only the firmware
1507 * commands from the restart code and block the other
1508 * commands since they are going to fail in any case since
1509 * the firmware has crashed
1510 */
1511 if (priv->hw_restart_in_progress) {
1512 if (priv->hw_restart_owner == current)
1513 return 0;
1514 else
1515 return -EBUSY;
1516 }
1517
1501 /* 1518 /*
1502 * The TX queues are stopped at this point, so this test 1519 * The TX queues are stopped at this point, so this test
1503 * doesn't need to take ->tx_lock. 1520 * doesn't need to take ->tx_lock.
@@ -1541,6 +1558,8 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
1541 wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n", 1558 wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
1542 MWL8K_TX_WAIT_TIMEOUT_MS); 1559 MWL8K_TX_WAIT_TIMEOUT_MS);
1543 mwl8k_dump_tx_rings(hw); 1560 mwl8k_dump_tx_rings(hw);
1561 priv->hw_restart_in_progress = true;
1562 ieee80211_queue_work(hw, &priv->fw_reload);
1544 1563
1545 rc = -ETIMEDOUT; 1564 rc = -ETIMEDOUT;
1546 } 1565 }
@@ -2058,7 +2077,9 @@ static int mwl8k_fw_lock(struct ieee80211_hw *hw)
2058 2077
2059 rc = mwl8k_tx_wait_empty(hw); 2078 rc = mwl8k_tx_wait_empty(hw);
2060 if (rc) { 2079 if (rc) {
2061 ieee80211_wake_queues(hw); 2080 if (!priv->hw_restart_in_progress)
2081 ieee80211_wake_queues(hw);
2082
2062 mutex_unlock(&priv->fw_mutex); 2083 mutex_unlock(&priv->fw_mutex);
2063 2084
2064 return rc; 2085 return rc;
@@ -2077,7 +2098,9 @@ static void mwl8k_fw_unlock(struct ieee80211_hw *hw)
2077 struct mwl8k_priv *priv = hw->priv; 2098 struct mwl8k_priv *priv = hw->priv;
2078 2099
2079 if (!--priv->fw_mutex_depth) { 2100 if (!--priv->fw_mutex_depth) {
2080 ieee80211_wake_queues(hw); 2101 if (!priv->hw_restart_in_progress)
2102 ieee80211_wake_queues(hw);
2103
2081 priv->fw_mutex_owner = NULL; 2104 priv->fw_mutex_owner = NULL;
2082 mutex_unlock(&priv->fw_mutex); 2105 mutex_unlock(&priv->fw_mutex);
2083 } 2106 }
@@ -4398,7 +4421,8 @@ static void mwl8k_stop(struct ieee80211_hw *hw)
4398 struct mwl8k_priv *priv = hw->priv; 4421 struct mwl8k_priv *priv = hw->priv;
4399 int i; 4422 int i;
4400 4423
4401 mwl8k_cmd_radio_disable(hw); 4424 if (!priv->hw_restart_in_progress)
4425 mwl8k_cmd_radio_disable(hw);
4402 4426
4403 ieee80211_stop_queues(hw); 4427 ieee80211_stop_queues(hw);
4404 4428
@@ -4499,6 +4523,16 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
4499 return 0; 4523 return 0;
4500} 4524}
4501 4525
4526static void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif)
4527{
4528 /* Has ieee80211_restart_hw re-added the removed interfaces? */
4529 if (!priv->macids_used)
4530 return;
4531
4532 priv->macids_used &= ~(1 << vif->macid);
4533 list_del(&vif->list);
4534}
4535
4502static void mwl8k_remove_interface(struct ieee80211_hw *hw, 4536static void mwl8k_remove_interface(struct ieee80211_hw *hw,
4503 struct ieee80211_vif *vif) 4537 struct ieee80211_vif *vif)
4504{ 4538{
@@ -4510,8 +4544,54 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw,
4510 4544
4511 mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00"); 4545 mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00");
4512 4546
4513 priv->macids_used &= ~(1 << mwl8k_vif->macid); 4547 mwl8k_remove_vif(priv, mwl8k_vif);
4514 list_del(&mwl8k_vif->list); 4548}
4549
4550static void mwl8k_hw_restart_work(struct work_struct *work)
4551{
4552 struct mwl8k_priv *priv =
4553 container_of(work, struct mwl8k_priv, fw_reload);
4554 struct ieee80211_hw *hw = priv->hw;
4555 struct mwl8k_device_info *di;
4556 int rc;
4557
4558 /* If some command is waiting for a response, clear it */
4559 if (priv->hostcmd_wait != NULL) {
4560 complete(priv->hostcmd_wait);
4561 priv->hostcmd_wait = NULL;
4562 }
4563
4564 priv->hw_restart_owner = current;
4565 di = priv->device_info;
4566 mwl8k_fw_lock(hw);
4567
4568 if (priv->ap_fw)
4569 rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
4570 else
4571 rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
4572
4573 if (rc)
4574 goto fail;
4575
4576 priv->hw_restart_owner = NULL;
4577 priv->hw_restart_in_progress = false;
4578
4579 /*
4580 * This unlock will wake up the queues and
4581 * also opens the command path for other
4582 * commands
4583 */
4584 mwl8k_fw_unlock(hw);
4585
4586 ieee80211_restart_hw(hw);
4587
4588 wiphy_err(hw->wiphy, "Firmware restarted successfully\n");
4589
4590 return;
4591fail:
4592 mwl8k_fw_unlock(hw);
4593
4594 wiphy_err(hw->wiphy, "Firmware restart failed\n");
4515} 4595}
4516 4596
4517static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) 4597static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
@@ -5024,7 +5104,11 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
5024 for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { 5104 for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) {
5025 rc = mwl8k_check_ba(hw, stream); 5105 rc = mwl8k_check_ba(hw, stream);
5026 5106
5027 if (!rc) 5107 /* If HW restart is in progress mwl8k_post_cmd will
5108 * return -EBUSY. Avoid retrying mwl8k_check_ba in
5109 * such cases
5110 */
5111 if (!rc || rc == -EBUSY)
5028 break; 5112 break;
5029 /* 5113 /*
5030 * HW queues take time to be flushed, give them 5114 * HW queues take time to be flushed, give them
@@ -5263,12 +5347,15 @@ fail:
5263 mwl8k_release_firmware(priv); 5347 mwl8k_release_firmware(priv);
5264} 5348}
5265 5349
5350#define MAX_RESTART_ATTEMPTS 1
5266static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image, 5351static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
5267 bool nowait) 5352 bool nowait)
5268{ 5353{
5269 struct mwl8k_priv *priv = hw->priv; 5354 struct mwl8k_priv *priv = hw->priv;
5270 int rc; 5355 int rc;
5356 int count = MAX_RESTART_ATTEMPTS;
5271 5357
5358retry:
5272 /* Reset firmware and hardware */ 5359 /* Reset firmware and hardware */
5273 mwl8k_hw_reset(priv); 5360 mwl8k_hw_reset(priv);
5274 5361
@@ -5290,6 +5377,16 @@ static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
5290 /* Reclaim memory once firmware is successfully loaded */ 5377 /* Reclaim memory once firmware is successfully loaded */
5291 mwl8k_release_firmware(priv); 5378 mwl8k_release_firmware(priv);
5292 5379
5380 if (rc && count) {
5381 /* FW did not start successfully;
5382 * lets try one more time
5383 */
5384 count--;
5385 wiphy_err(hw->wiphy, "Trying to reload the firmware again\n");
5386 msleep(20);
5387 goto retry;
5388 }
5389
5293 return rc; 5390 return rc;
5294} 5391}
5295 5392
@@ -5365,7 +5462,14 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)
5365 goto err_free_queues; 5462 goto err_free_queues;
5366 } 5463 }
5367 5464
5368 memset(priv->ampdu, 0, sizeof(priv->ampdu)); 5465 /*
5466 * When hw restart is requested,
5467 * mac80211 will take care of clearing
5468 * the ampdu streams, so do not clear
5469 * the ampdu state here
5470 */
5471 if (!priv->hw_restart_in_progress)
5472 memset(priv->ampdu, 0, sizeof(priv->ampdu));
5369 5473
5370 /* 5474 /*
5371 * Temporarily enable interrupts. Initial firmware host 5475 * Temporarily enable interrupts. Initial firmware host
@@ -5439,10 +5543,20 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
5439{ 5543{
5440 int i, rc = 0; 5544 int i, rc = 0;
5441 struct mwl8k_priv *priv = hw->priv; 5545 struct mwl8k_priv *priv = hw->priv;
5546 struct mwl8k_vif *vif, *tmp_vif;
5442 5547
5443 mwl8k_stop(hw); 5548 mwl8k_stop(hw);
5444 mwl8k_rxq_deinit(hw, 0); 5549 mwl8k_rxq_deinit(hw, 0);
5445 5550
5551 /*
5552 * All the existing interfaces are re-added by the ieee80211_reconfig;
5553 * which means driver should remove existing interfaces before calling
5554 * ieee80211_restart_hw
5555 */
5556 if (priv->hw_restart_in_progress)
5557 list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list)
5558 mwl8k_remove_vif(priv, vif);
5559
5446 for (i = 0; i < mwl8k_tx_queues(priv); i++) 5560 for (i = 0; i < mwl8k_tx_queues(priv); i++)
5447 mwl8k_txq_deinit(hw, i); 5561 mwl8k_txq_deinit(hw, i);
5448 5562
@@ -5454,6 +5568,9 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
5454 if (rc) 5568 if (rc)
5455 goto fail; 5569 goto fail;
5456 5570
5571 if (priv->hw_restart_in_progress)
5572 return rc;
5573
5457 rc = mwl8k_start(hw); 5574 rc = mwl8k_start(hw);
5458 if (rc) 5575 if (rc)
5459 goto fail; 5576 goto fail;
@@ -5524,6 +5641,8 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
5524 INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); 5641 INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
5525 /* Handle watchdog ba events */ 5642 /* Handle watchdog ba events */
5526 INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events); 5643 INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events);
5644 /* To reload the firmware if it crashes */
5645 INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work);
5527 5646
5528 /* TX reclaim and RX tasklets. */ 5647 /* TX reclaim and RX tasklets. */
5529 tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); 5648 tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
@@ -5667,6 +5786,9 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
5667 rc = mwl8k_init_firmware(hw, priv->fw_pref, true); 5786 rc = mwl8k_init_firmware(hw, priv->fw_pref, true);
5668 if (rc) 5787 if (rc)
5669 goto err_stop_firmware; 5788 goto err_stop_firmware;
5789
5790 priv->hw_restart_in_progress = false;
5791
5670 return rc; 5792 return rc;
5671 5793
5672err_stop_firmware: 5794err_stop_firmware: