b43: Rewrite pwork locking policy.
PATCH 1/1 b43: Rewrite pwork locking policy.
Implement much easier and more lightweight locking for the periodic work.
This also removes the last big busywait loop and replaces it by a sleeping loop.
Signed-off-by Michael Buesch <[email protected]>
drivers/net/wireless/b43/main.c +32 -56
@@ -1989,6 +1989,7 @@ void b43_mac_enable(struct b43_wldev *de { dev->mac_suspended--;B43_WARN_ON (dev->mac_suspended <0 ); +B43_WARN_ON (irqs_disabled ());if (dev->mac_suspended ==0 ) {b43_write32 (dev,B43_MMIO_MACCTL , b43_read32(dev, B43_MMIO_MACCTL)
@@ -1999,6 +2000,11 @@ void b43_mac_enable(struct b43_wldev *deb43_read32 (dev,B43_MMIO_MACCTL );b43_read32 (dev,B43_MMIO_GEN_IRQ_REASON );b43_power_saving_ctl_bits (dev,0 ); + +/* Re-enable IRQs. */ +spin_lock_irq (&dev->wl->irq_lock); +b43_interrupt_enable (dev, dev->irq_savedstate); +spin_unlock_irq (&dev->wl->irq_lock); } }
@@ -2008,23 +2014,34 @@ void b43_mac_suspend(struct b43_wldev *dint i;u32 tmp; +might_sleep (); +B43_WARN_ON (irqs_disabled ());B43_WARN_ON (dev->mac_suspended <0 ); +if (dev->mac_suspended ==0 ) { +/* Mask IRQs before suspending MAC. Otherwise +* the MAC stays busy and won't suspend. */ +spin_lock_irq (&dev->wl->irq_lock); + tmp =b43_interrupt_disable (dev,B43_IRQ_ALL ); +spin_unlock_irq (&dev->wl->irq_lock); +b43_synchronize_irq (dev); + dev->irq_savedstate = tmp; +b43_power_saving_ctl_bits (dev,B43_PS_AWAKE );b43_write32 (dev,B43_MMIO_MACCTL ,b43_read32 (dev,B43_MMIO_MACCTL ) & ~B43_MACCTL_ENABLED );/* force pci to flush the write */ b43_read32 (dev,B43_MMIO_MACCTL ); -for (i =10000 ; i; i--) { +for (i =40 ; i; i--) { tmp =b43_read32 (dev,B43_MMIO_GEN_IRQ_REASON );if (tmp &B43_IRQ_MAC_SUSPENDED ) goto out; -udelay (1 ); +msleep (1 ); }b43err (dev->wl,"MAC suspend failed\n" ); } - out: +out: dev->mac_suspended++; }
@@ -2373,77 +2390,36 @@ static void do_periodic_work(struct b43_b43_periodic_every15sec (dev); } -/* Estimate a "Badness" value based on the periodic work -* state-machine state. "Badness" is worse (bigger), if the -* periodic work will take longer. +/* Periodic work locking policy: +* The whole periodic work handler is protected by +* wl->mutex. If another lock is needed somewhere in the +* pwork callchain, it's aquired in-place, where it's needed. */ -static int estimate_periodic_work_badness (unsigned int state) -{ -int badness =0 ; - -if (state %8 ==0 )/* every 120 sec */ - badness +=10 ; -if (state %4 ==0 )/* every 60 sec */ - badness +=5 ; -if (state %2 ==0 )/* every 30 sec */ - badness +=1 ; - -#define BADNESS_LIMIT 4 -return badness; -} -static void b43_periodic_work_handler (struct work_struct *work) { -struct b43_wldev *dev = - container_of(work, struct b43_wldev, periodic_work.work); -unsigned long flags, delay; -u32 savedirqs =0 ; -int badness; + struct b43_wldev *dev = container_of(work, struct b43_wldev, + periodic_work.work); +struct b43_wl *wl = dev->wl; +unsigned long delay; -mutex_lock (&dev->wl->mutex); +mutex_lock (&wl->mutex);if (unlikely (b43_status (dev) !=B43_STAT_STARTED )) goto out;if (b43_debug (dev,B43_DBG_PWORK_STOP )) goto out_requeue; - badness =estimate_periodic_work_badness (dev->periodic_state); -if (badness >BADNESS_LIMIT ) { -spin_lock_irqsave (&dev->wl->irq_lock, flags); -/* Suspend TX as we don't want to transmit packets while -* we recalibrate the hardware. */ -b43_tx_suspend (dev); - savedirqs =b43_interrupt_disable (dev,B43_IRQ_ALL ); -/* Periodic work will take a long time, so we want it to -* be preemtible and release the spinlock. */ -spin_unlock_irqrestore (&dev->wl->irq_lock, flags); -b43_synchronize_irq (dev); - -do_periodic_work (dev); +do_periodic_work (dev); -spin_lock_irqsave (&dev->wl->irq_lock, flags); -b43_interrupt_enable (dev, savedirqs); -b43_tx_resume (dev); -mmiowb (); -spin_unlock_irqrestore (&dev->wl->irq_lock, flags); - }else { -/* Take the global driver lock. This will lock any operation. */ -spin_lock_irqsave (&dev->wl->irq_lock, flags); - -do_periodic_work (dev); - -mmiowb (); -spin_unlock_irqrestore (&dev->wl->irq_lock, flags); - } dev->periodic_state++; out_requeue:if (b43_debug (dev,B43_DBG_PWORK_FAST )) delay =msecs_to_jiffies (50 );else delay =round_jiffies (HZ *15 ); -queue_delayed_work (dev->wl->hw->workqueue, &dev->periodic_work, delay); +queue_delayed_work (wl->hw->workqueue, &dev->periodic_work, delay); out: -mutex_unlock (&dev->wl->mutex); +mutex_unlock (&wl->mutex); }static void b43_periodic_tasks_setup (struct b43_wldev *dev)