diff options
Diffstat (limited to 'net/mac80211')
58 files changed, 7185 insertions, 2830 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 62535fe9f57..97b5dcad502 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -4,6 +4,7 @@ config MAC80211  	select CRYPTO  	select CRYPTO_ARC4  	select CRYPTO_AES +	select CRYPTO_CCM  	select CRC32  	select AVERAGE  	---help--- @@ -258,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG  	  Do not select this option. +config MAC80211_MESH_CSA_DEBUG +	bool "Verbose mesh channel switch debugging" +	depends on MAC80211_DEBUG_MENU +	depends on MAC80211_MESH +	---help--- +	  Selecting this option causes mac80211 to print out very verbose mesh +	  channel switch debugging messages (when mac80211 is taking part in a +	  mesh network). + +	  Do not select this option. +  config MAC80211_MESH_PS_DEBUG  	bool "Verbose mesh powersave debugging"  	depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 9d7d840aac6..1e46ffa6916 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -25,7 +25,8 @@ mac80211-y := \  	wme.o \  	event.o \  	chan.o \ -	trace.o mlme.o +	trace.o mlme.o \ +	tdls.o  mac80211-$(CONFIG_MAC80211_LEDS) += led.o  mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index be7614b9ed2..ec24378caaa 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -2,6 +2,8 @@   * Copyright 2003-2004, Instant802 Networks, Inc.   * Copyright 2005-2006, Devicescape Software, Inc.   * + * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> + *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation. @@ -17,134 +19,76 @@  #include "key.h"  #include "aes_ccm.h" -static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a) +void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, +			       u8 *data, size_t data_len, u8 *mic)  { -	int i; -	u8 *b_0, *aad, *b, *s_0; - -	b_0 = scratch + 3 * AES_BLOCK_SIZE; -	aad = scratch + 4 * AES_BLOCK_SIZE; -	b = scratch; -	s_0 = scratch + AES_BLOCK_SIZE; - -	crypto_cipher_encrypt_one(tfm, b, b_0); +	struct scatterlist assoc, pt, ct[2]; -	/* Extra Authenticate-only data (always two AES blocks) */ -	for (i = 0; i < AES_BLOCK_SIZE; i++) -		aad[i] ^= b[i]; -	crypto_cipher_encrypt_one(tfm, b, aad); +	char aead_req_data[sizeof(struct aead_request) + +			   crypto_aead_reqsize(tfm)] +		__aligned(__alignof__(struct aead_request)); +	struct aead_request *aead_req = (void *) aead_req_data; -	aad += AES_BLOCK_SIZE; +	memset(aead_req, 0, sizeof(aead_req_data)); -	for (i = 0; i < AES_BLOCK_SIZE; i++) -		aad[i] ^= b[i]; -	crypto_cipher_encrypt_one(tfm, a, aad); +	sg_init_one(&pt, data, data_len); +	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); +	sg_init_table(ct, 2); +	sg_set_buf(&ct[0], data, data_len); +	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); -	/* Mask out bits from auth-only-b_0 */ -	b_0[0] &= 0x07; +	aead_request_set_tfm(aead_req, tfm); +	aead_request_set_assoc(aead_req, &assoc, assoc.length); +	aead_request_set_crypt(aead_req, &pt, ct, data_len, b_0); -	/* S_0 is used to encrypt T (= MIC) */ -	b_0[14] = 0; -	b_0[15] = 0; -	crypto_cipher_encrypt_one(tfm, s_0, b_0); +	crypto_aead_encrypt(aead_req);  } - -void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, -			       u8 *data, size_t data_len, -			       u8 *cdata, u8 *mic) +int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, +			      u8 *data, size_t data_len, u8 *mic)  { -	int i, j, last_len, num_blocks; -	u8 *pos, *cpos, *b, *s_0, *e, *b_0; - -	b = scratch; -	s_0 = scratch + AES_BLOCK_SIZE; -	e = scratch + 2 * AES_BLOCK_SIZE; -	b_0 = scratch + 3 * AES_BLOCK_SIZE; - -	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); -	last_len = data_len % AES_BLOCK_SIZE; -	aes_ccm_prepare(tfm, scratch, b); - -	/* Process payload blocks */ -	pos = data; -	cpos = cdata; -	for (j = 1; j <= num_blocks; j++) { -		int blen = (j == num_blocks && last_len) ? -			last_len : AES_BLOCK_SIZE; - -		/* Authentication followed by encryption */ -		for (i = 0; i < blen; i++) -			b[i] ^= pos[i]; -		crypto_cipher_encrypt_one(tfm, b, b); - -		b_0[14] = (j >> 8) & 0xff; -		b_0[15] = j & 0xff; -		crypto_cipher_encrypt_one(tfm, e, b_0); -		for (i = 0; i < blen; i++) -			*cpos++ = *pos++ ^ e[i]; -	} - -	for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) -		mic[i] = b[i] ^ s_0[i]; +	struct scatterlist assoc, pt, ct[2]; +	char aead_req_data[sizeof(struct aead_request) + +			   crypto_aead_reqsize(tfm)] +		__aligned(__alignof__(struct aead_request)); +	struct aead_request *aead_req = (void *) aead_req_data; + +	memset(aead_req, 0, sizeof(aead_req_data)); + +	sg_init_one(&pt, data, data_len); +	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); +	sg_init_table(ct, 2); +	sg_set_buf(&ct[0], data, data_len); +	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); + +	aead_request_set_tfm(aead_req, tfm); +	aead_request_set_assoc(aead_req, &assoc, assoc.length); +	aead_request_set_crypt(aead_req, ct, &pt, +			       data_len + IEEE80211_CCMP_MIC_LEN, b_0); + +	return crypto_aead_decrypt(aead_req);  } - -int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, -			      u8 *cdata, size_t data_len, u8 *mic, u8 *data) +struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])  { -	int i, j, last_len, num_blocks; -	u8 *pos, *cpos, *b, *s_0, *a, *b_0; - -	b = scratch; -	s_0 = scratch + AES_BLOCK_SIZE; -	a = scratch + 2 * AES_BLOCK_SIZE; -	b_0 = scratch + 3 * AES_BLOCK_SIZE; - -	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); -	last_len = data_len % AES_BLOCK_SIZE; -	aes_ccm_prepare(tfm, scratch, a); - -	/* Process payload blocks */ -	cpos = cdata; -	pos = data; -	for (j = 1; j <= num_blocks; j++) { -		int blen = (j == num_blocks && last_len) ? -			last_len : AES_BLOCK_SIZE; - -		/* Decryption followed by authentication */ -		b_0[14] = (j >> 8) & 0xff; -		b_0[15] = j & 0xff; -		crypto_cipher_encrypt_one(tfm, b, b_0); -		for (i = 0; i < blen; i++) { -			*pos = *cpos++ ^ b[i]; -			a[i] ^= *pos++; -		} -		crypto_cipher_encrypt_one(tfm, a, a); -	} - -	for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) { -		if ((mic[i] ^ s_0[i]) != a[i]) -			return -1; -	} - -	return 0; -} - +	struct crypto_aead *tfm; +	int err; -struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]) -{ -	struct crypto_cipher *tfm; +	tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(tfm)) +		return tfm; -	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); -	if (!IS_ERR(tfm)) -		crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP); +	err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP); +	if (!err) +		err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN); +	if (!err) +		return tfm; -	return tfm; +	crypto_free_aead(tfm); +	return ERR_PTR(err);  } - -void ieee80211_aes_key_free(struct crypto_cipher *tfm) +void ieee80211_aes_key_free(struct crypto_aead *tfm)  { -	crypto_free_cipher(tfm); +	crypto_free_aead(tfm);  } diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h index 5b7d744e237..2c7ab1948a2 100644 --- a/net/mac80211/aes_ccm.h +++ b/net/mac80211/aes_ccm.h @@ -12,13 +12,11 @@  #include <linux/crypto.h> -struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]); -void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, -			       u8 *data, size_t data_len, -			       u8 *cdata, u8 *mic); -int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, -			      u8 *cdata, size_t data_len, -			      u8 *mic, u8 *data); -void ieee80211_aes_key_free(struct crypto_cipher *tfm); +struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]); +void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, +			       u8 *data, size_t data_len, u8 *mic); +int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, +			      u8 *data, size_t data_len, u8 *mic); +void ieee80211_aes_key_free(struct crypto_aead *tfm);  #endif /* AES_CCM_H */ diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c index 537488cbf94..9b9009f9955 100644 --- a/net/mac80211/aes_cmac.c +++ b/net/mac80211/aes_cmac.c @@ -111,7 +111,7 @@ void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,  } -struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]) +struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[])  {  	struct crypto_cipher *tfm; diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h index 20785a64725..0ce6487af79 100644 --- a/net/mac80211/aes_cmac.h +++ b/net/mac80211/aes_cmac.h @@ -11,7 +11,7 @@  #include <linux/crypto.h> -struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]); +struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[]);  void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,  			const u8 *data, size_t data_len, u8 *mic);  void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 13b7683de5a..ce9633a3cfb 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -107,7 +107,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,  	mgmt->u.action.u.addba_req.start_seq_num =  					cpu_to_le16(start_seq_num << 4); -	ieee80211_tx_skb_tid(sdata, skb, tid); +	ieee80211_tx_skb(sdata, skb);  }  void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 2e7855a1b10..592f4b152ba 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -109,6 +109,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy,  static int ieee80211_start_p2p_device(struct wiphy *wiphy,  				      struct wireless_dev *wdev)  { +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	int ret; + +	mutex_lock(&sdata->local->chanctx_mtx); +	ret = ieee80211_check_combinations(sdata, NULL, 0, 0); +	mutex_unlock(&sdata->local->chanctx_mtx); +	if (ret < 0) +		return ret; +  	return ieee80211_do_open(wdev, true);  } @@ -133,7 +142,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  			     struct key_params *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta = NULL; +	const struct ieee80211_cipher_scheme *cs = NULL;  	struct ieee80211_key *key;  	int err; @@ -145,22 +156,28 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  	case WLAN_CIPHER_SUITE_WEP40:  	case WLAN_CIPHER_SUITE_TKIP:  	case WLAN_CIPHER_SUITE_WEP104: -		if (IS_ERR(sdata->local->wep_tx_tfm)) +		if (IS_ERR(local->wep_tx_tfm))  			return -EINVAL;  		break; +	case WLAN_CIPHER_SUITE_CCMP: +	case WLAN_CIPHER_SUITE_AES_CMAC: +	case WLAN_CIPHER_SUITE_GCMP: +		break;  	default: +		cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);  		break;  	}  	key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len, -				  params->key, params->seq_len, params->seq); +				  params->key, params->seq_len, params->seq, +				  cs);  	if (IS_ERR(key))  		return PTR_ERR(key);  	if (pairwise)  		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; -	mutex_lock(&sdata->local->sta_mtx); +	mutex_lock(&local->sta_mtx);  	if (mac_addr) {  		if (ieee80211_vif_is_mesh(&sdata->vif)) @@ -216,10 +233,13 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  		break;  	} +	if (sta) +		sta->cipher_scheme = cs; +  	err = ieee80211_key_link(key, sdata, sta);   out_unlock: -	mutex_unlock(&sdata->local->sta_mtx); +	mutex_unlock(&local->sta_mtx);  	return err;  } @@ -244,7 +264,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,  			goto out_unlock;  		if (pairwise) -			key = key_mtx_dereference(local, sta->ptk); +			key = key_mtx_dereference(local, sta->ptk[key_idx]);  		else  			key = key_mtx_dereference(local, sta->gtk[key_idx]);  	} else @@ -290,9 +310,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,  		if (!sta)  			goto out; -		if (pairwise) -			key = rcu_dereference(sta->ptk); -		else if (key_idx < NUM_DEFAULT_KEYS) +		if (pairwise && key_idx < NUM_DEFAULT_KEYS) +			key = rcu_dereference(sta->ptk[key_idx]); +		else if (!pairwise && +			 key_idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)  			key = rcu_dereference(sta->gtk[key_idx]);  	} else  		key = rcu_dereference(sdata->keys[key_idx]); @@ -439,11 +460,11 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)  		rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;  	if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)  		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; -	if (sta->last_rx_rate_flag & RX_FLAG_80MHZ) +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)  		rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; -	if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ) +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ)  		rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; -	if (sta->last_rx_rate_flag & RX_FLAG_160MHZ) +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)  		rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;  } @@ -451,10 +472,15 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = sta->sdata;  	struct ieee80211_local *local = sdata->local; +	struct rate_control_ref *ref = NULL;  	struct timespec uptime;  	u64 packets = 0; +	u32 thr = 0;  	int i, ac; +	if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) +		ref = local->rate_ctrl; +  	sinfo->generation = sdata->local->sta_generation;  	sinfo->filled = STATION_INFO_INACTIVE_TIME | @@ -521,8 +547,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  				 STATION_INFO_PEER_PM |  				 STATION_INFO_NONPEER_PM; -		sinfo->llid = le16_to_cpu(sta->llid); -		sinfo->plid = le16_to_cpu(sta->plid); +		sinfo->llid = sta->llid; +		sinfo->plid = sta->plid;  		sinfo->plink_state = sta->plink_state;  		if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {  			sinfo->filled |= STATION_INFO_T_OFFSET; @@ -566,6 +592,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);  	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))  		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); + +	/* check if the driver has a SW RC implementation */ +	if (ref && ref->ops->get_expected_throughput) +		thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); +	else +		thr = drv_get_expected_throughput(local, &sta->sta); + +	if (thr != 0) { +		sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; +		sinfo->expected_throughput = thr; +	}  }  static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { @@ -756,7 +793,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy,  }  static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, -				 int idx, u8 *mac, struct station_info *sinfo) +				  int idx, u8 *mac, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; @@ -786,7 +823,7 @@ static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac, struct station_info *sinfo) +				 const u8 *mac, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; @@ -816,6 +853,7 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,  	if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))  		return 0; +	mutex_lock(&local->mtx);  	mutex_lock(&local->iflist_mtx);  	if (local->use_chanctx) {  		sdata = rcu_dereference_protected( @@ -834,6 +872,7 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,  	if (ret == 0)  		local->monitor_chandef = *chandef;  	mutex_unlock(&local->iflist_mtx); +	mutex_unlock(&local->mtx);  	return ret;  } @@ -846,7 +885,7 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,  	if (!resp || !resp_len)  		return 1; -	old = rtnl_dereference(sdata->u.ap.probe_resp); +	old = sdata_dereference(sdata->u.ap.probe_resp, sdata);  	new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);  	if (!new) @@ -862,15 +901,16 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,  	return 0;  } -int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, -			    struct cfg80211_beacon_data *params) +static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, +				   struct cfg80211_beacon_data *params)  {  	struct beacon_data *new, *old;  	int new_head_len, new_tail_len;  	int size, err;  	u32 changed = BSS_CHANGED_BEACON; -	old = rtnl_dereference(sdata->u.ap.beacon); +	old = sdata_dereference(sdata->u.ap.beacon, sdata); +  	/* Need to have a beacon head if we don't have one yet */  	if (!params->head && !old) @@ -938,6 +978,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  			      struct cfg80211_ap_settings *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct beacon_data *old;  	struct ieee80211_sub_if_data *vlan;  	u32 changed = BSS_CHANGED_BEACON_INT | @@ -947,20 +988,22 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  		      BSS_CHANGED_P2P_PS;  	int err; -	old = rtnl_dereference(sdata->u.ap.beacon); +	old = sdata_dereference(sdata->u.ap.beacon, sdata);  	if (old)  		return -EALREADY;  	/* TODO: make hostapd tell us what it wants */  	sdata->smps_mode = IEEE80211_SMPS_OFF;  	sdata->needed_rx_chains = sdata->local->rx_chains; -	sdata->radar_required = params->radar_required; +	mutex_lock(&local->mtx);  	err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,  					IEEE80211_CHANCTX_SHARED); +	if (!err) +		ieee80211_vif_copy_chanctx_to_vlans(sdata, false); +	mutex_unlock(&local->mtx);  	if (err)  		return err; -	ieee80211_vif_copy_chanctx_to_vlans(sdata, false);  	/*  	 * Apply control port protocol, this allows us to @@ -968,11 +1011,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  	 */  	sdata->control_port_protocol = params->crypto.control_port_ethertype;  	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt; +	sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local, +							¶ms->crypto, +							sdata->vif.type); +  	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {  		vlan->control_port_protocol =  			params->crypto.control_port_ethertype;  		vlan->control_port_no_encrypt =  			params->crypto.control_port_no_encrypt; +		vlan->encrypt_headroom = +			ieee80211_cs_headroom(sdata->local, +					      ¶ms->crypto, +					      vlan->vif.type);  	}  	sdata->vif.bss_conf.beacon_int = params->beacon_interval; @@ -995,19 +1046,24 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  					IEEE80211_P2P_OPPPS_ENABLE_BIT;  	err = ieee80211_assign_beacon(sdata, ¶ms->beacon); -	if (err < 0) +	if (err < 0) { +		ieee80211_vif_release_channel(sdata);  		return err; +	}  	changed |= err;  	err = drv_start_ap(sdata->local, sdata);  	if (err) { -		old = rtnl_dereference(sdata->u.ap.beacon); +		old = sdata_dereference(sdata->u.ap.beacon, sdata); +  		if (old)  			kfree_rcu(old, rcu_head);  		RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); +		ieee80211_vif_release_channel(sdata);  		return err;  	} +	ieee80211_recalc_dtim(local, sdata);  	ieee80211_bss_info_change_notify(sdata, changed);  	netif_carrier_on(dev); @@ -1025,6 +1081,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,  	int err;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	sdata_assert_lock(sdata);  	/* don't allow changing the beacon while CSA is in place - offset  	 * of channel switch counter may change @@ -1032,7 +1089,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,  	if (sdata->vif.csa_active)  		return -EBUSY; -	old = rtnl_dereference(sdata->u.ap.beacon); +	old = sdata_dereference(sdata->u.ap.beacon, sdata);  	if (!old)  		return -ENOENT; @@ -1043,6 +1100,31 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,  	return 0;  } +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; + +	lockdep_assert_held(&local->mtx); + +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		if (!ieee80211_sdata_running(sdata)) +			continue; + +		if (!sdata->vif.csa_active) +			continue; + +		if (!sdata->csa_block_tx) +			continue; + +		rcu_read_unlock(); +		return true; +	} +	rcu_read_unlock(); + +	return false; +} +  static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1050,15 +1132,26 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	struct ieee80211_local *local = sdata->local;  	struct beacon_data *old_beacon;  	struct probe_resp *old_probe_resp; +	struct cfg80211_chan_def chandef; -	old_beacon = rtnl_dereference(sdata->u.ap.beacon); +	sdata_assert_lock(sdata); + +	old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);  	if (!old_beacon)  		return -ENOENT; -	old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); +	old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);  	/* abort any running channel switch */ +	mutex_lock(&local->mtx);  	sdata->vif.csa_active = false; -	cancel_work_sync(&sdata->csa_finalize_work); +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx); + +	kfree(sdata->u.ap.next_beacon); +	sdata->u.ap.next_beacon = NULL;  	/* turn off carrier for this interface and dependent VLANs */  	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) @@ -1071,18 +1164,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	kfree_rcu(old_beacon, rcu_head);  	if (old_probe_resp)  		kfree_rcu(old_probe_resp, rcu_head); +	sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF; -	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) -		sta_info_flush_defer(vlan); -	sta_info_flush_defer(sdata); -	synchronize_net(); -	rcu_barrier(); -	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { -		sta_info_flush_cleanup(vlan); -		ieee80211_free_keys(vlan); -	} -	sta_info_flush_cleanup(sdata); -	ieee80211_free_keys(sdata); +	__sta_info_flush(sdata, true); +	ieee80211_free_keys(sdata, true);  	sdata->vif.bss_conf.enable_beacon = false;  	sdata->vif.bss_conf.ssid_len = 0; @@ -1090,8 +1175,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);  	if (sdata->wdev.cac_started) { +		chandef = sdata->vif.bss_conf.chandef;  		cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); -		cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED, +		cfg80211_cac_event(sdata->dev, &chandef, +				   NL80211_RADAR_CAC_ABORTED,  				   GFP_KERNEL);  	} @@ -1101,8 +1188,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);  	skb_queue_purge(&sdata->u.ap.ps.bc_buf); +	mutex_lock(&local->mtx);  	ieee80211_vif_copy_chanctx_to_vlans(sdata, true);  	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&local->mtx);  	return 0;  } @@ -1314,6 +1403,15 @@ static int sta_apply_parameters(struct ieee80211_local *local,  		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,  						    params->vht_capa, sta); +	if (params->opmode_notif_used) { +		/* returned value is only needed for rc update, but the +		 * rc isn't initialized here yet, so ignore it +		 */ +		__ieee80211_vht_handle_opmode(sdata, sta, +					      params->opmode_notif, +					      band, false); +	} +  	if (ieee80211_vif_is_mesh(&sdata->vif)) {  #ifdef CONFIG_MAC80211_MESH  		u32 changed = 0; @@ -1342,8 +1440,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,  				sta->plink_state = params->plink_state;  				ieee80211_mps_sta_status_update(sta); -				changed |= -				      ieee80211_mps_local_status_update(sdata); +				changed |= ieee80211_mps_set_sta_local_pm(sta, +						NL80211_MESH_POWER_UNKNOWN);  				break;  			default:  				/*  nothing  */ @@ -1367,7 +1465,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,  			changed |=  			      ieee80211_mps_set_sta_local_pm(sta,  							     params->local_pm); -		ieee80211_bss_info_change_notify(sdata, changed); +		ieee80211_mbss_info_change_notify(sdata, changed);  #endif  	} @@ -1375,7 +1473,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,  }  static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac, struct station_parameters *params) +				 const u8 *mac, +				 struct station_parameters *params)  {  	struct ieee80211_local *local = wiphy_priv(wiphy);  	struct sta_info *sta; @@ -1409,6 +1508,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  	if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {  		sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);  		sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); +	} else { +		sta->sta.tdls = true;  	}  	err = sta_apply_parameters(local, sta, params); @@ -1442,7 +1543,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac) +				 const u8 *mac)  {  	struct ieee80211_sub_if_data *sdata; @@ -1456,7 +1557,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_change_station(struct wiphy *wiphy, -				    struct net_device *dev, u8 *mac, +				    struct net_device *dev, const u8 *mac,  				    struct station_parameters *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1525,7 +1626,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,  		if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&  		    sta->sdata->u.vlan.sta) { -			rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL); +			RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);  			prev_4addr = true;  		} @@ -1553,6 +1654,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,  	mutex_unlock(&local->sta_mtx); +	if ((sdata->vif.type == NL80211_IFTYPE_AP || +	     sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && +	    sta->known_smps_mode != sta->sdata->bss->req_smps && +	    test_sta_flag(sta, WLAN_STA_AUTHORIZED) && +	    sta_info_tx_streams(sta) != 1) { +		ht_dbg(sta->sdata, +		       "%pM just authorized and MIMO capable - update SMPS\n", +		       sta->sta.addr); +		ieee80211_send_smps_action(sta->sdata, +			sta->sdata->bss->req_smps, +			sta->sta.addr, +			sta->sdata->vif.bss_conf.bssid); +	} +  	if (sdata->vif.type == NL80211_IFTYPE_STATION &&  	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {  		ieee80211_recalc_ps(local, -1); @@ -1567,7 +1682,7 @@ out_err:  #ifdef CONFIG_MAC80211_MESH  static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, -				 u8 *dst, u8 *next_hop) +			       const u8 *dst, const u8 *next_hop)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; @@ -1595,7 +1710,7 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, -			       u8 *dst) +			       const u8 *dst)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1606,9 +1721,8 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,  	return 0;  } -static int ieee80211_change_mpath(struct wiphy *wiphy, -				    struct net_device *dev, -				    u8 *dst, u8 *next_hop) +static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev, +				  const u8 *dst, const u8 *next_hop)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; @@ -1700,8 +1814,8 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, -				 int idx, u8 *dst, u8 *next_hop, -				 struct mpath_info *pinfo) +				int idx, u8 *dst, u8 *next_hop, +				struct mpath_info *pinfo)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; @@ -1911,8 +2025,10 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,  	sdata->smps_mode = IEEE80211_SMPS_OFF;  	sdata->needed_rx_chains = sdata->local->rx_chains; +	mutex_lock(&sdata->local->mtx);  	err = ieee80211_vif_use_channel(sdata, &setup->chandef,  					IEEE80211_CHANCTX_SHARED); +	mutex_unlock(&sdata->local->mtx);  	if (err)  		return err; @@ -1924,7 +2040,9 @@ static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	ieee80211_stop_mesh(sdata); +	mutex_lock(&sdata->local->mtx);  	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&sdata->local->mtx);  	return 0;  } @@ -1938,7 +2056,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,  	enum ieee80211_band band;  	u32 changed = 0; -	if (!rtnl_dereference(sdata->u.ap.beacon)) +	if (!sdata_dereference(sdata->u.ap.beacon, sdata))  		return -ENOENT;  	band = ieee80211_get_sdata_band(sdata); @@ -2337,8 +2455,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,  }  #endif -int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, -			     enum ieee80211_smps_mode smps_mode) +int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, +				enum ieee80211_smps_mode smps_mode) +{ +	struct sta_info *sta; +	enum ieee80211_smps_mode old_req; +	int i; + +	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP)) +		return -EINVAL; + +	if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) +		return 0; + +	old_req = sdata->u.ap.req_smps; +	sdata->u.ap.req_smps = smps_mode; + +	/* AUTOMATIC doesn't mean much for AP - don't allow it */ +	if (old_req == smps_mode || +	    smps_mode == IEEE80211_SMPS_AUTOMATIC) +		return 0; + +	 /* If no associated stations, there's no need to do anything */ +	if (!atomic_read(&sdata->u.ap.num_mcast_sta)) { +		sdata->smps_mode = smps_mode; +		ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); +		return 0; +	} + +	ht_dbg(sdata, +	       "SMSP %d requested in AP mode, sending Action frame to %d stations\n", +	       smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); + +	mutex_lock(&sdata->local->sta_mtx); +	for (i = 0; i < STA_HASH_SIZE; i++) { +		for (sta = rcu_dereference_protected(sdata->local->sta_hash[i], +				lockdep_is_held(&sdata->local->sta_mtx)); +		     sta; +		     sta = rcu_dereference_protected(sta->hnext, +				lockdep_is_held(&sdata->local->sta_mtx))) { +			/* +			 * Only stations associated to our AP and +			 * associated VLANs +			 */ +			if (sta->sdata->bss != &sdata->u.ap) +				continue; + +			/* This station doesn't support MIMO - skip it */ +			if (sta_info_tx_streams(sta) == 1) +				continue; + +			/* +			 * Don't wake up a STA just to send the action frame +			 * unless we are getting more restrictive. +			 */ +			if (test_sta_flag(sta, WLAN_STA_PS_STA) && +			    !ieee80211_smps_is_restrictive(sta->known_smps_mode, +							   smps_mode)) { +				ht_dbg(sdata, +				       "Won't send SMPS to sleeping STA %pM\n", +				       sta->sta.addr); +				continue; +			} + +			/* +			 * If the STA is not authorized, wait until it gets +			 * authorized and the action frame will be sent then. +			 */ +			if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) +				continue; + +			ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr); +			ieee80211_send_smps_action(sdata, smps_mode, +						   sta->sta.addr, +						   sdata->vif.bss_conf.bssid); +		} +	} +	mutex_unlock(&sdata->local->sta_mtx); + +	sdata->smps_mode = smps_mode; +	ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); + +	return 0; +} + +int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, +				 enum ieee80211_smps_mode smps_mode)  {  	const u8 *ap;  	enum ieee80211_smps_mode old_req; @@ -2346,6 +2548,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,  	lockdep_assert_held(&sdata->wdev.mtx); +	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) +		return -EINVAL; +  	old_req = sdata->u.mgd.req_smps;  	sdata->u.mgd.req_smps = smps_mode; @@ -2386,8 +2591,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); -	if (sdata->vif.type != NL80211_IFTYPE_STATION && -	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT) +	if (sdata->vif.type != NL80211_IFTYPE_STATION)  		return -EOPNOTSUPP;  	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) @@ -2402,7 +2606,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,  	/* no change, but if automatic follow powersave */  	sdata_lock(sdata); -	__ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); +	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);  	sdata_unlock(sdata);  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) @@ -2460,8 +2664,8 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,  		int j;  		sdata->rc_rateidx_mask[i] = mask->control[i].legacy; -		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs, -		       sizeof(mask->control[i].mcs)); +		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs, +		       sizeof(mask->control[i].ht_mcs));  		sdata->rc_has_mcs_mask[i] = false;  		if (!sband) @@ -2497,6 +2701,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	if (!roc)  		return -ENOMEM; +	/* +	 * If the duration is zero, then the driver +	 * wouldn't actually do anything. Set it to +	 * 10 for now. +	 * +	 * TODO: cancel the off-channel operation +	 *       when we get the SKB's TX status and +	 *       the wait time was zero before. +	 */ +	if (!duration) +		duration = 10; +  	roc->chan = channel;  	roc->duration = duration;  	roc->req_duration = duration; @@ -2507,6 +2723,24 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);  	INIT_LIST_HEAD(&roc->dependents); +	/* +	 * cookie is either the roc cookie (for normal roc) +	 * or the SKB (for mgmt TX) +	 */ +	if (!txskb) { +		/* local->mtx protects this */ +		local->roc_cookie_counter++; +		roc->cookie = local->roc_cookie_counter; +		/* wow, you wrapped 64 bits ... more likely a bug */ +		if (WARN_ON(roc->cookie == 0)) { +			roc->cookie = 1; +			local->roc_cookie_counter++; +		} +		*cookie = roc->cookie; +	} else { +		*cookie = (unsigned long)txskb; +	} +  	/* if there's one pending or we're scanning, queue this one */  	if (!list_empty(&local->roc_list) ||  	    local->scanning || local->radar_detect_enabled) @@ -2520,18 +2754,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	/* otherwise actually kick it off here (for error handling) */ -	/* -	 * If the duration is zero, then the driver -	 * wouldn't actually do anything. Set it to -	 * 10 for now. -	 * -	 * TODO: cancel the off-channel operation -	 *       when we get the SKB's TX status and -	 *       the wait time was zero before. -	 */ -	if (!duration) -		duration = 10; -  	ret = drv_remain_on_channel(local, sdata, channel, duration, type);  	if (ret) {  		kfree(roc); @@ -2641,24 +2863,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	if (!queued)  		list_add_tail(&roc->list, &local->roc_list); -	/* -	 * cookie is either the roc cookie (for normal roc) -	 * or the SKB (for mgmt TX) -	 */ -	if (!txskb) { -		/* local->mtx protects this */ -		local->roc_cookie_counter++; -		roc->cookie = local->roc_cookie_counter; -		/* wow, you wrapped 64 bits ... more likely a bug */ -		if (WARN_ON(roc->cookie == 0)) { -			roc->cookie = 1; -			local->roc_cookie_counter++; -		} -		*cookie = roc->cookie; -	} else { -		*cookie = (unsigned long)txskb; -	} -  	return 0;  } @@ -2769,33 +2973,35 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,  static int ieee80211_start_radar_detection(struct wiphy *wiphy,  					   struct net_device *dev, -					   struct cfg80211_chan_def *chandef) +					   struct cfg80211_chan_def *chandef, +					   u32 cac_time_ms)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; -	unsigned long timeout;  	int err; -	if (!list_empty(&local->roc_list) || local->scanning) -		return -EBUSY; +	mutex_lock(&local->mtx); +	if (!list_empty(&local->roc_list) || local->scanning) { +		err = -EBUSY; +		goto out_unlock; +	}  	/* whatever, but channel contexts should not complain about that one */  	sdata->smps_mode = IEEE80211_SMPS_OFF;  	sdata->needed_rx_chains = local->rx_chains; -	sdata->radar_required = true; -	mutex_lock(&local->iflist_mtx);  	err = ieee80211_vif_use_channel(sdata, chandef,  					IEEE80211_CHANCTX_SHARED); -	mutex_unlock(&local->iflist_mtx);  	if (err) -		return err; +		goto out_unlock; -	timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);  	ieee80211_queue_delayed_work(&sdata->local->hw, -				     &sdata->dfs_cac_timer_work, timeout); +				     &sdata->dfs_cac_timer_work, +				     msecs_to_jiffies(cac_time_ms)); -	return 0; + out_unlock: +	mutex_unlock(&local->mtx); +	return err;  }  static struct cfg80211_beacon_data * @@ -2854,52 +3060,272 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)  	return new_beacon;  } +void ieee80211_csa_finish(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	ieee80211_queue_work(&sdata->local->hw, +			     &sdata->csa_finalize_work); +} +EXPORT_SYMBOL(ieee80211_csa_finish); + +static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, +					  u32 *changed) +{ +	int err; + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); +		kfree(sdata->u.ap.next_beacon); +		sdata->u.ap.next_beacon = NULL; + +		if (err < 0) +			return err; +		*changed |= err; +		break; +	case NL80211_IFTYPE_ADHOC: +		err = ieee80211_ibss_finish_csa(sdata); +		if (err < 0) +			return err; +		*changed |= err; +		break; +#ifdef CONFIG_MAC80211_MESH +	case NL80211_IFTYPE_MESH_POINT: +		err = ieee80211_mesh_finish_csa(sdata); +		if (err < 0) +			return err; +		*changed |= err; +		break; +#endif +	default: +		WARN_ON(1); +		return -EINVAL; +	} + +	return 0; +} + +static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	u32 changed = 0; +	int err; + +	sdata_assert_lock(sdata); +	lockdep_assert_held(&local->mtx); + +	sdata->radar_required = sdata->csa_radar_required; +	err = ieee80211_vif_change_channel(sdata, &changed); +	if (err < 0) +		return err; + +	if (!local->use_chanctx) { +		local->_oper_chandef = sdata->csa_chandef; +		ieee80211_hw_config(local, 0); +	} + +	sdata->vif.csa_active = false; + +	err = ieee80211_set_after_csa_beacon(sdata, &changed); +	if (err) +		return err; + +	ieee80211_bss_info_change_notify(sdata, changed); +	cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); + +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); + +	return 0; +} + +static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +{ +	if (__ieee80211_csa_finalize(sdata)) { +		sdata_info(sdata, "failed to finalize CSA, disconnecting\n"); +		cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev, +				    GFP_KERNEL); +	} +} +  void ieee80211_csa_finalize_work(struct work_struct *work)  {  	struct ieee80211_sub_if_data *sdata =  		container_of(work, struct ieee80211_sub_if_data,  			     csa_finalize_work);  	struct ieee80211_local *local = sdata->local; -	int err, changed; + +	sdata_lock(sdata); +	mutex_lock(&local->mtx); + +	/* AP might have been stopped while waiting for the lock. */ +	if (!sdata->vif.csa_active) +		goto unlock;  	if (!ieee80211_sdata_running(sdata)) -		return; +		goto unlock; -	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) -		return; +	ieee80211_csa_finalize(sdata); -	sdata->radar_required = sdata->csa_radar_required; -	err = ieee80211_vif_change_channel(sdata, &local->csa_chandef, -					   &changed); -	if (WARN_ON(err < 0)) -		return; +unlock: +	mutex_unlock(&local->mtx); +	sdata_unlock(sdata); +} -	err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); -	if (err < 0) -		return; +static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, +				    struct cfg80211_csa_settings *params, +				    u32 *changed) +{ +	int err; -	changed |= err; -	kfree(sdata->u.ap.next_beacon); -	sdata->u.ap.next_beacon = NULL; -	sdata->vif.csa_active = false; +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		sdata->u.ap.next_beacon = +			cfg80211_beacon_dup(¶ms->beacon_after); +		if (!sdata->u.ap.next_beacon) +			return -ENOMEM; -	ieee80211_wake_queues_by_reason(&sdata->local->hw, -					IEEE80211_MAX_QUEUE_MAP, -					IEEE80211_QUEUE_STOP_REASON_CSA); +		/* +		 * With a count of 0, we don't have to wait for any +		 * TBTT before switching, so complete the CSA +		 * immediately.  In theory, with a count == 1 we +		 * should delay the switch until just before the next +		 * TBTT, but that would complicate things so we switch +		 * immediately too.  If we would delay the switch +		 * until the next TBTT, we would have to set the probe +		 * response here. +		 * +		 * TODO: A channel switch with count <= 1 without +		 * sending a CSA action frame is kind of useless, +		 * because the clients won't know we're changing +		 * channels.  The action frame must be implemented +		 * either here or in the userspace. +		 */ +		if (params->count <= 1) +			break; -	ieee80211_bss_info_change_notify(sdata, changed); +		if ((params->n_counter_offsets_beacon > +		     IEEE80211_MAX_CSA_COUNTERS_NUM) || +		    (params->n_counter_offsets_presp > +		     IEEE80211_MAX_CSA_COUNTERS_NUM)) +			return -EINVAL; + +		/* make sure we don't have garbage in other counters */ +		memset(sdata->csa_counter_offset_beacon, 0, +		       sizeof(sdata->csa_counter_offset_beacon)); +		memset(sdata->csa_counter_offset_presp, 0, +		       sizeof(sdata->csa_counter_offset_presp)); + +		memcpy(sdata->csa_counter_offset_beacon, +		       params->counter_offsets_beacon, +		       params->n_counter_offsets_beacon * sizeof(u16)); +		memcpy(sdata->csa_counter_offset_presp, +		       params->counter_offsets_presp, +		       params->n_counter_offsets_presp * sizeof(u16)); + +		err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); +		if (err < 0) { +			kfree(sdata->u.ap.next_beacon); +			return err; +		} +		*changed |= err; + +		break; +	case NL80211_IFTYPE_ADHOC: +		if (!sdata->vif.bss_conf.ibss_joined) +			return -EINVAL; + +		if (params->chandef.width != sdata->u.ibss.chandef.width) +			return -EINVAL; + +		switch (params->chandef.width) { +		case NL80211_CHAN_WIDTH_40: +			if (cfg80211_get_chandef_type(¶ms->chandef) != +			    cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) +				return -EINVAL; +		case NL80211_CHAN_WIDTH_5: +		case NL80211_CHAN_WIDTH_10: +		case NL80211_CHAN_WIDTH_20_NOHT: +		case NL80211_CHAN_WIDTH_20: +			break; +		default: +			return -EINVAL; +		} + +		/* changes into another band are not supported */ +		if (sdata->u.ibss.chandef.chan->band != +		    params->chandef.chan->band) +			return -EINVAL; + +		/* see comments in the NL80211_IFTYPE_AP block */ +		if (params->count > 1) { +			err = ieee80211_ibss_csa_beacon(sdata, params); +			if (err < 0) +				return err; +			*changed |= err; +		} + +		ieee80211_send_action_csa(sdata, params); + +		break; +#ifdef CONFIG_MAC80211_MESH +	case NL80211_IFTYPE_MESH_POINT: { +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +		if (params->chandef.width != sdata->vif.bss_conf.chandef.width) +			return -EINVAL; + +		/* changes into another band are not supported */ +		if (sdata->vif.bss_conf.chandef.chan->band != +		    params->chandef.chan->band) +			return -EINVAL; + +		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) { +			ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT; +			if (!ifmsh->pre_value) +				ifmsh->pre_value = 1; +			else +				ifmsh->pre_value++; +		} + +		/* see comments in the NL80211_IFTYPE_AP block */ +		if (params->count > 1) { +			err = ieee80211_mesh_csa_beacon(sdata, params); +			if (err < 0) { +				ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; +				return err; +			} +			*changed |= err; +		} + +		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) +			ieee80211_send_action_csa(sdata, params); + +		break; +		} +#endif +	default: +		return -EOPNOTSUPP; +	} -	cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef); +	return 0;  } -static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, -				    struct cfg80211_csa_settings *params) +static int +__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_local *local = sdata->local; -	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_chanctx_conf *conf;  	struct ieee80211_chanctx *chanctx; -	int err, num_chanctx; +	int err, num_chanctx, changed = 0; + +	sdata_assert_lock(sdata); +	lockdep_assert_held(&local->mtx);  	if (!list_empty(&local->roc_list) || local->scanning)  		return -EBUSY; @@ -2911,23 +3337,24 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,  				       &sdata->vif.bss_conf.chandef))  		return -EINVAL; -	rcu_read_lock(); -	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); -	if (!chanctx_conf) { -		rcu_read_unlock(); +	mutex_lock(&local->chanctx_mtx); +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		mutex_unlock(&local->chanctx_mtx);  		return -EBUSY;  	}  	/* don't handle for multi-VIF cases */ -	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); -	if (chanctx->refcount > 1) { -		rcu_read_unlock(); +	chanctx = container_of(conf, struct ieee80211_chanctx, conf); +	if (ieee80211_chanctx_refcount(local, chanctx) > 1) { +		mutex_unlock(&local->chanctx_mtx);  		return -EBUSY;  	}  	num_chanctx = 0;  	list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)  		num_chanctx++; -	rcu_read_unlock(); +	mutex_unlock(&local->chanctx_mtx);  	if (num_chanctx > 1)  		return -EBUSY; @@ -2936,61 +3363,67 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,  	if (sdata->vif.csa_active)  		return -EBUSY; -	/* only handle AP for now. */ -	switch (sdata->vif.type) { -	case NL80211_IFTYPE_AP: -		break; -	default: -		return -EOPNOTSUPP; -	} - -	sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after); -	if (!sdata->u.ap.next_beacon) -		return -ENOMEM; +	err = ieee80211_set_csa_beacon(sdata, params, &changed); +	if (err) +		return err; -	sdata->csa_counter_offset_beacon = params->counter_offset_beacon; -	sdata->csa_counter_offset_presp = params->counter_offset_presp;  	sdata->csa_radar_required = params->radar_required; +	sdata->csa_chandef = params->chandef; +	sdata->csa_block_tx = params->block_tx; +	sdata->csa_current_counter = params->count; +	sdata->vif.csa_active = true; -	if (params->block_tx) +	if (sdata->csa_block_tx)  		ieee80211_stop_queues_by_reason(&local->hw, -				IEEE80211_MAX_QUEUE_MAP, -				IEEE80211_QUEUE_STOP_REASON_CSA); +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); -	err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); -	if (err < 0) -		return err; +	if (changed) { +		ieee80211_bss_info_change_notify(sdata, changed); +		drv_channel_switch_beacon(sdata, ¶ms->chandef); +	} else { +		/* if the beacon didn't change, we can finalize immediately */ +		ieee80211_csa_finalize(sdata); +	} -	local->csa_chandef = params->chandef; -	sdata->vif.csa_active = true; +	return 0; +} -	ieee80211_bss_info_change_notify(sdata, err); -	drv_channel_switch_beacon(sdata, ¶ms->chandef); +int 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_local *local = sdata->local; +	int err; -	return 0; +	mutex_lock(&local->mtx); +	err = __ieee80211_channel_switch(wiphy, dev, params); +	mutex_unlock(&local->mtx); + +	return err;  }  static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, -			     struct ieee80211_channel *chan, bool offchan, -			     unsigned int wait, const u8 *buf, size_t len, -			     bool no_cck, bool dont_wait_for_ack, u64 *cookie) +			     struct cfg80211_mgmt_tx_params *params, +			     u64 *cookie)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);  	struct ieee80211_local *local = sdata->local;  	struct sk_buff *skb;  	struct sta_info *sta; -	const struct ieee80211_mgmt *mgmt = (void *)buf; +	const struct ieee80211_mgmt *mgmt = (void *)params->buf;  	bool need_offchan = false;  	u32 flags;  	int ret; +	u8 *data; -	if (dont_wait_for_ack) +	if (params->dont_wait_for_ack)  		flags = IEEE80211_TX_CTL_NO_ACK;  	else  		flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |  			IEEE80211_TX_CTL_REQ_TX_STATUS; -	if (no_cck) +	if (params->no_cck)  		flags |= IEEE80211_TX_CTL_NO_CCK_RATE;  	switch (sdata->vif.type) { @@ -3014,7 +3447,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  			need_offchan = true;  		if (!ieee80211_is_action(mgmt->frame_control) ||  		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC || -		    mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED) +		    mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED || +		    mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)  			break;  		rcu_read_lock();  		sta = sta_info_get(sdata, mgmt->da); @@ -3037,7 +3471,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  	/* configurations requiring offchan cannot work if no channel has been  	 * specified  	 */ -	if (need_offchan && !chan) +	if (need_offchan && !params->chan)  		return -EINVAL;  	mutex_lock(&local->mtx); @@ -3050,8 +3484,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);  		if (chanctx_conf) { -			need_offchan = chan && (chan != chanctx_conf->def.chan); -		} else if (!chan) { +			need_offchan = params->chan && +				       (params->chan != +					chanctx_conf->def.chan); +		} else if (!params->chan) {  			ret = -EINVAL;  			rcu_read_unlock();  			goto out_unlock; @@ -3061,19 +3497,32 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  		rcu_read_unlock();  	} -	if (need_offchan && !offchan) { +	if (need_offchan && !params->offchan) {  		ret = -EBUSY;  		goto out_unlock;  	} -	skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);  	if (!skb) {  		ret = -ENOMEM;  		goto out_unlock;  	}  	skb_reserve(skb, local->hw.extra_tx_headroom); -	memcpy(skb_put(skb, len), buf, len); +	data = skb_put(skb, params->len); +	memcpy(data, params->buf, params->len); + +	/* Update CSA counters */ +	if (sdata->vif.csa_active && +	    (sdata->vif.type == NL80211_IFTYPE_AP || +	     sdata->vif.type == NL80211_IFTYPE_ADHOC) && +	    params->n_csa_offsets) { +		int i; +		u8 c = sdata->csa_current_counter; + +		for (i = 0; i < params->n_csa_offsets; i++) +			data[params->csa_offsets[i]] = c; +	}  	IEEE80211_SKB_CB(skb)->flags = flags; @@ -3093,8 +3542,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  			local->hw.offchannel_tx_hw_queue;  	/* This will handle all kinds of coalescing and immediate TX */ -	ret = ieee80211_start_roc_work(local, sdata, chan, -				       wait, cookie, skb, +	ret = ieee80211_start_roc_work(local, sdata, params->chan, +				       params->wait, cookie, skb,  				       IEEE80211_ROC_TYPE_MGMT_TX);  	if (ret)  		kfree_skb(skb); @@ -3182,320 +3631,6 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy,  	return 0;  } -static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) -{ -	u8 *pos = (void *)skb_put(skb, 7); - -	*pos++ = WLAN_EID_EXT_CAPABILITY; -	*pos++ = 5; /* len */ -	*pos++ = 0x0; -	*pos++ = 0x0; -	*pos++ = 0x0; -	*pos++ = 0x0; -	*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; -} - -static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_local *local = sdata->local; -	u16 capab; - -	capab = 0; -	if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) -		return capab; - -	if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) -		capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; -	if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) -		capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; - -	return capab; -} - -static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, -				       u8 *peer, u8 *bssid) -{ -	struct ieee80211_tdls_lnkie *lnkid; - -	lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); - -	lnkid->ie_type = WLAN_EID_LINK_ID; -	lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; - -	memcpy(lnkid->bssid, bssid, ETH_ALEN); -	memcpy(lnkid->init_sta, src_addr, ETH_ALEN); -	memcpy(lnkid->resp_sta, peer, ETH_ALEN); -} - -static int -ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, -			       u8 *peer, u8 action_code, u8 dialog_token, -			       u16 status_code, struct sk_buff *skb) -{ -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); -	struct ieee80211_tdls_data *tf; - -	tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); - -	memcpy(tf->da, peer, ETH_ALEN); -	memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); -	tf->ether_type = cpu_to_be16(ETH_P_TDLS); -	tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; - -	switch (action_code) { -	case WLAN_TDLS_SETUP_REQUEST: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_SETUP_REQUEST; - -		skb_put(skb, sizeof(tf->u.setup_req)); -		tf->u.setup_req.dialog_token = dialog_token; -		tf->u.setup_req.capability = -			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - -		ieee80211_add_srates_ie(sdata, skb, false, band); -		ieee80211_add_ext_srates_ie(sdata, skb, false, band); -		ieee80211_tdls_add_ext_capab(skb); -		break; -	case WLAN_TDLS_SETUP_RESPONSE: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_SETUP_RESPONSE; - -		skb_put(skb, sizeof(tf->u.setup_resp)); -		tf->u.setup_resp.status_code = cpu_to_le16(status_code); -		tf->u.setup_resp.dialog_token = dialog_token; -		tf->u.setup_resp.capability = -			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - -		ieee80211_add_srates_ie(sdata, skb, false, band); -		ieee80211_add_ext_srates_ie(sdata, skb, false, band); -		ieee80211_tdls_add_ext_capab(skb); -		break; -	case WLAN_TDLS_SETUP_CONFIRM: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_SETUP_CONFIRM; - -		skb_put(skb, sizeof(tf->u.setup_cfm)); -		tf->u.setup_cfm.status_code = cpu_to_le16(status_code); -		tf->u.setup_cfm.dialog_token = dialog_token; -		break; -	case WLAN_TDLS_TEARDOWN: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_TEARDOWN; - -		skb_put(skb, sizeof(tf->u.teardown)); -		tf->u.teardown.reason_code = cpu_to_le16(status_code); -		break; -	case WLAN_TDLS_DISCOVERY_REQUEST: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; - -		skb_put(skb, sizeof(tf->u.discover_req)); -		tf->u.discover_req.dialog_token = dialog_token; -		break; -	default: -		return -EINVAL; -	} - -	return 0; -} - -static int -ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, -			   u8 *peer, u8 action_code, u8 dialog_token, -			   u16 status_code, struct sk_buff *skb) -{ -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); -	struct ieee80211_mgmt *mgmt; - -	mgmt = (void *)skb_put(skb, 24); -	memset(mgmt, 0, 24); -	memcpy(mgmt->da, peer, ETH_ALEN); -	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); -	memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); - -	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | -					  IEEE80211_STYPE_ACTION); - -	switch (action_code) { -	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: -		skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); -		mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; -		mgmt->u.action.u.tdls_discover_resp.action_code = -			WLAN_PUB_ACTION_TDLS_DISCOVER_RES; -		mgmt->u.action.u.tdls_discover_resp.dialog_token = -			dialog_token; -		mgmt->u.action.u.tdls_discover_resp.capability = -			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - -		ieee80211_add_srates_ie(sdata, skb, false, band); -		ieee80211_add_ext_srates_ie(sdata, skb, false, band); -		ieee80211_tdls_add_ext_capab(skb); -		break; -	default: -		return -EINVAL; -	} - -	return 0; -} - -static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, -			       u8 *peer, u8 action_code, u8 dialog_token, -			       u16 status_code, const u8 *extra_ies, -			       size_t extra_ies_len) -{ -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	struct ieee80211_local *local = sdata->local; -	struct sk_buff *skb = NULL; -	bool send_direct; -	int ret; - -	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) -		return -ENOTSUPP; - -	/* make sure we are in managed mode, and associated */ -	if (sdata->vif.type != NL80211_IFTYPE_STATION || -	    !sdata->u.mgd.associated) -		return -EINVAL; - -	tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", -		 action_code, peer); - -	skb = dev_alloc_skb(local->hw.extra_tx_headroom + -			    max(sizeof(struct ieee80211_mgmt), -				sizeof(struct ieee80211_tdls_data)) + -			    50 + /* supported rates */ -			    7 + /* ext capab */ -			    extra_ies_len + -			    sizeof(struct ieee80211_tdls_lnkie)); -	if (!skb) -		return -ENOMEM; - -	skb_reserve(skb, local->hw.extra_tx_headroom); - -	switch (action_code) { -	case WLAN_TDLS_SETUP_REQUEST: -	case WLAN_TDLS_SETUP_RESPONSE: -	case WLAN_TDLS_SETUP_CONFIRM: -	case WLAN_TDLS_TEARDOWN: -	case WLAN_TDLS_DISCOVERY_REQUEST: -		ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, -						     action_code, dialog_token, -						     status_code, skb); -		send_direct = false; -		break; -	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: -		ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, -						 dialog_token, status_code, -						 skb); -		send_direct = true; -		break; -	default: -		ret = -ENOTSUPP; -		break; -	} - -	if (ret < 0) -		goto fail; - -	if (extra_ies_len) -		memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - -	/* the TDLS link IE is always added last */ -	switch (action_code) { -	case WLAN_TDLS_SETUP_REQUEST: -	case WLAN_TDLS_SETUP_CONFIRM: -	case WLAN_TDLS_TEARDOWN: -	case WLAN_TDLS_DISCOVERY_REQUEST: -		/* we are the initiator */ -		ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, -					   sdata->u.mgd.bssid); -		break; -	case WLAN_TDLS_SETUP_RESPONSE: -	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: -		/* we are the responder */ -		ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, -					   sdata->u.mgd.bssid); -		break; -	default: -		ret = -ENOTSUPP; -		goto fail; -	} - -	if (send_direct) { -		ieee80211_tx_skb(sdata, skb); -		return 0; -	} - -	/* -	 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise -	 * we should default to AC_VI. -	 */ -	switch (action_code) { -	case WLAN_TDLS_SETUP_REQUEST: -	case WLAN_TDLS_SETUP_RESPONSE: -		skb_set_queue_mapping(skb, IEEE80211_AC_BK); -		skb->priority = 2; -		break; -	default: -		skb_set_queue_mapping(skb, IEEE80211_AC_VI); -		skb->priority = 5; -		break; -	} - -	/* disable bottom halves when entering the Tx path */ -	local_bh_disable(); -	ret = ieee80211_subif_start_xmit(skb, dev); -	local_bh_enable(); - -	return ret; - -fail: -	dev_kfree_skb(skb); -	return ret; -} - -static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, -			       u8 *peer, enum nl80211_tdls_operation oper) -{ -	struct sta_info *sta; -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - -	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) -		return -ENOTSUPP; - -	if (sdata->vif.type != NL80211_IFTYPE_STATION) -		return -EINVAL; - -	tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); - -	switch (oper) { -	case NL80211_TDLS_ENABLE_LINK: -		rcu_read_lock(); -		sta = sta_info_get(sdata, peer); -		if (!sta) { -			rcu_read_unlock(); -			return -ENOLINK; -		} - -		set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); -		rcu_read_unlock(); -		break; -	case NL80211_TDLS_DISABLE_LINK: -		return sta_info_destroy_addr(sdata, peer); -	case NL80211_TDLS_TEARDOWN: -	case NL80211_TDLS_SETUP: -	case NL80211_TDLS_DISCOVERY_REQ: -		/* We don't support in-driver setup/teardown/discovery */ -		return -ENOTSUPP; -	default: -		return -ENOTSUPP; -	} - -	return 0; -} -  static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,  				  const u8 *peer, u64 *cookie)  { @@ -3518,7 +3653,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,  		return -EINVAL;  	}  	band = chanctx_conf->def.chan->band; -	sta = sta_info_get(sdata, peer); +	sta = sta_info_get_bss(sdata, peer);  	if (sta) {  		qos = test_sta_flag(sta, WLAN_STA_WME);  	} else { @@ -3609,7 +3744,47 @@ static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled)  }  #endif -struct cfg80211_ops mac80211_config_ops = { +static int ieee80211_set_qos_map(struct wiphy *wiphy, +				 struct net_device *dev, +				 struct cfg80211_qos_map *qos_map) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct mac80211_qos_map *new_qos_map, *old_qos_map; + +	if (qos_map) { +		new_qos_map = kzalloc(sizeof(*new_qos_map), GFP_KERNEL); +		if (!new_qos_map) +			return -ENOMEM; +		memcpy(&new_qos_map->qos_map, qos_map, sizeof(*qos_map)); +	} else { +		/* A NULL qos_map was passed to disable QoS mapping */ +		new_qos_map = NULL; +	} + +	old_qos_map = sdata_dereference(sdata->qos_map, sdata); +	rcu_assign_pointer(sdata->qos_map, new_qos_map); +	if (old_qos_map) +		kfree_rcu(old_qos_map, rcu_head); + +	return 0; +} + +static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, +				      struct net_device *dev, +				      struct cfg80211_chan_def *chandef) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	int ret; +	u32 changed = 0; + +	ret = ieee80211_vif_change_bandwidth(sdata, chandef, &changed); +	if (ret == 0) +		ieee80211_bss_info_change_notify(sdata, changed); + +	return ret; +} + +const struct cfg80211_ops mac80211_config_ops = {  	.add_virtual_intf = ieee80211_add_iface,  	.del_virtual_intf = ieee80211_del_iface,  	.change_virtual_intf = ieee80211_change_iface, @@ -3688,4 +3863,6 @@ struct cfg80211_ops mac80211_config_ops = {  	.get_channel = ieee80211_cfg_get_channel,  	.start_radar_detection = ieee80211_start_radar_detection,  	.channel_switch = ieee80211_channel_switch, +	.set_qos_map = ieee80211_set_qos_map, +	.set_ap_chanwidth = ieee80211_set_ap_chanwidth,  }; diff --git a/net/mac80211/cfg.h b/net/mac80211/cfg.h index 7d7879f5b00..2d51f62dc76 100644 --- a/net/mac80211/cfg.h +++ b/net/mac80211/cfg.h @@ -4,6 +4,6 @@  #ifndef __CFG_H  #define __CFG_H -extern struct cfg80211_ops mac80211_config_ops; +extern const struct cfg80211_ops mac80211_config_ops;  #endif /* __CFG_H */ diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 3a4764b2869..a310e33972d 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -9,6 +9,310 @@  #include "ieee80211_i.h"  #include "driver-ops.h" +static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, +					  struct ieee80211_chanctx *ctx) +{ +	struct ieee80211_sub_if_data *sdata; +	int num = 0; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) +		num++; + +	return num; +} + +static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local, +					  struct ieee80211_chanctx *ctx) +{ +	struct ieee80211_sub_if_data *sdata; +	int num = 0; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) +		num++; + +	return num; +} + +int ieee80211_chanctx_refcount(struct ieee80211_local *local, +			       struct ieee80211_chanctx *ctx) +{ +	return ieee80211_chanctx_num_assigned(local, ctx) + +	       ieee80211_chanctx_num_reserved(local, ctx); +} + +static int ieee80211_num_chanctx(struct ieee80211_local *local) +{ +	struct ieee80211_chanctx *ctx; +	int num = 0; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(ctx, &local->chanctx_list, list) +		num++; + +	return num; +} + +static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) +{ +	lockdep_assert_held(&local->chanctx_mtx); +	return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); +} + +static const struct cfg80211_chan_def * +ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, +				   struct ieee80211_chanctx *ctx, +				   const struct cfg80211_chan_def *compat) +{ +	struct ieee80211_sub_if_data *sdata; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(sdata, &ctx->reserved_vifs, +			    reserved_chanctx_list) { +		if (!compat) +			compat = &sdata->reserved_chandef; + +		compat = cfg80211_chandef_compatible(&sdata->reserved_chandef, +						     compat); +		if (!compat) +			break; +	} + +	return compat; +} + +static const struct cfg80211_chan_def * +ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, +				       struct ieee80211_chanctx *ctx, +				       const struct cfg80211_chan_def *compat) +{ +	struct ieee80211_sub_if_data *sdata; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(sdata, &ctx->assigned_vifs, +			    assigned_chanctx_list) { +		if (sdata->reserved_chanctx != NULL) +			continue; + +		if (!compat) +			compat = &sdata->vif.bss_conf.chandef; + +		compat = cfg80211_chandef_compatible( +				&sdata->vif.bss_conf.chandef, compat); +		if (!compat) +			break; +	} + +	return compat; +} + +static const struct cfg80211_chan_def * +ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, +				   struct ieee80211_chanctx *ctx, +				   const struct cfg80211_chan_def *compat) +{ +	lockdep_assert_held(&local->chanctx_mtx); + +	compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); +	if (!compat) +		return NULL; + +	compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); +	if (!compat) +		return NULL; + +	return compat; +} + +static bool +ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, +				      struct ieee80211_chanctx *ctx, +				      const struct cfg80211_chan_def *def) +{ +	lockdep_assert_held(&local->chanctx_mtx); + +	if (ieee80211_chanctx_combined_chandef(local, ctx, def)) +		return true; + +	if (!list_empty(&ctx->reserved_vifs) && +	    ieee80211_chanctx_reserved_chandef(local, ctx, def)) +		return true; + +	return false; +} + +static struct ieee80211_chanctx * +ieee80211_find_reservation_chanctx(struct ieee80211_local *local, +				   const struct cfg80211_chan_def *chandef, +				   enum ieee80211_chanctx_mode mode) +{ +	struct ieee80211_chanctx *ctx; + +	lockdep_assert_held(&local->chanctx_mtx); + +	if (mode == IEEE80211_CHANCTX_EXCLUSIVE) +		return NULL; + +	list_for_each_entry(ctx, &local->chanctx_list, list) { +		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) +			continue; + +		if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, +							   chandef)) +			continue; + +		return ctx; +	} + +	return NULL; +} + +static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) +{ +	switch (sta->bandwidth) { +	case IEEE80211_STA_RX_BW_20: +		if (sta->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; +	default: +		WARN_ON(1); +		return NL80211_CHAN_WIDTH_20; +	} +} + +static enum nl80211_chan_width +ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata) +{ +	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; +	struct sta_info *sta; + +	rcu_read_lock(); +	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { +		if (sdata != sta->sdata && +		    !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) +			continue; + +		if (!sta->uploaded) +			continue; + +		max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta)); +	} +	rcu_read_unlock(); + +	return max_bw; +} + +static enum nl80211_chan_width +ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, +				      struct ieee80211_chanctx_conf *conf) +{ +	struct ieee80211_sub_if_data *sdata; +	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; + +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		struct ieee80211_vif *vif = &sdata->vif; +		enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; + +		if (!ieee80211_sdata_running(sdata)) +			continue; + +		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) +			continue; + +		switch (vif->type) { +		case NL80211_IFTYPE_AP: +		case NL80211_IFTYPE_AP_VLAN: +			width = ieee80211_get_max_required_bw(sdata); +			break; +		case NL80211_IFTYPE_P2P_DEVICE: +			continue; +		case NL80211_IFTYPE_STATION: +		case NL80211_IFTYPE_ADHOC: +		case NL80211_IFTYPE_WDS: +		case NL80211_IFTYPE_MESH_POINT: +			width = vif->bss_conf.chandef.width; +			break; +		case NL80211_IFTYPE_UNSPECIFIED: +		case NUM_NL80211_IFTYPES: +		case NL80211_IFTYPE_MONITOR: +		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 = rcu_dereference(local->monitor_sdata); +	if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf) +		max_bw = max(max_bw, conf->def.width); + +	rcu_read_unlock(); + +	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. + */ +void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, +				      struct ieee80211_chanctx *ctx) +{ +	enum nl80211_chan_width max_bw; +	struct cfg80211_chan_def min_def; + +	lockdep_assert_held(&local->chanctx_mtx); + +	/* don't optimize 5MHz, 10MHz, and radar_enabled confs */ +	if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || +	    ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || +	    ctx->conf.radar_enabled) { +		ctx->conf.min_def = ctx->conf.def; +		return; +	} + +	max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); + +	/* downgrade chandef up to max_bw */ +	min_def = ctx->conf.def; +	while (min_def.width > max_bw) +		ieee80211_chandef_downgrade(&min_def); + +	if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) +		return; + +	ctx->conf.min_def = min_def; +	if (!ctx->driver_present) +		return; + +	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH); +} +  static void ieee80211_change_chanctx(struct ieee80211_local *local,  				     struct ieee80211_chanctx *ctx,  				     const struct cfg80211_chan_def *chandef) @@ -20,6 +324,7 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,  	ctx->conf.def = *chandef;  	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); +	ieee80211_recalc_chanctx_min_def(local, ctx);  	if (!local->use_chanctx) {  		local->_oper_chandef = *chandef; @@ -49,6 +354,11 @@ ieee80211_find_chanctx(struct ieee80211_local *local,  		if (!compat)  			continue; +		compat = ieee80211_chanctx_reserved_chandef(local, ctx, +							    compat); +		if (!compat) +			continue; +  		ieee80211_change_chanctx(local, ctx, compat);  		return ctx; @@ -61,6 +371,8 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata; +	lockdep_assert_held(&local->mtx); +  	rcu_read_lock();  	list_for_each_entry_rcu(sdata, &local->interfaces, list) {  		if (sdata->radar_required) { @@ -74,66 +386,91 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local)  }  static struct ieee80211_chanctx * -ieee80211_new_chanctx(struct ieee80211_local *local, -		      const struct cfg80211_chan_def *chandef, -		      enum ieee80211_chanctx_mode mode) +ieee80211_alloc_chanctx(struct ieee80211_local *local, +			const struct cfg80211_chan_def *chandef, +			enum ieee80211_chanctx_mode mode)  {  	struct ieee80211_chanctx *ctx; -	u32 changed; -	int err;  	lockdep_assert_held(&local->chanctx_mtx);  	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);  	if (!ctx) -		return ERR_PTR(-ENOMEM); +		return NULL; +	INIT_LIST_HEAD(&ctx->assigned_vifs); +	INIT_LIST_HEAD(&ctx->reserved_vifs);  	ctx->conf.def = *chandef;  	ctx->conf.rx_chains_static = 1;  	ctx->conf.rx_chains_dynamic = 1;  	ctx->mode = mode;  	ctx->conf.radar_enabled = ieee80211_is_radar_required(local); +	ieee80211_recalc_chanctx_min_def(local, ctx); + +	return ctx; +} + +static int ieee80211_add_chanctx(struct ieee80211_local *local, +				 struct ieee80211_chanctx *ctx) +{ +	u32 changed; +	int err; + +	lockdep_assert_held(&local->mtx); +	lockdep_assert_held(&local->chanctx_mtx); +  	if (!local->use_chanctx)  		local->hw.conf.radar_enabled = ctx->conf.radar_enabled; -	/* acquire mutex to prevent idle from changing */ -	mutex_lock(&local->mtx);  	/* turn idle off *before* setting channel -- some drivers need that */  	changed = ieee80211_idle_off(local);  	if (changed)  		ieee80211_hw_config(local, changed);  	if (!local->use_chanctx) { -		local->_oper_chandef = *chandef; -		ieee80211_hw_config(local, 0); +		local->_oper_chandef = ctx->conf.def; +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);  	} else {  		err = drv_add_chanctx(local, ctx);  		if (err) { -			kfree(ctx); -			ctx = ERR_PTR(err); -  			ieee80211_recalc_idle(local); -			goto out; +			return err;  		}  	} -	/* and keep the mutex held until the new chanctx is on the list */ -	list_add_rcu(&ctx->list, &local->chanctx_list); +	return 0; +} - out: -	mutex_unlock(&local->mtx); +static struct ieee80211_chanctx * +ieee80211_new_chanctx(struct ieee80211_local *local, +		      const struct cfg80211_chan_def *chandef, +		      enum ieee80211_chanctx_mode mode) +{ +	struct ieee80211_chanctx *ctx; +	int err; + +	lockdep_assert_held(&local->mtx); +	lockdep_assert_held(&local->chanctx_mtx); +	ctx = ieee80211_alloc_chanctx(local, chandef, mode); +	if (!ctx) +		return ERR_PTR(-ENOMEM); + +	err = ieee80211_add_chanctx(local, ctx); +	if (err) { +		kfree(ctx); +		return ERR_PTR(err); +	} + +	list_add_rcu(&ctx->list, &local->chanctx_list);  	return ctx;  } -static void ieee80211_free_chanctx(struct ieee80211_local *local, -				   struct ieee80211_chanctx *ctx) +static void ieee80211_del_chanctx(struct ieee80211_local *local, +				  struct ieee80211_chanctx *ctx)  { -	bool check_single_channel = false;  	lockdep_assert_held(&local->chanctx_mtx); -	WARN_ON_ONCE(ctx->refcount != 0); -  	if (!local->use_chanctx) {  		struct cfg80211_chan_def *chandef = &local->_oper_chandef;  		chandef->width = NL80211_CHAN_WIDTH_20_NOHT; @@ -143,49 +480,29 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,  		/* NOTE: Disabling radar is only valid here for  		 * single channel context. To be sure, check it ...  		 */ -		if (local->hw.conf.radar_enabled) -			check_single_channel = true; +		WARN_ON(local->hw.conf.radar_enabled && +			!list_empty(&local->chanctx_list)); +  		local->hw.conf.radar_enabled = false; -		ieee80211_hw_config(local, 0); +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);  	} else {  		drv_remove_chanctx(local, ctx);  	} -	list_del_rcu(&ctx->list); -	kfree_rcu(ctx, rcu_head); - -	/* throw a warning if this wasn't the only channel context. */ -	WARN_ON(check_single_channel && !list_empty(&local->chanctx_list)); - -	mutex_lock(&local->mtx);  	ieee80211_recalc_idle(local); -	mutex_unlock(&local->mtx);  } -static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, -					struct ieee80211_chanctx *ctx) +static void ieee80211_free_chanctx(struct ieee80211_local *local, +				   struct ieee80211_chanctx *ctx)  { -	struct ieee80211_local *local = sdata->local; -	int ret; -  	lockdep_assert_held(&local->chanctx_mtx); -	ret = drv_assign_vif_chanctx(local, sdata, ctx); -	if (ret) -		return ret; - -	rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); -	ctx->refcount++; - -	ieee80211_recalc_txpower(sdata); -	sdata->vif.bss_conf.idle = false; - -	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && -	    sdata->vif.type != NL80211_IFTYPE_MONITOR) -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); +	WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); -	return 0; +	list_del_rcu(&ctx->list); +	ieee80211_del_chanctx(local, ctx); +	kfree_rcu(ctx, rcu_head);  }  static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, @@ -221,72 +538,106 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,  	ieee80211_change_chanctx(local, ctx, compat);  } -static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, -					   struct ieee80211_chanctx *ctx) +static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, +					   struct ieee80211_chanctx *chanctx)  { -	struct ieee80211_local *local = sdata->local; +	bool radar_enabled;  	lockdep_assert_held(&local->chanctx_mtx); +	/* for setting local->radar_detect_enabled */ +	lockdep_assert_held(&local->mtx); -	ctx->refcount--; -	rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); - -	sdata->vif.bss_conf.idle = true; +	radar_enabled = ieee80211_is_radar_required(local); -	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && -	    sdata->vif.type != NL80211_IFTYPE_MONITOR) -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); +	if (radar_enabled == chanctx->conf.radar_enabled) +		return; -	drv_unassign_vif_chanctx(local, sdata, ctx); +	chanctx->conf.radar_enabled = radar_enabled; +	local->radar_detect_enabled = chanctx->conf.radar_enabled; -	if (ctx->refcount > 0) { -		ieee80211_recalc_chanctx_chantype(sdata->local, ctx); -		ieee80211_recalc_smps_chanctx(local, ctx); -		ieee80211_recalc_radar_chanctx(local, ctx); +	if (!local->use_chanctx) { +		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);  	} + +	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);  } -static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) +static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, +					struct ieee80211_chanctx *new_ctx)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_chanctx_conf *conf; -	struct ieee80211_chanctx *ctx; - -	lockdep_assert_held(&local->chanctx_mtx); +	struct ieee80211_chanctx *curr_ctx = NULL; +	int ret = 0;  	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,  					 lockdep_is_held(&local->chanctx_mtx)); -	if (!conf) -		return; -	ctx = container_of(conf, struct ieee80211_chanctx, conf); +	if (conf) { +		curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); -	ieee80211_unassign_vif_chanctx(sdata, ctx); -	if (ctx->refcount == 0) -		ieee80211_free_chanctx(local, ctx); +		drv_unassign_vif_chanctx(local, sdata, curr_ctx); +		conf = NULL; +		list_del(&sdata->assigned_chanctx_list); +	} + +	if (new_ctx) { +		ret = drv_assign_vif_chanctx(local, sdata, new_ctx); +		if (ret) +			goto out; + +		conf = &new_ctx->conf; +		list_add(&sdata->assigned_chanctx_list, +			 &new_ctx->assigned_vifs); +	} + +out: +	rcu_assign_pointer(sdata->vif.chanctx_conf, conf); + +	sdata->vif.bss_conf.idle = !conf; + +	if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { +		ieee80211_recalc_chanctx_chantype(local, curr_ctx); +		ieee80211_recalc_smps_chanctx(local, curr_ctx); +		ieee80211_recalc_radar_chanctx(local, curr_ctx); +		ieee80211_recalc_chanctx_min_def(local, curr_ctx); +	} + +	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { +		ieee80211_recalc_txpower(sdata); +		ieee80211_recalc_chanctx_min_def(local, new_ctx); +	} + +	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && +	    sdata->vif.type != NL80211_IFTYPE_MONITOR) +		ieee80211_bss_info_change_notify(sdata, +						 BSS_CHANGED_IDLE); + +	return ret;  } -void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, -				    struct ieee80211_chanctx *chanctx) +static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)  { -	bool radar_enabled; +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *ctx;  	lockdep_assert_held(&local->chanctx_mtx); -	radar_enabled = ieee80211_is_radar_required(local); - -	if (radar_enabled == chanctx->conf.radar_enabled) +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf)  		return; -	chanctx->conf.radar_enabled = radar_enabled; -	local->radar_detect_enabled = chanctx->conf.radar_enabled; +	ctx = container_of(conf, struct ieee80211_chanctx, conf); -	if (!local->use_chanctx) { -		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; -		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); -	} +	if (sdata->reserved_chanctx) +		ieee80211_vif_unreserve_chanctx(sdata); -	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); +	ieee80211_assign_vif_chanctx(sdata, NULL); +	if (ieee80211_chanctx_refcount(local, ctx) == 0) +		ieee80211_free_chanctx(local, ctx);  }  void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, @@ -351,6 +702,13 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,  		rx_chains_static = max(rx_chains_static, needed_static);  		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);  	} + +	/* Disable SMPS for the monitor interface */ +	sdata = rcu_dereference(local->monitor_sdata); +	if (sdata && +	    rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf) +		rx_chains_dynamic = rx_chains_static = local->rx_chains; +  	rcu_read_unlock();  	if (!local->use_chanctx) { @@ -378,11 +736,30 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_chanctx *ctx; +	u8 radar_detect_width = 0;  	int ret; +	lockdep_assert_held(&local->mtx); +  	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));  	mutex_lock(&local->chanctx_mtx); + +	ret = cfg80211_chandef_dfs_required(local->hw.wiphy, +					    chandef, +					    sdata->wdev.iftype); +	if (ret < 0) +		goto out; +	if (ret > 0) +		radar_detect_width = BIT(chandef->width); + +	sdata->radar_required = ret; + +	ret = ieee80211_check_combinations(sdata, chandef, mode, +					   radar_detect_width); +	if (ret < 0) +		goto out; +  	__ieee80211_vif_release_channel(sdata);  	ctx = ieee80211_find_chanctx(local, chandef, mode); @@ -398,7 +775,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,  	ret = ieee80211_assign_vif_chanctx(sdata, ctx);  	if (ret) {  		/* if assign fails refcount stays the same */ -		if (ctx->refcount == 0) +		if (ieee80211_chanctx_refcount(local, ctx) == 0)  			ieee80211_free_chanctx(local, ctx);  		goto out;  	} @@ -410,25 +787,142 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,  	return ret;  } +static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, +					  struct ieee80211_chanctx *ctx, +					  u32 *changed) +{ +	struct ieee80211_local *local = sdata->local; +	const struct cfg80211_chan_def *chandef = &sdata->csa_chandef; +	u32 chanctx_changed = 0; + +	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, +				     IEEE80211_CHAN_DISABLED)) +		return -EINVAL; + +	if (ieee80211_chanctx_refcount(local, ctx) != 1) +		return -EINVAL; + +	if (sdata->vif.bss_conf.chandef.width != chandef->width) { +		chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; +		*changed |= BSS_CHANGED_BANDWIDTH; +	} + +	sdata->vif.bss_conf.chandef = *chandef; +	ctx->conf.def = *chandef; + +	chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; +	drv_change_chanctx(local, ctx, chanctx_changed); + +	ieee80211_recalc_chanctx_chantype(local, ctx); +	ieee80211_recalc_smps_chanctx(local, ctx); +	ieee80211_recalc_radar_chanctx(local, ctx); +	ieee80211_recalc_chanctx_min_def(local, ctx); + +	return 0; +} +  int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, -				 const struct cfg80211_chan_def *chandef,  				 u32 *changed)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_chanctx_conf *conf;  	struct ieee80211_chanctx *ctx;  	int ret; -	u32 chanctx_changed = 0; + +	lockdep_assert_held(&local->mtx);  	/* should never be called if not performing a channel switch. */  	if (WARN_ON(!sdata->vif.csa_active))  		return -EINVAL; -	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, -				     IEEE80211_CHAN_DISABLED)) +	mutex_lock(&local->chanctx_mtx); +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		ret = -EINVAL; +		goto out; +	} + +	ctx = container_of(conf, struct ieee80211_chanctx, conf); + +	ret = __ieee80211_vif_change_channel(sdata, ctx, changed); + out: +	mutex_unlock(&local->chanctx_mtx); +	return ret; +} + +static void +__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, +				      bool clear) +{ +	struct ieee80211_local *local __maybe_unused = sdata->local; +	struct ieee80211_sub_if_data *vlan; +	struct ieee80211_chanctx_conf *conf; + +	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) +		return; + +	lockdep_assert_held(&local->mtx); + +	/* 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(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	WARN_ON(!conf); + +	if (clear) +		conf = NULL; + +	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +		rcu_assign_pointer(vlan->vif.chanctx_conf, conf); +} + +void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, +					 bool clear) +{ +	struct ieee80211_local *local = sdata->local; + +	mutex_lock(&local->chanctx_mtx); + +	__ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); + +	mutex_unlock(&local->chanctx_mtx); +} + +int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; + +	lockdep_assert_held(&sdata->local->chanctx_mtx); + +	if (WARN_ON(!ctx))  		return -EINVAL; +	list_del(&sdata->reserved_chanctx_list); +	sdata->reserved_chanctx = NULL; + +	if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) +		ieee80211_free_chanctx(sdata->local, ctx); + +	return 0; +} + +int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, +				  const struct cfg80211_chan_def *chandef, +				  enum ieee80211_chanctx_mode mode, +				  bool radar_required) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *new_ctx, *curr_ctx; +	int ret = 0; +  	mutex_lock(&local->chanctx_mtx); +  	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,  					 lockdep_is_held(&local->chanctx_mtx));  	if (!conf) { @@ -436,34 +930,108 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,  		goto out;  	} -	ctx = container_of(conf, struct ieee80211_chanctx, conf); -	if (ctx->refcount != 1) { +	curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); + +	new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); +	if (!new_ctx) { +		if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 && +		    (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) { +			/* if we're the only users of the chanctx and +			 * the driver supports changing a running +			 * context, reserve our current context +			 */ +			new_ctx = curr_ctx; +		} else if (ieee80211_can_create_new_chanctx(local)) { +			/* create a new context and reserve it */ +			new_ctx = ieee80211_new_chanctx(local, chandef, mode); +			if (IS_ERR(new_ctx)) { +				ret = PTR_ERR(new_ctx); +				goto out; +			} +		} else { +			ret = -EBUSY; +			goto out; +		} +	} + +	list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); +	sdata->reserved_chanctx = new_ctx; +	sdata->reserved_chandef = *chandef; +	sdata->reserved_radar_required = radar_required; +out: +	mutex_unlock(&local->chanctx_mtx); +	return ret; +} + +int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, +				       u32 *changed) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx *ctx; +	struct ieee80211_chanctx *old_ctx; +	struct ieee80211_chanctx_conf *conf; +	int ret; +	u32 tmp_changed = *changed; + +	/* TODO: need to recheck if the chandef is usable etc.? */ + +	lockdep_assert_held(&local->mtx); + +	mutex_lock(&local->chanctx_mtx); + +	ctx = sdata->reserved_chanctx; +	if (WARN_ON(!ctx)) {  		ret = -EINVAL;  		goto out;  	} -	if (sdata->vif.bss_conf.chandef.width != chandef->width) { -		chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; -		*changed |= BSS_CHANGED_BANDWIDTH; +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		ret = -EINVAL; +		goto out;  	} -	sdata->vif.bss_conf.chandef = *chandef; -	ctx->conf.def = *chandef; +	old_ctx = container_of(conf, struct ieee80211_chanctx, conf); -	chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; -	drv_change_chanctx(local, ctx, chanctx_changed); +	if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) +		tmp_changed |= BSS_CHANGED_BANDWIDTH; -	if (!local->use_chanctx) { -		local->_oper_chandef = *chandef; -		ieee80211_hw_config(local, 0); +	sdata->vif.bss_conf.chandef = sdata->reserved_chandef; + +	/* unref our reservation */ +	sdata->reserved_chanctx = NULL; +	sdata->radar_required = sdata->reserved_radar_required; +	list_del(&sdata->reserved_chanctx_list); + +	if (old_ctx == ctx) { +		/* This is our own context, just change it */ +		ret = __ieee80211_vif_change_channel(sdata, old_ctx, +						     &tmp_changed); +		if (ret) +			goto out; +	} else { +		ret = ieee80211_assign_vif_chanctx(sdata, ctx); +		if (ieee80211_chanctx_refcount(local, old_ctx) == 0) +			ieee80211_free_chanctx(local, old_ctx); +		if (ret) { +			/* if assign fails refcount stays the same */ +			if (ieee80211_chanctx_refcount(local, ctx) == 0) +				ieee80211_free_chanctx(local, ctx); +			goto out; +		} + +		if (sdata->vif.type == NL80211_IFTYPE_AP) +			__ieee80211_vif_copy_chanctx_to_vlans(sdata, false);  	} +	*changed = tmp_changed; +  	ieee80211_recalc_chanctx_chantype(local, ctx);  	ieee80211_recalc_smps_chanctx(local, ctx);  	ieee80211_recalc_radar_chanctx(local, ctx); - -	ret = 0; - out: +	ieee80211_recalc_chanctx_min_def(local, ctx); +out:  	mutex_unlock(&local->chanctx_mtx);  	return ret;  } @@ -521,6 +1089,8 @@ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)  {  	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); +	lockdep_assert_held(&sdata->local->mtx); +  	mutex_lock(&sdata->local->chanctx_mtx);  	__ieee80211_vif_release_channel(sdata);  	mutex_unlock(&sdata->local->chanctx_mtx); @@ -545,40 +1115,6 @@ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)  	mutex_unlock(&local->chanctx_mtx);  } -void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, -					 bool clear) -{ -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_sub_if_data *vlan; -	struct ieee80211_chanctx_conf *conf; - -	ASSERT_RTNL(); - -	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) -		return; - -	mutex_lock(&local->chanctx_mtx); - -	/* -	 * 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(sdata->vif.chanctx_conf, -				lockdep_is_held(&local->chanctx_mtx)); -	WARN_ON(!conf); - -	if (clear) -		conf = NULL; - -	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) -		rcu_assign_pointer(vlan->vif.chanctx_conf, conf); - -	mutex_unlock(&local->chanctx_mtx); -} -  void ieee80211_iter_chan_contexts_atomic(  	struct ieee80211_hw *hw,  	void (*iter)(struct ieee80211_hw *hw, diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index 4ccc5ed6237..493d68061f0 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h @@ -44,6 +44,12 @@  #define MAC80211_MESH_SYNC_DEBUG 0  #endif +#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG +#define MAC80211_MESH_CSA_DEBUG 1 +#else +#define MAC80211_MESH_CSA_DEBUG 0 +#endif +  #ifdef CONFIG_MAC80211_MESH_PS_DEBUG  #define MAC80211_MESH_PS_DEBUG 1  #else @@ -157,6 +163,10 @@ do {									\  	_sdata_dbg(MAC80211_MESH_SYNC_DEBUG,				\  		   sdata, fmt, ##__VA_ARGS__) +#define mcsa_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_MESH_CSA_DEBUG,				\ +		   sdata, fmt, ##__VA_ARGS__) +  #define mps_dbg(sdata, fmt, ...)					\  	_sdata_dbg(MAC80211_MESH_PS_DEBUG,				\  		   sdata, fmt, ##__VA_ARGS__) diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index b0e32d62811..0e963bc1cea 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -17,6 +17,172 @@  #define DEBUGFS_FORMAT_BUFFER_SIZE 100 +#define TX_LATENCY_BIN_DELIMTER_C ',' +#define TX_LATENCY_BIN_DELIMTER_S "," +#define TX_LATENCY_BINS_DISABLED "enable(bins disabled)\n" +#define TX_LATENCY_DISABLED "disable\n" + + +/* + * Display if Tx latency statistics & bins are enabled/disabled + */ +static ssize_t sta_tx_latency_stat_read(struct file *file, +					char __user *userbuf, +					size_t count, loff_t *ppos) +{ +	struct ieee80211_local *local = file->private_data; +	struct ieee80211_tx_latency_bin_ranges  *tx_latency; +	char *buf; +	int bufsz, i, ret; +	int pos = 0; + +	rcu_read_lock(); + +	tx_latency = rcu_dereference(local->tx_latency); + +	if (tx_latency && tx_latency->n_ranges) { +		bufsz = tx_latency->n_ranges * 15; +		buf = kzalloc(bufsz, GFP_ATOMIC); +		if (!buf) +			goto err; + +		for (i = 0; i < tx_latency->n_ranges; i++) +			pos += scnprintf(buf + pos, bufsz - pos, "%d,", +					 tx_latency->ranges[i]); +		pos += scnprintf(buf + pos, bufsz - pos, "\n"); +	} else if (tx_latency) { +		bufsz = sizeof(TX_LATENCY_BINS_DISABLED) + 1; +		buf = kzalloc(bufsz, GFP_ATOMIC); +		if (!buf) +			goto err; + +		pos += scnprintf(buf + pos, bufsz - pos, "%s\n", +				 TX_LATENCY_BINS_DISABLED); +	} else { +		bufsz = sizeof(TX_LATENCY_DISABLED) + 1; +		buf = kzalloc(bufsz, GFP_ATOMIC); +		if (!buf) +			goto err; + +		pos += scnprintf(buf + pos, bufsz - pos, "%s\n", +				 TX_LATENCY_DISABLED); +	} + +	rcu_read_unlock(); + +	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); +	kfree(buf); + +	return ret; +err: +	rcu_read_unlock(); +	return -ENOMEM; +} + +/* + * Receive input from user regarding Tx latency statistics + * The input should indicate if Tx latency statistics and bins are + * enabled/disabled. + * If bins are enabled input should indicate the amount of different bins and + * their ranges. Each bin will count how many Tx frames transmitted within the + * appropriate latency. + * Legal input is: + * a) "enable(bins disabled)" - to enable only general statistics + * b) "a,b,c,d,...z" - to enable general statistics and bins, where all are + * numbers and a < b < c < d.. < z + * c) "disable" - disable all statistics + * NOTE: must configure Tx latency statistics bins before stations connected. + */ + +static ssize_t sta_tx_latency_stat_write(struct file *file, +					 const char __user *userbuf, +					 size_t count, loff_t *ppos) +{ +	struct ieee80211_local *local = file->private_data; +	char buf[128] = {}; +	char *bins = buf; +	char *token; +	int buf_size, i, alloc_size; +	int prev_bin = 0; +	int n_ranges = 0; +	int ret = count; +	struct ieee80211_tx_latency_bin_ranges  *tx_latency; + +	if (sizeof(buf) <= count) +		return -EINVAL; +	buf_size = count; +	if (copy_from_user(buf, userbuf, buf_size)) +		return -EFAULT; + +	mutex_lock(&local->sta_mtx); + +	/* cannot change config once we have stations */ +	if (local->num_sta) +		goto unlock; + +	tx_latency = +		rcu_dereference_protected(local->tx_latency, +					  lockdep_is_held(&local->sta_mtx)); + +	/* disable Tx statistics */ +	if (!strcmp(buf, TX_LATENCY_DISABLED)) { +		if (!tx_latency) +			goto unlock; +		RCU_INIT_POINTER(local->tx_latency, NULL); +		synchronize_rcu(); +		kfree(tx_latency); +		goto unlock; +	} + +	/* Tx latency already enabled */ +	if (tx_latency) +		goto unlock; + +	if (strcmp(TX_LATENCY_BINS_DISABLED, buf)) { +		/* check how many bins and between what ranges user requested */ +		token = buf; +		while (*token != '\0') { +			if (*token == TX_LATENCY_BIN_DELIMTER_C) +				n_ranges++; +			token++; +		} +		n_ranges++; +	} + +	alloc_size = sizeof(struct ieee80211_tx_latency_bin_ranges) + +		     n_ranges * sizeof(u32); +	tx_latency = kzalloc(alloc_size, GFP_ATOMIC); +	if (!tx_latency) { +		ret = -ENOMEM; +		goto unlock; +	} +	tx_latency->n_ranges = n_ranges; +	for (i = 0; i < n_ranges; i++) { /* setting bin ranges */ +		token = strsep(&bins, TX_LATENCY_BIN_DELIMTER_S); +		sscanf(token, "%d", &tx_latency->ranges[i]); +		/* bins values should be in ascending order */ +		if (prev_bin >= tx_latency->ranges[i]) { +			ret = -EINVAL; +			kfree(tx_latency); +			goto unlock; +		} +		prev_bin = tx_latency->ranges[i]; +	} +	rcu_assign_pointer(local->tx_latency, tx_latency); + +unlock: +	mutex_unlock(&local->sta_mtx); + +	return ret; +} + +static const struct file_operations stats_tx_latency_ops = { +	.write = sta_tx_latency_stat_write, +	.read = sta_tx_latency_stat_read, +	.open = simple_open, +	.llseek = generic_file_llseek, +}; +  int mac80211_format_buffer(char __user *userbuf, size_t count,  				  loff_t *ppos, char *fmt, ...)  { @@ -103,54 +269,57 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,  	if (!buf)  		return 0; -	sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags); +	sf += scnprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);  	if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) -		sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n"); +		sf += scnprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");  	if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) -		sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n"); +		sf += scnprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");  	if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) -		sf += snprintf(buf + sf, mxln - sf, -			       "HOST_BCAST_PS_BUFFERING\n"); +		sf += scnprintf(buf + sf, mxln - sf, +				"HOST_BCAST_PS_BUFFERING\n");  	if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE) -		sf += snprintf(buf + sf, mxln - sf, -			       "2GHZ_SHORT_SLOT_INCAPABLE\n"); +		sf += scnprintf(buf + sf, mxln - sf, +				"2GHZ_SHORT_SLOT_INCAPABLE\n");  	if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE) -		sf += snprintf(buf + sf, mxln - sf, -			       "2GHZ_SHORT_PREAMBLE_INCAPABLE\n"); +		sf += scnprintf(buf + sf, mxln - sf, +				"2GHZ_SHORT_PREAMBLE_INCAPABLE\n");  	if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) -		sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n"); +		sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");  	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) -		sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n"); +		sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");  	if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC) -		sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n"); +		sf += scnprintf(buf + sf, mxln - sf, +				"NEED_DTIM_BEFORE_ASSOC\n");  	if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT) -		sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n"); +		sf += scnprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");  	if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) -		sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n"); +		sf += scnprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS) -		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n"); +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");  	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) -		sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n"); +		sf += scnprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) -		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");  	if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) -		sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); +		sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) -		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) -		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n"); +		sf += scnprintf(buf + sf, mxln - sf, +				"SUPPORTS_DYNAMIC_SMPS\n");  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) -		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");  	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) -		sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n"); +		sf += scnprintf(buf + sf, mxln - sf, +				"REPORTS_TX_ACK_STATUS\n");  	if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) -		sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n"); +		sf += scnprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK) -		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n"); +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");  	if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) -		sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); +		sf += scnprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");  	if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW) -		sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n"); +		sf += scnprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");  	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));  	kfree(buf); @@ -312,4 +481,6 @@ void debugfs_hw_add(struct ieee80211_local *local)  	DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);  	DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);  	DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); + +	DEBUGFS_DEVSTATS_ADD(tx_latency);  } diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h index 214ed4ecd73..60c35afee29 100644 --- a/net/mac80211/debugfs.h +++ b/net/mac80211/debugfs.h @@ -1,6 +1,8 @@  #ifndef __MAC80211_DEBUGFS_H  #define __MAC80211_DEBUGFS_H +#include "ieee80211_i.h" +  #ifdef CONFIG_MAC80211_DEBUGFS  void debugfs_hw_add(struct ieee80211_local *local);  int __printf(4, 5) mac80211_format_buffer(char __user *userbuf, size_t count, diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index cafe614ef93..e205ebabfa5 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -34,8 +34,7 @@ static ssize_t ieee80211_if_read(  	ssize_t ret = -EINVAL;  	read_lock(&dev_base_lock); -	if (sdata->dev->reg_state == NETREG_REGISTERED) -		ret = (*format)(sdata, buf, sizeof(buf)); +	ret = (*format)(sdata, buf, sizeof(buf));  	read_unlock(&dev_base_lock);  	if (ret >= 0) @@ -62,8 +61,7 @@ static ssize_t ieee80211_if_write(  	ret = -ENODEV;  	rtnl_lock(); -	if (sdata->dev->reg_state == NETREG_REGISTERED) -		ret = (*write)(sdata, buf, count); +	ret = (*write)(sdata, buf, count);  	rtnl_unlock();  	return ret; @@ -133,7 +131,15 @@ static ssize_t ieee80211_if_fmt_##name(					\  			 jiffies_to_msecs(sdata->field));		\  } -#define __IEEE80211_IF_FILE(name, _write)				\ +#define _IEEE80211_IF_FILE_OPS(name, _read, _write)			\ +static const struct file_operations name##_ops = {			\ +	.read = (_read),						\ +	.write = (_write),						\ +	.open = simple_open,						\ +	.llseek = generic_file_llseek,					\ +} + +#define _IEEE80211_IF_FILE_R_FN(name)					\  static ssize_t ieee80211_if_read_##name(struct file *file,		\  					char __user *userbuf,		\  					size_t count, loff_t *ppos)	\ @@ -141,28 +147,34 @@ static ssize_t ieee80211_if_read_##name(struct file *file,		\  	return ieee80211_if_read(file->private_data,			\  				 userbuf, count, ppos,			\  				 ieee80211_if_fmt_##name);		\ -}									\ -static const struct file_operations name##_ops = {			\ -	.read = ieee80211_if_read_##name,				\ -	.write = (_write),						\ -	.open = simple_open,						\ -	.llseek = generic_file_llseek,					\  } -#define __IEEE80211_IF_FILE_W(name)					\ +#define _IEEE80211_IF_FILE_W_FN(name)					\  static ssize_t ieee80211_if_write_##name(struct file *file,		\  					 const char __user *userbuf,	\  					 size_t count, loff_t *ppos)	\  {									\  	return ieee80211_if_write(file->private_data, userbuf, count,	\  				  ppos, ieee80211_if_parse_##name);	\ -}									\ -__IEEE80211_IF_FILE(name, ieee80211_if_write_##name) +} + +#define IEEE80211_IF_FILE_R(name)					\ +	_IEEE80211_IF_FILE_R_FN(name)					\ +	_IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name, NULL) +#define IEEE80211_IF_FILE_W(name)					\ +	_IEEE80211_IF_FILE_W_FN(name)					\ +	_IEEE80211_IF_FILE_OPS(name, NULL, ieee80211_if_write_##name) + +#define IEEE80211_IF_FILE_RW(name)					\ +	_IEEE80211_IF_FILE_R_FN(name)					\ +	_IEEE80211_IF_FILE_W_FN(name)					\ +	_IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name,		\ +			       ieee80211_if_write_##name)  #define IEEE80211_IF_FILE(name, field, format)				\ -		IEEE80211_IF_FMT_##format(name, field)			\ -		__IEEE80211_IF_FILE(name, NULL) +	IEEE80211_IF_FMT_##format(name, field)				\ +	IEEE80211_IF_FILE_R(name)  /* common attributes */  IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); @@ -199,7 +211,7 @@ ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata,  	return len;  } -__IEEE80211_IF_FILE(hw_queues, NULL); +IEEE80211_IF_FILE_R(hw_queues);  /* STA attributes */  IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); @@ -224,12 +236,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,  	     smps_mode == IEEE80211_SMPS_AUTOMATIC))  		return -EINVAL; -	/* supported only on managed interfaces for now */ -	if (sdata->vif.type != NL80211_IFTYPE_STATION) +	if (sdata->vif.type != NL80211_IFTYPE_STATION && +	    sdata->vif.type != NL80211_IFTYPE_AP)  		return -EOPNOTSUPP;  	sdata_lock(sdata); -	err = __ieee80211_request_smps(sdata, smps_mode); +	if (sdata->vif.type == NL80211_IFTYPE_STATION) +		err = __ieee80211_request_smps_mgd(sdata, smps_mode); +	else +		err = __ieee80211_request_smps_ap(sdata, smps_mode);  	sdata_unlock(sdata);  	return err; @@ -245,12 +260,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {  static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,  				     char *buf, int buflen)  { -	if (sdata->vif.type != NL80211_IFTYPE_STATION) -		return -EOPNOTSUPP; - -	return snprintf(buf, buflen, "request: %s\nused: %s\n", -			smps_modes[sdata->u.mgd.req_smps], -			smps_modes[sdata->smps_mode]); +	if (sdata->vif.type == NL80211_IFTYPE_STATION) +		return snprintf(buf, buflen, "request: %s\nused: %s\n", +				smps_modes[sdata->u.mgd.req_smps], +				smps_modes[sdata->smps_mode]); +	if (sdata->vif.type == NL80211_IFTYPE_AP) +		return snprintf(buf, buflen, "request: %s\nused: %s\n", +				smps_modes[sdata->u.ap.req_smps], +				smps_modes[sdata->smps_mode]); +	return -EINVAL;  }  static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, @@ -269,14 +287,7 @@ static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,  	return -EINVAL;  } - -__IEEE80211_IF_FILE_W(smps); - -static ssize_t ieee80211_if_fmt_tkip_mic_test( -	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) -{ -	return -EOPNOTSUPP; -} +IEEE80211_IF_FILE_RW(smps);  static ssize_t ieee80211_if_parse_tkip_mic_test(  	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) @@ -343,8 +354,19 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(  	return buflen;  } +IEEE80211_IF_FILE_W(tkip_mic_test); -__IEEE80211_IF_FILE_W(tkip_mic_test); +static ssize_t ieee80211_if_parse_beacon_loss( +	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ +	if (!ieee80211_sdata_running(sdata) || !sdata->vif.bss_conf.assoc) +		return -ENOTCONN; + +	ieee80211_beacon_loss(&sdata->vif); + +	return buflen; +} +IEEE80211_IF_FILE_W(beacon_loss);  static ssize_t ieee80211_if_fmt_uapsd_queues(  	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) @@ -372,7 +394,7 @@ static ssize_t ieee80211_if_parse_uapsd_queues(  	return buflen;  } -__IEEE80211_IF_FILE_W(uapsd_queues); +IEEE80211_IF_FILE_RW(uapsd_queues);  static ssize_t ieee80211_if_fmt_uapsd_max_sp_len(  	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) @@ -400,7 +422,7 @@ static ssize_t ieee80211_if_parse_uapsd_max_sp_len(  	return buflen;  } -__IEEE80211_IF_FILE_W(uapsd_max_sp_len); +IEEE80211_IF_FILE_RW(uapsd_max_sp_len);  /* AP attributes */  IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC); @@ -413,7 +435,7 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(  	return scnprintf(buf, buflen, "%u\n",  			 skb_queue_len(&sdata->u.ap.ps.bc_buf));  } -__IEEE80211_IF_FILE(num_buffered_multicast, NULL); +IEEE80211_IF_FILE_R(num_buffered_multicast);  /* IBSS attributes */  static ssize_t ieee80211_if_fmt_tsf( @@ -462,9 +484,10 @@ static ssize_t ieee80211_if_parse_tsf(  		}  	} +	ieee80211_recalc_dtim(local, sdata);  	return buflen;  } -__IEEE80211_IF_FILE_W(tsf); +IEEE80211_IF_FILE_RW(tsf);  /* WDS attributes */ @@ -556,6 +579,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)  	DEBUGFS_ADD(beacon_timeout);  	DEBUGFS_ADD_MODE(smps, 0600);  	DEBUGFS_ADD_MODE(tkip_mic_test, 0200); +	DEBUGFS_ADD_MODE(beacon_loss, 0200);  	DEBUGFS_ADD_MODE(uapsd_queues, 0600);  	DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);  } @@ -563,6 +587,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)  static void add_ap_files(struct ieee80211_sub_if_data *sdata)  {  	DEBUGFS_ADD(num_mcast_sta); +	DEBUGFS_ADD_MODE(smps, 0600);  	DEBUGFS_ADD(num_sta_ps);  	DEBUGFS_ADD(dtim_count);  	DEBUGFS_ADD(num_buffered_multicast); diff --git a/net/mac80211/debugfs_netdev.h b/net/mac80211/debugfs_netdev.h index 79025e79f4d..9f5501a9a79 100644 --- a/net/mac80211/debugfs_netdev.h +++ b/net/mac80211/debugfs_netdev.h @@ -3,6 +3,8 @@  #ifndef __IEEE80211_DEBUGFS_NETDEV_H  #define __IEEE80211_DEBUGFS_NETDEV_H +#include "ieee80211_i.h" +  #ifdef CONFIG_MAC80211_DEBUGFS  void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata);  void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 19c54a44ed4..2ecb4deddb5 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -38,6 +38,13 @@ static const struct file_operations sta_ ##name## _ops = {		\  	.llseek = generic_file_llseek,					\  } +#define STA_OPS_W(name)							\ +static const struct file_operations sta_ ##name## _ops = {		\ +	.write = sta_##name##_write,					\ +	.open = simple_open,						\ +	.llseek = generic_file_llseek,					\ +} +  #define STA_OPS_RW(name)						\  static const struct file_operations sta_ ##name## _ops = {		\  	.read = sta_##name##_read,					\ @@ -188,7 +195,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,  static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,  				    size_t count, loff_t *ppos)  { -	char _buf[12], *buf = _buf; +	char _buf[12] = {}, *buf = _buf;  	struct sta_info *sta = file->private_data;  	bool start, tx;  	unsigned long tid; @@ -388,6 +395,131 @@ static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,  }  STA_OPS(last_rx_rate); +static int +sta_tx_latency_stat_header(struct ieee80211_tx_latency_bin_ranges *tx_latency, +			   char *buf, int pos, int bufsz) +{ +	int i; +	int range_count = tx_latency->n_ranges; +	u32 *bin_ranges = tx_latency->ranges; + +	pos += scnprintf(buf + pos, bufsz - pos, +			  "Station\t\t\tTID\tMax\tAvg"); +	if (range_count) { +		pos += scnprintf(buf + pos, bufsz - pos, +				  "\t<=%d", bin_ranges[0]); +		for (i = 0; i < range_count - 1; i++) +			pos += scnprintf(buf + pos, bufsz - pos, "\t%d-%d", +					  bin_ranges[i], bin_ranges[i+1]); +		pos += scnprintf(buf + pos, bufsz - pos, +				  "\t%d<", bin_ranges[range_count - 1]); +	} + +	pos += scnprintf(buf + pos, bufsz - pos, "\n"); + +	return pos; +} + +static int +sta_tx_latency_stat_table(struct ieee80211_tx_latency_bin_ranges *tx_lat_range, +			  struct ieee80211_tx_latency_stat *tx_lat, +			  char *buf, int pos, int bufsz, int tid) +{ +	u32 avg = 0; +	int j; +	int bin_count = tx_lat->bin_count; + +	pos += scnprintf(buf + pos, bufsz - pos, "\t\t\t%d", tid); +	/* make sure you don't divide in 0 */ +	if (tx_lat->counter) +		avg = tx_lat->sum / tx_lat->counter; + +	pos += scnprintf(buf + pos, bufsz - pos, "\t%d\t%d", +			  tx_lat->max, avg); + +	if (tx_lat_range->n_ranges && tx_lat->bins) +		for (j = 0; j < bin_count; j++) +			pos += scnprintf(buf + pos, bufsz - pos, +					  "\t%d", tx_lat->bins[j]); +	pos += scnprintf(buf + pos, bufsz - pos, "\n"); + +	return pos; +} + +/* + * Output Tx latency statistics station && restart all statistics information + */ +static ssize_t sta_tx_latency_stat_read(struct file *file, +					char __user *userbuf, +					size_t count, loff_t *ppos) +{ +	struct sta_info *sta = file->private_data; +	struct ieee80211_local *local = sta->local; +	struct ieee80211_tx_latency_bin_ranges *tx_latency; +	char *buf; +	int bufsz, ret, i; +	int pos = 0; + +	bufsz = 20 * IEEE80211_NUM_TIDS * +		sizeof(struct ieee80211_tx_latency_stat); +	buf = kzalloc(bufsz, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	rcu_read_lock(); + +	tx_latency = rcu_dereference(local->tx_latency); + +	if (!sta->tx_lat) { +		pos += scnprintf(buf + pos, bufsz - pos, +				 "Tx latency statistics are not enabled\n"); +		goto unlock; +	} + +	pos = sta_tx_latency_stat_header(tx_latency, buf, pos, bufsz); + +	pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", sta->sta.addr); +	for (i = 0; i < IEEE80211_NUM_TIDS; i++) +		pos = sta_tx_latency_stat_table(tx_latency, &sta->tx_lat[i], +						buf, pos, bufsz, i); +unlock: +	rcu_read_unlock(); + +	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); +	kfree(buf); + +	return ret; +} +STA_OPS(tx_latency_stat); + +static ssize_t sta_tx_latency_stat_reset_write(struct file *file, +					       const char __user *userbuf, +					       size_t count, loff_t *ppos) +{ +	u32 *bins; +	int bin_count; +	struct sta_info *sta = file->private_data; +	int i; + +	if (!sta->tx_lat) +		return -EINVAL; + +	for (i = 0; i < IEEE80211_NUM_TIDS; i++) { +		bins = sta->tx_lat[i].bins; +		bin_count = sta->tx_lat[i].bin_count; + +		sta->tx_lat[i].max = 0; +		sta->tx_lat[i].sum = 0; +		sta->tx_lat[i].counter = 0; + +		if (bin_count) +			memset(bins, 0, bin_count * sizeof(u32)); +	} + +	return count; +} +STA_OPS_W(tx_latency_stat_reset); +  #define DEBUGFS_ADD(name) \  	debugfs_create_file(#name, 0400, \  		sta->debugfs.dir, sta, &sta_ ##name## _ops); @@ -441,6 +573,8 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)  	DEBUGFS_ADD(last_ack_signal);  	DEBUGFS_ADD(current_tx_rate);  	DEBUGFS_ADD(last_rx_rate); +	DEBUGFS_ADD(tx_latency_stat); +	DEBUGFS_ADD(tx_latency_stat_reset);  	DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);  	DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index b3ea11f3d52..bd782dcffcc 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -5,11 +5,11 @@  #include "ieee80211_i.h"  #include "trace.h" -static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) +static inline bool check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)  { -	WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), -	     "%s:  Failed check-sdata-in-driver check, flags: 0x%x\n", -	     sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); +	return !WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), +		     "%s:  Failed check-sdata-in-driver check, flags: 0x%x\n", +		     sdata->dev ? sdata->dev->name : sdata->name, sdata->flags);  }  static inline struct ieee80211_sub_if_data * @@ -168,7 +168,8 @@ static inline int drv_change_interface(struct ieee80211_local *local,  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_change_interface(local, sdata, type, p2p);  	ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); @@ -181,7 +182,8 @@ static inline void drv_remove_interface(struct ieee80211_local *local,  {  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_remove_interface(local, sdata);  	local->ops->remove_interface(&local->hw, &sdata->vif); @@ -219,7 +221,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,  			 sdata->vif.type == NL80211_IFTYPE_MONITOR))  		return; -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_bss_info_changed(local, sdata, info, changed);  	if (local->ops->bss_info_changed) @@ -242,22 +245,6 @@ static inline u64 drv_prepare_multicast(struct ieee80211_local *local,  	return ret;  } -static inline void drv_set_multicast_list(struct ieee80211_local *local, -					  struct ieee80211_sub_if_data *sdata, -					  struct netdev_hw_addr_list *mc_list) -{ -	bool allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI; - -	trace_drv_set_multicast_list(local, sdata, mc_list->count); - -	check_sdata_in_driver(sdata); - -	if (local->ops->set_multicast_list) -		local->ops->set_multicast_list(&local->hw, &sdata->vif, -					       allmulti, mc_list); -	trace_drv_return_void(local); -} -  static inline void drv_configure_filter(struct ieee80211_local *local,  					unsigned int changed_flags,  					unsigned int *total_flags, @@ -294,7 +281,8 @@ static inline int drv_set_key(struct ieee80211_local *local,  	might_sleep();  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_set_key(local, cmd, sdata, sta, key);  	ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); @@ -314,7 +302,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,  		ista = &sta->sta;  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_update_tkip_key(local, sdata, conf, ista, iv32);  	if (local->ops->update_tkip_key) @@ -331,7 +320,8 @@ static inline int drv_hw_scan(struct ieee80211_local *local,  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_hw_scan(local, sdata);  	ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); @@ -344,7 +334,8 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local,  {  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_cancel_hw_scan(local, sdata);  	local->ops->cancel_hw_scan(&local->hw, &sdata->vif); @@ -361,7 +352,8 @@ drv_sched_scan_start(struct ieee80211_local *local,  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_sched_scan_start(local, sdata);  	ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, @@ -370,16 +362,21 @@ drv_sched_scan_start(struct ieee80211_local *local,  	return ret;  } -static inline void drv_sched_scan_stop(struct ieee80211_local *local, -				       struct ieee80211_sub_if_data *sdata) +static inline int drv_sched_scan_stop(struct ieee80211_local *local, +				      struct ieee80211_sub_if_data *sdata)  { +	int ret; +  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_sched_scan_stop(local, sdata); -	local->ops->sched_scan_stop(&local->hw, &sdata->vif); -	trace_drv_return_void(local); +	ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif); +	trace_drv_return_int(local, ret); + +	return ret;  }  static inline void drv_sw_scan_start(struct ieee80211_local *local) @@ -474,7 +471,8 @@ static inline void drv_sta_notify(struct ieee80211_local *local,  				  struct ieee80211_sta *sta)  {  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_sta_notify(local, sdata, cmd, sta);  	if (local->ops->sta_notify) @@ -491,7 +489,8 @@ static inline int drv_sta_add(struct ieee80211_local *local,  	might_sleep();  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_sta_add(local, sdata, sta);  	if (local->ops->sta_add) @@ -509,7 +508,8 @@ static inline void drv_sta_remove(struct ieee80211_local *local,  	might_sleep();  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_sta_remove(local, sdata, sta);  	if (local->ops->sta_remove) @@ -527,7 +527,8 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local,  	might_sleep();  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	if (local->ops->sta_add_debugfs)  		local->ops->sta_add_debugfs(&local->hw, &sdata->vif, @@ -550,6 +551,23 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,  }  #endif +static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, +					  struct ieee80211_sub_if_data *sdata, +					  struct sta_info *sta) +{ +	might_sleep(); + +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta); +	if (local->ops->sta_pre_rcu_remove) +		local->ops->sta_pre_rcu_remove(&local->hw, &sdata->vif, +					       &sta->sta); +	trace_drv_return_void(local); +} +  static inline __must_check  int drv_sta_state(struct ieee80211_local *local,  		  struct ieee80211_sub_if_data *sdata, @@ -562,7 +580,8 @@ int drv_sta_state(struct ieee80211_local *local,  	might_sleep();  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);  	if (local->ops->sta_state) { @@ -586,7 +605,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local,  				     struct ieee80211_sta *sta, u32 changed)  {  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&  		(sdata->vif.type != NL80211_IFTYPE_ADHOC && @@ -608,7 +628,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local,  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_conf_tx(local, sdata, ac, params);  	if (local->ops->conf_tx) @@ -625,7 +646,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local,  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return ret;  	trace_drv_get_tsf(local, sdata);  	if (local->ops->get_tsf) @@ -640,7 +662,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local,  {  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_set_tsf(local, sdata, tsf);  	if (local->ops->set_tsf) @@ -653,7 +676,8 @@ static inline void drv_reset_tsf(struct ieee80211_local *local,  {  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_reset_tsf(local, sdata);  	if (local->ops->reset_tsf) @@ -685,7 +709,8 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,  	might_sleep();  	sdata = get_bss_sdata(sdata); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); @@ -722,13 +747,19 @@ static inline void drv_rfkill_poll(struct ieee80211_local *local)  }  static inline void drv_flush(struct ieee80211_local *local, +			     struct ieee80211_sub_if_data *sdata,  			     u32 queues, bool drop)  { +	struct ieee80211_vif *vif = sdata ? &sdata->vif : NULL; +  	might_sleep(); +	if (sdata && !check_sdata_in_driver(sdata)) +		return; +  	trace_drv_flush(local, queues, drop);  	if (local->ops->flush) -		local->ops->flush(&local->hw, queues, drop); +		local->ops->flush(&local->hw, vif, queues, drop);  	trace_drv_return_void(local);  } @@ -844,7 +875,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_set_bitrate_mask(local, sdata, mask);  	if (local->ops->set_bitrate_mask) @@ -859,7 +891,8 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,  				      struct ieee80211_sub_if_data *sdata,  				      struct cfg80211_gtk_rekey_data *data)  { -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_set_rekey_data(local, sdata, data);  	if (local->ops->set_rekey_data) @@ -927,7 +960,8 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,  {  	might_sleep(); -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);  	trace_drv_mgd_prepare_tx(local, sdata); @@ -954,6 +988,9 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,  static inline void drv_remove_chanctx(struct ieee80211_local *local,  				      struct ieee80211_chanctx *ctx)  { +	if (WARN_ON(!ctx->driver_present)) +		return; +  	trace_drv_remove_chanctx(local, ctx);  	if (local->ops->remove_chanctx)  		local->ops->remove_chanctx(&local->hw, &ctx->conf); @@ -979,7 +1016,8 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local,  {  	int ret = 0; -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_assign_vif_chanctx(local, sdata, ctx);  	if (local->ops->assign_vif_chanctx) { @@ -997,7 +1035,8 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,  					    struct ieee80211_sub_if_data *sdata,  					    struct ieee80211_chanctx *ctx)  { -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_unassign_vif_chanctx(local, sdata, ctx);  	if (local->ops->unassign_vif_chanctx) { @@ -1009,12 +1048,66 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,  	trace_drv_return_void(local);  } +static inline int +drv_switch_vif_chanctx(struct ieee80211_local *local, +		       struct ieee80211_vif_chanctx_switch *vifs, +		       int n_vifs, +		       enum ieee80211_chanctx_switch_mode mode) +{ +	int ret = 0; +	int i; + +	if (!local->ops->switch_vif_chanctx) +		return -EOPNOTSUPP; + +	for (i = 0; i < n_vifs; i++) { +		struct ieee80211_chanctx *new_ctx = +			container_of(vifs[i].new_ctx, +				     struct ieee80211_chanctx, +				     conf); +		struct ieee80211_chanctx *old_ctx = +			container_of(vifs[i].old_ctx, +				     struct ieee80211_chanctx, +				     conf); + +		WARN_ON_ONCE(!old_ctx->driver_present); +		WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && +			      new_ctx->driver_present) || +			     (mode == CHANCTX_SWMODE_REASSIGN_VIF && +			      !new_ctx->driver_present)); +	} + +	trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); +	ret = local->ops->switch_vif_chanctx(&local->hw, +					     vifs, n_vifs, mode); +	trace_drv_return_int(local, ret); + +	if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { +		for (i = 0; i < n_vifs; i++) { +			struct ieee80211_chanctx *new_ctx = +				container_of(vifs[i].new_ctx, +					     struct ieee80211_chanctx, +					     conf); +			struct ieee80211_chanctx *old_ctx = +				container_of(vifs[i].old_ctx, +					     struct ieee80211_chanctx, +					     conf); + +			new_ctx->driver_present = true; +			old_ctx->driver_present = false; +		} +	} + +	return ret; +} +  static inline int drv_start_ap(struct ieee80211_local *local,  			       struct ieee80211_sub_if_data *sdata)  {  	int ret = 0; -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO;  	trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);  	if (local->ops->start_ap) @@ -1026,7 +1119,8 @@ static inline int drv_start_ap(struct ieee80211_local *local,  static inline void drv_stop_ap(struct ieee80211_local *local,  			       struct ieee80211_sub_if_data *sdata)  { -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	trace_drv_stop_ap(local, sdata);  	if (local->ops->stop_ap) @@ -1049,7 +1143,8 @@ drv_set_default_unicast_key(struct ieee80211_local *local,  			    struct ieee80211_sub_if_data *sdata,  			    int key_idx)  { -	check_sdata_in_driver(sdata); +	if (!check_sdata_in_driver(sdata)) +		return;  	WARN_ON_ONCE(key_idx < -1 || key_idx > 3); @@ -1085,4 +1180,46 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,  	}  } +static inline int drv_join_ibss(struct ieee80211_local *local, +				struct ieee80211_sub_if_data *sdata) +{ +	int ret = 0; + +	might_sleep(); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf); +	if (local->ops->join_ibss) +		ret = local->ops->join_ibss(&local->hw, &sdata->vif); +	trace_drv_return_int(local, ret); +	return ret; +} + +static inline void drv_leave_ibss(struct ieee80211_local *local, +				  struct ieee80211_sub_if_data *sdata) +{ +	might_sleep(); +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_leave_ibss(local, sdata); +	if (local->ops->leave_ibss) +		local->ops->leave_ibss(&local->hw, &sdata->vif); +	trace_drv_return_void(local); +} + +static inline u32 drv_get_expected_throughput(struct ieee80211_local *local, +					      struct ieee80211_sta *sta) +{ +	u32 ret = 0; + +	trace_drv_get_expected_throughput(sta); +	if (local->ops->get_expected_throughput) +		ret = local->ops->get_expected_throughput(sta); +	trace_drv_return_u32(local, ret); + +	return ret; +} +  #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 529bf58bc14..15702ff64a4 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -31,6 +31,18 @@ static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,  	}  } +static void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa, +				  struct ieee80211_ht_cap *ht_capa_mask, +				  struct ieee80211_sta_ht_cap *ht_cap, +				  u16 flag) +{ +	__le16 le_flag = cpu_to_le16(flag); + +	if ((ht_capa_mask->cap_info & le_flag) && +	    (ht_capa->cap_info & le_flag)) +		ht_cap->cap |= flag; +} +  void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,  				     struct ieee80211_sta_ht_cap *ht_cap)  { @@ -59,7 +71,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,  	smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);  	/* NOTE:  If you add more over-rides here, update register_hw -	 * ht_capa_mod_msk logic in main.c as well. +	 * ht_capa_mod_mask logic in main.c as well.  	 * And, if this method can ever change ht_cap.ht_supported, fix  	 * the check in ieee80211_add_ht_ie.  	 */ @@ -86,6 +98,14 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,  	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,  			      IEEE80211_HT_CAP_MAX_AMSDU); +	/* Allow user to disable LDPC */ +	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, +			      IEEE80211_HT_CAP_LDPC_CODING); + +	/* Allow user to enable 40 MHz intolerant bit. */ +	__check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, +			     IEEE80211_HT_CAP_40MHZ_INTOLERANT); +  	/* Allow user to decrease AMPDU factor */  	if (ht_capa_mask->ampdu_params_info &  	    IEEE80211_HT_AMPDU_PARM_FACTOR) { @@ -375,7 +395,7 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,  	mgmt->u.action.u.delba.params = cpu_to_le16(params);  	mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); -	ieee80211_tx_skb_tid(sdata, skb, tid); +	ieee80211_tx_skb(sdata, skb);  }  void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, @@ -448,14 +468,27 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,  	return 0;  } -void ieee80211_request_smps_work(struct work_struct *work) +void ieee80211_request_smps_mgd_work(struct work_struct *work)  {  	struct ieee80211_sub_if_data *sdata =  		container_of(work, struct ieee80211_sub_if_data,  			     u.mgd.request_smps_work);  	sdata_lock(sdata); -	__ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode); +	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode); +	sdata_unlock(sdata); +} + +void ieee80211_request_smps_ap_work(struct work_struct *work) +{ +	struct ieee80211_sub_if_data *sdata = +		container_of(work, struct ieee80211_sub_if_data, +			     u.ap.request_smps_work); + +	sdata_lock(sdata); +	if (sdata_dereference(sdata->u.ap.beacon, sdata)) +		__ieee80211_request_smps_ap(sdata, +					    sdata->u.ap.driver_smps_mode);  	sdata_unlock(sdata);  } @@ -464,19 +497,26 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,  {  	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); -	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) -		return; - -	if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) -		smps_mode = IEEE80211_SMPS_AUTOMATIC; - -	if (sdata->u.mgd.driver_smps_mode == smps_mode) +	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION && +			 vif->type != NL80211_IFTYPE_AP))  		return; -	sdata->u.mgd.driver_smps_mode = smps_mode; - -	ieee80211_queue_work(&sdata->local->hw, -			     &sdata->u.mgd.request_smps_work); +	if (vif->type == NL80211_IFTYPE_STATION) { +		if (sdata->u.mgd.driver_smps_mode == smps_mode) +			return; +		sdata->u.mgd.driver_smps_mode = smps_mode; +		ieee80211_queue_work(&sdata->local->hw, +				     &sdata->u.mgd.request_smps_work); +	} else { +		/* AUTOMATIC is meaningless in AP mode */ +		if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC)) +			return; +		if (sdata->u.ap.driver_smps_mode == smps_mode) +			return; +		sdata->u.ap.driver_smps_mode = smps_mode; +		ieee80211_queue_work(&sdata->local->hw, +				     &sdata->u.ap.request_smps_work); +	}  }  /* this might change ... don't want non-open drivers using it */  EXPORT_SYMBOL_GPL(ieee80211_request_smps); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index a12afe77bb2..18ee0a256b1 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -39,7 +39,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,  			   const int beacon_int, const u32 basic_rates,  			   const u16 capability, u64 tsf,  			   struct cfg80211_chan_def *chandef, -			   bool *have_higher_than_11mbit) +			   bool *have_higher_than_11mbit, +			   struct cfg80211_csa_settings *csa_settings)  {  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  	struct ieee80211_local *local = sdata->local; @@ -59,6 +60,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,  		    2 + 8 /* max Supported Rates */ +  		    3 /* max DS params */ +  		    4 /* IBSS params */ + +		    5 /* Channel Switch Announcement */ +  		    2 + (IEEE80211_MAX_SUPP_RATES - 8) +  		    2 + sizeof(struct ieee80211_ht_cap) +  		    2 + sizeof(struct ieee80211_ht_operation) + @@ -135,6 +137,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,  	*pos++ = 0;  	*pos++ = 0; +	if (csa_settings) { +		*pos++ = WLAN_EID_CHANNEL_SWITCH; +		*pos++ = 3; +		*pos++ = csa_settings->block_tx ? 1 : 0; +		*pos++ = ieee80211_frequency_to_channel( +				csa_settings->chandef.chan->center_freq); +		sdata->csa_counter_offset_beacon[0] = (pos - presp->head); +		*pos++ = csa_settings->count; +	} +  	/* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */  	if (rates_n > 8) {  		*pos++ = WLAN_EID_EXT_SUPP_RATES; @@ -208,7 +220,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  {  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  	struct ieee80211_local *local = sdata->local; -	struct ieee80211_supported_band *sband;  	struct ieee80211_mgmt *mgmt;  	struct cfg80211_bss *bss;  	u32 bss_change; @@ -217,6 +228,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	struct beacon_data *presp;  	enum nl80211_bss_scan_width scan_width;  	bool have_higher_than_11mbit; +	bool radar_required; +	int err;  	sdata_assert_lock(sdata); @@ -235,11 +248,12 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  		ieee80211_bss_info_change_notify(sdata,  						 BSS_CHANGED_IBSS |  						 BSS_CHANGED_BEACON_ENABLED); +		drv_leave_ibss(local, sdata);  	}  	presp = rcu_dereference_protected(ifibss->presp,  					  lockdep_is_held(&sdata->wdev.mtx)); -	rcu_assign_pointer(ifibss->presp, NULL); +	RCU_INIT_POINTER(ifibss->presp, NULL);  	if (presp)  		kfree_rcu(presp, rcu_head); @@ -248,7 +262,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	/* make a copy of the chandef, it could be modified below. */  	chandef = *req_chandef;  	chan = chandef.chan; -	if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { +	if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef, +				     NL80211_IFTYPE_ADHOC)) {  		if (chandef.width == NL80211_CHAN_WIDTH_5 ||  		    chandef.width == NL80211_CHAN_WIDTH_10 ||  		    chandef.width == NL80211_CHAN_WIDTH_20_NOHT || @@ -259,24 +274,47 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  		}  		chandef.width = NL80211_CHAN_WIDTH_20;  		chandef.center_freq1 = chan->center_freq; +		/* check again for downgraded chandef */ +		if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef, +					     NL80211_IFTYPE_ADHOC)) { +			sdata_info(sdata, +				   "Failed to join IBSS, beacons forbidden\n"); +			return; +		}  	} -	ieee80211_vif_release_channel(sdata); +	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, +					    &chandef, NL80211_IFTYPE_ADHOC); +	if (err < 0) { +		sdata_info(sdata, +			   "Failed to join IBSS, invalid chandef\n"); +		return; +	} +	if (err > 0 && !ifibss->userspace_handles_dfs) { +		sdata_info(sdata, +			   "Failed to join IBSS, DFS channel without control program\n"); +		return; +	} + +	radar_required = err; + +	mutex_lock(&local->mtx);  	if (ieee80211_vif_use_channel(sdata, &chandef,  				      ifibss->fixed_channel ?  					IEEE80211_CHANCTX_SHARED :  					IEEE80211_CHANCTX_EXCLUSIVE)) {  		sdata_info(sdata, "Failed to join IBSS, no channel context\n"); +		mutex_unlock(&local->mtx);  		return;  	} +	sdata->radar_required = radar_required; +	mutex_unlock(&local->mtx);  	memcpy(ifibss->bssid, bssid, ETH_ALEN); -	sband = local->hw.wiphy->bands[chan->band]; -  	presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,  					   capability, tsf, &chandef, -					   &have_higher_than_11mbit); +					   &have_higher_than_11mbit, NULL);  	if (!presp)  		return; @@ -317,11 +355,28 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	else  		sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; +	ieee80211_set_wmm_default(sdata, true); +  	sdata->vif.bss_conf.ibss_joined = true;  	sdata->vif.bss_conf.ibss_creator = creator; -	ieee80211_bss_info_change_notify(sdata, bss_change); -	ieee80211_set_wmm_default(sdata, true); +	err = drv_join_ibss(local, sdata); +	if (err) { +		sdata->vif.bss_conf.ibss_joined = false; +		sdata->vif.bss_conf.ibss_creator = false; +		sdata->vif.bss_conf.enable_beacon = false; +		sdata->vif.bss_conf.ssid_len = 0; +		RCU_INIT_POINTER(ifibss->presp, NULL); +		kfree_rcu(presp, rcu_head); +		mutex_lock(&local->mtx); +		ieee80211_vif_release_channel(sdata); +		mutex_unlock(&local->mtx); +		sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n", +			   err); +		return; +	} + +	ieee80211_bss_info_change_notify(sdata, bss_change);  	ifibss->state = IEEE80211_IBSS_MLME_JOINED;  	mod_timer(&ifibss->timer, @@ -333,7 +388,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  					      presp->head_len, 0, GFP_KERNEL);  	cfg80211_put_bss(local->hw.wiphy, bss);  	netif_carrier_on(sdata->dev); -	cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); +	cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL);  }  static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, @@ -416,6 +471,111 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  				  tsf, false);  } +int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct beacon_data *presp, *old_presp; +	struct cfg80211_bss *cbss; +	const struct cfg80211_bss_ies *ies; +	u16 capability; +	u64 tsf; +	int ret = 0; + +	sdata_assert_lock(sdata); + +	capability = WLAN_CAPABILITY_IBSS; + +	if (ifibss->privacy) +		capability |= WLAN_CAPABILITY_PRIVACY; + +	cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, +				ifibss->bssid, ifibss->ssid, +				ifibss->ssid_len, WLAN_CAPABILITY_IBSS | +				WLAN_CAPABILITY_PRIVACY, +				capability); + +	if (WARN_ON(!cbss)) { +		ret = -EINVAL; +		goto out; +	} + +	rcu_read_lock(); +	ies = rcu_dereference(cbss->ies); +	tsf = ies->tsf; +	rcu_read_unlock(); +	cfg80211_put_bss(sdata->local->hw.wiphy, cbss); + +	old_presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&sdata->wdev.mtx)); + +	presp = ieee80211_ibss_build_presp(sdata, +					   sdata->vif.bss_conf.beacon_int, +					   sdata->vif.bss_conf.basic_rates, +					   capability, tsf, &ifibss->chandef, +					   NULL, csa_settings); +	if (!presp) { +		ret = -ENOMEM; +		goto out; +	} + +	rcu_assign_pointer(ifibss->presp, presp); +	if (old_presp) +		kfree_rcu(old_presp, rcu_head); + +	return BSS_CHANGED_BEACON; + out: +	return ret; +} + +int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct cfg80211_bss *cbss; +	int err, changed = 0; +	u16 capability; + +	sdata_assert_lock(sdata); + +	/* update cfg80211 bss information with the new channel */ +	if (!is_zero_ether_addr(ifibss->bssid)) { +		capability = WLAN_CAPABILITY_IBSS; + +		if (ifibss->privacy) +			capability |= WLAN_CAPABILITY_PRIVACY; + +		cbss = cfg80211_get_bss(sdata->local->hw.wiphy, +					ifibss->chandef.chan, +					ifibss->bssid, ifibss->ssid, +					ifibss->ssid_len, WLAN_CAPABILITY_IBSS | +					WLAN_CAPABILITY_PRIVACY, +					capability); +		/* XXX: should not really modify cfg80211 data */ +		if (cbss) { +			cbss->channel = sdata->csa_chandef.chan; +			cfg80211_put_bss(sdata->local->hw.wiphy, cbss); +		} +	} + +	ifibss->chandef = sdata->csa_chandef; + +	/* generate the beacon */ +	err = ieee80211_ibss_csa_beacon(sdata, NULL); +	if (err < 0) +		return err; + +	changed |= err; + +	return changed; +} + +void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + +	cancel_work_sync(&ifibss->csa_connection_drop_work); +} +  static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)  	__acquires(RCU)  { @@ -499,6 +659,290 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,  	return ieee80211_ibss_finish_sta(sta);  } +static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	int active = 0; +	struct sta_info *sta; + +	sdata_assert_lock(sdata); + +	rcu_read_lock(); + +	list_for_each_entry_rcu(sta, &local->sta_list, list) { +		if (sta->sdata == sdata && +		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, +			       jiffies)) { +			active++; +			break; +		} +	} + +	rcu_read_unlock(); + +	return active; +} + +static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct ieee80211_local *local = sdata->local; +	struct cfg80211_bss *cbss; +	struct beacon_data *presp; +	struct sta_info *sta; +	u16 capability; + +	if (!is_zero_ether_addr(ifibss->bssid)) { +		capability = WLAN_CAPABILITY_IBSS; + +		if (ifibss->privacy) +			capability |= WLAN_CAPABILITY_PRIVACY; + +		cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, +					ifibss->bssid, ifibss->ssid, +					ifibss->ssid_len, WLAN_CAPABILITY_IBSS | +					WLAN_CAPABILITY_PRIVACY, +					capability); + +		if (cbss) { +			cfg80211_unlink_bss(local->hw.wiphy, cbss); +			cfg80211_put_bss(sdata->local->hw.wiphy, cbss); +		} +	} + +	ifibss->state = IEEE80211_IBSS_MLME_SEARCH; + +	sta_info_flush(sdata); + +	spin_lock_bh(&ifibss->incomplete_lock); +	while (!list_empty(&ifibss->incomplete_stations)) { +		sta = list_first_entry(&ifibss->incomplete_stations, +				       struct sta_info, list); +		list_del(&sta->list); +		spin_unlock_bh(&ifibss->incomplete_lock); + +		sta_info_free(local, sta); +		spin_lock_bh(&ifibss->incomplete_lock); +	} +	spin_unlock_bh(&ifibss->incomplete_lock); + +	netif_carrier_off(sdata->dev); + +	sdata->vif.bss_conf.ibss_joined = false; +	sdata->vif.bss_conf.ibss_creator = false; +	sdata->vif.bss_conf.enable_beacon = false; +	sdata->vif.bss_conf.ssid_len = 0; + +	/* remove beacon */ +	presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&sdata->wdev.mtx)); +	RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); +	if (presp) +		kfree_rcu(presp, rcu_head); + +	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); +	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | +						BSS_CHANGED_IBSS); +	drv_leave_ibss(local, sdata); +	mutex_lock(&local->mtx); +	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&local->mtx); +} + +static void ieee80211_csa_connection_drop_work(struct work_struct *work) +{ +	struct ieee80211_sub_if_data *sdata = +		container_of(work, struct ieee80211_sub_if_data, +			     u.ibss.csa_connection_drop_work); + +	sdata_lock(sdata); + +	ieee80211_ibss_disconnect(sdata); +	synchronize_rcu(); +	skb_queue_purge(&sdata->skb_queue); + +	/* trigger a scan to find another IBSS network to join */ +	ieee80211_queue_work(&sdata->local->hw, &sdata->work); + +	sdata_unlock(sdata); +} + +static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	int err; + +	/* if the current channel is a DFS channel, mark the channel as +	 * unavailable. +	 */ +	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, +					    &ifibss->chandef, +					    NL80211_IFTYPE_ADHOC); +	if (err > 0) +		cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef, +				     GFP_ATOMIC); +} + +static bool +ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, +				  struct ieee802_11_elems *elems, +				  bool beacon) +{ +	struct cfg80211_csa_settings params; +	struct ieee80211_csa_ie csa_ie; +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	enum nl80211_channel_type ch_type; +	int err; +	u32 sta_flags; + +	sdata_assert_lock(sdata); + +	sta_flags = IEEE80211_STA_DISABLE_VHT; +	switch (ifibss->chandef.width) { +	case NL80211_CHAN_WIDTH_5: +	case NL80211_CHAN_WIDTH_10: +	case NL80211_CHAN_WIDTH_20_NOHT: +		sta_flags |= IEEE80211_STA_DISABLE_HT; +		/* fall through */ +	case NL80211_CHAN_WIDTH_20: +		sta_flags |= IEEE80211_STA_DISABLE_40MHZ; +		break; +	default: +		break; +	} + +	memset(¶ms, 0, sizeof(params)); +	memset(&csa_ie, 0, sizeof(csa_ie)); +	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, +					   ifibss->chandef.chan->band, +					   sta_flags, ifibss->bssid, &csa_ie); +	/* can't switch to destination channel, fail */ +	if (err < 0) +		goto disconnect; + +	/* did not contain a CSA */ +	if (err) +		return false; + +	/* channel switch is not supported, disconnect */ +	if (!(sdata->local->hw.wiphy->flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) +		goto disconnect; + +	params.count = csa_ie.count; +	params.chandef = csa_ie.chandef; + +	switch (ifibss->chandef.width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +	case NL80211_CHAN_WIDTH_40: +		/* keep our current HT mode (HT20/HT40+/HT40-), even if +		 * another mode  has been announced. The mode is not adopted +		 * within the beacon while doing CSA and we should therefore +		 * keep the mode which we announce. +		 */ +		ch_type = cfg80211_get_chandef_type(&ifibss->chandef); +		cfg80211_chandef_create(¶ms.chandef, params.chandef.chan, +					ch_type); +		break; +	case NL80211_CHAN_WIDTH_5: +	case NL80211_CHAN_WIDTH_10: +		if (params.chandef.width != ifibss->chandef.width) { +			sdata_info(sdata, +				   "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", +				   ifibss->bssid, +				   params.chandef.chan->center_freq, +				   params.chandef.width, +				   params.chandef.center_freq1, +				   params.chandef.center_freq2); +			goto disconnect; +		} +		break; +	default: +		/* should not happen, sta_flags should prevent VHT modes. */ +		WARN_ON(1); +		goto disconnect; +	} + +	if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef, +				     NL80211_IFTYPE_ADHOC)) { +		sdata_info(sdata, +			   "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", +			   ifibss->bssid, +			   params.chandef.chan->center_freq, +			   params.chandef.width, +			   params.chandef.center_freq1, +			   params.chandef.center_freq2); +		goto disconnect; +	} + +	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, +					    ¶ms.chandef, +					    NL80211_IFTYPE_ADHOC); +	if (err < 0) +		goto disconnect; +	if (err > 0 && !ifibss->userspace_handles_dfs) { +		/* IBSS-DFS only allowed with a control program */ +		goto disconnect; +	} + +	params.radar_required = err; + +	if (cfg80211_chandef_identical(¶ms.chandef, +				       &sdata->vif.bss_conf.chandef)) { +		ibss_dbg(sdata, +			 "received csa with an identical chandef, ignoring\n"); +		return true; +	} + +	/* all checks done, now perform the channel switch. */ +	ibss_dbg(sdata, +		 "received channel switch announcement to go to channel %d MHz\n", +		 params.chandef.chan->center_freq); + +	params.block_tx = !!csa_ie.mode; + +	if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev, +				     ¶ms)) +		goto disconnect; + +	ieee80211_ibss_csa_mark_radar(sdata); + +	return true; +disconnect: +	ibss_dbg(sdata, "Can't handle channel switch, disconnect\n"); +	ieee80211_queue_work(&sdata->local->hw, +			     &ifibss->csa_connection_drop_work); + +	ieee80211_ibss_csa_mark_radar(sdata); + +	return true; +} + +static void +ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata, +				struct ieee80211_mgmt *mgmt, size_t len, +				struct ieee80211_rx_status *rx_status, +				struct ieee802_11_elems *elems) +{ +	int required_len; + +	if (len < IEEE80211_MIN_ACTION_SIZE + 1) +		return; + +	/* CSA is the only action we handle for now */ +	if (mgmt->u.action.u.measurement.action_code != +	    WLAN_ACTION_SPCT_CHL_SWITCH) +		return; + +	required_len = IEEE80211_MIN_ACTION_SIZE + +		       sizeof(mgmt->u.action.u.chan_switch); +	if (len < required_len) +		return; + +	if (!sdata->vif.csa_active) +		ieee80211_ibss_process_chanswitch(sdata, elems, false); +} +  static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,  					  struct ieee80211_mgmt *mgmt,  					  size_t len) @@ -550,7 +994,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  				  struct ieee802_11_elems *elems)  {  	struct ieee80211_local *local = sdata->local; -	int freq;  	struct cfg80211_bss *cbss;  	struct ieee80211_bss *bss;  	struct sta_info *sta; @@ -562,15 +1005,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];  	bool rates_updated = false; -	if (elems->ds_params) -		freq = ieee80211_channel_to_frequency(elems->ds_params[0], -						      band); -	else -		freq = rx_status->freq; - -	channel = ieee80211_get_channel(local->hw.wiphy, freq); - -	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) +	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); +	if (!channel)  		return;  	if (sdata->vif.type == NL80211_IFTYPE_ADHOC && @@ -661,10 +1097,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  	/* check if we need to merge IBSS */ -	/* we use a fixed BSSID */ -	if (sdata->u.ibss.fixed_bssid) -		goto put_bss; -  	/* not an IBSS */  	if (!(cbss->capability & WLAN_CAPABILITY_IBSS))  		goto put_bss; @@ -680,10 +1112,19 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  				sdata->u.ibss.ssid_len))  		goto put_bss; +	/* process channel switch */ +	if (sdata->vif.csa_active || +	    ieee80211_ibss_process_chanswitch(sdata, elems, true)) +		goto put_bss; +  	/* same BSSID */  	if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))  		goto put_bss; +	/* we use a fixed BSSID */ +	if (sdata->u.ibss.fixed_bssid) +		goto put_bss; +  	if (ieee80211_have_rx_timestamp(rx_status)) {  		/* time when timestamp field was received */  		rx_timestamp = @@ -775,30 +1216,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,  	ieee80211_queue_work(&local->hw, &sdata->work);  } -static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_local *local = sdata->local; -	int active = 0; -	struct sta_info *sta; - -	sdata_assert_lock(sdata); - -	rcu_read_lock(); - -	list_for_each_entry_rcu(sta, &local->sta_list, list) { -		if (sta->sdata == sdata && -		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, -			       jiffies)) { -			active++; -			break; -		} -	} - -	rcu_read_unlock(); - -	return active; -} -  static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_local *local = sdata->local; @@ -1042,6 +1459,11 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,  	memcpy(((struct ieee80211_mgmt *) skb->data)->da, mgmt->sa, ETH_ALEN);  	ibss_dbg(sdata, "Sending ProbeResp to %pM\n", mgmt->sa);  	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + +	/* avoid excessive retries for probe request to wildcard SSIDs */ +	if (pos[1] == 0) +		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NO_ACK; +  	ieee80211_tx_skb(sdata, skb);  } @@ -1076,6 +1498,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_rx_status *rx_status;  	struct ieee80211_mgmt *mgmt;  	u16 fc; +	struct ieee802_11_elems elems; +	int ies_len;  	rx_status = IEEE80211_SKB_RXCB(skb);  	mgmt = (struct ieee80211_mgmt *) skb->data; @@ -1101,6 +1525,27 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  	case IEEE80211_STYPE_DEAUTH:  		ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);  		break; +	case IEEE80211_STYPE_ACTION: +		switch (mgmt->u.action.category) { +		case WLAN_CATEGORY_SPECTRUM_MGMT: +			ies_len = skb->len - +				  offsetof(struct ieee80211_mgmt, +					   u.action.u.chan_switch.variable); + +			if (ies_len < 0) +				break; + +			ieee802_11_parse_elems( +				mgmt->u.action.u.chan_switch.variable, +				ies_len, true, &elems); + +			if (elems.parse_error) +				break; + +			ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len, +							rx_status, &elems); +			break; +		}  	}   mgmt_out: @@ -1167,6 +1612,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)  		    (unsigned long) sdata);  	INIT_LIST_HEAD(&ifibss->incomplete_stations);  	spin_lock_init(&ifibss->incomplete_lock); +	INIT_WORK(&ifibss->csa_connection_drop_work, +		  ieee80211_csa_connection_drop_work);  }  /* scan finished notification */ @@ -1192,7 +1639,33 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  	u32 changed = 0;  	u32 rate_flags;  	struct ieee80211_supported_band *sband; +	enum ieee80211_chanctx_mode chanmode; +	struct ieee80211_local *local = sdata->local; +	int radar_detect_width = 0;  	int i; +	int ret; + +	ret = cfg80211_chandef_dfs_required(local->hw.wiphy, +					    ¶ms->chandef, +					    sdata->wdev.iftype); +	if (ret < 0) +		return ret; + +	if (ret > 0) { +		if (!params->userspace_handles_dfs) +			return -EINVAL; +		radar_detect_width = BIT(params->chandef.width); +	} + +	chanmode = (params->channel_fixed && !ret) ? +		IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE; + +	mutex_lock(&local->chanctx_mtx); +	ret = ieee80211_check_combinations(sdata, ¶ms->chandef, chanmode, +					   radar_detect_width); +	mutex_unlock(&local->chanctx_mtx); +	if (ret < 0) +		return ret;  	if (params->bssid) {  		memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN); @@ -1202,11 +1675,13 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  	sdata->u.ibss.privacy = params->privacy;  	sdata->u.ibss.control_port = params->control_port; +	sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;  	sdata->u.ibss.basic_rates = params->basic_rates; +	sdata->u.ibss.last_scan_completed = jiffies;  	/* fix basic_rates if channel does not support these rates */  	rate_flags = ieee80211_chandef_rate_flags(¶ms->chandef); -	sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band]; +	sband = local->hw.wiphy->bands[params->chandef.chan->band];  	for (i = 0; i < sband->n_bitrates; i++) {  		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)  			sdata->u.ibss.basic_rates &= ~BIT(i); @@ -1255,9 +1730,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  	ieee80211_bss_info_change_notify(sdata, changed);  	sdata->smps_mode = IEEE80211_SMPS_OFF; -	sdata->needed_rx_chains = sdata->local->rx_chains; +	sdata->needed_rx_chains = local->rx_chains; -	ieee80211_queue_work(&sdata->local->hw, &sdata->work); +	ieee80211_queue_work(&local->hw, &sdata->work);  	return 0;  } @@ -1265,73 +1740,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	struct ieee80211_local *local = sdata->local; -	struct cfg80211_bss *cbss; -	u16 capability; -	int active_ibss; -	struct sta_info *sta; -	struct beacon_data *presp; - -	active_ibss = ieee80211_sta_active_ibss(sdata); - -	if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { -		capability = WLAN_CAPABILITY_IBSS; -		if (ifibss->privacy) -			capability |= WLAN_CAPABILITY_PRIVACY; - -		cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, -					ifibss->bssid, ifibss->ssid, -					ifibss->ssid_len, WLAN_CAPABILITY_IBSS | -					WLAN_CAPABILITY_PRIVACY, -					capability); - -		if (cbss) { -			cfg80211_unlink_bss(local->hw.wiphy, cbss); -			cfg80211_put_bss(local->hw.wiphy, cbss); -		} -	} - -	ifibss->state = IEEE80211_IBSS_MLME_SEARCH; -	memset(ifibss->bssid, 0, ETH_ALEN); +	ieee80211_ibss_disconnect(sdata);  	ifibss->ssid_len = 0; - -	sta_info_flush(sdata); - -	spin_lock_bh(&ifibss->incomplete_lock); -	while (!list_empty(&ifibss->incomplete_stations)) { -		sta = list_first_entry(&ifibss->incomplete_stations, -				       struct sta_info, list); -		list_del(&sta->list); -		spin_unlock_bh(&ifibss->incomplete_lock); - -		sta_info_free(local, sta); -		spin_lock_bh(&ifibss->incomplete_lock); -	} -	spin_unlock_bh(&ifibss->incomplete_lock); - -	netif_carrier_off(sdata->dev); +	memset(ifibss->bssid, 0, ETH_ALEN);  	/* remove beacon */  	kfree(sdata->u.ibss.ie); -	presp = rcu_dereference_protected(ifibss->presp, -					  lockdep_is_held(&sdata->wdev.mtx)); -	RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);  	/* on the next join, re-program HT parameters */  	memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));  	memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask)); -	sdata->vif.bss_conf.ibss_joined = false; -	sdata->vif.bss_conf.ibss_creator = false; -	sdata->vif.bss_conf.enable_beacon = false; -	sdata->vif.bss_conf.ssid_len = 0; -	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); -	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | -						BSS_CHANGED_IBSS); -	ieee80211_vif_release_channel(sdata);  	synchronize_rcu(); -	kfree(presp);  	skb_queue_purge(&sdata->skb_queue); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b6186517ec5..ac9836e0aab 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -232,6 +232,7 @@ struct ieee80211_rx_data {  struct beacon_data {  	u8 *head, *tail;  	int head_len, tail_len; +	struct ieee80211_meshconf_ie *meshconf;  	struct rcu_head rcu_head;  }; @@ -245,7 +246,8 @@ struct ps_data {  	/* yes, this looks ugly, but guarantees that we can later use  	 * bitmap_empty :)  	 * NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */ -	u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]; +	u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)] +			__aligned(__alignof__(unsigned long));  	struct sk_buff_head bc_buf;  	atomic_t num_sta_ps; /* number of stations in PS mode */  	int dtim_count; @@ -258,10 +260,14 @@ struct ieee80211_if_ap {  	/* to be used after channel switch. */  	struct cfg80211_beacon_data *next_beacon; -	struct list_head vlans; +	struct list_head vlans; /* write-protected with RTNL and local->mtx */  	struct ps_data ps;  	atomic_t num_mcast_sta; /* number of stations receiving multicast */ +	enum ieee80211_smps_mode req_smps, /* requested smps mode */ +			 driver_smps_mode; /* smps mode request */ + +	struct work_struct request_smps_work;  };  struct ieee80211_if_wds { @@ -270,7 +276,7 @@ struct ieee80211_if_wds {  };  struct ieee80211_if_vlan { -	struct list_head list; +	struct list_head list; /* write-protected with RTNL and local->mtx */  	/* used for all tx if the VLAN is configured to 4-addr mode */  	struct sta_info __rcu *sta; @@ -311,6 +317,7 @@ struct ieee80211_roc_work {  	bool started, abort, hw_begun, notified;  	bool to_be_freed; +	bool on_channel;  	unsigned long hw_start_time; @@ -322,7 +329,6 @@ struct ieee80211_roc_work {  /* flags used in struct ieee80211_if_managed.flags */  enum ieee80211_sta_flags { -	IEEE80211_STA_BEACON_POLL	= BIT(0),  	IEEE80211_STA_CONNECTION_POLL	= BIT(1),  	IEEE80211_STA_CONTROL_PORT	= BIT(2),  	IEEE80211_STA_DISABLE_HT	= BIT(4), @@ -335,6 +341,7 @@ enum ieee80211_sta_flags {  	IEEE80211_STA_DISABLE_VHT	= BIT(11),  	IEEE80211_STA_DISABLE_80P80MHZ	= BIT(12),  	IEEE80211_STA_DISABLE_160MHZ	= BIT(13), +	IEEE80211_STA_DISABLE_WMM	= BIT(14),  };  struct ieee80211_mgd_auth_data { @@ -487,6 +494,7 @@ struct ieee80211_if_managed {  struct ieee80211_if_ibss {  	struct timer_list timer; +	struct work_struct csa_connection_drop_work;  	unsigned long last_scan_completed; @@ -497,6 +505,7 @@ struct ieee80211_if_ibss {  	bool privacy;  	bool control_port; +	bool userspace_handles_dfs;  	u8 bssid[ETH_ALEN] __aligned(2);  	u8 ssid[IEEE80211_MAX_SSID_LEN]; @@ -534,10 +543,18 @@ struct ieee80211_mesh_sync_ops {  			     struct ieee80211_mgmt *mgmt,  			     struct ieee802_11_elems *elems,  			     struct ieee80211_rx_status *rx_status); -	void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata); + +	/* should be called with beacon_data under RCU read lock */ +	void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata, +			    struct beacon_data *beacon);  	/* add other framework functions here */  }; +struct mesh_csa_settings { +	struct rcu_head rcu_head; +	struct cfg80211_csa_settings settings; +}; +  struct ieee80211_if_mesh {  	struct timer_list housekeeping_timer;  	struct timer_list mesh_path_timer; @@ -598,6 +615,18 @@ struct ieee80211_if_mesh {  	int ps_peers_light_sleep;  	int ps_peers_deep_sleep;  	struct ps_data ps; +	/* Channel Switching Support */ +	struct mesh_csa_settings __rcu *csa; +	enum { +		IEEE80211_MESH_CSA_ROLE_NONE, +		IEEE80211_MESH_CSA_ROLE_INIT, +		IEEE80211_MESH_CSA_ROLE_REPEATER, +	} csa_role; +	u8 chsw_ttl; +	u16 pre_value; + +	/* offset from skb->data while building IE */ +	int meshconf_offset;  };  #ifdef CONFIG_MAC80211_MESH @@ -663,13 +692,20 @@ struct ieee80211_chanctx {  	struct list_head list;  	struct rcu_head rcu_head; +	struct list_head assigned_vifs; +	struct list_head reserved_vifs; +  	enum ieee80211_chanctx_mode mode; -	int refcount;  	bool driver_present;  	struct ieee80211_chanctx_conf conf;  }; +struct mac80211_qos_map { +	struct cfg80211_qos_map qos_map; +	struct rcu_head rcu_head; +}; +  struct ieee80211_sub_if_data {  	struct list_head list; @@ -712,13 +748,26 @@ struct ieee80211_sub_if_data {  	u16 sequence_number;  	__be16 control_port_protocol;  	bool control_port_no_encrypt; +	int encrypt_headroom;  	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; +	struct mac80211_qos_map __rcu *qos_map;  	struct work_struct csa_finalize_work; -	int csa_counter_offset_beacon; -	int csa_counter_offset_presp; +	u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM]; +	u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM];  	bool csa_radar_required; +	bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ +	struct cfg80211_chan_def csa_chandef; + +	struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */ +	struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */ + +	/* context reservation -- protected with chanctx_mtx */ +	struct ieee80211_chanctx *reserved_chanctx; +	struct cfg80211_chan_def reserved_chandef; +	bool reserved_radar_required; +	u8 csa_current_counter;  	/* used to reconfigure hardware SM PS */  	struct work_struct recalc_smps; @@ -758,10 +807,6 @@ struct ieee80211_sub_if_data {  		u32 mntr_flags;  	} u; -	spinlock_t cleanup_stations_lock; -	struct list_head cleanup_stations; -	struct work_struct cleanup_stations_wk; -  #ifdef CONFIG_MAC80211_DEBUGFS  	struct {  		struct dentry *subdir_stations; @@ -795,6 +840,9 @@ static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata)  	__release(&sdata->wdev.mtx);  } +#define sdata_dereference(p, sdata) \ +	rcu_dereference_protected(p, lockdep_is_held(&sdata->wdev.mtx)) +  static inline void  sdata_assert_lock(struct ieee80211_sub_if_data *sdata)  { @@ -880,6 +928,24 @@ struct tpt_led_trigger {  };  #endif +/* + * struct ieee80211_tx_latency_bin_ranges - Tx latency statistics bins ranges + * + * Measuring Tx latency statistics. Counts how many Tx frames transmitted in a + * certain latency range (in Milliseconds). Each station that uses these + * ranges will have bins to count the amount of frames received in that range. + * The user can configure the ranges via debugfs. + * If ranges is NULL then Tx latency statistics bins are disabled for all + * stations. + * + * @n_ranges: number of ranges that are taken in account + * @ranges: the ranges that the user requested or NULL if disabled. + */ +struct ieee80211_tx_latency_bin_ranges { +	int n_ranges; +	u32 ranges[]; +}; +  /**   * mac80211 scan flags - currently active scan mode   * @@ -893,6 +959,8 @@ struct tpt_led_trigger {   *	that the scan completed.   * @SCAN_ABORTED: Set for our scan work function when the driver reported   *	a scan complete for an aborted scan. + * @SCAN_HW_CANCELLED: Set for our scan work function when the scan is being + *	cancelled.   */  enum {  	SCAN_SW_SCANNING, @@ -900,6 +968,7 @@ enum {  	SCAN_ONCHANNEL_SCANNING,  	SCAN_COMPLETED,  	SCAN_ABORTED, +	SCAN_HW_CANCELLED,  };  /** @@ -1029,6 +1098,12 @@ struct ieee80211_local {  	struct timer_list sta_cleanup;  	int sta_generation; +	/* +	 * Tx latency statistics parameters for all stations. +	 * Can enable via debugfs (NULL when disabled). +	 */ +	struct ieee80211_tx_latency_bin_ranges __rcu *tx_latency; +  	struct sk_buff_head pending[IEEE80211_MAX_QUEUES];  	struct tasklet_struct tx_pending_tasklet; @@ -1069,12 +1144,12 @@ struct ieee80211_local {  	struct work_struct sched_scan_stopped_work;  	struct ieee80211_sub_if_data __rcu *sched_scan_sdata; +	struct cfg80211_sched_scan_request *sched_scan_req;  	unsigned long leave_oper_channel_time;  	enum mac80211_scan_state next_scan_state;  	struct delayed_work scan_work;  	struct ieee80211_sub_if_data __rcu *scan_sdata; -	struct cfg80211_chan_def csa_chandef;  	/* For backward compatibility only -- do not use */  	struct cfg80211_chan_def _oper_chandef; @@ -1180,6 +1255,8 @@ struct ieee80211_local {  	struct ieee80211_sub_if_data __rcu *p2p_sdata; +	struct napi_struct *napi; +  	/* virtual monitor interface */  	struct ieee80211_sub_if_data __rcu *monitor_sdata;  	struct cfg80211_chan_def monitor_chandef; @@ -1203,6 +1280,15 @@ struct ieee80211_ra_tid {  	u16 tid;  }; +/* this struct holds the value parsing from channel switch IE  */ +struct ieee80211_csa_ie { +	struct cfg80211_chan_def chandef; +	u8 mode; +	u8 count; +	u8 ttl; +	u16 pre_value; +}; +  /* Parsed Information Elements */  struct ieee802_11_elems {  	const u8 *ie_start; @@ -1239,6 +1325,7 @@ struct ieee802_11_elems {  	const struct ieee80211_timeout_interval_ie *timeout_int;  	const u8 *opmode_notif;  	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; +	const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;  	/* length of them, respectively */  	u8 ssid_len; @@ -1317,6 +1404,7 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);  void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata);  void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,  				  __le16 fc, bool acked); +void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);  void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);  /* IBSS code */ @@ -1330,11 +1418,18 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);  void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);  void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  				   struct sk_buff *skb); +int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings); +int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata); +void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);  /* mesh code */  void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);  void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  				   struct sk_buff *skb); +int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings); +int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);  /* scan/BSS handling */  void ieee80211_scan_work(struct work_struct *work); @@ -1360,9 +1455,13 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,  			  struct ieee80211_bss *bss);  /* scheduled scan handling */ +int +__ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, +				     struct cfg80211_sched_scan_request *req);  int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,  				       struct cfg80211_sched_scan_request *req);  int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); +void ieee80211_sched_scan_end(struct ieee80211_local *local);  void ieee80211_sched_scan_stopped_work(struct work_struct *work);  /* off-channel helpers */ @@ -1377,7 +1476,10 @@ void ieee80211_sw_roc_work(struct work_struct *work);  void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);  /* channel switch handling */ +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local);  void ieee80211_csa_finalize_work(struct work_struct *work); +int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, +			     struct cfg80211_csa_settings *params);  /* interface handling */  int ieee80211_iface_init(void); @@ -1400,8 +1502,6 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);  bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);  void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); -int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, -			    struct cfg80211_beacon_data *params);  static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)  { @@ -1431,7 +1531,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,  int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,  			       enum ieee80211_smps_mode smps, const u8 *da,  			       const u8 *bssid); -void ieee80211_request_smps_work(struct work_struct *work); +void ieee80211_request_smps_ap_work(struct work_struct *work); +void ieee80211_request_smps_mgd_work(struct work_struct *work); +bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, +				   enum ieee80211_smps_mode smps_mode_new);  void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,  				     u16 initiator, u16 reason, bool stop); @@ -1471,6 +1574,9 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,  				    struct sta_info *sta);  enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);  void ieee80211_sta_set_rx_nss(struct sta_info *sta); +u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, +                                  struct sta_info *sta, u8 opmode, +                                  enum ieee80211_band band, bool nss_only);  void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,  				 struct sta_info *sta, u8 opmode,  				 enum ieee80211_band band, bool nss_only); @@ -1481,6 +1587,28 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,  void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,  				       struct ieee80211_mgmt *mgmt,  				       size_t len); +/** + * ieee80211_parse_ch_switch_ie - parses channel switch IEs + * @sdata: the sdata of the interface which has received the frame + * @elems: parsed 802.11 elements received with the frame + * @beacon: indicates if the frame was a beacon or probe response + * @current_band: indicates the current band + * @sta_flags: contains information about own capabilities and restrictions + *	to decide which channel switch announcements can be accepted. Only the + *	following subset of &enum ieee80211_sta_flags are evaluated: + *	%IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT, + *	%IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ, + *	%IEEE80211_STA_DISABLE_160MHZ. + * @bssid: the currently connected bssid (for reporting) + * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl. +	All of them will be filled with if success only. + * Return: 0 on success, <0 on error and >0 if there is nothing to parse. + */ +int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, +				 struct ieee802_11_elems *elems, bool beacon, +				 enum ieee80211_band current_band, +				 u32 sta_flags, u8 *bssid, +				 struct ieee80211_csa_ie *csa_ie);  /* Suspend/resume and hw reconfiguration */  int ieee80211_reconfig(struct ieee80211_local *local); @@ -1501,7 +1629,7 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)  }  /* utility functions/constants */ -extern void *mac80211_wiphy_privid; /* for wiphy privid */ +extern const void *const mac80211_wiphy_privid; /* for wiphy privid */  u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,  			enum nl80211_iftype type);  int ieee80211_frame_duration(enum ieee80211_band band, size_t len, @@ -1588,14 +1716,8 @@ void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,  void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);  void ieee80211_add_pending_skb(struct ieee80211_local *local,  			       struct sk_buff *skb); -void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, -				   struct sk_buff_head *skbs, -				   void (*fn)(void *data), void *data); -static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local, -					      struct sk_buff_head *skbs) -{ -	ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); -} +void ieee80211_add_pending_skbs(struct ieee80211_local *local, +				struct sk_buff_head *skbs);  void ieee80211_flush_queues(struct ieee80211_local *local,  			    struct ieee80211_sub_if_data *sdata); @@ -1626,9 +1748,12 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,  u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,  			    struct ieee802_11_elems *elems,  			    enum ieee80211_band band, u32 *basic_rates); -int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, -			     enum ieee80211_smps_mode smps_mode); +int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, +				 enum ieee80211_smps_mode smps_mode); +int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, +				enum ieee80211_smps_mode smps_mode);  void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); +void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);  size_t ieee80211_ie_split(const u8 *ies, size_t ielen,  			  const u8 *ids, int n_ids, size_t offset); @@ -1654,34 +1779,73 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,  void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,  				  const struct ieee80211_ht_operation *ht_oper,  				  struct cfg80211_chan_def *chandef); +u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);  int __must_check  ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,  			  const struct cfg80211_chan_def *chandef,  			  enum ieee80211_chanctx_mode mode);  int __must_check +ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, +			      const struct cfg80211_chan_def *chandef, +			      enum ieee80211_chanctx_mode mode, +			      bool radar_required); +int __must_check +ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, +				   u32 *changed); +int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata); + +int __must_check  ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,  			       const struct cfg80211_chan_def *chandef,  			       u32 *changed);  /* NOTE: only use ieee80211_vif_change_channel() for channel switch */  int __must_check  ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, -			     const struct cfg80211_chan_def *chandef,  			     u32 *changed);  void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);  void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);  void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,  					 bool clear); +int ieee80211_chanctx_refcount(struct ieee80211_local *local, +			       struct ieee80211_chanctx *ctx);  void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,  				   struct ieee80211_chanctx *chanctx); -void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, -				    struct ieee80211_chanctx *chanctx); +void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, +				      struct ieee80211_chanctx *ctx);  void ieee80211_dfs_cac_timer(unsigned long data);  void ieee80211_dfs_cac_timer_work(struct work_struct *work);  void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);  void ieee80211_dfs_radar_detected_work(struct work_struct *work); +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings); + +bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs); +bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n); +const struct ieee80211_cipher_scheme * +ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, +		 enum nl80211_iftype iftype); +int ieee80211_cs_headroom(struct ieee80211_local *local, +			  struct cfg80211_crypto_settings *crypto, +			  enum nl80211_iftype iftype); +void ieee80211_recalc_dtim(struct ieee80211_local *local, +			   struct ieee80211_sub_if_data *sdata); +int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, +				 const struct cfg80211_chan_def *chandef, +				 enum ieee80211_chanctx_mode chanmode, +				 u8 radar_detect); +int ieee80211_max_num_channels(struct ieee80211_local *local); + +/* TDLS */ +int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, +			const u8 *peer, u8 action_code, u8 dialog_token, +			u16 status_code, u32 peer_capability, +			const u8 *extra_ies, size_t extra_ies_len); +int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, +			const u8 *peer, enum nl80211_tdls_operation oper); +  #ifdef CONFIG_MAC80211_NOINLINE  #define debug_noinline noinline diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index fcecd633514..388b863e821 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -101,9 +101,8 @@ static u32 __ieee80211_idle_on(struct ieee80211_local *local)  static u32 __ieee80211_recalc_idle(struct ieee80211_local *local,  				   bool force_active)  { -	bool working = false, scanning, active; +	bool working, scanning, active;  	unsigned int led_trig_start = 0, led_trig_stop = 0; -	struct ieee80211_roc_work *roc;  	lockdep_assert_held(&local->mtx); @@ -111,12 +110,8 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local,  		 !list_empty(&local->chanctx_list) ||  		 local->monitors; -	if (!local->ops->remain_on_channel) { -		list_for_each_entry(roc, &local->roc_list, list) { -			working = true; -			break; -		} -	} +	working = !local->ops->remain_on_channel && +		  !list_empty(&local->roc_list);  	scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||  		   test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning); @@ -255,6 +250,7 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_sub_if_data *nsdata; +	int ret;  	ASSERT_RTNL(); @@ -305,7 +301,10 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,  		}  	} -	return 0; +	mutex_lock(&local->chanctx_mtx); +	ret = ieee80211_check_combinations(sdata, NULL, 0, 0); +	mutex_unlock(&local->chanctx_mtx); +	return ret;  }  static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata, @@ -400,6 +399,9 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)  	sdata->vif.type = NL80211_IFTYPE_MONITOR;  	snprintf(sdata->name, IFNAMSIZ, "%s-monitor",  		 wiphy_name(local->hw.wiphy)); +	sdata->wdev.iftype = NL80211_IFTYPE_MONITOR; + +	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;  	ieee80211_set_default_queues(sdata); @@ -416,18 +418,24 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)  		return ret;  	} +	mutex_lock(&local->iflist_mtx); +	rcu_assign_pointer(local->monitor_sdata, sdata); +	mutex_unlock(&local->iflist_mtx); + +	mutex_lock(&local->mtx);  	ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,  					IEEE80211_CHANCTX_EXCLUSIVE); +	mutex_unlock(&local->mtx);  	if (ret) { +		mutex_lock(&local->iflist_mtx); +		RCU_INIT_POINTER(local->monitor_sdata, NULL); +		mutex_unlock(&local->iflist_mtx); +		synchronize_net();  		drv_remove_interface(local, sdata);  		kfree(sdata);  		return ret;  	} -	mutex_lock(&local->iflist_mtx); -	rcu_assign_pointer(local->monitor_sdata, sdata); -	mutex_unlock(&local->iflist_mtx); -  	return 0;  } @@ -449,12 +457,14 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local)  		return;  	} -	rcu_assign_pointer(local->monitor_sdata, NULL); +	RCU_INIT_POINTER(local->monitor_sdata, NULL);  	mutex_unlock(&local->iflist_mtx);  	synchronize_net(); +	mutex_lock(&local->mtx);  	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&local->mtx);  	drv_remove_interface(local, sdata); @@ -487,7 +497,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)  		if (!sdata->bss)  			return -ENOLINK; +		mutex_lock(&local->mtx);  		list_add(&sdata->u.vlan.list, &sdata->bss->vlans); +		mutex_unlock(&local->mtx);  		master = container_of(sdata->bss,  				      struct ieee80211_sub_if_data, u.ap); @@ -717,8 +729,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)  		drv_stop(local);   err_del_bss:  	sdata->bss = NULL; -	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		mutex_lock(&local->mtx);  		list_del(&sdata->u.vlan.list); +		mutex_unlock(&local->mtx); +	}  	/* might already be clear but that doesn't matter */  	clear_bit(SDATA_STATE_RUNNING, &sdata->state);  	return res; @@ -749,6 +764,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	u32 hw_reconf_flags = 0;  	int i, flushed;  	struct ps_data *ps; +	struct cfg80211_chan_def chandef;  	clear_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -763,8 +779,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	ieee80211_roc_purge(local, sdata); -	if (sdata->vif.type == NL80211_IFTYPE_STATION) +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_STATION:  		ieee80211_mgd_stop(sdata); +		break; +	case NL80211_IFTYPE_ADHOC: +		ieee80211_ibss_stop(sdata); +		break; +	case NL80211_IFTYPE_AP: +		cancel_work_sync(&sdata->u.ap.request_smps_work); +		break; +	default: +		break; +	}  	/*  	 * Remove all stations associated with this interface. @@ -779,10 +806,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	 * This is relevant only in WDS mode, in all other modes we've  	 * already removed all stations when disconnecting or similar,  	 * so warn otherwise. -	 * -	 * We call sta_info_flush_cleanup() later, to combine RCU waits.  	 */ -	flushed = sta_info_flush_defer(sdata); +	flushed = sta_info_flush(sdata);  	WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||  		     (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); @@ -813,17 +838,28 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	cancel_work_sync(&local->dynamic_ps_enable_work);  	cancel_work_sync(&sdata->recalc_smps); +	sdata_lock(sdata); +	mutex_lock(&local->mtx);  	sdata->vif.csa_active = false; +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx); +	sdata_unlock(sdata); +  	cancel_work_sync(&sdata->csa_finalize_work);  	cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);  	if (sdata->wdev.cac_started) { +		chandef = sdata->vif.bss_conf.chandef;  		WARN_ON(local->suspended); -		mutex_lock(&local->iflist_mtx); +		mutex_lock(&local->mtx);  		ieee80211_vif_release_channel(sdata); -		mutex_unlock(&local->iflist_mtx); -		cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED, +		mutex_unlock(&local->mtx); +		cfg80211_cac_event(sdata->dev, &chandef, +				   NL80211_RADAR_CAC_ABORTED,  				   GFP_KERNEL);  	} @@ -856,8 +892,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_AP_VLAN: +		mutex_lock(&local->mtx);  		list_del(&sdata->u.vlan.list); -		rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); +		mutex_unlock(&local->mtx); +		RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);  		/* no need to tell driver */  		break;  	case NL80211_IFTYPE_MONITOR: @@ -876,29 +914,21 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  		break;  	case NL80211_IFTYPE_P2P_DEVICE:  		/* relies on synchronize_rcu() below */ -		rcu_assign_pointer(local->p2p_sdata, NULL); +		RCU_INIT_POINTER(local->p2p_sdata, NULL);  		/* fall through */  	default:  		cancel_work_sync(&sdata->work);  		/*  		 * When we get here, the interface is marked down. +		 * Free the remaining keys, if there are any +		 * (shouldn't be, except maybe in WDS mode?)  		 * -		 * sta_info_flush_cleanup() requires rcu_barrier() -		 * first to wait for the station call_rcu() calls -		 * to complete, and we also need synchronize_rcu() -		 * to wait for the RX path in case it is using the -		 * interface and enqueuing frames at this very time on +		 * Force the key freeing to always synchronize_net() +		 * to wait for the RX path in case it is using this +		 * interface enqueuing frames * at this very time on  		 * another CPU.  		 */ -		synchronize_rcu(); -		rcu_barrier(); -		sta_info_flush_cleanup(sdata); - -		/* -		 * Free all remaining keys, there shouldn't be any, -		 * except maybe in WDS mode? -		 */ -		ieee80211_free_keys(sdata); +		ieee80211_free_keys(sdata, true);  		/* fall through */  	case NL80211_IFTYPE_AP: @@ -1009,17 +1039,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev)  			atomic_dec(&local->iff_promiscs);  		sdata->flags ^= IEEE80211_SDATA_PROMISC;  	} - -	/* -	 * TODO: If somebody needs this on AP interfaces, -	 *	 it can be enabled easily but multicast -	 *	 addresses from VLANs need to be synced. -	 */ -	if (sdata->vif.type != NL80211_IFTYPE_MONITOR && -	    sdata->vif.type != NL80211_IFTYPE_AP_VLAN && -	    sdata->vif.type != NL80211_IFTYPE_AP) -		drv_set_multicast_list(local, sdata, &dev->mc); -  	spin_lock_bh(&local->filter_lock);  	__hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);  	spin_unlock_bh(&local->filter_lock); @@ -1032,11 +1051,10 @@ static void ieee80211_set_multicast_list(struct net_device *dev)   */  static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)  { -	int flushed;  	int i;  	/* free extra data */ -	ieee80211_free_keys(sdata); +	ieee80211_free_keys(sdata, false);  	ieee80211_debugfs_remove_netdev(sdata); @@ -1046,9 +1064,6 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)  	if (ieee80211_vif_is_mesh(&sdata->vif))  		mesh_rmc_free(sdata); - -	flushed = sta_info_flush(sdata); -	WARN_ON(flushed);  }  static void ieee80211_uninit(struct net_device *dev) @@ -1057,7 +1072,9 @@ static void ieee80211_uninit(struct net_device *dev)  }  static u16 ieee80211_netdev_select_queue(struct net_device *dev, -					 struct sk_buff *skb) +					 struct sk_buff *skb, +					 void *accel_priv, +					 select_queue_fallback_t fallback)  {  	return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);  } @@ -1074,7 +1091,9 @@ static const struct net_device_ops ieee80211_dataif_ops = {  };  static u16 ieee80211_monitor_select_queue(struct net_device *dev, -					  struct sk_buff *skb) +					  struct sk_buff *skb, +					  void *accel_priv, +					  select_queue_fallback_t fallback)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; @@ -1266,6 +1285,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,  	sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);  	sdata->control_port_no_encrypt = false; +	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; +	sdata->vif.bss_conf.idle = true;  	sdata->noack_map = 0; @@ -1279,6 +1300,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,  	INIT_WORK(&sdata->work, ieee80211_iface_work);  	INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);  	INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work); +	INIT_LIST_HEAD(&sdata->assigned_chanctx_list); +	INIT_LIST_HEAD(&sdata->reserved_chanctx_list);  	switch (type) {  	case NL80211_IFTYPE_P2P_GO: @@ -1289,7 +1312,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,  	case NL80211_IFTYPE_AP:  		skb_queue_head_init(&sdata->u.ap.ps.bc_buf);  		INIT_LIST_HEAD(&sdata->u.ap.vlans); +		INIT_WORK(&sdata->u.ap.request_smps_work, +			  ieee80211_request_smps_ap_work);  		sdata->vif.bss_conf.bssid = sdata->vif.addr; +		sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;  		break;  	case NL80211_IFTYPE_P2P_CLIENT:  		type = NL80211_IFTYPE_STATION; @@ -1318,7 +1344,6 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,  		sdata->vif.bss_conf.bssid = NULL;  		break;  	case NL80211_IFTYPE_AP_VLAN: -		break;  	case NL80211_IFTYPE_P2P_DEVICE:  		sdata->vif.bss_conf.bssid = sdata->vif.addr;  		break; @@ -1489,8 +1514,8 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,  			bool used = false;  			list_for_each_entry(sdata, &local->interfaces, list) { -				if (memcmp(local->hw.wiphy->addresses[i].addr, -					   sdata->vif.addr, ETH_ALEN) == 0) { +				if (ether_addr_equal(local->hw.wiphy->addresses[i].addr, +						     sdata->vif.addr)) {  					used = true;  					break;  				} @@ -1550,8 +1575,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,  			val += inc;  			list_for_each_entry(sdata, &local->interfaces, list) { -				if (memcmp(tmp_addr, sdata->vif.addr, -							ETH_ALEN) == 0) { +				if (ether_addr_equal(tmp_addr, sdata->vif.addr)) {  					used = true;  					break;  				} @@ -1571,15 +1595,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,  	mutex_unlock(&local->iflist_mtx);  } -static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk) -{ -	struct ieee80211_sub_if_data *sdata; - -	sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk); - -	ieee80211_cleanup_sdata_stas(sdata); -} -  int ieee80211_if_add(struct ieee80211_local *local, const char *name,  		     struct wireless_dev **new_wdev, enum nl80211_iftype type,  		     struct vif_params *params) @@ -1652,9 +1667,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,  	INIT_LIST_HEAD(&sdata->key_list); -	spin_lock_init(&sdata->cleanup_stations_lock); -	INIT_LIST_HEAD(&sdata->cleanup_stations); -	INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);  	INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,  			  ieee80211_dfs_cac_timer_work);  	INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk, @@ -1679,6 +1691,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,  	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;  	sdata->user_power_level = local->user_power_level; +	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; +  	/* setup type-dependent data */  	ieee80211_setup_sdata(sdata, type); @@ -1766,7 +1780,6 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)  	}  	mutex_unlock(&local->iflist_mtx);  	unregister_netdevice_many(&unreg_list); -	list_del(&unreg_list);  	list_for_each_entry_safe(sdata, tmp, &wdev_list, list) {  		list_del(&sdata->list); @@ -1782,20 +1795,19 @@ static int netdev_notify(struct notifier_block *nb,  	struct ieee80211_sub_if_data *sdata;  	if (state != NETDEV_CHANGENAME) -		return 0; +		return NOTIFY_DONE;  	if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) -		return 0; +		return NOTIFY_DONE;  	if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid) -		return 0; +		return NOTIFY_DONE;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); -  	memcpy(sdata->name, dev->name, IFNAMSIZ); -  	ieee80211_debugfs_rename_netdev(sdata); -	return 0; + +	return NOTIFY_OK;  }  static struct notifier_block mac80211_netdev_notifier = { diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 620677e897b..16d97f044a2 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -260,25 +260,29 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,  	int idx;  	bool defunikey, defmultikey, defmgmtkey; +	/* caller must provide at least one old/new */ +	if (WARN_ON(!new && !old)) +		return; +  	if (new)  		list_add_tail(&new->list, &sdata->key_list); -	if (sta && pairwise) { -		rcu_assign_pointer(sta->ptk, new); -	} else if (sta) { -		if (old) -			idx = old->conf.keyidx; -		else -			idx = new->conf.keyidx; -		rcu_assign_pointer(sta->gtk[idx], new); -	} else { -		WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); +	WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); -		if (old) -			idx = old->conf.keyidx; -		else -			idx = new->conf.keyidx; +	if (old) +		idx = old->conf.keyidx; +	else +		idx = new->conf.keyidx; +	if (sta) { +		if (pairwise) { +			rcu_assign_pointer(sta->ptk[idx], new); +			sta->ptk_idx = idx; +		} else { +			rcu_assign_pointer(sta->gtk[idx], new); +			sta->gtk_idx = idx; +		} +	} else {  		defunikey = old &&  			old == key_mtx_dereference(sdata->local,  						sdata->default_unicast_key); @@ -312,14 +316,17 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,  		list_del(&old->list);  } -struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, -					  const u8 *key_data, -					  size_t seq_len, const u8 *seq) +struct ieee80211_key * +ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, +		    const u8 *key_data, +		    size_t seq_len, const u8 *seq, +		    const struct ieee80211_cipher_scheme *cs)  {  	struct ieee80211_key *key;  	int i, j, err; -	BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); +	if (WARN_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)) +		return ERR_PTR(-EINVAL);  	key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL);  	if (!key) @@ -393,6 +400,18 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,  			return ERR_PTR(err);  		}  		break; +	default: +		if (cs) { +			size_t len = (seq_len > MAX_PN_LEN) ? +						MAX_PN_LEN : seq_len; + +			key->conf.iv_len = cs->hdr_len; +			key->conf.icv_len = cs->mic_len; +			for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) +				for (j = 0; j < len; j++) +					key->u.gen.rx_pn[i][j] = +							seq[len - j - 1]; +		}  	}  	memcpy(key->conf.key, key_data, key_len);  	INIT_LIST_HEAD(&key->list); @@ -463,8 +482,8 @@ int ieee80211_key_link(struct ieee80211_key *key,  	int idx, ret;  	bool pairwise; -	BUG_ON(!sdata); -	BUG_ON(!key); +	if (WARN_ON(!sdata || !key)) +		return -EINVAL;  	pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;  	idx = key->conf.keyidx; @@ -475,7 +494,7 @@ int ieee80211_key_link(struct ieee80211_key *key,  	mutex_lock(&sdata->local->key_mtx);  	if (sta && pairwise) -		old_key = key_mtx_dereference(sdata->local, sta->ptk); +		old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);  	else if (sta)  		old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);  	else @@ -571,14 +590,10 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,  }  EXPORT_SYMBOL(ieee80211_iter_keys); -void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) +static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata, +				      struct list_head *keys)  {  	struct ieee80211_key *key, *tmp; -	LIST_HEAD(keys); - -	cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk); - -	mutex_lock(&sdata->local->key_mtx);  	sdata->crypto_tx_tailroom_needed_cnt -=  		sdata->crypto_tx_tailroom_pending_dec; @@ -590,28 +605,51 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)  		ieee80211_key_replace(key->sdata, key->sta,  				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,  				key, NULL); -		list_add_tail(&key->list, &keys); +		list_add_tail(&key->list, keys);  	}  	ieee80211_debugfs_key_update_default(sdata); +} -	if (!list_empty(&keys)) { -		synchronize_net(); -		list_for_each_entry_safe(key, tmp, &keys, list) -			__ieee80211_key_destroy(key, false); +void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, +			 bool force_synchronize) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_sub_if_data *vlan; +	struct ieee80211_key *key, *tmp; +	LIST_HEAD(keys); + +	cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk); + +	mutex_lock(&local->key_mtx); + +	ieee80211_free_keys_iface(sdata, &keys); + +	if (sdata->vif.type == NL80211_IFTYPE_AP) { +		list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +			ieee80211_free_keys_iface(vlan, &keys);  	} +	if (!list_empty(&keys) || force_synchronize) +		synchronize_net(); +	list_for_each_entry_safe(key, tmp, &keys, list) +		__ieee80211_key_destroy(key, false); +  	WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||  		     sdata->crypto_tx_tailroom_pending_dec); +	if (sdata->vif.type == NL80211_IFTYPE_AP) { +		list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +			WARN_ON_ONCE(vlan->crypto_tx_tailroom_needed_cnt || +				     vlan->crypto_tx_tailroom_pending_dec); +	} -	mutex_unlock(&sdata->local->key_mtx); +	mutex_unlock(&local->key_mtx);  }  void ieee80211_free_sta_keys(struct ieee80211_local *local,  			     struct sta_info *sta)  { -	struct ieee80211_key *key, *tmp; -	LIST_HEAD(keys); +	struct ieee80211_key *key;  	int i;  	mutex_lock(&local->key_mtx); @@ -622,25 +660,18 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,  		ieee80211_key_replace(key->sdata, key->sta,  				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,  				key, NULL); -		list_add(&key->list, &keys); +		__ieee80211_key_destroy(key, true);  	} -	key = key_mtx_dereference(local, sta->ptk); -	if (key) { +	for (i = 0; i < NUM_DEFAULT_KEYS; i++) { +		key = key_mtx_dereference(local, sta->ptk[i]); +		if (!key) +			continue;  		ieee80211_key_replace(key->sdata, key->sta,  				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,  				key, NULL); -		list_add(&key->list, &keys); -	} - -	/* -	 * NB: the station code relies on this being -	 * done even if there aren't any keys -	 */ -	synchronize_net(); - -	list_for_each_entry_safe(key, tmp, &keys, list)  		__ieee80211_key_destroy(key, true); +	}  	mutex_unlock(&local->key_mtx);  } @@ -877,9 +908,9 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,  	key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx,  				  keyconf->keylen, keyconf->key, -				  0, NULL); +				  0, NULL, NULL);  	if (IS_ERR(key)) -		return ERR_PTR(PTR_ERR(key)); +		return ERR_CAST(key);  	if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)  		key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 036d57e76a5..19db68663d7 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -18,6 +18,7 @@  #define NUM_DEFAULT_KEYS 4  #define NUM_DEFAULT_MGMT_KEYS 2 +#define MAX_PN_LEN 16  struct ieee80211_local;  struct ieee80211_sub_if_data; @@ -83,7 +84,7 @@ struct ieee80211_key {  			 * Management frames.  			 */  			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; -			struct crypto_cipher *tfm; +			struct crypto_aead *tfm;  			u32 replays; /* dot11RSNAStatsCCMPReplays */  		} ccmp;  		struct { @@ -93,6 +94,10 @@ struct ieee80211_key {  			u32 replays; /* dot11RSNAStatsCMACReplays */  			u32 icverrors; /* dot11RSNAStatsCMACICVErrors */  		} aes_cmac; +		struct { +			/* generic cipher scheme */ +			u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN]; +		} gen;  	} u;  	/* number of times this key has been used */ @@ -113,9 +118,11 @@ struct ieee80211_key {  	struct ieee80211_key_conf conf;  }; -struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, -					  const u8 *key_data, -					  size_t seq_len, const u8 *seq); +struct ieee80211_key * +ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, +		    const u8 *key_data, +		    size_t seq_len, const u8 *seq, +		    const struct ieee80211_cipher_scheme *cs);  /*   * Insert a key into data structures (sdata, sta if necessary)   * to make it used, free old key. On failure, also free the new key. @@ -129,7 +136,8 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,  			       bool uni, bool multi);  void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,  				    int idx); -void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); +void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, +			 bool force_synchronize);  void ieee80211_free_sta_keys(struct ieee80211_local *local,  			     struct sta_info *sta);  void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 21d5d44444d..d17c26d6e36 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -148,6 +148,8 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)  	list_for_each_entry_rcu(sdata, &local->interfaces, list) {  		if (!rcu_access_pointer(sdata->vif.chanctx_conf))  			continue; +		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +			continue;  		power = min(power, sdata->vif.bss_conf.txpower);  	}  	rcu_read_unlock(); @@ -199,7 +201,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,  {  	struct ieee80211_local *local = sdata->local; -	if (!changed) +	if (!changed || sdata->vif.type == NL80211_IFTYPE_AP_VLAN)  		return;  	drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); @@ -250,12 +252,8 @@ static void ieee80211_restart_work(struct work_struct *work)  	/* wait for scan work complete */  	flush_workqueue(local->workqueue); -	mutex_lock(&local->mtx); -	WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) || -	     rcu_dereference_protected(local->sched_scan_sdata, -				       lockdep_is_held(&local->mtx)), -		"%s called with hardware scan in progress\n", __func__); -	mutex_unlock(&local->mtx); +	WARN(test_bit(SCAN_HW_SCANNING, &local->scanning), +	     "%s called with hardware scan in progress\n", __func__);  	rtnl_lock();  	ieee80211_scan_cancel(local); @@ -342,7 +340,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,  	sdata_unlock(sdata); -	return NOTIFY_DONE; +	return NOTIFY_OK;  }  #endif @@ -373,7 +371,7 @@ static int ieee80211_ifa6_changed(struct notifier_block *nb,  	drv_ipv6_addr_change(local, sdata, idev); -	return NOTIFY_DONE; +	return NOTIFY_OK;  }  #endif @@ -448,7 +446,9 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {  	.cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |  				IEEE80211_HT_CAP_MAX_AMSDU |  				IEEE80211_HT_CAP_SGI_20 | -				IEEE80211_HT_CAP_SGI_40), +				IEEE80211_HT_CAP_SGI_40 | +				IEEE80211_HT_CAP_LDPC_CODING | +				IEEE80211_HT_CAP_40MHZ_INTOLERANT),  	.mcs = {  		.rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff,  			     0xff, 0xff, 0xff, 0xff, 0xff, }, @@ -651,15 +651,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  }  EXPORT_SYMBOL(ieee80211_alloc_hw); -int ieee80211_register_hw(struct ieee80211_hw *hw) +static int ieee80211_init_cipher_suites(struct ieee80211_local *local)  { -	struct ieee80211_local *local = hw_to_local(hw); -	int result, i; -	enum ieee80211_band band; -	int channels, max_bitrates; -	bool supp_ht, supp_vht; -	netdev_features_t feature_whitelist; -	struct cfg80211_chan_def dflt_chandef = {}; +	bool have_wep = !(IS_ERR(local->wep_tx_tfm) || +			  IS_ERR(local->wep_rx_tfm)); +	bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE; +	const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes; +	int n_suites = 0, r = 0, w = 0; +	u32 *suites;  	static const u32 cipher_suites[] = {  		/* keep WEP first, it may be removed below */  		WLAN_CIPHER_SUITE_WEP40, @@ -671,6 +670,93 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  		WLAN_CIPHER_SUITE_AES_CMAC  	}; +	/* Driver specifies the ciphers, we have nothing to do... */ +	if (local->hw.wiphy->cipher_suites && have_wep) +		return 0; + +	/* Set up cipher suites if driver relies on mac80211 cipher defs */ +	if (!local->hw.wiphy->cipher_suites && !cs) { +		local->hw.wiphy->cipher_suites = cipher_suites; +		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + +		if (!have_mfp) +			local->hw.wiphy->n_cipher_suites--; + +		if (!have_wep) { +			local->hw.wiphy->cipher_suites += 2; +			local->hw.wiphy->n_cipher_suites -= 2; +		} + +		return 0; +	} + +	if (!local->hw.wiphy->cipher_suites) { +		/* +		 * Driver specifies cipher schemes only +		 * We start counting ciphers defined by schemes, TKIP and CCMP +		 */ +		n_suites = local->hw.n_cipher_schemes + 2; + +		/* check if we have WEP40 and WEP104 */ +		if (have_wep) +			n_suites += 2; + +		/* check if we have AES_CMAC */ +		if (have_mfp) +			n_suites++; + +		suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL); +		if (!suites) +			return -ENOMEM; + +		suites[w++] = WLAN_CIPHER_SUITE_CCMP; +		suites[w++] = WLAN_CIPHER_SUITE_TKIP; + +		if (have_wep) { +			suites[w++] = WLAN_CIPHER_SUITE_WEP40; +			suites[w++] = WLAN_CIPHER_SUITE_WEP104; +		} + +		if (have_mfp) +			suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC; + +		for (r = 0; r < local->hw.n_cipher_schemes; r++) +			suites[w++] = cs[r].cipher; +	} else { +		/* Driver provides cipher suites, but we need to exclude WEP */ +		suites = kmemdup(local->hw.wiphy->cipher_suites, +				 sizeof(u32) * local->hw.wiphy->n_cipher_suites, +				 GFP_KERNEL); +		if (!suites) +			return -ENOMEM; + +		for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) { +			u32 suite = local->hw.wiphy->cipher_suites[r]; + +			if (suite == WLAN_CIPHER_SUITE_WEP40 || +			    suite == WLAN_CIPHER_SUITE_WEP104) +				continue; +			suites[w++] = suite; +		} +	} + +	local->hw.wiphy->cipher_suites = suites; +	local->hw.wiphy->n_cipher_suites = w; +	local->wiphy_ciphers_allocated = true; + +	return 0; +} + +int ieee80211_register_hw(struct ieee80211_hw *hw) +{ +	struct ieee80211_local *local = hw_to_local(hw); +	int result, i; +	enum ieee80211_band band; +	int channels, max_bitrates; +	bool supp_ht, supp_vht; +	netdev_features_t feature_whitelist; +	struct cfg80211_chan_def dflt_chandef = {}; +  	if (hw->flags & IEEE80211_HW_QUEUE_CONTROL &&  	    (local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||  	     local->hw.offchannel_tx_hw_queue >= local->hw.queues)) @@ -764,17 +850,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  		/* TODO: consider VHT for RX chains, hopefully it's the same */  	} -	local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + -				      sizeof(void *) * channels, GFP_KERNEL); -	if (!local->int_scan_req) -		return -ENOMEM; - -	for (band = 0; band < IEEE80211_NUM_BANDS; band++) { -		if (!local->hw.wiphy->bands[band]) -			continue; -		local->int_scan_req->rates[band] = (u32) -1; -	} -  	/* if low-level driver supports AP, we also support VLAN */  	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {  		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); @@ -798,6 +873,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  				return -EINVAL;  	} +	local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + +				      sizeof(void *) * channels, GFP_KERNEL); +	if (!local->int_scan_req) +		return -ENOMEM; + +	for (band = 0; band < IEEE80211_NUM_BANDS; band++) { +		if (!local->hw.wiphy->bands[band]) +			continue; +		local->int_scan_req->rates[band] = (u32) -1; +	} +  #ifndef CONFIG_MAC80211_MESH  	/* mesh depends on Kconfig, but drivers should set it if they want */  	local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT); @@ -811,10 +897,15 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	/* mac80211 supports control port protocol changing */  	local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL; -	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) +	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {  		local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; -	else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) +	} else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {  		local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; +		if (hw->max_signal <= 0) { +			result = -EINVAL; +			goto fail_wiphy_register; +		} +	}  	WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)  	     && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK), @@ -851,43 +942,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	if (local->hw.wiphy->max_scan_ie_len)  		local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len; -	/* Set up cipher suites unless driver already did */ -	if (!local->hw.wiphy->cipher_suites) { -		local->hw.wiphy->cipher_suites = cipher_suites; -		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); -		if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) -			local->hw.wiphy->n_cipher_suites--; -	} -	if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) { -		if (local->hw.wiphy->cipher_suites == cipher_suites) { -			local->hw.wiphy->cipher_suites += 2; -			local->hw.wiphy->n_cipher_suites -= 2; -		} else { -			u32 *suites; -			int r, w = 0; - -			/* Filter out WEP */ - -			suites = kmemdup( -				local->hw.wiphy->cipher_suites, -				sizeof(u32) * local->hw.wiphy->n_cipher_suites, -				GFP_KERNEL); -			if (!suites) { -				result = -ENOMEM; -				goto fail_wiphy_register; -			} -			for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) { -				u32 suite = local->hw.wiphy->cipher_suites[r]; -				if (suite == WLAN_CIPHER_SUITE_WEP40 || -				    suite == WLAN_CIPHER_SUITE_WEP104) -					continue; -				suites[w++] = suite; -			} -			local->hw.wiphy->cipher_suites = suites; -			local->hw.wiphy->n_cipher_suites = w; -			local->wiphy_ciphers_allocated = true; -		} -	} +	WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes, +					 local->hw.n_cipher_schemes)); + +	result = ieee80211_init_cipher_suites(local); +	if (result < 0) +		goto fail_wiphy_register;  	if (!local->ops->remain_on_channel)  		local->hw.wiphy->max_remain_on_channel_duration = 5000; @@ -896,6 +956,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)  		local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; +	local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM; +  	result = wiphy_register(local->hw.wiphy);  	if (result < 0)  		goto fail_wiphy_register; @@ -940,6 +1002,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  		wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",  			    result); +	local->hw.conf.flags = IEEE80211_CONF_IDLE; +  	ieee80211_led_init(local);  	rtnl_lock(); @@ -1018,6 +1082,18 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  }  EXPORT_SYMBOL(ieee80211_register_hw); +void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi, +			struct net_device *napi_dev, +			int (*poll)(struct napi_struct *, int), +			int weight) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	netif_napi_add(napi_dev, napi, poll, weight); +	local->napi = napi; +} +EXPORT_SYMBOL_GPL(ieee80211_napi_add); +  void ieee80211_unregister_hw(struct ieee80211_hw *hw)  {  	struct ieee80211_local *local = hw_to_local(hw); @@ -1047,6 +1123,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)  	cancel_work_sync(&local->restart_work);  	cancel_work_sync(&local->reconfig_filter); +	flush_work(&local->sched_scan_stopped_work);  	ieee80211_clear_tx_pending(local);  	rate_control_deinitialize(local); @@ -1087,6 +1164,8 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)  		     ieee80211_free_ack_frame, NULL);  	idr_destroy(&local->ack_status_frames); +	kfree(rcu_access_pointer(local->tx_latency)); +  	wiphy_free(local->hw.wiphy);  }  EXPORT_SYMBOL(ieee80211_free_hw); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 707ac61d63e..6495a3f0428 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -12,6 +12,7 @@  #include <asm/unaligned.h>  #include "ieee80211_i.h"  #include "mesh.h" +#include "driver-ops.h"  static int mesh_allocated;  static struct kmem_cache *rm_cache; @@ -258,6 +259,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,  	*pos++ = WLAN_EID_MESH_CONFIG;  	*pos++ = meshconf_len; +	/* save a pointer for quick updates in pre-tbtt */ +	ifmsh->meshconf_offset = pos - skb->data; +  	/* Active path selection protocol ID */  	*pos++ = ifmsh->mesh_pp_id;  	/* Active path selection metric ID   */ @@ -362,20 +366,15 @@ int mesh_add_rsn_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)  		return 0;  	/* find RSN IE */ -	data = ifmsh->ie; -	while (data < ifmsh->ie + ifmsh->ie_len) { -		if (*data == WLAN_EID_RSN) { -			len = data[1] + 2; -			break; -		} -		data++; -	} +	data = cfg80211_find_ie(WLAN_EID_RSN, ifmsh->ie, ifmsh->ie_len); +	if (!data) +		return 0; -	if (len) { -		if (skb_tailroom(skb) < len) -			return -ENOMEM; -		memcpy(skb_put(skb, len), data, len); -	} +	len = data[1] + 2; + +	if (skb_tailroom(skb) < len) +		return -ENOMEM; +	memcpy(skb_put(skb, len), data, len);  	return 0;  } @@ -610,6 +609,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)  	struct sk_buff *skb;  	struct ieee80211_mgmt *mgmt;  	struct ieee80211_chanctx_conf *chanctx_conf; +	struct mesh_csa_settings *csa;  	enum ieee80211_band band;  	u8 *pos;  	struct ieee80211_sub_if_data *sdata; @@ -624,6 +624,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)  	head_len = hdr_len +  		   2 + /* NULL SSID */ +		   /* Channel Switch Announcement */ +		   2 + sizeof(struct ieee80211_channel_sw_ie) + +		   /* Mesh Channel Swith Parameters */ +		   2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +  		   2 + 8 + /* supported rates */  		   2 + 3; /* DS params */  	tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + @@ -665,6 +669,35 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)  	*pos++ = WLAN_EID_SSID;  	*pos++ = 0x0; +	rcu_read_lock(); +	csa = rcu_dereference(ifmsh->csa); +	if (csa) { +		pos = skb_put(skb, 13); +		memset(pos, 0, 13); +		*pos++ = WLAN_EID_CHANNEL_SWITCH; +		*pos++ = 3; +		*pos++ = 0x0; +		*pos++ = ieee80211_frequency_to_channel( +				csa->settings.chandef.chan->center_freq); +		sdata->csa_counter_offset_beacon[0] = hdr_len + 6; +		*pos++ = csa->settings.count; +		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM; +		*pos++ = 6; +		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) { +			*pos++ = ifmsh->mshcfg.dot11MeshTTL; +			*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; +		} else { +			*pos++ = ifmsh->chsw_ttl; +		} +		*pos++ |= csa->settings.block_tx ? +			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; +		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); +		pos += 2; +		put_unaligned_le16(ifmsh->pre_value, pos); +		pos += 2; +	} +	rcu_read_unlock(); +  	if (ieee80211_add_srates_ie(sdata, skb, true, band) ||  	    mesh_add_ds_params_ie(sdata, skb))  		goto out_free; @@ -688,6 +721,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)  	bcn->tail_len = skb->len;  	memcpy(bcn->tail, skb->data, bcn->tail_len); +	bcn->meshconf = (struct ieee80211_meshconf_ie *) +					(bcn->tail + ifmsh->meshconf_offset);  	dev_kfree_skb(skb);  	rcu_assign_pointer(ifmsh->beacon, bcn); @@ -767,6 +802,7 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)  		return -ENOMEM;  	} +	ieee80211_recalc_dtim(local, sdata);  	ieee80211_bss_info_change_notify(sdata, changed);  	netif_carrier_on(sdata->dev); @@ -788,7 +824,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)  	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);  	bcn = rcu_dereference_protected(ifmsh->beacon,  					lockdep_is_held(&sdata->wdev.mtx)); -	rcu_assign_pointer(ifmsh->beacon, NULL); +	RCU_INIT_POINTER(ifmsh->beacon, NULL);  	kfree_rcu(bcn, rcu_head);  	/* flush STAs and mpaths on this iface */ @@ -812,6 +848,97 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)  	ieee80211_configure_filter(local);  } +static bool +ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, +				 struct ieee802_11_elems *elems, bool beacon) +{ +	struct cfg80211_csa_settings params; +	struct ieee80211_csa_ie csa_ie; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	int err; +	u32 sta_flags; + +	sdata_assert_lock(sdata); + +	sta_flags = IEEE80211_STA_DISABLE_VHT; +	switch (sdata->vif.bss_conf.chandef.width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +		sta_flags |= IEEE80211_STA_DISABLE_HT; +	case NL80211_CHAN_WIDTH_20: +		sta_flags |= IEEE80211_STA_DISABLE_40MHZ; +		break; +	default: +		break; +	} + +	memset(¶ms, 0, sizeof(params)); +	memset(&csa_ie, 0, sizeof(csa_ie)); +	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band, +					   sta_flags, sdata->vif.addr, +					   &csa_ie); +	if (err < 0) +		return false; +	if (err) +		return false; + +	params.chandef = csa_ie.chandef; +	params.count = csa_ie.count; + +	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, +				     IEEE80211_CHAN_DISABLED)) { +		sdata_info(sdata, +			   "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n", +			   sdata->vif.addr, +			   params.chandef.chan->center_freq, +			   params.chandef.width, +			   params.chandef.center_freq1, +			   params.chandef.center_freq2); +		return false; +	} + +	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, +					    ¶ms.chandef, +					    NL80211_IFTYPE_MESH_POINT); +	if (err < 0) +		return false; +	if (err > 0) +		/* TODO: DFS not (yet) supported */ +		return false; + +	params.radar_required = err; + +	if (cfg80211_chandef_identical(¶ms.chandef, +				       &sdata->vif.bss_conf.chandef)) { +		mcsa_dbg(sdata, +			 "received csa with an identical chandef, ignoring\n"); +		return true; +	} + +	mcsa_dbg(sdata, +		 "received channel switch announcement to go to channel %d MHz\n", +		 params.chandef.chan->center_freq); + +	params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT; +	if (beacon) { +		ifmsh->chsw_ttl = csa_ie.ttl - 1; +		if (ifmsh->pre_value >= csa_ie.pre_value) +			return false; +		ifmsh->pre_value = csa_ie.pre_value; +	} + +	if (ifmsh->chsw_ttl >= ifmsh->mshcfg.dot11MeshTTL) +		return false; + +	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_REPEATER; + +	if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev, +				     ¶ms) < 0) +		return false; + +	return true; +} +  static void  ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,  			    struct ieee80211_mgmt *mgmt, size_t len) @@ -918,6 +1045,139 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,  	if (ifmsh->sync_ops)  		ifmsh->sync_ops->rx_bcn_presp(sdata,  			stype, mgmt, &elems, rx_status); + +	if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT && +	    !sdata->vif.csa_active) +		ieee80211_mesh_process_chnswitch(sdata, &elems, true); +} + +int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct mesh_csa_settings *tmp_csa_settings; +	int ret = 0; +	int changed = 0; + +	/* Reset the TTL value and Initiator flag */ +	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; +	ifmsh->chsw_ttl = 0; + +	/* Remove the CSA and MCSP elements from the beacon */ +	tmp_csa_settings = rcu_dereference(ifmsh->csa); +	RCU_INIT_POINTER(ifmsh->csa, NULL); +	if (tmp_csa_settings) +		kfree_rcu(tmp_csa_settings, rcu_head); +	ret = ieee80211_mesh_rebuild_beacon(sdata); +	if (ret) +		return -EINVAL; + +	changed |= BSS_CHANGED_BEACON; + +	mcsa_dbg(sdata, "complete switching to center freq %d MHz", +		 sdata->vif.bss_conf.chandef.chan->center_freq); +	return changed; +} + +int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct mesh_csa_settings *tmp_csa_settings; +	int ret = 0; + +	tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings), +				   GFP_ATOMIC); +	if (!tmp_csa_settings) +		return -ENOMEM; + +	memcpy(&tmp_csa_settings->settings, csa_settings, +	       sizeof(struct cfg80211_csa_settings)); + +	rcu_assign_pointer(ifmsh->csa, tmp_csa_settings); + +	ret = ieee80211_mesh_rebuild_beacon(sdata); +	if (ret) { +		tmp_csa_settings = rcu_dereference(ifmsh->csa); +		RCU_INIT_POINTER(ifmsh->csa, NULL); +		kfree_rcu(tmp_csa_settings, rcu_head); +		return ret; +	} + +	return BSS_CHANGED_BEACON; +} + +static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, +			       struct ieee80211_mgmt *mgmt, size_t len) +{ +	struct ieee80211_mgmt *mgmt_fwd; +	struct sk_buff *skb; +	struct ieee80211_local *local = sdata->local; +	u8 *pos = mgmt->u.action.u.chan_switch.variable; +	size_t offset_ttl; + +	skb = dev_alloc_skb(local->tx_headroom + len); +	if (!skb) +		return -ENOMEM; +	skb_reserve(skb, local->tx_headroom); +	mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len); + +	/* offset_ttl is based on whether the secondary channel +	 * offset is available or not. Substract 1 from the mesh TTL +	 * and disable the initiator flag before forwarding. +	 */ +	offset_ttl = (len < 42) ? 7 : 10; +	*(pos + offset_ttl) -= 1; +	*(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + +	memcpy(mgmt_fwd, mgmt, len); +	eth_broadcast_addr(mgmt_fwd->da); +	memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN); +	memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN); + +	ieee80211_tx_skb(sdata, skb); +	return 0; +} + +static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, +			      struct ieee80211_mgmt *mgmt, size_t len) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct ieee802_11_elems elems; +	u16 pre_value; +	bool fwd_csa = true; +	size_t baselen; +	u8 *pos; + +	if (mgmt->u.action.u.measurement.action_code != +	    WLAN_ACTION_SPCT_CHL_SWITCH) +		return; + +	pos = mgmt->u.action.u.chan_switch.variable; +	baselen = offsetof(struct ieee80211_mgmt, +			   u.action.u.chan_switch.variable); +	ieee802_11_parse_elems(pos, len - baselen, false, &elems); + +	ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl; +	if (!--ifmsh->chsw_ttl) +		fwd_csa = false; + +	pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value); +	if (ifmsh->pre_value >= pre_value) +		return; + +	ifmsh->pre_value = pre_value; + +	if (!sdata->vif.csa_active && +	    !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) { +		mcsa_dbg(sdata, "Failed to process CSA action frame"); +		return; +	} + +	/* forward or re-broadcast the CSA frame */ +	if (fwd_csa) { +		if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0) +			mcsa_dbg(sdata, "Failed to forward the CSA frame"); +	}  }  static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, @@ -939,6 +1199,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,  		if (mesh_action_is_path_sel(mgmt))  			mesh_rx_path_sel_frame(sdata, mgmt, len);  		break; +	case WLAN_CATEGORY_SPECTRUM_MGMT: +		mesh_rx_csa_frame(sdata, mgmt, len); +		break;  	}  } @@ -952,7 +1215,7 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  	sdata_lock(sdata);  	/* mesh already went down */ -	if (!sdata->wdev.mesh_id_len) +	if (!sdata->u.mesh.mesh_id_len)  		goto out;  	rx_status = IEEE80211_SKB_RXCB(skb); @@ -1005,7 +1268,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)  	sdata_lock(sdata);  	/* mesh already went down */ -	if (!sdata->wdev.mesh_id_len) +	if (!sdata->u.mesh.mesh_id_len)  		goto out;  	if (ifmsh->preq_queue_len && @@ -1056,13 +1319,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)  		    (unsigned long) sdata);  	ifmsh->accepting_plinks = true; -	ifmsh->preq_id = 0; -	ifmsh->sn = 0; -	ifmsh->num_gates = 0;  	atomic_set(&ifmsh->mpaths, 0);  	mesh_rmc_init(sdata);  	ifmsh->last_preq = jiffies;  	ifmsh->next_perr = jiffies; +	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;  	/* Allocate all mesh structures when creating the first mesh interface. */  	if (!mesh_allocated)  		ieee80211s_init(); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 2bc7fd2f787..f39a19f9090 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -215,8 +215,6 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,  bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,  			struct ieee802_11_elems *ie);  void mesh_ids_set_default(struct ieee80211_if_mesh *mesh); -void mesh_mgmt_ies_add(struct ieee80211_sub_if_data *sdata, -		       struct sk_buff *skb);  int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,  			 struct sk_buff *skb);  int mesh_add_meshid_ie(struct ieee80211_sub_if_data *sdata, @@ -303,8 +301,8 @@ void mesh_mpath_table_grow(void);  void mesh_mpp_table_grow(void);  /* Mesh paths */  int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata, -		       u8 ttl, const u8 *target, __le32 target_sn, -		       __le16 target_rcode, const u8 *ra); +		       u8 ttl, const u8 *target, u32 target_sn, +		       u16 target_rcode, const u8 *ra);  void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);  void mesh_path_flush_pending(struct mesh_path *mpath);  void mesh_path_tx_pending(struct mesh_path *mpath); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 486819cd02c..94758b9c9ed 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -37,7 +37,7 @@ static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)  	return get_unaligned_le32(preq_elem + offset);  } -static inline u32 u16_field_get(const u8 *preq_elem, int offset, bool ae) +static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)  {  	if (ae)  		offset += 6; @@ -102,12 +102,11 @@ enum mpath_frame_type {  static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};  static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, -				  const u8 *orig_addr, __le32 orig_sn, +				  const u8 *orig_addr, u32 orig_sn,  				  u8 target_flags, const u8 *target, -				  __le32 target_sn, const u8 *da, +				  u32 target_sn, const u8 *da,  				  u8 hop_count, u8 ttl, -				  __le32 lifetime, __le32 metric, -				  __le32 preq_id, +				  u32 lifetime, u32 metric, u32 preq_id,  				  struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_local *local = sdata->local; @@ -167,33 +166,33 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,  	if (action == MPATH_PREP) {  		memcpy(pos, target, ETH_ALEN);  		pos += ETH_ALEN; -		memcpy(pos, &target_sn, 4); +		put_unaligned_le32(target_sn, pos);  		pos += 4;  	} else {  		if (action == MPATH_PREQ) { -			memcpy(pos, &preq_id, 4); +			put_unaligned_le32(preq_id, pos);  			pos += 4;  		}  		memcpy(pos, orig_addr, ETH_ALEN);  		pos += ETH_ALEN; -		memcpy(pos, &orig_sn, 4); +		put_unaligned_le32(orig_sn, pos);  		pos += 4;  	} -	memcpy(pos, &lifetime, 4);	/* interval for RANN */ +	put_unaligned_le32(lifetime, pos); /* interval for RANN */  	pos += 4; -	memcpy(pos, &metric, 4); +	put_unaligned_le32(metric, pos);  	pos += 4;  	if (action == MPATH_PREQ) {  		*pos++ = 1; /* destination count */  		*pos++ = target_flags;  		memcpy(pos, target, ETH_ALEN);  		pos += ETH_ALEN; -		memcpy(pos, &target_sn, 4); +		put_unaligned_le32(target_sn, pos);  		pos += 4;  	} else if (action == MPATH_PREP) {  		memcpy(pos, orig_addr, ETH_ALEN);  		pos += ETH_ALEN; -		memcpy(pos, &orig_sn, 4); +		put_unaligned_le32(orig_sn, pos);  		pos += 4;  	} @@ -239,8 +238,8 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,   * frame directly but add it to the pending queue instead.   */  int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata, -		       u8 ttl, const u8 *target, __le32 target_sn, -		       __le16 target_rcode, const u8 *ra) +		       u8 ttl, const u8 *target, u32 target_sn, +		       u16 target_rcode, const u8 *ra)  {  	struct ieee80211_local *local = sdata->local;  	struct sk_buff *skb; @@ -254,13 +253,13 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,  		return -EAGAIN;  	skb = dev_alloc_skb(local->tx_headroom + -			    IEEE80211_ENCRYPT_HEADROOM + +			    sdata->encrypt_headroom +  			    IEEE80211_ENCRYPT_TAILROOM +  			    hdr_len +  			    2 + 15 /* PERR IE */);  	if (!skb)  		return -1; -	skb_reserve(skb, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM); +	skb_reserve(skb, local->tx_headroom + sdata->encrypt_headroom);  	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);  	memset(mgmt, 0, hdr_len);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | @@ -293,9 +292,9 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,  	pos++;  	memcpy(pos, target, ETH_ALEN);  	pos += ETH_ALEN; -	memcpy(pos, &target_sn, 4); +	put_unaligned_le32(target_sn, pos);  	pos += 4; -	memcpy(pos, &target_rcode, 2); +	put_unaligned_le16(target_rcode, pos);  	/* see note in function header */  	prepare_frame_for_deferred_tx(sdata, skb); @@ -545,9 +544,10 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,  		if (time_after(jiffies, ifmsh->last_sn_update +  					net_traversal_jiffies(sdata)) ||  		    time_before(jiffies, ifmsh->last_sn_update)) { -			target_sn = ++ifmsh->sn; +			++ifmsh->sn;  			ifmsh->last_sn_update = jiffies;  		} +		target_sn = ifmsh->sn;  	} else if (is_broadcast_ether_addr(target_addr) &&  		   (target_flags & IEEE80211_PREQ_TO_FLAG)) {  		rcu_read_lock(); @@ -592,10 +592,9 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,  		if (ttl != 0) {  			mhwmp_dbg(sdata, "replying to the PREQ\n");  			mesh_path_sel_frame_tx(MPATH_PREP, 0, orig_addr, -				cpu_to_le32(orig_sn), 0, target_addr, -				cpu_to_le32(target_sn), mgmt->sa, 0, ttl, -				cpu_to_le32(lifetime), cpu_to_le32(metric), -				0, sdata); +					       orig_sn, 0, target_addr, +					       target_sn, mgmt->sa, 0, ttl, +					       lifetime, metric, 0, sdata);  		} else {  			ifmsh->mshstats.dropped_frames_ttl++;  		} @@ -625,11 +624,9 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,  		}  		mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr, -				cpu_to_le32(orig_sn), target_flags, target_addr, -				cpu_to_le32(target_sn), da, -				hopcount, ttl, cpu_to_le32(lifetime), -				cpu_to_le32(metric), cpu_to_le32(preq_id), -				sdata); +				       orig_sn, target_flags, target_addr, +				       target_sn, da, hopcount, ttl, lifetime, +				       metric, preq_id, sdata);  		if (!is_multicast_ether_addr(da))  			ifmsh->mshstats.fwded_unicast++;  		else @@ -695,11 +692,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,  	target_sn = PREP_IE_TARGET_SN(prep_elem);  	orig_sn = PREP_IE_ORIG_SN(prep_elem); -	mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, -		cpu_to_le32(orig_sn), 0, target_addr, -		cpu_to_le32(target_sn), next_hop, hopcount, -		ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), -		0, sdata); +	mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, orig_sn, 0, +			       target_addr, target_sn, next_hop, hopcount, +			       ttl, lifetime, metric, 0, sdata);  	rcu_read_unlock();  	sdata->u.mesh.mshstats.fwded_unicast++; @@ -750,8 +745,7 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,  			if (!ifmsh->mshcfg.dot11MeshForwarding)  				goto endperr;  			mesh_path_error_tx(sdata, ttl, target_addr, -					   cpu_to_le32(target_sn), -					   cpu_to_le16(target_rcode), +					   target_sn, target_rcode,  					   broadcast_addr);  		} else  			spin_unlock_bh(&mpath->state_lock); @@ -847,11 +841,9 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,  	if (ifmsh->mshcfg.dot11MeshForwarding) {  		mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, -				       cpu_to_le32(orig_sn), -				       0, NULL, 0, broadcast_addr, -				       hopcount, ttl, cpu_to_le32(interval), -				       cpu_to_le32(metric + metric_txsta), -				       0, sdata); +				       orig_sn, 0, NULL, 0, broadcast_addr, +				       hopcount, ttl, interval, +				       metric + metric_txsta, 0, sdata);  	}  	rcu_read_unlock(); @@ -1049,11 +1041,9 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)  	spin_unlock_bh(&mpath->state_lock);  	da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr; -	mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr, -			cpu_to_le32(ifmsh->sn), target_flags, mpath->dst, -			cpu_to_le32(mpath->sn), da, 0, -			ttl, cpu_to_le32(lifetime), 0, -			cpu_to_le32(ifmsh->preq_id++), sdata); +	mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr, ifmsh->sn, +			       target_flags, mpath->dst, mpath->sn, da, 0, +			       ttl, lifetime, 0, ifmsh->preq_id++, sdata);  	mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout);  enddiscovery: @@ -1212,10 +1202,9 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)  	switch (ifmsh->mshcfg.dot11MeshHWMPRootMode) {  	case IEEE80211_PROACTIVE_RANN:  		mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr, -			       cpu_to_le32(++ifmsh->sn), -			       0, NULL, 0, broadcast_addr, -			       0, ifmsh->mshcfg.element_ttl, -			       cpu_to_le32(interval), 0, 0, sdata); +				       ++ifmsh->sn, 0, NULL, 0, broadcast_addr, +				       0, ifmsh->mshcfg.element_ttl, +				       interval, 0, 0, sdata);  		break;  	case IEEE80211_PROACTIVE_PREQ_WITH_PREP:  		flags |= IEEE80211_PREQ_PROACTIVE_PREP_FLAG; @@ -1224,11 +1213,10 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)  		target_flags |= IEEE80211_PREQ_TO_FLAG |  				IEEE80211_PREQ_USN_FLAG;  		mesh_path_sel_frame_tx(MPATH_PREQ, flags, sdata->vif.addr, -				cpu_to_le32(++ifmsh->sn), target_flags, -				(u8 *) broadcast_addr, 0, broadcast_addr, -				0, ifmsh->mshcfg.element_ttl, -				cpu_to_le32(interval), -				0, cpu_to_le32(ifmsh->preq_id++), sdata); +				       ++ifmsh->sn, target_flags, +				       (u8 *) broadcast_addr, 0, broadcast_addr, +				       0, ifmsh->mshcfg.element_ttl, interval, +				       0, ifmsh->preq_id++, sdata);  		break;  	default:  		mhwmp_dbg(sdata, "Proactive mechanism not supported\n"); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 89aacfd2756..cf032a8db9d 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -287,8 +287,10 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath,  	struct sk_buff_head failq;  	unsigned long flags; -	BUG_ON(gate_mpath == from_mpath); -	BUG_ON(!gate_mpath->next_hop); +	if (WARN_ON(gate_mpath == from_mpath)) +		return; +	if (WARN_ON(!gate_mpath->next_hop)) +		return;  	__skb_queue_head_init(&failq); @@ -722,7 +724,6 @@ void mesh_plink_broken(struct sta_info *sta)  	struct mpath_node *node;  	struct ieee80211_sub_if_data *sdata = sta->sdata;  	int i; -	__le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE);  	rcu_read_lock();  	tbl = rcu_dereference(mesh_paths); @@ -736,9 +737,9 @@ void mesh_plink_broken(struct sta_info *sta)  			++mpath->sn;  			spin_unlock_bh(&mpath->state_lock);  			mesh_path_error_tx(sdata, -					   sdata->u.mesh.mshcfg.element_ttl, -					   mpath->dst, cpu_to_le32(mpath->sn), -					   reason, bcast); +				sdata->u.mesh.mshcfg.element_ttl, +				mpath->dst, mpath->sn, +				WLAN_REASON_MESH_PATH_DEST_UNREACHABLE, bcast);  		}  	}  	rcu_read_unlock(); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 6b65d5055f5..e8f60aa2e84 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -19,12 +19,6 @@  #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \  				jiffies + HZ * t / 1000)) -/* We only need a valid sta if user configured a minimum rssi_threshold. */ -#define rssi_threshold_check(sta, sdata) \ -		(sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\ -		(sta && (s8) -ewma_read(&sta->avg_signal) > \ -		sdata->u.mesh.mshcfg.rssi_threshold)) -  enum plink_event {  	PLINK_UNDEFINED,  	OPN_ACPT, @@ -61,7 +55,17 @@ static const char * const mplevents[] = {  static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,  			       enum ieee80211_self_protected_actioncode action, -			       u8 *da, __le16 llid, __le16 plid, __le16 reason); +			       u8 *da, u16 llid, u16 plid, u16 reason); + + +/* We only need a valid sta if user configured a minimum rssi_threshold. */ +static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata, +				 struct sta_info *sta) +{ +	s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold; +	return rssi_threshold == 0 || +	       (sta && (s8) -ewma_read(&sta->avg_signal) > rssi_threshold); +}  /**   * mesh_plink_fsm_restart - restart a mesh peer link finite state machine @@ -222,7 +226,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)  	mesh_path_flush_by_nexthop(sta);  	ieee80211_mps_sta_status_update(sta); -	changed |= ieee80211_mps_local_status_update(sdata); +	changed |= ieee80211_mps_set_sta_local_pm(sta, +			NL80211_MESH_POWER_UNKNOWN);  	return changed;  } @@ -241,7 +246,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta)  	spin_lock_bh(&sta->lock);  	changed = __mesh_plink_deactivate(sta); -	sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED); +	sta->reason = WLAN_REASON_MESH_PEER_CANCELED;  	mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,  			    sta->sta.addr, sta->llid, sta->plid,  			    sta->reason); @@ -252,7 +257,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta)  static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,  			       enum ieee80211_self_protected_actioncode action, -			       u8 *da, __le16 llid, __le16 plid, __le16 reason) +			       u8 *da, u16 llid, u16 plid, u16 reason)  {  	struct ieee80211_local *local = sdata->local;  	struct sk_buff *skb; @@ -278,7 +283,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,  			    2 + 8 + /* peering IE */  			    sdata->u.mesh.ie_len);  	if (!skb) -		return -1; +		return err;  	info = IEEE80211_SKB_CB(skb);  	skb_reserve(skb, local->tx_headroom);  	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); @@ -300,7 +305,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,  		if (action == WLAN_SP_MESH_PEERING_CONFIRM) {  			/* AID */  			pos = skb_put(skb, 2); -			memcpy(pos + 2, &plid, 2); +			put_unaligned_le16(plid, pos + 2);  		}  		if (ieee80211_add_srates_ie(sdata, skb, true, band) ||  		    ieee80211_add_ext_srates_ie(sdata, skb, true, band) || @@ -342,14 +347,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,  	*pos++ = ie_len;  	memcpy(pos, &peering_proto, 2);  	pos += 2; -	memcpy(pos, &llid, 2); +	put_unaligned_le16(llid, pos);  	pos += 2;  	if (include_plid) { -		memcpy(pos, &plid, 2); +		put_unaligned_le16(plid, pos);  		pos += 2;  	}  	if (action == WLAN_SP_MESH_PEERING_CLOSE) { -		memcpy(pos, &reason, 2); +		put_unaligned_le16(reason, pos);  		pos += 2;  	} @@ -432,6 +437,7 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)  	sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);  	set_sta_flag(sta, WLAN_STA_WME); +	sta->sta.wme = true;  	return sta;  } @@ -517,7 +523,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,  	    sta->plink_state == NL80211_PLINK_LISTEN &&  	    sdata->u.mesh.accepting_plinks &&  	    sdata->u.mesh.mshcfg.auto_open_plinks && -	    rssi_threshold_check(sta, sdata)) +	    rssi_threshold_check(sdata, sta))  		changed = mesh_plink_open(sta);  	ieee80211_mps_frame_release(sta, elems); @@ -529,9 +535,10 @@ out:  static void mesh_plink_timer(unsigned long data)  {  	struct sta_info *sta; -	__le16 llid, plid, reason; +	u16 reason = 0;  	struct ieee80211_sub_if_data *sdata;  	struct mesh_config *mshcfg; +	enum ieee80211_self_protected_actioncode action = 0;  	/*  	 * This STA is valid because sta_info_destroy() will @@ -552,9 +559,6 @@ static void mesh_plink_timer(unsigned long data)  	mpl_dbg(sta->sdata,  		"Mesh plink timer for %pM fired on state %s\n",  		sta->sta.addr, mplstates[sta->plink_state]); -	reason = 0; -	llid = sta->llid; -	plid = sta->plid;  	sdata = sta->sdata;  	mshcfg = &sdata->u.mesh.mshcfg; @@ -573,33 +577,31 @@ static void mesh_plink_timer(unsigned long data)  					     rand % sta->plink_timeout;  			++sta->plink_retries;  			mod_plink_timer(sta, sta->plink_timeout); -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, -					    sta->sta.addr, llid, 0, 0); +			action = WLAN_SP_MESH_PEERING_OPEN;  			break;  		} -		reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES); +		reason = WLAN_REASON_MESH_MAX_RETRIES;  		/* fall through on else */  	case NL80211_PLINK_CNF_RCVD:  		/* confirm timer */  		if (!reason) -			reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT); +			reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;  		sta->plink_state = NL80211_PLINK_HOLDING;  		mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); -		spin_unlock_bh(&sta->lock); -		mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, -				    sta->sta.addr, llid, plid, reason); +		action = WLAN_SP_MESH_PEERING_CLOSE;  		break;  	case NL80211_PLINK_HOLDING:  		/* holding timer */  		del_timer(&sta->plink_timer);  		mesh_plink_fsm_restart(sta); -		spin_unlock_bh(&sta->lock);  		break;  	default: -		spin_unlock_bh(&sta->lock);  		break;  	} +	spin_unlock_bh(&sta->lock); +	if (action) +		mesh_plink_frame_tx(sdata, action, sta->sta.addr, +				    sta->llid, sta->plid, reason);  }  static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) @@ -611,9 +613,40 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)  	add_timer(&sta->plink_timer);  } +static bool llid_in_use(struct ieee80211_sub_if_data *sdata, +			u16 llid) +{ +	struct ieee80211_local *local = sdata->local; +	bool in_use = false; +	struct sta_info *sta; + +	rcu_read_lock(); +	list_for_each_entry_rcu(sta, &local->sta_list, list) { +		if (!memcmp(&sta->llid, &llid, sizeof(llid))) { +			in_use = true; +			break; +		} +	} +	rcu_read_unlock(); + +	return in_use; +} + +static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata) +{ +	u16 llid; + +	do { +		get_random_bytes(&llid, sizeof(llid)); +		/* for mesh PS we still only have the AID range for TIM bits */ +		llid = (llid % IEEE80211_MAX_AID) + 1; +	} while (llid_in_use(sdata, llid)); + +	return llid; +} +  u32 mesh_plink_open(struct sta_info *sta)  { -	__le16 llid;  	struct ieee80211_sub_if_data *sdata = sta->sdata;  	u32 changed; @@ -621,8 +654,7 @@ u32 mesh_plink_open(struct sta_info *sta)  		return 0;  	spin_lock_bh(&sta->lock); -	get_random_bytes(&llid, 2); -	sta->llid = llid; +	sta->llid = mesh_get_new_llid(sdata);  	if (sta->plink_state != NL80211_PLINK_LISTEN &&  	    sta->plink_state != NL80211_PLINK_BLOCKED) {  		spin_unlock_bh(&sta->lock); @@ -639,7 +671,7 @@ u32 mesh_plink_open(struct sta_info *sta)  	changed = ieee80211_mps_local_status_update(sdata);  	mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, -			    sta->sta.addr, llid, 0, 0); +			    sta->sta.addr, sta->llid, 0, 0);  	return changed;  } @@ -655,390 +687,147 @@ u32 mesh_plink_block(struct sta_info *sta)  	return changed;  } - -void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, -			 struct ieee80211_mgmt *mgmt, size_t len, -			 struct ieee80211_rx_status *rx_status) +static void mesh_plink_close(struct ieee80211_sub_if_data *sdata, +			     struct sta_info *sta, +			     enum plink_event event)  {  	struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; -	struct ieee802_11_elems elems; -	struct sta_info *sta; -	enum plink_event event; -	enum ieee80211_self_protected_actioncode ftype; -	size_t baselen; -	bool matches_local = true; -	u8 ie_len; -	u8 *baseaddr; -	u32 changed = 0; -	__le16 plid, llid, reason; - -	/* need action_code, aux */ -	if (len < IEEE80211_MIN_ACTION_SIZE + 3) -		return; - -	if (sdata->u.mesh.user_mpm) -		/* userspace must register for these */ -		return; - -	if (is_multicast_ether_addr(mgmt->da)) { -		mpl_dbg(sdata, -			"Mesh plink: ignore frame from multicast address\n"); -		return; -	} - -	baseaddr = mgmt->u.action.u.self_prot.variable; -	baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; -	if (mgmt->u.action.u.self_prot.action_code == -						WLAN_SP_MESH_PEERING_CONFIRM) { -		baseaddr += 4; -		baselen += 4; -	} -	ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); - -	if (!elems.peering) { -		mpl_dbg(sdata, -			"Mesh plink: missing necessary peer link ie\n"); -		return; -	} -	if (elems.rsn_len && -	    sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { -		mpl_dbg(sdata, -			"Mesh plink: can't establish link with secure peer\n"); -		return; -	} +	u16 reason = (event == CLS_ACPT) ? +		     WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG; -	ftype = mgmt->u.action.u.self_prot.action_code; -	ie_len = elems.peering_len; -	if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || -	    (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || -	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 -							&& ie_len != 8)) { -		mpl_dbg(sdata, -			"Mesh plink: incorrect plink ie length %d %d\n", -			ftype, ie_len); -		return; -	} - -	if (ftype != WLAN_SP_MESH_PEERING_CLOSE && -	    (!elems.mesh_id || !elems.mesh_config)) { -		mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); -		return; -	} -	/* Note the lines below are correct, the llid in the frame is the plid -	 * from the point of view of this host. -	 */ -	memcpy(&plid, PLINK_GET_LLID(elems.peering), 2); -	if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || -	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) -		memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); - -	/* WARNING: Only for sta pointer, is dropped & re-acquired */ -	rcu_read_lock(); - -	sta = sta_info_get(sdata, mgmt->sa); -	if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) { -		mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); -		rcu_read_unlock(); -		return; -	} - -	if (ftype == WLAN_SP_MESH_PEERING_OPEN && -	    !rssi_threshold_check(sta, sdata)) { -		mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", -			mgmt->sa); -		rcu_read_unlock(); -		return; -	} - -	if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { -		mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); -		rcu_read_unlock(); -		return; -	} +	sta->reason = reason; +	sta->plink_state = NL80211_PLINK_HOLDING; +	mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); +} -	if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) { -		rcu_read_unlock(); -		return; -	} +static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata, +				struct sta_info *sta) +{ +	struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; +	u32 changed = 0; -	/* Now we will figure out the appropriate event... */ -	event = PLINK_UNDEFINED; -	if (ftype != WLAN_SP_MESH_PEERING_CLOSE && -	    !mesh_matches_local(sdata, &elems)) { -		matches_local = false; -		switch (ftype) { -		case WLAN_SP_MESH_PEERING_OPEN: -			event = OPN_RJCT; -			break; -		case WLAN_SP_MESH_PEERING_CONFIRM: -			event = CNF_RJCT; -			break; -		default: -			break; -		} -	} +	del_timer(&sta->plink_timer); +	sta->plink_state = NL80211_PLINK_ESTAB; +	changed |= mesh_plink_inc_estab_count(sdata); +	changed |= mesh_set_ht_prot_mode(sdata); +	changed |= mesh_set_short_slot_time(sdata); +	mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); +	ieee80211_mps_sta_status_update(sta); +	changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode); +	return changed; +} -	if (!sta && !matches_local) { -		rcu_read_unlock(); -		reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); -		llid = 0; -		mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, -				    mgmt->sa, llid, plid, reason); -		return; -	} else if (!sta) { -		/* ftype == WLAN_SP_MESH_PEERING_OPEN */ -		if (!mesh_plink_free_count(sdata)) { -			mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); -			rcu_read_unlock(); -			return; -		} -		event = OPN_ACPT; -	} else if (matches_local) { -		switch (ftype) { -		case WLAN_SP_MESH_PEERING_OPEN: -			if (!mesh_plink_free_count(sdata) || -			    (sta->plid && sta->plid != plid)) -				event = OPN_IGNR; -			else -				event = OPN_ACPT; -			break; -		case WLAN_SP_MESH_PEERING_CONFIRM: -			if (!mesh_plink_free_count(sdata) || -			    (sta->llid != llid || sta->plid != plid)) -				event = CNF_IGNR; -			else -				event = CNF_ACPT; -			break; -		case WLAN_SP_MESH_PEERING_CLOSE: -			if (sta->plink_state == NL80211_PLINK_ESTAB) -				/* Do not check for llid or plid. This does not -				 * follow the standard but since multiple plinks -				 * per sta are not supported, it is necessary in -				 * order to avoid a livelock when MP A sees an -				 * establish peer link to MP B but MP B does not -				 * see it. This can be caused by a timeout in -				 * B's peer link establishment or B beign -				 * restarted. -				 */ -				event = CLS_ACPT; -			else if (sta->plid != plid) -				event = CLS_IGNR; -			else if (ie_len == 7 && sta->llid != llid) -				event = CLS_IGNR; -			else -				event = CLS_ACPT; -			break; -		default: -			mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); -			rcu_read_unlock(); -			return; -		} -	} +/** + * mesh_plink_fsm - step @sta MPM based on @event + * + * @sdata: interface + * @sta: mesh neighbor + * @event: peering event + * + * Return: changed MBSS flags + */ +static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata, +			  struct sta_info *sta, enum plink_event event) +{ +	struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; +	enum ieee80211_self_protected_actioncode action = 0; +	u32 changed = 0; -	if (event == OPN_ACPT) { -		rcu_read_unlock(); -		/* allocate sta entry if necessary and update info */ -		sta = mesh_sta_info_get(sdata, mgmt->sa, &elems); -		if (!sta) { -			mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); -			rcu_read_unlock(); -			return; -		} -	} +	mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr, +		mplstates[sta->plink_state], mplevents[event]); -	mpl_dbg(sdata, "peer %pM in state %s got event %s\n", mgmt->sa, -		       mplstates[sta->plink_state], mplevents[event]); -	reason = 0;  	spin_lock_bh(&sta->lock);  	switch (sta->plink_state) { -		/* spin_unlock as soon as state is updated at each case */  	case NL80211_PLINK_LISTEN:  		switch (event) {  		case CLS_ACPT:  			mesh_plink_fsm_restart(sta); -			spin_unlock_bh(&sta->lock);  			break;  		case OPN_ACPT:  			sta->plink_state = NL80211_PLINK_OPN_RCVD; -			sta->plid = plid; -			get_random_bytes(&llid, 2); -			sta->llid = llid; +			sta->llid = mesh_get_new_llid(sdata);  			mesh_plink_timer_set(sta,  					     mshcfg->dot11MeshRetryTimeout);  			/* set the non-peer mode to active during peering */  			changed |= ieee80211_mps_local_status_update(sdata); - -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, -					    WLAN_SP_MESH_PEERING_OPEN, -					    sta->sta.addr, llid, 0, 0); -			mesh_plink_frame_tx(sdata, -					    WLAN_SP_MESH_PEERING_CONFIRM, -					    sta->sta.addr, llid, plid, 0); +			action = WLAN_SP_MESH_PEERING_OPEN;  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; -  	case NL80211_PLINK_OPN_SNT:  		switch (event) {  		case OPN_RJCT:  		case CNF_RJCT: -			reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);  		case CLS_ACPT: -			if (!reason) -				reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); -			sta->reason = reason; -			sta->plink_state = NL80211_PLINK_HOLDING; -			if (!mod_plink_timer(sta, -					     mshcfg->dot11MeshHoldingTimeout)) -				sta->ignore_plink_timer = true; - -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, -					    WLAN_SP_MESH_PEERING_CLOSE, -					    sta->sta.addr, llid, plid, reason); +			mesh_plink_close(sdata, sta, event); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		case OPN_ACPT:  			/* retry timer is left untouched */  			sta->plink_state = NL80211_PLINK_OPN_RCVD; -			sta->plid = plid; -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, -					    WLAN_SP_MESH_PEERING_CONFIRM, -					    sta->sta.addr, llid, plid, 0); +			action = WLAN_SP_MESH_PEERING_CONFIRM;  			break;  		case CNF_ACPT:  			sta->plink_state = NL80211_PLINK_CNF_RCVD;  			if (!mod_plink_timer(sta,  					     mshcfg->dot11MeshConfirmTimeout))  				sta->ignore_plink_timer = true; - -			spin_unlock_bh(&sta->lock);  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; -  	case NL80211_PLINK_OPN_RCVD:  		switch (event) {  		case OPN_RJCT:  		case CNF_RJCT: -			reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);  		case CLS_ACPT: -			if (!reason) -				reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); -			sta->reason = reason; -			sta->plink_state = NL80211_PLINK_HOLDING; -			if (!mod_plink_timer(sta, -					     mshcfg->dot11MeshHoldingTimeout)) -				sta->ignore_plink_timer = true; - -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, -					    sta->sta.addr, llid, plid, reason); +			mesh_plink_close(sdata, sta, event); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		case OPN_ACPT: -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, -					    WLAN_SP_MESH_PEERING_CONFIRM, -					    sta->sta.addr, llid, plid, 0); +			action = WLAN_SP_MESH_PEERING_CONFIRM;  			break;  		case CNF_ACPT: -			del_timer(&sta->plink_timer); -			sta->plink_state = NL80211_PLINK_ESTAB; -			spin_unlock_bh(&sta->lock); -			changed |= mesh_plink_inc_estab_count(sdata); -			changed |= mesh_set_ht_prot_mode(sdata); -			changed |= mesh_set_short_slot_time(sdata); -			mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", -				sta->sta.addr); -			ieee80211_mps_sta_status_update(sta); -			changed |= ieee80211_mps_set_sta_local_pm(sta, -						       mshcfg->power_mode); +			changed |= mesh_plink_establish(sdata, sta);  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; -  	case NL80211_PLINK_CNF_RCVD:  		switch (event) {  		case OPN_RJCT:  		case CNF_RJCT: -			reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);  		case CLS_ACPT: -			if (!reason) -				reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); -			sta->reason = reason; -			sta->plink_state = NL80211_PLINK_HOLDING; -			if (!mod_plink_timer(sta, -					     mshcfg->dot11MeshHoldingTimeout)) -				sta->ignore_plink_timer = true; - -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, -					    WLAN_SP_MESH_PEERING_CLOSE, -					    sta->sta.addr, llid, plid, reason); +			mesh_plink_close(sdata, sta, event); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		case OPN_ACPT: -			del_timer(&sta->plink_timer); -			sta->plink_state = NL80211_PLINK_ESTAB; -			spin_unlock_bh(&sta->lock); -			changed |= mesh_plink_inc_estab_count(sdata); -			changed |= mesh_set_ht_prot_mode(sdata); -			changed |= mesh_set_short_slot_time(sdata); -			mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", -				sta->sta.addr); -			mesh_plink_frame_tx(sdata, -					    WLAN_SP_MESH_PEERING_CONFIRM, -					    sta->sta.addr, llid, plid, 0); -			ieee80211_mps_sta_status_update(sta); -			changed |= ieee80211_mps_set_sta_local_pm(sta, -							mshcfg->power_mode); +			changed |= mesh_plink_establish(sdata, sta); +			action = WLAN_SP_MESH_PEERING_CONFIRM;  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; -  	case NL80211_PLINK_ESTAB:  		switch (event) {  		case CLS_ACPT: -			reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); -			sta->reason = reason;  			changed |= __mesh_plink_deactivate(sta); -			sta->plink_state = NL80211_PLINK_HOLDING; -			llid = sta->llid; -			mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); -			spin_unlock_bh(&sta->lock);  			changed |= mesh_set_ht_prot_mode(sdata);  			changed |= mesh_set_short_slot_time(sdata); -			mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, -					    sta->sta.addr, llid, plid, reason); +			mesh_plink_close(sdata, sta, event); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		case OPN_ACPT: -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, -					    WLAN_SP_MESH_PEERING_CONFIRM, -					    sta->sta.addr, llid, plid, 0); +			action = WLAN_SP_MESH_PEERING_CONFIRM;  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; @@ -1048,32 +837,271 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,  			if (del_timer(&sta->plink_timer))  				sta->ignore_plink_timer = 1;  			mesh_plink_fsm_restart(sta); -			spin_unlock_bh(&sta->lock);  			break;  		case OPN_ACPT:  		case CNF_ACPT:  		case OPN_RJCT:  		case CNF_RJCT: -			llid = sta->llid; -			reason = sta->reason; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, -					    sta->sta.addr, llid, plid, reason); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		default: -			spin_unlock_bh(&sta->lock); +			break;  		}  		break;  	default:  		/* should not get here, PLINK_BLOCKED is dealt with at the  		 * beginning of the function  		 */ -		spin_unlock_bh(&sta->lock);  		break;  	} +	spin_unlock_bh(&sta->lock); +	if (action) { +		mesh_plink_frame_tx(sdata, action, sta->sta.addr, +				    sta->llid, sta->plid, sta->reason); + +		/* also send confirm in open case */ +		if (action == WLAN_SP_MESH_PEERING_OPEN) { +			mesh_plink_frame_tx(sdata, +					    WLAN_SP_MESH_PEERING_CONFIRM, +					    sta->sta.addr, sta->llid, +					    sta->plid, 0); +		} +	} + +	return changed; +} + +/* + * mesh_plink_get_event - get correct MPM event + * + * @sdata: interface + * @sta: peer, leave NULL if processing a frame from a new suitable peer + * @elems: peering management IEs + * @ftype: frame type + * @llid: peer's peer link ID + * @plid: peer's local link ID + * + * Return: new peering event for @sta, but PLINK_UNDEFINED should be treated as + * an error. + */ +static enum plink_event +mesh_plink_get_event(struct ieee80211_sub_if_data *sdata, +		     struct sta_info *sta, +		     struct ieee802_11_elems *elems, +		     enum ieee80211_self_protected_actioncode ftype, +		     u16 llid, u16 plid) +{ +	enum plink_event event = PLINK_UNDEFINED; +	u8 ie_len = elems->peering_len; +	bool matches_local; + +	matches_local = (ftype == WLAN_SP_MESH_PEERING_CLOSE || +			 mesh_matches_local(sdata, elems)); + +	/* deny open request from non-matching peer */ +	if (!matches_local && !sta) { +		event = OPN_RJCT; +		goto out; +	} + +	if (!sta) { +		if (ftype != WLAN_SP_MESH_PEERING_OPEN) { +			mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); +			goto out; +		} +		/* ftype == WLAN_SP_MESH_PEERING_OPEN */ +		if (!mesh_plink_free_count(sdata)) { +			mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); +			goto out; +		} +	} else { +		if (!test_sta_flag(sta, WLAN_STA_AUTH)) { +			mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); +			goto out; +		} +		if (sta->plink_state == NL80211_PLINK_BLOCKED) +			goto out; +	} + +	/* new matching peer */ +	if (!sta) { +		event = OPN_ACPT; +		goto out; +	} + +	switch (ftype) { +	case WLAN_SP_MESH_PEERING_OPEN: +		if (!matches_local) +			event = OPN_RJCT; +		if (!mesh_plink_free_count(sdata) || +		    (sta->plid && sta->plid != plid)) +			event = OPN_IGNR; +		else +			event = OPN_ACPT; +		break; +	case WLAN_SP_MESH_PEERING_CONFIRM: +		if (!matches_local) +			event = CNF_RJCT; +		if (!mesh_plink_free_count(sdata) || +		    (sta->llid != llid || sta->plid != plid)) +			event = CNF_IGNR; +		else +			event = CNF_ACPT; +		break; +	case WLAN_SP_MESH_PEERING_CLOSE: +		if (sta->plink_state == NL80211_PLINK_ESTAB) +			/* Do not check for llid or plid. This does not +			 * follow the standard but since multiple plinks +			 * per sta are not supported, it is necessary in +			 * order to avoid a livelock when MP A sees an +			 * establish peer link to MP B but MP B does not +			 * see it. This can be caused by a timeout in +			 * B's peer link establishment or B beign +			 * restarted. +			 */ +			event = CLS_ACPT; +		else if (sta->plid != plid) +			event = CLS_IGNR; +		else if (ie_len == 8 && sta->llid != llid) +			event = CLS_IGNR; +		else +			event = CLS_ACPT; +		break; +	default: +		mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); +		break; +	} + +out: +	return event; +} +static void +mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, +			 struct ieee80211_mgmt *mgmt, +			 struct ieee802_11_elems *elems) +{ + +	struct sta_info *sta; +	enum plink_event event; +	enum ieee80211_self_protected_actioncode ftype; +	u32 changed = 0; +	u8 ie_len = elems->peering_len; +	__le16 _plid, _llid; +	u16 plid, llid = 0; + +	if (!elems->peering) { +		mpl_dbg(sdata, +			"Mesh plink: missing necessary peer link ie\n"); +		return; +	} + +	if (elems->rsn_len && +	    sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { +		mpl_dbg(sdata, +			"Mesh plink: can't establish link with secure peer\n"); +		return; +	} + +	ftype = mgmt->u.action.u.self_prot.action_code; +	if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || +	    (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || +	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 +							&& ie_len != 8)) { +		mpl_dbg(sdata, +			"Mesh plink: incorrect plink ie length %d %d\n", +			ftype, ie_len); +		return; +	} + +	if (ftype != WLAN_SP_MESH_PEERING_CLOSE && +	    (!elems->mesh_id || !elems->mesh_config)) { +		mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); +		return; +	} +	/* Note the lines below are correct, the llid in the frame is the plid +	 * from the point of view of this host. +	 */ +	memcpy(&_plid, PLINK_GET_LLID(elems->peering), sizeof(__le16)); +	plid = le16_to_cpu(_plid); +	if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || +	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) { +		memcpy(&_llid, PLINK_GET_PLID(elems->peering), sizeof(__le16)); +		llid = le16_to_cpu(_llid); +	} + +	/* WARNING: Only for sta pointer, is dropped & re-acquired */ +	rcu_read_lock(); + +	sta = sta_info_get(sdata, mgmt->sa); + +	if (ftype == WLAN_SP_MESH_PEERING_OPEN && +	    !rssi_threshold_check(sdata, sta)) { +		mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", +			mgmt->sa); +		goto unlock_rcu; +	} + +	/* Now we will figure out the appropriate event... */ +	event = mesh_plink_get_event(sdata, sta, elems, ftype, llid, plid); + +	if (event == OPN_ACPT) { +		rcu_read_unlock(); +		/* allocate sta entry if necessary and update info */ +		sta = mesh_sta_info_get(sdata, mgmt->sa, elems); +		if (!sta) { +			mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); +			goto unlock_rcu; +		} +		sta->plid = plid; +	} else if (!sta && event == OPN_RJCT) { +		mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, +				    mgmt->sa, 0, plid, +				    WLAN_REASON_MESH_CONFIG); +		goto unlock_rcu; +	} else if (!sta || event == PLINK_UNDEFINED) { +		/* something went wrong */ +		goto unlock_rcu; +	} + +	changed |= mesh_plink_fsm(sdata, sta, event); + +unlock_rcu:  	rcu_read_unlock();  	if (changed)  		ieee80211_mbss_info_change_notify(sdata, changed);  } + +void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, +			 struct ieee80211_mgmt *mgmt, size_t len, +			 struct ieee80211_rx_status *rx_status) +{ +	struct ieee802_11_elems elems; +	size_t baselen; +	u8 *baseaddr; + +	/* need action_code, aux */ +	if (len < IEEE80211_MIN_ACTION_SIZE + 3) +		return; + +	if (sdata->u.mesh.user_mpm) +		/* userspace must register for these */ +		return; + +	if (is_multicast_ether_addr(mgmt->da)) { +		mpl_dbg(sdata, +			"Mesh plink: ignore frame from multicast address\n"); +		return; +	} + +	baseaddr = mgmt->u.action.u.self_prot.variable; +	baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; +	if (mgmt->u.action.u.self_prot.action_code == +						WLAN_SP_MESH_PEERING_CONFIRM) { +		baseaddr += 4; +		baselen += 4; +	} +	ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); +	mesh_process_plink_frame(sdata, mgmt, &elems); +} diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c index 22290a929b9..ad8b377b4b9 100644 --- a/net/mac80211/mesh_ps.c +++ b/net/mac80211/mesh_ps.c @@ -36,6 +36,7 @@ static struct sk_buff *mps_qos_null_get(struct sta_info *sta)  				      sdata->vif.addr);  	nullfunc->frame_control = fc;  	nullfunc->duration_id = 0; +	nullfunc->seq_ctrl = 0;  	/* no address resolution for this frame -> set addr 1 immediately */  	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);  	memset(skb_put(skb, 2), 0, 2); /* append QoS control field */ @@ -152,6 +153,9 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,  {  	struct ieee80211_sub_if_data *sdata = sta->sdata; +	if (sta->local_pm == pm) +		return 0; +  	mps_dbg(sdata, "local STA operates in mode %d with %pM\n",  		pm, sta->sta.addr); @@ -245,6 +249,14 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)  	do_buffer = (pm != NL80211_MESH_POWER_ACTIVE); +	/* clear the MPSP flags for non-peers or active STA */ +	if (sta->plink_state != NL80211_PLINK_ESTAB) { +		clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); +		clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); +	} else if (!do_buffer) { +		clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); +	} +  	/* Don't let the same PS state be set twice */  	if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)  		return; @@ -257,14 +269,6 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)  	} else {  		ieee80211_sta_ps_deliver_wakeup(sta);  	} - -	/* clear the MPSP flags for non-peers or active STA */ -	if (sta->plink_state != NL80211_PLINK_ESTAB) { -		clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); -		clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); -	} else if (!do_buffer) { -		clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); -	}  }  static void mps_set_sta_peer_pm(struct sta_info *sta, @@ -444,8 +448,7 @@ static void mpsp_qos_null_append(struct sta_info *sta,   */  static void mps_frame_deliver(struct sta_info *sta, int n_frames)  { -	struct ieee80211_sub_if_data *sdata = sta->sdata; -	struct ieee80211_local *local = sdata->local; +	struct ieee80211_local *local = sta->sdata->local;  	int ac;  	struct sk_buff_head frames;  	struct sk_buff *skb; @@ -558,10 +561,10 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,  }  /** - * ieee80211_mps_frame_release - release buffered frames in response to beacon + * ieee80211_mps_frame_release - release frames buffered due to mesh power save   *   * @sta: mesh STA - * @elems: beacon IEs + * @elems: IEs of beacon or probe response   *   * For peers if we have individually-addressed frames buffered or the peer   * indicates buffered frames, send a corresponding MPSP trigger frame. Since @@ -574,10 +577,9 @@ void ieee80211_mps_frame_release(struct sta_info *sta,  	int ac, buffer_local = 0;  	bool has_buffered = false; -	/* TIM map only for LLID <= IEEE80211_MAX_AID */  	if (sta->plink_state == NL80211_PLINK_ESTAB)  		has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len, -				le16_to_cpu(sta->llid) % IEEE80211_MAX_AID); +						   sta->llid);  	if (has_buffered)  		mps_dbg(sta->sdata, "%pM indicates buffered frames\n", @@ -588,9 +590,10 @@ void ieee80211_mps_frame_release(struct sta_info *sta,  	    (!elems->awake_window || !le16_to_cpu(*elems->awake_window)))  		return; -	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) -		buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + -				skb_queue_len(&sta->tx_filtered[ac]); +	if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) +		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) +			buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + +					skb_queue_len(&sta->tx_filtered[ac]);  	if (!has_buffered && !buffer_local)  		return; diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 05a256b38e2..09625d6205c 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -92,12 +92,20 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,  	if (stype != IEEE80211_STYPE_BEACON)  		return; -	/* The current tsf is a first approximation for the timestamp -	 * for the received beacon.  Further down we try to get a -	 * better value from the rx_status->mactime field if -	 * available. Also we have to call drv_get_tsf() before -	 * entering the rcu-read section.*/ -	t_r = drv_get_tsf(local, sdata); +	/* +	 * Get time when timestamp field was received.  If we don't +	 * have rx timestamps, then use current tsf as an approximation. +	 * drv_get_tsf() must be called before entering the rcu-read +	 * section. +	 */ +	if (ieee80211_have_rx_timestamp(rx_status)) +		t_r = ieee80211_calculate_rx_timestamp(local, rx_status, +						       24 + 12 + +						       elems->total_len + +						       FCS_LEN, +						       24); +	else +		t_r = drv_get_tsf(local, sdata);  	rcu_read_lock();  	sta = sta_info_get(sdata, mgmt->sa); @@ -117,14 +125,6 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,  		goto no_sync;  	} -	if (ieee80211_have_rx_timestamp(rx_status)) -		/* time when timestamp field was received */ -		t_r = ieee80211_calculate_rx_timestamp(local, rx_status, -						       24 + 12 + -						       elems->total_len + -						       FCS_LEN, -						       24); -  	/* Timing offset calculation (see 13.13.2.2.2) */  	t_t = le64_to_cpu(mgmt->u.beacon.timestamp);  	sta->t_offset = t_t - t_r; @@ -164,12 +164,15 @@ no_sync:  	rcu_read_unlock();  } -static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) +static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata, +					 struct beacon_data *beacon)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u8 cap;  	WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); -	BUG_ON(!rcu_read_lock_held()); +	WARN_ON(!rcu_read_lock_held()); +	cap = beacon->meshconf->meshconf_cap;  	spin_lock_bh(&ifmsh->sync_offset_lock); @@ -194,6 +197,10 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)  		ifmsh->adjusting_tbtt = false;  	}  	spin_unlock_bh(&ifmsh->sync_offset_lock); + +	beacon->meshconf->meshconf_cap = ifmsh->adjusting_tbtt ? +			IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING | cap : +			~IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING & cap;  }  static const struct sync_method sync_methods[] = { diff --git a/net/mac80211/michael.h b/net/mac80211/michael.h index 3b848dad958..0e4886f881f 100644 --- a/net/mac80211/michael.h +++ b/net/mac80211/michael.h @@ -11,6 +11,7 @@  #define MICHAEL_H  #include <linux/types.h> +#include <linux/ieee80211.h>  #define MICHAEL_MIC_LEN 8 diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 86e4ad56b57..3345401be1b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -131,13 +131,13 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)  	if (unlikely(!sdata->u.mgd.associated))  		return; +	ifmgd->probe_send_count = 0; +  	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)  		return;  	mod_timer(&sdata->u.mgd.conn_mon_timer,  		  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); - -	ifmgd->probe_send_count = 0;  }  static int ecw2cw(int ecw) @@ -145,66 +145,6 @@ static int ecw2cw(int ecw)  	return (1 << ecw) - 1;  } -static u32 chandef_downgrade(struct cfg80211_chan_def *c) -{ -	u32 ret; -	int tmp; - -	switch (c->width) { -	case NL80211_CHAN_WIDTH_20: -		c->width = NL80211_CHAN_WIDTH_20_NOHT; -		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; -		break; -	case NL80211_CHAN_WIDTH_40: -		c->width = NL80211_CHAN_WIDTH_20; -		c->center_freq1 = c->chan->center_freq; -		ret = IEEE80211_STA_DISABLE_40MHZ | -		      IEEE80211_STA_DISABLE_VHT; -		break; -	case NL80211_CHAN_WIDTH_80: -		tmp = (30 + c->chan->center_freq - c->center_freq1)/20; -		/* n_P40 */ -		tmp /= 2; -		/* freq_P40 */ -		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; -		c->width = NL80211_CHAN_WIDTH_40; -		ret = IEEE80211_STA_DISABLE_VHT; -		break; -	case NL80211_CHAN_WIDTH_80P80: -		c->center_freq2 = 0; -		c->width = NL80211_CHAN_WIDTH_80; -		ret = IEEE80211_STA_DISABLE_80P80MHZ | -		      IEEE80211_STA_DISABLE_160MHZ; -		break; -	case NL80211_CHAN_WIDTH_160: -		/* n_P20 */ -		tmp = (70 + c->chan->center_freq - c->center_freq1)/20; -		/* n_P80 */ -		tmp /= 4; -		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; -		c->width = NL80211_CHAN_WIDTH_80; -		ret = IEEE80211_STA_DISABLE_80P80MHZ | -		      IEEE80211_STA_DISABLE_160MHZ; -		break; -	default: -	case NL80211_CHAN_WIDTH_20_NOHT: -		WARN_ON_ONCE(1); -		c->width = NL80211_CHAN_WIDTH_20_NOHT; -		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; -		break; -	case NL80211_CHAN_WIDTH_5: -	case NL80211_CHAN_WIDTH_10: -		WARN_ON_ONCE(1); -		/* keep c->width */ -		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; -		break; -	} - -	WARN_ON_ONCE(!cfg80211_chandef_valid(c)); - -	return ret; -} -  static u32  ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  			     struct ieee80211_supported_band *sband, @@ -282,6 +222,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  	switch (vht_oper->chan_width) {  	case IEEE80211_VHT_CHANWIDTH_USE_HT:  		vht_chandef.width = chandef->width; +		vht_chandef.center_freq1 = chandef->center_freq1;  		break;  	case IEEE80211_VHT_CHANWIDTH_80MHZ:  		vht_chandef.width = NL80211_CHAN_WIDTH_80; @@ -331,6 +272,28 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  	ret = 0;  out: +	/* +	 * When tracking the current AP, don't do any further checks if the +	 * new chandef is identical to the one we're currently using for the +	 * connection. This keeps us from playing ping-pong with regulatory, +	 * without it the following can happen (for example): +	 *  - connect to an AP with 80 MHz, world regdom allows 80 MHz +	 *  - AP advertises regdom US +	 *  - CRDA loads regdom US with 80 MHz prohibited (old database) +	 *  - the code below detects an unsupported channel, downgrades, and +	 *    we disconnect from the AP in the caller +	 *  - disconnect causes CRDA to reload world regdomain and the game +	 *    starts anew. +	 * (see https://bugzilla.kernel.org/show_bug.cgi?id=70881) +	 * +	 * It seems possible that there are still scenarios with CSA or real +	 * bandwidth changes where a this could happen, but those cases are +	 * less common and wouldn't completely prevent using the AP. +	 */ +	if (tracking && +	    cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) +		return ret; +  	/* don't print the message below for VHT mismatch if VHT is disabled */  	if (ret & IEEE80211_STA_DISABLE_VHT)  		vht_chandef = *chandef; @@ -352,7 +315,7 @@ out:  			break;  		} -		ret |= chandef_downgrade(chandef); +		ret |= ieee80211_chandef_downgrade(chandef);  	}  	if (chandef->width != vht_chandef.width && !tracking) @@ -390,6 +353,16 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,  	if (WARN_ON_ONCE(!sta))  		return -EINVAL; +	/* +	 * if bss configuration changed store the new one - +	 * this may be applicable even if channel is identical +	 */ +	ht_opmode = le16_to_cpu(ht_oper->operation_mode); +	if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { +		*changed |= BSS_CHANGED_HT; +		sdata->vif.bss_conf.ht_operation_mode = ht_opmode; +	} +  	chan = sdata->vif.bss_conf.chandef.chan;  	sband = local->hw.wiphy->bands[chan->band]; @@ -406,13 +379,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,  	 */  	if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&  	    chandef.width == NL80211_CHAN_WIDTH_80P80) -		flags |= chandef_downgrade(&chandef); +		flags |= ieee80211_chandef_downgrade(&chandef);  	if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&  	    chandef.width == NL80211_CHAN_WIDTH_160) -		flags |= chandef_downgrade(&chandef); +		flags |= ieee80211_chandef_downgrade(&chandef);  	if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&  	    chandef.width > NL80211_CHAN_WIDTH_20) -		flags |= chandef_downgrade(&chandef); +		flags |= ieee80211_chandef_downgrade(&chandef);  	if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))  		return 0; @@ -476,14 +449,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,  					 IEEE80211_RC_BW_CHANGED);  	} -	ht_opmode = le16_to_cpu(ht_oper->operation_mode); - -	/* if bss configuration changed store the new one */ -	if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { -		*changed |= BSS_CHANGED_HT; -		sdata->vif.bss_conf.ht_operation_mode = ht_opmode; -	} -  	return 0;  } @@ -566,6 +531,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,  	u8 *pos;  	u32 cap;  	struct ieee80211_sta_vht_cap vht_cap; +	u32 mask, ap_bf_sts, our_bf_sts;  	BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); @@ -593,6 +559,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,  			cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))  		cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; +	mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + +	ap_bf_sts = le32_to_cpu(ap_vht_cap->vht_cap_info) & mask; +	our_bf_sts = cap & mask; + +	if (ap_bf_sts < our_bf_sts) { +		cap &= ~mask; +		cap |= ap_bf_sts; +	} +  	/* reserve and fill IE */  	pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);  	ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); @@ -774,7 +750,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)  	}  	/* if present, add any custom IEs that go before HT */ -	if (assoc_data->ie_len && assoc_data->ie) { +	if (assoc_data->ie_len) {  		static const u8 before_ht[] = {  			WLAN_EID_SSID,  			WLAN_EID_SUPP_RATES, @@ -803,12 +779,40 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)  		ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,  				    sband, chan, sdata->smps_mode); +	/* if present, add any custom IEs that go before VHT */ +	if (assoc_data->ie_len) { +		static const u8 before_vht[] = { +			WLAN_EID_SSID, +			WLAN_EID_SUPP_RATES, +			WLAN_EID_EXT_SUPP_RATES, +			WLAN_EID_PWR_CAPABILITY, +			WLAN_EID_SUPPORTED_CHANNELS, +			WLAN_EID_RSN, +			WLAN_EID_QOS_CAPA, +			WLAN_EID_RRM_ENABLED_CAPABILITIES, +			WLAN_EID_MOBILITY_DOMAIN, +			WLAN_EID_SUPPORTED_REGULATORY_CLASSES, +			WLAN_EID_HT_CAPABILITY, +			WLAN_EID_BSS_COEX_2040, +			WLAN_EID_EXT_CAPABILITY, +			WLAN_EID_QOS_TRAFFIC_CAPA, +			WLAN_EID_TIM_BCAST_REQ, +			WLAN_EID_INTERWORKING, +		}; +		noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, +					     before_vht, ARRAY_SIZE(before_vht), +					     offset); +		pos = skb_put(skb, noffset - offset); +		memcpy(pos, assoc_data->ie + offset, noffset - offset); +		offset = noffset; +	} +  	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))  		ieee80211_add_vht_ie(sdata, skb, sband,  				     &assoc_data->ap_vht_cap);  	/* if present, add any custom non-vendor IEs that go after HT */ -	if (assoc_data->ie_len && assoc_data->ie) { +	if (assoc_data->ie_len) {  		noffset = ieee80211_ie_split_vendor(assoc_data->ie,  						    assoc_data->ie_len,  						    offset); @@ -839,7 +843,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)  	}  	/* add any remaining custom (i.e. vendor specific here) IEs */ -	if (assoc_data->ie_len && assoc_data->ie) { +	if (assoc_data->ie_len) {  		noffset = assoc_data->ie_len;  		pos = skb_put(skb, noffset - offset);  		memcpy(pos, assoc_data->ie + offset, noffset - offset); @@ -893,8 +897,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,  	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)  		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; -	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | -			    IEEE80211_STA_CONNECTION_POLL)) +	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)  		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;  	ieee80211_tx_skb(sdata, skb); @@ -937,6 +940,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)  		container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	u32 changed = 0; +	int ret;  	if (!ieee80211_sdata_running(sdata))  		return; @@ -945,25 +950,48 @@ static void ieee80211_chswitch_work(struct work_struct *work)  	if (!ifmgd->associated)  		goto out; -	local->_oper_chandef = local->csa_chandef; +	mutex_lock(&local->mtx); +	ret = ieee80211_vif_change_channel(sdata, &changed); +	mutex_unlock(&local->mtx); +	if (ret) { +		sdata_info(sdata, +			   "vif channel switch failed, disconnecting\n"); +		ieee80211_queue_work(&sdata->local->hw, +				     &ifmgd->csa_connection_drop_work); +		goto out; +	} -	if (!local->ops->channel_switch) { -		/* call "hw_config" only if doing sw channel switch */ -		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); -	} else { -		/* update the device channel directly */ -		local->hw.conf.chandef = local->_oper_chandef; +	if (!local->use_chanctx) { +		local->_oper_chandef = sdata->csa_chandef; +		/* Call "hw_config" only if doing sw channel switch. +		 * Otherwise update the channel directly +		 */ +		if (!local->ops->channel_switch) +			ieee80211_hw_config(local, 0); +		else +			local->hw.conf.chandef = local->_oper_chandef;  	}  	/* XXX: shouldn't really modify cfg80211-owned data! */ -	ifmgd->associated->channel = local->_oper_chandef.chan; +	ifmgd->associated->channel = sdata->csa_chandef.chan; +	ieee80211_bss_info_change_notify(sdata, changed); + +	mutex_lock(&local->mtx); +	sdata->vif.csa_active = false;  	/* XXX: wait for a beacon first? */ -	ieee80211_wake_queues_by_reason(&local->hw, +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw,  					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_CSA); - out: +	mutex_unlock(&local->mtx); +  	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + +	ieee80211_sta_reset_beacon_monitor(sdata); +	ieee80211_sta_reset_conn_monitor(sdata); + +out:  	sdata_unlock(sdata);  } @@ -1000,20 +1028,10 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	struct cfg80211_bss *cbss = ifmgd->associated; -	struct ieee80211_bss *bss;  	struct ieee80211_chanctx *chanctx; -	enum ieee80211_band new_band; -	int new_freq; -	u8 new_chan_no; -	u8 count; -	u8 mode; -	struct ieee80211_channel *new_chan; -	struct cfg80211_chan_def new_chandef = {}; -	struct cfg80211_chan_def new_vht_chandef = {}; -	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; -	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; -	const struct ieee80211_ht_operation *ht_oper; -	int secondary_channel_offset = -1; +	enum ieee80211_band current_band; +	struct ieee80211_csa_ie csa_ie; +	int res;  	sdata_assert_lock(sdata); @@ -1027,187 +1045,58 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,  	if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)  		return; -	sec_chan_offs = elems->sec_chan_offs; -	wide_bw_chansw_ie = elems->wide_bw_chansw_ie; -	ht_oper = elems->ht_operation; - -	if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | -			    IEEE80211_STA_DISABLE_40MHZ)) { -		sec_chan_offs = NULL; -		wide_bw_chansw_ie = NULL; -		/* only used for bandwidth here */ -		ht_oper = NULL; -	} - -	if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) -		wide_bw_chansw_ie = NULL; - -	if (elems->ext_chansw_ie) { -		if (!ieee80211_operating_class_to_band( -				elems->ext_chansw_ie->new_operating_class, -				&new_band)) { -			sdata_info(sdata, -				   "cannot understand ECSA IE operating class %d, disconnecting\n", -				   elems->ext_chansw_ie->new_operating_class); -			ieee80211_queue_work(&local->hw, -					     &ifmgd->csa_connection_drop_work); -		} -		new_chan_no = elems->ext_chansw_ie->new_ch_num; -		count = elems->ext_chansw_ie->count; -		mode = elems->ext_chansw_ie->mode; -	} else if (elems->ch_switch_ie) { -		new_band = cbss->channel->band; -		new_chan_no = elems->ch_switch_ie->new_ch_num; -		count = elems->ch_switch_ie->count; -		mode = elems->ch_switch_ie->mode; -	} else { -		/* nothing here we understand */ +	current_band = cbss->channel->band; +	memset(&csa_ie, 0, sizeof(csa_ie)); +	res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band, +					   ifmgd->flags, +					   ifmgd->associated->bssid, &csa_ie); +	if (res	< 0) +		ieee80211_queue_work(&local->hw, +				     &ifmgd->csa_connection_drop_work); +	if (res)  		return; -	} - -	bss = (void *)cbss->priv; -	new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); -	new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); -	if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { +	if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, +				     IEEE80211_CHAN_DISABLED)) {  		sdata_info(sdata, -			   "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", -			   ifmgd->associated->bssid, new_freq); +			   "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", +			   ifmgd->associated->bssid, +			   csa_ie.chandef.chan->center_freq, +			   csa_ie.chandef.width, csa_ie.chandef.center_freq1, +			   csa_ie.chandef.center_freq2);  		ieee80211_queue_work(&local->hw,  				     &ifmgd->csa_connection_drop_work);  		return;  	} -	if (!beacon && sec_chan_offs) { -		secondary_channel_offset = sec_chan_offs->sec_chan_offs; -	} else if (beacon && ht_oper) { -		secondary_channel_offset = -			ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; -	} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { -		/* -		 * If it's not a beacon, HT is enabled and the IE not present, -		 * it's 20 MHz, 802.11-2012 8.5.2.6: -		 *	This element [the Secondary Channel Offset Element] is -		 *	present when switching to a 40 MHz channel. It may be -		 *	present when switching to a 20 MHz channel (in which -		 *	case the secondary channel offset is set to SCN). -		 */ -		secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; -	} - -	switch (secondary_channel_offset) { -	default: -		/* secondary_channel_offset was present but is invalid */ -	case IEEE80211_HT_PARAM_CHA_SEC_NONE: -		cfg80211_chandef_create(&new_chandef, new_chan, -					NL80211_CHAN_HT20); -		break; -	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: -		cfg80211_chandef_create(&new_chandef, new_chan, -					NL80211_CHAN_HT40PLUS); -		break; -	case IEEE80211_HT_PARAM_CHA_SEC_BELOW: -		cfg80211_chandef_create(&new_chandef, new_chan, -					NL80211_CHAN_HT40MINUS); -		break; -	case -1: -		cfg80211_chandef_create(&new_chandef, new_chan, -					NL80211_CHAN_NO_HT); -		/* keep width for 5/10 MHz channels */ -		switch (sdata->vif.bss_conf.chandef.width) { -		case NL80211_CHAN_WIDTH_5: -		case NL80211_CHAN_WIDTH_10: -			new_chandef.width = sdata->vif.bss_conf.chandef.width; -			break; -		default: -			break; -		} -		break; -	} +	ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; -	if (wide_bw_chansw_ie) { -		new_vht_chandef.chan = new_chan; -		new_vht_chandef.center_freq1 = -			ieee80211_channel_to_frequency( -				wide_bw_chansw_ie->new_center_freq_seg0, -				new_band); +	mutex_lock(&local->chanctx_mtx); +	if (local->use_chanctx) { +		u32 num_chanctx = 0; +		list_for_each_entry(chanctx, &local->chanctx_list, list) +		       num_chanctx++; -		switch (wide_bw_chansw_ie->new_channel_width) { -		default: -			/* hmmm, ignore VHT and use HT if present */ -		case IEEE80211_VHT_CHANWIDTH_USE_HT: -			new_vht_chandef.chan = NULL; -			break; -		case IEEE80211_VHT_CHANWIDTH_80MHZ: -			new_vht_chandef.width = NL80211_CHAN_WIDTH_80; -			break; -		case IEEE80211_VHT_CHANWIDTH_160MHZ: -			new_vht_chandef.width = NL80211_CHAN_WIDTH_160; -			break; -		case IEEE80211_VHT_CHANWIDTH_80P80MHZ: -			/* field is otherwise reserved */ -			new_vht_chandef.center_freq2 = -				ieee80211_channel_to_frequency( -					wide_bw_chansw_ie->new_center_freq_seg1, -					new_band); -			new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; -			break; -		} -		if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && -		    new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) -			chandef_downgrade(&new_vht_chandef); -		if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && -		    new_vht_chandef.width == NL80211_CHAN_WIDTH_160) -			chandef_downgrade(&new_vht_chandef); -		if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && -		    new_vht_chandef.width > NL80211_CHAN_WIDTH_20) -			chandef_downgrade(&new_vht_chandef); -	} - -	/* if VHT data is there validate & use it */ -	if (new_vht_chandef.chan) { -		if (!cfg80211_chandef_compatible(&new_vht_chandef, -						 &new_chandef)) { +		if (num_chanctx > 1 || +		    !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {  			sdata_info(sdata, -				   "AP %pM CSA has inconsistent channel data, disconnecting\n", -				   ifmgd->associated->bssid); +				   "not handling chan-switch with channel contexts\n");  			ieee80211_queue_work(&local->hw,  					     &ifmgd->csa_connection_drop_work); +			mutex_unlock(&local->chanctx_mtx);  			return;  		} -		new_chandef = new_vht_chandef;  	} -	if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef, -				     IEEE80211_CHAN_DISABLED)) { -		sdata_info(sdata, -			   "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", -			   ifmgd->associated->bssid, new_freq, -			   new_chandef.width, new_chandef.center_freq1, -			   new_chandef.center_freq2); -		ieee80211_queue_work(&local->hw, -				     &ifmgd->csa_connection_drop_work); -		return; -	} - -	ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; - -	if (local->use_chanctx) { -		sdata_info(sdata, -			   "not handling channel switch with channel contexts\n"); +	if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {  		ieee80211_queue_work(&local->hw,  				     &ifmgd->csa_connection_drop_work); -		return; -	} - -	mutex_lock(&local->chanctx_mtx); -	if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {  		mutex_unlock(&local->chanctx_mtx);  		return;  	}  	chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),  			       struct ieee80211_chanctx, conf); -	if (chanctx->refcount > 1) { +	if (ieee80211_chanctx_refcount(local, chanctx) > 1) {  		sdata_info(sdata,  			   "channel switch with multiple interfaces on the same channel, disconnecting\n");  		ieee80211_queue_work(&local->hw, @@ -1217,20 +1106,25 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,  	}  	mutex_unlock(&local->chanctx_mtx); -	local->csa_chandef = new_chandef; +	sdata->csa_chandef = csa_ie.chandef; + +	mutex_lock(&local->mtx); +	sdata->vif.csa_active = true; +	sdata->csa_block_tx = csa_ie.mode; -	if (mode) +	if (sdata->csa_block_tx)  		ieee80211_stop_queues_by_reason(&local->hw, -				IEEE80211_MAX_QUEUE_MAP, -				IEEE80211_QUEUE_STOP_REASON_CSA); +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx);  	if (local->ops->channel_switch) {  		/* use driver's channel switch callback */  		struct ieee80211_channel_switch ch_switch = {  			.timestamp = timestamp, -			.block_tx = mode, -			.chandef = new_chandef, -			.count = count, +			.block_tx = csa_ie.mode, +			.chandef = csa_ie.chandef, +			.count = csa_ie.count,  		};  		drv_channel_switch(local, &ch_switch); @@ -1238,11 +1132,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,  	}  	/* channel switch handled in software */ -	if (count <= 1) +	if (csa_ie.count <= 1)  		ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);  	else  		mod_timer(&ifmgd->chswitch_timer, -			  TU_TO_EXP_TIME(count * cbss->beacon_interval)); +			  TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));  }  static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, @@ -1374,8 +1268,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)  	if (!mgd->associated)  		return false; -	if (mgd->flags & (IEEE80211_STA_BEACON_POLL | -			  IEEE80211_STA_CONNECTION_POLL)) +	if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)  		return false;  	if (!mgd->have_beacon) @@ -1581,10 +1474,16 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)  	struct ieee80211_sub_if_data *sdata =  		container_of(delayed_work, struct ieee80211_sub_if_data,  			     dfs_cac_timer_work); +	struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chandef; -	ieee80211_vif_release_channel(sdata); - -	cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); +	mutex_lock(&sdata->local->mtx); +	if (sdata->wdev.cac_started) { +		ieee80211_vif_release_channel(sdata); +		cfg80211_cac_event(sdata->dev, &chandef, +				   NL80211_RADAR_CAC_FINISHED, +				   GFP_KERNEL); +	} +	mutex_unlock(&sdata->local->mtx);  }  /* MLME */ @@ -1691,8 +1590,7 @@ static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)  {  	lockdep_assert_held(&sdata->local->mtx); -	sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | -				IEEE80211_STA_BEACON_POLL); +	sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;  	ieee80211_run_deferred_scan(sdata->local);  } @@ -1879,7 +1777,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	memset(ifmgd->bssid, 0, ETH_ALEN);  	/* remove AP and TDLS peers */ -	sta_info_flush_defer(sdata); +	sta_info_flush(sdata);  	/* finally reset all BSS / config parameters */  	changed |= ieee80211_reset_erp_info(sdata); @@ -1928,7 +1826,17 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	ifmgd->have_beacon = false;  	ifmgd->flags = 0; +	mutex_lock(&local->mtx);  	ieee80211_vif_release_channel(sdata); + +	sdata->vif.csa_active = false; +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx); + +	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;  }  void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, @@ -1954,11 +1862,8 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)  	struct ieee80211_local *local = sdata->local;  	mutex_lock(&local->mtx); -	if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL | -			      IEEE80211_STA_CONNECTION_POLL))) { -		mutex_unlock(&local->mtx); -		return; -	} +	if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)) +		goto out;  	__ieee80211_stop_poll(sdata); @@ -2094,14 +1999,10 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,  	 * because otherwise we would reset the timer every time and  	 * never check whether we received a probe response!  	 */ -	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | -			    IEEE80211_STA_CONNECTION_POLL)) +	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)  		already = true; -	if (beacon) -		ifmgd->flags |= IEEE80211_STA_BEACON_POLL; -	else -		ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; +	ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;  	mutex_unlock(&sdata->local->mtx); @@ -2161,6 +2062,7 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get);  static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)  { +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -2174,9 +2076,14 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)  			       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,  			       true, frame_buf);  	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; -	ieee80211_wake_queues_by_reason(&sdata->local->hw, + +	mutex_lock(&local->mtx); +	sdata->vif.csa_active = false; +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw,  					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx);  	cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,  			      IEEE80211_DEAUTH_FRAME_LEN); @@ -2255,7 +2162,9 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,  		memset(sdata->u.mgd.bssid, 0, ETH_ALEN);  		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);  		sdata->u.mgd.flags = 0; +		mutex_lock(&sdata->local->mtx);  		ieee80211_vif_release_channel(sdata); +		mutex_unlock(&sdata->local->mtx);  	}  	cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss); @@ -2385,6 +2294,62 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,  	/* ignore frame -- wait for timeout */  } +#define case_WLAN(type) \ +	case WLAN_REASON_##type: return #type + +static const char *ieee80211_get_reason_code_string(u16 reason_code) +{ +	switch (reason_code) { +	case_WLAN(UNSPECIFIED); +	case_WLAN(PREV_AUTH_NOT_VALID); +	case_WLAN(DEAUTH_LEAVING); +	case_WLAN(DISASSOC_DUE_TO_INACTIVITY); +	case_WLAN(DISASSOC_AP_BUSY); +	case_WLAN(CLASS2_FRAME_FROM_NONAUTH_STA); +	case_WLAN(CLASS3_FRAME_FROM_NONASSOC_STA); +	case_WLAN(DISASSOC_STA_HAS_LEFT); +	case_WLAN(STA_REQ_ASSOC_WITHOUT_AUTH); +	case_WLAN(DISASSOC_BAD_POWER); +	case_WLAN(DISASSOC_BAD_SUPP_CHAN); +	case_WLAN(INVALID_IE); +	case_WLAN(MIC_FAILURE); +	case_WLAN(4WAY_HANDSHAKE_TIMEOUT); +	case_WLAN(GROUP_KEY_HANDSHAKE_TIMEOUT); +	case_WLAN(IE_DIFFERENT); +	case_WLAN(INVALID_GROUP_CIPHER); +	case_WLAN(INVALID_PAIRWISE_CIPHER); +	case_WLAN(INVALID_AKMP); +	case_WLAN(UNSUPP_RSN_VERSION); +	case_WLAN(INVALID_RSN_IE_CAP); +	case_WLAN(IEEE8021X_FAILED); +	case_WLAN(CIPHER_SUITE_REJECTED); +	case_WLAN(DISASSOC_UNSPECIFIED_QOS); +	case_WLAN(DISASSOC_QAP_NO_BANDWIDTH); +	case_WLAN(DISASSOC_LOW_ACK); +	case_WLAN(DISASSOC_QAP_EXCEED_TXOP); +	case_WLAN(QSTA_LEAVE_QBSS); +	case_WLAN(QSTA_NOT_USE); +	case_WLAN(QSTA_REQUIRE_SETUP); +	case_WLAN(QSTA_TIMEOUT); +	case_WLAN(QSTA_CIPHER_NOT_SUPP); +	case_WLAN(MESH_PEER_CANCELED); +	case_WLAN(MESH_MAX_PEERS); +	case_WLAN(MESH_CONFIG); +	case_WLAN(MESH_CLOSE); +	case_WLAN(MESH_MAX_RETRIES); +	case_WLAN(MESH_CONFIRM_TIMEOUT); +	case_WLAN(MESH_INVALID_GTK); +	case_WLAN(MESH_INCONSISTENT_PARAM); +	case_WLAN(MESH_INVALID_SECURITY); +	case_WLAN(MESH_PATH_ERROR); +	case_WLAN(MESH_PATH_NOFORWARD); +	case_WLAN(MESH_PATH_DEST_UNREACHABLE); +	case_WLAN(MAC_EXISTS_IN_MBSS); +	case_WLAN(MESH_CHAN_REGULATORY); +	case_WLAN(MESH_CHAN); +	default: return "<unknown>"; +	} +}  static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,  				     struct ieee80211_mgmt *mgmt, size_t len) @@ -2406,8 +2371,8 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,  	reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); -	sdata_info(sdata, "deauthenticated from %pM (Reason: %u)\n", -		   bssid, reason_code); +	sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n", +		   bssid, reason_code, ieee80211_get_reason_code_string(reason_code));  	ieee80211_set_disassoc(sdata, 0, 0, false, NULL); @@ -2504,7 +2469,9 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,  		memset(sdata->u.mgd.bssid, 0, ETH_ALEN);  		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);  		sdata->u.mgd.flags = 0; +		mutex_lock(&sdata->local->mtx);  		ieee80211_vif_release_channel(sdata); +		mutex_unlock(&sdata->local->mtx);  	}  	kfree(assoc_data); @@ -2717,7 +2684,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,  	 */  	ifmgd->wmm_last_param_set = -1; -	if (elems.wmm_param) +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)  		ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,  					 elems.wmm_param_len);  	else @@ -2838,28 +2805,20 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  				  struct ieee802_11_elems *elems)  {  	struct ieee80211_local *local = sdata->local; -	int freq;  	struct ieee80211_bss *bss;  	struct ieee80211_channel *channel;  	sdata_assert_lock(sdata); -	if (elems->ds_params) -		freq = ieee80211_channel_to_frequency(elems->ds_params[0], -						      rx_status->band); -	else -		freq = rx_status->freq; - -	channel = ieee80211_get_channel(local->hw.wiphy, freq); - -	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) +	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); +	if (!channel)  		return;  	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,  					channel);  	if (bss) { -		ieee80211_rx_bss_put(local, bss);  		sdata->vif.bss_conf.beacon_rate = bss->beacon_rate; +		ieee80211_rx_bss_put(local, bss);  	}  } @@ -3061,17 +3020,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  		}  	} -	if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { +	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {  		mlme_dbg_ratelimited(sdata,  				     "cancelling AP probe due to a received beacon\n"); -		mutex_lock(&local->mtx); -		ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; -		ieee80211_run_deferred_scan(local); -		mutex_unlock(&local->mtx); - -		mutex_lock(&local->iflist_mtx); -		ieee80211_recalc_ps(local, -1); -		mutex_unlock(&local->iflist_mtx); +		ieee80211_reset_ap_probe(sdata);  	}  	/* @@ -3152,7 +3104,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,  					 &elems, true); -	if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && +	    ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,  				     elems.wmm_param_len))  		changed |= BSS_CHANGED_QOS; @@ -3543,8 +3496,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)  	} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)  		run_again(sdata, ifmgd->assoc_data->timeout); -	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | -			    IEEE80211_STA_CONNECTION_POLL) && +	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&  	    ifmgd->associated) {  		u8 bssid[ETH_ALEN];  		int max_tries; @@ -3616,6 +3568,9 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)  	if (local->quiescing)  		return; +	if (sdata->vif.csa_active) +		return; +  	sdata->u.mgd.connection_loss = false;  	ieee80211_queue_work(&sdata->local->hw,  			     &sdata->u.mgd.beacon_connection_loss_work); @@ -3631,6 +3586,9 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)  	if (local->quiescing)  		return; +	if (sdata->vif.csa_active) +		return; +  	ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);  } @@ -3661,6 +3619,38 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)  }  #ifdef CONFIG_PM +void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; + +	sdata_lock(sdata); + +	if (ifmgd->auth_data || ifmgd->assoc_data) { +		const u8 *bssid = ifmgd->auth_data ? +				ifmgd->auth_data->bss->bssid : +				ifmgd->assoc_data->bss->bssid; + +		/* +		 * If we are trying to authenticate / associate while suspending, +		 * cfg80211 won't know and won't actually abort those attempts, +		 * thus we need to do that ourselves. +		 */ +		ieee80211_send_deauth_disassoc(sdata, bssid, +					       IEEE80211_STYPE_DEAUTH, +					       WLAN_REASON_DEAUTH_LEAVING, +					       false, frame_buf); +		if (ifmgd->assoc_data) +			ieee80211_destroy_assoc_data(sdata, false); +		if (ifmgd->auth_data) +			ieee80211_destroy_auth_data(sdata, false); +		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +				      IEEE80211_DEAUTH_FRAME_LEN); +	} + +	sdata_unlock(sdata); +} +  void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -3697,7 +3687,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)  		  ieee80211_beacon_connection_loss_work);  	INIT_WORK(&ifmgd->csa_connection_drop_work,  		  ieee80211_csa_connection_drop_work); -	INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); +	INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);  	setup_timer(&ifmgd->timer, ieee80211_sta_timer,  		    (unsigned long) sdata);  	setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, @@ -3745,7 +3735,7 @@ int ieee80211_max_network_latency(struct notifier_block *nb,  	ieee80211_recalc_ps(local, latency_usec);  	mutex_unlock(&local->iflist_mtx); -	return 0; +	return NOTIFY_OK;  }  static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, @@ -3862,6 +3852,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,  	/* will change later if needed */  	sdata->smps_mode = IEEE80211_SMPS_OFF; +	mutex_lock(&local->mtx);  	/*  	 * If this fails (possibly due to channel context sharing  	 * on incompatible channels, e.g. 80+80 and 160 sharing the @@ -3873,13 +3864,15 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,  	/* don't downgrade for 5 and 10 MHz channels, though. */  	if (chandef.width == NL80211_CHAN_WIDTH_5 ||  	    chandef.width == NL80211_CHAN_WIDTH_10) -		return ret; +		goto out;  	while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { -		ifmgd->flags |= chandef_downgrade(&chandef); +		ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);  		ret = ieee80211_vif_use_channel(sdata, &chandef,  						IEEE80211_CHANCTX_SHARED);  	} + out: +	mutex_unlock(&local->mtx);  	return ret;  } @@ -3930,6 +3923,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,  		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);  		if (WARN_ON(!chanctx_conf)) {  			rcu_read_unlock(); +			sta_info_free(local, new_sta);  			return -EINVAL;  		}  		rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def); @@ -4135,6 +4129,44 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,  	return err;  } +static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata, +					const u8 *wmm_param, int len) +{ +	const u8 *pos; +	size_t left; + +	if (len < 8) +		return false; + +	if (wmm_param[5] != 1 /* version */) +		return false; + +	pos = wmm_param + 8; +	left = len - 8; + +	for (; left >= 4; left -= 4, pos += 4) { +		u8 aifsn = pos[0] & 0x0f; +		u8 ecwmin = pos[1] & 0x0f; +		u8 ecwmax = (pos[1] & 0xf0) >> 4; +		int aci = (pos[0] >> 5) & 0x03; + +		if (aifsn < 2) { +			sdata_info(sdata, +				   "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n", +				   aifsn, aci); +			return false; +		} +		if (ecwmin > ecwmax) { +			sdata_info(sdata, +				   "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n", +				   ecwmin, ecwmax, aci); +			return false; +		} +	} + +	return true; +} +  int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  			struct cfg80211_assoc_request *req)  { @@ -4192,9 +4224,45 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	}  	/* prepare assoc data */ -	 +  	ifmgd->beacon_crc_valid = false; +	assoc_data->wmm = bss->wmm_used && +			  (local->hw.queues >= IEEE80211_NUM_ACS); +	if (assoc_data->wmm) { +		/* try to check validity of WMM params IE */ +		const struct cfg80211_bss_ies *ies; +		const u8 *wp, *start, *end; + +		rcu_read_lock(); +		ies = rcu_dereference(req->bss->ies); +		start = ies->data; +		end = start + ies->len; + +		while (true) { +			wp = cfg80211_find_vendor_ie( +				WLAN_OUI_MICROSOFT, +				WLAN_OUI_TYPE_MICROSOFT_WMM, +				start, end - start); +			if (!wp) +				break; +			start = wp + wp[1] + 2; +			/* if this IE is too short, try the next */ +			if (wp[1] <= 4) +				continue; +			/* if this IE is WMM params, we found what we wanted */ +			if (wp[6] == 1) +				break; +		} + +		if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2, +							wp[1] - 2)) { +			assoc_data->wmm = false; +			ifmgd->flags |= IEEE80211_STA_DISABLE_WMM; +		} +		rcu_read_unlock(); +	} +  	/*  	 * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.  	 * We still associate in non-HT mode (11a/b/g) if any one of these @@ -4224,18 +4292,22 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	/* Also disable HT if we don't support it or the AP doesn't use WMM */  	sband = local->hw.wiphy->bands[req->bss->channel->band];  	if (!sband->ht_cap.ht_supported || -	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { +	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used || +	    ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {  		ifmgd->flags |= IEEE80211_STA_DISABLE_HT; -		if (!bss->wmm_used) +		if (!bss->wmm_used && +		    !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))  			netdev_info(sdata->dev,  				    "disabling HT as WMM/QoS is not supported by the AP\n");  	}  	/* disable VHT if we don't support it or the AP doesn't use WMM */  	if (!sband->vht_cap.vht_supported || -	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { +	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used || +	    ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {  		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; -		if (!bss->wmm_used) +		if (!bss->wmm_used && +		    !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))  			netdev_info(sdata->dev,  				    "disabling VHT as WMM/QoS is not supported by the AP\n");  	} @@ -4264,8 +4336,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  		sdata->smps_mode = ifmgd->req_smps;  	assoc_data->capability = req->bss->capability; -	assoc_data->wmm = bss->wmm_used && -			  (local->hw.queues >= IEEE80211_NUM_ACS);  	assoc_data->supp_rates = bss->supp_rates;  	assoc_data->supp_rates_len = bss->supp_rates_len; @@ -4312,6 +4382,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	sdata->control_port_protocol = req->crypto.control_port_ethertype;  	sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt; +	sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto, +							sdata->vif.type);  	/* kick off associate process */ @@ -4397,37 +4469,41 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];  	bool tx = !req->local_state_change; -	bool report_frame = false; -	sdata_info(sdata, -		   "deauthenticating from %pM by local choice (reason=%d)\n", -		   req->bssid, req->reason_code); +	if (ifmgd->auth_data && +	    ether_addr_equal(ifmgd->auth_data->bss->bssid, req->bssid)) { +		sdata_info(sdata, +			   "aborting authentication with %pM by local choice (Reason: %u=%s)\n", +			   req->bssid, req->reason_code, +			   ieee80211_get_reason_code_string(req->reason_code)); -	if (ifmgd->auth_data) {  		drv_mgd_prepare_tx(sdata->local, sdata);  		ieee80211_send_deauth_disassoc(sdata, req->bssid,  					       IEEE80211_STYPE_DEAUTH,  					       req->reason_code, tx,  					       frame_buf);  		ieee80211_destroy_auth_data(sdata, false); +		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +				      IEEE80211_DEAUTH_FRAME_LEN); -		report_frame = true; -		goto out; +		return 0;  	}  	if (ifmgd->associated &&  	    ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { +		sdata_info(sdata, +			   "deauthenticating from %pM by local choice (Reason: %u=%s)\n", +			   req->bssid, req->reason_code, +			   ieee80211_get_reason_code_string(req->reason_code)); +  		ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,  				       req->reason_code, tx, frame_buf); -		report_frame = true; -	} - - out: -	if (report_frame)  		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,  				      IEEE80211_DEAUTH_FRAME_LEN); +		return 0; +	} -	return 0; +	return -ENOTCONN;  }  int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, @@ -4447,8 +4523,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,  		return -ENOLINK;  	sdata_info(sdata, -		   "disassociating from %pM by local choice (reason=%d)\n", -		   req->bss->bssid, req->reason_code); +		   "disassociating from %pM by local choice (Reason: %u=%s)\n", +		   req->bss->bssid, req->reason_code, ieee80211_get_reason_code_string(req->reason_code));  	memcpy(bssid, req->bss->bssid, ETH_ALEN);  	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC, diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index acd1f71adc0..7a17decd27f 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -333,7 +333,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)  		container_of(work, struct ieee80211_roc_work, work.work);  	struct ieee80211_sub_if_data *sdata = roc->sdata;  	struct ieee80211_local *local = sdata->local; -	bool started; +	bool started, on_channel;  	mutex_lock(&local->mtx); @@ -354,13 +354,26 @@ void ieee80211_sw_roc_work(struct work_struct *work)  	if (!roc->started) {  		struct ieee80211_roc_work *dep; -		/* start this ROC */ +		WARN_ON(local->use_chanctx); + +		/* If actually operating on the desired channel (with at least +		 * 20 MHz channel width) don't stop all the operations but still +		 * treat it as though the ROC operation started properly, so +		 * other ROC operations won't interfere with this one. +		 */ +		roc->on_channel = roc->chan == local->_oper_chandef.chan && +				  local->_oper_chandef.width != NL80211_CHAN_WIDTH_5 && +				  local->_oper_chandef.width != NL80211_CHAN_WIDTH_10; -		/* switch channel etc */ +		/* start this ROC */  		ieee80211_recalc_idle(local); -		local->tmp_channel = roc->chan; -		ieee80211_hw_config(local, 0); +		if (!roc->on_channel) { +			ieee80211_offchannel_stop_vifs(local); + +			local->tmp_channel = roc->chan; +			ieee80211_hw_config(local, 0); +		}  		/* tell userspace or send frame */  		ieee80211_handle_roc_started(roc); @@ -379,9 +392,10 @@ void ieee80211_sw_roc_work(struct work_struct *work)   finish:  		list_del(&roc->list);  		started = roc->started; +		on_channel = roc->on_channel;  		ieee80211_roc_notify_destroy(roc, !roc->abort); -		if (started) { +		if (started && !on_channel) {  			ieee80211_flush_queues(local, NULL);  			local->tmp_channel = NULL; @@ -394,6 +408,8 @@ void ieee80211_sw_roc_work(struct work_struct *work)  		if (started)  			ieee80211_start_next_roc(local); +		else if (list_empty(&local->roc_list)) +			ieee80211_run_deferred_scan(local);  	}   out_unlock: diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 34012620434..d478b880a0a 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -37,9 +37,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)  					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_SUSPEND); -	/* flush out all packets and station cleanup call_rcu()s */ +	/* flush out all packets */  	synchronize_net(); -	rcu_barrier();  	ieee80211_flush_queues(local, NULL); @@ -101,10 +100,18 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)  	/* remove all interfaces that were created in the driver */  	list_for_each_entry(sdata, &local->interfaces, list) { -		if (!ieee80211_sdata_running(sdata) || -		    sdata->vif.type == NL80211_IFTYPE_AP_VLAN || -		    sdata->vif.type == NL80211_IFTYPE_MONITOR) +		if (!ieee80211_sdata_running(sdata))  			continue; +		switch (sdata->vif.type) { +		case NL80211_IFTYPE_AP_VLAN: +		case NL80211_IFTYPE_MONITOR: +			continue; +		case NL80211_IFTYPE_STATION: +			ieee80211_mgd_quiesce(sdata); +			break; +		default: +			break; +		}  		drv_remove_interface(local, sdata);  	} diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index e126605cec6..8fdadfd94ba 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -10,15 +10,15 @@  #include <linux/kernel.h>  #include <linux/rtnetlink.h> -#include <linux/slab.h>  #include <linux/module.h> +#include <linux/slab.h>  #include "rate.h"  #include "ieee80211_i.h"  #include "debugfs.h"  struct rate_control_alg {  	struct list_head list; -	struct rate_control_ops *ops; +	const struct rate_control_ops *ops;  };  static LIST_HEAD(rate_ctrl_algs); @@ -29,7 +29,7 @@ module_param(ieee80211_default_rc_algo, charp, 0644);  MODULE_PARM_DESC(ieee80211_default_rc_algo,  		 "Default rate control algorithm for mac80211 to use"); -int ieee80211_rate_control_register(struct rate_control_ops *ops) +int ieee80211_rate_control_register(const struct rate_control_ops *ops)  {  	struct rate_control_alg *alg; @@ -60,7 +60,7 @@ int ieee80211_rate_control_register(struct rate_control_ops *ops)  }  EXPORT_SYMBOL(ieee80211_rate_control_register); -void ieee80211_rate_control_unregister(struct rate_control_ops *ops) +void ieee80211_rate_control_unregister(const struct rate_control_ops *ops)  {  	struct rate_control_alg *alg; @@ -76,32 +76,31 @@ void ieee80211_rate_control_unregister(struct rate_control_ops *ops)  }  EXPORT_SYMBOL(ieee80211_rate_control_unregister); -static struct rate_control_ops * +static const struct rate_control_ops *  ieee80211_try_rate_control_ops_get(const char *name)  {  	struct rate_control_alg *alg; -	struct rate_control_ops *ops = NULL; +	const struct rate_control_ops *ops = NULL;  	if (!name)  		return NULL;  	mutex_lock(&rate_ctrl_mutex);  	list_for_each_entry(alg, &rate_ctrl_algs, list) { -		if (!strcmp(alg->ops->name, name)) -			if (try_module_get(alg->ops->module)) { -				ops = alg->ops; -				break; -			} +		if (!strcmp(alg->ops->name, name)) { +			ops = alg->ops; +			break; +		}  	}  	mutex_unlock(&rate_ctrl_mutex);  	return ops;  }  /* Get the rate control algorithm. */ -static struct rate_control_ops * +static const struct rate_control_ops *  ieee80211_rate_control_ops_get(const char *name)  { -	struct rate_control_ops *ops; +	const struct rate_control_ops *ops;  	const char *alg_name;  	kparam_block_sysfs_write(ieee80211_default_rc_algo); @@ -111,10 +110,6 @@ ieee80211_rate_control_ops_get(const char *name)  		alg_name = name;  	ops = ieee80211_try_rate_control_ops_get(alg_name); -	if (!ops) { -		request_module("rc80211_%s", alg_name); -		ops = ieee80211_try_rate_control_ops_get(alg_name); -	}  	if (!ops && name)  		/* try default if specific alg requested but not found */  		ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); @@ -127,11 +122,6 @@ ieee80211_rate_control_ops_get(const char *name)  	return ops;  } -static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops) -{ -	module_put(ops->module); -} -  #ifdef CONFIG_MAC80211_DEBUGFS  static ssize_t rcname_read(struct file *file, char __user *userbuf,  			   size_t count, loff_t *ppos) @@ -158,11 +148,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name,  	ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);  	if (!ref) -		goto fail_ref; +		return NULL;  	ref->local = local;  	ref->ops = ieee80211_rate_control_ops_get(name);  	if (!ref->ops) -		goto fail_ops; +		goto free;  #ifdef CONFIG_MAC80211_DEBUGFS  	debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir); @@ -172,14 +162,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name,  	ref->priv = ref->ops->alloc(&local->hw, debugfsdir);  	if (!ref->priv) -		goto fail_priv; +		goto free;  	return ref; -fail_priv: -	ieee80211_rate_control_ops_put(ref->ops); -fail_ops: +free:  	kfree(ref); -fail_ref:  	return NULL;  } @@ -192,7 +179,6 @@ static void rate_control_free(struct rate_control_ref *ctrl_ref)  	ctrl_ref->local->debugfs.rcdir = NULL;  #endif -	ieee80211_rate_control_ops_put(ctrl_ref->ops);  	kfree(ctrl_ref);  } @@ -235,7 +221,8 @@ static void rc_send_low_basicrate(s8 *idx, u32 basic_rates,  static void __rate_control_send_low(struct ieee80211_hw *hw,  				    struct ieee80211_supported_band *sband,  				    struct ieee80211_sta *sta, -				    struct ieee80211_tx_info *info) +				    struct ieee80211_tx_info *info, +				    u32 rate_mask)  {  	int i;  	u32 rate_flags = @@ -247,6 +234,12 @@ static void __rate_control_send_low(struct ieee80211_hw *hw,  	info->control.rates[0].idx = 0;  	for (i = 0; i < sband->n_bitrates; i++) { +		if (!(rate_mask & BIT(i))) +			continue; + +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; +  		if (!rate_supported(sta, sband->band, i))  			continue; @@ -274,7 +267,8 @@ bool rate_control_send_low(struct ieee80211_sta *pubsta,  	bool use_basicrate = false;  	if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) { -		__rate_control_send_low(txrc->hw, sband, pubsta, info); +		__rate_control_send_low(txrc->hw, sband, pubsta, info, +					txrc->rate_idx_mask);  		if (!pubsta && txrc->bss) {  			mcast_rate = txrc->bss_conf->mcast_rate[sband->band]; @@ -656,7 +650,8 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,  		rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates);  	if (dest[0].idx < 0) -		__rate_control_send_low(&sdata->local->hw, sband, sta, info); +		__rate_control_send_low(&sdata->local->hw, sband, sta, info, +					sdata->rc_rateidx_mask[info->band]);  	if (sta)  		rate_fixup_ratelist(vif, sband, info, dest, max_rates); diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 5dedc56c94d..9aa2a1190a8 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -21,7 +21,7 @@  struct rate_control_ref {  	struct ieee80211_local *local; -	struct rate_control_ops *ops; +	const struct rate_control_ops *ops;  	void *priv;  }; @@ -54,6 +54,8 @@ static inline void rate_control_rate_init(struct sta_info *sta)  	struct ieee80211_supported_band *sband;  	struct ieee80211_chanctx_conf *chanctx_conf; +	ieee80211_sta_set_rx_nss(sta); +  	if (!ref)  		return; @@ -67,8 +69,6 @@ static inline void rate_control_rate_init(struct sta_info *sta)  	sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band]; -	ieee80211_sta_set_rx_nss(sta); -  	ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,  			    priv_sta);  	rcu_read_unlock(); @@ -144,8 +144,8 @@ void rate_control_deinitialize(struct ieee80211_local *local);  /* Rate control algorithms */  #ifdef CONFIG_MAC80211_RC_PID -extern int rc80211_pid_init(void); -extern void rc80211_pid_exit(void); +int rc80211_pid_init(void); +void rc80211_pid_exit(void);  #else  static inline int rc80211_pid_init(void)  { @@ -157,8 +157,8 @@ static inline void rc80211_pid_exit(void)  #endif  #ifdef CONFIG_MAC80211_RC_MINSTREL -extern int rc80211_minstrel_init(void); -extern void rc80211_minstrel_exit(void); +int rc80211_minstrel_init(void); +void rc80211_minstrel_exit(void);  #else  static inline int rc80211_minstrel_init(void)  { @@ -170,8 +170,8 @@ static inline void rc80211_minstrel_exit(void)  #endif  #ifdef CONFIG_MAC80211_RC_MINSTREL_HT -extern int rc80211_minstrel_ht_init(void); -extern void rc80211_minstrel_ht_exit(void); +int rc80211_minstrel_ht_init(void); +void rc80211_minstrel_ht_exit(void);  #else  static inline int rc80211_minstrel_ht_init(void)  { diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 8b5f7ef7c0c..1c1469c36dc 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -135,7 +135,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  	u32 usecs;  	int i; -	for (i=0; i < MAX_THR_RATES; i++) +	for (i = 0; i < MAX_THR_RATES; i++)  	    tmp_tp_rate[i] = 0;  	for (i = 0; i < mi->n_rates; i++) { @@ -190,7 +190,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  		 * choose the maximum throughput rate as max_prob_rate  		 * (2) if all success probabilities < 95%, the rate with  		 * highest success probability is choosen as max_prob_rate */ -		if (mr->probability >= MINSTREL_FRAC(95,100)) { +		if (mr->probability >= MINSTREL_FRAC(95, 100)) {  			if (mr->cur_tp >= mi->r[tmp_prob_rate].cur_tp)  				tmp_prob_rate = i;  		} else { @@ -203,6 +203,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  	memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));  	mi->max_prob_rate = tmp_prob_rate; +#ifdef CONFIG_MAC80211_DEBUGFS +	/* use fixed index if set */ +	if (mp->fixed_rate_idx != -1) { +		mi->max_tp_rate[0] = mp->fixed_rate_idx; +		mi->max_tp_rate[1] = mp->fixed_rate_idx; +		mi->max_prob_rate = mp->fixed_rate_idx; +	} +#endif +  	/* Reset update timer */  	mi->stats_update = jiffies; @@ -211,7 +220,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  static void  minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, -                   struct ieee80211_sta *sta, void *priv_sta, +		   struct ieee80211_sta *sta, void *priv_sta,  		   struct sk_buff *skb)  {  	struct minstrel_priv *mp = priv; @@ -251,7 +260,7 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,  static inline unsigned int  minstrel_get_retry_count(struct minstrel_rate *mr, -                         struct ieee80211_tx_info *info) +			 struct ieee80211_tx_info *info)  {  	unsigned int retry = mr->adjusted_retry_count; @@ -310,6 +319,11 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,  	/* increase sum packet counter */  	mi->packet_count++; +#ifdef CONFIG_MAC80211_DEBUGFS +	if (mp->fixed_rate_idx != -1) +		return; +#endif +  	delta = (mi->packet_count * sampling_ratio / 100) -  			(mi->sample_count + mi->sample_deferred / 2); @@ -408,10 +422,9 @@ init_sample_table(struct minstrel_sta_info *mi)  	memset(mi->sample_table, 0xff, SAMPLE_COLUMNS * mi->n_rates);  	for (col = 0; col < SAMPLE_COLUMNS; col++) { +		prandom_bytes(rnd, sizeof(rnd));  		for (i = 0; i < mi->n_rates; i++) { -			get_random_bytes(rnd, sizeof(rnd));  			new_idx = (i + rnd[i & 7]) % mi->n_rates; -  			while (SAMPLE_TBL(mi, new_idx, col) != 0xff)  				new_idx = (new_idx + 1) % mi->n_rates; @@ -644,7 +657,18 @@ minstrel_free(void *priv)  	kfree(priv);  } -struct rate_control_ops mac80211_minstrel = { +static u32 minstrel_get_expected_throughput(void *priv_sta) +{ +	struct minstrel_sta_info *mi = priv_sta; +	int idx = mi->max_tp_rate[0]; + +	/* convert pkt per sec in kbps (1200 is the average pkt size used for +	 * computing cur_tp +	 */ +	return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024; +} + +const struct rate_control_ops mac80211_minstrel = {  	.name = "minstrel",  	.tx_status = minstrel_tx_status,  	.get_rate = minstrel_get_rate, @@ -657,6 +681,7 @@ struct rate_control_ops mac80211_minstrel = {  	.add_sta_debugfs = minstrel_add_sta_debugfs,  	.remove_sta_debugfs = minstrel_remove_sta_debugfs,  #endif +	.get_expected_throughput = minstrel_get_expected_throughput,  };  int __init diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index f4301f4b2e4..046d1bd598a 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -123,7 +123,7 @@ struct minstrel_debugfs_info {  	char buf[];  }; -extern struct rate_control_ops mac80211_minstrel; +extern const struct rate_control_ops mac80211_minstrel;  void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);  void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 7c323f27ba2..85c1e74b771 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -22,7 +22,7 @@  #define MCS_NBITS (AVG_PKT_SIZE << 3)  /* Number of symbols for a packet with (bps) bits per symbol */ -#define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps)) +#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))  /* Transmission time (nanoseconds) for a packet containing (syms) symbols */  #define MCS_SYMBOL_TIME(sgi, syms)					\ @@ -63,7 +63,7 @@  #define CCK_DURATION(_bitrate, _short, _len)		\  	(1000 * (10 /* SIFS */ +			\ -	 (_short ? 72 + 24 : 144 + 48 ) +		\ +	 (_short ? 72 + 24 : 144 + 48) +		\  	 (8 * (_len + 4) * 10) / (_bitrate)))  #define CCK_ACK_DURATION(_bitrate, _short)			\ @@ -124,7 +124,7 @@ const struct mcs_group minstrel_mcs_groups[] = {  #define MINSTREL_CCK_GROUP	(ARRAY_SIZE(minstrel_mcs_groups) - 1) -static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES]; +static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;  static void  minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); @@ -135,7 +135,7 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);  static int  minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)  { -	return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1, +	return GROUP_IDX((rate->idx / 8) + 1,  			 !!(rate->flags & IEEE80211_TX_RC_SHORT_GI),  			 !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));  } @@ -148,7 +148,7 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,  	if (rate->flags & IEEE80211_TX_RC_MCS) {  		group = minstrel_ht_get_group_idx(rate); -		idx = rate->idx % MCS_GROUP_RATES; +		idx = rate->idx % 8;  	} else {  		group = MINSTREL_CCK_GROUP; @@ -226,8 +226,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)  		nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);  	nsecs += minstrel_mcs_groups[group].duration[rate]; -	tp = 1000000 * ((mr->probability * 1000) / nsecs); +	/* prob is scaled - see MINSTREL_FRAC above */ +	tp = 1000000 * ((prob * 1000) / nsecs);  	mr->cur_tp = MINSTREL_TRUNC(tp);  } @@ -277,13 +278,15 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  			if (!(mg->supported & BIT(i)))  				continue; +			index = MCS_GROUP_RATES * group + i; +  			/* initialize rates selections starting indexes */  			if (!mg_rates_valid) {  				mg->max_tp_rate = mg->max_tp_rate2 =  					mg->max_prob_rate = i;  				if (!mi_rates_valid) {  					mi->max_tp_rate = mi->max_tp_rate2 = -						mi->max_prob_rate = i; +						mi->max_prob_rate = index;  					mi_rates_valid = true;  				}  				mg_rates_valid = true; @@ -291,7 +294,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  			mr = &mg->rates[i];  			mr->retry_updated = false; -			index = MCS_GROUP_RATES * group + i;  			minstrel_calc_rate_ewma(mr);  			minstrel_ht_calc_tp(mi, group, i); @@ -365,6 +367,14 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		}  	} +#ifdef CONFIG_MAC80211_DEBUGFS +	/* use fixed index if set */ +	if (mp->fixed_rate_idx != -1) { +		mi->max_tp_rate = mp->fixed_rate_idx; +		mi->max_tp_rate2 = mp->fixed_rate_idx; +		mi->max_prob_rate = mp->fixed_rate_idx; +	} +#endif  	mi->stats_update = jiffies;  } @@ -628,8 +638,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,  		idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];  		flags = 0;  	} else { -		idx = index % MCS_GROUP_RATES + -		      (group->streams - 1) * MCS_GROUP_RATES; +		idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;  		flags = IEEE80211_TX_RC_MCS | group->flags;  	} @@ -693,12 +702,16 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  	if (!mi->sample_tries)  		return -1; -	mg = &mi->groups[mi->sample_group]; +	sample_group = mi->sample_group; +	mg = &mi->groups[sample_group];  	sample_idx = sample_table[mg->column][mg->index]; +	minstrel_next_sample_idx(mi); + +	if (!(mg->supported & BIT(sample_idx))) +		return -1; +  	mr = &mg->rates[sample_idx]; -	sample_group = mi->sample_group;  	sample_idx += sample_group * MCS_GROUP_RATES; -	minstrel_next_sample_idx(mi);  	/*  	 * Sampling might add some overhead (RTS, no aggregation) @@ -774,6 +787,11 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,  	info->flags |= mi->tx_flags;  	minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble); +#ifdef CONFIG_MAC80211_DEBUGFS +	if (mp->fixed_rate_idx != -1) +		return; +#endif +  	/* Don't use EAPOL frames for sampling on non-mrr hw */  	if (mp->hw->max_rates == 1 &&  	    (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) @@ -781,16 +799,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,  	else  		sample_idx = minstrel_get_sample_rate(mp, mi); -#ifdef CONFIG_MAC80211_DEBUGFS -	/* use fixed index if set */ -	if (mp->fixed_rate_idx != -1) { -		mi->max_tp_rate = mp->fixed_rate_idx; -		mi->max_tp_rate2 = mp->fixed_rate_idx; -		mi->max_prob_rate = mp->fixed_rate_idx; -		sample_idx = -1; -	} -#endif -  	mi->total_packets++;  	/* wraparound */ @@ -814,7 +822,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,  	}  	rate->idx = sample_idx % MCS_GROUP_RATES + -		    (sample_group->streams - 1) * MCS_GROUP_RATES; +		    (sample_group->streams - 1) * 8;  	rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags;  } @@ -1024,7 +1032,23 @@ minstrel_ht_free(void *priv)  	mac80211_minstrel.free(priv);  } -static struct rate_control_ops mac80211_minstrel_ht = { +static u32 minstrel_ht_get_expected_throughput(void *priv_sta) +{ +	struct minstrel_ht_sta_priv *msp = priv_sta; +	struct minstrel_ht_sta *mi = &msp->ht; +	int i, j; + +	if (!msp->is_ht) +		return mac80211_minstrel.get_expected_throughput(priv_sta); + +	i = mi->max_tp_rate / MCS_GROUP_RATES; +	j = mi->max_tp_rate % MCS_GROUP_RATES; + +	/* convert cur_tp from pkt per second in kbps */ +	return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024; +} + +static const struct rate_control_ops mac80211_minstrel_ht = {  	.name = "minstrel_ht",  	.tx_status = minstrel_ht_tx_status,  	.get_rate = minstrel_ht_get_rate, @@ -1038,21 +1062,20 @@ static struct rate_control_ops mac80211_minstrel_ht = {  	.add_sta_debugfs = minstrel_ht_add_sta_debugfs,  	.remove_sta_debugfs = minstrel_ht_remove_sta_debugfs,  #endif +	.get_expected_throughput = minstrel_ht_get_expected_throughput,  }; -static void -init_sample_table(void) +static void __init init_sample_table(void)  {  	int col, i, new_idx;  	u8 rnd[MCS_GROUP_RATES];  	memset(sample_table, 0xff, sizeof(sample_table));  	for (col = 0; col < SAMPLE_COLUMNS; col++) { +		prandom_bytes(rnd, sizeof(rnd));  		for (i = 0; i < MCS_GROUP_RATES; i++) { -			get_random_bytes(rnd, sizeof(rnd));  			new_idx = (i + rnd[i]) % MCS_GROUP_RATES; -  			while (sample_table[col][new_idx] != 0xff)  				new_idx = (new_idx + 1) % MCS_GROUP_RATES; diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index df44a5ad827..3e7d793de0c 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -54,8 +54,7 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)  			int r = bitrates[j % 4];  			p += sprintf(p, " %2u.%1uM", r / 10, r % 10);  		} else { -			p += sprintf(p, " MCS%-2u", (mg->streams - 1) * -					 MCS_GROUP_RATES + j); +			p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);  		}  		tp = mr->cur_tp / 10; diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 958fad07b54..d0da2a70fe6 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -452,7 +452,7 @@ static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,  	kfree(priv_sta);  } -static struct rate_control_ops mac80211_rcpid = { +static const struct rate_control_ops mac80211_rcpid = {  	.name = "pid",  	.tx_status = rate_control_pid_tx_status,  	.get_rate = rate_control_pid_get_rate, diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c index c97a0657c04..6ff134650a8 100644 --- a/net/mac80211/rc80211_pid_debugfs.c +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -167,29 +167,29 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,  	 * provide large enough buffers. */  	length = length < RC_PID_PRINT_BUF_SIZE ?  		 length : RC_PID_PRINT_BUF_SIZE; -	p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); +	p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);  	switch (ev->type) {  	case RC_PID_EVENT_TYPE_TX_STATUS: -		p += snprintf(pb + p, length - p, "tx_status %u %u", -			      !(ev->data.flags & IEEE80211_TX_STAT_ACK), -			      ev->data.tx_status.status.rates[0].idx); +		p += scnprintf(pb + p, length - p, "tx_status %u %u", +			       !(ev->data.flags & IEEE80211_TX_STAT_ACK), +			       ev->data.tx_status.status.rates[0].idx);  		break;  	case RC_PID_EVENT_TYPE_RATE_CHANGE: -		p += snprintf(pb + p, length - p, "rate_change %d %d", -			      ev->data.index, ev->data.rate); +		p += scnprintf(pb + p, length - p, "rate_change %d %d", +			       ev->data.index, ev->data.rate);  		break;  	case RC_PID_EVENT_TYPE_TX_RATE: -		p += snprintf(pb + p, length - p, "tx_rate %d %d", -			      ev->data.index, ev->data.rate); +		p += scnprintf(pb + p, length - p, "tx_rate %d %d", +			       ev->data.index, ev->data.rate);  		break;  	case RC_PID_EVENT_TYPE_PF_SAMPLE: -		p += snprintf(pb + p, length - p, -			      "pf_sample %d %d %d %d", -			      ev->data.pf_sample, ev->data.prop_err, -			      ev->data.int_err, ev->data.der_err); +		p += scnprintf(pb + p, length - p, +			       "pf_sample %d %d %d %d", +			       ev->data.pf_sample, ev->data.prop_err, +			       ev->data.int_err, ev->data.der_err);  		break;  	} -	p += snprintf(pb + p, length - p, "\n"); +	p += scnprintf(pb + p, length - p, "\n");  	spin_unlock_irqrestore(&events->lock, status); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 54395d7583b..394e201cde6 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -40,8 +40,6 @@  static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,  					   struct sk_buff *skb)  { -	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); -  	if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {  		if (likely(skb->len > FCS_LEN))  			__pskb_trim(skb, skb->len - FCS_LEN); @@ -53,31 +51,28 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,  		}  	} -	if (status->vendor_radiotap_len) -		__pskb_pull(skb, status->vendor_radiotap_len); -  	return skb;  } -static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len) +static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)  {  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); -	struct ieee80211_hdr *hdr; - -	hdr = (void *)(skb->data + status->vendor_radiotap_len); +	struct ieee80211_hdr *hdr = (void *)skb->data;  	if (status->flag & (RX_FLAG_FAILED_FCS_CRC |  			    RX_FLAG_FAILED_PLCP_CRC |  			    RX_FLAG_AMPDU_IS_ZEROLEN)) -		return 1; -	if (unlikely(skb->len < 16 + present_fcs_len + -				status->vendor_radiotap_len)) -		return 1; +		return true; + +	if (unlikely(skb->len < 16 + present_fcs_len)) +		return true; +  	if (ieee80211_is_ctl(hdr->frame_control) &&  	    !ieee80211_is_pspoll(hdr->frame_control) &&  	    !ieee80211_is_back_req(hdr->frame_control)) -		return 1; -	return 0; +		return true; + +	return false;  }  static int @@ -90,8 +85,6 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,  	len = sizeof(struct ieee80211_radiotap_header) + 8;  	/* allocate extra bitmaps */ -	if (status->vendor_radiotap_len) -		len += 4;  	if (status->chains)  		len += 4 * hweight8(status->chains); @@ -127,18 +120,6 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,  		len += 2 * hweight8(status->chains);  	} -	if (status->vendor_radiotap_len) { -		if (WARN_ON_ONCE(status->vendor_radiotap_align == 0)) -			status->vendor_radiotap_align = 1; -		/* align standard part of vendor namespace */ -		len = ALIGN(len, 2); -		/* allocate standard part of vendor namespace */ -		len += 6; -		/* align vendor-defined part */ -		len = ALIGN(len, status->vendor_radiotap_align); -		/* vendor-defined part is already in skb */ -	} -  	return len;  } @@ -172,7 +153,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  	it_present = &rthdr->it_present;  	/* radiotap header, set always present flags */ -	rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len); +	rthdr->it_len = cpu_to_le16(rtap_len);  	it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |  			 BIT(IEEE80211_RADIOTAP_CHANNEL) |  			 BIT(IEEE80211_RADIOTAP_RX_FLAGS); @@ -190,14 +171,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  				 BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);  	} -	if (status->vendor_radiotap_len) { -		it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) | -				  BIT(IEEE80211_RADIOTAP_EXT); -		put_unaligned_le32(it_present_val, it_present); -		it_present++; -		it_present_val = status->vendor_radiotap_bitmap; -	} -  	put_unaligned_le32(it_present_val, it_present);  	pos = (void *)(it_present + 1); @@ -307,6 +280,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  			*pos |= IEEE80211_RADIOTAP_MCS_BW_40;  		if (status->flag & RX_FLAG_HT_GF)  			*pos |= IEEE80211_RADIOTAP_MCS_FMT_GF; +		if (status->flag & RX_FLAG_LDPC) +			*pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;  		stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT;  		*pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;  		pos++; @@ -349,20 +324,25 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);  		/* known field - how to handle 80+80? */ -		if (status->flag & RX_FLAG_80P80MHZ) +		if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)  			known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;  		put_unaligned_le16(known, pos);  		pos += 2;  		/* flags */  		if (status->flag & RX_FLAG_SHORT_GI)  			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; +		/* in VHT, STBC is binary */ +		if (status->flag & RX_FLAG_STBC_MASK) +			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; +		if (status->vht_flag & RX_VHT_FLAG_BF) +			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED;  		pos++;  		/* bandwidth */ -		if (status->flag & RX_FLAG_80MHZ) +		if (status->vht_flag & RX_VHT_FLAG_80MHZ)  			*pos++ = 4; -		else if (status->flag & RX_FLAG_80P80MHZ) +		else if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)  			*pos++ = 0; /* marked not known above */ -		else if (status->flag & RX_FLAG_160MHZ) +		else if (status->vht_flag & RX_VHT_FLAG_160MHZ)  			*pos++ = 11;  		else if (status->flag & RX_FLAG_40MHZ)  			*pos++ = 1; @@ -372,6 +352,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  		*pos = (status->rate_idx << 4) | status->vht_nss;  		pos += 4;  		/* coding field */ +		if (status->flag & RX_FLAG_LDPC) +			*pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;  		pos++;  		/* group ID */  		pos++; @@ -383,21 +365,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  		*pos++ = status->chain_signal[chain];  		*pos++ = chain;  	} - -	if (status->vendor_radiotap_len) { -		/* ensure 2 byte alignment for the vendor field as required */ -		if ((pos - (u8 *)rthdr) & 1) -			*pos++ = 0; -		*pos++ = status->vendor_radiotap_oui[0]; -		*pos++ = status->vendor_radiotap_oui[1]; -		*pos++ = status->vendor_radiotap_oui[2]; -		*pos++ = status->vendor_radiotap_subns; -		put_unaligned_le16(status->vendor_radiotap_len, pos); -		pos += 2; -		/* align the actual payload as requested */ -		while ((pos - (u8 *)rthdr) & (status->vendor_radiotap_align - 1)) -			*pos++ = 0; -	}  }  /* @@ -428,8 +395,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,  	if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)  		present_fcs_len = FCS_LEN; -	/* ensure hdr->frame_control and vendor radiotap data are in skb head */ -	if (!pskb_may_pull(origskb, 2 + status->vendor_radiotap_len)) { +	/* ensure hdr->frame_control is in skb head */ +	if (!pskb_may_pull(origskb, 2)) {  		dev_kfree_skb(origskb);  		return NULL;  	} @@ -599,10 +566,10 @@ static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -	if (skb->len < 24 || is_multicast_ether_addr(hdr->addr1)) +	if (is_multicast_ether_addr(hdr->addr1))  		return 0; -	return ieee80211_is_robust_mgmt_frame(hdr); +	return ieee80211_is_robust_mgmt_frame(skb);  } @@ -610,10 +577,10 @@ static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -	if (skb->len < 24 || !is_multicast_ether_addr(hdr->addr1)) +	if (!is_multicast_ether_addr(hdr->addr1))  		return 0; -	return ieee80211_is_robust_mgmt_frame(hdr); +	return ieee80211_is_robust_mgmt_frame(skb);  } @@ -626,7 +593,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)  	if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))  		return -1; -	if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr)) +	if (!ieee80211_is_robust_mgmt_frame(skb))  		return -1; /* not a robust management frame */  	mmie = (struct ieee80211_mmie *) @@ -638,6 +605,27 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)  	return le16_to_cpu(mmie->key_id);  } +static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs, +				 struct sk_buff *skb) +{ +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	__le16 fc; +	int hdrlen; +	u8 keyid; + +	fc = hdr->frame_control; +	hdrlen = ieee80211_hdrlen(fc); + +	if (skb->len < hdrlen + cs->hdr_len) +		return -EINVAL; + +	skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1); +	keyid &= cs->key_idx_mask; +	keyid >>= cs->key_idx_shift; + +	return keyid; +} +  static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; @@ -729,9 +717,7 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata  	lockdep_assert_held(&tid_agg_rx->reorder_lock);  	while (ieee80211_sn_less(tid_agg_rx->head_seq_num, head_seq_num)) { -		index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, -					 tid_agg_rx->ssn) % -							tid_agg_rx->buf_size; +		index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;  		ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,  						frames);  	} @@ -757,8 +743,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,  	lockdep_assert_held(&tid_agg_rx->reorder_lock);  	/* release the buffer until next missing frame */ -	index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, -				 tid_agg_rx->ssn) % tid_agg_rx->buf_size; +	index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;  	if (!tid_agg_rx->reorder_buf[index] &&  	    tid_agg_rx->stored_mpdu_num) {  		/* @@ -793,15 +778,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,  	} else while (tid_agg_rx->reorder_buf[index]) {  		ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,  						frames); -		index =	ieee80211_sn_sub(tid_agg_rx->head_seq_num, -					 tid_agg_rx->ssn) % -							tid_agg_rx->buf_size; +		index =	tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;  	}  	if (tid_agg_rx->stored_mpdu_num) { -		j = index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, -					     tid_agg_rx->ssn) % -							tid_agg_rx->buf_size; +		j = index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;  		for (; j != (index - 1) % tid_agg_rx->buf_size;  		     j = (j + 1) % tid_agg_rx->buf_size) { @@ -861,8 +842,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata  	/* Now the new frame is always in the range of the reordering buffer */ -	index = ieee80211_sn_sub(mpdu_seq_num, -				 tid_agg_rx->ssn) % tid_agg_rx->buf_size; +	index = mpdu_seq_num % tid_agg_rx->buf_size;  	/* check if we already stored this frame */  	if (tid_agg_rx->reorder_buf[index]) { @@ -911,7 +891,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,  	u16 sc;  	u8 tid, ack_policy; -	if (!ieee80211_is_data_qos(hdr->frame_control)) +	if (!ieee80211_is_data_qos(hdr->frame_control) || +	    is_multicast_ether_addr(hdr->addr1))  		goto dont_reorder;  	/* @@ -995,8 +976,9 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)  				rx->sta->num_duplicates++;  			}  			return RX_DROP_UNUSABLE; -		} else +		} else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {  			rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl; +		}  	}  	if (unlikely(rx->skb->len < 16)) { @@ -1113,6 +1095,13 @@ static void sta_ps_end(struct sta_info *sta)  	       sta->sta.addr, sta->sta.aid);  	if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { +		/* +		 * Clear the flag only if the other one is still set +		 * so that the TX path won't start TX'ing new frames +		 * directly ... In the case that the driver flag isn't +		 * set ieee80211_sta_ps_deliver_wakeup() will clear it. +		 */ +		clear_sta_flag(sta, WLAN_STA_PS_STA);  		ps_dbg(sta->sdata, "STA %pM aid %d driver-ps-blocked\n",  		       sta->sta.addr, sta->sta.aid);  		return; @@ -1243,9 +1232,11 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)  		if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) &&  		    test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {  			sta->last_rx = jiffies; -			if (ieee80211_is_data(hdr->frame_control)) { +			if (ieee80211_is_data(hdr->frame_control) && +			    !is_multicast_ether_addr(hdr->addr1)) {  				sta->last_rx_rate_idx = status->rate_idx;  				sta->last_rx_rate_flag = status->flag; +				sta->last_rx_rate_vht_flag = status->vht_flag;  				sta->last_rx_rate_vht_nss = status->vht_nss;  			}  		} @@ -1258,6 +1249,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)  		if (ieee80211_is_data(hdr->frame_control)) {  			sta->last_rx_rate_idx = status->rate_idx;  			sta->last_rx_rate_flag = status->flag; +			sta->last_rx_rate_vht_flag = status->vht_flag;  			sta->last_rx_rate_vht_nss = status->vht_nss;  		}  	} @@ -1296,18 +1288,15 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)  	    !ieee80211_has_morefrags(hdr->frame_control) &&  	    !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&  	    (rx->sdata->vif.type == NL80211_IFTYPE_AP || -	     rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) { +	     rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && +	    /* PM bit is only checked in frames where it isn't reserved, +	     * in AP mode it's reserved in non-bufferable management frames +	     * (cf. IEEE 802.11-2012 8.2.4.1.7 Power Management field) +	     */ +	    (!ieee80211_is_mgmt(hdr->frame_control) || +	     ieee80211_is_bufferable_mmpdu(hdr->frame_control))) {  		if (test_sta_flag(sta, WLAN_STA_PS_STA)) { -			/* -			 * Ignore doze->wake transitions that are -			 * indicated by non-data frames, the standard -			 * is unclear here, but for example going to -			 * PS mode and then scanning would cause a -			 * doze->wake transition for the probe request, -			 * and that is clearly undesirable. -			 */ -			if (ieee80211_is_data(hdr->frame_control) && -			    !ieee80211_has_pm(hdr->frame_control)) +			if (!ieee80211_has_pm(hdr->frame_control))  				sta_ps_end(sta);  		} else {  			if (ieee80211_has_pm(hdr->frame_control)) @@ -1367,6 +1356,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  	struct ieee80211_key *sta_ptk = NULL;  	int mmie_keyidx = -1;  	__le16 fc; +	const struct ieee80211_cipher_scheme *cs = NULL;  	/*  	 * Key selection 101 @@ -1404,11 +1394,19 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  	/* start without a key */  	rx->key = NULL; +	fc = hdr->frame_control; -	if (rx->sta) -		sta_ptk = rcu_dereference(rx->sta->ptk); +	if (rx->sta) { +		int keyid = rx->sta->ptk_idx; -	fc = hdr->frame_control; +		if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) { +			cs = rx->sta->cipher_scheme; +			keyid = iwl80211_get_cs_keyid(cs, rx->skb); +			if (unlikely(keyid < 0)) +				return RX_DROP_UNUSABLE; +		} +		sta_ptk = rcu_dereference(rx->sta->ptk[keyid]); +	}  	if (!ieee80211_has_protected(fc))  		mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); @@ -1470,6 +1468,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  		return RX_CONTINUE;  	} else {  		u8 keyid; +  		/*  		 * The device doesn't give us the IV so we won't be  		 * able to look up the key. That's ok though, we @@ -1485,15 +1484,21 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  		hdrlen = ieee80211_hdrlen(fc); -		if (rx->skb->len < 8 + hdrlen) -			return RX_DROP_UNUSABLE; /* TODO: count this? */ +		if (cs) { +			keyidx = iwl80211_get_cs_keyid(cs, rx->skb); -		/* -		 * no need to call ieee80211_wep_get_keyidx, -		 * it verifies a bunch of things we've done already -		 */ -		skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); -		keyidx = keyid >> 6; +			if (unlikely(keyidx < 0)) +				return RX_DROP_UNUSABLE; +		} else { +			if (rx->skb->len < 8 + hdrlen) +				return RX_DROP_UNUSABLE; /* TODO: count this? */ +			/* +			 * no need to call ieee80211_wep_get_keyidx, +			 * it verifies a bunch of things we've done already +			 */ +			skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); +			keyidx = keyid >> 6; +		}  		/* check per-station GTK first, if multicast packet */  		if (is_multicast_ether_addr(hdr->addr1) && rx->sta) @@ -1541,11 +1546,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  		result = ieee80211_crypto_aes_cmac_decrypt(rx);  		break;  	default: -		/* -		 * We can reach here only with HW-only algorithms -		 * but why didn't it decrypt the frame?! -		 */ -		return RX_DROP_UNUSABLE; +		result = ieee80211_crypto_hw_decrypt(rx);  	}  	/* the hdr variable is invalid after the decrypt handlers */ @@ -1818,8 +1819,7 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)  		 * having configured keys.  		 */  		if (unlikely(ieee80211_is_action(fc) && !rx->key && -			     ieee80211_is_robust_mgmt_frame( -				     (struct ieee80211_hdr *) rx->skb->data))) +			     ieee80211_is_robust_mgmt_frame(rx->skb)))  			return -EACCES;  	} @@ -1936,20 +1936,17 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)  		}  	} -	if (skb) { -		int align __maybe_unused; -  #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS -		/* -		 * 'align' will only take the values 0 or 2 here -		 * since all frames are required to be aligned -		 * to 2-byte boundaries when being passed to -		 * mac80211; the code here works just as well if -		 * that isn't true, but mac80211 assumes it can -		 * access fields as 2-byte aligned (e.g. for -		 * compare_ether_addr) +	if (skb) { +		/* 'align' will only take the values 0 or 2 here since all +		 * frames are required to be aligned to 2-byte boundaries +		 * when being passed to mac80211; the code here works just +		 * as well if that isn't true, but mac80211 assumes it can +		 * access fields as 2-byte aligned (e.g. for ether_addr_equal)  		 */ -		align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3; +		int align; + +		align = (unsigned long)(skb->data + sizeof(struct ethhdr)) & 3;  		if (align) {  			if (WARN_ON(skb_headroom(skb) < 3)) {  				dev_kfree_skb(skb); @@ -1962,14 +1959,17 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)  				skb_set_tail_pointer(skb, len);  			}  		} +	}  #endif -		if (skb) { -			/* deliver to local stack */ -			skb->protocol = eth_type_trans(skb, dev); -			memset(skb->cb, 0, sizeof(skb->cb)); +	if (skb) { +		/* deliver to local stack */ +		skb->protocol = eth_type_trans(skb, dev); +		memset(skb->cb, 0, sizeof(skb->cb)); +		if (rx->local->napi) +			napi_gro_receive(rx->local->napi, skb); +		else  			netif_receive_skb(skb); -		}  	}  	if (xmit_skb) { @@ -2055,7 +2055,6 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; -	__le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD);  	u16 q, hdrlen;  	hdr = (struct ieee80211_hdr *) skb->data; @@ -2163,7 +2162,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  	} else {  		/* unable to resolve next hop */  		mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl, -				   fwd_hdr->addr3, 0, reason, fwd_hdr->addr2); +				   fwd_hdr->addr3, 0, +				   WLAN_REASON_MESH_PATH_NOFORWARD, +				   fwd_hdr->addr2);  		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);  		kfree_skb(fwd_skb);  		return RX_DROP_MONITOR; @@ -2402,7 +2403,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		return RX_DROP_UNUSABLE;  	if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC && -	    mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED) +	    mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED && +	    mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)  		return RX_DROP_UNUSABLE;  	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) @@ -2566,31 +2568,49 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		goto queue;  	case WLAN_CATEGORY_SPECTRUM_MGMT: -		if (status->band != IEEE80211_BAND_5GHZ) -			break; - -		if (sdata->vif.type != NL80211_IFTYPE_STATION) -			break; -  		/* verify action_code is present */  		if (len < IEEE80211_MIN_ACTION_SIZE + 1)  			break;  		switch (mgmt->u.action.u.measurement.action_code) {  		case WLAN_ACTION_SPCT_MSR_REQ: +			if (status->band != IEEE80211_BAND_5GHZ) +				break; +  			if (len < (IEEE80211_MIN_ACTION_SIZE +  				   sizeof(mgmt->u.action.u.measurement)))  				break; + +			if (sdata->vif.type != NL80211_IFTYPE_STATION) +				break; +  			ieee80211_process_measurement_req(sdata, mgmt, len);  			goto handled; -		case WLAN_ACTION_SPCT_CHL_SWITCH: -			if (sdata->vif.type != NL80211_IFTYPE_STATION) +		case WLAN_ACTION_SPCT_CHL_SWITCH: { +			u8 *bssid; +			if (len < (IEEE80211_MIN_ACTION_SIZE + +				   sizeof(mgmt->u.action.u.chan_switch)))  				break; -			if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) +			if (sdata->vif.type != NL80211_IFTYPE_STATION && +			    sdata->vif.type != NL80211_IFTYPE_ADHOC && +			    sdata->vif.type != NL80211_IFTYPE_MESH_POINT) +				break; + +			if (sdata->vif.type == NL80211_IFTYPE_STATION) +				bssid = sdata->u.mgd.bssid; +			else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) +				bssid = sdata->u.ibss.bssid; +			else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) +				bssid = mgmt->sa; +			else +				break; + +			if (!ether_addr_equal(mgmt->bssid, bssid))  				break;  			goto queue; +			}  		}  		break;  	case WLAN_CATEGORY_SA_QUERY: @@ -3032,8 +3052,8 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)  /* main receive path */ -static int prepare_for_handlers(struct ieee80211_rx_data *rx, -				struct ieee80211_hdr *hdr) +static bool prepare_for_handlers(struct ieee80211_rx_data *rx, +				 struct ieee80211_hdr *hdr)  {  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	struct sk_buff *skb = rx->skb; @@ -3044,26 +3064,29 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_STATION:  		if (!bssid && !sdata->u.mgd.use_4addr) -			return 0; +			return false;  		if (!multicast &&  		    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {  			if (!(sdata->dev->flags & IFF_PROMISC) ||  			    sdata->u.mgd.use_4addr) -				return 0; +				return false;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		}  		break;  	case NL80211_IFTYPE_ADHOC:  		if (!bssid) -			return 0; +			return false; +		if (ether_addr_equal(sdata->vif.addr, hdr->addr2) || +		    ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2)) +			return false;  		if (ieee80211_is_beacon(hdr->frame_control)) { -			return 1; +			return true;  		} else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) { -			return 0; +			return false;  		} else if (!multicast &&  			   !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {  			if (!(sdata->dev->flags & IFF_PROMISC)) -				return 0; +				return false;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		} else if (!rx->sta) {  			int rate_idx; @@ -3079,7 +3102,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  		if (!multicast &&  		    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {  			if (!(sdata->dev->flags & IFF_PROMISC)) -				return 0; +				return false;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		} @@ -3088,7 +3111,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  	case NL80211_IFTYPE_AP:  		if (!bssid) {  			if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) -				return 0; +				return false;  		} else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) {  			/*  			 * Accept public action frames even when the @@ -3098,26 +3121,26 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  			 */  			if (!multicast &&  			    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) -				return 0; +				return false;  			if (ieee80211_is_public_action(hdr, skb->len)) -				return 1; +				return true;  			if (!ieee80211_is_beacon(hdr->frame_control)) -				return 0; +				return false;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		}  		break;  	case NL80211_IFTYPE_WDS:  		if (bssid || !ieee80211_is_data(hdr->frame_control)) -			return 0; +			return false;  		if (!ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2)) -			return 0; +			return false;  		break;  	case NL80211_IFTYPE_P2P_DEVICE:  		if (!ieee80211_is_public_action(hdr, skb->len) &&  		    !ieee80211_is_probe_req(hdr->frame_control) &&  		    !ieee80211_is_probe_resp(hdr->frame_control) &&  		    !ieee80211_is_beacon(hdr->frame_control)) -			return 0; +			return false;  		if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) &&  		    !multicast)  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH; @@ -3128,7 +3151,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  		break;  	} -	return 1; +	return true;  }  /* @@ -3144,13 +3167,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_hdr *hdr = (void *)skb->data; -	int prepares;  	rx->skb = skb;  	status->rx_flags |= IEEE80211_RX_RA_MATCH; -	prepares = prepare_for_handlers(rx, hdr); -	if (!prepares) +	if (!prepare_for_handlers(rx, hdr))  		return false;  	if (!consume) { @@ -3171,7 +3192,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,  }  /* - * This is the actual Rx frames handler. as it blongs to Rx path it must + * This is the actual Rx frames handler. as it belongs to Rx path it must   * be called with rcu_read_lock protection.   */  static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 08afe74b98f..f40661eb75b 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -238,6 +238,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)  	enum ieee80211_band band;  	int i, ielen, n_chans; +	if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) +		return false; +  	do {  		if (local->hw_scan_band == IEEE80211_NUM_BANDS)  			return false; @@ -268,10 +271,11 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)  	return true;  } -static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, -				       bool was_hw_scan) +static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)  {  	struct ieee80211_local *local = hw_to_local(hw); +	bool hw_scan = local->ops->hw_scan; +	bool was_scanning = local->scanning;  	lockdep_assert_held(&local->mtx); @@ -287,7 +291,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,  	if (WARN_ON(!local->scan_req))  		return; -	if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { +	if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {  		int rc;  		rc = drv_hw_scan(local, @@ -305,7 +309,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,  	if (local->scan_req != local->int_scan_req)  		cfg80211_scan_done(local->scan_req, aborted);  	local->scan_req = NULL; -	rcu_assign_pointer(local->scan_sdata, NULL); +	RCU_INIT_POINTER(local->scan_sdata, NULL);  	local->scanning = 0;  	local->scan_chandef.chan = NULL; @@ -313,7 +317,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,  	/* Set power back to normal operating levels. */  	ieee80211_hw_config(local, 0); -	if (!was_hw_scan) { +	if (!hw_scan) {  		ieee80211_configure_filter(local);  		drv_sw_scan_complete(local);  		ieee80211_offchannel_return(local); @@ -324,7 +328,8 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,  	ieee80211_mlme_notify_scan_completed(local);  	ieee80211_ibss_notify_scan_completed(local);  	ieee80211_mesh_notify_scan_completed(local); -	ieee80211_start_next_roc(local); +	if (was_scanning) +		ieee80211_start_next_roc(local);  }  void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) @@ -391,8 +396,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local,  		return false;  	if (sdata->vif.type == NL80211_IFTYPE_STATION && -	    sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | -				  IEEE80211_STA_CONNECTION_POLL)) +	    sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)  		return false;  	return true; @@ -468,9 +472,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  	if (local->ops->hw_scan) {  		u8 *ies; -		local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN + -					     local->scan_ies_len + -					     req->ie_len; +		local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;  		local->hw_scan_req = kmalloc(  				sizeof(*local->hw_scan_req) +  				req->n_channels * sizeof(req->channels[0]) + @@ -524,7 +526,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  		ieee80211_hw_config(local, 0);  		if ((req->channels[0]->flags & -		     IEEE80211_CHAN_PASSIVE_SCAN) || +		     IEEE80211_CHAN_NO_IR) ||  		    !local->scan_req->n_ssids) {  			next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;  		} else { @@ -557,7 +559,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  		ieee80211_recalc_idle(local);  		local->scan_req = NULL; -		rcu_assign_pointer(local->scan_sdata, NULL); +		RCU_INIT_POINTER(local->scan_sdata, NULL);  	}  	return rc; @@ -570,7 +572,7 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)  	 * TODO: channel switching also consumes quite some time,  	 * add that delay as well to get a better estimation  	 */ -	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) +	if (chan->flags & IEEE80211_CHAN_NO_IR)  		return IEEE80211_PASSIVE_CHANNEL_TIME;  	return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;  } @@ -694,7 +696,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,  	 *  	 * In any case, it is not necessary for a passive scan.  	 */ -	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || +	if (chan->flags & IEEE80211_CHAN_NO_IR ||  	    !local->scan_req->n_ssids) {  		*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;  		local->next_scan_state = SCAN_DECISION; @@ -745,7 +747,7 @@ void ieee80211_scan_work(struct work_struct *work)  		container_of(work, struct ieee80211_local, scan_work.work);  	struct ieee80211_sub_if_data *sdata;  	unsigned long next_delay = 0; -	bool aborted, hw_scan; +	bool aborted;  	mutex_lock(&local->mtx); @@ -771,7 +773,7 @@ void ieee80211_scan_work(struct work_struct *work)  		int rc;  		local->scan_req = NULL; -		rcu_assign_pointer(local->scan_sdata, NULL); +		RCU_INIT_POINTER(local->scan_sdata, NULL);  		rc = __ieee80211_start_scan(sdata, req);  		if (rc) { @@ -784,14 +786,6 @@ void ieee80211_scan_work(struct work_struct *work)  	}  	/* -	 * Avoid re-scheduling when the sdata is going away. -	 */ -	if (!ieee80211_sdata_running(sdata)) { -		aborted = true; -		goto out_complete; -	} - -	/*  	 * as long as no delay is required advance immediately  	 * without scheduling a new work  	 */ @@ -832,8 +826,7 @@ void ieee80211_scan_work(struct work_struct *work)  	goto out;  out_complete: -	hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); -	__ieee80211_scan_completed(&local->hw, aborted, hw_scan); +	__ieee80211_scan_completed(&local->hw, aborted);  out:  	mutex_unlock(&local->mtx);  } @@ -879,7 +872,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,  				struct ieee80211_channel *tmp_ch =  				    &local->hw.wiphy->bands[band]->channels[i]; -				if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS | +				if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR |  						     IEEE80211_CHAN_DISABLED))  					continue; @@ -893,7 +886,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,  		local->int_scan_req->n_channels = n_ch;  	} else { -		if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS | +		if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR |  						IEEE80211_CHAN_DISABLED)))  			goto unlock; @@ -940,7 +933,23 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)  	if (!local->scan_req)  		goto out; +	/* +	 * We have a scan running and the driver already reported completion, +	 * but the worker hasn't run yet or is stuck on the mutex - mark it as +	 * cancelled. +	 */ +	if (test_bit(SCAN_HW_SCANNING, &local->scanning) && +	    test_bit(SCAN_COMPLETED, &local->scanning)) { +		set_bit(SCAN_HW_CANCELLED, &local->scanning); +		goto out; +	} +  	if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { +		/* +		 * Make sure that __ieee80211_scan_completed doesn't trigger a +		 * scan on another band. +		 */ +		set_bit(SCAN_HW_CANCELLED, &local->scanning);  		if (local->ops->cancel_hw_scan)  			drv_cancel_hw_scan(local,  				rcu_dereference_protected(local->scan_sdata, @@ -955,33 +964,25 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)  	 */  	cancel_delayed_work(&local->scan_work);  	/* and clean up */ -	__ieee80211_scan_completed(&local->hw, true, false); +	__ieee80211_scan_completed(&local->hw, true);  out:  	mutex_unlock(&local->mtx);  } -int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, -				       struct cfg80211_sched_scan_request *req) +int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, +					struct cfg80211_sched_scan_request *req)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_sched_scan_ies sched_scan_ies = {};  	struct cfg80211_chan_def chandef;  	int ret, i, iebufsz; -	iebufsz = 2 + IEEE80211_MAX_SSID_LEN + -		  local->scan_ies_len + req->ie_len; - -	mutex_lock(&local->mtx); +	iebufsz = local->scan_ies_len + req->ie_len; -	if (rcu_access_pointer(local->sched_scan_sdata)) { -		ret = -EBUSY; -		goto out; -	} +	lockdep_assert_held(&local->mtx); -	if (!local->ops->sched_scan_start) { -		ret = -ENOTSUPP; -		goto out; -	} +	if (!local->ops->sched_scan_start) +		return -ENOTSUPP;  	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {  		if (!local->hw.wiphy->bands[i]) @@ -1002,13 +1003,39 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,  	}  	ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); -	if (ret == 0) +	if (ret == 0) {  		rcu_assign_pointer(local->sched_scan_sdata, sdata); +		local->sched_scan_req = req; +	}  out_free:  	while (i > 0)  		kfree(sched_scan_ies.ie[--i]); -out: + +	if (ret) { +		/* Clean in case of failure after HW restart or upon resume. */ +		RCU_INIT_POINTER(local->sched_scan_sdata, NULL); +		local->sched_scan_req = NULL; +	} + +	return ret; +} + +int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, +				       struct cfg80211_sched_scan_request *req) +{ +	struct ieee80211_local *local = sdata->local; +	int ret; + +	mutex_lock(&local->mtx); + +	if (rcu_access_pointer(local->sched_scan_sdata)) { +		mutex_unlock(&local->mtx); +		return -EBUSY; +	} + +	ret = __ieee80211_request_sched_scan_start(sdata, req); +  	mutex_unlock(&local->mtx);  	return ret;  } @@ -1025,9 +1052,14 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)  		goto out;  	} -	if (rcu_access_pointer(local->sched_scan_sdata)) -		drv_sched_scan_stop(local, sdata); +	/* We don't want to restart sched scan anymore. */ +	local->sched_scan_req = NULL; +	if (rcu_access_pointer(local->sched_scan_sdata)) { +		ret = drv_sched_scan_stop(local, sdata); +		if (!ret) +			rcu_assign_pointer(local->sched_scan_sdata, NULL); +	}  out:  	mutex_unlock(&local->mtx); @@ -1044,12 +1076,8 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw)  }  EXPORT_SYMBOL(ieee80211_sched_scan_results); -void ieee80211_sched_scan_stopped_work(struct work_struct *work) +void ieee80211_sched_scan_end(struct ieee80211_local *local)  { -	struct ieee80211_local *local = -		container_of(work, struct ieee80211_local, -			     sched_scan_stopped_work); -  	mutex_lock(&local->mtx);  	if (!rcu_access_pointer(local->sched_scan_sdata)) { @@ -1057,19 +1085,31 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)  		return;  	} -	rcu_assign_pointer(local->sched_scan_sdata, NULL); +	RCU_INIT_POINTER(local->sched_scan_sdata, NULL); + +	/* If sched scan was aborted by the driver. */ +	local->sched_scan_req = NULL;  	mutex_unlock(&local->mtx);  	cfg80211_sched_scan_stopped(local->hw.wiphy);  } +void ieee80211_sched_scan_stopped_work(struct work_struct *work) +{ +	struct ieee80211_local *local = +		container_of(work, struct ieee80211_local, +			     sched_scan_stopped_work); + +	ieee80211_sched_scan_end(local); +} +  void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)  {  	struct ieee80211_local *local = hw_to_local(hw);  	trace_api_sched_scan_stopped(local); -	ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work); +	schedule_work(&local->sched_scan_stopped_work);  }  EXPORT_SYMBOL(ieee80211_sched_scan_stopped); diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 578eea3fc04..6ab00907008 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -21,6 +21,177 @@  #include "sta_info.h"  #include "wme.h" +int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, +				 struct ieee802_11_elems *elems, bool beacon, +				 enum ieee80211_band current_band, +				 u32 sta_flags, u8 *bssid, +				 struct ieee80211_csa_ie *csa_ie) +{ +	enum ieee80211_band new_band; +	int new_freq; +	u8 new_chan_no; +	struct ieee80211_channel *new_chan; +	struct cfg80211_chan_def new_vht_chandef = {}; +	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; +	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; +	const struct ieee80211_ht_operation *ht_oper; +	int secondary_channel_offset = -1; + +	sec_chan_offs = elems->sec_chan_offs; +	wide_bw_chansw_ie = elems->wide_bw_chansw_ie; +	ht_oper = elems->ht_operation; + +	if (sta_flags & (IEEE80211_STA_DISABLE_HT | +			 IEEE80211_STA_DISABLE_40MHZ)) { +		sec_chan_offs = NULL; +		wide_bw_chansw_ie = NULL; +		/* only used for bandwidth here */ +		ht_oper = NULL; +	} + +	if (sta_flags & IEEE80211_STA_DISABLE_VHT) +		wide_bw_chansw_ie = NULL; + +	if (elems->ext_chansw_ie) { +		if (!ieee80211_operating_class_to_band( +				elems->ext_chansw_ie->new_operating_class, +				&new_band)) { +			sdata_info(sdata, +				   "cannot understand ECSA IE operating class %d, disconnecting\n", +				   elems->ext_chansw_ie->new_operating_class); +			return -EINVAL; +		} +		new_chan_no = elems->ext_chansw_ie->new_ch_num; +		csa_ie->count = elems->ext_chansw_ie->count; +		csa_ie->mode = elems->ext_chansw_ie->mode; +	} else if (elems->ch_switch_ie) { +		new_band = current_band; +		new_chan_no = elems->ch_switch_ie->new_ch_num; +		csa_ie->count = elems->ch_switch_ie->count; +		csa_ie->mode = elems->ch_switch_ie->mode; +	} else { +		/* nothing here we understand */ +		return 1; +	} + +	/* Mesh Channel Switch Parameters Element */ +	if (elems->mesh_chansw_params_ie) { +		csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; +		csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags; +		csa_ie->pre_value = le16_to_cpu( +				elems->mesh_chansw_params_ie->mesh_pre_value); +	} + +	new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); +	new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); +	if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { +		sdata_info(sdata, +			   "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n", +			   bssid, new_freq); +		return -EINVAL; +	} + +	if (!beacon && sec_chan_offs) { +		secondary_channel_offset = sec_chan_offs->sec_chan_offs; +	} else if (beacon && ht_oper) { +		secondary_channel_offset = +			ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; +	} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) { +		/* If it's not a beacon, HT is enabled and the IE not present, +		 * it's 20 MHz, 802.11-2012 8.5.2.6: +		 *	This element [the Secondary Channel Offset Element] is +		 *	present when switching to a 40 MHz channel. It may be +		 *	present when switching to a 20 MHz channel (in which +		 *	case the secondary channel offset is set to SCN). +		 */ +		secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; +	} + +	switch (secondary_channel_offset) { +	default: +		/* secondary_channel_offset was present but is invalid */ +	case IEEE80211_HT_PARAM_CHA_SEC_NONE: +		cfg80211_chandef_create(&csa_ie->chandef, new_chan, +					NL80211_CHAN_HT20); +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: +		cfg80211_chandef_create(&csa_ie->chandef, new_chan, +					NL80211_CHAN_HT40PLUS); +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_BELOW: +		cfg80211_chandef_create(&csa_ie->chandef, new_chan, +					NL80211_CHAN_HT40MINUS); +		break; +	case -1: +		cfg80211_chandef_create(&csa_ie->chandef, new_chan, +					NL80211_CHAN_NO_HT); +		/* keep width for 5/10 MHz channels */ +		switch (sdata->vif.bss_conf.chandef.width) { +		case NL80211_CHAN_WIDTH_5: +		case NL80211_CHAN_WIDTH_10: +			csa_ie->chandef.width = +				sdata->vif.bss_conf.chandef.width; +			break; +		default: +			break; +		} +		break; +	} + +	if (wide_bw_chansw_ie) { +		new_vht_chandef.chan = new_chan; +		new_vht_chandef.center_freq1 = +			ieee80211_channel_to_frequency( +				wide_bw_chansw_ie->new_center_freq_seg0, +				new_band); + +		switch (wide_bw_chansw_ie->new_channel_width) { +		default: +			/* hmmm, ignore VHT and use HT if present */ +		case IEEE80211_VHT_CHANWIDTH_USE_HT: +			new_vht_chandef.chan = NULL; +			break; +		case IEEE80211_VHT_CHANWIDTH_80MHZ: +			new_vht_chandef.width = NL80211_CHAN_WIDTH_80; +			break; +		case IEEE80211_VHT_CHANWIDTH_160MHZ: +			new_vht_chandef.width = NL80211_CHAN_WIDTH_160; +			break; +		case IEEE80211_VHT_CHANWIDTH_80P80MHZ: +			/* field is otherwise reserved */ +			new_vht_chandef.center_freq2 = +				ieee80211_channel_to_frequency( +					wide_bw_chansw_ie->new_center_freq_seg1, +					new_band); +			new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; +			break; +		} +		if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ && +		    new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) +			ieee80211_chandef_downgrade(&new_vht_chandef); +		if (sta_flags & IEEE80211_STA_DISABLE_160MHZ && +		    new_vht_chandef.width == NL80211_CHAN_WIDTH_160) +			ieee80211_chandef_downgrade(&new_vht_chandef); +		if (sta_flags & IEEE80211_STA_DISABLE_40MHZ && +		    new_vht_chandef.width > NL80211_CHAN_WIDTH_20) +			ieee80211_chandef_downgrade(&new_vht_chandef); +	} + +	/* if VHT data is there validate & use it */ +	if (new_vht_chandef.chan) { +		if (!cfg80211_chandef_compatible(&new_vht_chandef, +						 &csa_ie->chandef)) { +			sdata_info(sdata, +				   "BSS %pM: CSA has inconsistent channel data, disconnecting\n", +				   bssid); +			return -EINVAL; +		} +		csa_ie->chandef = new_vht_chandef; +	} + +	return 0; +} +  static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,  					struct ieee80211_msrment_ie *request_ie,  					const u8 *da, const u8 *bssid, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aeb967a0aee..a9b46d8ea22 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -91,7 +91,7 @@ static int sta_info_hash_del(struct ieee80211_local *local,  	return -ENOENT;  } -static void cleanup_single_sta(struct sta_info *sta) +static void __cleanup_single_sta(struct sta_info *sta)  {  	int ac, i;  	struct tid_ampdu_tx *tid_tx; @@ -99,24 +99,8 @@ static void cleanup_single_sta(struct sta_info *sta)  	struct ieee80211_local *local = sdata->local;  	struct ps_data *ps; -	/* -	 * At this point, when being called as call_rcu callback, -	 * neither mac80211 nor the driver can reference this -	 * sta struct any more except by still existing timers -	 * associated with this station that we clean up below. -	 * -	 * Note though that this still uses the sdata and even -	 * calls the driver in AP and mesh mode, so interfaces -	 * of those types mush use call sta_info_flush_cleanup() -	 * (typically via sta_info_flush()) before deconfiguring -	 * the driver. -	 * -	 * In station mode, nothing happens here so it doesn't -	 * have to (and doesn't) do that, this is intentional to -	 * speed up roaming. -	 */ - -	if (test_sta_flag(sta, WLAN_STA_PS_STA)) { +	if (test_sta_flag(sta, WLAN_STA_PS_STA) || +	    test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {  		if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||  		    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)  			ps = &sdata->bss->ps; @@ -126,6 +110,7 @@ static void cleanup_single_sta(struct sta_info *sta)  			return;  		clear_sta_flag(sta, WLAN_STA_PS_STA); +		clear_sta_flag(sta, WLAN_STA_PS_DRIVER);  		atomic_dec(&ps->num_sta_ps);  		sta_info_recalc_tim(sta); @@ -156,39 +141,15 @@ static void cleanup_single_sta(struct sta_info *sta)  		ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);  		kfree(tid_tx);  	} - -	sta_info_free(local, sta);  } -void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata) -{ -	struct sta_info *sta; - -	spin_lock_bh(&sdata->cleanup_stations_lock); -	while (!list_empty(&sdata->cleanup_stations)) { -		sta = list_first_entry(&sdata->cleanup_stations, -				       struct sta_info, list); -		list_del(&sta->list); -		spin_unlock_bh(&sdata->cleanup_stations_lock); - -		cleanup_single_sta(sta); - -		spin_lock_bh(&sdata->cleanup_stations_lock); -	} - -	spin_unlock_bh(&sdata->cleanup_stations_lock); -} - -static void free_sta_rcu(struct rcu_head *h) +static void cleanup_single_sta(struct sta_info *sta)  { -	struct sta_info *sta = container_of(h, struct sta_info, rcu_head);  	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; -	spin_lock(&sdata->cleanup_stations_lock); -	list_add_tail(&sta->list, &sdata->cleanup_stations); -	spin_unlock(&sdata->cleanup_stations_lock); - -	ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk); +	__cleanup_single_sta(sta); +	sta_info_free(local, sta);  }  /* protected by RCU */ @@ -266,11 +227,20 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,   */  void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)  { +	int i; +  	if (sta->rate_ctrl)  		rate_control_free_sta(sta); +	if (sta->tx_lat) { +		for (i = 0; i < IEEE80211_NUM_TIDS; i++) +			kfree(sta->tx_lat[i].bins); +		kfree(sta->tx_lat); +	} +  	sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); +	kfree(rcu_dereference_raw(sta->sta.rates));  	kfree(sta);  } @@ -333,13 +303,44 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta;  	struct timespec uptime; +	struct ieee80211_tx_latency_bin_ranges *tx_latency;  	int i;  	sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);  	if (!sta)  		return NULL; +	rcu_read_lock(); +	tx_latency = rcu_dereference(local->tx_latency); +	/* init stations Tx latency statistics && TID bins */ +	if (tx_latency) { +		sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS * +				      sizeof(struct ieee80211_tx_latency_stat), +				      GFP_ATOMIC); +		if (!sta->tx_lat) { +			rcu_read_unlock(); +			goto free; +		} + +		if (tx_latency->n_ranges) { +			for (i = 0; i < IEEE80211_NUM_TIDS; i++) { +				/* size of bins is size of the ranges +1 */ +				sta->tx_lat[i].bin_count = +					tx_latency->n_ranges + 1; +				sta->tx_lat[i].bins = +					kcalloc(sta->tx_lat[i].bin_count, +						sizeof(u32), GFP_ATOMIC); +				if (!sta->tx_lat[i].bins) { +					rcu_read_unlock(); +					goto free; +				} +			} +		} +	} +	rcu_read_unlock(); +  	spin_lock_init(&sta->lock); +	spin_lock_init(&sta->ps_lock);  	INIT_WORK(&sta->drv_unblock_wk, sta_unblock);  	INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);  	mutex_init(&sta->ampdu_mlme.mtx); @@ -363,10 +364,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,  	for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)  		ewma_init(&sta->chain_signal_avg[i], 1024, 8); -	if (sta_prepare_rate_control(local, sta, gfp)) { -		kfree(sta); -		return NULL; -	} +	if (sta_prepare_rate_control(local, sta, gfp)) +		goto free;  	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {  		/* @@ -385,10 +384,42 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,  		sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);  	sta->sta.smps_mode = IEEE80211_SMPS_OFF; +	if (sdata->vif.type == NL80211_IFTYPE_AP || +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		struct ieee80211_supported_band *sband = +			local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; +		u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> +				IEEE80211_HT_CAP_SM_PS_SHIFT; +		/* +		 * Assume that hostapd advertises our caps in the beacon and +		 * this is the known_smps_mode for a station that just assciated +		 */ +		switch (smps) { +		case WLAN_HT_SMPS_CONTROL_DISABLED: +			sta->known_smps_mode = IEEE80211_SMPS_OFF; +			break; +		case WLAN_HT_SMPS_CONTROL_STATIC: +			sta->known_smps_mode = IEEE80211_SMPS_STATIC; +			break; +		case WLAN_HT_SMPS_CONTROL_DYNAMIC: +			sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC; +			break; +		default: +			WARN_ON(1); +		} +	}  	sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); -  	return sta; + +free: +	if (sta->tx_lat) { +		for (i = 0; i < IEEE80211_NUM_TIDS; i++) +			kfree(sta->tx_lat[i].bins); +		kfree(sta->tx_lat); +	} +	kfree(sta); +	return NULL;  }  static int sta_info_insert_check(struct sta_info *sta) @@ -467,22 +498,28 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)  		goto out_err;  	} -	/* notify driver */ -	err = sta_info_insert_drv_state(local, sdata, sta); -	if (err) -		goto out_err; -  	local->num_sta++;  	local->sta_generation++;  	smp_mb(); +	/* simplify things and don't accept BA sessions yet */ +	set_sta_flag(sta, WLAN_STA_BLOCK_BA); +  	/* make the station visible */  	sta_info_hash_add(local, sta);  	list_add_rcu(&sta->list, &local->sta_list); +	/* notify driver */ +	err = sta_info_insert_drv_state(local, sdata, sta); +	if (err) +		goto out_remove; +  	set_sta_flag(sta, WLAN_STA_INSERTED); +	/* accept BA sessions now */ +	clear_sta_flag(sta, WLAN_STA_BLOCK_BA); +	ieee80211_recalc_min_chandef(sdata);  	ieee80211_sta_debugfs_add(sta);  	rate_control_add_sta_debugfs(sta); @@ -501,6 +538,12 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)  		mesh_accept_plinks_update(sdata);  	return 0; + out_remove: +	sta_info_hash_del(local, sta); +	list_del_rcu(&sta->list); +	local->num_sta--; +	synchronize_net(); +	__cleanup_single_sta(sta);   out_err:  	mutex_unlock(&local->sta_mtx);  	rcu_read_lock(); @@ -510,7 +553,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)  int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)  {  	struct ieee80211_local *local = sta->local; -	int err = 0; +	int err;  	might_sleep(); @@ -528,7 +571,6 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)  	return 0;   out_free: -	BUG_ON(!err);  	sta_info_free(local, sta);  	return err;  } @@ -606,8 +648,8 @@ void sta_info_recalc_tim(struct sta_info *sta)  #ifdef CONFIG_MAC80211_MESH  	} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {  		ps = &sta->sdata->u.mesh.ps; -		/* TIM map only for PLID <= IEEE80211_MAX_AID */ -		id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID; +		/* TIM map only for 1 <= PLID <= IEEE80211_MAX_AID */ +		id = sta->plid % (IEEE80211_MAX_AID + 1);  #endif  	} else {  		return; @@ -783,7 +825,7 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,  	return have_buffered;  } -int __must_check __sta_info_destroy(struct sta_info *sta) +static int __must_check __sta_info_destroy_part1(struct sta_info *sta)  {  	struct ieee80211_local *local;  	struct ieee80211_sub_if_data *sdata; @@ -809,12 +851,35 @@ int __must_check __sta_info_destroy(struct sta_info *sta)  	ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);  	ret = sta_info_hash_del(local, sta); -	if (ret) +	if (WARN_ON(ret))  		return ret;  	list_del_rcu(&sta->list); -	/* this always calls synchronize_net() */ +	drv_sta_pre_rcu_remove(local, sta->sdata, sta); + +	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +	    rcu_access_pointer(sdata->u.vlan.sta) == sta) +		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); + +	return 0; +} + +static void __sta_info_destroy_part2(struct sta_info *sta) +{ +	struct ieee80211_local *local = sta->local; +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	int ret; + +	/* +	 * NOTE: This assumes at least synchronize_net() was done +	 *	 after _part1 and before _part2! +	 */ + +	might_sleep(); +	lockdep_assert_held(&local->sta_mtx); + +	/* now keys can no longer be reached */  	ieee80211_free_sta_keys(local, sta);  	sta->dead = true; @@ -822,9 +887,6 @@ int __must_check __sta_info_destroy(struct sta_info *sta)  	local->num_sta--;  	local->sta_generation++; -	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) -		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); -  	while (sta->sta_state > IEEE80211_STA_NONE) {  		ret = sta_info_move_state(sta, sta->sta_state - 1);  		if (ret) { @@ -845,8 +907,21 @@ int __must_check __sta_info_destroy(struct sta_info *sta)  	rate_control_remove_sta_debugfs(sta);  	ieee80211_sta_debugfs_remove(sta); +	ieee80211_recalc_min_chandef(sdata); -	call_rcu(&sta->rcu_head, free_sta_rcu); +	cleanup_single_sta(sta); +} + +int __must_check __sta_info_destroy(struct sta_info *sta) +{ +	int err = __sta_info_destroy_part1(sta); + +	if (err) +		return err; + +	synchronize_net(); + +	__sta_info_destroy_part2(sta);  	return 0;  } @@ -916,32 +991,38 @@ void sta_info_stop(struct ieee80211_local *local)  } -int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) +int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans)  {  	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta, *tmp; +	LIST_HEAD(free_list);  	int ret = 0;  	might_sleep(); +	WARN_ON(vlans && sdata->vif.type != NL80211_IFTYPE_AP); +	WARN_ON(vlans && !sdata->bss); +  	mutex_lock(&local->sta_mtx);  	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { -		if (sdata == sta->sdata) { -			WARN_ON(__sta_info_destroy(sta)); +		if (sdata == sta->sdata || +		    (vlans && sdata->bss == sta->sdata->bss)) { +			if (!WARN_ON(__sta_info_destroy_part1(sta))) +				list_add(&sta->free_list, &free_list);  			ret++;  		}  	} + +	if (!list_empty(&free_list)) { +		synchronize_net(); +		list_for_each_entry_safe(sta, tmp, &free_list, free_list) +			__sta_info_destroy_part2(sta); +	}  	mutex_unlock(&local->sta_mtx);  	return ret;  } -void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata) -{ -	ieee80211_cleanup_sdata_stas(sdata); -	cancel_work_sync(&sdata->cleanup_stations_wk); -} -  void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,  			  unsigned long exp_time)  { @@ -1011,10 +1092,14 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,  }  EXPORT_SYMBOL(ieee80211_find_sta); -static void clear_sta_ps_flags(void *_sta) +/* powersave support code */ +void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)  { -	struct sta_info *sta = _sta;  	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; +	struct sk_buff_head pending; +	int filtered = 0, buffered = 0, ac; +	unsigned long flags;  	struct ps_data *ps;  	if (sdata->vif.type == NL80211_IFTYPE_AP || @@ -1025,20 +1110,6 @@ static void clear_sta_ps_flags(void *_sta)  	else  		return; -	clear_sta_flag(sta, WLAN_STA_PS_DRIVER); -	if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA)) -		atomic_dec(&ps->num_sta_ps); -} - -/* powersave support code */ -void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) -{ -	struct ieee80211_sub_if_data *sdata = sta->sdata; -	struct ieee80211_local *local = sdata->local; -	struct sk_buff_head pending; -	int filtered = 0, buffered = 0, ac; -	unsigned long flags; -  	clear_sta_flag(sta, WLAN_STA_SP);  	BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1); @@ -1049,6 +1120,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)  	skb_queue_head_init(&pending); +	/* sync with ieee80211_tx_h_unicast_ps_buf */ +	spin_lock(&sta->ps_lock);  	/* Send all buffered frames to the station */  	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {  		int count = skb_queue_len(&pending), tmp; @@ -1067,7 +1140,26 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)  		buffered += tmp - count;  	} -	ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); +	ieee80211_add_pending_skbs(local, &pending); +	clear_sta_flag(sta, WLAN_STA_PS_DRIVER); +	clear_sta_flag(sta, WLAN_STA_PS_STA); +	spin_unlock(&sta->ps_lock); + +	atomic_dec(&ps->num_sta_ps); + +	/* This station just woke up and isn't aware of our SMPS state */ +	if (!ieee80211_vif_is_mesh(&sdata->vif) && +	    !ieee80211_smps_is_restrictive(sta->known_smps_mode, +					   sdata->smps_mode) && +	    sta->known_smps_mode != sdata->bss->req_smps && +	    sta_info_tx_streams(sta) != 1) { +		ht_dbg(sdata, +		       "%pM just woke up and MIMO capable - update SMPS\n", +		       sta->sta.addr); +		ieee80211_send_smps_action(sdata, sdata->bss->req_smps, +					   sta->sta.addr, +					   sdata->vif.bss_conf.bssid); +	}  	local->total_ps_buffered -= buffered; @@ -1080,7 +1172,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)  static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,  					 struct sta_info *sta, int tid, -					 enum ieee80211_frame_release_type reason) +					 enum ieee80211_frame_release_type reason, +					 bool call_driver)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_qos_hdr *nullfunc; @@ -1114,6 +1207,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,  	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);  	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);  	memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); +	nullfunc->seq_ctrl = 0;  	skb->priority = tid;  	skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); @@ -1138,7 +1232,9 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,  		       IEEE80211_TX_STATUS_EOSP |  		       IEEE80211_TX_CTL_REQ_TX_STATUS; -	drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); +	if (call_driver) +		drv_allow_buffered_frames(local, sta, BIT(tid), 1, +					  reason, false);  	skb->dev = sdata->dev; @@ -1154,6 +1250,17 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,  	rcu_read_unlock();  } +static int find_highest_prio_tid(unsigned long tids) +{ +	/* lower 3 TIDs aren't ordered perfectly */ +	if (tids & 0xF8) +		return fls(tids) - 1; +	/* TID 0 is BE just like TID 3 */ +	if (tids & BIT(0)) +		return 0; +	return fls(tids) - 1; +} +  static void  ieee80211_sta_ps_deliver_response(struct sta_info *sta,  				  int n_frames, u8 ignored_acs, @@ -1161,7 +1268,6 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,  {  	struct ieee80211_sub_if_data *sdata = sta->sdata;  	struct ieee80211_local *local = sdata->local; -	bool found = false;  	bool more_data = false;  	int ac;  	unsigned long driver_release_tids = 0; @@ -1172,9 +1278,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,  	__skb_queue_head_init(&frames); -	/* -	 * Get response frame(s) and more data bit for it. -	 */ +	/* Get response frame(s) and more data bit for the last one. */  	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {  		unsigned long tids; @@ -1183,43 +1287,48 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,  		tids = ieee80211_tids_for_ac(ac); -		if (!found) { -			driver_release_tids = sta->driver_buffered_tids & tids; -			if (driver_release_tids) { -				found = true; -			} else { -				struct sk_buff *skb; - -				while (n_frames > 0) { -					skb = skb_dequeue(&sta->tx_filtered[ac]); -					if (!skb) { -						skb = skb_dequeue( -							&sta->ps_tx_buf[ac]); -						if (skb) -							local->total_ps_buffered--; -					} -					if (!skb) -						break; -					n_frames--; -					found = true; -					__skb_queue_tail(&frames, skb); -				} -			} +		/* if we already have frames from software, then we can't also +		 * release from hardware queues +		 */ +		if (skb_queue_empty(&frames)) +			driver_release_tids |= sta->driver_buffered_tids & tids; -			/* -			 * If the driver has data on more than one TID then +		if (driver_release_tids) { +			/* If the driver has data on more than one TID then  			 * certainly there's more data if we release just a -			 * single frame now (from a single TID). +			 * single frame now (from a single TID). This will +			 * only happen for PS-Poll.  			 */  			if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&  			    hweight16(driver_release_tids) > 1) {  				more_data = true;  				driver_release_tids = -					BIT(ffs(driver_release_tids) - 1); +					BIT(find_highest_prio_tid( +						driver_release_tids));  				break;  			} +		} else { +			struct sk_buff *skb; + +			while (n_frames > 0) { +				skb = skb_dequeue(&sta->tx_filtered[ac]); +				if (!skb) { +					skb = skb_dequeue( +						&sta->ps_tx_buf[ac]); +					if (skb) +						local->total_ps_buffered--; +				} +				if (!skb) +					break; +				n_frames--; +				__skb_queue_tail(&frames, skb); +			}  		} +		/* If we have more frames buffered on this AC, then set the +		 * more-data bit and abort the loop since we can't send more +		 * data from other ACs before the buffered frames from this. +		 */  		if (!skb_queue_empty(&sta->tx_filtered[ac]) ||  		    !skb_queue_empty(&sta->ps_tx_buf[ac])) {  			more_data = true; @@ -1227,7 +1336,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,  		}  	} -	if (!found) { +	if (skb_queue_empty(&frames) && !driver_release_tids) {  		int tid;  		/* @@ -1248,15 +1357,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,  		/* This will evaluate to 1, 3, 5 or 7. */  		tid = 7 - ((ffs(~ignored_acs) - 1) << 1); -		ieee80211_send_null_response(sdata, sta, tid, reason); -		return; -	} - -	if (!driver_release_tids) { +		ieee80211_send_null_response(sdata, sta, tid, reason, true); +	} else if (!driver_release_tids) {  		struct sk_buff_head pending;  		struct sk_buff *skb;  		int num = 0;  		u16 tids = 0; +		bool need_null = false;  		skb_queue_head_init(&pending); @@ -1290,22 +1397,57 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,  			    ieee80211_is_qos_nullfunc(hdr->frame_control))  				qoshdr = ieee80211_get_qos_ctl(hdr); -			/* end service period after last frame */ -			if (skb_queue_empty(&frames)) { -				if (reason == IEEE80211_FRAME_RELEASE_UAPSD && -				    qoshdr) -					*qoshdr |= IEEE80211_QOS_CTL_EOSP; +			tids |= BIT(skb->priority); + +			__skb_queue_tail(&pending, skb); + +			/* end service period after last frame or add one */ +			if (!skb_queue_empty(&frames)) +				continue; +			if (reason != IEEE80211_FRAME_RELEASE_UAPSD) { +				/* for PS-Poll, there's only one frame */  				info->flags |= IEEE80211_TX_STATUS_EOSP |  					       IEEE80211_TX_CTL_REQ_TX_STATUS; +				break;  			} -			if (qoshdr) -				tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); -			else -				tids |= BIT(0); +			/* For uAPSD, things are a bit more complicated. If the +			 * last frame has a QoS header (i.e. is a QoS-data or +			 * QoS-nulldata frame) then just set the EOSP bit there +			 * and be done. +			 * If the frame doesn't have a QoS header (which means +			 * it should be a bufferable MMPDU) then we can't set +			 * the EOSP bit in the QoS header; add a QoS-nulldata +			 * frame to the list to send it after the MMPDU. +			 * +			 * Note that this code is only in the mac80211-release +			 * code path, we assume that the driver will not buffer +			 * anything but QoS-data frames, or if it does, will +			 * create the QoS-nulldata frame by itself if needed. +			 * +			 * Cf. 802.11-2012 10.2.1.10 (c). +			 */ +			if (qoshdr) { +				*qoshdr |= IEEE80211_QOS_CTL_EOSP; -			__skb_queue_tail(&pending, skb); +				info->flags |= IEEE80211_TX_STATUS_EOSP | +					       IEEE80211_TX_CTL_REQ_TX_STATUS; +			} else { +				/* The standard isn't completely clear on this +				 * as it says the more-data bit should be set +				 * if there are more BUs. The QoS-Null frame +				 * we're about to send isn't buffered yet, we +				 * only create it below, but let's pretend it +				 * was buffered just in case some clients only +				 * expect more-data=0 when eosp=1. +				 */ +				hdr->frame_control |= +					cpu_to_le16(IEEE80211_FCTL_MOREDATA); +				need_null = true; +				num++; +			} +			break;  		}  		drv_allow_buffered_frames(local, sta, tids, num, @@ -1313,17 +1455,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,  		ieee80211_add_pending_skbs(local, &pending); +		if (need_null) +			ieee80211_send_null_response( +				sdata, sta, find_highest_prio_tid(tids), +				reason, false); +  		sta_info_recalc_tim(sta);  	} else {  		/*  		 * We need to release a frame that is buffered somewhere in the  		 * driver ... it'll have to handle that. -		 * Note that, as per the comment above, it'll also have to see -		 * if there is more than just one frame on the specific TID that -		 * we're releasing from, and it needs to set the more-data bit -		 * accordingly if we tell it that there's no more data. If we do -		 * tell it there's more data, then of course the more-data bit -		 * needs to be set anyway. +		 * Note that the driver also has to check the number of frames +		 * on the TIDs we're releasing from - if there are more than +		 * n_frames it has to set the more-data bit (if we didn't ask +		 * it to set it anyway due to other buffered frames); if there +		 * are fewer than n_frames it has to make sure to adjust that +		 * to allow the service period to end properly.  		 */  		drv_release_buffered_frames(local, sta, driver_release_tids,  					    n_frames, reason, more_data); @@ -1331,9 +1478,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,  		/*  		 * Note that we don't recalculate the TIM bit here as it would  		 * most likely have no effect at all unless the driver told us -		 * that the TID became empty before returning here from the +		 * that the TID(s) became empty before returning here from the  		 * release function. -		 * Either way, however, when the driver tells us that the TID +		 * Either way, however, when the driver tells us that the TID(s)  		 * became empty we'll do the TIM recalculation.  		 */  	} @@ -1422,6 +1569,8 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,  	if (WARN_ON(tid >= IEEE80211_NUM_TIDS))  		return; +	trace_api_sta_set_buffered(sta->local, pubsta, tid, buffered); +  	if (buffered)  		set_bit(tid, &sta->driver_buffered_tids);  	else @@ -1520,3 +1669,38 @@ int sta_info_move_state(struct sta_info *sta,  	return 0;  } + +u8 sta_info_tx_streams(struct sta_info *sta) +{ +	struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap; +	u8 rx_streams; + +	if (!sta->sta.ht_cap.ht_supported) +		return 1; + +	if (sta->sta.vht_cap.vht_supported) { +		int i; +		u16 tx_mcs_map = +			le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map); + +		for (i = 7; i >= 0; i--) +			if ((tx_mcs_map & (0x3 << (i * 2))) != +			    IEEE80211_VHT_MCS_NOT_SUPPORTED) +				return i + 1; +	} + +	if (ht_cap->mcs.rx_mask[3]) +		rx_streams = 4; +	else if (ht_cap->mcs.rx_mask[2]) +		rx_streams = 3; +	else if (ht_cap->mcs.rx_mask[1]) +		rx_streams = 2; +	else +		rx_streams = 1; + +	if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF)) +		return rx_streams; + +	return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) +			>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4208dbd5861..4acc5fc402f 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -220,6 +220,25 @@ struct sta_ampdu_mlme {  	u8 dialog_token_allocator;  }; +/* + * struct ieee80211_tx_latency_stat - Tx latency statistics + * + * Measures TX latency and jitter for a station per TID. + * + * @max: worst case latency + * @sum: sum of all latencies + * @counter: amount of Tx frames sent from interface + * @bins: each bin counts how many frames transmitted within a certain + * latency range. when disabled it is NULL. + * @bin_count: amount of bins. + */ +struct ieee80211_tx_latency_stat { +	u32 max; +	u32 sum; +	u32 counter; +	u32 *bins; +	u32 bin_count; +};  /**   * struct sta_info - STA information @@ -228,23 +247,28 @@ struct sta_ampdu_mlme {   * mac80211 is communicating with.   *   * @list: global linked list entry + * @free_list: list entry for keeping track of stations to free   * @hnext: hash table linked list pointer   * @local: pointer to the global information   * @sdata: virtual interface this station belongs to - * @ptk: peer key negotiated with this station, if any + * @ptk: peer keys negotiated with this station, if any + * @ptk_idx: last installed peer key index   * @gtk: group keys negotiated with this station, if any + * @gtk_idx: last installed group key index   * @rate_ctrl: rate control algorithm reference   * @rate_ctrl_priv: rate control private per-STA pointer   * @last_tx_rate: rate used for last transmit, to report to userspace as   *	"the" transmit rate   * @last_rx_rate_idx: rx status rate index of the last data packet   * @last_rx_rate_flag: rx status flag of the last data packet + * @last_rx_rate_vht_flag: rx status vht flag of the last data packet   * @last_rx_rate_vht_nss: rx status nss of last data packet   * @lock: used for locking all fields that require locking, see comments   *	in the header file.   * @drv_unblock_wk: used for driver PS unblocking   * @listen_interval: listen interval of this station, when we're acting as AP   * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly + * @ps_lock: used for powersave (when mac80211 is the AP) related locking   * @ps_tx_buf: buffers (per AC) of frames to transmit to this station   *	when it leaves power saving state or polls   * @tx_filtered: buffers (per AC) of frames we already tried to @@ -274,6 +298,7 @@ struct sta_ampdu_mlme {   * @tid_seq: per-TID sequence numbers for sending to this STA   * @ampdu_mlme: A-MPDU state machine state   * @timer_to_tid: identity mapping to ID timers + * @tx_lat: Tx latency statistics   * @llid: Local link ID   * @plid: Peer link ID   * @reason: Cancel reason on PLINK_HOLDING state @@ -301,16 +326,21 @@ struct sta_ampdu_mlme {   * @chains: chains ever used for RX from this station   * @chain_signal_last: last signal (per chain)   * @chain_signal_avg: signal average (per chain) + * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for + *	AP only. + * @cipher_scheme: optional cipher scheme for this station   */  struct sta_info {  	/* General information, mostly static */ -	struct list_head list; +	struct list_head list, free_list;  	struct rcu_head rcu_head;  	struct sta_info __rcu *hnext;  	struct ieee80211_local *local;  	struct ieee80211_sub_if_data *sdata;  	struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; -	struct ieee80211_key __rcu *ptk; +	struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS]; +	u8 gtk_idx; +	u8 ptk_idx;  	struct rate_control_ref *rate_ctrl;  	void *rate_ctrl_priv;  	spinlock_t lock; @@ -328,10 +358,8 @@ struct sta_info {  	/* use the accessors defined below */  	unsigned long _flags; -	/* -	 * STA powersave frame queues, no more than the internal -	 * locking required. -	 */ +	/* STA powersave lock and frame queues */ +	spinlock_t ps_lock;  	struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];  	struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];  	unsigned long driver_buffered_tids; @@ -369,6 +397,7 @@ struct sta_info {  	struct ieee80211_tx_rate last_tx_rate;  	int last_rx_rate_idx;  	u32 last_rx_rate_flag; +	u32 last_rx_rate_vht_flag;  	u8 last_rx_rate_vht_nss;  	u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1]; @@ -378,14 +407,16 @@ struct sta_info {  	struct sta_ampdu_mlme ampdu_mlme;  	u8 timer_to_tid[IEEE80211_NUM_TIDS]; +	struct ieee80211_tx_latency_stat *tx_lat; +  #ifdef CONFIG_MAC80211_MESH  	/*  	 * Mesh peer link attributes  	 * TODO: move to a sub-structure that is referenced with pointer?  	 */ -	__le16 llid; -	__le16 plid; -	__le16 reason; +	u16 llid; +	u16 plid; +	u16 reason;  	u8 plink_retries;  	bool ignore_plink_timer;  	enum nl80211_plink_state plink_state; @@ -411,6 +442,9 @@ struct sta_info {  	unsigned int lost_packets;  	unsigned int beacon_loss_count; +	enum ieee80211_smps_mode known_smps_mode; +	const struct ieee80211_cipher_scheme *cipher_scheme; +  	/* keep last! */  	struct ieee80211_sta sta;  }; @@ -573,21 +607,6 @@ void sta_info_recalc_tim(struct sta_info *sta);  void sta_info_init(struct ieee80211_local *local);  void sta_info_stop(struct ieee80211_local *local); -int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata); - -/** - * sta_info_flush_cleanup - flush the sta_info cleanup queue - * @sdata: the interface - * - * Flushes the sta_info cleanup queue for a given interface; - * this is necessary before the interface is removed or, for - * AP/mesh interfaces, before it is deconfigured. - * - * Note an rcu_barrier() must precede the function, after all - * stations have been flushed/removed to ensure the call_rcu() - * calls that add stations to the cleanup queue have completed. - */ -void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata);  /**   * sta_info_flush - flush matching STA entries from the STA table @@ -595,15 +614,13 @@ void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata);   * Returns the number of removed STA entries.   *   * @sdata: sdata to remove all stations from + * @vlans: if the given interface is an AP interface, also flush VLANs   */ +int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans); +  static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata)  { -	int ret = sta_info_flush_defer(sdata); - -	rcu_barrier(); -	sta_info_flush_cleanup(sdata); - -	return ret; +	return __sta_info_flush(sdata, false);  }  void sta_set_rate_info_tx(struct sta_info *sta, @@ -613,11 +630,10 @@ void sta_set_rate_info_rx(struct sta_info *sta,  			  struct rate_info *rinfo);  void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,  			  unsigned long exp_time); +u8 sta_info_tx_streams(struct sta_info *sta);  void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);  void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);  void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); -void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata); -  #endif /* STA_INFO_H */ diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 368837fe3b8..ba29ebc8614 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -11,6 +11,7 @@  #include <linux/export.h>  #include <linux/etherdevice.h> +#include <linux/time.h>  #include <net/mac80211.h>  #include <asm/unaligned.h>  #include "ieee80211_i.h" @@ -180,6 +181,9 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)  	struct ieee80211_local *local = sta->local;  	struct ieee80211_sub_if_data *sdata = sta->sdata; +	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +		sta->last_rx = jiffies; +  	if (ieee80211_is_data_qos(mgmt->frame_control)) {  		struct ieee80211_hdr *hdr = (void *) skb->data;  		u8 *qc = ieee80211_get_qos_ctl(hdr); @@ -191,29 +195,36 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)  	if (ieee80211_is_action(mgmt->frame_control) &&  	    mgmt->u.action.category == WLAN_CATEGORY_HT &&  	    mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && -	    sdata->vif.type == NL80211_IFTYPE_STATION &&  	    ieee80211_sdata_running(sdata)) { -		/* -		 * This update looks racy, but isn't -- if we come -		 * here we've definitely got a station that we're -		 * talking to, and on a managed interface that can -		 * only be the AP. And the only other place updating -		 * this variable in managed mode is before association. -		 */ +		enum ieee80211_smps_mode smps_mode; +  		switch (mgmt->u.action.u.ht_smps.smps_control) {  		case WLAN_HT_SMPS_CONTROL_DYNAMIC: -			sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; +			smps_mode = IEEE80211_SMPS_DYNAMIC;  			break;  		case WLAN_HT_SMPS_CONTROL_STATIC: -			sdata->smps_mode = IEEE80211_SMPS_STATIC; +			smps_mode = IEEE80211_SMPS_STATIC;  			break;  		case WLAN_HT_SMPS_CONTROL_DISABLED:  		default: /* shouldn't happen since we don't send that */ -			sdata->smps_mode = IEEE80211_SMPS_OFF; +			smps_mode = IEEE80211_SMPS_OFF;  			break;  		} -		ieee80211_queue_work(&local->hw, &sdata->recalc_smps); +		if (sdata->vif.type == NL80211_IFTYPE_STATION) { +			/* +			 * This update looks racy, but isn't -- if we come +			 * here we've definitely got a station that we're +			 * talking to, and on a managed interface that can +			 * only be the AP. And the only other place updating +			 * this variable in managed mode is before association. +			 */ +			sdata->smps_mode = smps_mode; +			ieee80211_queue_work(&local->hw, &sdata->recalc_smps); +		} else if (sdata->vif.type == NL80211_IFTYPE_AP || +			   sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +			sta->known_smps_mode = smps_mode; +		}  	}  } @@ -303,10 +314,9 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,  	    !is_multicast_ether_addr(hdr->addr1))  		txflags |= IEEE80211_RADIOTAP_F_TX_FAIL; -	if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || -	    (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) +	if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)  		txflags |= IEEE80211_RADIOTAP_F_TX_CTS; -	else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) +	if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)  		txflags |= IEEE80211_RADIOTAP_F_TX_RTS;  	put_unaligned_le16(txflags, pos); @@ -453,6 +463,76 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,  }  /* + * Measure Tx frame completion and removal time for Tx latency statistics + * calculation. A single Tx frame latency should be measured from when it + * is entering the Kernel until we receive Tx complete confirmation indication + * and remove the skb. + */ +static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local, +					    struct sk_buff *skb, +					    struct sta_info *sta, +					    struct ieee80211_hdr *hdr) +{ +	ktime_t skb_dprt; +	struct timespec dprt_time; +	u32 msrmnt; +	u16 tid; +	u8 *qc; +	int i, bin_range_count; +	u32 *bin_ranges; +	__le16 fc; +	struct ieee80211_tx_latency_stat *tx_lat; +	struct ieee80211_tx_latency_bin_ranges *tx_latency; +	ktime_t skb_arv = skb->tstamp; + +	tx_latency = rcu_dereference(local->tx_latency); + +	/* assert Tx latency stats are enabled & frame arrived when enabled */ +	if (!tx_latency || !ktime_to_ns(skb_arv)) +		return; + +	fc = hdr->frame_control; + +	if (!ieee80211_is_data(fc)) /* make sure it is a data frame */ +		return; + +	/* get frame tid */ +	if (ieee80211_is_data_qos(hdr->frame_control)) { +		qc = ieee80211_get_qos_ctl(hdr); +		tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; +	} else { +		tid = 0; +	} + +	tx_lat = &sta->tx_lat[tid]; + +	ktime_get_ts(&dprt_time); /* time stamp completion time */ +	skb_dprt = ktime_set(dprt_time.tv_sec, dprt_time.tv_nsec); +	msrmnt = ktime_to_ms(ktime_sub(skb_dprt, skb_arv)); + +	if (tx_lat->max < msrmnt) /* update stats */ +		tx_lat->max = msrmnt; +	tx_lat->counter++; +	tx_lat->sum += msrmnt; + +	if (!tx_lat->bins) /* bins not activated */ +		return; + +	/* count how many Tx frames transmitted with the appropriate latency */ +	bin_range_count = tx_latency->n_ranges; +	bin_ranges = tx_latency->ranges; + +	for (i = 0; i < bin_range_count; i++) { +		if (msrmnt <= bin_ranges[i]) { +			tx_lat->bins[i]++; +			break; +		} +	} +	if (i == bin_range_count) /* msrmnt is bigger than the biggest range */ +		tx_lat->bins[i]++; +} + +/*   * Use a static threshold for now, best value to be determined   * by testing ...   * Should it depend on: @@ -461,6 +541,23 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,   */  #define STA_LOST_PKT_THRESHOLD	50 +static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb) +{ +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + +	/* This packet was aggregated but doesn't carry status info */ +	if ((info->flags & IEEE80211_TX_CTL_AMPDU) && +	    !(info->flags & IEEE80211_TX_STAT_AMPDU)) +		return; + +	if (++sta->lost_packets < STA_LOST_PKT_THRESHOLD) +		return; + +	cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, +				    sta->lost_packets, GFP_ATOMIC); +	sta->lost_packets = 0; +} +  void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  {  	struct sk_buff *skb2; @@ -537,6 +634,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  					sta, true, acked);  		if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) && +		    (ieee80211_is_data(hdr->frame_control)) &&  		    (rates_idx != -1))  			sta->last_tx_rate = info->status.rates[rates_idx]; @@ -599,17 +697,19 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  			if (info->flags & IEEE80211_TX_STAT_ACK) {  				if (sta->lost_packets)  					sta->lost_packets = 0; -			} else if (++sta->lost_packets >= STA_LOST_PKT_THRESHOLD) { -				cfg80211_cqm_pktloss_notify(sta->sdata->dev, -							    sta->sta.addr, -							    sta->lost_packets, -							    GFP_ATOMIC); -				sta->lost_packets = 0; +			} else { +				ieee80211_lost_packet(sta, skb);  			}  		}  		if (acked)  			sta->last_ack_signal = info->status.ack_signal; + +		/* +		 * Measure frame removal for tx latency +		 * statistics calculation +		 */ +		ieee80211_tx_latency_end_msrmnt(local, skb, sta, hdr);  	}  	rcu_read_unlock(); diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c new file mode 100644 index 00000000000..652813b2d3d --- /dev/null +++ b/net/mac80211/tdls.c @@ -0,0 +1,325 @@ +/* + * mac80211 TDLS handling code + * + * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net> + * Copyright 2014, Intel Corporation + * + * This file is GPLv2 as found in COPYING. + */ + +#include <linux/ieee80211.h> +#include "ieee80211_i.h" + +static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) +{ +	u8 *pos = (void *)skb_put(skb, 7); + +	*pos++ = WLAN_EID_EXT_CAPABILITY; +	*pos++ = 5; /* len */ +	*pos++ = 0x0; +	*pos++ = 0x0; +	*pos++ = 0x0; +	*pos++ = 0x0; +	*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; +} + +static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	u16 capab; + +	capab = 0; +	if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) +		return capab; + +	if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) +		capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; +	if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) +		capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + +	return capab; +} + +static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, +				       const u8 *peer, const u8 *bssid) +{ +	struct ieee80211_tdls_lnkie *lnkid; + +	lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + +	lnkid->ie_type = WLAN_EID_LINK_ID; +	lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; + +	memcpy(lnkid->bssid, bssid, ETH_ALEN); +	memcpy(lnkid->init_sta, src_addr, ETH_ALEN); +	memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +static int +ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, +			       const u8 *peer, u8 action_code, u8 dialog_token, +			       u16 status_code, struct sk_buff *skb) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	struct ieee80211_tdls_data *tf; + +	tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + +	memcpy(tf->da, peer, ETH_ALEN); +	memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); +	tf->ether_type = cpu_to_be16(ETH_P_TDLS); +	tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + +	switch (action_code) { +	case WLAN_TDLS_SETUP_REQUEST: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_SETUP_REQUEST; + +		skb_put(skb, sizeof(tf->u.setup_req)); +		tf->u.setup_req.dialog_token = dialog_token; +		tf->u.setup_req.capability = +			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + +		ieee80211_add_srates_ie(sdata, skb, false, band); +		ieee80211_add_ext_srates_ie(sdata, skb, false, band); +		ieee80211_tdls_add_ext_capab(skb); +		break; +	case WLAN_TDLS_SETUP_RESPONSE: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + +		skb_put(skb, sizeof(tf->u.setup_resp)); +		tf->u.setup_resp.status_code = cpu_to_le16(status_code); +		tf->u.setup_resp.dialog_token = dialog_token; +		tf->u.setup_resp.capability = +			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + +		ieee80211_add_srates_ie(sdata, skb, false, band); +		ieee80211_add_ext_srates_ie(sdata, skb, false, band); +		ieee80211_tdls_add_ext_capab(skb); +		break; +	case WLAN_TDLS_SETUP_CONFIRM: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + +		skb_put(skb, sizeof(tf->u.setup_cfm)); +		tf->u.setup_cfm.status_code = cpu_to_le16(status_code); +		tf->u.setup_cfm.dialog_token = dialog_token; +		break; +	case WLAN_TDLS_TEARDOWN: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_TEARDOWN; + +		skb_put(skb, sizeof(tf->u.teardown)); +		tf->u.teardown.reason_code = cpu_to_le16(status_code); +		break; +	case WLAN_TDLS_DISCOVERY_REQUEST: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + +		skb_put(skb, sizeof(tf->u.discover_req)); +		tf->u.discover_req.dialog_token = dialog_token; +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +static int +ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, +			   const u8 *peer, u8 action_code, u8 dialog_token, +			   u16 status_code, struct sk_buff *skb) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	struct ieee80211_mgmt *mgmt; + +	mgmt = (void *)skb_put(skb, 24); +	memset(mgmt, 0, 24); +	memcpy(mgmt->da, peer, ETH_ALEN); +	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); +	memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + +	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | +					  IEEE80211_STYPE_ACTION); + +	switch (action_code) { +	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: +		skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); +		mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; +		mgmt->u.action.u.tdls_discover_resp.action_code = +			WLAN_PUB_ACTION_TDLS_DISCOVER_RES; +		mgmt->u.action.u.tdls_discover_resp.dialog_token = +			dialog_token; +		mgmt->u.action.u.tdls_discover_resp.capability = +			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + +		ieee80211_add_srates_ie(sdata, skb, false, band); +		ieee80211_add_ext_srates_ie(sdata, skb, false, band); +		ieee80211_tdls_add_ext_capab(skb); +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, +			const u8 *peer, u8 action_code, u8 dialog_token, +			u16 status_code, u32 peer_capability, +			const u8 *extra_ies, size_t extra_ies_len) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local; +	struct sk_buff *skb = NULL; +	bool send_direct; +	int ret; + +	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) +		return -ENOTSUPP; + +	/* make sure we are in managed mode, and associated */ +	if (sdata->vif.type != NL80211_IFTYPE_STATION || +	    !sdata->u.mgd.associated) +		return -EINVAL; + +	tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", +		 action_code, peer); + +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + +			    max(sizeof(struct ieee80211_mgmt), +				sizeof(struct ieee80211_tdls_data)) + +			    50 + /* supported rates */ +			    7 + /* ext capab */ +			    extra_ies_len + +			    sizeof(struct ieee80211_tdls_lnkie)); +	if (!skb) +		return -ENOMEM; + +	skb_reserve(skb, local->hw.extra_tx_headroom); + +	switch (action_code) { +	case WLAN_TDLS_SETUP_REQUEST: +	case WLAN_TDLS_SETUP_RESPONSE: +	case WLAN_TDLS_SETUP_CONFIRM: +	case WLAN_TDLS_TEARDOWN: +	case WLAN_TDLS_DISCOVERY_REQUEST: +		ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, +						     action_code, dialog_token, +						     status_code, skb); +		send_direct = false; +		break; +	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: +		ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, +						 dialog_token, status_code, +						 skb); +		send_direct = true; +		break; +	default: +		ret = -ENOTSUPP; +		break; +	} + +	if (ret < 0) +		goto fail; + +	if (extra_ies_len) +		memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + +	/* the TDLS link IE is always added last */ +	switch (action_code) { +	case WLAN_TDLS_SETUP_REQUEST: +	case WLAN_TDLS_SETUP_CONFIRM: +	case WLAN_TDLS_TEARDOWN: +	case WLAN_TDLS_DISCOVERY_REQUEST: +		/* we are the initiator */ +		ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, +					   sdata->u.mgd.bssid); +		break; +	case WLAN_TDLS_SETUP_RESPONSE: +	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: +		/* we are the responder */ +		ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, +					   sdata->u.mgd.bssid); +		break; +	default: +		ret = -ENOTSUPP; +		goto fail; +	} + +	if (send_direct) { +		ieee80211_tx_skb(sdata, skb); +		return 0; +	} + +	/* +	 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise +	 * we should default to AC_VI. +	 */ +	switch (action_code) { +	case WLAN_TDLS_SETUP_REQUEST: +	case WLAN_TDLS_SETUP_RESPONSE: +		skb_set_queue_mapping(skb, IEEE80211_AC_BK); +		skb->priority = 2; +		break; +	default: +		skb_set_queue_mapping(skb, IEEE80211_AC_VI); +		skb->priority = 5; +		break; +	} + +	/* disable bottom halves when entering the Tx path */ +	local_bh_disable(); +	ret = ieee80211_subif_start_xmit(skb, dev); +	local_bh_enable(); + +	return ret; + +fail: +	dev_kfree_skb(skb); +	return ret; +} + +int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, +			const u8 *peer, enum nl80211_tdls_operation oper) +{ +	struct sta_info *sta; +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) +		return -ENOTSUPP; + +	if (sdata->vif.type != NL80211_IFTYPE_STATION) +		return -EINVAL; + +	tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); + +	switch (oper) { +	case NL80211_TDLS_ENABLE_LINK: +		rcu_read_lock(); +		sta = sta_info_get(sdata, peer); +		if (!sta) { +			rcu_read_unlock(); +			return -ENOLINK; +		} + +		set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); +		rcu_read_unlock(); +		break; +	case NL80211_TDLS_DISABLE_LINK: +		return sta_info_destroy_addr(sdata, peer); +	case NL80211_TDLS_TEARDOWN: +	case NL80211_TDLS_SETUP: +	case NL80211_TDLS_DISCOVERY_REQ: +		/* We don't support in-driver setup/teardown/discovery */ +		return -ENOTSUPP; +	default: +		return -ENOTSUPP; +	} + +	return 0; +} diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index 124b1fdc20d..0ae207771a5 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -186,7 +186,7 @@ void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf,  EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv);  void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, -                               const u8 *ta, u32 iv32, u16 *p1k) +			       const u8 *ta, u32 iv32, u16 *p1k)  {  	const u8 *tk = &keyconf->key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];  	struct tkip_ctx ctx; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 1aba645882b..cfe1a0688b5 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -21,10 +21,10 @@  #define VIF_ENTRY	__field(enum nl80211_iftype, vif_type) __field(void *, sdata)	\  			__field(bool, p2p)						\ -			__string(vif_name, sdata->dev ? sdata->dev->name : "<nodev>") +			__string(vif_name, sdata->name)  #define VIF_ASSIGN	__entry->vif_type = sdata->vif.type; __entry->sdata = sdata;	\  			__entry->p2p = sdata->vif.p2p;					\ -			__assign_str(vif_name, sdata->dev ? sdata->dev->name : sdata->name) +			__assign_str(vif_name, sdata->name)  #define VIF_PR_FMT	" vif:%s(%d%s)"  #define VIF_PR_ARG	__get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" @@ -41,14 +41,31 @@  #define CHANDEF_PR_ARG	__entry->control_freq, __entry->chan_width,			\  			__entry->center_freq1, __entry->center_freq2 +#define MIN_CHANDEF_ENTRY								\ +			__field(u32, min_control_freq)					\ +			__field(u32, min_chan_width)					\ +			__field(u32, min_center_freq1)					\ +			__field(u32, min_center_freq2) + +#define MIN_CHANDEF_ASSIGN(c)								\ +			__entry->min_control_freq = (c)->chan ? (c)->chan->center_freq : 0;	\ +			__entry->min_chan_width = (c)->width;				\ +			__entry->min_center_freq1 = (c)->center_freq1;			\ +			__entry->min_center_freq2 = (c)->center_freq2; +#define MIN_CHANDEF_PR_FMT	" min_control:%d MHz min_width:%d min_center: %d/%d MHz" +#define MIN_CHANDEF_PR_ARG	__entry->min_control_freq, __entry->min_chan_width,	\ +			__entry->min_center_freq1, __entry->min_center_freq2 +  #define CHANCTX_ENTRY	CHANDEF_ENTRY							\ +			MIN_CHANDEF_ENTRY						\  			__field(u8, rx_chains_static)					\  			__field(u8, rx_chains_dynamic)  #define CHANCTX_ASSIGN	CHANDEF_ASSIGN(&ctx->conf.def)					\ +			MIN_CHANDEF_ASSIGN(&ctx->conf.min_def)				\  			__entry->rx_chains_static = ctx->conf.rx_chains_static;		\  			__entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic -#define CHANCTX_PR_FMT	CHANDEF_PR_FMT " chains:%d/%d" -#define CHANCTX_PR_ARG	CHANDEF_PR_ARG,							\ +#define CHANCTX_PR_FMT	CHANDEF_PR_FMT MIN_CHANDEF_PR_FMT " chains:%d/%d" +#define CHANCTX_PR_ARG	CHANDEF_PR_ARG,	MIN_CHANDEF_PR_ARG,				\  			__entry->rx_chains_static, __entry->rx_chains_dynamic @@ -77,13 +94,13 @@ DECLARE_EVENT_CLASS(local_sdata_addr_evt,  	TP_STRUCT__entry(  		LOCAL_ENTRY  		VIF_ENTRY -		__array(char, addr, 6) +		__array(char, addr, ETH_ALEN)  	),  	TP_fast_assign(  		LOCAL_ASSIGN;  		VIF_ASSIGN; -		memcpy(__entry->addr, sdata->vif.addr, 6); +		memcpy(__entry->addr, sdata->vif.addr, ETH_ALEN);  	),  	TP_printk( @@ -167,6 +184,20 @@ TRACE_EVENT(drv_return_bool,  		  "true" : "false")  ); +TRACE_EVENT(drv_return_u32, +	TP_PROTO(struct ieee80211_local *local, u32 ret), +	TP_ARGS(local, ret), +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u32, ret) +	), +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->ret = ret; +	), +	TP_printk(LOCAL_PR_FMT " - %u", LOCAL_PR_ARG, __entry->ret) +); +  TRACE_EVENT(drv_return_u64,  	TP_PROTO(struct ieee80211_local *local, u64 ret),  	TP_ARGS(local, ret), @@ -426,30 +457,6 @@ TRACE_EVENT(drv_prepare_multicast,  	)  ); -TRACE_EVENT(drv_set_multicast_list, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, int mc_count), - -	TP_ARGS(local, sdata, mc_count), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(bool, allmulti) -		__field(int, mc_count) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI; -		__entry->mc_count = mc_count; -	), - -	TP_printk( -		LOCAL_PR_FMT " configure mc filter, count=%d, allmulti=%d", -		LOCAL_PR_ARG, __entry->mc_count, __entry->allmulti -	) -); -  TRACE_EVENT(drv_configure_filter,  	TP_PROTO(struct ieee80211_local *local,  		 unsigned int changed_flags, @@ -560,7 +567,7 @@ TRACE_EVENT(drv_update_tkip_key,  	TP_printk(  		LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " iv32:%#x", -		LOCAL_PR_ARG,VIF_PR_ARG,STA_PR_ARG, __entry->iv32 +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->iv32  	)  ); @@ -773,7 +780,7 @@ TRACE_EVENT(drv_sta_rc_update,  	)  ); -TRACE_EVENT(drv_sta_add, +DECLARE_EVENT_CLASS(sta_event,  	TP_PROTO(struct ieee80211_local *local,  		 struct ieee80211_sub_if_data *sdata,  		 struct ieee80211_sta *sta), @@ -798,29 +805,25 @@ TRACE_EVENT(drv_sta_add,  	)  ); -TRACE_EVENT(drv_sta_remove, +DEFINE_EVENT(sta_event, drv_sta_add,  	TP_PROTO(struct ieee80211_local *local,  		 struct ieee80211_sub_if_data *sdata,  		 struct ieee80211_sta *sta), +	TP_ARGS(local, sdata, sta) +); -	TP_ARGS(local, sdata, sta), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		STA_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		STA_ASSIGN; -	), +DEFINE_EVENT(sta_event, drv_sta_remove, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta), +	TP_ARGS(local, sdata, sta) +); -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT, -		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG -	) +DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta), +	TP_ARGS(local, sdata, sta)  );  TRACE_EVENT(drv_conf_tx, @@ -1386,6 +1389,91 @@ TRACE_EVENT(drv_change_chanctx,  	)  ); +#if !defined(__TRACE_VIF_ENTRY) +#define __TRACE_VIF_ENTRY +struct trace_vif_entry { +	enum nl80211_iftype vif_type; +	bool p2p; +	char vif_name[IFNAMSIZ]; +} __packed; + +struct trace_chandef_entry { +	u32 control_freq; +	u32 chan_width; +	u32 center_freq1; +	u32 center_freq2; +} __packed; + +struct trace_switch_entry { +	struct trace_vif_entry vif; +	struct trace_chandef_entry old_chandef; +	struct trace_chandef_entry new_chandef; +} __packed; + +#define SWITCH_ENTRY_ASSIGN(to, from) local_vifs[i].to = vifs[i].from +#endif + +TRACE_EVENT(drv_switch_vif_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_vif_chanctx_switch *vifs, +		 int n_vifs, enum ieee80211_chanctx_switch_mode mode), +	    TP_ARGS(local, vifs, n_vifs, mode), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(int, n_vifs) +		__field(u32, mode) +		__dynamic_array(u8, vifs, +				sizeof(struct trace_switch_entry) * n_vifs) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->n_vifs = n_vifs; +		__entry->mode = mode; +		{ +			struct trace_switch_entry *local_vifs = +				__get_dynamic_array(vifs); +			int i; + +			for (i = 0; i < n_vifs; i++) { +				struct ieee80211_sub_if_data *sdata; + +				sdata = container_of(vifs[i].vif, +						struct ieee80211_sub_if_data, +						vif); + +				SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type); +				SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p); +				strncpy(local_vifs[i].vif.vif_name, +					sdata->name, +					sizeof(local_vifs[i].vif.vif_name)); +				SWITCH_ENTRY_ASSIGN(old_chandef.control_freq, +						old_ctx->def.chan->center_freq); +				SWITCH_ENTRY_ASSIGN(old_chandef.chan_width, +						    old_ctx->def.width); +				SWITCH_ENTRY_ASSIGN(old_chandef.center_freq1, +						    old_ctx->def.center_freq1); +				SWITCH_ENTRY_ASSIGN(old_chandef.center_freq2, +						    old_ctx->def.center_freq2); +				SWITCH_ENTRY_ASSIGN(new_chandef.control_freq, +						new_ctx->def.chan->center_freq); +				SWITCH_ENTRY_ASSIGN(new_chandef.chan_width, +						    new_ctx->def.width); +				SWITCH_ENTRY_ASSIGN(new_chandef.center_freq1, +						    new_ctx->def.center_freq1); +				SWITCH_ENTRY_ASSIGN(new_chandef.center_freq2, +						    new_ctx->def.center_freq2); +			} +		} +	), + +	TP_printk( +		LOCAL_PR_FMT " n_vifs:%d mode:%d", +		LOCAL_PR_ARG, __entry->n_vifs, __entry->mode +	) +); +  DECLARE_EVENT_CLASS(local_sdata_chanctx,  	TP_PROTO(struct ieee80211_local *local,  		 struct ieee80211_sub_if_data *sdata, @@ -1475,6 +1563,59 @@ DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,  );  #endif +TRACE_EVENT(drv_join_ibss, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_bss_conf *info), + +	TP_ARGS(local, sdata, info), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u8, dtimper) +		__field(u16, bcnint) +		__dynamic_array(u8, ssid, info->ssid_len); +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->dtimper = info->dtim_period; +		__entry->bcnint = info->beacon_int; +		memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT, +		LOCAL_PR_ARG, VIF_PR_ARG +	) +); + +DEFINE_EVENT(local_sdata_evt, drv_leave_ibss, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +TRACE_EVENT(drv_get_expected_throughput, +	TP_PROTO(struct ieee80211_sta *sta), + +	TP_ARGS(sta), + +	TP_STRUCT__entry( +		STA_ENTRY +	), + +	TP_fast_assign( +		STA_ASSIGN; +	), + +	TP_printk( +		STA_PR_FMT, STA_PR_ARG +	) +); +  /*   * Tracing for API calls that drivers call.   */ @@ -1811,6 +1952,33 @@ TRACE_EVENT(api_eosp,  	)  ); +TRACE_EVENT(api_sta_set_buffered, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sta *sta, +		 u8 tid, bool buffered), + +	TP_ARGS(local, sta, tid, buffered), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		STA_ENTRY +		__field(u8, tid) +		__field(bool, buffered) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		STA_ASSIGN; +		__entry->tid = tid; +		__entry->buffered = buffered; +	), + +	TP_printk( +		LOCAL_PR_FMT STA_PR_FMT " tid:%d buffered:%d", +		LOCAL_PR_ARG, STA_PR_ARG, __entry->tid, __entry->buffered +	) +); +  /*   * Tracing for internal functions   * (which may also be called in response to driver calls) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3456c0486b4..1a252c606ad 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -19,6 +19,7 @@  #include <linux/bitmap.h>  #include <linux/rcupdate.h>  #include <linux/export.h> +#include <linux/time.h>  #include <net/net_namespace.h>  #include <net/ieee80211_radiotap.h>  #include <net/cfg80211.h> @@ -413,6 +414,9 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)  	if (ieee80211_has_order(hdr->frame_control))  		return TX_CONTINUE; +	if (ieee80211_is_probe_req(hdr->frame_control)) +		return TX_CONTINUE; +  	if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)  		info->hw_queue = tx->sdata->vif.cab_queue; @@ -451,8 +455,7 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta,  	if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP))  		return 0; -	if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) -					    skb->data)) +	if (!ieee80211_is_robust_mgmt_frame(skb))  		return 0;  	return 1; @@ -474,11 +477,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)  		     !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {  		int ac = skb_get_queue_mapping(tx->skb); -		/* only deauth, disassoc and action are bufferable MMPDUs */  		if (ieee80211_is_mgmt(hdr->frame_control) && -		    !ieee80211_is_deauth(hdr->frame_control) && -		    !ieee80211_is_disassoc(hdr->frame_control) && -		    !ieee80211_is_action(hdr->frame_control)) { +		    !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {  			info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;  			return TX_CONTINUE;  		} @@ -487,6 +487,20 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)  		       sta->sta.addr, sta->sta.aid, ac);  		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)  			purge_old_ps_buffers(tx->local); + +		/* sync with ieee80211_sta_ps_deliver_wakeup */ +		spin_lock(&sta->ps_lock); +		/* +		 * STA woke up the meantime and all the frames on ps_tx_buf have +		 * been queued to pending queue. No reordering can happen, go +		 * ahead and Tx the packet. +		 */ +		if (!test_sta_flag(sta, WLAN_STA_PS_STA) && +		    !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { +			spin_unlock(&sta->ps_lock); +			return TX_CONTINUE; +		} +  		if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) {  			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]);  			ps_dbg(tx->sdata, @@ -499,7 +513,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)  		info->control.jiffies = jiffies;  		info->control.vif = &tx->sdata->vif;  		info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +		info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;  		skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb); +		spin_unlock(&sta->ps_lock);  		if (!timer_pending(&local->sta_cleanup))  			mod_timer(&local->sta_cleanup, @@ -557,11 +573,12 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)  	if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))  		tx->key = NULL; -	else if (tx->sta && (key = rcu_dereference(tx->sta->ptk))) +	else if (tx->sta && +		 (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx])))  		tx->key = key;  	else if (ieee80211_is_mgmt(hdr->frame_control) &&  		 is_multicast_ether_addr(hdr->addr1) && -		 ieee80211_is_robust_mgmt_frame(hdr) && +		 ieee80211_is_robust_mgmt_frame(tx->skb) &&  		 (key = rcu_dereference(tx->sdata->default_mgmt_key)))  		tx->key = key;  	else if (is_multicast_ether_addr(hdr->addr1) && @@ -576,12 +593,12 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)  		tx->key = NULL;  	else if (tx->skb->protocol == tx->sdata->control_port_protocol)  		tx->key = NULL; -	else if (ieee80211_is_robust_mgmt_frame(hdr) && +	else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&  		 !(ieee80211_is_action(hdr->frame_control) &&  		   tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))  		tx->key = NULL;  	else if (ieee80211_is_mgmt(hdr->frame_control) && -		 !ieee80211_is_robust_mgmt_frame(hdr)) +		 !ieee80211_is_robust_mgmt_frame(tx->skb))  		tx->key = NULL;  	else {  		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); @@ -840,15 +857,16 @@ static int ieee80211_fragment(struct ieee80211_tx_data *tx,  		rem -= fraglen;  		tmp = dev_alloc_skb(local->tx_headroom +  				    frag_threshold + -				    IEEE80211_ENCRYPT_HEADROOM + +				    tx->sdata->encrypt_headroom +  				    IEEE80211_ENCRYPT_TAILROOM);  		if (!tmp)  			return -ENOMEM;  		__skb_queue_tail(&tx->skbs, tmp); -		skb_reserve(tmp, local->tx_headroom + -				 IEEE80211_ENCRYPT_HEADROOM); +		skb_reserve(tmp, +			    local->tx_headroom + tx->sdata->encrypt_headroom); +  		/* copy control information */  		memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); @@ -871,7 +889,7 @@ static int ieee80211_fragment(struct ieee80211_tx_data *tx,  	}  	/* adjust first fragment's length */ -	skb->len = hdrlen + per_fragm; +	skb_trim(skb, hdrlen + per_fragm);  	return 0;  } @@ -1070,6 +1088,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,  			queued = true;  			info->control.vif = &tx->sdata->vif;  			info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +			info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;  			__skb_queue_tail(&tid_tx->pending, skb);  			if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER)  				purge_skb = __skb_dequeue(&tid_tx->pending); @@ -1120,7 +1139,8 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,  		tx->sta = rcu_dereference(sdata->u.vlan.sta);  		if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)  			return TX_DROP; -	} else if (info->flags & IEEE80211_TX_CTL_INJECTED || +	} else if (info->flags & (IEEE80211_TX_CTL_INJECTED | +				  IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||  		   tx->sdata->control_port_protocol == tx->skb->protocol) {  		tx->sta = sta_info_get_bss(sdata, hdr->addr1);  	} @@ -1366,6 +1386,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)  	return 0;  } +bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, +			      struct ieee80211_vif *vif, struct sk_buff *skb, +			      int band, struct ieee80211_sta **sta) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_tx_data tx; + +	if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) +		return false; + +	info->band = band; +	info->control.vif = vif; +	info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)]; + +	if (invoke_tx_handlers(&tx)) +		return false; + +	if (sta) { +		if (tx.sta) +			*sta = &tx.sta->sta; +		else +			*sta = NULL; +	} + +	return true; +} +EXPORT_SYMBOL(ieee80211_tx_prepare_skb); +  /*   * Returns false if the frame couldn't be transmitted but was queued instead.   */ @@ -1455,7 +1504,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,  	headroom = local->tx_headroom;  	if (may_encrypt) -		headroom += IEEE80211_ENCRYPT_HEADROOM; +		headroom += sdata->encrypt_headroom;  	headroom -= skb_headroom(skb);  	headroom = max_t(int, 0, headroom); @@ -1694,8 +1743,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,  	 * radar detection by itself. We can do that later by adding a  	 * monitor flag interfaces used for AP support.  	 */ -	if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR | -			    IEEE80211_CHAN_PASSIVE_SCAN))) +	if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)))  		goto fail_rcu;  	ieee80211_xmit(sdata, skb, chan->band); @@ -1710,6 +1758,26 @@ fail:  	return NETDEV_TX_OK; /* meaning, we dealt with the skb */  } +/* + * Measure Tx frame arrival time for Tx latency statistics calculation + * A single Tx frame latency should be measured from when it is entering the + * Kernel until we receive Tx complete confirmation indication and the skb is + * freed. + */ +static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local, +					      struct sk_buff *skb) +{ +	struct timespec skb_arv; +	struct ieee80211_tx_latency_bin_ranges *tx_latency; + +	tx_latency = rcu_dereference(local->tx_latency); +	if (!tx_latency) +		return; + +	ktime_get_ts(&skb_arv); +	skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec); +} +  /**   * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type   * subinterfaces (wlan#, WDS, and VLAN interfaces) @@ -1760,6 +1828,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	rcu_read_lock(); +	/* Measure frame arrival for Tx latency statistics calculation */ +	ieee80211_tx_latency_start_msrmnt(local, skb); +  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_AP_VLAN:  		sta = rcu_dereference(sdata->u.vlan.sta); @@ -1981,7 +2052,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	 * EAPOL frames from the local station.  	 */  	if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && -		     !is_multicast_ether_addr(hdr.addr1) && !authorized && +		     !multicast && !authorized &&  		     (cpu_to_be16(ethertype) != sdata->control_port_protocol ||  		      !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -2079,7 +2150,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	 */  	if (head_need > 0 || skb_cloned(skb)) { -		head_need += IEEE80211_ENCRYPT_HEADROOM; +		head_need += sdata->encrypt_headroom;  		head_need += local->tx_headroom;  		head_need = max_t(int, 0, head_need);  		if (ieee80211_skb_resize(sdata, skb, head_need, true)) { @@ -2106,7 +2177,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	if (ieee80211_is_data_qos(fc)) {  		__le16 *qos_control; -		qos_control = (__le16*) skb_push(skb, 2); +		qos_control = (__le16 *) skb_push(skb, 2);  		memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2);  		/*  		 * Maybe we could actually set some fields here, for now just @@ -2257,7 +2328,8 @@ void ieee80211_tx_pending(unsigned long data)  /* functions for drivers to get certain frames */  static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, -				       struct ps_data *ps, struct sk_buff *skb) +				       struct ps_data *ps, struct sk_buff *skb, +				       bool is_template)  {  	u8 *pos, *tim;  	int aid0 = 0; @@ -2268,13 +2340,14 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,  	if (atomic_read(&ps->num_sta_ps) > 0)  		/* in the hope that this is faster than  		 * checking byte-for-byte */ -		have_bits = !bitmap_empty((unsigned long*)ps->tim, +		have_bits = !bitmap_empty((unsigned long *)ps->tim,  					  IEEE80211_MAX_AID+1); - -	if (ps->dtim_count == 0) -		ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; -	else -		ps->dtim_count--; +	if (!is_template) { +		if (ps->dtim_count == 0) +			ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; +		else +			ps->dtim_count--; +	}  	tim = pos = (u8 *) skb_put(skb, 6);  	*pos++ = WLAN_EID_TIM; @@ -2320,7 +2393,8 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,  }  static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, -				    struct ps_data *ps, struct sk_buff *skb) +				    struct ps_data *ps, struct sk_buff *skb, +				    bool is_template)  {  	struct ieee80211_local *local = sdata->local; @@ -2332,60 +2406,92 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,  	 * of the tim bitmap in mac80211 and the driver.  	 */  	if (local->tim_in_locked_section) { -		__ieee80211_beacon_add_tim(sdata, ps, skb); +		__ieee80211_beacon_add_tim(sdata, ps, skb, is_template);  	} else {  		spin_lock_bh(&local->tim_lock); -		__ieee80211_beacon_add_tim(sdata, ps, skb); +		__ieee80211_beacon_add_tim(sdata, ps, skb, is_template);  		spin_unlock_bh(&local->tim_lock);  	}  	return 0;  } -void ieee80211_csa_finish(struct ieee80211_vif *vif) -{ -	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - -	ieee80211_queue_work(&sdata->local->hw, -			     &sdata->csa_finalize_work); -} -EXPORT_SYMBOL(ieee80211_csa_finish); - -static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, -				 struct beacon_data *beacon) +static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata, +			      struct beacon_data *beacon)  {  	struct probe_resp *resp; -	int counter_offset_beacon = sdata->csa_counter_offset_beacon; -	int counter_offset_presp = sdata->csa_counter_offset_presp; +	u8 *beacon_data; +	size_t beacon_data_len; +	int i; +	u8 count = sdata->csa_current_counter; -	/* warn if the driver did not check for/react to csa completeness */ -	if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0)) +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		beacon_data = beacon->tail; +		beacon_data_len = beacon->tail_len; +		break; +	case NL80211_IFTYPE_ADHOC: +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +		break; +	case NL80211_IFTYPE_MESH_POINT: +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +		break; +	default:  		return; +	} + +	for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) { +		u16 counter_offset_beacon = +			sdata->csa_counter_offset_beacon[i]; +		u16 counter_offset_presp = sdata->csa_counter_offset_presp[i]; -	((u8 *)beacon->tail)[counter_offset_beacon]--; +		if (counter_offset_beacon) { +			if (WARN_ON(counter_offset_beacon >= beacon_data_len)) +				return; -	if (sdata->vif.type == NL80211_IFTYPE_AP && -	    counter_offset_presp) { -		rcu_read_lock(); -		resp = rcu_dereference(sdata->u.ap.probe_resp); +			beacon_data[counter_offset_beacon] = count; +		} -		/* if nl80211 accepted the offset, this should not happen. */ -		if (WARN_ON(!resp)) { +		if (sdata->vif.type == NL80211_IFTYPE_AP && +		    counter_offset_presp) { +			rcu_read_lock(); +			resp = rcu_dereference(sdata->u.ap.probe_resp); + +			/* If nl80211 accepted the offset, this should +			 * not happen. +			 */ +			if (WARN_ON(!resp)) { +				rcu_read_unlock(); +				return; +			} +			resp->data[counter_offset_presp] = count;  			rcu_read_unlock(); -			return;  		} -		resp->data[counter_offset_presp]--; -		rcu_read_unlock();  	}  } +u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	sdata->csa_current_counter--; + +	/* the counter should never reach 0 */ +	WARN_ON(!sdata->csa_current_counter); + +	return sdata->csa_current_counter; +} +EXPORT_SYMBOL(ieee80211_csa_update_counter); +  bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)  {  	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);  	struct beacon_data *beacon = NULL;  	u8 *beacon_data;  	size_t beacon_data_len; -	int counter_beacon = sdata->csa_counter_offset_beacon; +	int counter_beacon = sdata->csa_counter_offset_beacon[0];  	int ret = false;  	if (!ieee80211_sdata_running(sdata)) @@ -2400,6 +2506,24 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)  			goto out;  		beacon_data = beacon->tail;  		beacon_data_len = beacon->tail_len; +	} else if (vif->type == NL80211_IFTYPE_ADHOC) { +		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + +		beacon = rcu_dereference(ifibss->presp); +		if (!beacon) +			goto out; + +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +	} else if (vif->type == NL80211_IFTYPE_MESH_POINT) { +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +		beacon = rcu_dereference(ifmsh->beacon); +		if (!beacon) +			goto out; + +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len;  	} else {  		WARN_ON(1);  		goto out; @@ -2408,7 +2532,7 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)  	if (WARN_ON(counter_beacon > beacon_data_len))  		goto out; -	if (beacon_data[counter_beacon] == 0) +	if (beacon_data[counter_beacon] == 1)  		ret = true;   out:  	rcu_read_unlock(); @@ -2417,9 +2541,11 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)  }  EXPORT_SYMBOL(ieee80211_csa_is_complete); -struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, -					 struct ieee80211_vif *vif, -					 u16 *tim_offset, u16 *tim_length) +static struct sk_buff * +__ieee80211_beacon_get(struct ieee80211_hw *hw, +		       struct ieee80211_vif *vif, +		       struct ieee80211_mutable_offsets *offs, +		       bool is_template)  {  	struct ieee80211_local *local = hw_to_local(hw);  	struct sk_buff *skb = NULL; @@ -2428,6 +2554,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  	enum ieee80211_band band;  	struct ieee80211_tx_rate_control txrc;  	struct ieee80211_chanctx_conf *chanctx_conf; +	int csa_off_base = 0;  	rcu_read_lock(); @@ -2437,18 +2564,20 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  	if (!ieee80211_sdata_running(sdata) || !chanctx_conf)  		goto out; -	if (tim_offset) -		*tim_offset = 0; -	if (tim_length) -		*tim_length = 0; +	if (offs) +		memset(offs, 0, sizeof(*offs));  	if (sdata->vif.type == NL80211_IFTYPE_AP) {  		struct ieee80211_if_ap *ap = &sdata->u.ap;  		struct beacon_data *beacon = rcu_dereference(ap->beacon);  		if (beacon) { -			if (sdata->vif.csa_active) -				ieee80211_update_csa(sdata, beacon); +			if (sdata->vif.csa_active) { +				if (!is_template) +					ieee80211_csa_update_counter(vif); + +				ieee80211_set_csa(sdata, beacon); +			}  			/*  			 * headroom, head length, @@ -2456,7 +2585,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  			 */  			skb = dev_alloc_skb(local->tx_headroom +  					    beacon->head_len + -					    beacon->tail_len + 256); +					    beacon->tail_len + 256 + +					    local->hw.extra_beacon_tailroom);  			if (!skb)  				goto out; @@ -2464,12 +2594,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  			memcpy(skb_put(skb, beacon->head_len), beacon->head,  			       beacon->head_len); -			ieee80211_beacon_add_tim(sdata, &ap->ps, skb); +			ieee80211_beacon_add_tim(sdata, &ap->ps, skb, +						 is_template); + +			if (offs) { +				offs->tim_offset = beacon->head_len; +				offs->tim_length = skb->len - beacon->head_len; -			if (tim_offset) -				*tim_offset = beacon->head_len; -			if (tim_length) -				*tim_length = skb->len - beacon->head_len; +				/* for AP the csa offsets are from tail */ +				csa_off_base = skb->len; +			}  			if (beacon->tail)  				memcpy(skb_put(skb, beacon->tail_len), @@ -2484,7 +2618,15 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  		if (!presp)  			goto out; -		skb = dev_alloc_skb(local->tx_headroom + presp->head_len); +		if (sdata->vif.csa_active) { +			if (!is_template) +				ieee80211_csa_update_counter(vif); + +			ieee80211_set_csa(sdata, presp); +		} + +		skb = dev_alloc_skb(local->tx_headroom + presp->head_len + +				    local->hw.extra_beacon_tailroom);  		if (!skb)  			goto out;  		skb_reserve(skb, local->tx_headroom); @@ -2501,25 +2643,57 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  		if (!bcn)  			goto out; +		if (sdata->vif.csa_active) { +			if (!is_template) +				/* TODO: For mesh csa_counter is in TU, so +				 * decrementing it by one isn't correct, but +				 * for now we leave it consistent with overall +				 * mac80211's behavior. +				 */ +				ieee80211_csa_update_counter(vif); + +			ieee80211_set_csa(sdata, bcn); +		} +  		if (ifmsh->sync_ops) -			ifmsh->sync_ops->adjust_tbtt( -						sdata); +			ifmsh->sync_ops->adjust_tbtt(sdata, bcn);  		skb = dev_alloc_skb(local->tx_headroom +  				    bcn->head_len +  				    256 + /* TIM IE */ -				    bcn->tail_len); +				    bcn->tail_len + +				    local->hw.extra_beacon_tailroom);  		if (!skb)  			goto out;  		skb_reserve(skb, local->tx_headroom);  		memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len); -		ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb); +		ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template); + +		if (offs) { +			offs->tim_offset = bcn->head_len; +			offs->tim_length = skb->len - bcn->head_len; +		} +  		memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);  	} else {  		WARN_ON(1);  		goto out;  	} +	/* CSA offsets */ +	if (offs) { +		int i; + +		for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) { +			u16 csa_off = sdata->csa_counter_offset_beacon[i]; + +			if (!csa_off) +				continue; + +			offs->csa_counter_offs[i] = csa_off_base + csa_off; +		} +	} +  	band = chanctx_conf->def.chan->band;  	info = IEEE80211_SKB_CB(skb); @@ -2550,6 +2724,32 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,   out:  	rcu_read_unlock();  	return skb; + +} + +struct sk_buff * +ieee80211_beacon_get_template(struct ieee80211_hw *hw, +			      struct ieee80211_vif *vif, +			      struct ieee80211_mutable_offsets *offs) +{ +	return __ieee80211_beacon_get(hw, vif, offs, true); +} +EXPORT_SYMBOL(ieee80211_beacon_get_template); + +struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, +					 struct ieee80211_vif *vif, +					 u16 *tim_offset, u16 *tim_length) +{ +	struct ieee80211_mutable_offsets offs = {}; +	struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false); + +	if (tim_offset) +		*tim_offset = offs.tim_offset; + +	if (tim_length) +		*tim_length = offs.tim_length; + +	return bcn;  }  EXPORT_SYMBOL(ieee80211_beacon_get_tim); @@ -2787,7 +2987,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,  				cpu_to_le16(IEEE80211_FCTL_MOREDATA);  		} -		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +		if (sdata->vif.type == NL80211_IFTYPE_AP)  			sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);  		if (!ieee80211_tx_prepare(sdata, &tx, skb))  			break; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e1b34a18b24..a6cda52ed92 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -34,7 +34,7 @@  #include "wep.h"  /* privid for wiphys to determine whether they belong to us or not */ -void *mac80211_wiphy_privid = &mac80211_wiphy_privid; +const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid;  struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)  { @@ -76,7 +76,7 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,  	}  	if (ieee80211_is_ctl(fc)) { -		if(ieee80211_is_pspoll(fc)) +		if (ieee80211_is_pspoll(fc))  			return hdr->addr1;  		if (ieee80211_is_back_req(fc)) { @@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)  		if (!sdata->dev)  			continue; -		if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) -			continue; -  		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&  		    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)  			continue; @@ -438,9 +435,8 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);  } -void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, -				   struct sk_buff_head *skbs, -				   void (*fn)(void *data), void *data) +void ieee80211_add_pending_skbs(struct ieee80211_local *local, +				struct sk_buff_head *skbs)  {  	struct ieee80211_hw *hw = &local->hw;  	struct sk_buff *skb; @@ -464,9 +460,6 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,  		__skb_queue_tail(&local->pending[queue], skb);  	} -	if (fn) -		fn(data); -  	for (i = 0; i < hw->queues; i++)  		__ieee80211_wake_queue(hw, i,  			IEEE80211_QUEUE_STOP_REASON_SKB_ADD); @@ -561,24 +554,21 @@ void ieee80211_flush_queues(struct ieee80211_local *local,  	ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_FLUSH); -	drv_flush(local, queues, false); +	drv_flush(local, sdata, queues, false);  	ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_FLUSH);  } -void ieee80211_iterate_active_interfaces( -	struct ieee80211_hw *hw, u32 iter_flags, -	void (*iterator)(void *data, u8 *mac, -			 struct ieee80211_vif *vif), -	void *data) +static void __iterate_active_interfaces(struct ieee80211_local *local, +					u32 iter_flags, +					void (*iterator)(void *data, u8 *mac, +						struct ieee80211_vif *vif), +					void *data)  { -	struct ieee80211_local *local = hw_to_local(hw);  	struct ieee80211_sub_if_data *sdata; -	mutex_lock(&local->iflist_mtx); - -	list_for_each_entry(sdata, &local->interfaces, list) { +	list_for_each_entry_rcu(sdata, &local->interfaces, list) {  		switch (sdata->vif.type) {  		case NL80211_IFTYPE_MONITOR:  			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) @@ -597,13 +587,25 @@ void ieee80211_iterate_active_interfaces(  				 &sdata->vif);  	} -	sdata = rcu_dereference_protected(local->monitor_sdata, -					  lockdep_is_held(&local->iflist_mtx)); +	sdata = rcu_dereference_check(local->monitor_sdata, +				      lockdep_is_held(&local->iflist_mtx) || +				      lockdep_rtnl_is_held());  	if (sdata &&  	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||  	     sdata->flags & IEEE80211_SDATA_IN_DRIVER))  		iterator(data, sdata->vif.addr, &sdata->vif); +} + +void ieee80211_iterate_active_interfaces( +	struct ieee80211_hw *hw, u32 iter_flags, +	void (*iterator)(void *data, u8 *mac, +			 struct ieee80211_vif *vif), +	void *data) +{ +	struct ieee80211_local *local = hw_to_local(hw); +	mutex_lock(&local->iflist_mtx); +	__iterate_active_interfaces(local, iter_flags, iterator, data);  	mutex_unlock(&local->iflist_mtx);  }  EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); @@ -615,38 +617,37 @@ void ieee80211_iterate_active_interfaces_atomic(  	void *data)  {  	struct ieee80211_local *local = hw_to_local(hw); -	struct ieee80211_sub_if_data *sdata;  	rcu_read_lock(); +	__iterate_active_interfaces(local, iter_flags, iterator, data); +	rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); -	list_for_each_entry_rcu(sdata, &local->interfaces, list) { -		switch (sdata->vif.type) { -		case NL80211_IFTYPE_MONITOR: -			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) -				continue; -			break; -		case NL80211_IFTYPE_AP_VLAN: -			continue; -		default: -			break; -		} -		if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && -		    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) -			continue; -		if (ieee80211_sdata_running(sdata)) -			iterator(data, sdata->vif.addr, -				 &sdata->vif); -	} +void ieee80211_iterate_active_interfaces_rtnl( +	struct ieee80211_hw *hw, u32 iter_flags, +	void (*iterator)(void *data, u8 *mac, +			 struct ieee80211_vif *vif), +	void *data) +{ +	struct ieee80211_local *local = hw_to_local(hw); -	sdata = rcu_dereference(local->monitor_sdata); -	if (sdata && -	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || -	     sdata->flags & IEEE80211_SDATA_IN_DRIVER)) -		iterator(data, sdata->vif.addr, &sdata->vif); +	ASSERT_RTNL(); -	rcu_read_unlock(); +	__iterate_active_interfaces(local, iter_flags, iterator, data);  } -EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); +EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); + +struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + +	if (!ieee80211_sdata_running(sdata) || +	    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) +		return NULL; +	return &sdata->vif; +} +EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);  /*   * Nothing should have been stuffed into the workqueue during @@ -746,6 +747,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,  		case WLAN_EID_TIMEOUT_INTERVAL:  		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:  		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: +		case WLAN_EID_CHAN_SWITCH_PARAM:  		/*  		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible  		 * that if the content gets bigger it might be needed more than once @@ -911,6 +913,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,  			}  			elems->sec_chan_offs = (void *)pos;  			break; +		case WLAN_EID_CHAN_SWITCH_PARAM: +			if (elen != +			    sizeof(*elems->mesh_chansw_params_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->mesh_chansw_params_ie = (void *)pos; +			break;  		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:  			if (!action ||  			    elen != sizeof(*elems->wide_bw_chansw_ie)) { @@ -1007,14 +1017,21 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,  	 */  	enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); -	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { -		/* Set defaults according to 802.11-2007 Table 7-37 */ -		aCWmax = 1023; -		if (use_11b) -			aCWmin = 31; -		else -			aCWmin = 15; +	/* Set defaults according to 802.11-2007 Table 7-37 */ +	aCWmax = 1023; +	if (use_11b) +		aCWmin = 31; +	else +		aCWmin = 15; + +	/* Confiure old 802.11b/g medium access rules. */ +	qparam.cw_max = aCWmax; +	qparam.cw_min = aCWmin; +	qparam.txop = 0; +	qparam.aifs = 2; +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +		/* Update if QoS is enabled. */  		if (enable_qos) {  			switch (ac) {  			case IEEE80211_AC_BK: @@ -1050,12 +1067,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,  				qparam.aifs = 2;  				break;  			} -		} else { -			/* Confiure old 802.11b/g medium access rules. */ -			qparam.cw_max = aCWmax; -			qparam.cw_min = aCWmin; -			qparam.txop = 0; -			qparam.aifs = 2;  		}  		qparam.uapsd = false; @@ -1084,12 +1095,13 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_mgmt *mgmt;  	int err; -	skb = dev_alloc_skb(local->hw.extra_tx_headroom + -			    sizeof(*mgmt) + 6 + extra_len); +	/* 24 + 6 = header + auth_algo + auth_transaction + status_code */ +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN + +			    24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN);  	if (!skb)  		return; -	skb_reserve(skb, local->hw.extra_tx_headroom); +	skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN);  	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);  	memset(mgmt, 0, 24 + 6); @@ -1266,13 +1278,32 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,  	 * that calculates local->scan_ies_len.  	 */ -	/* add any remaining custom IEs */ +	/* insert custom IEs that go before VHT */  	if (ie && ie_len) { -		noffset = ie_len; +		static const u8 before_vht[] = { +			WLAN_EID_SSID, +			WLAN_EID_SUPP_RATES, +			WLAN_EID_REQUEST, +			WLAN_EID_EXT_SUPP_RATES, +			WLAN_EID_DS_PARAMS, +			WLAN_EID_SUPPORTED_REGULATORY_CLASSES, +			WLAN_EID_HT_CAPABILITY, +			WLAN_EID_BSS_COEX_2040, +			WLAN_EID_EXT_CAPABILITY, +			WLAN_EID_SSID_LIST, +			WLAN_EID_CHANNEL_USAGE, +			WLAN_EID_INTERWORKING, +			/* mesh ID can't happen here */ +			/* 60 GHz can't happen here right now */ +		}; +		noffset = ieee80211_ie_split(ie, ie_len, +					     before_vht, ARRAY_SIZE(before_vht), +					     offset);  		if (end - pos < noffset - offset)  			goto out_err;  		memcpy(pos, ie + offset, noffset - offset);  		pos += noffset - offset; +		offset = noffset;  	}  	if (sband->vht_cap.vht_supported) { @@ -1282,6 +1313,15 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,  						 sband->vht_cap.cap);  	} +	/* add any remaining custom IEs */ +	if (ie && ie_len) { +		noffset = ie_len; +		if (end - pos < noffset - offset) +			goto out_err; +		memcpy(pos, ie + offset, noffset - offset); +		pos += noffset - offset; +	} +  	return pos - buffer;   out_err:  	WARN_ONCE(1, "not enough space for preq IEs\n"); @@ -1359,7 +1399,6 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,  			    enum ieee80211_band band, u32 *basic_rates)  {  	struct ieee80211_supported_band *sband; -	struct ieee80211_rate *bitrates;  	size_t num_rates;  	u32 supp_rates, rate_flags;  	int i, j, shift; @@ -1371,7 +1410,6 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,  	if (WARN_ON(!sband))  		return 1; -	bitrates = sband->bitrates;  	num_rates = sband->n_bitrates;  	supp_rates = 0;  	for (i = 0; i < elems->supp_rates_len + @@ -1420,6 +1458,44 @@ void ieee80211_stop_device(struct ieee80211_local *local)  	drv_stop(local);  } +static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_chanctx *ctx; + +	/* +	 * We get here if during resume the device can't be restarted properly. +	 * We might also get here if this happens during HW reset, which is a +	 * slightly different situation and we need to drop all connections in +	 * the latter case. +	 * +	 * Ask cfg80211 to turn off all interfaces, this will result in more +	 * warnings but at least we'll then get into a clean stopped state. +	 */ + +	local->resuming = false; +	local->suspended = false; +	local->started = false; + +	/* scheduled scan clearly can't be running any more, but tell +	 * cfg80211 and clear local state +	 */ +	ieee80211_sched_scan_end(local); + +	list_for_each_entry(sdata, &local->interfaces, list) +		sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; + +	/* Mark channel contexts as not being in the driver any more to avoid +	 * removing them from the driver during the shutdown process... +	 */ +	mutex_lock(&local->chanctx_mtx); +	list_for_each_entry(ctx, &local->chanctx_list, list) +		ctx->driver_present = false; +	mutex_unlock(&local->chanctx_mtx); + +	cfg80211_shutdown_all_interfaces(local->hw.wiphy); +} +  static void ieee80211_assign_chanctx(struct ieee80211_local *local,  				     struct ieee80211_sub_if_data *sdata)  { @@ -1447,6 +1523,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	struct sta_info *sta;  	int res, i;  	bool reconfig_due_to_wowlan = false; +	struct ieee80211_sub_if_data *sched_scan_sdata; +	bool sched_scan_stopped = false;  #ifdef CONFIG_PM  	if (local->suspended) @@ -1481,9 +1559,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	 */  	res = drv_start(local);  	if (res) { -		WARN(local->suspended, "Hardware became unavailable " -		     "upon resume. This could be a software issue " -		     "prior to suspend or a hardware issue.\n"); +		if (local->suspended) +			WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); +		else +			WARN(1, "Hardware became unavailable during restart.\n"); +		ieee80211_handle_reconfig_failure(local);  		return res;  	} @@ -1507,7 +1587,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)  		WARN_ON(local->resuming);  		res = drv_add_interface(local, sdata);  		if (WARN_ON(res)) { -			rcu_assign_pointer(local->monitor_sdata, NULL); +			RCU_INIT_POINTER(local->monitor_sdata, NULL);  			synchronize_net();  			kfree(sdata);  		} @@ -1526,17 +1606,17 @@ int ieee80211_reconfig(struct ieee80211_local *local)  		list_for_each_entry(ctx, &local->chanctx_list, list)  			WARN_ON(drv_add_chanctx(local, ctx));  		mutex_unlock(&local->chanctx_mtx); -	} -	list_for_each_entry(sdata, &local->interfaces, list) { -		if (!ieee80211_sdata_running(sdata)) -			continue; -		ieee80211_assign_chanctx(local, sdata); -	} +		list_for_each_entry(sdata, &local->interfaces, list) { +			if (!ieee80211_sdata_running(sdata)) +				continue; +			ieee80211_assign_chanctx(local, sdata); +		} -	sdata = rtnl_dereference(local->monitor_sdata); -	if (sdata && ieee80211_sdata_running(sdata)) -		ieee80211_assign_chanctx(local, sdata); +		sdata = rtnl_dereference(local->monitor_sdata); +		if (sdata && ieee80211_sdata_running(sdata)) +			ieee80211_assign_chanctx(local, sdata); +	}  	/* add STAs back */  	mutex_lock(&local->sta_mtx); @@ -1632,13 +1712,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)  			}  			break;  		case NL80211_IFTYPE_WDS: -			break;  		case NL80211_IFTYPE_AP_VLAN:  		case NL80211_IFTYPE_MONITOR: -			/* ignore virtual */ -			break;  		case NL80211_IFTYPE_P2P_DEVICE: -			changed = BSS_CHANGED_IDLE; +			/* nothing to do */  			break;  		case NL80211_IFTYPE_UNSPECIFIED:  		case NUM_NL80211_IFTYPES: @@ -1724,6 +1801,26 @@ int ieee80211_reconfig(struct ieee80211_local *local)  					IEEE80211_QUEUE_STOP_REASON_SUSPEND);  	/* +	 * Reconfigure sched scan if it was interrupted by FW restart or +	 * suspend. +	 */ +	mutex_lock(&local->mtx); +	sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, +						lockdep_is_held(&local->mtx)); +	if (sched_scan_sdata && local->sched_scan_req) +		/* +		 * Sched scan stopped, but we don't want to report it. Instead, +		 * we're trying to reschedule. +		 */ +		if (__ieee80211_request_sched_scan_start(sched_scan_sdata, +							 local->sched_scan_req)) +			sched_scan_stopped = true; +	mutex_unlock(&local->mtx); + +	if (sched_scan_stopped) +		cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy); + +	/*  	 * If this is for hw restart things are still running.  	 * We may want to change that later, however.  	 */ @@ -1750,6 +1847,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)  #else  	WARN_ON(1);  #endif +  	return 0;  } @@ -1800,6 +1898,26 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)  	mutex_unlock(&local->chanctx_mtx);  } +void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_chanctx *chanctx; + +	mutex_lock(&local->chanctx_mtx); + +	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					lockdep_is_held(&local->chanctx_mtx)); + +	if (WARN_ON_ONCE(!chanctx_conf)) +		goto unlock; + +	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); +	ieee80211_recalc_chanctx_min_def(local, chanctx); + unlock: +	mutex_unlock(&local->chanctx_mtx); +} +  static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)  {  	int i; @@ -2103,7 +2221,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_supported_band *sband; -	int rate, skip, shift; +	int rate, shift;  	u8 i, exrates, *pos;  	u32 basic_rates = sdata->vif.bss_conf.basic_rates;  	u32 rate_flags; @@ -2131,14 +2249,11 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,  		pos = skb_put(skb, exrates + 2);  		*pos++ = WLAN_EID_EXT_SUPP_RATES;  		*pos++ = exrates; -		skip = 0;  		for (i = 8; i < sband->n_bitrates; i++) {  			u8 basic = 0;  			if ((rate_flags & sband->bitrates[i].flags)  			    != rate_flags)  				continue; -			if (skip++ < 8) -				continue;  			if (need_basic && basic_rates & BIT(i))  				basic = 0x80;  			rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, @@ -2217,11 +2332,11 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,  		ri.nss = status->vht_nss;  		if (status->flag & RX_FLAG_40MHZ)  			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; -		if (status->flag & RX_FLAG_80MHZ) +		if (status->vht_flag & RX_VHT_FLAG_80MHZ)  			ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; -		if (status->flag & RX_FLAG_80P80MHZ) +		if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)  			ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; -		if (status->flag & RX_FLAG_160MHZ) +		if (status->vht_flag & RX_VHT_FLAG_160MHZ)  			ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;  		if (status->flag & RX_FLAG_SHORT_GI)  			ri.flags |= RATE_INFO_FLAGS_SHORT_GI; @@ -2241,6 +2356,10 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,  	}  	rate = cfg80211_calculate_bitrate(&ri); +	if (WARN_ONCE(!rate, +		      "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n", +		      status->flag, status->rate_idx, status->vht_nss)) +		return 0;  	/* rewind from end of MPDU */  	if (status->flag & RX_FLAG_MACTIME_END) @@ -2254,36 +2373,43 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,  void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata; +	struct cfg80211_chan_def chandef; +	mutex_lock(&local->mtx);  	mutex_lock(&local->iflist_mtx);  	list_for_each_entry(sdata, &local->interfaces, list) { -		cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); +		/* it might be waiting for the local->mtx, but then +		 * by the time it gets it, sdata->wdev.cac_started +		 * will no longer be true +		 */ +		cancel_delayed_work(&sdata->dfs_cac_timer_work);  		if (sdata->wdev.cac_started) { +			chandef = sdata->vif.bss_conf.chandef;  			ieee80211_vif_release_channel(sdata);  			cfg80211_cac_event(sdata->dev, +					   &chandef,  					   NL80211_RADAR_CAC_ABORTED,  					   GFP_KERNEL);  		}  	}  	mutex_unlock(&local->iflist_mtx); +	mutex_unlock(&local->mtx);  }  void ieee80211_dfs_radar_detected_work(struct work_struct *work)  {  	struct ieee80211_local *local =  		container_of(work, struct ieee80211_local, radar_detected_work); -	struct cfg80211_chan_def chandef; +	struct cfg80211_chan_def chandef = local->hw.conf.chandef;  	ieee80211_dfs_cac_cancel(local);  	if (local->use_chanctx)  		/* currently not handled */  		WARN_ON(1); -	else { -		chandef = local->hw.conf.chandef; +	else  		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); -	}  }  void ieee80211_radar_detected(struct ieee80211_hw *hw) @@ -2295,3 +2421,535 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw)  	ieee80211_queue_work(hw, &local->radar_detected_work);  }  EXPORT_SYMBOL(ieee80211_radar_detected); + +u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) +{ +	u32 ret; +	int tmp; + +	switch (c->width) { +	case NL80211_CHAN_WIDTH_20: +		c->width = NL80211_CHAN_WIDTH_20_NOHT; +		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; +		break; +	case NL80211_CHAN_WIDTH_40: +		c->width = NL80211_CHAN_WIDTH_20; +		c->center_freq1 = c->chan->center_freq; +		ret = IEEE80211_STA_DISABLE_40MHZ | +		      IEEE80211_STA_DISABLE_VHT; +		break; +	case NL80211_CHAN_WIDTH_80: +		tmp = (30 + c->chan->center_freq - c->center_freq1)/20; +		/* n_P40 */ +		tmp /= 2; +		/* freq_P40 */ +		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; +		c->width = NL80211_CHAN_WIDTH_40; +		ret = IEEE80211_STA_DISABLE_VHT; +		break; +	case NL80211_CHAN_WIDTH_80P80: +		c->center_freq2 = 0; +		c->width = NL80211_CHAN_WIDTH_80; +		ret = IEEE80211_STA_DISABLE_80P80MHZ | +		      IEEE80211_STA_DISABLE_160MHZ; +		break; +	case NL80211_CHAN_WIDTH_160: +		/* n_P20 */ +		tmp = (70 + c->chan->center_freq - c->center_freq1)/20; +		/* n_P80 */ +		tmp /= 4; +		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; +		c->width = NL80211_CHAN_WIDTH_80; +		ret = IEEE80211_STA_DISABLE_80P80MHZ | +		      IEEE80211_STA_DISABLE_160MHZ; +		break; +	default: +	case NL80211_CHAN_WIDTH_20_NOHT: +		WARN_ON_ONCE(1); +		c->width = NL80211_CHAN_WIDTH_20_NOHT; +		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; +		break; +	case NL80211_CHAN_WIDTH_5: +	case NL80211_CHAN_WIDTH_10: +		WARN_ON_ONCE(1); +		/* keep c->width */ +		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; +		break; +	} + +	WARN_ON_ONCE(!cfg80211_chandef_valid(c)); + +	return ret; +} + +/* + * Returns true if smps_mode_new is strictly more restrictive than + * smps_mode_old. + */ +bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, +				   enum ieee80211_smps_mode smps_mode_new) +{ +	if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || +			 smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) +		return false; + +	switch (smps_mode_old) { +	case IEEE80211_SMPS_STATIC: +		return false; +	case IEEE80211_SMPS_DYNAMIC: +		return smps_mode_new == IEEE80211_SMPS_STATIC; +	case IEEE80211_SMPS_OFF: +		return smps_mode_new != IEEE80211_SMPS_OFF; +	default: +		WARN_ON(1); +	} + +	return false; +} + +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings) +{ +	struct sk_buff *skb; +	struct ieee80211_mgmt *mgmt; +	struct ieee80211_local *local = sdata->local; +	int freq; +	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + +			       sizeof(mgmt->u.action.u.chan_switch); +	u8 *pos; + +	if (sdata->vif.type != NL80211_IFTYPE_ADHOC && +	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT) +		return -EOPNOTSUPP; + +	skb = dev_alloc_skb(local->tx_headroom + hdr_len + +			    5 + /* channel switch announcement element */ +			    3 + /* secondary channel offset element */ +			    8); /* mesh channel switch parameters element */ +	if (!skb) +		return -ENOMEM; + +	skb_reserve(skb, local->tx_headroom); +	mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); +	memset(mgmt, 0, hdr_len); +	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | +					  IEEE80211_STYPE_ACTION); + +	eth_broadcast_addr(mgmt->da); +	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); +	} else { +		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +		memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); +	} +	mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; +	mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; +	pos = skb_put(skb, 5); +	*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */ +	*pos++ = 3;						/* IE length */ +	*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */ +	freq = csa_settings->chandef.chan->center_freq; +	*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */ +	*pos++ = csa_settings->count;				/* count */ + +	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { +		enum nl80211_channel_type ch_type; + +		skb_put(skb, 3); +		*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */ +		*pos++ = 1;					/* IE length */ +		ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); +		if (ch_type == NL80211_CHAN_HT40PLUS) +			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; +		else +			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; +	} + +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +		skb_put(skb, 8); +		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;		/* EID */ +		*pos++ = 6;					/* IE length */ +		*pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;	/* Mesh TTL */ +		*pos = 0x00;	/* Mesh Flag: Tx Restrict, Initiator, Reason */ +		*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; +		*pos++ |= csa_settings->block_tx ? +			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; +		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ +		pos += 2; +		put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */ +		pos += 2; +	} + +	ieee80211_tx_skb(sdata, skb); +	return 0; +} + +bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs) +{ +	return !(cs == NULL || cs->cipher == 0 || +		 cs->hdr_len < cs->pn_len + cs->pn_off || +		 cs->hdr_len <= cs->key_idx_off || +		 cs->key_idx_shift > 7 || +		 cs->key_idx_mask == 0); +} + +bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n) +{ +	int i; + +	/* Ensure we have enough iftype bitmap space for all iftype values */ +	WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype)); + +	for (i = 0; i < n; i++) +		if (!ieee80211_cs_valid(&cs[i])) +			return false; + +	return true; +} + +const struct ieee80211_cipher_scheme * +ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, +		 enum nl80211_iftype iftype) +{ +	const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes; +	int n = local->hw.n_cipher_schemes; +	int i; +	const struct ieee80211_cipher_scheme *cs = NULL; + +	for (i = 0; i < n; i++) { +		if (l[i].cipher == cipher) { +			cs = &l[i]; +			break; +		} +	} + +	if (!cs || !(cs->iftype & BIT(iftype))) +		return NULL; + +	return cs; +} + +int ieee80211_cs_headroom(struct ieee80211_local *local, +			  struct cfg80211_crypto_settings *crypto, +			  enum nl80211_iftype iftype) +{ +	const struct ieee80211_cipher_scheme *cs; +	int headroom = IEEE80211_ENCRYPT_HEADROOM; +	int i; + +	for (i = 0; i < crypto->n_ciphers_pairwise; i++) { +		cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i], +				      iftype); + +		if (cs && headroom < cs->hdr_len) +			headroom = cs->hdr_len; +	} + +	cs = ieee80211_cs_get(local, crypto->cipher_group, iftype); +	if (cs && headroom < cs->hdr_len) +		headroom = cs->hdr_len; + +	return headroom; +} + +static bool +ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) +{ +	s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); +	int skip; + +	if (end > 0) +		return false; + +	/* End time is in the past, check for repetitions */ +	skip = DIV_ROUND_UP(-end, data->desc[i].interval); +	if (data->count[i] < 255) { +		if (data->count[i] <= skip) { +			data->count[i] = 0; +			return false; +		} + +		data->count[i] -= skip; +	} + +	data->desc[i].start += skip * data->desc[i].interval; + +	return true; +} + +static bool +ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf, +			     s32 *offset) +{ +	bool ret = false; +	int i; + +	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { +		s32 cur; + +		if (!data->count[i]) +			continue; + +		if (ieee80211_extend_noa_desc(data, tsf + *offset, i)) +			ret = true; + +		cur = data->desc[i].start - tsf; +		if (cur > *offset) +			continue; + +		cur = data->desc[i].start + data->desc[i].duration - tsf; +		if (cur > *offset) +			*offset = cur; +	} + +	return ret; +} + +static u32 +ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf) +{ +	s32 offset = 0; +	int tries = 0; +	/* +	 * arbitrary limit, used to avoid infinite loops when combined NoA +	 * descriptors cover the full time period. +	 */ +	int max_tries = 5; + +	ieee80211_extend_absent_time(data, tsf, &offset); +	do { +		if (!ieee80211_extend_absent_time(data, tsf, &offset)) +			break; + +		tries++; +	} while (tries < max_tries); + +	return offset; +} + +void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf) +{ +	u32 next_offset = BIT(31) - 1; +	int i; + +	data->absent = 0; +	data->has_next_tsf = false; +	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { +		s32 start; + +		if (!data->count[i]) +			continue; + +		ieee80211_extend_noa_desc(data, tsf, i); +		start = data->desc[i].start - tsf; +		if (start <= 0) +			data->absent |= BIT(i); + +		if (next_offset > start) +			next_offset = start; + +		data->has_next_tsf = true; +	} + +	if (data->absent) +		next_offset = ieee80211_get_noa_absent_time(data, tsf); + +	data->next_tsf = tsf + next_offset; +} +EXPORT_SYMBOL(ieee80211_update_p2p_noa); + +int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, +			    struct ieee80211_noa_data *data, u32 tsf) +{ +	int ret = 0; +	int i; + +	memset(data, 0, sizeof(*data)); + +	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { +		const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i]; + +		if (!desc->count || !desc->duration) +			continue; + +		data->count[i] = desc->count; +		data->desc[i].start = le32_to_cpu(desc->start_time); +		data->desc[i].duration = le32_to_cpu(desc->duration); +		data->desc[i].interval = le32_to_cpu(desc->interval); + +		if (data->count[i] > 1 && +		    data->desc[i].interval < data->desc[i].duration) +			continue; + +		ieee80211_extend_noa_desc(data, tsf, i); +		ret++; +	} + +	if (ret) +		ieee80211_update_p2p_noa(data, tsf); + +	return ret; +} +EXPORT_SYMBOL(ieee80211_parse_p2p_noa); + +void ieee80211_recalc_dtim(struct ieee80211_local *local, +			   struct ieee80211_sub_if_data *sdata) +{ +	u64 tsf = drv_get_tsf(local, sdata); +	u64 dtim_count = 0; +	u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024; +	u8 dtim_period = sdata->vif.bss_conf.dtim_period; +	struct ps_data *ps; +	u8 bcns_from_dtim; + +	if (tsf == -1ULL || !beacon_int || !dtim_period) +		return; + +	if (sdata->vif.type == NL80211_IFTYPE_AP || +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		if (!sdata->bss) +			return; + +		ps = &sdata->bss->ps; +	} else if (ieee80211_vif_is_mesh(&sdata->vif)) { +		ps = &sdata->u.mesh.ps; +	} else { +		return; +	} + +	/* +	 * actually finds last dtim_count, mac80211 will update in +	 * __beacon_add_tim(). +	 * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period +	 */ +	do_div(tsf, beacon_int); +	bcns_from_dtim = do_div(tsf, dtim_period); +	/* just had a DTIM */ +	if (!bcns_from_dtim) +		dtim_count = 0; +	else +		dtim_count = dtim_period - bcns_from_dtim; + +	ps->dtim_count = dtim_count; +} + +int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, +				 const struct cfg80211_chan_def *chandef, +				 enum ieee80211_chanctx_mode chanmode, +				 u8 radar_detect) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_sub_if_data *sdata_iter; +	enum nl80211_iftype iftype = sdata->wdev.iftype; +	int num[NUM_NL80211_IFTYPES]; +	struct ieee80211_chanctx *ctx; +	int num_different_channels = 0; +	int total = 1; + +	lockdep_assert_held(&local->chanctx_mtx); + +	if (WARN_ON(hweight32(radar_detect) > 1)) +		return -EINVAL; + +	if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED && +		    !chandef->chan)) +		return -EINVAL; + +	if (chandef) +		num_different_channels = 1; + +	if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) +		return -EINVAL; + +	/* Always allow software iftypes */ +	if (local->hw.wiphy->software_iftypes & BIT(iftype)) { +		if (radar_detect) +			return -EINVAL; +		return 0; +	} + +	memset(num, 0, sizeof(num)); + +	if (iftype != NL80211_IFTYPE_UNSPECIFIED) +		num[iftype] = 1; + +	list_for_each_entry(ctx, &local->chanctx_list, list) { +		if (ctx->conf.radar_enabled) +			radar_detect |= BIT(ctx->conf.def.width); +		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { +			num_different_channels++; +			continue; +		} +		if (chandef && chanmode == IEEE80211_CHANCTX_SHARED && +		    cfg80211_chandef_compatible(chandef, +						&ctx->conf.def)) +			continue; +		num_different_channels++; +	} + +	list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) { +		struct wireless_dev *wdev_iter; + +		wdev_iter = &sdata_iter->wdev; + +		if (sdata_iter == sdata || +		    rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL || +		    local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype)) +			continue; + +		num[wdev_iter->iftype]++; +		total++; +	} + +	if (total == 1 && !radar_detect) +		return 0; + +	return cfg80211_check_combinations(local->hw.wiphy, +					   num_different_channels, +					   radar_detect, num); +} + +static void +ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c, +			 void *data) +{ +	u32 *max_num_different_channels = data; + +	*max_num_different_channels = max(*max_num_different_channels, +					  c->num_different_channels); +} + +int ieee80211_max_num_channels(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; +	int num[NUM_NL80211_IFTYPES] = {}; +	struct ieee80211_chanctx *ctx; +	int num_different_channels = 0; +	u8 radar_detect = 0; +	u32 max_num_different_channels = 1; +	int err; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(ctx, &local->chanctx_list, list) { +		num_different_channels++; + +		if (ctx->conf.radar_enabled) +			radar_detect |= BIT(ctx->conf.def.width); +	} + +	list_for_each_entry_rcu(sdata, &local->interfaces, list) +		num[sdata->wdev.iftype]++; + +	err = cfg80211_iter_combinations(local->hw.wiphy, +					 num_different_channels, radar_detect, +					 num, ieee80211_iter_max_chans, +					 &max_num_different_channels); +	if (err < 0) +		return err; + +	return max_num_different_channels; +} diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 97c289414e3..9265adfdabf 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -129,9 +129,12 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,  	if (!vht_cap_ie || !sband->vht_cap.vht_supported)  		return; -	/* A VHT STA must support 40 MHz */ -	if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) -		return; +	/* +	 * A VHT STA must support 40 MHz, but if we verify that here +	 * then we break a few things - some APs (e.g. Netgear R6300v2 +	 * and others based on the BCM4360 chipset) will unset this +	 * capability bit when operating in 20 MHz. +	 */  	vht_cap->vht_supported = true; @@ -182,16 +185,15 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,  			 IEEE80211_VHT_CAP_SHORT_GI_160);  	/* remaining ones */ -	if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) { +	if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)  		vht_cap->cap |= cap_info &  				(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | -				 IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX | -				 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX); -	} +				 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK);  	if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)  		vht_cap->cap |= cap_info & -				IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; +				(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | +				 IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK);  	if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)  		vht_cap->cap |= cap_info & @@ -350,9 +352,9 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta)  	sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);  } -void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, -				 struct sta_info *sta, u8 opmode, -				 enum ieee80211_band band, bool nss_only) +u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, +				  struct sta_info *sta, u8 opmode, +				  enum ieee80211_band band, bool nss_only)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_supported_band *sband; @@ -364,7 +366,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,  	/* ignore - no support for BF yet */  	if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) -		return; +		return 0;  	nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;  	nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; @@ -376,7 +378,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,  	}  	if (nss_only) -		goto change; +		return changed;  	switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {  	case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: @@ -399,7 +401,19 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,  		changed |= IEEE80211_RC_BW_CHANGED;  	} - change: -	if (changed) +	return changed; +} + +void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, +				 struct sta_info *sta, u8 opmode, +				 enum ieee80211_band band, bool nss_only) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; + +	u32 changed = __ieee80211_vht_handle_opmode(sdata, sta, opmode, +						    band, nss_only); + +	if (changed > 0)  		rate_control_rate_update(local, sband, sta, changed);  } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index afba19cb6f8..d51422c778d 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -106,6 +106,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  	struct sta_info *sta = NULL;  	const u8 *ra = NULL;  	bool qos = false; +	struct mac80211_qos_map *qos_map;  	if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {  		skb->priority = 0; /* required for correct WPA/11i MIC */ @@ -153,9 +154,18 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  		return IEEE80211_AC_BE;  	} +	if (skb->protocol == sdata->control_port_protocol) { +		skb->priority = 7; +		return ieee80211_downgrade_queue(sdata, skb); +	} +  	/* use the data classifier to determine what 802.1d tag the  	 * data frame has */ -	skb->priority = cfg80211_classify8021d(skb); +	rcu_read_lock(); +	qos_map = rcu_dereference(sdata->qos_map); +	skb->priority = cfg80211_classify8021d(skb, qos_map ? +					       &qos_map->qos_map : NULL); +	rcu_read_unlock();  	return ieee80211_downgrade_queue(sdata, skb);  } diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index c9edfcb7a13..9b3dcc20114 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -127,7 +127,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)  		 * APs with pairwise keys should never receive Michael MIC  		 * errors for non-zero keyidx because these are reserved for  		 * group keys and only the AP is sending real multicast -		 * frames in the BSS. ( +		 * frames in the BSS.  		 */  		return RX_DROP_UNUSABLE;  	} @@ -301,22 +301,15 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)  } -static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, -				int encrypted) +static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)  {  	__le16 mask_fc;  	int a4_included, mgmt;  	u8 qos_tid; -	u8 *b_0, *aad; -	u16 data_len, len_a; +	u16 len_a;  	unsigned int hdrlen;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; -	memset(scratch, 0, 6 * AES_BLOCK_SIZE); - -	b_0 = scratch + 3 * AES_BLOCK_SIZE; -	aad = scratch + 4 * AES_BLOCK_SIZE; -  	/*  	 * Mask FC: zero subtype b4 b5 b6 (if not mgmt)  	 * Retry, PwrMgt, MoreData; set Protected @@ -338,20 +331,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,  	else  		qos_tid = 0; -	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN; -	if (encrypted) -		data_len -= IEEE80211_CCMP_MIC_LEN; +	/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC +	 * mode authentication are not allowed to collide, yet both are derived +	 * from this vector b_0. We only set L := 1 here to indicate that the +	 * data size can be represented in (L+1) bytes. The CCM layer will take +	 * care of storing the data length in the top (L+1) bytes and setting +	 * and clearing the other bits as is required to derive the two IVs. +	 */ +	b_0[0] = 0x1; -	/* First block, b_0 */ -	b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */  	/* Nonce: Nonce Flags | A2 | PN  	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)  	 */  	b_0[1] = qos_tid | (mgmt << 4);  	memcpy(&b_0[2], hdr->addr2, ETH_ALEN);  	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); -	/* l(m) */ -	put_unaligned_be16(data_len, &b_0[14]);  	/* AAD (extra authenticate-only data) / masked 802.11 header  	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ @@ -407,11 +401,15 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	u8 *pos;  	u8 pn[6];  	u64 pn64; -	u8 scratch[6 * AES_BLOCK_SIZE]; +	u8 aad[2 * AES_BLOCK_SIZE]; +	u8 b_0[AES_BLOCK_SIZE];  	if (info->control.hw_key &&  	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && -	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && +	    !((info->control.hw_key->flags & +	       IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) && +	      ieee80211_is_mgmt(hdr->frame_control))) {  		/*  		 * hwaccel has no need for preallocated room for CCMP  		 * header or MIC fields @@ -460,9 +458,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  		return 0;  	pos += IEEE80211_CCMP_HDR_LEN; -	ccmp_special_blocks(skb, pn, scratch, 0); -	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, -				  pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN)); +	ccmp_special_blocks(skb, pn, b_0, aad); +	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, +				  skb_put(skb, IEEE80211_CCMP_MIC_LEN));  	return 0;  } @@ -499,7 +497,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)  	hdrlen = ieee80211_hdrlen(hdr->frame_control);  	if (!ieee80211_is_data(hdr->frame_control) && -	    !ieee80211_is_robust_mgmt_frame(hdr)) +	    !ieee80211_is_robust_mgmt_frame(skb))  		return RX_CONTINUE;  	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - @@ -525,16 +523,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)  	}  	if (!(status->flag & RX_FLAG_DECRYPTED)) { -		u8 scratch[6 * AES_BLOCK_SIZE]; +		u8 aad[2 * AES_BLOCK_SIZE]; +		u8 b_0[AES_BLOCK_SIZE];  		/* hardware didn't decrypt/verify MIC */ -		ccmp_special_blocks(skb, pn, scratch, 1); +		ccmp_special_blocks(skb, pn, b_0, aad);  		if (ieee80211_aes_ccm_decrypt( -			    key->u.ccmp.tfm, scratch, +			    key->u.ccmp.tfm, b_0, aad,  			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,  			    data_len, -			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN, -			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN)) +			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))  			return RX_DROP_UNUSABLE;  	} @@ -549,6 +547,106 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)  	return RX_CONTINUE;  } +static ieee80211_tx_result +ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, +			    struct sk_buff *skb) +{ +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	struct ieee80211_key *key = tx->key; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme; +	int hdrlen; +	u8 *pos; + +	if (info->control.hw_key && +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { +		/* hwaccel has no need for preallocated head room */ +		return TX_CONTINUE; +	} + +	if (unlikely(skb_headroom(skb) < cs->hdr_len && +		     pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC))) +		return TX_DROP; + +	hdrlen = ieee80211_hdrlen(hdr->frame_control); + +	pos = skb_push(skb, cs->hdr_len); +	memmove(pos, pos + cs->hdr_len, hdrlen); +	skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len); + +	return TX_CONTINUE; +} + +static inline int ieee80211_crypto_cs_pn_compare(u8 *pn1, u8 *pn2, int len) +{ +	int i; + +	/* pn is little endian */ +	for (i = len - 1; i >= 0; i--) { +		if (pn1[i] < pn2[i]) +			return -1; +		else if (pn1[i] > pn2[i]) +			return 1; +	} + +	return 0; +} + +static ieee80211_rx_result +ieee80211_crypto_cs_decrypt(struct ieee80211_rx_data *rx) +{ +	struct ieee80211_key *key = rx->key; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; +	const struct ieee80211_cipher_scheme *cs = NULL; +	int hdrlen = ieee80211_hdrlen(hdr->frame_control); +	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); +	int data_len; +	u8 *rx_pn; +	u8 *skb_pn; +	u8 qos_tid; + +	if (!rx->sta || !rx->sta->cipher_scheme || +	    !(status->flag & RX_FLAG_DECRYPTED)) +		return RX_DROP_UNUSABLE; + +	if (!ieee80211_is_data(hdr->frame_control)) +		return RX_CONTINUE; + +	cs = rx->sta->cipher_scheme; + +	data_len = rx->skb->len - hdrlen - cs->hdr_len; + +	if (data_len < 0) +		return RX_DROP_UNUSABLE; + +	if (ieee80211_is_data_qos(hdr->frame_control)) +		qos_tid = *ieee80211_get_qos_ctl(hdr) & +				IEEE80211_QOS_CTL_TID_MASK; +	else +		qos_tid = 0; + +	if (skb_linearize(rx->skb)) +		return RX_DROP_UNUSABLE; + +	hdr = (struct ieee80211_hdr *)rx->skb->data; + +	rx_pn = key->u.gen.rx_pn[qos_tid]; +	skb_pn = rx->skb->data + hdrlen + cs->pn_off; + +	if (ieee80211_crypto_cs_pn_compare(skb_pn, rx_pn, cs->pn_len) <= 0) +		return RX_DROP_UNUSABLE; + +	memcpy(rx_pn, skb_pn, cs->pn_len); + +	/* remove security header and MIC */ +	if (pskb_trim(rx->skb, rx->skb->len - cs->mic_len)) +		return RX_DROP_UNUSABLE; + +	memmove(rx->skb->data + cs->hdr_len, rx->skb->data, hdrlen); +	skb_pull(rx->skb, cs->hdr_len); + +	return RX_CONTINUE; +}  static void bip_aad(struct sk_buff *skb, u8 *aad)  { @@ -689,6 +787,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)  {  	struct sk_buff *skb;  	struct ieee80211_tx_info *info = NULL; +	ieee80211_tx_result res;  	skb_queue_walk(&tx->skbs, skb) {  		info  = IEEE80211_SKB_CB(skb); @@ -696,9 +795,24 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)  		/* handle hw-only algorithm */  		if (!info->control.hw_key)  			return TX_DROP; + +		if (tx->key->sta->cipher_scheme) { +			res = ieee80211_crypto_cs_encrypt(tx, skb); +			if (res != TX_CONTINUE) +				return res; +		}  	}  	ieee80211_tx_set_protected(tx);  	return TX_CONTINUE;  } + +ieee80211_rx_result +ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx) +{ +	if (rx->sta->cipher_scheme) +		return ieee80211_crypto_cs_decrypt(rx); + +	return RX_DROP_UNUSABLE; +} diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h index 07e33f899c7..62e5a12dfe0 100644 --- a/net/mac80211/wpa.h +++ b/net/mac80211/wpa.h @@ -34,5 +34,7 @@ ieee80211_rx_result  ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx);  ieee80211_tx_result  ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx); +ieee80211_rx_result +ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx);  #endif /* WPA_H */  | 
