From c00ddf3338e01a6f586adc9280ec218af71a7eb2 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Wed, 2 Sep 2009 22:06:22 -0700 Subject: [PATCH] NFE - Fix bug with imtimer transitions and improve performance * When the imtimer transitions to a new state via a sysctl the code called ifp->if_init() which more often then not caused the device to stop taking all interrupts. * Change the way dynamic interrupt moderation works. Timer moderation is not turned on until the discrete interrupt rate exceeds the threshold. e.g. by default 500uS is 2000 interrupts/second so timer moderation is not turned on until the discrete interrupt rate exceeds 2000 ips. This allows the device to respond interactively as long as traffic levels are reasonable, before converting into moderation-timer-based batching. Timer moderation is turned off and we go back to discrete interrupts if the average rate over ~4 seconds falls below the threshold. * Change the interrupt moderation default from 125uS to 500uS. 125uS can saturate the cpu if the interrupt line is shared with other devices. A larger moderation timer is also more reasonable now that discrete interrupts are left intact until the rate exceeds the threshold. The moderation timer should be roughly designed to deal with the txring and rxring size. --- sys/dev/netif/nfe/if_nfe.c | 63 +++++++++++++++++++++++++---------- sys/dev/netif/nfe/if_nfevar.h | 3 ++ 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/sys/dev/netif/nfe/if_nfe.c b/sys/dev/netif/nfe/if_nfe.c index 1faa21e4c2..b6c2b22d5a 100644 --- a/sys/dev/netif/nfe/if_nfe.c +++ b/sys/dev/netif/nfe/if_nfe.c @@ -160,8 +160,14 @@ static int nfe_sysctl_imtime(SYSCTL_HANDLER_ARGS); static int nfe_debug = 0; static int nfe_rx_ring_count = NFE_RX_RING_DEF_COUNT; static int nfe_tx_ring_count = NFE_TX_RING_DEF_COUNT; -/* hw timer simulated interrupt moderation @8000Hz */ -static int nfe_imtime = -125; +/* + * hw timer simulated interrupt moderation @2000Hz. Negative values + * disable the timer when no traffic is present. + * + * XXX 8000Hz might be better but if the interrupt is shared it can + * blow out the cpu. + */ +static int nfe_imtime = -500; /* uS */ TUNABLE_INT("hw.nfe.rx_ring_count", &nfe_rx_ring_count); TUNABLE_INT("hw.nfe.tx_ring_count", &nfe_tx_ring_count); @@ -887,6 +893,25 @@ nfe_intr(void *arg) return; /* not for us */ NFE_WRITE(sc, NFE_IRQ_STATUS, r); + if (sc->sc_rate_second != time_second) { + /* + * Calculate sc_rate_avg - interrupts per second. + */ + sc->sc_rate_second = time_second; + if (sc->sc_rate_avg < sc->sc_rate_acc) + sc->sc_rate_avg = sc->sc_rate_acc; + else + sc->sc_rate_avg = (sc->sc_rate_avg * 3 + + sc->sc_rate_acc) / 4; + sc->sc_rate_acc = 0; + } else if (sc->sc_rate_avg < sc->sc_rate_acc) { + /* + * Don't wait for a tick to roll over if we are taking + * a lot of interrupts. + */ + sc->sc_rate_avg = sc->sc_rate_acc; + } + DPRINTFN(sc, 5, "%s: interrupt register %x\n", __func__, r); if (r & NFE_IRQ_LINK) { @@ -897,6 +922,7 @@ nfe_intr(void *arg) if (ifp->if_flags & IFF_RUNNING) { int ret; + int rate; /* check Rx ring */ ret = nfe_rxeof(sc); @@ -904,18 +930,26 @@ nfe_intr(void *arg) /* check Tx ring */ ret |= nfe_txeof(sc, 1); + /* update the rate accumulator */ + if (ret) + ++sc->sc_rate_acc; + if (sc->sc_flags & NFE_F_DYN_IM) { - if (ret && (sc->sc_flags & NFE_F_IRQ_TIMER) == 0) { + rate = 1000000 / sc->sc_imtime; + if ((sc->sc_flags & NFE_F_IRQ_TIMER) == 0 && + sc->sc_rate_avg > rate) { /* - * Assume that using hardware timer could reduce - * the interrupt rate. + * Use the hardware timer to reduce the + * interrupt rate if the discrete interrupt + * rate has exceeded our threshold. */ NFE_WRITE(sc, NFE_IRQ_MASK, NFE_IRQ_IMTIMER); sc->sc_flags |= NFE_F_IRQ_TIMER; - } else if (!ret && (sc->sc_flags & NFE_F_IRQ_TIMER)) { + } else if ((sc->sc_flags & NFE_F_IRQ_TIMER) && + sc->sc_rate_avg <= rate) { /* - * Nothing needs to be processed, fall back to - * use TX/RX interrupts. + * Use discrete TX/RX interrupts if the rate + * has fallen below our threshold. */ NFE_WRITE(sc, NFE_IRQ_MASK, NFE_IRQ_NOIMTIMER); sc->sc_flags &= ~NFE_F_IRQ_TIMER; @@ -2288,22 +2322,15 @@ nfe_sysctl_imtime(SYSCTL_HANDLER_ARGS) } if (v != sc->sc_imtime || (flags ^ sc->sc_flags)) { - int old_imtime = sc->sc_imtime; - uint32_t old_flags = sc->sc_flags; - + if (NFE_IMTIME(v) == 0) + v = 0; sc->sc_imtime = v; sc->sc_flags = flags; sc->sc_irq_enable = NFE_IRQ_ENABLE(sc); if ((ifp->if_flags & (IFF_POLLING | IFF_RUNNING)) == IFF_RUNNING) { - if (old_imtime * sc->sc_imtime == 0 || - (old_flags ^ sc->sc_flags)) { - ifp->if_init(sc); - } else { - NFE_WRITE(sc, NFE_IMTIMER, - NFE_IMTIME(sc->sc_imtime)); - } + nfe_enable_intrs(sc); } } back: diff --git a/sys/dev/netif/nfe/if_nfevar.h b/sys/dev/netif/nfe/if_nfevar.h index 1e27a8b6c0..02a1c50816 100644 --- a/sys/dev/netif/nfe/if_nfevar.h +++ b/sys/dev/netif/nfe/if_nfevar.h @@ -78,6 +78,9 @@ struct nfe_softc { struct arpcom arpcom; int sc_mem_rid; + time_t sc_rate_second; + int sc_rate_acc; + int sc_rate_avg; struct resource *sc_mem_res; bus_space_handle_t sc_memh; bus_space_tag_t sc_memt; -- 2.41.0