netisr.c revision 217775
1/*-
2 * Copyright (c) 2010-2011 Juniper Networks, Inc.
3 * All rights reserved.
4 *
5 * This software was developed by Robert N. M. Watson under contract
6 * to Juniper Networks, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31
32__FBSDID("$FreeBSD: head/usr.bin/netstat/netisr.c 217775 2011-01-24 10:54:09Z rwatson $");
33
34#include <sys/param.h>
35#include <sys/sysctl.h>
36
37#include <sys/_lock.h>
38#include <sys/_mutex.h>
39
40#define	_WANT_NETISR_INTERNAL
41#include <net/netisr.h>
42#include <net/netisr_internal.h>
43
44#include <err.h>
45#include <kvm.h>
46#include <stdint.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50
51#include "netstat.h"
52
53/*
54 * Print statistics for the kernel netisr subsystem.
55 */
56static u_int				 bindthreads;
57static u_int				 maxthreads;
58static u_int				 numthreads;
59
60static u_int				 defaultqlimit;
61static u_int				 maxqlimit;
62
63static u_int				 direct;
64static u_int				 direct_force;
65
66static struct sysctl_netisr_proto	*proto_array;
67static u_int				 proto_array_len;
68
69static struct sysctl_netisr_workstream	*workstream_array;
70static u_int				 workstream_array_len;
71
72static struct sysctl_netisr_work	*work_array;
73static u_int				 work_array_len;
74
75static u_int				*nws_array;
76
77static u_int				 maxprot;
78
79static void
80netisr_load_kvm_uint(kvm_t *kd, char *name, u_int *p)
81{
82	struct nlist nl[] = {
83		{ .n_name = name },
84		{ .n_name = NULL },
85	};
86	int ret;
87
88	ret = kvm_nlist(kd, nl);
89	if (ret < 0)
90		errx(-1, "%s: kvm_nlist(%s): %s", __func__, name,
91		    kvm_geterr(kd));
92	if (ret != 0)
93		errx(-1, "%s: kvm_nlist(%s): unresolved symbol", __func__,
94		    name);
95	if (kvm_read(kd, nl[0].n_value, p, sizeof(*p)) != sizeof(*p))
96		errx(-1, "%s: kvm_read(%s): %s", __func__, name,
97		    kvm_geterr(kd));
98}
99
100/*
101 * Load a nul-terminated string from KVM up to 'limit', guarantee that the
102 * string in local memory is nul-terminated.
103 */
104static void
105netisr_load_kvm_string(kvm_t *kd, uintptr_t addr, char *dest, u_int limit)
106{
107	u_int i;
108
109	for (i = 0; i < limit; i++) {
110		if (kvm_read(kd, addr + i, &dest[i], sizeof(dest[i])) !=
111		    sizeof(dest[i]))
112			err(-1, "%s: kvm_read: %s", __func__,
113			    kvm_geterr(kd));
114		if (dest[i] == '\0')
115			break;
116	}
117	dest[limit - 1] = '\0';
118}
119
120static const char *
121netisr_proto2name(u_int proto)
122{
123	u_int i;
124
125	for (i = 0; i < proto_array_len; i++) {
126		if (proto_array[i].snp_proto == proto)
127			return (proto_array[i].snp_name);
128	}
129	return ("unknown");
130}
131
132static int
133netisr_protoispresent(u_int proto)
134{
135	u_int i;
136
137	for (i = 0; i < proto_array_len; i++) {
138		if (proto_array[i].snp_proto == proto)
139			return (1);
140	}
141	return (0);
142}
143
144static void
145netisr_load_kvm_config(kvm_t *kd)
146{
147
148	netisr_load_kvm_uint(kd, "_netisr_bindthreads", &bindthreads);
149	netisr_load_kvm_uint(kd, "_netisr_maxthreads", &maxthreads);
150	netisr_load_kvm_uint(kd, "_nws_count", &numthreads);
151
152	netisr_load_kvm_uint(kd, "_netisr_defaultqlimit", &defaultqlimit);
153	netisr_load_kvm_uint(kd, "_netisr_maxqlimit", &maxqlimit);
154
155	netisr_load_kvm_uint(kd, "_netisr_direct", &direct);
156	netisr_load_kvm_uint(kd, "_netisr_direct_force", &direct_force);
157}
158
159static void
160netisr_load_sysctl_uint(const char *name, u_int *p)
161{
162	size_t retlen;
163
164	retlen = sizeof(u_int);
165	if (sysctlbyname(name, p, &retlen, NULL, 0) < 0)
166		err(-1, "%s", name);
167	if (retlen != sizeof(u_int))
168		errx(-1, "%s: invalid len %ju", name, (uintmax_t)retlen);
169}
170
171static void
172netisr_load_sysctl_config(void)
173{
174
175	netisr_load_sysctl_uint("net.isr.bindthreads", &bindthreads);
176	netisr_load_sysctl_uint("net.isr.maxthreads", &maxthreads);
177	netisr_load_sysctl_uint("net.isr.numthreads", &numthreads);
178
179	netisr_load_sysctl_uint("net.isr.defaultqlimit", &defaultqlimit);
180	netisr_load_sysctl_uint("net.isr.maxqlimit", &maxqlimit);
181
182	netisr_load_sysctl_uint("net.isr.direct", &direct);
183	netisr_load_sysctl_uint("net.isr.direct_force", &direct_force);
184}
185
186static void
187netisr_load_kvm_proto(kvm_t *kd)
188{
189	struct nlist nl[] = {
190#define	NLIST_NETISR_PROTO	0
191		{ .n_name = "_netisr_proto" },
192		{ .n_name = NULL },
193	};
194	struct netisr_proto *np_array, *npp;
195	u_int i, protocount;
196	struct sysctl_netisr_proto *snpp;
197	size_t len;
198	int ret;
199
200	/*
201	 * Kernel compile-time and user compile-time definitions of
202	 * NETISR_MAXPROT must match, as we use that to size work arrays.
203	 */
204	netisr_load_kvm_uint(kd, "_netisr_maxprot", &maxprot);
205	if (maxprot != NETISR_MAXPROT)
206		errx(-1, "%s: NETISR_MAXPROT mismatch", __func__);
207	len = maxprot * sizeof(*np_array);
208	np_array = malloc(len);
209	if (np_array == NULL)
210		err(-1, "%s: malloc", __func__);
211	ret = kvm_nlist(kd, nl);
212	if (ret < 0)
213		errx(-1, "%s: kvm_nlist(_netisr_proto): %s", __func__,
214		    kvm_geterr(kd));
215	if (ret != 0)
216		errx(-1, "%s: kvm_nlist(_netisr_proto): unresolved symbol",
217		    __func__);
218	if (kvm_read(kd, nl[NLIST_NETISR_PROTO].n_value, np_array, len) !=
219	    (ssize_t)len)
220		errx(-1, "%s: kvm_read(_netisr_proto): %s", __func__,
221		    kvm_geterr(kd));
222
223	/*
224	 * Size and allocate memory to hold only live protocols.
225	 */
226	protocount = 0;
227	for (i = 0; i < maxprot; i++) {
228		if (np_array[i].np_name == NULL)
229			continue;
230		protocount++;
231	}
232	proto_array = calloc(protocount, sizeof(*proto_array));
233	if (proto_array == NULL)
234		err(-1, "malloc");
235	protocount = 0;
236	for (i = 0; i < maxprot; i++) {
237		npp = &np_array[i];
238		if (npp->np_name == NULL)
239			continue;
240		snpp = &proto_array[protocount];
241		snpp->snp_version = sizeof(*snpp);
242		netisr_load_kvm_string(kd, (uintptr_t)npp->np_name,
243		    snpp->snp_name, sizeof(snpp->snp_name));
244		snpp->snp_proto = i;
245		snpp->snp_qlimit = npp->np_qlimit;
246		snpp->snp_policy = npp->np_policy;
247		if (npp->np_m2flow != NULL)
248			snpp->snp_flags |= NETISR_SNP_FLAGS_M2FLOW;
249		if (npp->np_m2cpuid != NULL)
250			snpp->snp_flags |= NETISR_SNP_FLAGS_M2CPUID;
251		if (npp->np_drainedcpu != NULL)
252			snpp->snp_flags |= NETISR_SNP_FLAGS_DRAINEDCPU;
253		protocount++;
254	}
255	proto_array_len = protocount;
256	free(np_array);
257}
258
259static void
260netisr_load_sysctl_proto(void)
261{
262	size_t len;
263
264	if (sysctlbyname("net.isr.proto", NULL, &len, NULL, 0) < 0)
265		err(-1, "net.isr.proto: query len");
266	if (len % sizeof(*proto_array) != 0)
267		errx(-1, "net.isr.proto: invalid len");
268	proto_array = malloc(len);
269	if (proto_array == NULL)
270		err(-1, "malloc");
271	if (sysctlbyname("net.isr.proto", proto_array, &len, NULL, 0) < 0)
272		err(-1, "net.isr.proto: query data");
273	if (len % sizeof(*proto_array) != 0)
274		errx(-1, "net.isr.proto: invalid len");
275	proto_array_len = len / sizeof(*proto_array);
276	if (proto_array_len < 1)
277		errx(-1, "net.isr.proto: no data");
278	if (proto_array[0].snp_version != sizeof(proto_array[0]))
279		errx(-1, "net.isr.proto: invalid version");
280}
281
282static void
283netisr_load_kvm_workstream(kvm_t *kd)
284{
285	struct nlist nl[] = {
286#define	NLIST_NWS_ARRAY		0
287		{ .n_name = "_nws_array" },
288		{ .n_name = NULL },
289	};
290	struct netisr_workstream nws;
291	struct sysctl_netisr_workstream *snwsp;
292	struct sysctl_netisr_work *snwp;
293	struct netisr_work *nwp;
294	struct nlist nl_nws[2];
295	u_int counter, cpuid, proto, wsid;
296	size_t len;
297	int ret;
298
299	len = numthreads * sizeof(*nws_array);
300	nws_array = malloc(len);
301	if (nws_array == NULL)
302		err(-1, "malloc");
303	ret = kvm_nlist(kd, nl);
304	if (ret < 0)
305		errx(-1, "%s: kvm_nlist: %s", __func__, kvm_geterr(kd));
306	if (ret != 0)
307		errx(-1, "%s: kvm_nlist: unresolved symbol", __func__);
308	if (kvm_read(kd, nl[NLIST_NWS_ARRAY].n_value, nws_array, len) !=
309	    (ssize_t)len)
310		errx(-1, "%s: kvm_read(_nws_array): %s", __func__,
311		    kvm_geterr(kd));
312	workstream_array = calloc(numthreads, sizeof(*workstream_array));
313	if (workstream_array == NULL)
314		err(-1, "calloc");
315	workstream_array_len = numthreads;
316	work_array = calloc(numthreads * proto_array_len, sizeof(*work_array));
317	if (work_array == NULL)
318		err(-1, "calloc");
319	counter = 0;
320	for (wsid = 0; wsid < numthreads; wsid++) {
321		cpuid = nws_array[wsid];
322		if (kvm_dpcpu_setcpu(kd, cpuid) < 0)
323			errx(-1, "%s: kvm_dpcpu_setcpu(%u): %s", __func__,
324			    cpuid, kvm_geterr(kd));
325		bzero(nl_nws, sizeof(nl_nws));
326		nl_nws[0].n_name = "_nws";
327		ret = kvm_nlist(kd, nl_nws);
328		if (ret < 0)
329			errx(-1, "%s: kvm_nlist looking up nws on CPU %u: %s",
330			    __func__, cpuid, kvm_geterr(kd));
331		if (ret != 0)
332			errx(-1, "%s: kvm_nlist(nws): unresolved symbol on "
333			    "CPU %u", __func__, cpuid);
334		if (kvm_read(kd, nl_nws[0].n_value, &nws, sizeof(nws)) !=
335		    sizeof(nws))
336			errx(-1, "%s: kvm_read(nw): %s", __func__,
337			    kvm_geterr(kd));
338		snwsp = &workstream_array[wsid];
339		snwsp->snws_version = sizeof(*snwsp);
340		snwsp->snws_wsid = cpuid;
341		snwsp->snws_cpu = cpuid;
342		if (nws.nws_intr_event != NULL)
343			snwsp->snws_flags |= NETISR_SNWS_FLAGS_INTR;
344
345		/*
346		 * Extract the CPU's per-protocol work information.
347		 */
348		printf("counting to maxprot: %u\n", maxprot);
349		for (proto = 0; proto < maxprot; proto++) {
350			if (!netisr_protoispresent(proto))
351				continue;
352			nwp = &nws.nws_work[proto];
353			snwp = &work_array[counter];
354			snwp->snw_version = sizeof(*snwp);
355			snwp->snw_wsid = cpuid;
356			snwp->snw_proto = proto;
357			snwp->snw_len = nwp->nw_len;
358			snwp->snw_watermark = nwp->nw_watermark;
359			snwp->snw_dispatched = nwp->nw_dispatched;
360			snwp->snw_hybrid_dispatched =
361			    nwp->nw_hybrid_dispatched;
362			snwp->snw_qdrops = nwp->nw_qdrops;
363			snwp->snw_queued = nwp->nw_queued;
364			snwp->snw_handled = nwp->nw_handled;
365			counter++;
366		}
367	}
368	work_array_len = counter;
369}
370
371static void
372netisr_load_sysctl_workstream(void)
373{
374	size_t len;
375
376	if (sysctlbyname("net.isr.workstream", NULL, &len, NULL, 0) < 0)
377		err(-1, "net.isr.workstream: query len");
378	if (len % sizeof(*workstream_array) != 0)
379		errx(-1, "net.isr.workstream: invalid len");
380	workstream_array = malloc(len);
381	if (workstream_array == NULL)
382		err(-1, "malloc");
383	if (sysctlbyname("net.isr.workstream", workstream_array, &len, NULL,
384	    0) < 0)
385		err(-1, "net.isr.workstream: query data");
386	if (len % sizeof(*workstream_array) != 0)
387		errx(-1, "net.isr.workstream: invalid len");
388	workstream_array_len = len / sizeof(*workstream_array);
389	if (workstream_array_len < 1)
390		errx(-1, "net.isr.workstream: no data");
391	if (workstream_array[0].snws_version != sizeof(workstream_array[0]))
392		errx(-1, "net.isr.workstream: invalid version");
393}
394
395static void
396netisr_load_sysctl_work(void)
397{
398	size_t len;
399
400	if (sysctlbyname("net.isr.work", NULL, &len, NULL, 0) < 0)
401		err(-1, "net.isr.work: query len");
402	if (len % sizeof(*work_array) != 0)
403		errx(-1, "net.isr.work: invalid len");
404	work_array = malloc(len);
405	if (work_array == NULL)
406		err(-1, "malloc");
407	if (sysctlbyname("net.isr.work", work_array, &len, NULL, 0) < 0)
408		err(-1, "net.isr.work: query data");
409	if (len % sizeof(*work_array) != 0)
410		errx(-1, "net.isr.work: invalid len");
411	work_array_len = len / sizeof(*work_array);
412	if (work_array_len < 1)
413		errx(-1, "net.isr.work: no data");
414	if (work_array[0].snw_version != sizeof(work_array[0]))
415		errx(-1, "net.isr.work: invalid version");
416}
417
418static void
419netisr_print_proto(struct sysctl_netisr_proto *snpp)
420{
421
422	printf("%-6s", snpp->snp_name);
423	printf(" %5u", snpp->snp_proto);
424	printf(" %6u", snpp->snp_qlimit);
425	printf(" %6s",
426	    (snpp->snp_policy == NETISR_POLICY_SOURCE) ?  "source" :
427	    (snpp->snp_policy == NETISR_POLICY_FLOW) ? "flow" :
428	    (snpp->snp_policy == NETISR_POLICY_CPU) ? "cpu" : "-");
429	printf("   %s%s%s\n",
430	    (snpp->snp_flags & NETISR_SNP_FLAGS_M2CPUID) ?  "C" : "-",
431	    (snpp->snp_flags & NETISR_SNP_FLAGS_DRAINEDCPU) ?  "D" : "-",
432	    (snpp->snp_flags & NETISR_SNP_FLAGS_M2FLOW) ? "F" : "-");
433}
434
435static void
436netisr_print_workstream(struct sysctl_netisr_workstream *snwsp)
437{
438	struct sysctl_netisr_work *snwp;
439	int first;
440	u_int i;
441
442	first = 1;
443	for (i = 0; i < work_array_len; i++) {
444		snwp = &work_array[i];
445		if (snwp->snw_wsid != snwsp->snws_wsid)
446			continue;
447		if (first) {
448			printf("%4u ", snwsp->snws_wsid);
449			printf("%3u ", snwsp->snws_cpu);
450			first = 0;
451		} else
452			printf("%4s %3s ", "", "");
453		printf("%2s", "");
454		printf("%-6s", netisr_proto2name(snwp->snw_proto));
455		printf(" %5u", snwp->snw_len);
456		printf(" %5u", snwp->snw_watermark);
457		printf(" %8ju", snwp->snw_dispatched);
458		printf(" %8ju", snwp->snw_hybrid_dispatched);
459		printf(" %8ju", snwp->snw_qdrops);
460		printf(" %8ju", snwp->snw_queued);
461		printf(" %8ju", snwp->snw_handled);
462		printf("\n");
463	}
464}
465
466void
467netisr_stats(void *kvmd)
468{
469	struct sysctl_netisr_workstream *snwsp;
470	struct sysctl_netisr_proto *snpp;
471	kvm_t *kd = kvmd;
472	u_int i;
473
474	if (live) {
475		netisr_load_sysctl_config();
476		netisr_load_sysctl_proto();
477		netisr_load_sysctl_workstream();
478		netisr_load_sysctl_work();
479	} else {
480		if (kd == NULL)
481			errx(-1, "netisr_stats: !live but !kd");
482		netisr_load_kvm_config(kd);
483		netisr_load_kvm_proto(kd);
484		netisr_load_kvm_workstream(kd);		/* Also does work. */
485	}
486
487	printf("Configuration:\n");
488	printf("%-25s %12s %12s\n", "Setting", "Current", "Limit");
489	printf("%-25s %12u %12u\n", "Thread count", numthreads, maxthreads);
490	printf("%-25s %12u %12u\n", "Default queue limit", defaultqlimit,
491	    maxqlimit);
492	printf("%-25s %12s %12s\n", "Direct dispatch",
493	    direct ? "enabled" : "disabled", "n/a");
494	printf("%-25s %12s %12s\n", "Forced direct dispatch",
495	    direct_force ? "enabled" : "disabled", "n/a");
496	printf("%-25s %12s %12s\n", "Threads bound to CPUs",
497	    bindthreads ? "enabled" : "disabled", "n/a");
498	printf("\n");
499
500	printf("Protocols:\n");
501	printf("%-6s %5s %6s %-6s %-5s\n", "Name", "Proto", "QLimit",
502	    "Policy", "Flags");
503	for (i = 0; i < proto_array_len; i++) {
504		snpp = &proto_array[i];
505		netisr_print_proto(snpp);
506	}
507	printf("\n");
508
509	printf("Workstreams:\n");
510	printf("%4s %3s ", "WSID", "CPU");
511	printf("%2s", "");
512	printf("%-6s %5s %5s %8s %8s %8s %8s %8s\n", "Name", "Len", "WMark",
513	    "Disp'd", "HDisp'd", "QDrops", "Queued", "Handled");
514	for (i = 0; i < workstream_array_len; i++) {
515		snwsp = &workstream_array[i];
516		netisr_print_workstream(snwsp);
517	}
518}
519