/* no effect if this STA has no presence on this link */ if (!link_sta) return NL80211_CHAN_WIDTH_20_NOHT;
/* * We assume that TX/RX might be asymmetric (so e.g. VHT operating * mode notification changes what a STA wants to receive, but not * necessarily what it will transmit to us), and therefore use the * capabilities here. Calling it RX bandwidth capability is a bit * wrong though, since capabilities are in fact symmetric.
*/
width = ieee80211_sta_cap_rx_bw(link_sta);
switch (width) { case IEEE80211_STA_RX_BW_20: if (link_sta->pub->ht_cap.ht_supported) return NL80211_CHAN_WIDTH_20; else return NL80211_CHAN_WIDTH_20_NOHT; case IEEE80211_STA_RX_BW_40: return NL80211_CHAN_WIDTH_40; case IEEE80211_STA_RX_BW_80: return NL80211_CHAN_WIDTH_80; case IEEE80211_STA_RX_BW_160: /* * This applied for both 160 and 80+80. since we use * the returned value to consider degradation of * ctx->conf.min_def, we have to make sure to take * the bigger one (NL80211_CHAN_WIDTH_160). * Otherwise we might try degrading even when not * needed, as the max required sta_bw returned (80+80) * might be smaller than the configured bw (160).
*/ return NL80211_CHAN_WIDTH_160; case IEEE80211_STA_RX_BW_320: return NL80211_CHAN_WIDTH_320; default:
WARN_ON(1); return NL80211_CHAN_WIDTH_20;
}
}
if (check_reserved) { if (link->reserved_chanctx != ctx) continue;
} elseif (link != rsvd_for &&
rcu_access_pointer(link->conf->chanctx_conf) != &ctx->conf) continue;
switch (link->sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!link->sdata->vif.cfg.assoc) { /* * The AP's sta->bandwidth may not yet be set * at this point (pre-association), so simply * take the width from the chandef. We cannot * have TDLS peers yet (only after association).
*/
width = link->conf->chanreq.oper.width; break;
} /* * otherwise just use min_def like in AP, depending on what * we currently think the AP STA (and possibly TDLS peers) * require(s)
*/
fallthrough; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN:
width = ieee80211_get_max_required_bw(link); break; case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: continue; case NL80211_IFTYPE_MONITOR:
WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
NO_VIRTUAL_MONITOR));
fallthrough; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB:
width = link->conf->chanreq.oper.width; break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO:
WARN_ON_ONCE(1);
}
max_bw = max(max_bw, width);
}
/* use the configured bandwidth in case of monitor interface */
sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); if (sdata &&
rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &ctx->conf)
max_bw = max(max_bw, ctx->conf.def.width);
return max_bw;
}
/* * recalc the min required chan width of the channel context, which is * the max of min required widths of all the interfaces bound to this * channel context.
*/ static u32
_ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, struct ieee80211_link_data *rsvd_for, bool check_reserved)
{ enum nl80211_chan_width max_bw; struct cfg80211_chan_def min_def;
/* nothing change */ if (new_sta_bw == link_sta->pub->bandwidth) continue;
/* vif changed to narrow BW and narrow BW for station wasn't
* requested or vice versa */ if ((new_sta_bw < link_sta->pub->bandwidth) == !narrowed) continue;
/* * recalc the min required chan width of the channel context, which is * the max of min required widths of all the interfaces bound to this * channel context.
*/ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, struct ieee80211_link_data *rsvd_for, bool check_reserved)
{
u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for,
check_reserved);
if (!changed) return;
/* check is BW narrowed */
ieee80211_chan_bw_change(local, ctx, false, true);
/* expected to handle only 20/40/80/160/320 channel widths */ switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_40: case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_160: case NL80211_CHAN_WIDTH_320: break; default:
WARN_ON(1);
}
/* Check maybe BW narrowed - we do this _before_ calling recalc_chanctx_min_def * due to maybe not returning from it, e.g in case new context was added * first time with all parameters up to date.
*/
ieee80211_chan_bw_change(local, old_ctx, false, true);
if (ieee80211_chanreq_identical(&ctx_req, chanreq)) {
ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for, false); return;
}
/* Note: if successful, the returned chanctx is reserved for the link */ staticstruct ieee80211_chanctx *
ieee80211_find_chanctx(struct ieee80211_local *local, struct ieee80211_link_data *link, conststruct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode)
{ struct ieee80211_chan_req tmp; struct ieee80211_chanctx *ctx;
lockdep_assert_wiphy(local->hw.wiphy);
if (mode == IEEE80211_CHANCTX_EXCLUSIVE) return NULL;
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) continue;
if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) continue;
compat = ieee80211_chanctx_compatible(ctx, chanreq, &tmp); if (!compat) continue;
compat = ieee80211_chanctx_reserved_chanreq(local, ctx,
compat, &tmp); if (!compat) continue;
/* * Reserve the chanctx temporarily, as the driver might change * active links during callbacks we make into it below and/or * later during assignment, which could (otherwise) cause the * context to actually be removed.
*/
link->reserved_chanctx = ctx;
list_add(&link->reserved_chanctx_list,
&ctx->reserved_links);
for_each_sdata_link(local, link) { if (link->radar_required) { if (wiphy->n_radio < 2) returntrue;
chan = link->conf->chanreq.oper.chan;
radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chan); /* * The radio index (radio_idx) is expected to be valid, * as it's derived from a channel tied to a link. If * it's invalid (i.e., negative), return true to avoid * potential issues with radar-sensitive operations.
*/ if (radio_idx < 0) returntrue;
if (ieee80211_is_radio_idx_in_scan_req(wiphy, req,
radio_idx)) returntrue;
}
}
/* turn idle off *before* setting channel -- some drivers need that */
changed = ieee80211_idle_off(local); if (changed)
ieee80211_hw_config(local, -1, changed);
tdls_link_id = ieee80211_tdls_sta_link_id(sta);
link = sdata_dereference(sdata->link[tdls_link_id], sdata); if (!link) continue;
if (rcu_access_pointer(link->conf->chanctx_conf) != conf) continue;
tdls_chanreq.oper = sta->tdls_chandef;
/* note this always fills and returns &tmp if compat */
compat = ieee80211_chanreq_compatible(&tdls_chanreq,
compat, &tmp); if (WARN_ON_ONCE(!compat)) return;
}
if (new_ctx) { /* recalc considering the link we'll use it for now */
ieee80211_recalc_chanctx_min_def(local, new_ctx, link, false);
ret = drv_assign_vif_chanctx(local, sdata, link->conf, new_ctx); if (assign_on_failure || !ret) { /* Need to continue, see _ieee80211_set_active_links */
WARN_ON_ONCE(ret && !local->in_reconfig);
ret = 0;
/* succeeded, so commit it to the data structures */
conf = &new_ctx->conf; if (!local->in_reconfig)
list_add(&link->assigned_chanctx_list,
&new_ctx->assigned_links);
}
} else {
ret = 0;
}
switch (link->sdata->vif.type) { case NL80211_IFTYPE_STATION: if (!link->sdata->u.mgd.associated) continue; break; case NL80211_IFTYPE_MONITOR: if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) continue; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB: break; default: continue;
}
if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf) continue;
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) return;
lockdep_assert_wiphy(local->hw.wiphy);
/* Check that conf exists, even when clearing this function * must be called with the AP's channel context still there * as it would otherwise cause VLANs to have an invalid * channel context pointer for a while, possibly pointing * to a channel context that has already been freed.
*/
conf = rcu_dereference_protected(link_conf->chanctx_conf,
lockdep_is_held(&local->hw.wiphy->mtx));
WARN_ON(!conf);
if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { if (WARN_ON(!ctx->replace_ctx)) return;
if (!curr_ctx || (curr_ctx->replace_state ==
IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
!list_empty(&curr_ctx->reserved_links)) { /* * Another link already requested this context for a * reservation. Find another one hoping all links assigned * to it will also switch soon enough. * * TODO: This needs a little more work as some cases * (more than 2 chanctx capable devices) may fail which could * otherwise succeed provided some channel context juggling was * performed. * * Consider ctx1..3, link1..6, each ctx has 2 links. link1 and * link2 from ctx1 request new different chandefs starting 2 * in-place reservations with ctx4 and ctx5 replacing ctx1 and * ctx2 respectively. Next link5 and link6 from ctx3 reserve * ctx4. If link3 and link4 remain on ctx2 as they are then this * fails unless `replace_ctx` from ctx5 is replaced with ctx3.
*/
list_for_each_entry(ctx, &local->chanctx_list, list) { if (ctx->replace_state !=
IEEE80211_CHANCTX_REPLACE_NONE) continue;
if (!list_empty(&ctx->reserved_links)) continue;
if (ctx->conf.radio_idx >= 0) {
radio = &wiphy->radio[ctx->conf.radio_idx]; if (!cfg80211_radio_chandef_valid(radio, &chanreq->oper)) continue;
}
curr_ctx = ctx; break;
}
}
/* * If that's true then all available contexts already have reservations * and cannot be used.
*/ if (!curr_ctx || (curr_ctx->replace_state ==
IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
!list_empty(&curr_ctx->reserved_links)) return ERR_PTR(-EBUSY);
new_ctx = ieee80211_alloc_chanctx(local, chanreq, mode, -1); if (!new_ctx) return ERR_PTR(-ENOMEM);
switch (sdata->vif.type) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB:
wiphy_work_queue(sdata->local->hw.wiphy,
&link->csa.finalize_work); break; case NL80211_IFTYPE_STATION:
wiphy_hrtimer_work_queue(sdata->local->hw.wiphy,
&link->u.mgd.csa.switch_work, 0); break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: case NUM_NL80211_IFTYPES:
WARN_ON(1); break;
}
}
/* * If there are 2 independent pairs of channel contexts performing * cross-switch of their vifs this code will still wait until both are * ready even though it could be possible to switch one before the * other is ready. * * For practical reasons and code simplicity just do a single huge * switch.
*/
/* * Verify if the reservation is still feasible. * - if it's not then disconnect * - if it is but not all vifs necessary are ready then defer
*/
/* * All necessary vifs are ready. Perform the switch now depending on * reservations and driver capabilities.
*/
if (n_vifs_switch > 0) {
err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); if (err) goto err;
}
if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
err = ieee80211_chsw_switch_ctxs(local); if (err) goto err;
}
/* * Update all structures, values and pointers to point to new channel * context(s).
*/
list_for_each_entry(ctx, &local->chanctx_list, list) { struct ieee80211_link_data *link, *link_tmp;
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) continue;
if (WARN_ON(!ctx->replace_ctx)) {
err = -EINVAL; goto err;
}
/* * This context might have been a dependency for an already * ready re-assign reservation interface that was deferred. Do * not propagate error to the caller though. The in-place * reservation for originally requested interface has already * succeeded at this point.
*/
list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
reserved_chanctx_list) { if (WARN_ON(ieee80211_link_has_in_place_reservation(link))) continue;
if (WARN_ON(link->reserved_chanctx != ctx)) continue;
if (!link->reserved_ready) continue;
if (ieee80211_link_get_chanctx(link))
err = ieee80211_link_use_reserved_reassign(link); else
err = ieee80211_link_use_reserved_assign(link);
if (err) {
link_info(link, "failed to finalize (re-)assign reservation (err=%d)\n",
err);
ieee80211_link_unreserve_chanctx(link);
cfg80211_stop_iface(local->hw.wiphy,
&link->sdata->wdev,
GFP_KERNEL);
}
}
}
/* * Finally free old contexts
*/
list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) continue;
if (ret) { /* if assign fails refcount stays the same */ if (ieee80211_chanctx_refcount(local, ctx) == 0)
ieee80211_free_chanctx(local, ctx, false); goto out;
}
ieee80211_recalc_smps_chanctx(local, ctx);
ieee80211_recalc_radar_chanctx(local, ctx);
out: if (ret)
link->radar_required = false;
/* * In-place reservation may need to be finalized now either if: * a) sdata is taking part in the swapping itself and is the last one * b) sdata has switched with a re-assign reservation to an existing * context readying in-place switching of old_ctx * * In case of (b) do not propagate the error up because the requested * sdata already switched successfully. Just spill an extra warning. * The ieee80211_vif_use_reserved_switch() already stops all necessary * interfaces upon failure.
*/ if ((old_ctx &&
old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
err = ieee80211_vif_use_reserved_switch(local); if (err && err != -EAGAIN) { if (new_ctx->replace_state ==
IEEE80211_CHANCTX_REPLACES_OTHER) return err;
/* * This is similar to ieee80211_chanctx_compatible(), but rechecks * against all the links actually using it (except the one that's * passed, since that one is changing). * This is done in order to allow changes to the AP's bandwidth for * wider bandwidth OFDMA purposes, which wouldn't be treated as * compatible by ieee80211_chanctx_recheck() but is OK if the link * requesting the update is the only one using it.
*/ staticconststruct ieee80211_chan_req *
ieee80211_chanctx_recheck(struct ieee80211_local *local, struct ieee80211_link_data *skip_link, struct ieee80211_chanctx *ctx, conststruct ieee80211_chan_req *req, struct ieee80211_chan_req *tmp)
{ conststruct ieee80211_chan_req *ret = req; struct ieee80211_link_data *link;
lockdep_assert_wiphy(local->hw.wiphy);
for_each_sdata_link(local, link) { if (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); if (!ret) return NULL;
}
}
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy,
&chanreq->oper,
IEEE80211_CHAN_DISABLED)) return -EINVAL;
/* for non-HT 20 MHz the rest doesn't matter */ if (chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT &&
cfg80211_chandef_identical(&chanreq->oper, &link_conf->chanreq.oper)) return 0;
/* but you cannot switch to/from it */ if (chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT ||
link_conf->chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return -EINVAL;
conf = rcu_dereference_protected(link_conf->chanctx_conf,
lockdep_is_held(&local->hw.wiphy->mtx)); if (!conf) return -EINVAL;
switch (ctx->replace_state) { case IEEE80211_CHANCTX_REPLACE_NONE: if (!ieee80211_chanctx_reserved_chanreq(local, ctx, compat,
&tmp)) return -EBUSY; break; case IEEE80211_CHANCTX_WILL_BE_REPLACED: /* TODO: Perhaps the bandwidth change could be treated as a
* reservation itself? */ return -EBUSY; case IEEE80211_CHANCTX_REPLACES_OTHER: /* channel context that is going to replace another channel * context doesn't really exist and shouldn't be assigned
* anywhere yet */
WARN_ON(1); break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.