this is a first pass at converting the stats gathering in em(4) to
kstat instead of a disabled printf.

there are some semantic differences. the most obvious is that hardware
counters are not fed into the stacks counters on struct ifnet. i also
don't collect ring based counters (yet).

they look like this:

em0:0:em-stats:0
     rx crc errs: 0 packets
   rx align errs: 0 packets
   rx align errs: 0 packets
         rx errs: 0 packets
       rx missed: 0 packets
  tx single coll: 0 packets
  tx excess coll: 0 packets
   tx multi coll: 0 packets
    tx late coll: 0 packets
         tx coll: 0
       tx defers: 0
       tx no CRS: 0 packets
        seq errs: 0
   carr ext errs: 0 packets
     rx len errs: 0 packets
          rx xon: 0 packets
          tx xon: 0 packets
         rx xoff: 0 packets
         tx xoff: 0 packets
  FC unsupported: 0 packets
          rx 64B: 6 packets
      rx 65-127B: 223 packets
     rx 128-255B: 15 packets
     rx 256-511B: 7 packets
    rx 512-1023B: 2 packets
    rx 1024-maxB: 1234584 packets
         rx good: 1234837 packets
        rx bcast: 19 packets
        rx mcast: 0 packets
         tx good: 630873 packets
         rx good: 1874122399 bytes
         tx good: 44180108 bytes
   rx no buffers: 0 packets
    rx undersize: 0 packets
    rx fragments: 0 packets
     rx oversize: 0 packets
      rx jabbers: 0 packets
         rx mgmt: 0 packets
   rx mgmt drops: 0 packets
         tx mgmt: 0 packets
        rx total: 1874146525 bytes
        tx total: 44180108 bytes
        rx total: 1235017 packets
        tx total: 630873 packets
          tx 64B: 113 packets
      tx 65-127B: 630681 packets
     tx 128-255B: 47 packets
     tx 256-511B: 29 packets
    tx 512-1023B: 2 packets
    tx 1024-maxB: 1 packets
        tx mcast: 0 packets
        tx bcast: 10 packets

unfortunately em(4) covers a lot of chips of different vintages, so if
anyone has a super old one they can try this diff on with kstat enabled
in their kernel config, that would be appreciated.

