diff options
Diffstat (limited to 'net/wireless/core.c')
| -rw-r--r-- | net/wireless/core.c | 172 | 
1 files changed, 127 insertions, 45 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index fe8d4f2be49..a1c40654dd9 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -69,7 +69,7 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)  int get_wiphy_idx(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	return rdev->wiphy_idx;  } @@ -130,7 +130,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,  			    newname))  		pr_err("failed to rename debugfs dir to %s!\n", newname); -	nl80211_notify_dev_rename(rdev); +	nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);  	return 0;  } @@ -204,27 +204,18 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,  	rdev->opencount--;  	if (rdev->scan_req && rdev->scan_req->wdev == wdev) { -		/* -		 * If the scan request wasn't notified as done, set it -		 * to aborted and leak it after a warning. The driver -		 * should have notified us that it ended at the latest -		 * during rdev_stop_p2p_device(). -		 */  		if (WARN_ON(!rdev->scan_req->notified))  			rdev->scan_req->aborted = true; -		___cfg80211_scan_done(rdev, !rdev->scan_req->notified); +		___cfg80211_scan_done(rdev, false);  	}  } -static int cfg80211_rfkill_set_block(void *data, bool blocked) +void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = data; +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct wireless_dev *wdev; -	if (!blocked) -		return 0; - -	rtnl_lock(); +	ASSERT_RTNL();  	list_for_each_entry(wdev, &rdev->wdev_list, list) {  		if (wdev->netdev) { @@ -240,7 +231,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)  			break;  		}  	} +} +EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces); + +static int cfg80211_rfkill_set_block(void *data, bool blocked) +{ +	struct cfg80211_registered_device *rdev = data; + +	if (!blocked) +		return 0; +	rtnl_lock(); +	cfg80211_shutdown_all_interfaces(&rdev->wiphy);  	rtnl_unlock();  	return 0; @@ -266,6 +268,45 @@ static void cfg80211_event_work(struct work_struct *work)  	rtnl_unlock();  } +void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) +{ +	struct cfg80211_iface_destroy *item; + +	ASSERT_RTNL(); + +	spin_lock_irq(&rdev->destroy_list_lock); +	while ((item = list_first_entry_or_null(&rdev->destroy_list, +						struct cfg80211_iface_destroy, +						list))) { +		struct wireless_dev *wdev, *tmp; +		u32 nlportid = item->nlportid; + +		list_del(&item->list); +		kfree(item); +		spin_unlock_irq(&rdev->destroy_list_lock); + +		list_for_each_entry_safe(wdev, tmp, &rdev->wdev_list, list) { +			if (nlportid == wdev->owner_nlportid) +				rdev_del_virtual_intf(rdev, wdev); +		} + +		spin_lock_irq(&rdev->destroy_list_lock); +	} +	spin_unlock_irq(&rdev->destroy_list_lock); +} + +static void cfg80211_destroy_iface_wk(struct work_struct *work) +{ +	struct cfg80211_registered_device *rdev; + +	rdev = container_of(work, struct cfg80211_registered_device, +			    destroy_work); + +	rtnl_lock(); +	cfg80211_destroy_ifaces(rdev); +	rtnl_unlock(); +} +  /* exported functions */  struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) @@ -324,6 +365,10 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)  	rdev->wiphy.dev.class = &ieee80211_class;  	rdev->wiphy.dev.platform_data = rdev; +	INIT_LIST_HEAD(&rdev->destroy_list); +	spin_lock_init(&rdev->destroy_list_lock); +	INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); +  #ifdef CONFIG_CFG80211_DEFAULT_PS  	rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;  #endif @@ -357,7 +402,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)  	rdev->wiphy.rts_threshold = (u32) -1;  	rdev->wiphy.coverage_class = 0; -	rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH; +	rdev->wiphy.max_num_csa_counters = 1;  	return &rdev->wiphy;  } @@ -404,10 +449,7 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)  		for (j = 0; j < c->n_limits; j++) {  			u16 types = c->limits[j].types; -			/* -			 * interface types shouldn't overlap, this is -			 * used in cfg80211_can_change_interface() -			 */ +			/* interface types shouldn't overlap */  			if (WARN_ON(types & all_iftypes))  				return -EINVAL;  			all_iftypes |= types; @@ -443,7 +485,7 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)  int wiphy_register(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	int res;  	enum ieee80211_band band;  	struct ieee80211_supported_band *sband; @@ -451,6 +493,12 @@ int wiphy_register(struct wiphy *wiphy)  	int i;  	u16 ifmodes = wiphy->interface_modes; +	/* +	 * There are major locking problems in nl80211/mac80211 for CSA, +	 * disable for all drivers until this has been reworked. +	 */ +	wiphy->flags &= ~WIPHY_FLAG_HAS_CHANNEL_SWITCH; +  #ifdef CONFIG_PM  	if (WARN_ON(wiphy->wowlan &&  		    (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && @@ -566,6 +614,8 @@ int wiphy_register(struct wiphy *wiphy)  	/* check and set up bitrates */  	ieee80211_set_bitrate_flags(wiphy); +	rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH; +  	rtnl_lock();  	res = device_add(&rdev->wiphy.dev);  	if (res) { @@ -586,7 +636,7 @@ int wiphy_register(struct wiphy *wiphy)  	if (IS_ERR(rdev->wiphy.debugfsdir))  		rdev->wiphy.debugfsdir = NULL; -	if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { +	if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {  		struct regulatory_request request;  		request.wiphy_idx = get_wiphy_idx(wiphy); @@ -610,13 +660,15 @@ int wiphy_register(struct wiphy *wiphy)  		return res;  	} +	nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); +  	return 0;  }  EXPORT_SYMBOL(wiphy_register);  void wiphy_rfkill_start_polling(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	if (!rdev->ops->rfkill_poll)  		return; @@ -627,7 +679,7 @@ EXPORT_SYMBOL(wiphy_rfkill_start_polling);  void wiphy_rfkill_stop_polling(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	rfkill_pause_polling(rdev->rfkill);  } @@ -635,7 +687,7 @@ EXPORT_SYMBOL(wiphy_rfkill_stop_polling);  void wiphy_unregister(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	wait_event(rdev->dev_wait, ({  		int __count; @@ -648,9 +700,10 @@ void wiphy_unregister(struct wiphy *wiphy)  		rfkill_unregister(rdev->rfkill);  	rtnl_lock(); +	nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);  	rdev->wiphy.registered = false; -	BUG_ON(!list_empty(&rdev->wdev_list)); +	WARN_ON(!list_empty(&rdev->wdev_list));  	/*  	 * First remove the hardware from everywhere, this makes @@ -675,6 +728,7 @@ void wiphy_unregister(struct wiphy *wiphy)  	cancel_work_sync(&rdev->conn_work);  	flush_work(&rdev->event_work);  	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); +	flush_work(&rdev->destroy_work);  #ifdef CONFIG_PM  	if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) @@ -707,7 +761,7 @@ EXPORT_SYMBOL(wiphy_free);  void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	if (rfkill_set_hw_state(rdev->rfkill, blocked))  		schedule_work(&rdev->rfkill_sync); @@ -716,7 +770,7 @@ EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);  void cfg80211_unregister_wdev(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	ASSERT_RTNL(); @@ -737,7 +791,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)  }  EXPORT_SYMBOL(cfg80211_unregister_wdev); -static struct device_type wiphy_type = { +static const struct device_type wiphy_type = {  	.name	= "wlan",  }; @@ -751,20 +805,23 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,  		rdev->num_running_monitor_ifaces += num;  } -void cfg80211_leave(struct cfg80211_registered_device *rdev, -		    struct wireless_dev *wdev) +void __cfg80211_leave(struct cfg80211_registered_device *rdev, +		      struct wireless_dev *wdev)  {  	struct net_device *dev = wdev->netdev; +	ASSERT_RTNL(); +	ASSERT_WDEV_LOCK(wdev); +  	switch (wdev->iftype) {  	case NL80211_IFTYPE_ADHOC: -		cfg80211_leave_ibss(rdev, dev, true); +		__cfg80211_leave_ibss(rdev, dev, true);  		break;  	case NL80211_IFTYPE_P2P_CLIENT:  	case NL80211_IFTYPE_STATION: -		__cfg80211_stop_sched_scan(rdev, false); +		if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) +			__cfg80211_stop_sched_scan(rdev, false); -		wdev_lock(wdev);  #ifdef CONFIG_CFG80211_WEXT  		kfree(wdev->wext.ie);  		wdev->wext.ie = NULL; @@ -773,21 +830,48 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,  #endif  		cfg80211_disconnect(rdev, dev,  				    WLAN_REASON_DEAUTH_LEAVING, true); -		wdev_unlock(wdev);  		break;  	case NL80211_IFTYPE_MESH_POINT: -		cfg80211_leave_mesh(rdev, dev); +		__cfg80211_leave_mesh(rdev, dev);  		break;  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_P2P_GO: -		cfg80211_stop_ap(rdev, dev); +		__cfg80211_stop_ap(rdev, dev, true);  		break;  	default:  		break;  	} +} + +void cfg80211_leave(struct cfg80211_registered_device *rdev, +		    struct wireless_dev *wdev) +{ +	wdev_lock(wdev); +	__cfg80211_leave(rdev, wdev); +	wdev_unlock(wdev); +} -	wdev->beacon_interval = 0; +void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, +			 gfp_t gfp) +{ +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); +	struct cfg80211_event *ev; +	unsigned long flags; + +	trace_cfg80211_stop_iface(wiphy, wdev); + +	ev = kzalloc(sizeof(*ev), gfp); +	if (!ev) +		return; + +	ev->type = EVENT_STOPPED; + +	spin_lock_irqsave(&wdev->event_lock, flags); +	list_add_tail(&ev->list, &wdev->event_list); +	spin_unlock_irqrestore(&wdev->event_lock, flags); +	queue_work(cfg80211_wq, &rdev->event_work);  } +EXPORT_SYMBOL(cfg80211_stop_iface);  static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  					 unsigned long state, void *ptr) @@ -795,12 +879,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev; -	int ret;  	if (!wdev)  		return NOTIFY_DONE; -	rdev = wiphy_to_dev(wdev->wiphy); +	rdev = wiphy_to_rdev(wdev->wiphy);  	WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); @@ -859,7 +942,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  		if (rdev->scan_req && rdev->scan_req->wdev == wdev) {  			if (WARN_ON(!rdev->scan_req->notified))  				rdev->scan_req->aborted = true; -			___cfg80211_scan_done(rdev, true); +			___cfg80211_scan_done(rdev, false);  		}  		if (WARN_ON(rdev->sched_scan_req && @@ -960,13 +1043,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  			return notifier_from_errno(-EOPNOTSUPP);  		if (rfkill_blocked(rdev->rfkill))  			return notifier_from_errno(-ERFKILL); -		ret = cfg80211_can_add_interface(rdev, wdev->iftype); -		if (ret) -			return notifier_from_errno(ret);  		break; +	default: +		return NOTIFY_DONE;  	} -	return NOTIFY_DONE; +	return NOTIFY_OK;  }  static struct notifier_block cfg80211_netdev_notifier = {  | 
