Commit a85d0d7f authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by John W. Linville

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: default avatarFelix Fietkau <nbd@openwrt.org>
Tested-by: default avatarFelix Fietkau <nbd@openwrt.org>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarLuis R. Rodriguez <mcgrof@do-not-panic.com>
Reviewed-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 78c04c0b
...@@ -350,6 +350,9 @@ static void reg_regdb_search(struct work_struct *work) ...@@ -350,6 +350,9 @@ static void reg_regdb_search(struct work_struct *work)
struct reg_regdb_search_request *request; struct reg_regdb_search_request *request;
const struct ieee80211_regdomain *curdom, *regdom; const struct ieee80211_regdomain *curdom, *regdom;
int i, r; int i, r;
bool set_reg = false;
mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_regdb_search_mutex); mutex_lock(&reg_regdb_search_mutex);
while (!list_empty(&reg_regdb_search_list)) { while (!list_empty(&reg_regdb_search_list)) {
...@@ -365,9 +368,7 @@ static void reg_regdb_search(struct work_struct *work) ...@@ -365,9 +368,7 @@ static void reg_regdb_search(struct work_struct *work)
r = reg_copy_regd(&regdom, curdom); r = reg_copy_regd(&regdom, curdom);
if (r) if (r)
break; break;
mutex_lock(&cfg80211_mutex); set_reg = true;
set_regdom(regdom);
mutex_unlock(&cfg80211_mutex);
break; break;
} }
} }
...@@ -375,6 +376,11 @@ static void reg_regdb_search(struct work_struct *work) ...@@ -375,6 +376,11 @@ static void reg_regdb_search(struct work_struct *work)
kfree(request); kfree(request);
} }
mutex_unlock(&reg_regdb_search_mutex); mutex_unlock(&reg_regdb_search_mutex);
if (set_reg)
set_regdom(regdom);
mutex_unlock(&cfg80211_mutex);
} }
static DECLARE_WORK(reg_regdb_work, reg_regdb_search); static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment