re(4) hardware has some useful counters that are worth looking at: dlg@router ~$ kstat re-stats re0:0:re-stats:0 TxOk: 12697 packets RxOk: 22919 packets TxEr: 0 packets RxEr: 0 packets MissPkt: 0 packets FAE: 8371 packets Tx1Col: 0 packets TxMCol: 0 packets RxOkPhy: 22919 packets RxOkBrd: 0 packets RxOkMul: 0 packets TxAbt: 0 packets TxUndrn: 0 packets re1:0:re-stats:0 TxOk: 27098 packets RxOk: 19325 packets TxEr: 0 packets RxEr: 0 packets MissPkt: 20 packets FAE: 456 packets Tx1Col: 0 packets TxMCol: 0 packets RxOkPhy: 18340 packets RxOkBrd: 471 packets RxOkMul: 985 packets TxAbt: 0 packets TxUndrn: 0 packets
The most interesting (to me) is MissPkt, which shows how many packets were missed because the rx fifo (ring?) was full. The diff is bigger that I expected, half because the hardware is dumb, and half because the driver is... in need of some general improvement. The code just pushes the counters straight to userland to look at, which means the 16 bit counters aren't accumulated in any way. If you're scraping the numbers, you just have to make sure to do it often enough that you won't miss an interval with them. jmatthew@ and I have tested it a bit, but I'd like tests on old boards if possible. Index: re.c =================================================================== RCS file: /cvs/src/sys/dev/ic/re.c,v retrieving revision 1.211 diff -u -p -r1.211 re.c --- re.c 17 May 2021 11:59:53 -0000 1.211 +++ re.c 22 Apr 2022 00:45:04 -0000 @@ -109,6 +109,7 @@ #include "bpfilter.h" #include "vlan.h" +#include "kstat.h" #include <sys/param.h> #include <sys/endian.h> @@ -135,6 +136,10 @@ #include <net/bpf.h> #endif +#if NKSTAT > 0 +#include <sys/kstat.h> +#endif + #include <dev/mii/mii.h> #include <dev/mii/miivar.h> @@ -192,6 +197,9 @@ void re_setup_intr(struct rl_softc *, in #ifndef SMALL_KERNEL int re_wol(struct ifnet*, int); #endif +#if NKSTAT > 0 +void re_kstat_attach(struct rl_softc *); +#endif void in_delayed_cksum(struct mbuf *); @@ -1073,6 +1081,10 @@ re_attach(struct rl_softc *sc, const cha if_attach(ifp); ether_ifattach(ifp); +#if NKSTAT > 0 + re_kstat_attach(sc); +#endif + return (0); fail_8: @@ -2378,3 +2390,224 @@ re_wol(struct ifnet *ifp, int enable) return (0); } #endif + +#if NKSTAT > 0 + +#define RE_DTCCR_CMD (1U << 3) +#define RE_DTCCR_LO 0x10 +#define RE_DTCCR_HI 0x14 + +struct re_kstats { + struct kstat_kv tx_ok; + struct kstat_kv rx_ok; + struct kstat_kv tx_er; + struct kstat_kv rx_er; + struct kstat_kv miss_pkt; + struct kstat_kv fae; + struct kstat_kv tx_1col; + struct kstat_kv tx_mcol; + struct kstat_kv rx_ok_phy; + struct kstat_kv rx_ok_brd; + struct kstat_kv rx_ok_mul; + struct kstat_kv tx_abt; + struct kstat_kv tx_undrn; +}; + +static const struct re_kstats re_kstats_tpl = { + .tx_ok = KSTAT_KV_UNIT_INITIALIZER("TxOk", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + .rx_ok = KSTAT_KV_UNIT_INITIALIZER("RxOk", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + .tx_er = KSTAT_KV_UNIT_INITIALIZER("TxEr", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + .rx_er = KSTAT_KV_UNIT_INITIALIZER("RxEr", + KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS), + .miss_pkt = KSTAT_KV_UNIT_INITIALIZER("MissPkt", + KSTAT_KV_T_COUNTER16, KSTAT_KV_U_PACKETS), + .fae = KSTAT_KV_UNIT_INITIALIZER("FAE", + KSTAT_KV_T_COUNTER16, KSTAT_KV_U_PACKETS), + .tx_1col = KSTAT_KV_UNIT_INITIALIZER("Tx1Col", + KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS), + .tx_mcol = KSTAT_KV_UNIT_INITIALIZER("TxMCol", + KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS), + .rx_ok_phy = KSTAT_KV_UNIT_INITIALIZER("RxOkPhy", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + .rx_ok_brd = KSTAT_KV_UNIT_INITIALIZER("RxOkBrd", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + .rx_ok_mul = KSTAT_KV_UNIT_INITIALIZER("RxOkMul", + KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS), + .tx_abt = KSTAT_KV_UNIT_INITIALIZER("TxAbt", + KSTAT_KV_T_COUNTER16, KSTAT_KV_U_PACKETS), + .tx_undrn = KSTAT_KV_UNIT_INITIALIZER("TxUndrn", + KSTAT_KV_T_COUNTER16, KSTAT_KV_U_PACKETS), +}; + +struct re_kstat_softc { + struct re_stats *re_ks_sc_stats; + + bus_dmamap_t re_ks_sc_map; + bus_dma_segment_t re_ks_sc_seg; + int re_ks_sc_nsegs; + + struct rwlock re_ks_sc_rwl; +}; + +static int +re_kstat_read(struct kstat *ks) +{ + struct rl_softc *sc = ks->ks_softc; + struct re_kstat_softc *re_ks_sc = ks->ks_ptr; + bus_dmamap_t map; + uint64_t cmd; + uint32_t reg; + uint8_t command; + int tmo; + + command = CSR_READ_1(sc, RL_COMMAND); + if (!ISSET(command, RL_CMD_RX_ENB) || command == 0xff) + return (ENETDOWN); + + map = re_ks_sc->re_ks_sc_map; + cmd = map->dm_segs[0].ds_addr | RE_DTCCR_CMD; + + bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, + BUS_DMASYNC_PREREAD); + + CSR_WRITE_4(sc, RE_DTCCR_HI, cmd >> 32); + bus_space_barrier(sc->rl_btag, sc->rl_bhandle, RE_DTCCR_HI, 8, + BUS_SPACE_BARRIER_WRITE); + CSR_WRITE_4(sc, RE_DTCCR_LO, cmd); + bus_space_barrier(sc->rl_btag, sc->rl_bhandle, RE_DTCCR_LO, 4, + BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE); + + tmo = 1000; + do { + reg = CSR_READ_4(sc, RE_DTCCR_LO); + if (!ISSET(reg, RE_DTCCR_CMD)) + break; + + delay(10); + bus_space_barrier(sc->rl_btag, sc->rl_bhandle, RE_DTCCR_LO, 4, + BUS_SPACE_BARRIER_READ); + } while (--tmo); + + bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, + BUS_DMASYNC_POSTREAD); + + if (ISSET(reg, RE_DTCCR_CMD)) + return (EIO); + + nanouptime(&ks->ks_updated); + + return (0); +} + +static int +re_kstat_copy(struct kstat *ks, void *dst) +{ + struct re_kstat_softc *re_ks_sc = ks->ks_ptr; + struct re_stats *rs = re_ks_sc->re_ks_sc_stats; + struct re_kstats *kvs = dst; + + *kvs = re_kstats_tpl; + kstat_kv_u64(&kvs->tx_ok) = lemtoh64(&rs->re_tx_ok); + kstat_kv_u64(&kvs->rx_ok) = lemtoh64(&rs->re_rx_ok); + kstat_kv_u64(&kvs->tx_er) = lemtoh64(&rs->re_tx_er); + kstat_kv_u32(&kvs->rx_er) = lemtoh32(&rs->re_rx_er); + kstat_kv_u16(&kvs->miss_pkt) = lemtoh16(&rs->re_miss_pkt); + kstat_kv_u16(&kvs->fae) = lemtoh16(&rs->re_fae); + kstat_kv_u32(&kvs->tx_1col) = lemtoh32(&rs->re_tx_1col); + kstat_kv_u32(&kvs->tx_mcol) = lemtoh32(&rs->re_tx_mcol); + kstat_kv_u64(&kvs->rx_ok_phy) = lemtoh64(&rs->re_rx_ok_phy); + kstat_kv_u64(&kvs->rx_ok_brd) = lemtoh64(&rs->re_rx_ok_brd); + kstat_kv_u32(&kvs->rx_ok_mul) = lemtoh64(&rs->re_rx_ok_mul); + kstat_kv_u16(&kvs->tx_abt) = lemtoh64(&rs->re_tx_abt); + kstat_kv_u16(&kvs->tx_undrn) = lemtoh64(&rs->re_tx_undrn); + + return (0); +} + +void +re_kstat_attach(struct rl_softc *sc) +{ + struct re_kstat_softc *re_ks_sc; + struct kstat *ks; + + re_ks_sc = malloc(sizeof(*re_ks_sc), M_DEVBUF, M_NOWAIT); + if (re_ks_sc == NULL) { + printf("%s: cannot allocate kstat softc\n", + sc->sc_dev.dv_xname); + return; + } + + if (bus_dmamap_create(sc->sc_dmat, + sizeof(struct re_stats), 1, sizeof(struct re_stats), 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT, + &re_ks_sc->re_ks_sc_map) != 0) { + printf("%s: cannot create counter dma memory map\n", + sc->sc_dev.dv_xname); + goto free; + } + + if (bus_dmamem_alloc(sc->sc_dmat, + sizeof(struct re_stats), RE_STATS_ALIGNMENT, 0, + &re_ks_sc->re_ks_sc_seg, 1, &re_ks_sc->re_ks_sc_nsegs, + BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0) { + printf("%s: cannot allocate counter dma memory\n", + sc->sc_dev.dv_xname); + goto destroy; + } + + if (bus_dmamem_map(sc->sc_dmat, + &re_ks_sc->re_ks_sc_seg, re_ks_sc->re_ks_sc_nsegs, + sizeof(struct re_stats), (caddr_t *)&re_ks_sc->re_ks_sc_stats, + BUS_DMA_NOWAIT) != 0) { + printf("%s: cannot map counter dma memory\n", + sc->sc_dev.dv_xname); + goto freedma; + } + + if (bus_dmamap_load(sc->sc_dmat, re_ks_sc->re_ks_sc_map, + (caddr_t)re_ks_sc->re_ks_sc_stats, sizeof(struct re_stats), + NULL, BUS_DMA_NOWAIT) != 0) { + printf("%s: cannot load counter dma memory\n", + sc->sc_dev.dv_xname); + goto unmap; + } + + ks = kstat_create(sc->sc_dev.dv_xname, 0, "re-stats", 0, + KSTAT_T_KV, 0); + if (ks == NULL) { + printf("%s: cannot create re-stats kstat\n", + sc->sc_dev.dv_xname); + goto unload; + } + + ks->ks_datalen = sizeof(re_kstats_tpl); + + rw_init(&re_ks_sc->re_ks_sc_rwl, "restats"); + kstat_set_wlock(ks, &re_ks_sc->re_ks_sc_rwl); + ks->ks_softc = sc; + ks->ks_ptr = re_ks_sc; + ks->ks_read = re_kstat_read; + ks->ks_copy = re_kstat_copy; + + kstat_install(ks); + + sc->rl_kstat = ks; + + return; + +unload: + bus_dmamap_unload(sc->sc_dmat, re_ks_sc->re_ks_sc_map); +unmap: + bus_dmamem_unmap(sc->sc_dmat, + (caddr_t)re_ks_sc->re_ks_sc_stats, sizeof(struct re_stats)); +freedma: + bus_dmamem_free(sc->sc_dmat, &re_ks_sc->re_ks_sc_seg, 1); +destroy: + bus_dmamap_destroy(sc->sc_dmat, re_ks_sc->re_ks_sc_map); +free: + free(re_ks_sc, M_DEVBUF, sizeof(*re_ks_sc)); +} +#endif /* NKSTAT > 0 */ Index: rtl81x9reg.h =================================================================== RCS file: /cvs/src/sys/dev/ic/rtl81x9reg.h,v retrieving revision 1.103 diff -u -p -r1.103 rtl81x9reg.h --- rtl81x9reg.h 9 Jan 2022 05:42:38 -0000 1.103 +++ rtl81x9reg.h 22 Apr 2022 00:45:04 -0000 @@ -690,24 +690,24 @@ struct rl_desc { /* * Statistics counter structure (8139C+ and 8169 only) */ -struct rl_stats { - u_int32_t rl_tx_pkts_lo; - u_int32_t rl_tx_pkts_hi; - u_int32_t rl_tx_errs_lo; - u_int32_t rl_tx_errs_hi; - u_int32_t rl_tx_errs; - u_int16_t rl_missed_pkts; - u_int16_t rl_rx_framealign_errs; - u_int32_t rl_tx_onecoll; - u_int32_t rl_tx_multicolls; - u_int32_t rl_rx_ucasts_hi; - u_int32_t rl_rx_ucasts_lo; - u_int32_t rl_rx_bcasts_lo; - u_int32_t rl_rx_bcasts_hi; - u_int32_t rl_rx_mcasts; - u_int16_t rl_tx_aborts; - u_int16_t rl_rx_underruns; -}; + +struct re_stats { + uint64_t re_tx_ok; + uint64_t re_rx_ok; + uint64_t re_tx_er; + uint32_t re_rx_er; + uint16_t re_miss_pkt; + uint16_t re_fae; + uint32_t re_tx_1col; + uint32_t re_tx_mcol; + uint64_t re_rx_ok_phy; + uint64_t re_rx_ok_brd; + uint32_t re_rx_ok_mul; + uint16_t re_tx_abt; + uint16_t re_tx_undrn; +} __packed __aligned(sizeof(uint64_t)); + +#define RE_STATS_ALIGNMENT 64 /* * Rx/Tx descriptor parameters (8139C+ and 8169 only) @@ -858,6 +858,8 @@ struct rl_list_data { int rl_rx_listnseg; }; +struct kstat; + struct rl_softc { struct device sc_dev; /* us, as a device */ void * sc_ih; /* interrupt vectoring */ @@ -930,6 +932,8 @@ struct rl_softc { #define RL_IMTYPE_SIM 1 /* simulated */ #define RL_IMTYPE_HW 2 /* hardware based */ int rl_timerintr; + + struct kstat *rl_kstat; }; /*