Commit a5aa46f1 authored by Benjamin Berg's avatar Benjamin Berg Committed by Johannes Berg
Browse files

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: default avatarBenjamin Berg <benjamin.berg@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20251110141514.677915f8f6bb.If4e04a57052f9ca763562a67248b06fd80d0c2c1@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent b54cf0f4
Loading
Loading
Loading
Loading
+32 −15
Original line number Diff line number Diff line
@@ -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)))
	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)) {
+2 −1
Original line number Diff line number Diff line
@@ -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);
+40 −6
Original line number Diff line number Diff line
@@ -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 */
+2 −1
Original line number Diff line number Diff line
@@ -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;