summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Berg <benjamin.berg@intel.com>2025-11-10 14:18:20 +0200
committerJohannes Berg <johannes.berg@intel.com>2025-11-11 11:05:10 +0100
commita5aa46f1ac4f53e03b9b75cbf55634131f2f8cac (patch)
treede7828ceb072809021f213078fc5c41f9f3cd50c
parentb54cf0f4495a8f3fa94245cdda7716792400299e (diff)
wifi: mac80211: track MU-MIMO configuration on disabled interfaces
For monitoring, userspace will try to configure the VIF sdata, while the driver may see the monitor_sdata that is created when only monitor interfaces are up. This causes the odd situation that it may not be possible to store the MU-MIMO configuration on monitor_sdata. Fix this by storing that information on the VIF sdata and updating the monitor_sdata when available and the interface is up. Also, adjust the code that adds monitor_sdata so that it will configure MU-MIMO based on the newly added interface or one of the existing ones. This should give a mostly consistent behaviour when configuring MU-MIMO on sniffer interfaces. Should the user configure MU-MIMO on multiple sniffer interfaces, then mac80211 will simply select one of the configurations. This behaviour should be good enough and avoids breaking user expectations in the common scenarios. Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Reviewed-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com> Link: https://patch.msgid.link/20251110141514.677915f8f6bb.If4e04a57052f9ca763562a67248b06fd80d0c2c1@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/mac80211/cfg.c47
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/iface.c46
-rw-r--r--net/mac80211/util.c3
4 files changed, 76 insertions, 23 deletions
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/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 7f1ce9fc01c7..9d9313eee59f 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -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);
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/util.c b/net/mac80211/util.c
index 1a128f7aae82..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;