diff options
author | Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com> | 2014-03-17 15:34:25 +0200 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-03-17 13:44:19 -0400 |
commit | 097638a08acde0320c44969a5dff3af105c341a0 (patch) | |
tree | 44551edf737386ad546046171d0ac2dc3b972283 /drivers/net/wireless/ath/wil6210/main.c | |
parent | 260e695196de8b91bbab482d3804e4e0312a59b6 (diff) |
wil6210: fix race between disconnect and Tx NAPI
When disconnecting some CID, corresponded Tx vring get released. During vring
release, all descriptors get freed. It is possible that Tx NAPI working on the same
vring simultaneously. If it happens, descriptor may be double freed.
To protect from the race above, make sure NAPI won't process the same vring.
Introduce 'enabled' flag in the struct vring_tx_data. Proceed with Tx NAPI only if
'enabled' flag set. Prior to Tx vring release, clear this flag and make sure NAPI
get synchronized.
NAPI enablement status protected by wil->mutex, add protection where it was
missing and check for it.
During reset, disconnect all peers first, then proceed with the Rx vring. It allows for
the disconnect flow to observe proper 'wil->status' and correctly notify cfg80211 about
connection status change
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/main.c')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/main.c | 17 |
1 files changed, 13 insertions, 4 deletions
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 0005d9b90772..95f4efe9ef37 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -133,7 +133,9 @@ static void wil_disconnect_worker(struct work_struct *work) struct wil6210_priv *wil = container_of(work, struct wil6210_priv, disconnect_worker); + mutex_lock(&wil->mutex); _wil6210_disconnect(wil, NULL); + mutex_unlock(&wil->mutex); } static void wil_connect_timer_fn(ulong x) @@ -260,7 +262,9 @@ void wil_priv_deinit(struct wil6210_priv *wil) { cancel_work_sync(&wil->disconnect_worker); cancel_work_sync(&wil->fw_error_worker); + mutex_lock(&wil->mutex); wil6210_disconnect(wil, NULL); + mutex_unlock(&wil->mutex); wmi_event_flush(wil); destroy_workqueue(wil->wmi_wq_conn); destroy_workqueue(wil->wmi_wq); @@ -374,10 +378,14 @@ int wil_reset(struct wil6210_priv *wil) { int rc; + WARN_ON(!mutex_is_locked(&wil->mutex)); + + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + wil->status = 0; /* prevent NAPI from being scheduled */ if (test_bit(wil_status_napi_en, &wil->status)) { napi_synchronize(&wil->napi_rx); - napi_synchronize(&wil->napi_tx); } if (wil->scan_request) { @@ -387,9 +395,6 @@ int wil_reset(struct wil6210_priv *wil) wil->scan_request = NULL; } - cancel_work_sync(&wil->disconnect_worker); - wil6210_disconnect(wil, NULL); - wil6210_disable_irq(wil); wmi_event_flush(wil); @@ -447,6 +452,8 @@ static int __wil_up(struct wil6210_priv *wil) struct wireless_dev *wdev = wil->wdev; int rc; + WARN_ON(!mutex_is_locked(&wil->mutex)); + rc = wil_reset(wil); if (rc) return rc; @@ -506,6 +513,8 @@ int wil_up(struct wil6210_priv *wil) static int __wil_down(struct wil6210_priv *wil) { + WARN_ON(!mutex_is_locked(&wil->mutex)); + clear_bit(wil_status_napi_en, &wil->status); napi_disable(&wil->napi_rx); napi_disable(&wil->napi_tx); |