diff options
Diffstat (limited to 'sys/dev/re')
-rw-r--r-- | sys/dev/re/if_re.c | 207 |
1 files changed, 175 insertions, 32 deletions
diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c index 5708e98..e8ae822 100644 --- a/sys/dev/re/if_re.c +++ b/sys/dev/re/if_re.c @@ -157,6 +157,8 @@ MODULE_DEPEND(re, miibus, 1, 1, 1); #include "miibus_if.h" /* Tunables. */ +static int intr_filter = 0; +TUNABLE_INT("hw.re.intr_filter", &intr_filter); static int msi_disable = 0; TUNABLE_INT("hw.re.msi_disable", &msi_disable); static int msix_disable = 0; @@ -253,6 +255,7 @@ static int re_poll (struct ifnet *, enum poll_cmd, int); static int re_poll_locked (struct ifnet *, enum poll_cmd, int); #endif static int re_intr (void *); +static void re_intr_msi (void *); static void re_tick (void *); static void re_int_task (void *, int); static void re_start (struct ifnet *); @@ -290,6 +293,8 @@ static int re_diag (struct rl_softc *); static void re_add_sysctls (struct rl_softc *); static int re_sysctl_stats (SYSCTL_HANDLER_ARGS); +static int sysctl_int_range (SYSCTL_HANDLER_ARGS, int, int); +static int sysctl_hw_re_int_mod (SYSCTL_HANDLER_ARGS); static device_method_t re_methods[] = { /* Device interface */ @@ -1574,19 +1579,19 @@ re_attach(device_t dev) } #endif +#ifdef RE_TX_MODERATION + intr_filter = 1; +#endif /* Hook interrupt last to avoid having to lock softc */ - if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0) + if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0 && + intr_filter == 0) { + error = bus_setup_intr(dev, sc->rl_irq[0], + INTR_TYPE_NET | INTR_MPSAFE, NULL, re_intr_msi, sc, + &sc->rl_intrhand[0]); + } else { error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc, &sc->rl_intrhand[0]); - else { - for (i = 0; i < RL_MSI_MESSAGES; i++) { - error = bus_setup_intr(dev, sc->rl_irq[i], - INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc, - &sc->rl_intrhand[i]); - if (error != 0) - break; - } } if (error) { device_printf(dev, "couldn't set up irq\n"); @@ -1657,31 +1662,22 @@ re_detach(device_t dev) * stopped here. */ - for (i = 0; i < RL_MSI_MESSAGES; i++) { - if (sc->rl_intrhand[i] != NULL) { - bus_teardown_intr(dev, sc->rl_irq[i], - sc->rl_intrhand[i]); - sc->rl_intrhand[i] = NULL; - } + if (sc->rl_intrhand[0] != NULL) { + bus_teardown_intr(dev, sc->rl_irq[0], sc->rl_intrhand[0]); + sc->rl_intrhand[0] = NULL; } if (ifp != NULL) if_free(ifp); - if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0) { - if (sc->rl_irq[0] != NULL) { - bus_release_resource(dev, SYS_RES_IRQ, 0, - sc->rl_irq[0]); - sc->rl_irq[0] = NULL; - } - } else { - for (i = 0, rid = 1; i < RL_MSI_MESSAGES; i++, rid++) { - if (sc->rl_irq[i] != NULL) { - bus_release_resource(dev, SYS_RES_IRQ, rid, - sc->rl_irq[i]); - sc->rl_irq[i] = NULL; - } - } - pci_release_msi(dev); + if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0) + rid = 0; + else + rid = 1; + if (sc->rl_irq[0] != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, rid, sc->rl_irq[0]); + sc->rl_irq[0] = NULL; } + if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0) + pci_release_msi(dev); if (sc->rl_res_pba) { rid = PCIR_BAR(4); bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->rl_res_pba); @@ -1970,6 +1966,7 @@ re_rx_list_init(struct rl_softc *sc) sc->rl_ldata.rl_rx_prodidx = 0; sc->rl_head = sc->rl_tail = NULL; + sc->rl_int_rx_act = 0; return (0); } @@ -1993,6 +1990,7 @@ re_jrx_list_init(struct rl_softc *sc) sc->rl_ldata.rl_rx_prodidx = 0; sc->rl_head = sc->rl_tail = NULL; + sc->rl_int_rx_act = 0; return (0); } @@ -2478,6 +2476,87 @@ re_int_task(void *arg, int npending) CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); } +static void +re_intr_msi(void *xsc) +{ + struct rl_softc *sc; + struct ifnet *ifp; + uint16_t intrs, status; + + sc = xsc; + RL_LOCK(sc); + + ifp = sc->rl_ifp; +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + RL_UNLOCK(sc); + return; + } +#endif + /* Disable interrupts. */ + CSR_WRITE_2(sc, RL_IMR, 0); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + RL_UNLOCK(sc); + return; + } + + intrs = RL_INTRS_CPLUS; + status = CSR_READ_2(sc, RL_ISR); + CSR_WRITE_2(sc, RL_ISR, status); + if (sc->rl_int_rx_act > 0) { + intrs &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR | RL_ISR_FIFO_OFLOW | + RL_ISR_RX_OVERRUN); + status &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR | RL_ISR_FIFO_OFLOW | + RL_ISR_RX_OVERRUN); + } + + if (status & (RL_ISR_TIMEOUT_EXPIRED | RL_ISR_RX_OK | RL_ISR_RX_ERR | + RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN)) { + re_rxeof(sc, NULL); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + if (sc->rl_int_rx_mod != 0 && + (status & (RL_ISR_RX_OK | RL_ISR_RX_ERR | + RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN)) != 0) { + /* Rearm one-shot timer. */ + CSR_WRITE_4(sc, RL_TIMERCNT, 1); + intrs &= ~(RL_ISR_RX_OK | RL_ISR_RX_ERR | + RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN); + sc->rl_int_rx_act = 1; + } else { + intrs |= RL_ISR_RX_OK | RL_ISR_RX_ERR | + RL_ISR_FIFO_OFLOW | RL_ISR_RX_OVERRUN; + sc->rl_int_rx_act = 0; + } + } + } + + /* + * Some chips will ignore a second TX request issued + * while an existing transmission is in progress. If + * the transmitter goes idle but there are still + * packets waiting to be sent, we need to restart the + * channel here to flush them out. This only seems to + * be required with the PCIe devices. + */ + if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) && + (sc->rl_flags & RL_FLAG_PCIE)) + CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); + if (status & (RL_ISR_TX_OK | RL_ISR_TX_ERR | RL_ISR_TX_DESC_UNAVAIL)) + re_txeof(sc); + + if (status & RL_ISR_SYSTEM_ERR) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + re_init_locked(sc); + } + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + re_start_locked(ifp); + CSR_WRITE_2(sc, RL_IMR, intrs); + } + RL_UNLOCK(sc); +} + static int re_encap(struct rl_softc *sc, struct mbuf **m_head) { @@ -3007,18 +3086,35 @@ re_init_locked(struct rl_softc *sc) CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); #endif -#ifdef RE_TX_MODERATION /* * Initialize the timer interrupt register so that * a timer interrupt will be generated once the timer * reaches a certain number of ticks. The timer is - * reloaded on each transmit. This gives us TX interrupt + * reloaded on each transmit. + */ +#ifdef RE_TX_MODERATION + /* + * Use timer interrupt register to moderate TX interrupt * moderation, which dramatically improves TX frame rate. */ if (sc->rl_type == RL_8169) CSR_WRITE_4(sc, RL_TIMERINT_8169, 0x800); else CSR_WRITE_4(sc, RL_TIMERINT, 0x400); +#else + /* + * Use timer interrupt register to moderate RX interrupt + * moderation. + */ + if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) != 0 && + intr_filter == 0) { + if (sc->rl_type == RL_8169) + CSR_WRITE_4(sc, RL_TIMERINT_8169, + RL_USECS(sc->rl_int_rx_mod)); + } else { + if (sc->rl_type == RL_8169) + CSR_WRITE_4(sc, RL_TIMERINT_8169, RL_USECS(0)); + } #endif /* @@ -3535,6 +3631,7 @@ re_add_sysctls(struct rl_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *children; + int error; ctx = device_get_sysctl_ctx(sc->rl_dev); children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->rl_dev)); @@ -3542,6 +3639,26 @@ re_add_sysctls(struct rl_softc *sc) SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "stats", CTLTYPE_INT | CTLFLAG_RW, sc, 0, re_sysctl_stats, "I", "Statistics Information"); + if ((sc->rl_flags & (RL_FLAG_MSI | RL_FLAG_MSIX)) == 0) + return; + + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "int_rx_mod", + CTLTYPE_INT | CTLFLAG_RW, &sc->rl_int_rx_mod, 0, + sysctl_hw_re_int_mod, "I", "re RX interrupt moderation"); + /* Pull in device tunables. */ + sc->rl_int_rx_mod = RL_TIMER_DEFAULT; + error = resource_int_value(device_get_name(sc->rl_dev), + device_get_unit(sc->rl_dev), "int_rx_mod", &sc->rl_int_rx_mod); + if (error == 0) { + if (sc->rl_int_rx_mod < RL_TIMER_MIN || + sc->rl_int_rx_mod > RL_TIMER_MAX) { + device_printf(sc->rl_dev, "int_rx_mod value out of " + "range; using default: %d\n", + RL_TIMER_DEFAULT); + sc->rl_int_rx_mod = RL_TIMER_DEFAULT; + } + } + } static int @@ -3619,3 +3736,29 @@ done: return (error); } + +static int +sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) +{ + int error, value; + + if (arg1 == NULL) + return (EINVAL); + value = *(int *)arg1; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error || req->newptr == NULL) + return (error); + if (value < low || value > high) + return (EINVAL); + *(int *)arg1 = value; + + return (0); +} + +static int +sysctl_hw_re_int_mod(SYSCTL_HANDLER_ARGS) +{ + + return (sysctl_int_range(oidp, arg1, arg2, req, RL_TIMER_MIN, + RL_TIMER_MAX)); +} |