aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2012-03-03 15:18:00 -0500
committerLuciano Coelho <coelho@ti.com>2012-03-08 07:25:18 -0500
commit55df5afb13718cda49128fa5985556df91d07765 (patch)
treebb6c9279e12b90af623ba6ea4b17ea85f679e251
parent8ccd16e6cb17a1e961617cc67798bbb222e4cd13 (diff)
wl12xx: implement SW Tx watchdog
Track freed FW blocks during Tx. If no blocks were freed during a predefined timeout, initiate a HW recovery. This helps in situations when the FW watchdog fails. Don't trigger recovery during activities that can temporarily stop Tx. This includes: - scanning - buffering packets for sleeping stations (AP role) - ROC on any role Signed-off-by: Arik Nemtsov <arik@wizery.com> Signed-off-by: Eliad Peller <eliad@wizery.com> Signed-off-by: Luciano Coelho <coelho@ti.com>
-rw-r--r--drivers/net/wireless/wl12xx/cmd.c8
-rw-r--r--drivers/net/wireless/wl12xx/conf.h3
-rw-r--r--drivers/net/wireless/wl12xx/main.c110
-rw-r--r--drivers/net/wireless/wl12xx/scan.c6
-rw-r--r--drivers/net/wireless/wl12xx/tx.c4
-rw-r--r--drivers/net/wireless/wl12xx/tx.h1
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx.h3
7 files changed, 135 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index 1ef212f6440f..3414fc11e9ba 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -1818,6 +1818,14 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id)
1818 goto out; 1818 goto out;
1819 1819
1820 __clear_bit(role_id, wl->roc_map); 1820 __clear_bit(role_id, wl->roc_map);
1821
1822 /*
1823 * Rearm the tx watchdog when removing the last ROC. This prevents
1824 * recoveries due to just finished ROCs - when Tx hasn't yet had
1825 * a chance to get out.
1826 */
1827 if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES)
1828 wl12xx_rearm_tx_watchdog_locked(wl);
1821out: 1829out:
1822 return ret; 1830 return ret;
1823} 1831}
diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h
index cc50faaf03d1..3e581e19424c 100644
--- a/drivers/net/wireless/wl12xx/conf.h
+++ b/drivers/net/wireless/wl12xx/conf.h
@@ -690,6 +690,9 @@ struct conf_tx_settings {
690 */ 690 */
691 u8 tmpl_short_retry_limit; 691 u8 tmpl_short_retry_limit;
692 u8 tmpl_long_retry_limit; 692 u8 tmpl_long_retry_limit;
693
694 /* Time in ms for Tx watchdog timer to expire */
695 u32 tx_watchdog_timeout;
693}; 696};
694 697
695enum { 698enum {
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 95a76a5f9eeb..39002363611e 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -217,6 +217,7 @@ static struct conf_drv_settings default_conf = {
217 .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, 217 .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
218 .tmpl_short_retry_limit = 10, 218 .tmpl_short_retry_limit = 10,
219 .tmpl_long_retry_limit = 10, 219 .tmpl_long_retry_limit = 10,
220 .tx_watchdog_timeout = 5000,
220 }, 221 },
221 .conn = { 222 .conn = {
222 .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, 223 .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
@@ -553,6 +554,80 @@ static void wl1271_rx_streaming_timer(unsigned long data)
553 ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work); 554 ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
554} 555}
555 556
557/* wl->mutex must be taken */
558void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl)
559{
560 /* if the watchdog is not armed, don't do anything */
561 if (wl->tx_allocated_blocks == 0)
562 return;
563
564 cancel_delayed_work(&wl->tx_watchdog_work);
565 ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work,
566 msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));
567}
568
569static void wl12xx_tx_watchdog_work(struct work_struct *work)
570{
571 struct delayed_work *dwork;
572 struct wl1271 *wl;
573
574 dwork = container_of(work, struct delayed_work, work);
575 wl = container_of(dwork, struct wl1271, tx_watchdog_work);
576
577 mutex_lock(&wl->mutex);
578
579 if (unlikely(wl->state == WL1271_STATE_OFF))
580 goto out;
581
582 /* Tx went out in the meantime - everything is ok */
583 if (unlikely(wl->tx_allocated_blocks == 0))
584 goto out;
585
586 /*
587 * if a ROC is in progress, we might not have any Tx for a long
588 * time (e.g. pending Tx on the non-ROC channels)
589 */
590 if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
591 wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC",
592 wl->conf.tx.tx_watchdog_timeout);
593 wl12xx_rearm_tx_watchdog_locked(wl);
594 goto out;
595 }
596
597 /*
598 * if a scan is in progress, we might not have any Tx for a long
599 * time
600 */
601 if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
602 wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan",
603 wl->conf.tx.tx_watchdog_timeout);
604 wl12xx_rearm_tx_watchdog_locked(wl);
605 goto out;
606 }
607
608 /*
609 * AP might cache a frame for a long time for a sleeping station,
610 * so rearm the timer if there's an AP interface with stations. If
611 * Tx is genuinely stuck we will most hopefully discover it when all
612 * stations are removed due to inactivity.
613 */
614 if (wl->active_sta_count) {
615 wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has "
616 " %d stations",
617 wl->conf.tx.tx_watchdog_timeout,
618 wl->active_sta_count);
619 wl12xx_rearm_tx_watchdog_locked(wl);
620 goto out;
621 }
622
623 wl1271_error("Tx stuck (in FW) for %d ms. Starting recovery",
624 wl->conf.tx.tx_watchdog_timeout);
625 wl12xx_queue_recovery_work(wl);
626
627out:
628 mutex_unlock(&wl->mutex);
629}
630
556static void wl1271_conf_init(struct wl1271 *wl) 631static void wl1271_conf_init(struct wl1271 *wl)
557{ 632{
558 633
@@ -745,6 +820,18 @@ static void wl12xx_fw_status(struct wl1271 *wl,
745 820
746 wl->tx_allocated_blocks -= freed_blocks; 821 wl->tx_allocated_blocks -= freed_blocks;
747 822
823 /*
824 * If the FW freed some blocks:
825 * If we still have allocated blocks - re-arm the timer, Tx is
826 * not stuck. Otherwise, cancel the timer (no Tx currently).
827 */
828 if (freed_blocks) {
829 if (wl->tx_allocated_blocks)
830 wl12xx_rearm_tx_watchdog_locked(wl);
831 else
832 cancel_delayed_work(&wl->tx_watchdog_work);
833 }
834
748 avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks; 835 avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks;
749 836
750 /* 837 /*
@@ -1418,6 +1505,7 @@ int wl1271_plt_stop(struct wl1271 *wl)
1418 cancel_work_sync(&wl->netstack_work); 1505 cancel_work_sync(&wl->netstack_work);
1419 cancel_work_sync(&wl->recovery_work); 1506 cancel_work_sync(&wl->recovery_work);
1420 cancel_delayed_work_sync(&wl->elp_work); 1507 cancel_delayed_work_sync(&wl->elp_work);
1508 cancel_delayed_work_sync(&wl->tx_watchdog_work);
1421 1509
1422 mutex_lock(&wl->mutex); 1510 mutex_lock(&wl->mutex);
1423 wl1271_power_off(wl); 1511 wl1271_power_off(wl);
@@ -1789,6 +1877,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
1789 cancel_work_sync(&wl->netstack_work); 1877 cancel_work_sync(&wl->netstack_work);
1790 cancel_work_sync(&wl->tx_work); 1878 cancel_work_sync(&wl->tx_work);
1791 cancel_delayed_work_sync(&wl->elp_work); 1879 cancel_delayed_work_sync(&wl->elp_work);
1880 cancel_delayed_work_sync(&wl->tx_watchdog_work);
1792 1881
1793 /* let's notify MAC80211 about the remaining pending TX frames */ 1882 /* let's notify MAC80211 about the remaining pending TX frames */
1794 wl12xx_tx_reset(wl, true); 1883 wl12xx_tx_reset(wl, true);
@@ -2218,6 +2307,12 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
2218 2307
2219 if (wl->scan.state != WL1271_SCAN_STATE_IDLE && 2308 if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
2220 wl->scan_vif == vif) { 2309 wl->scan_vif == vif) {
2310 /*
2311 * Rearm the tx watchdog just before idling scan. This
2312 * prevents just-finished scans from triggering the watchdog
2313 */
2314 wl12xx_rearm_tx_watchdog_locked(wl);
2315
2221 wl->scan.state = WL1271_SCAN_STATE_IDLE; 2316 wl->scan.state = WL1271_SCAN_STATE_IDLE;
2222 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); 2317 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
2223 wl->scan_vif = NULL; 2318 wl->scan_vif = NULL;
@@ -3129,6 +3224,13 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
3129 if (ret < 0) 3224 if (ret < 0)
3130 goto out_sleep; 3225 goto out_sleep;
3131 } 3226 }
3227
3228 /*
3229 * Rearm the tx watchdog just before idling scan. This
3230 * prevents just-finished scans from triggering the watchdog
3231 */
3232 wl12xx_rearm_tx_watchdog_locked(wl);
3233
3132 wl->scan.state = WL1271_SCAN_STATE_IDLE; 3234 wl->scan.state = WL1271_SCAN_STATE_IDLE;
3133 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); 3235 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
3134 wl->scan_vif = NULL; 3236 wl->scan_vif = NULL;
@@ -4138,6 +4240,13 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
4138 __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); 4240 __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
4139 wl12xx_free_link(wl, wlvif, &hlid); 4241 wl12xx_free_link(wl, wlvif, &hlid);
4140 wl->active_sta_count--; 4242 wl->active_sta_count--;
4243
4244 /*
4245 * rearm the tx watchdog when the last STA is freed - give the FW a
4246 * chance to return STA-buffered packets before complaining.
4247 */
4248 if (wl->active_sta_count == 0)
4249 wl12xx_rearm_tx_watchdog_locked(wl);
4141} 4250}
4142 4251
4143static int wl12xx_sta_add(struct wl1271 *wl, 4252static int wl12xx_sta_add(struct wl1271 *wl,
@@ -5212,6 +5321,7 @@ static struct ieee80211_hw *wl1271_alloc_hw(void)
5212 INIT_WORK(&wl->tx_work, wl1271_tx_work); 5321 INIT_WORK(&wl->tx_work, wl1271_tx_work);
5213 INIT_WORK(&wl->recovery_work, wl1271_recovery_work); 5322 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
5214 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); 5323 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
5324 INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
5215 5325
5216 wl->freezable_wq = create_freezable_workqueue("wl12xx_wq"); 5326 wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
5217 if (!wl->freezable_wq) { 5327 if (!wl->freezable_wq) {
diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c
index e43a6b2c1d91..fcba055ef196 100644
--- a/drivers/net/wireless/wl12xx/scan.c
+++ b/drivers/net/wireless/wl12xx/scan.c
@@ -55,6 +55,12 @@ void wl1271_scan_complete_work(struct work_struct *work)
55 vif = wl->scan_vif; 55 vif = wl->scan_vif;
56 wlvif = wl12xx_vif_to_data(vif); 56 wlvif = wl12xx_vif_to_data(vif);
57 57
58 /*
59 * Rearm the tx watchdog just before idling scan. This
60 * prevents just-finished scans from triggering the watchdog
61 */
62 wl12xx_rearm_tx_watchdog_locked(wl);
63
58 wl->scan.state = WL1271_SCAN_STATE_IDLE; 64 wl->scan.state = WL1271_SCAN_STATE_IDLE;
59 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); 65 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
60 wl->scan.req = NULL; 66 wl->scan.req = NULL;
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 8f78fddcb723..43ae49143d68 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -226,6 +226,10 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
226 wl->tx_blocks_available -= total_blocks; 226 wl->tx_blocks_available -= total_blocks;
227 wl->tx_allocated_blocks += total_blocks; 227 wl->tx_allocated_blocks += total_blocks;
228 228
229 /* If the FW was empty before, arm the Tx watchdog */
230 if (wl->tx_allocated_blocks == total_blocks)
231 wl12xx_rearm_tx_watchdog_locked(wl);
232
229 ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); 233 ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
230 wl->tx_allocated_pkts[ac]++; 234 wl->tx_allocated_pkts[ac]++;
231 235
diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
index e3977b55a710..5cf8c32d40d1 100644
--- a/drivers/net/wireless/wl12xx/tx.h
+++ b/drivers/net/wireless/wl12xx/tx.h
@@ -227,5 +227,6 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
227 227
228/* from main.c */ 228/* from main.c */
229void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); 229void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
230void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl);
230 231
231#endif 232#endif
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 6e13a3073e9f..749a15a75d38 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -495,6 +495,9 @@ struct wl1271 {
495 495
496 /* last wlvif we transmitted from */ 496 /* last wlvif we transmitted from */
497 struct wl12xx_vif *last_wlvif; 497 struct wl12xx_vif *last_wlvif;
498
499 /* work to fire when Tx is stuck */
500 struct delayed_work tx_watchdog_work;
498}; 501};
499 502
500struct wl1271_station { 503struct wl1271_station {