diff options
Diffstat (limited to 'net/mac80211')
35 files changed, 2324 insertions, 1729 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a6893602f87..1a89c80e640 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -175,7 +175,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  		 *       add it to the device after the station.  		 */  		if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) { -			ieee80211_key_free(sdata->local, key); +			ieee80211_key_free_unused(key);  			err = -ENOENT;  			goto out_unlock;  		} @@ -214,8 +214,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  	}  	err = ieee80211_key_link(key, sdata, sta); -	if (err) -		ieee80211_key_free(sdata->local, key);   out_unlock:  	mutex_unlock(&sdata->local->sta_mtx); @@ -254,7 +252,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,  		goto out_unlock;  	} -	__ieee80211_key_free(key); +	ieee80211_key_free(key, true);  	ret = 0;   out_unlock: @@ -445,12 +443,14 @@ 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 timespec uptime; +	u64 packets = 0; +	int ac;  	sinfo->generation = sdata->local->sta_generation;  	sinfo->filled = STATION_INFO_INACTIVE_TIME | -			STATION_INFO_RX_BYTES | -			STATION_INFO_TX_BYTES | +			STATION_INFO_RX_BYTES64 | +			STATION_INFO_TX_BYTES64 |  			STATION_INFO_RX_PACKETS |  			STATION_INFO_TX_PACKETS |  			STATION_INFO_TX_RETRIES | @@ -467,10 +467,14 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  	sinfo->connected_time = uptime.tv_sec - sta->last_connected;  	sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); +	sinfo->tx_bytes = 0; +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +		sinfo->tx_bytes += sta->tx_bytes[ac]; +		packets += sta->tx_packets[ac]; +	} +	sinfo->tx_packets = packets;  	sinfo->rx_bytes = sta->rx_bytes; -	sinfo->tx_bytes = sta->tx_bytes;  	sinfo->rx_packets = sta->rx_packets; -	sinfo->tx_packets = sta->tx_packets;  	sinfo->tx_retries = sta->tx_retry_count;  	sinfo->tx_failed = sta->tx_retry_failed;  	sinfo->rx_dropped_misc = sta->rx_dropped; @@ -598,8 +602,8 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,  		data[i++] += sta->rx_fragments;		\  		data[i++] += sta->rx_dropped;		\  							\ -		data[i++] += sta->tx_packets;		\ -		data[i++] += sta->tx_bytes;		\ +		data[i++] += sinfo.tx_packets;		\ +		data[i++] += sinfo.tx_bytes;		\  		data[i++] += sta->tx_fragments;		\  		data[i++] += sta->tx_filtered_count;	\  		data[i++] += sta->tx_retry_failed;	\ @@ -621,13 +625,14 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,  		if (!(sta && !WARN_ON(sta->sdata->dev != dev)))  			goto do_survey; +		sinfo.filled = 0; +		sta_set_sinfo(sta, &sinfo); +  		i = 0;  		ADD_STA_STATS(sta);  		data[i++] = sta->sta_state; -		sinfo.filled = 0; -		sta_set_sinfo(sta, &sinfo);  		if (sinfo.filled & STATION_INFO_TX_BITRATE)  			data[i] = 100000 * @@ -800,8 +805,7 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,  					IEEE80211_CHANCTX_EXCLUSIVE);  		}  	} else if (local->open_count == local->monitors) { -		local->_oper_channel = chandef->chan; -		local->_oper_channel_type = cfg80211_get_chandef_type(chandef); +		local->_oper_chandef = *chandef;  		ieee80211_hw_config(local, 0);  	} @@ -960,8 +964,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  	sdata->vif.bss_conf.hidden_ssid =  		(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); -	sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow; -	sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps; +	memset(&sdata->vif.bss_conf.p2p_noa_attr, 0, +	       sizeof(sdata->vif.bss_conf.p2p_noa_attr)); +	sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow = +		params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK; +	if (params->p2p_opp_ps) +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= +					IEEE80211_P2P_OPPPS_ENABLE_BIT;  	err = ieee80211_assign_beacon(sdata, ¶ms->beacon);  	if (err < 0) @@ -1034,12 +1043,17 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	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) +	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);  	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); @@ -1177,6 +1191,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,  			mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);  		if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))  			set |= BIT(NL80211_STA_FLAG_ASSOCIATED); +	} else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { +		/* +		 * TDLS -- everything follows authorized, but +		 * only becoming authorized is possible, not +		 * going back +		 */ +		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { +			set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) | +			       BIT(NL80211_STA_FLAG_ASSOCIATED); +			mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) | +				BIT(NL80211_STA_FLAG_ASSOCIATED); +		}  	}  	ret = sta_apply_auth_flags(local, sta, mask, set); @@ -1261,7 +1287,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,  	if (ieee80211_vif_is_mesh(&sdata->vif)) {  #ifdef CONFIG_MAC80211_MESH  		u32 changed = 0; -		if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) { + +		if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {  			switch (params->plink_state) {  			case NL80211_PLINK_ESTAB:  				if (sta->plink_state != NL80211_PLINK_ESTAB) @@ -1292,15 +1319,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,  				/*  nothing  */  				break;  			} -		} else { -			switch (params->plink_action) { -			case PLINK_ACTION_OPEN: -				changed |= mesh_plink_open(sta); -				break; -			case PLINK_ACTION_BLOCK: -				changed |= mesh_plink_block(sta); -				break; -			} +		} + +		switch (params->plink_action) { +		case NL80211_PLINK_ACTION_NO_ACTION: +			/* nothing */ +			break; +		case NL80211_PLINK_ACTION_OPEN: +			changed |= mesh_plink_open(sta); +			break; +		case NL80211_PLINK_ACTION_BLOCK: +			changed |= mesh_plink_block(sta); +			break;  		}  		if (params->local_pm) @@ -1346,8 +1376,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  	 * defaults -- if userspace wants something else we'll  	 * change it accordingly in sta_apply_parameters()  	 */ -	sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); -	sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); +	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); +	}  	err = sta_apply_parameters(local, sta, params);  	if (err) { @@ -1356,8 +1388,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  	}  	/* -	 * for TDLS, rate control should be initialized only when supported -	 * rates are known. +	 * for TDLS, rate control should be initialized only when +	 * rates are known and station is marked authorized  	 */  	if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))  		rate_control_rate_init(sta); @@ -1394,50 +1426,67 @@ 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, u8 *mac,  				    struct station_parameters *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = wiphy_priv(wiphy);  	struct sta_info *sta;  	struct ieee80211_sub_if_data *vlansdata; +	enum cfg80211_station_type statype;  	int err;  	mutex_lock(&local->sta_mtx);  	sta = sta_info_get_bss(sdata, mac);  	if (!sta) { -		mutex_unlock(&local->sta_mtx); -		return -ENOENT; +		err = -ENOENT; +		goto out_err;  	} -	/* in station mode, some updates are only valid with TDLS */ -	if (sdata->vif.type == NL80211_IFTYPE_STATION && -	    (params->supported_rates || params->ht_capa || params->vht_capa || -	     params->sta_modify_mask || -	     (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) && -	    !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { -		mutex_unlock(&local->sta_mtx); -		return -EINVAL; +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_MESH_POINT: +		if (sdata->u.mesh.user_mpm) +			statype = CFG80211_STA_MESH_PEER_USER; +		else +			statype = CFG80211_STA_MESH_PEER_KERNEL; +		break; +	case NL80211_IFTYPE_ADHOC: +		statype = CFG80211_STA_IBSS; +		break; +	case NL80211_IFTYPE_STATION: +		if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { +			statype = CFG80211_STA_AP_STA; +			break; +		} +		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) +			statype = CFG80211_STA_TDLS_PEER_ACTIVE; +		else +			statype = CFG80211_STA_TDLS_PEER_SETUP; +		break; +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_AP_VLAN: +		statype = CFG80211_STA_AP_CLIENT; +		break; +	default: +		err = -EOPNOTSUPP; +		goto out_err;  	} +	err = cfg80211_check_station_change(wiphy, params, statype); +	if (err) +		goto out_err; +  	if (params->vlan && params->vlan != sta->sdata->dev) {  		bool prev_4addr = false;  		bool new_4addr = false;  		vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); -		if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN && -		    vlansdata->vif.type != NL80211_IFTYPE_AP) { -			mutex_unlock(&local->sta_mtx); -			return -EINVAL; -		} -  		if (params->vlan->ieee80211_ptr->use_4addr) {  			if (vlansdata->u.vlan.sta) { -				mutex_unlock(&local->sta_mtx); -				return -EBUSY; +				err = -EBUSY; +				goto out_err;  			}  			rcu_assign_pointer(vlansdata->u.vlan.sta, sta); @@ -1464,12 +1513,12 @@ static int ieee80211_change_station(struct wiphy *wiphy,  	}  	err = sta_apply_parameters(local, sta, params); -	if (err) { -		mutex_unlock(&local->sta_mtx); -		return err; -	} +	if (err) +		goto out_err; -	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates) +	/* When peer becomes authorized, init rate control as well */ +	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && +	    test_sta_flag(sta, WLAN_STA_AUTHORIZED))  		rate_control_rate_init(sta);  	mutex_unlock(&local->sta_mtx); @@ -1479,7 +1528,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,  		ieee80211_recalc_ps(local, -1);  		ieee80211_recalc_ps_vif(sdata);  	} +  	return 0; +out_err: +	mutex_unlock(&local->sta_mtx); +	return err;  }  #ifdef CONFIG_MAC80211_MESH @@ -1489,7 +1542,6 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath;  	struct sta_info *sta; -	int err;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1500,17 +1552,12 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,  		return -ENOENT;  	} -	err = mesh_path_add(sdata, dst); -	if (err) { +	mpath = mesh_path_add(sdata, dst); +	if (IS_ERR(mpath)) {  		rcu_read_unlock(); -		return err; +		return PTR_ERR(mpath);  	} -	mpath = mesh_path_lookup(sdata, dst); -	if (!mpath) { -		rcu_read_unlock(); -		return -ENXIO; -	}  	mesh_path_fix_nexthop(mpath, sta);  	rcu_read_unlock(); @@ -1687,6 +1734,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,  	ifmsh->mesh_sp_id = setup->sync_method;  	ifmsh->mesh_pp_id = setup->path_sel_proto;  	ifmsh->mesh_pm_id = setup->path_metric; +	ifmsh->user_mpm = setup->user_mpm;  	ifmsh->security = IEEE80211_MESH_SEC_NONE;  	if (setup->is_authenticated)  		ifmsh->security |= IEEE80211_MESH_SEC_AUTHED; @@ -1730,8 +1778,11 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,  		conf->dot11MeshTTL = nconf->dot11MeshTTL;  	if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))  		conf->element_ttl = nconf->element_ttl; -	if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) +	if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) { +		if (ifmsh->user_mpm) +			return -EBUSY;  		conf->auto_open_plinks = nconf->auto_open_plinks; +	}  	if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask))  		conf->dot11MeshNbrOffsetMaxNeighbor =  			nconf->dot11MeshNbrOffsetMaxNeighbor; @@ -1910,12 +1961,20 @@ static int ieee80211_change_bss(struct wiphy *wiphy,  	}  	if (params->p2p_ctwindow >= 0) { -		sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow; +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow &= +					~IEEE80211_P2P_OPPPS_CTWINDOW_MASK; +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= +			params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK;  		changed |= BSS_CHANGED_P2P_PS;  	} -	if (params->p2p_opp_ps >= 0) { -		sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps; +	if (params->p2p_opp_ps > 0) { +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= +					IEEE80211_P2P_OPPPS_ENABLE_BIT; +		changed |= BSS_CHANGED_P2P_PS; +	} else if (params->p2p_opp_ps == 0) { +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow &= +					~IEEE80211_P2P_OPPPS_ENABLE_BIT;  		changed |= BSS_CHANGED_P2P_PS;  	} @@ -2359,9 +2418,22 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,  	}  	for (i = 0; i < IEEE80211_NUM_BANDS; i++) { +		struct ieee80211_supported_band *sband = wiphy->bands[i]; +		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)); + +		sdata->rc_has_mcs_mask[i] = false; +		if (!sband) +			continue; + +		for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) +			if (~sdata->rc_rateidx_mcs_mask[i][j]) { +				sdata->rc_has_mcs_mask[i] = true; +				break; +			}  	}  	return 0; @@ -2371,7 +2443,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  				    struct ieee80211_sub_if_data *sdata,  				    struct ieee80211_channel *channel,  				    unsigned int duration, u64 *cookie, -				    struct sk_buff *txskb) +				    struct sk_buff *txskb, +				    enum ieee80211_roc_type type)  {  	struct ieee80211_roc_work *roc, *tmp;  	bool queued = false; @@ -2390,6 +2463,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	roc->duration = duration;  	roc->req_duration = duration;  	roc->frame = txskb; +	roc->type = type;  	roc->mgmt_tx_cookie = (unsigned long)txskb;  	roc->sdata = sdata;  	INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work); @@ -2420,7 +2494,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	if (!duration)  		duration = 10; -	ret = drv_remain_on_channel(local, sdata, channel, duration); +	ret = drv_remain_on_channel(local, sdata, channel, duration, type);  	if (ret) {  		kfree(roc);  		return ret; @@ -2439,10 +2513,13 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  		 *  		 * If it hasn't started yet, just increase the duration  		 * and add the new one to the list of dependents. +		 * If the type of the new ROC has higher priority, modify the +		 * type of the previous one to match that of the new one.  		 */  		if (!tmp->started) {  			list_add_tail(&roc->list, &tmp->dependents);  			tmp->duration = max(tmp->duration, roc->duration); +			tmp->type = max(tmp->type, roc->type);  			queued = true;  			break;  		} @@ -2454,16 +2531,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  			/*  			 * In the offloaded ROC case, if it hasn't begun, add  			 * this new one to the dependent list to be handled -			 * when the the master one begins. If it has begun, +			 * when the master one begins. If it has begun,  			 * check that there's still a minimum time left and  			 * if so, start this one, transmitting the frame, but -			 * add it to the list directly after this one with a +			 * add it to the list directly after this one with  			 * a reduced time so we'll ask the driver to execute  			 * it right after finishing the previous one, in the  			 * hope that it'll also be executed right afterwards,  			 * effectively extending the old one.  			 * If there's no minimum time left, just add it to the  			 * normal list. +			 * TODO: the ROC type is ignored here, assuming that it +			 * is better to immediately use the current ROC.  			 */  			if (!tmp->hw_begun) {  				list_add_tail(&roc->list, &tmp->dependents); @@ -2557,7 +2636,8 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,  	mutex_lock(&local->mtx);  	ret = ieee80211_start_roc_work(local, sdata, chan, -				       duration, cookie, NULL); +				       duration, cookie, NULL, +				       IEEE80211_ROC_TYPE_NORMAL);  	mutex_unlock(&local->mtx);  	return ret; @@ -2792,7 +2872,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  	/* This will handle all kinds of coalescing and immediate TX */  	ret = ieee80211_start_roc_work(local, sdata, chan, -				       wait, cookie, skb); +				       wait, cookie, skb, +				       IEEE80211_ROC_TYPE_MGMT_TX);  	if (ret)  		kfree_skb(skb);   out_unlock: @@ -3302,9 +3383,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,  		if (local->use_chanctx)  			*chandef = local->monitor_chandef;  		else -			cfg80211_chandef_create(chandef, -						local->_oper_channel, -						local->_oper_channel_type); +			*chandef = local->_oper_chandef;  		ret = 0;  	}  	rcu_read_unlock(); diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 931be419ab5..03e8d2e3270 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -22,7 +22,7 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,  	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);  	if (!local->use_chanctx) { -		local->_oper_channel_type = cfg80211_get_chandef_type(chandef); +		local->_oper_chandef = *chandef;  		ieee80211_hw_config(local, 0);  	}  } @@ -57,6 +57,22 @@ ieee80211_find_chanctx(struct ieee80211_local *local,  	return NULL;  } +static bool ieee80211_is_radar_required(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; + +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		if (sdata->radar_required) { +			rcu_read_unlock(); +			return true; +		} +	} +	rcu_read_unlock(); + +	return false; +} +  static struct ieee80211_chanctx *  ieee80211_new_chanctx(struct ieee80211_local *local,  		      const struct cfg80211_chan_def *chandef, @@ -76,6 +92,9 @@ ieee80211_new_chanctx(struct ieee80211_local *local,  	ctx->conf.rx_chains_static = 1;  	ctx->conf.rx_chains_dynamic = 1;  	ctx->mode = mode; +	ctx->conf.radar_enabled = ieee80211_is_radar_required(local); +	if (!local->use_chanctx) +		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;  	/* acquire mutex to prevent idle from changing */  	mutex_lock(&local->mtx); @@ -85,9 +104,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local,  		ieee80211_hw_config(local, changed);  	if (!local->use_chanctx) { -		local->_oper_channel_type = -			cfg80211_get_chandef_type(chandef); -		local->_oper_channel = chandef->chan; +		local->_oper_chandef = *chandef;  		ieee80211_hw_config(local, 0);  	} else {  		err = drv_add_chanctx(local, ctx); @@ -112,12 +129,24 @@ ieee80211_new_chanctx(struct ieee80211_local *local,  static void ieee80211_free_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) { -		local->_oper_channel_type = NL80211_CHAN_NO_HT; +		struct cfg80211_chan_def *chandef = &local->_oper_chandef; +		chandef->width = NL80211_CHAN_WIDTH_20_NOHT; +		chandef->center_freq1 = chandef->chan->center_freq; +		chandef->center_freq2 = 0; + +		/* 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; +		local->hw.conf.radar_enabled = false; +  		ieee80211_hw_config(local, 0);  	} else {  		drv_remove_chanctx(local, ctx); @@ -126,6 +155,9 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,  	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); @@ -237,19 +269,11 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)  void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,  				    struct ieee80211_chanctx *chanctx)  { -	struct ieee80211_sub_if_data *sdata; -	bool radar_enabled = false; +	bool radar_enabled;  	lockdep_assert_held(&local->chanctx_mtx); -	rcu_read_lock(); -	list_for_each_entry_rcu(sdata, &local->interfaces, list) { -		if (sdata->radar_required) { -			radar_enabled = true; -			break; -		} -	} -	rcu_read_unlock(); +	radar_enabled = ieee80211_is_radar_required(local);  	if (radar_enabled == chanctx->conf.radar_enabled)  		return; diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index c3a3082b72e..1521cabad3d 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -295,7 +295,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)  	char buf[50];  	struct ieee80211_key *key; -	if (!sdata->debugfs.dir) +	if (!sdata->vif.debugfs_dir)  		return;  	lockdep_assert_held(&sdata->local->key_mtx); @@ -311,7 +311,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)  		sprintf(buf, "../keys/%d", key->debugfs.cnt);  		sdata->debugfs.default_unicast_key =  			debugfs_create_symlink("default_unicast_key", -					       sdata->debugfs.dir, buf); +					       sdata->vif.debugfs_dir, buf);  	}  	if (sdata->debugfs.default_multicast_key) { @@ -325,7 +325,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)  		sprintf(buf, "../keys/%d", key->debugfs.cnt);  		sdata->debugfs.default_multicast_key =  			debugfs_create_symlink("default_multicast_key", -					       sdata->debugfs.dir, buf); +					       sdata->vif.debugfs_dir, buf);  	}  } @@ -334,7 +334,7 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)  	char buf[50];  	struct ieee80211_key *key; -	if (!sdata->debugfs.dir) +	if (!sdata->vif.debugfs_dir)  		return;  	key = key_mtx_dereference(sdata->local, @@ -343,7 +343,7 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)  		sprintf(buf, "../keys/%d", key->debugfs.cnt);  		sdata->debugfs.default_mgmt_key =  			debugfs_create_symlink("default_mgmt_key", -					       sdata->debugfs.dir, buf); +					       sdata->vif.debugfs_dir, buf);  	} else  		ieee80211_debugfs_key_remove_mgmt_default(sdata);  } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 059bbb82e84..14abcf44f97 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -124,6 +124,15 @@ static ssize_t ieee80211_if_fmt_##name(					\  	return scnprintf(buf, buflen, "%d\n", sdata->field / 16);	\  } +#define IEEE80211_IF_FMT_JIFFIES_TO_MS(name, field)			\ +static ssize_t ieee80211_if_fmt_##name(					\ +	const struct ieee80211_sub_if_data *sdata,			\ +	char *buf, int buflen)						\ +{									\ +	return scnprintf(buf, buflen, "%d\n",				\ +			 jiffies_to_msecs(sdata->field));		\ +} +  #define __IEEE80211_IF_FILE(name, _write)				\  static ssize_t ieee80211_if_read_##name(struct file *file,		\  					char __user *userbuf,		\ @@ -197,6 +206,7 @@ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);  IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);  IEEE80211_IF_FILE(last_beacon, u.mgd.last_beacon_signal, DEC);  IEEE80211_IF_FILE(ave_beacon, u.mgd.ave_beacon_signal, DEC_DIV_16); +IEEE80211_IF_FILE(beacon_timeout, u.mgd.beacon_timeout, JIFFIES_TO_MS);  static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,  			      enum ieee80211_smps_mode smps_mode) @@ -521,7 +531,7 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,  #endif  #define DEBUGFS_ADD_MODE(name, mode) \ -	debugfs_create_file(#name, mode, sdata->debugfs.dir, \ +	debugfs_create_file(#name, mode, sdata->vif.debugfs_dir, \  			    sdata, &name##_ops);  #define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400) @@ -542,6 +552,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)  	DEBUGFS_ADD(aid);  	DEBUGFS_ADD(last_beacon);  	DEBUGFS_ADD(ave_beacon); +	DEBUGFS_ADD(beacon_timeout);  	DEBUGFS_ADD_MODE(smps, 0600);  	DEBUGFS_ADD_MODE(tkip_mic_test, 0200);  	DEBUGFS_ADD_MODE(uapsd_queues, 0600); @@ -577,7 +588,7 @@ static void add_mesh_files(struct ieee80211_sub_if_data *sdata)  static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)  {  	struct dentry *dir = debugfs_create_dir("mesh_stats", -						sdata->debugfs.dir); +						sdata->vif.debugfs_dir);  #define MESHSTATS_ADD(name)\  	debugfs_create_file(#name, 0400, dir, sdata, &name##_ops); @@ -594,7 +605,7 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)  static void add_mesh_config(struct ieee80211_sub_if_data *sdata)  {  	struct dentry *dir = debugfs_create_dir("mesh_config", -						sdata->debugfs.dir); +						sdata->vif.debugfs_dir);  #define MESHPARAMS_ADD(name) \  	debugfs_create_file(#name, 0600, dir, sdata, &name##_ops); @@ -631,7 +642,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)  static void add_files(struct ieee80211_sub_if_data *sdata)  { -	if (!sdata->debugfs.dir) +	if (!sdata->vif.debugfs_dir)  		return;  	DEBUGFS_ADD(flags); @@ -673,21 +684,21 @@ void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)  	char buf[10+IFNAMSIZ];  	sprintf(buf, "netdev:%s", sdata->name); -	sdata->debugfs.dir = debugfs_create_dir(buf, +	sdata->vif.debugfs_dir = debugfs_create_dir(buf,  		sdata->local->hw.wiphy->debugfsdir); -	if (sdata->debugfs.dir) +	if (sdata->vif.debugfs_dir)  		sdata->debugfs.subdir_stations = debugfs_create_dir("stations", -			sdata->debugfs.dir); +			sdata->vif.debugfs_dir);  	add_files(sdata);  }  void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)  { -	if (!sdata->debugfs.dir) +	if (!sdata->vif.debugfs_dir)  		return; -	debugfs_remove_recursive(sdata->debugfs.dir); -	sdata->debugfs.dir = NULL; +	debugfs_remove_recursive(sdata->vif.debugfs_dir); +	sdata->vif.debugfs_dir = NULL;  }  void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata) @@ -695,7 +706,7 @@ void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)  	struct dentry *dir;  	char buf[10 + IFNAMSIZ]; -	dir = sdata->debugfs.dir; +	dir = sdata->vif.debugfs_dir;  	if (!dir)  		return; diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index c7591f73dbc..44e201d60a1 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -54,6 +54,7 @@ STA_FILE(aid, sta.aid, D);  STA_FILE(dev, sdata->name, S);  STA_FILE(last_signal, last_signal, D);  STA_FILE(last_ack_signal, last_ack_signal, D); +STA_FILE(beacon_loss_count, beacon_loss_count, D);  static ssize_t sta_flags_read(struct file *file, char __user *userbuf,  			      size_t count, loff_t *ppos) @@ -325,6 +326,36 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,  }  STA_OPS(ht_capa); +static ssize_t sta_vht_capa_read(struct file *file, char __user *userbuf, +				 size_t count, loff_t *ppos) +{ +	char buf[128], *p = buf; +	struct sta_info *sta = file->private_data; +	struct ieee80211_sta_vht_cap *vhtc = &sta->sta.vht_cap; + +	p += scnprintf(p, sizeof(buf) + buf - p, "VHT %ssupported\n", +			vhtc->vht_supported ? "" : "not "); +	if (vhtc->vht_supported) { +		p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.8x\n", vhtc->cap); + +		p += scnprintf(p, sizeof(buf)+buf-p, "RX MCS: %.4x\n", +			       le16_to_cpu(vhtc->vht_mcs.rx_mcs_map)); +		if (vhtc->vht_mcs.rx_highest) +			p += scnprintf(p, sizeof(buf)+buf-p, +				       "MCS RX highest: %d Mbps\n", +				       le16_to_cpu(vhtc->vht_mcs.rx_highest)); +		p += scnprintf(p, sizeof(buf)+buf-p, "TX MCS: %.4x\n", +			       le16_to_cpu(vhtc->vht_mcs.tx_mcs_map)); +		if (vhtc->vht_mcs.tx_highest) +			p += scnprintf(p, sizeof(buf)+buf-p, +				       "MCS TX highest: %d Mbps\n", +				       le16_to_cpu(vhtc->vht_mcs.tx_highest)); +	} + +	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); +} +STA_OPS(vht_capa); +  static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf,  					size_t count, loff_t *ppos)  { @@ -404,7 +435,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)  	DEBUGFS_ADD(agg_status);  	DEBUGFS_ADD(dev);  	DEBUGFS_ADD(last_signal); +	DEBUGFS_ADD(beacon_loss_count);  	DEBUGFS_ADD(ht_capa); +	DEBUGFS_ADD(vht_capa);  	DEBUGFS_ADD(last_ack_signal);  	DEBUGFS_ADD(current_tx_rate);  	DEBUGFS_ADD(last_rx_rate); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index ee56d0779d8..169664c122e 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -241,6 +241,22 @@ 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, @@ -531,43 +547,6 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,  		local->ops->sta_remove_debugfs(&local->hw, &sdata->vif,  					       sta, dir);  } - -static inline -void drv_add_interface_debugfs(struct ieee80211_local *local, -			       struct ieee80211_sub_if_data *sdata) -{ -	might_sleep(); - -	check_sdata_in_driver(sdata); - -	if (!local->ops->add_interface_debugfs) -		return; - -	local->ops->add_interface_debugfs(&local->hw, &sdata->vif, -					  sdata->debugfs.dir); -} - -static inline -void drv_remove_interface_debugfs(struct ieee80211_local *local, -				  struct ieee80211_sub_if_data *sdata) -{ -	might_sleep(); - -	check_sdata_in_driver(sdata); - -	if (!local->ops->remove_interface_debugfs) -		return; - -	local->ops->remove_interface_debugfs(&local->hw, &sdata->vif, -					     sdata->debugfs.dir); -} -#else -static inline -void drv_add_interface_debugfs(struct ieee80211_local *local, -			       struct ieee80211_sub_if_data *sdata) {} -static inline -void drv_remove_interface_debugfs(struct ieee80211_local *local, -				  struct ieee80211_sub_if_data *sdata) {}  #endif  static inline __must_check @@ -741,13 +720,14 @@ static inline void drv_rfkill_poll(struct ieee80211_local *local)  		local->ops->rfkill_poll(&local->hw);  } -static inline void drv_flush(struct ieee80211_local *local, bool drop) +static inline void drv_flush(struct ieee80211_local *local, +			     u32 queues, bool drop)  {  	might_sleep(); -	trace_drv_flush(local, drop); +	trace_drv_flush(local, queues, drop);  	if (local->ops->flush) -		local->ops->flush(&local->hw, drop); +		local->ops->flush(&local->hw, queues, drop);  	trace_drv_return_void(local);  } @@ -787,15 +767,16 @@ static inline int drv_get_antenna(struct ieee80211_local *local,  static inline int drv_remain_on_channel(struct ieee80211_local *local,  					struct ieee80211_sub_if_data *sdata,  					struct ieee80211_channel *chan, -					unsigned int duration) +					unsigned int duration, +					enum ieee80211_roc_type type)  {  	int ret;  	might_sleep(); -	trace_drv_remain_on_channel(local, sdata, chan, duration); +	trace_drv_remain_on_channel(local, sdata, chan, duration, type);  	ret = local->ops->remain_on_channel(&local->hw, &sdata->vif, -					    chan, duration); +					    chan, duration, type);  	trace_drv_return_int(local, ret);  	return ret; diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 0db25d4bb22..af8cee06e4f 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -40,13 +40,6 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,  	if (!ht_cap->ht_supported)  		return; -	if (sdata->vif.type != NL80211_IFTYPE_STATION) { -		/* AP interfaces call this code when adding new stations, -		 * so just silently ignore non station interfaces. -		 */ -		return; -	} -  	/* NOTE:  If you add more over-rides here, update register_hw  	 * ht_capa_mod_msk logic in main.c as well.  	 * And, if this method can ever change ht_cap.ht_supported, fix @@ -97,7 +90,7 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  				       const struct ieee80211_ht_cap *ht_cap_ie,  				       struct sta_info *sta)  { -	struct ieee80211_sta_ht_cap ht_cap; +	struct ieee80211_sta_ht_cap ht_cap, own_cap;  	u8 ampdu_info, tx_mcs_set_cap;  	int i, max_tx_streams;  	bool changed; @@ -111,6 +104,18 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  	ht_cap.ht_supported = true; +	own_cap = sband->ht_cap; + +	/* +	 * If user has specified capability over-rides, take care +	 * of that if the station we're setting up is the AP that +	 * we advertised a restricted capability set to. Override +	 * our own capabilities and then use those below. +	 */ +	if (sdata->vif.type == NL80211_IFTYPE_STATION && +	    !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) +		ieee80211_apply_htcap_overrides(sdata, &own_cap); +  	/*  	 * The bits listed in this expression should be  	 * the same for the peer and us, if the station @@ -118,21 +123,20 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  	 * we mask them out.  	 */  	ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) & -		(sband->ht_cap.cap | -		 ~(IEEE80211_HT_CAP_LDPC_CODING | -		   IEEE80211_HT_CAP_SUP_WIDTH_20_40 | -		   IEEE80211_HT_CAP_GRN_FLD | -		   IEEE80211_HT_CAP_SGI_20 | -		   IEEE80211_HT_CAP_SGI_40 | -		   IEEE80211_HT_CAP_DSSSCCK40)); +		(own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING | +				 IEEE80211_HT_CAP_SUP_WIDTH_20_40 | +				 IEEE80211_HT_CAP_GRN_FLD | +				 IEEE80211_HT_CAP_SGI_20 | +				 IEEE80211_HT_CAP_SGI_40 | +				 IEEE80211_HT_CAP_DSSSCCK40));  	/*  	 * The STBC bits are asymmetric -- if we don't have  	 * TX then mask out the peer's RX and vice versa.  	 */ -	if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) +	if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC))  		ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC; -	if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)) +	if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC))  		ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;  	ampdu_info = ht_cap_ie->ampdu_params_info; @@ -142,7 +146,7 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  		(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;  	/* own MCS TX capabilities */ -	tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; +	tx_mcs_set_cap = own_cap.mcs.tx_params;  	/* Copy peer MCS TX capabilities, the driver might need them. */  	ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params; @@ -168,26 +172,20 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,  	 */  	for (i = 0; i < max_tx_streams; i++)  		ht_cap.mcs.rx_mask[i] = -			sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; +			own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];  	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)  		for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;  		     i < IEEE80211_HT_MCS_MASK_LEN; i++)  			ht_cap.mcs.rx_mask[i] = -				sband->ht_cap.mcs.rx_mask[i] & +				own_cap.mcs.rx_mask[i] &  					ht_cap_ie->mcs.rx_mask[i];  	/* handle MCS rate 32 too */ -	if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) +	if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)  		ht_cap.mcs.rx_mask[32/8] |= 1;   apply: -	/* -	 * If user has specified capability over-rides, take care -	 * of that here. -	 */ -	ieee80211_apply_htcap_overrides(sdata, &ht_cap); -  	changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));  	memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 40b71dfcc79..170f9a7fa31 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -44,7 +44,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;  	int rates, i; -	struct sk_buff *skb;  	struct ieee80211_mgmt *mgmt;  	u8 *pos;  	struct ieee80211_supported_band *sband; @@ -52,20 +51,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	u32 bss_change;  	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];  	struct cfg80211_chan_def chandef; +	struct beacon_data *presp; +	int frame_len;  	lockdep_assert_held(&ifibss->mtx);  	/* Reset own TSF to allow time synchronization work. */  	drv_reset_tsf(local, sdata); -	skb = ifibss->skb; -	RCU_INIT_POINTER(ifibss->presp, NULL); -	synchronize_rcu(); -	skb->data = skb->head; -	skb->len = 0; -	skb_reset_tail_pointer(skb); -	skb_reserve(skb, sdata->local->hw.extra_tx_headroom); -  	if (!ether_addr_equal(ifibss->bssid, bssid))  		sta_info_flush(sdata); @@ -73,10 +66,19 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	if (sdata->vif.bss_conf.ibss_joined) {  		sdata->vif.bss_conf.ibss_joined = false;  		sdata->vif.bss_conf.ibss_creator = false; +		sdata->vif.bss_conf.enable_beacon = false;  		netif_carrier_off(sdata->dev); -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS); +		ieee80211_bss_info_change_notify(sdata, +						 BSS_CHANGED_IBSS | +						 BSS_CHANGED_BEACON_ENABLED);  	} +	presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&ifibss->mtx)); +	rcu_assign_pointer(ifibss->presp, NULL); +	if (presp) +		kfree_rcu(presp, rcu_head); +  	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;  	cfg80211_chandef_create(&chandef, chan, ifibss->channel_type); @@ -98,19 +100,24 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	sband = local->hw.wiphy->bands[chan->band]; -	/* build supported rates array */ -	pos = supp_rates; -	for (i = 0; i < sband->n_bitrates; i++) { -		int rate = sband->bitrates[i].bitrate; -		u8 basic = 0; -		if (basic_rates & BIT(i)) -			basic = 0x80; -		*pos++ = basic | (u8) (rate / 5); -	} -  	/* Build IBSS probe response */ -	mgmt = (void *) skb_put(skb, 24 + sizeof(mgmt->u.beacon)); -	memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); +	frame_len = sizeof(struct ieee80211_hdr_3addr) + +		    12 /* struct ieee80211_mgmt.u.beacon */ + +		    2 + IEEE80211_MAX_SSID_LEN /* max SSID */ + +		    2 + 8 /* max Supported Rates */ + +		    3 /* max DS params */ + +		    4 /* IBSS params */ + +		    2 + (IEEE80211_MAX_SUPP_RATES - 8) + +		    2 + sizeof(struct ieee80211_ht_cap) + +		    2 + sizeof(struct ieee80211_ht_operation) + +		    ifibss->ie_len; +	presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL); +	if (!presp) +		return; + +	presp->head = (void *)(presp + 1); + +	mgmt = (void *) presp->head;  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_PROBE_RESP);  	eth_broadcast_addr(mgmt->da); @@ -120,27 +127,30 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	mgmt->u.beacon.timestamp = cpu_to_le64(tsf);  	mgmt->u.beacon.capab_info = cpu_to_le16(capability); -	pos = skb_put(skb, 2 + ifibss->ssid_len); +	pos = (u8 *)mgmt + offsetof(struct ieee80211_mgmt, u.beacon.variable); +  	*pos++ = WLAN_EID_SSID;  	*pos++ = ifibss->ssid_len;  	memcpy(pos, ifibss->ssid, ifibss->ssid_len); +	pos += ifibss->ssid_len; -	rates = sband->n_bitrates; -	if (rates > 8) -		rates = 8; -	pos = skb_put(skb, 2 + rates); +	rates = min_t(int, 8, sband->n_bitrates);  	*pos++ = WLAN_EID_SUPP_RATES;  	*pos++ = rates; -	memcpy(pos, supp_rates, rates); +	for (i = 0; i < rates; i++) { +		int rate = sband->bitrates[i].bitrate; +		u8 basic = 0; +		if (basic_rates & BIT(i)) +			basic = 0x80; +		*pos++ = basic | (u8) (rate / 5); +	}  	if (sband->band == IEEE80211_BAND_2GHZ) { -		pos = skb_put(skb, 2 + 1);  		*pos++ = WLAN_EID_DS_PARAMS;  		*pos++ = 1;  		*pos++ = ieee80211_frequency_to_channel(chan->center_freq);  	} -	pos = skb_put(skb, 2 + 2);  	*pos++ = WLAN_EID_IBSS_PARAMS;  	*pos++ = 2;  	/* FIX: set ATIM window based on scan results */ @@ -148,23 +158,25 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	*pos++ = 0;  	if (sband->n_bitrates > 8) { -		rates = sband->n_bitrates - 8; -		pos = skb_put(skb, 2 + rates);  		*pos++ = WLAN_EID_EXT_SUPP_RATES; -		*pos++ = rates; -		memcpy(pos, &supp_rates[8], rates); +		*pos++ = sband->n_bitrates - 8; +		for (i = 8; i < sband->n_bitrates; i++) { +			int rate = sband->bitrates[i].bitrate; +			u8 basic = 0; +			if (basic_rates & BIT(i)) +				basic = 0x80; +			*pos++ = basic | (u8) (rate / 5); +		}  	} -	if (ifibss->ie_len) -		memcpy(skb_put(skb, ifibss->ie_len), -		       ifibss->ie, ifibss->ie_len); +	if (ifibss->ie_len) { +		memcpy(pos, ifibss->ie, ifibss->ie_len); +		pos += ifibss->ie_len; +	}  	/* add HT capability and information IEs */  	if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&  	    sband->ht_cap.ht_supported) { -		pos = skb_put(skb, 4 + -				   sizeof(struct ieee80211_ht_cap) + -				   sizeof(struct ieee80211_ht_operation));  		pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,  						sband->ht_cap.cap);  		/* @@ -177,7 +189,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	}  	if (local->hw.queues >= IEEE80211_NUM_ACS) { -		pos = skb_put(skb, 9);  		*pos++ = WLAN_EID_VENDOR_SPECIFIC;  		*pos++ = 7; /* len */  		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ @@ -189,11 +200,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  		*pos++ = 0; /* U-APSD no in use */  	} -	rcu_assign_pointer(ifibss->presp, skb); +	presp->head_len = pos - presp->head; +	if (WARN_ON(presp->head_len > frame_len)) +		return; + +	rcu_assign_pointer(ifibss->presp, presp);  	sdata->vif.bss_conf.enable_beacon = true;  	sdata->vif.bss_conf.beacon_int = beacon_int;  	sdata->vif.bss_conf.basic_rates = basic_rates; +	sdata->vif.bss_conf.ssid_len = ifibss->ssid_len; +	memcpy(sdata->vif.bss_conf.ssid, ifibss->ssid, ifibss->ssid_len);  	bss_change = BSS_CHANGED_BEACON_INT;  	bss_change |= ieee80211_reset_erp_info(sdata);  	bss_change |= BSS_CHANGED_BSSID; @@ -202,6 +219,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	bss_change |= BSS_CHANGED_BASIC_RATES;  	bss_change |= BSS_CHANGED_HT;  	bss_change |= BSS_CHANGED_IBSS; +	bss_change |= BSS_CHANGED_SSID;  	/*  	 * In 5 GHz/802.11a, we can always use short slot time. @@ -227,7 +245,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));  	bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan, -					mgmt, skb->len, 0, GFP_KERNEL); +					mgmt, 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); @@ -448,7 +466,7 @@ 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 && elems->ds_params_len == 1) +	if (elems->ds_params)  		freq = ieee80211_channel_to_frequency(elems->ds_params[0],  						      band);  	else @@ -822,8 +840,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_local *local = sdata->local;  	int tx_last_beacon, len = req->len;  	struct sk_buff *skb; -	struct ieee80211_mgmt *resp; -	struct sk_buff *presp; +	struct beacon_data *presp;  	u8 *pos, *end;  	lockdep_assert_held(&ifibss->mtx); @@ -864,13 +881,15 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,  	}  	/* Reply with ProbeResp */ -	skb = skb_copy(presp, GFP_KERNEL); +	skb = dev_alloc_skb(local->tx_headroom + presp->head_len);  	if (!skb)  		return; -	resp = (struct ieee80211_mgmt *) skb->data; -	memcpy(resp->da, mgmt->sa, ETH_ALEN); -	ibss_dbg(sdata, "Sending ProbeResp to %pM\n", resp->da); +	skb_reserve(skb, local->tx_headroom); +	memcpy(skb_put(skb, presp->head_len), presp->head, presp->head_len); + +	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;  	ieee80211_tx_skb(sdata, skb);  } @@ -895,7 +914,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,  		return;  	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, -				&elems); +			       false, &elems);  	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);  } @@ -985,36 +1004,9 @@ static void ieee80211_ibss_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	struct ieee80211_local *local = sdata->local; - -	if (local->quiescing) { -		ifibss->timer_running = true; -		return; -	} - -	ieee80211_queue_work(&local->hw, &sdata->work); -} - -#ifdef CONFIG_PM -void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	if (del_timer_sync(&ifibss->timer)) -		ifibss->timer_running = true; -} - -void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - -	if (ifibss->timer_running) { -		add_timer(&ifibss->timer); -		ifibss->timer_running = false; -	} +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  } -#endif  void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)  { @@ -1047,23 +1039,8 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)  int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  			struct cfg80211_ibss_params *params)  { -	struct sk_buff *skb;  	u32 changed = 0; -	skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + -			    sizeof(struct ieee80211_hdr_3addr) + -			    12 /* struct ieee80211_mgmt.u.beacon */ + -			    2 + IEEE80211_MAX_SSID_LEN /* max SSID */ + -			    2 + 8 /* max Supported Rates */ + -			    3 /* max DS params */ + -			    4 /* IBSS params */ + -			    2 + (IEEE80211_MAX_SUPP_RATES - 8) + -			    2 + sizeof(struct ieee80211_ht_cap) + -			    2 + sizeof(struct ieee80211_ht_operation) + -			    params->ie_len); -	if (!skb) -		return -ENOMEM; -  	mutex_lock(&sdata->u.ibss.mtx);  	if (params->bssid) { @@ -1092,7 +1069,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  			sdata->u.ibss.ie_len = params->ie_len;  	} -	sdata->u.ibss.skb = skb;  	sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;  	sdata->u.ibss.ibss_join_req = jiffies; @@ -1128,13 +1104,13 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)  { -	struct sk_buff *skb;  	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;  	mutex_lock(&sdata->u.ibss.mtx); @@ -1180,17 +1156,18 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)  	/* remove beacon */  	kfree(sdata->u.ibss.ie); -	skb = rcu_dereference_protected(sdata->u.ibss.presp, -					lockdep_is_held(&sdata->u.ibss.mtx)); +	presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&sdata->u.ibss.mtx));  	RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);  	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);  	synchronize_rcu(); -	kfree_skb(skb); +	kfree(presp);  	skb_queue_purge(&sdata->skb_queue); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5672533a083..158e6eb188d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -156,6 +156,7 @@ struct ieee80211_tx_data {  	struct ieee80211_sub_if_data *sdata;  	struct sta_info *sta;  	struct ieee80211_key *key; +	struct ieee80211_tx_rate rate;  	unsigned int flags;  }; @@ -316,6 +317,7 @@ struct ieee80211_roc_work {  	u32 duration, req_duration;  	struct sk_buff *frame;  	u64 cookie, mgmt_tx_cookie; +	enum ieee80211_roc_type type;  };  /* flags used in struct ieee80211_if_managed.flags */ @@ -401,7 +403,6 @@ struct ieee80211_if_managed {  	u16 aid; -	unsigned long timers_running; /* used for quiesce/restart */  	bool powersave; /* powersave requested for this iface */  	bool broken_ap; /* AP is broken -- turn off powersave */  	u8 dtim_period; @@ -443,7 +444,7 @@ struct ieee80211_if_managed {  	u8 use_4addr; -	u8 p2p_noa_index; +	s16 p2p_noa_index;  	/* Signal strength from the last Beacon frame in the current BSS. */  	int last_beacon_signal; @@ -480,6 +481,8 @@ struct ieee80211_if_managed {  	struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */  	struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */ +	struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */ +	struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */  };  struct ieee80211_if_ibss { @@ -491,8 +494,6 @@ struct ieee80211_if_ibss {  	u32 basic_rates; -	bool timer_running; -  	bool fixed_bssid;  	bool fixed_channel;  	bool privacy; @@ -509,8 +510,7 @@ struct ieee80211_if_ibss {  	unsigned long ibss_join_req;  	/* probe response/beacon for IBSS */ -	struct sk_buff __rcu *presp; -	struct sk_buff *skb; +	struct beacon_data __rcu *presp;  	spinlock_t incomplete_lock;  	struct list_head incomplete_stations; @@ -544,8 +544,6 @@ struct ieee80211_if_mesh {  	struct timer_list mesh_path_timer;  	struct timer_list mesh_path_root_timer; -	unsigned long timers_running; -  	unsigned long wrkq_flags;  	u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; @@ -591,6 +589,7 @@ struct ieee80211_if_mesh {  		IEEE80211_MESH_SEC_AUTHED = 0x1,  		IEEE80211_MESH_SEC_SECURED = 0x2,  	} security; +	bool user_mpm;  	/* Extensible Synchronization Framework */  	const struct ieee80211_mesh_sync_ops *sync_ops;  	s64 sync_offset_clockdrift_max; @@ -683,6 +682,8 @@ struct ieee80211_sub_if_data {  	/* count for keys needing tailroom space allocation */  	int crypto_tx_tailroom_needed_cnt; +	int crypto_tx_tailroom_pending_dec; +	struct delayed_work dec_tailroom_needed_wk;  	struct net_device *dev;  	struct ieee80211_local *local; @@ -740,6 +741,8 @@ struct ieee80211_sub_if_data {  	/* bitmap of allowed (non-MCS) rate indexes for rate control */  	u32 rc_rateidx_mask[IEEE80211_NUM_BANDS]; + +	bool rc_has_mcs_mask[IEEE80211_NUM_BANDS];  	u8  rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN];  	union { @@ -758,7 +761,6 @@ struct ieee80211_sub_if_data {  #ifdef CONFIG_MAC80211_DEBUGFS  	struct { -		struct dentry *dir;  		struct dentry *subdir_stations;  		struct dentry *default_unicast_key;  		struct dentry *default_multicast_key; @@ -766,10 +768,6 @@ struct ieee80211_sub_if_data {  	} debugfs;  #endif -#ifdef CONFIG_PM -	struct ieee80211_bss_conf suspend_bss_conf; -#endif -  	/* must be last, dynamically sized area in this! */  	struct ieee80211_vif vif;  }; @@ -804,11 +802,6 @@ enum sdata_queue_type {  enum {  	IEEE80211_RX_MSG	= 1,  	IEEE80211_TX_STATUS_MSG	= 2, -	IEEE80211_EOSP_MSG	= 3, -}; - -struct skb_eosp_msg_data { -	u8 sta[ETH_ALEN], iface[ETH_ALEN];  };  enum queue_stop_reason { @@ -819,6 +812,7 @@ enum queue_stop_reason {  	IEEE80211_QUEUE_STOP_REASON_SUSPEND,  	IEEE80211_QUEUE_STOP_REASON_SKB_ADD,  	IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, +	IEEE80211_QUEUE_STOP_REASON_FLUSH,  };  #ifdef CONFIG_MAC80211_LEDS @@ -1029,10 +1023,9 @@ struct ieee80211_local {  	enum mac80211_scan_state next_scan_state;  	struct delayed_work scan_work;  	struct ieee80211_sub_if_data __rcu *scan_sdata; -	struct ieee80211_channel *csa_channel; +	struct cfg80211_chan_def csa_chandef;  	/* For backward compatibility only -- do not use */ -	struct ieee80211_channel *_oper_channel; -	enum nl80211_channel_type _oper_channel_type; +	struct cfg80211_chan_def _oper_chandef;  	/* Temporary remain-on-channel for off-channel operations */  	struct ieee80211_channel *tmp_channel; @@ -1137,11 +1130,6 @@ struct ieee80211_local {  	struct ieee80211_sub_if_data __rcu *p2p_sdata; -	/* dummy netdev for use w/ NAPI */ -	struct net_device napi_dev; - -	struct napi_struct napi; -  	/* virtual monitor interface */  	struct ieee80211_sub_if_data __rcu *monitor_sdata;  	struct cfg80211_chan_def monitor_chandef; @@ -1173,11 +1161,8 @@ struct ieee802_11_elems {  	/* pointers to IEs */  	const u8 *ssid;  	const u8 *supp_rates; -	const u8 *fh_params;  	const u8 *ds_params; -	const u8 *cf_params;  	const struct ieee80211_tim_ie *tim; -	const u8 *ibss_params;  	const u8 *challenge;  	const u8 *rsn;  	const u8 *erp_info; @@ -1197,23 +1182,20 @@ struct ieee802_11_elems {  	const u8 *perr;  	const struct ieee80211_rann_ie *rann;  	const struct ieee80211_channel_sw_ie *ch_switch_ie; +	const struct ieee80211_ext_chansw_ie *ext_chansw_ie; +	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;  	const u8 *country_elem;  	const u8 *pwr_constr_elem; -	const u8 *quiet_elem;	/* first quite element */ -	const u8 *timeout_int; +	const struct ieee80211_timeout_interval_ie *timeout_int;  	const u8 *opmode_notif; +	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;  	/* length of them, respectively */  	u8 ssid_len;  	u8 supp_rates_len; -	u8 fh_params_len; -	u8 ds_params_len; -	u8 cf_params_len;  	u8 tim_len; -	u8 ibss_params_len;  	u8 challenge_len;  	u8 rsn_len; -	u8 erp_info_len;  	u8 ext_supp_rates_len;  	u8 wmm_info_len;  	u8 wmm_param_len; @@ -1223,9 +1205,6 @@ struct ieee802_11_elems {  	u8 prep_len;  	u8 perr_len;  	u8 country_elem_len; -	u8 quiet_elem_len; -	u8 num_of_quiet_elem;	/* can be more the one */ -	u8 timeout_int_len;  	/* whether a parse error occurred while retrieving these elements */  	bool parse_error; @@ -1280,12 +1259,6 @@ void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata);  int ieee80211_max_network_latency(struct notifier_block *nb,  				  unsigned long data, void *dummy);  int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata); -void -ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, -				 const struct ieee80211_channel_sw_ie *sw_elem, -				 struct ieee80211_bss *bss, u64 timestamp); -void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); -void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);  void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);  void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  				  struct sk_buff *skb); @@ -1303,8 +1276,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,  int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  			struct cfg80211_ibss_params *params);  int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); -void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata); -void ieee80211_ibss_restart(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); @@ -1347,7 +1318,8 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local);  void ieee80211_offchannel_return(struct ieee80211_local *local);  void ieee80211_roc_setup(struct ieee80211_local *local);  void ieee80211_start_next_roc(struct ieee80211_local *local); -void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata); +void ieee80211_roc_purge(struct ieee80211_local *local, +			 struct ieee80211_sub_if_data *sdata);  void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);  void ieee80211_sw_roc_work(struct work_struct *work);  void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); @@ -1368,6 +1340,8 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,  				    const int offset);  int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up);  void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata); +int ieee80211_add_virtual_monitor(struct ieee80211_local *local); +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); @@ -1443,6 +1417,8 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta);  void 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_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, +				      struct ieee80211_sta_vht_cap *vht_cap);  /* Spectrum management */  void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, @@ -1520,11 +1496,15 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,  	ieee80211_tx_skb_tid(sdata, skb, 7);  } -void ieee802_11_parse_elems(u8 *start, size_t len, -			    struct ieee802_11_elems *elems); -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, +u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action,  			       struct ieee802_11_elems *elems,  			       u64 filter, u32 crc); +static inline void ieee802_11_parse_elems(u8 *start, size_t len, bool action, +					  struct ieee802_11_elems *elems) +{ +	ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); +} +  u32 ieee80211_mandatory_rates(struct ieee80211_local *local,  			      enum ieee80211_band band); @@ -1540,8 +1520,10 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,  			     struct ieee80211_hdr *hdr, bool ack);  void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, +				     unsigned long queues,  				     enum queue_stop_reason reason);  void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, +				     unsigned long queues,  				     enum queue_stop_reason reason);  void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,  				    enum queue_stop_reason reason); @@ -1558,6 +1540,8 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local,  {  	ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL);  } +void ieee80211_flush_queues(struct ieee80211_local *local, +			    struct ieee80211_sub_if_data *sdata);  void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,  			 u16 transaction, u16 auth_alg, u16 status, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 9ed49ad0380..60f1ce5e5e5 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1,5 +1,5 @@  /* - * Interface handling (except master interface) + * Interface handling   *   * Copyright 2002-2005, Instant802 Networks, Inc.   * Copyright 2005-2006, Devicescape Software, Inc. @@ -92,7 +92,7 @@ static u32 __ieee80211_idle_on(struct ieee80211_local *local)  	if (local->hw.conf.flags & IEEE80211_CONF_IDLE)  		return 0; -	drv_flush(local, false); +	ieee80211_flush_queues(local, NULL);  	local->hw.conf.flags |= IEEE80211_CONF_IDLE;  	return IEEE80211_CONF_CHANGE_IDLE; @@ -357,7 +357,7 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)  	sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;  } -static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) +int ieee80211_add_virtual_monitor(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata;  	int ret; @@ -410,7 +410,7 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)  	return 0;  } -static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) +void ieee80211_del_virtual_monitor(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata; @@ -499,8 +499,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)  		res = drv_start(local);  		if (res)  			goto err_del_bss; -		if (local->ops->napi_poll) -			napi_enable(&local->napi);  		/* we're brought up, everything changes */  		hw_reconf_flags = ~0;  		ieee80211_led_radio(local, true); @@ -573,8 +571,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)  				goto err_del_interface;  		} -		drv_add_interface_debugfs(local, sdata); -  		if (sdata->vif.type == NL80211_IFTYPE_AP) {  			local->fif_pspoll++;  			local->fif_probe_req++; @@ -599,7 +595,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)  		case NL80211_IFTYPE_P2P_DEVICE:  			break;  		default: -			netif_carrier_on(dev); +			/* not reached */ +			WARN_ON(1);  		}  		/* @@ -656,8 +653,28 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)  	ieee80211_recalc_ps(local, -1); -	if (dev) -		netif_tx_start_all_queues(dev); +	if (dev) { +		unsigned long flags; +		int n_acs = IEEE80211_NUM_ACS; +		int ac; + +		if (local->hw.queues < IEEE80211_NUM_ACS) +			n_acs = 1; + +		spin_lock_irqsave(&local->queue_stop_reason_lock, flags); +		if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE || +		    (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 && +		     skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) { +			for (ac = 0; ac < n_acs; ac++) { +				int ac_queue = sdata->vif.hw_queue[ac]; + +				if (local->queue_stop_reasons[ac_queue] == 0 && +				    skb_queue_empty(&local->pending[ac_queue])) +					netif_start_subqueue(dev, ac); +			} +		} +		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); +	}  	return 0;   err_del_interface: @@ -711,7 +728,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	if (sdata->dev)  		netif_tx_stop_all_queues(sdata->dev); -	ieee80211_roc_purge(sdata); +	ieee80211_roc_purge(local, sdata);  	if (sdata->vif.type == NL80211_IFTYPE_STATION)  		ieee80211_mgd_stop(sdata); @@ -736,12 +753,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||  		     (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); -	/* -	 * Don't count this interface for promisc/allmulti while it -	 * is down. dev_mc_unsync() will invoke set_multicast_list -	 * on the master interface which will sync these down to the -	 * hardware as filter flags. -	 */ +	/* don't count this interface for promisc/allmulti while it is down */  	if (sdata->flags & IEEE80211_SDATA_ALLMULTI)  		atomic_dec(&local->iff_allmultis); @@ -762,8 +774,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  				 sdata->dev->addr_len);  		spin_unlock_bh(&local->filter_lock);  		netif_addr_unlock_bh(sdata->dev); - -		ieee80211_configure_filter(local);  	}  	del_timer_sync(&local->dynamic_ps_timer); @@ -774,6 +784,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);  	if (sdata->wdev.cac_started) { +		WARN_ON(local->suspended);  		mutex_lock(&local->iflist_mtx);  		ieee80211_vif_release_channel(sdata);  		mutex_unlock(&local->iflist_mtx); @@ -824,14 +835,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  		if (local->monitors == 0) {  			local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;  			hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; -			ieee80211_del_virtual_monitor(local);  		}  		ieee80211_adjust_monitor_flags(sdata, -1); -		ieee80211_configure_filter(local); -		mutex_lock(&local->mtx); -		ieee80211_recalc_idle(local); -		mutex_unlock(&local->mtx);  		break;  	case NL80211_IFTYPE_P2P_DEVICE:  		/* relies on synchronize_rcu() below */ @@ -844,46 +850,28 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  		 *  		 * sta_info_flush_cleanup() requires rcu_barrier()  		 * first to wait for the station call_rcu() calls -		 * to complete, here we need at least sychronize_rcu() -		 * it to wait for the RX path in case it is using the +		 * 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  		 * another CPU.  		 */ +		synchronize_rcu();  		rcu_barrier();  		sta_info_flush_cleanup(sdata); -		skb_queue_purge(&sdata->skb_queue); -  		/*  		 * Free all remaining keys, there shouldn't be any, -		 * except maybe group keys in AP more or WDS? +		 * except maybe in WDS mode?  		 */  		ieee80211_free_keys(sdata); -		drv_remove_interface_debugfs(local, sdata); - -		if (going_down) -			drv_remove_interface(local, sdata); +		/* fall through */ +	case NL80211_IFTYPE_AP: +		skb_queue_purge(&sdata->skb_queue);  	}  	sdata->bss = NULL; -	ieee80211_recalc_ps(local, -1); - -	if (local->open_count == 0) { -		if (local->ops->napi_poll) -			napi_disable(&local->napi); -		ieee80211_clear_tx_pending(local); -		ieee80211_stop_device(local); - -		/* no reconfiguring after stop! */ -		hw_reconf_flags = 0; -	} - -	/* do after stop to avoid reconfiguring when we stop anyway */ -	if (hw_reconf_flags) -		ieee80211_hw_config(local, hw_reconf_flags); -  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);  	for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {  		skb_queue_walk_safe(&local->pending[i], skb, tmp) { @@ -896,7 +884,54 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	}  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); -	if (local->monitors == local->open_count && local->monitors > 0) +	if (local->open_count == 0) +		ieee80211_clear_tx_pending(local); + +	/* +	 * If the interface goes down while suspended, presumably because +	 * the device was unplugged and that happens before our resume, +	 * then the driver is already unconfigured and the remainder of +	 * this function isn't needed. +	 * XXX: what about WoWLAN? If the device has software state, e.g. +	 *	memory allocated, it might expect teardown commands from +	 *	mac80211 here? +	 */ +	if (local->suspended) { +		WARN_ON(local->wowlan); +		WARN_ON(rtnl_dereference(local->monitor_sdata)); +		return; +	} + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP_VLAN: +		break; +	case NL80211_IFTYPE_MONITOR: +		if (local->monitors == 0) +			ieee80211_del_virtual_monitor(local); + +		mutex_lock(&local->mtx); +		ieee80211_recalc_idle(local); +		mutex_unlock(&local->mtx); +		break; +	default: +		if (going_down) +			drv_remove_interface(local, sdata); +	} + +	ieee80211_recalc_ps(local, -1); + +	if (local->open_count == 0) { +		ieee80211_stop_device(local); + +		/* no reconfiguring after stop! */ +		return; +	} + +	/* do after stop to avoid reconfiguring when we stop anyway */ +	ieee80211_configure_filter(local); +	ieee80211_hw_config(local, hw_reconf_flags); + +	if (local->monitors == local->open_count)  		ieee80211_add_virtual_monitor(local);  } @@ -935,6 +970,17 @@ 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); @@ -1561,6 +1607,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,  	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, +			  ieee80211_delayed_tailroom_dec);  	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {  		struct ieee80211_supported_band *sband; diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ef252eb58c3..67059b88fea 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -248,11 +248,11 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,  } -static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, -				    struct sta_info *sta, -				    bool pairwise, -				    struct ieee80211_key *old, -				    struct ieee80211_key *new) +static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, +				  struct sta_info *sta, +				  bool pairwise, +				  struct ieee80211_key *old, +				  struct ieee80211_key *new)  {  	int idx;  	bool defunikey, defmultikey, defmgmtkey; @@ -397,7 +397,41 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,  	return key;  } -static void __ieee80211_key_destroy(struct ieee80211_key *key) +static void ieee80211_key_free_common(struct ieee80211_key *key) +{ +	if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP) +		ieee80211_aes_key_free(key->u.ccmp.tfm); +	if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) +		ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); +	kfree(key); +} + +static void __ieee80211_key_destroy(struct ieee80211_key *key, +				    bool delay_tailroom) +{ +	if (key->local) +		ieee80211_key_disable_hw_accel(key); + +	if (key->local) { +		struct ieee80211_sub_if_data *sdata = key->sdata; + +		ieee80211_debugfs_key_remove(key); + +		if (delay_tailroom) { +			/* see ieee80211_delayed_tailroom_dec */ +			sdata->crypto_tx_tailroom_pending_dec++; +			schedule_delayed_work(&sdata->dec_tailroom_needed_wk, +					      HZ/2); +		} else { +			sdata->crypto_tx_tailroom_needed_cnt--; +		} +	} + +	ieee80211_key_free_common(key); +} + +static void ieee80211_key_destroy(struct ieee80211_key *key, +				  bool delay_tailroom)  {  	if (!key)  		return; @@ -408,19 +442,13 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)  	 */  	synchronize_net(); -	if (key->local) -		ieee80211_key_disable_hw_accel(key); - -	if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP) -		ieee80211_aes_key_free(key->u.ccmp.tfm); -	if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) -		ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); -	if (key->local) { -		ieee80211_debugfs_key_remove(key); -		key->sdata->crypto_tx_tailroom_needed_cnt--; -	} +	__ieee80211_key_destroy(key, delay_tailroom); +} -	kfree(key); +void ieee80211_key_free_unused(struct ieee80211_key *key) +{ +	WARN_ON(key->sdata || key->local); +	ieee80211_key_free_common(key);  }  int ieee80211_key_link(struct ieee80211_key *key, @@ -440,32 +468,6 @@ int ieee80211_key_link(struct ieee80211_key *key,  	key->sdata = sdata;  	key->sta = sta; -	if (sta) { -		/* -		 * some hardware cannot handle TKIP with QoS, so -		 * we indicate whether QoS could be in use. -		 */ -		if (test_sta_flag(sta, WLAN_STA_WME)) -			key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; -	} else { -		if (sdata->vif.type == NL80211_IFTYPE_STATION) { -			struct sta_info *ap; - -			/* -			 * We're getting a sta pointer in, so must be under -			 * appropriate locking for sta_info_get(). -			 */ - -			/* same here, the AP could be using QoS */ -			ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid); -			if (ap) { -				if (test_sta_flag(ap, WLAN_STA_WME)) -					key->conf.flags |= -						IEEE80211_KEY_FLAG_WMM_STA; -			} -		} -	} -  	mutex_lock(&sdata->local->key_mtx);  	if (sta && pairwise) @@ -477,19 +479,22 @@ int ieee80211_key_link(struct ieee80211_key *key,  	increment_tailroom_need_count(sdata); -	__ieee80211_key_replace(sdata, sta, pairwise, old_key, key); -	__ieee80211_key_destroy(old_key); +	ieee80211_key_replace(sdata, sta, pairwise, old_key, key); +	ieee80211_key_destroy(old_key, true);  	ieee80211_debugfs_key_add(key);  	ret = ieee80211_key_enable_hw_accel(key); +	if (ret) +		ieee80211_key_free(key, true); +  	mutex_unlock(&sdata->local->key_mtx);  	return ret;  } -void __ieee80211_key_free(struct ieee80211_key *key) +void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)  {  	if (!key)  		return; @@ -498,18 +503,10 @@ void __ieee80211_key_free(struct ieee80211_key *key)  	 * Replace key with nothingness if it was ever used.  	 */  	if (key->sdata) -		__ieee80211_key_replace(key->sdata, key->sta, +		ieee80211_key_replace(key->sdata, key->sta,  				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,  				key, NULL); -	__ieee80211_key_destroy(key); -} - -void ieee80211_key_free(struct ieee80211_local *local, -			struct ieee80211_key *key) -{ -	mutex_lock(&local->key_mtx); -	__ieee80211_key_free(key); -	mutex_unlock(&local->key_mtx); +	ieee80211_key_destroy(key, delay_tailroom);  }  void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) @@ -566,36 +563,109 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,  }  EXPORT_SYMBOL(ieee80211_iter_keys); -void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) +void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)  { -	struct ieee80211_key *key; +	struct ieee80211_key *key, *tmp; +	LIST_HEAD(keys); -	ASSERT_RTNL(); +	cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);  	mutex_lock(&sdata->local->key_mtx); -	list_for_each_entry(key, &sdata->key_list, list) -		ieee80211_key_disable_hw_accel(key); +	sdata->crypto_tx_tailroom_needed_cnt -= +		sdata->crypto_tx_tailroom_pending_dec; +	sdata->crypto_tx_tailroom_pending_dec = 0; + +	ieee80211_debugfs_key_remove_mgmt_default(sdata); + +	list_for_each_entry_safe(key, tmp, &sdata->key_list, list) { +		ieee80211_key_replace(key->sdata, key->sta, +				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, +				key, NULL); +		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); +	} + +	WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt || +		     sdata->crypto_tx_tailroom_pending_dec);  	mutex_unlock(&sdata->local->key_mtx);  } -void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) +void ieee80211_free_sta_keys(struct ieee80211_local *local, +			     struct sta_info *sta)  {  	struct ieee80211_key *key, *tmp; +	LIST_HEAD(keys); +	int i; -	mutex_lock(&sdata->local->key_mtx); +	mutex_lock(&local->key_mtx); +	for (i = 0; i < NUM_DEFAULT_KEYS; i++) { +		key = key_mtx_dereference(local, sta->gtk[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); +	} -	ieee80211_debugfs_key_remove_mgmt_default(sdata); +	key = key_mtx_dereference(local, sta->ptk); +	if (key) { +		ieee80211_key_replace(key->sdata, key->sta, +				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, +				key, NULL); +		list_add(&key->list, &keys); +	} -	list_for_each_entry_safe(key, tmp, &sdata->key_list, list) -		__ieee80211_key_free(key); +	/* +	 * NB: the station code relies on this being +	 * done even if there aren't any keys +	 */ +	synchronize_net(); -	ieee80211_debugfs_key_update_default(sdata); +	list_for_each_entry_safe(key, tmp, &keys, list) +		__ieee80211_key_destroy(key, true); -	mutex_unlock(&sdata->local->key_mtx); +	mutex_unlock(&local->key_mtx);  } +void ieee80211_delayed_tailroom_dec(struct work_struct *wk) +{ +	struct ieee80211_sub_if_data *sdata; + +	sdata = container_of(wk, struct ieee80211_sub_if_data, +			     dec_tailroom_needed_wk.work); + +	/* +	 * The reason for the delayed tailroom needed decrementing is to +	 * make roaming faster: during roaming, all keys are first deleted +	 * and then new keys are installed. The first new key causes the +	 * crypto_tx_tailroom_needed_cnt to go from 0 to 1, which invokes +	 * the cost of synchronize_net() (which can be slow). Avoid this +	 * by deferring the crypto_tx_tailroom_needed_cnt decrementing on +	 * key removal for a while, so if we roam the value is larger than +	 * zero and no 0->1 transition happens. +	 * +	 * The cost is that if the AP switching was from an AP with keys +	 * to one without, we still allocate tailroom while it would no +	 * longer be needed. However, in the typical (fast) roaming case +	 * within an ESS this usually won't happen. +	 */ + +	mutex_lock(&sdata->local->key_mtx); +	sdata->crypto_tx_tailroom_needed_cnt -= +		sdata->crypto_tx_tailroom_pending_dec; +	sdata->crypto_tx_tailroom_pending_dec = 0; +	mutex_unlock(&sdata->local->key_mtx); +}  void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,  				const u8 *replay_ctr, gfp_t gfp) diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 382dc44ed33..e8de3e6d780 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -129,23 +129,25 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,  					  size_t seq_len, const u8 *seq);  /*   * Insert a key into data structures (sdata, sta if necessary) - * to make it used, free old key. + * to make it used, free old key. On failure, also free the new key.   */ -int __must_check ieee80211_key_link(struct ieee80211_key *key, -				    struct ieee80211_sub_if_data *sdata, -				    struct sta_info *sta); -void __ieee80211_key_free(struct ieee80211_key *key); -void ieee80211_key_free(struct ieee80211_local *local, -			struct ieee80211_key *key); +int ieee80211_key_link(struct ieee80211_key *key, +		       struct ieee80211_sub_if_data *sdata, +		       struct sta_info *sta); +void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom); +void ieee80211_key_free_unused(struct ieee80211_key *key);  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_sta_keys(struct ieee80211_local *local, +			     struct sta_info *sta);  void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); -void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata);  #define key_mtx_dereference(local, ref) \  	rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx))) +void ieee80211_delayed_tailroom_dec(struct work_struct *wk); +  #endif /* IEEE80211_KEY_H */ diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 1a8591b77a1..8a7bfc47d57 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -95,43 +95,47 @@ static void ieee80211_reconfig_filter(struct work_struct *work)  static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata; -	struct ieee80211_channel *chan; +	struct cfg80211_chan_def chandef = {};  	u32 changed = 0;  	int power; -	enum nl80211_channel_type channel_type;  	u32 offchannel_flag; -	bool scanning = false;  	offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; +  	if (local->scan_channel) { -		chan = local->scan_channel; +		chandef.chan = local->scan_channel;  		/* If scanning on oper channel, use whatever channel-type  		 * is currently in use.  		 */ -		if (chan == local->_oper_channel) -			channel_type = local->_oper_channel_type; -		else -			channel_type = NL80211_CHAN_NO_HT; +		if (chandef.chan == local->_oper_chandef.chan) { +			chandef = local->_oper_chandef; +		} else { +			chandef.width = NL80211_CHAN_WIDTH_20_NOHT; +			chandef.center_freq1 = chandef.chan->center_freq; +		}  	} else if (local->tmp_channel) { -		chan = local->tmp_channel; -		channel_type = NL80211_CHAN_NO_HT; -	} else { -		chan = local->_oper_channel; -		channel_type = local->_oper_channel_type; -	} +		chandef.chan = local->tmp_channel; +		chandef.width = NL80211_CHAN_WIDTH_20_NOHT; +		chandef.center_freq1 = chandef.chan->center_freq; +	} else +		chandef = local->_oper_chandef; -	if (chan != local->_oper_channel || -	    channel_type != local->_oper_channel_type) +	WARN(!cfg80211_chandef_valid(&chandef), +	     "control:%d MHz width:%d center: %d/%d MHz", +	     chandef.chan->center_freq, chandef.width, +	     chandef.center_freq1, chandef.center_freq2); + +	if (!cfg80211_chandef_identical(&chandef, &local->_oper_chandef))  		local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;  	else  		local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;  	offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; -	if (offchannel_flag || chan != local->hw.conf.channel || -	    channel_type != local->hw.conf.channel_type) { -		local->hw.conf.channel = chan; -		local->hw.conf.channel_type = channel_type; +	if (offchannel_flag || +	    !cfg80211_chandef_identical(&local->hw.conf.chandef, +					&local->_oper_chandef)) { +		local->hw.conf.chandef = chandef;  		changed |= IEEE80211_CONF_CHANGE_CHANNEL;  	} @@ -147,10 +151,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)  		changed |= IEEE80211_CONF_CHANGE_SMPS;  	} -	scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) || -		   test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) || -		   test_bit(SCAN_HW_SCANNING, &local->scanning); -	power = chan->max_power; +	power = chandef.chan->max_power;  	rcu_read_lock();  	list_for_each_entry_rcu(sdata, &local->interfaces, list) { @@ -226,8 +227,6 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)  static void ieee80211_tasklet_handler(unsigned long data)  {  	struct ieee80211_local *local = (struct ieee80211_local *) data; -	struct sta_info *sta, *tmp; -	struct skb_eosp_msg_data *eosp_data;  	struct sk_buff *skb;  	while ((skb = skb_dequeue(&local->skb_queue)) || @@ -243,18 +242,6 @@ static void ieee80211_tasklet_handler(unsigned long data)  			skb->pkt_type = 0;  			ieee80211_tx_status(&local->hw, skb);  			break; -		case IEEE80211_EOSP_MSG: -			eosp_data = (void *)skb->cb; -			for_each_sta_info(local, eosp_data->sta, sta, tmp) { -				/* skip wrong virtual interface */ -				if (memcmp(eosp_data->iface, -					   sta->sdata->vif.addr, ETH_ALEN)) -					continue; -				clear_sta_flag(sta, WLAN_STA_SP); -				break; -			} -			dev_kfree_skb(skb); -			break;  		default:  			WARN(1, "mac80211: Packet is of unknown type %d\n",  			     skb->pkt_type); @@ -295,8 +282,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)  		   "Hardware restart was requested\n");  	/* use this reason, ieee80211_reconfig will unblock it */ -	ieee80211_stop_queues_by_reason(hw, -		IEEE80211_QUEUE_STOP_REASON_SUSPEND); +	ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_SUSPEND);  	/*  	 * Stop all Rx during the reconfig. We don't want state changes @@ -399,30 +386,6 @@ static int ieee80211_ifa6_changed(struct notifier_block *nb,  }  #endif -static int ieee80211_napi_poll(struct napi_struct *napi, int budget) -{ -	struct ieee80211_local *local = -		container_of(napi, struct ieee80211_local, napi); - -	return local->ops->napi_poll(&local->hw, budget); -} - -void ieee80211_napi_schedule(struct ieee80211_hw *hw) -{ -	struct ieee80211_local *local = hw_to_local(hw); - -	napi_schedule(&local->napi); -} -EXPORT_SYMBOL(ieee80211_napi_schedule); - -void ieee80211_napi_complete(struct ieee80211_hw *hw) -{ -	struct ieee80211_local *local = hw_to_local(hw); - -	napi_complete(&local->napi); -} -EXPORT_SYMBOL(ieee80211_napi_complete); -  /* There isn't a lot of sense in it, but you can transmit anything you like */  static const struct ieee80211_txrx_stypes  ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { @@ -501,6 +464,27 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {  	},  }; +static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = { +	.vht_cap_info = +		cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC | +			    IEEE80211_VHT_CAP_SHORT_GI_80 | +			    IEEE80211_VHT_CAP_SHORT_GI_160 | +			    IEEE80211_VHT_CAP_RXSTBC_1 | +			    IEEE80211_VHT_CAP_RXSTBC_2 | +			    IEEE80211_VHT_CAP_RXSTBC_3 | +			    IEEE80211_VHT_CAP_RXSTBC_4 | +			    IEEE80211_VHT_CAP_TXSTBC | +			    IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | +			    IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | +			    IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN | +			    IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | +			    IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK), +	.supp_mcs = { +		.rx_mcs_map = cpu_to_le16(~0), +		.tx_mcs_map = cpu_to_le16(~0), +	}, +}; +  static const u8 extended_capabilities[] = {  	0, 0, 0, 0, 0, 0, 0,  	WLAN_EXT_CAPA8_OPMODE_NOTIF, @@ -572,7 +556,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  	wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |  			   NL80211_FEATURE_SAE |  			   NL80211_FEATURE_HT_IBSS | -			   NL80211_FEATURE_VIF_TXPOWER; +			   NL80211_FEATURE_VIF_TXPOWER | +			   NL80211_FEATURE_USERSPACE_MPM;  	if (!ops->hw_scan)  		wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | @@ -607,8 +592,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  					 IEEE80211_RADIOTAP_MCS_HAVE_BW;  	local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |  					 IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; +	local->hw.uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; +	local->hw.uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;  	local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;  	wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; +	wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;  	INIT_LIST_HEAD(&local->interfaces); @@ -664,9 +652,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  	skb_queue_head_init(&local->skb_queue);  	skb_queue_head_init(&local->skb_queue_unreliable); -	/* init dummy netdev for use w/ NAPI */ -	init_dummy_netdev(&local->napi_dev); -  	ieee80211_led_names(local);  	ieee80211_roc_setup(local); @@ -683,6 +668,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	int channels, max_bitrates;  	bool supp_ht, supp_vht;  	netdev_features_t feature_whitelist; +	struct cfg80211_chan_def dflt_chandef = {};  	static const u32 cipher_suites[] = {  		/* keep WEP first, it may be removed below */  		WLAN_CIPHER_SUITE_WEP40, @@ -760,15 +746,19 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  		sband = local->hw.wiphy->bands[band];  		if (!sband)  			continue; -		if (!local->use_chanctx && !local->_oper_channel) { + +		if (!dflt_chandef.chan) { +			cfg80211_chandef_create(&dflt_chandef, +						&sband->channels[0], +						NL80211_CHAN_NO_HT);  			/* init channel we're on */ -			local->hw.conf.channel = -			local->_oper_channel = &sband->channels[0]; -			local->hw.conf.channel_type = NL80211_CHAN_NO_HT; +			if (!local->use_chanctx && !local->_oper_chandef.chan) { +				local->hw.conf.chandef = dflt_chandef; +				local->_oper_chandef = dflt_chandef; +			} +			local->monitor_chandef = dflt_chandef;  		} -		cfg80211_chandef_create(&local->monitor_chandef, -					&sband->channels[0], -					NL80211_CHAN_NO_HT); +  		channels += sband->n_channels;  		if (max_bitrates < sband->n_bitrates) @@ -851,22 +841,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	if (supp_ht)  		local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap); -	if (supp_vht) { +	if (supp_vht)  		local->scan_ies_len +=  			2 + sizeof(struct ieee80211_vht_cap); -		/* -		 * (for now at least), drivers wanting to use VHT must -		 * support channel contexts, as they contain all the -		 * necessary VHT information and the global hw config -		 * doesn't (yet) -		 */ -		if (WARN_ON(!local->use_chanctx)) { -			result = -EINVAL; -			goto fail_wiphy_register; -		} -	} -  	if (!local->ops->hw_scan) {  		/* For hw_scan, driver needs to set these up. */  		local->hw.wiphy->max_scan_ssids = 4; @@ -1021,9 +999,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  		goto fail_ifa6;  #endif -	netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll, -			local->hw.napi_weight); -  	return 0;  #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 4749b385869..6952760881c 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -13,10 +13,6 @@  #include "ieee80211_i.h"  #include "mesh.h" -#define TMR_RUNNING_HK	0 -#define TMR_RUNNING_MP	1 -#define TMR_RUNNING_MPR	2 -  static int mesh_allocated;  static struct kmem_cache *rm_cache; @@ -50,11 +46,6 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)  	set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); -	if (local->quiescing) { -		set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); -		return; -	} -  	ieee80211_queue_work(&local->hw, &sdata->work);  } @@ -165,7 +156,7 @@ void mesh_sta_cleanup(struct sta_info *sta)  	 * an update.  	 */  	changed = mesh_accept_plinks_update(sdata); -	if (sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { +	if (!sdata->u.mesh.user_mpm) {  		changed |= mesh_plink_deactivate(sta);  		del_timer_sync(&sta->plink_timer);  	} @@ -479,15 +470,8 @@ static void ieee80211_mesh_path_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; -	struct ieee80211_local *local = sdata->local; - -	if (local->quiescing) { -		set_bit(TMR_RUNNING_MP, &ifmsh->timers_running); -		return; -	} -	ieee80211_queue_work(&local->hw, &sdata->work); +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  }  static void ieee80211_mesh_path_root_timer(unsigned long data) @@ -495,16 +479,10 @@ static void ieee80211_mesh_path_root_timer(unsigned long data)  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data;  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; -	struct ieee80211_local *local = sdata->local;  	set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags); -	if (local->quiescing) { -		set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running); -		return; -	} - -	ieee80211_queue_work(&local->hw, &sdata->work); +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  }  void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh) @@ -622,35 +600,6 @@ static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata)  		  round_jiffies(TU_TO_EXP_TIME(interval)));  } -#ifdef CONFIG_PM -void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - -	/* use atomic bitops in case all timers fire at the same time */ - -	if (del_timer_sync(&ifmsh->housekeeping_timer)) -		set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); -	if (del_timer_sync(&ifmsh->mesh_path_timer)) -		set_bit(TMR_RUNNING_MP, &ifmsh->timers_running); -	if (del_timer_sync(&ifmsh->mesh_path_root_timer)) -		set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running); -} - -void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - -	if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running)) -		add_timer(&ifmsh->housekeeping_timer); -	if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running)) -		add_timer(&ifmsh->mesh_path_timer); -	if (test_and_clear_bit(TMR_RUNNING_MPR, &ifmsh->timers_running)) -		add_timer(&ifmsh->mesh_path_root_timer); -	ieee80211_mesh_root_setup(ifmsh); -} -#endif -  static int  ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)  { @@ -750,10 +699,8 @@ out_free:  static int  ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh)  { -	struct ieee80211_sub_if_data *sdata;  	struct beacon_data *old_bcn;  	int ret; -	sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);  	mutex_lock(&ifmsh->mtx); @@ -871,8 +818,6 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)  	local->fif_other_bss--;  	atomic_dec(&local->iff_allmultis);  	ieee80211_configure_filter(local); - -	sdata->u.mesh.timers_running = 0;  }  static void @@ -886,15 +831,14 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_mgmt *hdr;  	struct ieee802_11_elems elems;  	size_t baselen; -	u8 *pos, *end; +	u8 *pos; -	end = ((u8 *) mgmt) + len;  	pos = mgmt->u.probe_req.variable;  	baselen = (u8 *) pos - (u8 *) mgmt;  	if (baselen > len)  		return; -	ieee802_11_parse_elems(pos, len - baselen, &elems); +	ieee802_11_parse_elems(pos, len - baselen, false, &elems);  	/* 802.11-2012 10.1.4.3.2 */  	if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) && @@ -955,7 +899,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,  		return;  	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, -			       &elems); +			       false, &elems);  	/* ignore non-mesh or secure / unsecure mismatch */  	if ((!elems.mesh_id || !elems.mesh_config) || @@ -963,7 +907,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,  	    (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))  		return; -	if (elems.ds_params && elems.ds_params_len == 1) +	if (elems.ds_params)  		freq = ieee80211_channel_to_frequency(elems.ds_params[0], band);  	else  		freq = rx_status->freq; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 336c88a1668..da158774eeb 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -275,7 +275,8 @@ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);  void mesh_path_expire(struct ieee80211_sub_if_data *sdata);  void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,  			    struct ieee80211_mgmt *mgmt, size_t len); -int mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst); +struct mesh_path * +mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst);  int mesh_path_add_gate(struct mesh_path *mpath);  int mesh_path_send_to_gates(struct mesh_path *mpath); @@ -313,8 +314,6 @@ void mesh_path_timer(unsigned long data);  void mesh_path_flush_by_nexthop(struct sta_info *sta);  void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata,  			     struct sk_buff *skb); -void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); -void mesh_path_restart(struct ieee80211_sub_if_data *sdata);  void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);  bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); @@ -359,22 +358,12 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)  void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); -void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); -void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata); -void mesh_plink_quiesce(struct sta_info *sta); -void mesh_plink_restart(struct sta_info *sta);  void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata);  void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata);  void ieee80211s_stop(void);  #else  static inline void  ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {} -static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) -{} -static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata) -{} -static inline void mesh_plink_quiesce(struct sta_info *sta) {} -static inline void mesh_plink_restart(struct sta_info *sta) {}  static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)  { return false; }  static inline void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index bdb8d3b1458..486819cd02c 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -144,7 +144,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,  		*pos++ = WLAN_EID_PREQ;  		break;  	case MPATH_PREP: -		mhwmp_dbg(sdata, "sending PREP to %pM\n", target); +		mhwmp_dbg(sdata, "sending PREP to %pM\n", orig_addr);  		ie_len = 31;  		pos = skb_put(skb, 2 + ie_len);  		*pos++ = WLAN_EID_PREP; @@ -445,9 +445,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  				}  			}  		} else { -			mesh_path_add(sdata, orig_addr); -			mpath = mesh_path_lookup(sdata, orig_addr); -			if (!mpath) { +			mpath = mesh_path_add(sdata, orig_addr); +			if (IS_ERR(mpath)) {  				rcu_read_unlock();  				return 0;  			} @@ -486,9 +485,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  					(last_hop_metric > mpath->metric)))  				fresh_info = false;  		} else { -			mesh_path_add(sdata, ta); -			mpath = mesh_path_lookup(sdata, ta); -			if (!mpath) { +			mpath = mesh_path_add(sdata, ta); +			if (IS_ERR(mpath)) {  				rcu_read_unlock();  				return 0;  			} @@ -661,7 +659,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,  	u32 target_sn, orig_sn, lifetime;  	mhwmp_dbg(sdata, "received PREP from %pM\n", -		  PREP_IE_ORIG_ADDR(prep_elem)); +		  PREP_IE_TARGET_ADDR(prep_elem));  	orig_addr = PREP_IE_ORIG_ADDR(prep_elem);  	if (ether_addr_equal(orig_addr, sdata->vif.addr)) @@ -804,9 +802,8 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,  	mpath = mesh_path_lookup(sdata, orig_addr);  	if (!mpath) { -		mesh_path_add(sdata, orig_addr); -		mpath = mesh_path_lookup(sdata, orig_addr); -		if (!mpath) { +		mpath = mesh_path_add(sdata, orig_addr); +		if (IS_ERR(mpath)) {  			rcu_read_unlock();  			sdata->u.mesh.mshstats.dropped_frames_no_route++;  			return; @@ -883,7 +880,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,  	baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;  	ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, -			len - baselen, &elems); +			       len - baselen, false, &elems);  	if (elems.preq) {  		if (elems.preq_len != 37) @@ -1098,11 +1095,10 @@ int mesh_nexthop_resolve(struct ieee80211_sub_if_data *sdata,  	/* no nexthop found, start resolving */  	mpath = mesh_path_lookup(sdata, target_addr);  	if (!mpath) { -		mesh_path_add(sdata, target_addr); -		mpath = mesh_path_lookup(sdata, target_addr); -		if (!mpath) { +		mpath = mesh_path_add(sdata, target_addr); +		if (IS_ERR(mpath)) {  			mesh_path_discard_frame(sdata, skb); -			err = -ENOSPC; +			err = PTR_ERR(mpath);  			goto endlookup;  		}  	} diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index dc7c8df40c2..89aacfd2756 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -493,7 +493,8 @@ int mesh_gate_num(struct ieee80211_sub_if_data *sdata)   *   * State: the initial state of the new path is set to 0   */ -int mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst) +struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, +				const u8 *dst)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct ieee80211_local *local = sdata->local; @@ -502,18 +503,33 @@ int mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst)  	struct mpath_node *node, *new_node;  	struct hlist_head *bucket;  	int grow = 0; -	int err = 0; +	int err;  	u32 hash_idx;  	if (ether_addr_equal(dst, sdata->vif.addr))  		/* never add ourselves as neighbours */ -		return -ENOTSUPP; +		return ERR_PTR(-ENOTSUPP);  	if (is_multicast_ether_addr(dst)) -		return -ENOTSUPP; +		return ERR_PTR(-ENOTSUPP);  	if (atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS) == 0) -		return -ENOSPC; +		return ERR_PTR(-ENOSPC); + +	read_lock_bh(&pathtbl_resize_lock); +	tbl = resize_dereference_mesh_paths(); + +	hash_idx = mesh_table_hash(dst, sdata, tbl); +	bucket = &tbl->hash_buckets[hash_idx]; + +	spin_lock(&tbl->hashwlock[hash_idx]); + +	hlist_for_each_entry(node, bucket, list) { +		mpath = node->mpath; +		if (mpath->sdata == sdata && +		    ether_addr_equal(dst, mpath->dst)) +			goto found; +	}  	err = -ENOMEM;  	new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); @@ -524,7 +540,6 @@ int mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst)  	if (!new_node)  		goto err_node_alloc; -	read_lock_bh(&pathtbl_resize_lock);  	memcpy(new_mpath->dst, dst, ETH_ALEN);  	eth_broadcast_addr(new_mpath->rann_snd_addr);  	new_mpath->is_root = false; @@ -538,21 +553,6 @@ int mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst)  	spin_lock_init(&new_mpath->state_lock);  	init_timer(&new_mpath->timer); -	tbl = resize_dereference_mesh_paths(); - -	hash_idx = mesh_table_hash(dst, sdata, tbl); -	bucket = &tbl->hash_buckets[hash_idx]; - -	spin_lock(&tbl->hashwlock[hash_idx]); - -	err = -EEXIST; -	hlist_for_each_entry(node, bucket, list) { -		mpath = node->mpath; -		if (mpath->sdata == sdata && -		    ether_addr_equal(dst, mpath->dst)) -			goto err_exists; -	} -  	hlist_add_head_rcu(&new_node->list, bucket);  	if (atomic_inc_return(&tbl->entries) >=  	    tbl->mean_chain_len * (tbl->hash_mask + 1)) @@ -560,23 +560,23 @@ int mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst)  	mesh_paths_generation++; -	spin_unlock(&tbl->hashwlock[hash_idx]); -	read_unlock_bh(&pathtbl_resize_lock);  	if (grow) {  		set_bit(MESH_WORK_GROW_MPATH_TABLE,  &ifmsh->wrkq_flags);  		ieee80211_queue_work(&local->hw, &sdata->work);  	} -	return 0; - -err_exists: +	mpath = new_mpath; +found:  	spin_unlock(&tbl->hashwlock[hash_idx]);  	read_unlock_bh(&pathtbl_resize_lock); -	kfree(new_node); +	return mpath; +  err_node_alloc:  	kfree(new_mpath);  err_path_alloc:  	atomic_dec(&sdata->u.mesh.mpaths); -	return err; +	spin_unlock(&tbl->hashwlock[hash_idx]); +	read_unlock_bh(&pathtbl_resize_lock); +	return ERR_PTR(err);  }  static void mesh_table_free_rcu(struct rcu_head *rcu) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 07d396d5707..09bebed9941 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -420,7 +420,6 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)  		return NULL;  	sta->plink_state = NL80211_PLINK_LISTEN; -	init_timer(&sta->plink_timer);  	sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);  	sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); @@ -437,8 +436,9 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,  {  	struct sta_info *sta = NULL; -	/* Userspace handles peer allocation when security is enabled */ -	if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) +	/* Userspace handles station allocation */ +	if (sdata->u.mesh.user_mpm || +	    sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)  		cfg80211_notify_new_peer_candidate(sdata->dev, addr,  						   elems->ie_start,  						   elems->total_len, @@ -534,10 +534,8 @@ static void mesh_plink_timer(unsigned long data)  	 */  	sta = (struct sta_info *) data; -	if (sta->sdata->local->quiescing) { -		sta->plink_timer_was_running = true; +	if (sta->sdata->local->quiescing)  		return; -	}  	spin_lock_bh(&sta->lock);  	if (sta->ignore_plink_timer) { @@ -546,8 +544,8 @@ static void mesh_plink_timer(unsigned long data)  		return;  	}  	mpl_dbg(sta->sdata, -		"Mesh plink timer for %pM fired on state %d\n", -		sta->sta.addr, sta->plink_state); +		"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; @@ -598,29 +596,6 @@ static void mesh_plink_timer(unsigned long data)  	}  } -#ifdef CONFIG_PM -void mesh_plink_quiesce(struct sta_info *sta) -{ -	if (!ieee80211_vif_is_mesh(&sta->sdata->vif)) -		return; - -	/* no kernel mesh sta timers have been initialized */ -	if (sta->sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) -		return; - -	if (del_timer_sync(&sta->plink_timer)) -		sta->plink_timer_was_running = true; -} - -void mesh_plink_restart(struct sta_info *sta) -{ -	if (sta->plink_timer_was_running) { -		add_timer(&sta->plink_timer); -		sta->plink_timer_was_running = false; -	} -} -#endif -  static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)  {  	sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); @@ -695,6 +670,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,  	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"); @@ -708,7 +687,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,  		baseaddr += 4;  		baselen += 4;  	} -	ieee802_11_parse_elems(baseaddr, len - baselen, &elems); +	ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);  	if (!elems.peering) {  		mpl_dbg(sdata, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 346ad4cfb01..29620bfc7a6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -56,7 +56,10 @@ MODULE_PARM_DESC(max_probe_tries,   * probe on beacon miss before declaring the connection lost   * default to what we want.   */ -#define IEEE80211_BEACON_LOSS_COUNT	7 +static int beacon_loss_count = 7; +module_param(beacon_loss_count, int, 0644); +MODULE_PARM_DESC(beacon_loss_count, +		 "Number of beacon intervals before we decide beacon was lost.");  /*   * Time the connection can be idle before we probe @@ -87,9 +90,6 @@ MODULE_PARM_DESC(probe_wait_ms,   */  #define IEEE80211_SIGNAL_AVE_MIN_COUNT	4 -#define TMR_RUNNING_TIMER	0 -#define TMR_RUNNING_CHANSW	1 -  /*   * All cfg80211 functions have to be called outside a locked   * section so that they can acquire a lock themselves... This @@ -289,6 +289,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  	} else {  		/* 40 MHz (and 80 MHz) must be supported for VHT */  		ret = IEEE80211_STA_DISABLE_VHT; +		/* also mark 40 MHz disabled */ +		ret |= IEEE80211_STA_DISABLE_40MHZ;  		goto out;  	} @@ -303,12 +305,6 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  					       channel->band);  	vht_chandef.center_freq2 = 0; -	if (vht_oper->center_freq_seg2_idx) -		vht_chandef.center_freq2 = -			ieee80211_channel_to_frequency( -				vht_oper->center_freq_seg2_idx, -				channel->band); -  	switch (vht_oper->chan_width) {  	case IEEE80211_VHT_CHANWIDTH_USE_HT:  		vht_chandef.width = chandef->width; @@ -321,6 +317,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  		break;  	case IEEE80211_VHT_CHANWIDTH_80P80MHZ:  		vht_chandef.width = NL80211_CHAN_WIDTH_80P80; +		vht_chandef.center_freq2 = +			ieee80211_channel_to_frequency( +				vht_oper->center_freq_seg2_idx, +				channel->band);  		break;  	default:  		if (verbose) @@ -604,11 +604,11 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,  	u8 *pos;  	u32 cap;  	struct ieee80211_sta_vht_cap vht_cap; -	int i;  	BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));  	memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); +	ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);  	/* determine capability flags */  	cap = vht_cap.cap; @@ -631,37 +631,6 @@ 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; -	if (!(ap_vht_cap->vht_cap_info & -			cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC))) -		cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 | -			 IEEE80211_VHT_CAP_RXSTBC_3 | -			 IEEE80211_VHT_CAP_RXSTBC_4); - -	for (i = 0; i < 8; i++) { -		int shift = i * 2; -		u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift; -		u16 ap_mcs, our_mcs; - -		ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) & -								mask) >> shift; -		our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) & -								mask) >> shift; - -		if (our_mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) -			continue; - -		switch (ap_mcs) { -		default: -			if (our_mcs <= ap_mcs) -				break; -			/* fall through */ -		case IEEE80211_VHT_MCS_NOT_SUPPORTED: -			vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask); -			vht_cap.vht_mcs.rx_mcs_map |= -				cpu_to_le16(ap_mcs << shift); -		} -	} -  	/* reserve and fill IE */  	pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);  	ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); @@ -987,6 +956,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)  {  	struct ieee80211_sub_if_data *sdata =  		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;  	if (!ieee80211_sdata_running(sdata)) @@ -996,21 +966,22 @@ static void ieee80211_chswitch_work(struct work_struct *work)  	if (!ifmgd->associated)  		goto out; -	sdata->local->_oper_channel = sdata->local->csa_channel; -	if (!sdata->local->ops->channel_switch) { +	local->_oper_chandef = local->csa_chandef; + +	if (!local->ops->channel_switch) {  		/* call "hw_config" only if doing sw channel switch */ -		ieee80211_hw_config(sdata->local, -			IEEE80211_CONF_CHANGE_CHANNEL); +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);  	} else {  		/* update the device channel directly */ -		sdata->local->hw.conf.channel = sdata->local->_oper_channel; +		local->hw.conf.chandef = local->_oper_chandef;  	}  	/* XXX: shouldn't really modify cfg80211-owned data! */ -	ifmgd->associated->channel = sdata->local->_oper_channel; +	ifmgd->associated->channel = local->_oper_chandef.chan;  	/* XXX: wait for a beacon first? */ -	ieee80211_wake_queues_by_reason(&sdata->local->hw, +	ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_CSA);   out:  	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; @@ -1038,66 +1009,197 @@ static void ieee80211_chswitch_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	if (sdata->local->quiescing) { -		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); -		return; -	} - -	ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); +	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);  } -void +static void  ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, -				 const struct ieee80211_channel_sw_ie *sw_elem, -				 struct ieee80211_bss *bss, u64 timestamp) +				 u64 timestamp, struct ieee802_11_elems *elems)  { -	struct cfg80211_bss *cbss = -		container_of((void *)bss, struct cfg80211_bss, priv); -	struct ieee80211_channel *new_ch; +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num, -						      cbss->channel->band); +	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; +	int secondary_channel_offset = -1;  	ASSERT_MGD_MTX(ifmgd); -	if (!ifmgd->associated) +	if (!cbss)  		return; -	if (sdata->local->scanning) +	if (local->scanning)  		return; -	/* Disregard subsequent beacons if we are already running a timer -	   processing a CSA */ - +	/* disregard subsequent announcements if we are already processing */  	if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)  		return; -	new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); -	if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { +	sec_chan_offs = elems->sec_chan_offs; +	wide_bw_chansw_ie = elems->wide_bw_chansw_ie; + +	if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | +			    IEEE80211_STA_DISABLE_40MHZ)) { +		sec_chan_offs = NULL; +		wide_bw_chansw_ie = 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 */ +		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) {  		sdata_info(sdata,  			   "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",  			   ifmgd->associated->bssid, new_freq); -		ieee80211_queue_work(&sdata->local->hw, +		ieee80211_queue_work(&local->hw, +				     &ifmgd->csa_connection_drop_work); +		return; +	} + +	if (sec_chan_offs) { +		secondary_channel_offset = sec_chan_offs->sec_chan_offs; +	} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { +		/* if HT is enabled and the IE not present, it's still HT */ +		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); +		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 (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)) { +			sdata_info(sdata, +				   "AP %pM CSA has inconsistent channel data, disconnecting\n", +				   ifmgd->associated->bssid); +			ieee80211_queue_work(&local->hw, +					     &ifmgd->csa_connection_drop_work); +			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 (sdata->local->use_chanctx) { +	if (local->use_chanctx) {  		sdata_info(sdata,  			   "not handling channel switch with channel contexts\n"); -		ieee80211_queue_work(&sdata->local->hw, +		ieee80211_queue_work(&local->hw,  				     &ifmgd->csa_connection_drop_work);  		return;  	} -	mutex_lock(&sdata->local->chanctx_mtx); +	mutex_lock(&local->chanctx_mtx);  	if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { -		mutex_unlock(&sdata->local->chanctx_mtx); +		mutex_unlock(&local->chanctx_mtx);  		return;  	}  	chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), @@ -1105,39 +1207,39 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,  	if (chanctx->refcount > 1) {  		sdata_info(sdata,  			   "channel switch with multiple interfaces on the same channel, disconnecting\n"); -		ieee80211_queue_work(&sdata->local->hw, +		ieee80211_queue_work(&local->hw,  				     &ifmgd->csa_connection_drop_work); -		mutex_unlock(&sdata->local->chanctx_mtx); +		mutex_unlock(&local->chanctx_mtx);  		return;  	} -	mutex_unlock(&sdata->local->chanctx_mtx); +	mutex_unlock(&local->chanctx_mtx); -	sdata->local->csa_channel = new_ch; +	local->csa_chandef = new_chandef; -	if (sw_elem->mode) -		ieee80211_stop_queues_by_reason(&sdata->local->hw, +	if (mode) +		ieee80211_stop_queues_by_reason(&local->hw, +				IEEE80211_MAX_QUEUE_MAP,  				IEEE80211_QUEUE_STOP_REASON_CSA); -	if (sdata->local->ops->channel_switch) { +	if (local->ops->channel_switch) {  		/* use driver's channel switch callback */  		struct ieee80211_channel_switch ch_switch = {  			.timestamp = timestamp, -			.block_tx = sw_elem->mode, -			.channel = new_ch, -			.count = sw_elem->count, +			.block_tx = mode, +			.chandef = new_chandef, +			.count = count,  		}; -		drv_channel_switch(sdata->local, &ch_switch); +		drv_channel_switch(local, &ch_switch);  		return;  	}  	/* channel switch handled in software */ -	if (sw_elem->count <= 1) -		ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); +	if (count <= 1) +		ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);  	else  		mod_timer(&ifmgd->chswitch_timer, -			  TU_TO_EXP_TIME(sw_elem->count * -					 cbss->beacon_interval)); +			  TU_TO_EXP_TIME(count * cbss->beacon_interval));  }  static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, @@ -1383,6 +1485,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)  	}  	ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_PS);  } @@ -1435,16 +1538,14 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)  	if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&  	    !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { -		netif_tx_stop_all_queues(sdata->dev); - -		if (drv_tx_frames_pending(local)) +		if (drv_tx_frames_pending(local)) {  			mod_timer(&local->dynamic_ps_timer, jiffies +  				  msecs_to_jiffies(  				  local->hw.conf.dynamic_ps_timeout)); -		else { +		} else {  			ieee80211_send_nullfunc(local, sdata, 1);  			/* Flush to get the tx status of nullfunc frame */ -			drv_flush(local, false); +			ieee80211_flush_queues(local, sdata);  		}  	} @@ -1455,9 +1556,6 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)  		local->hw.conf.flags |= IEEE80211_CONF_PS;  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);  	} - -	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) -		netif_tx_wake_all_queues(sdata->dev);  }  void ieee80211_dynamic_ps_timer(unsigned long data) @@ -1563,6 +1661,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,  		params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);  		params.cw_min = ecw2cw(pos[1] & 0x0f);  		params.txop = get_unaligned_le16(pos + 2); +		params.acm = acm;  		params.uapsd = uapsd;  		mlme_dbg(sdata, @@ -1650,7 +1749,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,  		bss_conf->assoc_capability, bss->has_erp_value, bss->erp_value);  	sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec( -		IEEE80211_BEACON_LOSS_COUNT * bss_conf->beacon_int)); +		beacon_loss_count * bss_conf->beacon_int));  	sdata->u.mgd.associated = cbss;  	memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); @@ -1663,18 +1762,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,  		rcu_read_lock();  		ies = rcu_dereference(cbss->ies);  		if (ies) { -			u8 noa[2];  			int ret;  			ret = cfg80211_get_p2p_attr(  					ies->data, ies->len,  					IEEE80211_P2P_ATTR_ABSENCE_NOTICE, -					noa, sizeof(noa)); +					(u8 *) &bss_conf->p2p_noa_attr, +					sizeof(bss_conf->p2p_noa_attr));  			if (ret >= 2) { -				bss_conf->p2p_oppps = noa[1] & 0x80; -				bss_conf->p2p_ctwindow = noa[1] & 0x7f; +				sdata->u.mgd.p2p_noa_index = +					bss_conf->p2p_noa_attr.index;  				bss_info_changed |= BSS_CHANGED_P2P_PS; -				sdata->u.mgd.p2p_noa_index = noa[0];  			}  		}  		rcu_read_unlock(); @@ -1718,7 +1816,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,  	ieee80211_recalc_smps(sdata);  	ieee80211_recalc_ps_vif(sdata); -	netif_tx_start_all_queues(sdata->dev);  	netif_carrier_on(sdata->dev);  } @@ -1741,22 +1838,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	ieee80211_stop_poll(sdata);  	ifmgd->associated = NULL; - -	/* -	 * we need to commit the associated = NULL change because the -	 * scan code uses that to determine whether this iface should -	 * go to/wake up from powersave or not -- and could otherwise -	 * wake the queues erroneously. -	 */ -	smp_mb(); - -	/* -	 * Thus, we can only afterwards stop the queues -- to account -	 * for the case where another CPU is finishing a scan at this -	 * time -- we don't want the scan code to enable queues. -	 */ - -	netif_tx_stop_all_queues(sdata->dev);  	netif_carrier_off(sdata->dev);  	/* @@ -1775,7 +1856,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	/* flush out any pending frame (e.g. DELBA) before deauth/disassoc */  	if (tx) -		drv_flush(local, false); +		ieee80211_flush_queues(local, sdata);  	/* deauthenticate/disassociate now */  	if (tx || frame_buf) @@ -1784,7 +1865,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	/* flush out frame */  	if (tx) -		drv_flush(local, false); +		ieee80211_flush_queues(local, sdata);  	/* clear bssid only after building the needed mgmt frames */  	memset(ifmgd->bssid, 0, ETH_ALEN); @@ -1799,12 +1880,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	changed |= BSS_CHANGED_ASSOC;  	sdata->vif.bss_conf.assoc = false; -	sdata->vif.bss_conf.p2p_ctwindow = 0; -	sdata->vif.bss_conf.p2p_oppps = false; +	ifmgd->p2p_noa_index = -1; +	memset(&sdata->vif.bss_conf.p2p_noa_attr, 0, +	       sizeof(sdata->vif.bss_conf.p2p_noa_attr)); -	/* on the next assoc, re-program HT parameters */ +	/* on the next assoc, re-program HT/VHT parameters */  	memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));  	memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); +	memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa)); +	memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));  	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; @@ -1830,8 +1914,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	del_timer_sync(&sdata->u.mgd.timer);  	del_timer_sync(&sdata->u.mgd.chswitch_timer); -	sdata->u.mgd.timers_running = 0; -  	sdata->vif.bss_conf.dtim_period = 0;  	ifmgd->flags = 0; @@ -1956,7 +2038,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)  	ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);  	run_again(ifmgd, ifmgd->probe_timeout);  	if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) -		drv_flush(sdata->local, false); +		ieee80211_flush_queues(sdata->local, sdata);  }  static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, @@ -1980,12 +2062,15 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,  		goto out;  	} -	if (beacon) +	if (beacon) {  		mlme_dbg_ratelimited(sdata, -				     "detected beacon loss from AP - probing\n"); +				     "detected beacon loss from AP (missed %d beacons) - probing\n", +				     beacon_loss_count); -	ieee80211_cqm_rssi_notify(&sdata->vif, -		NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL); +		ieee80211_cqm_rssi_notify(&sdata->vif, +					  NL80211_CQM_RSSI_BEACON_LOSS_EVENT, +					  GFP_KERNEL); +	}  	/*  	 * The driver/our work has already reported this event or the @@ -2079,6 +2164,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)  			       true, frame_buf);  	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;  	ieee80211_wake_queues_by_reason(&sdata->local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_CSA);  	mutex_unlock(&ifmgd->mtx); @@ -2130,7 +2216,6 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)  	trace_api_beacon_loss(sdata); -	WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);  	sdata->u.mgd.connection_loss = false;  	ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);  } @@ -2180,7 +2265,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,  	u32 tx_flags = 0;  	pos = mgmt->u.auth.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);  	if (!elems.challenge)  		return;  	auth_data->expected_transaction = 4; @@ -2445,7 +2530,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,  	}  	pos = mgmt->u.assoc_resp.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);  	if (!elems.supp_rates) {  		sdata_info(sdata, "no SuppRates element in AssocResp\n"); @@ -2614,13 +2699,13 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,  		   capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));  	pos = mgmt->u.assoc_resp.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);  	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && -	    elems.timeout_int && elems.timeout_int_len == 5 && -	    elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { +	    elems.timeout_int && +	    elems.timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {  		u32 tu, ms; -		tu = get_unaligned_le32(elems.timeout_int + 1); +		tu = le32_to_cpu(elems.timeout_int->value);  		ms = tu * 1024 / 1000;  		sdata_info(sdata,  			   "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n", @@ -2669,6 +2754,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_channel *channel;  	bool need_ps = false; +	lockdep_assert_held(&sdata->u.mgd.mtx); +  	if ((sdata->u.mgd.associated &&  	     ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||  	    (sdata->u.mgd.assoc_data && @@ -2683,7 +2770,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  		}  	} -	if (elems->ds_params && elems->ds_params_len == 1) +	if (elems->ds_params)  		freq = ieee80211_channel_to_frequency(elems->ds_params[0],  						      rx_status->band);  	else @@ -2699,7 +2786,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  	if (bss)  		ieee80211_rx_bss_put(local, bss); -	if (!sdata->u.mgd.associated) +	if (!sdata->u.mgd.associated || +	    !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid))  		return;  	if (need_ps) { @@ -2708,10 +2796,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  		mutex_unlock(&local->iflist_mtx);  	} -	if (elems->ch_switch_ie && -	    memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0) -		ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie, -						 bss, rx_status->mactime); +	ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); +  } @@ -2736,7 +2822,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,  		return;  	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, -				&elems); +			       false, &elems);  	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); @@ -2819,7 +2905,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&  	    ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {  		ieee802_11_parse_elems(mgmt->u.beacon.variable, -				       len - baselen, &elems); +				       len - baselen, false, &elems);  		ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);  		ifmgd->assoc_data->have_beacon = true; @@ -2929,7 +3015,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);  	ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, -					  len - baselen, &elems, +					  len - baselen, false, &elems,  					  care_about_ies, ncrc);  	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { @@ -2961,22 +3047,30 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	}  	if (sdata->vif.p2p) { -		u8 noa[2]; +		struct ieee80211_p2p_noa_attr noa = {};  		int ret;  		ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,  					    len - baselen,  					    IEEE80211_P2P_ATTR_ABSENCE_NOTICE, -					    noa, sizeof(noa)); -		if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) { -			bss_conf->p2p_oppps = noa[1] & 0x80; -			bss_conf->p2p_ctwindow = noa[1] & 0x7f; +					    (u8 *) &noa, sizeof(noa)); +		if (ret >= 2) { +			if (sdata->u.mgd.p2p_noa_index != noa.index) { +				/* valid noa_attr and index changed */ +				sdata->u.mgd.p2p_noa_index = noa.index; +				memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa)); +				changed |= BSS_CHANGED_P2P_PS; +				/* +				 * make sure we update all information, the CRC +				 * mechanism doesn't look at P2P attributes. +				 */ +				ifmgd->beacon_crc_valid = false; +			} +		} else if (sdata->u.mgd.p2p_noa_index != -1) { +			/* noa_attr not found and we had valid noa_attr before */ +			sdata->u.mgd.p2p_noa_index = -1; +			memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr));  			changed |= BSS_CHANGED_P2P_PS; -			sdata->u.mgd.p2p_noa_index = noa[0]; -			/* -			 * make sure we update all information, the CRC -			 * mechanism doesn't look at P2P attributes. -			 */  			ifmgd->beacon_crc_valid = false;  		}  	} @@ -3018,7 +3112,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  		changed |= BSS_CHANGED_DTIM_PERIOD;  	} -	if (elems.erp_info && elems.erp_info_len >= 1) { +	if (elems.erp_info) {  		erp_valid = true;  		erp_value = elems.erp_info[0];  	} else { @@ -3068,6 +3162,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  	enum rx_mgmt_action rma = RX_MGMT_NONE;  	u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];  	u16 fc; +	struct ieee802_11_elems elems; +	int ies_len;  	rx_status = (struct ieee80211_rx_status *) skb->cb;  	mgmt = (struct ieee80211_mgmt *) skb->data; @@ -3097,14 +3193,48 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  		rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss);  		break;  	case IEEE80211_STYPE_ACTION: -		switch (mgmt->u.action.category) { -		case WLAN_CATEGORY_SPECTRUM_MGMT: +		if (mgmt->u.action.category == 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_sta_process_chanswitch(sdata, -					&mgmt->u.action.u.chan_switch.sw_elem, -					(void *)ifmgd->associated->priv, -					rx_status->mactime); -			break; +							 rx_status->mactime, +							 &elems); +		} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { +			ies_len = skb->len - +				  offsetof(struct ieee80211_mgmt, +					   u.action.u.ext_chan_switch.variable); + +			if (ies_len < 0) +				break; + +			ieee802_11_parse_elems( +				mgmt->u.action.u.ext_chan_switch.variable, +				ies_len, true, &elems); + +			if (elems.parse_error) +				break; + +			/* for the handling code pretend this was also an IE */ +			elems.ext_chansw_ie = +				&mgmt->u.action.u.ext_chan_switch.data; + +			ieee80211_sta_process_chanswitch(sdata, +							 rx_status->mactime, +							 &elems);  		} +		break;  	}  	mutex_unlock(&ifmgd->mtx); @@ -3140,15 +3270,8 @@ static void ieee80211_sta_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	struct ieee80211_local *local = sdata->local; -	if (local->quiescing) { -		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); -		return; -	} - -	ieee80211_queue_work(&local->hw, &sdata->work); +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  }  static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, @@ -3500,72 +3623,6 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)  	}  } -#ifdef CONFIG_PM -void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - -	/* -	 * Stop timers before deleting work items, as timers -	 * could race and re-add the work-items. They will be -	 * re-established on connection. -	 */ -	del_timer_sync(&ifmgd->conn_mon_timer); -	del_timer_sync(&ifmgd->bcn_mon_timer); - -	/* -	 * we need to use atomic bitops for the running bits -	 * only because both timers might fire at the same -	 * time -- the code here is properly synchronised. -	 */ - -	cancel_work_sync(&ifmgd->request_smps_work); - -	cancel_work_sync(&ifmgd->monitor_work); -	cancel_work_sync(&ifmgd->beacon_connection_loss_work); -	cancel_work_sync(&ifmgd->csa_connection_drop_work); -	if (del_timer_sync(&ifmgd->timer)) -		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); - -	if (del_timer_sync(&ifmgd->chswitch_timer)) -		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); -	cancel_work_sync(&ifmgd->chswitch_work); -} - -void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - -	mutex_lock(&ifmgd->mtx); -	if (!ifmgd->associated) { -		mutex_unlock(&ifmgd->mtx); -		return; -	} - -	if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { -		sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; -		mlme_dbg(sdata, "driver requested disconnect after resume\n"); -		ieee80211_sta_connection_lost(sdata, -					      ifmgd->associated->bssid, -					      WLAN_REASON_UNSPECIFIED, -					      true); -		mutex_unlock(&ifmgd->mtx); -		return; -	} -	mutex_unlock(&ifmgd->mtx); - -	if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running)) -		add_timer(&ifmgd->timer); -	if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running)) -		add_timer(&ifmgd->chswitch_timer); -	ieee80211_sta_reset_beacon_monitor(sdata); - -	mutex_lock(&sdata->local->mtx); -	ieee80211_restart_sta_timer(sdata); -	mutex_unlock(&sdata->local->mtx); -} -#endif -  /* interface setup */  void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)  { @@ -3590,8 +3647,9 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)  	ifmgd->flags = 0;  	ifmgd->powersave = sdata->wdev.ps; -	ifmgd->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; -	ifmgd->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; +	ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues; +	ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len; +	ifmgd->p2p_noa_index = -1;  	mutex_init(&ifmgd->mtx); @@ -4089,6 +4147,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;  	} +	if (req->flags & ASSOC_REQ_DISABLE_VHT) +		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; +  	/* 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 || @@ -4112,6 +4173,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,  	       sizeof(ifmgd->ht_capa_mask)); +	memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa)); +	memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask, +	       sizeof(ifmgd->vht_capa_mask)); +  	if (req->ie && req->ie_len) {  		memcpy(assoc_data->ie, req->ie, req->ie_len);  		assoc_data->ie_len = req->ie_len; @@ -4149,7 +4214,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	rcu_read_unlock();  	if (bss->wmm_used && bss->uapsd_supported && -	    (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { +	    (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) && +	    sdata->wmm_acm != 0xff) {  		assoc_data->uapsd = true;  		ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;  	} else { diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 430bd254e49..acd1f71adc0 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -118,9 +118,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)  	 * Stop queues and transmit all frames queued by the driver  	 * before sending nullfunc to enable powersave at the AP.  	 */ -	ieee80211_stop_queues_by_reason(&local->hw, +	ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); -	drv_flush(local, false); +	ieee80211_flush_queues(local, NULL);  	mutex_lock(&local->iflist_mtx);  	list_for_each_entry(sdata, &local->interfaces, list) { @@ -181,7 +181,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)  	}  	mutex_unlock(&local->iflist_mtx); -	ieee80211_wake_queues_by_reason(&local->hw, +	ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);  } @@ -277,7 +277,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)  			duration = 10;  		ret = drv_remain_on_channel(local, roc->sdata, roc->chan, -					    duration); +					    duration, roc->type);  		roc->started = true; @@ -382,7 +382,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)  		ieee80211_roc_notify_destroy(roc, !roc->abort);  		if (started) { -			drv_flush(local, false); +			ieee80211_flush_queues(local, NULL);  			local->tmp_channel = NULL;  			ieee80211_hw_config(local, 0); @@ -445,15 +445,15 @@ void ieee80211_roc_setup(struct ieee80211_local *local)  	INIT_LIST_HEAD(&local->roc_list);  } -void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata) +void ieee80211_roc_purge(struct ieee80211_local *local, +			 struct ieee80211_sub_if_data *sdata)  { -	struct ieee80211_local *local = sdata->local;  	struct ieee80211_roc_work *roc, *tmp;  	LIST_HEAD(tmp_list);  	mutex_lock(&local->mtx);  	list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { -		if (roc->sdata != sdata) +		if (sdata && roc->sdata != sdata)  			continue;  		if (roc->started && local->ops->remain_on_channel) { diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index d0275f34bf7..7fc5d0d8149 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -6,32 +6,11 @@  #include "driver-ops.h"  #include "led.h" -/* return value indicates whether the driver should be further notified */ -static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) -{ -	switch (sdata->vif.type) { -	case NL80211_IFTYPE_STATION: -		ieee80211_sta_quiesce(sdata); -		break; -	case NL80211_IFTYPE_ADHOC: -		ieee80211_ibss_quiesce(sdata); -		break; -	case NL80211_IFTYPE_MESH_POINT: -		ieee80211_mesh_quiesce(sdata); -		break; -	default: -		break; -	} - -	cancel_work_sync(&sdata->work); -} -  int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)  {  	struct ieee80211_local *local = hw_to_local(hw);  	struct ieee80211_sub_if_data *sdata;  	struct sta_info *sta; -	struct ieee80211_chanctx *ctx;  	if (!local->open_count)  		goto suspend; @@ -40,6 +19,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)  	ieee80211_dfs_cac_cancel(local); +	ieee80211_roc_purge(local, NULL); + +	ieee80211_del_virtual_monitor(local); +  	if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {  		mutex_lock(&local->sta_mtx);  		list_for_each_entry(sta, &local->sta_list, list) { @@ -51,12 +34,14 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)  	}  	ieee80211_stop_queues_by_reason(hw, -			IEEE80211_QUEUE_STOP_REASON_SUSPEND); +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_SUSPEND); -	/* flush out all packets */ +	/* flush out all packets and station cleanup call_rcu()s */  	synchronize_net(); +	rcu_barrier(); -	drv_flush(local, false); +	ieee80211_flush_queues(local, NULL);  	local->quiescing = true;  	/* make quiescing visible to timers everywhere */ @@ -89,23 +74,17 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)  				mutex_unlock(&local->sta_mtx);  			}  			ieee80211_wake_queues_by_reason(hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_SUSPEND);  			return err;  		} else if (err > 0) {  			WARN_ON(err != 1); -			local->wowlan = false; +			return err;  		} else { -			list_for_each_entry(sdata, &local->interfaces, list) -				if (ieee80211_sdata_running(sdata)) -					ieee80211_quiesce(sdata);  			goto suspend;  		}  	} -	/* disable keys */ -	list_for_each_entry(sdata, &local->interfaces, list) -		ieee80211_disable_keys(sdata); -  	/* tear down aggregation sessions and remove STAs */  	mutex_lock(&local->sta_mtx);  	list_for_each_entry(sta, &local->sta_list, list) { @@ -117,100 +96,21 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)  				WARN_ON(drv_sta_state(local, sta->sdata, sta,  						      state, state - 1));  		} - -		mesh_plink_quiesce(sta);  	}  	mutex_unlock(&local->sta_mtx);  	/* remove all interfaces */  	list_for_each_entry(sdata, &local->interfaces, list) { -		static u8 zero_addr[ETH_ALEN] = {}; -		u32 changed = 0; -  		if (!ieee80211_sdata_running(sdata))  			continue; - -		switch (sdata->vif.type) { -		case NL80211_IFTYPE_AP_VLAN: -		case NL80211_IFTYPE_MONITOR: -			/* skip these */ -			continue; -		case NL80211_IFTYPE_STATION: -			if (sdata->vif.bss_conf.assoc) -				changed = BSS_CHANGED_ASSOC | -					  BSS_CHANGED_BSSID | -					  BSS_CHANGED_IDLE; -			break; -		case NL80211_IFTYPE_AP: -		case NL80211_IFTYPE_ADHOC: -		case NL80211_IFTYPE_MESH_POINT: -			if (sdata->vif.bss_conf.enable_beacon) -				changed = BSS_CHANGED_BEACON_ENABLED; -			break; -		default: -			break; -		} - -		ieee80211_quiesce(sdata); - -		sdata->suspend_bss_conf = sdata->vif.bss_conf; -		memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf)); -		sdata->vif.bss_conf.idle = true; -		if (sdata->suspend_bss_conf.bssid) -			sdata->vif.bss_conf.bssid = zero_addr; - -		/* disable beaconing or remove association */ -		ieee80211_bss_info_change_notify(sdata, changed); - -		if (sdata->vif.type == NL80211_IFTYPE_AP && -		    rcu_access_pointer(sdata->u.ap.beacon)) -			drv_stop_ap(local, sdata); - -		if (local->use_chanctx) { -			struct ieee80211_chanctx_conf *conf; - -			mutex_lock(&local->chanctx_mtx); -			conf = rcu_dereference_protected( -					sdata->vif.chanctx_conf, -					lockdep_is_held(&local->chanctx_mtx)); -			if (conf) { -				ctx = container_of(conf, -						   struct ieee80211_chanctx, -						   conf); -				drv_unassign_vif_chanctx(local, sdata, ctx); -			} - -			mutex_unlock(&local->chanctx_mtx); -		}  		drv_remove_interface(local, sdata);  	} -	sdata = rtnl_dereference(local->monitor_sdata); -	if (sdata) { -		if (local->use_chanctx) { -			struct ieee80211_chanctx_conf *conf; - -			mutex_lock(&local->chanctx_mtx); -			conf = rcu_dereference_protected( -					sdata->vif.chanctx_conf, -					lockdep_is_held(&local->chanctx_mtx)); -			if (conf) { -				ctx = container_of(conf, -						   struct ieee80211_chanctx, -						   conf); -				drv_unassign_vif_chanctx(local, sdata, ctx); -			} - -			mutex_unlock(&local->chanctx_mtx); -		} - -		drv_remove_interface(local, sdata); -	} - -	mutex_lock(&local->chanctx_mtx); -	list_for_each_entry(ctx, &local->chanctx_list, list) -		drv_remove_chanctx(local, ctx); -	mutex_unlock(&local->chanctx_mtx); +	/* +	 * We disconnected on all interfaces before suspend, all channel +	 * contexts should be released. +	 */ +	WARN_ON(!list_empty(&local->chanctx_list));  	/* stop hardware - this must stop RX */  	if (local->open_count) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index dd88381c53b..0d51877efdb 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -252,6 +252,25 @@ rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,  	return 0;  } +static void __rate_control_send_low(struct ieee80211_hw *hw, +				    struct ieee80211_supported_band *sband, +				    struct ieee80211_sta *sta, +				    struct ieee80211_tx_info *info) +{ +	if ((sband->band != IEEE80211_BAND_2GHZ) || +	    !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) +		info->control.rates[0].idx = rate_lowest_index(sband, sta); +	else +		info->control.rates[0].idx = +			rate_lowest_non_cck_index(sband, sta); + +	info->control.rates[0].count = +		(info->flags & IEEE80211_TX_CTL_NO_ACK) ? +		1 : hw->max_rate_tries; + +	info->control.skip_table = 1; +} +  bool rate_control_send_low(struct ieee80211_sta *sta,  			   void *priv_sta, @@ -262,16 +281,8 @@ bool rate_control_send_low(struct ieee80211_sta *sta,  	int mcast_rate;  	if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) { -		if ((sband->band != IEEE80211_BAND_2GHZ) || -		    !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) -			info->control.rates[0].idx = -				rate_lowest_index(txrc->sband, sta); -		else -			info->control.rates[0].idx = -				rate_lowest_non_cck_index(txrc->sband, sta); -		info->control.rates[0].count = -			(info->flags & IEEE80211_TX_CTL_NO_ACK) ? -			1 : txrc->hw->max_rate_tries; +		__rate_control_send_low(txrc->hw, sband, sta, info); +  		if (!sta && txrc->bss) {  			mcast_rate = txrc->bss_conf->mcast_rate[sband->band];  			if (mcast_rate > 0) { @@ -355,7 +366,8 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,  static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, -				struct ieee80211_tx_rate_control *txrc, +				struct ieee80211_supported_band *sband, +				enum nl80211_chan_width chan_width,  				u32 mask,  				u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])  { @@ -375,27 +387,17 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,  				  IEEE80211_TX_RC_USE_SHORT_PREAMBLE);  		alt_rate.count = rate->count;  		if (rate_idx_match_legacy_mask(&alt_rate, -					       txrc->sband->n_bitrates, -					       mask)) { +					       sband->n_bitrates, mask)) {  			*rate = alt_rate;  			return;  		}  	} else { -		struct sk_buff *skb = txrc->skb; -		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -		__le16 fc; -  		/* handle legacy rates */ -		if (rate_idx_match_legacy_mask(rate, txrc->sband->n_bitrates, -					       mask)) +		if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask))  			return;  		/* if HT BSS, and we handle a data frame, also try HT rates */ -		if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) -			return; - -		fc = hdr->frame_control; -		if (!ieee80211_is_data(fc)) +		if (chan_width == NL80211_CHAN_WIDTH_20_NOHT)  			return;  		alt_rate.idx = 0; @@ -408,7 +410,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,  		alt_rate.flags |= IEEE80211_TX_RC_MCS; -		if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_40) +		if (chan_width == NL80211_CHAN_WIDTH_40)  			alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;  		if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) { @@ -426,6 +428,228 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,  	 */  } +static void rate_fixup_ratelist(struct ieee80211_vif *vif, +				struct ieee80211_supported_band *sband, +				struct ieee80211_tx_info *info, +				struct ieee80211_tx_rate *rates, +				int max_rates) +{ +	struct ieee80211_rate *rate; +	bool inval = false; +	int i; + +	/* +	 * Set up the RTS/CTS rate as the fastest basic rate +	 * that is not faster than the data rate unless there +	 * is no basic rate slower than the data rate, in which +	 * case we pick the slowest basic rate +	 * +	 * XXX: Should this check all retry rates? +	 */ +	if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) { +		u32 basic_rates = vif->bss_conf.basic_rates; +		s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0; + +		rate = &sband->bitrates[rates[0].idx]; + +		for (i = 0; i < sband->n_bitrates; i++) { +			/* must be a basic rate */ +			if (!(basic_rates & BIT(i))) +				continue; +			/* must not be faster than the data rate */ +			if (sband->bitrates[i].bitrate > rate->bitrate) +				continue; +			/* maximum */ +			if (sband->bitrates[baserate].bitrate < +			     sband->bitrates[i].bitrate) +				baserate = i; +		} + +		info->control.rts_cts_rate_idx = baserate; +	} + +	for (i = 0; i < max_rates; i++) { +		/* +		 * make sure there's no valid rate following +		 * an invalid one, just in case drivers don't +		 * take the API seriously to stop at -1. +		 */ +		if (inval) { +			rates[i].idx = -1; +			continue; +		} +		if (rates[i].idx < 0) { +			inval = true; +			continue; +		} + +		/* +		 * For now assume MCS is already set up correctly, this +		 * needs to be fixed. +		 */ +		if (rates[i].flags & IEEE80211_TX_RC_MCS) { +			WARN_ON(rates[i].idx > 76); + +			if (!(rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) && +			    info->control.use_cts_prot) +				rates[i].flags |= +					IEEE80211_TX_RC_USE_CTS_PROTECT; +			continue; +		} + +		if (rates[i].flags & IEEE80211_TX_RC_VHT_MCS) { +			WARN_ON(ieee80211_rate_get_vht_mcs(&rates[i]) > 9); +			continue; +		} + +		/* set up RTS protection if desired */ +		if (info->control.use_rts) { +			rates[i].flags |= IEEE80211_TX_RC_USE_RTS_CTS; +			info->control.use_cts_prot = false; +		} + +		/* RC is busted */ +		if (WARN_ON_ONCE(rates[i].idx >= sband->n_bitrates)) { +			rates[i].idx = -1; +			continue; +		} + +		rate = &sband->bitrates[rates[i].idx]; + +		/* set up short preamble */ +		if (info->control.short_preamble && +		    rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) +			rates[i].flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; + +		/* set up G protection */ +		if (!(rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) && +		    info->control.use_cts_prot && +		    rate->flags & IEEE80211_RATE_ERP_G) +			rates[i].flags |= IEEE80211_TX_RC_USE_CTS_PROTECT; +	} +} + + +static void rate_control_fill_sta_table(struct ieee80211_sta *sta, +					struct ieee80211_tx_info *info, +					struct ieee80211_tx_rate *rates, +					int max_rates) +{ +	struct ieee80211_sta_rates *ratetbl = NULL; +	int i; + +	if (sta && !info->control.skip_table) +		ratetbl = rcu_dereference(sta->rates); + +	/* Fill remaining rate slots with data from the sta rate table. */ +	max_rates = min_t(int, max_rates, IEEE80211_TX_RATE_TABLE_SIZE); +	for (i = 0; i < max_rates; i++) { +		if (i < ARRAY_SIZE(info->control.rates) && +		    info->control.rates[i].idx >= 0 && +		    info->control.rates[i].count) { +			if (rates != info->control.rates) +				rates[i] = info->control.rates[i]; +		} else if (ratetbl) { +			rates[i].idx = ratetbl->rate[i].idx; +			rates[i].flags = ratetbl->rate[i].flags; +			if (info->control.use_rts) +				rates[i].count = ratetbl->rate[i].count_rts; +			else if (info->control.use_cts_prot) +				rates[i].count = ratetbl->rate[i].count_cts; +			else +				rates[i].count = ratetbl->rate[i].count; +		} else { +			rates[i].idx = -1; +			rates[i].count = 0; +		} + +		if (rates[i].idx < 0 || !rates[i].count) +			break; +	} +} + +static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata, +				    struct ieee80211_sta *sta, +				    struct ieee80211_supported_band *sband, +				    struct ieee80211_tx_info *info, +				    struct ieee80211_tx_rate *rates, +				    int max_rates) +{ +	enum nl80211_chan_width chan_width; +	u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; +	bool has_mcs_mask; +	u32 mask; +	int i; + +	/* +	 * Try to enforce the rateidx mask the user wanted. skip this if the +	 * default mask (allow all rates) is used to save some processing for +	 * the common case. +	 */ +	mask = sdata->rc_rateidx_mask[info->band]; +	has_mcs_mask = sdata->rc_has_mcs_mask[info->band]; +	if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask) +		return; + +	if (has_mcs_mask) +		memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band], +		       sizeof(mcs_mask)); +	else +		memset(mcs_mask, 0xff, sizeof(mcs_mask)); + +	if (sta) { +		/* Filter out rates that the STA does not support */ +		mask &= sta->supp_rates[info->band]; +		for (i = 0; i < sizeof(mcs_mask); i++) +			mcs_mask[i] &= sta->ht_cap.mcs.rx_mask[i]; +	} + +	/* +	 * Make sure the rate index selected for each TX rate is +	 * included in the configured mask and change the rate indexes +	 * if needed. +	 */ +	chan_width = sdata->vif.bss_conf.chandef.width; +	for (i = 0; i < max_rates; i++) { +		/* Skip invalid rates */ +		if (rates[i].idx < 0) +			break; + +		rate_idx_match_mask(&rates[i], sband, mask, chan_width, +				    mcs_mask); +	} +} + +void ieee80211_get_tx_rates(struct ieee80211_vif *vif, +			    struct ieee80211_sta *sta, +			    struct sk_buff *skb, +			    struct ieee80211_tx_rate *dest, +			    int max_rates) +{ +	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_supported_band *sband; + +	rate_control_fill_sta_table(sta, info, dest, max_rates); + +	if (!vif) +		return; + +	sdata = vif_to_sdata(vif); +	sband = sdata->local->hw.wiphy->bands[info->band]; + +	if (ieee80211_is_data(hdr->frame_control)) +		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); + +	if (sta) +		rate_fixup_ratelist(vif, sband, info, dest, max_rates); +} +EXPORT_SYMBOL(ieee80211_get_tx_rates); +  void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,  			   struct sta_info *sta,  			   struct ieee80211_tx_rate_control *txrc) @@ -435,8 +659,6 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_sta *ista = NULL;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);  	int i; -	u32 mask; -	u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];  	if (sta && test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) {  		ista = &sta->sta; @@ -454,37 +676,27 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,  	ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); -	/* -	 * Try to enforce the rateidx mask the user wanted. skip this if the -	 * default mask (allow all rates) is used to save some processing for -	 * the common case. -	 */ -	mask = sdata->rc_rateidx_mask[info->band]; -	memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band], -	       sizeof(mcs_mask)); -	if (mask != (1 << txrc->sband->n_bitrates) - 1) { -		if (sta) { -			/* Filter out rates that the STA does not support */ -			mask &= sta->sta.supp_rates[info->band]; -			for (i = 0; i < sizeof(mcs_mask); i++) -				mcs_mask[i] &= sta->sta.ht_cap.mcs.rx_mask[i]; -		} -		/* -		 * Make sure the rate index selected for each TX rate is -		 * included in the configured mask and change the rate indexes -		 * if needed. -		 */ -		for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { -			/* Skip invalid rates */ -			if (info->control.rates[i].idx < 0) -				break; -			rate_idx_match_mask(&info->control.rates[i], txrc, -					    mask, mcs_mask); -		} -	} +	if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_RC_TABLE) +		return; + +	ieee80211_get_tx_rates(&sdata->vif, ista, txrc->skb, +			       info->control.rates, +			       ARRAY_SIZE(info->control.rates)); +} -	BUG_ON(info->control.rates[0].idx < 0); +int rate_control_set_rates(struct ieee80211_hw *hw, +			   struct ieee80211_sta *pubsta, +			   struct ieee80211_sta_rates *rates) +{ +	struct ieee80211_sta_rates *old = rcu_dereference(pubsta->rates); + +	rcu_assign_pointer(pubsta->rates, rates); +	if (old) +		kfree_rcu(old, rcu_head); + +	return 0;  } +EXPORT_SYMBOL(rate_control_set_rates);  int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,  				 const char *name) diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index eea45a2c7c3..ac7ef5414bd 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -55,7 +55,6 @@  #include "rate.h"  #include "rc80211_minstrel.h" -#define SAMPLE_COLUMNS	10  #define SAMPLE_TBL(_mi, _idx, _col) \  		_mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col] @@ -70,16 +69,75 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix)  	return i;  } +/* find & sort topmost throughput rates */ +static inline void +minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) +{ +	int j = MAX_THR_RATES; + +	while (j > 0 && mi->r[i].cur_tp > mi->r[tp_list[j - 1]].cur_tp) +		j--; +	if (j < MAX_THR_RATES - 1) +		memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); +	if (j < MAX_THR_RATES) +		tp_list[j] = i; +} + +static void +minstrel_set_rate(struct minstrel_sta_info *mi, struct ieee80211_sta_rates *ratetbl, +		  int offset, int idx) +{ +	struct minstrel_rate *r = &mi->r[idx]; + +	ratetbl->rate[offset].idx = r->rix; +	ratetbl->rate[offset].count = r->adjusted_retry_count; +	ratetbl->rate[offset].count_cts = r->retry_count_cts; +	ratetbl->rate[offset].count_rts = r->retry_count_rtscts; +} + +static void +minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi) +{ +	struct ieee80211_sta_rates *ratetbl; +	int i = 0; + +	ratetbl = kzalloc(sizeof(*ratetbl), GFP_ATOMIC); +	if (!ratetbl) +		return; + +	/* Start with max_tp_rate */ +	minstrel_set_rate(mi, ratetbl, i++, mi->max_tp_rate[0]); + +	if (mp->hw->max_rates >= 3) { +		/* At least 3 tx rates supported, use max_tp_rate2 next */ +		minstrel_set_rate(mi, ratetbl, i++, mi->max_tp_rate[1]); +	} + +	if (mp->hw->max_rates >= 2) { +		/* At least 2 tx rates supported, use max_prob_rate next */ +		minstrel_set_rate(mi, ratetbl, i++, mi->max_prob_rate); +	} + +	/* Use lowest rate last */ +	ratetbl->rate[i].idx = mi->lowest_rix; +	ratetbl->rate[i].count = mp->max_retry; +	ratetbl->rate[i].count_cts = mp->max_retry; +	ratetbl->rate[i].count_rts = mp->max_retry; + +	rate_control_set_rates(mp->hw, mi->sta, ratetbl); +} +  static void  minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  { -	u32 max_tp = 0, index_max_tp = 0, index_max_tp2 = 0; -	u32 max_prob = 0, index_max_prob = 0; +	u8 tmp_tp_rate[MAX_THR_RATES]; +	u8 tmp_prob_rate = 0;  	u32 usecs; -	u32 p;  	int i; -	mi->stats_update = jiffies; +	for (i=0; i < MAX_THR_RATES; i++) +	    tmp_tp_rate[i] = 0; +  	for (i = 0; i < mi->n_rates; i++) {  		struct minstrel_rate *mr = &mi->r[i]; @@ -87,27 +145,32 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  		if (!usecs)  			usecs = 1000000; -		/* To avoid rounding issues, probabilities scale from 0 (0%) -		 * to 18000 (100%) */ -		if (mr->attempts) { -			p = (mr->success * 18000) / mr->attempts; +		if (unlikely(mr->attempts > 0)) { +			mr->sample_skipped = 0; +			mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts);  			mr->succ_hist += mr->success;  			mr->att_hist += mr->attempts; -			mr->cur_prob = p; -			p = ((p * (100 - mp->ewma_level)) + (mr->probability * -				mp->ewma_level)) / 100; -			mr->probability = p; -			mr->cur_tp = p * (1000000 / usecs); -		} +			mr->probability = minstrel_ewma(mr->probability, +							mr->cur_prob, +							EWMA_LEVEL); +		} else +			mr->sample_skipped++;  		mr->last_success = mr->success;  		mr->last_attempts = mr->attempts;  		mr->success = 0;  		mr->attempts = 0; +		/* Update throughput per rate, reset thr. below 10% success */ +		if (mr->probability < MINSTREL_FRAC(10, 100)) +			mr->cur_tp = 0; +		else +			mr->cur_tp = mr->probability * (1000000 / usecs); +  		/* Sample less often below the 10% chance of success.  		 * Sample less often above the 95% chance of success. */ -		if ((mr->probability > 17100) || (mr->probability < 1800)) { +		if (mr->probability > MINSTREL_FRAC(95, 100) || +		    mr->probability < MINSTREL_FRAC(10, 100)) {  			mr->adjusted_retry_count = mr->retry_count >> 1;  			if (mr->adjusted_retry_count > 2)  				mr->adjusted_retry_count = 2; @@ -118,35 +181,32 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  		}  		if (!mr->adjusted_retry_count)  			mr->adjusted_retry_count = 2; -	} -	for (i = 0; i < mi->n_rates; i++) { -		struct minstrel_rate *mr = &mi->r[i]; -		if (max_tp < mr->cur_tp) { -			index_max_tp = i; -			max_tp = mr->cur_tp; -		} -		if (max_prob < mr->probability) { -			index_max_prob = i; -			max_prob = mr->probability; +		minstrel_sort_best_tp_rates(mi, i, tmp_tp_rate); + +		/* To determine the most robust rate (max_prob_rate) used at +		 * 3rd mmr stage we distinct between two cases: +		 * (1) if any success probabilitiy >= 95%, out of those rates +		 * 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->cur_tp >= mi->r[tmp_prob_rate].cur_tp) +				tmp_prob_rate = i; +		} else { +			if (mr->probability >= mi->r[tmp_prob_rate].probability) +				tmp_prob_rate = i;  		}  	} -	max_tp = 0; -	for (i = 0; i < mi->n_rates; i++) { -		struct minstrel_rate *mr = &mi->r[i]; +	/* Assign the new rate set */ +	memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate)); +	mi->max_prob_rate = tmp_prob_rate; -		if (i == index_max_tp) -			continue; +	/* Reset update timer */ +	mi->stats_update = jiffies; -		if (max_tp < mr->cur_tp) { -			index_max_tp2 = i; -			max_tp = mr->cur_tp; -		} -	} -	mi->max_tp_rate = index_max_tp; -	mi->max_tp_rate2 = index_max_tp2; -	mi->max_prob_rate = index_max_prob; +	minstrel_update_rates(mp, mi);  }  static void @@ -195,9 +255,9 @@ minstrel_get_retry_count(struct minstrel_rate *mr,  {  	unsigned int retry = mr->adjusted_retry_count; -	if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) +	if (info->control.use_rts)  		retry = max(2U, min(mr->retry_count_rtscts, retry)); -	else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) +	else if (info->control.use_cts_prot)  		retry = max(2U, min(mr->retry_count_cts, retry));  	return retry;  } @@ -207,10 +267,10 @@ static int  minstrel_get_next_sample(struct minstrel_sta_info *mi)  {  	unsigned int sample_ndx; -	sample_ndx = SAMPLE_TBL(mi, mi->sample_idx, mi->sample_column); -	mi->sample_idx++; -	if ((int) mi->sample_idx > (mi->n_rates - 2)) { -		mi->sample_idx = 0; +	sample_ndx = SAMPLE_TBL(mi, mi->sample_row, mi->sample_column); +	mi->sample_row++; +	if ((int) mi->sample_row >= mi->n_rates) { +		mi->sample_row = 0;  		mi->sample_column++;  		if (mi->sample_column >= SAMPLE_COLUMNS)  			mi->sample_column = 0; @@ -226,111 +286,96 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	struct minstrel_sta_info *mi = priv_sta;  	struct minstrel_priv *mp = priv; -	struct ieee80211_tx_rate *ar = info->control.rates; -	unsigned int ndx, sample_ndx = 0; -	bool mrr; -	bool sample_slower = false; -	bool sample = false; -	int i, delta; -	int mrr_ndx[3]; -	int sample_rate; +	struct ieee80211_tx_rate *rate = &info->control.rates[0]; +	struct minstrel_rate *msr, *mr; +	unsigned int ndx; +	bool mrr_capable; +	bool prev_sample = mi->prev_sample; +	int delta; +	int sampling_ratio; +	/* management/no-ack frames do not use rate control */  	if (rate_control_send_low(sta, priv_sta, txrc))  		return; -	mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot; - -	ndx = mi->max_tp_rate; - -	if (mrr) -		sample_rate = mp->lookaround_rate_mrr; +	/* check multi-rate-retry capabilities & adjust lookaround_rate */ +	mrr_capable = mp->has_mrr && +		      !txrc->rts && +		      !txrc->bss_conf->use_cts_prot; +	if (mrr_capable) +		sampling_ratio = mp->lookaround_rate_mrr;  	else -		sample_rate = mp->lookaround_rate; +		sampling_ratio = mp->lookaround_rate; +	/* increase sum packet counter */  	mi->packet_count++; -	delta = (mi->packet_count * sample_rate / 100) - + +	delta = (mi->packet_count * sampling_ratio / 100) -  			(mi->sample_count + mi->sample_deferred / 2); -	/* delta > 0: sampling required */ -	if ((delta > 0) && (mrr || !mi->prev_sample)) { -		struct minstrel_rate *msr; -		if (mi->packet_count >= 10000) { -			mi->sample_deferred = 0; -			mi->sample_count = 0; -			mi->packet_count = 0; -		} else if (delta > mi->n_rates * 2) { -			/* With multi-rate retry, not every planned sample -			 * attempt actually gets used, due to the way the retry -			 * chain is set up - [max_tp,sample,prob,lowest] for -			 * sample_rate < max_tp. -			 * -			 * If there's too much sampling backlog and the link -			 * starts getting worse, minstrel would start bursting -			 * out lots of sampling frames, which would result -			 * in a large throughput loss. */ -			mi->sample_count += (delta - mi->n_rates * 2); -		} +	/* delta < 0: no sampling required */ +	mi->prev_sample = false; +	if (delta < 0 || (!mrr_capable && prev_sample)) +		return; -		sample_ndx = minstrel_get_next_sample(mi); -		msr = &mi->r[sample_ndx]; -		sample = true; -		sample_slower = mrr && (msr->perfect_tx_time > -			mi->r[ndx].perfect_tx_time); +	if (mi->packet_count >= 10000) { +		mi->sample_deferred = 0; +		mi->sample_count = 0; +		mi->packet_count = 0; +	} else if (delta > mi->n_rates * 2) { +		/* With multi-rate retry, not every planned sample +		 * attempt actually gets used, due to the way the retry +		 * chain is set up - [max_tp,sample,prob,lowest] for +		 * sample_rate < max_tp. +		 * +		 * If there's too much sampling backlog and the link +		 * starts getting worse, minstrel would start bursting +		 * out lots of sampling frames, which would result +		 * in a large throughput loss. */ +		mi->sample_count += (delta - mi->n_rates * 2); +	} -		if (!sample_slower) { -			if (msr->sample_limit != 0) { -				ndx = sample_ndx; -				mi->sample_count++; -				if (msr->sample_limit > 0) -					msr->sample_limit--; -			} else { -				sample = false; -			} -		} else { -			/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark -			 * packets that have the sampling rate deferred to the -			 * second MRR stage. Increase the sample counter only -			 * if the deferred sample rate was actually used. -			 * Use the sample_deferred counter to make sure that -			 * the sampling is not done in large bursts */ -			info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; -			mi->sample_deferred++; -		} +	/* get next random rate sample */ +	ndx = minstrel_get_next_sample(mi); +	msr = &mi->r[ndx]; +	mr = &mi->r[mi->max_tp_rate[0]]; + +	/* Decide if direct ( 1st mrr stage) or indirect (2nd mrr stage) +	 * rate sampling method should be used. +	 * Respect such rates that are not sampled for 20 interations. +	 */ +	if (mrr_capable && +	    msr->perfect_tx_time > mr->perfect_tx_time && +	    msr->sample_skipped < 20) { +		/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark +		 * packets that have the sampling rate deferred to the +		 * second MRR stage. Increase the sample counter only +		 * if the deferred sample rate was actually used. +		 * Use the sample_deferred counter to make sure that +		 * the sampling is not done in large bursts */ +		info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; +		rate++; +		mi->sample_deferred++; +	} else { +		if (!msr->sample_limit != 0) +			return; + +		mi->sample_count++; +		if (msr->sample_limit > 0) +			msr->sample_limit--;  	} -	mi->prev_sample = sample;  	/* If we're not using MRR and the sampling rate already  	 * has a probability of >95%, we shouldn't be attempting  	 * to use it, as this only wastes precious airtime */ -	if (!mrr && sample && (mi->r[ndx].probability > 17100)) -		ndx = mi->max_tp_rate; - -	ar[0].idx = mi->r[ndx].rix; -	ar[0].count = minstrel_get_retry_count(&mi->r[ndx], info); - -	if (!mrr) { -		if (!sample) -			ar[0].count = mp->max_retry; -		ar[1].idx = mi->lowest_rix; -		ar[1].count = mp->max_retry; +	if (!mrr_capable && +	   (mi->r[ndx].probability > MINSTREL_FRAC(95, 100)))  		return; -	} -	/* MRR setup */ -	if (sample) { -		if (sample_slower) -			mrr_ndx[0] = sample_ndx; -		else -			mrr_ndx[0] = mi->max_tp_rate; -	} else { -		mrr_ndx[0] = mi->max_tp_rate2; -	} -	mrr_ndx[1] = mi->max_prob_rate; -	mrr_ndx[2] = 0; -	for (i = 1; i < 4; i++) { -		ar[i].idx = mi->r[mrr_ndx[i - 1]].rix; -		ar[i].count = mi->r[mrr_ndx[i - 1]].adjusted_retry_count; -	} +	mi->prev_sample = true; + +	rate->idx = mi->r[ndx].rix; +	rate->count = minstrel_get_retry_count(&mi->r[ndx], info);  } @@ -351,26 +396,21 @@ static void  init_sample_table(struct minstrel_sta_info *mi)  {  	unsigned int i, col, new_idx; -	unsigned int n_srates = mi->n_rates - 1;  	u8 rnd[8];  	mi->sample_column = 0; -	mi->sample_idx = 0; -	memset(mi->sample_table, 0, SAMPLE_COLUMNS * mi->n_rates); +	mi->sample_row = 0; +	memset(mi->sample_table, 0xff, SAMPLE_COLUMNS * mi->n_rates);  	for (col = 0; col < SAMPLE_COLUMNS; col++) { -		for (i = 0; i < n_srates; i++) { +		for (i = 0; i < mi->n_rates; i++) {  			get_random_bytes(rnd, sizeof(rnd)); -			new_idx = (i + rnd[i & 7]) % n_srates; +			new_idx = (i + rnd[i & 7]) % mi->n_rates; -			while (SAMPLE_TBL(mi, new_idx, col) != 0) -				new_idx = (new_idx + 1) % n_srates; +			while (SAMPLE_TBL(mi, new_idx, col) != 0xff) +				new_idx = (new_idx + 1) % mi->n_rates; -			/* Don't sample the slowest rate (i.e. slowest base -			 * rate). We must presume that the slowest rate works -			 * fine, or else other management frames will also be -			 * failing and the link will break */ -			SAMPLE_TBL(mi, new_idx, col) = i + 1; +			SAMPLE_TBL(mi, new_idx, col) = i;  		}  	}  } @@ -385,12 +425,16 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,  	unsigned int i, n = 0;  	unsigned int t_slot = 9; /* FIXME: get real slot time */ +	mi->sta = sta;  	mi->lowest_rix = rate_lowest_index(sband, sta);  	ctl_rate = &sband->bitrates[mi->lowest_rix];  	mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,  				ctl_rate->bitrate,  				!!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1); +	memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate)); +	mi->max_prob_rate = 0; +  	for (i = 0; i < sband->n_bitrates; i++) {  		struct minstrel_rate *mr = &mi->r[n];  		unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0; @@ -433,6 +477,8 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,  		} while ((tx_time < mp->segment_size) &&  				(++mr->retry_count < mp->max_retry));  		mr->adjusted_retry_count = mr->retry_count; +		if (!(sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)) +			mr->retry_count_cts = mr->retry_count;  	}  	for (i = n; i < sband->n_bitrates; i++) { @@ -444,6 +490,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,  	mi->stats_update = jiffies;  	init_sample_table(mi); +	minstrel_update_rates(mp, mi);  }  static void * @@ -542,9 +589,6 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)  	mp->lookaround_rate = 5;  	mp->lookaround_rate_mrr = 10; -	/* moving average weight for EWMA */ -	mp->ewma_level = 75; -  	/* maximum time that the hw is allowed to stay in one MRR segment */  	mp->segment_size = 6000; diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 5ecf757817f..f4301f4b2e4 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -9,6 +9,29 @@  #ifndef __RC_MINSTREL_H  #define __RC_MINSTREL_H +#define EWMA_LEVEL	96	/* ewma weighting factor [/EWMA_DIV] */ +#define EWMA_DIV	128 +#define SAMPLE_COLUMNS	10	/* number of columns in sample table */ + + +/* scaled fraction values */ +#define MINSTREL_SCALE  16 +#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) +#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) + +/* number of highest throughput rates to consider*/ +#define MAX_THR_RATES 4 + +/* + * Perform EWMA (Exponentially Weighted Moving Average) calculation +  */ +static inline int +minstrel_ewma(int old, int new, int weight) +{ +	return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV; +} + +  struct minstrel_rate {  	int bitrate;  	int rix; @@ -26,6 +49,7 @@ struct minstrel_rate {  	u32 attempts;  	u32 last_attempts;  	u32 last_success; +	u8 sample_skipped;  	/* parts per thousand */  	u32 cur_prob; @@ -39,20 +63,21 @@ struct minstrel_rate {  };  struct minstrel_sta_info { +	struct ieee80211_sta *sta; +  	unsigned long stats_update;  	unsigned int sp_ack_dur;  	unsigned int rate_avg;  	unsigned int lowest_rix; -	unsigned int max_tp_rate; -	unsigned int max_tp_rate2; -	unsigned int max_prob_rate; +	u8 max_tp_rate[MAX_THR_RATES]; +	u8 max_prob_rate;  	unsigned int packet_count;  	unsigned int sample_count;  	int sample_deferred; -	unsigned int sample_idx; +	unsigned int sample_row;  	unsigned int sample_column;  	int n_rates; @@ -73,7 +98,6 @@ struct minstrel_priv {  	unsigned int cw_min;  	unsigned int cw_max;  	unsigned int max_retry; -	unsigned int ewma_level;  	unsigned int segment_size;  	unsigned int update_interval;  	unsigned int lookaround_rate; diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index d5a56226e67..fd0b9ca1570 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -68,23 +68,25 @@ minstrel_stats_open(struct inode *inode, struct file *file)  	file->private_data = ms;  	p = ms->buf; -	p += sprintf(p, "rate     throughput  ewma prob   this prob  " +	p += sprintf(p, "rate      throughput  ewma prob  this prob  "  			"this succ/attempt   success    attempts\n");  	for (i = 0; i < mi->n_rates; i++) {  		struct minstrel_rate *mr = &mi->r[i]; -		*(p++) = (i == mi->max_tp_rate) ? 'T' : ' '; -		*(p++) = (i == mi->max_tp_rate2) ? 't' : ' '; +		*(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' '; +		*(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' '; +		*(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' '; +		*(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';  		*(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';  		p += sprintf(p, "%3u%s", mr->bitrate / 2,  				(mr->bitrate & 1 ? ".5" : "  ")); -		tp = mr->cur_tp / ((18000 << 10) / 96); -		prob = mr->cur_prob / 18; -		eprob = mr->probability / 18; +		tp = MINSTREL_TRUNC(mr->cur_tp / 10); +		prob = MINSTREL_TRUNC(mr->cur_prob * 1000); +		eprob = MINSTREL_TRUNC(mr->probability * 1000);  		p += sprintf(p, "  %6u.%1u   %6u.%1u   %6u.%1u        " -				"%3u(%3u)   %8llu    %8llu\n", +				"   %3u(%3u)  %8llu    %8llu\n",  				tp / 10, tp % 10,  				eprob / 10, eprob % 10,  				prob / 10, prob % 10, diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 3af141c6971..5b2d3012b98 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -17,8 +17,6 @@  #include "rc80211_minstrel_ht.h"  #define AVG_PKT_SIZE	1200 -#define SAMPLE_COLUMNS	10 -#define EWMA_LEVEL		75  /* Number of bits for an average sized packet */  #define MCS_NBITS (AVG_PKT_SIZE << 3) @@ -26,11 +24,11 @@  /* Number of symbols for a packet with (bps) bits per symbol */  #define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps)) -/* Transmission time for a packet containing (syms) symbols */ +/* Transmission time (nanoseconds) for a packet containing (syms) symbols */  #define MCS_SYMBOL_TIME(sgi, syms)					\  	(sgi ?								\ -	  ((syms) * 18 + 4) / 5 :	/* syms * 3.6 us */		\ -	  (syms) << 2			/* syms * 4 us */		\ +	  ((syms) * 18000 + 4000) / 5 :	/* syms * 3.6 us */		\ +	  ((syms) * 1000) << 2		/* syms * 4 us */		\  	)  /* Transmit duration for the raw data part of an average sized packet */ @@ -64,9 +62,9 @@  }  #define CCK_DURATION(_bitrate, _short, _len)		\ -	(10 /* SIFS */ +				\ +	(1000 * (10 /* SIFS */ +			\  	 (_short ? 72 + 24 : 144 + 48 ) +		\ -	 (8 * (_len + 4) * 10) / (_bitrate)) +	 (8 * (_len + 4) * 10) / (_bitrate)))  #define CCK_ACK_DURATION(_bitrate, _short)			\  	(CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) +	\ @@ -128,14 +126,8 @@ const struct mcs_group minstrel_mcs_groups[] = {  static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES]; -/* - * Perform EWMA (Exponentially Weighted Moving Average) calculation - */ -static int -minstrel_ewma(int old, int new, int weight) -{ -	return (new * (100 - weight) + old * weight) / 100; -} +static void +minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);  /*   * Look up an MCS group index based on mac80211 rate information @@ -211,20 +203,32 @@ static void  minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)  {  	struct minstrel_rate_stats *mr; -	unsigned int usecs = 0; +	unsigned int nsecs = 0; +	unsigned int tp; +	unsigned int prob;  	mr = &mi->groups[group].rates[rate]; +	prob = mr->probability; -	if (mr->probability < MINSTREL_FRAC(1, 10)) { +	if (prob < MINSTREL_FRAC(1, 10)) {  		mr->cur_tp = 0;  		return;  	} +	/* +	 * For the throughput calculation, limit the probability value to 90% to +	 * account for collision related packet error rate fluctuation +	 */ +	if (prob > MINSTREL_FRAC(9, 10)) +		prob = MINSTREL_FRAC(9, 10); +  	if (group != MINSTREL_CCK_GROUP) -		usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); +		nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); -	usecs += minstrel_mcs_groups[group].duration[rate]; -	mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability); +	nsecs += minstrel_mcs_groups[group].duration[rate]; +	tp = 1000000 * ((mr->probability * 1000) / nsecs); + +	mr->cur_tp = MINSTREL_TRUNC(tp);  }  /* @@ -243,6 +247,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  	struct minstrel_rate_stats *mr;  	int cur_prob, cur_prob_tp, cur_tp, cur_tp2;  	int group, i, index; +	bool mi_rates_valid = false;  	if (mi->ampdu_packets > 0) {  		mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, @@ -253,11 +258,10 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  	mi->sample_slow = 0;  	mi->sample_count = 0; -	mi->max_tp_rate = 0; -	mi->max_tp_rate2 = 0; -	mi->max_prob_rate = 0;  	for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { +		bool mg_rates_valid = false; +  		cur_prob = 0;  		cur_prob_tp = 0;  		cur_tp = 0; @@ -267,15 +271,24 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		if (!mg->supported)  			continue; -		mg->max_tp_rate = 0; -		mg->max_tp_rate2 = 0; -		mg->max_prob_rate = 0;  		mi->sample_count++;  		for (i = 0; i < MCS_GROUP_RATES; i++) {  			if (!(mg->supported & BIT(i)))  				continue; +			/* 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_rates_valid = true; +				} +				mg_rates_valid = true; +			} +  			mr = &mg->rates[i];  			mr->retry_updated = false;  			index = MCS_GROUP_RATES * group + i; @@ -308,8 +321,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		}  	} -	/* try to sample up to half of the available rates during each interval */ -	mi->sample_count *= 4; +	/* try to sample all available rates during each interval */ +	mi->sample_count *= 8;  	cur_prob = 0;  	cur_prob_tp = 0; @@ -320,20 +333,13 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		if (!mg->supported)  			continue; -		mr = minstrel_get_ratestats(mi, mg->max_prob_rate); -		if (cur_prob_tp < mr->cur_tp && -		    minstrel_mcs_groups[group].streams == 1) { -			mi->max_prob_rate = mg->max_prob_rate; -			cur_prob = mr->cur_prob; -			cur_prob_tp = mr->cur_tp; -		} -  		mr = minstrel_get_ratestats(mi, mg->max_tp_rate);  		if (cur_tp < mr->cur_tp) {  			mi->max_tp_rate2 = mi->max_tp_rate;  			cur_tp2 = cur_tp;  			mi->max_tp_rate = mg->max_tp_rate;  			cur_tp = mr->cur_tp; +			mi->max_prob_streams = minstrel_mcs_groups[group].streams - 1;  		}  		mr = minstrel_get_ratestats(mi, mg->max_tp_rate2); @@ -343,6 +349,23 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		}  	} +	if (mi->max_prob_streams < 1) +		mi->max_prob_streams = 1; + +	for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { +		mg = &mi->groups[group]; +		if (!mg->supported) +			continue; +		mr = minstrel_get_ratestats(mi, mg->max_prob_rate); +		if (cur_prob_tp < mr->cur_tp && +		    minstrel_mcs_groups[group].streams <= mi->max_prob_streams) { +			mi->max_prob_rate = mg->max_prob_rate; +			cur_prob = mr->cur_prob; +			cur_prob_tp = mr->cur_tp; +		} +	} + +  	mi->stats_update = jiffies;  } @@ -445,7 +468,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,  	struct ieee80211_tx_rate *ar = info->status.rates;  	struct minstrel_rate_stats *rate, *rate2;  	struct minstrel_priv *mp = priv; -	bool last; +	bool last, update = false;  	int i;  	if (!msp->is_ht) @@ -467,7 +490,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,  	if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) {  		mi->sample_wait = 16 + 2 * MINSTREL_TRUNC(mi->avg_ampdu_len); -		mi->sample_tries = 2; +		mi->sample_tries = 1;  		mi->sample_count--;  	} @@ -494,21 +517,29 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,  	rate = minstrel_get_ratestats(mi, mi->max_tp_rate);  	if (rate->attempts > 30 &&  	    MINSTREL_FRAC(rate->success, rate->attempts) < -	    MINSTREL_FRAC(20, 100)) +	    MINSTREL_FRAC(20, 100)) {  		minstrel_downgrade_rate(mi, &mi->max_tp_rate, true); +		update = true; +	}  	rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2);  	if (rate2->attempts > 30 &&  	    MINSTREL_FRAC(rate2->success, rate2->attempts) < -	    MINSTREL_FRAC(20, 100)) +	    MINSTREL_FRAC(20, 100)) {  		minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false); +		update = true; +	}  	if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { +		update = true;  		minstrel_ht_update_stats(mp, mi);  		if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&  		    mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)  			minstrel_aggr_check(sta, skb);  	} + +	if (update) +		minstrel_ht_update_rates(mp, mi);  }  static void @@ -536,7 +567,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,  	mr->retry_updated = true;  	group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; -	tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len; +	tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000;  	/* Contention time for first 2 tries */  	ctime = (t_slot * cw) >> 1; @@ -572,36 +603,71 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,  static void  minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -                     struct ieee80211_tx_rate *rate, int index, -                     bool sample, bool rtscts) +                     struct ieee80211_sta_rates *ratetbl, int offset, int index)  {  	const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];  	struct minstrel_rate_stats *mr; +	u8 idx; +	u16 flags;  	mr = minstrel_get_ratestats(mi, index);  	if (!mr->retry_updated)  		minstrel_calc_retransmit(mp, mi, index); -	if (sample) -		rate->count = 1; -	else if (mr->probability < MINSTREL_FRAC(20, 100)) -		rate->count = 2; -	else if (rtscts) -		rate->count = mr->retry_count_rtscts; -	else -		rate->count = mr->retry_count; - -	rate->flags = 0; -	if (rtscts) -		rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; +	if (mr->probability < MINSTREL_FRAC(20, 100) || !mr->retry_count) { +		ratetbl->rate[offset].count = 2; +		ratetbl->rate[offset].count_rts = 2; +		ratetbl->rate[offset].count_cts = 2; +	} else { +		ratetbl->rate[offset].count = mr->retry_count; +		ratetbl->rate[offset].count_cts = mr->retry_count; +		ratetbl->rate[offset].count_rts = mr->retry_count_rtscts; +	}  	if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { -		rate->idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; +		idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; +		flags = 0; +	} else { +		idx = index % MCS_GROUP_RATES + +		      (group->streams - 1) * MCS_GROUP_RATES; +		flags = IEEE80211_TX_RC_MCS | group->flags; +	} + +	if (offset > 0) { +		ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts; +		flags |= IEEE80211_TX_RC_USE_RTS_CTS; +	} + +	ratetbl->rate[offset].idx = idx; +	ratetbl->rate[offset].flags = flags; +} + +static void +minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) +{ +	struct ieee80211_sta_rates *rates; +	int i = 0; + +	rates = kzalloc(sizeof(*rates), GFP_ATOMIC); +	if (!rates)  		return; + +	/* Start with max_tp_rate */ +	minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate); + +	if (mp->hw->max_rates >= 3) { +		/* At least 3 tx rates supported, use max_tp_rate2 next */ +		minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate2);  	} -	rate->flags |= IEEE80211_TX_RC_MCS | group->flags; -	rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES; +	if (mp->hw->max_rates >= 2) { +		/* +		 * At least 2 tx rates supported, use max_prob_rate next */ +		minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_prob_rate); +	} + +	rates->rate[i].idx = -1; +	rate_control_set_rates(mp->hw, mi->sta, rates);  }  static inline int @@ -616,6 +682,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  {  	struct minstrel_rate_stats *mr;  	struct minstrel_mcs_group_data *mg; +	unsigned int sample_dur, sample_group;  	int sample_idx = 0;  	if (mi->sample_wait > 0) { @@ -626,39 +693,46 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  	if (!mi->sample_tries)  		return -1; -	mi->sample_tries--;  	mg = &mi->groups[mi->sample_group];  	sample_idx = sample_table[mg->column][mg->index];  	mr = &mg->rates[sample_idx]; -	sample_idx += mi->sample_group * MCS_GROUP_RATES; +	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)  	 * to the frame. Hence, don't use sampling for the currently -	 * used max TP rate. +	 * used rates.  	 */ -	if (sample_idx == mi->max_tp_rate) +	if (sample_idx == mi->max_tp_rate || +	    sample_idx == mi->max_tp_rate2 || +	    sample_idx == mi->max_prob_rate)  		return -1; +  	/* -	 * When not using MRR, do not sample if the probability is already -	 * higher than 95% to avoid wasting airtime +	 * Do not sample if the probability is already higher than 95% +	 * to avoid wasting airtime.  	 */ -	if (!mp->has_mrr && (mr->probability > MINSTREL_FRAC(95, 100))) +	if (mr->probability > MINSTREL_FRAC(95, 100))  		return -1;  	/*  	 * Make sure that lower rates get sampled only occasionally,  	 * if the link is working perfectly.  	 */ -	if (minstrel_get_duration(sample_idx) > -	    minstrel_get_duration(mi->max_tp_rate)) { +	sample_dur = minstrel_get_duration(sample_idx); +	if (sample_dur >= minstrel_get_duration(mi->max_tp_rate2) && +	    (mi->max_prob_streams < +	     minstrel_mcs_groups[sample_group].streams || +	     sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {  		if (mr->sample_skipped < 20)  			return -1;  		if (mi->sample_slow++ > 2)  			return -1;  	} +	mi->sample_tries--;  	return sample_idx;  } @@ -683,13 +757,13 @@ static void  minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,                       struct ieee80211_tx_rate_control *txrc)  { +	const struct mcs_group *sample_group;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); -	struct ieee80211_tx_rate *ar = info->status.rates; +	struct ieee80211_tx_rate *rate = &info->status.rates[0];  	struct minstrel_ht_sta_priv *msp = priv_sta;  	struct minstrel_ht_sta *mi = &msp->ht;  	struct minstrel_priv *mp = priv;  	int sample_idx; -	bool sample = false;  	if (rate_control_send_low(sta, priv_sta, txrc))  		return; @@ -717,51 +791,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,  	}  #endif -	if (sample_idx >= 0) { -		sample = true; -		minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx, -			true, false); -		info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; -	} else { -		minstrel_ht_set_rate(mp, mi, &ar[0], mi->max_tp_rate, -			false, false); -	} - -	if (mp->hw->max_rates >= 3) { -		/* -		 * At least 3 tx rates supported, use -		 * sample_rate -> max_tp_rate -> max_prob_rate for sampling and -		 * max_tp_rate -> max_tp_rate2 -> max_prob_rate by default. -		 */ -		if (sample_idx >= 0) -			minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate, -				false, false); -		else -			minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2, -				false, true); - -		minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate, -				     false, !sample); - -		ar[3].count = 0; -		ar[3].idx = -1; -	} else if (mp->hw->max_rates == 2) { -		/* -		 * Only 2 tx rates supported, use -		 * sample_rate -> max_prob_rate for sampling and -		 * max_tp_rate -> max_prob_rate by default. -		 */ -		minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_prob_rate, -				     false, !sample); - -		ar[2].count = 0; -		ar[2].idx = -1; -	} else { -		/* Not using MRR, only use the first rate */ -		ar[1].count = 0; -		ar[1].idx = -1; -	} -  	mi->total_packets++;  	/* wraparound */ @@ -769,6 +798,16 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,  		mi->total_packets = 0;  		mi->sample_packets = 0;  	} + +	if (sample_idx < 0) +		return; + +	sample_group = &minstrel_mcs_groups[sample_idx / MCS_GROUP_RATES]; +	info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; +	rate->idx = sample_idx % MCS_GROUP_RATES + +		    (sample_group->streams - 1) * MCS_GROUP_RATES; +	rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags; +	rate->count = 1;  }  static void @@ -818,6 +857,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,  	msp->is_ht = true;  	memset(mi, 0, sizeof(*mi)); + +	mi->sta = sta;  	mi->stats_update = jiffies;  	ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1); @@ -879,6 +920,10 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,  	if (!n_supported)  		goto use_legacy; +	/* create an initial rate table with the lowest supported rates */ +	minstrel_ht_update_stats(mp, mi); +	minstrel_ht_update_rates(mp, mi); +  	return;  use_legacy: diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index 302dbd52180..d655586773a 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -16,11 +16,6 @@  #define MINSTREL_MAX_STREAMS	3  #define MINSTREL_STREAM_GROUPS	4 -/* scaled fraction values */ -#define MINSTREL_SCALE	16 -#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) -#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) -  #define MCS_GROUP_RATES	8  struct mcs_group { @@ -70,6 +65,8 @@ struct minstrel_mcs_group_data {  };  struct minstrel_ht_sta { +	struct ieee80211_sta *sta; +  	/* ampdu length (average, per sampling interval) */  	unsigned int ampdu_len;  	unsigned int ampdu_packets; @@ -85,6 +82,7 @@ struct minstrel_ht_sta {  	/* best probability rate */  	unsigned int max_prob_rate; +	unsigned int max_prob_streams;  	/* time of last status update */  	unsigned long stats_update; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c6844ad080b..c8447af76ea 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -648,24 +648,6 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)  	return RX_CONTINUE;  } -#define SEQ_MODULO 0x1000 -#define SEQ_MASK   0xfff - -static inline int seq_less(u16 sq1, u16 sq2) -{ -	return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1); -} - -static inline u16 seq_inc(u16 sq) -{ -	return (sq + 1) & SEQ_MASK; -} - -static inline u16 seq_sub(u16 sq1, u16 sq2) -{ -	return (sq1 - sq2) & SEQ_MASK; -} -  static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,  					    struct tid_ampdu_rx *tid_agg_rx,  					    int index, @@ -687,7 +669,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,  	__skb_queue_tail(frames, skb);  no_frame: -	tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); +	tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);  }  static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata, @@ -699,8 +681,9 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata  	lockdep_assert_held(&tid_agg_rx->reorder_lock); -	while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { -		index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % +	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;  		ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,  						frames); @@ -727,8 +710,8 @@ 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 = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % -						tid_agg_rx->buf_size; +	index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, +				 tid_agg_rx->ssn) % tid_agg_rx->buf_size;  	if (!tid_agg_rx->reorder_buf[index] &&  	    tid_agg_rx->stored_mpdu_num) {  		/* @@ -756,19 +739,22 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,  			 * Increment the head seq# also for the skipped slots.  			 */  			tid_agg_rx->head_seq_num = -				(tid_agg_rx->head_seq_num + skipped) & SEQ_MASK; +				(tid_agg_rx->head_seq_num + +				 skipped) & IEEE80211_SN_MASK;  			skipped = 0;  		}  	} else while (tid_agg_rx->reorder_buf[index]) {  		ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,  						frames); -		index =	seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % +		index =	ieee80211_sn_sub(tid_agg_rx->head_seq_num, +					 tid_agg_rx->ssn) %  							tid_agg_rx->buf_size;  	}  	if (tid_agg_rx->stored_mpdu_num) { -		j = index = seq_sub(tid_agg_rx->head_seq_num, -				    tid_agg_rx->ssn) % tid_agg_rx->buf_size; +		j = index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, +					     tid_agg_rx->ssn) % +							tid_agg_rx->buf_size;  		for (; j != (index - 1) % tid_agg_rx->buf_size;  		     j = (j + 1) % tid_agg_rx->buf_size) { @@ -809,7 +795,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata  	head_seq_num = tid_agg_rx->head_seq_num;  	/* frame with out of date sequence number */ -	if (seq_less(mpdu_seq_num, head_seq_num)) { +	if (ieee80211_sn_less(mpdu_seq_num, head_seq_num)) {  		dev_kfree_skb(skb);  		goto out;  	} @@ -818,8 +804,9 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata  	 * If frame the sequence number exceeds our buffering window  	 * size release some previous frames to make room for this one.  	 */ -	if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) { -		head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size)); +	if (!ieee80211_sn_less(mpdu_seq_num, head_seq_num + buf_size)) { +		head_seq_num = ieee80211_sn_inc( +				ieee80211_sn_sub(mpdu_seq_num, buf_size));  		/* release stored frames up to new head to stack */  		ieee80211_release_reorder_frames(sdata, tid_agg_rx,  						 head_seq_num, frames); @@ -827,7 +814,8 @@ 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 = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; +	index = ieee80211_sn_sub(mpdu_seq_num, +				 tid_agg_rx->ssn) % tid_agg_rx->buf_size;  	/* check if we already stored this frame */  	if (tid_agg_rx->reorder_buf[index]) { @@ -843,7 +831,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata  	 */  	if (mpdu_seq_num == tid_agg_rx->head_seq_num &&  	    tid_agg_rx->stored_mpdu_num == 0) { -		tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); +		tid_agg_rx->head_seq_num = +			ieee80211_sn_inc(tid_agg_rx->head_seq_num);  		ret = false;  		goto out;  	} @@ -1894,8 +1883,10 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)  		 * '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. That also explains the __skb_push() -		 * below. +		 * 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)  		 */  		align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;  		if (align) { @@ -2094,6 +2085,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  	}  	fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data; +	fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);  	info = IEEE80211_SKB_CB(fwd_skb);  	memset(info, 0, sizeof(*info));  	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; @@ -2432,6 +2424,22 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		}  		break; +	case WLAN_CATEGORY_PUBLIC: +		if (len < IEEE80211_MIN_ACTION_SIZE + 1) +			goto invalid; +		if (sdata->vif.type != NL80211_IFTYPE_STATION) +			break; +		if (!rx->sta) +			break; +		if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) +			break; +		if (mgmt->u.action.u.ext_chan_switch.action_code != +				WLAN_PUB_ACTION_EXT_CHANSW_ANN) +			break; +		if (len < offsetof(struct ieee80211_mgmt, +				   u.action.u.ext_chan_switch.variable)) +			goto invalid; +		goto queue;  	case WLAN_CATEGORY_VHT:  		if (sdata->vif.type != NL80211_IFTYPE_STATION &&  		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT && @@ -2515,10 +2523,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  			ieee80211_process_measurement_req(sdata, mgmt, len);  			goto handled;  		case WLAN_ACTION_SPCT_CHL_SWITCH: -			if (len < (IEEE80211_MIN_ACTION_SIZE + -				   sizeof(mgmt->u.action.u.chan_switch))) -				break; -  			if (sdata->vif.type != NL80211_IFTYPE_STATION)  				break; @@ -2552,7 +2556,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		case WLAN_SP_MESH_PEERING_CONFIRM:  			if (!ieee80211_vif_is_mesh(&sdata->vif))  				goto invalid; -			if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) +			if (sdata->u.mesh.user_mpm)  				/* userspace handles this frame */  				break;  			goto queue; @@ -3051,7 +3055,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  		    !ieee80211_is_probe_resp(hdr->frame_control) &&  		    !ieee80211_is_beacon(hdr->frame_control))  			return 0; -		if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) +		if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) && +		    !multicast)  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		break;  	default: diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 43a45cf00e0..99b103921a4 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -98,9 +98,8 @@ ieee80211_bss_info_update(struct ieee80211_local *local,  	}  	/* save the ERP value so that it is available at association time */ -	if (elems->erp_info && elems->erp_info_len >= 1 && -			(!elems->parse_error || -			 !(bss->valid_data & IEEE80211_BSS_VALID_ERP))) { +	if (elems->erp_info && (!elems->parse_error || +				!(bss->valid_data & IEEE80211_BSS_VALID_ERP))) {  		bss->erp_value = elems->erp_info[0];  		bss->has_erp_value = true;  		if (!elems->parse_error) @@ -153,7 +152,6 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)  	u8 *elements;  	struct ieee80211_channel *channel;  	size_t baselen; -	bool beacon;  	struct ieee802_11_elems elems;  	if (skb->len < 24 || @@ -175,17 +173,15 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)  		elements = mgmt->u.probe_resp.variable;  		baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); -		beacon = false;  	} else {  		baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);  		elements = mgmt->u.beacon.variable; -		beacon = true;  	}  	if (baselen > skb->len)  		return; -	ieee802_11_parse_elems(elements, skb->len - baselen, &elems); +	ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems);  	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); @@ -335,7 +331,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)  	ieee80211_offchannel_stop_vifs(local);  	/* ensure nullfunc is transmitted before leaving operating channel */ -	drv_flush(local, false); +	ieee80211_flush_queues(local, NULL);  	ieee80211_configure_filter(local); @@ -387,7 +383,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,  {  	int i;  	struct ieee80211_sub_if_data *sdata; -	enum ieee80211_band band = local->hw.conf.channel->band; +	enum ieee80211_band band = local->hw.conf.chandef.chan->band;  	u32 tx_flags;  	tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK; @@ -404,7 +400,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,  			local->scan_req->ssids[i].ssid_len,  			local->scan_req->ie, local->scan_req->ie_len,  			local->scan_req->rates[band], false, -			tx_flags, local->hw.conf.channel, true); +			tx_flags, local->hw.conf.chandef.chan, true);  	/*  	 * After sending probe requests, wait for probe responses @@ -470,7 +466,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  	if (local->ops->hw_scan) {  		__set_bit(SCAN_HW_SCANNING, &local->scanning);  	} else if ((req->n_channels == 1) && -		   (req->channels[0] == local->_oper_channel)) { +		   (req->channels[0] == local->_oper_chandef.chan)) {  		/*  		 * If we are scanning only on the operating channel  		 * then we do not need to stop normal activities @@ -671,7 +667,7 @@ static void ieee80211_scan_state_resume(struct ieee80211_local *local,  	ieee80211_offchannel_stop_vifs(local);  	if (local->ops->flush) { -		drv_flush(local, false); +		ieee80211_flush_queues(local, NULL);  		*next_delay = 0;  	} else  		*next_delay = HZ / 10; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 238a0cca320..11216bc13b2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -342,6 +342,11 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,  	INIT_WORK(&sta->drv_unblock_wk, sta_unblock);  	INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);  	mutex_init(&sta->ampdu_mlme.mtx); +#ifdef CONFIG_MAC80211_MESH +	if (ieee80211_vif_is_mesh(&sdata->vif) && +	    !sdata->u.mesh.user_mpm) +		init_timer(&sta->plink_timer); +#endif  	memcpy(sta->sta.addr, addr, ETH_ALEN);  	sta->local = local; @@ -551,6 +556,15 @@ static inline void __bss_tim_clear(u8 *tim, u16 id)  	tim[id / 8] &= ~(1 << (id % 8));  } +static inline bool __bss_tim_get(u8 *tim, u16 id) +{ +	/* +	 * This format has been mandated by the IEEE specifications, +	 * so this line may not be changed to use the test_bit() format. +	 */ +	return tim[id / 8] & (1 << (id % 8)); +} +  static unsigned long ieee80211_tids_for_ac(int ac)  {  	/* If we ever support TIDs > 7, this obviously needs to be adjusted */ @@ -631,6 +645,9 @@ void sta_info_recalc_tim(struct sta_info *sta)   done:  	spin_lock_bh(&local->tim_lock); +	if (indicate_tim == __bss_tim_get(ps->tim, id)) +		goto out_unlock; +  	if (indicate_tim)  		__bss_tim_set(ps->tim, id);  	else @@ -642,6 +659,7 @@ void sta_info_recalc_tim(struct sta_info *sta)  		local->tim_in_locked_section = false;  	} +out_unlock:  	spin_unlock_bh(&local->tim_lock);  } @@ -765,8 +783,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)  {  	struct ieee80211_local *local;  	struct ieee80211_sub_if_data *sdata; -	int ret, i; -	bool have_key = false; +	int ret;  	might_sleep(); @@ -793,19 +810,8 @@ int __must_check __sta_info_destroy(struct sta_info *sta)  	list_del_rcu(&sta->list); -	mutex_lock(&local->key_mtx); -	for (i = 0; i < NUM_DEFAULT_KEYS; i++) { -		__ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i])); -		have_key = true; -	} -	if (sta->ptk) { -		__ieee80211_key_free(key_mtx_dereference(local, sta->ptk)); -		have_key = true; -	} -	mutex_unlock(&local->key_mtx); - -	if (!have_key) -		synchronize_net(); +	/* this always calls synchronize_net() */ +	ieee80211_free_sta_keys(local, sta);  	sta->dead = true; @@ -1391,30 +1397,16 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,  }  EXPORT_SYMBOL(ieee80211_sta_block_awake); -void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta) +void ieee80211_sta_eosp(struct ieee80211_sta *pubsta)  {  	struct sta_info *sta = container_of(pubsta, struct sta_info, sta);  	struct ieee80211_local *local = sta->local; -	struct sk_buff *skb; -	struct skb_eosp_msg_data *data;  	trace_api_eosp(local, pubsta); -	skb = alloc_skb(0, GFP_ATOMIC); -	if (!skb) { -		/* too bad ... but race is better than loss */ -		clear_sta_flag(sta, WLAN_STA_SP); -		return; -	} - -	data = (void *)skb->cb; -	memcpy(data->sta, pubsta->addr, ETH_ALEN); -	memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN); -	skb->pkt_type = IEEE80211_EOSP_MSG; -	skb_queue_tail(&local->skb_queue, skb); -	tasklet_schedule(&local->tasklet); +	clear_sta_flag(sta, WLAN_STA_SP);  } -EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe); +EXPORT_SYMBOL(ieee80211_sta_eosp);  void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,  				u8 tid, bool buffered) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4947341a2a8..adc30045f99 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -281,7 +281,6 @@ struct sta_ampdu_mlme {   * @plink_state: peer link state   * @plink_timeout: timeout of peer link   * @plink_timer: peer link watch timer - * @plink_timer_was_running: used by suspend/resume to restore timers   * @t_offset: timing offset relative to this host   * @t_offset_setpoint: reference timing offset of this sta to be used when   * 	calculating clockdrift @@ -334,7 +333,8 @@ struct sta_info {  	unsigned long driver_buffered_tids;  	/* Updated from RX path only, no locking requirements */ -	unsigned long rx_packets, rx_bytes; +	unsigned long rx_packets; +	u64 rx_bytes;  	unsigned long wep_weak_iv_count;  	unsigned long last_rx;  	long last_connected; @@ -354,9 +354,9 @@ struct sta_info {  	unsigned int fail_avg;  	/* Updated from TX path only, no locking requirements */ -	unsigned long tx_packets; -	unsigned long tx_bytes; -	unsigned long tx_fragments; +	u32 tx_fragments; +	u64 tx_packets[IEEE80211_NUM_ACS]; +	u64 tx_bytes[IEEE80211_NUM_ACS];  	struct ieee80211_tx_rate last_tx_rate;  	int last_rx_rate_idx;  	u32 last_rx_rate_flag; @@ -379,7 +379,6 @@ struct sta_info {  	__le16 reason;  	u8 plink_retries;  	bool ignore_plink_timer; -	bool plink_timer_was_running;  	enum nl80211_plink_state plink_state;  	u32 plink_timeout;  	struct timer_list plink_timer; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 3d7cd2a0582..c215fafd7a2 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -28,27 +28,27 @@  #define VIF_PR_FMT	" vif:%s(%d%s)"  #define VIF_PR_ARG	__get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" -#define CHANDEF_ENTRY	__field(u32, control_freq)				\ -			__field(u32, chan_width)				\ -			__field(u32, center_freq1)				\ +#define CHANDEF_ENTRY	__field(u32, control_freq)					\ +			__field(u32, chan_width)					\ +			__field(u32, center_freq1)					\  			__field(u32, center_freq2) -#define CHANDEF_ASSIGN(c)							\ -			__entry->control_freq = (c)->chan->center_freq;		\ -			__entry->chan_width = (c)->width;			\ -			__entry->center_freq1 = (c)->center_freq1;		\ +#define CHANDEF_ASSIGN(c)								\ +			__entry->control_freq = (c)->chan ? (c)->chan->center_freq : 0;	\ +			__entry->chan_width = (c)->width;				\ +			__entry->center_freq1 = (c)->center_freq1;			\  			__entry->center_freq2 = (c)->center_freq2;  #define CHANDEF_PR_FMT	" control:%d MHz width:%d center: %d/%d MHz" -#define CHANDEF_PR_ARG	__entry->control_freq, __entry->chan_width,		\ +#define CHANDEF_PR_ARG	__entry->control_freq, __entry->chan_width,			\  			__entry->center_freq1, __entry->center_freq2 -#define CHANCTX_ENTRY	CHANDEF_ENTRY						\ -			__field(u8, rx_chains_static)				\ +#define CHANCTX_ENTRY	CHANDEF_ENTRY							\ +			__field(u8, rx_chains_static)					\  			__field(u8, rx_chains_dynamic) -#define CHANCTX_ASSIGN	CHANDEF_ASSIGN(&ctx->conf.def)				\ -			__entry->rx_chains_static = ctx->conf.rx_chains_static;	\ +#define CHANCTX_ASSIGN	CHANDEF_ASSIGN(&ctx->conf.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_ARG	CHANDEF_PR_ARG,							\  			__entry->rx_chains_static, __entry->rx_chains_dynamic @@ -286,8 +286,7 @@ TRACE_EVENT(drv_config,  		__field(u16, listen_interval)  		__field(u8, long_frame_max_tx_count)  		__field(u8, short_frame_max_tx_count) -		__field(int, center_freq) -		__field(int, channel_type) +		CHANDEF_ENTRY  		__field(int, smps)  	), @@ -303,15 +302,13 @@ TRACE_EVENT(drv_config,  			local->hw.conf.long_frame_max_tx_count;  		__entry->short_frame_max_tx_count =  			local->hw.conf.short_frame_max_tx_count; -		__entry->center_freq = local->hw.conf.channel ? -					local->hw.conf.channel->center_freq : 0; -		__entry->channel_type = local->hw.conf.channel_type; +		CHANDEF_ASSIGN(&local->hw.conf.chandef)  		__entry->smps = local->hw.conf.smps_mode;  	),  	TP_printk( -		LOCAL_PR_FMT " ch:%#x freq:%d", -		LOCAL_PR_ARG, __entry->changed, __entry->center_freq +		LOCAL_PR_FMT " ch:%#x" CHANDEF_PR_FMT, +		LOCAL_PR_ARG, __entry->changed, CHANDEF_PR_ARG  	)  ); @@ -359,8 +356,7 @@ TRACE_EVENT(drv_bss_info_changed,  		__dynamic_array(u8, ssid, info->ssid_len);  		__field(bool, hidden_ssid);  		__field(int, txpower) -		__field(u8, p2p_ctwindow) -		__field(bool, p2p_oppps) +		__field(u8, p2p_oppps_ctwindow)  	),  	TP_fast_assign( @@ -400,8 +396,7 @@ TRACE_EVENT(drv_bss_info_changed,  		memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);  		__entry->hidden_ssid = info->hidden_ssid;  		__entry->txpower = info->txpower; -		__entry->p2p_ctwindow = info->p2p_ctwindow; -		__entry->p2p_oppps = info->p2p_oppps; +		__entry->p2p_oppps_ctwindow = info->p2p_noa_attr.oppps_ctwindow;  	),  	TP_printk( @@ -431,6 +426,30 @@ 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, @@ -940,23 +959,26 @@ TRACE_EVENT(drv_get_survey,  );  TRACE_EVENT(drv_flush, -	TP_PROTO(struct ieee80211_local *local, bool drop), +	TP_PROTO(struct ieee80211_local *local, +		 u32 queues, bool drop), -	TP_ARGS(local, drop), +	TP_ARGS(local, queues, drop),  	TP_STRUCT__entry(  		LOCAL_ENTRY  		__field(bool, drop) +		__field(u32, queues)  	),  	TP_fast_assign(  		LOCAL_ASSIGN;  		__entry->drop = drop; +		__entry->queues = queues;  	),  	TP_printk( -		LOCAL_PR_FMT " drop:%d", -		LOCAL_PR_ARG, __entry->drop +		LOCAL_PR_FMT " queues:0x%x drop:%d", +		LOCAL_PR_ARG, __entry->queues, __entry->drop  	)  ); @@ -968,23 +990,23 @@ TRACE_EVENT(drv_channel_switch,  	TP_STRUCT__entry(  		LOCAL_ENTRY +		CHANDEF_ENTRY  		__field(u64, timestamp)  		__field(bool, block_tx) -		__field(u16, freq)  		__field(u8, count)  	),  	TP_fast_assign(  		LOCAL_ASSIGN; +		CHANDEF_ASSIGN(&ch_switch->chandef)  		__entry->timestamp = ch_switch->timestamp;  		__entry->block_tx = ch_switch->block_tx; -		__entry->freq = ch_switch->channel->center_freq;  		__entry->count = ch_switch->count;  	),  	TP_printk( -		LOCAL_PR_FMT " new freq:%u count:%d", -		LOCAL_PR_ARG, __entry->freq, __entry->count +		LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d", +		LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count  	)  ); @@ -1042,15 +1064,17 @@ TRACE_EVENT(drv_remain_on_channel,  	TP_PROTO(struct ieee80211_local *local,  		 struct ieee80211_sub_if_data *sdata,  		 struct ieee80211_channel *chan, -		 unsigned int duration), +		 unsigned int duration, +		 enum ieee80211_roc_type type), -	TP_ARGS(local, sdata, chan, duration), +	TP_ARGS(local, sdata, chan, duration, type),  	TP_STRUCT__entry(  		LOCAL_ENTRY  		VIF_ENTRY  		__field(int, center_freq)  		__field(unsigned int, duration) +		__field(u32, type)  	),  	TP_fast_assign( @@ -1058,12 +1082,13 @@ TRACE_EVENT(drv_remain_on_channel,  		VIF_ASSIGN;  		__entry->center_freq = chan->center_freq;  		__entry->duration = duration; +		__entry->type = type;  	),  	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT " freq:%dMHz duration:%dms", +		LOCAL_PR_FMT  VIF_PR_FMT " freq:%dMHz duration:%dms type=%d",  		LOCAL_PR_ARG, VIF_PR_ARG, -		__entry->center_freq, __entry->duration +		__entry->center_freq, __entry->duration, __entry->type  	)  ); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 8914d2d2881..9972e07a2f9 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -48,15 +48,15 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	/* assume HW handles this */ -	if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) +	if (tx->rate.flags & IEEE80211_TX_RC_MCS)  		return 0;  	/* uh huh? */ -	if (WARN_ON_ONCE(info->control.rates[0].idx < 0)) +	if (WARN_ON_ONCE(tx->rate.idx < 0))  		return 0;  	sband = local->hw.wiphy->bands[info->band]; -	txrate = &sband->bitrates[info->control.rates[0].idx]; +	txrate = &sband->bitrates[tx->rate.idx];  	erp = txrate->flags & IEEE80211_RATE_ERP_G; @@ -233,6 +233,7 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)  	if (local->hw.conf.flags & IEEE80211_CONF_PS) {  		ieee80211_stop_queues_by_reason(&local->hw, +						IEEE80211_MAX_QUEUE_MAP,  						IEEE80211_QUEUE_STOP_REASON_PS);  		ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;  		ieee80211_queue_work(&local->hw, @@ -616,11 +617,9 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);  	struct ieee80211_hdr *hdr = (void *)tx->skb->data;  	struct ieee80211_supported_band *sband; -	struct ieee80211_rate *rate; -	int i;  	u32 len; -	bool inval = false, rts = false, short_preamble = false;  	struct ieee80211_tx_rate_control txrc; +	struct ieee80211_sta_rates *ratetbl = NULL;  	bool assoc = false;  	memset(&txrc, 0, sizeof(txrc)); @@ -641,18 +640,23 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  		txrc.max_rate_idx = -1;  	else  		txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; -	memcpy(txrc.rate_idx_mcs_mask, -	       tx->sdata->rc_rateidx_mcs_mask[info->band], -	       sizeof(txrc.rate_idx_mcs_mask)); + +	if (tx->sdata->rc_has_mcs_mask[info->band]) +		txrc.rate_idx_mcs_mask = +			tx->sdata->rc_rateidx_mcs_mask[info->band]; +  	txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP ||  		    tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||  		    tx->sdata->vif.type == NL80211_IFTYPE_ADHOC);  	/* set up RTS protection if desired */  	if (len > tx->local->hw.wiphy->rts_threshold) { -		txrc.rts = rts = true; +		txrc.rts = true;  	} +	info->control.use_rts = txrc.rts; +	info->control.use_cts_prot = tx->sdata->vif.bss_conf.use_cts_prot; +  	/*  	 * Use short preamble if the BSS can handle it, but not for  	 * management frames unless we know the receiver can handle @@ -662,7 +666,9 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  	if (tx->sdata->vif.bss_conf.use_short_preamble &&  	    (ieee80211_is_data(hdr->frame_control) ||  	     (tx->sta && test_sta_flag(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) -		txrc.short_preamble = short_preamble = true; +		txrc.short_preamble = true; + +	info->control.short_preamble = txrc.short_preamble;  	if (tx->sta)  		assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); @@ -686,16 +692,38 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  	 */  	rate_control_get_rate(tx->sdata, tx->sta, &txrc); -	if (unlikely(info->control.rates[0].idx < 0)) -		return TX_DROP; +	if (tx->sta && !info->control.skip_table) +		ratetbl = rcu_dereference(tx->sta->sta.rates); + +	if (unlikely(info->control.rates[0].idx < 0)) { +		if (ratetbl) { +			struct ieee80211_tx_rate rate = { +				.idx = ratetbl->rate[0].idx, +				.flags = ratetbl->rate[0].flags, +				.count = ratetbl->rate[0].count +			}; + +			if (ratetbl->rate[0].idx < 0) +				return TX_DROP; + +			tx->rate = rate; +		} else { +			return TX_DROP; +		} +	} else { +		tx->rate = info->control.rates[0]; +	}  	if (txrc.reported_rate.idx < 0) { -		txrc.reported_rate = info->control.rates[0]; +		txrc.reported_rate = tx->rate;  		if (tx->sta && ieee80211_is_data(hdr->frame_control))  			tx->sta->last_tx_rate = txrc.reported_rate;  	} else if (tx->sta)  		tx->sta->last_tx_rate = txrc.reported_rate; +	if (ratetbl) +		return TX_CONTINUE; +  	if (unlikely(!info->control.rates[0].count))  		info->control.rates[0].count = 1; @@ -703,91 +731,6 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  			 (info->flags & IEEE80211_TX_CTL_NO_ACK)))  		info->control.rates[0].count = 1; -	if (is_multicast_ether_addr(hdr->addr1)) { -		/* -		 * XXX: verify the rate is in the basic rateset -		 */ -		return TX_CONTINUE; -	} - -	/* -	 * set up the RTS/CTS rate as the fastest basic rate -	 * that is not faster than the data rate -	 * -	 * XXX: Should this check all retry rates? -	 */ -	if (!(info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) { -		s8 baserate = 0; - -		rate = &sband->bitrates[info->control.rates[0].idx]; - -		for (i = 0; i < sband->n_bitrates; i++) { -			/* must be a basic rate */ -			if (!(tx->sdata->vif.bss_conf.basic_rates & BIT(i))) -				continue; -			/* must not be faster than the data rate */ -			if (sband->bitrates[i].bitrate > rate->bitrate) -				continue; -			/* maximum */ -			if (sband->bitrates[baserate].bitrate < -			     sband->bitrates[i].bitrate) -				baserate = i; -		} - -		info->control.rts_cts_rate_idx = baserate; -	} - -	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { -		/* -		 * make sure there's no valid rate following -		 * an invalid one, just in case drivers don't -		 * take the API seriously to stop at -1. -		 */ -		if (inval) { -			info->control.rates[i].idx = -1; -			continue; -		} -		if (info->control.rates[i].idx < 0) { -			inval = true; -			continue; -		} - -		/* -		 * For now assume MCS is already set up correctly, this -		 * needs to be fixed. -		 */ -		if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) { -			WARN_ON(info->control.rates[i].idx > 76); -			continue; -		} - -		/* set up RTS protection if desired */ -		if (rts) -			info->control.rates[i].flags |= -				IEEE80211_TX_RC_USE_RTS_CTS; - -		/* RC is busted */ -		if (WARN_ON_ONCE(info->control.rates[i].idx >= -				 sband->n_bitrates)) { -			info->control.rates[i].idx = -1; -			continue; -		} - -		rate = &sband->bitrates[info->control.rates[i].idx]; - -		/* set up short preamble */ -		if (short_preamble && -		    rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) -			info->control.rates[i].flags |= -				IEEE80211_TX_RC_USE_SHORT_PREAMBLE; - -		/* set up G protection */ -		if (!rts && tx->sdata->vif.bss_conf.use_cts_prot && -		    rate->flags & IEEE80211_RATE_ERP_G) -			info->control.rates[i].flags |= -				IEEE80211_TX_RC_USE_CTS_PROTECT; -	} -  	return TX_CONTINUE;  } @@ -991,15 +934,18 @@ static ieee80211_tx_result debug_noinline  ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)  {  	struct sk_buff *skb; +	int ac = -1;  	if (!tx->sta)  		return TX_CONTINUE; -	tx->sta->tx_packets++;  	skb_queue_walk(&tx->skbs, skb) { +		ac = skb_get_queue_mapping(skb);  		tx->sta->tx_fragments++; -		tx->sta->tx_bytes += skb->len; +		tx->sta->tx_bytes[ac] += skb->len;  	} +	if (ac >= 0) +		tx->sta->tx_packets[ac]++;  	return TX_CONTINUE;  } @@ -1705,7 +1651,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,  	if (chanctx_conf)  		chan = chanctx_conf->def.chan;  	else if (!local->use_chanctx) -		chan = local->_oper_channel; +		chan = local->_oper_chandef.chan;  	else  		goto fail_rcu; @@ -1839,7 +1785,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  		 * This is the exception! WDS style interfaces are prohibited  		 * when channel contexts are in used so this must be valid  		 */ -		band = local->hw.conf.channel->band; +		band = local->hw.conf.chandef.chan->band;  		break;  #ifdef CONFIG_MAC80211_MESH  	case NL80211_IFTYPE_MESH_POINT: @@ -2085,7 +2031,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  		encaps_data = bridge_tunnel_header;  		encaps_len = sizeof(bridge_tunnel_header);  		skip_header_bytes -= 2; -	} else if (ethertype >= 0x600) { +	} else if (ethertype >= ETH_P_802_3_MIN) {  		encaps_data = rfc1042_header;  		encaps_len = sizeof(rfc1042_header);  		skip_header_bytes -= 2; @@ -2438,14 +2384,17 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  	} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {  		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  		struct ieee80211_hdr *hdr; -		struct sk_buff *presp = rcu_dereference(ifibss->presp); +		struct beacon_data *presp = rcu_dereference(ifibss->presp);  		if (!presp)  			goto out; -		skb = skb_copy(presp, GFP_ATOMIC); +		skb = dev_alloc_skb(local->tx_headroom + presp->head_len);  		if (!skb)  			goto out; +		skb_reserve(skb, local->tx_headroom); +		memcpy(skb_put(skb, presp->head_len), presp->head, +		       presp->head_len);  		hdr = (struct ieee80211_hdr *) skb->data;  		hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | @@ -2495,8 +2444,6 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  		txrc.max_rate_idx = -1;  	else  		txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; -	memcpy(txrc.rate_idx_mcs_mask, sdata->rc_rateidx_mcs_mask[band], -	       sizeof(txrc.rate_idx_mcs_mask));  	txrc.bss = true;  	rate_control_get_rate(sdata, NULL, &txrc); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0f38f43ac62..3f87fa468b1 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -453,7 +453,8 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,  }  void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, -				    enum queue_stop_reason reason) +				     unsigned long queues, +				     enum queue_stop_reason reason)  {  	struct ieee80211_local *local = hw_to_local(hw);  	unsigned long flags; @@ -461,7 +462,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -	for (i = 0; i < hw->queues; i++) +	for_each_set_bit(i, &queues, hw->queues)  		__ieee80211_stop_queue(hw, i, reason);  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -469,7 +470,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,  void ieee80211_stop_queues(struct ieee80211_hw *hw)  { -	ieee80211_stop_queues_by_reason(hw, +	ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_DRIVER);  }  EXPORT_SYMBOL(ieee80211_stop_queues); @@ -484,13 +485,15 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)  		return true;  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -	ret = !!local->queue_stop_reasons[queue]; +	ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER, +		       &local->queue_stop_reasons[queue]);  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);  	return ret;  }  EXPORT_SYMBOL(ieee80211_queue_stopped);  void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, +				     unsigned long queues,  				     enum queue_stop_reason reason)  {  	struct ieee80211_local *local = hw_to_local(hw); @@ -499,7 +502,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -	for (i = 0; i < hw->queues; i++) +	for_each_set_bit(i, &queues, hw->queues)  		__ieee80211_wake_queue(hw, i, reason);  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -507,10 +510,42 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,  void ieee80211_wake_queues(struct ieee80211_hw *hw)  { -	ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER); +	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_DRIVER);  }  EXPORT_SYMBOL(ieee80211_wake_queues); +void ieee80211_flush_queues(struct ieee80211_local *local, +			    struct ieee80211_sub_if_data *sdata) +{ +	u32 queues; + +	if (!local->ops->flush) +		return; + +	if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) { +		int ac; + +		queues = 0; + +		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) +			queues |= BIT(sdata->vif.hw_queue[ac]); +		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE) +			queues |= BIT(sdata->vif.cab_queue); +	} else { +		/* all queues */ +		queues = BIT(local->hw.queues) - 1; +	} + +	ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_FLUSH); + +	drv_flush(local, 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, @@ -626,7 +661,7 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,  }  EXPORT_SYMBOL(ieee80211_queue_delayed_work); -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, +u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action,  			       struct ieee802_11_elems *elems,  			       u64 filter, u32 crc)  { @@ -634,6 +669,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  	u8 *pos = start;  	bool calc_crc = filter != 0;  	DECLARE_BITMAP(seen_elems, 256); +	const u8 *ie;  	bitmap_zero(seen_elems, 256);  	memset(elems, 0, sizeof(*elems)); @@ -681,6 +717,12 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  		case WLAN_EID_COUNTRY:  		case WLAN_EID_PWR_CONSTRAINT:  		case WLAN_EID_TIMEOUT_INTERVAL: +		case WLAN_EID_SECONDARY_CHANNEL_OFFSET: +		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: +		/* +		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible +		 * that if the content gets bigger it might be needed more than once +		 */  			if (test_bit(id, seen_elems)) {  				elems->parse_error = true;  				left -= elen; @@ -704,17 +746,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			elems->supp_rates = pos;  			elems->supp_rates_len = elen;  			break; -		case WLAN_EID_FH_PARAMS: -			elems->fh_params = pos; -			elems->fh_params_len = elen; -			break;  		case WLAN_EID_DS_PARAMS: -			elems->ds_params = pos; -			elems->ds_params_len = elen; -			break; -		case WLAN_EID_CF_PARAMS: -			elems->cf_params = pos; -			elems->cf_params_len = elen; +			if (elen >= 1) +				elems->ds_params = pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_TIM:  			if (elen >= sizeof(struct ieee80211_tim_ie)) { @@ -723,10 +759,6 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			} else  				elem_parse_failed = true;  			break; -		case WLAN_EID_IBSS_PARAMS: -			elems->ibss_params = pos; -			elems->ibss_params_len = elen; -			break;  		case WLAN_EID_CHALLENGE:  			elems->challenge = pos;  			elems->challenge_len = elen; @@ -756,8 +788,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			elems->rsn_len = elen;  			break;  		case WLAN_EID_ERP_INFO: -			elems->erp_info = pos; -			elems->erp_info_len = elen; +			if (elen >= 1) +				elems->erp_info = pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_EXT_SUPP_RATES:  			elems->ext_supp_rates = pos; @@ -836,12 +870,47 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			}  			elems->ch_switch_ie = (void *)pos;  			break; -		case WLAN_EID_QUIET: -			if (!elems->quiet_elem) { -				elems->quiet_elem = pos; -				elems->quiet_elem_len = elen; +		case WLAN_EID_EXT_CHANSWITCH_ANN: +			if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->ext_chansw_ie = (void *)pos; +			break; +		case WLAN_EID_SECONDARY_CHANNEL_OFFSET: +			if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->sec_chan_offs = (void *)pos; +			break; +		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: +			if (!action || +			    elen != sizeof(*elems->wide_bw_chansw_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->wide_bw_chansw_ie = (void *)pos; +			break; +		case WLAN_EID_CHANNEL_SWITCH_WRAPPER: +			if (action) { +				elem_parse_failed = true; +				break; +			} +			/* +			 * This is a bit tricky, but as we only care about +			 * the wide bandwidth channel switch element, so +			 * just parse it out manually. +			 */ +			ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, +					      pos, elen); +			if (ie) { +				if (ie[1] == sizeof(*elems->wide_bw_chansw_ie)) +					elems->wide_bw_chansw_ie = +						(void *)(ie + 2); +				else +					elem_parse_failed = true;  			} -			elems->num_of_quiet_elem++;  			break;  		case WLAN_EID_COUNTRY:  			elems->country_elem = pos; @@ -855,8 +924,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			elems->pwr_constr_elem = pos;  			break;  		case WLAN_EID_TIMEOUT_INTERVAL: -			elems->timeout_int = pos; -			elems->timeout_int_len = elen; +			if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) +				elems->timeout_int = (void *)pos; +			else +				elem_parse_failed = true;  			break;  		default:  			break; @@ -877,12 +948,6 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  	return crc;  } -void ieee802_11_parse_elems(u8 *start, size_t len, -			    struct ieee802_11_elems *elems) -{ -	ieee802_11_parse_elems_crc(start, len, elems, 0, 0); -} -  void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,  			       bool bss_notify)  { @@ -1357,6 +1422,25 @@ void ieee80211_stop_device(struct ieee80211_local *local)  	drv_stop(local);  } +static void ieee80211_assign_chanctx(struct ieee80211_local *local, +				     struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *ctx; + +	if (!local->use_chanctx) +		return; + +	mutex_lock(&local->chanctx_mtx); +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (conf) { +		ctx = container_of(conf, struct ieee80211_chanctx, conf); +		drv_assign_vif_chanctx(local, sdata, ctx); +	} +	mutex_unlock(&local->chanctx_mtx); +} +  int ieee80211_reconfig(struct ieee80211_local *local)  {  	struct ieee80211_hw *hw = &local->hw; @@ -1421,6 +1505,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	/* add interfaces */  	sdata = rtnl_dereference(local->monitor_sdata);  	if (sdata) { +		/* in HW restart it exists already */ +		WARN_ON(local->resuming);  		res = drv_add_interface(local, sdata);  		if (WARN_ON(res)) {  			rcu_assign_pointer(local->monitor_sdata, NULL); @@ -1445,36 +1531,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	}  	list_for_each_entry(sdata, &local->interfaces, list) { -		struct ieee80211_chanctx_conf *ctx_conf; -  		if (!ieee80211_sdata_running(sdata))  			continue; - -		mutex_lock(&local->chanctx_mtx); -		ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, -				lockdep_is_held(&local->chanctx_mtx)); -		if (ctx_conf) { -			ctx = container_of(ctx_conf, struct ieee80211_chanctx, -					   conf); -			drv_assign_vif_chanctx(local, sdata, ctx); -		} -		mutex_unlock(&local->chanctx_mtx); +		ieee80211_assign_chanctx(local, sdata);  	}  	sdata = rtnl_dereference(local->monitor_sdata); -	if (sdata && local->use_chanctx && ieee80211_sdata_running(sdata)) { -		struct ieee80211_chanctx_conf *ctx_conf; - -		mutex_lock(&local->chanctx_mtx); -		ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, -				lockdep_is_held(&local->chanctx_mtx)); -		if (ctx_conf) { -			ctx = container_of(ctx_conf, struct ieee80211_chanctx, -					   conf); -			drv_assign_vif_chanctx(local, sdata, ctx); -		} -		mutex_unlock(&local->chanctx_mtx); -	} +	if (sdata && ieee80211_sdata_running(sdata)) +		ieee80211_assign_chanctx(local, sdata);  	/* add STAs back */  	mutex_lock(&local->sta_mtx); @@ -1534,11 +1598,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)  			  BSS_CHANGED_IDLE |  			  BSS_CHANGED_TXPOWER; -#ifdef CONFIG_PM -		if (local->resuming && !reconfig_due_to_wowlan) -			sdata->vif.bss_conf = sdata->suspend_bss_conf; -#endif -  		switch (sdata->vif.type) {  		case NL80211_IFTYPE_STATION:  			changed |= BSS_CHANGED_ASSOC | @@ -1637,6 +1696,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	local->in_reconfig = false;  	barrier(); +	if (local->monitors == local->open_count && local->monitors > 0) +		ieee80211_add_virtual_monitor(local); +  	/*  	 * Clear the WLAN_STA_BLOCK_BA flag so new aggregation  	 * sessions can be established after a resume. @@ -1659,8 +1721,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)  		mutex_unlock(&local->sta_mtx);  	} -	ieee80211_wake_queues_by_reason(hw, -			IEEE80211_QUEUE_STOP_REASON_SUSPEND); +	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_SUSPEND);  	/*  	 * If this is for hw restart things are still running. @@ -1678,28 +1740,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	mb();  	local->resuming = false; -	list_for_each_entry(sdata, &local->interfaces, list) { -		switch(sdata->vif.type) { -		case NL80211_IFTYPE_STATION: -			ieee80211_sta_restart(sdata); -			break; -		case NL80211_IFTYPE_ADHOC: -			ieee80211_ibss_restart(sdata); -			break; -		case NL80211_IFTYPE_MESH_POINT: -			ieee80211_mesh_restart(sdata); -			break; -		default: -			break; -		} -	} -  	mod_timer(&local->sta_cleanup, jiffies + 1); - -	mutex_lock(&local->sta_mtx); -	list_for_each_entry(sta, &local->sta_list, list) -		mesh_plink_restart(sta); -	mutex_unlock(&local->sta_mtx);  #else  	WARN_ON(1);  #endif @@ -2051,7 +2092,7 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif)  		/* non-managed type inferfaces */  		return 0;  	} -	return ifmgd->ave_beacon_signal; +	return ifmgd->ave_beacon_signal / 16;  }  EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); @@ -2166,8 +2207,7 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work)  		/* currently not handled */  		WARN_ON(1);  	else { -		cfg80211_chandef_create(&chandef, local->hw.conf.channel, -					local->hw.conf.channel_type); +		chandef = local->hw.conf.chandef;  		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);  	}  } diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index a2c2258bc84..171344d4eb7 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -13,6 +13,104 @@  #include "rate.h" +static void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata, +				   struct ieee80211_sta_vht_cap *vht_cap, +				   u32 flag) +{ +	__le32 le_flag = cpu_to_le32(flag); + +	if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag && +	    !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag)) +		vht_cap->cap &= ~flag; +} + +void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, +				      struct ieee80211_sta_vht_cap *vht_cap) +{ +	int i; +	u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n; + +	if (!vht_cap->vht_supported) +		return; + +	if (sdata->vif.type != NL80211_IFTYPE_STATION) +		return; + +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_RXLDPC); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_SHORT_GI_80); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_SHORT_GI_160); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_TXSTBC); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN); + +	/* Allow user to decrease AMPDU length exponent */ +	if (sdata->u.mgd.vht_capa_mask.vht_cap_info & +	    cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) { +		u32 cap, n; + +		n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) & +			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; +		n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; +		cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; +		cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + +		if (n < cap) { +			vht_cap->cap &= +				~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; +			vht_cap->cap |= +				n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; +		} +	} + +	/* Allow the user to decrease MCSes */ +	rxmcs_mask = +		le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map); +	rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map); +	rxmcs_n &= rxmcs_mask; +	rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); + +	txmcs_mask = +		le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map); +	txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map); +	txmcs_n &= txmcs_mask; +	txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); +	for (i = 0; i < 8; i++) { +		u8 m, n, c; + +		m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; +		n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; +		c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || +			  n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { +			rxmcs_cap &= ~(3 << 2*i); +			rxmcs_cap |= (rxmcs_n & (3 << 2*i)); +		} + +		m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; +		n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; +		c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || +			  n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { +			txmcs_cap &= ~(3 << 2*i); +			txmcs_cap |= (txmcs_n & (3 << 2*i)); +		} +	} +	vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap); +	vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap); +} +  void  ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,  				    struct ieee80211_supported_band *sband, @@ -20,6 +118,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,  				    struct sta_info *sta)  {  	struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; +	struct ieee80211_sta_vht_cap own_cap; +	u32 cap_info, i;  	memset(vht_cap, 0, sizeof(*vht_cap)); @@ -35,12 +135,122 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,  	vht_cap->vht_supported = true; -	vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); +	own_cap = sband->vht_cap; +	/* +	 * If user has specified capability overrides, take care +	 * of that if the station we're setting up is the AP that +	 * we advertised a restricted capability set to. Override +	 * our own capabilities and then use those below. +	 */ +	if (sdata->vif.type == NL80211_IFTYPE_STATION && +	    !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) +		ieee80211_apply_vhtcap_overrides(sdata, &own_cap); + +	/* take some capabilities as-is */ +	cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info); +	vht_cap->cap = cap_info; +	vht_cap->cap &= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 | +			IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 | +			IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | +			IEEE80211_VHT_CAP_RXLDPC | +			IEEE80211_VHT_CAP_VHT_TXOP_PS | +			IEEE80211_VHT_CAP_HTC_VHT | +			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | +			IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB | +			IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB | +			IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | +			IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; + +	/* and some based on our own capabilities */ +	switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { +	case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; +		break; +	case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; +		break; +	default: +		/* nothing */ +		break; +	} + +	/* symmetric capabilities */ +	vht_cap->cap |= cap_info & own_cap.cap & +			(IEEE80211_VHT_CAP_SHORT_GI_80 | +			 IEEE80211_VHT_CAP_SHORT_GI_160); + +	/* remaining ones */ +	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); +	} + +	if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + +	if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + +	if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; + +	if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC) +		vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK; + +	if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) +		vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC;  	/* Copy peer MCS info, the driver might need them. */  	memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,  	       sizeof(struct ieee80211_vht_mcs_info)); +	/* but also restrict MCSes */ +	for (i = 0; i < 8; i++) { +		u16 own_rx, own_tx, peer_rx, peer_tx; + +		own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map); +		own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map); +		own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); +		peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); +		peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { +			if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED) +				peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; +			else if (own_rx < peer_tx) +				peer_tx = own_rx; +		} + +		if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { +			if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED) +				peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; +			else if (own_tx < peer_rx) +				peer_rx = own_tx; +		} + +		vht_cap->vht_mcs.rx_mcs_map &= +			~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); +		vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2); + +		vht_cap->vht_mcs.tx_mcs_map &= +			~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); +		vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2); +	} + +	/* finally set up the bandwidth */  	switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {  	case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:  	case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:  |