diff mbox

[trusty] UBUNTU: SAUCE: brcmsmac: fix deadlock on missing firmware

Message ID 1396470747-22892-1-git-send-email-seth.forshee@canonical.com
State New
Headers show

Commit Message

Seth Forshee April 2, 2014, 8:32 p.m. UTC
From: Emil Goode <emilgoode@gmail.com>

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>
(cherry picked from commit 8fc1e8c240aab968db658b2d8d079b4391207a36
 git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git)
BugLink: http://bugs.launchpad.net/bugs/1300416
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
---
 drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

Comments

Stefan Bader April 3, 2014, 7:31 a.m. UTC | #1
Looks sensible to me. I don't know the code in detail but removing hw related
things sounds like something better be done in a higher layer... Worst case
there is some unused structure leaked which would be less fatal than dead locking.
Andy Whitcroft April 3, 2014, 7:54 a.m. UTC | #2
On Wed, Apr 02, 2014 at 03:32:27PM -0500, Seth Forshee wrote:
> From: Emil Goode <emilgoode@gmail.com>
> 
> 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>
> (cherry picked from commit 8fc1e8c240aab968db658b2d8d079b4391207a36
>  git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git)
> BugLink: http://bugs.launchpad.net/bugs/1300416
> Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
> ---
>  drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 14 ++++++--------
>  1 file 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 edc5d10..03a56df 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;

Looks sane to check for the firmware early and bail without initialising
at all.

Acked-by: Andy Whitcroft <apw@canonical.com>

-apw
Tim Gardner April 3, 2014, 12:29 p.m. UTC | #3

diff mbox

Patch

diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index edc5d10..03a56df 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;