1 /* 2 * Copyright (c) 1999, Matthew Dillon. All Rights Reserved. 3 * Copyright (c) 2001, Thomas Moestl 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided under the terms of the BSD 7 * Copyright as found in /usr/src/COPYRIGHT in the FreeBSD source tree. 8 */ 9 10 #ifndef lint 11 static const char copyright[] = 12 "@(#) Copyright (c) 1999\n" 13 "Matthew Dillon. All rights reserved.\n"; 14 #endif /* not lint */ 15 16 #ifndef lint 17 static const char rcsid[] = 18 "$FreeBSD$"; 19 #endif /* not lint */ 20 21 #include <sys/param.h> 22 #include <sys/lock.h> 23 #include <sys/mutex.h> 24 #include <sys/time.h> 25 #include <sys/stat.h> 26 #include <sys/conf.h> 27 #include <sys/blist.h> 28 #include <sys/sysctl.h> 29 30 #include <vm/vm_param.h> 31 32 #include <err.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <kvm.h> 36 #include <nlist.h> 37 #include <paths.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <limits.h> 43 44 #include "kvm_private.h" 45 46 static struct nlist kvm_swap_nl[] = { 47 { "_swapblist" }, /* new radix swap list */ 48 { "_swdevt" }, /* list of swap devices and sizes */ 49 { "_nswdev" }, /* number of swap devices */ 50 { "_dmmax" }, /* maximum size of a swap block */ 51 { "" } 52 }; 53 54 #define NL_SWAPBLIST 0 55 #define NL_SWDEVT 1 56 #define NL_NSWDEV 2 57 #define NL_DMMAX 3 58 59 static int kvm_swap_nl_cached = 0; 60 static int nswdev; 61 static int unswdev; /* number of found swap dev's */ 62 static int dmmax; 63 64 static void getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, 65 int swap_max, int flags); 66 static int kvm_getswapinfo2(kvm_t *kd, struct kvm_swap *swap_ary, 67 int swap_max, int flags); 68 static int kvm_getswapinfo_kvm(kvm_t *, struct kvm_swap *, int, int); 69 static int kvm_getswapinfo_sysctl(kvm_t *, struct kvm_swap *, int, int); 70 static int nlist_init(kvm_t *); 71 static int getsysctl(kvm_t *, char *, void *, size_t); 72 73 #define SVAR(var) __STRING(var) /* to force expansion */ 74 #define KGET(idx, var) \ 75 KGET1(idx, &var, sizeof(var), SVAR(var)) 76 #define KGET1(idx, p, s, msg) \ 77 KGET2(kvm_swap_nl[idx].n_value, p, s, msg) 78 #define KGET2(addr, p, s, msg) \ 79 if (kvm_read(kd, (u_long)(addr), p, s) != s) \ 80 warnx("cannot read %s: %s", msg, kvm_geterr(kd)) 81 #define KGETN(idx, var) \ 82 KGET1N(idx, &var, sizeof(var), SVAR(var)) 83 #define KGET1N(idx, p, s, msg) \ 84 KGET2N(kvm_swap_nl[idx].n_value, p, s, msg) 85 #define KGET2N(addr, p, s, msg) \ 86 ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0) 87 #define KGETRET(addr, p, s, msg) \ 88 if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ 89 warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ 90 return (0); \ 91 } 92 93 #define GETSWDEVNAME(dev, str, flags) \ 94 if (dev == NODEV) { \ 95 strlcpy(str, "[NFS swap]", sizeof(str)); \ 96 } else { \ 97 snprintf( \ 98 str, sizeof(str),"%s%s", \ 99 ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), \ 100 devname(dev, S_IFCHR) \ 101 ); \ 102 } 103 104 int 105 kvm_getswapinfo( 106 kvm_t *kd, 107 struct kvm_swap *swap_ary, 108 int swap_max, 109 int flags 110 ) { 111 int rv; 112 #ifdef DEBUG_SWAPINFO 113 int i; 114 #endif 115 116 /* 117 * clear cache 118 */ 119 if (kd == NULL) { 120 kvm_swap_nl_cached = 0; 121 return(0); 122 } 123 124 rv = kvm_getswapinfo2(kd, swap_ary, swap_max, flags); 125 126 /* This is only called when the tree shall be dumped. It needs kvm. */ 127 if (flags & SWIF_DUMP_TREE) { 128 #ifdef DEBUG_SWAPINFO 129 /* 130 * sanity check: Sizes must be equal - used field must be 131 * 0 after this. Fill it with total-used before, where 132 * getswapinfo_radix will subtrat total-used. 133 * This will of course only work if there is no swap activity 134 * while we are working, so this code is normally not active. 135 */ 136 for (i = 0; i < unswdev; i++) { 137 swap_ary[i].ksw_used = swap_ary[i].ksw_total - 138 swap_ary[i].ksw_used; 139 } 140 #endif 141 getswapinfo_radix(kd, swap_ary, swap_max, flags); 142 #ifdef DEBUG_SWAPINFO 143 for (i = 0; i < unswdev; i++) { 144 if (swap_ary[i].ksw_used != 0) { 145 fprintf(stderr, "kvm_getswapinfo: swap size " 146 "mismatch (%d blocks)!\n", 147 swap_ary[i].ksw_used 148 ); 149 } 150 } 151 /* This is fast enough now, so just do it again. */ 152 rv = kvm_getswapinfo2(kd, swap_ary, swap_max, flags); 153 #endif 154 } 155 156 return rv; 157 } 158 159 static int 160 kvm_getswapinfo2( 161 kvm_t *kd, 162 struct kvm_swap *swap_ary, 163 int swap_max, 164 int flags 165 ) { 166 if (ISALIVE(kd)) { 167 return kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags); 168 } else { 169 return kvm_getswapinfo_kvm(kd, swap_ary, swap_max, flags); 170 } 171 } 172 173 int 174 kvm_getswapinfo_kvm( 175 kvm_t *kd, 176 struct kvm_swap *swap_ary, 177 int swap_max, 178 int flags 179 ) { 180 int ti = 0; 181 182 /* 183 * namelist 184 */ 185 if (!nlist_init(kd)) 186 return (-1); 187 188 { 189 struct swdevt *sw; 190 int i; 191 192 ti = unswdev; 193 if (ti >= swap_max) 194 ti = swap_max - 1; 195 196 if (ti >= 0) 197 bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1)); 198 199 KGET(NL_SWDEVT, sw); 200 for (i = 0; i < unswdev; ++i) { 201 struct swdevt swinfo; 202 int ttl; 203 204 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 205 206 /* 207 * old style: everything in DEV_BSIZE'd chunks, 208 * convert to pages. 209 * 210 * new style: swinfo in DEV_BSIZE'd chunks but dmmax 211 * in pages. 212 * 213 * The first dmmax is never allocating to avoid 214 * trashing the disklabels 215 */ 216 217 ttl = swinfo.sw_nblks - dmmax; 218 219 if (ttl == 0) 220 continue; 221 222 if (i < ti) { 223 swap_ary[i].ksw_total = ttl; 224 swap_ary[i].ksw_used = swinfo.sw_used; 225 swap_ary[i].ksw_flags = swinfo.sw_flags; 226 GETSWDEVNAME(swinfo.sw_dev, 227 swap_ary[i].ksw_devname, flags 228 ); 229 } 230 if (ti >= 0) { 231 swap_ary[ti].ksw_total += ttl; 232 swap_ary[ti].ksw_used += swinfo.sw_used; 233 } 234 } 235 } 236 237 return(ti); 238 } 239 240 /* 241 * scanradix() - support routine for radix scanner 242 */ 243 244 #define TABME tab, tab, "" 245 246 static int 247 scanradix( 248 blmeta_t *scan, 249 daddr_t blk, 250 daddr_t radix, 251 daddr_t skip, 252 daddr_t count, 253 kvm_t *kd, 254 int dmmax, 255 int nswdev, 256 struct kvm_swap *swap_ary, 257 int swap_max, 258 int tab, 259 int flags 260 ) { 261 blmeta_t meta; 262 #ifdef DEBUG_SWAPINFO 263 int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev; 264 #endif 265 266 KGET2(scan, &meta, sizeof(meta), "blmeta_t"); 267 268 /* 269 * Terminator 270 */ 271 if (meta.bm_bighint == (daddr_t)-1) { 272 if (flags & SWIF_DUMP_TREE) { 273 printf("%*.*s(0x%06x,%d) Terminator\n", 274 TABME, 275 blk, 276 radix 277 ); 278 } 279 return(-1); 280 } 281 282 if (radix == BLIST_BMAP_RADIX) { 283 /* 284 * Leaf bitmap 285 */ 286 #ifdef DEBUG_SWAPINFO 287 int i; 288 #endif 289 290 if (flags & SWIF_DUMP_TREE) { 291 printf("%*.*s(0x%06x,%d) Bitmap %08x big=%d\n", 292 TABME, 293 blk, 294 radix, 295 (int)meta.u.bmu_bitmap, 296 meta.bm_bighint 297 ); 298 } 299 300 #ifdef DEBUG_SWAPINFO 301 /* 302 * If not all allocated, count. 303 */ 304 if (meta.u.bmu_bitmap != 0) { 305 for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) { 306 /* 307 * A 0 bit means allocated 308 */ 309 if ((meta.u.bmu_bitmap & (1 << i))) { 310 int t = 0; 311 312 if (nswdev) 313 t = (blk + i) / dmmax % nswdev; 314 if (t < ti) 315 --swap_ary[t].ksw_used; 316 if (ti >= 0) 317 --swap_ary[ti].ksw_used; 318 } 319 } 320 } 321 #endif 322 } else if (meta.u.bmu_avail == radix) { 323 /* 324 * Meta node if all free 325 */ 326 if (flags & SWIF_DUMP_TREE) { 327 printf("%*.*s(0x%06x,%d) Submap ALL-FREE {\n", 328 TABME, 329 blk, 330 radix 331 ); 332 } 333 #ifdef DEBUG_SWAPINFO 334 /* 335 * Note: both dmmax and radix are powers of 2. However, dmmax 336 * may be larger then radix so use a smaller increment if 337 * necessary. 338 */ 339 { 340 int t; 341 int tinc = dmmax; 342 343 while (tinc > radix) 344 tinc >>= 1; 345 346 for (t = blk; t < blk + radix; t += tinc) { 347 int u = (nswdev) ? (t / dmmax % nswdev) : 0; 348 349 if (u < ti) 350 swap_ary[u].ksw_used -= tinc; 351 if (ti >= 0) 352 swap_ary[ti].ksw_used -= tinc; 353 } 354 } 355 #endif 356 } else if (meta.u.bmu_avail == 0) { 357 /* 358 * Meta node if all used 359 */ 360 if (flags & SWIF_DUMP_TREE) { 361 printf("%*.*s(0x%06x,%d) Submap ALL-ALLOCATED\n", 362 TABME, 363 blk, 364 radix 365 ); 366 } 367 } else { 368 /* 369 * Meta node if not all free 370 */ 371 int i; 372 int next_skip; 373 374 if (flags & SWIF_DUMP_TREE) { 375 printf("%*.*s(0x%06x,%d) Submap avail=%d big=%d {\n", 376 TABME, 377 blk, 378 radix, 379 (int)meta.u.bmu_avail, 380 meta.bm_bighint 381 ); 382 } 383 384 radix >>= BLIST_META_RADIX_SHIFT; 385 next_skip = skip >> BLIST_META_RADIX_SHIFT; 386 387 for (i = 1; i <= skip; i += next_skip) { 388 int r; 389 daddr_t vcount = (count > radix) ? radix : count; 390 391 r = scanradix( 392 &scan[i], 393 blk, 394 radix, 395 next_skip - 1, 396 vcount, 397 kd, 398 dmmax, 399 nswdev, 400 swap_ary, 401 swap_max, 402 tab + 4, 403 flags 404 ); 405 if (r < 0) 406 break; 407 blk += radix; 408 } 409 if (flags & SWIF_DUMP_TREE) { 410 printf("%*.*s}\n", TABME); 411 } 412 } 413 return(0); 414 } 415 416 static void 417 getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags) 418 { 419 struct blist *swapblist = NULL; 420 struct blist blcopy = { 0 }; 421 422 if (!nlist_init(kd)) { 423 fprintf(stderr, "radix tree: nlist_init failed!\n"); 424 return; 425 } 426 427 KGET(NL_SWAPBLIST, swapblist); 428 429 if (swapblist == NULL) { 430 if (flags & SWIF_DUMP_TREE) 431 printf("radix tree: NULL - no swap in system\n"); 432 return; 433 } 434 435 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 436 437 if (flags & SWIF_DUMP_TREE) { 438 printf("radix tree: %d/%d/%d blocks, %dK wired\n", 439 blcopy.bl_free, 440 blcopy.bl_blocks, 441 blcopy.bl_radix, 442 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 443 1024) 444 ); 445 } 446 scanradix( 447 blcopy.bl_root, 448 0, 449 blcopy.bl_radix, 450 blcopy.bl_skip, 451 blcopy.bl_rootblks, 452 kd, 453 dmmax, 454 nswdev, 455 swap_ary, 456 swap_max, 457 0, 458 flags 459 ); 460 } 461 462 #define GETSYSCTL(kd, name, var) \ 463 getsysctl(kd, name, &(var), sizeof(var)) 464 465 /* The maximum MIB length for vm.swap_info and an additional device number */ 466 #define SWI_MAXMIB 3 467 468 int 469 kvm_getswapinfo_sysctl( 470 kvm_t *kd, 471 struct kvm_swap *swap_ary, 472 int swap_max, 473 int flags 474 ) { 475 int ti, ttl; 476 size_t mibi, len; 477 int soid[SWI_MAXMIB]; 478 struct xswdev xsd; 479 struct kvm_swap tot; 480 481 if (!GETSYSCTL(kd, "vm.dmmax", dmmax)) 482 return -1; 483 484 mibi = SWI_MAXMIB - 1; 485 if (sysctlnametomib("vm.swap_info", soid, &mibi) == -1) { 486 _kvm_err(kd, kd->program, "sysctlnametomib failed: %s", 487 strerror(errno)); 488 return -1; 489 } 490 bzero(&tot, sizeof(tot)); 491 for (unswdev = 0;; unswdev++) { 492 soid[mibi] = unswdev; 493 len = sizeof(xsd); 494 if (sysctl(soid, mibi + 1, &xsd, &len, NULL, 0) == -1) { 495 if (errno == ENOENT) 496 break; 497 _kvm_err(kd, kd->program, "cannot read sysctl: %s.", 498 strerror(errno)); 499 return -1; 500 } 501 if (len != sizeof(xsd)) { 502 _kvm_err(kd, kd->program, "struct xswdev has unexpected " 503 "size; kernel and libkvm out of sync?"); 504 return -1; 505 } 506 if (xsd.xsw_version != XSWDEV_VERSION) { 507 _kvm_err(kd, kd->program, "struct xswdev version " 508 "mismatch; kernel and libkvm out of sync?"); 509 return -1; 510 } 511 512 ttl = xsd.xsw_nblks - dmmax; 513 if (unswdev < swap_max - 1) { 514 bzero(&swap_ary[unswdev], sizeof(swap_ary[unswdev])); 515 swap_ary[unswdev].ksw_total = ttl; 516 swap_ary[unswdev].ksw_used = xsd.xsw_used; 517 swap_ary[unswdev].ksw_flags = xsd.xsw_flags; 518 GETSWDEVNAME(xsd.xsw_dev, swap_ary[unswdev].ksw_devname, 519 flags); 520 } 521 tot.ksw_total += ttl; 522 tot.ksw_used += xsd.xsw_used; 523 } 524 525 ti = unswdev; 526 if (ti >= swap_max) 527 ti = swap_max - 1; 528 if (ti >= 0) 529 swap_ary[ti] = tot; 530 531 return(ti); 532 } 533 534 static int 535 nlist_init ( 536 kvm_t *kd 537 ) { 538 struct swdevt *sw; 539 540 if (kvm_swap_nl_cached) 541 return (1); 542 543 if (kvm_nlist(kd, kvm_swap_nl) < 0) 544 return (0); 545 546 /* 547 * required entries 548 */ 549 if ( 550 kvm_swap_nl[NL_SWDEVT].n_value == 0 || 551 kvm_swap_nl[NL_NSWDEV].n_value == 0 || 552 kvm_swap_nl[NL_DMMAX].n_value == 0 || 553 kvm_swap_nl[NL_SWAPBLIST].n_type == 0 554 ) { 555 return (0); 556 } 557 558 /* 559 * get globals, type of swap 560 */ 561 KGET(NL_NSWDEV, nswdev); 562 KGET(NL_DMMAX, dmmax); 563 564 /* 565 * figure out how many actual swap devices are enabled 566 */ 567 KGET(NL_SWDEVT, sw); 568 for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) { 569 struct swdevt swinfo; 570 571 KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo"); 572 if (swinfo.sw_nblks) 573 break; 574 } 575 ++unswdev; 576 577 kvm_swap_nl_cached = 1; 578 return (1); 579 } 580 581 static int 582 getsysctl ( 583 kvm_t *kd, 584 char *name, 585 void *ptr, 586 size_t len 587 ) { 588 size_t nlen = len; 589 if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { 590 _kvm_err(kd, kd->program, "cannot read sysctl %s:%s", name, 591 strerror(errno)); 592 return (0); 593 } 594 if (nlen != len) { 595 _kvm_err(kd, kd->program, "sysctl %s has unexpected size", name); 596 return (0); 597 } 598 return (1); 599 } 600