* Re: [PATCH ath-next v7] wifi: ath12k: avoid dynamic alloc when parsing wmi tb
From: Rameshkumar Sundaram @ 2026-04-09 18:57 UTC (permalink / raw)
To: Nicolas Escande, ath12k; +Cc: linux-wireless
In-Reply-To: <20260407095426.3285574-1-nico.escande@gmail.com>
On 4/7/2026 3:24 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. This memory is allocated as needed and refcounted to exist only
> as long as one struct ath12k_base lives.
>
> 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.
>
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00218-QCAHKSWPL_SILICONZ-1
>
> Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
> ---
> changes from v6:
> - fix useless ath12k_wmi_tb NULL init
> - do not use refcount_inc when refount is at 0, it can warn
>
> changes from v5:
> - use of the refount_t kernel api
>
> changes from v4:
> - moved to a single instance, refcounted per cpu memory alloc
>
> changes from v3:
> - simplified ath12k_core_init() with a single statement
> - move perpcu.h include directly to wmi.c
>
> changes from v2:
> - removed now superfluous return in ath12k_wmi_event_teardown_complete()
> - moved ath12k_wmi_tb declaration to wmi.c & added two functions to
> alloc / free it
> - removed useless error message on memory allocation failure
>
> 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 | 6 +
> drivers/net/wireless/ath/ath12k/wmi.c | 217 +++++++++----------------
> drivers/net/wireless/ath/ath12k/wmi.h | 3 +
> 3 files changed, 87 insertions(+), 139 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
> index c31c47fb5a73..6f0f4bfbf699 100644
> --- a/drivers/net/wireless/ath/ath12k/core.c
> +++ b/drivers/net/wireless/ath/ath12k/core.c
> @@ -2256,6 +2256,7 @@ void ath12k_core_deinit(struct ath12k_base *ab)
> void ath12k_core_free(struct ath12k_base *ab)
> {
> timer_delete_sync(&ab->rx_replenish_retry);
> + ath12k_wmi_free();
> destroy_workqueue(ab->workqueue_aux);
> destroy_workqueue(ab->workqueue);
> kfree(ab);
> @@ -2280,6 +2281,9 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
> if (!ab->workqueue_aux)
> goto err_free_wq;
>
> + if (ath12k_wmi_alloc() < 0)
> + goto err_free_wq_aux;
> +
> mutex_init(&ab->core_lock);
> spin_lock_init(&ab->base_lock);
> init_completion(&ab->reset_complete);
> @@ -2314,6 +2318,8 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
>
> return ab;
>
> +err_free_wq_aux:
> + destroy_workqueue(ab->workqueue_aux);
> err_free_wq:
> destroy_workqueue(ab->workqueue);
> err_sc_free:
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
> index 65a05a9520ff..484fdd3b1b7f 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.c
> +++ b/drivers/net/wireless/ath/ath12k/wmi.c
> @@ -15,6 +15,8 @@
> #include <linux/time.h>
> #include <linux/of.h>
> #include <linux/cleanup.h>
> +#include <linux/percpu.h>
> +#include <linux/refcount.h>
> #include "core.h"
> #include "debugfs.h"
> #include "debug.h"
> @@ -134,6 +136,10 @@ struct wmi_pdev_set_obss_bitmap_arg {
> const char *label;
> };
>
> +static DEFINE_MUTEX(ath12k_wmi_mutex);
> +static refcount_t ath12k_wmi_refcount;
> +static void __percpu *ath12k_wmi_tb;
> +
> static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
> [WMI_TAG_ARRAY_BYTE] = { .min_len = 0 },
> [WMI_TAG_ARRAY_UINT32] = { .min_len = 0 },
> @@ -289,29 +295,19 @@ static int ath12k_wmi_tlv_iter_parse(struct ath12k_base *ab, u16 tag, u16 len,
> return 0;
> }
>
> -static int ath12k_wmi_tlv_parse(struct ath12k_base *ar, const void **tb,
> - const void *ptr, size_t len)
> -{
> - return ath12k_wmi_tlv_iter(ar, ptr, len, ath12k_wmi_tlv_iter_parse,
> - (void *)tb);
> -}
> -
> static const void **
> -ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab,
> - struct sk_buff *skb, gfp_t gfp)
> +ath12k_wmi_tlv_parse(struct ath12k_base *ab, struct sk_buff *skb)
> {
> const void **tb;
> int ret;
>
> - tb = kzalloc_objs(*tb, WMI_TAG_MAX, gfp);
> - if (!tb)
> - return ERR_PTR(-ENOMEM);
> + tb = this_cpu_ptr(ath12k_wmi_tb);
> + memset(tb, 0, WMI_TAG_MAX * sizeof(*tb));
>
> - ret = ath12k_wmi_tlv_parse(ab, tb, skb->data, skb->len);
> - if (ret) {
> - kfree(tb);
> + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
> + ath12k_wmi_tlv_iter_parse, (void *)tb);
> + if (ret)
> return ERR_PTR(ret);
> - }
>
> return tb;
> }
> @@ -3911,9 +3907,10 @@ ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *sk
> const struct wmi_obss_color_collision_event *ev;
> struct ath12k_link_vif *arvif;
> u32 vdev_id, evt_type;
> + const void **tb;
> u64 bitmap;
>
> - const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
> + tb = ath12k_wmi_tlv_parse(ab, skb);
> if (IS_ERR(tb)) {
> ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n",
> PTR_ERR(tb));
> @@ -5714,7 +5711,7 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf
> const struct wmi_vdev_start_resp_event *ev;
> 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 tlv: %d\n", ret);
> @@ -5724,13 +5721,11 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf
> ev = tb[WMI_TAG_VDEV_START_RESPONSE_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch vdev start resp ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> *vdev_rsp = *ev;
>
> - kfree(tb);
> return 0;
> }
>
> @@ -5809,7 +5804,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
>
> ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n");
>
> - 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 tlv: %d\n", ret);
> @@ -5819,7 +5814,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
> ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch reg chan list ext update ev\n");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -5849,7 +5843,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
> if (num_2g_reg_rules > MAX_REG_RULES || num_5g_reg_rules > MAX_REG_RULES) {
> ath12k_warn(ab, "Num reg rules for 2G/5G exceeds max limit (num_2g_reg_rules: %d num_5g_reg_rules: %d max_rules: %d)\n",
> num_2g_reg_rules, num_5g_reg_rules, MAX_REG_RULES);
> - kfree(tb);
> return -EINVAL;
> }
>
> @@ -5859,7 +5852,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
> if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) {
> ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n",
> i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES);
> - kfree(tb);
> return -EINVAL;
> }
>
> @@ -5884,14 +5876,12 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
> num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) {
> ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n",
> i);
> - kfree(tb);
> return -EINVAL;
> }
> }
>
> if (!total_reg_rules) {
> ath12k_warn(ab, "No reg rules available\n");
> - kfree(tb);
> return -EINVAL;
> }
>
> @@ -5993,7 +5983,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
> ext_wmi_reg_rule);
>
> if (!reg_info->reg_rules_2g_ptr) {
> - kfree(tb);
> ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n");
> return -ENOMEM;
> }
> @@ -6027,7 +6016,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
> ext_wmi_reg_rule);
>
> if (!reg_info->reg_rules_5g_ptr) {
> - kfree(tb);
> ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n");
> return -ENOMEM;
> }
> @@ -6046,7 +6034,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
> ext_wmi_reg_rule);
>
> if (!reg_info->reg_rules_6g_ap_ptr[i]) {
> - kfree(tb);
> ath12k_warn(ab, "Unable to Allocate memory for 6g ap rules\n");
> return -ENOMEM;
> }
> @@ -6061,7 +6048,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
> ext_wmi_reg_rule);
>
> if (!reg_info->reg_rules_6g_client_ptr[j][i]) {
> - kfree(tb);
> ath12k_warn(ab, "Unable to Allocate memory for 6g client rules\n");
> return -ENOMEM;
> }
> @@ -6096,7 +6082,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
>
> ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel list\n");
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6107,7 +6092,7 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
> const struct wmi_peer_delete_resp_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6117,7 +6102,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
> ev = tb[WMI_TAG_PEER_DELETE_RESP_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch peer delete resp ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -6127,7 +6111,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
> ether_addr_copy(peer_del_resp->peer_macaddr.addr,
> ev->peer_macaddr.addr);
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6139,7 +6122,7 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab,
> const struct wmi_vdev_delete_resp_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6149,13 +6132,11 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab,
> ev = tb[WMI_TAG_VDEV_DELETE_RESP_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch vdev delete resp ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> *vdev_id = le32_to_cpu(ev->vdev_id);
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6167,7 +6148,7 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab,
> const struct wmi_bcn_tx_status_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6177,14 +6158,12 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab,
> ev = tb[WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch bcn tx status ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> *vdev_id = le32_to_cpu(ev->vdev_id);
> *tx_status = le32_to_cpu(ev->tx_status);
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6195,7 +6174,7 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_
> const struct wmi_vdev_stopped_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6205,13 +6184,11 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_
> ev = tb[WMI_TAG_VDEV_STOPPED_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch vdev stop ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> *vdev_id = le32_to_cpu(ev->vdev_id);
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6350,7 +6327,7 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
> const struct wmi_mgmt_tx_compl_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6360,7 +6337,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
> ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch mgmt tx compl ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -6370,7 +6346,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
> param->ppdu_id = ev->ppdu_id;
> param->ack_rssi = ev->ack_rssi;
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6533,7 +6508,7 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
> const struct wmi_scan_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6543,7 +6518,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
> ev = tb[WMI_TAG_SCAN_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch scan ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -6555,7 +6529,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
> scan_evt_param->vdev_id = ev->vdev_id;
> scan_evt_param->tsf_timestamp = ev->tsf_timestamp;
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6566,7 +6539,7 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
> const struct wmi_peer_sta_kickout_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6576,7 +6549,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
> ev = tb[WMI_TAG_PEER_STA_KICKOUT_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch peer sta kickout ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -6584,7 +6556,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
> arg->reason = le32_to_cpu(ev->reason);
> arg->rssi = le32_to_cpu(ev->rssi);
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6595,7 +6566,7 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
> const struct wmi_roam_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6605,7 +6576,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
> ev = tb[WMI_TAG_ROAM_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch roam ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -6613,7 +6583,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
> roam_ev->reason = ev->reason;
> roam_ev->rssi = ev->rssi;
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6647,7 +6616,7 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
> const struct wmi_chan_info_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6657,7 +6626,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
> ev = tb[WMI_TAG_CHAN_INFO_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch chan info ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -6674,7 +6642,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
> ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz;
> ch_info_ev->vdev_id = ev->vdev_id;
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6686,7 +6653,7 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
> const struct wmi_pdev_bss_chan_info_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6696,7 +6663,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
> ev = tb[WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch pdev bss chan info ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -6714,7 +6680,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
> bss_ch_info_ev->rx_bss_cycle_count_low = ev->rx_bss_cycle_count_low;
> bss_ch_info_ev->rx_bss_cycle_count_high = ev->rx_bss_cycle_count_high;
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6726,7 +6691,7 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
> const struct wmi_vdev_install_key_compl_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6736,7 +6701,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
> ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch vdev install key compl ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> @@ -6746,7 +6710,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
> arg->key_flags = le32_to_cpu(ev->key_flags);
> arg->status = le32_to_cpu(ev->status);
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6757,7 +6720,7 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff
> const struct wmi_peer_assoc_conf_event *ev;
> 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 tlv: %d\n", ret);
> @@ -6767,14 +6730,12 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff
> ev = tb[WMI_TAG_PEER_ASSOC_CONF_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch peer assoc conf ev");
> - kfree(tb);
> return -EPROTO;
> }
>
> peer_assoc_conf->vdev_id = le32_to_cpu(ev->vdev_id);
> peer_assoc_conf->macaddr = ev->peer_macaddr.addr;
>
> - kfree(tb);
> return 0;
> }
>
> @@ -6792,7 +6753,7 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
> const void **tb;
> int ret, i;
>
> - 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 tlv: %d\n", ret);
> @@ -6801,7 +6762,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
>
> ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT];
> if (!ev) {
> - kfree(tb);
> ath12k_warn(ab, "failed to fetch 11d new cc ev");
> return -EPROTO;
> }
> @@ -6814,8 +6774,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
> ab->new_alpha2[0],
> ab->new_alpha2[1]);
>
> - kfree(tb);
> -
> for (i = 0; i < ab->num_radios; i++) {
> pdev = &ab->pdevs[i];
> ar = pdev->ar;
> @@ -8557,7 +8515,7 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
> const struct wmi_pdev_ctl_failsafe_chk_event *ev;
> 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 tlv: %d\n", ret);
> @@ -8567,7 +8525,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
> ev = tb[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch pdev ctl failsafe check ev");
> - kfree(tb);
> return;
> }
>
> @@ -8581,8 +8538,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
> if (ev->ctl_failsafe_status != 0)
> ath12k_warn(ab, "pdev ctl failsafe failure status %d",
> ev->ctl_failsafe_status);
> -
> - kfree(tb);
> }
>
> static void
> @@ -8654,7 +8609,7 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
> const u32 *vdev_ids;
> 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 tlv: %d\n", ret);
> @@ -8666,7 +8621,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
>
> if (!ev || !vdev_ids) {
> ath12k_warn(ab, "failed to fetch pdev csa switch count ev");
> - kfree(tb);
> return;
> }
>
> @@ -8676,8 +8630,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
> ev->num_vdevs);
>
> ath12k_wmi_process_csa_switch_count_event(ab, ev, vdev_ids);
> -
> - kfree(tb);
> }
>
> static void
> @@ -8689,7 +8641,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
> struct ath12k *ar;
> 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 tlv: %d\n", ret);
> @@ -8700,7 +8652,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
>
> if (!ev) {
> ath12k_warn(ab, "failed to fetch pdev dfs radar detected ev");
> - kfree(tb);
> return;
> }
>
> @@ -8739,8 +8690,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
>
> exit:
> rcu_read_unlock();
> -
> - kfree(tb);
> }
>
> static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
> @@ -8751,7 +8700,7 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
> int ret;
> u16 length;
>
> - 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);
> @@ -8762,14 +8711,11 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
> ev = tb[WMI_TAG_ARRAY_BYTE];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch ftm msg\n");
> - kfree(tb);
> return;
> }
>
> length = skb->len - TLV_HDR_SIZE;
> ath12k_tm_process_event(ab, cmd_id, ev, length);
> - kfree(tb);
> - tb = NULL;
> }
>
> static void
> @@ -8782,7 +8728,7 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
> int temp;
> u32 pdev_id;
>
> - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
> + tb = ath12k_wmi_tlv_parse(ab, skb);
> if (IS_ERR(tb)) {
> ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb));
> return;
> @@ -8791,15 +8737,12 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
> ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch pdev temp ev\n");
> - kfree(tb);
> return;
> }
>
> temp = a_sle32_to_cpu(ev->temp);
> pdev_id = le32_to_cpu(ev->pdev_id);
>
> - kfree(tb);
> -
> ath12k_dbg(ab, ATH12K_DBG_WMI,
> "pdev temperature ev temp %d pdev_id %u\n",
> temp, pdev_id);
> @@ -8826,7 +8769,7 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab,
> const struct wmi_fils_discovery_event *ev;
> 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,
> @@ -8838,15 +8781,12 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab,
> ev = tb[WMI_TAG_HOST_SWFDA_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch FILS discovery event\n");
> - kfree(tb);
> return;
> }
>
> ath12k_warn(ab,
> "FILS discovery frame expected from host for vdev_id: %u, transmission scheduled at %u, next TBTT: %u\n",
> ev->vdev_id, ev->fils_tt, ev->tbtt);
> -
> - kfree(tb);
> }
>
> static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
> @@ -8856,7 +8796,7 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
> const struct wmi_probe_resp_tx_status_event *ev;
> 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,
> @@ -8869,7 +8809,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
> if (!ev) {
> ath12k_warn(ab,
> "failed to fetch probe response transmission status event");
> - kfree(tb);
> return;
> }
>
> @@ -8877,8 +8816,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
> ath12k_warn(ab,
> "Probe response transmission failed for vdev_id %u, status %u\n",
> ev->vdev_id, ev->tx_status);
> -
> - kfree(tb);
> }
>
> static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
> @@ -8890,7 +8827,7 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
> struct ath12k *ar;
> int ret, vdev_id;
>
> - 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 P2P NoA TLV: %d\n", ret);
> @@ -8900,10 +8837,8 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
> ev = tb[WMI_TAG_P2P_NOA_EVENT];
> noa = tb[WMI_TAG_P2P_NOA_INFO];
>
> - if (!ev || !noa) {
> - ret = -EPROTO;
> - goto out;
> - }
> + if (!ev || !noa)
> + return -EPROTO;
>
> vdev_id = __le32_to_cpu(ev->vdev_id);
>
> @@ -8926,8 +8861,6 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
>
> unlock:
> rcu_read_unlock();
> -out:
> - kfree(tb);
> return ret;
> }
>
> @@ -8938,7 +8871,7 @@ static void ath12k_rfkill_state_change_event(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 tlv: %d\n", ret);
> @@ -8946,10 +8879,8 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
> }
>
> ev = tb[WMI_TAG_RFKILL_EVENT];
> - if (!ev) {
> - kfree(tb);
> + if (!ev)
> return;
> - }
>
> ath12k_dbg(ab, ATH12K_DBG_MAC,
> "wmi tlv rfkill state change gpio %d type %d radio_state %d\n",
> @@ -8962,7 +8893,6 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
> spin_unlock_bh(&ab->base_lock);
>
> queue_work(ab->workqueue, &ab->rfkill_work);
> - kfree(tb);
> }
>
> static void
> @@ -8978,7 +8908,7 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab,
> const struct wmi_twt_enable_event *ev;
> 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 wmi twt enable status event tlv: %d\n",
> @@ -8989,15 +8919,12 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab,
> ev = tb[WMI_TAG_TWT_ENABLE_COMPLETE_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch twt enable wmi event\n");
> - goto exit;
> + return;
> }
>
> ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt enable event pdev id %u status %u\n",
> le32_to_cpu(ev->pdev_id),
> le32_to_cpu(ev->status));
> -
> -exit:
> - kfree(tb);
> }
>
> static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
> @@ -9007,7 +8934,7 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
> const struct wmi_twt_disable_event *ev;
> 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 wmi twt disable status event tlv: %d\n",
> @@ -9018,15 +8945,12 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
> ev = tb[WMI_TAG_TWT_DISABLE_COMPLETE_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch twt disable wmi event\n");
> - goto exit;
> + return;
> }
>
> ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt disable event pdev id %d status %u\n",
> le32_to_cpu(ev->pdev_id),
> le32_to_cpu(ev->status));
> -
> -exit:
> - kfree(tb);
> }
>
> static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab,
> @@ -9099,7 +9023,7 @@ static void ath12k_wmi_gtk_offload_status_event(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 tlv: %d\n", ret);
> @@ -9109,7 +9033,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
> ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch gtk offload status ev");
> - kfree(tb);
> return;
> }
>
> @@ -9119,7 +9042,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
> rcu_read_unlock();
> ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n",
> le32_to_cpu(ev->vdev_id));
> - kfree(tb);
> return;
> }
>
> @@ -9135,8 +9057,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
> (void *)&replay_ctr_be, GFP_ATOMIC);
>
> rcu_read_unlock();
> -
> - kfree(tb);
> }
>
> static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
> @@ -9148,7 +9068,7 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
> const void **tb;
> int ret, i;
>
> - 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 mlo setup complete event tlv: %d\n",
> @@ -9159,7 +9079,6 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
> ev = tb[WMI_TAG_MLO_SETUP_COMPLETE_EVENT];
> if (!ev) {
> ath12k_warn(ab, "failed to fetch mlo setup complete event\n");
> - kfree(tb);
> return;
> }
>
> @@ -9178,14 +9097,11 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
> if (!ar) {
> ath12k_warn(ab, "invalid pdev_id %d status %u in setup complete event\n",
> ev->pdev_id, ev->status);
> - goto out;
> + return;
> }
>
> ar->mlo_setup_status = le32_to_cpu(ev->status);
> complete(&ar->mlo_setup_done);
> -
> -out:
> - kfree(tb);
> }
>
> static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
> @@ -9195,7 +9111,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);
> @@ -9203,13 +9119,8 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
> }
>
> ev = tb[WMI_TAG_MLO_TEARDOWN_COMPLETE];
> - if (!ev) {
> + if (!ev)
> ath12k_warn(ab, "failed to fetch teardown complete event\n");
> - kfree(tb);
> - return;
> - }
> -
> - kfree(tb);
> }
>
> #ifdef CONFIG_ATH12K_DEBUGFS
> @@ -11239,3 +11150,31 @@ int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab,
> dev_kfree_skb(skb);
> return ret;
> }
> +
> +int ath12k_wmi_alloc(void)
> +{
> + guard(mutex)(&ath12k_wmi_mutex);
> +
> + if (!ath12k_wmi_tb) {
> + ath12k_wmi_tb = __alloc_percpu(WMI_TAG_MAX * sizeof(void *),
> + __alignof__(void *));
> + if (!ath12k_wmi_tb)
> + return -ENOMEM;
> +
> + refcount_set(&ath12k_wmi_refcount, 1);
> + } else {
> + refcount_inc(&ath12k_wmi_refcount);
> + }
> +
> + return 0;
> +}
> +
> +void ath12k_wmi_free(void)
> +{
> + guard(mutex)(&ath12k_wmi_mutex);
> +
> + if (refcount_dec_and_test(&ath12k_wmi_refcount)) {
> + free_percpu(ath12k_wmi_tb);
> + ath12k_wmi_tb = NULL;
> + }
> +}
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
> index 5ba9b7d3a888..4a34b2ca99ea 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.h
> +++ b/drivers/net/wireless/ath/ath12k/wmi.h
> @@ -6576,4 +6576,7 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
> struct ath12k_reg_tpc_power_info *param);
> int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab,
> struct wmi_mlo_link_set_active_arg *param);
> +int ath12k_wmi_alloc(void);
> +void ath12k_wmi_free(void);
> +
> #endif
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
^ permalink raw reply
* [PATCH ath-next 1/2] wifi: ath12k: Fix HTC prototype ath12k_base parameters
From: Jeff Johnson @ 2026-04-09 18:44 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, linux-kernel, Jeff Johnson
In-Reply-To: <20260409-ath12k-htc-proto-v1-0-cda86d6355f1@oss.qualcomm.com>
Per ath12k convention, a pointer to struct ath12k_base should be named
'ab'. However, in htc.h, several function prototypes do not follow the
convention, and instead use 'ar'. Conversely, in htc.c, the function
implementations all correctly use 'ab'. So update the prototypes to
match the implementations.
No functional changes, compile tested only.
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/htc.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/htc.h b/drivers/net/wireless/ath/ath12k/htc.h
index 7e3dccc7cc14..05b3e593542d 100644
--- a/drivers/net/wireless/ath/ath12k/htc.h
+++ b/drivers/net/wireless/ath/ath12k/htc.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH12K_HTC_H
@@ -301,7 +301,7 @@ struct ath12k_htc {
u8 wmi_ep_count;
};
-int ath12k_htc_init(struct ath12k_base *ar);
+int ath12k_htc_init(struct ath12k_base *ab);
int ath12k_htc_wait_target(struct ath12k_htc *htc);
int ath12k_htc_start(struct ath12k_htc *htc);
int ath12k_htc_connect_service(struct ath12k_htc *htc,
@@ -309,8 +309,8 @@ int ath12k_htc_connect_service(struct ath12k_htc *htc,
struct ath12k_htc_svc_conn_resp *conn_resp);
int ath12k_htc_send(struct ath12k_htc *htc, enum ath12k_htc_ep_id eid,
struct sk_buff *packet);
-struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ar, int size);
-void ath12k_htc_rx_completion_handler(struct ath12k_base *ar,
+struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ab, int size);
+void ath12k_htc_rx_completion_handler(struct ath12k_base *ab,
struct sk_buff *skb);
#endif
--
2.43.0
^ permalink raw reply related
* [PATCH ath-next 2/2] wifi: ath12k: Fix ath12k_dp_htt_tlv_iter()'s iter() signature
From: Jeff Johnson @ 2026-04-09 18:44 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, linux-kernel, Jeff Johnson
In-Reply-To: <20260409-ath12k-htc-proto-v1-0-cda86d6355f1@oss.qualcomm.com>
Per ath12k convention, a pointer to struct ath12k_base should be named
'ab'. However, the current signature of the 'iter' parameter of
ath12k_dp_htt_tlv_iter() uses 'ar'. Change it to use 'ab'.
No functional changes, compile tested only.
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/dp_htt.c | 2 +-
drivers/net/wireless/ath/ath12k/dp_htt.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c
index 9c19d9707abf..52e10059c6d5 100644
--- a/drivers/net/wireless/ath/ath12k/dp_htt.c
+++ b/drivers/net/wireless/ath/ath12k/dp_htt.c
@@ -146,7 +146,7 @@ static int ath12k_htt_tlv_ppdu_stats_parse(struct ath12k_base *ab,
}
int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len,
- int (*iter)(struct ath12k_base *ar, u16 tag, u16 len,
+ int (*iter)(struct ath12k_base *ab, u16 tag, u16 len,
const void *ptr, void *data),
void *data)
{
diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.h b/drivers/net/wireless/ath/ath12k/dp_htt.h
index 6020e632f74e..987689f11cda 100644
--- a/drivers/net/wireless/ath/ath12k/dp_htt.h
+++ b/drivers/net/wireless/ath/ath12k/dp_htt.h
@@ -1523,7 +1523,7 @@ int ath12k_dp_tx_htt_srng_setup(struct ath12k_base *ab, u32 ring_id,
void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
struct sk_buff *skb);
int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len,
- int (*iter)(struct ath12k_base *ar, u16 tag, u16 len,
+ int (*iter)(struct ath12k_base *ab, u16 tag, u16 len,
const void *ptr, void *data),
void *data);
int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab);
--
2.43.0
^ permalink raw reply related
* [PATCH ath-next 0/2] wifi: ath12k: Consistently name struct ath12k_base pointers
From: Jeff Johnson @ 2026-04-09 18:44 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, linux-kernel, Jeff Johnson
Per ath12k convention, a pointer to struct ath12k_base should be named
'ab', but there are a few places it is named 'ar', so fix them.
Note that one instance in ath12k_wmi_tlv_parse() is not modified since
that instance is being removed as part of:
https://patch.msgid.link/20260407095426.3285574-1-nico.escande@gmail.com/
---
Jeff Johnson (2):
wifi: ath12k: Fix HTC prototype ath12k_base parameters
wifi: ath12k: Fix ath12k_dp_htt_tlv_iter()'s iter() signature
drivers/net/wireless/ath/ath12k/dp_htt.c | 2 +-
drivers/net/wireless/ath/ath12k/dp_htt.h | 2 +-
drivers/net/wireless/ath/ath12k/htc.h | 8 ++++----
3 files changed, 6 insertions(+), 6 deletions(-)
---
base-commit: 15551ababf6d4e857f2101366a0c3eaa86dd822c
change-id: 20260403-ath12k-htc-proto-9cdc961f39dc
^ permalink raw reply
* Re: [PATCH 00/61] treewide: Use IS_ERR_OR_NULL over manual NULL check - refactor
From: Al Viro @ 2026-04-09 18:16 UTC (permalink / raw)
To: Philipp Hahn
Cc: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
linux-sctp, linux-security-module, linux-sh, linux-sound,
linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
netdev, ntfs3, samba-technical, sched-ext, target-devel,
tipc-discussion, v9fs, Julia Lawall, Nicolas Palix, Chris Mason,
David Sterba, Ilya Dryomov, Alex Markuze, Viacheslav Dubeyko,
Theodore Ts'o, Andreas Dilger, Steve French, Paulo Alcantara,
Ronnie Sahlberg, Shyam Prasad N, Tom Talpey, Bharath SM,
Eric Van Hensbergen, Latchesar Ionkov, Dominique Martinet,
Christian Schoenebeck, Gao Xiang, Chao Yu, Yue Hu, Jeffle Xu,
Sandeep Dhavale, Hongbo Li, Chunhai Guo, Miklos Szeredi,
Konstantin Komarov, Andreas Gruenbacher, Kees Cook, Tony Luck,
Guilherme G. Piccoli, Jan Kara, Phillip Lougher,
Christian Brauner, Jan Kara, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Tejun Heo, David Vernet, Andrea Righi,
Changwoo Min, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Ben Segall, Mel Gorman,
Valentin Schneider, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Sylwester Nawrocki, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai, Max Filippov,
Paolo Bonzini, John Johansen, Paul Moore, James Morris,
Serge E. Hallyn, Andrew Morton, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski, David S. Miller, David Ahern,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Marcel Holtmann, Johan Hedberg, Luiz Augusto von Dentz,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Jamal Hadi Salim, Jiri Pirko,
Marcelo Ricardo Leitner, Xin Long, Trond Myklebust,
Anna Schumaker, Chuck Lever, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Jon Maloy, Johannes Berg,
Catalin Marinas, Russell King, John Crispin, Thomas Bogendoerfer,
Yoshinori Sato, Rich Felker, John Paul Adrian Glaubitz,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Zhenyu Wang,
Zhi Wang, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Alex Deucher, Christian König, Sandy Huang,
Heiko Stübner, Andy Yan, Igor Russkikh, Andrew Lunn,
Pavan Chebbi, Michael Chan, Potnuri Bharat Teja, Tony Nguyen,
Przemek Kitszel, Taras Chornyi, Maxime Coquelin, Alexandre Torgue,
Iyappan Subramanian, Keyur Chudgar, Quan Nguyen, Heiner Kallweit,
Marc Zyngier, Thomas Gleixner, Andrew Lunn, Gregory Clement,
Sebastian Hesselbarth, Vinod Koul, Linus Walleij, Ulf Hansson,
Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
Christian Borntraeger, Sven Schnelle, Martin K. Petersen,
Eduardo Valentin, Keerthy, Rafael J. Wysocki, Daniel Lezcano,
Zhang Rui, Lukasz Luba, Alex Williamson, Mark Greer,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Shuah Khan, Kieran Bingham, Mauro Carvalho Chehab, Joerg Roedel,
Will Deacon, Robin Murphy, Lee Jones, Pavel Machek, Dave Penkler,
K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
Justin Sanders, Jens Axboe, Georgi Djakov, Michael Turquette,
Stephen Boyd, Philipp Zabel, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Pali Rohár, Dmitry Torokhov
In-Reply-To: <20260310-b4-is_err_or_null-v1-0-bd63b656022d@avm.de>
On Tue, Mar 10, 2026 at 12:48:26PM +0100, Philipp Hahn wrote:
> While doing some static code analysis I stumbled over a common pattern,
> where IS_ERR() is combined with a NULL check. For that there is
> IS_ERR_OR_NULL().
... and valid uses of IS_ERR_OR_NULL are rare as hen teeth.
Most of those are "I'm not sure how this function returns an
error, let's use that just in case".
Please, do not introduce more of that crap.
^ permalink raw reply
* Re: nl80211: missing minimum TX power attribute causes misleading userspace behavior
From: Ben Greear @ 2026-04-09 15:27 UTC (permalink / raw)
To: Steffen May, linux-wireless
In-Reply-To: <9ec6fd0b1c7392fce8c913a46c1b197e@posteo.de>
On 4/9/26 01:05, Steffen May wrote:
>
> This issue was discovered during the analysis of two documented OpenWrt bugs. Bug 1 is a type comparison error in LuCI wireless.js that causes 0 dBm to be
> displayed incorrectly. Bug 2 is in ucode mac80211.sh where the value 0 is treated as falsy, causing the router to transmit at maximum power instead. Both bugs
> are proven and reported.
>
> During verification of Bug 2 on five devices with three different chipsets, it became apparent that the hardware floor is device-dependent and completely
> unknown to the stack. This is not a bug but a missing feature.
>
> Because the floor is unknown, iwinfo generates selection lists containing values that have no real effect on the actual output power. Userspace accepts
> configurations such as 0 dBm even though the hardware cannot apply them. The system reports success while the hardware remains at its minimum supported level.
> This creates false assumptions.
>
> Measurements
>
> The deviation between the requested transmit power and the actual hardware floor is not constant but depends on the hardware:
>
> Device Chipset Requested Actual floor Difference
> Buffalo WZR-600DHP Atheros AR7161 1 dBm 3 dBm +2 dB
> TP-Link Archer C7 Qualcomm QCA9558 1 dBm 5 dBm +4 dB
> OpenWrt One MTK Filogic 1 dBm 1 dBm 0 dB
> Cudy WR3000 MTK Filogic 1 dBm 1 dBm 0 dB
> GL.iNet GL-MT6000 MTK Filogic 1 dBm 1 dBm 0 dB
If you set that tplink to 1dbm, and then read the reported power, does it properly show 5dbm?
If so, that seems good enough?
If not, then we should fix the reporting, but having a floor reported doesn't seem helpful
to me. There are lots of things that can affect actual txpower. User-space can at best
offer its suggestion of preferred txpower. Kernel/driver/firmware/hardware then makes final
decision.
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* [PATCH v2 3/4] wifi: Rename EMLSR delay constants and add EMLMR helpers and definitions
From: Pablo Martin-Gomez @ 2026-04-09 13:50 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
In-Reply-To: <20260409135035.4018725-1-pmartin-gomez@freebox.fr>
In the final version of 802.11be-2024, Transition Delay and Padding
Delay subfields are for both EMLSR and EMLMR. Depending on whether the
mode is EMLSR or EMLMR, the interpretation of the encoded value might
change.
This patch defines the constants and helpers to interpret delay
subfields both in EMLSR and EMLMR modes.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
.../net/wireless/intel/iwlwifi/mld/mac80211.c | 4 +-
.../net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 +-
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 4 +-
include/linux/ieee80211-eht.h | 100 ++++++++++++++++--
net/mac80211/eht.c | 4 +-
5 files changed, 99 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index e3aec814aa0d..4510caed82a7 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -115,9 +115,9 @@ static const u8 ext_capa_base[IWL_MLD_STA_EXT_CAPA_SIZE] = {
#define IWL_MLD_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \
IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \
- __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \
+ __bf_shf(IEEE80211_EML_CAP_EML_PADDING_DELAY) | \
IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \
- __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY))
+ __bf_shf(IEEE80211_EML_CAP_EML_TRANSITION_DELAY))
#define IWL_MLD_CAPA_OPS (FIELD_PREP_CONST( \
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 1ec9807e4827..eece3c871203 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -264,9 +264,9 @@ static const u8 tm_if_types_ext_capa_sta[] = {
#define IWL_MVM_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \
IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \
- __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \
+ __bf_shf(IEEE80211_EML_CAP_EML_PADDING_DELAY) | \
IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \
- __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY))
+ __bf_shf(IEEE80211_EML_CAP_EML_TRANSITION_DELAY))
#define IWL_MVM_MLD_CAPA_OPS (FIELD_PREP_CONST( \
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 37cdf3e8a067..22bad3cba8df 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1880,8 +1880,8 @@ mt7925_mcu_sta_eht_mld_tlv(struct sk_buff *skb,
eml_cap = (vif->cfg.eml_cap & (IEEE80211_EML_CAP_EMLSR_SUPP |
IEEE80211_EML_CAP_TRANSITION_TIMEOUT)) |
- (ext_capa->eml_capabilities & (IEEE80211_EML_CAP_EMLSR_PADDING_DELAY |
- IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY));
+ (ext_capa->eml_capabilities & (IEEE80211_EML_CAP_EML_PADDING_DELAY |
+ IEEE80211_EML_CAP_EML_TRANSITION_DELAY));
if (eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP) {
eht_mld->eml_cap[0] = u16_get_bits(eml_cap, GENMASK(7, 0));
diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h
index 6324d888073b..82efd01d7708 100644
--- a/include/linux/ieee80211-eht.h
+++ b/include/linux/ieee80211-eht.h
@@ -485,19 +485,27 @@ struct ieee80211_multi_link_elem {
#define IEEE80211_MED_SYNC_DELAY_DEFAULT 0x10ac
#define IEEE80211_EML_CAP_EMLSR_SUPP 0x0001
-#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY 0x000e
-#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_0US 0
-#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US 1
-#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_64US 2
-#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_128US 3
-#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US 4
-#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY 0x0070
+#define IEEE80211_EML_CAP_EML_PADDING_DELAY 0x000e
+/* Described Tables 9-417i & 9-417k in 802.11be-2024, which have the same values */
+#define IEEE80211_EML_CAP_EML_PADDING_DELAY_0US 0
+#define IEEE80211_EML_CAP_EML_PADDING_DELAY_32US 1
+#define IEEE80211_EML_CAP_EML_PADDING_DELAY_64US 2
+#define IEEE80211_EML_CAP_EML_PADDING_DELAY_128US 3
+#define IEEE80211_EML_CAP_EML_PADDING_DELAY_256US 4
+#define IEEE80211_EML_CAP_EML_TRANSITION_DELAY 0x0070
+/* Described in Table 9-417j in 802.11be-2024 */
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_0US 0
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_16US 1
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_32US 2
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US 3
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_128US 4
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US 5
+/* Described in Table 9-417l in 802.11be-2024 */
+#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_0US 0
+#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_32US 1
+#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_64US 2
+#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_128US 3
+#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_256US 4
#define IEEE80211_EML_CAP_EMLMR_SUPPORT 0x0080
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT 0x7800
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_0 0
@@ -1114,11 +1122,17 @@ static inline bool ieee80211_tid_to_link_map_size_ok(const u8 *data, size_t len)
static inline u32 ieee80211_emlsr_pad_delay_in_us(u16 eml_cap)
{
+ u32 emlsr_supp =
+ u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP);
+
+ if (!emlsr_supp)
+ return 0;
+
/* IEEE Std 802.11be-2024 Table 9-417i—Encoding of the EMLSR
* Padding Delay subfield.
*/
u32 pad_delay = u16_get_bits(eml_cap,
- IEEE80211_EML_CAP_EMLSR_PADDING_DELAY);
+ IEEE80211_EML_CAP_EML_PADDING_DELAY);
if (!pad_delay ||
pad_delay > IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US)
@@ -1138,12 +1152,18 @@ static inline u32 ieee80211_emlsr_pad_delay_in_us(u16 eml_cap)
static inline u32 ieee80211_emlsr_trans_delay_in_us(u16 eml_cap)
{
+ u32 emlsr_supp =
+ u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP);
+
+ if (!emlsr_supp)
+ return 0;
+
/* IEEE Std 802.11be-2024 Table 9-417j—Encoding of the EMLSR
* Transition Delay subfield.
*/
u32 trans_delay =
u16_get_bits(eml_cap,
- IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY);
+ IEEE80211_EML_CAP_EML_TRANSITION_DELAY);
/* invalid values also just use 0 */
if (!trans_delay ||
@@ -1153,6 +1173,68 @@ static inline u32 ieee80211_emlsr_trans_delay_in_us(u16 eml_cap)
return 16 * (1 << (trans_delay - 1));
}
+/**
+ * ieee80211_emlmr_pad_delay_in_us - Fetch the EMLMR Padding delay
+ * in microseconds
+ * @eml_cap: EML capabilities field value from common info field of
+ * the Multi-link element
+ * Return: the EMLMR Padding delay (in microseconds) encoded in the
+ * EML Capabilities field
+ */
+
+static inline u32 ieee80211_emlmr_pad_delay_in_us(u16 eml_cap)
+{
+ u32 emlmr_supp =
+ u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLMR_SUPP);
+
+ if (!emlmr_supp)
+ return 0;
+
+ /* IEEE Std 802.11be-2024 Table 9-417k—Encoding of the EMLMR
+ * Padding Delay subfield.
+ */
+ u32 pad_delay = u16_get_bits(eml_cap,
+ IEEE80211_EML_CAP_EML_PADDING_DELAY);
+
+ if (!pad_delay ||
+ pad_delay > IEEE80211_EML_CAP_EMLMR_PADDING_DELAY_256US)
+ return 0;
+
+ return 32 * (1 << (pad_delay - 1));
+}
+
+/**
+ * ieee80211_emlmr_trans_delay_in_us - Fetch the EMLMR Transition
+ * delay in microseconds
+ * @eml_cap: EML capabilities field value from common info field of
+ * the Multi-link element
+ * Return: the EMLMR Transition delay (in microseconds) encoded in the
+ * EML Capabilities field
+ */
+
+static inline u32 ieee80211_emlmr_trans_delay_in_us(u16 eml_cap)
+{
+ u32 emlmr_supp =
+ u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLMR_SUPP);
+
+ if (!emlmr_supp)
+ return 0;
+
+ /* IEEE Std 802.11be-2024 Table 9-417l—Encoding of the EMLSR
+ * Transition Delay subfield.
+ */
+ u32 trans_delay =
+ u16_get_bits(eml_cap,
+ IEEE80211_EML_CAP_EML_TRANSITION_DELAY);
+
+ /* invalid values also just use 0 */
+ if (!trans_delay ||
+ trans_delay > IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_256US)
+ return 0;
+
+ return 32 * (1 << (trans_delay - 1));
+}
+
/**
* ieee80211_eml_trans_timeout_in_us - Fetch the EMLSR Transition
* timeout value in microseconds
diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c
index 768bfc4e737d..1e412405ae44 100644
--- a/net/mac80211/eht.c
+++ b/net/mac80211/eht.c
@@ -217,11 +217,11 @@ void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
sta->sta.eml_cap =
u8_replace_bits(sta->sta.eml_cap,
pad_delay,
- IEEE80211_EML_CAP_EMLSR_PADDING_DELAY);
+ IEEE80211_EML_CAP_EML_PADDING_DELAY);
sta->sta.eml_cap =
u8_replace_bits(sta->sta.eml_cap,
trans_delay,
- IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY);
+ IEEE80211_EML_CAP_EML_TRANSITION_DELAY);
}
}
--
2.43.0
^ permalink raw reply related
* [PATCH v2 4/4] wifi: Update EML function documentation to remove EMLSR-specific references
From: Pablo Martin-Gomez @ 2026-04-09 13:50 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
In-Reply-To: <20260409135035.4018725-1-pmartin-gomez@freebox.fr>
Transition Timeout is not specific to EMLSR and is used by both EMLSR
and EMLMR modes.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
include/linux/ieee80211-eht.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h
index 82efd01d7708..92196018a950 100644
--- a/include/linux/ieee80211-eht.h
+++ b/include/linux/ieee80211-eht.h
@@ -1236,11 +1236,11 @@ static inline u32 ieee80211_emlmr_trans_delay_in_us(u16 eml_cap)
}
/**
- * ieee80211_eml_trans_timeout_in_us - Fetch the EMLSR Transition
+ * ieee80211_eml_trans_timeout_in_us - Fetch the EML Transition
* timeout value in microseconds
* @eml_cap: EML capabilities field value from common info field of
* the Multi-link element
- * Return: the EMLSR Transition timeout (in microseconds) encoded in
+ * Return: the EML Transition timeout (in microseconds) encoded in
* the EML Capabilities field
*/
--
2.43.0
^ permalink raw reply related
* [PATCH v2 2/4] wifi: Remove EMLMR Delay subfield definitions
From: Pablo Martin-Gomez @ 2026-04-09 13:50 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
In-Reply-To: <20260409135035.4018725-1-pmartin-gomez@freebox.fr>
In the finalized version of 802.11be-2024, the EMLMR delay values have
been merged into the EMLSR Padding/Transition Delay subfields and
therefore the EMLMR Delay subfield has been converted to a reserved field.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
include/linux/ieee80211-eht.h | 6 ------
1 file changed, 6 deletions(-)
diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h
index bbff10a05b48..6324d888073b 100644
--- a/include/linux/ieee80211-eht.h
+++ b/include/linux/ieee80211-eht.h
@@ -499,12 +499,6 @@ struct ieee80211_multi_link_elem {
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_128US 4
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US 5
#define IEEE80211_EML_CAP_EMLMR_SUPPORT 0x0080
-#define IEEE80211_EML_CAP_EMLMR_DELAY 0x0700
-#define IEEE80211_EML_CAP_EMLMR_DELAY_0US 0
-#define IEEE80211_EML_CAP_EMLMR_DELAY_32US 1
-#define IEEE80211_EML_CAP_EMLMR_DELAY_64US 2
-#define IEEE80211_EML_CAP_EMLMR_DELAY_128US 3
-#define IEEE80211_EML_CAP_EMLMR_DELAY_256US 4
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT 0x7800
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_0 0
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128US 1
--
2.43.0
^ permalink raw reply related
* [PATCH v2 1/4] wifi: Remove invalid 128TU transition timeout constant
From: Pablo Martin-Gomez @ 2026-04-09 13:50 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
In-Reply-To: <20260409135035.4018725-1-pmartin-gomez@freebox.fr>
In Table 9-417m of 802.11be-2024, Transition Timeout is defined for
values up to 10, representing 64 TUs. The value 11 is reserved and
does not correspond to a Transition Timeout of 128 TUs.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
drivers/net/wireless/intel/iwlwifi/mld/iface.c | 2 +-
include/linux/ieee80211-eht.h | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 46c8d943fd55..756b3ca36105 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -220,7 +220,7 @@ static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,
IEEE80211_EML_CAP_TRANSITION_TIMEOUT);
cmd->client.esr_transition_timeout =
- min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU,
+ min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU,
esr_transition_timeout);
cmd->client.medium_sync_delay =
cpu_to_le16(vif->cfg.eml_med_sync_delay);
diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h
index a97b1d01f3ac..bbff10a05b48 100644
--- a/include/linux/ieee80211-eht.h
+++ b/include/linux/ieee80211-eht.h
@@ -517,7 +517,6 @@ struct ieee80211_multi_link_elem {
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_16TU 8
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_32TU 9
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU 10
-#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU 11
#define IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS 0x000f
#define IEEE80211_MLD_CAP_OP_SRS_SUPPORT 0x0010
@@ -1178,7 +1177,7 @@ static inline u32 ieee80211_eml_trans_timeout_in_us(u16 eml_cap)
IEEE80211_EML_CAP_TRANSITION_TIMEOUT);
/* invalid values also just use 0 */
- if (!timeout || timeout > IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU)
+ if (!timeout || timeout > IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU)
return 0;
return 128 * (1 << (timeout - 1));
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/4] EML Capabilities compliance changes
From: Pablo Martin-Gomez @ 2026-04-09 13:50 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
There is some discrepencies between our codebase and the final version
of 802.11be-2024 regarding the EML Capabilities field. Given that no
driver supports EMLMR or tries to use 128TUs transition timeout, those
changes should not have any real impact.
---
v2:
- update the commit messages to reflect the changes made
- simplify `EMLSR_EMLMR` to `EML`
- add constants and helpers to interpret delay subfield in EMLMR mode
- add a small documentation fix
---
Pablo Martin-Gomez (4):
wifi: Remove invalid 128TU transition timeout constant
wifi: Remove EMLMR Delay subfield definitions
wifi: Rename EMLSR delay constants and add EMLMR helpers and
definitions
wifi: Update EML function documentation to remove EMLSR-specific
references
.../net/wireless/intel/iwlwifi/mld/iface.c | 2 +-
.../net/wireless/intel/iwlwifi/mld/mac80211.c | 4 +-
.../net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 +-
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 4 +-
include/linux/ieee80211-eht.h | 113 +++++++++++++++---
net/mac80211/eht.c | 4 +-
6 files changed, 103 insertions(+), 28 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH 3/3] wifi: Transition/Padding delay subfields are for both EMLSR and EMLMR
From: Johannes Berg @ 2026-04-09 13:14 UTC (permalink / raw)
To: Pablo MARTIN-GOMEZ; +Cc: linux-wireless
In-Reply-To: <1fdb014e-9b40-4e89-b90d-b4283338fd91@freebox.fr>
On Thu, 2026-04-09 at 11:04 +0200, Pablo MARTIN-GOMEZ wrote:
>
> What about `IEEE80211_EML_CAP_EMLSR_EMLMR_PADDING_DELAY`? It also has
> two tables for EMLSR (9-417i) and EMLMR (9-417k) but they have the same
> content (as of 802.11be). Do I just rename
> `IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_XUS` to
> `IEEE80211_EML_CAP_EML_PADDING_DELAY_XUS` or do I split it in two
> identical `IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_XUS` &
> `IEEE80211_EML_CAP_EMLMR_PADDING_DELAY_XUS` to future proof it in case
> of a change in a future amendment?
> >
It isn't going to change, this field will need to stay around like this
forever, only thing the spec could do is add new values (i.e. use some
currently reserved values), but even that is tricky ... So I don't think
we need to consider future changes here. I'd probably just rename it,
but it doesn't matter all that much?
johannes
^ permalink raw reply
* Re: [BUG] mt7921e: Intermittent connection failure
From: silverducks @ 2026-04-09 12:18 UTC (permalink / raw)
To: Sean Wang
Cc: linux-wireless, nbd, lorenzo, ryder.lee, shayne.chen, sean.wang,
matthias.bgg, angelogioacchino.delregno,
moderated list:ARM/Mediatek SoC support
In-Reply-To: <CAGp9LzrfD+a84ZVGjUnrv7KYCpgfe88NyrXos8wW8U7aKM8BZw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 4164 bytes --]
On 02/04/2026 01:58, Sean Wang wrote:
> I think the current test setup is still mixing too many variables, so
> it is hard to tell what is actually triggering the issue.
>
> In particular, if the goal is to test the NetworkManager path, the
> script should not also manually manage wpa_supplicant, and iwd should
> not be part of the same test either. NetworkManager normally manages
> the Wi-Fi backend itself, so mixing manual wpa_supplicant handling,
> iwd, and NetworkManager in one setup makes the result difficult to
> interpret.
> Could you first simplify the setup and test one path at a time?
> If you want to test NetworkManager, use only NetworkManager, for
> example by using nmcli to explicitly control the connection steps.
> If you want to test plain wpa_supplicant, stop NetworkManager
> completely and use only wpa_supplicant + wpa_cli. I would suggest
> starting with this path, since that is also the setup I usually use
> for testing.
> If you want to test iwd, please test it separately as well.
Some confusion here: I have indeed tested them separately. I left in
wpa_supplicant start in that particular script by mistake. Extra stop
commands
for iwd and supplicant at the end of the loop are also redundant. I
don't think
either affects outcome of the tests, but just in case, I'll clean up the
test
scripts and rerun.
I am also attaching all scripts used alongside the resulting logs for
clarity.
> Also suggest to avoid suspend/resume or hibernation for now.
> The log you shared includes a clear S4 resume path (ACPI: PM: Waking
> up from system sleep state S4 and pci_pm_restore returns -110), which
> does not match a simple reconnect or module reload test.
Those are the logs I collected when originally diagnosing the issue. Scripts
reload kernel module instead because I since realised that this is enough to
trigger the bug and it's much faster to do it this way when searching for a
failstate.
I am attaching logs where module reload is used for all cases.
------------
New data
------------
After some additional testing, I noticed simply reconnecting enough
times seems
to trigger the bug as well. I'll attach an additional script for this too.
Given this, I've looked through the code relevant to connection process. I
think I may have found something of interest. The following patch gets
rid of
original lockup state, but introduces a new error into dmesg.
---
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 462b4a68c4f0..47621ff13839 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -1788,7 +1788,7 @@ int mt76_connac_mcu_cancel_hw_scan(struct mt76_phy
*phy,
}
return mt76_mcu_send_msg(phy->dev, MCU_CE_CMD(CANCEL_HW_SCAN),
- &req, sizeof(req), false);
+ &req, sizeof(req), true);
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_cancel_hw_scan);
--
2.50.1
Seems like making this call wait for the response is fixing the issue at
least
partially. Judging from the relevant logs, it effectively makes the driver
reset on it's own whenever there's a problem.
Logs of two runs with a kernel compiled with this patch are attached.
One for reconnecting variant, one for module reload variant.
------------
Attachments
------------
I've structured the archive with a subfolder for each testing run, each
containing the relevant logs and the script, if applicable. Overview:
iwd and wpa_supplicant -only setups work as expected.
NetworkManager with module reload ends up in failstate.
NetworkManager with reconnection ends up in failstate.
Both of the latter start to have additional driver reloads instead of
locking
when run with patched kernel.
Finally, I am also attaching a case where problem occurred immediately
after boot.
All failed cases were ended after confirming the failstate.
All successful cases were interrupted manually after a few minutes.
[-- Attachment #2: attachments.tar.gz --]
[-- Type: application/gzip, Size: 712828 bytes --]
^ permalink raw reply related
* Re: [PATCH mt76 v2] wifi: mt76: mt7996: limit work in set_bitrate_mask
From: Lorenzo Bianconi @ 2026-04-09 10:48 UTC (permalink / raw)
To: dylan.eskew; +Cc: nbd, ryder.lee, linux-wireless, shayne.chen, sean.wang
In-Reply-To: <20260408145057.2356878-2-dylan.eskew@candelatech.com>
[-- Attachment #1: Type: text/plain, Size: 2203 bytes --]
> From: Dylan Eskew <dylan.eskew@candelatech.com>
>
> Calls to mt7996_set_bitrate_mask() would propagate work for all stations
> on the ieee80211_hw regardless of the vif specified in the call. To
> prevent unnecessary work in FW, limit setting the sta_rate to only the
> specified vif in mt7996_sta_rate_ctrl_update().
>
> Fixes: afff4325548f0 ("wifi: mt76: mt7996: Use proper link_id in link_sta_rc_update callback")
> Signed-off-by: Dylan Eskew <dylan.eskew@candelatech.com>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> v2:
> - Assign to correct tree
> - Fix spelling error in commit message
> ---
> drivers/net/wireless/mediatek/mt76/mt7996/main.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
> index a8a6552d49f6..26b8c91db0a8 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
> @@ -1959,7 +1959,11 @@ static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta)
> {
> struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
> struct mt7996_sta_link *msta_link;
> - u32 *changed = data;
> + struct mt7996_vif *mvif = data;
> + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
> +
> + if (msta->vif != mvif)
> + return;
>
> msta_link = rcu_dereference(msta->link[msta->deflink_id]);
> if (msta_link)
> @@ -1972,7 +1976,6 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> {
> struct mt7996_dev *dev = mt7996_hw_dev(hw);
> struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
> - u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
>
> mvif->deflink.bitrate_mask = *mask;
>
> @@ -1985,7 +1988,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
> * then multiple MCS setting (MCS 4,5,6) is not supported.
> */
> ieee80211_iterate_stations_atomic(hw, mt7996_sta_rate_ctrl_update,
> - &changed);
> + mvif);
> ieee80211_queue_work(hw, &dev->rc_work);
>
> return 0;
> --
> 2.52.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support
From: Rameshkumar Sundaram @ 2026-04-09 10:34 UTC (permalink / raw)
To: Maharaja Kennadyrajan, ath12k; +Cc: linux-wireless
In-Reply-To: <20260331142446.2951809-1-maharaja.kennadyrajan@oss.qualcomm.com>
On 3/31/2026 7:54 PM, Maharaja Kennadyrajan wrote:
> Patch 1 handles the firmware stats event so we can track the current
> temperature and throttle level per pdev without spamming logs.
>
> Patch 2 enables thermal throttling at bring-up and programs default level
> tables to firmware via WMI_THERM_THROT_SET_CONF_CMDID; the driver picks
> IPA/XFEM defaults based on the firmware WMI service bitmap, supports 4 or 5
> levels as advertised, and only fills optional fields (pout reduction,
> tx chain mask) when the corresponding WMI service bits are present.
>
> Patch 3 refactors per-radio thermal hwmon cleanup to reduce code duplication and
> ensure consistent cleanup across thermal register and unregister paths.
>
> Patch 4 reorders the group teardown logic symmetric for safe thermal sysfs cleanup.
>
> Patch 5 exposes a thermal cooling device per radio so the kernel thermal
> framework or userspace can set the TX duty-cycle off percentage; writes
> are validated against the throttling state range and host state is kept in
> sync with successful firmware updates.
>
> Examples:
> echo 40 > /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_stat
> cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_state
> cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/max_state
>
> Maharaja Kennadyrajan (5):
> wifi: ath12k: handle thermal throttle stats WMI event
> wifi: ath12k: configure firmware thermal throttling via WMI
> wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup
> wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup
> wifi: ath12k: add thermal cooling device support
>
> drivers/net/wireless/ath/ath12k/core.c | 50 +++--
> drivers/net/wireless/ath/ath12k/mac.c | 9 +
> drivers/net/wireless/ath/ath12k/thermal.c | 252 ++++++++++++++++++----
> drivers/net/wireless/ath/ath12k/thermal.h | 35 +++
> drivers/net/wireless/ath/ath12k/wmi.c | 107 +++++++++
> drivers/net/wireless/ath/ath12k/wmi.h | 50 +++++
> 6 files changed, 446 insertions(+), 57 deletions(-)
>
>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH 3/3] wifi: Transition/Padding delay subfields are for both EMLSR and EMLMR
From: Pablo MARTIN-GOMEZ @ 2026-04-09 9:04 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <f60497d95314a9a595d0830cf47de8533674e811.camel@sipsolutions.net>
On 08/04/2026 14:07, Johannes Berg wrote:
> On Tue, 2026-04-07 at 17:47 +0200, Pablo MARTIN-GOMEZ wrote:
>> On 07/04/2026 16:00, Johannes Berg wrote:
>>> On Fri, 2026-03-27 at 21:11 +0100, Pablo Martin-Gomez wrote:
>>>> -#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY 0x0070
>>>> +#define IEEE80211_EML_CAP_EMLSR_EMLMR_TRANSITION_DELAY 0x0070
>>>> #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_0US 0
>>>> #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_16US 1
>>>> #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_32US 2
>>>
>>> I think this is confusing. You have the "EMLSR_EMLMR_" prefix in the
>>> definition for the mask, but not in the values, but also the prefix
>>> itself gets very long, not sure what to do about that. Maybe just
>>> ..._EML_TRANSITION_DELAY even if it doesn't match the spec completely.
>> In the standard, there is two different tables to convert the field
>> value to a delay: 9-417j for EMLSR and 9-417l for EMLMR. E.g. if the
>> field has the value 1, in EMLSR mode, it's a 16 µs delay, in EMLMR mode,
>> it's a 32 µs delay.
>
> Ouch, good catch. Why do they just want to make everyone's life harder
> all the time...
>
>> As no driver implements EMLMR, I was expecting the first one to
>> implement it to create the defines:
>> ```
>> #define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_0US 0
>> #define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_32US 1
>> [...]
>> ```
>
> That seems dangerous, you have to really look hard at the spec to really
> notice?
>
>> If you prefer, I can implement it +
>> `ieee80211_emlmr_[trans/pad]_delay_in_us` but it will be dead code for now.
>
> Dead code in form of a bunch of defines doesn't seem so bad I guess?
>
> But I still think the naming is confusing. Maybe we just drop the
> "EMLSR_EMLMR_" from the *mask* define, and have it only for the
> individual defines, as say
>
> #define IEEE80211_EML_CAP_EML_TRANSITION_DELAY 0x0070
> #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_0US 0
> #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_16US 1
> #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_32US 2
> ...
> #define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_32US 1
> ...
>
> I think that'd also call out that in fact those are (needlessly)
> different.
What about `IEEE80211_EML_CAP_EMLSR_EMLMR_PADDING_DELAY`? It also has
two tables for EMLSR (9-417i) and EMLMR (9-417k) but they have the same
content (as of 802.11be). Do I just rename
`IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_XUS` to
`IEEE80211_EML_CAP_EML_PADDING_DELAY_XUS` or do I split it in two
identical `IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_XUS` &
`IEEE80211_EML_CAP_EMLMR_PADDING_DELAY_XUS` to future proof it in case
of a change in a future amendment?
> > johannes
Pablo MG
^ permalink raw reply
* nl80211: missing minimum TX power attribute causes misleading userspace behavior
From: Steffen May @ 2026-04-09 8:05 UTC (permalink / raw)
To: linux-wireless
This issue was discovered during the analysis of two documented OpenWrt
bugs. Bug 1 is a type comparison error in LuCI wireless.js that causes 0
dBm to be displayed incorrectly. Bug 2 is in ucode mac80211.sh where the
value 0 is treated as falsy, causing the router to transmit at maximum
power instead. Both bugs are proven and reported.
During verification of Bug 2 on five devices with three different
chipsets, it became apparent that the hardware floor is device-dependent
and completely unknown to the stack. This is not a bug but a missing
feature.
Because the floor is unknown, iwinfo generates selection lists
containing values that have no real effect on the actual output power.
Userspace accepts configurations such as 0 dBm even though the hardware
cannot apply them. The system reports success while the hardware remains
at its minimum supported level. This creates false assumptions.
Measurements
The deviation between the requested transmit power and the actual
hardware floor is not constant but depends on the hardware:
Device Chipset Requested Actual floor
Difference
Buffalo WZR-600DHP Atheros AR7161 1 dBm 3 dBm +2
dB
TP-Link Archer C7 Qualcomm QCA9558 1 dBm 5 dBm +4
dB
OpenWrt One MTK Filogic 1 dBm 1 dBm 0
dB
Cudy WR3000 MTK Filogic 1 dBm 1 dBm 0
dB
GL.iNet GL-MT6000 MTK Filogic 1 dBm 1 dBm 0
dB
Concrete example: Buffalo WZR-600DHP with Atheros AR7161
Because the system does not know the actual floor, the list generated by
iwinfo contains values with no real effect:
0 dBm (1 mW) reported, hardware remains at floor
1 dBm (1 mW) reported, no actual reduction
2 dBm (1 mW) reported, no actual reduction
3 dBm (1 mW) actual hardware floor
4 dBm (2 mW) first real power step above the floor
Proposed solution
Introduction of a new nl80211 attribute NL80211_ATTR_WIPHY_TX_POWER_MIN.
nl80211 already exposes the maximum transmit power. Minimum and maximum
are both fixed hardware properties -- it is inconsistent that only one
of the two values is available.
No patch is provided. This mail is intended to describe the problem and
to encourage interest in an implementation.
^ permalink raw reply
* Re: [PATCH v10 00/21] wifi: nxpwifi: create nxpwifi to support
From: Johannes Berg @ 2026-04-09 6:54 UTC (permalink / raw)
To: Jeff Chen; +Cc: linux-wireless, linux-kernel, francesco, wyatt.hsu, s.hauer
In-Reply-To: <adcblxRaTTyEvVWN@nxpwireless-Inspiron-14-Plus-7440>
Hi Jeff,
On Thu, 2026-04-09 at 11:23 +0800, Jeff Chen wrote:
> I will keep the SDIO ID change in the current nxpwifi patch series so it
> builds independently in wireless-next during review. For the final merge,
> I will send a pull request and include a precise cherry-pick of the SDIO ID
> commit from the mmc tree.
I suppose I should have thought of this earlier (but I didn't pay much
attention to the timing when I had just come back from vacation); at
this point, unless all of that happens _really_ quickly (the next day or
so!), it doesn't matter because the merge window will open on Sunday,
and then we'd get that commit anyway from 7.1-rc.
johannes
^ permalink raw reply
* Re: [PATCH v10 00/21] wifi: nxpwifi: create nxpwifi to support
From: Jeff Chen @ 2026-04-09 3:23 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, linux-kernel, francesco, wyatt.hsu, s.hauer
In-Reply-To: <4ccf21800166b28cc28c72b0b1cf2672b43af8a9.camel@sipsolutions.net>
On Tue, Apr 07, 2026 at 11:06:36 AM +0200, Johannes Berg wrote:
> On Wed, 2026-03-25 at 21:45 +0800, Jeff Chen wrote:
> > Ulf prefers not to rebase or drop the SDIO ID patch from the mmc tree,
> > and his suggestion is for me to include an identical copy of that patch
> > in the nxpwifi series for wireless-next, so that the driver can build
> > independently during review. Since the change is trivial, he expects git
> > to resolve the duplication automatically when the wireless and mmc trees
> > are merged.
> > Would this approach be acceptable for wireless-next?
>
> Yeah I guess we don't have a choice ...
>
> > For reference, please see Ulf’s feedback here:
> >
> > https://patchwork.kernel.org/project/linux-mmc/patch/20260113031517.244714-1-jeff.chen_1@nxp.com/
>
> So I think we said before that for final merge you should send a pull
> request anyway instead of all the individual patches.
>
> Can you include it in the pull request, as a precise cherry-pick from
> his tree?
>
> johannes
>
Hi Johannes,
Thanks for the clarification.
I will keep the SDIO ID change in the current nxpwifi patch series so it
builds independently in wireless-next during review. For the final merge,
I will send a pull request and include a precise cherry-pick of the SDIO ID
commit from the mmc tree.
Best regards,
Jeff
^ permalink raw reply
* RE: [PATCH] wifi: rtw89: fix wrong pci_get_drvdata type in AER handlers
From: Ping-Ke Shih @ 2026-04-09 3:17 UTC (permalink / raw)
To: Christos Longros
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260329073857.113081-1-chris.longros@gmail.com>
Christos Longros <chris.longros@gmail.com> wrote:
> rtw89 stores an ieee80211_hw pointer via pci_set_drvdata() at probe
> time, but io_error_detected() and io_resume() retrieve it as a
> net_device pointer. This causes netif_device_detach/attach to
> operate on an ieee80211_hw struct, reading and writing at wrong
> offsets. The adjacent io_slot_reset() already does it correctly.
>
> Use ieee80211_stop_queues/wake_queues instead, consistent with
> every other queue stop/start path in the driver.
>
> Tested on RTL8852CE by calling the handlers from a test module
> before and after the fix.
Could you share the "test module" you did? I'd like to know how to
test (simulate) the io error.
>
> Signed-off-by: Christos Longros <chris.longros@gmail.com>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
^ permalink raw reply
* [PATCH wireless-next] wifi: cfg80211: Explicitly include <linux/export.h> in michael-mic.c
From: Eric Biggers @ 2026-04-09 3:03 UTC (permalink / raw)
To: linux-wireless, Johannes Berg; +Cc: linux-kernel, Eric Biggers
This happened to be included transitively via a long chain starting with
<linux/bitops.h>, but it's less fragile to include it explicitly.
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
net/wireless/michael-mic.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/wireless/michael-mic.c b/net/wireless/michael-mic.c
index 50cdb67f0503d..ec5164756e0a1 100644
--- a/net/wireless/michael-mic.c
+++ b/net/wireless/michael-mic.c
@@ -3,10 +3,11 @@
* Michael MIC implementation - optimized for TKIP MIC operations
* Copyright 2002-2003, Instant802 Networks, Inc.
*/
#include <linux/types.h>
#include <linux/bitops.h>
+#include <linux/export.h>
#include <linux/ieee80211.h>
#include <linux/unaligned.h>
struct michael_mic_ctx {
u32 l, r;
base-commit: 929298742bb5dfacd53ea99cf989cc81210f90a3
--
2.53.0
^ permalink raw reply related
* Re: [GIT PULL] wireless-2026-04-08
From: patchwork-bot+netdevbpf @ 2026-04-09 2:00 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, linux-wireless
In-Reply-To: <20260408081802.111623-3-johannes@sipsolutions.net>
Hello:
This pull request was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Wed, 8 Apr 2026 10:15:25 +0200 you wrote:
> Hi,
>
> So in a way I'd hoped it wouldn't come to this, but while I
> was out last week a couple of things came in that seemed
> relevant enough to squeeze in now. I guess it wouldn't be
> much of an issue if not, but I figured I'd try anyway :)
>
> [...]
Here is the summary with links:
- [GIT,PULL] wireless-2026-04-08
https://git.kernel.org/netdev/net/c/d65b175cfac6
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: mt7921e: repeated driver own failures cause hanging
From: Vinicius @ 2026-04-09 1:41 UTC (permalink / raw)
To: Sean Wang, moosager90
Cc: linux-wireless, moderated list:ARM/Mediatek SoC support
In-Reply-To: <CAGp9Lzp8NjVwkn2atNqT9x9-QTkWMLxK+Ntkb=3Z1fWcM6N=7A@mail.gmail.com>
Hi Sean,
moosager90 recommended me to reply to this thread when we were talking
about a bug reported at
https://bugzilla.kernel.org/show_bug.cgi?id=220353
This bug reported in bugzilla is related to mt7921e raising several
"driver own failed" messages in dmesg. But it seems this same bug is
also affecting me through **mt7925e** driver.
Before posting below a log from my dmesg, which also shows several
"driver own failed" messages, I would like to say in advance that yes,
the bug was occurring every time my computer suspended. I first tried
to disable the auto-suspend feature of my linux distro and the bug
stopped happening too often, but was still happening sometimes. In
~March 28th the bug was being triggered too much (even without the
computer being suspended), freezing my computer and requiring a reboot.
So I tried adding `options mt7925e disable_aspm=1` to
`/etc/modprobe.d/mt7925e.conf`. I think this is what you suggested when
you said "could you also try disabling PCIe ASPM...".
**Since I added this line to disable ASPM, I have not experienced more
freezes. It seems this line really fixed the issue.**
Below you can find all the information about the kernel version I'm
using, as well as the driver version, firmware version and logs from
dmesg (a copy-paste from my message at bugzilla on March 25th):
```
# uname -a
Linux arch 6.19.9-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 19 Mar 2026
16:33:17 +0000 x86_64 GNU/Linux
---
# journalctl -k -g mt7925e -b -1
mar 25 09:00:09 arch kernel: mt7925e 0000:01:00.0: enabling device
(0000 -> 0002)
mar 25 09:00:09 arch kernel: mt7925e 0000:01:00.0: ASIC revision:
79250000
mar 25 09:00:09 arch kernel: mt7925e 0000:01:00.0: HW/SW Version:
0x8a108a10, Build Time: 20260106153007a
mar 25 09:00:09 arch kernel: mt7925e 0000:01:00.0: WM Firmware Version:
____000000, Build Time: 20260106153120
mar 25 09:00:10 arch kernel: mt7925e 0000:01:00.0 wlp1s0: renamed from
wlan0
mar 25 10:25:16 arch kernel: mt7925e 0000:01:00.0: driver own failed
mar 25 10:25:17 arch kernel: mt7925e 0000:01:00.0: driver own failed
...
mar 25 10:25:42 arch kernel: mt7925e 0000:01:00.0: chip reset failed
mar 25 10:25:45 arch kernel: mt7925e 0000:01:00.0: Message 00020001
(seq 9) timeout
mar 25 10:25:48 arch kernel: mt7925e 0000:01:00.0: Message 00020004
(seq 10) timeout
mar 25 10:25:51 arch kernel: mt7925e 0000:01:00.0: Message 00020002
(seq 11) timeout
mar 25 10:25:54 arch kernel: mt7925e 0000:01:00.0: Message 00020001
(seq 12) timeout
mar 25 10:25:57 arch kernel: mt7925e 0000:01:00.0: Message 00020004
(seq 13) timeout
mar 25 10:25:58 arch kernel: mt7925e 0000:01:00.0: driver own failed
mar 25 10:26:00 arch kernel: mt7925e 0000:01:00.0: Message 00020027
(seq 14) timeout
mar 25 10:26:04 arch kernel: mt7925e 0000:01:00.0: driver own failed
mar 25 10:26:06 arch kernel: mt7925e 0000:01:00.0: driver own failed
mar 25 10:26:07 arch kernel: mt7925e 0000:01:00.0: driver own failed
mar 25 10:26:08 arch kernel: mt7925e 0000:01:00.0: driver own failed
mar 25 10:26:14 arch kernel: mt7925e 0000:01:00.0: Message 00020003
(seq 15) timeout
---
# modinfo mt7925e
filename: /lib/modules/6.19.9-arch1-
1/kernel/drivers/net/wireless/mediatek/mt76/mt7925/mt7925e.ko.zst
license: Dual BSD/GPL
description: MediaTek MT7925E (PCIe) wireless driver
author: Lorenzo Bianconi <lorenzo@kernel.org>
author: Deren Wu <deren.wu@mediatek.com>
firmware: mediatek/mt7925/WIFI_MT7925_PATCH_MCU_1_1_hdr.bin
firmware: mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin
srcversion: CB386A1D0C64CC7F4C723D9
alias: pci:v000014C3d00000717sv*sd*bc*sc*i*
alias: pci:v000014C3d00007925sv*sd*bc*sc*i*
depends: mt792x-lib,mt76-connac-lib,mt76,mt7925-common,rfkill
intree: Y
name: mt7925e
retpoline: Y
vermagic: 6.19.9-arch1-1 SMP preempt mod_unload
sig_id: PKCS#7
signer: Build time autogenerated kernel key
sig_key:
6F:56:40:C8:60:B3:9B:AD:DF:E1:84:24:BC:D4:A9:07:90:4E:7B:C6
sig_hashalgo: sha512
signature:
30:64:02:30:54:34:C4:0C:8E:A8:D6:EB:38:02:71:55:A3:09:CA:39:
28:2E:F3:8C:2C:38:AB:26:89:8A:B7:3E:66:77:40:26:C7:D5:
13:12:
CA:39:F4:99:1A:EC:3B:0C:D4:8D:D7:C0:02:30:62:78:5D:DC:
90:B7:
57:5E:CE:F5:64:99:EF:AC:B9:3B:B7:00:3E:EE:BA:B7:81:C2:
8F:2C:
DF:B2:F4:64:3C:76:54:24:0B:70:72:56:80:7E:28:5A:56:71:
45:D6:
A7:57
parm: disable_aspm:disable PCI ASPM support (bool)
```
Best regards.
On Sat, 2026-01-03 at 01:10 -0600, Sean Wang wrote:
> Hi moosager90,
>
> Could you please share more details on how this issue can be
> reproduced? For example, does it occur after suspend/resume, under
> heavy traffic, or during normal runtime? Also, please confirm which
> firmware version you are using.
>
> It would be helpful to test with the latest linux-firmware version:
> https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/commit/mediatek?id=5cf85776762a544ad27c4447b61eaabb0d4716e7
>
> As a debugging step, could you also try disabling PCIe ASPM and check
> whether the issue still occurs?
>
> Sean
>
> On Fri, Jan 2, 2026 at 4:35 AM moosager90 <moosager90@gmail.com>
> wrote:
> >
> > Hello,
> >
> > There is an issue with mt7921e which causes repeated failures in
> > chip resets,
> > bringing the network down and causing hanging on every command or
> > action on the
> > system; the only workaround is to reboot. This is what the kernel
> > output looks
> > like at those times:
> >
> > mt7921e 0000:62:00.0: driver own failed
> > kernel: mt7921e 0000:62:00.0: Timeout for driver own
> > kernel: mt7921e 0000:62:00.0: driver own failed
> > kernel: mt7921e 0000:62:00.0: Timeout for driver own
> > kernel: mt7921e 0000:62:00.0: driver own failed
> > kernel: mt7921e 0000:62:00.0: Timeout for driver own
> > kernel: mt7921e 0000:62:00.0: driver own failed
> > kernel: mt7921e 0000:62:00.0: chip reset failed
> > kernel: mt7921e 0000:62:00.0: Timeout for driver own
> > kernel: Console: switching to colour frame buffer device 360x112
> > kernel: fbcon: Taking over console
> > kernel: mt7921e 0000:62:00.0: Message 00020001 (seq 1) timeout
> >
> > I have observed the issue on many untainted kernels, and I have had
> > it happen on
> > vanilla kernels not provided by my distro as well.
> >
> > Mine and some other people's reports are available at [1]. More
> > logs of the
> > issue on my system are in the attachments of my original report on
> > the Red Hat
> > Bugzilla [2].
> >
> > In the past, the issue was reported at [3], which resulted in a
> > patch [4] that
> > only keeps the system running instead of panicking. This means the
> > driver still
> > causes system hangs.
> >
> > I still don't know the root cause of the issue and I would like to
> > get to the
> > bottom of this; any help or guidance is appreciated. Crucially, I
> > have not found
> > a way to reproduce the issue at will.
> >
> > Best regards.
> >
> > [1] https://bugzilla.kernel.org/show_bug.cgi?id=220353
> > [2] https://bugzilla.redhat.com/show_bug.cgi?id=2411854
> > [3]
> > https://lore.kernel.org/linux-wireless/VE1PR04MB64945C660A81D38F290E4A4BE59F9@VE1PR04MB6494.eurprd04.prod.outlook.com/T/#u
> > [4]
> > https://patchwork.kernel.org/project/linux-wireless/patch/727eb5ffd3c7c805245e512da150ecf0a7154020.1659452909.git.deren.wu@mediatek.com/
> >
> >
> >
> >
>
^ permalink raw reply
* [PATCH][next] wifi: iwlwifi: mld: rx: Avoid a -Wflex-array-member-not-at-end warning
From: Gustavo A. R. Silva @ 2026-04-08 20:20 UTC (permalink / raw)
To: Miri Korenblit
Cc: linux-wireless, linux-kernel, Gustavo A. R. Silva,
linux-hardening
-Wflex-array-member-not-at-end was introduced in GCC-14, and we are
getting ready to enable it, globally.
Use an anonymous embedded struct (enabled via -fms-extensions) to split the
header portion from the flexible-array member in struct iwl_rxq_sync_cmd,
so the new header type struct iwl_rxq_sync_cmd_hdr can be referenced
independently (of the flexible-array member), and fix the following
warning:
drivers/net/wireless/intel/iwlwifi/mld/rx.c:2172:41: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]
Add comments and a static_assert() to prevent adding new members
directly to struct iwl_rxq_sync_cmd. Any new members must be added
to struct iwl_rxq_sync_cmd_hdr instead. This preserves the expected
memory layout between the header and the flexible-array member.
Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
---
.../net/wireless/intel/iwlwifi/fw/api/rx.h | 21 ++++++++++++++++---
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 2 +-
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
index ac6c1ef2cbcd..02a5bacfb175 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
@@ -918,19 +918,34 @@ struct iwl_rss_config_cmd {
#define IWL_MULTI_QUEUE_SYNC_SENDER_MSK 0xf
/**
- * struct iwl_rxq_sync_cmd - RXQ notification trigger
+ * struct iwl_rxq_sync_cmd_hdr - RXQ notification trigger header
*
* @flags: flags of the notification. bit 0:3 are the sender queue
* @rxq_mask: rx queues to send the notification on
* @count: number of bytes in payload, should be DWORD aligned
- * @payload: data to send to rx queues
*/
-struct iwl_rxq_sync_cmd {
+struct iwl_rxq_sync_cmd_hdr {
__le32 flags;
__le32 rxq_mask;
__le32 count;
+} __packed;
+
+/**
+ * struct iwl_rxq_sync_cmd - RXQ notification trigger
+ *
+ * (NOTE: New members MUST NOT be added directly to this struct. Add them to
+ * struct iwl_rxq_sync_cmd_hdr instead.)
+ *
+ * @iwl_rxq_sync_cmd_hdr: anonymous embedded header - members are directly
+ * accessible
+ * @payload: data to send to rx queues
+ */
+struct iwl_rxq_sync_cmd {
+ struct iwl_rxq_sync_cmd_hdr;
u8 payload[];
} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */
+static_assert(offsetof(struct iwl_rxq_sync_cmd, payload) == sizeof(struct iwl_rxq_sync_cmd_hdr),
+ "New members must be added to struct iwl_rxq_sync_cmd_hdr instead.");
/**
* struct iwl_rxq_sync_notification - Notification triggered by RXQ
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index a2e586c6ea67..492c03b8e5a0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -2172,7 +2172,7 @@ void iwl_mld_sync_rx_queues(struct iwl_mld *mld,
{
u8 num_rx_queues = mld->trans->info.num_rxqs;
struct {
- struct iwl_rxq_sync_cmd sync_cmd;
+ struct iwl_rxq_sync_cmd_hdr sync_cmd;
struct iwl_mld_internal_rxq_notif notif;
} __packed cmd = {
.sync_cmd.rxq_mask = cpu_to_le32(BIT(num_rx_queues) - 1),
--
2.43.0
^ permalink raw reply related
* [PATCH][next] wifi: iwlwifi: mld: tlc: Avoid a -Wflex-array-member-not-at-end warning
From: Gustavo A. R. Silva @ 2026-04-08 20:19 UTC (permalink / raw)
To: Miri Korenblit
Cc: linux-wireless, linux-kernel, Gustavo A. R. Silva,
linux-hardening
-Wflex-array-member-not-at-end was introduced in GCC-14, and we are
getting ready to enable it, globally.
Use an anonymous embedded struct (enabled via -fms-extensions) to split
the header portion from the flexible-array member in struct iwl_dhc_cmd,
so the new header type struct iwl_dhc_cmd_hdr can be referenced
independently (of the flexible-array member), and fix the following
warning:
drivers/net/wireless/intel/iwlwifi/mld/tlc.c:544:36: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]
Add comments and a static_assert() to prevent adding new members
directly to struct iwl_dhc_cmd. Any new members must be added to
struct iwl_dhc_cmd_hdr instead. This preserves the expected memory
layout between the header and the flexible-array member.
Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
---
.../net/wireless/intel/iwlwifi/fw/api/dhc.h | 21 ++++++++++++++++---
drivers/net/wireless/intel/iwlwifi/mld/tlc.c | 2 +-
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h
index b6d79c678cd8..bce4097fee27 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h
@@ -51,7 +51,7 @@ enum iwl_dhc_umac_integration_table {
#define DHC_TARGET_UMAC BIT(27)
/**
- * struct iwl_dhc_cmd - debug host command
+ * struct iwl_dhc_cmd_hdr - debug host command header
* @length: length in DWs of the data structure that is concatenated to the end
* of this struct
* @index_and_mask: bit 31 is 1 for data set operation else it's 0
@@ -62,14 +62,29 @@ enum iwl_dhc_umac_integration_table {
* bit 26 is 0 if the cmd targeted to LMAC0 and 1 if targeted to LMAC1,
* relevant only if bit 27 set to 0
* bits 0-25 is a specific entry index in the table specified in bits 28-30
+ */
+struct iwl_dhc_cmd_hdr {
+ __le32 length;
+ __le32 index_and_mask;
+} __packed;
+
+/**
+ * struct iwl_dhc_cmd - debug host command
+ *
+ * (NOTE: New members MUST NOT be added directly to this struct. Add them to
+ * struct iwl_dhc_cmd_hdr instead.)
*
+ * @iwl_dhc_cmd_hdr: anonymous embedded header - members are directly
+ * accessible
* @data: the concatenated data.
+ *
*/
struct iwl_dhc_cmd {
- __le32 length;
- __le32 index_and_mask;
+ struct iwl_dhc_cmd_hdr;
__le32 data[];
} __packed; /* DHC_CMD_API_S */
+static_assert(offsetof(struct iwl_dhc_cmd, data) == sizeof(struct iwl_dhc_cmd_hdr),
+ "New members must be added to struct iwl_dhc_cmd_hdr instead.");
/**
* struct iwl_dhc_payload_hdr - DHC payload header
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
index 78d6162d9297..b6f41a8c138e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
@@ -619,7 +619,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data)
{
struct {
- struct iwl_dhc_cmd dhc;
+ struct iwl_dhc_cmd_hdr dhc;
struct iwl_dhc_tlc_cmd tlc;
} __packed cmd = {
.tlc.sta_id = sta_id,
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox