summaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-06-04 19:21:08 +0200
committerJohannes Berg <johannes.berg@intel.com>2013-06-04 22:22:41 +0200
commit256c90dedf538c59c70e65ba1a1340ce793c5b37 (patch)
tree9b8e1290e91ee99bfc9edc88a5e7bcf500f64e70 /net/wireless
parentregulatory: use proper enum return value (diff)
downloadlinux-256c90dedf538c59c70e65ba1a1340ce793c5b37.tar.xz
linux-256c90dedf538c59c70e65ba1a1340ce793c5b37.zip
cfg80211: fix potential deadlock regression
My big locking cleanups caused a problem by registering the rfkill instance with the RTNL held, while the callback also acquires the RTNL. This potentially causes a deadlock since the two locks used (rfkill mutex and RTNL) can be acquired in two different orders. Fix this by (un)registering rfkill without holding the RTNL. This needs to be done after the device struct is registered, but that can also be done w/o holding the RTNL. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.c23
1 files changed, 8 insertions, 15 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 221e76b53a97..99d86ddb6331 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -555,14 +555,18 @@ int wiphy_register(struct wiphy *wiphy)
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
- rtnl_lock();
res = device_add(&rdev->wiphy.dev);
+ if (res)
+ return res;
+
+ res = rfkill_register(rdev->rfkill);
if (res) {
- rtnl_unlock();
+ device_del(&rdev->wiphy.dev);
return res;
}
+ rtnl_lock();
/* set up regulatory info */
wiphy_regulatory_register(wiphy);
@@ -589,17 +593,6 @@ int wiphy_register(struct wiphy *wiphy)
cfg80211_debugfs_rdev_add(rdev);
- res = rfkill_register(rdev->rfkill);
- if (res) {
- device_del(&rdev->wiphy.dev);
-
- debugfs_remove_recursive(rdev->wiphy.debugfsdir);
- list_del_rcu(&rdev->list);
- wiphy_regulatory_deregister(wiphy);
- rtnl_unlock();
- return res;
- }
-
rdev->wiphy.registered = true;
rtnl_unlock();
return 0;
@@ -636,11 +629,11 @@ void wiphy_unregister(struct wiphy *wiphy)
rtnl_unlock();
__count == 0; }));
+ rfkill_unregister(rdev->rfkill);
+
rtnl_lock();
rdev->wiphy.registered = false;
- rfkill_unregister(rdev->rfkill);
-
BUG_ON(!list_empty(&rdev->wdev_list));
/*