696db7d
From 8125696991194aacb1173b6e8196d19098b44e17 Mon Sep 17 00:00:00 2001
696db7d
From: Stanislaw Gruszka <sgruszka@redhat.com>
696db7d
Date: Thu, 28 Feb 2013 09:55:25 +0000
696db7d
Subject: cfg80211/mac80211: disconnect on suspend
696db7d
696db7d
If possible that after suspend, cfg80211 will receive request to
696db7d
disconnect what require action on interface that was removed during
696db7d
suspend.
696db7d
696db7d
Problem can manifest itself by various warnings similar to below one:
696db7d
696db7d
WARNING: at net/mac80211/driver-ops.h:12 ieee80211_bss_info_change_notify+0x2f9/0x300 [mac80211]()
696db7d
wlan0:  Failed check-sdata-in-driver check, flags: 0x4
696db7d
Call Trace:
696db7d
 [<c043e0b3>] warn_slowpath_fmt+0x33/0x40
696db7d
 [<f83707c9>] ieee80211_bss_info_change_notify+0x2f9/0x300 [mac80211]
696db7d
 [<f83a660a>] ieee80211_recalc_ps_vif+0x2a/0x30 [mac80211]
696db7d
 [<f83a6706>] ieee80211_set_disassoc+0xf6/0x500 [mac80211]
696db7d
 [<f83a9441>] ieee80211_mgd_deauth+0x1f1/0x280 [mac80211]
696db7d
 [<f8381b36>] ieee80211_deauth+0x16/0x20 [mac80211]
696db7d
 [<f8261e70>] cfg80211_mlme_down+0x70/0xc0 [cfg80211]
696db7d
 [<f8264de1>] __cfg80211_disconnect+0x1b1/0x1d0 [cfg80211]
696db7d
696db7d
To fix the problem disconnect from any associated network before
696db7d
suspend. User space is responsible to establish connection again
696db7d
after resume. This basically need to be done by user space anyway,
696db7d
because associated stations can go away during suspend (for example
696db7d
NetworkManager disconnects on suspend and connect on resume by default).
696db7d
696db7d
Patch also handle situation when driver refuse to suspend with wowlan
696db7d
configured and try to suspend again without it.
696db7d
696db7d
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
696db7d
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
696db7d
---
696db7d
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
696db7d
index d0275f3..4d105c7 100644
696db7d
--- a/net/mac80211/pm.c
696db7d
+++ b/net/mac80211/pm.c
696db7d
@@ -93,7 +93,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
696db7d
 			return err;
696db7d
 		} else if (err > 0) {
696db7d
 			WARN_ON(err != 1);
696db7d
-			local->wowlan = false;
696db7d
+			return err;
696db7d
 		} else {
696db7d
 			list_for_each_entry(sdata, &local->interfaces, list)
696db7d
 				if (ieee80211_sdata_running(sdata))
696db7d
diff --git a/net/wireless/core.c b/net/wireless/core.c
696db7d
index ea4155f..f382cae 100644
696db7d
--- a/net/wireless/core.c
696db7d
+++ b/net/wireless/core.c
696db7d
@@ -814,6 +814,46 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
696db7d
 		rdev->num_running_monitor_ifaces += num;
696db7d
 }
696db7d
 
696db7d
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
696db7d
+		   struct wireless_dev *wdev)
696db7d
+{
696db7d
+	struct net_device *dev = wdev->netdev;
696db7d
+
696db7d
+	switch (wdev->iftype) {
696db7d
+	case NL80211_IFTYPE_ADHOC:
696db7d
+		cfg80211_leave_ibss(rdev, dev, true);
696db7d
+		break;
696db7d
+	case NL80211_IFTYPE_P2P_CLIENT:
696db7d
+	case NL80211_IFTYPE_STATION:
696db7d
+		mutex_lock(&rdev->sched_scan_mtx);
696db7d
+		__cfg80211_stop_sched_scan(rdev, false);
696db7d
+		mutex_unlock(&rdev->sched_scan_mtx);
696db7d
+
696db7d
+		wdev_lock(wdev);
696db7d
+#ifdef CONFIG_CFG80211_WEXT
696db7d
+		kfree(wdev->wext.ie);
696db7d
+		wdev->wext.ie = NULL;
696db7d
+		wdev->wext.ie_len = 0;
696db7d
+		wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
696db7d
+#endif
696db7d
+		__cfg80211_disconnect(rdev, dev,
696db7d
+				      WLAN_REASON_DEAUTH_LEAVING, true);
696db7d
+		cfg80211_mlme_down(rdev, dev);
696db7d
+		wdev_unlock(wdev);
696db7d
+		break;
696db7d
+	case NL80211_IFTYPE_MESH_POINT:
696db7d
+		cfg80211_leave_mesh(rdev, dev);
696db7d
+		break;
696db7d
+	case NL80211_IFTYPE_AP:
696db7d
+		cfg80211_stop_ap(rdev, dev);
696db7d
+		break;
696db7d
+	default:
696db7d
+		break;
696db7d
+	}
696db7d
+
696db7d
+	wdev->beacon_interval = 0;
696db7d
+}
696db7d
+
696db7d
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
696db7d
 					 unsigned long state,
696db7d
 					 void *ndev)
696db7d
@@ -882,38 +922,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
696db7d
 			dev->priv_flags |= IFF_DONT_BRIDGE;
696db7d
 		break;
696db7d
 	case NETDEV_GOING_DOWN:
