aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorLuis R. Rodriguez <mcgrof@do-not-panic.com>2012-09-14 18:36:57 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-09-18 20:43:23 -0400
commita85d0d7f3460b1a123b78e7f7e39bf72c37dfb78 (patch)
tree9c306ee34cfcee02c6e009443b0b67957dfbccec /net
parent78c04c0bf52360dc2f7185e99c8e9aa05d73ae5a (diff)
cfg80211: fix possible circular lock on reg_regdb_search()
When call_crda() is called we kick off a witch hunt search for the same regulatory domain on our internal regulatory database and that work gets kicked off on a workqueue, this is done while the cfg80211_mutex is held. If that workqueue kicks off it will first lock reg_regdb_search_mutex and later cfg80211_mutex but to ensure two CPUs will not contend against cfg80211_mutex the right thing to do is to have the reg_regdb_search() wait until the cfg80211_mutex is let go. The lockdep report is pasted below. cfg80211: Calling CRDA to update world regulatory domain ====================================================== [ INFO: possible circular locking dependency detected ] 3.3.8 #3 Tainted: G O ------------------------------------------------------- kworker/0:1/235 is trying to acquire lock: (cfg80211_mutex){+.+...}, at: [<816468a4>] set_regdom+0x78c/0x808 [cfg80211] but task is already holding lock: (reg_regdb_search_mutex){+.+...}, at: [<81646828>] set_regdom+0x710/0x808 [cfg80211] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (reg_regdb_search_mutex){+.+...}: [<800a8384>] lock_acquire+0x60/0x88 [<802950a8>] mutex_lock_nested+0x54/0x31c [<81645778>] is_world_regdom+0x9f8/0xc74 [cfg80211] -> #1 (reg_mutex#2){+.+...}: [<800a8384>] lock_acquire+0x60/0x88 [<802950a8>] mutex_lock_nested+0x54/0x31c [<8164539c>] is_world_regdom+0x61c/0xc74 [cfg80211] -> #0 (cfg80211_mutex){+.+...}: [<800a77b8>] __lock_acquire+0x10d4/0x17bc [<800a8384>] lock_acquire+0x60/0x88 [<802950a8>] mutex_lock_nested+0x54/0x31c [<816468a4>] set_regdom+0x78c/0x808 [cfg80211] other info that might help us debug this: Chain exists of: cfg80211_mutex --> reg_mutex#2 --> reg_regdb_search_mutex Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(reg_regdb_search_mutex); lock(reg_mutex#2); lock(reg_regdb_search_mutex); lock(cfg80211_mutex); *** DEADLOCK *** 3 locks held by kworker/0:1/235: #0: (events){.+.+..}, at: [<80089a00>] process_one_work+0x230/0x460 #1: (reg_regdb_work){+.+...}, at: [<80089a00>] process_one_work+0x230/0x460 #2: (reg_regdb_search_mutex){+.+...}, at: [<81646828>] set_regdom+0x710/0x808 [cfg80211] stack backtrace: Call Trace: [<80290fd4>] dump_stack+0x8/0x34 [<80291bc4>] print_circular_bug+0x2ac/0x2d8 [<800a77b8>] __lock_acquire+0x10d4/0x17bc [<800a8384>] lock_acquire+0x60/0x88 [<802950a8>] mutex_lock_nested+0x54/0x31c [<816468a4>] set_regdom+0x78c/0x808 [cfg80211] Reported-by: Felix Fietkau <nbd@openwrt.org> Tested-by: Felix Fietkau <nbd@openwrt.org> Cc: stable@vger.kernel.org Signed-off-by: Luis R. Rodriguez <mcgrof@do-not-panic.com> Reviewed-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/wireless/reg.c12
1 files changed, 9 insertions, 3 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 2ded3c7fad06..72d170ca3406 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -350,6 +350,9 @@ static void reg_regdb_search(struct work_struct *work)
350 struct reg_regdb_search_request *request; 350 struct reg_regdb_search_request *request;
351 const struct ieee80211_regdomain *curdom, *regdom; 351 const struct ieee80211_regdomain *curdom, *regdom;
352 int i, r; 352 int i, r;
353 bool set_reg = false;
354
355 mutex_lock(&cfg80211_mutex);
353 356
354 mutex_lock(&reg_regdb_search_mutex); 357 mutex_lock(&reg_regdb_search_mutex);
355 while (!list_empty(&reg_regdb_search_list)) { 358 while (!list_empty(&reg_regdb_search_list)) {
@@ -365,9 +368,7 @@ static void reg_regdb_search(struct work_struct *work)
365 r = reg_copy_regd(&regdom, curdom); 368 r = reg_copy_regd(&regdom, curdom);
366 if (r) 369 if (r)
367 break; 370 break;
368 mutex_lock(&cfg80211_mutex); 371 set_reg = true;
369 set_regdom(regdom);
370 mutex_unlock(&cfg80211_mutex);
371 break; 372 break;
372 } 373 }
373 } 374 }
@@ -375,6 +376,11 @@ static void reg_regdb_search(struct work_struct *work)
375 kfree(request); 376 kfree(request);
376 } 377 }
377 mutex_unlock(&reg_regdb_search_mutex); 378 mutex_unlock(&reg_regdb_search_mutex);
379
380 if (set_reg)
381 set_regdom(regdom);
382
383 mutex_unlock(&cfg80211_mutex);
378} 384}
379 385
380static DECLARE_WORK(reg_regdb_work, reg_regdb_search); 386static DECLARE_WORK(reg_regdb_work, reg_regdb_search);