aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/wil6210/main.c
diff options
context:
space:
mode:
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>2014-09-10 16:34:50 +0300
committerJohn W. Linville <linville@tuxdriver.com>2014-09-11 15:27:39 -0400
commit73d839ae503d591bc0dc802490c00935b19250d1 (patch)
tree18f13d672d27a30c20d89ff233a10ac87fa1a2bd /drivers/net/wireless/ath/wil6210/main.c
parente4dbb093de182bb515821105dcdb8f2268bf6ffc (diff)
wil6210: fix for oops while stopping interface
When interface stopped while running intensive Rx traffic, the following oops observed: [89846.734683] Call trace: [89846.737117] [<ffffffc00083aa64>] dev_gro_receive+0xac/0x358 [89846.742674] [<ffffffc00083ae94>] napi_gro_receive+0x24/0xa4 [89846.748251] [<ffffffbffc1c2f88>] $x+0xec/0x1f8 [wil6210] wil_netif_rx_any [89846.753547] [<ffffffbffc1c4830>] $x+0x34/0x54 [wil6210] wil_release_reorder_frame [89846.758755] [<ffffffbffc1c48ac>] wil_release_reorder_frames+0x5c/0x78 [wil6210] [89846.766044] [<ffffffbffc1c4bf8>] wil_tid_ampdu_rx_free+0x20/0x48 [wil6210] [89846.772901] [<ffffffbffc1bedc8>] $x+0x190/0x1e8 [wil6210] [89846.778285] [<ffffffbffc1c0ed4>] wmi_event_worker+0x230/0x2f8 [wil6210] [89846.784865] [<ffffffc0000b0bc8>] process_one_work+0x278/0x3fc [89846.790591] [<ffffffc0000b1218>] worker_thread+0x200/0x330 [89846.796060] [<ffffffc0000b6664>] kthread+0xac/0xb8 [89846.800836] Code: b940c661 f9406a62 8b010041 f9400026 (f8636882) [89846.807008] ---[ end trace d6fdc17cd27d18f6 ]--- Reason is the following: when removing Rx vring (wil_netdev_ops.ndo_stop -> wil_stop -> wil_down -> __wil_down -> wil_rx_fini), Rx interrupt occurs. It trigger Rx NAPI, calling wil_rx_handle() that reaps (already cleaned) buffer, causing skb referring to garbage memory being set into reorder buffer. Then, network stack trying to access this buffer and fails. Prevent Rx NAPI from being scheduled if device going to stop. Bit wil_status_napi_en reflects NAPI enablement state, check it when triggering Rx NAPI. Testing shows that check for wil_status_napi_en sometimes gets negative, and new error message get printed - in this case kernel oops would be observed. Original oops is no more reproducible. This change requires also changes in the AP flows. Properly enable/disable NAPI for the AP. Make sure Rx VRING is disabled when resetting target. For this, promote __wil_up() and __wil_down() to the module scope, and use it in the relevant flows. 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.c25
1 files changed, 13 insertions, 12 deletions
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 8443d827d2f8..21667e0c3d14 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -221,10 +221,8 @@ static void wil_fw_error_worker(struct work_struct *work)
case NL80211_IFTYPE_MONITOR:
wil_info(wil, "fw error recovery started (try %d)...\n",
wil->recovery_count);
- wil_reset(wil);
-
- /* need to re-allocate Rx ring after reset */
- wil_rx_init(wil);
+ __wil_down(wil);
+ __wil_up(wil);
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
@@ -480,13 +478,12 @@ int wil_reset(struct wil6210_priv *wil)
wil_dbg_misc(wil, "%s()\n", __func__);
WARN_ON(!mutex_is_locked(&wil->mutex));
+ WARN_ON(test_bit(wil_status_napi_en, &wil->status));
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);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
@@ -575,7 +572,7 @@ void wil_link_off(struct wil6210_priv *wil)
netif_carrier_off(ndev);
}
-static int __wil_up(struct wil6210_priv *wil)
+int __wil_up(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
@@ -621,7 +618,7 @@ static int __wil_up(struct wil6210_priv *wil)
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
-
+ wil_dbg_misc(wil, "NAPI enable\n");
napi_enable(&wil->napi_rx);
napi_enable(&wil->napi_tx);
set_bit(wil_status_napi_en, &wil->status);
@@ -646,7 +643,7 @@ int wil_up(struct wil6210_priv *wil)
return rc;
}
-static int __wil_down(struct wil6210_priv *wil)
+int __wil_down(struct wil6210_priv *wil)
{
int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS /
WAIT_FOR_DISCONNECT_INTERVAL_MS;
@@ -656,9 +653,13 @@ static int __wil_down(struct wil6210_priv *wil)
if (wil->platform_ops.bus_request)
wil->platform_ops.bus_request(wil->platform_handle, 0);
- clear_bit(wil_status_napi_en, &wil->status);
- napi_disable(&wil->napi_rx);
- napi_disable(&wil->napi_tx);
+ wil_disable_irq(wil);
+ if (test_and_clear_bit(wil_status_napi_en, &wil->status)) {
+ napi_disable(&wil->napi_rx);
+ napi_disable(&wil->napi_tx);
+ wil_dbg_misc(wil, "NAPI disable\n");
+ }
+ wil_enable_irq(wil);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",