wifi: mac80211: extend ifcomb check functions for multi-radio

Add support for counting global and per-radio max/current number of
channels, as well as checking radio-specific interface combinations.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Link: https://patch.msgid.link/e76307f8ce562a91a74faab274ae01f6a5ba0a2e.1720514221.git-series.nbd@nbd.name
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Felix Fietkau 2024-07-09 10:38:35 +02:00 committed by Johannes Berg
parent 2920bc8d91
commit 0874bcd0e1
6 changed files with 114 additions and 65 deletions

View File

@ -263,7 +263,7 @@ static int ieee80211_start_p2p_device(struct wiphy *wiphy,
lockdep_assert_wiphy(sdata->local->hw.wiphy); lockdep_assert_wiphy(sdata->local->hw.wiphy);
ret = ieee80211_check_combinations(sdata, NULL, 0, 0); ret = ieee80211_check_combinations(sdata, NULL, 0, 0, -1);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -285,7 +285,7 @@ static int ieee80211_start_nan(struct wiphy *wiphy,
lockdep_assert_wiphy(sdata->local->hw.wiphy); lockdep_assert_wiphy(sdata->local->hw.wiphy);
ret = ieee80211_check_combinations(sdata, NULL, 0, 0); ret = ieee80211_check_combinations(sdata, NULL, 0, 0, -1);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -4008,7 +4008,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
goto out; goto out;
/* if reservation is invalid then this will fail */ /* if reservation is invalid then this will fail */
err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0); err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0, -1);
if (err) { if (err) {
ieee80211_link_unreserve_chanctx(link_data); ieee80211_link_unreserve_chanctx(link_data);
goto out; goto out;
@ -5203,4 +5203,5 @@ const struct cfg80211_ops mac80211_config_ops = {
.del_link_station = ieee80211_del_link_station, .del_link_station = ieee80211_del_link_station,
.set_hw_timestamp = ieee80211_set_hw_timestamp, .set_hw_timestamp = ieee80211_set_hw_timestamp,
.set_ttlm = ieee80211_set_ttlm, .set_ttlm = ieee80211_set_ttlm,
.get_radio_mask = ieee80211_get_radio_mask,
}; };

View File

@ -47,24 +47,29 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
ieee80211_chanctx_num_reserved(local, ctx); ieee80211_chanctx_num_reserved(local, ctx);
} }
static int ieee80211_num_chanctx(struct ieee80211_local *local) static int ieee80211_num_chanctx(struct ieee80211_local *local, int radio_idx)
{ {
struct ieee80211_chanctx *ctx; struct ieee80211_chanctx *ctx;
int num = 0; int num = 0;
lockdep_assert_wiphy(local->hw.wiphy); lockdep_assert_wiphy(local->hw.wiphy);
list_for_each_entry(ctx, &local->chanctx_list, list) list_for_each_entry(ctx, &local->chanctx_list, list) {
if (radio_idx >= 0 && ctx->conf.radio_idx != radio_idx)
continue;
num++; num++;
}
return num; return num;
} }
static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local,
int radio_idx)
{ {
lockdep_assert_wiphy(local->hw.wiphy); lockdep_assert_wiphy(local->hw.wiphy);
return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); return ieee80211_num_chanctx(local, radio_idx) <
ieee80211_max_num_channels(local, radio_idx);
} }
static struct ieee80211_chanctx * static struct ieee80211_chanctx *
@ -1101,7 +1106,7 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link,
new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode); new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode);
if (!new_ctx) { if (!new_ctx) {
if (ieee80211_can_create_new_chanctx(local)) { if (ieee80211_can_create_new_chanctx(local, -1)) {
new_ctx = ieee80211_new_chanctx(local, chanreq, mode, new_ctx = ieee80211_new_chanctx(local, chanreq, mode,
false); false);
if (IS_ERR(new_ctx)) if (IS_ERR(new_ctx))
@ -1822,7 +1827,7 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
link->radar_required = ret; link->radar_required = ret;
ret = ieee80211_check_combinations(sdata, &chanreq->oper, mode, ret = ieee80211_check_combinations(sdata, &chanreq->oper, mode,
radar_detect_width); radar_detect_width, -1);
if (ret < 0) if (ret < 0)
goto out; goto out;

View File

@ -1746,7 +1746,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE; IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE;
ret = ieee80211_check_combinations(sdata, &params->chandef, chanmode, ret = ieee80211_check_combinations(sdata, &params->chandef, chanmode,
radar_detect_width); radar_detect_width, -1);
if (ret < 0) if (ret < 0)
return ret; return ret;

View File

@ -2640,8 +2640,9 @@ void ieee80211_recalc_dtim(struct ieee80211_local *local,
int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef, const struct cfg80211_chan_def *chandef,
enum ieee80211_chanctx_mode chanmode, enum ieee80211_chanctx_mode chanmode,
u8 radar_detect); u8 radar_detect, int radio_idx);
int ieee80211_max_num_channels(struct ieee80211_local *local); int ieee80211_max_num_channels(struct ieee80211_local *local, int radio_idx);
u32 ieee80211_get_radio_mask(struct wiphy *wiphy, struct net_device *dev);
void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx); struct ieee80211_chanctx *ctx);

View File

@ -397,7 +397,7 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
} }
} }
return ieee80211_check_combinations(sdata, NULL, 0, 0); return ieee80211_check_combinations(sdata, NULL, 0, 0, -1);
} }
static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata, static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata,

View File

