diff options
author | Michael Buesch <mb@bu3sch.de> | 2006-06-05 14:24:21 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2006-06-15 15:48:13 -0400 |
commit | 91769e7dd9cef7988dc4280f74ed168351beb5b8 (patch) | |
tree | ef854d83ec881882b94a3e88b580e2468f62bac1 | |
parent | 78ff56a06edc3407996173daf63e48f6b90c7062 (diff) |
[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>
-rw-r--r-- | drivers/net/wireless/bcm43xx/bcm43xx_main.c | 97 | ||||
-rw-r--r-- | drivers/net/wireless/bcm43xx/bcm43xx_phy.c | 7 | ||||
-rw-r--r-- | drivers/net/wireless/bcm43xx/bcm43xx_pio.c | 39 | ||||
-rw-r--r-- | drivers/net/wireless/bcm43xx/bcm43xx_pio.h | 13 |
4 files changed, 143 insertions, 13 deletions
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index 835a2df1fe30..77d0e390b021 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c | |||
@@ -498,11 +498,21 @@ static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_private *bcm, u32 mas | |||
498 | return old_mask; | 498 | return old_mask; |
499 | } | 499 | } |
500 | 500 | ||
501 | /* Synchronize IRQ top- and bottom-half. | ||
502 | * IRQs must be masked before calling this. | ||
503 | * This must not be called with the irq_lock held. | ||
504 | */ | ||
505 | static void bcm43xx_synchronize_irq(struct bcm43xx_private *bcm) | ||
506 | { | ||
507 | synchronize_irq(bcm->irq); | ||
508 | tasklet_disable(&bcm->isr_tasklet); | ||
509 | } | ||
510 | |||
501 | /* Make sure we don't receive more data from the device. */ | 511 | /* Make sure we don't receive more data from the device. */ |
502 | static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate) | 512 | static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate) |
503 | { | 513 | { |
504 | u32 old; | ||
505 | unsigned long flags; | 514 | unsigned long flags; |
515 | u32 old; | ||
506 | 516 | ||
507 | bcm43xx_lock_irqonly(bcm, flags); | 517 | bcm43xx_lock_irqonly(bcm, flags); |
508 | if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) { | 518 | if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) { |
@@ -510,8 +520,9 @@ static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *old | |||
510 | return -EBUSY; | 520 | return -EBUSY; |
511 | } | 521 | } |
512 | old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); | 522 | old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); |
513 | tasklet_disable(&bcm->isr_tasklet); | ||
514 | bcm43xx_unlock_irqonly(bcm, flags); | 523 | bcm43xx_unlock_irqonly(bcm, flags); |
524 | bcm43xx_synchronize_irq(bcm); | ||
525 | |||
515 | if (oldstate) | 526 | if (oldstate) |
516 | *oldstate = old; | 527 | *oldstate = old; |
517 | 528 | ||
@@ -3108,14 +3119,10 @@ static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm) | |||
3108 | //TODO for APHY (temperature?) | 3119 | //TODO for APHY (temperature?) |
3109 | } | 3120 | } |
3110 | 3121 | ||
3111 | static void bcm43xx_periodic_work_handler(void *d) | 3122 | static void do_periodic_work(struct bcm43xx_private *bcm) |
3112 | { | 3123 | { |
3113 | struct bcm43xx_private *bcm = d; | ||
3114 | unsigned long flags; | ||
3115 | unsigned int state; | 3124 | unsigned int state; |
3116 | 3125 | ||
3117 | bcm43xx_lock_irqsafe(bcm, flags); | ||
3118 | |||
3119 | state = bcm->periodic_state; | 3126 | state = bcm->periodic_state; |
3120 | if (state % 8 == 0) | 3127 | if (state % 8 == 0) |
3121 | bcm43xx_periodic_every120sec(bcm); | 3128 | bcm43xx_periodic_every120sec(bcm); |
@@ -3123,13 +3130,79 @@ static void bcm43xx_periodic_work_handler(void *d) | |||
3123 | bcm43xx_periodic_every60sec(bcm); | 3130 | bcm43xx_periodic_every60sec(bcm); |
3124 | if (state % 2 == 0) | 3131 | if (state % 2 == 0) |
3125 | bcm43xx_periodic_every30sec(bcm); | 3132 | bcm43xx_periodic_every30sec(bcm); |
3126 | bcm43xx_periodic_every15sec(bcm); | 3133 | if (state % 1 == 0) |
3134 | bcm43xx_periodic_every15sec(bcm); | ||
3127 | bcm->periodic_state = state + 1; | 3135 | bcm->periodic_state = state + 1; |
3128 | 3136 | ||
3129 | schedule_delayed_work(&bcm->periodic_work, HZ * 15); | 3137 | schedule_delayed_work(&bcm->periodic_work, HZ * 15); |
3138 | } | ||
3130 | 3139 | ||
3131 | mmiowb(); | 3140 | /* Estimate a "Badness" value based on the periodic work |
3132 | bcm43xx_unlock_irqsafe(bcm, flags); | 3141 | * state-machine state. "Badness" is worse (bigger), if the |
3142 | * periodic work will take longer. | ||
3143 | */ | ||
3144 | static int estimate_periodic_work_badness(unsigned int state) | ||
3145 | { | ||
3146 | int badness = 0; | ||
3147 | |||
3148 | if (state % 8 == 0) /* every 120 sec */ | ||
3149 | badness += 10; | ||
3150 | if (state % 4 == 0) /* every 60 sec */ | ||
3151 | badness += 5; | ||
3152 | if (state % 2 == 0) /* every 30 sec */ | ||
3153 | badness += 1; | ||
3154 | if (state % 1 == 0) /* every 15 sec */ | ||
3155 | badness += 1; | ||
3156 | |||
3157 | #define BADNESS_LIMIT 4 | ||
3158 | return badness; | ||
3159 | } | ||
3160 | |||
3161 | static void bcm43xx_periodic_work_handler(void *d) | ||
3162 | { | ||
3163 | struct bcm43xx_private *bcm = d; | ||
3164 | unsigned long flags; | ||
3165 | u32 savedirqs = 0; | ||
3166 | int badness; | ||
3167 | |||
3168 | badness = estimate_periodic_work_badness(bcm->periodic_state); | ||
3169 | if (badness > BADNESS_LIMIT) { | ||
3170 | /* Periodic work will take a long time, so we want it to | ||
3171 | * be preemtible. | ||
3172 | */ | ||
3173 | bcm43xx_lock_irqonly(bcm, flags); | ||
3174 | netif_stop_queue(bcm->net_dev); | ||
3175 | if (bcm43xx_using_pio(bcm)) | ||
3176 | bcm43xx_pio_freeze_txqueues(bcm); | ||
3177 | savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); | ||
3178 | bcm43xx_unlock_irqonly(bcm, flags); | ||
3179 | bcm43xx_lock_noirq(bcm); | ||
3180 | bcm43xx_synchronize_irq(bcm); | ||
3181 | } else { | ||
3182 | /* Periodic work should take short time, so we want low | ||
3183 | * locking overhead. | ||
3184 | */ | ||
3185 | bcm43xx_lock_irqsafe(bcm, flags); | ||
3186 | } | ||
3187 | |||
3188 | do_periodic_work(bcm); | ||
3189 | |||
3190 | if (badness > BADNESS_LIMIT) { | ||
3191 | bcm43xx_lock_irqonly(bcm, flags); | ||
3192 | if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) { | ||
3193 | tasklet_enable(&bcm->isr_tasklet); | ||
3194 | bcm43xx_interrupt_enable(bcm, savedirqs); | ||
3195 | if (bcm43xx_using_pio(bcm)) | ||
3196 | bcm43xx_pio_thaw_txqueues(bcm); | ||
3197 | } | ||
3198 | netif_wake_queue(bcm->net_dev); | ||
3199 | mmiowb(); | ||
3200 | bcm43xx_unlock_irqonly(bcm, flags); | ||
3201 | bcm43xx_unlock_noirq(bcm); | ||
3202 | } else { | ||
3203 | mmiowb(); | ||
3204 | bcm43xx_unlock_irqsafe(bcm, flags); | ||
3205 | } | ||
3133 | } | 3206 | } |
3134 | 3207 | ||
3135 | static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm) | 3208 | static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm) |
@@ -3670,9 +3743,11 @@ static int bcm43xx_net_open(struct net_device *net_dev) | |||
3670 | static int bcm43xx_net_stop(struct net_device *net_dev) | 3743 | static int bcm43xx_net_stop(struct net_device *net_dev) |
3671 | { | 3744 | { |
3672 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); | 3745 | struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); |
3746 | int err; | ||
3673 | 3747 | ||
3674 | ieee80211softmac_stop(net_dev); | 3748 | ieee80211softmac_stop(net_dev); |
3675 | bcm43xx_disable_interrupts_sync(bcm, NULL); | 3749 | err = bcm43xx_disable_interrupts_sync(bcm, NULL); |
3750 | assert(!err); | ||
3676 | bcm43xx_free_board(bcm); | 3751 | bcm43xx_free_board(bcm); |
3677 | 3752 | ||
3678 | return 0; | 3753 | return 0; |
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c index 952c2f8b8b5e..f8200deecc8a 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c | |||
@@ -1410,7 +1410,10 @@ static inline | |||
1410 | u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) | 1410 | u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) |
1411 | { | 1411 | { |
1412 | struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); | 1412 | struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); |
1413 | u16 ret; | ||
1414 | unsigned long flags; | ||
1413 | 1415 | ||
1416 | local_irq_save(flags); | ||
1414 | if (phy->connected) { | 1417 | if (phy->connected) { |
1415 | bcm43xx_phy_write(bcm, 0x15, 0xE300); | 1418 | bcm43xx_phy_write(bcm, 0x15, 0xE300); |
1416 | control <<= 8; | 1419 | control <<= 8; |
@@ -1430,8 +1433,10 @@ u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) | |||
1430 | bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0); | 1433 | bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0); |
1431 | udelay(8); | 1434 | udelay(8); |
1432 | } | 1435 | } |
1436 | ret = bcm43xx_phy_read(bcm, 0x002D); | ||
1437 | local_irq_restore(flags); | ||
1433 | 1438 | ||
1434 | return bcm43xx_phy_read(bcm, 0x002D); | 1439 | return ret; |
1435 | } | 1440 | } |
1436 | 1441 | ||
1437 | static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control) | 1442 | static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control) |
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c index e77e9d859f87..574085c46152 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c | |||
@@ -264,6 +264,8 @@ static void tx_tasklet(unsigned long d) | |||
264 | 264 | ||
265 | bcm43xx_lock_irqonly(bcm, flags); | 265 | bcm43xx_lock_irqonly(bcm, flags); |
266 | 266 | ||
267 | if (queue->tx_frozen) | ||
268 | goto out_unlock; | ||
267 | txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); | 269 | txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); |
268 | if (txctl & BCM43xx_PIO_TXCTL_SUSPEND) | 270 | if (txctl & BCM43xx_PIO_TXCTL_SUSPEND) |
269 | goto out_unlock; | 271 | goto out_unlock; |
@@ -633,5 +635,40 @@ void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) | |||
633 | bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL) | 635 | bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL) |
634 | & ~BCM43xx_PIO_TXCTL_SUSPEND); | 636 | & ~BCM43xx_PIO_TXCTL_SUSPEND); |
635 | bcm43xx_power_saving_ctl_bits(queue->bcm, -1, -1); | 637 | bcm43xx_power_saving_ctl_bits(queue->bcm, -1, -1); |
636 | tasklet_schedule(&queue->txtask); | 638 | if (!list_empty(&queue->txqueue)) |
639 | tasklet_schedule(&queue->txtask); | ||
640 | } | ||
641 | |||
642 | void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm) | ||
643 | { | ||
644 | struct bcm43xx_pio *pio; | ||
645 | |||
646 | assert(bcm43xx_using_pio(bcm)); | ||
647 | pio = bcm43xx_current_pio(bcm); | ||
648 | pio->queue0->tx_frozen = 1; | ||
649 | pio->queue1->tx_frozen = 1; | ||
650 | pio->queue2->tx_frozen = 1; | ||
651 | pio->queue3->tx_frozen = 1; | ||
637 | } | 652 | } |
653 | |||
654 | void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm) | ||
655 | { | ||
656 | struct bcm43xx_pio *pio; | ||
657 | |||
658 | assert(bcm43xx_using_pio(bcm)); | ||
659 | pio = bcm43xx_current_pio(bcm); | ||
660 | pio->queue0->tx_frozen = 0; | ||
661 | pio->queue1->tx_frozen = 0; | ||
662 | pio->queue2->tx_frozen = 0; | ||
663 | pio->queue3->tx_frozen = 0; | ||
664 | if (!list_empty(&pio->queue0->txqueue)) | ||
665 | tasklet_schedule(&pio->queue0->txtask); | ||
666 | if (!list_empty(&pio->queue1->txqueue)) | ||
667 | tasklet_schedule(&pio->queue1->txtask); | ||
668 | if (!list_empty(&pio->queue2->txqueue)) | ||
669 | tasklet_schedule(&pio->queue2->txtask); | ||
670 | if (!list_empty(&pio->queue3->txqueue)) | ||
671 | tasklet_schedule(&pio->queue3->txtask); | ||
672 | } | ||
673 | |||
674 | |||
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h index dfc78209e3a3..bc78a3c2cafb 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h | |||
@@ -54,6 +54,7 @@ struct bcm43xx_pioqueue { | |||
54 | u16 mmio_base; | 54 | u16 mmio_base; |
55 | 55 | ||
56 | u8 tx_suspended:1, | 56 | u8 tx_suspended:1, |
57 | tx_frozen:1, | ||
57 | need_workarounds:1; /* Workarounds needed for core.rev < 3 */ | 58 | need_workarounds:1; /* Workarounds needed for core.rev < 3 */ |
58 | 59 | ||
59 | /* Adjusted size of the device internal TX buffer. */ | 60 | /* Adjusted size of the device internal TX buffer. */ |
@@ -108,8 +109,12 @@ void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, | |||
108 | struct bcm43xx_xmitstatus *status); | 109 | struct bcm43xx_xmitstatus *status); |
109 | void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue); | 110 | void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue); |
110 | 111 | ||
112 | /* Suspend a TX queue on hardware level. */ | ||
111 | void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue); | 113 | void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue); |
112 | void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue); | 114 | void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue); |
115 | /* Suspend (freeze) the TX tasklet (software level). */ | ||
116 | void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm); | ||
117 | void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm); | ||
113 | 118 | ||
114 | #else /* CONFIG_BCM43XX_PIO */ | 119 | #else /* CONFIG_BCM43XX_PIO */ |
115 | 120 | ||
@@ -145,6 +150,14 @@ static inline | |||
145 | void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) | 150 | void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) |
146 | { | 151 | { |
147 | } | 152 | } |
153 | static inline | ||
154 | void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm) | ||
155 | { | ||
156 | } | ||
157 | static inline | ||
158 | void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm) | ||
159 | { | ||
160 | } | ||
148 | 161 | ||
149 | #endif /* CONFIG_BCM43XX_PIO */ | 162 | #endif /* CONFIG_BCM43XX_PIO */ |
150 | #endif /* BCM43xx_PIO_H_ */ | 163 | #endif /* BCM43xx_PIO_H_ */ |