diff options
Diffstat (limited to 'net/mac80211/iface.c')
| -rw-r--r-- | net/mac80211/iface.c | 68 | 
1 files changed, 44 insertions, 24 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 2c059e54e88..9ed49ad0380 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) +static u32 __ieee80211_idle_off(struct ieee80211_local *local)  {  	if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE))  		return 0; @@ -87,7 +87,7 @@ static u32 ieee80211_idle_off(struct ieee80211_local *local)  	return IEEE80211_CONF_CHANGE_IDLE;  } -static u32 ieee80211_idle_on(struct ieee80211_local *local) +static u32 __ieee80211_idle_on(struct ieee80211_local *local)  {  	if (local->hw.conf.flags & IEEE80211_CONF_IDLE)  		return 0; @@ -98,16 +98,18 @@ static u32 ieee80211_idle_on(struct ieee80211_local *local)  	return IEEE80211_CONF_CHANGE_IDLE;  } -void ieee80211_recalc_idle(struct ieee80211_local *local) +static u32 __ieee80211_recalc_idle(struct ieee80211_local *local, +				   bool force_active)  {  	bool working = false, scanning, active;  	unsigned int led_trig_start = 0, led_trig_stop = 0;  	struct ieee80211_roc_work *roc; -	u32 change;  	lockdep_assert_held(&local->mtx); -	active = !list_empty(&local->chanctx_list); +	active = force_active || +		 !list_empty(&local->chanctx_list) || +		 local->monitors;  	if (!local->ops->remain_on_channel) {  		list_for_each_entry(roc, &local->roc_list, list) { @@ -132,9 +134,18 @@ void ieee80211_recalc_idle(struct ieee80211_local *local)  	ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop);  	if (working || scanning || active) -		change = ieee80211_idle_off(local); -	else -		change = ieee80211_idle_on(local); +		return __ieee80211_idle_off(local); +	return __ieee80211_idle_on(local); +} + +u32 ieee80211_idle_off(struct ieee80211_local *local) +{ +	return __ieee80211_recalc_idle(local, true); +} + +void ieee80211_recalc_idle(struct ieee80211_local *local) +{ +	u32 change = __ieee80211_recalc_idle(local, false);  	if (change)  		ieee80211_hw_config(local, change);  } @@ -349,21 +360,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 +386,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 +400,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 +417,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 +438,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 +555,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 +829,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 */  |