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