@ -3932,20 +3932,103 @@ static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local,
return radar_detect; return radar_detect;
} }
static u32
__ieee80211_get_radio_mask(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_bss_conf *link_conf;
struct ieee80211_chanctx_conf *conf;
unsigned int link_id;
u32 mask = 0;
for_each_vif_active_link(&sdata->vif, link_conf, link_id) {
conf = sdata_dereference(link_conf->chanctx_conf, sdata);
if (!conf || conf->radio_idx < 0)
continue;
mask |= BIT(conf->radio_idx);
}
return mask;
}
u32 ieee80211_get_radio_mask(struct wiphy *wiphy, struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
return __ieee80211_get_radio_mask(sdata);
}
static bool
ieee80211_sdata_uses_radio(struct ieee80211_sub_if_data *sdata, int radio_idx)
{
if (radio_idx < 0)
return true;
return __ieee80211_get_radio_mask(sdata) & BIT(radio_idx);
}
static int
ieee80211_fill_ifcomb_params(struct ieee80211_local *local,
struct iface_combination_params *params,
const struct cfg80211_chan_def *chandef,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_sub_if_data *sdata_iter;
struct ieee80211_chanctx *ctx;
int total = !!sdata;
list_for_each_entry(ctx, &local->chanctx_list, list) {
if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
continue;
if (params->radio_idx >= 0 &&
ctx->conf.radio_idx != params->radio_idx)
continue;
params->radar_detect |=
ieee80211_chanctx_radar_detect(local, ctx);
if (chandef && ctx->mode != IEEE80211_CHANCTX_EXCLUSIVE &&
cfg80211_chandef_compatible(chandef, &ctx->conf.def))
continue;
params->num_different_channels++;
}
list_for_each_entry(sdata_iter, &local->interfaces, list) {
struct wireless_dev *wdev_iter;
wdev_iter = &sdata_iter->wdev;
if (sdata_iter == sdata ||
!ieee80211_sdata_running(sdata_iter) ||
cfg80211_iftype_allowed(local->hw.wiphy,
wdev_iter->iftype, 0, 1))
continue;
if (!ieee80211_sdata_uses_radio(sdata_iter, params->radio_idx))
continue;
params->iftype_num[wdev_iter->iftype]++;
total++;
}
return total;
}
int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef, const struct cfg80211_chan_def *chandef,
enum ieee80211_chanctx_mode chanmode, enum ieee80211_chanctx_mode chanmode,
u8 radar_detect) u8 radar_detect, int radio_idx)
{ {
bool shared = chanmode == IEEE80211_CHANCTX_SHARED;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_sub_if_data *sdata_iter;
enum nl80211_iftype iftype = sdata->wdev.iftype; enum nl80211_iftype iftype = sdata->wdev.iftype;
struct ieee80211_chanctx *ctx;
int total = 1;
struct iface_combination_params params = { struct iface_combination_params params = {
.radar_detect = radar_detect, .radar_detect = radar_detect,
.radio_idx = -1, .radio_idx = radio_idx,
}; };
int total;
lockdep_assert_wiphy(local->hw.wiphy); lockdep_assert_wiphy(local->hw.wiphy);
@ -3982,37 +4065,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
if (iftype != NL80211_IFTYPE_UNSPECIFIED) if (iftype != NL80211_IFTYPE_UNSPECIFIED)
params.iftype_num[iftype] = 1; params.iftype_num[iftype] = 1;
list_for_each_entry(ctx, &local->chanctx_list, list) { total = ieee80211_fill_ifcomb_params(local, &params,
if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) shared ? chandef : NULL,
continue; sdata);
params.radar_detect |=
ieee80211_chanctx_radar_detect(local, ctx);
if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
params.num_different_channels++;
continue;
}
if (chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
cfg80211_chandef_compatible(chandef,
&ctx->conf.def))
continue;
params.num_different_channels++;
}
list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) {
struct wireless_dev *wdev_iter;
wdev_iter = &sdata_iter->wdev;
if (sdata_iter == sdata ||
!ieee80211_sdata_running(sdata_iter) ||
cfg80211_iftype_allowed(local->hw.wiphy,
wdev_iter->iftype, 0, 1))
continue;
params.iftype_num[wdev_iter->iftype]++;
total++;
}
if (total == 1 && !params.radar_detect) if (total == 1 && !params.radar_detect)
return 0; return 0;
@ -4029,30 +4084,17 @@ ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c,
c->num_different_channels); c->num_different_channels);
} }
int ieee80211_max_num_channels(struct ieee80211_local *local) int ieee80211_max_num_channels(struct ieee80211_local *local, int radio_idx)
{ {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_chanctx *ctx;
u32 max_num_different_channels = 1; u32 max_num_different_channels = 1;
int err; int err;
struct iface_combination_params params = { struct iface_combination_params params = {
.radio_idx = -1, .radio_idx = radio_idx,
}; };
lockdep_assert_wiphy(local->hw.wiphy); lockdep_assert_wiphy(local->hw.wiphy);
list_for_each_entry(ctx, &local->chanctx_list, list) { ieee80211_fill_ifcomb_params(local, &params, NULL, NULL);
if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
continue;
params.num_different_channels++;
params.radar_detect |=
ieee80211_chanctx_radar_detect(local, ctx);
}
list_for_each_entry_rcu(sdata, &local->interfaces, list)
params.iftype_num[sdata->wdev.iftype]++;
err = cfg80211_iter_combinations(local->hw.wiphy, &params, err = cfg80211_iter_combinations(local->hw.wiphy, &params,
ieee80211_iter_max_chans, ieee80211_iter_max_chans,