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