1 /* 2 * Copyright (c) 1999 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $FreeBSD: src/lib/libkvm/kvm_getswapinfo.c,v 1.10.2.4 2003/01/12 09:23:13 dillon Exp $ 35 * $DragonFly: src/lib/libkvm/kvm_getswapinfo.c,v 1.5 2006/03/18 17:15:35 dillon Exp $ 36 */ 37 38 #define _KERNEL_STRUCTURES 39 40 #include <sys/param.h> 41 #include <sys/time.h> 42 #include <sys/ucred.h> 43 #include <sys/stat.h> 44 #include <sys/conf.h> 45 #include <sys/blist.h> 46 #include <sys/sysctl.h> 47 #include <vm/vm_param.h> 48 49 #include <err.h> 50 #include <fcntl.h> 51 #include <kvm.h> 52 #include <nlist.h> 53 #include <paths.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <limits.h> 59 60 #include "kvm_private.h" 61 62 static struct nlist kvm_swap_nl[] = { 63 { "_swapblist" }, /* new radix swap list */ 64 { "_swdevt" }, /* list of swap devices and sizes */ 65 { "_nswdev" }, /* number of swap devices */ 66 { "_dmmax" }, /* maximum size of a swap block */ 67 { "" } 68 }; 69 70 #define NL_SWAPBLIST 0 71 #define NL_SWDEVT 1 72 #define NL_NSWDEV 2 73 #define NL_DMMAX 3 74 75 static int kvm_swap_nl_cached = 0; 76 static int nswdev; 77 static int unswdev; 78 static int dmmax; 79 80 static int nlist_init(kvm_t *kd); 81 static void dump_blist(kvm_t *kd); 82 static int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, 83 int swap_max, int flags); 84 85 #define SVAR(var) __STRING(var) /* to force expansion */ 86 #define KGET(idx, var) \ 87 KGET1(idx, &var, sizeof(var), SVAR(var)) 88 #define KGET1(idx, p, s, msg) \ 89 KGET2(kvm_swap_nl[idx].n_value, p, s, msg) 90 #define KGET2(addr, p, s, msg) \ 91 if (kvm_read(kd, (u_long)(addr), p, s) != s) \ 92 warnx("cannot read %s: %s", msg, kvm_geterr(kd)) 93 #define KGETN(idx, var) \ 94 KGET1N(idx, &var, sizeof(var), SVAR(var)) 95 #define KGET1N(idx, p, s, msg) \ 96 KGET2N(kvm_swap_nl[idx].n_value, p, s, msg) 97 #define KGET2N(addr, p, s, msg) \ 98 ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0) 99 #define KGETRET(addr, p, s, msg) \ 100 if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ 101 warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ 102 return (0); \ 103 } 104 105 #define GETSWDEVNAME(dev, str, flags) \ 106 if (dev == NODEV) { \ 107 strlcpy(str, "[NFS swap]", sizeof(str)); \ 108 } else { \ 109 snprintf( \ 110 str, sizeof(str), "%s%s", \ 111 ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), \ 112 devname(dev, S_IFCHR) \ 113 ); \ 114 } 115 116 int 117 kvm_getswapinfo( 118 kvm_t *kd, 119 struct kvm_swap *swap_ary, 120 int swap_max, 121 int flags 122 ) { 123 int i, ti, swi; 124 int ttl; 125 struct swdevt *sw; 126 struct swdevt swinfo; 127 128 /* 129 * clear cache 130 */ 131 if (kd == NULL) { 132 kvm_swap_nl_cached = 0; 133 return(0); 134 } 135 136 if (swap_max < 1) 137 return (-1); 138 139 /* 140 * Use sysctl if possible 141 */ 142 if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) { 143 ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags); 144 if (ti >= 0) 145 return(ti); 146 } 147 148 /* 149 * namelist 150 */ 151 if (!nlist_init(kd)) 152 return (-1); 153 154 swi = unswdev; 155 if (swi >= swap_max) 156 swi = swap_max - 1; 157 158 bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1)); 159 160 KGET(NL_SWDEVT, sw); 161 for (i = ti = 0; i < nswdev; ++i) { 162 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 163 164 if (swinfo.sw_nblks == 0) 165 continue; 166 167 /* 168 * The first dmmax is never allocated to avoid 169 * trashing the disklabels. 170 */ 171 ttl = swinfo.sw_nblks - dmmax; 172 if (ttl == 0) 173 continue; 174 175 swap_ary[swi].ksw_total += ttl; 176 swap_ary[swi].ksw_used += swinfo.sw_nused; 177 178 if (ti < swi) { 179 swap_ary[ti].ksw_total = ttl; 180 swap_ary[ti].ksw_used = swinfo.sw_nused; 181 swap_ary[ti].ksw_flags = swinfo.sw_flags; 182 GETSWDEVNAME(swinfo.sw_dev, swap_ary[ti].ksw_devname, 183 flags); 184 ++ti; 185 } 186 } 187 188 if (flags & SWIF_DUMP_TREE) 189 dump_blist(kd); 190 return (swi); 191 } 192 193 static int 194 nlist_init(kvm_t *kd) 195 { 196 int i; 197 struct swdevt *sw; 198 struct swdevt swinfo; 199 200 if (kvm_swap_nl_cached) 201 return (1); 202 203 if (kvm_nlist(kd, kvm_swap_nl) < 0) 204 return (0); 205 206 /* 207 * required entries 208 */ 209 if (kvm_swap_nl[NL_SWDEVT].n_value == 0 || 210 kvm_swap_nl[NL_NSWDEV].n_value == 0 || 211 kvm_swap_nl[NL_DMMAX].n_value == 0 || 212 kvm_swap_nl[NL_SWAPBLIST].n_type == 0) { 213 return (0); 214 } 215 216 /* 217 * get globals, type of swap 218 */ 219 KGET(NL_NSWDEV, nswdev); 220 KGET(NL_DMMAX, dmmax); 221 222 /* 223 * figure out how many actual swap devices are enabled 224 */ 225 KGET(NL_SWDEVT, sw); 226 for (i = unswdev = 0; i < nswdev; ++i) { 227 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 228 if (swinfo.sw_nblks) 229 ++unswdev; 230 231 } 232 233 kvm_swap_nl_cached = 1; 234 return (1); 235 } 236 237 /* 238 * scanradix() - support routine for radix scanner 239 */ 240 241 #define TABME tab, tab, "" 242 243 static int 244 scanradix( 245 blmeta_t *scan, 246 blmeta_t *scan_cache, 247 swblk_t blk, 248 int64_t radix, 249 swblk_t skip, 250 swblk_t count, 251 kvm_t *kd, 252 int dmmax, 253 int nswdev, 254 int tab 255 ) { 256 blmeta_t meta; 257 blmeta_t scan_array[BLIST_BMAP_RADIX]; 258 259 if (scan_cache) { 260 meta = *scan_cache; 261 } else if (skip == BLIST_META_RADIX) { 262 if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) { 263 warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd)); 264 bzero(scan_array, sizeof(scan_array)); 265 } 266 meta = scan_array[0]; 267 } else { 268 KGET2(scan, &meta, sizeof(meta), "blmeta_t"); 269 } 270 271 /* 272 * Terminator 273 */ 274 if (meta.bm_bighint == (swblk_t)-1) { 275 printf("%*.*s(0x%06x,%lld) Terminator\n", 276 TABME, 277 blk, 278 (long long)radix 279 ); 280 return(-1); 281 } 282 283 if (radix == BLIST_BMAP_RADIX) { 284 /* 285 * Leaf bitmap 286 */ 287 printf("%*.*s(0x%06x,%lld) Bitmap %08x big=%d\n", 288 TABME, 289 blk, 290 (long long)radix, 291 (int)meta.u.bmu_bitmap, 292 meta.bm_bighint 293 ); 294 295 } else if (meta.u.bmu_avail == radix) { 296 /* 297 * Meta node if all free 298 */ 299 printf("%*.*s(0x%06x,%lld) Submap ALL-FREE {\n", 300 TABME, 301 blk, 302 (long long)radix 303 ); 304 305 } else if (meta.u.bmu_avail == 0) { 306 /* 307 * Meta node if all used 308 */ 309 printf("%*.*s(0x%06x,%lld) Submap ALL-ALLOCATED\n", 310 TABME, 311 blk, 312 (long long)radix 313 ); 314 } else { 315 /* 316 * Meta node if not all free 317 */ 318 int i; 319 int next_skip; 320 321 printf("%*.*s(0x%06x,%lld) Submap avail=%d big=%d {\n", 322 TABME, 323 blk, 324 (long long)radix, 325 (int)meta.u.bmu_avail, 326 meta.bm_bighint 327 ); 328 329 radix /= BLIST_META_RADIX; 330 next_skip = skip / BLIST_META_RADIX; 331 332 for (i = 1; i <= skip; i += next_skip) { 333 int r; 334 swblk_t vcount = (count > radix) ? 335 (swblk_t)radix : count; 336 337 r = scanradix( 338 &scan[i], 339 ((next_skip == 1) ? &scan_array[i] : NULL), 340 blk, 341 radix, 342 next_skip - 1, 343 vcount, 344 kd, 345 dmmax, 346 nswdev, 347 tab + 4 348 ); 349 if (r < 0) 350 break; 351 blk += (swblk_t)radix; 352 } 353 printf("%*.*s}\n", TABME); 354 } 355 return(0); 356 } 357 358 static void 359 dump_blist(kvm_t *kd) 360 { 361 struct blist *swapblist = NULL; 362 struct blist blcopy = { 0 }; 363 364 KGET(NL_SWAPBLIST, swapblist); 365 366 if (swapblist == NULL) { 367 printf("radix tree: NULL - no swap in system\n"); 368 return; 369 } 370 371 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 372 373 printf("radix tree: %d/%d/%lld blocks, %dK wired\n", 374 blcopy.bl_free, 375 blcopy.bl_blocks, 376 (long long)blcopy.bl_radix, 377 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 378 1024) 379 ); 380 381 scanradix( 382 blcopy.bl_root, 383 NULL, 384 0, 385 blcopy.bl_radix, 386 blcopy.bl_skip, 387 blcopy.bl_rootblks, 388 kd, 389 dmmax, 390 nswdev, 391 0 392 ); 393 } 394 395 static 396 int 397 kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, 398 int swap_max, int flags) 399 { 400 size_t bytes = 0; 401 size_t ksize; 402 int ti; 403 int swi; 404 int n; 405 int i; 406 char *xswbuf; 407 struct xswdev *xsw; 408 409 if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) 410 return(-1); 411 if (bytes == 0) 412 return(-1); 413 414 xswbuf = malloc(bytes); 415 if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) { 416 free(xswbuf); 417 return(-1); 418 } 419 if (bytes == 0) { 420 free(xswbuf); 421 return(-1); 422 } 423 424 /* 425 * Calculate size of xsw entry returned by kernel (it can be larger 426 * than the one we have if there is a version mismatch). 427 */ 428 ksize = ((struct xswdev *)xswbuf)->xsw_size; 429 n = (int)(bytes / ksize); 430 431 /* 432 * Calculate the number of live swap devices and calculate 433 * the swap_ary[] index used for the cumulative result (swi) 434 */ 435 for (i = swi = 0; i < n; ++i) { 436 xsw = (void *)((char *)xswbuf + i * ksize); 437 if ((xsw->xsw_flags & SW_FREED) == 0) 438 continue; 439 ++swi; 440 } 441 if (swi >= swap_max) 442 swi = swap_max - 1; 443 444 bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1)); 445 446 /* 447 * Accumulate results. If the provided swap_ary[] is too 448 * small will only populate up to the available entries, 449 * but we always populate the cumulative results entry. 450 */ 451 for (i = ti = 0; i < n; ++i) { 452 xsw = (void *)((char *)xswbuf + i * ksize); 453 454 if ((xsw->xsw_flags & SW_FREED) == 0) 455 continue; 456 457 swap_ary[swi].ksw_total += xsw->xsw_nblks; 458 swap_ary[swi].ksw_used += xsw->xsw_used; 459 460 if (ti < swi) { 461 swap_ary[ti].ksw_total = xsw->xsw_nblks; 462 swap_ary[ti].ksw_used = xsw->xsw_used; 463 swap_ary[ti].ksw_flags = xsw->xsw_flags; 464 GETSWDEVNAME(xsw->xsw_dev, swap_ary[ti].ksw_devname, 465 flags); 466 ++ti; 467 } 468 } 469 470 free(xswbuf); 471 return(swi); 472 } 473