aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>2014-03-17 09:34:25 -0400
committerJohn W. Linville <linville@tuxdriver.com>2014-03-17 13:44:19 -0400
commit097638a08acde0320c44969a5dff3af105c341a0 (patch)
tree44551edf737386ad546046171d0ac2dc3b972283 /drivers
parent260e695196de8b91bbab482d3804e4e0312a59b6 (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')
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c17
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c17
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h9
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c2
6 files changed, 47 insertions, 4 deletions
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index ed5a7e145027..4806a49cb61b 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -674,7 +674,11 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
674 struct net_device *dev, u8 *mac) 674 struct net_device *dev, u8 *mac)
675{ 675{
676 struct wil6210_priv *wil = wiphy_to_wil(wiphy); 676 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
677
678 mutex_lock(&wil->mutex);
677 wil6210_disconnect(wil, mac); 679 wil6210_disconnect(wil, mac);
680 mutex_unlock(&wil->mutex);
681
678 return 0; 682 return 0;
679} 683}
680 684
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)
133 struct wil6210_priv *wil = container_of(work, 133 struct wil6210_priv *wil = container_of(work,
134 struct wil6210_priv, disconnect_worker); 134 struct wil6210_priv, disconnect_worker);
135 135
136 mutex_lock(&wil->mutex);
136 _wil6210_disconnect(wil, NULL); 137 _wil6210_disconnect(wil, NULL);
138 mutex_unlock(&wil->mutex);
137} 139}
138 140
139static void wil_connect_timer_fn(ulong x) 141static void wil_connect_timer_fn(ulong x)
@@ -260,7 +262,9 @@ void wil_priv_deinit(struct wil6210_priv *wil)
260{ 262{
261 cancel_work_sync(&wil->disconnect_worker); 263 cancel_work_sync(&wil->disconnect_worker);
262 cancel_work_sync(&wil->fw_error_worker); 264 cancel_work_sync(&wil->fw_error_worker);
265 mutex_lock(&wil->mutex);
263 wil6210_disconnect(wil, NULL); 266 wil6210_disconnect(wil, NULL);
267 mutex_unlock(&wil->mutex);
264 wmi_event_flush(wil); 268 wmi_event_flush(wil);
265 destroy_workqueue(wil->wmi_wq_conn); 269 destroy_workqueue(wil->wmi_wq_conn);
266 destroy_workqueue(wil->wmi_wq); 270 destroy_workqueue(wil->wmi_wq);
@@ -374,10 +378,14 @@ int wil_reset(struct wil6210_priv *wil)
374{ 378{
375 int rc; 379 int rc;
376 380
381 WARN_ON(!mutex_is_locked(&wil->mutex));
382
383 cancel_work_sync(&wil->disconnect_worker);
384 wil6210_disconnect(wil, NULL);
385
377 wil->status = 0; /* prevent NAPI from being scheduled */ 386 wil->status = 0; /* prevent NAPI from being scheduled */
378 if (test_bit(wil_status_napi_en, &wil->status)) { 387 if (test_bit(wil_status_napi_en, &wil->status)) {
379 napi_synchronize(&wil->napi_rx); 388 napi_synchronize(&wil->napi_rx);
380 napi_synchronize(&wil->napi_tx);
381 } 389 }
382 390
383 if (wil->scan_request) { 391 if (wil->scan_request) {
@@ -387,9 +395,6 @@ int wil_reset(struct wil6210_priv *wil)
387 wil->scan_request = NULL; 395 wil->scan_request = NULL;
388 } 396 }
389 397
390 cancel_work_sync(&wil->disconnect_worker);
391 wil6210_disconnect(wil, NULL);
392
393 wil6210_disable_irq(wil); 398 wil6210_disable_irq(wil);
394 399
395 wmi_event_flush(wil); 400 wmi_event_flush(wil);
@@ -447,6 +452,8 @@ static int __wil_up(struct wil6210_priv *wil)
447 struct wireless_dev *wdev = wil->wdev; 452 struct wireless_dev *wdev = wil->wdev;
448 int rc; 453 int rc;
449 454
455 WARN_ON(!mutex_is_locked(&wil->mutex));
456
450 rc = wil_reset(wil); 457 rc = wil_reset(wil);
451 if (rc) 458 if (rc)
452 return rc; 459 return rc;
@@ -506,6 +513,8 @@ int wil_up(struct wil6210_priv *wil)
506 513
507static int __wil_down(struct wil6210_priv *wil) 514static int __wil_down(struct wil6210_priv *wil)
508{ 515{
516 WARN_ON(!mutex_is_locked(&wil->mutex));
517
509 clear_bit(wil_status_napi_en, &wil->status); 518 clear_bit(wil_status_napi_en, &wil->status);
510 napi_disable(&wil->napi_rx); 519 napi_disable(&wil->napi_rx);
511 napi_disable(&wil->napi_tx); 520 napi_disable(&wil->napi_tx);
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index c60976144db3..58fc0962e2e2 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -70,7 +70,9 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
70 goto stop_master; 70 goto stop_master;
71 71
72 /* need reset here to obtain MAC */ 72 /* need reset here to obtain MAC */
73 mutex_lock(&wil->mutex);
73 rc = wil_reset(wil); 74 rc = wil_reset(wil);
75 mutex_unlock(&wil->mutex);
74 if (rc) 76 if (rc)
75 goto release_irq; 77 goto release_irq;
76 78
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index cfd36cc0336b..c8c547457eb4 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -618,6 +618,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
618 struct wmi_vring_cfg_done_event cmd; 618 struct wmi_vring_cfg_done_event cmd;
619 } __packed reply; 619 } __packed reply;
620 struct vring *vring = &wil->vring_tx[id]; 620 struct vring *vring = &wil->vring_tx[id];
621 struct vring_tx_data *txdata = &wil->vring_tx_data[id];
621 622
622 if (vring->va) { 623 if (vring->va) {
623 wil_err(wil, "Tx ring [%d] already allocated\n", id); 624 wil_err(wil, "Tx ring [%d] already allocated\n", id);
@@ -625,6 +626,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
625 goto out; 626 goto out;
626 } 627 }
627 628
629 memset(txdata, 0, sizeof(*txdata));
628 vring->size = size; 630 vring->size = size;
629 rc = wil_vring_alloc(wil, vring); 631 rc = wil_vring_alloc(wil, vring);
630 if (rc) 632 if (rc)
@@ -648,6 +650,8 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
648 } 650 }
649 vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); 651 vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
650 652
653 txdata->enabled = 1;
654
651 return 0; 655 return 0;
652 out_free: 656 out_free:
653 wil_vring_free(wil, vring, 1); 657 wil_vring_free(wil, vring, 1);
@@ -660,9 +664,16 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
660{ 664{
661 struct vring *vring = &wil->vring_tx[id]; 665 struct vring *vring = &wil->vring_tx[id];
662 666
667 WARN_ON(!mutex_is_locked(&wil->mutex));
668
663 if (!vring->va) 669 if (!vring->va)
664 return; 670 return;
665 671
672 /* make sure NAPI won't touch this vring */
673 wil->vring_tx_data[id].enabled = 0;
674 if (test_bit(wil_status_napi_en, &wil->status))
675 napi_synchronize(&wil->napi_tx);
676
666 wil_vring_free(wil, vring, 1); 677 wil_vring_free(wil, vring, 1);
667} 678}
668 679
@@ -1028,6 +1039,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
1028 struct net_device *ndev = wil_to_ndev(wil); 1039 struct net_device *ndev = wil_to_ndev(wil);
1029 struct device *dev = wil_to_dev(wil); 1040 struct device *dev = wil_to_dev(wil);
1030 struct vring *vring = &wil->vring_tx[ringid]; 1041 struct vring *vring = &wil->vring_tx[ringid];
1042 struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
1031 int done = 0; 1043 int done = 0;
1032 int cid = wil->vring2cid_tid[ringid][0]; 1044 int cid = wil->vring2cid_tid[ringid][0];
1033 struct wil_net_stats *stats = &wil->sta[cid].stats; 1045 struct wil_net_stats *stats = &wil->sta[cid].stats;
@@ -1038,6 +1050,11 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
1038 return 0; 1050 return 0;
1039 } 1051 }
1040 1052
1053 if (!txdata->enabled) {
1054 wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid);
1055 return 0;
1056 }
1057
1041 wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid); 1058 wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
1042 1059
1043 while (!wil_vring_is_empty(vring)) { 1060 while (!wil_vring_is_empty(vring)) {
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index fb1006b2a4e2..2a2dec75f026 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -243,6 +243,14 @@ struct vring {
243 struct wil_ctx *ctx; /* ctx[size] - software context */ 243 struct wil_ctx *ctx; /* ctx[size] - software context */
244}; 244};
245 245
246/**
247 * Additional data for Tx Vring
248 */
249struct vring_tx_data {
250 int enabled;
251
252};
253
246enum { /* for wil6210_priv.status */ 254enum { /* for wil6210_priv.status */
247 wil_status_fwready = 0, 255 wil_status_fwready = 0,
248 wil_status_fwconnecting, 256 wil_status_fwconnecting,
@@ -386,6 +394,7 @@ struct wil6210_priv {
386 /* DMA related */ 394 /* DMA related */
387 struct vring vring_rx; 395 struct vring vring_rx;
388 struct vring vring_tx[WIL6210_MAX_TX_RINGS]; 396 struct vring vring_tx[WIL6210_MAX_TX_RINGS];
397 struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
389 u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ 398 u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
390 struct wil_sta_info sta[WIL6210_MAX_CID]; 399 struct wil_sta_info sta[WIL6210_MAX_CID];
391 /* scan */ 400 /* scan */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 58c3afcf839d..2ba56eef0c45 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -462,7 +462,9 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
462 462
463 wil->sinfo_gen++; 463 wil->sinfo_gen++;
464 464
465 mutex_lock(&wil->mutex);
465 wil6210_disconnect(wil, evt->bssid); 466 wil6210_disconnect(wil, evt->bssid);
467 mutex_unlock(&wil->mutex);
466} 468}
467 469
468static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) 470static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)