Index: if_em.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_em.c,v
retrieving revision 1.354
diff -u -p -r1.354 if_em.c
--- if_em.c     22 Jun 2020 02:31:32 -0000      1.354
+++ if_em.c     7 Jul 2020 08:48:37 -0000
@@ -270,9 +270,6 @@ void em_receive_checksum(struct em_softc
 u_int  em_transmit_checksum_setup(struct em_queue *, struct mbuf *, u_int,
            u_int32_t *, u_int32_t *);
 void em_iff(struct em_softc *);
-#ifdef EM_DEBUG
-void em_print_hw_stats(struct em_softc *);
-#endif
 void em_update_link_status(struct em_softc *);
 int  em_get_buf(struct em_queue *, int);
 void em_enable_hw_vlans(struct em_softc *);
@@ -302,6 +299,12 @@ void em_enable_queue_intr_msix(struct em
 #define em_allocate_msix(_sc)  (-1)
 #endif
 
+#if NKSTAT > 0
+void   em_kstat_attach(struct em_softc *);
+int    em_kstat_read(struct kstat *);
+void   em_tbi_adjust_stats(struct em_softc *, uint32_t, uint8_t *);
+#endif
+
 /*********************************************************************
  *  OpenBSD Device Interface Entry Points
  *********************************************************************/
@@ -561,8 +564,8 @@ em_attach(struct device *parent, struct 
 
        /* Initialize statistics */
        em_clear_hw_cntrs(&sc->hw);
-#ifndef SMALL_KERNEL
-       em_update_stats_counters(sc);
+#if NKSTAT > 0
+       em_kstat_attach(sc);
 #endif
        sc->hw.get_link_status = 1;
        if (!defer)
@@ -1467,26 +1470,21 @@ em_iff(struct em_softc *sc)
 void
 em_local_timer(void *arg)
 {
-       struct ifnet   *ifp;
        struct em_softc *sc = arg;
        int s;
 
-       ifp = &sc->sc_ac.ac_if;
+       timeout_add_sec(&sc->timer_handle, 1);
 
        s = splnet();
-
-#ifndef SMALL_KERNEL
-       em_update_stats_counters(sc);
-#ifdef EM_DEBUG
-       if (ifp->if_flags & IFF_DEBUG && ifp->if_flags & IFF_RUNNING)
-               em_print_hw_stats(sc);
-#endif
-#endif
        em_smartspeed(sc);
-
-       timeout_add_sec(&sc->timer_handle, 1);
-
        splx(s);
+
+#if NKSTAT > 0
+       if (sc->kstat != NULL && mtx_enter_try(&sc->kstat_mtx)) {
+               em_kstat_read(sc->kstat);
+               mtx_leave(&sc->kstat_mtx);
+       }
+#endif
 }
 
 void
@@ -2934,8 +2932,8 @@ em_rxeof(struct em_queue *que)
                        last_byte = *(mtod(m, caddr_t) + desc_len - 1);
                        if (TBI_ACCEPT(&sc->hw, status, desc->errors,
                            pkt_len, last_byte)) {
-#ifndef SMALL_KERNEL
-                               em_tbi_adjust_stats(&sc->hw, &sc->stats, 
+#if NKSTAT > 0
+                               em_tbi_adjust_stats(sc,
                                    pkt_len, sc->hw.mac_addr);
 #endif
                                if (len > 0)
@@ -3396,228 +3394,365 @@ em_allocate_legacy(struct em_softc *sc)
        return (0);
 }
 
+#if NKSTAT > 0
+/* this is used to look up the array of kstats quickly */
+enum em_stat {
+       em_stat_crcerrs,
+       em_stat_algnerrc,
+       em_stat_symerrs,
+       em_stat_rxerrc,
+       em_stat_mpc,
+       em_stat_scc,
+       em_stat_ecol,
+       em_stat_mcc,
+       em_stat_latecol,
+       em_stat_colc,
+       em_stat_dc,
+       em_stat_tncrs,
+       em_stat_sec,
+       em_stat_cexterr,
+       em_stat_rlec,
+       em_stat_xonrxc,
+       em_stat_xontxc,
+       em_stat_xoffrxc,
+       em_stat_xofftxc,
+       em_stat_fcruc,
+       em_stat_prc64,
+       em_stat_prc127,
+       em_stat_prc255,
+       em_stat_prc511,
+       em_stat_prc1023,
+       em_stat_prc1522,
+       em_stat_gprc,
+       em_stat_bprc,
+       em_stat_mprc,
+       em_stat_gptc,
+       em_stat_gorc,
+       em_stat_gotc,
+       em_stat_rnbc,
+       em_stat_ruc,
+       em_stat_rfc,
+       em_stat_roc,
+       em_stat_rjc,
+       em_stat_mgtprc,
+       em_stat_mgtpdc,
+       em_stat_mgtptc,
+       em_stat_tor,
+       em_stat_tot,
+       em_stat_tpr,
+       em_stat_tpt,
+       em_stat_ptc64,
+       em_stat_ptc127,
+       em_stat_ptc255,
+       em_stat_ptc511,
+       em_stat_ptc1023,
+       em_stat_ptc1522,
+       em_stat_mptc,
+       em_stat_bptc,
+#if 0
+       em_stat_tsctc,
+       em_stat_tsctf,
+#endif
+
+       em_stat_count,
+};
+
+struct em_counter {
+       const char              *name;
+       enum kstat_kv_unit       unit;
+       uint32_t                 reg;
+};
+
+static const struct em_counter em_counters[em_stat_count] = {
+       [em_stat_crcerrs] =
+           { "rx crc errs",    KSTAT_KV_U_PACKETS,     E1000_CRCERRS },
+       [em_stat_algnerrc] = /* >= em_82543 */
+           { "rx align errs",  KSTAT_KV_U_PACKETS,     0 },
+       [em_stat_symerrs] = /* >= em_82543 */
+           { "rx align errs",  KSTAT_KV_U_PACKETS,     0 },
+       [em_stat_rxerrc] =
+           { "rx errs",        KSTAT_KV_U_PACKETS,     E1000_RXERRC },
+       [em_stat_mpc] =
+           { "rx missed",      KSTAT_KV_U_PACKETS,     E1000_MPC },
+       [em_stat_scc] =
+           { "tx single coll", KSTAT_KV_U_PACKETS,     E1000_SCC },
+       [em_stat_ecol] =
+           { "tx excess coll", KSTAT_KV_U_PACKETS,     E1000_ECOL },
+       [em_stat_mcc] =
+           { "tx multi coll",  KSTAT_KV_U_PACKETS,     E1000_MCC },
+       [em_stat_latecol] =
+           { "tx late coll",   KSTAT_KV_U_PACKETS,     E1000_LATECOL },
+       [em_stat_colc] =
+           { "tx coll",        KSTAT_KV_U_NONE,        E1000_COLC },
+       [em_stat_dc] =
+           { "tx defers",      KSTAT_KV_U_NONE,        E1000_DC },
+       [em_stat_tncrs] = /* >= em_82543 */
+           { "tx no CRS",      KSTAT_KV_U_PACKETS,     0 },
+       [em_stat_sec] =
+           { "seq errs",       KSTAT_KV_U_NONE,        E1000_SEC },
+       [em_stat_cexterr] = /* >= em_82543 */
+           { "carr ext errs",  KSTAT_KV_U_PACKETS,     0 },
+       [em_stat_rlec] =
+           { "rx len errs",    KSTAT_KV_U_PACKETS,     E1000_RLEC },
+       [em_stat_xonrxc] =
+           { "rx xon",         KSTAT_KV_U_PACKETS,     E1000_XONRXC },
+       [em_stat_xontxc] =
+           { "tx xon",         KSTAT_KV_U_PACKETS,     E1000_XONTXC },
+       [em_stat_xoffrxc] =
+           { "rx xoff",        KSTAT_KV_U_PACKETS,     E1000_XOFFRXC },
+       [em_stat_xofftxc] =
+           { "tx xoff",        KSTAT_KV_U_PACKETS,     E1000_XOFFTXC },
+       [em_stat_fcruc] =
+           { "FC unsupported", KSTAT_KV_U_PACKETS,     E1000_FCRUC },
+       [em_stat_prc64] =
+           { "rx 64B",         KSTAT_KV_U_PACKETS,     E1000_PRC64 },
+       [em_stat_prc127] =
+           { "rx 65-127B",     KSTAT_KV_U_PACKETS,     E1000_PRC127 },
+       [em_stat_prc255] =
+           { "rx 128-255B",    KSTAT_KV_U_PACKETS,     E1000_PRC255 },
+       [em_stat_prc511] =
+           { "rx 256-511B",    KSTAT_KV_U_PACKETS,     E1000_PRC511 },
+       [em_stat_prc1023] =
+           { "rx 512-1023B",   KSTAT_KV_U_PACKETS,     E1000_PRC1023 },
+       [em_stat_prc1522] =
+           { "rx 1024-maxB",   KSTAT_KV_U_PACKETS,     E1000_PRC1522 },
+       [em_stat_gprc] =
+           { "rx good",        KSTAT_KV_U_PACKETS,     E1000_GPRC },
+       [em_stat_bprc] =
+           { "rx bcast",       KSTAT_KV_U_PACKETS,     E1000_BPRC },
+       [em_stat_mprc] =
+           { "rx mcast",       KSTAT_KV_U_PACKETS,     E1000_MPRC },
+       [em_stat_gptc] =
+           { "tx good",        KSTAT_KV_U_PACKETS,     E1000_GPTC },
+       [em_stat_gorc] = /* 64bit */
+           { "rx good",        KSTAT_KV_U_BYTES,       0 },
+       [em_stat_gotc] = /* 64bit */
+           { "tx good",        KSTAT_KV_U_BYTES,       0 },
+       [em_stat_rnbc] =
+           { "rx no buffers",  KSTAT_KV_U_PACKETS,     E1000_RNBC },
+       [em_stat_ruc] =
+           { "rx undersize",   KSTAT_KV_U_PACKETS,     E1000_RUC },
+       [em_stat_rfc] =
+           { "rx fragments",   KSTAT_KV_U_PACKETS,     E1000_RFC },
+       [em_stat_roc] =
+           { "rx oversize",    KSTAT_KV_U_PACKETS,     E1000_ROC },
+       [em_stat_rjc] =
+           { "rx jabbers",     KSTAT_KV_U_PACKETS,     E1000_RJC },
+       [em_stat_mgtprc] =
+           { "rx mgmt",        KSTAT_KV_U_PACKETS,     E1000_MGTPRC },
+       [em_stat_mgtpdc] =
+           { "rx mgmt drops",  KSTAT_KV_U_PACKETS,     E1000_MGTPDC },
+       [em_stat_mgtptc] =
+           { "tx mgmt",        KSTAT_KV_U_PACKETS,     E1000_MGTPTC },
+       [em_stat_tor] = /* 64bit */
+           { "rx total",       KSTAT_KV_U_BYTES,       0 },
+       [em_stat_tot] = /* 64bit */
+           { "tx total",       KSTAT_KV_U_BYTES,       0 },
+       [em_stat_tpr] =
+           { "rx total",       KSTAT_KV_U_PACKETS,     E1000_TPR },
+       [em_stat_tpt] =
+           { "tx total",       KSTAT_KV_U_PACKETS,     E1000_TPT },
+       [em_stat_ptc64] =
+           { "tx 64B",         KSTAT_KV_U_PACKETS,     E1000_PTC64 },
+       [em_stat_ptc127] =
+           { "tx 65-127B",     KSTAT_KV_U_PACKETS,     E1000_PTC127 },
+       [em_stat_ptc255] =
+           { "tx 128-255B",    KSTAT_KV_U_PACKETS,     E1000_PTC255 },
+       [em_stat_ptc511] =
+           { "tx 256-511B",    KSTAT_KV_U_PACKETS,     E1000_PTC511 },
+       [em_stat_ptc1023] =
+           { "tx 512-1023B",   KSTAT_KV_U_PACKETS,     E1000_PTC1023 },
+       [em_stat_ptc1522] =
+           { "tx 1024-maxB",   KSTAT_KV_U_PACKETS,     E1000_PTC1522 },
+       [em_stat_mptc] =
+           { "tx mcast",       KSTAT_KV_U_PACKETS,     E1000_MPTC },
+       [em_stat_bptc] =
+           { "tx bcast",       KSTAT_KV_U_PACKETS,     E1000_BPTC },
+};
 
-#ifndef SMALL_KERNEL
 /**********************************************************************
  *
  *  Update the board statistics counters. 
  *
  **********************************************************************/
-void
-em_update_stats_counters(struct em_softc *sc)
+int
+em_kstat_read(struct kstat *ks)
 {
-       struct em_queue *que = sc->queues; /* Use only first queue. */
-       struct ifnet   *ifp = &sc->sc_ac.ac_if;
-       uint64_t        colc, rxerrc, crcerrs, algnerrc;
-       uint64_t        ruc, roc, mpc, cexterr;
-       uint64_t        ecol, latecol;
-
-       crcerrs = E1000_READ_REG(&sc->hw, CRCERRS);
-       sc->stats.crcerrs += crcerrs;
-       mpc = E1000_READ_REG(&sc->hw, MPC);
-       sc->stats.mpc += mpc;
-       ecol = E1000_READ_REG(&sc->hw, ECOL);
-       sc->stats.ecol += ecol;
-
-       latecol = E1000_READ_REG(&sc->hw, LATECOL);
-       sc->stats.latecol += latecol;
-       colc = E1000_READ_REG(&sc->hw, COLC);
-       sc->stats.colc += colc;
-
-       ruc = E1000_READ_REG(&sc->hw, RUC);
-       sc->stats.ruc += ruc;
-       roc = E1000_READ_REG(&sc->hw, ROC);
-       sc->stats.roc += roc;
+       struct em_softc *sc = ks->ks_softc;
+       struct em_hw *hw = &sc->hw;
+       struct kstat_kv *kvs = ks->ks_data;
+       uint32_t lo, hi;
+       unsigned int i;
+
+       for (i = 0; i < nitems(em_counters); i++) {
+               const struct em_counter *c = &em_counters[i];
+               if (c->reg == 0)
+                       continue;
 
-       algnerrc = rxerrc = cexterr = 0;
-       if (sc->hw.mac_type >= em_82543) {
-               algnerrc = E1000_READ_REG(&sc->hw, ALGNERRC);
-               rxerrc = E1000_READ_REG(&sc->hw, RXERRC);
-               cexterr = E1000_READ_REG(&sc->hw, CEXTERR);
-       }
-       sc->stats.algnerrc += algnerrc;
-       sc->stats.rxerrc += rxerrc;
-       sc->stats.cexterr += cexterr;
+               kstat_kv_u64(&kvs[i]) += EM_READ_REG(hw,
+                   E1000_REG_TR(hw, c->reg)); /* wtf */
+       }
 
-#ifdef EM_DEBUG
-       if (sc->hw.media_type == em_media_type_copper ||
-           (E1000_READ_REG(&sc->hw, STATUS) & E1000_STATUS_LU)) {
-               sc->stats.symerrs += E1000_READ_REG(&sc->hw, SYMERRS);
-               sc->stats.sec += E1000_READ_REG(&sc->hw, SEC);
-       }
-       sc->stats.scc += E1000_READ_REG(&sc->hw, SCC);
-
-       sc->stats.mcc += E1000_READ_REG(&sc->hw, MCC);
-       sc->stats.dc += E1000_READ_REG(&sc->hw, DC);
-       sc->stats.rlec += E1000_READ_REG(&sc->hw, RLEC);
-       sc->stats.xonrxc += E1000_READ_REG(&sc->hw, XONRXC);
-       sc->stats.xontxc += E1000_READ_REG(&sc->hw, XONTXC);
-       sc->stats.xoffrxc += E1000_READ_REG(&sc->hw, XOFFRXC);
-       sc->stats.xofftxc += E1000_READ_REG(&sc->hw, XOFFTXC);
-       sc->stats.fcruc += E1000_READ_REG(&sc->hw, FCRUC);
-       sc->stats.prc64 += E1000_READ_REG(&sc->hw, PRC64);
-       sc->stats.prc127 += E1000_READ_REG(&sc->hw, PRC127);
-       sc->stats.prc255 += E1000_READ_REG(&sc->hw, PRC255);
-       sc->stats.prc511 += E1000_READ_REG(&sc->hw, PRC511);
-       sc->stats.prc1023 += E1000_READ_REG(&sc->hw, PRC1023);
-       sc->stats.prc1522 += E1000_READ_REG(&sc->hw, PRC1522);
-       sc->stats.gprc += E1000_READ_REG(&sc->hw, GPRC);
-       sc->stats.bprc += E1000_READ_REG(&sc->hw, BPRC);
-       sc->stats.mprc += E1000_READ_REG(&sc->hw, MPRC);
-       sc->stats.gptc += E1000_READ_REG(&sc->hw, GPTC);
+       /* Handle the exceptions. */
+
+       if (sc->hw.mac_type >= em_82543) {
+               kstat_kv_u64(&kvs[em_stat_algnerrc]) +=
+                   E1000_READ_REG(hw, ALGNERRC);
+               kstat_kv_u64(&kvs[em_stat_rxerrc]) +=
+                   E1000_READ_REG(hw, RXERRC);
+               kstat_kv_u64(&kvs[em_stat_cexterr]) +=
+                   E1000_READ_REG(hw, CEXTERR);
+               kstat_kv_u64(&kvs[em_stat_tncrs]) +=
+                   E1000_READ_REG(hw, TNCRS);
+#if 0
+               sc->stats.tsctc += 
+               E1000_READ_REG(hw, TSCTC);
+               sc->stats.tsctfc += 
+               E1000_READ_REG(hw, TSCTFC);
+#endif
+       }
 
        /* For the 64-bit byte counters the low dword must be read first. */
        /* Both registers clear on the read of the high dword */
 
-       sc->stats.gorcl += E1000_READ_REG(&sc->hw, GORCL); 
-       sc->stats.gorch += E1000_READ_REG(&sc->hw, GORCH);
-       sc->stats.gotcl += E1000_READ_REG(&sc->hw, GOTCL);
-       sc->stats.gotch += E1000_READ_REG(&sc->hw, GOTCH);
-
-       sc->stats.rnbc += E1000_READ_REG(&sc->hw, RNBC);
-       sc->stats.rfc += E1000_READ_REG(&sc->hw, RFC);
-       sc->stats.rjc += E1000_READ_REG(&sc->hw, RJC);
-
-       sc->stats.torl += E1000_READ_REG(&sc->hw, TORL);
-       sc->stats.torh += E1000_READ_REG(&sc->hw, TORH);
-       sc->stats.totl += E1000_READ_REG(&sc->hw, TOTL);
-       sc->stats.toth += E1000_READ_REG(&sc->hw, TOTH);
-
-       sc->stats.tpr += E1000_READ_REG(&sc->hw, TPR);
-       sc->stats.tpt += E1000_READ_REG(&sc->hw, TPT);
-       sc->stats.ptc64 += E1000_READ_REG(&sc->hw, PTC64);
-       sc->stats.ptc127 += E1000_READ_REG(&sc->hw, PTC127);
-       sc->stats.ptc255 += E1000_READ_REG(&sc->hw, PTC255);
-       sc->stats.ptc511 += E1000_READ_REG(&sc->hw, PTC511);
-       sc->stats.ptc1023 += E1000_READ_REG(&sc->hw, PTC1023);
-       sc->stats.ptc1522 += E1000_READ_REG(&sc->hw, PTC1522);
-       sc->stats.mptc += E1000_READ_REG(&sc->hw, MPTC);
-       sc->stats.bptc += E1000_READ_REG(&sc->hw, BPTC);
-       sc->stats.sdpc += E1000_READ_REG(&sc->hw, SDPC);
-       sc->stats.mngpdc += E1000_READ_REG(&sc->hw, MGTPDC);
-       sc->stats.mngprc += E1000_READ_REG(&sc->hw, MGTPRC);
-       sc->stats.mngptc += E1000_READ_REG(&sc->hw, MGTPTC);
-       sc->stats.b2ospc += E1000_READ_REG(&sc->hw, B2OSPC);
-       sc->stats.o2bgptc += E1000_READ_REG(&sc->hw, O2BGPTC);
-       sc->stats.b2ogprc += E1000_READ_REG(&sc->hw, B2OGPRC);
-       sc->stats.o2bspc += E1000_READ_REG(&sc->hw, O2BSPC);
-       sc->stats.rpthc += E1000_READ_REG(&sc->hw, RPTHC);
+       lo = E1000_READ_REG(hw, GORCL);
+       hi = E1000_READ_REG(hw, GORCL);
+       kstat_kv_u64(&kvs[em_stat_gorc]) +=
+           ((uint64_t)hi << 32) | (uint64_t)lo;
+
+       lo = E1000_READ_REG(hw, GOTCL);
+       hi = E1000_READ_REG(hw, GOTCL);
+       kstat_kv_u64(&kvs[em_stat_gotc]) +=
+           ((uint64_t)hi << 32) | (uint64_t)lo;
+
+       lo = E1000_READ_REG(hw, TORL);
+       hi = E1000_READ_REG(hw, TORH);
+       kstat_kv_u64(&kvs[em_stat_tor]) +=
+           ((uint64_t)hi << 32) | (uint64_t)lo;
+
+       lo = E1000_READ_REG(hw, TOTL);
+       hi = E1000_READ_REG(hw, TOTH);
+       kstat_kv_u64(&kvs[em_stat_tot]) +=
+           ((uint64_t)hi << 32) | (uint64_t)lo;
 
-       if (sc->hw.mac_type >= em_82543) {
-               sc->stats.tncrs += 
-               E1000_READ_REG(&sc->hw, TNCRS);
-               sc->stats.tsctc += 
-               E1000_READ_REG(&sc->hw, TSCTC);
-               sc->stats.tsctfc += 
-               E1000_READ_REG(&sc->hw, TSCTFC);
+       getnanouptime(&ks->ks_updated);
+
+       return (0);
+}
+
+void
+em_kstat_attach(struct em_softc *sc)
+{
+       struct kstat *ks;
+       struct kstat_kv *kvs;
+       unsigned int i;
+
+       mtx_init(&sc->kstat_mtx, IPL_SOFTCLOCK);
+
+       ks = kstat_create(DEVNAME(sc), 0, "em-stats", 0,
+           KSTAT_T_KV, 0);
+       if (ks == NULL)
+               return;
+
+       kvs = mallocarray(nitems(em_counters), sizeof(*kvs),
+           M_DEVBUF, M_WAITOK|M_ZERO);
+       for (i = 0; i < nitems(em_counters); i++) {
+               const struct em_counter *c = &em_counters[i];
+               kstat_kv_unit_init(&kvs[i], c->name,
+                   KSTAT_KV_T_COUNTER64, c->unit);
        }
-#endif
 
-       /* Fill out the OS statistics structure */
-       ifp->if_collisions = colc;
+       ks->ks_softc = sc;
+       ks->ks_data = kvs;
+       ks->ks_datalen = nitems(em_counters) * sizeof(*kvs);
+       ks->ks_read = em_kstat_read;
+       kstat_set_mutex(ks, &sc->kstat_mtx);
 
-       /* Rx Errors */
-       ifp->if_ierrors =
-           que->rx.dropped_pkts +
-           rxerrc +
-           crcerrs +
-           algnerrc +
-           ruc +
-           roc +
-           mpc +
-           cexterr +
-           sc->rx_overruns;
-
-       /* Tx Errors */
-       ifp->if_oerrors = ecol + latecol +
-           sc->watchdog_events;
+       kstat_install(ks);
 }
 
-#ifdef EM_DEBUG
-/**********************************************************************
- *
- *  This routine is called only when IFF_DEBUG is enabled.
- *  This routine provides a way to take a look at important statistics
- *  maintained by the driver and hardware.
- *
- **********************************************************************/
+/******************************************************************************
+ * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT
+ *****************************************************************************/
 void
-em_print_hw_stats(struct em_softc *sc)
+em_tbi_adjust_stats(struct em_softc *sc, uint32_t frame_len, uint8_t *mac_addr)
 {
-       const char * const unit = DEVNAME(sc);
-       struct em_queue *que;
+       struct em_hw *hw = &sc->hw;
+       struct kstat *ks = sc->kstat;
+       struct kstat_kv *kvs;
 
-       printf("%s: Excessive collisions = %lld\n", unit,
-               (long long)sc->stats.ecol);
-       printf("%s: Symbol errors = %lld\n", unit,
-               (long long)sc->stats.symerrs);
-       printf("%s: Sequence errors = %lld\n", unit,
-               (long long)sc->stats.sec);
-       printf("%s: Defer count = %lld\n", unit,
-               (long long)sc->stats.dc);
-
-       printf("%s: Missed Packets = %lld\n", unit,
-               (long long)sc->stats.mpc);
-       printf("%s: Receive No Buffers = %lld\n", unit,
-               (long long)sc->stats.rnbc);
-       /* RLEC is inaccurate on some hardware, calculate our own */
-       printf("%s: Receive Length Errors = %lld\n", unit,
-               ((long long)sc->stats.roc +
-               (long long)sc->stats.ruc));
-       printf("%s: Receive errors = %lld\n", unit,
-               (long long)sc->stats.rxerrc);
-       printf("%s: Crc errors = %lld\n", unit,
-               (long long)sc->stats.crcerrs);
-       printf("%s: Alignment errors = %lld\n", unit,
-               (long long)sc->stats.algnerrc);
-       printf("%s: Carrier extension errors = %lld\n", unit,
-               (long long)sc->stats.cexterr);
-
-       printf("%s: RX overruns = %ld\n", unit,
-               sc->rx_overruns);
-       printf("%s: watchdog timeouts = %ld\n", unit,
-               sc->watchdog_events);
-
-       printf("%s: XON Rcvd = %lld\n", unit,
-               (long long)sc->stats.xonrxc);
-       printf("%s: XON Xmtd = %lld\n", unit,
-               (long long)sc->stats.xontxc);
-       printf("%s: XOFF Rcvd = %lld\n", unit,
-               (long long)sc->stats.xoffrxc);
-       printf("%s: XOFF Xmtd = %lld\n", unit,
-               (long long)sc->stats.xofftxc);
-
-       printf("%s: Good Packets Rcvd = %lld\n", unit,
-               (long long)sc->stats.gprc);
-       printf("%s: Good Packets Xmtd = %lld\n", unit,
-               (long long)sc->stats.gptc);
-       printf("%s: Switch Drop Packet Count = %lld\n", unit,
-           (long long)sc->stats.sdpc);
-       printf("%s: Management Packets Dropped Count  = %lld\n", unit,
-           (long long)sc->stats.mngptc);
-       printf("%s: Management Packets Received Count  = %lld\n", unit,
-           (long long)sc->stats.mngprc);
-       printf("%s: Management Packets Transmitted Count  = %lld\n", unit,
-           (long long)sc->stats.mngptc);
-       printf("%s: OS2BMC Packets Sent by MC Count  = %lld\n", unit,
-           (long long)sc->stats.b2ospc);
-       printf("%s: OS2BMC Packets Received by MC Count  = %lld\n", unit,
-           (long long)sc->stats.o2bgptc);
-       printf("%s: OS2BMC Packets Received by Host Count  = %lld\n", unit,
-           (long long)sc->stats.b2ogprc);
-       printf("%s: OS2BMC Packets Transmitted by Host Count  = %lld\n", unit,
-           (long long)sc->stats.o2bspc);
-       printf("%s: Multicast Packets Received Count  = %lld\n", unit,
-           (long long)sc->stats.mprc);
-       printf("%s: Rx Packets to Host Count = %lld\n", unit,
-           (long long)sc->stats.rpthc);
-       FOREACH_QUEUE(sc, que) {
-               printf("%s: Queue %d Good Packets Received = %d\n", unit,
-                   que->me, E1000_READ_REG(&sc->hw, PQGPRC(que->me)));
+       if (ks == NULL)
+               return;
+
+       /* First adjust the frame length. */
+       frame_len--;
+
+       mtx_enter(&sc->kstat_mtx);
+       kvs = ks->ks_data;
+
+       /*
+        * We need to adjust the statistics counters, since the hardware
+        * counters overcount this packet as a CRC error and undercount the
+        * packet as a good packet
+        */
+
+       /* This packet should not be counted as a CRC error.    */
+       kstat_kv_u64(&kvs[em_stat_crcerrs])--;
+       /* This packet does count as a Good Packet Received.    */
+       kstat_kv_u64(&kvs[em_stat_gprc])++;
+
+       /* Adjust the Good Octets received counters             */
+       kstat_kv_u64(&kvs[em_stat_gorc]) += frame_len;
+
+       /*
+        * Is this a broadcast or multicast?  Check broadcast first, since
+        * the test for a multicast frame will test positive on a broadcast
+        * frame.
+        */
+       if (ETHER_IS_BROADCAST(mac_addr)) {
+               /* Broadcast packet */
+               kstat_kv_u64(&kvs[em_stat_bprc])++;
+       } else if (ETHER_IS_MULTICAST(mac_addr)) { 
+               /* Multicast packet */
+               kstat_kv_u64(&kvs[em_stat_mprc])++;
        }
+
+       if (frame_len == hw->max_frame_size) {
+               /*
+                * In this case, the hardware has overcounted the number of
+                * oversize frames.
+                */
+               kstat_kv_u64(&kvs[em_stat_roc])--;
+       }
+
+       /*
+        * Adjust the bin counters when the extra byte put the frame in the
+        * wrong bin. Remember that the frame_len was adjusted above.
+        */
+       if (frame_len == 64) {
+               kstat_kv_u64(&kvs[em_stat_prc64])++;
+               kstat_kv_u64(&kvs[em_stat_prc127])--;
+       } else if (frame_len == 127) {
+               kstat_kv_u64(&kvs[em_stat_prc127])++;
+               kstat_kv_u64(&kvs[em_stat_prc255])--;
+       } else if (frame_len == 255) {
+               kstat_kv_u64(&kvs[em_stat_prc255])++;
+               kstat_kv_u64(&kvs[em_stat_prc511])--;
+       } else if (frame_len == 511) {
+               kstat_kv_u64(&kvs[em_stat_prc511])++;
+               kstat_kv_u64(&kvs[em_stat_prc1023])--;
+       } else if (frame_len == 1023) {
+               kstat_kv_u64(&kvs[em_stat_prc1023])++;
+               kstat_kv_u64(&kvs[em_stat_prc1522])--;
+       } else if (frame_len == 1522) {
+               kstat_kv_u64(&kvs[em_stat_prc1522])++;
+       }
+
+       mtx_leave(&sc->kstat_mtx);
 }
-#endif
+#endif /* NKSTAT > 0 */
 
+#ifndef SMALL_KERNEL
 int
 em_allocate_msix(struct em_softc *sc)
 {
Index: if_em.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_em.h,v
retrieving revision 1.77
diff -u -p -r1.77 if_em.h
--- if_em.h     22 Apr 2020 08:47:11 -0000      1.77
+++ if_em.h     7 Jul 2020 08:48:37 -0000
@@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE.
 
 #include "bpfilter.h"
 #include "vlan.h"
+#include "kstat.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -50,6 +51,7 @@ POSSIBILITY OF SUCH DAMAGE.
 #include <sys/socket.h>
 #include <sys/timeout.h>
 #include <sys/atomic.h>
+#include <sys/kstat.h>
 
 #include <net/if.h>
 #include <net/if_media.h>
@@ -439,7 +441,6 @@ struct em_softc {
 
        /* For 82544 PCI-X Workaround */
        boolean_t       pcix_82544;
-       struct em_hw_stats stats;
 
        int                      msix;
        uint32_t                 msix_linkvec;
@@ -447,6 +448,9 @@ struct em_softc {
        uint32_t                 msix_queuesmask;
        int                      num_queues;
        struct em_queue         *queues;
+
+       struct kstat            *kstat;
+       struct mutex             kstat_mtx;
 };
 
 #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
Index: if_em_hw.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_em_hw.c,v
retrieving revision 1.108
diff -u -p -r1.108 if_em_hw.c
--- if_em_hw.c  22 Apr 2020 08:47:11 -0000      1.108
+++ if_em_hw.c  7 Jul 2020 08:48:37 -0000
@@ -44,6 +44,7 @@
 #include <sys/kernel.h>
 #include <sys/device.h>
 #include <sys/socket.h>
+#include <sys/kstat.h>
 
 #include <net/if.h>
 #include <net/if_media.h>
@@ -8027,88 +8028,6 @@ em_clear_hw_cntrs(struct em_hw *hw)
        temp = E1000_READ_REG(hw, ICTXQMTC);
        temp = E1000_READ_REG(hw, ICRXDMTC);
 }
-
-#ifndef SMALL_KERNEL
-/******************************************************************************
- * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT
- *
- * hw - Struct containing variables accessed by shared code
- * frame_len - The length of the frame in question
- * mac_addr - The Ethernet destination address of the frame in question
- *****************************************************************************/
-void
-em_tbi_adjust_stats(struct em_hw *hw, struct em_hw_stats *stats,
-    uint32_t frame_len, uint8_t *mac_addr)
-{
-       uint64_t carry_bit;
-       /* First adjust the frame length. */
-       frame_len--;
-       /*
-        * We need to adjust the statistics counters, since the hardware
-        * counters overcount this packet as a CRC error and undercount the
-        * packet as a good packet
-        */
-       /* This packet should not be counted as a CRC error.    */
-       stats->crcerrs--;
-       /* This packet does count as a Good Packet Received.    */
-       stats->gprc++;
-
-       /* Adjust the Good Octets received counters             */
-       carry_bit = 0x80000000 & stats->gorcl;
-       stats->gorcl += frame_len;
-       /*
-        * If the high bit of Gorcl (the low 32 bits of the Good Octets
-        * Received Count) was one before the addition, AND it is zero after,
-        * then we lost the carry out, need to add one to Gorch (Good Octets
-        * Received Count High). This could be simplified if all environments
-        * supported 64-bit integers.
-        */
-       if (carry_bit && ((stats->gorcl & 0x80000000) == 0))
-               stats->gorch++;
-       /*
-        * Is this a broadcast or multicast?  Check broadcast first, since
-        * the test for a multicast frame will test positive on a broadcast
-        * frame.
-        */
-       if ((mac_addr[0] == (uint8_t) 0xff) && (mac_addr[1] == (uint8_t) 0xff))
-               /* Broadcast packet */
-               stats->bprc++;
-       else if (*mac_addr & 0x01)
-               /* Multicast packet */
-               stats->mprc++;
-
-       if (frame_len == hw->max_frame_size) {
-               /*
-                * In this case, the hardware has overcounted the number of
-                * oversize frames.
-                */
-               if (stats->roc > 0)
-                       stats->roc--;
-       }
-       /*
-        * Adjust the bin counters when the extra byte put the frame in the
-        * wrong bin. Remember that the frame_len was adjusted above.
-        */
-       if (frame_len == 64) {
-               stats->prc64++;
-               stats->prc127--;
-       } else if (frame_len == 127) {
-               stats->prc127++;
-               stats->prc255--;
-       } else if (frame_len == 255) {
-               stats->prc255++;
-               stats->prc511--;
-       } else if (frame_len == 511) {
-               stats->prc511++;
-               stats->prc1023--;
-       } else if (frame_len == 1023) {
-               stats->prc1023++;
-               stats->prc1522--;
-       } else if (frame_len == 1522) {
-               stats->prc1522++;
-       }
-}
-#endif /* !SMALL_KERNEL */
 
 /******************************************************************************
  * Gets the current PCI bus type, speed, and width of the hardware
Index: if_em_hw.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_em_hw.h,v
retrieving revision 1.82
diff -u -p -r1.82 if_em_hw.h
--- if_em_hw.h  23 Mar 2020 15:02:51 -0000      1.82
+++ if_em_hw.h  7 Jul 2020 08:48:37 -0000
@@ -440,7 +440,6 @@ int32_t em_blink_led_start(struct em_hw 
 void em_clear_hw_cntrs(struct em_hw *hw);
 void em_reset_adaptive(struct em_hw *hw);
 void em_update_adaptive(struct em_hw *hw);
-void em_tbi_adjust_stats(struct em_hw *hw, struct em_hw_stats *stats, uint32_t 
frame_len, uint8_t *mac_addr);
 void em_get_bus_info(struct em_hw *hw);
 void em_pci_set_mwi(struct em_hw *hw);
 void em_pci_clear_mwi(struct em_hw *hw);

Reply via email to