Index: sys/pci/if_rlreg.h =================================================================== --- sys/pci/if_rlreg.h (revision 214627) +++ sys/pci/if_rlreg.h (working copy) @@ -233,17 +233,15 @@ RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR) -#ifdef RE_TX_MODERATION -#define RL_INTRS_CPLUS \ +#define RL_INTRS_CPLUS \ (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED) -#else -#define RL_INTRS_CPLUS \ - (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR|RL_ISR_TX_OK| \ + +#define RL_INTRS_CPLUS_TXMOD \ + (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED) -#endif /* * Media status register. (8139 only) @@ -723,19 +721,16 @@ * Statistics counter structure (8139C+ and 8169 only) */ struct rl_stats { - uint32_t rl_tx_pkts_lo; - uint32_t rl_tx_pkts_hi; - uint32_t rl_tx_errs_lo; - uint32_t rl_tx_errs_hi; - uint32_t rl_tx_errs; + uint64_t rl_tx_pkts; + uint64_t rl_rx_pkts; + uint64_t rl_tx_errs; + uint32_t rl_rx_errs; uint16_t rl_missed_pkts; uint16_t rl_rx_framealign_errs; uint32_t rl_tx_onecoll; uint32_t rl_tx_multicolls; - uint32_t rl_rx_ucasts_hi; - uint32_t rl_rx_ucasts_lo; - uint32_t rl_rx_bcasts_lo; - uint32_t rl_rx_bcasts_hi; + uint64_t rl_rx_ucasts; + uint64_t rl_rx_bcasts; uint32_t rl_rx_mcasts; uint16_t rl_tx_aborts; uint16_t rl_rx_underruns; @@ -769,6 +764,7 @@ #define RL_NTXSEGS 32 #define RL_RING_ALIGN 256 +#define RL_DUMP_ALIGN 64 #define RL_IFQ_MAXLEN 512 #define RL_TX_DESC_NXT(sc,x) ((x + 1) & ((sc)->rl_ldata.rl_tx_desc_cnt - 1)) #define RL_TX_DESC_PRV(sc,x) ((x - 1) & ((sc)->rl_ldata.rl_tx_desc_cnt - 1)) @@ -883,22 +879,25 @@ struct task rl_inttask; int rl_txstart; + uint16_t rl_intrs_cplus; + uint16_t rl_txevents; uint32_t rl_flags; -#define RL_FLAG_MSI 0x0001 -#define RL_FLAG_AUTOPAD 0x0002 -#define RL_FLAG_PHYWAKE_PM 0x0004 -#define RL_FLAG_PHYWAKE 0x0008 -#define RL_FLAG_NOJUMBO 0x0010 -#define RL_FLAG_PAR 0x0020 -#define RL_FLAG_DESCV2 0x0040 -#define RL_FLAG_MACSTAT 0x0080 -#define RL_FLAG_FASTETHER 0x0100 -#define RL_FLAG_CMDSTOP 0x0200 -#define RL_FLAG_MACRESET 0x0400 -#define RL_FLAG_WOLRXENB 0x1000 -#define RL_FLAG_MACSLEEP 0x2000 -#define RL_FLAG_PCIE 0x4000 -#define RL_FLAG_LINK 0x8000 +#define RL_FLAG_MSI 0x00000001 +#define RL_FLAG_AUTOPAD 0x00000002 +#define RL_FLAG_PHYWAKE_PM 0x00000004 +#define RL_FLAG_PHYWAKE 0x00000008 +#define RL_FLAG_NOJUMBO 0x00000010 +#define RL_FLAG_PAR 0x00000020 +#define RL_FLAG_DESCV2 0x00000040 +#define RL_FLAG_MACSTAT 0x00000080 +#define RL_FLAG_FASTETHER 0x00000100 +#define RL_FLAG_CMDSTOP 0x00000200 +#define RL_FLAG_MACRESET 0x00000400 +#define RL_FLAG_WOLRXENB 0x00001000 +#define RL_FLAG_MACSLEEP 0x00002000 +#define RL_FLAG_PCIE 0x00004000 +#define RL_FLAG_TXMOD 0x10000000 +#define RL_FLAG_LINK 0x80000000 }; #define RL_LOCK(_sc) mtx_lock(&(_sc)->rl_mtx) Index: sys/dev/re/if_re.c =================================================================== --- sys/dev/re/if_re.c (revision 214627) +++ sys/dev/re/if_re.c (working copy) @@ -123,6 +123,7 @@ #include #include #include +#include #include #include @@ -250,7 +251,9 @@ static void re_tick (void *); static void re_tx_task (void *, int); static void re_int_task (void *, int); +static void re_msi_intr (void *); static void re_start (struct ifnet *); +static void re_start_locked (struct ifnet *); static int re_ioctl (struct ifnet *, u_long, caddr_t); static void re_init (void *); static void re_init_locked (struct rl_softc *); @@ -281,6 +284,9 @@ static int re_diag (struct rl_softc *); #endif +static void re_add_sysctls (struct rl_softc *); +static int re_sysctl_stats (SYSCTL_HANDLER_ARGS); + static device_method_t re_methods[] = { /* Device interface */ DEVMETHOD(device_probe, re_probe), @@ -880,7 +886,7 @@ uint16_t devid, vendor; uint16_t revid, sdevid; int i; - + vendor = pci_get_vendor(dev); devid = pci_get_device(dev); revid = pci_get_revid(dev); @@ -934,6 +940,7 @@ static int re_allocmem(device_t dev, struct rl_softc *sc) { + bus_addr_t lowaddr; bus_size_t rx_list_size, tx_list_size; int error; int i; @@ -946,11 +953,13 @@ * In order to use DAC, RL_CPLUSCMD_PCI_DAC bit of RL_CPLUS_CMD * register should be set. However some RealTek chips are known * to be buggy on DAC handling, therefore disable DAC by limiting - * DMA address space to 32bit. PCIe variants of RealTek chips - * may not have the limitation but I took safer path. + * DMA address space to 32bit. */ + lowaddr = BUS_SPACE_MAXADDR; + if ((sc->rl_flags & RL_FLAG_PCIE) == 0) + lowaddr = BUS_SPACE_MAXADDR_32BIT; error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + lowaddr, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->rl_parent_tag); if (error) { @@ -1080,6 +1089,35 @@ } } + /* Create DMA map for statistics. */ + error = bus_dma_tag_create(sc->rl_parent_tag, RL_DUMP_ALIGN, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + sizeof(struct rl_stats), 1, sizeof(struct rl_stats), 0, NULL, NULL, + &sc->rl_ldata.rl_stag); + if (error) { + device_printf(dev, "could not create statistics DMA tag\n"); + return (error); + } + /* Allocate DMA'able memory for statistics. */ + error = bus_dmamem_alloc(sc->rl_ldata.rl_stag, + (void **)&sc->rl_ldata.rl_stats, + BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, + &sc->rl_ldata.rl_smap); + if (error) { + device_printf(dev, + "could not allocate statistics DMA memory\n"); + return (error); + } + /* Load the map for statistics. */ + sc->rl_ldata.rl_stats_addr = 0; + error = bus_dmamap_load(sc->rl_ldata.rl_stag, sc->rl_ldata.rl_smap, + sc->rl_ldata.rl_stats, sizeof(struct rl_stats), re_dma_map_addr, + &sc->rl_ldata.rl_stats_addr, BUS_DMA_NOWAIT); + if (error != 0 || sc->rl_ldata.rl_stats_addr == 0) { + device_printf(dev, "could not load statistics DMA memory\n"); + return (ENOMEM); + } + return (0); } @@ -1117,7 +1155,7 @@ /* * Prefer memory space register mapping over IO space. * Because RTL8169SC does not seem to work when memory mapping - * is used always activate io mapping. + * is used always activate io mapping. */ if (devid == RT_DEVICEID_8169SC) prefer_iomap = 1; @@ -1370,6 +1408,7 @@ error = re_allocmem(dev, sc); if (error) goto fail; + re_add_sysctls(sc); ifp = sc->rl_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { @@ -1484,14 +1523,27 @@ #endif /* Hook interrupt last to avoid having to lock softc */ - if ((sc->rl_flags & RL_FLAG_MSI) == 0) + if ((sc->rl_flags & RL_FLAG_MSI) == 0) { + sc->rl_intrs_cplus = RL_INTRS_CPLUS; + sc->rl_txevents = RL_ISR_TX_ERR | RL_ISR_TX_DESC_UNAVAIL | + RL_ISR_TX_OK; error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc, &sc->rl_intrhand[0]); - else { + } else { +#ifdef DEVICE_POLLING + sc->rl_intrs_cplus = RL_INTRS_CPLUS; + sc->rl_txevents = RL_ISR_TX_ERR | RL_ISR_TX_DESC_UNAVAIL | + RL_ISR_TX_OK; +#else + sc->rl_intrs_cplus = RL_INTRS_CPLUS_TXMOD; + sc->rl_flags |= RL_FLAG_TXMOD; + sc->rl_txevents = RL_ISR_TX_ERR | RL_ISR_TX_DESC_UNAVAIL | + RL_ISR_TIMEOUT_EXPIRED; +#endif 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, + INTR_TYPE_NET | INTR_MPSAFE, NULL, re_msi_intr, sc, &sc->rl_intrhand[i]); if (error != 0) break; @@ -1599,22 +1651,26 @@ /* Unload and free the RX DMA ring memory and map */ if (sc->rl_ldata.rl_rx_list_tag) { - bus_dmamap_unload(sc->rl_ldata.rl_rx_list_tag, - sc->rl_ldata.rl_rx_list_map); - bus_dmamem_free(sc->rl_ldata.rl_rx_list_tag, - sc->rl_ldata.rl_rx_list, - sc->rl_ldata.rl_rx_list_map); + if (sc->rl_ldata.rl_rx_list_map) + bus_dmamap_unload(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list_map); + if (sc->rl_ldata.rl_rx_list_map && sc->rl_ldata.rl_rx_list) + bus_dmamem_free(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list, + sc->rl_ldata.rl_rx_list_map); bus_dma_tag_destroy(sc->rl_ldata.rl_rx_list_tag); } /* Unload and free the TX DMA ring memory and map */ if (sc->rl_ldata.rl_tx_list_tag) { - bus_dmamap_unload(sc->rl_ldata.rl_tx_list_tag, - sc->rl_ldata.rl_tx_list_map); - bus_dmamem_free(sc->rl_ldata.rl_tx_list_tag, - sc->rl_ldata.rl_tx_list, - sc->rl_ldata.rl_tx_list_map); + if (sc->rl_ldata.rl_tx_list_map) + bus_dmamap_unload(sc->rl_ldata.rl_tx_list_tag, + sc->rl_ldata.rl_tx_list_map); + if (sc->rl_ldata.rl_tx_list_map && sc->rl_ldata.rl_tx_list) + bus_dmamem_free(sc->rl_ldata.rl_tx_list_tag, + sc->rl_ldata.rl_tx_list, + sc->rl_ldata.rl_tx_list_map); bus_dma_tag_destroy(sc->rl_ldata.rl_tx_list_tag); } @@ -1639,11 +1695,12 @@ /* Unload and free the stats buffer and map */ if (sc->rl_ldata.rl_stag) { - bus_dmamap_unload(sc->rl_ldata.rl_stag, - sc->rl_ldata.rl_rx_list_map); - bus_dmamem_free(sc->rl_ldata.rl_stag, - sc->rl_ldata.rl_stats, - sc->rl_ldata.rl_smap); + if (sc->rl_ldata.rl_smap) + bus_dmamap_unload(sc->rl_ldata.rl_stag, + sc->rl_ldata.rl_smap); + if (sc->rl_ldata.rl_smap && sc->rl_ldata.rl_stats) + bus_dmamem_free(sc->rl_ldata.rl_stag, + sc->rl_ldata.rl_stats, sc->rl_ldata.rl_smap); bus_dma_tag_destroy(sc->rl_ldata.rl_stag); } @@ -2016,9 +2073,9 @@ if (rx_npktsp != NULL) *rx_npktsp = rx_npkts; if (maxpkt) - return(EAGAIN); + return (EAGAIN); - return(0); + return (0); } static void @@ -2076,15 +2133,17 @@ /* No changes made to the TX ring, so no flush needed */ if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt) { -#ifdef RE_TX_MODERATION - /* - * If not all descriptors have been reaped yet, reload - * the timer so that we will eventually get another - * interrupt that will cause us to re-enter this routine. - * This is done in case the transmitter has gone idle. - */ - CSR_WRITE_4(sc, RL_TIMERCNT, 1); -#endif + if (sc->rl_flags & RL_FLAG_TXMOD) { + /* + * If not all descriptors have been reaped yet, + * reload the timer so that we will eventually + * get another interrupt that will cause us to + * re-enter this routine. + * This is done in case the transmitter has + * gone idle. + */ + CSR_WRITE_4(sc, RL_TIMERCNT, 1); + } } else sc->rl_watchdog_timer = 0; } @@ -2107,7 +2166,7 @@ * Reclaim transmitted frames here. Technically it is not * necessary to do here but it ensures periodic reclamation * regardless of Tx completion interrupt which seems to be - * lost on PCIe based controllers under certain situations. + * lost on PCIe based controllers under certain situations. */ re_txeof(sc); re_watchdog(sc); @@ -2177,7 +2236,7 @@ sc = arg; status = CSR_READ_2(sc, RL_ISR); - if (status == 0xFFFF || (status & RL_INTRS_CPLUS) == 0) + if (status == 0xFFFF || (status & sc->rl_intrs_cplus) == 0) return (FILTER_STRAY); CSR_WRITE_2(sc, RL_IMR, 0); @@ -2229,13 +2288,7 @@ 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 & ( -#ifdef RE_TX_MODERATION - RL_ISR_TIMEOUT_EXPIRED| -#else - RL_ISR_TX_OK| -#endif - RL_ISR_TX_ERR|RL_ISR_TX_DESC_UNAVAIL)) + if (status & sc->rl_txevents) re_txeof(sc); if (status & RL_ISR_SYSTEM_ERR) { @@ -2248,14 +2301,68 @@ RL_UNLOCK(sc); - if ((CSR_READ_2(sc, RL_ISR) & RL_INTRS_CPLUS) || rval) { + if ((CSR_READ_2(sc, RL_ISR) & sc->rl_intrs_cplus) || rval) { taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask); return; } - CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); + CSR_WRITE_2(sc, RL_IMR, sc->rl_intrs_cplus); } +static void +re_msi_intr(void *arg) +{ + struct rl_softc *sc; + struct ifnet *ifp; + u_int16_t status; + int i; + + sc = arg; + ifp = sc->rl_ifp; + + RL_LOCK(sc); + + /* Disable interrupts. */ + CSR_WRITE_2(sc, RL_IMR, 0); + + status = CSR_READ_2(sc, RL_ISR); + CSR_WRITE_2(sc, RL_ISR, status); + + if (sc->suspended || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + RL_UNLOCK(sc); + return; + } + + if (status & (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_FIFO_OFLOW)) + for (i = 0; i < 4 && re_rxeof(sc, NULL) != 0; i++) + ; + /* + * 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 & sc->rl_txevents) + 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) { + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + re_start_locked(sc->rl_ifp); + CSR_WRITE_2(sc, RL_IMR, sc->rl_intrs_cplus); + } + RL_UNLOCK(sc); +} + static int re_encap(struct rl_softc *sc, struct mbuf **m_head) { @@ -2451,11 +2558,23 @@ re_start(ifp); } +static void +re_start(struct ifnet *ifp) +{ + struct rl_softc *sc; + + sc = ifp->if_softc; + + RL_LOCK(sc); + re_start_locked(sc->rl_ifp); + RL_UNLOCK(sc); +} + /* * Main transmit routine for C+ and gigE NICs. */ static void -re_start(struct ifnet *ifp) +re_start_locked(struct ifnet *ifp) { struct rl_softc *sc; struct mbuf *m_head; @@ -2463,13 +2582,9 @@ sc = ifp->if_softc; - RL_LOCK(sc); - if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != - IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) { - RL_UNLOCK(sc); + IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) return; - } for (queued = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->rl_ldata.rl_tx_free > 1;) { @@ -2494,14 +2609,8 @@ queued++; } - if (queued == 0) { -#ifdef RE_TX_MODERATION - if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt) - CSR_WRITE_4(sc, RL_TIMERCNT, 1); -#endif - RL_UNLOCK(sc); + if (queued == 0) return; - } /* Flush the TX descriptors */ @@ -2511,24 +2620,22 @@ CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); -#ifdef RE_TX_MODERATION - /* - * Use the countdown timer for interrupt moderation. - * 'TX done' interrupts are disabled. Instead, we reset the - * countdown timer, which will begin counting until it hits - * the value in the TIMERINT register, and then trigger an - * interrupt. Each time we write to the TIMERCNT register, - * the timer count is reset to 0. - */ - CSR_WRITE_4(sc, RL_TIMERCNT, 1); -#endif + if (sc->rl_flags & RL_FLAG_TXMOD) { + /* + * Use the countdown timer for interrupt moderation. + * 'TX done' interrupts are disabled. Instead, we reset the + * countdown timer, which will begin counting until it hits + * the value in the TIMERINT register, and then trigger an + * interrupt. Each time we write to the TIMERCNT register, + * the timer count is reset to 0. + */ + CSR_WRITE_4(sc, RL_TIMERCNT, 1); + } /* * Set a timeout in case the chip goes out to lunch. */ sc->rl_watchdog_timer = 5; - - RL_UNLOCK(sc); } static void @@ -2679,8 +2786,8 @@ if (sc->rl_testmode) CSR_WRITE_2(sc, RL_IMR, 0); else - CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); - CSR_WRITE_2(sc, RL_ISR, RL_INTRS_CPLUS); + CSR_WRITE_2(sc, RL_IMR, sc->rl_intrs_cplus); + CSR_WRITE_2(sc, RL_ISR, sc->rl_intrs_cplus); /* Set initial TX threshold */ sc->rl_txthresh = RL_TX_THRESH_INIT; @@ -2692,19 +2799,19 @@ 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 - * 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); -#endif + if (sc->rl_flags & RL_FLAG_TXMOD) { + /* + * 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 + * 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); + } /* * For 8169 gigE NICs, set the max allowed RX packet @@ -2835,7 +2942,7 @@ if (ifr->ifr_reqcap & IFCAP_POLLING) { error = ether_poll_register(re_poll, ifp); if (error) - return(error); + return (error); RL_LOCK(sc); /* Disable interrupts */ CSR_WRITE_2(sc, RL_IMR, 0x0000); @@ -2845,7 +2952,7 @@ error = ether_poll_deregister(ifp); /* Enable interrupts. */ RL_LOCK(sc); - CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); + CSR_WRITE_2(sc, RL_IMR, sc->rl_intrs_cplus); ifp->if_capenable &= ~IFCAP_POLLING; RL_UNLOCK(sc); } @@ -3181,3 +3288,88 @@ v &= ~RL_CFG5_WOL_LANWAKE; CSR_WRITE_1(sc, RL_CFG5, v); } + +static void +re_add_sysctls(struct rl_softc *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *children; + + ctx = device_get_sysctl_ctx(sc->rl_dev); + children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->rl_dev)); + + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "stats", + CTLTYPE_INT | CTLFLAG_RW, sc, 0, re_sysctl_stats, "I", + "Statistics Information"); +} + +static int +re_sysctl_stats(SYSCTL_HANDLER_ARGS) +{ + struct rl_softc *sc; + struct rl_stats *stats; + int error, i, result; + + result = -1; + error = sysctl_handle_int(oidp, &result, 0, req); + if (error || (req->newptr == NULL)) + return (error); + + if (result == 1) { + sc = (struct rl_softc *)arg1; + RL_LOCK(sc); + bus_dmamap_sync(sc->rl_ldata.rl_stag, + sc->rl_ldata.rl_smap, BUS_DMASYNC_PREREAD); + CSR_WRITE_4(sc, RL_DUMPSTATS_HI, + RL_ADDR_HI(sc->rl_ldata.rl_stats_addr)); + CSR_WRITE_4(sc, RL_DUMPSTATS_LO, + RL_ADDR_LO(sc->rl_ldata.rl_stats_addr)); + CSR_WRITE_4(sc, RL_DUMPSTATS_LO, + RL_ADDR_LO(sc->rl_ldata.rl_stats_addr | + RL_DUMPSTATS_START)); + for (i = RL_TIMEOUT; i > 0; i--) { + if ((CSR_READ_4(sc, RL_DUMPSTATS_LO) & + RL_DUMPSTATS_START) == 0) + break; + DELAY(1000); + } + bus_dmamap_sync(sc->rl_ldata.rl_stag, + sc->rl_ldata.rl_smap, BUS_DMASYNC_POSTREAD); + RL_UNLOCK(sc); + if (i == 0) { + device_printf(sc->rl_dev, + "DUMP statistics request timedout\n"); + return (ETIMEDOUT); + } + stats = sc->rl_ldata.rl_stats; + printf("%s statistics:\n", device_get_nameunit(sc->rl_dev)); + printf("Transmit good frames : %ju\n", + (uintmax_t)le64toh(stats->rl_tx_pkts)); + printf("Receive good frames : %ju\n", + (uintmax_t)le64toh(stats->rl_rx_pkts)); + printf("Tx errors : %ju\n", + (uintmax_t)le64toh(stats->rl_tx_errs)); + printf("Rx errors : %u\n", + le32toh(stats->rl_rx_errs)); + printf("Rx missed frames : %u\n", + (uint32_t)le16toh(stats->rl_missed_pkts)); + printf("Rx frame alignment errs : %u\n", + (uint32_t)le16toh(stats->rl_rx_framealign_errs)); + printf("Tx single collisions : %u\n", + le32toh(stats->rl_tx_onecoll)); + printf("Tx multiple collisions : %u\n", + le32toh(stats->rl_tx_multicolls)); + printf("Rx unicast frames : %ju\n", + (uintmax_t)le64toh(stats->rl_rx_ucasts)); + printf("Rx broadcast frames : %ju\n", + (uintmax_t)le64toh(stats->rl_rx_bcasts)); + printf("Rx multicast frames : %u\n", + le32toh(stats->rl_rx_mcasts)); + printf("Tx aborts : %u\n", + (uint32_t)le16toh(stats->rl_tx_aborts)); + printf("Tx underruns : %u\n", + (uint32_t)le16toh(stats->rl_rx_underruns)); + } + + return (error); +}