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