Skip to content

Commit 39efc7c

Browse files
anderssonkvalo
authored andcommitted
wcn36xx: Introduce mutual exclusion of fw configuration
As the association status changes the driver needs to configure the hardware. This is done based on information in the "sta" acquired by ieee80211_find_sta(), which requires the caller to ensure that the "sta" is valid while its being used; generally by entering an rcu read section. But the operations acting on the "sta" has to communicate with the firmware and may therefor sleep, resulting in the following report: [ 31.418190] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:238 [ 31.425919] in_atomic(): 0, irqs_disabled(): 0, pid: 34, name: kworker/u8:1 [ 31.434609] CPU: 0 PID: 34 Comm: kworker/u8:1 Tainted: G W 4.12.0-rc4-next-20170607+ #993 [ 31.441002] Hardware name: Qualcomm Technologies, Inc. APQ 8016 SBC (DT) [ 31.450380] Workqueue: phy0 ieee80211_iface_work [ 31.457226] Call trace: [ 31.461830] [<ffffff8008088c58>] dump_backtrace+0x0/0x260 [ 31.464004] [<ffffff8008088f7c>] show_stack+0x14/0x20 [ 31.469557] [<ffffff8008392e70>] dump_stack+0x98/0xb8 [ 31.474592] [<ffffff80080e4330>] ___might_sleep+0xf0/0x118 [ 31.479626] [<ffffff80080e43a8>] __might_sleep+0x50/0x88 [ 31.485010] [<ffffff80088ff9a4>] mutex_lock+0x24/0x60 [ 31.490479] [<ffffff8008595c38>] wcn36xx_smd_set_link_st+0x30/0x130 [ 31.495428] [<ffffff8008591ed8>] wcn36xx_bss_info_changed+0x148/0x448 [ 31.501504] [<ffffff80088ab3c4>] ieee80211_bss_info_change_notify+0xbc/0x118 [ 31.508102] [<ffffff80088f841c>] ieee80211_assoc_success+0x664/0x7f8 [ 31.515220] [<ffffff80088e13d4>] ieee80211_rx_mgmt_assoc_resp+0x144/0x2d8 [ 31.521555] [<ffffff80088e1e20>] ieee80211_sta_rx_queued_mgmt+0x190/0x698 [ 31.528239] [<ffffff80088bc44c>] ieee80211_iface_work+0x234/0x368 [ 31.535011] [<ffffff80080d81ac>] process_one_work+0x1cc/0x340 [ 31.541086] [<ffffff80080d8368>] worker_thread+0x48/0x430 [ 31.546814] [<ffffff80080de448>] kthread+0x108/0x138 [ 31.552195] [<ffffff8008082ec0>] ret_from_fork+0x10/0x50 In order to ensure that the "sta" remains alive (and consistent) for the duration of bss_info_changed() mutual exclusion has to be ensured with sta_remove(). This is done by introducing a mutex to cover firmware configuration changes, which is made to also ensure mutual exclusion between other operations changing the state or configuration of the firmware. With this we can drop the rcu read lock. Cc: [email protected] Signed-off-by: Bjorn Andersson <[email protected]> Signed-off-by: Kalle Valo <[email protected]>
1 parent ab3f9c8 commit 39efc7c

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
372372

373373
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed);
374374

375+
mutex_lock(&wcn->conf_mutex);
376+
375377
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
376378
int ch = WCN36XX_HW_CHANNEL(wcn);
377379
wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
@@ -382,6 +384,8 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
382384
}
383385
}
384386

387+
mutex_unlock(&wcn->conf_mutex);
388+
385389
return 0;
386390
}
387391

@@ -396,6 +400,8 @@ static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
396400

397401
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
398402

403+
mutex_lock(&wcn->conf_mutex);
404+
399405
*total &= FIF_ALLMULTI;
400406

401407
fp = (void *)(unsigned long)multicast;
@@ -408,6 +414,8 @@ static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
408414
else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc)
409415
wcn36xx_smd_set_mc_list(wcn, vif, fp);
410416
}
417+
418+
mutex_unlock(&wcn->conf_mutex);
411419
kfree(fp);
412420
}
413421

@@ -471,6 +479,8 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
471479
key_conf->key,
472480
key_conf->keylen);
473481

482+
mutex_lock(&wcn->conf_mutex);
483+
474484
switch (key_conf->cipher) {
475485
case WLAN_CIPHER_SUITE_WEP40:
476486
vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
@@ -565,6 +575,8 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
565575
}
566576

567577
out:
578+
mutex_unlock(&wcn->conf_mutex);
579+
568580
return ret;
569581
}
570582

@@ -725,6 +737,8 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
725737
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
726738
vif, changed);
727739

