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

mac80211: TDLS: correctly configure SMPS state

The IEEE802.11-2012 specification is vague regarding SMPS operation during
TDLS. It does not define a clear way to transition between SMPS states.

To avoid interop issues, set SMPS to off when TDLS peers are connected.
Accomplish this by extending the definition of the AUTOMATIC state. If the
driver forces a state other than OFF, disconnect all TDLS peers.

While at it, avoid changing the SMPS state of the peer STA. We have no
way to control it, so try and behave correctly towards it.

Move the TDLS peer-teardown function to where the rest of the TDLS code
resides.

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

authored by

Arik Nemtsov and committed by
Johannes Berg
d51c2ea3 3633ebeb

+51 -25
+18 -3
net/mac80211/cfg.c
··· 2368 2368 const u8 *ap; 2369 2369 enum ieee80211_smps_mode old_req; 2370 2370 int err; 2371 + struct sta_info *sta; 2372 + bool tdls_peer_found = false; 2371 2373 2372 2374 lockdep_assert_held(&sdata->wdev.mtx); 2373 2375 ··· 2394 2392 2395 2393 ap = sdata->u.mgd.associated->bssid; 2396 2394 2395 + rcu_read_lock(); 2396 + list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 2397 + if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded || 2398 + !test_sta_flag(sta, WLAN_STA_AUTHORIZED)) 2399 + continue; 2400 + 2401 + tdls_peer_found = true; 2402 + break; 2403 + } 2404 + rcu_read_unlock(); 2405 + 2397 2406 if (smps_mode == IEEE80211_SMPS_AUTOMATIC) { 2398 - if (sdata->u.mgd.powersave) 2399 - smps_mode = IEEE80211_SMPS_DYNAMIC; 2400 - else 2407 + if (tdls_peer_found || !sdata->u.mgd.powersave) 2401 2408 smps_mode = IEEE80211_SMPS_OFF; 2409 + else 2410 + smps_mode = IEEE80211_SMPS_DYNAMIC; 2402 2411 } 2403 2412 2404 2413 /* send SM PS frame to AP */ ··· 2417 2404 ap, ap); 2418 2405 if (err) 2419 2406 sdata->u.mgd.req_smps = old_req; 2407 + else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found) 2408 + ieee80211_teardown_tdls_peers(sdata); 2420 2409 2421 2410 return err; 2422 2411 }
+1
net/mac80211/ieee80211_i.h
··· 2054 2054 const u8 *addr); 2055 2055 void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, 2056 2056 struct sk_buff *skb); 2057 + void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata); 2057 2058 2058 2059 extern const struct ethtool_ops ieee80211_ethtool_ops; 2059 2060
-18
net/mac80211/mlme.c
··· 1096 1096 ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work); 1097 1097 } 1098 1098 1099 - static void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata) 1100 - { 1101 - struct sta_info *sta; 1102 - u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; 1103 - 1104 - rcu_read_lock(); 1105 - list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 1106 - if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded || 1107 - !test_sta_flag(sta, WLAN_STA_AUTHORIZED)) 1108 - continue; 1109 - 1110 - ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr, 1111 - NL80211_TDLS_TEARDOWN, reason, 1112 - GFP_ATOMIC); 1113 - } 1114 - rcu_read_unlock(); 1115 - } 1116 - 1117 1099 static void 1118 1100 ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, 1119 1101 u64 timestamp, u32 device_timestamp,
+32 -4
net/mac80211/tdls.c
··· 4 4 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 5 5 * Copyright 2014, Intel Corporation 6 6 * Copyright 2014 Intel Mobile Communications GmbH 7 + * Copyright 2015 Intel Deutschland GmbH 7 8 * 8 9 * This file is GPLv2 as found in COPYING. 9 10 */ ··· 449 448 ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); 450 449 } else if (action_code == WLAN_TDLS_SETUP_RESPONSE && 451 450 ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) { 452 - /* disable SMPS in TDLS responder */ 453 - sta->sta.ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED 454 - << IEEE80211_HT_CAP_SM_PS_SHIFT; 455 - 456 451 /* the peer caps are already intersected with our own */ 457 452 memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap)); 458 453 ··· 1060 1063 { 1061 1064 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 1062 1065 struct ieee80211_local *local = sdata->local; 1066 + enum ieee80211_smps_mode smps_mode = sdata->u.mgd.driver_smps_mode; 1063 1067 int ret; 1068 + 1069 + /* don't support setup with forced SMPS mode that's not off */ 1070 + if (smps_mode != IEEE80211_SMPS_AUTOMATIC && 1071 + smps_mode != IEEE80211_SMPS_OFF) { 1072 + tdls_dbg(sdata, "Aborting TDLS setup due to SMPS mode %d\n", 1073 + smps_mode); 1074 + return -ENOTSUPP; 1075 + } 1064 1076 1065 1077 mutex_lock(&local->mtx); 1066 1078 ··· 1328 1322 cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work); 1329 1323 eth_zero_addr(sdata->u.mgd.tdls_peer); 1330 1324 } 1325 + 1326 + if (ret == 0) 1327 + ieee80211_queue_work(&sdata->local->hw, 1328 + &sdata->u.mgd.request_smps_work); 1331 1329 1332 1330 mutex_unlock(&local->mtx); 1333 1331 return ret; ··· 1828 1818 WARN_ON_ONCE(1); 1829 1819 return; 1830 1820 } 1821 + } 1822 + 1823 + void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata) 1824 + { 1825 + struct sta_info *sta; 1826 + u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; 1827 + 1828 + rcu_read_lock(); 1829 + list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 1830 + if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded || 1831 + !test_sta_flag(sta, WLAN_STA_AUTHORIZED)) 1832 + continue; 1833 + 1834 + ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr, 1835 + NL80211_TDLS_TEARDOWN, reason, 1836 + GFP_ATOMIC); 1837 + } 1838 + rcu_read_unlock(); 1831 1839 }