Search Linux Wireless

Re: [PATCH] bcm43xx: Fix cancellation of work queues crashes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tuesday 04 September 2007, Larry Finger wrote:
> 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@xxxxxxxxxxxx>
> ---
> 
> John,
> 
> This fix should be sent to 2.6.23. Once it it there, I'll send it on to
> -stable.
> 
> Larry
> 
> 
>  drivers/net/wireless/bcm43xx/bcm43xx_main.c  |   28 +++++++++++++++++++--------
>  drivers/net/wireless/bcm43xx/bcm43xx_main.h  |    2 -
>  drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c |    3 +-
>  3 files changed, 23 insertions(+), 10 deletions(-)
> 
> Index: linux-2.6/drivers/net/wireless/bcm43xx/bcm43xx_main.c
> ===================================================================
> --- linux-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx_main.c
> +++ linux-2.6/drivers/net/wireless/bcm43xx/bcm43xx_main.c
> @@ -3197,6 +3197,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.
> @@ -3242,14 +3245,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;
> @@ -3299,6 +3298,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_delayed_work_sync(&bcm->periodic_work);
> +}
> +
>  static int bcm43xx_shutdown_all_wireless_cores(struct bcm43xx_private *bcm)
>  {
>  	int ret = 0;
> @@ -3335,7 +3342,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);
> @@ -4030,7 +4042,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;
>  }
> @@ -4162,9 +4174,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)
> Index: linux-2.6/drivers/net/wireless/bcm43xx/bcm43xx_main.h
> ===================================================================
> --- linux-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx_main.h
> +++ linux-2.6/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);
> Index: linux-2.6/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
> ===================================================================
> --- linux-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
> +++ linux-2.6/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
> @@ -327,8 +327,9 @@ static ssize_t bcm43xx_attr_phymode_stor
>  		goto out;
>  	}
>  
> -	bcm43xx_periodic_tasks_delete(bcm);
> +	bcm43xx_cancel_work(bcm);
>  	mutex_lock(&(bcm)->mutex);
> +	bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);

Don't change the status here. That is _wrong_.
The rest of the patch is OK.

>  	err = bcm43xx_select_wireless_core(bcm, phytype);
>  	if (!err)
>  		bcm43xx_periodic_tasks_setup(bcm);


-
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux