15e53a4f9SPedro F. Giffuni /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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
29e92324a2SMatthew Dillon #include <sys/param.h>
30e92324a2SMatthew Dillon #include <sys/time.h>
31e92324a2SMatthew Dillon #include <sys/stat.h>
32e92324a2SMatthew Dillon #include <sys/blist.h>
336bff85ffSDag-Erling Smørgrav #include <sys/queue.h>
34ae3a37adSRobert Watson #include <sys/sysctl.h>
35e92324a2SMatthew Dillon
36e8865cafSJohn Baldwin #include <vm/swap_pager.h>
37e04a7c4aSThomas Moestl #include <vm/vm_param.h>
38e04a7c4aSThomas Moestl
39e92324a2SMatthew Dillon #include <err.h>
40e04a7c4aSThomas Moestl #include <errno.h>
41e92324a2SMatthew Dillon #include <fcntl.h>
42e92324a2SMatthew Dillon #include <kvm.h>
43e92324a2SMatthew Dillon #include <nlist.h>
441a37aa56SDavid E. O'Brien #include <paths.h>
45e92324a2SMatthew Dillon #include <stdio.h>
46e92324a2SMatthew Dillon #include <stdlib.h>
47e92324a2SMatthew Dillon #include <string.h>
48e92324a2SMatthew Dillon #include <unistd.h>
49ae3a37adSRobert Watson #include <limits.h>
50ae3a37adSRobert Watson
51ae3a37adSRobert Watson #include "kvm_private.h"
52e92324a2SMatthew Dillon
53e8865cafSJohn Baldwin static struct nlist kvm_swap_nl[] = {
54c10970ddSUlrich Spörlein { .n_name = "_swtailq" }, /* list of swap devices and sizes */
55c10970ddSUlrich Spörlein { .n_name = "_dmmax" }, /* maximum size of a swap block */
56c10970ddSUlrich Spörlein { .n_name = NULL }
57e8865cafSJohn Baldwin };
58e8865cafSJohn Baldwin
59e8865cafSJohn Baldwin #define NL_SWTAILQ 0
60e8865cafSJohn Baldwin #define NL_DMMAX 1
61e92324a2SMatthew Dillon
62e92324a2SMatthew Dillon static int kvm_swap_nl_cached = 0;
63ae3a37adSRobert Watson static int unswdev; /* number of found swap dev's */
64e92324a2SMatthew Dillon static int dmmax;
65e92324a2SMatthew Dillon
66e8865cafSJohn Baldwin static int kvm_getswapinfo_kvm(kvm_t *, struct kvm_swap *, int, int);
67ae3a37adSRobert Watson static int kvm_getswapinfo_sysctl(kvm_t *, struct kvm_swap *, int, int);
68e8865cafSJohn Baldwin static int nlist_init(kvm_t *);
69c10970ddSUlrich Spörlein static int getsysctl(kvm_t *, const char *, void *, size_t);
70e92324a2SMatthew Dillon
71e8865cafSJohn Baldwin #define KREAD(kd, addr, obj) \
72e8865cafSJohn Baldwin (kvm_read(kd, addr, (char *)(obj), sizeof(*obj)) != sizeof(*obj))
73e8865cafSJohn Baldwin #define KGET(idx, var) \
74e8865cafSJohn Baldwin KGET2(kvm_swap_nl[(idx)].n_value, var, kvm_swap_nl[(idx)].n_name)
75e8865cafSJohn Baldwin #define KGET2(addr, var, msg) \
76e8865cafSJohn Baldwin if (KREAD(kd, (u_long)(addr), (var))) { \
77e8865cafSJohn Baldwin _kvm_err(kd, kd->program, "cannot read %s", msg); \
78e8865cafSJohn Baldwin return (-1); \
79e8865cafSJohn Baldwin }
80e8865cafSJohn Baldwin
81ae3a37adSRobert Watson #define GETSWDEVNAME(dev, str, flags) \
82ae3a37adSRobert Watson if (dev == NODEV) { \
83ae3a37adSRobert Watson strlcpy(str, "[NFS swap]", sizeof(str)); \
84ae3a37adSRobert Watson } else { \
85ae3a37adSRobert Watson snprintf( \
86ae3a37adSRobert Watson str, sizeof(str),"%s%s", \
87ae3a37adSRobert Watson ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), \
88ae3a37adSRobert Watson devname(dev, S_IFCHR) \
89ae3a37adSRobert Watson ); \
90ae3a37adSRobert Watson }
91ae3a37adSRobert Watson
92e92324a2SMatthew Dillon int
kvm_getswapinfo(kvm_t * kd,struct kvm_swap * swap_ary,int swap_max,int flags)93c10970ddSUlrich Spörlein kvm_getswapinfo(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags)
94c10970ddSUlrich Spörlein {
95e92324a2SMatthew Dillon
96e92324a2SMatthew Dillon /*
97e92324a2SMatthew Dillon * clear cache
98e92324a2SMatthew Dillon */
99e92324a2SMatthew Dillon if (kd == NULL) {
100e92324a2SMatthew Dillon kvm_swap_nl_cached = 0;
101e92324a2SMatthew Dillon return(0);
102e92324a2SMatthew Dillon }
103e92324a2SMatthew Dillon
104ae3a37adSRobert Watson if (ISALIVE(kd)) {
105ae3a37adSRobert Watson return kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
106ae3a37adSRobert Watson } else {
107e8865cafSJohn Baldwin return kvm_getswapinfo_kvm(kd, swap_ary, swap_max, flags);
108ae3a37adSRobert Watson }
109ae3a37adSRobert Watson }
110ae3a37adSRobert Watson
111e8865cafSJohn Baldwin int
kvm_getswapinfo_kvm(kvm_t * kd,struct kvm_swap * swap_ary,int swap_max,int flags)112c10970ddSUlrich Spörlein kvm_getswapinfo_kvm(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max,
113c10970ddSUlrich Spörlein int flags)
114c10970ddSUlrich Spörlein {
11534e2051fSMark Johnston int i, ttl;
116e8865cafSJohn Baldwin TAILQ_HEAD(, swdevt) swtailq;
117e8865cafSJohn Baldwin struct swdevt *sp, swinfo;
118e8865cafSJohn Baldwin struct kvm_swap tot;
119e8865cafSJohn Baldwin
1207f911abeSJohn Baldwin if (!kd->arch->ka_native(kd)) {
1217f911abeSJohn Baldwin _kvm_err(kd, kd->program,
1227f911abeSJohn Baldwin "cannot read swapinfo from non-native core");
1237f911abeSJohn Baldwin return (-1);
1247f911abeSJohn Baldwin }
1257f911abeSJohn Baldwin
126e8865cafSJohn Baldwin if (!nlist_init(kd))
127e8865cafSJohn Baldwin return (-1);
128e8865cafSJohn Baldwin
129e8865cafSJohn Baldwin bzero(&tot, sizeof(tot));
130e8865cafSJohn Baldwin KGET(NL_SWTAILQ, &swtailq);
131e8865cafSJohn Baldwin sp = TAILQ_FIRST(&swtailq);
132e8865cafSJohn Baldwin for (i = 0; sp != NULL; i++) {
133e8865cafSJohn Baldwin KGET2(sp, &swinfo, "swinfo");
134e8865cafSJohn Baldwin ttl = swinfo.sw_nblks - dmmax;
135e8865cafSJohn Baldwin if (i < swap_max - 1) {
136e8865cafSJohn Baldwin bzero(&swap_ary[i], sizeof(swap_ary[i]));
137e8865cafSJohn Baldwin swap_ary[i].ksw_total = ttl;
138e8865cafSJohn Baldwin swap_ary[i].ksw_used = swinfo.sw_used;
139e8865cafSJohn Baldwin swap_ary[i].ksw_flags = swinfo.sw_flags;
140e8865cafSJohn Baldwin GETSWDEVNAME(swinfo.sw_dev, swap_ary[i].ksw_devname,
141e8865cafSJohn Baldwin flags);
142e8865cafSJohn Baldwin }
143e8865cafSJohn Baldwin tot.ksw_total += ttl;
144e8865cafSJohn Baldwin tot.ksw_used += swinfo.sw_used;
145e8865cafSJohn Baldwin sp = TAILQ_NEXT(&swinfo, sw_list);
146e8865cafSJohn Baldwin }
147e8865cafSJohn Baldwin
148e8865cafSJohn Baldwin if (i >= swap_max)
149e8865cafSJohn Baldwin i = swap_max - 1;
150e8865cafSJohn Baldwin if (i >= 0)
151e8865cafSJohn Baldwin swap_ary[i] = tot;
152e8865cafSJohn Baldwin
153e8865cafSJohn Baldwin return(i);
154e8865cafSJohn Baldwin }
155e8865cafSJohn Baldwin
156ae3a37adSRobert Watson #define GETSYSCTL(kd, name, var) \
157ae3a37adSRobert Watson getsysctl(kd, name, &(var), sizeof(var))
158e04a7c4aSThomas Moestl
159e04a7c4aSThomas Moestl /* The maximum MIB length for vm.swap_info and an additional device number */
160e04a7c4aSThomas Moestl #define SWI_MAXMIB 3
161ae3a37adSRobert Watson
162ae3a37adSRobert Watson int
kvm_getswapinfo_sysctl(kvm_t * kd,struct kvm_swap * swap_ary,int swap_max,int flags)163c10970ddSUlrich Spörlein kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max,
164c10970ddSUlrich Spörlein int flags)
165c10970ddSUlrich Spörlein {
16634e2051fSMark Johnston int ti, ttl;
167e04a7c4aSThomas Moestl size_t mibi, len;
168e04a7c4aSThomas Moestl int soid[SWI_MAXMIB];
169e04a7c4aSThomas Moestl struct xswdev xsd;
170e04a7c4aSThomas Moestl struct kvm_swap tot;
171ae3a37adSRobert Watson
172ae3a37adSRobert Watson if (!GETSYSCTL(kd, "vm.dmmax", dmmax))
173ae3a37adSRobert Watson return -1;
174ae3a37adSRobert Watson
175e04a7c4aSThomas Moestl mibi = SWI_MAXMIB - 1;
176e04a7c4aSThomas Moestl if (sysctlnametomib("vm.swap_info", soid, &mibi) == -1) {
177e04a7c4aSThomas Moestl _kvm_err(kd, kd->program, "sysctlnametomib failed: %s",
178e04a7c4aSThomas Moestl strerror(errno));
179e04a7c4aSThomas Moestl return -1;
180e04a7c4aSThomas Moestl }
181e04a7c4aSThomas Moestl bzero(&tot, sizeof(tot));
182e04a7c4aSThomas Moestl for (unswdev = 0;; unswdev++) {
183e04a7c4aSThomas Moestl soid[mibi] = unswdev;
184e04a7c4aSThomas Moestl len = sizeof(xsd);
185e04a7c4aSThomas Moestl if (sysctl(soid, mibi + 1, &xsd, &len, NULL, 0) == -1) {
186e04a7c4aSThomas Moestl if (errno == ENOENT)
187e04a7c4aSThomas Moestl break;
188e04a7c4aSThomas Moestl _kvm_err(kd, kd->program, "cannot read sysctl: %s.",
189e04a7c4aSThomas Moestl strerror(errno));
190e04a7c4aSThomas Moestl return -1;
191e04a7c4aSThomas Moestl }
192e04a7c4aSThomas Moestl if (len != sizeof(xsd)) {
193e04a7c4aSThomas Moestl _kvm_err(kd, kd->program, "struct xswdev has unexpected "
194e04a7c4aSThomas Moestl "size; kernel and libkvm out of sync?");
195e04a7c4aSThomas Moestl return -1;
196e04a7c4aSThomas Moestl }
197e04a7c4aSThomas Moestl if (xsd.xsw_version != XSWDEV_VERSION) {
198e04a7c4aSThomas Moestl _kvm_err(kd, kd->program, "struct xswdev version "
199e04a7c4aSThomas Moestl "mismatch; kernel and libkvm out of sync?");
200ae3a37adSRobert Watson return -1;
201ae3a37adSRobert Watson }
202ae3a37adSRobert Watson
203e04a7c4aSThomas Moestl ttl = xsd.xsw_nblks - dmmax;
204e04a7c4aSThomas Moestl if (unswdev < swap_max - 1) {
205e04a7c4aSThomas Moestl bzero(&swap_ary[unswdev], sizeof(swap_ary[unswdev]));
206e04a7c4aSThomas Moestl swap_ary[unswdev].ksw_total = ttl;
207e04a7c4aSThomas Moestl swap_ary[unswdev].ksw_used = xsd.xsw_used;
208e04a7c4aSThomas Moestl swap_ary[unswdev].ksw_flags = xsd.xsw_flags;
209e04a7c4aSThomas Moestl GETSWDEVNAME(xsd.xsw_dev, swap_ary[unswdev].ksw_devname,
210e04a7c4aSThomas Moestl flags);
211e04a7c4aSThomas Moestl }
212e04a7c4aSThomas Moestl tot.ksw_total += ttl;
213e04a7c4aSThomas Moestl tot.ksw_used += xsd.xsw_used;
214e04a7c4aSThomas Moestl }
215ae3a37adSRobert Watson
216e04a7c4aSThomas Moestl ti = unswdev;
217e04a7c4aSThomas Moestl if (ti >= swap_max)
218e04a7c4aSThomas Moestl ti = swap_max - 1;
219e04a7c4aSThomas Moestl if (ti >= 0)
220e04a7c4aSThomas Moestl swap_ary[ti] = tot;
221ae3a37adSRobert Watson
222ae3a37adSRobert Watson return(ti);
223ae3a37adSRobert Watson }
224ae3a37adSRobert Watson
225ae3a37adSRobert Watson static int
nlist_init(kvm_t * kd)226e8865cafSJohn Baldwin nlist_init(kvm_t *kd)
227e8865cafSJohn Baldwin {
228e8865cafSJohn Baldwin
229e8865cafSJohn Baldwin if (kvm_swap_nl_cached)
230e8865cafSJohn Baldwin return (1);
231e8865cafSJohn Baldwin
232e8865cafSJohn Baldwin if (kvm_nlist(kd, kvm_swap_nl) < 0)
233e8865cafSJohn Baldwin return (0);
234e8865cafSJohn Baldwin
235e8865cafSJohn Baldwin /* Required entries */
236e8865cafSJohn Baldwin if (kvm_swap_nl[NL_SWTAILQ].n_value == 0) {
237e8865cafSJohn Baldwin _kvm_err(kd, kd->program, "unable to find swtailq");
238e8865cafSJohn Baldwin return (0);
239e8865cafSJohn Baldwin }
240e8865cafSJohn Baldwin
241e8865cafSJohn Baldwin if (kvm_swap_nl[NL_DMMAX].n_value == 0) {
242e8865cafSJohn Baldwin _kvm_err(kd, kd->program, "unable to find dmmax");
243e8865cafSJohn Baldwin return (0);
244e8865cafSJohn Baldwin }
245e8865cafSJohn Baldwin
246e8865cafSJohn Baldwin /* Get globals, type of swap */
247e8865cafSJohn Baldwin KGET(NL_DMMAX, &dmmax);
248e8865cafSJohn Baldwin
249e8865cafSJohn Baldwin kvm_swap_nl_cached = 1;
250e8865cafSJohn Baldwin return (1);
251e8865cafSJohn Baldwin }
252e8865cafSJohn Baldwin
253e8865cafSJohn Baldwin static int
getsysctl(kvm_t * kd,const char * name,void * ptr,size_t len)254c10970ddSUlrich Spörlein getsysctl(kvm_t *kd, const char *name, void *ptr, size_t len)
255c10970ddSUlrich Spörlein {
256e04a7c4aSThomas Moestl size_t nlen = len;
257ae3a37adSRobert Watson if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
258e04a7c4aSThomas Moestl _kvm_err(kd, kd->program, "cannot read sysctl %s:%s", name,
259e04a7c4aSThomas Moestl strerror(errno));
260ae3a37adSRobert Watson return (0);
261ae3a37adSRobert Watson }
262ae3a37adSRobert Watson if (nlen != len) {
263ae3a37adSRobert Watson _kvm_err(kd, kd->program, "sysctl %s has unexpected size", name);
264ae3a37adSRobert Watson return (0);
265ae3a37adSRobert Watson }
266ae3a37adSRobert Watson return (1);
267ae3a37adSRobert Watson }
268