From: Johannes Berg When two interfaces share a channel context, disable NPCA unless both are AP interfaces that require NPCA. This way, two AP interfaces can have identical chandefs set up and share the channel context, but any non-APs cannot share a chanctx with NPCA (they'd almost certainly have different BSS color.) This doesn't mean the chanctx cannot be shared but rather that NPCA will be disabled on the shared channel context. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 7 +++++++ net/mac80211/cfg.c | 15 ++++++++++++--- net/mac80211/chan.c | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 02318a4be0e1..62bfa298c67f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -218,6 +218,8 @@ struct ieee80211_low_level_stats { * bandwidth) OFDMA settings need to be changed * @IEEE80211_CHANCTX_CHANGE_PUNCTURING: The punctured channel(s) bitmap * was changed. + * @IEEE80211_CHANCTX_CHANGE_NPCA: NPCA configuration changed + * @IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT: NPCA puncturing changed */ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0), @@ -227,6 +229,8 @@ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_MIN_DEF = BIT(4), IEEE80211_CHANCTX_CHANGE_AP = BIT(5), IEEE80211_CHANCTX_CHANGE_PUNCTURING = BIT(6), + IEEE80211_CHANCTX_CHANGE_NPCA = BIT(7), + IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT = BIT(8), }; /** @@ -234,10 +238,13 @@ enum ieee80211_chanctx_change { * @oper: channel definition to use for operation * @ap: the channel definition of the AP, if any * (otherwise the chan member is %NULL) + * @require_npca: If NPCA is configured, require it to + * remain, this is used by AP interfaces */ struct ieee80211_chan_req { struct cfg80211_chan_def oper; struct cfg80211_chan_def ap; + bool require_npca; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 00261bd6674b..8f8d860944a8 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1612,7 +1612,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id = params->beacon.link_id; struct ieee80211_link_data *link; struct ieee80211_bss_conf *link_conf; - struct ieee80211_chan_req chanreq = { .oper = params->chandef }; + struct ieee80211_chan_req chanreq = { + .oper = params->chandef, + .require_npca = true, + }; u64 tsf; lockdep_assert_wiphy(local->hw.wiphy); @@ -4625,7 +4628,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_chan_req chanreq = { .oper = params->chandef }; + struct ieee80211_chan_req chanreq = { + .oper = params->chandef, + .require_npca = true, + }; struct ieee80211_local *local = sdata->local; struct ieee80211_channel_switch ch_switch = { .link_id = params->link_id, @@ -5067,7 +5073,10 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_link_data *link; - struct ieee80211_chan_req chanreq = { .oper = *chandef }; + struct ieee80211_chan_req chanreq = { + .oper = *chandef, + .require_npca = true, + }; int ret; u64 changed = 0; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 248531051a4e..6c7f5c9de026 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -285,19 +285,41 @@ ieee80211_chanreq_compatible(const struct ieee80211_chan_req *a, const struct ieee80211_chan_req *b, struct ieee80211_chan_req *tmp) { + struct ieee80211_chan_req _a = *a, _b = *b; const struct cfg80211_chan_def *compat; if (a->ap.chan && b->ap.chan && !cfg80211_chandef_identical(&a->ap, &b->ap)) return NULL; - compat = cfg80211_chandef_compatible(&a->oper, &b->oper); + /* + * Remove NPCA if it's not required, so that interfaces + * sharing a channel context will not use NPCA while the + * channel context is shared. + * If both sides are AP interfaces requiring NPC, there's + * an assumption that userspace will set them up with + * identical configurations and the same BSS color + * (if the config is not identical, sharing will fail due + * to cfg80211_chandef_compatible() failing below.) + */ + if (!_a.require_npca) { + _a.oper.npca_chan = NULL; + _a.oper.npca_punctured = 0; + } + + if (!_b.require_npca) { + _b.oper.npca_chan = NULL; + _b.oper.npca_punctured = 0; + } + + compat = cfg80211_chandef_compatible(&_a.oper, &_b.oper); if (!compat) return NULL; /* Note: later code assumes this always fills & returns tmp if compat */ tmp->oper = *compat; tmp->ap = a->ap.chan ? a->ap : b->ap; + tmp->require_npca = a->require_npca && b->require_npca; return tmp; } @@ -753,7 +775,6 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, const struct ieee80211_chan_req *chanreq, struct ieee80211_link_data *rsvd_for) { - const struct cfg80211_chan_def *chandef = &chanreq->oper; struct ieee80211_chan_req ctx_req = { .oper = ctx->conf.def, .ap = ctx->conf.ap, @@ -761,7 +782,7 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, u32 changed = 0; /* 5/10 MHz not handled here */ - switch (chandef->width) { + switch (chanreq->oper.width) { case NL80211_CHAN_WIDTH_1: case NL80211_CHAN_WIDTH_2: case NL80211_CHAN_WIDTH_4: @@ -806,10 +827,15 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; if (ctx->conf.def.punctured != chanreq->oper.punctured) changed |= IEEE80211_CHANCTX_CHANGE_PUNCTURING; + if (ctx->conf.def.npca_chan != chanreq->oper.npca_chan) + changed |= IEEE80211_CHANCTX_CHANGE_NPCA; + if (chanreq->oper.npca_chan && + ctx->conf.def.npca_punctured != chanreq->oper.npca_punctured) + changed |= IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT; } if (!cfg80211_chandef_identical(&ctx->conf.ap, &chanreq->ap)) changed |= IEEE80211_CHANCTX_CHANGE_AP; - ctx->conf.def = *chandef; + ctx->conf.def = chanreq->oper; ctx->conf.ap = chanreq->ap; /* check if min chanctx also changed */ -- 2.53.0