Skip to content

Commit 18ae7d0

Browse files
quic-bjorandejeff-t-johnson
authored andcommitted
wifi: ath12k: Avoid CPU busy-wait by handling VDEV_STAT and BCN_STAT
When the ath12k driver is built without CONFIG_ATH12K_DEBUG, the recently refactored stats code can cause any user space application (such at NetworkManager) to consume 100% CPU for 3 seconds, every time stats are read. Commit 'b8a0d83fe4c7 ("wifi: ath12k: move firmware stats out of debugfs")' moved ath12k_debugfs_fw_stats_request() out of debugfs, by merging the additional logic into ath12k_mac_get_fw_stats(). Among the added responsibility of ath12k_mac_get_fw_stats() was the busy-wait for `fw_stats_done`. Signalling of `fw_stats_done` happens when one of the WMI_REQUEST_PDEV_STAT, WMI_REQUEST_VDEV_STAT, and WMI_REQUEST_BCN_STAT messages are received, but the handling of the latter two commands remained in the debugfs code. As `fw_stats_done` isn't signalled, the calling processes will spin until the timeout (3 seconds) is reached. Moving the handling of these two additional responses out of debugfs resolves the issue. Fixes: b8a0d83 ("wifi: ath12k: move firmware stats out of debugfs") Signed-off-by: Bjorn Andersson <[email protected]> Tested-by: Abel Vesa <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jeff Johnson <[email protected]>
1 parent 27605c8 commit 18ae7d0

File tree

3 files changed

+60
-72
lines changed

3 files changed

+60
-72
lines changed

drivers/net/wireless/ath/ath12k/debugfs.c

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,64 +1251,6 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
12511251
*/
12521252
}
12531253

1254-
void
1255-
ath12k_debugfs_fw_stats_process(struct ath12k *ar,
1256-
struct ath12k_fw_stats *stats)
1257-
{
1258-
struct ath12k_base *ab = ar->ab;
1259-
struct ath12k_pdev *pdev;
1260-
bool is_end;
1261-
static unsigned int num_vdev, num_bcn;
1262-
size_t total_vdevs_started = 0;
1263-
int i;
1264-
1265-
if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
1266-
if (list_empty(&stats->vdevs)) {
1267-
ath12k_warn(ab, "empty vdev stats");
1268-
return;
1269-
}
1270-
/* FW sends all the active VDEV stats irrespective of PDEV,
1271-
* hence limit until the count of all VDEVs started
1272-
*/
1273-
rcu_read_lock();
1274-
for (i = 0; i < ab->num_radios; i++) {
1275-
pdev = rcu_dereference(ab->pdevs_active[i]);
1276-
if (pdev && pdev->ar)
1277-
total_vdevs_started += pdev->ar->num_started_vdevs;
1278-
}
1279-
rcu_read_unlock();
1280-
1281-
is_end = ((++num_vdev) == total_vdevs_started);
1282-
1283-
list_splice_tail_init(&stats->vdevs,
1284-
&ar->fw_stats.vdevs);
1285-
1286-
if (is_end) {
1287-
ar->fw_stats.fw_stats_done = true;
1288-
num_vdev = 0;
1289-
}
1290-
return;
1291-
}
1292-
if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
1293-
if (list_empty(&stats->bcn)) {
1294-
ath12k_warn(ab, "empty beacon stats");
1295-
return;
1296-
}
1297-
/* Mark end until we reached the count of all started VDEVs
1298-
* within the PDEV
1299-
*/
1300-
is_end = ((++num_bcn) == ar->num_started_vdevs);
1301-
1302-
list_splice_tail_init(&stats->bcn,
1303-
&ar->fw_stats.bcn);
1304-
1305-
if (is_end) {
1306-
ar->fw_stats.fw_stats_done = true;
1307-
num_bcn = 0;
1308-
}
1309-
}
1310-
}
1311-
13121254
static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
13131255
{
13141256
struct ath12k *ar = inode->i_private;

drivers/net/wireless/ath/ath12k/debugfs.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab);
1212
void ath12k_debugfs_soc_destroy(struct ath12k_base *ab);
1313
void ath12k_debugfs_register(struct ath12k *ar);
1414
void ath12k_debugfs_unregister(struct ath12k *ar);
15-
void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
16-
struct ath12k_fw_stats *stats);
1715
void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw,
1816
struct ieee80211_vif *vif);
1917
void ath12k_debugfs_pdev_create(struct ath12k_base *ab);
@@ -126,11 +124,6 @@ static inline void ath12k_debugfs_unregister(struct ath12k *ar)
126124
{
127125
}
128126

