summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2025-11-12 20:33:23 +0300
committerJakub Kicinski <kuba@kernel.org>2025-11-12 20:33:24 +0300
commite949824730daf381bd0fe37e1a77458e0c26e8df (patch)
treeb140784ef0b46fd97b278ec1ebcb9c49a23975b1 /net
parent7e975caa0f7bf2f83a5e18b4ae69c3d7fff4eafa (diff)
parent0eb272033b64ef05fffa30288284659c33e17830 (diff)
downloadlinux-e949824730daf381bd0fe37e1a77458e0c26e8df.tar.xz
Merge tag 'wireless-next-2025-11-12' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next
Johannes Berg says: ==================== More -next material, notably: - split ieee80211.h file, it's way too big - mac80211: initial chanctx work towards NAN - mac80211: MU-MIMO sniffer improvements - ath12k: statistics improvements * tag 'wireless-next-2025-11-12' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next: (26 commits) wifi: cw1200: Fix potential memory leak in cw1200_bh_rx_helper() wifi: mac80211: make monitor link info check more specific wifi: mac80211: track MU-MIMO configuration on disabled interfaces wifi: cfg80211/mac80211: Add fallback mechanism for INDOOR_SP connection wifi: cfg80211/mac80211: clean up duplicate ap_power handling wifi: cfg80211: use a C99 initializer in wiphy_register wifi: cfg80211: fix doc of struct key_params wifi: mac80211: remove unnecessary vlan NULL check wifi: mac80211: pass frame type to element parsing wifi: mac80211: remove "disabling VHT" message wifi: mac80211: add and use chanctx usage iteration wifi: mac80211: simplify ieee80211_recalc_chanctx_min_def() API wifi: mac80211: remove chanctx to link back-references wifi: mac80211: make link iteration safe for 'break' wifi: mac80211: fix EHT typo wifi: cfg80211: fix EHT typo wifi: ieee80211: split NAN definitions out wifi: ieee80211: split P2P definitions out wifi: ieee80211: split S1G definitions out wifi: ieee80211: split EHT definitions out ... ==================== Link: https://patch.msgid.link/20251112115126.16223-4-johannes@sipsolutions.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/agg-rx.c7
-rw-r--r--net/mac80211/cfg.c47
-rw-r--r--net/mac80211/chan.c397
-rw-r--r--net/mac80211/driver-ops.c8
-rw-r--r--net/mac80211/he.c6
-rw-r--r--net/mac80211/ibss.c14
-rw-r--r--net/mac80211/ieee80211_i.h50
-rw-r--r--net/mac80211/iface.c46
-rw-r--r--net/mac80211/link.c5
-rw-r--r--net/mac80211/main.c3
-rw-r--r--net/mac80211/mesh.c26
-rw-r--r--net/mac80211/mesh_hwmp.c7
-rw-r--r--net/mac80211/mesh_plink.c7
-rw-r--r--net/mac80211/mlme.c71
-rw-r--r--net/mac80211/parse.c30
-rw-r--r--net/mac80211/scan.c6
-rw-r--r--net/mac80211/tdls.c12
-rw-r--r--net/mac80211/tests/elems.c4
-rw-r--r--net/mac80211/util.c35
-rw-r--r--net/wireless/core.c12
-rw-r--r--net/wireless/core.h3
-rw-r--r--net/wireless/nl80211.c4
-rw-r--r--net/wireless/scan.c20
23 files changed, 488 insertions, 332 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index e38f46ffebfa..7da909d78c68 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2010, Intel Corporation
* Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*/
/**
@@ -206,7 +206,10 @@ u8 ieee80211_retrieve_addba_ext_data(struct sta_info *sta,
if (elem_len <= 0)
return 0;
- elems = ieee802_11_parse_elems(elem_data, elem_len, true, NULL);
+ elems = ieee802_11_parse_elems(elem_data, elem_len,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (!elems || elems->parse_error || !elems->addba_ext_ie)
goto free;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c52b0456039d..b51c2c8584ae 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -63,12 +63,14 @@ static void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata,
memcpy(sdata->vif.bss_conf.mu_group.position,
params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
WLAN_USER_POSITION_LEN);
- ieee80211_link_info_change_notify(sdata, &sdata->deflink,
- BSS_CHANGED_MU_GROUPS);
+
/* don't care about endianness - just check for 0 */
memcpy(&membership, params->vht_mumimo_groups,
WLAN_MEMBERSHIP_LEN);
mu_mimo_groups = membership != 0;
+
+ /* Unset following if configured explicitly */
+ eth_broadcast_addr(sdata->u.mntr.mu_follow_addr);
}
if (params->vht_mumimo_follow_addr) {
@@ -76,16 +78,26 @@ static void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata,
is_valid_ether_addr(params->vht_mumimo_follow_addr);
ether_addr_copy(sdata->u.mntr.mu_follow_addr,
params->vht_mumimo_follow_addr);
+
+ /* Unset current membership until a management frame is RXed */
+ memset(sdata->vif.bss_conf.mu_group.membership, 0,
+ WLAN_MEMBERSHIP_LEN);
}
sdata->vif.bss_conf.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow;
+
+ /* Notify only after setting mu_mimo_owner */
+ if (sdata->vif.bss_conf.mu_mimo_owner &&
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER)
+ ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+ BSS_CHANGED_MU_GROUPS);
}
static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
struct vif_params *params)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_sub_if_data *monitor_sdata;
+ struct ieee80211_sub_if_data *monitor_sdata = NULL;
/* check flags first */
if (params->flags && ieee80211_sdata_running(sdata)) {
@@ -103,23 +115,28 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
return -EBUSY;
}
- /* also validate MU-MIMO change */
- if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
- monitor_sdata = sdata;
- else
- monitor_sdata = wiphy_dereference(local->hw.wiphy,
- local->monitor_sdata);
-
- if (!monitor_sdata &&
+ /* validate whether MU-MIMO can be configured */
+ if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
(params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
return -EOPNOTSUPP;
+ /* Also update dependent monitor_sdata if required */
+ if (test_bit(SDATA_STATE_RUNNING, &sdata->state) &&
+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ monitor_sdata = wiphy_dereference(local->hw.wiphy,
+ local->monitor_sdata);
+
/* apply all changes now - no failures allowed */
- if (monitor_sdata &&
- (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) ||
- ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)))
- ieee80211_set_mu_mimo_follow(monitor_sdata, params);
+ if (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) ||
+ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
+ /* This is copied in when the VIF is activated */
+ ieee80211_set_mu_mimo_follow(sdata, params);
+
+ if (monitor_sdata)
+ ieee80211_set_mu_mimo_follow(monitor_sdata, params);
+ }
if (params->flags) {
if (ieee80211_sdata_running(sdata)) {
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 7f8799fd673e..6aa305839f53 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -12,15 +12,131 @@
#include "driver-ops.h"
#include "rate.h"
+struct ieee80211_chanctx_user_iter {
+ struct ieee80211_chan_req *chanreq;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_link_data *link;
+ enum nl80211_iftype iftype;
+ bool reserved, radar_required, done;
+ enum {
+ CHANCTX_ITER_POS_ASSIGNED,
+ CHANCTX_ITER_POS_RESERVED,
+ CHANCTX_ITER_POS_DONE,
+ } per_link;
+};
+
+enum ieee80211_chanctx_iter_type {
+ CHANCTX_ITER_ALL,
+ CHANCTX_ITER_RESERVED,
+ CHANCTX_ITER_ASSIGNED,
+};
+
+static void ieee80211_chanctx_user_iter_next(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ struct ieee80211_chanctx_user_iter *iter,
+ enum ieee80211_chanctx_iter_type type,
+ bool start)
+{
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ if (start) {
+ memset(iter, 0, sizeof(*iter));
+ goto next_interface;
+ }
+
+next_link:
+ for (int link_id = iter->link ? iter->link->link_id : 0;
+ link_id < ARRAY_SIZE(iter->sdata->link);
+ link_id++) {
+ struct ieee80211_link_data *link;
+
+ link = sdata_dereference(iter->sdata->link[link_id],
+ iter->sdata);
+ if (!link)
+ continue;
+
+ switch (iter->per_link) {
+ case CHANCTX_ITER_POS_ASSIGNED:
+ iter->per_link = CHANCTX_ITER_POS_RESERVED;
+ if (type != CHANCTX_ITER_RESERVED &&
+ rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf) {
+ iter->link = link;
+ iter->reserved = false;
+ iter->radar_required = link->radar_required;
+ iter->chanreq = &link->conf->chanreq;
+ return;
+ }
+ fallthrough;
+ case CHANCTX_ITER_POS_RESERVED:
+ iter->per_link = CHANCTX_ITER_POS_DONE;
+ if (type != CHANCTX_ITER_ASSIGNED &&
+ link->reserved_chanctx == ctx) {
+ iter->link = link;
+ iter->reserved = true;
+ iter->radar_required =
+ link->reserved_radar_required;
+
+ iter->chanreq = &link->reserved;
+ return;
+ }
+ fallthrough;
+ case CHANCTX_ITER_POS_DONE:
+ iter->per_link = CHANCTX_ITER_POS_ASSIGNED;
+ continue;
+ }
+ }
+
+next_interface:
+ /* next (or first) interface */
+ iter->sdata = list_prepare_entry(iter->sdata, &local->interfaces, list);
+ list_for_each_entry_continue(iter->sdata, &local->interfaces, list) {
+ /* AP_VLAN has a chanctx pointer but follows AP */
+ if (iter->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ continue;
+
+ iter->link = NULL;
+ iter->per_link = CHANCTX_ITER_POS_ASSIGNED;
+ iter->iftype = iter->sdata->vif.type;
+ goto next_link;
+ }
+
+ iter->done = true;
+}
+
+#define for_each_chanctx_user_assigned(local, ctx, iter) \
+ for (ieee80211_chanctx_user_iter_next(local, ctx, iter, \
+ CHANCTX_ITER_ASSIGNED, \
+ true); \
+ !((iter)->done); \
+ ieee80211_chanctx_user_iter_next(local, ctx, iter, \
+ CHANCTX_ITER_ASSIGNED, \
+ false))
+
+#define for_each_chanctx_user_reserved(local, ctx, iter) \
+ for (ieee80211_chanctx_user_iter_next(local, ctx, iter, \
+ CHANCTX_ITER_RESERVED, \
+ true); \
+ !((iter)->done); \
+ ieee80211_chanctx_user_iter_next(local, ctx, iter, \
+ CHANCTX_ITER_RESERVED, \
+ false))
+
+#define for_each_chanctx_user_all(local, ctx, iter) \
+ for (ieee80211_chanctx_user_iter_next(local, ctx, iter, \
+ CHANCTX_ITER_ALL, \
+ true); \
+ !((iter)->done); \
+ ieee80211_chanctx_user_iter_next(local, ctx, iter, \
+ CHANCTX_ITER_ALL, \
+ false))
+
static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
- struct ieee80211_link_data *link;
+ struct ieee80211_chanctx_user_iter iter;
int num = 0;
- lockdep_assert_wiphy(local->hw.wiphy);
-
- list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list)
+ for_each_chanctx_user_assigned(local, ctx, &iter)
num++;
return num;
@@ -29,12 +145,10 @@ static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
- struct ieee80211_link_data *link;
+ struct ieee80211_chanctx_user_iter iter;
int num = 0;
- lockdep_assert_wiphy(local->hw.wiphy);
-
- list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list)
+ for_each_chanctx_user_reserved(local, ctx, &iter)
num++;
return num;
@@ -43,8 +157,13 @@ static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
int ieee80211_chanctx_refcount(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
- return ieee80211_chanctx_num_assigned(local, ctx) +
- ieee80211_chanctx_num_reserved(local, ctx);
+ struct ieee80211_chanctx_user_iter iter;
+ int num = 0;
+
+ for_each_chanctx_user_all(local, ctx, &iter)
+ num++;
+
+ return num;
}
static int ieee80211_num_chanctx(struct ieee80211_local *local, int radio_idx)
@@ -143,15 +262,15 @@ ieee80211_chanctx_reserved_chanreq(struct ieee80211_local *local,
const struct ieee80211_chan_req *req,
struct ieee80211_chan_req *tmp)
{
- struct ieee80211_link_data *link;
+ struct ieee80211_chanctx_user_iter iter;
lockdep_assert_wiphy(local->hw.wiphy);
if (WARN_ON(!req))
return NULL;
- list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) {
- req = ieee80211_chanreq_compatible(&link->reserved, req, tmp);
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ req = ieee80211_chanreq_compatible(iter.chanreq, req, tmp);
if (!req)
break;
}
@@ -165,18 +284,16 @@ ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
const struct ieee80211_chan_req *compat,
struct ieee80211_chan_req *tmp)
{
- struct ieee80211_link_data *link;
const struct ieee80211_chan_req *comp_def = compat;
+ struct ieee80211_chanctx_user_iter iter;
lockdep_assert_wiphy(local->hw.wiphy);
- list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) {
- struct ieee80211_bss_conf *link_conf = link->conf;
-
- if (link->reserved_chanctx)
+ for_each_chanctx_user_assigned(local, ctx, &iter) {
+ if (iter.link->reserved_chanctx)
continue;
- comp_def = ieee80211_chanreq_compatible(&link_conf->chanreq,
+ comp_def = ieee80211_chanreq_compatible(iter.chanreq,
comp_def, tmp);
if (!comp_def)
break;
@@ -200,7 +317,7 @@ ieee80211_chanctx_can_reserve(struct ieee80211_local *local,
if (!ieee80211_chanctx_non_reserved_chandef(local, ctx, req, &tmp))
return false;
- if (!list_empty(&ctx->reserved_links) &&
+ if (ieee80211_chanctx_num_reserved(local, ctx) != 0 &&
ieee80211_chanctx_reserved_chanreq(local, ctx, req, &tmp))
return true;
@@ -389,10 +506,10 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
* channel context.
*/
static u32
-_ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
- struct ieee80211_chanctx *ctx,
- struct ieee80211_link_data *rsvd_for,
- bool check_reserved)
+__ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ struct ieee80211_link_data *rsvd_for,
+ bool check_reserved)
{
enum nl80211_chan_width max_bw;
struct cfg80211_chan_def min_def;
@@ -497,13 +614,14 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local,
* the max of min required widths of all the interfaces bound to this
* channel context.
*/
-void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
- struct ieee80211_chanctx *ctx,
- struct ieee80211_link_data *rsvd_for,
- bool check_reserved)
+static void
+_ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ struct ieee80211_link_data *rsvd_for,
+ bool check_reserved)
{
- u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for,
- check_reserved);
+ u32 changed = __ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for,
+ check_reserved);
if (!changed)
return;
@@ -517,6 +635,12 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
ieee80211_chan_bw_change(local, ctx, false, false);
}
+void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ _ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false);
+}
+
static void _ieee80211_change_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx,
struct ieee80211_chanctx *old_ctx,
@@ -551,7 +675,7 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
ieee80211_chan_bw_change(local, old_ctx, false, true);
if (ieee80211_chanreq_identical(&ctx_req, chanreq)) {
- ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for, false);
+ _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for, false);
return;
}
@@ -572,7 +696,8 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
ctx->conf.ap = chanreq->ap;
/* check if min chanctx also changed */
- changed |= _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for, false);
+ changed |= __ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for,
+ false);
ieee80211_add_wbrf(local, &ctx->conf.def);
@@ -633,8 +758,6 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
* context to actually be removed.
*/
link->reserved_chanctx = ctx;
- list_add(&link->reserved_chanctx_list,
- &ctx->reserved_links);
ieee80211_change_chanctx(local, ctx, ctx, compat);
@@ -675,17 +798,13 @@ static bool
ieee80211_chanctx_radar_required(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
- struct ieee80211_chanctx_conf *conf = &ctx->conf;
- struct ieee80211_link_data *link;
+ struct ieee80211_chanctx_user_iter iter;
lockdep_assert_wiphy(local->hw.wiphy);
- for_each_sdata_link(local, link) {
- if (rcu_access_pointer(link->conf->chanctx_conf) != conf)
- continue;
- if (!link->radar_required)
- continue;
- return true;
+ for_each_chanctx_user_assigned(local, ctx, &iter) {
+ if (iter.radar_required)
+ return true;
}
return false;
@@ -705,8 +824,6 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local,
if (!ctx)
return NULL;
- INIT_LIST_HEAD(&ctx->assigned_links);
- INIT_LIST_HEAD(&ctx->reserved_links);
ctx->conf.def = chanreq->oper;
ctx->conf.ap = chanreq->ap;
ctx->conf.rx_chains_static = 1;
@@ -715,7 +832,7 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local,
ctx->conf.radar_enabled = false;
ctx->conf.radio_idx = radio_idx;
ctx->radar_detected = false;
- _ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false);
+ __ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false);
return ctx;
}
@@ -804,27 +921,17 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
{
struct ieee80211_chanctx_conf *conf = &ctx->conf;
const struct ieee80211_chan_req *compat = NULL;
- struct ieee80211_link_data *link;
+ struct ieee80211_chanctx_user_iter iter;
struct ieee80211_chan_req tmp;
struct sta_info *sta;
lockdep_assert_wiphy(local->hw.wiphy);
- for_each_sdata_link(local, link) {
- struct ieee80211_bss_conf *link_conf;
-
- if (link->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- continue;
-
- link_conf = link->conf;
-
- if (rcu_access_pointer(link_conf->chanctx_conf) != conf)
- continue;
-
+ for_each_chanctx_user_assigned(local, ctx, &iter) {
if (!compat)
- compat = &link_conf->chanreq;
+ compat = iter.chanreq;
- compat = ieee80211_chanreq_compatible(&link_conf->chanreq,
+ compat = ieee80211_chanreq_compatible(iter.chanreq,
compat, &tmp);
if (WARN_ON_ONCE(!compat))
return;
@@ -837,6 +944,7 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
list_for_each_entry(sta, &local->sta_list, list) {
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_chan_req tdls_chanreq = {};
+ struct ieee80211_link_data *link;
int tdls_link_id;
if (!sta->uploaded ||
@@ -904,12 +1012,11 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
drv_unassign_vif_chanctx(local, sdata, link->conf, curr_ctx);
conf = NULL;
- list_del(&link->assigned_chanctx_list);
}
if (new_ctx) {
/* recalc considering the link we'll use it for now */
- ieee80211_recalc_chanctx_min_def(local, new_ctx, link, false);
+ _ieee80211_recalc_chanctx_min_def(local, new_ctx, link, false);
ret = drv_assign_vif_chanctx(local, sdata, link->conf, new_ctx);
if (assign_on_failure || !ret) {
@@ -919,9 +1026,6 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
/* succeeded, so commit it to the data structures */
conf = &new_ctx->conf;
- if (!local->in_reconfig)
- list_add(&link->assigned_chanctx_list,
- &new_ctx->assigned_links);
}
} else {
ret = 0;
@@ -933,12 +1037,12 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
ieee80211_recalc_chanctx_chantype(local, curr_ctx);
ieee80211_recalc_smps_chanctx(local, curr_ctx);
ieee80211_recalc_radar_chanctx(local, curr_ctx);
- ieee80211_recalc_chanctx_min_def(local, curr_ctx, NULL, false);
+ ieee80211_recalc_chanctx_min_def(local, curr_ctx);
}
if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
ieee80211_recalc_txpower(link, false);
- ieee80211_recalc_chanctx_min_def(local, new_ctx, NULL, false);
+ ieee80211_recalc_chanctx_min_def(local, new_ctx);
}
if (conf) {
@@ -971,21 +1075,21 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx)
{
+ struct ieee80211_chanctx_user_iter iter;
struct ieee80211_sub_if_data *sdata;
u8 rx_chains_static, rx_chains_dynamic;
- struct ieee80211_link_data *link;
lockdep_assert_wiphy(local->hw.wiphy);
rx_chains_static = 1;
rx_chains_dynamic = 1;
- for_each_sdata_link(local, link) {
+ for_each_chanctx_user_assigned(local, chanctx, &iter) {
u8 needed_static, needed_dynamic;
- switch (link->sdata->vif.type) {
+ switch (iter.iftype) {
case NL80211_IFTYPE_STATION:
- if (!link->sdata->u.mgd.associated)
+ if (!iter.sdata->u.mgd.associated)
continue;
break;
case NL80211_IFTYPE_MONITOR:
@@ -1001,26 +1105,23 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
continue;
}
- if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
- continue;
-
- if (link->sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ if (iter.iftype == NL80211_IFTYPE_MONITOR) {
rx_chains_dynamic = rx_chains_static = local->rx_chains;
break;
}
- switch (link->smps_mode) {
+ switch (iter.link->smps_mode) {
default:
WARN_ONCE(1, "Invalid SMPS mode %d\n",
- link->smps_mode);
+ iter.link->smps_mode);
fallthrough;
case IEEE80211_SMPS_OFF:
- needed_static = link->needed_rx_chains;
- needed_dynamic = link->needed_rx_chains;
+ needed_static = iter.link->needed_rx_chains;
+ needed_dynamic = iter.link->needed_rx_chains;
break;
case IEEE80211_SMPS_DYNAMIC:
needed_static = 1;
- needed_dynamic = link->needed_rx_chains;
+ needed_dynamic = iter.link->needed_rx_chains;
break;
case IEEE80211_SMPS_STATIC:
needed_static = 1;
@@ -1108,7 +1209,6 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
if (WARN_ON(!ctx))
return;
- list_del(&link->reserved_chanctx_list);
link->reserved_chanctx = NULL;
if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
@@ -1142,9 +1242,9 @@ ieee80211_replace_chanctx(struct ieee80211_local *local,
struct wiphy *wiphy = local->hw.wiphy;
const struct wiphy_radio *radio;
- if (!curr_ctx || (curr_ctx->replace_state ==
- IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
- !list_empty(&curr_ctx->reserved_links)) {
+ if (!curr_ctx ||
+ curr_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED ||
+ ieee80211_chanctx_num_reserved(local, curr_ctx) != 0) {
/*
* Another link already requested this context for a
* reservation. Find another one hoping all links assigned
@@ -1167,7 +1267,7 @@ ieee80211_replace_chanctx(struct ieee80211_local *local,
IEEE80211_CHANCTX_REPLACE_NONE)
continue;
- if (!list_empty(&ctx->reserved_links))
+ if (ieee80211_chanctx_num_reserved(local, ctx) != 0)
continue;
if (ctx->conf.radio_idx >= 0) {
@@ -1185,9 +1285,9 @@ ieee80211_replace_chanctx(struct ieee80211_local *local,
* If that's true then all available contexts already have reservations
* and cannot be used.
*/
- if (!curr_ctx || (curr_ctx->replace_state ==
- IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
- !list_empty(&curr_ctx->reserved_links))
+ if (!curr_ctx ||
+ curr_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED ||
+ ieee80211_chanctx_num_reserved(local, curr_ctx) != 0)
return ERR_PTR(-EBUSY);
new_ctx = ieee80211_alloc_chanctx(local, chanreq, mode, -1);
@@ -1267,7 +1367,6 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link,
return PTR_ERR(new_ctx);
}
- list_add(&link->reserved_chanctx_list, &new_ctx->reserved_links);
link->reserved_chanctx = new_ctx;
link->reserved = *chanreq;
link->reserved_radar_required = radar_required;
@@ -1381,7 +1480,6 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
vif_chsw[0].new_ctx = &new_ctx->conf;
vif_chsw[0].link_conf = link->conf;
- list_del(&link->reserved_chanctx_list);
link->reserved_chanctx = NULL;
err = drv_switch_vif_chanctx(local, vif_chsw, 1,
@@ -1394,7 +1492,6 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
}
link->radar_required = link->reserved_radar_required;
- list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links);
rcu_assign_pointer(link_conf->chanctx_conf, &new_ctx->conf);
if (sdata->vif.type == NL80211_IFTYPE_AP)
@@ -1405,7 +1502,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
ieee80211_free_chanctx(local, old_ctx, false);
- ieee80211_recalc_chanctx_min_def(local, new_ctx, NULL, false);
+ ieee80211_recalc_chanctx_min_def(local, new_ctx);
ieee80211_recalc_smps_chanctx(local, new_ctx);
ieee80211_recalc_radar_chanctx(local, new_ctx);
@@ -1451,7 +1548,6 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
ieee80211_change_chanctx(local, new_ctx, new_ctx, chanreq);
- list_del(&link->reserved_chanctx_list);
link->reserved_chanctx = NULL;
err = ieee80211_assign_link_chanctx(link, new_ctx, false);
@@ -1497,7 +1593,6 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
int n_vifs)
{
struct ieee80211_vif_chanctx_switch *vif_chsw;
- struct ieee80211_link_data *link;
struct ieee80211_chanctx *ctx, *old_ctx;
int i, err;
@@ -1509,6 +1604,8 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
i = 0;
list_for_each_entry(ctx, &local->chanctx_list, list) {
+ struct ieee80211_chanctx_user_iter iter;
+
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
continue;
@@ -1517,16 +1614,15 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
goto out;
}
- list_for_each_entry(link, &ctx->reserved_links,
- reserved_chanctx_list) {
- if (!ieee80211_link_has_in_place_reservation(link))
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ if (!ieee80211_link_has_in_place_reservation(iter.link))
continue;
- old_ctx = ieee80211_link_get_chanctx(link);
- vif_chsw[i].vif = &link->sdata->vif;
+ old_ctx = ieee80211_link_get_chanctx(iter.link);
+ vif_chsw[i].vif = &iter.sdata->vif;
vif_chsw[i].old_ctx = &old_ctx->conf;
vif_chsw[i].new_ctx = &ctx->conf;
- vif_chsw[i].link_conf = link->conf;
+ vif_chsw[i].link_conf = iter.link->conf;
i++;
}
@@ -1551,7 +1647,7 @@ static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
continue;
- if (!list_empty(&ctx->replace_ctx->assigned_links))
+ if (ieee80211_chanctx_num_assigned(local, ctx) != 0)
continue;
ieee80211_del_chanctx(local, ctx->replace_ctx, false);
@@ -1568,7 +1664,7 @@ err:
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
continue;
- if (!list_empty(&ctx->replace_ctx->assigned_links))
+ if (ieee80211_chanctx_num_assigned(local, ctx) != 0)
continue;
ieee80211_del_chanctx(local, ctx, false);
@@ -1603,7 +1699,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
*/
list_for_each_entry(ctx, &local->chanctx_list, list) {
- struct ieee80211_link_data *link;
+ struct ieee80211_chanctx_user_iter iter;
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
continue;
@@ -1619,12 +1715,11 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
n_reserved = 0;
n_ready = 0;
- list_for_each_entry(link, &ctx->replace_ctx->assigned_links,
- assigned_chanctx_list) {
+ for_each_chanctx_user_assigned(local, ctx, &iter) {
n_assigned++;
- if (link->reserved_chanctx) {
+ if (iter.link->reserved_chanctx) {
n_reserved++;
- if (link->reserved_ready)
+ if (iter.link->reserved_ready)
n_ready++;
}
}
@@ -1641,13 +1736,12 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
}
ctx->conf.radar_enabled = false;
- list_for_each_entry(link, &ctx->reserved_links,
- reserved_chanctx_list) {
- if (ieee80211_link_has_in_place_reservation(link) &&
- !link->reserved_ready)
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ if (ieee80211_link_has_in_place_reservation(iter.link) &&
+ !iter.link->reserved_ready)
return -EAGAIN;
- old_ctx = ieee80211_link_get_chanctx(link);
+ old_ctx = ieee80211_link_get_chanctx(iter.link);
if (old_ctx) {
if (old_ctx->replace_state ==
IEEE80211_CHANCTX_WILL_BE_REPLACED)
@@ -1658,7 +1752,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
n_vifs_ctxless++;
}
- if (link->reserved_radar_required)
+ if (iter.radar_required)
ctx->conf.radar_enabled = true;
}
}
@@ -1673,7 +1767,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
/* update station rate control and min width before switch */
list_for_each_entry(ctx, &local->chanctx_list, list) {
- struct ieee80211_link_data *link;
+ struct ieee80211_chanctx_user_iter iter;
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
continue;
@@ -1683,17 +1777,16 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
goto err;
}
- list_for_each_entry(link, &ctx->reserved_links,
- reserved_chanctx_list) {
- if (!ieee80211_link_has_in_place_reservation(link))
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ if (!ieee80211_link_has_in_place_reservation(iter.link))
continue;
ieee80211_chan_bw_change(local,
- ieee80211_link_get_chanctx(link),
+ ieee80211_link_get_chanctx(iter.link),
true, true);
}
- ieee80211_recalc_chanctx_min_def(local, ctx, NULL, true);
+ _ieee80211_recalc_chanctx_min_def(local, ctx, NULL, true);
}
/*
@@ -1718,7 +1811,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
* context(s).
*/
list_for_each_entry(ctx, &local->chanctx_list, list) {
- struct ieee80211_link_data *link, *link_tmp;
+ struct ieee80211_chanctx_user_iter iter;
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
continue;
@@ -1728,9 +1821,9 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
goto err;
}
- list_for_each_entry(link, &ctx->reserved_links,
- reserved_chanctx_list) {
- struct ieee80211_sub_if_data *sdata = link->sdata;
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ struct ieee80211_link_data *link = iter.link;
+ struct ieee80211_sub_if_data *sdata = iter.sdata;
struct ieee80211_bss_conf *link_conf = link->conf;
u64 changed = 0;
@@ -1746,9 +1839,9 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
ieee80211_check_fast_xmit_iface(sdata);
- link->radar_required = link->reserved_radar_required;
+ link->radar_required = iter.radar_required;
- if (link_conf->chanreq.oper.width != link->reserved.oper.width)
+ if (link_conf->chanreq.oper.width != iter.chanreq->oper.width)
changed = BSS_CHANGED_BANDWIDTH;
ieee80211_link_update_chanreq(link, &link->reserved);
@@ -1763,19 +1856,15 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
ieee80211_recalc_chanctx_chantype(local, ctx);
ieee80211_recalc_smps_chanctx(local, ctx);
ieee80211_recalc_radar_chanctx(local, ctx);
- ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false);
+ ieee80211_recalc_chanctx_min_def(local, ctx);
- list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
- reserved_chanctx_list) {
- if (ieee80211_link_get_chanctx(link) != ctx)
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ if (ieee80211_link_get_chanctx(iter.link) != ctx)
continue;
- list_del(&link->reserved_chanctx_list);
- list_move(&link->assigned_chanctx_list,
- &ctx->assigned_links);
- link->reserved_chanctx = NULL;
+ iter.link->reserved_chanctx = NULL;
- ieee80211_link_chanctx_reservation_complete(link);
+ ieee80211_link_chanctx_reservation_complete(iter.link);
ieee80211_chan_bw_change(local, ctx, false, false);
}
@@ -1786,12 +1875,10 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
* reservation for originally requested interface has already
* succeeded at this point.
*/
- list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
- reserved_chanctx_list) {
- if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
- continue;
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ struct ieee80211_link_data *link = iter.link;
- if (WARN_ON(link->reserved_chanctx != ctx))
+ if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
continue;
if (!link->reserved_ready)
@@ -1834,15 +1921,14 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
err:
list_for_each_entry(ctx, &local->chanctx_list, list) {
- struct ieee80211_link_data *link, *link_tmp;
+ struct ieee80211_chanctx_user_iter iter;
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
continue;
- list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
- reserved_chanctx_list) {
- ieee80211_link_unreserve_chanctx(link);
- ieee80211_link_chanctx_reservation_complete(link);
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ ieee80211_link_unreserve_chanctx(iter.link);
+ ieee80211_link_chanctx_reservation_complete(iter.link);
}
}
@@ -1949,7 +2035,6 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
/* remove reservation */
WARN_ON(link->reserved_chanctx != ctx);
link->reserved_chanctx = NULL;
- list_del(&link->reserved_chanctx_list);
}
if (ret) {
@@ -2046,29 +2131,17 @@ ieee80211_chanctx_recheck(struct ieee80211_local *local,
struct ieee80211_chan_req *tmp)
{
const struct ieee80211_chan_req *ret = req;
- struct ieee80211_link_data *link;
+ struct ieee80211_chanctx_user_iter iter;
lockdep_assert_wiphy(local->hw.wiphy);
- for_each_sdata_link(local, link) {
- if (link == skip_link)
+ for_each_chanctx_user_all(local, ctx, &iter) {
+ if (iter.link == skip_link)
continue;
- if (rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf) {
- ret = ieee80211_chanreq_compatible(ret,
- &link->conf->chanreq,
- tmp);
- if (!ret)
- return NULL;
- }
-
- if (link->reserved_chanctx == ctx) {
- ret = ieee80211_chanreq_compatible(ret,
- &link->reserved,
- tmp);
- if (!ret)
- return NULL;
- }
+ ret = ieee80211_chanreq_compatible(ret, iter.chanreq, tmp);
+ if (!ret)
+ return NULL;
}
*tmp = *ret;
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
index ba9fba165926..49753b73aba2 100644
--- a/net/mac80211/driver-ops.c
+++ b/net/mac80211/driver-ops.c
@@ -476,8 +476,12 @@ void drv_link_info_changed(struct ieee80211_local *local,
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
sdata->vif.type == NL80211_IFTYPE_NAN ||
(sdata->vif.type == NL80211_IFTYPE_MONITOR &&
- !sdata->vif.bss_conf.mu_mimo_owner &&
- !(changed & BSS_CHANGED_TXPOWER))))
+ changed & ~(BSS_CHANGED_TXPOWER |
+ BSS_CHANGED_MU_GROUPS))))
+ return;
+
+ if (WARN_ON_ONCE(changed & BSS_CHANGED_MU_GROUPS &&
+ !sdata->vif.bss_conf.mu_mimo_owner))
return;
if (!check_sdata_in_driver(sdata))
diff --git a/net/mac80211/he.c b/net/mac80211/he.c
index 5792ef77e986..f7b05e59374c 100644
--- a/net/mac80211/he.c
+++ b/net/mac80211/he.c
@@ -3,7 +3,7 @@
* HE handling
*
* Copyright(c) 2017 Intel Deutschland GmbH
- * Copyright(c) 2019 - 2024 Intel Corporation
+ * Copyright(c) 2019-2025 Intel Corporation
*/
#include "ieee80211_i.h"
@@ -313,7 +313,7 @@ bool ieee80211_prepare_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta,
ieee80211_link_sta_rc_update_omi(link, link_sta);
} else {
link_sta->rx_omi_bw_rx = bw;
- ieee80211_recalc_chanctx_min_def(local, chanctx, NULL, false);
+ ieee80211_recalc_chanctx_min_def(local, chanctx);
}
link_sta->rx_omi_bw_staging = bw;
@@ -359,7 +359,7 @@ void ieee80211_finalize_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta)
/* channel context in finalize only when narrowing bandwidth */
WARN_ON(link_sta->rx_omi_bw_rx < link_sta->rx_omi_bw_staging);
link_sta->rx_omi_bw_rx = link_sta->rx_omi_bw_staging;
- ieee80211_recalc_chanctx_min_def(local, chanctx, NULL, false);
+ ieee80211_recalc_chanctx_min_def(local, chanctx);
}
trace_api_return_void(local);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 6e36b09fe97f..168f84a1353b 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -9,7 +9,7 @@
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright(c) 2016 Intel Deutschland GmbH
- * Copyright(c) 2018-2024 Intel Corporation
+ * Copyright(c) 2018-2025 Intel Corporation
*/
#include <linux/delay.h>
@@ -1554,6 +1554,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
{
size_t baselen;
struct ieee802_11_elems *elems;
+ u16 type;
BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) !=
offsetof(typeof(mgmt->u.beacon), variable));
@@ -1566,8 +1567,9 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
if (baselen > len)
return;
+ type = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_TYPE;
elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
- len - baselen, false, NULL);
+ len - baselen, type, NULL);
if (elems) {
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, elems);
@@ -1616,9 +1618,11 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
if (ies_len < 0)
break;
- elems = ieee802_11_parse_elems(
- mgmt->u.action.u.chan_switch.variable,
- ies_len, true, NULL);
+ elems = ieee802_11_parse_elems(mgmt->u.action.u.chan_switch.variable,
+ ies_len,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (elems && !elems->parse_error)
ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 878c3b14aeb8..9d9313eee59f 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -916,9 +916,6 @@ struct ieee80211_chanctx {
struct list_head list;
struct rcu_head rcu_head;
- struct list_head assigned_links;
- struct list_head reserved_links;
-
enum ieee80211_chanctx_replace_state replace_state;
struct ieee80211_chanctx *replace_ctx;
@@ -1071,9 +1068,6 @@ struct ieee80211_link_data {
struct ieee80211_sub_if_data *sdata;
unsigned int link_id;
- struct list_head assigned_chanctx_list; /* protected by wiphy mutex */
- struct list_head reserved_chanctx_list; /* protected by wiphy mutex */
-
/* multicast keys only */
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS +
NUM_DEFAULT_MGMT_KEYS +
@@ -1239,9 +1233,12 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
for (struct ieee80211_sub_if_data *___sdata = NULL; \
!___sdata; \
___sdata = (void *)~0 /* always stop */) \
+ for (int ___link_id = ARRAY_SIZE(___sdata->link); \
+ ___link_id; ___link_id = 0 /* always stop */) \
list_for_each_entry(___sdata, &(_local)->interfaces, list) \
- if (ieee80211_sdata_running(___sdata)) \
- for (int ___link_id = 0; \
+ if (___link_id == ARRAY_SIZE(___sdata->link) && \
+ ieee80211_sdata_running(___sdata)) \
+ for (___link_id = 0; \
___link_id < ARRAY_SIZE(___sdata->link); \
___link_id++) \
if ((_link = wiphy_dereference((_local)->hw.wiphy, \
@@ -1255,9 +1252,12 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
for (struct ieee80211_sub_if_data *___sdata = NULL; \
!___sdata; \
___sdata = (void *)~0 /* always stop */) \
- list_for_each_entry_rcu(___sdata, &(_local)->interfaces, list) \
- if (ieee80211_sdata_running(___sdata)) \
- for (int ___link_id = 0; \
+ for (int ___link_id = ARRAY_SIZE(___sdata->link); \
+ ___link_id; ___link_id = 0 /* always stop */) \
+ list_for_each_entry(___sdata, &(_local)->interfaces, list) \
+ if (___link_id == ARRAY_SIZE(___sdata->link) && \
+ ieee80211_sdata_running(___sdata)) \
+ for (___link_id = 0; \
___link_id < ARRAY_SIZE((___sdata)->link); \
___link_id++) \
if ((_link = rcu_dereference((___sdata)->link[___link_id])))
@@ -2107,7 +2107,8 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
const int offset);
int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up);
void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
-int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
+int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *creator_sdata);
void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
bool __ieee80211_recalc_txpower(struct ieee80211_link_data *link);
@@ -2422,7 +2423,8 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
* @mode: connection mode for parsing
* @start: pointer to the elements
* @len: length of the elements
- * @action: %true if the elements came from an action frame
+ * @type: type of the frame the elements came from
+ * (action, probe response, beacon, etc.)
* @filter: bitmap of element IDs to filter out while calculating
* the element CRC
* @crc: CRC starting value
@@ -2440,7 +2442,7 @@ struct ieee80211_elems_parse_params {
enum ieee80211_conn_mode mode;
const u8 *start;
size_t len;
- bool action;
+ u8 type;
u64 filter;
u32 crc;
struct cfg80211_bss *bss;
@@ -2452,17 +2454,14 @@ struct ieee802_11_elems *
ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params);
static inline struct ieee802_11_elems *
-ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
- u64 filter, u32 crc,
- struct cfg80211_bss *bss)
+ieee802_11_parse_elems(const u8 *start, size_t len, u8 type,
+ struct cfg80211_bss *bss)
{
struct ieee80211_elems_parse_params params = {
.mode = IEEE80211_CONN_MODE_HIGHEST,
.start = start,
.len = len,
- .action = action,
- .filter = filter,
- .crc = crc,
+ .type = type,
.bss = bss,
.link_id = -1,
};
@@ -2470,13 +2469,6 @@ ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
return ieee802_11_parse_elems_full(&params);
}
-static inline struct ieee802_11_elems *
-ieee802_11_parse_elems(const u8 *start, size_t len, bool action,
- struct cfg80211_bss *bss)
-{
- return ieee802_11_parse_elems_crc(start, len, action, 0, 0, bss);
-}
-
extern const int ieee802_1d_to_ac[8];
static inline int ieee80211_ac_from_tid(int tid)
@@ -2768,9 +2760,7 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx);
void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
- struct ieee80211_chanctx *ctx,
- struct ieee80211_link_data *rsvd_for,
- bool check_reserved);
+ struct ieee80211_chanctx *ctx);
bool ieee80211_is_radar_required(struct ieee80211_local *local,
struct cfg80211_scan_request *req);
bool ieee80211_is_radio_idx_in_scan_req(struct wiphy *wiphy,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index a7873832d4fa..d7a4f203cde4 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -733,8 +733,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
ieee80211_configure_filter(local);
ieee80211_hw_config(local, -1, hw_reconf_flags);
+ /* Passing NULL means an interface is picked for configuration */
if (local->virt_monitors == local->open_count)
- ieee80211_add_virtual_monitor(local);
+ ieee80211_add_virtual_monitor(local, NULL);
}
void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata)
@@ -1168,7 +1169,8 @@ static void ieee80211_sdata_init(struct ieee80211_local *local,
ieee80211_link_init(sdata, -1, &sdata->deflink, &sdata->vif.bss_conf);
}
-int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
+int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *creator_sdata)
{
struct ieee80211_sub_if_data *sdata;
int ret;
@@ -1176,10 +1178,14 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy);
- if (local->monitor_sdata ||
- ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
return 0;
+ /* Already have a monitor set up, configure it */
+ sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
+ if (sdata)
+ goto configure_monitor;
+
sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
if (!sdata)
return -ENOMEM;
@@ -1232,6 +1238,32 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
skb_queue_head_init(&sdata->status_queue);
wiphy_work_init(&sdata->work, ieee80211_iface_work);
+configure_monitor:
+ /* Copy in the MU-MIMO configuration if set */
+ if (!creator_sdata) {
+ struct ieee80211_sub_if_data *other;
+
+ list_for_each_entry(other, &local->mon_list, list) {
+ if (!other->vif.bss_conf.mu_mimo_owner)
+ continue;
+
+ creator_sdata = other;
+ break;
+ }
+ }
+
+ if (creator_sdata && creator_sdata->vif.bss_conf.mu_mimo_owner) {
+ sdata->vif.bss_conf.mu_mimo_owner = true;
+ memcpy(&sdata->vif.bss_conf.mu_group,
+ &creator_sdata->vif.bss_conf.mu_group,
+ sizeof(sdata->vif.bss_conf.mu_group));
+ memcpy(&sdata->u.mntr.mu_follow_addr,
+ creator_sdata->u.mntr.mu_follow_addr, ETH_ALEN);
+
+ ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+ BSS_CHANGED_MU_GROUPS);
+ }
+
return 0;
}
@@ -1388,11 +1420,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
if (res)
goto err_stop;
} else {
- if (local->virt_monitors == 0 && local->open_count == 0) {
- res = ieee80211_add_virtual_monitor(local);
+ /* add/configure if there is no non-monitor interface */
+ if (local->virt_monitors == local->open_count) {
+ res = ieee80211_add_virtual_monitor(local, sdata);
if (res)
goto err_stop;
}
+
local->virt_monitors++;
/* must be before the call to ieee80211_configure_filter */
diff --git a/net/mac80211/link.c b/net/mac80211/link.c
index 4a19b765ccb6..1e05845872af 100644
--- a/net/mac80211/link.c
+++ b/net/mac80211/link.c
@@ -23,9 +23,6 @@ static void ieee80211_update_apvlan_links(struct ieee80211_sub_if_data *sdata)
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
int link_id;
- if (!vlan)
- continue;
-
/* No support for 4addr with MLO yet */
if (vlan->wdev.use_4addr)
return;
@@ -119,8 +116,6 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
ieee80211_color_change_finalize_work);
wiphy_delayed_work_init(&link->color_collision_detect_work,
ieee80211_color_collision_detection_work);
- INIT_LIST_HEAD(&link->assigned_chanctx_list);
- INIT_LIST_HEAD(&link->reserved_chanctx_list);
wiphy_delayed_work_init(&link->dfs_cac_timer_work,
ieee80211_dfs_cac_timer_work);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index eefa6f7e899b..b05e313c7f17 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -356,8 +356,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
sdata->vif.type == NL80211_IFTYPE_NAN ||
(sdata->vif.type == NL80211_IFTYPE_MONITOR &&
- !sdata->vif.bss_conf.mu_mimo_owner &&
- !(changed & BSS_CHANGED_TXPOWER))))
+ changed & ~BSS_CHANGED_TXPOWER)))
return;
if (!check_sdata_in_driver(sdata))
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index f37068a533f4..68901f1def0d 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2008, 2009 open80211s Ltd.
- * Copyright (C) 2018 - 2024 Intel Corporation
+ * Copyright (C) 2018 - 2025 Intel Corporation
* Authors: Luis Carlos Cobo <luisca@cozybit.com>
* Javier Cardona <javier@cozybit.com>
*/
@@ -1410,7 +1410,10 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
if (baselen > len)
return;
- elems = ieee802_11_parse_elems(pos, len - baselen, false, NULL);
+ elems = ieee802_11_parse_elems(pos, len - baselen,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_REQ,
+ NULL);
if (!elems)
return;
@@ -1455,11 +1458,11 @@ free:
}
static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
- u16 stype,
struct ieee80211_mgmt *mgmt,
size_t len,
struct ieee80211_rx_status *rx_status)
{
+ u16 type = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_TYPE;
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee802_11_elems *elems;
@@ -1469,7 +1472,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
enum nl80211_band band = rx_status->band;
/* ignore ProbeResp to foreign address */
- if (stype == IEEE80211_STYPE_PROBE_RESP &&
+ if (type == (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP) &&
!ether_addr_equal(mgmt->da, sdata->vif.addr))
return;
@@ -1478,8 +1481,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
return;
elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
- len - baselen,
- false, NULL);
+ len - baselen, type, NULL);
if (!elems)
return;
@@ -1514,7 +1516,9 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
}
if (ifmsh->sync_ops)
- ifmsh->sync_ops->rx_bcn_presp(sdata, stype, mgmt, len,
+ ifmsh->sync_ops->rx_bcn_presp(sdata,
+ type & IEEE80211_FCTL_STYPE,
+ mgmt, len,
elems->mesh_config, rx_status);
free:
kfree(elems);
@@ -1622,7 +1626,10 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
pos = mgmt->u.action.u.chan_switch.variable;
baselen = offsetof(struct ieee80211_mgmt,
u.action.u.chan_switch.variable);
- elems = ieee802_11_parse_elems(pos, len - baselen, true, NULL);
+ elems = ieee802_11_parse_elems(pos, len - baselen,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (!elems)
return;
@@ -1699,8 +1706,7 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
switch (stype) {
case IEEE80211_STYPE_PROBE_RESP:
case IEEE80211_STYPE_BEACON:
- ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len,
- rx_status);
+ ieee80211_mesh_rx_bcn_presp(sdata, mgmt, skb->len, rx_status);
break;
case IEEE80211_STYPE_PROBE_REQ:
ieee80211_mesh_rx_probe_req(sdata, mgmt, skb->len);
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 9101858525dd..a41b57bd11ff 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2008, 2009 open80211s Ltd.
- * Copyright (C) 2019, 2021-2023 Intel Corporation
+ * Copyright (C) 2019, 2021-2023, 2025 Intel Corporation
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*/
@@ -951,7 +951,10 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
elems = ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
- len - baselen, false, NULL);
+ len - baselen,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (!elems)
return;
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index cb45a5d2009d..04c931cd2063 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2008, 2009 open80211s Ltd.
- * Copyright (C) 2019, 2021-2024 Intel Corporation
+ * Copyright (C) 2019, 2021-2025 Intel Corporation
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*/
#include <linux/gfp.h>
@@ -1248,7 +1248,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
if (baselen > len)
return;
}
- elems = ieee802_11_parse_elems(baseaddr, len - baselen, true, NULL);
+ elems = ieee802_11_parse_elems(baseaddr, len - baselen,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (elems) {
mesh_process_plink_frame(sdata, mgmt, elems, rx_status);
kfree(elems);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 767804e41a34..c705d3f45aff 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -276,11 +276,8 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
return IEEE80211_CONN_MODE_VHT;
}
} else if (!vht_oper || !elems->vht_cap_elem) {
- if (sband->band == NL80211_BAND_5GHZ) {
- sdata_info(sdata,
- "VHT information is missing, disabling VHT\n");
+ if (sband->band == NL80211_BAND_5GHZ)
return IEEE80211_CONN_MODE_HT;
- }
no_vht = true;
} else if (sband->band == NL80211_BAND_2GHZ) {
no_vht = true;
@@ -1002,6 +999,9 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata,
.from_ap = true,
.start = ies->data,
.len = ies->len,
+ .type = ies->from_beacon ?
+ IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON :
+ IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP,
};
struct ieee802_11_elems *elems;
struct ieee80211_supported_band *sband;
@@ -5180,7 +5180,9 @@ static void ieee80211_epcs_teardown(struct ieee80211_sub_if_data *sdata)
continue;
}
- elems = ieee802_11_parse_elems(ies->data, ies->len, false,
+ elems = ieee802_11_parse_elems(ies->data, ies->len,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_BEACON,
NULL);
if (!elems) {
rcu_read_unlock();
@@ -5226,6 +5228,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
.len = elem_len,
.link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id,
.from_ap = true,
+ .type = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_TYPE,
};
bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
@@ -6031,24 +6034,6 @@ ieee80211_determine_our_sta_mode_assoc(struct ieee80211_sub_if_data *sdata,
conn->bw_limit, tmp.bw_limit);
}
-static enum ieee80211_ap_reg_power
-ieee80211_ap_power_type(u8 control)
-{
- switch (u8_get_bits(control, IEEE80211_HE_6GHZ_OPER_CTRL_REG_INFO)) {
- case IEEE80211_6GHZ_CTRL_REG_LPI_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_LPI_AP:
- return IEEE80211_REG_LPI_AP;
- case IEEE80211_6GHZ_CTRL_REG_SP_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_SP_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_SP_AP_OLD:
- return IEEE80211_REG_SP_AP;
- case IEEE80211_6GHZ_CTRL_REG_VLP_AP:
- return IEEE80211_REG_VLP_AP;
- default:
- return IEEE80211_REG_UNSET_AP;
- }
-}
-
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
struct ieee80211_link_data *link,
int link_id,
@@ -6091,7 +6076,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
he_6ghz_oper = ieee80211_he_6ghz_oper(elems->he_operation);
if (he_6ghz_oper)
link->conf->power_type =
- ieee80211_ap_power_type(he_6ghz_oper->control);
+ cfg80211_6ghz_power_type(he_6ghz_oper->control,
+ cbss->channel->flags);
else
link_info(link,
"HE 6 GHz operation missing (on %d MHz), expect issues\n",
@@ -6359,6 +6345,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
.bss = NULL,
.link_id = -1,
.from_ap = true,
+ .type = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_TYPE,
};
struct ieee802_11_elems *elems;
int ac;
@@ -7267,7 +7254,9 @@ ieee80211_mgd_check_cross_link_csa(struct ieee80211_sub_if_data *sdata,
(prof->sta_info_len - 1),
len -
(prof->sta_info_len - 1),
- false, NULL);
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_BEACON,
+ NULL);
/* memory allocation failed - let's hope that's transient */
if (!prof_elems)
@@ -7371,6 +7360,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
.mode = link->u.mgd.conn.mode,
.link_id = -1,
.from_ap = true,
+ .type = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_TYPE,
};
lockdep_assert_wiphy(local->hw.wiphy);
@@ -7973,7 +7963,10 @@ void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata,
ies_len = len - offsetof(struct ieee80211_mgmt,
u.action.u.ttlm_req.variable);
elems = ieee802_11_parse_elems(mgmt->u.action.u.ttlm_req.variable,
- ies_len, true, NULL);
+ ies_len,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (!elems) {
ttlm_res = NEG_TTLM_RES_REJECT;
goto out;
@@ -8179,9 +8172,11 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
break;
/* CSA IE cannot be overridden, no need for BSSID */
- elems = ieee802_11_parse_elems(
- mgmt->u.action.u.chan_switch.variable,
- ies_len, true, NULL);
+ elems = ieee802_11_parse_elems(mgmt->u.action.u.chan_switch.variable,
+ ies_len,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (elems && !elems->parse_error) {
enum ieee80211_csa_source src =
@@ -8208,9 +8203,11 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
* extended CSA IE can't be overridden, no need for
* BSSID
*/
- elems = ieee802_11_parse_elems(
- mgmt->u.action.u.ext_chan_switch.variable,
- ies_len, true, NULL);
+ elems = ieee802_11_parse_elems(mgmt->u.action.u.ext_chan_switch.variable,
+ ies_len,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (elems && !elems->parse_error) {
enum ieee80211_csa_source src;
@@ -10988,7 +10985,10 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata,
pos = scratch + sizeof(control);
len -= sizeof(control);
- link_elems = ieee802_11_parse_elems(pos, len, false, NULL);
+ link_elems = ieee802_11_parse_elems(pos, len,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (!link_elems)
continue;
@@ -11039,7 +11039,10 @@ void ieee80211_process_epcs_ena_resp(struct ieee80211_sub_if_data *sdata,
u.action.u.epcs.variable) -
IEEE80211_EPCS_ENA_RESP_BODY_LEN;
- elems = ieee802_11_parse_elems(pos, ies_len, true, NULL);
+ elems = ieee802_11_parse_elems(pos, ies_len,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (!elems)
return;
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index c5e0f7f46004..bfc4ecb7a048 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -6,7 +6,7 @@
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
*
* element parsing for mac80211
*/
@@ -286,6 +286,24 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
bitmap_zero(seen_elems, 256);
+ switch (params->type) {
+ /* we don't need to parse assoc request, luckily (it's value 0) */
+ case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ:
+ case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ:
+ default:
+ WARN(1, "invalid frame type 0x%x for element parsing\n",
+ params->type);
+ break;
+ case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP:
+ case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP:
+ case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
+ case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP:
+ case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON:
+ case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION:
+ case IEEE80211_FTYPE_EXT | IEEE80211_STYPE_S1G_BEACON:
+ break;
+ }
+
for_each_element(elem, params->start, params->len) {
const struct element *subelem;
u8 elem_parse_failed;
@@ -566,7 +584,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
if (params->mode < IEEE80211_CONN_MODE_VHT)
break;
- if (!params->action) {
+ if (params->type != (IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION)) {
elem_parse_failed =
IEEE80211_PARSE_ERR_UNEXPECTED_ELEM;
break;
@@ -582,7 +601,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
case WLAN_EID_CHANNEL_SWITCH_WRAPPER:
if (params->mode < IEEE80211_CONN_MODE_VHT)
break;
- if (params->action) {
+ if (params->type == (IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION)) {
elem_parse_failed =
IEEE80211_PARSE_ERR_UNEXPECTED_ELEM;
break;
@@ -942,7 +962,7 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse,
sub->len = end - sub->start;
sub->mode = params->mode;
- sub->action = params->action;
+ sub->type = params->type;
sub->from_ap = params->from_ap;
sub->link_id = -1;
@@ -1041,7 +1061,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
sub.start = elems_parse->scratch_pos;
sub.mode = params->mode;
sub.len = nontx_len;
- sub.action = params->action;
+ sub.type = params->type;
sub.link_id = params->link_id;
/* consume the space used for non-transmitted profile */
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index bb9563f50e7b..5ef315ed3b0f 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -76,7 +76,11 @@ void ieee80211_inform_bss(struct wiphy *wiphy,
if (!update_data)
return;
- elems = ieee802_11_parse_elems(ies->data, ies->len, false, NULL);
+ elems = ieee802_11_parse_elems(ies->data, ies->len,
+ update_data->beacon ?
+ IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON :
+ IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP,
+ NULL);
if (!elems)
return;
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index ba5fbacbeeda..dbbfe2d6842f 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -6,7 +6,7 @@
* Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2019, 2021-2024 Intel Corporation
+ * Copyright (C) 2019, 2021-2025 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -1783,7 +1783,10 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
}
elems = ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
- skb->len - baselen, false, NULL);
+ skb->len - baselen,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (!elems) {
ret = -ENOMEM;
goto out;
@@ -1902,7 +1905,10 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
}
elems = ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
- skb->len - baselen, false, NULL);
+ skb->len - baselen,
+ IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION,
+ NULL);
if (!elems)
return -ENOMEM;
diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c
index a53c55a879a8..1039794a0183 100644
--- a/net/mac80211/tests/elems.c
+++ b/net/mac80211/tests/elems.c
@@ -2,7 +2,7 @@
/*
* KUnit tests for element parsing
*
- * Copyright (C) 2023-2024 Intel Corporation
+ * Copyright (C) 2023-2025 Intel Corporation
*/
#include <kunit/test.h>
#include "../ieee80211_i.h"
@@ -15,6 +15,8 @@ static void mle_defrag(struct kunit *test)
.link_id = 12,
.from_ap = true,
.mode = IEEE80211_CONN_MODE_EHT,
+ /* type is not really relevant here */
+ .type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON,
};
struct ieee802_11_elems *parsed;
struct sk_buff *skb;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index c9931537f9d2..0c46009a3d63 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -2206,9 +2206,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
}
+ /* Passing NULL means an interface is picked for configuration */
if (local->virt_monitors > 0 &&
local->virt_monitors == local->open_count)
- ieee80211_add_virtual_monitor(local);
+ ieee80211_add_virtual_monitor(local, NULL);
if (!suspended)
return 0;
@@ -2347,7 +2348,7 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata,
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx,
conf);
- ieee80211_recalc_chanctx_min_def(local, chanctx, NULL, false);
+ ieee80211_recalc_chanctx_min_def(local, chanctx);
}
}
@@ -4016,23 +4017,23 @@ static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local,
if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED))
return 0;
- list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list)
- if (link->reserved_radar_required)
- radar_detect |= BIT(link->reserved.oper.width);
-
- /*
- * An in-place reservation context should not have any assigned vifs
- * until it replaces the other context.
- */
- WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER &&
- !list_empty(&ctx->assigned_links));
+ for_each_sdata_link(local, link) {
+ if (rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf) {
+ /*
+ * An in-place reservation context should not have any
+ * assigned links until it replaces the other context.
+ */
+ WARN_ON(ctx->replace_state ==
+ IEEE80211_CHANCTX_REPLACES_OTHER);
- list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) {
- if (!link->radar_required)
- continue;
+ if (link->radar_required)
+ radar_detect |=
+ BIT(link->conf->chanreq.oper.width);
+ }
- radar_detect |=
- BIT(link->conf->chanreq.oper.width);
+ if (link->reserved_chanctx == ctx &&
+ link->reserved_radar_required)
+ radar_detect |= BIT(link->reserved.oper.width);
}
return radar_detect;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 9f858a83e912..aa78dbaaf093 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1066,12 +1066,12 @@ int wiphy_register(struct wiphy *wiphy)
wiphy_regulatory_register(wiphy);
if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
- struct regulatory_request request;
-
- request.wiphy_idx = get_wiphy_idx(wiphy);
- request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
- request.alpha2[0] = '9';
- request.alpha2[1] = '9';
+ struct regulatory_request request = {
+ .wiphy_idx = get_wiphy_idx(wiphy),
+ .initiator = NL80211_REGDOM_SET_BY_DRIVER,
+ .alpha2[0] = '9',
+ .alpha2[1] = '9',
+ };
nl80211_send_reg_change_event(&request);
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b6bd7f4d6385..82f343663e8f 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -550,7 +550,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
bool signal_valid, unsigned long ts);
enum ieee80211_ap_reg_power
-cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len);
+cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,
+ u32 client_flags);
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 2187e148389d..29c92bc8291b 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6748,7 +6748,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
beacon_check.relax = true;
beacon_check.reg_power =
cfg80211_get_6ghz_power_type(params->beacon.tail,
- params->beacon.tail_len);
+ params->beacon.tail_len, 0);
if (!cfg80211_reg_check_beaconing(&rdev->wiphy, &params->chandef,
&beacon_check)) {
err = -EINVAL;
@@ -6927,7 +6927,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
beacon_check.relax = true;
beacon_check.reg_power =
cfg80211_get_6ghz_power_type(params->beacon.tail,
- params->beacon.tail_len);
+ params->beacon.tail_len, 0);
if (!cfg80211_reg_check_beaconing(&rdev->wiphy,
&wdev->links[link_id].ap.chandef,
&beacon_check)) {
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 90a9187a6b13..7546647752fd 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -2212,7 +2212,8 @@ struct cfg80211_inform_single_bss_data {
};
enum ieee80211_ap_reg_power
-cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len)
+cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,
+ u32 client_flags)
{
const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
struct ieee80211_he_operation *he_oper;
@@ -2230,26 +2231,13 @@ cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len)
if (!he_6ghz_oper)
return IEEE80211_REG_UNSET_AP;
- switch (u8_get_bits(he_6ghz_oper->control,
- IEEE80211_HE_6GHZ_OPER_CTRL_REG_INFO)) {
- case IEEE80211_6GHZ_CTRL_REG_LPI_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_LPI_AP:
- return IEEE80211_REG_LPI_AP;
- case IEEE80211_6GHZ_CTRL_REG_SP_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_SP_AP:
- case IEEE80211_6GHZ_CTRL_REG_INDOOR_SP_AP_OLD:
- return IEEE80211_REG_SP_AP;
- case IEEE80211_6GHZ_CTRL_REG_VLP_AP:
- return IEEE80211_REG_VLP_AP;
- default:
- return IEEE80211_REG_UNSET_AP;
- }
+ return cfg80211_6ghz_power_type(he_6ghz_oper->control, client_flags);
}
static bool cfg80211_6ghz_power_type_valid(const u8 *elems, size_t elems_len,
const u32 flags)
{
- switch (cfg80211_get_6ghz_power_type(elems, elems_len)) {
+ switch (cfg80211_get_6ghz_power_type(elems, elems_len, flags)) {
case IEEE80211_REG_LPI_AP:
return true;
case IEEE80211_REG_SP_AP: