kvm_getswapinfo.c revision 43173
1/* 2 * Copyright (c) 1999, Matthew Dillon. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided under the terms of the BSD 6 * Copyright as found in /usr/src/COPYRIGHT in the FreeBSD source tree. 7 */ 8 9#ifndef lint 10static const char copyright[] = 11 "@(#) Copyright (c) 1999\n" 12 "Matthew Dillon. All rights reserved.\n"; 13#endif /* not lint */ 14 15#ifndef lint 16static const char rcsid[] = 17 "$Id: kvm_getswapinfo.c,v 1.2 1999/01/22 10:57:03 dillon Exp $"; 18#endif /* not lint */ 19 20#include <sys/param.h> 21#include <sys/time.h> 22#include <sys/vnode.h> 23#include <sys/ucred.h> 24#include <sys/stat.h> 25#include <sys/conf.h> 26#include <sys/rlist.h> 27#include <sys/blist.h> 28 29#include <err.h> 30#include <fcntl.h> 31#include <kvm.h> 32#include <limits.h> 33#include <nlist.h> 34#include <stdio.h> 35#include <stdlib.h> 36#include <string.h> 37#include <unistd.h> 38 39static struct nlist kvm_swap_nl[] = { 40 { "_swaplist" }, /* old style swap list */ 41 { "_swapblist" }, /* new radix swap list */ 42 { "_swdevt" }, /* list of swap devices and sizes */ 43 { "_nswdev" }, /* number of swap devices */ 44 { "_dmmax" }, /* maximum size of a swap block */ 45 { "" } 46}; 47 48#define NL_SWAPLIST 0 49#define NL_SWAPBLIST 1 50#define NL_SWDEVT 2 51#define NL_NSWDEV 3 52#define NL_DMMAX 4 53 54static int kvm_swap_nl_cached = 0; 55static int nswdev; 56static int unswdev; 57static int dmmax; 58static int type; 59 60static void getswapinfo_old(kvm_t *kd, kvm_swap_t swap_ary, int swap_max, int flags); 61static void getswapinfo_radix(kvm_t *kd, kvm_swap_t swap_ary, int swap_max, int flags); 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 83int 84kvm_getswapinfo( 85 kvm_t *kd, 86 struct kvm_swap *swap_ary, 87 int swap_max, 88 int flags 89) { 90 int ti = 0; 91 92 /* 93 * clear cache 94 */ 95 if (kd == NULL) { 96 kvm_swap_nl_cached = 0; 97 return(0); 98 } 99 100 /* 101 * namelist 102 */ 103 if (kvm_swap_nl_cached == 0) { 104 struct swdevt *sw; 105 106 if (kvm_nlist(kd, kvm_swap_nl) < 0) 107 return(-1); 108 109 /* 110 * required entries 111 */ 112 113 if ( 114 kvm_swap_nl[NL_SWDEVT].n_value == 0 || 115 kvm_swap_nl[NL_NSWDEV].n_value == 0 || 116 kvm_swap_nl[NL_DMMAX].n_value == 0 117 ) { 118 return(-1); 119 } 120 121 /* 122 * get globals, type of swap 123 */ 124 125 KGET(NL_NSWDEV, nswdev); 126 KGET(NL_DMMAX, dmmax); 127 128 if (kvm_swap_nl[NL_SWAPLIST].n_value) 129 type = 1; 130 131 if (kvm_swap_nl[NL_SWAPBLIST].n_value) 132 type = 2; 133 134 /* 135 * figure out how many actual swap devices are enabled 136 */ 137 138 KGET(NL_SWDEVT, sw); 139 for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) { 140 struct swdevt swinfo; 141 142 KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo"); 143 if (swinfo.sw_nblks) 144 break; 145 } 146 ++unswdev; 147 148 kvm_swap_nl_cached = 1; 149 } 150 151 152 { 153 struct swdevt *sw; 154 int i; 155 156 ti = unswdev; 157 if (ti >= swap_max) 158 ti = swap_max - 1; 159 160 if (ti >= 0) 161 bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1)); 162 163 KGET(NL_SWDEVT, sw); 164 for (i = 0; i < unswdev; ++i) { 165 struct swdevt swinfo; 166 int ttl; 167 168 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 169 170 /* 171 * old style: everything in DEV_BSIZE'd chunks, 172 * convert to pages. 173 * 174 * new style: swinfo in DEV_BSIZE'd chunks but dmmax 175 * in pages. 176 */ 177 178 if (type == 1) 179 ttl = dbtoc(swinfo.sw_nblks); 180 else 181 ttl = swinfo.sw_nblks; 182 183 if (ttl == 0) 184 continue; 185 186 if (i < ti) { 187 swap_ary[i].ksw_total = ttl; 188 swap_ary[i].ksw_used = ttl; 189 swap_ary[i].ksw_flags = swinfo.sw_flags; 190 if (swinfo.sw_dev == NODEV) { 191 snprintf( 192 swap_ary[i].ksw_devname, 193 sizeof(swap_ary[i].ksw_devname), 194 "%s", 195 "[NFS swap]" 196 ); 197 } else { 198 snprintf( 199 swap_ary[i].ksw_devname, 200 sizeof(swap_ary[i].ksw_devname), 201 "%s%s", 202 ((flags & SWIF_DEV_PREFIX) ? "/dev/" : ""), 203 devname(swinfo.sw_dev, S_IFBLK) 204 ); 205 } 206 } 207 if (ti >= 0) { 208 swap_ary[ti].ksw_total += ttl; 209 swap_ary[ti].ksw_used += ttl; 210 } 211 } 212 } 213 214 switch(type) { 215 case 1: 216 getswapinfo_old(kd, swap_ary, swap_max, flags); 217 break; 218 case 2: 219 getswapinfo_radix(kd, swap_ary, swap_max, flags); 220 break; 221 default: 222 ti = -1; 223 break; 224 } 225 return(ti); 226} 227 228/* 229 * scanradix() - support routine for radix scanner 230 */ 231 232#define TABME tab, tab, "" 233 234static int 235scanradix( 236 blmeta_t *scan, 237 daddr_t blk, 238 daddr_t radix, 239 daddr_t skip, 240 daddr_t count, 241 kvm_t *kd, 242 int dmmax, 243 int nswdev, 244 kvm_swap_t swap_ary, 245 int swap_max, 246 int tab, 247 int flags 248) { 249 blmeta_t meta; 250 int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev; 251 252 KGET2(scan, &meta, sizeof(meta), "blmeta_t"); 253 254 /* 255 * Terminator 256 */ 257 if (meta.bm_bighint == (daddr_t)-1) { 258 if (flags & SWIF_DUMP_TREE) { 259 printf("%*.*s(0x%06x,%d) Terminator\n", 260 TABME, 261 blk, 262 radix 263 ); 264 } 265 return(-1); 266 } 267 268 if (radix == BLIST_BMAP_RADIX) { 269 /* 270 * Leaf bitmap 271 */ 272 int i; 273 274 if (flags & SWIF_DUMP_TREE) { 275 printf("%*.*s(0x%06x,%d) Bitmap %08x big=%d\n", 276 TABME, 277 blk, 278 radix, 279 (int)meta.u.bmu_bitmap, 280 meta.bm_bighint 281 ); 282 } 283 284 /* 285 * If not all allocated, count. 286 */ 287 if (meta.u.bmu_bitmap != 0) { 288 for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) { 289 /* 290 * A 0 bit means allocated 291 */ 292 if ((meta.u.bmu_bitmap & (1 << i))) { 293 int t = 0; 294 295 if (nswdev) 296 t = (blk + i) / dmmax % nswdev; 297 if (t < ti) 298 --swap_ary[t].ksw_used; 299 if (ti >= 0) 300 --swap_ary[ti].ksw_used; 301 } 302 } 303 } 304 } else if (meta.u.bmu_avail == radix) { 305 /* 306 * Meta node if all free 307 */ 308 if (flags & SWIF_DUMP_TREE) { 309 printf("%*.*s(0x%06x,%d) Submap ALL-FREE {\n", 310 TABME, 311 blk, 312 radix, 313 (int)meta.u.bmu_avail, 314 meta.bm_bighint 315 ); 316 } 317 /* 318 * Note: both dmmax and radix are powers of 2. However, dmmax 319 * may be larger then radix so use a smaller increment if 320 * necessary. 321 */ 322 { 323 int t; 324 int tinc = dmmax; 325 326 while (tinc > radix) 327 tinc >>= 1; 328 329 for (t = blk; t < blk + radix; t += tinc) { 330 int u = (nswdev) ? (t / dmmax % nswdev) : 0; 331 332 if (u < ti) 333 swap_ary[u].ksw_used -= tinc; 334 if (ti >= 0) 335 swap_ary[ti].ksw_used -= tinc; 336 } 337 } 338 } else if (meta.u.bmu_avail == 0) { 339 /* 340 * Meta node if all used 341 */ 342 if (flags & SWIF_DUMP_TREE) { 343 printf("%*.*s(0x%06x,%d) Submap ALL-ALLOCATED\n", 344 TABME, 345 blk, 346 radix, 347 (int)meta.u.bmu_avail, 348 meta.bm_bighint 349 ); 350 } 351 } else { 352 /* 353 * Meta node if not all free 354 */ 355 int i; 356 int next_skip; 357 358 if (flags & SWIF_DUMP_TREE) { 359 printf("%*.*s(0x%06x,%d) Submap avail=%d big=%d {\n", 360 TABME, 361 blk, 362 radix, 363 (int)meta.u.bmu_avail, 364 meta.bm_bighint 365 ); 366 } 367 368 radix >>= BLIST_META_RADIX_SHIFT; 369 next_skip = skip >> BLIST_META_RADIX_SHIFT; 370 371 for (i = 1; i <= skip; i += next_skip) { 372 int r; 373 daddr_t vcount = (count > radix) ? radix : count; 374 375 r = scanradix( 376 &scan[i], 377 blk, 378 radix, 379 next_skip - 1, 380 vcount, 381 kd, 382 dmmax, 383 nswdev, 384 swap_ary, 385 swap_max, 386 tab + 4, 387 flags 388 ); 389 if (r < 0) 390 break; 391 blk += radix; 392 } 393 if (flags & SWIF_DUMP_TREE) { 394 printf("%*.*s}\n", TABME); 395 } 396 } 397 return(0); 398} 399 400static void 401getswapinfo_radix(kvm_t *kd, kvm_swap_t swap_ary, int swap_max, int flags) 402{ 403 struct blist *swapblist = NULL; 404 struct blist blcopy = { 0 }; 405 406 KGET(NL_SWAPBLIST, swapblist); 407 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 408 409 if (flags & SWIF_DUMP_TREE) { 410 printf("radix tree: %d/%d/%d blocks, %dK wired\n", 411 blcopy.bl_free, 412 blcopy.bl_blocks, 413 blcopy.bl_radix, 414 (blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 415 1024 416 ); 417 } 418 scanradix( 419 blcopy.bl_root, 420 0, 421 blcopy.bl_radix, 422 blcopy.bl_skip, 423 blcopy.bl_rootblks, 424 kd, 425 dmmax, 426 nswdev, 427 swap_ary, 428 swap_max, 429 0, 430 flags 431 ); 432} 433 434static void 435getswapinfo_old(kvm_t *kd, kvm_swap_t swap_ary, int swap_max, int flags) 436{ 437 struct rlist *swapptr; 438 struct rlisthdr swaplist; 439 int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev; 440 441 KGET(NL_SWAPLIST, swaplist); 442 443 swapptr = swaplist.rlh_list; 444 445 while (swapptr) { 446 int top; 447 int bottom; 448 int next_block; 449 int t; 450 int v; 451 struct rlist head; 452 453 KGET2(swapptr, &head, sizeof(head), "swapptr"); 454 455 top = head.rl_end; 456 bottom = head.rl_start; 457 458 /* 459 * Handle interleave indexing 460 */ 461 462 while (top / dmmax != bottom / dmmax) { 463 next_block = ((bottom + dmmax) / dmmax); 464 465 t = (bottom / dmmax) % nswdev; 466 v = next_block * dmmax - bottom; 467 468 if (t < ti) 469 swap_ary[t].ksw_used -= dbtoc(v); 470 if (ti >= 0) 471 swap_ary[ti].ksw_used -= dbtoc(v); 472 473 bottom = next_block * dmmax; 474 } 475 476 t = (bottom / dmmax) % nswdev; 477 v = top - bottom + 1; 478 479 if (t < ti) 480 swap_ary[t].ksw_used -= dbtoc(v); 481 if (ti >= 0) 482 swap_ary[ti].ksw_used -= dbtoc(v); 483 484 swapptr = head.rl_next; 485 } 486} 487 488