Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac80211: upgrade BW of TDLS peers when possible

Define a station chandef, to be used for wider-bw TDLS peers. When both
peers support the feature, upgrade the channel bandwidth to the maximum
allowed by both peers and regulatory. Currently widths up to 80MHz are
supported in the 5GHz band.

When a TDLS peer connects/disconnects recalculate the channel type of the
current chanctx.
Make the chanctx width calculation consider wider-bw TDLS peers and
similarly fix the max_required_bw calculation for the chanctx min_def.
Since the sta->bandwidth is calculated only later on, take
bss_conf.chandef.width as the minimal width for station interface.

Set the upgraded channel width in the VHT-operation set during TDLS setup.

Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Arik Nemtsov and committed by
Johannes Berg
0fabfaaf b98fb44f

+132 -11
+27 -4
net/mac80211/chan.c
··· 190 190 return NULL; 191 191 } 192 192 193 - static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) 193 + enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) 194 194 { 195 195 switch (sta->bandwidth) { 196 196 case IEEE80211_STA_RX_BW_20: ··· 264 264 case NL80211_IFTYPE_AP_VLAN: 265 265 width = ieee80211_get_max_required_bw(sdata); 266 266 break; 267 + case NL80211_IFTYPE_STATION: 268 + /* 269 + * The ap's sta->bandwidth is not set yet at this 270 + * point, so take the width from the chandef, but 271 + * account also for TDLS peers 272 + */ 273 + width = max(vif->bss_conf.chandef.width, 274 + ieee80211_get_max_required_bw(sdata)); 275 + break; 267 276 case NL80211_IFTYPE_P2P_DEVICE: 268 277 continue; 269 - case NL80211_IFTYPE_STATION: 270 278 case NL80211_IFTYPE_ADHOC: 271 279 case NL80211_IFTYPE_WDS: 272 280 case NL80211_IFTYPE_MESH_POINT: ··· 562 554 kfree_rcu(ctx, rcu_head); 563 555 } 564 556 565 - static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 566 - struct ieee80211_chanctx *ctx) 557 + void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 558 + struct ieee80211_chanctx *ctx) 567 559 { 568 560 struct ieee80211_chanctx_conf *conf = &ctx->conf; 569 561 struct ieee80211_sub_if_data *sdata; 570 562 const struct cfg80211_chan_def *compat = NULL; 563 + struct sta_info *sta; 571 564 572 565 lockdep_assert_held(&local->chanctx_mtx); 573 566 ··· 587 578 588 579 compat = cfg80211_chandef_compatible( 589 580 &sdata->vif.bss_conf.chandef, compat); 581 + if (WARN_ON_ONCE(!compat)) 582 + break; 583 + } 584 + 585 + /* TDLS peers can sometimes affect the chandef width */ 586 + list_for_each_entry_rcu(sta, &local->sta_list, list) { 587 + if (!sta->uploaded || 588 + !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) || 589 + !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || 590 + !sta->tdls_chandef.chan) 591 + continue; 592 + 593 + compat = cfg80211_chandef_compatible(&sta->tdls_chandef, 594 + compat); 590 595 if (WARN_ON_ONCE(!compat)) 591 596 break; 592 597 }
+3
net/mac80211/ieee80211_i.h
··· 2033 2033 enum ieee80211_chanctx_mode chanmode, 2034 2034 u8 radar_detect); 2035 2035 int ieee80211_max_num_channels(struct ieee80211_local *local); 2036 + enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta); 2037 + void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 2038 + struct ieee80211_chanctx *ctx); 2036 2039 2037 2040 /* TDLS */ 2038 2041 int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+4
net/mac80211/sta_info.h
··· 403 403 * @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID 404 404 * entry for non-QoS frames 405 405 * @fast_tx: TX fastpath information 406 + * @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to 407 + * the BSS one. 406 408 */ 407 409 struct sta_info { 408 410 /* General information, mostly static */ ··· 512 510 unsigned long last_tdls_pkt_time; 513 511 514 512 u8 reserved_tid; 513 + 514 + struct cfg80211_chan_def tdls_chandef; 515 515 516 516 /* keep last! */ 517 517 struct ieee80211_sta sta;
+98 -7
net/mac80211/tdls.c
··· 292 292 } 293 293 294 294 static void 295 + ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata, 296 + struct sta_info *sta) 297 + { 298 + /* IEEE802.11ac-2013 Table E-4 */ 299 + u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 }; 300 + struct cfg80211_chan_def uc = sta->tdls_chandef; 301 + enum nl80211_chan_width max_width = ieee80211_get_sta_bw(&sta->sta); 302 + int i; 303 + 304 + /* only support upgrading non-narrow channels up to 80Mhz */ 305 + if (max_width == NL80211_CHAN_WIDTH_5 || 306 + max_width == NL80211_CHAN_WIDTH_10) 307 + return; 308 + 309 + if (max_width > NL80211_CHAN_WIDTH_80) 310 + max_width = NL80211_CHAN_WIDTH_80; 311 + 312 + if (uc.width == max_width) 313 + return; 314 + /* 315 + * Channel usage constrains in the IEEE802.11ac-2013 specification only 316 + * allow expanding a 20MHz channel to 80MHz in a single way. In 317 + * addition, there are no 40MHz allowed channels that are not part of 318 + * the allowed 80MHz range in the 5GHz spectrum (the relevant one here). 319 + */ 320 + for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++) 321 + if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) { 322 + uc.center_freq1 = centers_80mhz[i]; 323 + uc.width = NL80211_CHAN_WIDTH_80; 324 + break; 325 + } 326 + 327 + if (!uc.center_freq1) 328 + return; 329 + 330 + /* proceed to downgrade the chandef until usable or the same */ 331 + while (uc.width > max_width && 332 + !cfg80211_reg_can_beacon(sdata->local->hw.wiphy, 333 + &uc, sdata->wdev.iftype)) 334 + ieee80211_chandef_downgrade(&uc); 335 + 336 + if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) { 337 + tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n", 338 + sta->tdls_chandef.width, uc.width); 339 + 340 + /* 341 + * the station is not yet authorized when BW upgrade is done, 342 + * locking is not required 343 + */ 344 + sta->tdls_chandef = uc; 345 + } 346 + } 347 + 348 + static void 295 349 ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, 296 350 struct sk_buff *skb, const u8 *peer, 297 351 u8 action_code, bool initiator, ··· 412 358 offset = noffset; 413 359 } 414 360 415 - rcu_read_lock(); 361 + mutex_lock(&local->sta_mtx); 416 362 417 363 /* we should have the peer STA if we're already responding */ 418 364 if (action_code == WLAN_TDLS_SETUP_RESPONSE) { 419 365 sta = sta_info_get(sdata, peer); 420 366 if (WARN_ON_ONCE(!sta)) { 421 - rcu_read_unlock(); 367 + mutex_unlock(&local->sta_mtx); 422 368 return; 423 369 } 370 + 371 + sta->tdls_chandef = sdata->vif.bss_conf.chandef; 424 372 } 425 373 426 374 ieee80211_tdls_add_oper_classes(sdata, skb); ··· 512 456 513 457 pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); 514 458 ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); 459 + 460 + /* 461 + * if both peers support WIDER_BW, we can expand the chandef to 462 + * a wider compatible one, up to 80MHz 463 + */ 464 + if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW)) 465 + ieee80211_tdls_chandef_vht_upgrade(sdata, sta); 515 466 } 516 467 517 - rcu_read_unlock(); 468 + mutex_unlock(&local->sta_mtx); 518 469 519 470 /* add any remaining IEs */ 520 471 if (extra_ies_len) { ··· 545 482 enum ieee80211_band band = ieee80211_get_sdata_band(sdata); 546 483 u8 *pos; 547 484 548 - rcu_read_lock(); 485 + mutex_lock(&local->sta_mtx); 549 486 550 487 sta = sta_info_get(sdata, peer); 551 488 ap_sta = sta_info_get(sdata, ifmgd->bssid); 552 489 if (WARN_ON_ONCE(!sta || !ap_sta)) { 553 - rcu_read_unlock(); 490 + mutex_unlock(&local->sta_mtx); 554 491 return; 555 492 } 493 + 494 + sta->tdls_chandef = sdata->vif.bss_conf.chandef; 556 495 557 496 /* add any custom IEs that go before the QoS IE */ 558 497 if (extra_ies_len) { ··· 603 538 604 539 /* only include VHT-operation if not on the 2.4GHz band */ 605 540 if (band != IEEE80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) { 541 + /* 542 + * if both peers support WIDER_BW, we can expand the chandef to 543 + * a wider compatible one, up to 80MHz 544 + */ 545 + if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW)) 546 + ieee80211_tdls_chandef_vht_upgrade(sdata, sta); 547 + 606 548 pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation)); 607 549 ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap, 608 - &sdata->vif.bss_conf.chandef); 550 + &sta->tdls_chandef); 609 551 } 610 552 611 - rcu_read_unlock(); 553 + mutex_unlock(&local->sta_mtx); 612 554 613 555 /* add any remaining IEs */ 614 556 if (extra_ies_len) { ··· 1226 1154 return ret; 1227 1155 } 1228 1156 1157 + static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata) 1158 + { 1159 + struct ieee80211_local *local = sdata->local; 1160 + struct ieee80211_chanctx_conf *conf; 1161 + struct ieee80211_chanctx *ctx; 1162 + 1163 + mutex_lock(&local->chanctx_mtx); 1164 + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 1165 + lockdep_is_held(&local->chanctx_mtx)); 1166 + if (conf) { 1167 + ctx = container_of(conf, struct ieee80211_chanctx, conf); 1168 + ieee80211_recalc_chanctx_chantype(local, ctx); 1169 + } 1170 + mutex_unlock(&local->chanctx_mtx); 1171 + } 1172 + 1229 1173 int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, 1230 1174 const u8 *peer, enum nl80211_tdls_operation oper) 1231 1175 { ··· 1278 1190 break; 1279 1191 } 1280 1192 1193 + iee80211_tdls_recalc_chanctx(sdata); 1194 + 1281 1195 rcu_read_lock(); 1282 1196 sta = sta_info_get(sdata, peer); 1283 1197 if (!sta) { ··· 1311 1221 ieee80211_flush_queues(local, sdata, false); 1312 1222 1313 1223 ret = sta_info_destroy_addr(sdata, peer); 1224 + iee80211_tdls_recalc_chanctx(sdata); 1314 1225 break; 1315 1226 default: 1316 1227 ret = -ENOTSUPP;