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: [<ffffffff8164c831>] cfg80211_netdev_notifier_call+0x131/0x5b0 kernel: kernel: but task is already holding lock: kernel: (&rdev->devlist_mtx){+.+.+.}, at: [<ffffffff8164dcef>] 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 <duaneg@dghda.com> Signed-off-by: Luciano Coelho <coelho@ti.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
committed by
John W. Linville
parent
37000b305b
commit
c10841ca72
@@ -3461,9 +3461,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
||||
return -EINVAL;
|
||||
|
||||
if (rdev->sched_scan_req)
|
||||
return -EINPROGRESS;
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
|
||||
return -EINVAL;
|
||||
|
||||
@@ -3502,12 +3499,21 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
if (ie_len > wiphy->max_scan_ie_len)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&rdev->sched_scan_mtx);
|
||||
|
||||
if (rdev->sched_scan_req) {
|
||||
err = -EINPROGRESS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
request = kzalloc(sizeof(*request)
|
||||
+ sizeof(*request->ssids) * n_ssids
|
||||
+ sizeof(*request->channels) * n_channels
|
||||
+ ie_len, GFP_KERNEL);
|
||||
if (!request)
|
||||
return -ENOMEM;
|
||||
if (!request) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (n_ssids)
|
||||
request->ssids = (void *)&request->channels[n_channels];
|
||||
@@ -3605,6 +3611,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
out_free:
|
||||
kfree(request);
|
||||
out:
|
||||
mutex_unlock(&rdev->sched_scan_mtx);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -3612,12 +3619,17 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
int err;
|
||||
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
||||
!rdev->ops->sched_scan_stop)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return __cfg80211_stop_sched_scan(rdev, false);
|
||||
mutex_lock(&rdev->sched_scan_mtx);
|
||||
err = __cfg80211_stop_sched_scan(rdev, false);
|
||||
mutex_unlock(&rdev->sched_scan_mtx);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
||||
|
||||
Reference in New Issue
Block a user