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>
This commit is contained in:
Benjamin Berg
2025-11-10 14:18:20 +02:00
committed by Johannes Berg
parent b54cf0f449
commit a5aa46f1ac
4 changed files with 76 additions and 23 deletions

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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;