Return-path: Received: from mx1.redhat.com ([209.132.183.28]:22162 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752375Ab3BYLuS (ORCPT ); Mon, 25 Feb 2013 06:50:18 -0500 From: Stanislaw Gruszka To: Johannes Berg Cc: linux-wireless@vger.kernel.org, Thomas Pedersen , Stanislaw Gruszka Subject: [PATCH 1/6] cfg80211: disconnect on suspend Date: Mon, 25 Feb 2013 12:50:03 +0100 Message-Id: <1361793008-2857-1-git-send-email-sgruszka@redhat.com> (sfid-20130225_125024_087298_34A63F22) Sender: linux-wireless-owner@vger.kernel.org List-ID: 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: [] warn_slowpath_fmt+0x33/0x40 [] ieee80211_bss_info_change_notify+0x2f9/0x300 [mac80211] [] ieee80211_recalc_ps_vif+0x2a/0x30 [mac80211] [] ieee80211_set_disassoc+0xf6/0x500 [mac80211] [] ieee80211_mgd_deauth+0x1f1/0x280 [mac80211] [] ieee80211_deauth+0x16/0x20 [mac80211] [] cfg80211_mlme_down+0x70/0xc0 [cfg80211] [] __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). Signed-off-by: Stanislaw Gruszka --- net/wireless/core.c | 73 +++++++++++++++++++++++++++++----------------------- net/wireless/core.h | 3 +++ net/wireless/sysfs.c | 15 ++++++++--- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 5ffff03..5a4dc09 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -815,6 +815,46 @@ 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) +{ + struct net_device *dev = wdev->netdev; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + cfg80211_leave_ibss(rdev, dev, true); + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + mutex_lock(&rdev->sched_scan_mtx); + __cfg80211_stop_sched_scan(rdev, false); + mutex_unlock(&rdev->sched_scan_mtx); + + wdev_lock(wdev); +#ifdef CONFIG_CFG80211_WEXT + kfree(wdev->wext.ie); + wdev->wext.ie = NULL; + wdev->wext.ie_len = 0; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; +#endif + __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); + cfg80211_mlme_down(rdev, dev); + wdev_unlock(wdev); + break; + case NL80211_IFTYPE_MESH_POINT: + cfg80211_leave_mesh(rdev, dev); + break; + case NL80211_IFTYPE_AP: + cfg80211_stop_ap(rdev, dev); + break; + default: + break; + } + + wdev->beacon_interval = 0; +} + static int cfg80211_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ndev) @@ -883,38 +923,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, dev->priv_flags |= IFF_DONT_BRIDGE; break; case NETDEV_GOING_DOWN: - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - cfg80211_leave_ibss(rdev, dev, true); - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_STATION: - mutex_lock(&rdev->sched_scan_mtx); - __cfg80211_stop_sched_scan(rdev, false); - mutex_unlock(&rdev->sched_scan_mtx); - - wdev_lock(wdev); -#ifdef CONFIG_CFG80211_WEXT - kfree(wdev->wext.ie); - wdev->wext.ie = NULL; - wdev->wext.ie_len = 0; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; -#endif - __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, true); - cfg80211_mlme_down(rdev, dev); - wdev_unlock(wdev); - break; - case NL80211_IFTYPE_MESH_POINT: - cfg80211_leave_mesh(rdev, dev); - break; - case NL80211_IFTYPE_AP: - cfg80211_stop_ap(rdev, dev); - break; - default: - break; - } - wdev->beacon_interval = 0; + cfg80211_leave(rdev, wdev); break; case NETDEV_DOWN: cfg80211_update_iface_num(rdev, wdev->iftype, -1); diff --git a/net/wireless/core.h b/net/wireless/core.h index 3aec0e4..878d204 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -503,6 +503,9 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); +void cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); + #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 238ee49..cf58a91 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -86,16 +86,23 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env) static int wiphy_suspend(struct device *dev, pm_message_t state) { struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + struct wireless_dev *wdev; int ret = 0; rdev->suspend_at = get_seconds(); - if (rdev->ops->suspend) { - rtnl_lock(); - if (rdev->wiphy.registered) + rtnl_lock(); + if (rdev->wiphy.registered) { + if (!rdev->wowlan) { + list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) + cfg80211_leave(rdev, wdev); + } + + if (rdev->ops->suspend) ret = rdev_suspend(rdev); - rtnl_unlock(); + } + rtnl_unlock(); return ret; } -- 1.7.11.7