summaryrefslogtreecommitdiff
path: root/net/wireless/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/core.c')
-rw-r--r--net/wireless/core.c72
1 files changed, 63 insertions, 9 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 54c89b0db352..200b97f912eb 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -329,16 +329,21 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
ASSERT_RTNL();
+ /*
+ * Some netdev interfaces need to be closed before some non-netdev
+ * ones, i.e. NAN_DATA interfaces need to be closed before the NAN
+ * interface
+ */
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (wdev->netdev) {
dev_close(wdev->netdev);
continue;
}
+ }
- /* otherwise, check iftype */
-
- guard(wiphy)(wiphy);
+ guard(wiphy)(wiphy);
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
switch (wdev->iftype) {
case NL80211_IFTYPE_P2P_DEVICE:
cfg80211_stop_p2p_device(rdev, wdev);
@@ -396,6 +401,8 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
if (wdev->nl_owner_dead) {
+ cfg80211_close_dependents(rdev, wdev);
+
if (wdev->netdev)
dev_close(wdev->netdev);
@@ -406,6 +413,21 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
}
}
+void cfg80211_close_dependents(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ ASSERT_RTNL();
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return;
+
+ /* Close all NAN DATA interfaces */
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (wdev->iftype == NL80211_IFTYPE_NAN_DATA)
+ dev_close(wdev->netdev);
+ }
+}
+
static void cfg80211_destroy_iface_wk(struct work_struct *work)
{
struct cfg80211_registered_device *rdev;
@@ -1419,9 +1441,8 @@ 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,
- int link_id)
+void cfg80211_leave_locked(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int link_id)
{
struct net_device *dev = wdev->netdev;
struct cfg80211_sched_scan_request *pos, *tmp;
@@ -1472,6 +1493,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_NAN_DATA:
/* nothing to do */
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -1482,6 +1504,19 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
}
}
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int link_id)
+{
+ ASSERT_RTNL();
+
+ /* NAN_DATA interfaces must be closed before stopping NAN */
+ cfg80211_close_dependents(rdev, wdev);
+
+ guard(wiphy)(&rdev->wiphy);
+
+ cfg80211_leave_locked(rdev, wdev, link_id);
+}
+
void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev,
int link_id, gfp_t gfp)
{
@@ -1497,6 +1532,9 @@ void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev,
trace_cfg80211_stop_link(wiphy, wdev, link_id);
+ if (wdev->iftype == NL80211_IFTYPE_NAN)
+ return;
+
ev = kzalloc_obj(*ev, gfp);
if (!ev)
return;
@@ -1647,10 +1685,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
}
break;
case NETDEV_GOING_DOWN:
- scoped_guard(wiphy, &rdev->wiphy) {
- cfg80211_leave(rdev, wdev, -1);
+ cfg80211_leave(rdev, wdev, -1);
+ scoped_guard(wiphy, &rdev->wiphy)
cfg80211_remove_links(wdev);
- }
/* since we just did cfg80211_leave() nothing to do there */
cancel_work_sync(&wdev->disconnect_wk);
cancel_work_sync(&wdev->pmsr_free_wk);
@@ -1731,6 +1768,23 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
if (rfkill_blocked(rdev->wiphy.rfkill))
return notifier_from_errno(-ERFKILL);
+
+ /* NAN_DATA interfaces require a running NAN interface */
+ if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) {
+ struct wireless_dev *iter;
+ bool nan_started = false;
+
+ list_for_each_entry(iter, &rdev->wiphy.wdev_list, list) {
+ if (iter->iftype == NL80211_IFTYPE_NAN &&
+ wdev_running(iter)) {
+ nan_started = true;
+ break;
+ }
+ }
+
+ if (!nan_started)
+ return notifier_from_errno(-ENOLINK);
+ }
break;
default:
return NOTIFY_DONE;