[PATCH] bcm43xx: Fix cancellation of work queue crashes
authorLarry Finger <Larry.Finger@lwfinger.net>
Tue, 4 Sep 2007 19:14:20 +0000 (14:14 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 14 Sep 2007 18:32:23 +0000 (14:32 -0400)
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>
drivers/net/wireless/bcm43xx/bcm43xx_main.c
drivers/net/wireless/bcm43xx/bcm43xx_main.h
drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c

index c5d6753a55ea80aa54e051b5d0ab7d671d352b4b..dfbd01eaaf34a5f5a088b15678d849b35de27316 100644 (file)
@@ -3183,6 +3183,9 @@ static void bcm43xx_periodic_work_handler(struct work_struct *work)
        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_handler(struct work_struct *work)
        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 bcm43xx_private *bcm)
        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_delayed_work_sync(&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 bcm43xx_private *bcm)
 {
        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);
@@ -4016,7 +4028,7 @@ static int bcm43xx_net_stop(struct net_device *net_dev)
        err = bcm43xx_disable_interrupts_sync(bcm);
        assert(!err);
        bcm43xx_free_board(bcm);
-       flush_scheduled_work();
+       bcm43xx_cancel_work(bcm);
 
        return 0;
 }
@@ -4148,9 +4160,9 @@ static void bcm43xx_chip_reset(struct work_struct *work)
        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)
index c8f3c532bab5d8b28bc482de6cd59328cec95ba6..14cfbeb582ef84e9303f53dbe85a5fc7701a5766 100644 (file)
@@ -122,7 +122,7 @@ void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy);
 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);
index c71b998a3694a2ed26b6d46ae5bb98f4c1c40ef4..8ab5f93d192ae7cac1c42452dcb6fa2843978be1 100644 (file)
@@ -327,7 +327,7 @@ static ssize_t bcm43xx_attr_phymode_store(struct device *dev,
                goto out;
        }
 
-       bcm43xx_periodic_tasks_delete(bcm);
+       bcm43xx_cancel_work(bcm);
        mutex_lock(&(bcm)->mutex);
        err = bcm43xx_select_wireless_core(bcm, phytype);
        if (!err)