1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1983, 1988, 1993
5 *	The Regents of the University of California.
6 * Copyright (c) 2005 Robert N. M. Watson
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by the University of
20 *	California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#include <sys/param.h>
39#include <sys/mbuf.h>
40#include <sys/protosw.h>
41#include <sys/sf_buf.h>
42#include <sys/socket.h>
43#include <sys/socketvar.h>
44#include <sys/sysctl.h>
45
46#include <err.h>
47#include <kvm.h>
48#include <memstat.h>
49#include <stdint.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <stdbool.h>
53#include <string.h>
54#include <libxo/xo.h>
55#include "netstat.h"
56
57/*
58 * Print mbuf statistics.
59 */
60void
61mbpr(void *kvmd, u_long mbaddr)
62{
63	struct memory_type_list *mtlp;
64	struct memory_type *mtp;
65	uintmax_t mbuf_count, mbuf_bytes, mbuf_free, mbuf_failures, mbuf_size;
66	uintmax_t mbuf_sleeps;
67	uintmax_t cluster_count, cluster_limit, cluster_free;
68	uintmax_t cluster_failures, cluster_size, cluster_sleeps;
69	uintmax_t packet_count, packet_bytes, packet_free, packet_failures;
70	uintmax_t packet_sleeps;
71	uintmax_t tag_bytes;
72	uintmax_t jumbop_count, jumbop_limit, jumbop_free;
73	uintmax_t jumbop_failures, jumbop_sleeps, jumbop_size;
74	uintmax_t jumbo9_count, jumbo9_limit, jumbo9_free;
75	uintmax_t jumbo9_failures, jumbo9_sleeps, jumbo9_size;
76	uintmax_t jumbo16_count, jumbo16_limit, jumbo16_free;
77	uintmax_t jumbo16_failures, jumbo16_sleeps, jumbo16_size;
78	uintmax_t bytes_inuse, bytes_incache, bytes_total;
79	int nsfbufs, nsfbufspeak, nsfbufsused;
80	struct sfstat sfstat;
81	size_t mlen;
82	int error;
83
84	mtlp = memstat_mtl_alloc();
85	if (mtlp == NULL) {
86		xo_warn("memstat_mtl_alloc");
87		return;
88	}
89
90	/*
91	 * Use memstat_*_all() because some mbuf-related memory is in uma(9),
92	 * and some malloc(9).
93	 */
94	if (live) {
95		if (memstat_sysctl_all(mtlp, 0) < 0) {
96			xo_warnx("memstat_sysctl_all: %s",
97			    memstat_strerror(memstat_mtl_geterror(mtlp)));
98			goto out;
99		}
100	} else {
101		if (memstat_kvm_all(mtlp, kvmd) < 0) {
102			error = memstat_mtl_geterror(mtlp);
103			if (error == MEMSTAT_ERROR_KVM)
104				xo_warnx("memstat_kvm_all: %s",
105				    kvm_geterr(kvmd));
106			else
107				xo_warnx("memstat_kvm_all: %s",
108				    memstat_strerror(error));
109			goto out;
110		}
111	}
112
113	mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_MEM_NAME);
114	if (mtp == NULL) {
115		xo_warnx("memstat_mtl_find: zone %s not found", MBUF_MEM_NAME);
116		goto out;
117	}
118	mbuf_count = memstat_get_count(mtp);
119	mbuf_bytes = memstat_get_bytes(mtp);
120	mbuf_free = memstat_get_free(mtp);
121	mbuf_failures = memstat_get_failures(mtp);
122	mbuf_sleeps = memstat_get_sleeps(mtp);
123	mbuf_size = memstat_get_size(mtp);
124
125	mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_PACKET_MEM_NAME);
126	if (mtp == NULL) {
127		xo_warnx("memstat_mtl_find: zone %s not found",
128		    MBUF_PACKET_MEM_NAME);
129		goto out;
130	}
131	packet_count = memstat_get_count(mtp);
132	packet_bytes = memstat_get_bytes(mtp);
133	packet_free = memstat_get_free(mtp);
134	packet_sleeps = memstat_get_sleeps(mtp);
135	packet_failures = memstat_get_failures(mtp);
136
137	mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_CLUSTER_MEM_NAME);
138	if (mtp == NULL) {
139		xo_warnx("memstat_mtl_find: zone %s not found",
140		    MBUF_CLUSTER_MEM_NAME);
141		goto out;
142	}
143	cluster_count = memstat_get_count(mtp);
144	cluster_limit = memstat_get_countlimit(mtp);
145	cluster_free = memstat_get_free(mtp);
146	cluster_failures = memstat_get_failures(mtp);
147	cluster_sleeps = memstat_get_sleeps(mtp);
148	cluster_size = memstat_get_size(mtp);
149
150	mtp = memstat_mtl_find(mtlp, ALLOCATOR_MALLOC, MBUF_TAG_MEM_NAME);
151	if (mtp == NULL) {
152		xo_warnx("memstat_mtl_find: malloc type %s not found",
153		    MBUF_TAG_MEM_NAME);
154		goto out;
155	}
156	tag_bytes = memstat_get_bytes(mtp);
157
158	mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_JUMBOP_MEM_NAME);
159	if (mtp == NULL) {
160		xo_warnx("memstat_mtl_find: zone %s not found",
161		    MBUF_JUMBOP_MEM_NAME);
162		goto out;
163	}
164	jumbop_count = memstat_get_count(mtp);
165	jumbop_limit = memstat_get_countlimit(mtp);
166	jumbop_free = memstat_get_free(mtp);
167	jumbop_failures = memstat_get_failures(mtp);
168	jumbop_sleeps = memstat_get_sleeps(mtp);
169	jumbop_size = memstat_get_size(mtp);
170
171	mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_JUMBO9_MEM_NAME);
172	if (mtp == NULL) {
173		xo_warnx("memstat_mtl_find: zone %s not found",
174		    MBUF_JUMBO9_MEM_NAME);
175		goto out;
176	}
177	jumbo9_count = memstat_get_count(mtp);
178	jumbo9_limit = memstat_get_countlimit(mtp);
179	jumbo9_free = memstat_get_free(mtp);
180	jumbo9_failures = memstat_get_failures(mtp);
181	jumbo9_sleeps = memstat_get_sleeps(mtp);
182	jumbo9_size = memstat_get_size(mtp);
183
184	mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_JUMBO16_MEM_NAME);
185	if (mtp == NULL) {
186		xo_warnx("memstat_mtl_find: zone %s not found",
187		    MBUF_JUMBO16_MEM_NAME);
188		goto out;
189	}
190	jumbo16_count = memstat_get_count(mtp);
191	jumbo16_limit = memstat_get_countlimit(mtp);
192	jumbo16_free = memstat_get_free(mtp);
193	jumbo16_failures = memstat_get_failures(mtp);
194	jumbo16_sleeps = memstat_get_sleeps(mtp);
195	jumbo16_size = memstat_get_size(mtp);
196
197	xo_open_container("mbuf-statistics");
198
199	xo_emit("{:mbuf-current/%ju}/{:mbuf-cache/%ju}/{:mbuf-total/%ju} "
200	    "{N:mbufs in use (current\\/cache\\/total)}\n",
201	    mbuf_count + packet_count, mbuf_free + packet_free,
202	    mbuf_count + packet_count + mbuf_free + packet_free);
203
204	xo_emit("{:cluster-current/%ju}/{:cluster-cache/%ju}/"
205	    "{:cluster-total/%ju}/{:cluster-max/%ju} "
206	    "{N:mbuf clusters in use (current\\/cache\\/total\\/max)}\n",
207	    cluster_count - packet_free, cluster_free + packet_free,
208	    cluster_count + cluster_free, cluster_limit);
209
210	xo_emit("{:packet-count/%ju}/{:packet-free/%ju} "
211	    "{N:mbuf+clusters out of packet secondary zone in use "
212	    "(current\\/cache)}\n",
213	    packet_count, packet_free);
214
215	xo_emit("{:jumbo-count/%ju}/{:jumbo-cache/%ju}/{:jumbo-total/%ju}/"
216	    "{:jumbo-max/%ju} {:jumbo-page-size/%ju}{U:k} {N:(page size)} "
217	    "{N:jumbo clusters in use (current\\/cache\\/total\\/max)}\n",
218	    jumbop_count, jumbop_free, jumbop_count + jumbop_free,
219	    jumbop_limit, jumbop_size / 1024);
220
221	xo_emit("{:jumbo9-count/%ju}/{:jumbo9-cache/%ju}/"
222	    "{:jumbo9-total/%ju}/{:jumbo9-max/%ju} "
223	    "{N:9k jumbo clusters in use (current\\/cache\\/total\\/max)}\n",
224	    jumbo9_count, jumbo9_free, jumbo9_count + jumbo9_free,
225	    jumbo9_limit);
226
227	xo_emit("{:jumbo16-count/%ju}/{:jumbo16-cache/%ju}/"
228	    "{:jumbo16-total/%ju}/{:jumbo16-limit/%ju} "
229	    "{N:16k jumbo clusters in use (current\\/cache\\/total\\/max)}\n",
230	    jumbo16_count, jumbo16_free, jumbo16_count + jumbo16_free,
231	    jumbo16_limit);
232
233#if 0
234	xo_emit("{:tag-count/%ju} {N:mbuf tags in use}\n", tag_count);
235#endif
236
237	/*-
238	 * Calculate in-use bytes as:
239	 * - straight mbuf memory
240	 * - mbuf memory in packets
241	 * - the clusters attached to packets
242	 * - and the rest of the non-packet-attached clusters.
243	 * - m_tag memory
244	 * This avoids counting the clusters attached to packets in the cache.
245	 * This currently excludes sf_buf space.
246	 */
247	bytes_inuse =
248	    mbuf_bytes +			/* straight mbuf memory */
249	    packet_bytes +			/* mbufs in packets */
250	    (packet_count * cluster_size) +	/* clusters in packets */
251	    /* other clusters */
252	    ((cluster_count - packet_count - packet_free) * cluster_size) +
253	    tag_bytes +
254	    (jumbop_count * jumbop_size) +	/* jumbo clusters */
255	    (jumbo9_count * jumbo9_size) +
256	    (jumbo16_count * jumbo16_size);
257
258	/*
259	 * Calculate in-cache bytes as:
260	 * - cached straught mbufs
261	 * - cached packet mbufs
262	 * - cached packet clusters
263	 * - cached straight clusters
264	 * This currently excludes sf_buf space.
265	 */
266	bytes_incache =
267	    (mbuf_free * mbuf_size) +		/* straight free mbufs */
268	    (packet_free * mbuf_size) +		/* mbufs in free packets */
269	    (packet_free * cluster_size) +	/* clusters in free packets */
270	    (cluster_free * cluster_size) +	/* free clusters */
271	    (jumbop_free * jumbop_size) +	/* jumbo clusters */
272	    (jumbo9_free * jumbo9_size) +
273	    (jumbo16_free * jumbo16_size);
274
275	/*
276	 * Total is bytes in use + bytes in cache.  This doesn't take into
277	 * account various other misc data structures, overhead, etc, but
278	 * gives the user something useful despite that.
279	 */
280	bytes_total = bytes_inuse + bytes_incache;
281
282	xo_emit("{:bytes-in-use/%ju}{U:K}/{:bytes-in-cache/%ju}{U:K}/"
283	    "{:bytes-total/%ju}{U:K} "
284	    "{N:bytes allocated to network (current\\/cache\\/total)}\n",
285	    bytes_inuse / 1024, bytes_incache / 1024, bytes_total / 1024);
286
287	xo_emit("{:mbuf-failures/%ju}/{:cluster-failures/%ju}/"
288	    "{:packet-failures/%ju} {N:requests for mbufs denied "
289	    "(mbufs\\/clusters\\/mbuf+clusters)}\n",
290	    mbuf_failures, cluster_failures, packet_failures);
291	xo_emit("{:mbuf-sleeps/%ju}/{:cluster-sleeps/%ju}/{:packet-sleeps/%ju} "
292	    "{N:requests for mbufs delayed "
293	    "(mbufs\\/clusters\\/mbuf+clusters)}\n",
294	    mbuf_sleeps, cluster_sleeps, packet_sleeps);
295
296	xo_emit("{:jumbop-sleeps/%ju}/{:jumbo9-sleeps/%ju}/"
297	    "{:jumbo16-sleeps/%ju} {N:/requests for jumbo clusters delayed "
298	    "(%juk\\/9k\\/16k)}\n",
299	    jumbop_sleeps, jumbo9_sleeps, jumbo16_sleeps, jumbop_size / 1024);
300	xo_emit("{:jumbop-failures/%ju}/{:jumbo9-failures/%ju}/"
301	    "{:jumbo16-failures/%ju} {N:/requests for jumbo clusters denied "
302	    "(%juk\\/9k\\/16k)}\n",
303	    jumbop_failures, jumbo9_failures, jumbo16_failures,
304	    jumbop_size / 1024);
305
306	mlen = sizeof(nsfbufs);
307	if (live &&
308	    sysctlbyname("kern.ipc.nsfbufs", &nsfbufs, &mlen, NULL, 0) == 0 &&
309	    sysctlbyname("kern.ipc.nsfbufsused", &nsfbufsused, &mlen,
310	    NULL, 0) == 0 &&
311	    sysctlbyname("kern.ipc.nsfbufspeak", &nsfbufspeak, &mlen,
312	    NULL, 0) == 0)
313		xo_emit("{:nsfbufs-current/%d}/{:nsfbufs-peak/%d}/"
314		    "{:nsfbufs/%d} "
315		    "{N:sfbufs in use (current\\/peak\\/max)}\n",
316		    nsfbufsused, nsfbufspeak, nsfbufs);
317
318	if (fetch_stats("kern.ipc.sfstat", mbaddr, &sfstat, sizeof(sfstat),
319	    kread_counters) != 0)
320		goto out;
321
322        xo_emit("{:sendfile-syscalls/%ju} {N:sendfile syscalls}\n",
323	    (uintmax_t)sfstat.sf_syscalls);
324        xo_emit("{:sendfile-no-io/%ju} "
325	    "{N:sendfile syscalls completed without I\\/O request}\n",
326            (uintmax_t)sfstat.sf_noiocnt);
327	xo_emit("{:sendfile-io-count/%ju} "
328	    "{N:requests for I\\/O initiated by sendfile}\n",
329	    (uintmax_t)sfstat.sf_iocnt);
330        xo_emit("{:sendfile-pages-sent/%ju} "
331	    "{N:pages read by sendfile as part of a request}\n",
332            (uintmax_t)sfstat.sf_pages_read);
333        xo_emit("{:sendfile-pages-valid/%ju} "
334	    "{N:pages were valid at time of a sendfile request}\n",
335            (uintmax_t)sfstat.sf_pages_valid);
336        xo_emit("{:sendfile-pages-bogus/%ju} "
337	    "{N:pages were valid and substituted to bogus page}\n",
338            (uintmax_t)sfstat.sf_pages_bogus);
339        xo_emit("{:sendfile-requested-readahead/%ju} "
340	    "{N:pages were requested for read ahead by applications}\n",
341            (uintmax_t)sfstat.sf_rhpages_requested);
342        xo_emit("{:sendfile-readahead/%ju} "
343	    "{N:pages were read ahead by sendfile}\n",
344            (uintmax_t)sfstat.sf_rhpages_read);
345	xo_emit("{:sendfile-busy-encounters/%ju} "
346	    "{N:times sendfile encountered an already busy page}\n",
347	    (uintmax_t)sfstat.sf_busy);
348	xo_emit("{:sfbufs-alloc-failed/%ju} {N:requests for sfbufs denied}\n",
349	    (uintmax_t)sfstat.sf_allocfail);
350	xo_emit("{:sfbufs-alloc-wait/%ju} {N:requests for sfbufs delayed}\n",
351	    (uintmax_t)sfstat.sf_allocwait);
352out:
353	xo_close_container("mbuf-statistics");
354	memstat_mtl_free(mtlp);
355}
356