[PATCH ath-next v2] wifi: ath12k: avoid dynamic alloc when parsing wmi tb

Baochen Qiang baochen.qiang at oss.qualcomm.com
Sun Mar 15 19:27:58 PDT 2026



On 3/13/2026 7:38 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, that is reused across
> calls: ath12K_wmi_tb that is global to the ath12K module. To alloc & free
> we added two new module_init/exit functions for this module.
> 
> ath12k_wmi_tlv_parse_alloc() and ath12k_wmi_tlv_parse() are merged
> together as it no longer allocs mem but returns the existing per-cpu one.
> 
> Signed-off-by: Nicolas Escande <nico.escande at gmail.com>
> ---
> Small note on the new ath12k_wmi_tb variable. It is located in core.c and
> I did not exported it in core.h (nor is it exported with EXPORT_SYMBOL())
> as it is only used in wmi.c and should not be used by sub modules like
> ath12k_wifi7.

agree

or alternately you can define ath12k_wmi_tb in wmi.c and add alloc/free functions in
wmi.c, then call them in core.c. this helps modularity IMO?

either way works for me!

> 
> changes from v1:
>   - rebased on ath-next 27401c9b1432
>   - changed wording according to Jeff's comment
>   - moved alloc/cleanup to new module_init/exit functions in the
>     ath12k module as per Baochen's comment
> 
> 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()
> ---
>  drivers/net/wireless/ath/ath12k/core.c |  22 +++
>  drivers/net/wireless/ath/ath12k/core.h |   1 +
>  drivers/net/wireless/ath/ath12k/wmi.c  | 181 ++++++-------------------
>  3 files changed, 68 insertions(+), 136 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
> index c31c47fb5a73..c9a5baf41845 100644
> --- a/drivers/net/wireless/ath/ath12k/core.c
> +++ b/drivers/net/wireless/ath/ath12k/core.c
> @@ -34,6 +34,8 @@ module_param_named(ftm_mode, ath12k_ftm_mode, bool, 0444);
>  MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode");
>  EXPORT_SYMBOL(ath12k_ftm_mode);
>  
> +void __percpu * ath12k_wmi_tb;
> +
>  /* protected with ath12k_hw_group_mutex */
>  static struct list_head ath12k_hw_group_list = LIST_HEAD_INIT(ath12k_hw_group_list);
>  
> @@ -2321,5 +2323,25 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
>  	return NULL;
>  }
>  
> +static int ath12k_init(void)
> +{
> +	ath12k_wmi_tb = __alloc_percpu(WMI_TAG_MAX * sizeof(void *),
> +				       __alignof__(void *));
> +	if (!ath12k_wmi_tb) {
> +		pr_warn("Failed to alloc ath12k WMI tb\n");

generally we don't log memory allocation failure

> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static void ath12k_exit(void)
> +{
> +	free_percpu(ath12k_wmi_tb);
> +}
> +
> +module_init(ath12k_init);
> +module_exit(ath12k_exit);
> +
>  MODULE_DESCRIPTION("Driver support for Qualcomm Technologies WLAN devices");
>  MODULE_LICENSE("Dual BSD/GPL");


[...]

> @@ -9205,7 +9117,7 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
>  	const void **tb;
>  	int ret;
>  
> -	tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
> +	tb = ath12k_wmi_tlv_parse(ab, skb);
>  	if (IS_ERR(tb)) {
>  		ret = PTR_ERR(tb);
>  		ath12k_warn(ab, "failed to parse teardown complete event tlv: %d\n", ret);
> @@ -9215,11 +9127,8 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
>  	ev = tb[WMI_TAG_MLO_TEARDOWN_COMPLETE];
>  	if (!ev) {
>  		ath12k_warn(ab, "failed to fetch teardown complete event\n");
> -		kfree(tb);
>  		return;

nit: better to remove return as well

>  	}
> -
> -	kfree(tb);
>  }
>  
>  #ifdef CONFIG_ATH12K_DEBUGFS




More information about the ath12k mailing list