ath5k: reduce interrupt load caused by rx/tx interrupts
authorFelix Fietkau <nbd@openwrt.org>
Sun, 10 Apr 2011 16:32:19 +0000 (18:32 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 12 Apr 2011 20:59:27 +0000 (16:59 -0400)
While the rx/tx tasklet is pending, new unnecessary interrupts may arrive.
Decrease the load by temporarily disabling the interrupts until the tasklet
has completed.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath5k/ath5k.h
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h

index fcaf4ed84ca5fb82d40975552e849f41680b8a7c..e303db7ee6f667c77f7d148b872a287cb575bc85 100644 (file)
@@ -872,6 +872,19 @@ enum ath5k_int {
        AR5K_INT_QTRIG  =       0x40000000, /* Non common */
        AR5K_INT_GLOBAL =       0x80000000,
 
+       AR5K_INT_TX_ALL = AR5K_INT_TXOK
+               | AR5K_INT_TXDESC
+               | AR5K_INT_TXERR
+               | AR5K_INT_TXEOL
+               | AR5K_INT_TXURN,
+
+       AR5K_INT_RX_ALL = AR5K_INT_RXOK
+               | AR5K_INT_RXDESC
+               | AR5K_INT_RXERR
+               | AR5K_INT_RXNOFRM
+               | AR5K_INT_RXEOL
+               | AR5K_INT_RXORN,
+
        AR5K_INT_COMMON  = AR5K_INT_RXOK
                | AR5K_INT_RXDESC
                | AR5K_INT_RXERR
index a799d04e0c8cfe9b42bc3cb5b62a94142b0769d1..c7da004543971b58458ae03f734085ae65907e90 100644 (file)
@@ -1443,6 +1443,21 @@ ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
        return true;
 }
 
+static void
+ath5k_set_current_imask(struct ath5k_softc *sc)
+{
+       enum ath5k_int imask = sc->imask;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sc->irqlock, flags);
+       if (sc->rx_pending)
+               imask &= ~AR5K_INT_RX_ALL;
+       if (sc->tx_pending)
+               imask &= ~AR5K_INT_TX_ALL;
+       ath5k_hw_set_imr(sc->ah, imask);
+       spin_unlock_irqrestore(&sc->irqlock, flags);
+}
+
 static void
 ath5k_tasklet_rx(unsigned long data)
 {
@@ -1506,6 +1521,8 @@ next:
        } while (ath5k_rxbuf_setup(sc, bf) == 0);
 unlock:
        spin_unlock(&sc->rxbuflock);
+       sc->rx_pending = false;
+       ath5k_set_current_imask(sc);
 }
 
 
@@ -1693,6 +1710,9 @@ ath5k_tasklet_tx(unsigned long data)
        for (i=0; i < AR5K_NUM_TX_QUEUES; i++)
                if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i)))
                        ath5k_tx_processq(sc, &sc->txqs[i]);
+
+       sc->tx_pending = false;
+       ath5k_set_current_imask(sc);
 }
 
 
@@ -2122,6 +2142,20 @@ ath5k_intr_calibration_poll(struct ath5k_hw *ah)
         * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
 }
 
+static void
+ath5k_schedule_rx(struct ath5k_softc *sc)
+{
+       sc->rx_pending = true;
+       tasklet_schedule(&sc->rxtq);
+}
+
+static void
+ath5k_schedule_tx(struct ath5k_softc *sc)
+{
+       sc->tx_pending = true;
+       tasklet_schedule(&sc->txtq);
+}
+
 irqreturn_t
 ath5k_intr(int irq, void *dev_id)
 {
@@ -2164,7 +2198,7 @@ ath5k_intr(int irq, void *dev_id)
                                ieee80211_queue_work(sc->hw, &sc->reset_work);
                        }
                        else
-                               tasklet_schedule(&sc->rxtq);
+                               ath5k_schedule_rx(sc);
                } else {
                        if (status & AR5K_INT_SWBA) {
                                tasklet_hi_schedule(&sc->beacontq);
@@ -2182,10 +2216,10 @@ ath5k_intr(int irq, void *dev_id)
                                ath5k_hw_update_tx_triglevel(ah, true);
                        }
                        if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
-                               tasklet_schedule(&sc->rxtq);
+                               ath5k_schedule_rx(sc);
                        if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
                                        | AR5K_INT_TXERR | AR5K_INT_TXEOL))
-                               tasklet_schedule(&sc->txtq);
+                               ath5k_schedule_tx(sc);
                        if (status & AR5K_INT_BMISS) {
                                /* TODO */
                        }
@@ -2204,6 +2238,9 @@ ath5k_intr(int irq, void *dev_id)
 
        } while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
 
+       if (sc->rx_pending || sc->tx_pending)
+               ath5k_set_current_imask(sc);
+
        if (unlikely(!counter))
                ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
 
@@ -2575,6 +2612,8 @@ done:
 
 static void stop_tasklets(struct ath5k_softc *sc)
 {
+       sc->rx_pending = false;
+       sc->tx_pending = false;
        tasklet_kill(&sc->rxtq);
        tasklet_kill(&sc->txtq);
        tasklet_kill(&sc->calib);
index 978f1f4ac2f33b89c92dd40652faca4fc75ecb8d..4c4e36064a3b339f3f4ffc07baeae951d52c6ec6 100644 (file)
@@ -207,6 +207,10 @@ struct ath5k_softc {
 
        enum ath5k_int          imask;          /* interrupt mask copy */
 
+       spinlock_t              irqlock;
+       bool                    rx_pending;     /* rx tasklet pending */
+       bool                    tx_pending;     /* tx tasklet pending */
+
        u8                      lladdr[ETH_ALEN];
        u8                      bssidmask[ETH_ALEN];