summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/ieee8023ad_lacp.c81
-rw-r--r--sys/net/if.c9
-rw-r--r--sys/net/if_arp.h30
-rw-r--r--sys/net/if_gif.c14
-rw-r--r--sys/net/if_lagg.c30
-rw-r--r--sys/net/if_lagg.h5
-rw-r--r--sys/net/if_var.h2
-rw-r--r--sys/net/rtsock.c15
-rw-r--r--sys/net/vnet.h50
9 files changed, 200 insertions, 36 deletions
diff --git a/sys/net/ieee8023ad_lacp.c b/sys/net/ieee8023ad_lacp.c
index 87c4e9b..a2d42e0 100644
--- a/sys/net/ieee8023ad_lacp.c
+++ b/sys/net/ieee8023ad_lacp.c
@@ -188,29 +188,43 @@ static void lacp_dprintf(const struct lacp_port *, const char *, ...)
__attribute__((__format__(__printf__, 2, 3)));
static int lacp_debug = 0;
-SYSCTL_INT(_net, OID_AUTO, lacp_debug, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_NODE(_net_link_lagg, OID_AUTO, lacp, CTLFLAG_RD, 0, "ieee802.3ad");
+SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN,
&lacp_debug, 0, "Enable LACP debug logging (1=debug, 2=trace)");
-TUNABLE_INT("net.lacp_debug", &lacp_debug);
+TUNABLE_INT("net.link.lagg.lacp.debug", &lacp_debug);
-#define LACP_DPRINTF(a) if (lacp_debug > 0) { lacp_dprintf a ; }
-#define LACP_TRACE(a) if (lacp_debug > 1) { lacp_dprintf(a,"%s\n",__func__); }
+/* bitmap of ports */
+static int lacp_rx_test = 0;
+static int lacp_tx_test = 0;
+SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, rxtest, CTLFLAG_RW, &lacp_rx_test, 0,
+ "RXTest");
+SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, txtest, CTLFLAG_RW, &lacp_tx_test, 0,
+ "TXTest");
+
+static int lacp_strict = 1;
+SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, strict, CTLFLAG_RW, &lacp_strict,
+ 0, "Strict spec compliance");
+
+#define LACP_DPRINTF(a) if (lacp_debug & 0x01) { lacp_dprintf a ; }
+#define LACP_TRACE(a) if (lacp_debug & 0x02) { lacp_dprintf(a,"%s\n",__func__); }
+#define LACP_TPRINTF(a) if (lacp_debug & 0x04) { lacp_dprintf a ; }
/*
* partner administration variables.
* XXX should be configurable.
*/
-static const struct lacp_peerinfo lacp_partner_admin = {
+static const struct lacp_peerinfo lacp_partner_admin_optimistic = {
.lip_systemid = { .lsi_prio = 0xffff },
.lip_portid = { .lpi_prio = 0xffff },
-#if 1
- /* optimistic */
.lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION |
LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING,
-#else
- /* pessimistic */
+};
+
+static const struct lacp_peerinfo lacp_partner_admin_strict = {
+ .lip_systemid = { .lsi_prio = 0xffff },
+ .lip_portid = { .lpi_prio = 0xffff },
.lip_state = 0,
-#endif
};
static const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = {
@@ -301,6 +315,11 @@ lacp_pdu_input(struct lacp_port *lp, struct mbuf *m)
lacp_dump_lacpdu(du);
}
+ if ((1 << lp->lp_ifp->if_dunit) & lacp_rx_test) {
+ LACP_TPRINTF((lp, "Dropping RX PDU\n"));
+ goto bad;
+ }
+
LACP_LOCK(lsc);
lacp_sm_rx(lp, du);
LACP_UNLOCK(lsc);
@@ -653,6 +672,7 @@ lacp_disable_distributing(struct lacp_port *lp)
{
struct lacp_aggregator *la = lp->lp_aggregator;
struct lacp_softc *lsc = lp->lp_lsc;
+ struct lagg_softc *sc = lsc->lsc_softc;
char buf[LACP_LAGIDSTR_MAX+1];
LACP_LOCK_ASSERT(lsc);
@@ -672,6 +692,7 @@ lacp_disable_distributing(struct lacp_port *lp)
TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q);
la->la_nports--;
+ sc->sc_active = la->la_nports;
if (lsc->lsc_active_aggregator == la) {
lacp_suppress_distributing(lsc, la);
@@ -688,6 +709,7 @@ lacp_enable_distributing(struct lacp_port *lp)
{
struct lacp_aggregator *la = lp->lp_aggregator;
struct lacp_softc *lsc = lp->lp_lsc;
+ struct lagg_softc *sc = lsc->lsc_softc;
char buf[LACP_LAGIDSTR_MAX+1];
LACP_LOCK_ASSERT(lsc);
@@ -704,6 +726,7 @@ lacp_enable_distributing(struct lacp_port *lp)
KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid"));
TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q);
la->la_nports++;
+ sc->sc_active = la->la_nports;
lp->lp_state |= LACP_STATE_DISTRIBUTING;
@@ -908,7 +931,6 @@ lacp_aggregator_bandwidth(struct lacp_aggregator *la)
static void
lacp_select_active_aggregator(struct lacp_softc *lsc)
{
- struct lagg_softc *sc = lsc->lsc_softc;
struct lacp_aggregator *la;
struct lacp_aggregator *best_la = NULL;
uint64_t best_speed = 0;
@@ -960,7 +982,6 @@ lacp_select_active_aggregator(struct lacp_softc *lsc)
lacp_format_lagid_aggregator(best_la, buf, sizeof(buf))));
if (lsc->lsc_active_aggregator != best_la) {
- sc->sc_ifp->if_baudrate = best_speed;
lsc->lsc_active_aggregator = best_la;
lacp_update_portmap(lsc);
if (best_la) {
@@ -976,15 +997,18 @@ lacp_select_active_aggregator(struct lacp_softc *lsc)
static void
lacp_update_portmap(struct lacp_softc *lsc)
{
+ struct lagg_softc *sc = lsc->lsc_softc;
struct lacp_aggregator *la;
struct lacp_portmap *p;
struct lacp_port *lp;
+ uint64_t speed;
u_int newmap;
int i;
newmap = lsc->lsc_activemap == 0 ? 1 : 0;
p = &lsc->lsc_pmap[newmap];
la = lsc->lsc_active_aggregator;
+ speed = 0;
bzero(p, sizeof(struct lacp_portmap));
if (la != NULL && la->la_nports > 0) {
@@ -993,7 +1017,9 @@ lacp_update_portmap(struct lacp_softc *lsc)
TAILQ_FOREACH(lp, &la->la_ports, lp_dist_q)
p->pm_map[i++] = lp;
KASSERT(i == p->pm_count, ("Invalid port count"));
+ speed = lacp_aggregator_bandwidth(la);
}
+ sc->sc_ifp->if_baudrate = speed;
/* switch the active portmap over */
atomic_store_rel_int(&lsc->lsc_activemap, newmap);
@@ -1264,6 +1290,8 @@ lacp_unselect(struct lacp_port *lp)
static void
lacp_sm_mux(struct lacp_port *lp)
{
+ struct lagg_port *lgp = lp->lp_lagg;
+ struct lagg_softc *sc = lgp->lp_softc;
enum lacp_mux_state new_state;
boolean_t p_sync =
(lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0;
@@ -1273,7 +1301,9 @@ lacp_sm_mux(struct lacp_port *lp)
struct lacp_aggregator *la;
if (lacp_debug > 1)
- lacp_dprintf(lp, "%s: state %d\n", __func__, lp->lp_mux_state);
+ lacp_dprintf(lp, "%s: state= 0x%x, selected= 0x%x, "
+ "p_sync= 0x%x, p_collecting= 0x%x\n", __func__,
+ lp->lp_mux_state, selected, p_sync, p_collecting);
re_eval:
la = lp->lp_aggregator;
@@ -1313,6 +1343,8 @@ re_eval:
case LACP_MUX_DISTRIBUTING:
if (selected != LACP_SELECTED || !p_sync || !p_collecting) {
new_state = LACP_MUX_COLLECTING;
+ lacp_dprintf(lp, "Interface stopped DISTRIBUTING, possible flaping\n");
+ sc->sc_flapping++;
}
break;
default:
@@ -1561,6 +1593,10 @@ lacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du)
sizeof(buf))));
}
+ /* XXX Hack, still need to implement 5.4.9 para 2,3,4 */
+ if (lacp_strict)
+ lp->lp_partner.lip_state |= LACP_STATE_SYNC;
+
lacp_sm_ptx_update_timeout(lp, oldpstate);
}
@@ -1586,7 +1622,10 @@ lacp_sm_rx_record_default(struct lacp_port *lp)
LACP_TRACE(lp);
oldpstate = lp->lp_partner.lip_state;
- lp->lp_partner = lacp_partner_admin;
+ if (lacp_strict)
+ lp->lp_partner = lacp_partner_admin_strict;
+ else
+ lp->lp_partner = lacp_partner_admin_optimistic;;
lp->lp_state |= LACP_STATE_DEFAULTED;
lacp_sm_ptx_update_timeout(lp, oldpstate);
}
@@ -1621,7 +1660,12 @@ lacp_sm_rx_update_default_selected(struct lacp_port *lp)
LACP_TRACE(lp);
- lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin);
+ if (lacp_strict)
+ lacp_sm_rx_update_selected_from_peerinfo(lp,
+ &lacp_partner_admin_strict);
+ else
+ lacp_sm_rx_update_selected_from_peerinfo(lp,
+ &lacp_partner_admin_optimistic);
}
/* transmit machine */
@@ -1629,7 +1673,7 @@ lacp_sm_rx_update_default_selected(struct lacp_port *lp)
static void
lacp_sm_tx(struct lacp_port *lp)
{
- int error;
+ int error = 0;
if (!(lp->lp_state & LACP_STATE_AGGREGATION)
#if 1
@@ -1651,7 +1695,10 @@ lacp_sm_tx(struct lacp_port *lp)
return;
}
- error = lacp_xmit_lacpdu(lp);
+ if (((1 << lp->lp_ifp->if_dunit) & lacp_tx_test) == 0)
+ error = lacp_xmit_lacpdu(lp);
+ else
+ LACP_TPRINTF((lp, "Dropping TX PDU\n"));
if (error == 0) {
lp->lp_flags &= ~LACP_PORT_NTT;
diff --git a/sys/net/if.c b/sys/net/if.c
index 326860d..2cb3da0 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -505,6 +505,7 @@ if_free(struct ifnet *ifp)
ifp->if_flags |= IFF_DYING; /* XXX: Locking */
+ CURVNET_SET_QUIET(ifp->if_vnet);
IFNET_WLOCK();
KASSERT(ifp == ifnet_byindex_locked(ifp->if_index),
("%s: freeing unallocated ifnet", ifp->if_xname));
@@ -512,9 +513,9 @@ if_free(struct ifnet *ifp)
ifindex_free_locked(ifp->if_index);
IFNET_WUNLOCK();
- if (!refcount_release(&ifp->if_refcount))
- return;
- if_free_internal(ifp);
+ if (refcount_release(&ifp->if_refcount))
+ if_free_internal(ifp);
+ CURVNET_RESTORE();
}
/*
@@ -803,7 +804,9 @@ void
if_detach(struct ifnet *ifp)
{
+ CURVNET_SET_QUIET(ifp->if_vnet);
if_detach_internal(ifp, 0);
+ CURVNET_RESTORE();
}
static void
diff --git a/sys/net/if_arp.h b/sys/net/if_arp.h
index 38c6402..2dd8c32 100644
--- a/sys/net/if_arp.h
+++ b/sys/net/if_arp.h
@@ -113,27 +113,35 @@ struct arpcom {
struct arpstat {
/* Normal things that happen: */
- u_long txrequests; /* # of ARP requests sent by this host. */
- u_long txreplies; /* # of ARP replies sent by this host. */
- u_long rxrequests; /* # of ARP requests received by this host. */
- u_long rxreplies; /* # of ARP replies received by this host. */
- u_long received; /* # of ARP packets received by this host. */
+ uint64_t txrequests; /* # of ARP requests sent by this host. */
+ uint64_t txreplies; /* # of ARP replies sent by this host. */
+ uint64_t rxrequests; /* # of ARP requests received by this host. */
+ uint64_t rxreplies; /* # of ARP replies received by this host. */
+ uint64_t received; /* # of ARP packets received by this host. */
- u_long arp_spares[4]; /* For either the upper or lower half. */
+ uint64_t arp_spares[4]; /* For either the upper or lower half. */
/* Abnormal event and error counting: */
- u_long dropped; /* # of packets dropped waiting for a reply. */
- u_long timeouts; /* # of times with entries removed */
+ uint64_t dropped; /* # of packets dropped waiting for a reply. */
+ uint64_t timeouts; /* # of times with entries removed */
/* due to timeout. */
- u_long dupips; /* # of duplicate IPs detected. */
+ uint64_t dupips; /* # of duplicate IPs detected. */
};
+#ifdef _KERNEL
+#include <sys/counter.h>
+#include <net/vnet.h>
+
+VNET_PCPUSTAT_DECLARE(struct arpstat, arpstat);
/*
* In-kernel consumers can use these accessor macros directly to update
* stats.
*/
-#define ARPSTAT_ADD(name, val) V_arpstat.name += (val)
-#define ARPSTAT_SUB(name, val) V_arpstat.name -= (val)
+#define ARPSTAT_ADD(name, val) \
+ VNET_PCPUSTAT_ADD(struct arpstat, arpstat, name, (val))
+#define ARPSTAT_SUB(name, val) ARPSTAT_ADD(name, -(val))
#define ARPSTAT_INC(name) ARPSTAT_ADD(name, 1)
#define ARPSTAT_DEC(name) ARPSTAT_SUB(name, 1)
+#endif /* _KERNEL */
+
#endif /* !_NET_IF_ARP_H_ */
diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c
index 2a6f890..874bbc00 100644
--- a/sys/net/if_gif.c
+++ b/sys/net/if_gif.c
@@ -173,7 +173,7 @@ gif_clone_create(ifc, unit, params)
if_initname(GIF2IFP(sc), gifname, unit);
sc->encap_cookie4 = sc->encap_cookie6 = NULL;
- sc->gif_options = GIF_ACCEPT_REVETHIP;
+ sc->gif_options = 0;
GIF2IFP(sc)->if_addrlen = 0;
GIF2IFP(sc)->if_mtu = GIF_MTU;
@@ -437,6 +437,11 @@ gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
goto end;
}
#endif
+ if ((ifp->if_flags & IFF_MONITOR) != 0) {
+ error = ENETDOWN;
+ m_freem(m);
+ goto end;
+ }
/*
* gif may cause infinite recursion calls when misconfigured.
@@ -551,6 +556,13 @@ gif_input(m, af, ifp)
bpf_mtap2(ifp->if_bpf, &af1, sizeof(af1), m);
}
+ if ((ifp->if_flags & IFF_MONITOR) != 0) {
+ ifp->if_ipackets++;
+ ifp->if_ibytes += m->m_pkthdr.len;
+ m_freem(m);
+ return;
+ }
+
if (ng_gif_input_p != NULL) {
(*ng_gif_input_p)(ifp, &m, af);
if (m == NULL)
diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
index 9867c97..bc2127c 100644
--- a/sys/net/if_lagg.c
+++ b/sys/net/if_lagg.c
@@ -122,6 +122,7 @@ static void lagg_media_status(struct ifnet *, struct ifmediareq *);
static struct lagg_port *lagg_link_active(struct lagg_softc *,
struct lagg_port *);
static const void *lagg_gethdr(struct mbuf *, u_int, u_int, void *);
+static int lagg_sysctl_active(SYSCTL_HANDLER_ARGS);
/* Simple round robin */
static int lagg_rr_attach(struct lagg_softc *);
@@ -171,7 +172,7 @@ static const struct {
};
SYSCTL_DECL(_net_link);
-static SYSCTL_NODE(_net_link, OID_AUTO, lagg, CTLFLAG_RW, 0,
+SYSCTL_NODE(_net_link, OID_AUTO, lagg, CTLFLAG_RW, 0,
"Link Aggregation");
static int lagg_failover_rx_all = 0; /* Allow input on any failover links */
@@ -298,6 +299,12 @@ lagg_clone_create(struct if_clone *ifc, int unit, caddr_t params)
SYSCTL_ADD_INT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
"count", CTLTYPE_INT|CTLFLAG_RD, &sc->sc_count, sc->sc_count,
"Total number of ports");
+ SYSCTL_ADD_PROC(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "active", CTLTYPE_INT|CTLFLAG_RD, sc, 0, lagg_sysctl_active,
+ "I", "Total number of active ports");
+ SYSCTL_ADD_INT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "flapping", CTLTYPE_INT|CTLFLAG_RD, &sc->sc_flapping,
+ sc->sc_flapping, "Total number of port change events");
/* Hash all layers by default */
sc->sc_flags = LAGG_F_HASHL2|LAGG_F_HASHL3|LAGG_F_HASHL4;
@@ -1488,6 +1495,27 @@ lagg_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
return (mtod(m, char *) + off);
}
+static int
+lagg_sysctl_active(SYSCTL_HANDLER_ARGS)
+{
+ struct lagg_softc *sc = (struct lagg_softc *)arg1;
+ struct lagg_port *lp;
+ int error;
+
+ /* LACP tracks active links automatically, the others do not */
+ if (sc->sc_proto != LAGG_PROTO_LACP) {
+ sc->sc_active = 0;
+ SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
+ sc->sc_active += LAGG_PORTACTIVE(lp);
+ }
+
+ error = sysctl_handle_int(oidp, &sc->sc_active, 0, req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ return (0);
+}
+
uint32_t
lagg_hashmbuf(struct lagg_softc *sc, struct mbuf *m, uint32_t key)
{
diff --git a/sys/net/if_lagg.h b/sys/net/if_lagg.h
index fe3365b..cbec8d6 100644
--- a/sys/net/if_lagg.h
+++ b/sys/net/if_lagg.h
@@ -190,6 +190,9 @@ struct lagg_softc {
struct rwlock sc_mtx;
int sc_proto; /* lagg protocol */
u_int sc_count; /* number of ports */
+ u_int sc_active; /* active port count */
+ u_int sc_flapping; /* number of flapping
+ * events */
struct lagg_port *sc_primary; /* primary port */
struct ifmedia sc_media; /* media config */
caddr_t sc_psc; /* protocol data */
@@ -266,6 +269,8 @@ extern void (*lagg_linkstate_p)(struct ifnet *, int );
int lagg_enqueue(struct ifnet *, struct mbuf *);
uint32_t lagg_hashmbuf(struct lagg_softc *, struct mbuf *, uint32_t);
+SYSCTL_DECL(_net_link_lagg);
+
#endif /* _KERNEL */
#endif /* _NET_LAGG_H */
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index 3babc22..3288a4f 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -204,7 +204,7 @@ struct ifnet {
u_int if_fib; /* interface FIB */
u_char if_alloctype; /* if_type at time of allocation */
- u_int if_hw_tsomax; /* tso burst length limit, the minmum
+ u_int if_hw_tsomax; /* tso burst length limit, the minimum
* is (IP_MAXPACKET / 8).
* XXXAO: Have to find a better place
* for it eventually. */
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
index d665280..928d2ab 100644
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -1905,6 +1905,7 @@ sysctl_rtsock(SYSCTL_HANDLER_ARGS)
u_int namelen = arg2;
struct radix_node_head *rnh = NULL; /* silence compiler. */
int i, lim, error = EINVAL;
+ int fib = 0;
u_char af;
struct walkarg w;
@@ -1912,7 +1913,17 @@ sysctl_rtsock(SYSCTL_HANDLER_ARGS)
namelen--;
if (req->newptr)
return (EPERM);
- if (namelen != 3)
+ if (name[1] == NET_RT_DUMP) {
+ if (namelen == 3)
+ fib = req->td->td_proc->p_fibnum;
+ else if (namelen == 4)
+ fib = (name[3] == -1) ?
+ req->td->td_proc->p_fibnum : name[3];
+ else
+ return ((namelen < 3) ? EISDIR : ENOTDIR);
+ if (fib < 0 || fib >= rt_numfibs)
+ return (EINVAL);
+ } else if (namelen != 3)
return ((namelen < 3) ? EISDIR : ENOTDIR);
af = name[0];
if (af > AF_MAX)
@@ -1951,7 +1962,7 @@ sysctl_rtsock(SYSCTL_HANDLER_ARGS)
* take care of routing entries
*/
for (error = 0; error == 0 && i <= lim; i++) {
- rnh = rt_tables_get_rnh(req->td->td_proc->p_fibnum, i);
+ rnh = rt_tables_get_rnh(fib, i);
if (rnh != NULL) {
RADIX_NODE_HEAD_RLOCK(rnh);
error = rnh->rnh_walktree(rnh,
diff --git a/sys/net/vnet.h b/sys/net/vnet.h
index 6bdf163..4e9de11 100644
--- a/sys/net/vnet.h
+++ b/sys/net/vnet.h
@@ -85,6 +85,56 @@ struct vnet {
#ifdef _KERNEL
+#define VNET_PCPUSTAT_DECLARE(type, name) \
+ VNET_DECLARE(counter_u64_t, name[sizeof(type) / sizeof(uint64_t)])
+
+#define VNET_PCPUSTAT_DEFINE(type, name) \
+ VNET_DEFINE(counter_u64_t, name[sizeof(type) / sizeof(uint64_t)])
+
+#define VNET_PCPUSTAT_ALLOC(name, wait) \
+ COUNTER_ARRAY_ALLOC(VNET(name), \
+ sizeof(VNET(name)) / sizeof(counter_u64_t), (wait))
+
+#define VNET_PCPUSTAT_FREE(name) \
+ COUNTER_ARRAY_FREE(VNET(name), sizeof(VNET(name)) / sizeof(counter_u64_t))
+
+#define VNET_PCPUSTAT_ADD(type, name, f, v) \
+ counter_u64_add(VNET(name)[offsetof(type, f) / sizeof(uint64_t)], (v))
+
+#define VNET_PCPUSTAT_SYSINIT(name) \
+static void \
+vnet_##name##_init(const void *unused) \
+{ \
+ VNET_PCPUSTAT_ALLOC(name, M_WAITOK); \
+} \
+VNET_SYSINIT(vnet_ ## name ## _init, SI_SUB_PROTO_IFATTACHDOMAIN, \
+ SI_ORDER_ANY, vnet_ ## name ## _init, NULL)
+
+#define VNET_PCPUSTAT_SYSUNINIT(name) \
+static void \
+vnet_##name##_uninit(const void *unused) \
+{ \
+ VNET_PCPUSTAT_FREE(name); \
+} \
+VNET_SYSUNINIT(vnet_ ## name ## _uninit, SI_SUB_PROTO_IFATTACHDOMAIN, \
+ SI_ORDER_ANY, vnet_ ## name ## _uninit, NULL)
+
+#define SYSCTL_VNET_PCPUSTAT(parent, nbr, name, type, array, desc) \
+static int \
+array##_sysctl(SYSCTL_HANDLER_ARGS) \
+{ \
+ type s; \
+ CTASSERT((sizeof(type) / sizeof(uint64_t)) == \
+ (sizeof(VNET(array)) / sizeof(counter_u64_t))); \
+ COUNTER_ARRAY_COPY(VNET(array), &s, sizeof(type) / sizeof(uint64_t));\
+ if (req->newptr) \
+ COUNTER_ARRAY_ZERO(VNET(array), \
+ sizeof(type) / sizeof(uint64_t)); \
+ return (SYSCTL_OUT(req, &s, sizeof(type))); \
+} \
+SYSCTL_VNET_PROC(parent, nbr, name, CTLTYPE_OPAQUE | CTLFLAG_RW, NULL, \
+ 0, array ## _sysctl, "I", desc)
+
#ifdef VIMAGE
#include <sys/lock.h>
#include <sys/proc.h> /* for struct thread */
OpenPOWER on IntegriCloud