summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordlg <dlg@openbsd.org>2020-07-07 02:38:56 +0000
committerdlg <dlg@openbsd.org>2020-07-07 02:38:56 +0000
commit2ee1fcb37df9a931d73bf73dacc3c7d9df9193ec (patch)
treec037d2f61673675e6dc377d2f8d3b5ddade1f55e
parentGet rid of some rasops callbacks in efifb that only call rasops (diff)
downloadwireguard-openbsd-2ee1fcb37df9a931d73bf73dacc3c7d9df9193ec.tar.xz
wireguard-openbsd-2ee1fcb37df9a931d73bf73dacc3c7d9df9193ec.zip
add kstat support for reading hardware counters.
this chip is annoyingly complicated, which is reflected in how complicated it is to read counters off the chip. while we just use ixl as a normal network interface, the chip is capable of being a switch with physical ports, virtual ports and all sorts of other functionality, and there are counters in different places for all these different pieces. in our simple setup the driver interface is mapped to a single physical port which we talk to via a single virtual switch interface. this diff adds counters on each interface for the physical port and for the virtual switch interface (vsi). the port counters show what the hardware is doing, while the vsi counters show how the driver is interacting with the chip. for things like packet counters, these numbers tend to correlate strongly, but there are some differences. if the chip drops packets cos there's no descriptors on the rx ring, that's shown in the vsi counters. problems talking to the physical network (eg, packet corruption off the wire) are reported on the port counters. on top of the chip just being complicated, reading the counters is a complicated activity on its own. because the counters can be read by multiple consumers in a virtualised environment, the counters never really get reset. they are also stored in annoyingly small fields. this means you basically have to poll the chip periodically and calculate differences between the polls to avoid losing numbers if they overflow too quickly.
-rw-r--r--sys/dev/pci/if_ixl.c320
1 files changed, 319 insertions, 1 deletions
diff --git a/sys/dev/pci/if_ixl.c b/sys/dev/pci/if_ixl.c
index f681041ae70..c739b4e5291 100644
--- a/sys/dev/pci/if_ixl.c
+++ b/sys/dev/pci/if_ixl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_ixl.c,v 1.59 2020/06/26 02:51:12 dlg Exp $ */
+/* $OpenBSD: if_ixl.c,v 1.60 2020/07/07 02:38:56 dlg Exp $ */
/*
* Copyright (c) 2013-2015, Intel Corporation
@@ -48,6 +48,7 @@
*/
#include "bpfilter.h"
+#include "kstat.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -76,6 +77,10 @@
#include <net/bpf.h>
#endif
+#if NKSTAT > 0
+#include <sys/kstat.h>
+#endif
+
#include <netinet/in.h>
#include <netinet/if_ether.h>
@@ -1244,6 +1249,7 @@ struct ixl_softc {
uint16_t sc_vsi_number; /* le */
uint16_t sc_seid;
unsigned int sc_base_queue;
+ unsigned int sc_port;
struct ixl_dmamem sc_scratch;
@@ -1281,6 +1287,13 @@ struct ixl_softc {
unsigned int sc_dead;
uint8_t sc_enaddr[ETHER_ADDR_LEN];
+
+#if NKSTAT > 0
+ struct mutex sc_kstat_mtx;
+ struct timeout sc_kstat_tmo;
+ struct kstat *sc_port_kstat;
+ struct kstat *sc_vsi_kstat;
+#endif
};
#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
@@ -1373,6 +1386,10 @@ static void ixl_rxfill(struct ixl_softc *, struct ixl_rx_ring *);
static void ixl_rxrefill(void *);
static int ixl_rxrinfo(struct ixl_softc *, struct if_rxrinfo *);
+#if NKSTAT > 0
+static void ixl_kstat_attach(struct ixl_softc *);
+#endif
+
struct cfdriver ixl_cd = {
NULL,
"ixl",
@@ -1663,6 +1680,7 @@ ixl_attach(struct device *parent, struct device *self, void *aux)
port = ixl_rd(sc, I40E_PFGEN_PORTNUM);
port &= I40E_PFGEN_PORTNUM_PORT_NUM_MASK;
port >>= I40E_PFGEN_PORTNUM_PORT_NUM_SHIFT;
+ sc->sc_port = port;
printf(": port %u", port);
ari = ixl_rd(sc, I40E_GLPCI_CAPSUP);
@@ -1937,6 +1955,10 @@ ixl_attach(struct device *parent, struct device *self, void *aux)
ixl_intr_enable(sc);
+#if NKSTAT > 0
+ ixl_kstat_attach(sc);
+#endif
+
return;
free_vectors:
if (sc->sc_intrmap != NULL) {
@@ -4996,3 +5018,299 @@ ixl_dmamem_free(struct ixl_softc *sc, struct ixl_dmamem *ixm)
bus_dmamem_free(sc->sc_dmat, &ixm->ixm_seg, 1);
bus_dmamap_destroy(sc->sc_dmat, ixm->ixm_map);
}
+
+#if NKSTAT > 0
+
+#if 0
+static uint64_t
+ixl_st64(struct ixl_softc *sc, bus_addr_t r)
+{
+ uint32_t hi, ohi, lo;
+
+ hi = ixl_rd(sc, r + 4);
+ do {
+ ohi = hi;
+ lo = ixl_rd(sc, r);
+ hi = ixl_rd(sc, r + 4);
+ } while (__predict_false(ohi != hi));
+
+ return (((uint64_t)hi << 32) | (uint64_t)lo);
+}
+#endif
+
+#define ixl_st32(_sc, _r) ixl_rd((_sc), (_r))
+#define ixl_st48(_sc, _r) ixl_st64((_sc), (_r))
+
+CTASSERT(KSTAT_KV_U_NONE <= 0xffU);
+CTASSERT(KSTAT_KV_U_PACKETS <= 0xffU);
+CTASSERT(KSTAT_KV_U_BYTES <= 0xffU);
+
+struct ixl_counter {
+ const char *c_name;
+ uint32_t c_base;
+ uint8_t c_width;
+ uint8_t c_type;
+};
+
+const struct ixl_counter ixl_port_counters[] = {
+ /* GORC */
+ { "rx bytes", 0x00300000, 48, KSTAT_KV_U_BYTES },
+ /* MLFC */
+ { "mac local errs", 0x00300020, 32, KSTAT_KV_U_NONE },
+ /* MRFC */
+ { "mac remote errs", 0x00300040, 32, KSTAT_KV_U_NONE },
+ /* MSPDC */
+ { "mac short", 0x00300060, 32, KSTAT_KV_U_PACKETS },
+ /* CRCERRS */
+ { "crc errs", 0x00300080, 32, KSTAT_KV_U_PACKETS },
+ /* RLEC */
+ { "rx len errs", 0x003000a0, 32, KSTAT_KV_U_PACKETS },
+ /* ERRBC */
+ { "byte errs", 0x003000c0, 32, KSTAT_KV_U_PACKETS },
+ /* ILLERRC */
+ { "illegal byte", 0x003000d0, 32, KSTAT_KV_U_PACKETS },
+ /* RUC */
+ { "rx undersize", 0x00300100, 32, KSTAT_KV_U_PACKETS },
+ /* ROC */
+ { "rx oversize", 0x00300120, 32, KSTAT_KV_U_PACKETS },
+ /* LXONRXCNT */
+ { "rx link xon", 0x00300140, 32, KSTAT_KV_U_PACKETS },
+ /* LXOFFRXCNT */
+ { "rx link xoff", 0x00300160, 32, KSTAT_KV_U_PACKETS },
+
+ /* Priority XON Received Count */
+ /* Priority XOFF Received Count */
+ /* Priority XON to XOFF Count */
+
+ /* PRC64 */
+ { "rx 64B", 0x00300480, 48, KSTAT_KV_U_PACKETS },
+ /* PRC127 */
+ { "rx 65-127B", 0x003004A0, 48, KSTAT_KV_U_PACKETS },
+ /* PRC255 */
+ { "rx 128-255B", 0x003004C0, 48, KSTAT_KV_U_PACKETS },
+ /* PRC511 */
+ { "rx 256-511B", 0x003004E0, 48, KSTAT_KV_U_PACKETS },
+ /* PRC1023 */
+ { "rx 512-1023B", 0x00300500, 48, KSTAT_KV_U_PACKETS },
+ /* PRC1522 */
+ { "rx 1024-1522B", 0x00300520, 48, KSTAT_KV_U_PACKETS },
+ /* PRC9522 */
+ { "rx 1523-9522B", 0x00300540, 48, KSTAT_KV_U_PACKETS },
+ /* ROC */
+ { "rx fragment", 0x00300560, 32, KSTAT_KV_U_PACKETS },
+ /* RJC */
+ { "rx jabber", 0x00300580, 32, KSTAT_KV_U_PACKETS },
+ /* UPRC */
+ { "rx ucasts", 0x003005a0, 48, KSTAT_KV_U_PACKETS },
+ /* MPRC */
+ { "rx mcasts", 0x003005c0, 48, KSTAT_KV_U_PACKETS },
+ /* BPRC */
+ { "rx bcasts", 0x003005e0, 48, KSTAT_KV_U_PACKETS },
+ /* RDPC */
+ { "rx discards", 0x00300600, 32, KSTAT_KV_U_PACKETS },
+ /* LDPC */
+ { "rx lo discards", 0x00300620, 32, KSTAT_KV_U_PACKETS },
+ /* RUPP */
+ { "rx no dest", 0x00300660, 32, KSTAT_KV_U_PACKETS },
+
+ /* GOTC */
+ { "tx bytes", 0x00300680, 48, KSTAT_KV_U_BYTES },
+ /* PTC64 */
+ { "tx 64B", 0x003006A0, 48, KSTAT_KV_U_PACKETS },
+ /* PTC127 */
+ { "tx 65-127B", 0x003006C0, 48, KSTAT_KV_U_PACKETS },
+ /* PTC255 */
+ { "tx 128-255B", 0x003006E0, 48, KSTAT_KV_U_PACKETS },
+ /* PTC511 */
+ { "tx 256-511B", 0x00300700, 48, KSTAT_KV_U_PACKETS },
+ /* PTC1023 */
+ { "tx 512-1023B", 0x00300720, 48, KSTAT_KV_U_PACKETS },
+ /* PTC1522 */
+ { "tx 1024-1522B", 0x00300740, 48, KSTAT_KV_U_PACKETS },
+ /* PTC9522 */
+ { "tx 1523-9522B", 0x00300760, 48, KSTAT_KV_U_PACKETS },
+
+ /* Priority XON Transmitted Count */
+ /* Priority XOFF Transmitted Count */
+
+ /* LXONTXC */
+ { "tx link xon", 0x00300980, 48, KSTAT_KV_U_PACKETS },
+ /* LXOFFTXC */
+ { "tx link xoff", 0x003009a0, 48, KSTAT_KV_U_PACKETS },
+ /* UPTC */
+ { "tx ucasts", 0x003009c0, 48, KSTAT_KV_U_PACKETS },
+ /* MPTC */
+ { "tx mcasts", 0x003009e0, 48, KSTAT_KV_U_PACKETS },
+ /* BPTC */
+ { "tx bcasts", 0x00300a00, 48, KSTAT_KV_U_PACKETS },
+ /* TDOLD */
+ { "tx link down", 0x00300a20, 48, KSTAT_KV_U_PACKETS },
+};
+
+const struct ixl_counter ixl_vsi_counters[] = {
+ /* VSI RDPC */
+ { "rx discards", 0x00310000, 32, KSTAT_KV_U_PACKETS },
+ /* VSI GOTC */
+ { "tx bytes", 0x00328000, 48, KSTAT_KV_U_BYTES },
+ /* VSI UPTC */
+ { "tx ucasts", 0x0033c000, 48, KSTAT_KV_U_PACKETS },
+ /* VSI MPTC */
+ { "tx mcasts", 0x0033cc00, 48, KSTAT_KV_U_PACKETS },
+ /* VSI BPTC */
+ { "tx bcasts", 0x0033d800, 48, KSTAT_KV_U_PACKETS },
+ /* VSI TEPC */
+ { "tx errs", 0x00344000, 48, KSTAT_KV_U_PACKETS },
+ /* VSI TDPC */
+ { "tx discards", 0x00348000, 48, KSTAT_KV_U_PACKETS },
+ /* VSI GORC */
+ { "rx bytes", 0x00358000, 48, KSTAT_KV_U_BYTES },
+ /* VSI UPRC */
+ { "rx ucasts", 0x0036c000, 48, KSTAT_KV_U_PACKETS },
+ /* VSI MPRC */
+ { "rx mcasts", 0x0036cc00, 48, KSTAT_KV_U_PACKETS },
+ /* VSI BPRC */
+ { "rx bcasts", 0x0036d800, 48, KSTAT_KV_U_PACKETS },
+ /* VSI RUPP */
+ { "rx noproto", 0x0036e400, 32, KSTAT_KV_U_PACKETS },
+};
+
+struct ixl_counter_state {
+ const struct ixl_counter
+ *counters;
+ uint64_t *values;
+ size_t n;
+ uint32_t index;
+ unsigned int gen;
+};
+
+static void
+ixl_rd_counters(struct ixl_softc *sc, const struct ixl_counter_state *state,
+ uint64_t *vs)
+{
+ const struct ixl_counter *c;
+ bus_addr_t r;
+ uint64_t v;
+ size_t i;
+
+ for (i = 0; i < state->n; i++) {
+ c = &state->counters[i];
+
+ r = c->c_base + (state->index * 8);
+
+ if (c->c_width == 32)
+ v = bus_space_read_4(sc->sc_memt, sc->sc_memh, r);
+ else
+ v = bus_space_read_8(sc->sc_memt, sc->sc_memh, r);
+
+ vs[i] = v;
+ }
+}
+
+static int
+ixl_kstat_read(struct kstat *ks)
+{
+ struct ixl_softc *sc = ks->ks_softc;
+ struct kstat_kv *kvs = ks->ks_data;
+ struct ixl_counter_state *state = ks->ks_ptr;
+ unsigned int gen = (state->gen++) & 1;
+ uint64_t *ovs = state->values + (gen * state->n);
+ uint64_t *nvs = state->values + (!gen * state->n);
+ size_t i;
+
+ ixl_rd_counters(sc, state, nvs);
+ getnanouptime(&ks->ks_updated);
+
+ for (i = 0; i < state->n; i++) {
+ const struct ixl_counter *c = &state->counters[i];
+ uint64_t n = nvs[i], o = ovs[i];
+
+ if (c->c_width < 64) {
+ if (n < o)
+ n += (1ULL << c->c_width);
+ }
+
+ kstat_kv_u64(&kvs[i]) += (n - o);
+ }
+
+ return (0);
+}
+
+static void
+ixl_kstat_tick(void *arg)
+{
+ struct ixl_softc *sc = arg;
+
+ timeout_add_sec(&sc->sc_kstat_tmo, 4);
+
+ if (mtx_enter_try(&sc->sc_kstat_mtx) == 0)
+ return; /* try again later */
+
+ ixl_kstat_read(sc->sc_port_kstat);
+ ixl_kstat_read(sc->sc_vsi_kstat);
+
+ mtx_leave(&sc->sc_kstat_mtx);
+}
+
+static struct kstat *
+ixl_kstat_create(struct ixl_softc *sc, const char *name,
+ const struct ixl_counter *counters, size_t n, uint32_t index)
+{
+ struct kstat *ks;
+ struct kstat_kv *kvs;
+ struct ixl_counter_state *state;
+ const struct ixl_counter *c;
+ unsigned int i;
+
+ ks = kstat_create(DEVNAME(sc), 0, name, 0, KSTAT_T_KV, 0);
+ if (ks == NULL) {
+ /* unable to create kstats */
+ return (NULL);
+ }
+
+ kvs = mallocarray(n, sizeof(*kvs), M_DEVBUF, M_WAITOK|M_ZERO);
+ for (i = 0; i < n; i++) {
+ c = &counters[i];
+
+ kstat_kv_unit_init(&kvs[i], c->c_name,
+ KSTAT_KV_T_COUNTER64, c->c_type);
+ }
+
+ ks->ks_data = kvs;
+ ks->ks_datalen = n * sizeof(*kvs);
+ ks->ks_read = ixl_kstat_read;
+
+ state = malloc(sizeof(*state), M_DEVBUF, M_WAITOK|M_ZERO);
+ state->counters = counters;
+ state->n = n;
+ state->values = mallocarray(n * 2, sizeof(*state->values),
+ M_DEVBUF, M_WAITOK|M_ZERO);
+ state->index = index;
+ ks->ks_ptr = state;
+
+ kstat_set_mutex(ks, &sc->sc_kstat_mtx);
+ ks->ks_softc = sc;
+ kstat_install(ks);
+
+ /* fetch a baseline */
+ ixl_rd_counters(sc, state, state->values);
+
+ return (ks);
+}
+
+static void
+ixl_kstat_attach(struct ixl_softc *sc)
+{
+ mtx_init(&sc->sc_kstat_mtx, IPL_SOFTCLOCK);
+ timeout_set(&sc->sc_kstat_tmo, ixl_kstat_tick, sc);
+
+ sc->sc_port_kstat = ixl_kstat_create(sc, "ixl-port",
+ ixl_port_counters, nitems(ixl_port_counters), sc->sc_port);
+ sc->sc_vsi_kstat = ixl_kstat_create(sc, "ixl-vsi",
+ ixl_vsi_counters, nitems(ixl_vsi_counters),
+ lemtoh16(&sc->sc_vsi_number));
+
+ /* ixl counters go up even when the interface is down */
+ timeout_add_sec(&sc->sc_kstat_tmo, 4);
+}
+
+#endif /* NKSTAT > 0 */