From c10841ca722a0bc960dc541c51582773f9a24f98 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 30 Jun 2011 08:32:41 +0300 Subject: cfg80211: fix deadlock with rfkill/sched_scan by adding new mutex There was a deadlock when rfkill-blocking a wireless interface, because we were locking the rdev mutex on NETDEV_GOING_DOWN to stop sched_scans that were eventually running. The rfkill block code was already holding a mutex under rdev: kernel: ======================================================= kernel: [ INFO: possible circular locking dependency detected ] kernel: 3.0.0-rc1-00049-g1fa7b6a #57 kernel: ------------------------------------------------------- kernel: kworker/0:1/4525 is trying to acquire lock: kernel: (&rdev->mtx){+.+.+.}, at: [] cfg80211_netdev_notifier_call+0x131/0x5b0 kernel: kernel: but task is already holding lock: kernel: (&rdev->devlist_mtx){+.+.+.}, at: [] cfg80211_rfkill_set_block+0x4f/0xa0 kernel: kernel: which lock already depends on the new lock. To fix this, add a new mutex specifically for sched_scan, to protect the sched_scan_req element in the rdev struct, instead of using the global rdev mutex. Reported-by: Duane Griffin Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- net/wireless/scan.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net/wireless/scan.c') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 7a6c67667d7..ae0c2256ba3 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -100,14 +100,14 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) rdev = container_of(wk, struct cfg80211_registered_device, sched_scan_results_wk); - cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->sched_scan_mtx); /* we don't have sched_scan_req anymore if the scan is stopping */ if (rdev->sched_scan_req) nl80211_send_sched_scan_results(rdev, rdev->sched_scan_req->dev); - cfg80211_unlock_rdev(rdev); + mutex_unlock(&rdev->sched_scan_mtx); } void cfg80211_sched_scan_results(struct wiphy *wiphy) @@ -123,9 +123,9 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->sched_scan_mtx); __cfg80211_stop_sched_scan(rdev, true); - cfg80211_unlock_rdev(rdev); + mutex_unlock(&rdev->sched_scan_mtx); } EXPORT_SYMBOL(cfg80211_sched_scan_stopped); @@ -135,7 +135,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, int err; struct net_device *dev; - ASSERT_RDEV_LOCK(rdev); + lockdep_assert_held(&rdev->sched_scan_mtx); if (!rdev->sched_scan_req) return 0; -- cgit v1.2.3-70-g09d2 From 3b4670ffe7824d1fc4db2f73101015056ecb4415 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Wed, 29 Jun 2011 22:49:33 +0200 Subject: net, wireless: Don't return uninitialized in __cfg80211_stop_sched_scan() If the 'driver_initiated' function argument to __cfg80211_stop_sched_scan() is not 0 then we'll return an uninitialized 'err' from the function. Signed-off-by: Jesper Juhl Signed-off-by: John W. Linville --- net/wireless/scan.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'net/wireless/scan.c') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 7a6c67667d7..5d23503dd5e 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -132,7 +132,6 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, bool driver_initiated) { - int err; struct net_device *dev; ASSERT_RDEV_LOCK(rdev); @@ -143,7 +142,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, dev = rdev->sched_scan_req->dev; if (!driver_initiated) { - err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); + int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); if (err) return err; } @@ -153,7 +152,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, kfree(rdev->sched_scan_req); rdev->sched_scan_req = NULL; - return err; + return 0; } static void bss_release(struct kref *ref) -- cgit v1.2.3-70-g09d2 From 1a84ff7564ae43dd1ea20e17f867de2700ca5b5b Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 8 Jul 2011 11:16:16 +0300 Subject: cfg80211: return -ENOENT when stopping sched_scan while not running If we try to stop a scheduled scan while it is not running, we should return -ENOENT instead of simply ignoring the command and returning success. This is more consistent with other parts of the code. Reported-by: Johannes Berg Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- include/linux/nl80211.h | 3 ++- net/wireless/scan.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'net/wireless/scan.c') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 3ec2f949bf7..8cb025a0009 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -247,7 +247,8 @@ * passed, all channels allowed for the current regulatory domain * are used. Extra IEs can also be passed from the userspace by * using the %NL80211_ATTR_IE attribute. - * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT + * if scheduled scan is not running. * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan * results available. * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 5d23503dd5e..ce04566a2ec 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -137,7 +137,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, ASSERT_RDEV_LOCK(rdev); if (!rdev->sched_scan_req) - return 0; + return -ENOENT; dev = rdev->sched_scan_req->dev; -- cgit v1.2.3-70-g09d2