diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-08-17 06:25:37 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-20 11:35:56 -0400 |
commit | 0ff6ce7b36199f67f709c750e9a2a66659a4babe (patch) | |
tree | 3b023b960e010b7ce84d35d8e9b516f5cb285f77 /net/wireless/core.c | |
parent | 96909e97716de1d86e6e24d6aabce09980372771 (diff) |
cfg80211: fix deadlock
When removing an interface with nl80211, cfg80211 will
deadlock in the netdev notifier because we're already
holding rdev->mtx and try to acquire it again to verify
the scan has been done.
This bug was introduced by my patch
"cfg80211: check for and abort dangling scan requests".
To fix this, move the dangling scan request check into
wiphy_unregister(). This will not be able to catch all
cases right away, but if the scan problem happens with
a manual ifdown or so it will be possible to remedy it
by removing the module/device.
Additionally, add comments about the deadlock scenario.
Reported-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Tested-by: Christian Lamparter <chunkeey@web.de>
Tested-by: Kalle Valo <kalle.valo@iki.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/core.c')
-rw-r--r-- | net/wireless/core.c | 30 |
1 files changed, 18 insertions, 12 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index bc99e4ec746..69a185ba9ff 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -586,9 +586,14 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
586 | * get to lock contention here if userspace issues a command | 586 | * get to lock contention here if userspace issues a command |
587 | * that identified the hardware by wiphy index. | 587 | * that identified the hardware by wiphy index. |
588 | */ | 588 | */ |
589 | mutex_lock(&rdev->mtx); | 589 | cfg80211_lock_rdev(rdev); |
590 | /* unlock again before freeing */ | 590 | |
591 | mutex_unlock(&rdev->mtx); | 591 | if (WARN_ON(rdev->scan_req)) { |
592 | rdev->scan_req->aborted = true; | ||
593 | ___cfg80211_scan_done(rdev); | ||
594 | } | ||
595 | |||
596 | cfg80211_unlock_rdev(rdev); | ||
592 | 597 | ||
593 | cfg80211_debugfs_rdev_del(rdev); | 598 | cfg80211_debugfs_rdev_del(rdev); |
594 | 599 | ||
@@ -605,7 +610,6 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
605 | 610 | ||
606 | flush_work(&rdev->scan_done_wk); | 611 | flush_work(&rdev->scan_done_wk); |
607 | cancel_work_sync(&rdev->conn_work); | 612 | cancel_work_sync(&rdev->conn_work); |
608 | kfree(rdev->scan_req); | ||
609 | flush_work(&rdev->event_work); | 613 | flush_work(&rdev->event_work); |
610 | } | 614 | } |
611 | EXPORT_SYMBOL(wiphy_unregister); | 615 | EXPORT_SYMBOL(wiphy_unregister); |
@@ -653,6 +657,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
653 | 657 | ||
654 | switch (state) { | 658 | switch (state) { |
655 | case NETDEV_REGISTER: | 659 | case NETDEV_REGISTER: |
660 | /* | ||
661 | * NB: cannot take rdev->mtx here because this may be | ||
662 | * called within code protected by it when interfaces | ||
663 | * are added with nl80211. | ||
664 | */ | ||
656 | mutex_init(&wdev->mtx); | 665 | mutex_init(&wdev->mtx); |
657 | INIT_LIST_HEAD(&wdev->event_list); | 666 | INIT_LIST_HEAD(&wdev->event_list); |
658 | spin_lock_init(&wdev->event_lock); | 667 | spin_lock_init(&wdev->event_lock); |
@@ -730,13 +739,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
730 | #endif | 739 | #endif |
731 | break; | 740 | break; |
732 | case NETDEV_UNREGISTER: | 741 | case NETDEV_UNREGISTER: |
733 | cfg80211_lock_rdev(rdev); | 742 | /* |
734 | 743 | * NB: cannot take rdev->mtx here because this may be | |
735 | if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == dev)) { | 744 | * called within code protected by it when interfaces |
736 | rdev->scan_req->aborted = true; | 745 | * are removed with nl80211. |
737 | ___cfg80211_scan_done(rdev); | 746 | */ |
738 | } | ||
739 | |||
740 | mutex_lock(&rdev->devlist_mtx); | 747 | mutex_lock(&rdev->devlist_mtx); |
741 | /* | 748 | /* |
742 | * It is possible to get NETDEV_UNREGISTER | 749 | * It is possible to get NETDEV_UNREGISTER |
@@ -755,7 +762,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
755 | #endif | 762 | #endif |
756 | } | 763 | } |
757 | mutex_unlock(&rdev->devlist_mtx); | 764 | mutex_unlock(&rdev->devlist_mtx); |
758 | cfg80211_unlock_rdev(rdev); | ||
759 | break; | 765 | break; |
760 | case NETDEV_PRE_UP: | 766 | case NETDEV_PRE_UP: |
761 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) | 767 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) |