summaryrefslogtreecommitdiff
path: root/net/mac80211
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-03-26 18:17:14 -0700
committerJakub Kicinski <kuba@kernel.org>2026-03-26 18:17:14 -0700
commitdbd94b9831bc52a1efb7ff3de841ffc3457428ce (patch)
treec542864d16b4f97130ce1d2aa8afcc1bebc85b62 /net/mac80211
parent7d89349fb8849a6147cc7310fcf9059c1504f50f (diff)
parent7dd6f81f4ef801b57f6ce7b0eee32aef5c488538 (diff)
downloadlwn-dbd94b9831bc52a1efb7ff3de841ffc3457428ce.tar.gz
lwn-dbd94b9831bc52a1efb7ff3de841ffc3457428ce.zip
Merge tag 'wireless-next-2026-03-26' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next
Johannes Berg says: ==================== A fairly big set of changes all over, notably with: - cfg80211: new APIs for NAN (Neighbor Aware Networking, aka Wi-Fi Aware) so less work must be in firmware - mt76: - mt7996/mt7925 MLO fixes/improvements - mt7996 NPU support (HW eth/wifi traffic offload) - iwlwifi: UNII-9 and continuing UHR work * tag 'wireless-next-2026-03-26' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next: (230 commits) wifi: mac80211: ignore reserved bits in reconfiguration status wifi: cfg80211: allow protected action frame TX for NAN wifi: ieee80211: Add some missing NAN definitions wifi: nl80211: Add a notification to notify NAN channel evacuation wifi: nl80211: add NL80211_CMD_NAN_ULW_UPDATE notification wifi: nl80211: allow reporting spurious NAN Data frames wifi: cfg80211: allow ToDS=0/FromDS=0 data frames on NAN data interfaces wifi: nl80211: define an API for configuring the NAN peer's schedule wifi: nl80211: add support for NAN stations wifi: cfg80211: separately store HT, VHT and HE capabilities for NAN wifi: cfg80211: add support for NAN data interface wifi: cfg80211: make sure NAN chandefs are valid wifi: cfg80211: Add an API to configure local NAN schedule wifi: mac80211: cleanup error path of ieee80211_do_open wifi: mac80211: extract channel logic from link logic wifi: iwlwifi: mld: set RX_FLAG_RADIOTAP_TLV_AT_END generically wifi: iwlwifi: reduce the number of prints upon firmware crash wifi: iwlwifi: fix the description of SESSION_PROTECTION_CMD wifi: iwlwifi: mld: introduce iwl_mld_vif_fw_id_valid wifi: iwlwifi: mld: block EMLSR during TDLS connections ... ==================== Link: https://patch.msgid.link/20260326152021.305959-3-johannes@sipsolutions.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/cfg.c4
-rw-r--r--net/mac80211/chan.c199
-rw-r--r--net/mac80211/he.c37
-rw-r--r--net/mac80211/ht.c6
-rw-r--r--net/mac80211/ibss.c4
-rw-r--r--net/mac80211/ieee80211_i.h12
-rw-r--r--net/mac80211/iface.c28
-rw-r--r--net/mac80211/mesh_plink.c3
-rw-r--r--net/mac80211/mlme.c17
-rw-r--r--net/mac80211/rx.c2
-rw-r--r--net/mac80211/trace.h5
-rw-r--r--net/mac80211/util.c1
-rw-r--r--net/mac80211/vht.c33
13 files changed, 220 insertions, 131 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c25d7c579952..b6163dcc7e92 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -718,6 +718,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_NAN_DATA:
/* shouldn't happen */
WARN_ON_ONCE(1);
break;
@@ -2140,12 +2141,13 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
return -EINVAL;
if (params->ht_capa)
- ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
params->ht_capa, link_sta);
/* VHT can override some HT caps such as the A-MSDU max length */
if (params->vht_capa)
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &sband->vht_cap,
params->vht_capa, NULL,
link_sta);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index fab9c1acd8eb..dd99fdc1ea9d 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -166,6 +166,13 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
for_each_chanctx_user_all(local, ctx, &iter)
num++;
+ /*
+ * This ctx is in the process of getting used,
+ * take it into consideration
+ */
+ if (ctx->will_be_used)
+ num++;
+
return num;
}
@@ -448,73 +455,93 @@ ieee80211_get_max_required_bw(struct ieee80211_link_data *link)
}
static enum nl80211_chan_width
+ieee80211_get_width_of_link(struct ieee80211_link_data *link)
+{
+ struct ieee80211_local *local = link->sdata->local;
+
+ switch (link->sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ if (!link->sdata->vif.cfg.assoc) {
+ /*
+ * The AP's sta->bandwidth may not yet be set
+ * at this point (pre-association), so simply
+ * take the width from the chandef. We cannot
+ * have TDLS peers yet (only after association).
+ */
+ return link->conf->chanreq.oper.width;
+ }
+ /*
+ * otherwise just use min_def like in AP, depending on what
+ * we currently think the AP STA (and possibly TDLS peers)
+ * require(s)
+ */
+ fallthrough;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ return ieee80211_get_max_required_bw(link);
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN:
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
+ NO_VIRTUAL_MONITOR));
+ fallthrough;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
+ return link->conf->chanreq.oper.width;
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_NAN_DATA:
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ /* Take the lowest possible, so it won't change the max width */
+ return NL80211_CHAN_WIDTH_20_NOHT;
+}
+
+static enum nl80211_chan_width
ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx,
struct ieee80211_link_data *rsvd_for,
bool check_reserved)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_link_data *link;
enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
+ struct ieee80211_chanctx_user_iter iter;
+ struct ieee80211_sub_if_data *sdata;
+ enum nl80211_chan_width width;
if (WARN_ON(check_reserved && rsvd_for))
return ctx->conf.def.width;
- for_each_sdata_link(local, link) {
- enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
-
- if (check_reserved) {
- if (link->reserved_chanctx != ctx)
- continue;
- } else if (link != rsvd_for &&
- rcu_access_pointer(link->conf->chanctx_conf) != &ctx->conf)
- continue;
-
- switch (link->sdata->vif.type) {
- case NL80211_IFTYPE_STATION:
- if (!link->sdata->vif.cfg.assoc) {
- /*
- * The AP's sta->bandwidth may not yet be set
- * at this point (pre-association), so simply
- * take the width from the chandef. We cannot
- * have TDLS peers yet (only after association).
- */
- width = link->conf->chanreq.oper.width;
- break;
- }
- /*
- * otherwise just use min_def like in AP, depending on what
- * we currently think the AP STA (and possibly TDLS peers)
- * require(s)
- */
- fallthrough;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- width = ieee80211_get_max_required_bw(link);
- break;
- case NL80211_IFTYPE_P2P_DEVICE:
- case NL80211_IFTYPE_NAN:
- continue;
- case NL80211_IFTYPE_MONITOR:
- WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
- NO_VIRTUAL_MONITOR));
- fallthrough;
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- case NL80211_IFTYPE_OCB:
- width = link->conf->chanreq.oper.width;
- break;
- case NL80211_IFTYPE_WDS:
- case NL80211_IFTYPE_UNSPECIFIED:
- case NUM_NL80211_IFTYPES:
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- WARN_ON_ONCE(1);
+ /* When this is true we only care about the reserving links */
+ if (check_reserved) {
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ width = ieee80211_get_width_of_link(iter.link);
+ max_bw = max(max_bw, width);
}
+ goto check_monitor;
+ }
+ /* Consider all assigned links */
+ for_each_chanctx_user_assigned(local, ctx, &iter) {
+ width = ieee80211_get_width_of_link(iter.link);
max_bw = max(max_bw, width);
}
+ if (!rsvd_for ||
+ rsvd_for->sdata == rcu_access_pointer(local->monitor_sdata))
+ goto check_monitor;
+
+ /* Consider the link for which this chanctx is reserved/going to be assigned */
+ width = ieee80211_get_width_of_link(rsvd_for);
+ max_bw = max(max_bw, width);
+
+check_monitor:
/* use the configured bandwidth in case of monitor interface */
sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
if (sdata &&
@@ -752,10 +779,9 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,
_ieee80211_change_chanctx(local, ctx, old_ctx, chanreq, NULL);
}
-/* Note: if successful, the returned chanctx is reserved for the link */
+/* Note: if successful, the returned chanctx will_be_used flag is set */
static struct ieee80211_chanctx *
ieee80211_find_chanctx(struct ieee80211_local *local,
- struct ieee80211_link_data *link,
const struct ieee80211_chan_req *chanreq,
enum ieee80211_chanctx_mode mode)
{
@@ -767,9 +793,6 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
return NULL;
- if (WARN_ON(link->reserved_chanctx))
- return NULL;
-
list_for_each_entry(ctx, &local->chanctx_list, list) {
const struct ieee80211_chan_req *compat;
@@ -790,12 +813,12 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
continue;
/*
- * Reserve the chanctx temporarily, as the driver might change
+ * Mark the chanctx as will be used, as the driver might change
* active links during callbacks we make into it below and/or
* later during assignment, which could (otherwise) cause the
* context to actually be removed.
*/
- link->reserved_chanctx = ctx;
+ ctx->will_be_used = true;
ieee80211_change_chanctx(local, ctx, ctx, compat);
@@ -1438,6 +1461,7 @@ ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link)
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
case NUM_NL80211_IFTYPES:
WARN_ON(1);
break;
@@ -2011,6 +2035,36 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link,
ieee80211_vif_use_reserved_switch(local);
}
+static struct ieee80211_chanctx *
+ieee80211_find_or_create_chanctx(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_chan_req *chanreq,
+ enum ieee80211_chanctx_mode mode,
+ bool assign_on_failure,
+ bool *reused_ctx)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx *ctx;
+ int radio_idx;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ ctx = ieee80211_find_chanctx(local, chanreq, mode);
+ if (ctx) {
+ *reused_ctx = true;
+ return ctx;
+ }
+
+ *reused_ctx = false;
+
+ if (!ieee80211_find_available_radio(local, chanreq,
+ sdata->wdev.radio_mask,
+ &radio_idx))
+ return ERR_PTR(-EBUSY);
+
+ return ieee80211_new_chanctx(local, chanreq, mode,
+ assign_on_failure, radio_idx);
+}
+
int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
const struct ieee80211_chan_req *chanreq,
enum ieee80211_chanctx_mode mode,
@@ -2020,8 +2074,7 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
struct ieee80211_local *local = sdata->local;
struct ieee80211_chanctx *ctx;
u8 radar_detect_width = 0;
- bool reserved = false;
- int radio_idx;
+ bool reused_ctx = false;
int ret;
lockdep_assert_wiphy(local->hw.wiphy);
@@ -2049,17 +2102,8 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
if (!local->in_reconfig)
__ieee80211_link_release_channel(link, false);
- ctx = ieee80211_find_chanctx(local, link, chanreq, mode);
- /* Note: context is now reserved */
- if (ctx)
- reserved = true;
- else if (!ieee80211_find_available_radio(local, chanreq,
- sdata->wdev.radio_mask,
- &radio_idx))
- ctx = ERR_PTR(-EBUSY);
- else
- ctx = ieee80211_new_chanctx(local, chanreq, mode,
- assign_on_failure, radio_idx);
+ ctx = ieee80211_find_or_create_chanctx(sdata, chanreq, mode,
+ assign_on_failure, &reused_ctx);
if (IS_ERR(ctx)) {
ret = PTR_ERR(ctx);
goto out;
@@ -2069,10 +2113,13 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
ret = ieee80211_assign_link_chanctx(link, ctx, assign_on_failure);
- if (reserved) {
- /* remove reservation */
- WARN_ON(link->reserved_chanctx != ctx);
- link->reserved_chanctx = NULL;
+ /*
+ * In case an existing channel context is being used, we marked it as
+ * will_be_used, now that it is assigned - clear this indication
+ */
+ if (reused_ctx) {
+ WARN_ON(!ctx->will_be_used);
+ ctx->will_be_used = false;
}
if (ret) {
diff --git a/net/mac80211/he.c b/net/mac80211/he.c
index f7b05e59374c..93e0342cff4f 100644
--- a/net/mac80211/he.c
+++ b/net/mac80211/he.c
@@ -108,14 +108,13 @@ static void ieee80211_he_mcs_intersection(__le16 *he_own_rx, __le16 *he_peer_rx,
}
void
-ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
- const u8 *he_cap_ie, u8 he_cap_len,
- const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
- struct link_sta_info *link_sta)
+_ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_sta_he_cap *own_he_cap_ptr,
+ const u8 *he_cap_ie, u8 he_cap_len,
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+ struct link_sta_info *link_sta)
{
struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
- const struct ieee80211_sta_he_cap *own_he_cap_ptr;
struct ieee80211_sta_he_cap own_he_cap;
struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie;
u8 he_ppe_size;
@@ -125,12 +124,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
memset(he_cap, 0, sizeof(*he_cap));
- if (!he_cap_ie)
- return;
-
- own_he_cap_ptr =
- ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
- if (!own_he_cap_ptr)
+ if (!he_cap_ie || !own_he_cap_ptr || !own_he_cap_ptr->has_he)
return;
own_he_cap = *own_he_cap_ptr;
@@ -164,7 +158,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta);
link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
- if (sband->band == NL80211_BAND_6GHZ && he_6ghz_capa)
+ if (he_6ghz_capa)
ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta);
ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80,
@@ -208,6 +202,23 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
}
void
+ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ const u8 *he_cap_ie, u8 he_cap_len,
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+ struct link_sta_info *link_sta)
+{
+ const struct ieee80211_sta_he_cap *own_he_cap =
+ ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
+
+ _ieee80211_he_cap_ie_to_sta_he_cap(sdata, own_he_cap, he_cap_ie,
+ he_cap_len,
+ (sband->band == NL80211_BAND_6GHZ) ?
+ he_6ghz_capa : NULL,
+ link_sta);
+}
+
+void
ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif,
const struct ieee80211_he_operation *he_op_ie)
{
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 33f1e1b235e9..410e2354f33a 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -136,7 +136,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_ht_cap *own_cap_ptr,
const struct ieee80211_ht_cap *ht_cap_ie,
struct link_sta_info *link_sta)
{
@@ -151,12 +151,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
memset(&ht_cap, 0, sizeof(ht_cap));
- if (!ht_cap_ie || !sband->ht_cap.ht_supported)
+ if (!ht_cap_ie || !own_cap_ptr->ht_supported)
goto apply;
ht_cap.ht_supported = true;
- own_cap = sband->ht_cap;
+ own_cap = *own_cap_ptr;
/*
* If user has specified capability over-rides, take care
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 0298272c37ec..1e1ab25d9d8d 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -1014,7 +1014,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
ieee80211_chandef_ht_oper(elems->ht_operation, &chandef);
memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
- rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata,
+ &sband->ht_cap,
&htcap_ie,
&sta->deflink);
@@ -1033,6 +1034,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
&chandef);
memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &sband->vht_cap,
&cap_ie, NULL,
&sta->deflink);
if (memcmp(&cap, &sta->sta.deflink.vht_cap, sizeof(cap)))
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d71e0c6d2165..bacb49ad2817 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -928,6 +928,9 @@ struct ieee80211_chanctx {
bool radar_detected;
+ /* This chanctx is in process of getting used */
+ bool will_be_used;
+
/* MUST be last - ends in a flexible-array member. */
struct ieee80211_chanctx_conf conf;
};
@@ -2185,7 +2188,7 @@ void ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata,
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_ht_cap *ht_cap);
bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_ht_cap *own_cap,
const struct ieee80211_ht_cap *ht_cap_ie,
struct link_sta_info *link_sta);
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
@@ -2270,6 +2273,7 @@ void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local,
void
ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_vht_cap *own_vht_cap,
const struct ieee80211_vht_cap *vht_cap_ie,
const struct ieee80211_vht_cap *vht_cap_ie2,
struct link_sta_info *link_sta);
@@ -2310,6 +2314,12 @@ ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *sta);
/* HE */
void
+_ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_sta_he_cap *own_he_cap,
+ const u8 *he_cap_ie, u8 he_cap_len,
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+ struct link_sta_info *link_sta);
+void
ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
const u8 *he_cap_ie, u8 he_cap_len,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 40ce0bb72726..125897717a4c 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1222,14 +1222,14 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
}
}
- set_bit(SDATA_STATE_RUNNING, &sdata->state);
-
ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
if (ret) {
kfree(sdata);
return ret;
}
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
mutex_lock(&local->iflist_mtx);
rcu_assign_pointer(local->monitor_sdata, sdata);
mutex_unlock(&local->iflist_mtx);
@@ -1242,6 +1242,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
mutex_unlock(&local->iflist_mtx);
synchronize_net();
drv_remove_interface(local, sdata);
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
kfree(sdata);
return ret;
}
@@ -1360,8 +1361,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
break;
}
case NL80211_IFTYPE_AP:
- sdata->bss = &sdata->u.ap;
- break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_MONITOR:
@@ -1369,6 +1368,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
/* no special treatment */
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -1386,8 +1386,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
local->reconfig_failure = false;
res = drv_start(local);
- if (res)
- goto err_del_bss;
+ if (res) {
+ /*
+ * no need to worry about AP_VLAN cleanup since in that
+ * case we can't have open_count == 0
+ */
+ return res;
+ }
ieee80211_led_radio(local, true);
ieee80211_mod_tpt_led_trig(local,
IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
@@ -1458,6 +1463,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
netif_carrier_on(dev);
list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list);
break;
+ case NL80211_IFTYPE_AP:
+ sdata->bss = &sdata->u.ap;
+ fallthrough;
default:
if (coming_up) {
ieee80211_del_virtual_monitor(local);
@@ -1546,12 +1554,10 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
err_stop:
if (!local->open_count)
drv_stop(local, false);
- err_del_bss:
- sdata->bss = NULL;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
list_del(&sdata->u.vlan.list);
- /* might already be clear but that doesn't matter */
- clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+ /* Might not be initialized yet, but it is harmless */
+ sdata->bss = NULL;
return res;
}
@@ -1940,6 +1946,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_P2P_DEVICE:
sdata->vif.bss_conf.bssid = sdata->vif.addr;
break;
+ case NL80211_IFTYPE_NAN_DATA:
+ break;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_WDS:
case NUM_NL80211_IFTYPES:
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 7d823a55636f..803106fc3134 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -450,12 +450,13 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
sta->sta.deflink.supp_rates[sband->band] = rates;
- if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
elems->ht_cap_elem,
&sta->deflink))
changed |= IEEE80211_RC_BW_CHANGED;
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &sband->vht_cap,
elems->vht_cap_elem, NULL,
&sta->deflink);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 0cd8d07bf668..7fc5616cb244 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5586,7 +5586,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
/* Set up internal HT/VHT capabilities */
if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT)
- ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
elems->ht_cap_elem,
link_sta);
@@ -5622,6 +5622,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
}
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &sband->vht_cap,
elems->vht_cap_elem,
bss_vht_cap, link_sta);
rcu_read_unlock();
@@ -10458,8 +10459,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
pos = mgmt->u.action.ml_reconf_resp.variable;
len -= offsetofend(typeof(*mgmt), u.action.ml_reconf_resp);
- /* each status duple is 3 octets */
- if (len < mgmt->u.action.ml_reconf_resp.count * 3) {
+ if (len < mgmt->u.action.ml_reconf_resp.count *
+ sizeof(struct ieee80211_ml_reconf_status)) {
sdata_info(sdata,
"mlo: reconf: unexpected len=%zu, count=%u\n",
len, mgmt->u.action.ml_reconf_resp.count);
@@ -10468,9 +10469,11 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
link_mask = sta_changed_links;
for (i = 0; i < mgmt->u.action.ml_reconf_resp.count; i++) {
- u16 status = get_unaligned_le16(pos + 1);
+ struct ieee80211_ml_reconf_status *reconf_status = (void *)pos;
+ u16 status = le16_to_cpu(reconf_status->status);
- link_id = *pos;
+ link_id = u8_get_bits(reconf_status->info,
+ IEEE80211_ML_RECONF_LINK_ID_MASK);
if (!(link_mask & BIT(link_id))) {
sdata_info(sdata,
@@ -10505,8 +10508,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.reconf.added_links &= ~BIT(link_id);
}
- pos += 3;
- len -= 3;
+ pos += sizeof(*reconf_status);
+ len -= sizeof(*reconf_status);
}
if (link_mask) {
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 19c33f7a8193..d9a654ef082d 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4607,6 +4607,8 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
(ieee80211_is_public_action(hdr, skb->len) ||
(ieee80211_is_auth(hdr->frame_control) &&
ether_addr_equal(sdata->vif.addr, hdr->addr1)));
+ case NL80211_IFTYPE_NAN_DATA:
+ return false;
default:
break;
}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 1f0c07eaad1b..e5968d754f8b 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1778,9 +1778,8 @@ TRACE_EVENT(drv_switch_vif_chanctx,
SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type);
SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p);
SWITCH_ENTRY_ASSIGN(link_id, link_conf->link_id);
- strncpy(local_vifs[i].vif.vif_name,
- sdata->name,
- sizeof(local_vifs[i].vif.vif_name));
+ strscpy_pad(local_vifs[i].vif.vif_name,
+ sdata->name);
SWITCH_ENTRY_ASSIGN(old_chandef.control_freq,
old_ctx->def.chan->center_freq);
SWITCH_ENTRY_ASSIGN(old_chandef.freq_offset,
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 55054de62508..8987a4504520 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -2118,6 +2118,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
return res;
}
break;
+ case NL80211_IFTYPE_NAN_DATA:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_P2P_DEVICE:
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 80120f9f17b6..a6570781740a 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -4,7 +4,7 @@
*
* Portions of this file
* Copyright(c) 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2018-2026 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -115,6 +115,7 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
void
ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_vht_cap *own_vht_cap,
const struct ieee80211_vht_cap *vht_cap_ie,
const struct ieee80211_vht_cap *vht_cap_ie2,
struct link_sta_info *link_sta)
@@ -122,7 +123,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
struct ieee80211_sta_vht_cap own_cap;
u32 cap_info, i;
- bool have_80mhz;
u32 mpdu_len;
memset(vht_cap, 0, sizeof(*vht_cap));
@@ -130,22 +130,25 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
if (!link_sta->pub->ht_cap.ht_supported)
return;
- if (!vht_cap_ie || !sband->vht_cap.vht_supported)
+ if (!vht_cap_ie || !own_vht_cap->vht_supported)
return;
- /* Allow VHT if at least one channel on the sband supports 80 MHz */
- have_80mhz = false;
- for (i = 0; i < sband->n_channels; i++) {
- if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_NO_80MHZ))
- continue;
+ if (sband) {
+ /* Allow VHT if at least one channel on the sband supports 80 MHz */
+ bool have_80mhz = false;
- have_80mhz = true;
- break;
- }
+ for (i = 0; i < sband->n_channels; i++) {
+ if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_NO_80MHZ))
+ continue;
- if (!have_80mhz)
- return;
+ have_80mhz = true;
+ break;
+ }
+
+ if (!have_80mhz)
+ return;
+ }
/*
* A VHT STA must support 40 MHz, but if we verify that here
@@ -156,7 +159,7 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
vht_cap->vht_supported = true;
- own_cap = sband->vht_cap;
+ own_cap = *own_vht_cap;
/*
* If user has specified capability overrides, take care
* of that if the station we're setting up is the AP that