Commit 7dece1c8 authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Luciano Coelho

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: default avatarArik Nemtsov <arik@wizery.com>
Signed-off-by: default avatarLuciano Coelho <coelho@ti.com>
parent 2dc5a5c2
...@@ -352,7 +352,8 @@ static struct conf_drv_settings default_conf = { ...@@ -352,7 +352,8 @@ static struct conf_drv_settings default_conf = {
.hci_io_ds = HCI_IO_DS_6MA, .hci_io_ds = HCI_IO_DS_6MA,
}; };
static void __wl1271_op_remove_interface(struct wl1271 *wl); static void __wl1271_op_remove_interface(struct wl1271 *wl,
bool reset_tx_queues);
static void wl1271_free_ap_keys(struct wl1271 *wl); static void wl1271_free_ap_keys(struct wl1271 *wl);
...@@ -972,10 +973,19 @@ static void wl1271_recovery_work(struct work_struct *work) ...@@ -972,10 +973,19 @@ static void wl1271_recovery_work(struct work_struct *work)
if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
ieee80211_connection_loss(wl->vif); ieee80211_connection_loss(wl->vif);
/* Prevent spurious TX during FW restart */
ieee80211_stop_queues(wl->hw);
/* reboot the chipset */ /* reboot the chipset */
__wl1271_op_remove_interface(wl); __wl1271_op_remove_interface(wl, false);
ieee80211_restart_hw(wl->hw); ieee80211_restart_hw(wl->hw);
/*
* Its safe to enable TX now - the queues are stopped after a request
* to restart the HW.
*/
ieee80211_wake_queues(wl->hw);
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
} }
...@@ -1479,7 +1489,8 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -1479,7 +1489,8 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
return ret; return ret;
} }
static void __wl1271_op_remove_interface(struct wl1271 *wl) static void __wl1271_op_remove_interface(struct wl1271 *wl,
bool reset_tx_queues)
{ {
int i; int i;
...@@ -1525,7 +1536,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) ...@@ -1525,7 +1536,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
/* let's notify MAC80211 about the remaining pending TX frames */ /* let's notify MAC80211 about the remaining pending TX frames */
wl1271_tx_reset(wl); wl1271_tx_reset(wl, reset_tx_queues);
wl1271_power_off(wl); wl1271_power_off(wl);
memset(wl->bssid, 0, ETH_ALEN); memset(wl->bssid, 0, ETH_ALEN);
...@@ -1586,7 +1597,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, ...@@ -1586,7 +1597,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
*/ */
if (wl->vif) { if (wl->vif) {
WARN_ON(wl->vif != vif); WARN_ON(wl->vif != vif);
__wl1271_op_remove_interface(wl); __wl1271_op_remove_interface(wl, true);
} }
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
......
...@@ -769,8 +769,8 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) ...@@ -769,8 +769,8 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
wl1271_handle_tx_low_watermark(wl); wl1271_handle_tx_low_watermark(wl);
} }
/* caller must hold wl->mutex */ /* caller must hold wl->mutex and TX must be stopped */
void wl1271_tx_reset(struct wl1271 *wl) void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
{ {
int i; int i;
struct sk_buff *skb; struct sk_buff *skb;
...@@ -806,8 +806,10 @@ void wl1271_tx_reset(struct wl1271 *wl) ...@@ -806,8 +806,10 @@ void wl1271_tx_reset(struct wl1271 *wl)
/* /*
* Make sure the driver is at a consistent state, in case this * Make sure the driver is at a consistent state, in case this
* function is called from a context other than interface removal. * function is called from a context other than interface removal.
* This call will always wake the TX queues.
*/ */
wl1271_handle_tx_low_watermark(wl); if (reset_tx_queues)
wl1271_handle_tx_low_watermark(wl);
for (i = 0; i < ACX_TX_DESCRIPTORS; i++) { for (i = 0; i < ACX_TX_DESCRIPTORS; i++) {
if (wl->tx_frames[i] == NULL) if (wl->tx_frames[i] == NULL)
......
...@@ -185,7 +185,7 @@ static inline int wl1271_tx_get_queue(int queue) ...@@ -185,7 +185,7 @@ static inline int wl1271_tx_get_queue(int queue)
void wl1271_tx_work(struct work_struct *work); void wl1271_tx_work(struct work_struct *work);
void wl1271_tx_work_locked(struct wl1271 *wl); void wl1271_tx_work_locked(struct wl1271 *wl);
void wl1271_tx_complete(struct wl1271 *wl); void wl1271_tx_complete(struct wl1271 *wl);
void wl1271_tx_reset(struct wl1271 *wl); void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
void wl1271_tx_flush(struct wl1271 *wl); void wl1271_tx_flush(struct wl1271 *wl);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment