diff options
Diffstat (limited to 'net/mac80211')
| -rw-r--r-- | net/mac80211/cfg.c | 17 | ||||
| -rw-r--r-- | net/mac80211/chan.c | 17 | ||||
| -rw-r--r-- | net/mac80211/ieee80211_i.h | 4 | ||||
| -rw-r--r-- | net/mac80211/iface.c | 45 | ||||
| -rw-r--r-- | net/mac80211/mesh.c | 3 | ||||
| -rw-r--r-- | net/mac80211/mlme.c | 34 | ||||
| -rw-r--r-- | net/mac80211/offchannel.c | 23 | ||||
| -rw-r--r-- | net/mac80211/rx.c | 14 | ||||
| -rw-r--r-- | net/mac80211/sta_info.c | 12 | ||||
| -rw-r--r-- | net/mac80211/tx.c | 80 | 
10 files changed, 179 insertions, 70 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 09d96a8f6c2..a6893602f87 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2582,7 +2582,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,  			list_del(&dep->list);  			mutex_unlock(&local->mtx); -			ieee80211_roc_notify_destroy(dep); +			ieee80211_roc_notify_destroy(dep, true);  			return 0;  		} @@ -2622,7 +2622,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,  			ieee80211_start_next_roc(local);  		mutex_unlock(&local->mtx); -		ieee80211_roc_notify_destroy(found); +		ieee80211_roc_notify_destroy(found, true);  	} else {  		/* work may be pending so use it all the time */  		found->abort = true; @@ -2632,6 +2632,8 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,  		/* work will clean up etc */  		flush_delayed_work(&found->work); +		WARN_ON(!found->to_be_freed); +		kfree(found);  	}  	return 0; @@ -3285,6 +3287,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,  				     struct cfg80211_chan_def *chandef)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	struct ieee80211_local *local = wiphy_priv(wiphy);  	struct ieee80211_chanctx_conf *chanctx_conf;  	int ret = -ENODATA; @@ -3293,6 +3296,16 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,  	if (chanctx_conf) {  		*chandef = chanctx_conf->def;  		ret = 0; +	} else if (local->open_count > 0 && +		   local->open_count == local->monitors && +		   sdata->vif.type == NL80211_IFTYPE_MONITOR) { +		if (local->use_chanctx) +			*chandef = local->monitor_chandef; +		else +			cfg80211_chandef_create(chandef, +						local->_oper_channel, +						local->_oper_channel_type); +		ret = 0;  	}  	rcu_read_unlock(); diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 78c0d90dd64..931be419ab5 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -63,6 +63,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local,  		      enum ieee80211_chanctx_mode mode)  {  	struct ieee80211_chanctx *ctx; +	u32 changed;  	int err;  	lockdep_assert_held(&local->chanctx_mtx); @@ -76,6 +77,13 @@ ieee80211_new_chanctx(struct ieee80211_local *local,  	ctx->conf.rx_chains_dynamic = 1;  	ctx->mode = mode; +	/* acquire mutex to prevent idle from changing */ +	mutex_lock(&local->mtx); +	/* turn idle off *before* setting channel -- some drivers need that */ +	changed = ieee80211_idle_off(local); +	if (changed) +		ieee80211_hw_config(local, changed); +  	if (!local->use_chanctx) {  		local->_oper_channel_type =  			cfg80211_get_chandef_type(chandef); @@ -85,14 +93,17 @@ ieee80211_new_chanctx(struct ieee80211_local *local,  		err = drv_add_chanctx(local, ctx);  		if (err) {  			kfree(ctx); -			return ERR_PTR(err); +			ctx = ERR_PTR(err); + +			ieee80211_recalc_idle(local); +			goto out;  		}  	} +	/* and keep the mutex held until the new chanctx is on the list */  	list_add_rcu(&ctx->list, &local->chanctx_list); -	mutex_lock(&local->mtx); -	ieee80211_recalc_idle(local); + out:  	mutex_unlock(&local->mtx);  	return ctx; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 388580a1bad..5672533a083 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -309,6 +309,7 @@ struct ieee80211_roc_work {  	struct ieee80211_channel *chan;  	bool started, abort, hw_begun, notified; +	bool to_be_freed;  	unsigned long hw_start_time; @@ -1347,7 +1348,7 @@ 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_notify_destroy(struct ieee80211_roc_work *roc); +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); @@ -1361,6 +1362,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,  			     enum nl80211_iftype type);  void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);  void ieee80211_remove_interfaces(struct ieee80211_local *local); +u32 ieee80211_idle_off(struct ieee80211_local *local);  void ieee80211_recalc_idle(struct ieee80211_local *local);  void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,  				    const int offset); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 2c059e54e88..58150f877ec 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -78,7 +78,7 @@ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)  		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);  } -static u32 ieee80211_idle_off(struct ieee80211_local *local) +u32 ieee80211_idle_off(struct ieee80211_local *local)  {  	if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE))  		return 0; @@ -107,7 +107,7 @@ void ieee80211_recalc_idle(struct ieee80211_local *local)  	lockdep_assert_held(&local->mtx); -	active = !list_empty(&local->chanctx_list); +	active = !list_empty(&local->chanctx_list) || local->monitors;  	if (!local->ops->remain_on_channel) {  		list_for_each_entry(roc, &local->roc_list, list) { @@ -349,21 +349,19 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)  static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata; -	int ret = 0; +	int ret;  	if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))  		return 0; -	mutex_lock(&local->iflist_mtx); +	ASSERT_RTNL();  	if (local->monitor_sdata) -		goto out_unlock; +		return 0;  	sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); -	if (!sdata) { -		ret = -ENOMEM; -		goto out_unlock; -	} +	if (!sdata) +		return -ENOMEM;  	/* set up data */  	sdata->local = local; @@ -377,13 +375,13 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)  	if (WARN_ON(ret)) {  		/* ok .. stupid driver, it asked for this! */  		kfree(sdata); -		goto out_unlock; +		return ret;  	}  	ret = ieee80211_check_queues(sdata);  	if (ret) {  		kfree(sdata); -		goto out_unlock; +		return ret;  	}  	ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef, @@ -391,13 +389,14 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)  	if (ret) {  		drv_remove_interface(local, sdata);  		kfree(sdata); -		goto out_unlock; +		return ret;  	} +	mutex_lock(&local->iflist_mtx);  	rcu_assign_pointer(local->monitor_sdata, sdata); - out_unlock:  	mutex_unlock(&local->iflist_mtx); -	return ret; + +	return 0;  }  static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) @@ -407,14 +406,20 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)  	if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))  		return; +	ASSERT_RTNL(); +  	mutex_lock(&local->iflist_mtx);  	sdata = rcu_dereference_protected(local->monitor_sdata,  					  lockdep_is_held(&local->iflist_mtx)); -	if (!sdata) -		goto out_unlock; +	if (!sdata) { +		mutex_unlock(&local->iflist_mtx); +		return; +	}  	rcu_assign_pointer(local->monitor_sdata, NULL); +	mutex_unlock(&local->iflist_mtx); +  	synchronize_net();  	ieee80211_vif_release_channel(sdata); @@ -422,8 +427,6 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)  	drv_remove_interface(local, sdata);  	kfree(sdata); - out_unlock: -	mutex_unlock(&local->iflist_mtx);  }  /* @@ -541,6 +544,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)  		ieee80211_adjust_monitor_flags(sdata, 1);  		ieee80211_configure_filter(local); +		mutex_lock(&local->mtx); +		ieee80211_recalc_idle(local); +		mutex_unlock(&local->mtx);  		netif_carrier_on(dev);  		break; @@ -812,6 +818,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  		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 */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 29ce2aa87e7..4749b385869 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1060,7 +1060,8 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)  	rcu_read_lock();  	list_for_each_entry_rcu(sdata, &local->interfaces, list) -		if (ieee80211_vif_is_mesh(&sdata->vif)) +		if (ieee80211_vif_is_mesh(&sdata->vif) && +		    ieee80211_sdata_running(sdata))  			ieee80211_queue_work(&local->hw, &sdata->work);  	rcu_read_unlock();  } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9f6464f3e05..82cc30318a8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -647,6 +647,9 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,  		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) @@ -3503,6 +3506,14 @@ 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. @@ -3516,13 +3527,9 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)  	if (del_timer_sync(&ifmgd->timer))  		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); -	cancel_work_sync(&ifmgd->chswitch_work);  	if (del_timer_sync(&ifmgd->chswitch_timer))  		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); - -	/* these will just be re-established on connection */ -	del_timer_sync(&ifmgd->conn_mon_timer); -	del_timer_sync(&ifmgd->bcn_mon_timer); +	cancel_work_sync(&ifmgd->chswitch_work);  }  void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) @@ -3601,8 +3608,10 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)  	/* Restart STA timers */  	rcu_read_lock(); -	list_for_each_entry_rcu(sdata, &local->interfaces, list) -		ieee80211_restart_sta_timer(sdata); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		if (ieee80211_sdata_running(sdata)) +			ieee80211_restart_sta_timer(sdata); +	}  	rcu_read_unlock();  } @@ -4315,6 +4324,17 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	/* +	 * Make sure some work items will not run after this, +	 * they will not do anything but might not have been +	 * cancelled when disconnecting. +	 */ +	cancel_work_sync(&ifmgd->monitor_work); +	cancel_work_sync(&ifmgd->beacon_connection_loss_work); +	cancel_work_sync(&ifmgd->request_smps_work); +	cancel_work_sync(&ifmgd->csa_connection_drop_work); +	cancel_work_sync(&ifmgd->chswitch_work); +  	mutex_lock(&ifmgd->mtx);  	if (ifmgd->assoc_data)  		ieee80211_destroy_assoc_data(sdata, false); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index cc79b4a2e82..430bd254e49 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -297,10 +297,13 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)  	}  } -void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) +void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free)  {  	struct ieee80211_roc_work *dep, *tmp; +	if (WARN_ON(roc->to_be_freed)) +		return; +  	/* was never transmitted */  	if (roc->frame) {  		cfg80211_mgmt_tx_status(&roc->sdata->wdev, @@ -316,9 +319,12 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)  						   GFP_KERNEL);  	list_for_each_entry_safe(dep, tmp, &roc->dependents, list) -		ieee80211_roc_notify_destroy(dep); +		ieee80211_roc_notify_destroy(dep, true); -	kfree(roc); +	if (free) +		kfree(roc); +	else +		roc->to_be_freed = true;  }  void ieee80211_sw_roc_work(struct work_struct *work) @@ -331,6 +337,9 @@ void ieee80211_sw_roc_work(struct work_struct *work)  	mutex_lock(&local->mtx); +	if (roc->to_be_freed) +		goto out_unlock; +  	if (roc->abort)  		goto finish; @@ -370,7 +379,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)   finish:  		list_del(&roc->list);  		started = roc->started; -		ieee80211_roc_notify_destroy(roc); +		ieee80211_roc_notify_destroy(roc, !roc->abort);  		if (started) {  			drv_flush(local, false); @@ -410,7 +419,7 @@ static void ieee80211_hw_roc_done(struct work_struct *work)  	list_del(&roc->list); -	ieee80211_roc_notify_destroy(roc); +	ieee80211_roc_notify_destroy(roc, true);  	/* if there's another roc, start it now */  	ieee80211_start_next_roc(local); @@ -460,12 +469,14 @@ void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata)  	list_for_each_entry_safe(roc, tmp, &tmp_list, list) {  		if (local->ops->remain_on_channel) {  			list_del(&roc->list); -			ieee80211_roc_notify_destroy(roc); +			ieee80211_roc_notify_destroy(roc, true);  		} else {  			ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);  			/* work will clean up etc */  			flush_delayed_work(&roc->work); +			WARN_ON(!roc->to_be_freed); +			kfree(roc);  		}  	} diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index bb73ed2d20b..c6844ad080b 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2675,7 +2675,19 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)  		memset(nskb->cb, 0, sizeof(nskb->cb)); -		ieee80211_tx_skb(rx->sdata, nskb); +		if (rx->sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { +			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(nskb); + +			info->flags = IEEE80211_TX_CTL_TX_OFFCHAN | +				      IEEE80211_TX_INTFL_OFFCHAN_TX_OK | +				      IEEE80211_TX_CTL_NO_CCK_RATE; +			if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) +				info->hw_queue = +					local->hw.offchannel_tx_hw_queue; +		} + +		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, +					    status->band);  	}  	dev_kfree_skb(rx->skb);  	return RX_QUEUED; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index a79ce820cb5..238a0cca320 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -766,6 +766,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;  	might_sleep(); @@ -793,12 +794,19 @@ 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++) +	for (i = 0; i < NUM_DEFAULT_KEYS; i++) {  		__ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i])); -	if (sta->ptk) +		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(); +  	sta->dead = true;  	local->num_sta--; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index de8548bf0a7..8914d2d2881 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1231,34 +1231,40 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,  		if (local->queue_stop_reasons[q] ||  		    (!txpending && !skb_queue_empty(&local->pending[q]))) {  			if (unlikely(info->flags & -					IEEE80211_TX_INTFL_OFFCHAN_TX_OK && -				     local->queue_stop_reasons[q] & -					~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL))) { +				     IEEE80211_TX_INTFL_OFFCHAN_TX_OK)) { +				if (local->queue_stop_reasons[q] & +				    ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL)) { +					/* +					 * Drop off-channel frames if queues +					 * are stopped for any reason other +					 * than off-channel operation. Never +					 * queue them. +					 */ +					spin_unlock_irqrestore( +						&local->queue_stop_reason_lock, +						flags); +					ieee80211_purge_tx_queue(&local->hw, +								 skbs); +					return true; +				} +			} else { +  				/* -				 * Drop off-channel frames if queues are stopped -				 * for any reason other than off-channel -				 * operation. Never queue them. +				 * Since queue is stopped, queue up frames for +				 * later transmission from the tx-pending +				 * tasklet when the queue is woken again.  				 */ -				spin_unlock_irqrestore( -					&local->queue_stop_reason_lock, flags); -				ieee80211_purge_tx_queue(&local->hw, skbs); -				return true; -			} - -			/* -			 * Since queue is stopped, queue up frames for later -			 * transmission from the tx-pending tasklet when the -			 * queue is woken again. -			 */ -			if (txpending) -				skb_queue_splice_init(skbs, &local->pending[q]); -			else -				skb_queue_splice_tail_init(skbs, -							   &local->pending[q]); +				if (txpending) +					skb_queue_splice_init(skbs, +							      &local->pending[q]); +				else +					skb_queue_splice_tail_init(skbs, +								   &local->pending[q]); -			spin_unlock_irqrestore(&local->queue_stop_reason_lock, -					       flags); -			return false; +				spin_unlock_irqrestore(&local->queue_stop_reason_lock, +						       flags); +				return false; +			}  		}  		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -1844,9 +1850,24 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  		}  		if (!is_multicast_ether_addr(skb->data)) { +			struct sta_info *next_hop; +			bool mpp_lookup = true; +  			mpath = mesh_path_lookup(sdata, skb->data); -			if (!mpath) +			if (mpath) { +				mpp_lookup = false; +				next_hop = rcu_dereference(mpath->next_hop); +				if (!next_hop || +				    !(mpath->flags & (MESH_PATH_ACTIVE | +						      MESH_PATH_RESOLVING))) +					mpp_lookup = true; +			} + +			if (mpp_lookup)  				mppath = mpp_path_lookup(sdata, skb->data); + +			if (mppath && mpath) +				mesh_path_del(mpath->sdata, mpath->dst);  		}  		/* @@ -2350,9 +2371,9 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,  	if (local->tim_in_locked_section) {  		__ieee80211_beacon_add_tim(sdata, ps, skb);  	} else { -		spin_lock(&local->tim_lock); +		spin_lock_bh(&local->tim_lock);  		__ieee80211_beacon_add_tim(sdata, ps, skb); -		spin_unlock(&local->tim_lock); +		spin_unlock_bh(&local->tim_lock);  	}  	return 0; @@ -2724,7 +2745,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,  				cpu_to_le16(IEEE80211_FCTL_MOREDATA);  		} -		sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev); +		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +			sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);  		if (!ieee80211_tx_prepare(sdata, &tx, skb))  			break;  		dev_kfree_skb_any(skb);  |