Re: [RFC PATCH 1/1] wifi: ath12k: avoid dynamic alloc when parsing wmi tb

Baochen Qiang
2026-02-27T03:18:30Z

On 2/27/2026 12:55 AM, 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.
>
> Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
> ---
> drivers/net/wireless/ath/ath12k/core.c | 7 +
> drivers/net/wireless/ath/ath12k/core.h | 2 +
> drivers/net/wireless/ath/ath12k/wmi.c | 170 ++++++-------------------
> 3 files changed, 49 insertions(+), 130 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
> index 9d6c50a94e64..961c9df69aa1 100644
> --- a/drivers/net/wireless/ath/ath12k/core.c
> +++ b/drivers/net/wireless/ath/ath12k/core.c
> @@ -2237,6 +2237,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);
> }
>
> @@ -2249,6 +2250,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");
> @@ -2296,6 +2302,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 990934ec92fc..378573a100e8 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"
> @@ -934,6 +935,7 @@ struct ath12k_base {
> struct device *dev;
> struct ath12k_qmi qmi;
> struct ath12k_wmi_base wmi_ab;
> + void __percpu *wmi_tb;

instead of allocating it per device, how about making it global and allocating once when
loading driver. This way we may save some memory in case where more than one devices get
probed?