[PATCH ath-next] wifi: ath12k: avoid dynamic alloc when parsing wmi tb
Baochen Qiang
baochen.qiang at oss.qualcomm.com
Mon Mar 9 19:05:11 PDT 2026
On 3/9/2026 11:20 PM, Nicolas Escande wrote:
> On each WMI message received from the hardware, we alloc a temporary array
> of WMI_TAG_MAX entries of type void *. This array is then populated with
> pointers of parsed structs depending on the WMI type, and then freed. This
> alloc can fail when memory pressure in the system is high enough.
>
> Given the fact that it is scheduled in softirq with the system_bh_wq, we
> should not be able to parse more than one WMI message per CPU at any time
>
> So instead lets move to a per cpu allocated array, stored in the struct
> ath12k_base, that is reused accros calls. The ath12k_wmi_tlv_parse_alloc()
> is also renamed into / merged with ath12k_wmi_tlv_parse() as it no longer
> allocs memory but returns the existing per-cpu one.
>
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00218-QCAHKSWPL_SILICONZ-1
>
> Signed-off-by: Nicolas Escande <nico.escande at gmail.com>
> ---
> changes from RFC:
> - rebased on ath-next 8e0ab5b9adb7
> - converted missing call sites ath12k_wmi_obss_color_collision_event()
> & ath12k_wmi_pdev_temperature_event()
> - changed alloc order & cleanup path in ath12k_core_alloc() as it seems
> it confused people
> - used sizeof(*tb) in ath12k_wmi_tlv_parse()
>
> Note I did try to move to a DEFINE_PER_CPU() allocated array but the module
> loader complained about the array size. So I stuck to the per ab alloc.
> ---
> drivers/net/wireless/ath/ath12k/core.c | 7 +
> drivers/net/wireless/ath/ath12k/core.h | 2 +
> drivers/net/wireless/ath/ath12k/wmi.c | 178 ++++++-------------------
> 3 files changed, 51 insertions(+), 136 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
> index c31c47fb5a73..c1de70b24828 100644
> --- a/drivers/net/wireless/ath/ath12k/core.c
> +++ b/drivers/net/wireless/ath/ath12k/core.c
> @@ -2258,6 +2258,7 @@ void ath12k_core_free(struct ath12k_base *ab)
> timer_delete_sync(&ab->rx_replenish_retry);
> destroy_workqueue(ab->workqueue_aux);
> destroy_workqueue(ab->workqueue);
> + free_percpu(ab->wmi_tb);
> kfree(ab);
> }
>
> @@ -2270,6 +2271,11 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
> if (!ab)
> return NULL;
>
> + ab->wmi_tb = __alloc_percpu(WMI_TAG_MAX * sizeof(void *),
> + __alignof__(void *));
> + if (!ab->wmi_tb)
> + goto err_sc_free;
> +
> init_completion(&ab->driver_recovery);
>
> ab->workqueue = create_singlethread_workqueue("ath12k_wq");
> @@ -2317,6 +2323,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
> err_free_wq:
> destroy_workqueue(ab->workqueue);
> err_sc_free:
> + free_percpu(ab->wmi_tb);
> kfree(ab);
> return NULL;
> }
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index 59c193b24764..ebe7b94fd712 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -19,6 +19,7 @@
> #include <linux/average.h>
> #include <linux/of.h>
> #include <linux/rhashtable.h>
> +#include <linux/percpu.h>
> #include "qmi.h"
> #include "htc.h"
> #include "wmi.h"
> @@ -937,6 +938,7 @@ struct ath12k_base {
> struct device *dev;
> struct ath12k_qmi qmi;
> struct ath12k_wmi_base wmi_ab;
> + void __percpu *wmi_tb;
any reason why my v1 suggestion is not considered?
instead of allocating it per device, how about making it global and define/allocate once
when loading driver. This way we may save some memory in case where more than one devices
get probed?
More information about the ath12k
mailing list