| From 8fc1e8c240aab968db658b2d8d079b4391207a36 Mon Sep 17 00:00:00 2001 |
| From: Emil Goode <emilgoode@gmail.com> |
| Date: Sun, 9 Mar 2014 21:06:51 +0100 |
| Subject: brcmsmac: fix deadlock on missing firmware |
| |
| From: Emil Goode <emilgoode@gmail.com> |
| |
| commit 8fc1e8c240aab968db658b2d8d079b4391207a36 upstream. |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 14 ++++++-------- |
| 1 file changed, 6 insertions(+), 8 deletions(-) |
| |
| --- 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 ieee80 |
| 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 ieee80 |
| 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; |