NFE - Fix bug with imtimer transitions and improve performance
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 3 Sep 2009 05:06:22 +0000 (22:06 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 3 Sep 2009 05:06:22 +0000 (22:06 -0700)
* 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
sys/dev/netif/nfe/if_nfevar.h

index 1faa21e..b6c2b22 100644 (file)
@@ -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:
index 1e27a8b..02a1c50 100644 (file)
@@ -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;