1/*	$OpenBSD: mbufs.c,v 1.44 2020/12/22 06:57:18 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 */
37int mbpool_index = -1;
38int mclpools_index[MCLPOOLS];
39int mclpool_count = 0;
40struct kinfo_pool mbpool;
41u_int mcllivelocks, mcllivelocks_cur, mcllivelocks_diff;
42
43/* interfaces */
44static int num_ifs = 0;
45struct if_info {
46	char name[16];
47	struct if_rxrinfo data;
48} *interfaces = NULL;
49
50static int sock;
51
52void print_mb(void);
53int read_mb(void);
54int select_mb(void);
55static void showmbuf(struct if_info *, int, int);
56
57/* Define fields */
58field_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 */
85field_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
93struct view_manager mbuf_mgr = {
94	"Mbufs", select_mb, read_mb, NULL, print_header,
95	print_mb, keyboard_callback, NULL, NULL
96};
97
98field_view views_mb[] = {
99	{view_mbuf, "mbufs", '4', &mbuf_mgr},
100	{NULL, NULL, 0, NULL}
101};
102
103
104int
105initmembufs(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
198int
199select_mb(void)
200{
201	num_disp = 0;
202	return (0);
203}
204
205int
206read_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
354void
355print_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
391static void
392showmbuf(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 < ifi->data.ifri_total) {
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