b43legacy: Rewrite pwork locking

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.

This patch for b43legacy is patterned aftar the same patch
for b43 by Michael Buesch <mb@bu3sch.de>.

Signed-off-by: Larry Finger <larry.finger@lwfinger.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Larry Finger 2007-10-11 00:05:57 -05:00 committed by David S. Miller
parent 6be50837e3
commit f34eb692ac

View file

@ -1797,6 +1797,7 @@ void b43legacy_mac_enable(struct b43legacy_wldev *dev)
{
dev->mac_suspended--;
B43legacy_WARN_ON(dev->mac_suspended < 0);
B43legacy_WARN_ON(irqs_disabled());
if (dev->mac_suspended == 0) {
b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
b43legacy_read32(dev,
@ -1808,6 +1809,11 @@ void b43legacy_mac_enable(struct b43legacy_wldev *dev)
b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
b43legacy_power_saving_ctl_bits(dev, -1, -1);
/* Re-enable IRQs. */
spin_lock_irq(&dev->wl->irq_lock);
b43legacy_interrupt_enable(dev, dev->irq_savedstate);
spin_unlock_irq(&dev->wl->irq_lock);
}
}
@ -1817,20 +1823,31 @@ void b43legacy_mac_suspend(struct b43legacy_wldev *dev)
int i;
u32 tmp;
might_sleep();
B43legacy_WARN_ON(irqs_disabled());
B43legacy_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 = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
spin_unlock_irq(&dev->wl->irq_lock);
b43legacy_synchronize_irq(dev);
dev->irq_savedstate = tmp;
b43legacy_power_saving_ctl_bits(dev, -1, 1);
b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
b43legacy_read32(dev,
B43legacy_MMIO_STATUS_BITFIELD)
& ~B43legacy_SBF_MAC_ENABLED);
b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
for (i = 10000; i; i--) {
for (i = 40; i; i--) {
tmp = b43legacy_read32(dev,
B43legacy_MMIO_GEN_IRQ_REASON);
if (tmp & B43legacy_IRQ_MAC_SUSPENDED)
goto out;
udelay(1);
msleep(1);
}
b43legacyerr(dev->wl, "MAC suspend failed\n");
}
@ -2145,81 +2162,36 @@ static void do_periodic_work(struct b43legacy_wldev *dev)
b43legacy_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 b43legacy_periodic_work_handler(struct work_struct *work)
{
struct b43legacy_wldev *dev =
container_of(work, struct b43legacy_wldev,
periodic_work.work);
unsigned long flags;
struct b43legacy_wldev *dev = container_of(work, struct b43legacy_wldev,
periodic_work.work);
struct b43legacy_wl *wl = dev->wl;
unsigned long delay;
u32 savedirqs = 0;
int badness;
mutex_lock(&dev->wl->mutex);
mutex_lock(&wl->mutex);
if (unlikely(b43legacy_status(dev) != B43legacy_STAT_STARTED))
goto out;
if (b43legacy_debug(dev, B43legacy_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. */
b43legacy_tx_suspend(dev);
savedirqs = b43legacy_interrupt_disable(dev,
B43legacy_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);
b43legacy_synchronize_irq(dev);
do_periodic_work(dev);
do_periodic_work(dev);
spin_lock_irqsave(&dev->wl->irq_lock, flags);
b43legacy_interrupt_enable(dev, savedirqs);
b43legacy_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 (b43legacy_debug(dev, B43legacy_DBG_PWORK_FAST))
delay = msecs_to_jiffies(50);
else
delay = round_jiffies_relative(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 b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev)