Commit c266c71a authored by Felix Fietkau's avatar Felix Fietkau Committed by John W. Linville

ath5k: reduce interrupt load caused by rx/tx interrupts

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: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5b7916ad
...@@ -872,6 +872,19 @@ enum ath5k_int { ...@@ -872,6 +872,19 @@ enum ath5k_int {
AR5K_INT_QTRIG = 0x40000000, /* Non common */ AR5K_INT_QTRIG = 0x40000000, /* Non common */
AR5K_INT_GLOBAL = 0x80000000, 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_COMMON = AR5K_INT_RXOK
| AR5K_INT_RXDESC | AR5K_INT_RXDESC
| AR5K_INT_RXERR | AR5K_INT_RXERR
......
...@@ -1443,6 +1443,21 @@ ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs) ...@@ -1443,6 +1443,21 @@ ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
return true; 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 static void
ath5k_tasklet_rx(unsigned long data) ath5k_tasklet_rx(unsigned long data)
{ {
...@@ -1506,6 +1521,8 @@ ath5k_tasklet_rx(unsigned long data) ...@@ -1506,6 +1521,8 @@ ath5k_tasklet_rx(unsigned long data)
} while (ath5k_rxbuf_setup(sc, bf) == 0); } while (ath5k_rxbuf_setup(sc, bf) == 0);
unlock: unlock:
spin_unlock(&sc->rxbuflock); spin_unlock(&sc->rxbuflock);
sc->rx_pending = false;
ath5k_set_current_imask(sc);
} }
...@@ -1693,6 +1710,9 @@ ath5k_tasklet_tx(unsigned long data) ...@@ -1693,6 +1710,9 @@ ath5k_tasklet_tx(unsigned long data)
for (i=0; i < AR5K_NUM_TX_QUEUES; i++) for (i=0; i < AR5K_NUM_TX_QUEUES; i++)
if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i))) if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i)))
ath5k_tx_processq(sc, &sc->txqs[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) ...@@ -2122,6 +2142,20 @@ ath5k_intr_calibration_poll(struct ath5k_hw *ah)
* AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */ * 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 irqreturn_t
ath5k_intr(int irq, void *dev_id) ath5k_intr(int irq, void *dev_id)
{ {
...@@ -2164,7 +2198,7 @@ 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); ieee80211_queue_work(sc->hw, &sc->reset_work);
} }
else else
tasklet_schedule(&sc->rxtq); ath5k_schedule_rx(sc);
} else { } else {
if (status & AR5K_INT_SWBA) { if (status & AR5K_INT_SWBA) {
tasklet_hi_schedule(&sc->beacontq); tasklet_hi_schedule(&sc->beacontq);
...@@ -2182,10 +2216,10 @@ ath5k_intr(int irq, void *dev_id) ...@@ -2182,10 +2216,10 @@ ath5k_intr(int irq, void *dev_id)
ath5k_hw_update_tx_triglevel(ah, true); ath5k_hw_update_tx_triglevel(ah, true);
} }
if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR)) if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
tasklet_schedule(&sc->rxtq); ath5k_schedule_rx(sc);
if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
| AR5K_INT_TXERR | AR5K_INT_TXEOL)) | AR5K_INT_TXERR | AR5K_INT_TXEOL))
tasklet_schedule(&sc->txtq); ath5k_schedule_tx(sc);
if (status & AR5K_INT_BMISS) { if (status & AR5K_INT_BMISS) {
/* TODO */ /* TODO */
} }
...@@ -2204,6 +2238,9 @@ ath5k_intr(int irq, void *dev_id) ...@@ -2204,6 +2238,9 @@ ath5k_intr(int irq, void *dev_id)
} while (ath5k_hw_is_intr_pending(ah) && --counter > 0); } while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
if (sc->rx_pending || sc->tx_pending)
ath5k_set_current_imask(sc);
if (unlikely(!counter)) if (unlikely(!counter))
ATH5K_WARN(sc, "too many interrupts, giving up for now\n"); ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
...@@ -2575,6 +2612,8 @@ ath5k_init_hw(struct ath5k_softc *sc) ...@@ -2575,6 +2612,8 @@ ath5k_init_hw(struct ath5k_softc *sc)
static void stop_tasklets(struct ath5k_softc *sc) static void stop_tasklets(struct ath5k_softc *sc)
{ {
sc->rx_pending = false;
sc->tx_pending = false;
tasklet_kill(&sc->rxtq); tasklet_kill(&sc->rxtq);
tasklet_kill(&sc->txtq); tasklet_kill(&sc->txtq);
tasklet_kill(&sc->calib); tasklet_kill(&sc->calib);
......
...@@ -207,6 +207,10 @@ struct ath5k_softc { ...@@ -207,6 +207,10 @@ struct ath5k_softc {
enum ath5k_int imask; /* interrupt mask copy */ 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 lladdr[ETH_ALEN];
u8 bssidmask[ETH_ALEN]; u8 bssidmask[ETH_ALEN];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment