xref: /openbsd/usr.bin/systat/mbufs.c (revision 09467b48)
1 /*	$OpenBSD: mbufs.c,v 1.43 2020/06/15 10:54:29 dlg Exp $ */
2 /*
3  * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/signal.h>
19 #include <sys/socket.h>
20 #include <sys/sysctl.h>
21 #include <sys/queue.h>
22 #include <sys/mbuf.h>
23 #include <sys/pool.h>
24 #include <net/if.h>
25 #include <sys/sockio.h>
26 #include <sys/ioctl.h>
27 
28 #include <err.h>
29 #include <errno.h>
30 #include <ifaddrs.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "systat.h"
35 
36 /* pool info */
37 int mbpool_index = -1;
38 int mclpools_index[MCLPOOLS];
39 int mclpool_count = 0;
40 struct kinfo_pool mbpool;
41 u_int mcllivelocks, mcllivelocks_cur, mcllivelocks_diff;
42 
43 /* interfaces */
44 static int num_ifs = 0;
45 struct if_info {
46 	char name[16];
47 	struct if_rxrinfo data;
48 } *interfaces = NULL;
49 
50 static int sock;
51 
52 void print_mb(void);
53 int read_mb(void);
54 int select_mb(void);
55 static void showmbuf(struct if_info *, int, int);
56 
57 /* Define fields */
58 field_def fields_mbuf[] = {
59 	{"IFACE", 8, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
60 	{"RING", 8, 8, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
61 	{"RXDELAY", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
62 	{"TXDELAY", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
63 	{"LIVELOCKS", 5, 10, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
64 	{"SIZE", 3, 5, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
65 	{"ALIVE", 3, 5, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
66 	{"LWM", 3, 5, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
67 	{"HWM", 3, 5, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
68 	{"CWM", 3, 5, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
69 };
70 
71 
72 #define FLD_MB_IFACE	FIELD_ADDR(fields_mbuf,0)
73 #define FLD_MB_RING	FIELD_ADDR(fields_mbuf,1)
74 #define FLD_MB_RXDELAY	FIELD_ADDR(fields_mbuf,2)
75 #define FLD_MB_TXDELAY	FIELD_ADDR(fields_mbuf,3)
76 #define FLD_MB_LLOCKS	FIELD_ADDR(fields_mbuf,4)
77 #define FLD_MB_MSIZE	FIELD_ADDR(fields_mbuf,5)
78 #define FLD_MB_MALIVE	FIELD_ADDR(fields_mbuf,6)
79 #define FLD_MB_MLWM	FIELD_ADDR(fields_mbuf,7)
80 #define FLD_MB_MHWM	FIELD_ADDR(fields_mbuf,8)
81 #define FLD_MB_MCWM	FIELD_ADDR(fields_mbuf,9)
82 
83 
84 /* Define views */
85 field_def *view_mbuf[] = {
86 	FLD_MB_IFACE, FLD_MB_RING,
87 	FLD_MB_LLOCKS, FLD_MB_MSIZE, FLD_MB_MALIVE, FLD_MB_MLWM, FLD_MB_MHWM,
88 	FLD_MB_MCWM, NULL
89 };
90 
91 /* Define view managers */
92 
93 struct view_manager mbuf_mgr = {
94 	"Mbufs", select_mb, read_mb, NULL, print_header,
95 	print_mb, keyboard_callback, NULL, NULL
96 };
97 
98 field_view views_mb[] = {
99 	{view_mbuf, "mbufs", '4', &mbuf_mgr},
100 	{NULL, NULL, 0, NULL}
101 };
102 
103 
104 int
105 initmembufs(void)
106 {
107 	struct if_rxring_info *ifr;
108 	field_view *v;
109 	int i, mib[4], npools;
110 	struct kinfo_pool pool;
111 	char pname[32];
112 	size_t size;
113 
114 	sock = socket(AF_INET, SOCK_DGRAM, 0);
115 	if (sock == -1) {
116 		err(1, "socket()");
117 		/* NOTREACHED */
118 	}
119 
120 	/* set up the "System" interface */
121 
122 	interfaces = calloc(1, sizeof(*interfaces));
123 	if (interfaces == NULL)
124 		err(1, "calloc: interfaces");
125 
126 	ifr = calloc(MCLPOOLS, sizeof(*ifr));
127 	if (ifr == NULL)
128 		err(1, "calloc: system pools");
129 
130 	strlcpy(interfaces[0].name, "System", sizeof(interfaces[0].name));
131 	interfaces[0].data.ifri_total = MCLPOOLS;
132 	interfaces[0].data.ifri_entries = ifr;
133 	num_ifs = 1;
134 
135 	/* go through all pools to identify mbuf and cluster pools */
136 
137 	mib[0] = CTL_KERN;
138 	mib[1] = KERN_POOL;
139 	mib[2] = KERN_POOL_NPOOLS;
140 	size = sizeof(npools);
141 
142 	if (sysctl(mib, 3, &npools, &size, NULL, 0) == -1) {
143 		err(1, "sysctl(KERN_POOL_NPOOLS)");
144 		/* NOTREACHED */
145 	}
146 
147 	for (i = 1; i <= npools; i++) {
148 		mib[0] = CTL_KERN;
149 		mib[1] = KERN_POOL;
150 		mib[2] = KERN_POOL_NAME;
151 		mib[3] = i;
152 		size = sizeof(pname);
153 		if (sysctl(mib, 4, &pname, &size, NULL, 0) == -1) {
154 			continue;
155 		}
156 
157 		if (strcmp(pname, "mbufpl") == 0) {
158 			mbpool_index = i;
159 			continue;
160 		}
161 
162 		if (strncmp(pname, "mcl", 3) != 0)
163 			continue;
164 
165 		if (mclpool_count == MCLPOOLS) {
166 			warnx("mbufs: Too many mcl* pools");
167 			break;
168 		}
169 
170 		mib[2] = KERN_POOL_POOL;
171 		size = sizeof(pool);
172 
173 		if (sysctl(mib, 4, &pool, &size, NULL, 0) == -1) {
174 			err(1, "sysctl(KERN_POOL_POOL, %d)", i);
175 			/* NOTREACHED */
176 		}
177 
178 		strlcpy(ifr[mclpool_count].ifr_name, pname,
179 		    sizeof(ifr[mclpool_count].ifr_name));
180 		ifr[mclpool_count].ifr_size = pool.pr_size;
181 
182 		mclpools_index[mclpool_count++] = i;
183 	}
184 
185 	if (mclpool_count != MCLPOOLS)
186 		warnx("mbufs: Unable to read all %d mcl* pools", MCLPOOLS);
187 
188 	/* add view to the engine */
189 	for (v = views_mb; v->name != NULL; v++)
190 		add_view(v);
191 
192 	/* finally read it once */
193 	read_mb();
194 
195 	return(1);
196 }
197 
198 int
199 select_mb(void)
200 {
201 	num_disp = 0;
202 	return (0);
203 }
204 
205 int
206 read_mb(void)
207 {
208 	struct kinfo_pool pool;
209 	struct ifaddrs *ifap = NULL, *ifa;
210 	struct if_info *ifi;
211 	struct if_rxring_info *ifr;
212 	int mib[4];
213 	int i, p, nif, ret = 1, rv;
214 	u_int rings;
215 	size_t size;
216 
217 	mib[0] = CTL_KERN;
218 	mib[1] = KERN_NETLIVELOCKS;
219 	size = sizeof(mcllivelocks_cur);
220 	if (sysctl(mib, 2, &mcllivelocks_cur, &size, NULL, 0) == -1 &&
221 	    errno != EOPNOTSUPP) {
222 		error("sysctl(KERN_NETLIVELOCKS)");
223 		goto exit;
224 	}
225 	mcllivelocks_diff = mcllivelocks_cur - mcllivelocks;
226 	mcllivelocks = mcllivelocks_cur;
227 
228 	num_disp = 0;
229 	if (getifaddrs(&ifap)) {
230 		error("getifaddrs: %s", strerror(errno));
231 		return (1);
232 	}
233 
234 	nif = 1;
235 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
236 		if (ifa->ifa_addr == NULL ||
237 		    ifa->ifa_addr->sa_family != AF_LINK)
238 			continue;
239 
240 		nif++;
241 	}
242 
243 	if (num_ifs < nif) {
244 		ifi = reallocarray(interfaces, nif, sizeof(*interfaces));
245 		if (ifi == NULL) {
246 			error("reallocarray: %d interfaces", nif);
247 			goto exit;
248 		}
249 
250 		interfaces = ifi;
251 		while (num_ifs < nif)
252 			memset(&interfaces[num_ifs++], 0, sizeof(*interfaces));
253 	}
254 
255 	/* Fill in the "real" interfaces */
256 	ifi = interfaces + 1;
257 
258 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
259 		if (ifa->ifa_addr == NULL ||
260 		    ifa->ifa_addr->sa_family != AF_LINK)
261 			continue;
262 
263 		strlcpy(ifi->name, ifa->ifa_name, sizeof(ifi->name));
264 		for (;;) {
265 			struct ifreq ifreq;
266 			rings = ifi->data.ifri_total;
267 
268 			memset(&ifreq, 0, sizeof(ifreq));
269 			strlcpy(ifreq.ifr_name, ifa->ifa_name,
270 			    sizeof(ifreq.ifr_name));
271 			ifreq.ifr_data = (caddr_t)&ifi->data;
272 
273 			rv = ioctl(sock, SIOCGIFRXR, &ifreq);
274 			if (rv == -1) {
275 				if (errno == ENOTTY) {
276 					free(ifi->data.ifri_entries);
277 					ifi->data.ifri_total = 0;
278 					ifi->data.ifri_entries = NULL;
279 					break;
280 				}
281 
282 				error("ioctl(SIOCGIFRXR) %s", strerror(errno));
283 				break;
284 			}
285 
286 			if (rings >= ifi->data.ifri_total)
287 				break;
288 
289 			ifr = reallocarray(ifi->data.ifri_entries,
290 			    ifi->data.ifri_total, sizeof(*ifr));
291 			if (ifr == NULL) {
292 				ifi->data.ifri_total = rings;
293 				error("reallocarray: %u rings",
294 				    ifi->data.ifri_total);
295 				goto exit;
296 			}
297 
298 			ifi->data.ifri_entries = ifr;
299 		}
300 
301 		ifi++;
302 	}
303 
304 	/* Fill in the "System" entry from pools */
305 
306 	mib[0] = CTL_KERN;
307 	mib[1] = KERN_POOL;
308 	mib[2] = KERN_POOL_POOL;
309 	mib[3] = mbpool_index;
310 	size = sizeof(mbpool);
311 
312 	if (sysctl(mib, 4, &mbpool, &size, NULL, 0) == -1) {
313 		error("sysctl(KERN_POOL_POOL, %d)", mib[3]);
314 		goto exit;
315 	}
316 
317 	for (i = 0; i < mclpool_count; i++) {
318 		ifr = &interfaces[0].data.ifri_entries[i];
319 
320 		mib[3] = mclpools_index[i];
321 		size = sizeof(pool);
322 
323 		if (sysctl(mib, 4, &pool, &size, NULL, 0) == -1) {
324 			error("sysctl(KERN_POOL_POOL, %d)", mib[3]);
325 			continue;
326 		}
327 
328 		ifr->ifr_info.rxr_alive = pool.pr_nget - pool.pr_nput;
329 		ifr->ifr_info.rxr_hwm = pool.pr_hiwat;
330 	}
331 
332 	num_disp = 1;
333 	ret = 0;
334 
335 	for (i = 0; i < num_ifs; i++) {
336 		struct if_info *ifi = &interfaces[i];
337 		int pnd = num_disp;
338 		for (p = 0; p < ifi->data.ifri_total; p++) {
339 			ifr = &ifi->data.ifri_entries[p];
340 			if (ifr->ifr_info.rxr_alive == 0)
341 				continue;
342 			num_disp++;
343 		}
344 		if (i && pnd == num_disp)
345 			num_disp++;
346 	}
347 
348  exit:
349 	if (ifap)
350 		freeifaddrs(ifap);
351 	return (ret);
352 }
353 
354 void
355 print_mb(void)
356 {
357 	int i, p, n, count = 0;
358 
359 	showmbuf(interfaces, -1, 1);
360 
361 	for (n = i = 0; i < num_ifs; i++) {
362 		struct if_info *ifi = &interfaces[i];
363 		int pcnt = count;
364 		int showif = i;
365 
366 		if (maxprint > 0 && count >= maxprint)
367 			return;
368 
369 		for (p = 0; p < ifi->data.ifri_total; p++) {
370 			struct if_rxring_info *ifr = &ifi->data.ifri_entries[p];
371 			if (ifr->ifr_info.rxr_hwm == 0)
372 				continue;
373 			if (n++ >= dispstart) {
374 				showmbuf(ifi, p, showif);
375 				showif = 0;
376 				count++;
377 			}
378 		}
379 
380 		if (i && pcnt == count) {
381 			/* only print the first line */
382 			if (n++ >= dispstart) {
383 				showmbuf(ifi, -1, 1);
384 				count++;
385 			}
386 		}
387 	}
388 }
389 
390 
391 static void
392 showmbuf(struct if_info *ifi, int p, int showif)
393 {
394 	if (showif)
395 		print_fld_str(FLD_MB_IFACE, ifi->name);
396 
397 	if (p == -1 && ifi == interfaces) {
398 		print_fld_str(FLD_MB_RING, "mbufs");
399 		print_fld_uint(FLD_MB_LLOCKS, mcllivelocks_diff);
400 		print_fld_size(FLD_MB_MSIZE, mbpool.pr_size);
401 		print_fld_size(FLD_MB_MALIVE, mbpool.pr_nget - mbpool.pr_nput);
402 		print_fld_size(FLD_MB_MHWM, mbpool.pr_hiwat);
403 	}
404 
405 	if (p >= 0 && p < mclpool_count) {
406 		struct if_rxring_info *ifr = &ifi->data.ifri_entries[p];
407 		struct if_rxring *rxr= &ifr->ifr_info;
408 		print_fld_str(FLD_MB_RING, ifr->ifr_name);
409 		print_fld_uint(FLD_MB_MSIZE, ifr->ifr_size);
410 		print_fld_uint(FLD_MB_MALIVE, rxr->rxr_alive);
411 		if (rxr->rxr_lwm)
412 			print_fld_size(FLD_MB_MLWM, rxr->rxr_lwm);
413 		if (rxr->rxr_hwm)
414 			print_fld_size(FLD_MB_MHWM, rxr->rxr_hwm);
415 		if (rxr->rxr_cwm)
416 			print_fld_size(FLD_MB_MCWM, rxr->rxr_cwm);
417 	}
418 
419 	end_line();
420 }
421