aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStanislaw Gruszka <sgruszka@redhat.com>2013-02-28 04:55:25 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-03-06 10:35:55 -0500
commit8125696991194aacb1173b6e8196d19098b44e17 (patch)
tree870dacf2225b328a8393be930128b056dfee0539
parent30c97120c6c7893e5c6857a16229699b2b79dfbe (diff)
cfg80211/mac80211: disconnect on suspend
If possible that after suspend, cfg80211 will receive request to disconnect what require action on interface that was removed during suspend. Problem can manifest itself by various warnings similar to below one: WARNING: at net/mac80211/driver-ops.h:12 ieee80211_bss_info_change_notify+0x2f9/0x300 [mac80211]() wlan0: Failed check-sdata-in-driver check, flags: 0x4 Call Trace: [<c043e0b3>] warn_slowpath_fmt+0x33/0x40 [<f83707c9>] ieee80211_bss_info_change_notify+0x2f9/0x300 [mac80211] [<f83a660a>] ieee80211_recalc_ps_vif+0x2a/0x30 [mac80211] [<f83a6706>] ieee80211_set_disassoc+0xf6/0x500 [mac80211] [<f83a9441>] ieee80211_mgd_deauth+0x1f1/0x280 [mac80211] [<f8381b36>] ieee80211_deauth+0x16/0x20 [mac80211] [<f8261e70>] cfg80211_mlme_down+0x70/0xc0 [cfg80211] [<f8264de1>] __cfg80211_disconnect+0x1b1/0x1d0 [cfg80211] To fix the problem disconnect from any associated network before suspend. User space is responsible to establish connection again after resume. This basically need to be done by user space anyway, because associated stations can go away during suspend (for example NetworkManager disconnects on suspend and connect on resume by default). Patch also handle situation when driver refuse to suspend with wowlan configured and try to suspend again without it. Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/mac80211/pm.c2
-rw-r--r--net/wireless/core.c73
-rw-r--r--net/wireless/core.h3
-rw-r--r--net/wireless/rdev-ops.h7
-rw-r--r--net/wireless/sysfs.c25
5 files changed, 69 insertions, 41 deletions
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index d0275f34bf70..4d105c7f26b7 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -93,7 +93,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
93 return err; 93 return err;
94 } else if (err > 0) { 94 } else if (err > 0) {
95 WARN_ON(err != 1); 95 WARN_ON(err != 1);
96 local->wowlan = false; 96 return err;
97 } else { 97 } else {
98 list_for_each_entry(sdata, &local->interfaces, list) 98 list_for_each_entry(sdata, &local->interfaces, list)
99 if (ieee80211_sdata_running(sdata)) 99 if (ieee80211_sdata_running(sdata))
diff --git a/net/wireless/core.c b/net/wireless/core.c
index ea4155fe9733..f382cae983ba 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -814,6 +814,46 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
814 rdev->num_running_monitor_ifaces += num; 814 rdev->num_running_monitor_ifaces += num;
815} 815}
816 816
817void cfg80211_leave(struct cfg80211_registered_device *rdev,
818 struct wireless_dev *wdev)
819{
820 struct net_device *dev = wdev->netdev;
821
822 switch (wdev->iftype) {
823 case NL80211_IFTYPE_ADHOC:
824 cfg80211_leave_ibss(rdev, dev, true);
825 break;
826 case NL80211_IFTYPE_P2P_CLIENT:
827 case NL80211_IFTYPE_STATION:
828 mutex_lock(&rdev->sched_scan_mtx);
829 __cfg80211_stop_sched_scan(rdev, false);
830 mutex_unlock(&rdev->sched_scan_mtx);
831
832 wdev_lock(wdev);
833#ifdef CONFIG_CFG80211_WEXT
834 kfree(wdev->wext.ie);
835 wdev->wext.ie = NULL;
836 wdev->wext.ie_len = 0;
837 wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
838#endif
839 __cfg80211_disconnect(rdev, dev,
840 WLAN_REASON_DEAUTH_LEAVING, true);
841 cfg80211_mlme_down(rdev, dev);
842 wdev_unlock(wdev);
843 break;
844 case NL80211_IFTYPE_MESH_POINT:
845 cfg80211_leave_mesh(rdev, dev);
846 break;
847 case NL80211_IFTYPE_AP:
848 cfg80211_stop_ap(rdev, dev);
849 break;
850 default:
851 break;
852 }
853
854 wdev->beacon_interval = 0;
855}
856
817static int cfg80211_netdev_notifier_call(struct notifier_block *nb, 857static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
818 unsigned long state, 858 unsigned long state,
819 void *ndev) 859 void *ndev)
@@ -882,38 +922,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
882 dev->priv_flags |= IFF_DONT_BRIDGE; 922 dev->priv_flags |= IFF_DONT_BRIDGE;
883 break; 923 break;
884 case NETDEV_GOING_DOWN: 924 case NETDEV_GOING_DOWN:
885 switch (wdev->iftype) { 925 cfg80211_leave(rdev, wdev);
886 case NL80211_IFTYPE_ADHOC:
887 cfg80211_leave_ibss(rdev, dev, true);
888 break;
889 case NL80211_IFTYPE_P2P_CLIENT:
890 case NL80211_IFTYPE_STATION:
891 mutex_lock(&rdev->sched_scan_mtx);
892 __cfg80211_stop_sched_scan(rdev, false);
893 mutex_unlock(&rdev->sched_scan_mtx);
894
895 wdev_lock(wdev);
896#ifdef CONFIG_CFG80211_WEXT
897 kfree(wdev->wext.ie);
898 wdev->wext.ie = NULL;
899 wdev->wext.ie_len = 0;
900 wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
901#endif
902 __cfg80211_disconnect(rdev, dev,
903 WLAN_REASON_DEAUTH_LEAVING, true);
904 cfg80211_mlme_down(rdev, dev);
905 wdev_unlock(wdev);
906 break;
907 case NL80211_IFTYPE_MESH_POINT:
908 cfg80211_leave_mesh(rdev, dev);
909 break;
910 case NL80211_IFTYPE_AP:
911 cfg80211_stop_ap(rdev, dev);
912 break;
913 default:
914 break;
915 }
916 wdev->beacon_interval = 0;
917 break; 926 break;
918 case NETDEV_DOWN: 927 case NETDEV_DOWN:
919 cfg80211_update_iface_num(rdev, wdev->iftype, -1); 928 cfg80211_update_iface_num(rdev, wdev->iftype, -1);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 9a2be8ddac5f..d5d06fdea961 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -500,6 +500,9 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
500void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, 500void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
501 enum nl80211_iftype iftype, int num); 501 enum nl80211_iftype iftype, int num);
502 502
503void cfg80211_leave(struct cfg80211_registered_device *rdev,
504 struct wireless_dev *wdev);
505
503#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 506#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
504 507
505#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS 508#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 8c8b26f574e8..d77e1c1d3a0e 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -6,11 +6,12 @@
6#include "core.h" 6#include "core.h"
7#include "trace.h" 7#include "trace.h"
8 8
9static inline int rdev_suspend(struct cfg80211_registered_device *rdev) 9static inline int rdev_suspend(struct cfg80211_registered_device *rdev,
10 struct cfg80211_wowlan *wowlan)
10{ 11{
11 int ret; 12 int ret;
12 trace_rdev_suspend(&rdev->wiphy, rdev->wowlan); 13 trace_rdev_suspend(&rdev->wiphy, wowlan);
13 ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); 14 ret = rdev->ops->suspend(&rdev->wiphy, wowlan);
14 trace_rdev_return_int(&rdev->wiphy, ret); 15 trace_rdev_return_int(&rdev->wiphy, ret);
15 return ret; 16 return ret;
16} 17}
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 238ee49b3868..8f28b9f798d8 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -83,6 +83,14 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
83 return 0; 83 return 0;
84} 84}
85 85
86static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
87{
88 struct wireless_dev *wdev;
89
90 list_for_each_entry(wdev, &rdev->wdev_list, list)
91 cfg80211_leave(rdev, wdev);
92}
93
86static int wiphy_suspend(struct device *dev, pm_message_t state) 94static int wiphy_suspend(struct device *dev, pm_message_t state)
87{ 95{
88 struct cfg80211_registered_device *rdev = dev_to_rdev(dev); 96 struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
@@ -90,12 +98,19 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
90 98
91 rdev->suspend_at = get_seconds(); 99 rdev->suspend_at = get_seconds();
92 100
93 if (rdev->ops->suspend) { 101 rtnl_lock();
94 rtnl_lock(); 102 if (rdev->wiphy.registered) {
95 if (rdev->wiphy.registered) 103 if (!rdev->wowlan)
96 ret = rdev_suspend(rdev); 104 cfg80211_leave_all(rdev);
97 rtnl_unlock(); 105 if (rdev->ops->suspend)
106 ret = rdev_suspend(rdev, rdev->wowlan);
107 if (ret == 1) {
108 /* Driver refuse to configure wowlan */
109 cfg80211_leave_all(rdev);
110 ret = rdev_suspend(rdev, NULL);
111 }
98 } 112 }
113 rtnl_unlock();
99 114
100 return ret; 115 return ret;
101} 116}