aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2011-04-18 07:15:28 -0400
committerLuciano Coelho <coelho@ti.com>2011-05-02 03:31:28 -0400
commit7dece1c8e1044287287d44ac183a946333b55fc3 (patch)
tree82330386e96e8292bdea78301af98c047eb8a51c /drivers/net
parent2dc5a5c2c656b9029a0e635bb3a1cbcfbcb4ca5c (diff)
wl12xx: fix race condition during recovery in AP mode
When operating as AP, the TX queues are not stopped when we start recovery. mac80211 is notified only after the fact. When there is pending TX, it will be queued even after the FW is down. This leads to situations where the TX queues are stopped (because of the TX-watermark mechanism), and are never woken up when we return from recovery. Fix this by explicitly stopping the TX queues when before initiating recovery. Signed-off-by: Arik Nemtsov <arik@wizery.com> Signed-off-by: Luciano Coelho <coelho@ti.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/wl12xx/main.c21
-rw-r--r--drivers/net/wireless/wl12xx/tx.c8
-rw-r--r--drivers/net/wireless/wl12xx/tx.h2
3 files changed, 22 insertions, 9 deletions
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 433bc035741..6dd42c98766 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -352,7 +352,8 @@ static struct conf_drv_settings default_conf = {
352 .hci_io_ds = HCI_IO_DS_6MA, 352 .hci_io_ds = HCI_IO_DS_6MA,
353}; 353};
354 354
355static void __wl1271_op_remove_interface(struct wl1271 *wl); 355static void __wl1271_op_remove_interface(struct wl1271 *wl,
356 bool reset_tx_queues);
356static void wl1271_free_ap_keys(struct wl1271 *wl); 357static void wl1271_free_ap_keys(struct wl1271 *wl);
357 358
358 359
@@ -972,10 +973,19 @@ static void wl1271_recovery_work(struct work_struct *work)
972 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) 973 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
973 ieee80211_connection_loss(wl->vif); 974 ieee80211_connection_loss(wl->vif);
974 975
976 /* Prevent spurious TX during FW restart */
977 ieee80211_stop_queues(wl->hw);
978
975 /* reboot the chipset */ 979 /* reboot the chipset */
976 __wl1271_op_remove_interface(wl); 980 __wl1271_op_remove_interface(wl, false);
977 ieee80211_restart_hw(wl->hw); 981 ieee80211_restart_hw(wl->hw);
978 982
983 /*
984 * Its safe to enable TX now - the queues are stopped after a request
985 * to restart the HW.
986 */
987 ieee80211_wake_queues(wl->hw);
988
979out: 989out:
980 mutex_unlock(&wl->mutex); 990 mutex_unlock(&wl->mutex);
981} 991}
@@ -1479,7 +1489,8 @@ out:
1479 return ret; 1489 return ret;
1480} 1490}
1481 1491
1482static void __wl1271_op_remove_interface(struct wl1271 *wl) 1492static void __wl1271_op_remove_interface(struct wl1271 *wl,
1493 bool reset_tx_queues)
1483{ 1494{
1484 int i; 1495 int i;
1485 1496
@@ -1525,7 +1536,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
1525 mutex_lock(&wl->mutex); 1536 mutex_lock(&wl->mutex);
1526 1537
1527 /* let's notify MAC80211 about the remaining pending TX frames */ 1538 /* let's notify MAC80211 about the remaining pending TX frames */
1528 wl1271_tx_reset(wl); 1539 wl1271_tx_reset(wl, reset_tx_queues);
1529 wl1271_power_off(wl); 1540 wl1271_power_off(wl);
1530 1541
1531 memset(wl->bssid, 0, ETH_ALEN); 1542 memset(wl->bssid, 0, ETH_ALEN);
@@ -1586,7 +1597,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1586 */ 1597 */
1587 if (wl->vif) { 1598 if (wl->vif) {
1588 WARN_ON(wl->vif != vif); 1599 WARN_ON(wl->vif != vif);
1589 __wl1271_op_remove_interface(wl); 1600 __wl1271_op_remove_interface(wl, true);
1590 } 1601 }
1591 1602
1592 mutex_unlock(&wl->mutex); 1603 mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index cc837bba546..ca3ab1c1ace 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -769,8 +769,8 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
769 wl1271_handle_tx_low_watermark(wl); 769 wl1271_handle_tx_low_watermark(wl);
770} 770}
771 771
772/* caller must hold wl->mutex */ 772/* caller must hold wl->mutex and TX must be stopped */
773void wl1271_tx_reset(struct wl1271 *wl) 773void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
774{ 774{
775 int i; 775 int i;
776 struct sk_buff *skb; 776 struct sk_buff *skb;
@@ -806,8 +806,10 @@ void wl1271_tx_reset(struct wl1271 *wl)
806 /* 806 /*
807 * Make sure the driver is at a consistent state, in case this 807 * Make sure the driver is at a consistent state, in case this
808 * function is called from a context other than interface removal. 808 * function is called from a context other than interface removal.
809 * This call will always wake the TX queues.
809 */ 810 */
810 wl1271_handle_tx_low_watermark(wl); 811 if (reset_tx_queues)
812 wl1271_handle_tx_low_watermark(wl);
811 813
812 for (i = 0; i < ACX_TX_DESCRIPTORS; i++) { 814 for (i = 0; i < ACX_TX_DESCRIPTORS; i++) {
813 if (wl->tx_frames[i] == NULL) 815 if (wl->tx_frames[i] == NULL)
diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
index fc7835c4cf6..832f9258d67 100644
--- a/drivers/net/wireless/wl12xx/tx.h
+++ b/drivers/net/wireless/wl12xx/tx.h
@@ -185,7 +185,7 @@ static inline int wl1271_tx_get_queue(int queue)
185void wl1271_tx_work(struct work_struct *work); 185void wl1271_tx_work(struct work_struct *work);
186void wl1271_tx_work_locked(struct wl1271 *wl); 186void wl1271_tx_work_locked(struct wl1271 *wl);
187void wl1271_tx_complete(struct wl1271 *wl); 187void wl1271_tx_complete(struct wl1271 *wl);
188void wl1271_tx_reset(struct wl1271 *wl); 188void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
189void wl1271_tx_flush(struct wl1271 *wl); 189void wl1271_tx_flush(struct wl1271 *wl);
190u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); 190u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
191u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); 191u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set);