xref: /freebsd/lib/libkvm/kvm_getswapinfo.c (revision 6bff85ff9a293c67840c40974e821c9fc76bff71)
15e53a4f9SPedro F. Giffuni /*-
25e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
35e53a4f9SPedro F. Giffuni  *
4e92324a2SMatthew Dillon  * Copyright (c) 1999, Matthew Dillon.  All Rights Reserved.
59e749cc9SWarner Losh  * Copyright (c) 2001, Thomas Moestl.  All Rights Reserved.
6e92324a2SMatthew Dillon  *
7e92324a2SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
89e749cc9SWarner Losh  * modification, are permitted provided that the following conditions
99e749cc9SWarner Losh  * are met:
109e749cc9SWarner Losh  * 1. Redistributions of source code must retain the above copyright
119e749cc9SWarner Losh  *    notice, this list of conditions and the following disclaimer.
129e749cc9SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
139e749cc9SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
149e749cc9SWarner Losh  *    documentation and/or other materials provided with the distribution.
159e749cc9SWarner Losh  *
169e749cc9SWarner Losh  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
179e749cc9SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
189e749cc9SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
199e749cc9SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
209e749cc9SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
219e749cc9SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
229e749cc9SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
239e749cc9SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
249e749cc9SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
259e749cc9SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
269e749cc9SWarner Losh  * SUCH DAMAGE.
27e92324a2SMatthew Dillon  */
28e92324a2SMatthew Dillon 
29e67f5b9fSMatthew Dillon #include <sys/cdefs.h>
30e67f5b9fSMatthew Dillon __FBSDID("$FreeBSD$");
31e92324a2SMatthew Dillon 
32e92324a2SMatthew Dillon #include <sys/param.h>
33e92324a2SMatthew Dillon #include <sys/time.h>
34e92324a2SMatthew Dillon #include <sys/stat.h>
35e92324a2SMatthew Dillon #include <sys/blist.h>
36*6bff85ffSDag-Erling Smørgrav #include <sys/queue.h>
37ae3a37adSRobert Watson #include <sys/sysctl.h>
38e92324a2SMatthew Dillon 
39e8865cafSJohn Baldwin #include <vm/swap_pager.h>
40e04a7c4aSThomas Moestl #include <vm/vm_param.h>
41e04a7c4aSThomas Moestl 
42e92324a2SMatthew Dillon #include <err.h>
43e04a7c4aSThomas Moestl #include <errno.h>
44e92324a2SMatthew Dillon #include <fcntl.h>
45e92324a2SMatthew Dillon #include <kvm.h>
46e92324a2SMatthew Dillon #include <nlist.h>
471a37aa56SDavid E. O'Brien #include <paths.h>
48e92324a2SMatthew Dillon #include <stdio.h>
49e92324a2SMatthew Dillon #include <stdlib.h>
50e92324a2SMatthew Dillon #include <string.h>
51e92324a2SMatthew Dillon #include <unistd.h>
52ae3a37adSRobert Watson #include <limits.h>
53ae3a37adSRobert Watson 
54ae3a37adSRobert Watson #include "kvm_private.h"
55e92324a2SMatthew Dillon 
56e8865cafSJohn Baldwin static struct nlist kvm_swap_nl[] = {
57c10970ddSUlrich Spörlein 	{ .n_name = "_swtailq" },	/* list of swap devices and sizes */
58c10970ddSUlrich Spörlein 	{ .n_name = "_dmmax" },		/* maximum size of a swap block */
59c10970ddSUlrich Spörlein 	{ .n_name = NULL }
60e8865cafSJohn Baldwin };
61e8865cafSJohn Baldwin 
62e8865cafSJohn Baldwin #define NL_SWTAILQ	0
63e8865cafSJohn Baldwin #define NL_DMMAX	1
64e92324a2SMatthew Dillon 
65e92324a2SMatthew Dillon static int kvm_swap_nl_cached = 0;
66ae3a37adSRobert Watson static int unswdev;  /* number of found swap dev's */
67e92324a2SMatthew Dillon static int dmmax;
68e92324a2SMatthew Dillon 
69e8865cafSJohn Baldwin static int  kvm_getswapinfo_kvm(kvm_t *, struct kvm_swap *, int, int);
70ae3a37adSRobert Watson static int  kvm_getswapinfo_sysctl(kvm_t *, struct kvm_swap *, int, int);
71e8865cafSJohn Baldwin static int  nlist_init(kvm_t *);
72c10970ddSUlrich Spörlein static int  getsysctl(kvm_t *, const char *, void *, size_t);
73e92324a2SMatthew Dillon 
74e8865cafSJohn Baldwin #define KREAD(kd, addr, obj) \
75e8865cafSJohn Baldwin 	(kvm_read(kd, addr, (char *)(obj), sizeof(*obj)) != sizeof(*obj))
76e8865cafSJohn Baldwin #define	KGET(idx, var)							\
77e8865cafSJohn Baldwin 	KGET2(kvm_swap_nl[(idx)].n_value, var, kvm_swap_nl[(idx)].n_name)
78e8865cafSJohn Baldwin #define KGET2(addr, var, msg)						\
79e8865cafSJohn Baldwin 	if (KREAD(kd, (u_long)(addr), (var))) {				\
80e8865cafSJohn Baldwin 		_kvm_err(kd, kd->program, "cannot read %s", msg);	\
81e8865cafSJohn Baldwin 		return (-1);						\
82e8865cafSJohn Baldwin 	}
83e8865cafSJohn Baldwin 
84ae3a37adSRobert Watson #define GETSWDEVNAME(dev, str, flags)					\
85ae3a37adSRobert Watson 	if (dev == NODEV) {						\
86ae3a37adSRobert Watson 		strlcpy(str, "[NFS swap]", sizeof(str));		\
87ae3a37adSRobert Watson 	} else {							\
88ae3a37adSRobert Watson 		snprintf(						\
89ae3a37adSRobert Watson 		    str, sizeof(str),"%s%s",				\
90ae3a37adSRobert Watson 		    ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),	\
91ae3a37adSRobert Watson 		    devname(dev, S_IFCHR)				\
92ae3a37adSRobert Watson 		);							\
93ae3a37adSRobert Watson 	}
94ae3a37adSRobert Watson 
95e92324a2SMatthew Dillon int
96c10970ddSUlrich Spörlein kvm_getswapinfo(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags)
97c10970ddSUlrich Spörlein {
98e92324a2SMatthew Dillon 
99e92324a2SMatthew Dillon 	/*
100e92324a2SMatthew Dillon 	 * clear cache
101e92324a2SMatthew Dillon 	 */
102e92324a2SMatthew Dillon 	if (kd == NULL) {
103e92324a2SMatthew Dillon 		kvm_swap_nl_cached = 0;
104e92324a2SMatthew Dillon 		return(0);
105e92324a2SMatthew Dillon 	}
106e92324a2SMatthew Dillon 
107ae3a37adSRobert Watson 	if (ISALIVE(kd)) {
108ae3a37adSRobert Watson 		return kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
109ae3a37adSRobert Watson 	} else {
110e8865cafSJohn Baldwin 		return kvm_getswapinfo_kvm(kd, swap_ary, swap_max, flags);
111ae3a37adSRobert Watson 	}
112ae3a37adSRobert Watson }
113ae3a37adSRobert Watson 
114e8865cafSJohn Baldwin int
115c10970ddSUlrich Spörlein kvm_getswapinfo_kvm(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max,
116c10970ddSUlrich Spörlein     int flags)
117c10970ddSUlrich Spörlein {
118eca80cd0SPedro F. Giffuni 	int i;
119eca80cd0SPedro F. Giffuni 	swblk_t ttl;
120e8865cafSJohn Baldwin 	TAILQ_HEAD(, swdevt) swtailq;
121e8865cafSJohn Baldwin 	struct swdevt *sp, swinfo;
122e8865cafSJohn Baldwin 	struct kvm_swap tot;
123e8865cafSJohn Baldwin 
1247f911abeSJohn Baldwin 	if (!kd->arch->ka_native(kd)) {
1257f911abeSJohn Baldwin 		_kvm_err(kd, kd->program,
1267f911abeSJohn Baldwin 		    "cannot read swapinfo from non-native core");
1277f911abeSJohn Baldwin 		return (-1);
1287f911abeSJohn Baldwin 	}
1297f911abeSJohn Baldwin 
130e8865cafSJohn Baldwin 	if (!nlist_init(kd))
131e8865cafSJohn Baldwin 		return (-1);
132e8865cafSJohn Baldwin 
133e8865cafSJohn Baldwin 	bzero(&tot, sizeof(tot));
134e8865cafSJohn Baldwin 	KGET(NL_SWTAILQ, &swtailq);
135e8865cafSJohn Baldwin 	sp = TAILQ_FIRST(&swtailq);
136e8865cafSJohn Baldwin 	for (i = 0; sp != NULL; i++) {
137e8865cafSJohn Baldwin 		KGET2(sp, &swinfo, "swinfo");
138e8865cafSJohn Baldwin 		ttl = swinfo.sw_nblks - dmmax;
139e8865cafSJohn Baldwin 		if (i < swap_max - 1) {
140e8865cafSJohn Baldwin 			bzero(&swap_ary[i], sizeof(swap_ary[i]));
141e8865cafSJohn Baldwin 			swap_ary[i].ksw_total = ttl;
142e8865cafSJohn Baldwin 			swap_ary[i].ksw_used = swinfo.sw_used;
143e8865cafSJohn Baldwin 			swap_ary[i].ksw_flags = swinfo.sw_flags;
144e8865cafSJohn Baldwin 			GETSWDEVNAME(swinfo.sw_dev, swap_ary[i].ksw_devname,
145e8865cafSJohn Baldwin 			     flags);
146e8865cafSJohn Baldwin 		}
147e8865cafSJohn Baldwin 		tot.ksw_total += ttl;
148e8865cafSJohn Baldwin 		tot.ksw_used += swinfo.sw_used;
149e8865cafSJohn Baldwin 		sp = TAILQ_NEXT(&swinfo, sw_list);
150e8865cafSJohn Baldwin 	}
151e8865cafSJohn Baldwin 
152e8865cafSJohn Baldwin 	if (i >= swap_max)
153e8865cafSJohn Baldwin 		i = swap_max - 1;
154e8865cafSJohn Baldwin 	if (i >= 0)
155e8865cafSJohn Baldwin 		swap_ary[i] = tot;
156e8865cafSJohn Baldwin 
157e8865cafSJohn Baldwin         return(i);
158e8865cafSJohn Baldwin }
159e8865cafSJohn Baldwin 
160ae3a37adSRobert Watson #define	GETSYSCTL(kd, name, var)					\
161ae3a37adSRobert Watson 	    getsysctl(kd, name, &(var), sizeof(var))
162e04a7c4aSThomas Moestl 
163e04a7c4aSThomas Moestl /* The maximum MIB length for vm.swap_info and an additional device number */
164e04a7c4aSThomas Moestl #define	SWI_MAXMIB	3
165ae3a37adSRobert Watson 
166ae3a37adSRobert Watson int
167c10970ddSUlrich Spörlein kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max,
168c10970ddSUlrich Spörlein     int flags)
169c10970ddSUlrich Spörlein {
170eca80cd0SPedro F. Giffuni 	int ti;
171eca80cd0SPedro F. Giffuni 	swblk_t ttl;
172e04a7c4aSThomas Moestl 	size_t mibi, len;
173e04a7c4aSThomas Moestl 	int soid[SWI_MAXMIB];
174e04a7c4aSThomas Moestl 	struct xswdev xsd;
175e04a7c4aSThomas Moestl 	struct kvm_swap tot;
176ae3a37adSRobert Watson 
177ae3a37adSRobert Watson 	if (!GETSYSCTL(kd, "vm.dmmax", dmmax))
178ae3a37adSRobert Watson 		return -1;
179ae3a37adSRobert Watson 
180e04a7c4aSThomas Moestl 	mibi = SWI_MAXMIB - 1;
181e04a7c4aSThomas Moestl 	if (sysctlnametomib("vm.swap_info", soid, &mibi) == -1) {
182e04a7c4aSThomas Moestl 		_kvm_err(kd, kd->program, "sysctlnametomib failed: %s",
183e04a7c4aSThomas Moestl 		    strerror(errno));
184e04a7c4aSThomas Moestl 		return -1;
185e04a7c4aSThomas Moestl 	}
186e04a7c4aSThomas Moestl 	bzero(&tot, sizeof(tot));
187e04a7c4aSThomas Moestl 	for (unswdev = 0;; unswdev++) {
188e04a7c4aSThomas Moestl 		soid[mibi] = unswdev;
189e04a7c4aSThomas Moestl 		len = sizeof(xsd);
190e04a7c4aSThomas Moestl 		if (sysctl(soid, mibi + 1, &xsd, &len, NULL, 0) == -1) {
191e04a7c4aSThomas Moestl 			if (errno == ENOENT)
192e04a7c4aSThomas Moestl 				break;
193e04a7c4aSThomas Moestl 			_kvm_err(kd, kd->program, "cannot read sysctl: %s.",
194e04a7c4aSThomas Moestl 			    strerror(errno));
195e04a7c4aSThomas Moestl 			return -1;
196e04a7c4aSThomas Moestl 		}
197e04a7c4aSThomas Moestl 		if (len != sizeof(xsd)) {
198e04a7c4aSThomas Moestl 			_kvm_err(kd, kd->program, "struct xswdev has unexpected "
199e04a7c4aSThomas Moestl 			    "size;  kernel and libkvm out of sync?");
200e04a7c4aSThomas Moestl 			return -1;
201e04a7c4aSThomas Moestl 		}
202e04a7c4aSThomas Moestl 		if (xsd.xsw_version != XSWDEV_VERSION) {
203e04a7c4aSThomas Moestl 			_kvm_err(kd, kd->program, "struct xswdev version "
204e04a7c4aSThomas Moestl 			    "mismatch; kernel and libkvm out of sync?");
205ae3a37adSRobert Watson 			return -1;
206ae3a37adSRobert Watson 		}
207ae3a37adSRobert Watson 
208e04a7c4aSThomas Moestl 		ttl = xsd.xsw_nblks - dmmax;
209e04a7c4aSThomas Moestl 		if (unswdev < swap_max - 1) {
210e04a7c4aSThomas Moestl 			bzero(&swap_ary[unswdev], sizeof(swap_ary[unswdev]));
211e04a7c4aSThomas Moestl 			swap_ary[unswdev].ksw_total = ttl;
212e04a7c4aSThomas Moestl 			swap_ary[unswdev].ksw_used = xsd.xsw_used;
213e04a7c4aSThomas Moestl 			swap_ary[unswdev].ksw_flags = xsd.xsw_flags;
214e04a7c4aSThomas Moestl 			GETSWDEVNAME(xsd.xsw_dev, swap_ary[unswdev].ksw_devname,
215e04a7c4aSThomas Moestl 			     flags);
216e04a7c4aSThomas Moestl 		}
217e04a7c4aSThomas Moestl 		tot.ksw_total += ttl;
218e04a7c4aSThomas Moestl 		tot.ksw_used += xsd.xsw_used;
219e04a7c4aSThomas Moestl 	}
220ae3a37adSRobert Watson 
221e04a7c4aSThomas Moestl 	ti = unswdev;
222e04a7c4aSThomas Moestl 	if (ti >= swap_max)
223e04a7c4aSThomas Moestl 		ti = swap_max - 1;
224e04a7c4aSThomas Moestl 	if (ti >= 0)
225e04a7c4aSThomas Moestl 		swap_ary[ti] = tot;
226ae3a37adSRobert Watson 
227ae3a37adSRobert Watson         return(ti);
228ae3a37adSRobert Watson }
229ae3a37adSRobert Watson 
230ae3a37adSRobert Watson static int
231e8865cafSJohn Baldwin nlist_init(kvm_t *kd)
232e8865cafSJohn Baldwin {
233e8865cafSJohn Baldwin 
234e8865cafSJohn Baldwin 	if (kvm_swap_nl_cached)
235e8865cafSJohn Baldwin 		return (1);
236e8865cafSJohn Baldwin 
237e8865cafSJohn Baldwin 	if (kvm_nlist(kd, kvm_swap_nl) < 0)
238e8865cafSJohn Baldwin 		return (0);
239e8865cafSJohn Baldwin 
240e8865cafSJohn Baldwin 	/* Required entries */
241e8865cafSJohn Baldwin 	if (kvm_swap_nl[NL_SWTAILQ].n_value == 0) {
242e8865cafSJohn Baldwin 		_kvm_err(kd, kd->program, "unable to find swtailq");
243e8865cafSJohn Baldwin 		return (0);
244e8865cafSJohn Baldwin 	}
245e8865cafSJohn Baldwin 
246e8865cafSJohn Baldwin 	if (kvm_swap_nl[NL_DMMAX].n_value == 0) {
247e8865cafSJohn Baldwin 		_kvm_err(kd, kd->program, "unable to find dmmax");
248e8865cafSJohn Baldwin 		return (0);
249e8865cafSJohn Baldwin 	}
250e8865cafSJohn Baldwin 
251e8865cafSJohn Baldwin 	/* Get globals, type of swap */
252e8865cafSJohn Baldwin 	KGET(NL_DMMAX, &dmmax);
253e8865cafSJohn Baldwin 
254e8865cafSJohn Baldwin 	kvm_swap_nl_cached = 1;
255e8865cafSJohn Baldwin 	return (1);
256e8865cafSJohn Baldwin }
257e8865cafSJohn Baldwin 
258e8865cafSJohn Baldwin static int
259c10970ddSUlrich Spörlein getsysctl(kvm_t *kd, const char *name, void *ptr, size_t len)
260c10970ddSUlrich Spörlein {
261e04a7c4aSThomas Moestl 	size_t nlen = len;
262ae3a37adSRobert Watson 	if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
263e04a7c4aSThomas Moestl 		_kvm_err(kd, kd->program, "cannot read sysctl %s:%s", name,
264e04a7c4aSThomas Moestl 		    strerror(errno));
265ae3a37adSRobert Watson 		return (0);
266ae3a37adSRobert Watson 	}
267ae3a37adSRobert Watson 	if (nlen != len) {
268ae3a37adSRobert Watson 		_kvm_err(kd, kd->program, "sysctl %s has unexpected size", name);
269ae3a37adSRobert Watson 		return (0);
270ae3a37adSRobert Watson 	}
271ae3a37adSRobert Watson 	return (1);
272ae3a37adSRobert Watson }
273