[PATCH] bcm43xx: preemptible periodic work

Make the heavy periodic work preemptible to avoid disabling
local IRQs for several msecs.

Signed-off-by: Michael Buesch <mb@buesch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Michael Buesch
2006-06-05 20:24:21 +02:00
committed by John W. Linville
parent 78ff56a06e
commit 91769e7dd9
4 changed files with 143 additions and 13 deletions

View File

@@ -498,11 +498,21 @@ static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_private *bcm, u32 mas
return old_mask;
}
/* Synchronize IRQ top- and bottom-half.
* IRQs must be masked before calling this.
* This must not be called with the irq_lock held.
*/
static void bcm43xx_synchronize_irq(struct bcm43xx_private *bcm)
{
synchronize_irq(bcm->irq);
tasklet_disable(&bcm->isr_tasklet);
}
/* Make sure we don't receive more data from the device. */
static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate)
{
u32 old;
unsigned long flags;
u32 old;
bcm43xx_lock_irqonly(bcm, flags);
if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) {
@@ -510,8 +520,9 @@ static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *old
return -EBUSY;
}
old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
tasklet_disable(&bcm->isr_tasklet);
bcm43xx_unlock_irqonly(bcm, flags);
bcm43xx_synchronize_irq(bcm);
if (oldstate)
*oldstate = old;
@@ -3108,14 +3119,10 @@ static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm)
//TODO for APHY (temperature?)
}
static void bcm43xx_periodic_work_handler(void *d)
static void do_periodic_work(struct bcm43xx_private *bcm)
{
struct bcm43xx_private *bcm = d;
unsigned long flags;
unsigned int state;
bcm43xx_lock_irqsafe(bcm, flags);
state = bcm->periodic_state;
if (state % 8 == 0)
bcm43xx_periodic_every120sec(bcm);
@@ -3123,13 +3130,79 @@ static void bcm43xx_periodic_work_handler(void *d)
bcm43xx_periodic_every60sec(bcm);
if (state % 2 == 0)
bcm43xx_periodic_every30sec(bcm);
bcm43xx_periodic_every15sec(bcm);
if (state % 1 == 0)
bcm43xx_periodic_every15sec(bcm);
bcm->periodic_state = state + 1;
schedule_delayed_work(&bcm->periodic_work, HZ * 15);
}
mmiowb();
bcm43xx_unlock_irqsafe(bcm, flags);
/* Estimate a "Badness" value based on the periodic work
* state-machine state. "Badness" is worse (bigger), if the
* periodic work will take longer.
*/
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;
if (state % 1 == 0) /* every 15 sec */
badness += 1;
#define BADNESS_LIMIT 4
return badness;
}
static void bcm43xx_periodic_work_handler(void *d)
{
struct bcm43xx_private *bcm = d;
unsigned long flags;
u32 savedirqs = 0;
int badness;
badness = estimate_periodic_work_badness(bcm->periodic_state);
if (badness > BADNESS_LIMIT) {
/* Periodic work will take a long time, so we want it to
* be preemtible.
*/
bcm43xx_lock_irqonly(bcm, flags);
netif_stop_queue(bcm->net_dev);
if (bcm43xx_using_pio(bcm))
bcm43xx_pio_freeze_txqueues(bcm);
savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
bcm43xx_unlock_irqonly(bcm, flags);
bcm43xx_lock_noirq(bcm);
bcm43xx_synchronize_irq(bcm);
} else {
/* Periodic work should take short time, so we want low
* locking overhead.
*/
bcm43xx_lock_irqsafe(bcm, flags);
}
do_periodic_work(bcm);
if (badness > BADNESS_LIMIT) {
bcm43xx_lock_irqonly(bcm, flags);
if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
tasklet_enable(&bcm->isr_tasklet);
bcm43xx_interrupt_enable(bcm, savedirqs);
if (bcm43xx_using_pio(bcm))
bcm43xx_pio_thaw_txqueues(bcm);
}
netif_wake_queue(bcm->net_dev);
mmiowb();
bcm43xx_unlock_irqonly(bcm, flags);
bcm43xx_unlock_noirq(bcm);
} else {
mmiowb();
bcm43xx_unlock_irqsafe(bcm, flags);
}
}
static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
@@ -3670,9 +3743,11 @@ static int bcm43xx_net_open(struct net_device *net_dev)
static int bcm43xx_net_stop(struct net_device *net_dev)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
int err;
ieee80211softmac_stop(net_dev);
bcm43xx_disable_interrupts_sync(bcm, NULL);
err = bcm43xx_disable_interrupts_sync(bcm, NULL);
assert(!err);
bcm43xx_free_board(bcm);
return 0;