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