Commit 1ce954c9 authored by Johannes Berg's avatar Johannes Berg
Browse files

wifi: mac80211: add and use chanctx usage iteration

In preparation for NAN interfaces using multiple channel
contexts, add an iterator macro that iterates all users
of a given channel context.

The logic during reserved assign/reassign handling the
bandwidth in ieee80211_get_chanctx_max_required_bw() is
a bit more complicated and should be cleaned up, so it
isn't yet converted.

Link: https://patch.msgid.link/20251105160431.5aaccc2f127d.I2b7fd0858a263916f43abab49c6d3cc0b5aa16ec@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 52363af3
Loading
Loading
Loading
Loading
+195 −147
Original line number Diff line number Diff line
@@ -12,18 +12,132 @@
#include "driver-ops.h"
#include "rate.h"

struct ieee80211_chanctx_user_iter {
	struct ieee80211_chan_req *chanreq;
	struct ieee80211_sub_if_data *sdata;
	struct ieee80211_link_data *link;
	enum nl80211_iftype iftype;
	bool reserved, radar_required, done;
	enum {
		CHANCTX_ITER_POS_ASSIGNED,
		CHANCTX_ITER_POS_RESERVED,
		CHANCTX_ITER_POS_DONE,
	} per_link;
};

enum ieee80211_chanctx_iter_type {
	CHANCTX_ITER_ALL,
	CHANCTX_ITER_RESERVED,
	CHANCTX_ITER_ASSIGNED,
};

static void ieee80211_chanctx_user_iter_next(struct ieee80211_local *local,
					     struct ieee80211_chanctx *ctx,
					     struct ieee80211_chanctx_user_iter *iter,
					     enum ieee80211_chanctx_iter_type type,
					     bool start)
{
	lockdep_assert_wiphy(local->hw.wiphy);

	if (start) {
		memset(iter, 0, sizeof(*iter));
		goto next_interface;
	}

next_link:
	for (int link_id = iter->link ? iter->link->link_id : 0;
	     link_id < ARRAY_SIZE(iter->sdata->link);
	     link_id++) {
		struct ieee80211_link_data *link;

		link = sdata_dereference(iter->sdata->link[link_id],
					 iter->sdata);
		if (!link)
			continue;

		switch (iter->per_link) {
		case CHANCTX_ITER_POS_ASSIGNED:
			iter->per_link = CHANCTX_ITER_POS_RESERVED;
			if (type != CHANCTX_ITER_RESERVED &&
			    rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf) {
				iter->link = link;
				iter->reserved = false;
				iter->radar_required = link->radar_required;
				iter->chanreq = &link->conf->chanreq;
				return;
			}
			fallthrough;
		case CHANCTX_ITER_POS_RESERVED:
			iter->per_link = CHANCTX_ITER_POS_DONE;
			if (type != CHANCTX_ITER_ASSIGNED &&
			    link->reserved_chanctx == ctx) {
				iter->link = link;
				iter->reserved = true;
				iter->radar_required =
					link->reserved_radar_required;

				iter->chanreq = &link->reserved;
				return;
			}
			fallthrough;
		case CHANCTX_ITER_POS_DONE:
			iter->per_link = CHANCTX_ITER_POS_ASSIGNED;
			continue;
		}
	}

next_interface:
	/* next (or first) interface */
	iter->sdata = list_prepare_entry(iter->sdata, &local->interfaces, list);
	list_for_each_entry_continue(iter->sdata, &local->interfaces, list) {
		/* AP_VLAN has a chanctx pointer but follows AP */
		if (iter->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
			continue;

		iter->link = NULL;
		iter->per_link = CHANCTX_ITER_POS_ASSIGNED;
		iter->iftype = iter->sdata->vif.type;
		goto next_link;
	}

	iter->done = true;
}

#define for_each_chanctx_user_assigned(local, ctx, iter)		\
	for (ieee80211_chanctx_user_iter_next(local, ctx, iter,		\
					      CHANCTX_ITER_ASSIGNED,	\
					      true);			\
	     !((iter)->done);						\
	     ieee80211_chanctx_user_iter_next(local, ctx, iter,		\
					      CHANCTX_ITER_ASSIGNED,	\
					      false))

#define for_each_chanctx_user_reserved(local, ctx, iter)		\
	for (ieee80211_chanctx_user_iter_next(local, ctx, iter,		\
					      CHANCTX_ITER_RESERVED,	\
					      true);			\
	     !((iter)->done);						\
	     ieee80211_chanctx_user_iter_next(local, ctx, iter,		\
					      CHANCTX_ITER_RESERVED,	\
					      false))

#define for_each_chanctx_user_all(local, ctx, iter)			\
	for (ieee80211_chanctx_user_iter_next(local, ctx, iter,		\
					      CHANCTX_ITER_ALL,		\
					      true);			\
	     !((iter)->done);						\
	     ieee80211_chanctx_user_iter_next(local, ctx, iter,		\
					      CHANCTX_ITER_ALL,		\
					      false))

static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
					  struct ieee80211_chanctx *ctx)
{
	struct ieee80211_link_data *link;
	struct ieee80211_chanctx_user_iter iter;
	int num = 0;

