summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorEmil Goode <emilgoode@gmail.com>2014-03-09 21:06:51 +0100
committerJohn W. Linville <linville@tuxdriver.com>2014-03-14 19:49:15 +0100
commit8fc1e8c240aab968db658b2d8d079b4391207a36 (patch)
treecf2d7891b9ab63be0663316ef1ea07048cdf7b64 /drivers
parentath9k: clean up and enhance ANI debugfs file (diff)
downloadlinux-8fc1e8c240aab968db658b2d8d079b4391207a36.tar.xz
linux-8fc1e8c240aab968db658b2d8d079b4391207a36.zip
brcmsmac: fix deadlock on missing firmware
When brcm80211 firmware is not installed networking hangs. A deadlock happens because we call ieee80211_unregister_hw() from the .start callback of struct ieee80211_ops. When .start is called we are under rtnl lock and ieee80211_unregister_hw() tries to take it again. Function call stack: dev_change_flags() __dev_change_flags() __dev_open() ASSERT_RTNL() <-- Assert rtnl lock ops->ndo_open() .ndo_open = ieee80211_open, ieee80211_open() ieee80211_do_open() drv_start() local->ops->start() .start = brcms_ops_start, brcms_ops_start() brcms_remove() ieee80211_unregister_hw() rtnl_lock() <-- Here we deadlock Introduced by: commit 25b5632fb35ca61b8ae3eee235edcdc2883f7a5e ("brcmsmac: request firmware in .start() callback") This patch fixes the bug by removing the call to brcms_remove() and moves the brcms_request_fw() call to the top of the .start callback to not initiate anything unless firmware is installed. Signed-off-by: Emil Goode <emilgoode@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c14
1 files changed, 6 insertions, 8 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 925034b80e9c..93598cd7ee6a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -426,6 +426,12 @@ static int brcms_ops_start(struct ieee80211_hw *hw)
bool blocked;
int err;
+ if (!wl->ucode.bcm43xx_bomminor) {
+ err = brcms_request_fw(wl, wl->wlc->hw->d11core);
+ if (err)
+ return -ENOENT;
+ }
+
ieee80211_wake_queues(hw);
spin_lock_bh(&wl->lock);
blocked = brcms_rfkill_set_hw_state(wl);
@@ -433,14 +439,6 @@ static int brcms_ops_start(struct ieee80211_hw *hw)
if (!blocked)
wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
- if (!wl->ucode.bcm43xx_bomminor) {
- err = brcms_request_fw(wl, wl->wlc->hw->d11core);
- if (err) {
- brcms_remove(wl->wlc->hw->d11core);
- return -ENOENT;
- }
- }
-
spin_lock_bh(&wl->lock);
/* avoid acknowledging frames before a non-monitor device is added */
wl->mute_tx = true;