xref: /freebsd/usr.bin/netstat/netisr.c (revision ade9ccfe211ac8b27c58a4dc11a6cd1d33767ce0)
10153eb66SRobert Watson /*-
27ff67a20SRobert Watson  * Copyright (c) 2010-2011 Juniper Networks, Inc.
30153eb66SRobert Watson  * All rights reserved.
40153eb66SRobert Watson  *
50153eb66SRobert Watson  * This software was developed by Robert N. M. Watson under contract
60153eb66SRobert Watson  * to Juniper Networks, Inc.
70153eb66SRobert Watson  *
80153eb66SRobert Watson  * Redistribution and use in source and binary forms, with or without
90153eb66SRobert Watson  * modification, are permitted provided that the following conditions
100153eb66SRobert Watson  * are met:
110153eb66SRobert Watson  * 1. Redistributions of source code must retain the above copyright
120153eb66SRobert Watson  *    notice, this list of conditions and the following disclaimer.
130153eb66SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
140153eb66SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
150153eb66SRobert Watson  *    documentation and/or other materials provided with the distribution.
160153eb66SRobert Watson  *
170153eb66SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
180153eb66SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
190153eb66SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
200153eb66SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
210153eb66SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
220153eb66SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
230153eb66SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
240153eb66SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
250153eb66SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
260153eb66SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
270153eb66SRobert Watson  * SUCH DAMAGE.
280153eb66SRobert Watson  */
290153eb66SRobert Watson 
300153eb66SRobert Watson #include <sys/cdefs.h>
310153eb66SRobert Watson 
320153eb66SRobert Watson __FBSDID("$FreeBSD$");
330153eb66SRobert Watson 
3488737be2SRobert Watson #include <sys/param.h>
350153eb66SRobert Watson #include <sys/sysctl.h>
360153eb66SRobert Watson 
3788737be2SRobert Watson #include <sys/_lock.h>
3888737be2SRobert Watson #include <sys/_mutex.h>
3988737be2SRobert Watson 
4088737be2SRobert Watson #define	_WANT_NETISR_INTERNAL
410153eb66SRobert Watson #include <net/netisr.h>
4288737be2SRobert Watson #include <net/netisr_internal.h>
430153eb66SRobert Watson 
440153eb66SRobert Watson #include <err.h>
4588737be2SRobert Watson #include <kvm.h>
460153eb66SRobert Watson #include <stdint.h>
470153eb66SRobert Watson #include <stdio.h>
480153eb66SRobert Watson #include <stdlib.h>
49*ade9ccfeSMarcel Moolenaar #include <stdbool.h>
5088737be2SRobert Watson #include <string.h>
51*ade9ccfeSMarcel Moolenaar #include <libxo/xo.h>
520153eb66SRobert Watson #include "netstat.h"
530153eb66SRobert Watson 
540153eb66SRobert Watson /*
550153eb66SRobert Watson  * Print statistics for the kernel netisr subsystem.
560153eb66SRobert Watson  */
570153eb66SRobert Watson static u_int				 bindthreads;
580153eb66SRobert Watson static u_int				 maxthreads;
590153eb66SRobert Watson static u_int				 numthreads;
600153eb66SRobert Watson 
610153eb66SRobert Watson static u_int				 defaultqlimit;
620153eb66SRobert Watson static u_int				 maxqlimit;
630153eb66SRobert Watson 
648f092df0SRobert Watson static char				 dispatch_policy[20];
650153eb66SRobert Watson 
660153eb66SRobert Watson static struct sysctl_netisr_proto	*proto_array;
670153eb66SRobert Watson static u_int				 proto_array_len;
680153eb66SRobert Watson 
690153eb66SRobert Watson static struct sysctl_netisr_workstream	*workstream_array;
700153eb66SRobert Watson static u_int				 workstream_array_len;
710153eb66SRobert Watson 
720153eb66SRobert Watson static struct sysctl_netisr_work	*work_array;
730153eb66SRobert Watson static u_int				 work_array_len;
740153eb66SRobert Watson 
7588737be2SRobert Watson static u_int				*nws_array;
7688737be2SRobert Watson 
7788737be2SRobert Watson static u_int				 maxprot;
7888737be2SRobert Watson 
7988737be2SRobert Watson static void
80321ae07fSPhilippe Charnier netisr_dispatch_policy_to_string(u_int policy, char *buf,
818f092df0SRobert Watson     size_t buflen)
828f092df0SRobert Watson {
838f092df0SRobert Watson 	const char *str;
848f092df0SRobert Watson 
85321ae07fSPhilippe Charnier 	switch (policy) {
868f092df0SRobert Watson 	case NETISR_DISPATCH_DEFAULT:
878f092df0SRobert Watson 		str = "default";
888f092df0SRobert Watson 		break;
898f092df0SRobert Watson 	case NETISR_DISPATCH_DEFERRED:
908f092df0SRobert Watson 		str = "deferred";
918f092df0SRobert Watson 		break;
928f092df0SRobert Watson 	case NETISR_DISPATCH_HYBRID:
938f092df0SRobert Watson 		str = "hybrid";
948f092df0SRobert Watson 		break;
958f092df0SRobert Watson 	case NETISR_DISPATCH_DIRECT:
968f092df0SRobert Watson 		str = "direct";
978f092df0SRobert Watson 		break;
988f092df0SRobert Watson 	default:
998f092df0SRobert Watson 		str = "unknown";
1008f092df0SRobert Watson 		break;
1018f092df0SRobert Watson 	}
1028f092df0SRobert Watson 	snprintf(buf, buflen, "%s", str);
1038f092df0SRobert Watson }
1048f092df0SRobert Watson 
1058f092df0SRobert Watson static void
106321ae07fSPhilippe Charnier netisr_load_kvm_uint(kvm_t *kd, const char *name, u_int *p)
10788737be2SRobert Watson {
10888737be2SRobert Watson 	struct nlist nl[] = {
10988737be2SRobert Watson 		{ .n_name = name },
11088737be2SRobert Watson 		{ .n_name = NULL },
11188737be2SRobert Watson 	};
11288737be2SRobert Watson 	int ret;
11388737be2SRobert Watson 
11488737be2SRobert Watson 	ret = kvm_nlist(kd, nl);
11588737be2SRobert Watson 	if (ret < 0)
116*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_nlist(%s): %s", __func__, name,
11788737be2SRobert Watson 		    kvm_geterr(kd));
11888737be2SRobert Watson 	if (ret != 0)
119*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_nlist(%s): unresolved symbol", __func__,
12088737be2SRobert Watson 		    name);
12188737be2SRobert Watson 	if (kvm_read(kd, nl[0].n_value, p, sizeof(*p)) != sizeof(*p))
122*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_read(%s): %s", __func__, name,
12388737be2SRobert Watson 		    kvm_geterr(kd));
12488737be2SRobert Watson }
12588737be2SRobert Watson 
12688737be2SRobert Watson /*
12788737be2SRobert Watson  * Load a nul-terminated string from KVM up to 'limit', guarantee that the
12888737be2SRobert Watson  * string in local memory is nul-terminated.
12988737be2SRobert Watson  */
13088737be2SRobert Watson static void
13188737be2SRobert Watson netisr_load_kvm_string(kvm_t *kd, uintptr_t addr, char *dest, u_int limit)
13288737be2SRobert Watson {
13388737be2SRobert Watson 	u_int i;
13488737be2SRobert Watson 
13588737be2SRobert Watson 	for (i = 0; i < limit; i++) {
13688737be2SRobert Watson 		if (kvm_read(kd, addr + i, &dest[i], sizeof(dest[i])) !=
13788737be2SRobert Watson 		    sizeof(dest[i]))
138*ade9ccfeSMarcel Moolenaar 			xo_err(-1, "%s: kvm_read: %s", __func__,
13988737be2SRobert Watson 			    kvm_geterr(kd));
14088737be2SRobert Watson 		if (dest[i] == '\0')
14188737be2SRobert Watson 			break;
14288737be2SRobert Watson 	}
14388737be2SRobert Watson 	dest[limit - 1] = '\0';
14488737be2SRobert Watson }
14588737be2SRobert Watson 
14688737be2SRobert Watson static const char *
14788737be2SRobert Watson netisr_proto2name(u_int proto)
14888737be2SRobert Watson {
14988737be2SRobert Watson 	u_int i;
15088737be2SRobert Watson 
15188737be2SRobert Watson 	for (i = 0; i < proto_array_len; i++) {
15288737be2SRobert Watson 		if (proto_array[i].snp_proto == proto)
15388737be2SRobert Watson 			return (proto_array[i].snp_name);
15488737be2SRobert Watson 	}
15588737be2SRobert Watson 	return ("unknown");
15688737be2SRobert Watson }
15788737be2SRobert Watson 
15888737be2SRobert Watson static int
15988737be2SRobert Watson netisr_protoispresent(u_int proto)
16088737be2SRobert Watson {
16188737be2SRobert Watson 	u_int i;
16288737be2SRobert Watson 
16388737be2SRobert Watson 	for (i = 0; i < proto_array_len; i++) {
16488737be2SRobert Watson 		if (proto_array[i].snp_proto == proto)
16588737be2SRobert Watson 			return (1);
16688737be2SRobert Watson 	}
16788737be2SRobert Watson 	return (0);
16888737be2SRobert Watson }
16988737be2SRobert Watson 
17088737be2SRobert Watson static void
17188737be2SRobert Watson netisr_load_kvm_config(kvm_t *kd)
17288737be2SRobert Watson {
1738f092df0SRobert Watson 	u_int tmp;
17488737be2SRobert Watson 
17588737be2SRobert Watson 	netisr_load_kvm_uint(kd, "_netisr_bindthreads", &bindthreads);
17688737be2SRobert Watson 	netisr_load_kvm_uint(kd, "_netisr_maxthreads", &maxthreads);
17788737be2SRobert Watson 	netisr_load_kvm_uint(kd, "_nws_count", &numthreads);
17888737be2SRobert Watson 
17988737be2SRobert Watson 	netisr_load_kvm_uint(kd, "_netisr_defaultqlimit", &defaultqlimit);
18088737be2SRobert Watson 	netisr_load_kvm_uint(kd, "_netisr_maxqlimit", &maxqlimit);
18188737be2SRobert Watson 
1828f092df0SRobert Watson 	netisr_load_kvm_uint(kd, "_netisr_dispatch_policy", &tmp);
1838f092df0SRobert Watson 	netisr_dispatch_policy_to_string(tmp, dispatch_policy,
1848f092df0SRobert Watson 	    sizeof(dispatch_policy));
18588737be2SRobert Watson }
18688737be2SRobert Watson 
1870153eb66SRobert Watson static void
1880153eb66SRobert Watson netisr_load_sysctl_uint(const char *name, u_int *p)
1890153eb66SRobert Watson {
1900153eb66SRobert Watson 	size_t retlen;
1910153eb66SRobert Watson 
1920153eb66SRobert Watson 	retlen = sizeof(u_int);
1930153eb66SRobert Watson 	if (sysctlbyname(name, p, &retlen, NULL, 0) < 0)
194*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "%s", name);
1950153eb66SRobert Watson 	if (retlen != sizeof(u_int))
196*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: invalid len %ju", name, (uintmax_t)retlen);
1970153eb66SRobert Watson }
1980153eb66SRobert Watson 
1990153eb66SRobert Watson static void
2008f092df0SRobert Watson netisr_load_sysctl_string(const char *name, char *p, size_t len)
2018f092df0SRobert Watson {
2028f092df0SRobert Watson 	size_t retlen;
2038f092df0SRobert Watson 
2048f092df0SRobert Watson 	retlen = len;
2058f092df0SRobert Watson 	if (sysctlbyname(name, p, &retlen, NULL, 0) < 0)
206*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "%s", name);
2078f092df0SRobert Watson 	p[len - 1] = '\0';
2088f092df0SRobert Watson }
2098f092df0SRobert Watson 
2108f092df0SRobert Watson static void
21188737be2SRobert Watson netisr_load_sysctl_config(void)
2120153eb66SRobert Watson {
2130153eb66SRobert Watson 
2140153eb66SRobert Watson 	netisr_load_sysctl_uint("net.isr.bindthreads", &bindthreads);
2150153eb66SRobert Watson 	netisr_load_sysctl_uint("net.isr.maxthreads", &maxthreads);
2160153eb66SRobert Watson 	netisr_load_sysctl_uint("net.isr.numthreads", &numthreads);
2170153eb66SRobert Watson 
2180153eb66SRobert Watson 	netisr_load_sysctl_uint("net.isr.defaultqlimit", &defaultqlimit);
2190153eb66SRobert Watson 	netisr_load_sysctl_uint("net.isr.maxqlimit", &maxqlimit);
2200153eb66SRobert Watson 
2218f092df0SRobert Watson 	netisr_load_sysctl_string("net.isr.dispatch", dispatch_policy,
2228f092df0SRobert Watson 	    sizeof(dispatch_policy));
2230153eb66SRobert Watson }
2240153eb66SRobert Watson 
2250153eb66SRobert Watson static void
22688737be2SRobert Watson netisr_load_kvm_proto(kvm_t *kd)
22788737be2SRobert Watson {
22888737be2SRobert Watson 	struct nlist nl[] = {
22988737be2SRobert Watson #define	NLIST_NETISR_PROTO	0
23088737be2SRobert Watson 		{ .n_name = "_netisr_proto" },
23188737be2SRobert Watson 		{ .n_name = NULL },
23288737be2SRobert Watson 	};
23388737be2SRobert Watson 	struct netisr_proto *np_array, *npp;
23488737be2SRobert Watson 	u_int i, protocount;
23588737be2SRobert Watson 	struct sysctl_netisr_proto *snpp;
23688737be2SRobert Watson 	size_t len;
23788737be2SRobert Watson 	int ret;
23888737be2SRobert Watson 
23988737be2SRobert Watson 	/*
24088737be2SRobert Watson 	 * Kernel compile-time and user compile-time definitions of
24188737be2SRobert Watson 	 * NETISR_MAXPROT must match, as we use that to size work arrays.
24288737be2SRobert Watson 	 */
24388737be2SRobert Watson 	netisr_load_kvm_uint(kd, "_netisr_maxprot", &maxprot);
24488737be2SRobert Watson 	if (maxprot != NETISR_MAXPROT)
245*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: NETISR_MAXPROT mismatch", __func__);
24688737be2SRobert Watson 	len = maxprot * sizeof(*np_array);
24788737be2SRobert Watson 	np_array = malloc(len);
24888737be2SRobert Watson 	if (np_array == NULL)
249*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "%s: malloc", __func__);
25088737be2SRobert Watson 	ret = kvm_nlist(kd, nl);
25188737be2SRobert Watson 	if (ret < 0)
252*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_nlist(_netisr_proto): %s", __func__,
25388737be2SRobert Watson 		    kvm_geterr(kd));
25488737be2SRobert Watson 	if (ret != 0)
255*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_nlist(_netisr_proto): unresolved symbol",
25688737be2SRobert Watson 		    __func__);
25788737be2SRobert Watson 	if (kvm_read(kd, nl[NLIST_NETISR_PROTO].n_value, np_array, len) !=
25888737be2SRobert Watson 	    (ssize_t)len)
259*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_read(_netisr_proto): %s", __func__,
26088737be2SRobert Watson 		    kvm_geterr(kd));
26188737be2SRobert Watson 
26288737be2SRobert Watson 	/*
26388737be2SRobert Watson 	 * Size and allocate memory to hold only live protocols.
26488737be2SRobert Watson 	 */
26588737be2SRobert Watson 	protocount = 0;
26688737be2SRobert Watson 	for (i = 0; i < maxprot; i++) {
26788737be2SRobert Watson 		if (np_array[i].np_name == NULL)
26888737be2SRobert Watson 			continue;
26988737be2SRobert Watson 		protocount++;
27088737be2SRobert Watson 	}
27188737be2SRobert Watson 	proto_array = calloc(protocount, sizeof(*proto_array));
27288737be2SRobert Watson 	if (proto_array == NULL)
27388737be2SRobert Watson 		err(-1, "malloc");
27488737be2SRobert Watson 	protocount = 0;
27588737be2SRobert Watson 	for (i = 0; i < maxprot; i++) {
27688737be2SRobert Watson 		npp = &np_array[i];
27788737be2SRobert Watson 		if (npp->np_name == NULL)
27888737be2SRobert Watson 			continue;
27988737be2SRobert Watson 		snpp = &proto_array[protocount];
28088737be2SRobert Watson 		snpp->snp_version = sizeof(*snpp);
28188737be2SRobert Watson 		netisr_load_kvm_string(kd, (uintptr_t)npp->np_name,
28288737be2SRobert Watson 		    snpp->snp_name, sizeof(snpp->snp_name));
28388737be2SRobert Watson 		snpp->snp_proto = i;
28488737be2SRobert Watson 		snpp->snp_qlimit = npp->np_qlimit;
28588737be2SRobert Watson 		snpp->snp_policy = npp->np_policy;
2868f092df0SRobert Watson 		snpp->snp_dispatch = npp->np_dispatch;
28788737be2SRobert Watson 		if (npp->np_m2flow != NULL)
28888737be2SRobert Watson 			snpp->snp_flags |= NETISR_SNP_FLAGS_M2FLOW;
28988737be2SRobert Watson 		if (npp->np_m2cpuid != NULL)
29088737be2SRobert Watson 			snpp->snp_flags |= NETISR_SNP_FLAGS_M2CPUID;
29188737be2SRobert Watson 		if (npp->np_drainedcpu != NULL)
29288737be2SRobert Watson 			snpp->snp_flags |= NETISR_SNP_FLAGS_DRAINEDCPU;
29388737be2SRobert Watson 		protocount++;
29488737be2SRobert Watson 	}
29588737be2SRobert Watson 	proto_array_len = protocount;
29688737be2SRobert Watson 	free(np_array);
29788737be2SRobert Watson }
29888737be2SRobert Watson 
29988737be2SRobert Watson static void
30088737be2SRobert Watson netisr_load_sysctl_proto(void)
3010153eb66SRobert Watson {
3020153eb66SRobert Watson 	size_t len;
3030153eb66SRobert Watson 
3040153eb66SRobert Watson 	if (sysctlbyname("net.isr.proto", NULL, &len, NULL, 0) < 0)
305*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "net.isr.proto: query len");
3060153eb66SRobert Watson 	if (len % sizeof(*proto_array) != 0)
307*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.proto: invalid len");
3080153eb66SRobert Watson 	proto_array = malloc(len);
3090153eb66SRobert Watson 	if (proto_array == NULL)
310*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "malloc");
3110153eb66SRobert Watson 	if (sysctlbyname("net.isr.proto", proto_array, &len, NULL, 0) < 0)
312*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "net.isr.proto: query data");
3130153eb66SRobert Watson 	if (len % sizeof(*proto_array) != 0)
314*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.proto: invalid len");
3150153eb66SRobert Watson 	proto_array_len = len / sizeof(*proto_array);
3160153eb66SRobert Watson 	if (proto_array_len < 1)
317*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.proto: no data");
3180153eb66SRobert Watson 	if (proto_array[0].snp_version != sizeof(proto_array[0]))
319*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.proto: invalid version");
3200153eb66SRobert Watson }
3210153eb66SRobert Watson 
3220153eb66SRobert Watson static void
32388737be2SRobert Watson netisr_load_kvm_workstream(kvm_t *kd)
32488737be2SRobert Watson {
32588737be2SRobert Watson 	struct nlist nl[] = {
32688737be2SRobert Watson #define	NLIST_NWS_ARRAY		0
32788737be2SRobert Watson 		{ .n_name = "_nws_array" },
32888737be2SRobert Watson 		{ .n_name = NULL },
32988737be2SRobert Watson 	};
33088737be2SRobert Watson 	struct netisr_workstream nws;
33188737be2SRobert Watson 	struct sysctl_netisr_workstream *snwsp;
33288737be2SRobert Watson 	struct sysctl_netisr_work *snwp;
33388737be2SRobert Watson 	struct netisr_work *nwp;
33488737be2SRobert Watson 	struct nlist nl_nws[2];
33588737be2SRobert Watson 	u_int counter, cpuid, proto, wsid;
33688737be2SRobert Watson 	size_t len;
33788737be2SRobert Watson 	int ret;
33888737be2SRobert Watson 
33988737be2SRobert Watson 	len = numthreads * sizeof(*nws_array);
34088737be2SRobert Watson 	nws_array = malloc(len);
34188737be2SRobert Watson 	if (nws_array == NULL)
342*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "malloc");
34388737be2SRobert Watson 	ret = kvm_nlist(kd, nl);
34488737be2SRobert Watson 	if (ret < 0)
345*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_nlist: %s", __func__, kvm_geterr(kd));
34688737be2SRobert Watson 	if (ret != 0)
347*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_nlist: unresolved symbol", __func__);
34888737be2SRobert Watson 	if (kvm_read(kd, nl[NLIST_NWS_ARRAY].n_value, nws_array, len) !=
34988737be2SRobert Watson 	    (ssize_t)len)
350*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "%s: kvm_read(_nws_array): %s", __func__,
35188737be2SRobert Watson 		    kvm_geterr(kd));
35288737be2SRobert Watson 	workstream_array = calloc(numthreads, sizeof(*workstream_array));
35388737be2SRobert Watson 	if (workstream_array == NULL)
354*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "calloc");
35588737be2SRobert Watson 	workstream_array_len = numthreads;
35688737be2SRobert Watson 	work_array = calloc(numthreads * proto_array_len, sizeof(*work_array));
35788737be2SRobert Watson 	if (work_array == NULL)
358*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "calloc");
35988737be2SRobert Watson 	counter = 0;
36088737be2SRobert Watson 	for (wsid = 0; wsid < numthreads; wsid++) {
36188737be2SRobert Watson 		cpuid = nws_array[wsid];
36288737be2SRobert Watson 		if (kvm_dpcpu_setcpu(kd, cpuid) < 0)
363*ade9ccfeSMarcel Moolenaar 			xo_errx(-1, "%s: kvm_dpcpu_setcpu(%u): %s", __func__,
36488737be2SRobert Watson 			    cpuid, kvm_geterr(kd));
36588737be2SRobert Watson 		bzero(nl_nws, sizeof(nl_nws));
36688737be2SRobert Watson 		nl_nws[0].n_name = "_nws";
36788737be2SRobert Watson 		ret = kvm_nlist(kd, nl_nws);
36888737be2SRobert Watson 		if (ret < 0)
369*ade9ccfeSMarcel Moolenaar 			xo_errx(-1, "%s: kvm_nlist looking up nws on CPU "
370*ade9ccfeSMarcel Moolenaar 			    "%u: %s", __func__, cpuid, kvm_geterr(kd));
37188737be2SRobert Watson 		if (ret != 0)
372*ade9ccfeSMarcel Moolenaar 			xo_errx(-1, "%s: kvm_nlist(nws): unresolved symbol on "
37388737be2SRobert Watson 			    "CPU %u", __func__, cpuid);
37488737be2SRobert Watson 		if (kvm_read(kd, nl_nws[0].n_value, &nws, sizeof(nws)) !=
37588737be2SRobert Watson 		    sizeof(nws))
376*ade9ccfeSMarcel Moolenaar 			xo_errx(-1, "%s: kvm_read(nw): %s", __func__,
37788737be2SRobert Watson 			    kvm_geterr(kd));
37888737be2SRobert Watson 		snwsp = &workstream_array[wsid];
37988737be2SRobert Watson 		snwsp->snws_version = sizeof(*snwsp);
38088737be2SRobert Watson 		snwsp->snws_wsid = cpuid;
38188737be2SRobert Watson 		snwsp->snws_cpu = cpuid;
38288737be2SRobert Watson 		if (nws.nws_intr_event != NULL)
38388737be2SRobert Watson 			snwsp->snws_flags |= NETISR_SNWS_FLAGS_INTR;
38488737be2SRobert Watson 
38588737be2SRobert Watson 		/*
38688737be2SRobert Watson 		 * Extract the CPU's per-protocol work information.
38788737be2SRobert Watson 		 */
388*ade9ccfeSMarcel Moolenaar 		xo_emit("counting to maxprot: {:maxprot/%u}\n", maxprot);
38988737be2SRobert Watson 		for (proto = 0; proto < maxprot; proto++) {
39088737be2SRobert Watson 			if (!netisr_protoispresent(proto))
39188737be2SRobert Watson 				continue;
39288737be2SRobert Watson 			nwp = &nws.nws_work[proto];
39388737be2SRobert Watson 			snwp = &work_array[counter];
39488737be2SRobert Watson 			snwp->snw_version = sizeof(*snwp);
39588737be2SRobert Watson 			snwp->snw_wsid = cpuid;
39688737be2SRobert Watson 			snwp->snw_proto = proto;
39788737be2SRobert Watson 			snwp->snw_len = nwp->nw_len;
39888737be2SRobert Watson 			snwp->snw_watermark = nwp->nw_watermark;
39988737be2SRobert Watson 			snwp->snw_dispatched = nwp->nw_dispatched;
40088737be2SRobert Watson 			snwp->snw_hybrid_dispatched =
40188737be2SRobert Watson 			    nwp->nw_hybrid_dispatched;
40288737be2SRobert Watson 			snwp->snw_qdrops = nwp->nw_qdrops;
40388737be2SRobert Watson 			snwp->snw_queued = nwp->nw_queued;
40488737be2SRobert Watson 			snwp->snw_handled = nwp->nw_handled;
40588737be2SRobert Watson 			counter++;
40688737be2SRobert Watson 		}
40788737be2SRobert Watson 	}
40888737be2SRobert Watson 	work_array_len = counter;
40988737be2SRobert Watson }
41088737be2SRobert Watson 
41188737be2SRobert Watson static void
41288737be2SRobert Watson netisr_load_sysctl_workstream(void)
4130153eb66SRobert Watson {
4140153eb66SRobert Watson 	size_t len;
4150153eb66SRobert Watson 
4160153eb66SRobert Watson 	if (sysctlbyname("net.isr.workstream", NULL, &len, NULL, 0) < 0)
417*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "net.isr.workstream: query len");
4180153eb66SRobert Watson 	if (len % sizeof(*workstream_array) != 0)
419*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.workstream: invalid len");
4200153eb66SRobert Watson 	workstream_array = malloc(len);
4210153eb66SRobert Watson 	if (workstream_array == NULL)
422*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "malloc");
4230153eb66SRobert Watson 	if (sysctlbyname("net.isr.workstream", workstream_array, &len, NULL,
4240153eb66SRobert Watson 	    0) < 0)
425*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "net.isr.workstream: query data");
4260153eb66SRobert Watson 	if (len % sizeof(*workstream_array) != 0)
427*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.workstream: invalid len");
4280153eb66SRobert Watson 	workstream_array_len = len / sizeof(*workstream_array);
4290153eb66SRobert Watson 	if (workstream_array_len < 1)
430*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.workstream: no data");
4310153eb66SRobert Watson 	if (workstream_array[0].snws_version != sizeof(workstream_array[0]))
432*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.workstream: invalid version");
4330153eb66SRobert Watson }
4340153eb66SRobert Watson 
4350153eb66SRobert Watson static void
43688737be2SRobert Watson netisr_load_sysctl_work(void)
4370153eb66SRobert Watson {
4380153eb66SRobert Watson 	size_t len;
4390153eb66SRobert Watson 
4400153eb66SRobert Watson 	if (sysctlbyname("net.isr.work", NULL, &len, NULL, 0) < 0)
441*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "net.isr.work: query len");
4420153eb66SRobert Watson 	if (len % sizeof(*work_array) != 0)
443*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.work: invalid len");
4440153eb66SRobert Watson 	work_array = malloc(len);
4450153eb66SRobert Watson 	if (work_array == NULL)
446*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "malloc");
4470153eb66SRobert Watson 	if (sysctlbyname("net.isr.work", work_array, &len, NULL, 0) < 0)
448*ade9ccfeSMarcel Moolenaar 		xo_err(-1, "net.isr.work: query data");
4490153eb66SRobert Watson 	if (len % sizeof(*work_array) != 0)
450*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.work: invalid len");
4510153eb66SRobert Watson 	work_array_len = len / sizeof(*work_array);
4520153eb66SRobert Watson 	if (work_array_len < 1)
453*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.work: no data");
4540153eb66SRobert Watson 	if (work_array[0].snw_version != sizeof(work_array[0]))
455*ade9ccfeSMarcel Moolenaar 		xo_errx(-1, "net.isr.work: invalid version");
4560153eb66SRobert Watson }
4570153eb66SRobert Watson 
4580153eb66SRobert Watson static void
4590153eb66SRobert Watson netisr_print_proto(struct sysctl_netisr_proto *snpp)
4600153eb66SRobert Watson {
4618f092df0SRobert Watson 	char tmp[20];
4620153eb66SRobert Watson 
463*ade9ccfeSMarcel Moolenaar 	xo_emit("{[:-6}{k:name/%s}{]:}", snpp->snp_name);
464*ade9ccfeSMarcel Moolenaar 	xo_emit(" {:protocol/%5u}", snpp->snp_proto);
465*ade9ccfeSMarcel Moolenaar 	xo_emit(" {:queue-limit/%6u}", snpp->snp_qlimit);
466*ade9ccfeSMarcel Moolenaar 	xo_emit(" {:policy-type/%6s}",
4670153eb66SRobert Watson 	    (snpp->snp_policy == NETISR_POLICY_SOURCE) ?  "source" :
4680153eb66SRobert Watson 	    (snpp->snp_policy == NETISR_POLICY_FLOW) ? "flow" :
4690153eb66SRobert Watson 	    (snpp->snp_policy == NETISR_POLICY_CPU) ? "cpu" : "-");
4708f092df0SRobert Watson 	netisr_dispatch_policy_to_string(snpp->snp_dispatch, tmp,
4718f092df0SRobert Watson 	    sizeof(tmp));
472*ade9ccfeSMarcel Moolenaar 	xo_emit(" {:policy/%8s}", tmp);
473*ade9ccfeSMarcel Moolenaar 	xo_emit("   {:flags/%s%s%s}\n",
4740153eb66SRobert Watson 	    (snpp->snp_flags & NETISR_SNP_FLAGS_M2CPUID) ?  "C" : "-",
4750153eb66SRobert Watson 	    (snpp->snp_flags & NETISR_SNP_FLAGS_DRAINEDCPU) ?  "D" : "-",
4760153eb66SRobert Watson 	    (snpp->snp_flags & NETISR_SNP_FLAGS_M2FLOW) ? "F" : "-");
4770153eb66SRobert Watson }
4780153eb66SRobert Watson 
4790153eb66SRobert Watson static void
4800153eb66SRobert Watson netisr_print_workstream(struct sysctl_netisr_workstream *snwsp)
4810153eb66SRobert Watson {
4820153eb66SRobert Watson 	struct sysctl_netisr_work *snwp;
4830153eb66SRobert Watson 	u_int i;
4840153eb66SRobert Watson 
485*ade9ccfeSMarcel Moolenaar 	xo_open_list("work");
4860153eb66SRobert Watson 	for (i = 0; i < work_array_len; i++) {
4870153eb66SRobert Watson 		snwp = &work_array[i];
4880153eb66SRobert Watson 		if (snwp->snw_wsid != snwsp->snws_wsid)
4890153eb66SRobert Watson 			continue;
490*ade9ccfeSMarcel Moolenaar 		xo_open_instance("work");
491*ade9ccfeSMarcel Moolenaar 		xo_emit("{t:workstream/%4u} ", snwsp->snws_wsid);
492*ade9ccfeSMarcel Moolenaar 		xo_emit("{t:cpu/%3u} ", snwsp->snws_cpu);
493*ade9ccfeSMarcel Moolenaar 		xo_emit("{P:  }");
494*ade9ccfeSMarcel Moolenaar 		xo_emit("{t:name/%-6s}", netisr_proto2name(snwp->snw_proto));
495*ade9ccfeSMarcel Moolenaar 		xo_emit(" {t:length/%5u}", snwp->snw_len);
496*ade9ccfeSMarcel Moolenaar 		xo_emit(" {t:watermark/%5u}", snwp->snw_watermark);
497*ade9ccfeSMarcel Moolenaar 		xo_emit(" {t:dispatched/%8ju}", snwp->snw_dispatched);
498*ade9ccfeSMarcel Moolenaar 		xo_emit(" {t:hybrid-dispatched/%8ju}",
499*ade9ccfeSMarcel Moolenaar 		    snwp->snw_hybrid_dispatched);
500*ade9ccfeSMarcel Moolenaar 		xo_emit(" {t:queue-drops/%8ju}", snwp->snw_qdrops);
501*ade9ccfeSMarcel Moolenaar 		xo_emit(" {t:queued/%8ju}", snwp->snw_queued);
502*ade9ccfeSMarcel Moolenaar 		xo_emit(" {t:handled/%8ju}", snwp->snw_handled);
503*ade9ccfeSMarcel Moolenaar 		xo_emit("\n");
504*ade9ccfeSMarcel Moolenaar 		xo_close_instance("work");
5050153eb66SRobert Watson 	}
506*ade9ccfeSMarcel Moolenaar 	xo_close_list("work");
5070153eb66SRobert Watson }
5080153eb66SRobert Watson 
5090153eb66SRobert Watson void
51088737be2SRobert Watson netisr_stats(void *kvmd)
5110153eb66SRobert Watson {
5120153eb66SRobert Watson 	struct sysctl_netisr_workstream *snwsp;
5130153eb66SRobert Watson 	struct sysctl_netisr_proto *snpp;
51488737be2SRobert Watson 	kvm_t *kd = kvmd;
5150153eb66SRobert Watson 	u_int i;
5160153eb66SRobert Watson 
51788737be2SRobert Watson 	if (live) {
51888737be2SRobert Watson 		netisr_load_sysctl_config();
51988737be2SRobert Watson 		netisr_load_sysctl_proto();
52088737be2SRobert Watson 		netisr_load_sysctl_workstream();
52188737be2SRobert Watson 		netisr_load_sysctl_work();
52288737be2SRobert Watson 	} else {
52388737be2SRobert Watson 		if (kd == NULL)
524*ade9ccfeSMarcel Moolenaar 			xo_errx(-1, "netisr_stats: !live but !kd");
52588737be2SRobert Watson 		netisr_load_kvm_config(kd);
52688737be2SRobert Watson 		netisr_load_kvm_proto(kd);
52788737be2SRobert Watson 		netisr_load_kvm_workstream(kd);		/* Also does work. */
52888737be2SRobert Watson 	}
5290153eb66SRobert Watson 
530*ade9ccfeSMarcel Moolenaar 	xo_open_container("netisr");
5310153eb66SRobert Watson 
532*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:Configuration}:\n");
533*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:/%-25s} {T:/%12s} {T:/%12s}\n",
534*ade9ccfeSMarcel Moolenaar 	    "Setting", "Current", "Limit");
535*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:/%-25s} {T:/%12u} {T:/%12u}\n",
536*ade9ccfeSMarcel Moolenaar 	    "Thread count", numthreads, maxthreads);
537*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:/%-25s} {T:/%12u} {T:/%12u}\n",
538*ade9ccfeSMarcel Moolenaar 	    "Default queue limit", defaultqlimit, maxqlimit);
539*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:/%-25s} {T:/%12s} {T:/%12s}\n",
540*ade9ccfeSMarcel Moolenaar 	    "Dispatch policy", dispatch_policy, "n/a");
541*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:/%-25s} {T:/%12s} {T:/%12s}\n",
542*ade9ccfeSMarcel Moolenaar 	    "Threads bound to CPUs", bindthreads ? "enabled" : "disabled",
543*ade9ccfeSMarcel Moolenaar 	    "n/a");
544*ade9ccfeSMarcel Moolenaar 	xo_emit("\n");
545*ade9ccfeSMarcel Moolenaar 
546*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:Protocols}:\n");
547*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:/%-6s} {T:/%5s} {T:/%6s} {T:/%-6s} {T:/%-8s} {T:/%-5s}\n",
548*ade9ccfeSMarcel Moolenaar 	    "Name", "Proto", "QLimit", "Policy", "Dispatch", "Flags");
549*ade9ccfeSMarcel Moolenaar 	xo_open_list("protocol");
5500153eb66SRobert Watson 	for (i = 0; i < proto_array_len; i++) {
551*ade9ccfeSMarcel Moolenaar 		xo_open_instance("protocol");
5520153eb66SRobert Watson 		snpp = &proto_array[i];
5530153eb66SRobert Watson 		netisr_print_proto(snpp);
554*ade9ccfeSMarcel Moolenaar 		xo_close_instance("protocol");
5550153eb66SRobert Watson 	}
556*ade9ccfeSMarcel Moolenaar 	xo_close_list("protocol");
557*ade9ccfeSMarcel Moolenaar 	xo_emit("\n");
5580153eb66SRobert Watson 
559*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:Workstreams}:\n");
560*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:/%4s} {T:/%3s} ", "WSID", "CPU");
561*ade9ccfeSMarcel Moolenaar 	xo_emit("{P:/%2s}", "");
562*ade9ccfeSMarcel Moolenaar 	xo_emit("{T:/%-6s} {T:/%5s} {T:/%5s} {T:/%8s} {T:/%8s} {T:/%8s} "
563*ade9ccfeSMarcel Moolenaar 	    "{T:/%8s} {T:/%8s}\n",
564*ade9ccfeSMarcel Moolenaar 	    "Name", "Len", "WMark", "Disp'd", "HDisp'd", "QDrops", "Queued",
565*ade9ccfeSMarcel Moolenaar 	    "Handled");
566*ade9ccfeSMarcel Moolenaar 	xo_open_list("workstream");
5670153eb66SRobert Watson 	for (i = 0; i < workstream_array_len; i++) {
568*ade9ccfeSMarcel Moolenaar 		xo_open_instance("workstream");
5690153eb66SRobert Watson 		snwsp = &workstream_array[i];
5700153eb66SRobert Watson 		netisr_print_workstream(snwsp);
571*ade9ccfeSMarcel Moolenaar 		xo_close_instance("workstream");
5720153eb66SRobert Watson 	}
573*ade9ccfeSMarcel Moolenaar 	xo_close_list("workstream");
574*ade9ccfeSMarcel Moolenaar 	xo_close_container("netisr");
5750153eb66SRobert Watson }
576