	lockdep_assert_wiphy(local->hw.wiphy);

	for_each_sdata_link(local, link) {
		if (rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf)
	for_each_chanctx_user_assigned(local, ctx, &iter)
		num++;
	}

	return num;
}
@@ -31,15 +145,11 @@ static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
					  struct ieee80211_chanctx *ctx)
{
	struct ieee80211_link_data *link;
	struct ieee80211_chanctx_user_iter iter;
	int num = 0;

	lockdep_assert_wiphy(local->hw.wiphy);

	for_each_sdata_link(local, link) {
		if (link->reserved_chanctx == ctx)
	for_each_chanctx_user_reserved(local, ctx, &iter)
		num++;
	}

	return num;
}
@@ -47,17 +157,11 @@ static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
int ieee80211_chanctx_refcount(struct ieee80211_local *local,
			       struct ieee80211_chanctx *ctx)
{
	struct ieee80211_link_data *link;
	struct ieee80211_chanctx_user_iter iter;
	int num = 0;

	lockdep_assert_wiphy(local->hw.wiphy);

	for_each_sdata_link(local, link) {
		if (rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf)
	for_each_chanctx_user_all(local, ctx, &iter)
		num++;
		if (link->reserved_chanctx == ctx)
			num++;
	}

	return num;
}
@@ -158,18 +262,15 @@ ieee80211_chanctx_reserved_chanreq(struct ieee80211_local *local,
				   const struct ieee80211_chan_req *req,
				   struct ieee80211_chan_req *tmp)
{
	struct ieee80211_link_data *link;
	struct ieee80211_chanctx_user_iter iter;

	lockdep_assert_wiphy(local->hw.wiphy);

	if (WARN_ON(!req))
		return NULL;

	for_each_sdata_link(local, link) {
		if (link->reserved_chanctx != ctx)
			continue;

		req = ieee80211_chanreq_compatible(&link->reserved, req, tmp);
	for_each_chanctx_user_reserved(local, ctx, &iter) {
		req = ieee80211_chanreq_compatible(iter.chanreq, req, tmp);
		if (!req)
			break;
	}
@@ -183,21 +284,16 @@ ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
				       const struct ieee80211_chan_req *compat,
				       struct ieee80211_chan_req *tmp)
{
	struct ieee80211_link_data *link;
	const struct ieee80211_chan_req *comp_def = compat;
	struct ieee80211_chanctx_user_iter iter;

	lockdep_assert_wiphy(local->hw.wiphy);