740+
mutex_lock(&wcn->conf_mutex);
741+
728742
if (changed & BSS_CHANGED_BEACON_INFO) {
729743
wcn36xx_dbg(WCN36XX_DBG_MAC,
730744
"mac bss changed dtim period %d\n",
@@ -787,7 +801,13 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
787801
bss_conf->aid);
788802

789803
vif_priv->sta_assoc = true;
790-
rcu_read_lock();
804+
805+
/*
806+
* Holding conf_mutex ensures mutal exclusion with
807+
* wcn36xx_sta_remove() and as such ensures that sta
808+
* won't be freed while we're operating on it. As such
809+
* we do not need to hold the rcu_read_lock().
810+
*/
791811
sta = ieee80211_find_sta(vif, bss_conf->bssid);
792812
if (!sta) {
793813
wcn36xx_err("sta %pM is not found\n",
@@ -811,7 +831,6 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
811831
* place where AID is available.
812832
*/
813833
wcn36xx_smd_config_sta(wcn, vif, sta);
814-
rcu_read_unlock();
815834
} else {
816835
wcn36xx_dbg(WCN36XX_DBG_MAC,
817836
"disassociated bss %pM vif %pM AID=%d\n",
@@ -873,6 +892,9 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
873892
}
874893
}
875894
out:
895+
896+
mutex_unlock(&wcn->conf_mutex);
897+
876898
return;
877899
}
878900

@@ -882,7 +904,10 @@ static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
882904
struct wcn36xx *wcn = hw->priv;
883905
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
884906

907+
mutex_lock(&wcn->conf_mutex);
885908
wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
909+
mutex_unlock(&wcn->conf_mutex);
910+
886911
return 0;
887912
}
888913

@@ -893,8 +918,12 @@ static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
893918
struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
894919
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
895920

921+
mutex_lock(&wcn->conf_mutex);
922+
896923
list_del(&vif_priv->list);
897924
wcn36xx_smd_delete_sta_self(wcn, vif->addr);
925+
926+
mutex_unlock(&wcn->conf_mutex);
898927
}
899928

900929
static int wcn36xx_add_interface(struct ieee80211_hw *hw,
@@ -915,9 +944,13 @@ static int wcn36xx_add_interface(struct ieee80211_hw *hw,
915944
return -EOPNOTSUPP;
916945
}
917946

947+
mutex_lock(&wcn->conf_mutex);
948+
918949
list_add(&vif_priv->list, &wcn->vif_list);
919950
wcn36xx_smd_add_sta_self(wcn, vif);
920951

952+
mutex_unlock(&wcn->conf_mutex);
953+
921954
return 0;
922955
}
923956

@@ -930,6 +963,8 @@ static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
930963
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
931964
vif, sta->addr);
932965

966+
mutex_lock(&wcn->conf_mutex);
967+
933968
spin_lock_init(&sta_priv->ampdu_lock);
934969
sta_priv->vif = vif_priv;
935970
/*
@@ -941,6 +976,9 @@ static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
941976
sta_priv->aid = sta->aid;
942977
wcn36xx_smd_config_sta(wcn, vif, sta);
943978
}
979+
980+
mutex_unlock(&wcn->conf_mutex);
981+
944982
return 0;
945983
}
946984

@@ -954,8 +992,13 @@ static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
954992
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
955993
vif, sta->addr, sta_priv->sta_index);
956994

995+
mutex_lock(&wcn->conf_mutex);
996+
957997
wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
958998
sta_priv->vif = NULL;
999+
1000+
mutex_unlock(&wcn->conf_mutex);
1001+
9591002
return 0;
9601003
}
9611004

@@ -999,6 +1042,8 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
9991042
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
10001043
action, tid);
10011044

1045+
mutex_lock(&wcn->conf_mutex);
1046+
10021047
switch (action) {
10031048
case IEEE80211_AMPDU_RX_START:
10041049
sta_priv->tid = tid;
@@ -1038,6 +1083,8 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
10381083
wcn36xx_err("Unknown AMPDU action\n");
10391084
}
10401085

1086+
mutex_unlock(&wcn->conf_mutex);
1087+
10411088
return 0;
10421089
}
10431090

@@ -1216,6 +1263,7 @@ static int wcn36xx_probe(struct platform_device *pdev)
12161263
wcn = hw->priv;
12171264
wcn->hw = hw;
12181265
wcn->dev = &pdev->dev;
1266+
mutex_init(&wcn->conf_mutex);
12191267
mutex_init(&wcn->hal_mutex);
12201268
mutex_init(&wcn->scan_lock);
12211269

drivers/net/wireless/ath/wcn36xx/wcn36xx.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ struct wcn36xx {
202202
struct qcom_smem_state *tx_rings_empty_state;
203203
unsigned tx_rings_empty_state_bit;
204204

205+
/* prevents concurrent FW reconfiguration */
206+
struct mutex conf_mutex;
207+
205208
/*
206209
* smd_buf must be protected with smd_mutex to garantee
207210
* that all messages are sent one after another

0 commit comments

Comments
 (0)