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