Skip to content

Commit 097638a

Browse files
Vladimir Kondratievlinvjw
authored andcommitted
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 <[email protected]> Signed-off-by: John W. Linville <[email protected]>
1 parent 260e695 commit 097638a

File tree

6 files changed

+47
-4
lines changed

6 files changed

+47
-4
lines changed

drivers/net/wireless/ath/wil6210/cfg80211.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,11 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
674674
struct net_device *dev, u8 *mac)
675675
{
676676
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
677+
678+
mutex_lock(&wil->mutex);
677679
wil6210_disconnect(wil, mac);
680+
mutex_unlock(&wil->mutex);
681+
678682
return 0;
679683
}
680684

drivers/net/wireless/ath/wil6210/main.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,9 @@ static void wil_disconnect_worker(struct work_struct *work)
133133
struct wil6210_priv *wil = container_of(work,
134134
struct wil6210_priv, disconnect_worker);
135135

136+
mutex_lock(&wil->mutex);
136137
_wil6210_disconnect(wil, NULL);
138+
mutex_unlock(&wil->mutex);
137139
}
138140

139141
static void wil_connect_timer_fn(ulong x)
@@ -260,7 +262,9 @@ void wil_priv_deinit(struct wil6210_priv *wil)
260262
{
261263
cancel_work_sync(&wil->disconnect_worker);
262264
cancel_work_sync(&wil->fw_error_worker);
265+
mutex_lock(&wil->mutex);
263266
wil6210_disconnect(wil, NULL);
267+
mutex_unlock(&wil->mutex);
264268
wmi_event_flush(wil);
265269
destroy_workqueue(wil->wmi_wq_conn);
266270
destroy_workqueue(wil->wmi_wq);
@@ -374,10 +378,14 @@ int wil_reset(struct wil6210_priv *wil)
374378
{
375379
int rc;
376380

381+
WARN_ON(!mutex_is_locked(&wil->mutex));
382+
383+
cancel_work_sync(&wil->disconnect_worker);
384+
wil6210_disconnect(wil, NULL);
385+
377386
wil->status = 0; /* prevent NAPI from being scheduled */
378387
if (test_bit(wil_status_napi_en, &wil->status)) {
379388
napi_synchronize(&wil->napi_rx);
380-
napi_synchronize(&wil->napi_tx);
381389
}
382390

383391
if (wil->scan_request) {
@@ -387,9 +395,6 @@ int wil_reset(struct wil6210_priv *wil)
387395
wil->scan_request = NULL;
388396
}
389397

390-
cancel_work_sync(&wil->disconnect_worker);
391-
wil6210_disconnect(wil, NULL);
392-
393398
wil6210_disable_irq(wil);
394399

395400
wmi_event_flush(wil);
@@ -447,6 +452,8 @@ static int __wil_up(struct wil6210_priv *wil)
447452
struct wireless_dev *wdev = wil->wdev;
448453
int rc;
449454

455+
WARN_ON(!mutex_is_locked(&wil->mutex));
456+
450457
rc = wil_reset(wil);
451458
if (rc)
452459
return rc;
@@ -506,6 +513,8 @@ int wil_up(struct wil6210_priv *wil)
506513

507514
static int __wil_down(struct wil6210_priv *wil)
508515
{
516+
WARN_ON(!mutex_is_locked(&wil->mutex));
517+
509518
clear_bit(wil_status_napi_en, &wil->status);
510519
napi_disable(&wil->napi_rx);
511520
napi_disable(&wil->napi_tx);

drivers/net/wireless/ath/wil6210/pcie_bus.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
7070
goto stop_master;
7171

7272
/* need reset here to obtain MAC */
73+
mutex_lock(&wil->mutex);
7374
rc = wil_reset(wil);
75+
mutex_unlock(&wil->mutex);
7476
if (rc)
7577
goto release_irq;
7678

drivers/net/wireless/ath/wil6210/txrx.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,13 +618,15 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
618618
struct wmi_vring_cfg_done_event cmd;
619619
} __packed reply;
620620
struct vring *vring = &wil->vring_tx[id];
621+
struct vring_tx_data *txdata = &wil->vring_tx_data[id];
621622

622623
if (vring->va) {
623624
wil_err(wil, "Tx ring [%d] already allocated\n", id);
624625
rc = -EINVAL;
625626
goto out;
626627
}
627628

629+
memset(txdata, 0, sizeof(*txdata));
628630
vring->size = size;
629631
rc = wil_vring_alloc(wil, vring);
630632
if (rc)
@@ -648,6 +650,8 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
648650
}
649651
vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
650652

653+
txdata->enabled = 1;
654+
651655
return 0;
652656
out_free:
653657
wil_vring_free(wil, vring, 1);
@@ -660,9 +664,16 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
660664
{
661665
struct vring *vring = &wil->vring_tx[id];
662666

667+
WARN_ON(!mutex_is_locked(&wil->mutex));
668+
663669
if (!vring->va)
664670
return;
665671

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+
666677
wil_vring_free(wil, vring, 1);
667678
}
668679

@@ -1028,6 +1039,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
10281039
struct net_device *ndev = wil_to_ndev(wil);
10291040
struct device *dev = wil_to_dev(wil);
10301041
struct vring *vring = &wil->vring_tx[ringid];
1042+
struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
10311043
int done = 0;
10321044
int cid = wil->vring2cid_tid[ringid][0];
10331045
struct wil_net_stats *stats = &wil->sta[cid].stats;
@@ -1038,6 +1050,11 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
10381050
return 0;
10391051
}
10401052

1053+
if (!txdata->enabled) {
1054+
wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid);
1055+
return 0;
1056+
}
1057+
10411058
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
10421059

10431060
while (!wil_vring_is_empty(vring)) {

drivers/net/wireless/ath/wil6210/wil6210.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,14 @@ struct vring {
243243
struct wil_ctx *ctx; /* ctx[size] - software context */
244244
};
245245

246+
/**
247+
* Additional data for Tx Vring
248+
*/
249+
struct vring_tx_data {
250+
int enabled;
251+
252+
};
253+
246254
enum { /* for wil6210_priv.status */
247255
wil_status_fwready = 0,
248256
wil_status_fwconnecting,
@@ -386,6 +394,7 @@ struct wil6210_priv {
386394
/* DMA related */
387395
struct vring vring_rx;
388396
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
397+
struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
389398
u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
390399
struct wil_sta_info sta[WIL6210_MAX_CID];
391400
/* scan */

drivers/net/wireless/ath/wil6210/wmi.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,9 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
462462

463463
wil->sinfo_gen++;
464464

465+
mutex_lock(&wil->mutex);
465466
wil6210_disconnect(wil, evt->bssid);
467+
mutex_unlock(&wil->mutex);
466468
}
467469

468470
static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)

0 commit comments

Comments
 (0)