696db7d
-		switch (wdev->iftype) {
696db7d
-		case NL80211_IFTYPE_ADHOC:
696db7d
-			cfg80211_leave_ibss(rdev, dev, true);
696db7d
-			break;
696db7d
-		case NL80211_IFTYPE_P2P_CLIENT:
696db7d
-		case NL80211_IFTYPE_STATION:
696db7d
-			mutex_lock(&rdev->sched_scan_mtx);
696db7d
-			__cfg80211_stop_sched_scan(rdev, false);
696db7d
-			mutex_unlock(&rdev->sched_scan_mtx);
696db7d
-
696db7d
-			wdev_lock(wdev);
696db7d
-#ifdef CONFIG_CFG80211_WEXT
696db7d
-			kfree(wdev->wext.ie);
696db7d
-			wdev->wext.ie = NULL;
696db7d
-			wdev->wext.ie_len = 0;
696db7d
-			wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
696db7d
-#endif
696db7d
-			__cfg80211_disconnect(rdev, dev,
696db7d
-					      WLAN_REASON_DEAUTH_LEAVING, true);
696db7d
-			cfg80211_mlme_down(rdev, dev);
696db7d
-			wdev_unlock(wdev);
696db7d
-			break;
696db7d
-		case NL80211_IFTYPE_MESH_POINT:
696db7d
-			cfg80211_leave_mesh(rdev, dev);
696db7d
-			break;
696db7d
-		case NL80211_IFTYPE_AP:
696db7d
-			cfg80211_stop_ap(rdev, dev);
696db7d
-			break;
696db7d
-		default:
696db7d
-			break;
696db7d
-		}
696db7d
-		wdev->beacon_interval = 0;
696db7d
+		cfg80211_leave(rdev, wdev);
696db7d
 		break;
696db7d
 	case NETDEV_DOWN:
696db7d
 		cfg80211_update_iface_num(rdev, wdev->iftype, -1);
696db7d
diff --git a/net/wireless/core.h b/net/wireless/core.h
696db7d
index 9a2be8d..d5d06fd 100644
696db7d
--- a/net/wireless/core.h
696db7d
+++ b/net/wireless/core.h
f545817
@@ -503,6 +503,9 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
696db7d
 void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
696db7d
 			       enum nl80211_iftype iftype, int num);
696db7d
 
696db7d
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
696db7d
+		    struct wireless_dev *wdev);
696db7d
+
f545817
 void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
f545817
 			      struct wireless_dev *wdev);
696db7d
 
696db7d
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
696db7d
index 8c8b26f..d77e1c1 100644
696db7d
--- a/net/wireless/rdev-ops.h
696db7d
+++ b/net/wireless/rdev-ops.h
696db7d
@@ -6,11 +6,12 @@
696db7d
 #include "core.h"
696db7d
 #include "trace.h"
696db7d
 
696db7d
-static inline int rdev_suspend(struct cfg80211_registered_device *rdev)
696db7d
+static inline int rdev_suspend(struct cfg80211_registered_device *rdev,
696db7d
+			       struct cfg80211_wowlan *wowlan)
696db7d
 {
696db7d
 	int ret;
696db7d
-	trace_rdev_suspend(&rdev->wiphy, rdev->wowlan);
696db7d
-	ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan);
696db7d
+	trace_rdev_suspend(&rdev->wiphy, wowlan);
696db7d
+	ret = rdev->ops->suspend(&rdev->wiphy, wowlan);
696db7d
 	trace_rdev_return_int(&rdev->wiphy, ret);
696db7d
 	return ret;
696db7d
 }
696db7d
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
696db7d
index 238ee49..8f28b9f 100644
696db7d
--- a/net/wireless/sysfs.c
696db7d
+++ b/net/wireless/sysfs.c
696db7d
@@ -83,6 +83,14 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
696db7d
 	return 0;
696db7d
 }
696db7d
 
696db7d
+static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
696db7d
+{
696db7d
+	struct wireless_dev *wdev;
696db7d
+
696db7d
+	list_for_each_entry(wdev, &rdev->wdev_list, list)
696db7d
+		cfg80211_leave(rdev, wdev);
696db7d
+}
696db7d
+
696db7d
 static int wiphy_suspend(struct device *dev, pm_message_t state)
696db7d
 {
696db7d
 	struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
696db7d
@@ -90,12 +98,19 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
696db7d
 
696db7d
 	rdev->suspend_at = get_seconds();
696db7d
 
696db7d
-	if (rdev->ops->suspend) {
696db7d
-		rtnl_lock();
696db7d
-		if (rdev->wiphy.registered)
696db7d
-			ret = rdev_suspend(rdev);
696db7d
-		rtnl_unlock();
696db7d
+	rtnl_lock();
696db7d
+	if (rdev->wiphy.registered) {
696db7d
+		if (!rdev->wowlan)
696db7d
+			cfg80211_leave_all(rdev);
696db7d
+		if (rdev->ops->suspend)
696db7d
+			ret = rdev_suspend(rdev, rdev->wowlan);
696db7d
+		if (ret == 1) {
696db7d
+			/* Driver refuse to configure wowlan */
696db7d
+			cfg80211_leave_all(rdev);
696db7d
+			ret = rdev_suspend(rdev, NULL);
696db7d
+		}
696db7d
 	}
696db7d
+	rtnl_unlock();
696db7d
 
696db7d
 	return ret;
696db7d
 }
696db7d
--
696db7d
cgit v0.9.1