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