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 void getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, 81 int swap_max, int flags); 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 int 106 kvm_getswapinfo( 107 kvm_t *kd, 108 struct kvm_swap *swap_ary, 109 int swap_max, 110 int flags 111 ) { 112 int ti; 113 114 /* 115 * clear cache 116 */ 117 if (kd == NULL) { 118 kvm_swap_nl_cached = 0; 119 return(0); 120 } 121 122 /* 123 * Use sysctl if possible 124 */ 125 if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) { 126 ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags); 127 if (ti >= 0) 128 return(ti); 129 } 130 131 /* 132 * namelist 133 */ 134 if (kvm_swap_nl_cached == 0) { 135 struct swdevt *sw; 136 137 if (kvm_nlist(kd, kvm_swap_nl) < 0) 138 return(-1); 139 140 /* 141 * required entries 142 */ 143 144 if ( 145 kvm_swap_nl[NL_SWDEVT].n_value == 0 || 146 kvm_swap_nl[NL_NSWDEV].n_value == 0 || 147 kvm_swap_nl[NL_DMMAX].n_value == 0 || 148 kvm_swap_nl[NL_SWAPBLIST].n_type == 0 149 ) { 150 return(-1); 151 } 152 153 /* 154 * get globals, type of swap 155 */ 156 157 KGET(NL_NSWDEV, nswdev); 158 KGET(NL_DMMAX, dmmax); 159 160 /* 161 * figure out how many actual swap devices are enabled 162 */ 163 164 KGET(NL_SWDEVT, sw); 165 for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) { 166 struct swdevt swinfo; 167 168 KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo"); 169 if (swinfo.sw_nblks) 170 break; 171 } 172 ++unswdev; 173 174 kvm_swap_nl_cached = 1; 175 } 176 177 { 178 struct swdevt *sw; 179 int i; 180 181 ti = unswdev; 182 if (ti >= swap_max) 183 ti = swap_max - 1; 184 185 if (ti >= 0) 186 bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1)); 187 188 KGET(NL_SWDEVT, sw); 189 for (i = 0; i < unswdev; ++i) { 190 struct swdevt swinfo; 191 int ttl; 192 193 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 194 195 /* 196 * old style: everything in DEV_BSIZE'd chunks, 197 * convert to pages. 198 * 199 * new style: swinfo in DEV_BSIZE'd chunks but dmmax 200 * in pages. 201 * 202 * The first dmmax is never allocating to avoid 203 * trashing the disklabels 204 */ 205 206 ttl = swinfo.sw_nblks - dmmax; 207 208 if (ttl == 0) 209 continue; 210 211 if (i < ti) { 212 swap_ary[i].ksw_total = ttl; 213 swap_ary[i].ksw_used = ttl; 214 swap_ary[i].ksw_flags = swinfo.sw_flags; 215 if (swinfo.sw_dev == NODEV) { 216 snprintf( 217 swap_ary[i].ksw_devname, 218 sizeof(swap_ary[i].ksw_devname), 219 "%s", 220 "[NFS swap]" 221 ); 222 } else { 223 snprintf( 224 swap_ary[i].ksw_devname, 225 sizeof(swap_ary[i].ksw_devname), 226 "%s%s", 227 ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), 228 devname(swinfo.sw_dev, S_IFCHR) 229 ); 230 } 231 } 232 if (ti >= 0) { 233 swap_ary[ti].ksw_total += ttl; 234 swap_ary[ti].ksw_used += ttl; 235 } 236 } 237 } 238 239 getswapinfo_radix(kd, swap_ary, swap_max, flags); 240 return(ti); 241 } 242 243 /* 244 * scanradix() - support routine for radix scanner 245 */ 246 247 #define TABME tab, tab, "" 248 249 static int 250 scanradix( 251 blmeta_t *scan, 252 blmeta_t *scan_cache, 253 swblk_t blk, 254 int64_t radix, 255 swblk_t skip, 256 swblk_t count, 257 kvm_t *kd, 258 int dmmax, 259 int nswdev, 260 struct kvm_swap *swap_ary, 261 int swap_max, 262 int tab, 263 int flags 264 ) { 265 blmeta_t meta; 266 blmeta_t scan_array[BLIST_BMAP_RADIX]; 267 int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev; 268 269 if (scan_cache) { 270 meta = *scan_cache; 271 } else if (skip == BLIST_META_RADIX) { 272 if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) { 273 warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd)); 274 bzero(scan_array, sizeof(scan_array)); 275 } 276 meta = scan_array[0]; 277 } else { 278 KGET2(scan, &meta, sizeof(meta), "blmeta_t"); 279 } 280 281 /* 282 * Terminator 283 */ 284 if (meta.bm_bighint == (swblk_t)-1) { 285 if (flags & SWIF_DUMP_TREE) { 286 printf("%*.*s(0x%06x,%lld) Terminator\n", 287 TABME, 288 blk, 289 (long long)radix 290 ); 291 } 292 return(-1); 293 } 294 295 if (radix == BLIST_BMAP_RADIX) { 296 /* 297 * Leaf bitmap 298 */ 299 int i; 300 301 if (flags & SWIF_DUMP_TREE) { 302 printf("%*.*s(0x%06x,%lld) Bitmap %08x big=%d\n", 303 TABME, 304 blk, 305 (long long)radix, 306 (int)meta.u.bmu_bitmap, 307 meta.bm_bighint 308 ); 309 } 310 311 /* 312 * If not all allocated, count. 313 */ 314 if (meta.u.bmu_bitmap != 0) { 315 for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) { 316 /* 317 * A 0 bit means allocated 318 */ 319 if ((meta.u.bmu_bitmap & (1 << i))) { 320 int t = 0; 321 322 if (nswdev) 323 t = (blk + i) / dmmax % nswdev; 324 if (t < ti) 325 --swap_ary[t].ksw_used; 326 if (ti >= 0) 327 --swap_ary[ti].ksw_used; 328 } 329 } 330 } 331 } else if (meta.u.bmu_avail == radix) { 332 /* 333 * Meta node if all free 334 */ 335 if (flags & SWIF_DUMP_TREE) { 336 printf("%*.*s(0x%06x,%lld) Submap ALL-FREE {\n", 337 TABME, 338 blk, 339 (long long)radix 340 ); 341 } 342 /* 343 * Note: both dmmax and radix are powers of 2. However, dmmax 344 * may be larger then radix so use a smaller increment if 345 * necessary. 346 */ 347 { 348 int t; 349 int tinc = dmmax; 350 351 while (tinc > radix) 352 tinc >>= 1; 353 354 for (t = blk; t < blk + radix; t += tinc) { 355 int u = (nswdev) ? (t / dmmax % nswdev) : 0; 356 357 if (u < ti) 358 swap_ary[u].ksw_used -= tinc; 359 if (ti >= 0) 360 swap_ary[ti].ksw_used -= tinc; 361 } 362 } 363 } else if (meta.u.bmu_avail == 0) { 364 /* 365 * Meta node if all used 366 */ 367 if (flags & SWIF_DUMP_TREE) { 368 printf("%*.*s(0x%06x,%lld) Submap ALL-ALLOCATED\n", 369 TABME, 370 blk, 371 (long long)radix 372 ); 373 } 374 } else { 375 /* 376 * Meta node if not all free 377 */ 378 int i; 379 int next_skip; 380 381 if (flags & SWIF_DUMP_TREE) { 382 printf("%*.*s(0x%06x,%lld) Submap avail=%d big=%d {\n", 383 TABME, 384 blk, 385 (long long)radix, 386 (int)meta.u.bmu_avail, 387 meta.bm_bighint 388 ); 389 } 390 391 radix /= BLIST_META_RADIX; 392 next_skip = skip / BLIST_META_RADIX; 393 394 for (i = 1; i <= skip; i += next_skip) { 395 int r; 396 swblk_t vcount = (count > radix) ? 397 (swblk_t)radix : count; 398 399 r = scanradix( 400 &scan[i], 401 ((next_skip == 1) ? &scan_array[i] : NULL), 402 blk, 403 radix, 404 next_skip - 1, 405 vcount, 406 kd, 407 dmmax, 408 nswdev, 409 swap_ary, 410 swap_max, 411 tab + 4, 412 flags 413 ); 414 if (r < 0) 415 break; 416 blk += (swblk_t)radix; 417 } 418 if (flags & SWIF_DUMP_TREE) { 419 printf("%*.*s}\n", TABME); 420 } 421 } 422 return(0); 423 } 424 425 static void 426 getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags) 427 { 428 struct blist *swapblist = NULL; 429 struct blist blcopy = { 0 }; 430 431 KGET(NL_SWAPBLIST, swapblist); 432 433 if (swapblist == NULL) { 434 if (flags & SWIF_DUMP_TREE) 435 printf("radix tree: NULL - no swap in system\n"); 436 return; 437 } 438 439 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 440 441 if (flags & SWIF_DUMP_TREE) { 442 printf("radix tree: %d/%d/%lld blocks, %dK wired\n", 443 blcopy.bl_free, 444 blcopy.bl_blocks, 445 (long long)blcopy.bl_radix, 446 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 447 1024) 448 ); 449 } 450 451 /* 452 * XXX Scan the radix tree in the kernel if we have more then one 453 * swap device so we can get per-device statistics. This can 454 * get nasty because swap devices are interleaved based on the 455 * maximum of (4), so the blist winds up not using any shortcuts. 456 * 457 * Otherwise just pull the free count out of the blist header, 458 * which is a billion times faster. 459 */ 460 if ((flags & SWIF_DUMP_TREE) || unswdev > 1) { 461 scanradix( 462 blcopy.bl_root, 463 NULL, 464 0, 465 blcopy.bl_radix, 466 blcopy.bl_skip, 467 blcopy.bl_rootblks, 468 kd, 469 dmmax, 470 nswdev, 471 swap_ary, 472 swap_max, 473 0, 474 flags 475 ); 476 } else { 477 swap_ary[0].ksw_used -= blcopy.bl_free; 478 } 479 } 480 481 static 482 int 483 kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, 484 int swap_max, int flags) 485 { 486 size_t bytes = 0; 487 size_t ksize; 488 int ti; 489 int n; 490 int i; 491 char *xswbuf; 492 struct xswdev *xsw; 493 494 if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) 495 return(-1); 496 if (bytes == 0) 497 return(-1); 498 499 xswbuf = malloc(bytes); 500 if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) { 501 free(xswbuf); 502 return(-1); 503 } 504 if (bytes == 0) { 505 free(xswbuf); 506 return(-1); 507 } 508 509 bzero(swap_ary, sizeof(struct kvm_swap) * swap_max); 510 --swap_max; 511 512 /* 513 * Calculate size of xsw entry returned by kernel (it can be larger 514 * than the one we have if there is a version mismatch). 515 * 516 * Then iterate the list looking for live swap devices. 517 */ 518 ksize = ((struct xswdev *)xswbuf)->xsw_size; 519 n = (int)(bytes / ksize); 520 521 for (i = ti = 0; i < n && ti < swap_max; ++i) { 522 xsw = (void *)((char *)xswbuf + i * ksize); 523 524 if ((xsw->xsw_flags & SW_FREED) == 0) 525 continue; 526 527 swap_ary[ti].ksw_total = xsw->xsw_nblks; 528 swap_ary[ti].ksw_used = xsw->xsw_used; 529 swap_ary[ti].ksw_flags = xsw->xsw_flags; 530 531 if (xsw->xsw_dev == NODEV) { 532 snprintf( 533 swap_ary[ti].ksw_devname, 534 sizeof(swap_ary[ti].ksw_devname), 535 "%s", 536 "[NFS swap]" 537 ); 538 } else { 539 snprintf( 540 swap_ary[ti].ksw_devname, 541 sizeof(swap_ary[ti].ksw_devname), 542 "%s%s", 543 ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), 544 devname(xsw->xsw_dev, S_IFCHR) 545 ); 546 } 547 ++ti; 548 } 549 free(xswbuf); 550 return(ti); 551 } 552