Skip to content

Commit 0fabfaa

Browse files
ariknemjmberg-intel
authored andcommitted
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 <[email protected]> Signed-off-by: Emmanuel Grumbach <[email protected]> Signed-off-by: Johannes Berg <[email protected]>
1 parent b98fb44 commit 0fabfaa

File tree

4 files changed

+132
-11
lines changed

4 files changed

+132
-11
lines changed

net/mac80211/chan.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
190190
return NULL;
191191
}
192192

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)
194194
{
195195
switch (sta->bandwidth) {
196196
case IEEE80211_STA_RX_BW_20:
@@ -264,9 +264,17 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
264264
case NL80211_IFTYPE_AP_VLAN:
265265
width = ieee80211_get_max_required_bw(sdata);
266266
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;
267276
case NL80211_IFTYPE_P2P_DEVICE:
268277
continue;
269-
case NL80211_IFTYPE_STATION:
270278
case NL80211_IFTYPE_ADHOC:
271279
case NL80211_IFTYPE_WDS:
272280
case NL80211_IFTYPE_MESH_POINT:
@@ -554,12 +562,13 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
554562
kfree_rcu(ctx, rcu_head);
555563
}
556564

557-
static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
558-
struct ieee80211_chanctx *ctx)
565+
void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
566+
struct ieee80211_chanctx *ctx)
559567
{
560568
struct ieee80211_chanctx_conf *conf = &ctx->conf;
561569
struct ieee80211_sub_if_data *sdata;
562570
const struct cfg80211_chan_def *compat = NULL;
571+
struct sta_info *sta;
563572

564573
lockdep_assert_held(&local->chanctx_mtx);
565574

@@ -581,6 +590,20 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
581590
if (WARN_ON_ONCE(!compat))
582591
break;
583592
}
593+
594+
/* TDLS peers can sometimes affect the chandef width */
595+
list_for_each_entry_rcu(sta, &local->sta_list, list) {
596+
if (!sta->uploaded ||
597+
!test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) ||
598+
!test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
599+
!sta->tdls_chandef.chan)
600+
continue;
601+
602+
compat = cfg80211_chandef_compatible(&sta->tdls_chandef,
603+
compat);
604+
if (WARN_ON_ONCE(!compat))
605+
break;
606+
}
584607
rcu_read_unlock();
585608

586609
if (!compat)

net/mac80211/ieee80211_i.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
20332033
enum ieee80211_chanctx_mode chanmode,
20342034
u8 radar_detect);
20352035
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);
20362039

20372040
/* TDLS */
20382041
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,

net/mac80211/sta_info.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ struct mesh_sta {
403403
* @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID
404404
* entry for non-QoS frames
405405
* @fast_tx: TX fastpath information
406+
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
407+
* the BSS one.
406408
*/
407409
struct sta_info {
408410
/* General information, mostly static */
@@ -511,6 +513,8 @@ struct sta_info {
511513

512514
u8 reserved_tid;
513515

516+
struct cfg80211_chan_def tdls_chandef;
517+
514518
/* keep last! */
515519
struct ieee80211_sta sta;
516520
};

net/mac80211/tdls.c

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,60 @@ static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
291291
}
292292
}
293293

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+
294348
static void
295349
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
296350
struct sk_buff *skb, const u8 *peer,
@@ -358,15 +412,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
358412
offset = noffset;
359413
}
360414

361-
rcu_read_lock();
415+
mutex_lock(&local->sta_mtx);
362416

363417
/* we should have the peer STA if we're already responding */
364418
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
365419
sta = sta_info_get(sdata, peer);
366420
if (WARN_ON_ONCE(!sta)) {
367-
rcu_read_unlock();
421+
mutex_unlock(&local->sta_mtx);
368422
return;
369423
}
424+
425+
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
370426
}
371427

372428
ieee80211_tdls_add_oper_classes(sdata, skb);
@@ -456,9 +512,16 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
456512

457513
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
458514
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
515+
516+
/*
517+
* if both peers support WIDER_BW, we can expand the chandef to
518+
* a wider compatible one, up to 80MHz
519+
*/
520+
if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
521+
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
459522
}
460523

461-
rcu_read_unlock();
524+
mutex_unlock(&local->sta_mtx);
462525

463526
/* add any remaining IEs */
464527
if (extra_ies_len) {
@@ -482,15 +545,17 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
482545
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
483546
u8 *pos;
484547

485-
rcu_read_lock();
548+
mutex_lock(&local->sta_mtx);
486549

487550
sta = sta_info_get(sdata, peer);
488551
ap_sta = sta_info_get(sdata, ifmgd->bssid);
489552
if (WARN_ON_ONCE(!sta || !ap_sta)) {
490-
rcu_read_unlock();
553+
mutex_unlock(&local->sta_mtx);
491554
return;
492555
}
493556

557+
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
558+
494559
/* add any custom IEs that go before the QoS IE */
495560
if (extra_ies_len) {
496561
static const u8 before_qos[] = {
@@ -538,12 +603,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
538603

539604
/* only include VHT-operation if not on the 2.4GHz band */
540605
if (band != IEEE80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
606+
/*
607+
* if both peers support WIDER_BW, we can expand the chandef to
608+
* a wider compatible one, up to 80MHz
609+
*/
610+
if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
611+
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
612+
541613
pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
542614
ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
543-
&sdata->vif.bss_conf.chandef);
615+
&sta->tdls_chandef);
544616
}
545617

546-
rcu_read_unlock();
618+
mutex_unlock(&local->sta_mtx);
547619

548620
/* add any remaining IEs */
549621
if (extra_ies_len) {
@@ -1154,6 +1226,22 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
11541226
return ret;
11551227
}
11561228

1229+
static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata)
1230+
{
1231+
struct ieee80211_local *local = sdata->local;
1232+
struct ieee80211_chanctx_conf *conf;
1233+
struct ieee80211_chanctx *ctx;
1234+
1235+
mutex_lock(&local->chanctx_mtx);
1236+
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1237+
lockdep_is_held(&local->chanctx_mtx));
1238+
if (conf) {
1239+
ctx = container_of(conf, struct ieee80211_chanctx, conf);
1240+
ieee80211_recalc_chanctx_chantype(local, ctx);
1241+
}
1242+
mutex_unlock(&local->chanctx_mtx);
1243+
}
1244+
11571245
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
11581246
const u8 *peer, enum nl80211_tdls_operation oper)
11591247
{
@@ -1190,6 +1278,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
11901278
break;
11911279
}
11921280

1281+
iee80211_tdls_recalc_chanctx(sdata);
1282+
11931283
rcu_read_lock();
11941284
sta = sta_info_get(sdata, peer);
11951285
if (!sta) {
@@ -1221,6 +1311,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
12211311
ieee80211_flush_queues(local, sdata, false);
12221312

12231313
ret = sta_info_destroy_addr(sdata, peer);
1314+
iee80211_tdls_recalc_chanctx(sdata);
12241315
break;
12251316
default:
12261317
ret = -ENOTSUPP;

0 commit comments

Comments
 (0)