--- /dev/null
+From Larry.Finger@lwfinger.net Fri Sep 21 16:46:56 2007
+From: Larry Finger <Larry.Finger@lwfinger.net>
+Date: Fri, 21 Sep 2007 19:20:01 -0500
+Subject: bcm43xx: Fix cancellation of work queue crashes
+To: stable@kernel.org
+Message-ID: <46f45fb1.1/fJmN2AFimI/WTt%Larry.Finger@lwfinger.net>
+
+
+port of 3f7086978fc0193eff24a77d8b57ac4debc088fa from mainline.
+
+A crash upon booting that is caused by bcm43xx has been reported [1] and
+found to be due to a work queue being reinitialized while work on that
+queue is still pending. This fix modifies the shutdown of work queues and
+prevents periodic work from being requeued during shutdown. With this patch,
+no more crashes on reboot were observed by the original reporter. I do not
+get that particular failure on my system; however, when running a large
+number of ifdown/ifup sequences, my system would kernel panic with the
+'caps lock' light blinking at roughly a 1 Hz rate. In addition, there were
+infrequent failures in the firmware that resulted in 'IRQ READY TIMEOUT'
+errors. With this patch, no more of the first type of failure occur, and
+incidence of the second type is greatly reduced.
+
+[1] http://bugzilla.kernel.org/show_bug.cgi?id=8937
+
+Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
+Acked-by: Michael Buesch <mb@bu3sch.de>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/net/wireless/bcm43xx/bcm43xx_main.c | 28 +++++++++++++++++++--------
+ drivers/net/wireless/bcm43xx/bcm43xx_main.h | 2 -
+ drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c | 2 -
+ 3 files changed, 22 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
++++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+@@ -3183,6 +3183,9 @@ static void bcm43xx_periodic_work_handle
+ unsigned long orig_trans_start = 0;
+
+ mutex_lock(&bcm->mutex);
++ /* keep from doing and rearming periodic work if shutting down */
++ if (bcm43xx_status(bcm) == BCM43xx_STAT_UNINIT)
++ goto unlock_mutex;
+ if (unlikely(bcm->periodic_state % 60 == 0)) {
+ /* Periodic work will take a long time, so we want it to
+ * be preemtible.
+@@ -3228,14 +3231,10 @@ static void bcm43xx_periodic_work_handle
+ mmiowb();
+ bcm->periodic_state++;
+ spin_unlock_irqrestore(&bcm->irq_lock, flags);
++unlock_mutex:
+ mutex_unlock(&bcm->mutex);
+ }
+
+-void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
+-{
+- cancel_rearming_delayed_work(&bcm->periodic_work);
+-}
+-
+ void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm)
+ {
+ struct delayed_work *work = &bcm->periodic_work;
+@@ -3285,6 +3284,14 @@ static int bcm43xx_rng_init(struct bcm43
+ return err;
+ }
+
++void bcm43xx_cancel_work(struct bcm43xx_private *bcm)
++{
++ /* The system must be unlocked when this routine is entered.
++ * If not, the next 2 steps may deadlock */
++ cancel_work_sync(&bcm->restart_work);
++ cancel_rearming_delayed_work(&bcm->periodic_work);
++}
++
+ static int bcm43xx_shutdown_all_wireless_cores(struct bcm43xx_private *bcm)
+ {
+ int ret = 0;
+@@ -3321,7 +3328,12 @@ static void bcm43xx_free_board(struct bc
+ {
+ bcm43xx_rng_exit(bcm);
+ bcm43xx_sysfs_unregister(bcm);
+- bcm43xx_periodic_tasks_delete(bcm);
++
++ mutex_lock(&(bcm)->mutex);
++ bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
++ mutex_unlock(&(bcm)->mutex);
++
++ bcm43xx_cancel_work(bcm);
+
+ mutex_lock(&(bcm)->mutex);
+ bcm43xx_shutdown_all_wireless_cores(bcm);
+@@ -4018,7 +4030,7 @@ static int bcm43xx_net_stop(struct net_d
+ err = bcm43xx_disable_interrupts_sync(bcm);
+ assert(!err);
+ bcm43xx_free_board(bcm);
+- flush_scheduled_work();
++ bcm43xx_cancel_work(bcm);
+
+ return 0;
+ }
+@@ -4150,9 +4162,9 @@ static void bcm43xx_chip_reset(struct wo
+ struct bcm43xx_phyinfo *phy;
+ int err = -ENODEV;
+
++ bcm43xx_cancel_work(bcm);
+ mutex_lock(&(bcm)->mutex);
+ if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
+- bcm43xx_periodic_tasks_delete(bcm);
+ phy = bcm43xx_current_phy(bcm);
+ err = bcm43xx_select_wireless_core(bcm, phy->type);
+ if (!err)
+--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.h
++++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h
+@@ -122,7 +122,7 @@ void bcm43xx_wireless_core_reset(struct
+ void bcm43xx_mac_suspend(struct bcm43xx_private *bcm);
+ void bcm43xx_mac_enable(struct bcm43xx_private *bcm);
+
+-void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm);
++void bcm43xx_cancel_work(struct bcm43xx_private *bcm);
+ void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm);
+
+ void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason);
+--- a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
++++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
+@@ -327,7 +327,7 @@ static ssize_t bcm43xx_attr_phymode_stor
+ goto out;
+ }
+
+- bcm43xx_periodic_tasks_delete(bcm);
++ bcm43xx_cancel_work(bcm);
+ mutex_lock(&(bcm)->mutex);
+ err = bcm43xx_select_wireless_core(bcm, phytype);
+ if (!err)