	for_each_sdata_link(local, link) {
		struct ieee80211_bss_conf *link_conf = link->conf;

		if (rcu_access_pointer(link_conf->chanctx_conf) != &ctx->conf)
	for_each_chanctx_user_assigned(local, ctx, &iter) {
		if (iter.link->reserved_chanctx)
			continue;

		if (link->reserved_chanctx)
			continue;

		comp_def = ieee80211_chanreq_compatible(&link_conf->chanreq,
		comp_def = ieee80211_chanreq_compatible(iter.chanreq,
							comp_def, tmp);
		if (!comp_def)
			break;
@@ -702,16 +798,12 @@ static bool
ieee80211_chanctx_radar_required(struct ieee80211_local *local,
				 struct ieee80211_chanctx *ctx)
{
	struct ieee80211_chanctx_conf *conf = &ctx->conf;
	struct ieee80211_link_data *link;
	struct ieee80211_chanctx_user_iter iter;

	lockdep_assert_wiphy(local->hw.wiphy);

	for_each_sdata_link(local, link) {
		if (rcu_access_pointer(link->conf->chanctx_conf) != conf)
			continue;
		if (!link->radar_required)
			continue;
	for_each_chanctx_user_assigned(local, ctx, &iter) {
		if (iter.radar_required)
			return true;
	}

@@ -829,27 +921,17 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
{
	struct ieee80211_chanctx_conf *conf = &ctx->conf;
	const struct ieee80211_chan_req *compat = NULL;
	struct ieee80211_link_data *link;
	struct ieee80211_chanctx_user_iter iter;
	struct ieee80211_chan_req tmp;
	struct sta_info *sta;

	lockdep_assert_wiphy(local->hw.wiphy);

	for_each_sdata_link(local, link) {
		struct ieee80211_bss_conf *link_conf;

		if (link->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
			continue;

		link_conf = link->conf;

		if (rcu_access_pointer(link_conf->chanctx_conf) != conf)
			continue;

	for_each_chanctx_user_assigned(local, ctx, &iter) {
		if (!compat)
			compat = &link_conf->chanreq;
			compat = iter.chanreq;

		compat = ieee80211_chanreq_compatible(&link_conf->chanreq,
		compat = ieee80211_chanreq_compatible(iter.chanreq,
						      compat, &tmp);
		if (WARN_ON_ONCE(!compat))
			return;
@@ -862,6 +944,7 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
	list_for_each_entry(sta, &local->sta_list, list) {
		struct ieee80211_sub_if_data *sdata = sta->sdata;
		struct ieee80211_chan_req tdls_chanreq = {};
		struct ieee80211_link_data *link;
		int tdls_link_id;

		if (!sta->uploaded ||
@@ -992,21 +1075,21 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
				   struct ieee80211_chanctx *chanctx)
{
	struct ieee80211_chanctx_user_iter iter;
	struct ieee80211_sub_if_data *sdata;
	u8 rx_chains_static, rx_chains_dynamic;
	struct ieee80211_link_data *link;

	lockdep_assert_wiphy(local->hw.wiphy);

	rx_chains_static = 1;
	rx_chains_dynamic = 1;

	for_each_sdata_link(local, link) {
	for_each_chanctx_user_assigned(local, chanctx, &iter) {
		u8 needed_static, needed_dynamic;

		switch (link->sdata->vif.type) {
		switch (iter.iftype) {
		case NL80211_IFTYPE_STATION:
			if (!link->sdata->u.mgd.associated)
			if (!iter.sdata->u.mgd.associated)
				continue;
			break;
		case NL80211_IFTYPE_MONITOR:
@@ -1022,26 +1105,23 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
			continue;
		}

		if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
			continue;

		if (link->sdata->vif.type == NL80211_IFTYPE_MONITOR) {
		if (iter.iftype == NL80211_IFTYPE_MONITOR) {
			rx_chains_dynamic = rx_chains_static = local->rx_chains;
			break;
		}

		switch (link->smps_mode) {
		switch (iter.link->smps_mode) {
		default:
			WARN_ONCE(1, "Invalid SMPS mode %d\n",
				  link->smps_mode);
				  iter.link->smps_mode);
			fallthrough;
		case IEEE80211_SMPS_OFF:
			needed_static = link->needed_rx_chains;
			needed_dynamic = link->needed_rx_chains;
			needed_static = iter.link->needed_rx_chains;
			needed_dynamic = iter.link->needed_rx_chains;
			break;
		case IEEE80211_SMPS_DYNAMIC:
			needed_static = 1;
			needed_dynamic = link->needed_rx_chains;
			needed_dynamic = iter.link->needed_rx_chains;
			break;
		case IEEE80211_SMPS_STATIC:
			needed_static = 1;
@@ -1513,7 +1593,6 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
				      int n_vifs)
{
	struct ieee80211_vif_chanctx_switch *vif_chsw;
	struct ieee80211_link_data *link;
	struct ieee80211_chanctx *ctx, *old_ctx;
	int i, err;

@@ -1525,6 +1604,8 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,

	i = 0;
	list_for_each_entry(ctx, &local->chanctx_list, list) {
		struct ieee80211_chanctx_user_iter iter;

		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
			continue;

@@ -1533,18 +1614,15 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
			goto out;
		}

		for_each_sdata_link(local, link) {
			if (link->reserved_chanctx != ctx)
				continue;

			if (!ieee80211_link_has_in_place_reservation(link))
		for_each_chanctx_user_reserved(local, ctx, &iter) {
			if (!ieee80211_link_has_in_place_reservation(iter.link))
				continue;

			old_ctx = ieee80211_link_get_chanctx(link);
			vif_chsw[i].vif = &link->sdata->vif;
			old_ctx = ieee80211_link_get_chanctx(iter.link);
			vif_chsw[i].vif = &iter.sdata->vif;
			vif_chsw[i].old_ctx = &old_ctx->conf;
			vif_chsw[i].new_ctx = &ctx->conf;
			vif_chsw[i].link_conf = link->conf;
			vif_chsw[i].link_conf = iter.link->conf;

			i++;
		}
@@ -1621,7 +1699,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
	 */

	list_for_each_entry(ctx, &local->chanctx_list, list) {
		struct ieee80211_link_data *link;
		struct ieee80211_chanctx_user_iter iter;

		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
			continue;
@@ -1637,14 +1715,11 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
		n_reserved = 0;
		n_ready = 0;

		for_each_sdata_link(local, link) {
			if (rcu_access_pointer(link->conf->chanctx_conf) != &ctx->conf)
				continue;

		for_each_chanctx_user_assigned(local, ctx, &iter) {
			n_assigned++;
			if (link->reserved_chanctx) {
			if (iter.link->reserved_chanctx) {
				n_reserved++;
				if (link->reserved_ready)
				if (iter.link->reserved_ready)
					n_ready++;
			}
		}
@@ -1661,15 +1736,12 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
		}

		ctx->conf.radar_enabled = false;
		for_each_sdata_link(local, link) {
			if (link->reserved_chanctx != ctx)
				continue;

			if (ieee80211_link_has_in_place_reservation(link) &&
			    !link->reserved_ready)
		for_each_chanctx_user_reserved(local, ctx, &iter) {
			if (ieee80211_link_has_in_place_reservation(iter.link) &&
			    !iter.link->reserved_ready)
				return -EAGAIN;

			old_ctx = ieee80211_link_get_chanctx(link);
			old_ctx = ieee80211_link_get_chanctx(iter.link);
			if (old_ctx) {
				if (old_ctx->replace_state ==
				    IEEE80211_CHANCTX_WILL_BE_REPLACED)
@@ -1680,7 +1752,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
				n_vifs_ctxless++;
			}

			if (link->reserved_radar_required)
			if (iter.radar_required)
				ctx->conf.radar_enabled = true;
		}
	}
@@ -1695,7 +1767,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)

	/* update station rate control and min width before switch */
	list_for_each_entry(ctx, &local->chanctx_list, list) {
		struct ieee80211_link_data *link;
		struct ieee80211_chanctx_user_iter iter;

		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
			continue;
@@ -1705,15 +1777,12 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
			goto err;
		}

		for_each_sdata_link(local, link) {
			if (link->reserved_chanctx != ctx)
				continue;

			if (!ieee80211_link_has_in_place_reservation(link))
		for_each_chanctx_user_reserved(local, ctx, &iter) {
			if (!ieee80211_link_has_in_place_reservation(iter.link))
				continue;

			ieee80211_chan_bw_change(local,
						 ieee80211_link_get_chanctx(link),
						 ieee80211_link_get_chanctx(iter.link),
						 true, true);
		}

@@ -1742,7 +1811,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
	 * context(s).
	 */
	list_for_each_entry(ctx, &local->chanctx_list, list) {
		struct ieee80211_link_data *link;
		struct ieee80211_chanctx_user_iter iter;

		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
			continue;
@@ -1752,14 +1821,12 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
			goto err;
		}

		for_each_sdata_link(local, link) {
			struct ieee80211_sub_if_data *sdata = link->sdata;
		for_each_chanctx_user_reserved(local, ctx, &iter) {
			struct ieee80211_link_data *link = iter.link;
			struct ieee80211_sub_if_data *sdata = iter.sdata;
			struct ieee80211_bss_conf *link_conf = link->conf;
			u64 changed = 0;

			if (link->reserved_chanctx != ctx)
				continue;

			if (!ieee80211_link_has_in_place_reservation(link))
				continue;

@@ -1772,9 +1839,9 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)

			ieee80211_check_fast_xmit_iface(sdata);

			link->radar_required = link->reserved_radar_required;
			link->radar_required = iter.radar_required;

			if (link_conf->chanreq.oper.width != link->reserved.oper.width)
			if (link_conf->chanreq.oper.width != iter.chanreq->oper.width)
				changed = BSS_CHANGED_BANDWIDTH;

			ieee80211_link_update_chanreq(link, &link->reserved);
@@ -1791,16 +1858,13 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
		ieee80211_recalc_radar_chanctx(local, ctx);
		ieee80211_recalc_chanctx_min_def(local, ctx);

		for_each_sdata_link(local, link) {
			if (link->reserved_chanctx != ctx)
		for_each_chanctx_user_reserved(local, ctx, &iter) {
			if (ieee80211_link_get_chanctx(iter.link) != ctx)
				continue;

			if (ieee80211_link_get_chanctx(link) != ctx)
				continue;
			iter.link->reserved_chanctx = NULL;

			link->reserved_chanctx = NULL;

			ieee80211_link_chanctx_reservation_complete(link);
			ieee80211_link_chanctx_reservation_complete(iter.link);
			ieee80211_chan_bw_change(local, ctx, false, false);
		}

@@ -1811,9 +1875,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
		 * reservation for originally requested interface has already
		 * succeeded at this point.
		 */
		for_each_sdata_link(local, link) {
			if (link->reserved_chanctx != ctx)
				continue;
		for_each_chanctx_user_reserved(local, ctx, &iter) {
			struct ieee80211_link_data *link = iter.link;

			if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
				continue;
@@ -1858,17 +1921,14 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)

err:
	list_for_each_entry(ctx, &local->chanctx_list, list) {
		struct ieee80211_link_data *link;
		struct ieee80211_chanctx_user_iter iter;

		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
			continue;

		for_each_sdata_link(local, link) {
			if (link->reserved_chanctx != ctx)
				continue;

			ieee80211_link_unreserve_chanctx(link);
			ieee80211_link_chanctx_reservation_complete(link);
		for_each_chanctx_user_reserved(local, ctx, &iter) {
			ieee80211_link_unreserve_chanctx(iter.link);
			ieee80211_link_chanctx_reservation_complete(iter.link);
		}
	}

@@ -2071,30 +2131,18 @@ ieee80211_chanctx_recheck(struct ieee80211_local *local,
			  struct ieee80211_chan_req *tmp)
{
	const struct ieee80211_chan_req *ret = req;
	struct ieee80211_link_data *link;
	struct ieee80211_chanctx_user_iter iter;

	lockdep_assert_wiphy(local->hw.wiphy);

	for_each_sdata_link(local, link) {
		if (link == skip_link)
	for_each_chanctx_user_all(local, ctx, &iter) {
		if (iter.link == skip_link)
			continue;

		if (rcu_access_pointer(link->conf->chanctx_conf) == &ctx->conf) {
			ret = ieee80211_chanreq_compatible(ret,
							   &link->conf->chanreq,
							   tmp);
			if (!ret)
				return NULL;
		}

		if (link->reserved_chanctx == ctx) {
			ret = ieee80211_chanreq_compatible(ret,
							   &link->reserved,
							   tmp);
		ret = ieee80211_chanreq_compatible(ret, iter.chanreq, tmp);
		if (!ret)
			return NULL;
	}
	}

	*tmp = *ret;
	return tmp;