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