mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 09:04:39 +02:00
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: b8a0d83fe4 ("wifi: ath12k: move firmware stats out of debugfs")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
Tested-by: Abel Vesa <abel.vesa@linaro.org>
Link: https://patch.msgid.link/20250609-ath12k-fw-stats-done-v1-1-2b3624656697@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
This commit is contained in:
parent
27605c8c0f
commit
18ae7d0cdd
|
|
@ -1251,64 +1251,6 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
|
|||
*/
|
||||
}
|
||||
|
||||
void
|
||||
ath12k_debugfs_fw_stats_process(struct ath12k *ar,
|
||||
struct ath12k_fw_stats *stats)
|
||||
{
|
||||
struct ath12k_base *ab = ar->ab;
|
||||
struct ath12k_pdev *pdev;
|
||||
bool is_end;
|
||||
static unsigned int num_vdev, num_bcn;
|
||||
size_t total_vdevs_started = 0;
|
||||
int i;
|
||||
|
||||
if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
|
||||
if (list_empty(&stats->vdevs)) {
|
||||
ath12k_warn(ab, "empty vdev stats");
|
||||
return;
|
||||
}
|
||||
/* FW sends all the active VDEV stats irrespective of PDEV,
|
||||
* hence limit until the count of all VDEVs started
|
||||
*/
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < ab->num_radios; i++) {
|
||||
pdev = rcu_dereference(ab->pdevs_active[i]);
|
||||
if (pdev && pdev->ar)
|
||||
total_vdevs_started += pdev->ar->num_started_vdevs;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
is_end = ((++num_vdev) == total_vdevs_started);
|
||||
|
||||
list_splice_tail_init(&stats->vdevs,
|
||||
&ar->fw_stats.vdevs);
|
||||
|
||||
if (is_end) {
|
||||
ar->fw_stats.fw_stats_done = true;
|
||||
num_vdev = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
|
||||
if (list_empty(&stats->bcn)) {
|
||||
ath12k_warn(ab, "empty beacon stats");
|
||||
return;
|
||||
}
|
||||
/* Mark end until we reached the count of all started VDEVs
|
||||
* within the PDEV
|
||||
*/
|
||||
is_end = ((++num_bcn) == ar->num_started_vdevs);
|
||||
|
||||
list_splice_tail_init(&stats->bcn,
|
||||
&ar->fw_stats.bcn);
|
||||
|
||||
if (is_end) {
|
||||
ar->fw_stats.fw_stats_done = true;
|
||||
num_bcn = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ath12k *ar = inode->i_private;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab);
|
|||
void ath12k_debugfs_soc_destroy(struct ath12k_base *ab);
|
||||
void ath12k_debugfs_register(struct ath12k *ar);
|
||||
void ath12k_debugfs_unregister(struct ath12k *ar);
|
||||
void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
|
||||
struct ath12k_fw_stats *stats);
|
||||
void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
void ath12k_debugfs_pdev_create(struct ath12k_base *ab);
|
||||
|
|
@ -126,11 +124,6 @@ static inline void ath12k_debugfs_unregister(struct ath12k *ar)
|
|||
{
|
||||
}
|
||||
|
||||
static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
|
||||
struct ath12k_fw_stats *stats)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -7626,6 +7626,63 @@ static int ath12k_wmi_pull_fw_stats(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
&parse);
|
||||
}
|
||||
|
||||
static void ath12k_wmi_fw_stats_process(struct ath12k *ar,
|
||||
struct ath12k_fw_stats *stats)
|
||||
{
|
||||
struct ath12k_base *ab = ar->ab;
|
||||
struct ath12k_pdev *pdev;
|
||||
bool is_end;
|
||||
static unsigned int num_vdev, num_bcn;
|
||||
size_t total_vdevs_started = 0;
|
||||
int i;
|
||||
|
||||
if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
|
||||
if (list_empty(&stats->vdevs)) {
|
||||
ath12k_warn(ab, "empty vdev stats");
|
||||
return;
|
||||
}
|
||||
/* FW sends all the active VDEV stats irrespective of PDEV,
|
||||
* hence limit until the count of all VDEVs started
|
||||
*/
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < ab->num_radios; i++) {
|
||||
pdev = rcu_dereference(ab->pdevs_active[i]);
|
||||
if (pdev && pdev->ar)
|
||||
total_vdevs_started += pdev->ar->num_started_vdevs;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
is_end = ((++num_vdev) == total_vdevs_started);
|
||||
|
||||
list_splice_tail_init(&stats->vdevs,
|
||||
&ar->fw_stats.vdevs);
|
||||
|
||||
if (is_end) {
|
||||
ar->fw_stats.fw_stats_done = true;
|
||||
num_vdev = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
|
||||
if (list_empty(&stats->bcn)) {
|
||||
ath12k_warn(ab, "empty beacon stats");
|
||||
return;
|
||||
}
|
||||
/* Mark end until we reached the count of all started VDEVs
|
||||
* within the PDEV
|
||||
*/
|
||||
is_end = ((++num_bcn) == ar->num_started_vdevs);
|
||||
|
||||
list_splice_tail_init(&stats->bcn,
|
||||
&ar->fw_stats.bcn);
|
||||
|
||||
if (is_end) {
|
||||
ar->fw_stats.fw_stats_done = true;
|
||||
num_bcn = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb)
|
||||
{
|
||||
struct ath12k_fw_stats stats = {};
|
||||
|
|
@ -7655,19 +7712,15 @@ static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *sk
|
|||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
|
||||
/* WMI_REQUEST_PDEV_STAT can be requested via .get_txpower mac ops or via
|
||||
* debugfs fw stats. Therefore, processing it separately.
|
||||
*/
|
||||
/* Handle WMI_REQUEST_PDEV_STAT status update */
|
||||
if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
|
||||
list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
|
||||
ar->fw_stats.fw_stats_done = true;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
/* WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT are currently requested only
|
||||
* via debugfs fw stats. Hence, processing these in debugfs context.
|
||||
*/
|
||||
ath12k_debugfs_fw_stats_process(ar, &stats);
|
||||
/* Handle WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT updates. */
|
||||
ath12k_wmi_fw_stats_process(ar, &stats);
|
||||
|
||||
complete:
|
||||
complete(&ar->fw_stats_complete);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user