aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/bcm43xx
diff options
context:
space:
mode:
authorLarry Finger <Larry.Finger@lwfinger.net>2007-09-04 15:14:20 -0400
committerJohn W. Linville <linville@tuxdriver.com>2007-09-14 14:32:23 -0400
commit3f7086978fc0193eff24a77d8b57ac4debc088fa (patch)
tree28fb8efbc4b3532fe79e4d0d19969f8fb0a5939f /drivers/net/wireless/bcm43xx
parent0d4cbb5e7f60b2f1a4d8b7f6ea4cc264262c7a01 (diff)
[PATCH] bcm43xx: Fix cancellation of work queue crashes
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>
Diffstat (limited to 'drivers/net/wireless/bcm43xx')
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c28
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.h2
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c2
3 files changed, 22 insertions, 10 deletions
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index c5d6753a55ea..dfbd01eaaf34 100644
--- 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_handler(struct work_struct *work)
3183 unsigned long orig_trans_start = 0; 3183 unsigned long orig_trans_start = 0;
3184 3184
3185 mutex_lock(&bcm->mutex); 3185 mutex_lock(&bcm->mutex);
3186 /* keep from doing and rearming periodic work if shutting down */
3187 if (bcm43xx_status(bcm) == BCM43xx_STAT_UNINIT)
3188 goto unlock_mutex;
3186 if (unlikely(bcm->periodic_state % 60 == 0)) { 3189 if (unlikely(bcm->periodic_state % 60 == 0)) {
3187 /* Periodic work will take a long time, so we want it to 3190 /* Periodic work will take a long time, so we want it to
3188 * be preemtible. 3191 * be preemtible.
@@ -3228,14 +3231,10 @@ static void bcm43xx_periodic_work_handler(struct work_struct *work)
3228 mmiowb(); 3231 mmiowb();
3229 bcm->periodic_state++; 3232 bcm->periodic_state++;
3230 spin_unlock_irqrestore(&bcm->irq_lock, flags); 3233 spin_unlock_irqrestore(&bcm->irq_lock, flags);
3234unlock_mutex:
3231 mutex_unlock(&bcm->mutex); 3235 mutex_unlock(&bcm->mutex);
3232} 3236}
3233 3237
3234void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
3235{
3236 cancel_rearming_delayed_work(&bcm->periodic_work);
3237}
3238
3239void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm) 3238void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm)
3240{ 3239{
3241 struct delayed_work *work = &bcm->periodic_work; 3240 struct delayed_work *work = &bcm->periodic_work;
@@ -3285,6 +3284,14 @@ static int bcm43xx_rng_init(struct bcm43xx_private *bcm)
3285 return err; 3284 return err;
3286} 3285}
3287 3286
3287void bcm43xx_cancel_work(struct bcm43xx_private *bcm)
3288{
3289 /* The system must be unlocked when this routine is entered.
3290 * If not, the next 2 steps may deadlock */
3291 cancel_work_sync(&bcm->restart_work);
3292 cancel_delayed_work_sync(&bcm->periodic_work);
3293}
3294
3288static int bcm43xx_shutdown_all_wireless_cores(struct bcm43xx_private *bcm) 3295static int bcm43xx_shutdown_all_wireless_cores(struct bcm43xx_private *bcm)
3289{ 3296{
3290 int ret = 0; 3297 int ret = 0;
@@ -3321,7 +3328,12 @@ static void bcm43xx_free_board(struct bcm43xx_private *bcm)
3321{ 3328{
3322 bcm43xx_rng_exit(bcm); 3329 bcm43xx_rng_exit(bcm);
3323 bcm43xx_sysfs_unregister(bcm); 3330 bcm43xx_sysfs_unregister(bcm);
3324 bcm43xx_periodic_tasks_delete(bcm); 3331
3332 mutex_lock(&(bcm)->mutex);
3333 bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
3334 mutex_unlock(&(bcm)->mutex);
3335
3336 bcm43xx_cancel_work(bcm);
3325 3337
3326 mutex_lock(&(bcm)->mutex); 3338 mutex_lock(&(bcm)->mutex);
3327 bcm43xx_shutdown_all_wireless_cores(bcm); 3339 bcm43xx_shutdown_all_wireless_cores(bcm);
@@ -4016,7 +4028,7 @@ static int bcm43xx_net_stop(struct net_device *net_dev)
4016 err = bcm43xx_disable_interrupts_sync(bcm); 4028 err = bcm43xx_disable_interrupts_sync(bcm);
4017 assert(!err); 4029 assert(!err);
4018 bcm43xx_free_board(bcm); 4030 bcm43xx_free_board(bcm);
4019 flush_scheduled_work(); 4031 bcm43xx_cancel_work(bcm);
4020 4032
4021 return 0; 4033 return 0;
4022} 4034}
@@ -4148,9 +4160,9 @@ static void bcm43xx_chip_reset(struct work_struct *work)
4148 struct bcm43xx_phyinfo *phy; 4160 struct bcm43xx_phyinfo *phy;
4149 int err = -ENODEV; 4161 int err = -ENODEV;
4150 4162
4163 bcm43xx_cancel_work(bcm);
4151 mutex_lock(&(bcm)->mutex); 4164 mutex_lock(&(bcm)->mutex);
4152 if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { 4165 if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
4153 bcm43xx_periodic_tasks_delete(bcm);
4154 phy = bcm43xx_current_phy(bcm); 4166 phy = bcm43xx_current_phy(bcm);
4155 err = bcm43xx_select_wireless_core(bcm, phy->type); 4167 err = bcm43xx_select_wireless_core(bcm, phy->type);
4156 if (!err) 4168 if (!err)
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h
index c8f3c532bab5..14cfbeb582ef 100644
--- 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 bcm43xx_private *bcm, int connect_phy);
122void bcm43xx_mac_suspend(struct bcm43xx_private *bcm); 122void bcm43xx_mac_suspend(struct bcm43xx_private *bcm);
123void bcm43xx_mac_enable(struct bcm43xx_private *bcm); 123void bcm43xx_mac_enable(struct bcm43xx_private *bcm);
124 124
125void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm); 125void bcm43xx_cancel_work(struct bcm43xx_private *bcm);
126void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm); 126void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm);
127 127
128void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason); 128void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason);
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
index c71b998a3694..8ab5f93d192a 100644
--- 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_store(struct device *dev,
327 goto out; 327 goto out;
328 } 328 }
329 329
330 bcm43xx_periodic_tasks_delete(bcm); 330 bcm43xx_cancel_work(bcm);
331 mutex_lock(&(bcm)->mutex); 331 mutex_lock(&(bcm)->mutex);
332 err = bcm43xx_select_wireless_core(bcm, phytype); 332 err = bcm43xx_select_wireless_core(bcm, phytype);
333 if (!err) 333 if (!err)