129-
static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
130-
struct ath12k_fw_stats *stats)
131-
{
132-
}
133-
134127
static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
135128
{
136129
return false;

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

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7626,6 +7626,63 @@ static int ath12k_wmi_pull_fw_stats(struct ath12k_base *ab, struct sk_buff *skb,
76267626
&parse);
76277627
}
76287628

7629+
static void ath12k_wmi_fw_stats_process(struct ath12k *ar,
7630+
struct ath12k_fw_stats *stats)
7631+
{
7632+
struct ath12k_base *ab = ar->ab;
7633+
struct ath12k_pdev *pdev;
7634+
bool is_end;
7635+
static unsigned int num_vdev, num_bcn;
7636+
size_t total_vdevs_started = 0;
7637+
int i;
7638+
7639+
if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
7640+
if (list_empty(&stats->vdevs)) {
7641+
ath12k_warn(ab, "empty vdev stats");
7642+
return;
7643+
}
7644+
/* FW sends all the active VDEV stats irrespective of PDEV,
7645+
* hence limit until the count of all VDEVs started
7646+
*/
7647+
rcu_read_lock();
7648+
for (i = 0; i < ab->num_radios; i++) {
7649+
pdev = rcu_dereference(ab->pdevs_active[i]);
7650+
if (pdev && pdev->ar)
7651+
total_vdevs_started += pdev->ar->num_started_vdevs;
7652+
}
7653+
rcu_read_unlock();
7654+
7655+
is_end = ((++num_vdev) == total_vdevs_started);
7656+
7657+
list_splice_tail_init(&stats->vdevs,
7658+
&ar->fw_stats.vdevs);
7659+
7660+
if (is_end) {
7661+
ar->fw_stats.fw_stats_done = true;
7662+
num_vdev = 0;
7663+
}
7664+
return;
7665+
}
7666+
if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
7667+
if (list_empty(&stats->bcn)) {
7668+
ath12k_warn(ab, "empty beacon stats");
7669+
return;
7670+
}
7671+
/* Mark end until we reached the count of all started VDEVs
7672+
* within the PDEV
7673+
*/
7674+
is_end = ((++num_bcn) == ar->num_started_vdevs);
7675+
7676+
list_splice_tail_init(&stats->bcn,
7677+
&ar->fw_stats.bcn);
7678+
7679+
if (is_end) {
7680+
ar->fw_stats.fw_stats_done = true;
7681+
num_bcn = 0;
7682+
}
7683+
}
7684+
}
7685+
76297686
static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb)
76307687
{
76317688
struct ath12k_fw_stats stats = {};
@@ -7655,19 +7712,15 @@ static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *sk
76557712

76567713
spin_lock_bh(&ar->data_lock);
76577714

7658-
/* WMI_REQUEST_PDEV_STAT can be requested via .get_txpower mac ops or via
7659-
* debugfs fw stats. Therefore, processing it separately.
7660-
*/
7715+
/* Handle WMI_REQUEST_PDEV_STAT status update */
76617716
if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
76627717
list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
76637718
ar->fw_stats.fw_stats_done = true;
76647719
goto complete;
76657720
}
76667721

7667-
/* WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT are currently requested only
7668-
* via debugfs fw stats. Hence, processing these in debugfs context.
7669-
*/
7670-
ath12k_debugfs_fw_stats_process(ar, &stats);
7722+
/* Handle WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT updates. */
7723+
ath12k_wmi_fw_stats_process(ar, &stats);
76717724

76727725
complete:
76737726
complete(&ar->fw_stats_complete);

0 commit comments

Comments
 (0)