dlg@r6415 net$ kstat aggr-port aggr0:0:aggr-port:5 interface: mcx0 tx-lacp-pkts: 100 packets tx-lacp-bytes: 12400 bytes tx-marker-pkts: 0 packets tx-marker-bytes: 0 bytes rx-lacp-pkts: 97 packets rx-lacp-bytes: 12028 bytes rx-marker-pkts: 0 packets rx-marker-bytes: 0 bytes rx-drops: 0 packets selected: true select-changes: 3 aggr0:0:aggr-port:6 interface: mcx1 tx-lacp-pkts: 93 packets tx-lacp-bytes: 11532 bytes tx-marker-pkts: 0 packets tx-marker-bytes: 0 bytes rx-lacp-pkts: 95 packets rx-lacp-bytes: 11780 bytes rx-marker-pkts: 0 packets rx-marker-bytes: 0 bytes rx-drops: 0 packets selected: true select-changes: 1 Index: if_aggr.c =================================================================== RCS file: /cvs/src/sys/net/if_aggr.c,v retrieving revision 1.42 diff -u -p -r1.42 if_aggr.c --- if_aggr.c 23 Dec 2023 10:52:54 -0000 1.42 +++ if_aggr.c 4 Mar 2024 03:38:13 -0000 @@ -53,6 +53,7 @@ */ #include "bpfilter.h" +#include "kstat.h" #include #include @@ -67,6 +68,7 @@ #include #include #include +#include #include #include @@ -299,8 +301,11 @@ static const char *lacp_mux_event_names[ * aggr interface */ -#define AGGR_MAX_PORTS 32 -#define AGGR_MAX_SLOW_PKTS (AGGR_MAX_PORTS * 3) +#define AGGR_PORT_BITS 5 +#define AGGR_FLOWID_SHIFT (16 - AGGR_PORT_BITS) + +#define AGGR_MAX_PORTS (1 << AGGR_PORT_BITS) +#define AGGR_MAX_SLOW_PKTS 3 struct aggr_multiaddr { TAILQ_ENTRY(aggr_multiaddr) @@ -326,8 +331,22 @@ static const char *aggr_port_selected_na "STANDBY", }; +struct aggr_proto_count { + uint64_t c_pkts; + uint64_t c_bytes; +}; + +#define AGGR_PROTO_TX_LACP 0 +#define AGGR_PROTO_TX_MARKER 1 +#define AGGR_PROTO_RX_LACP 2 +#define AGGR_PROTO_RX_MARKER 3 + +#define AGGR_PROTO_COUNT 4 + struct aggr_port { struct ifnet *p_ifp0; + struct kstat *p_kstat; + struct mutex p_mtx; uint8_t p_lladdr[ETHER_ADDR_LEN]; uint32_t p_mtu; @@ -362,7 +381,7 @@ struct aggr_port { /* Receive machine */ enum lacp_rxm_state p_rxm_state; - struct mbuf_queue p_rxm_mq; + struct mbuf_list p_rxm_ml; struct task p_rxm_task; /* Periodic Transmission machine */ @@ -375,6 +394,11 @@ struct aggr_port { int p_txm_log[LACP_TX_MACHINE_RATE]; unsigned int p_txm_slot; struct timeout p_txm_ntt; + + /* Counters */ + struct aggr_proto_count p_proto_counts[AGGR_PROTO_COUNT]; + uint64_t p_rx_drops; + uint32_t p_nselectch; }; TAILQ_HEAD(aggr_port_list, aggr_port); @@ -506,6 +530,11 @@ static void aggr_unselected(struct aggr_ static void aggr_selection_logic(struct aggr_softc *, struct aggr_port *); +#if NKSTAT > 0 +static void aggr_port_kstat_attach(struct aggr_port *); +static void aggr_port_kstat_detach(struct aggr_port *); +#endif + static struct if_clone aggr_cloner = IF_CLONE_INITIALIZER("aggr", aggr_clone_create, aggr_clone_destroy); @@ -660,7 +689,7 @@ aggr_transmit(struct aggr_softc *sc, con #endif if (ISSET(m->m_pkthdr.csum_flags, M_FLOWID)) - flow = m->m_pkthdr.ph_flowid; + flow = m->m_pkthdr.ph_flowid >> AGGR_FLOWID_SHIFT; ifp0 = map->m_ifp0s[flow % AGGR_MAX_PORTS]; @@ -736,6 +765,8 @@ aggr_input(struct ifnet *ifp0, struct mb struct ifnet *ifp = &sc->sc_if; struct ether_header *eh; int hlen = sizeof(*eh); + int drop = 0; + unsigned int rx_proto = AGGR_PROTO_RX_LACP; if (!ISSET(ifp->if_flags, IFF_RUNNING)) goto drop; @@ -757,9 +788,25 @@ aggr_input(struct ifnet *ifp0, struct mb sph = (struct ether_slowproto_hdr *)(eh + 1); switch (sph->sph_subtype) { - case SLOWPROTOCOLS_SUBTYPE_LACP: case SLOWPROTOCOLS_SUBTYPE_LACP_MARKER: - if (mq_enqueue(&p->p_rxm_mq, m) == 0) + rx_proto = AGGR_PROTO_RX_MARKER; + /* FALLTHROUGH */ + case SLOWPROTOCOLS_SUBTYPE_LACP: + mtx_enter(&p->p_mtx); + p->p_proto_counts[rx_proto].c_pkts++; + p->p_proto_counts[rx_proto].c_bytes += m->m_pkthdr.len; + + if (ml_len(&p->p_rxm_ml) < AGGR_MAX_SLOW_PKTS) + ml_enqueue(&p->p_rxm_ml, m); + else { + p->p_rx_drops++; + drop = 1; + } + mtx_leave(&p->p_mtx); + + if (drop) + goto drop; + else task_add(systq, &p->p_rxm_task); return; default: @@ -1112,6 +1159,7 @@ aggr_add_port(struct aggr_softc *sc, con p->p_ifp0 = ifp0; p->p_aggr = sc; p->p_mtu = ifp0->if_mtu; + mtx_init(&p->p_mtx, IPL_SOFTNET); CTASSERT(sizeof(p->p_lladdr) == sizeof(ac0->ac_enaddr)); memcpy(p->p_lladdr, ac0->ac_enaddr, sizeof(p->p_lladdr)); @@ -1152,7 +1200,7 @@ aggr_add_port(struct aggr_softc *sc, con if_detachhook_add(ifp0, &p->p_dhook); task_set(&p->p_rxm_task, aggr_rx, p); - mq_init(&p->p_rxm_mq, 3, IPL_NET); + ml_init(&p->p_rxm_ml); timeout_set_proc(&p->p_ptm_tx, aggr_ptm_tx, p); timeout_set_proc(&p->p_txm_ntt, aggr_transmit_machine, p); @@ -1170,6 +1218,10 @@ aggr_add_port(struct aggr_softc *sc, con DPRINTF(sc, "%s %s trunkport: creating port\n", ifp->if_xname, ifp0->if_xname); +#if NKSTAT > 0 + aggr_port_kstat_attach(p); /* this prints warnings itself */ +#endif + TAILQ_INSERT_TAIL(&sc->sc_ports, p, p_entry); sc->sc_nports++; @@ -1415,6 +1467,10 @@ aggr_p_dtor(struct aggr_softc *sc, struc ifp0->if_ioctl = p->p_ioctl; ifp0->if_output = p->p_output; +#if NKSTAT > 0 + aggr_port_kstat_detach(p); +#endif + TAILQ_REMOVE(&sc->sc_ports, p, p_entry); sc->sc_nports--; @@ -1457,7 +1513,6 @@ aggr_p_dtor(struct aggr_softc *sc, struc if_detachhook_del(ifp0, &p->p_dhook); if_linkstatehook_del(ifp0, &p->p_lhook); - if_put(ifp0); free(p, M_DEVBUF, sizeof(*p)); @@ -1754,6 +1809,11 @@ aggr_marker_response(struct aggr_port *p memcpy(eh->ether_shost, ac->ac_enaddr, sizeof(eh->ether_shost)); eh->ether_type = htons(ETHERTYPE_SLOW); + mtx_enter(&p->p_mtx); + p->p_proto_counts[AGGR_PROTO_TX_MARKER].c_pkts++; + p->p_proto_counts[AGGR_PROTO_TX_MARKER].c_bytes += m->m_pkthdr.len; + mtx_leave(&p->p_mtx); + (void)if_enqueue(ifp0, m); } @@ -1786,7 +1846,10 @@ aggr_rx(void *arg) struct mbuf_list ml; struct mbuf *m; - mq_delist(&p->p_rxm_mq, &ml); + mtx_enter(&p->p_mtx); + ml = p->p_rxm_ml; + ml_init(&p->p_rxm_ml); + mtx_leave(&p->p_mtx); while ((m = ml_dequeue(&ml)) != NULL) { struct ether_slowproto_hdr *sph; @@ -1820,7 +1883,16 @@ aggr_set_selected(struct aggr_port *p, e sc->sc_if.if_xname, p->p_ifp0->if_xname, aggr_port_selected_names[p->p_selected], aggr_port_selected_names[s]); + + /* + * setting p_selected doesnt need the mtx except to + * coordinate with a kstat read. + */ + + mtx_enter(&p->p_mtx); p->p_selected = s; + p->p_nselectch++; + mtx_leave(&p->p_mtx); } aggr_mux(sc, p, ev); } @@ -2736,6 +2808,11 @@ aggr_ntt_transmit(struct aggr_port *p) lacpdu->lacp_terminator.lacp_tlv_type = LACP_T_TERMINATOR; lacpdu->lacp_terminator.lacp_tlv_length = 0; + mtx_enter(&p->p_mtx); + p->p_proto_counts[AGGR_PROTO_TX_LACP].c_pkts++; + p->p_proto_counts[AGGR_PROTO_TX_LACP].c_bytes += m->m_pkthdr.len; + mtx_leave(&p->p_mtx); + (void)if_enqueue(ifp0, m); } @@ -2919,3 +2996,129 @@ aggr_multi_del(struct aggr_softc *sc, st return (0); } + +#if NKSTAT > 0 +static const char *aggr_proto_names[AGGR_PROTO_COUNT] = { + [AGGR_PROTO_TX_LACP] = "tx-lacp", + [AGGR_PROTO_TX_MARKER] = "tx-marker", + [AGGR_PROTO_RX_LACP] = "rx-lacp", + [AGGR_PROTO_RX_MARKER] = "rx-marker", +}; + +struct aggr_port_kstat { + struct kstat_kv interface; + + struct { + struct kstat_kv pkts; + struct kstat_kv bytes; + } protos[AGGR_PROTO_COUNT]; + + struct kstat_kv rx_drops; + + struct kstat_kv selected; + struct kstat_kv nselectch; +}; + +static int +aggr_port_kstat_read(struct kstat *ks) +{ + struct aggr_port *p = ks->ks_softc; + struct aggr_port_kstat *pk = ks->ks_data; + unsigned int proto; + + mtx_enter(&p->p_mtx); + for (proto = 0; proto < AGGR_PROTO_COUNT; proto++) { + kstat_kv_u64(&pk->protos[proto].pkts) = + p->p_proto_counts[proto].c_pkts; + kstat_kv_u64(&pk->protos[proto].bytes) = + p->p_proto_counts[proto].c_bytes; + } + kstat_kv_u64(&pk->rx_drops) = p->p_rx_drops; + + kstat_kv_bool(&pk->selected) = p->p_selected == AGGR_PORT_SELECTED; + kstat_kv_u32(&pk->nselectch) = p->p_nselectch; + mtx_leave(&p->p_mtx); + + nanouptime(&ks->ks_updated); + + return (0); +} + +static void +aggr_port_kstat_attach(struct aggr_port *p) +{ + struct aggr_softc *sc = p->p_aggr; + struct ifnet *ifp = &sc->sc_if; + struct ifnet *ifp0 = p->p_ifp0; + struct kstat *ks; + struct aggr_port_kstat *pk; + unsigned int proto; + + pk = malloc(sizeof(*pk), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO); + if (pk == NULL) { + log(LOG_WARNING, "%s %s: unable to allocate aggr-port kstat\n", + ifp->if_xname, ifp0->if_xname); + return; + } + + ks = kstat_create(ifp->if_xname, 0, "aggr-port", ifp0->if_index, + KSTAT_T_KV, 0); + if (ks == NULL) { + log(LOG_WARNING, "%s %s: unable to create aggr-port kstat\n", + ifp->if_xname, ifp0->if_xname); + free(pk, M_DEVBUF, sizeof(*pk)); + return; + } + + kstat_kv_init(&pk->interface, "interface", KSTAT_KV_T_ISTR); + strlcpy(kstat_kv_istr(&pk->interface), ifp0->if_xname, + sizeof(kstat_kv_istr(&pk->interface))); + + for (proto = 0; proto < AGGR_PROTO_COUNT; proto++) { + char kvname[KSTAT_KV_NAMELEN]; + + snprintf(kvname, sizeof(kvname), + "%s-pkts", aggr_proto_names[proto]); + kstat_kv_unit_init(&pk->protos[proto].pkts, + kvname, KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS); + + snprintf(kvname, sizeof(kvname), + "%s-bytes", aggr_proto_names[proto]); + kstat_kv_unit_init(&pk->protos[proto].bytes, + kvname, KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES); + } + + kstat_kv_unit_init(&pk->rx_drops, "rx-drops", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS); + + kstat_kv_init(&pk->selected, "selected", KSTAT_KV_T_BOOL); + kstat_kv_init(&pk->nselectch, "select-changes", KSTAT_KV_T_COUNTER32); + + ks->ks_softc = p; + ks->ks_data = pk; + ks->ks_datalen = sizeof(*pk); + ks->ks_read = aggr_port_kstat_read; + + kstat_install(ks); + + p->p_kstat = ks; +} + +static void +aggr_port_kstat_detach(struct aggr_port *p) +{ + struct kstat *ks = p->p_kstat; + struct aggr_port_kstat *pk; + + if (ks == NULL) + return; + + p->p_kstat = NULL; + + kstat_remove(ks); + pk = ks->ks_data; + kstat_destroy(ks); + + free(pk, M_DEVBUF, sizeof(*pk)); +} +#endif