]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211: track MU-MIMO configuration on disabled interfaces
authorBenjamin Berg <benjamin.berg@intel.com>
Mon, 10 Nov 2025 12:18:20 +0000 (14:18 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 11 Nov 2025 10:05:10 +0000 (11:05 +0100)
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>
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/util.c

index c52b0456039ddcf460ecfbe91c98c5897eb975df..b51c2c8584ae09dab745c9b0972d3a43ede05676 100644 (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)) {
index 7f1ce9fc01c73ee6066966de839e43d65e82db60..9d9313eee59f8150734fa3abf1246161cd525621 100644 (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);
index a7873832d4fa68792ee1503a4c780d3bf46327f6..d7a4f203cde4f5b2b4db330f62785552fdd8a182 100644 (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 */
index 1a128f7aae820735e64ef5dacc9dffefbbeb341a..0c46009a3d63ff07dc8d7daa12f8b8250706e501 100644 (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;