1 /* 2 * Copyright (c) 1999 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $FreeBSD: src/lib/libkvm/kvm_getswapinfo.c,v 1.10.2.4 2003/01/12 09:23:13 dillon Exp $ 35 * $DragonFly: src/lib/libkvm/kvm_getswapinfo.c,v 1.5 2006/03/18 17:15:35 dillon Exp $ 36 */ 37 38 #define _KERNEL_STRUCTURES 39 40 #include <sys/param.h> 41 #include <sys/time.h> 42 #include <sys/ucred.h> 43 #include <sys/stat.h> 44 #include <sys/conf.h> 45 #include <sys/blist.h> 46 #include <sys/sysctl.h> 47 #include <vm/vm_param.h> 48 49 #include <err.h> 50 #include <fcntl.h> 51 #include <kvm.h> 52 #include <nlist.h> 53 #include <paths.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <limits.h> 59 60 #include "kvm_private.h" 61 62 static struct nlist kvm_swap_nl[] = { 63 { "_swapblist" }, /* new radix swap list */ 64 { "_swdevt" }, /* list of swap devices and sizes */ 65 { "_nswdev" }, /* number of swap devices */ 66 { "_dmmax" }, /* maximum size of a swap block */ 67 { "" } 68 }; 69 70 #define NL_SWAPBLIST 0 71 #define NL_SWDEVT 1 72 #define NL_NSWDEV 2 73 #define NL_DMMAX 3 74 75 static int kvm_swap_nl_cached = 0; 76 static int nswdev; 77 static int unswdev; 78 static int dmmax; 79 80 static int nlist_init(kvm_t *kd); 81 static void dump_blist(kvm_t *kd); 82 static int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, 83 int swap_max, int flags); 84 85 #define SVAR(var) __STRING(var) /* to force expansion */ 86 #define KGET(idx, var) \ 87 KGET1(idx, &var, sizeof(var), SVAR(var)) 88 #define KGET1(idx, p, s, msg) \ 89 KGET2(kvm_swap_nl[idx].n_value, p, s, msg) 90 #define KGET2(addr, p, s, msg) \ 91 if (kvm_read(kd, (u_long)(addr), p, s) != s) \ 92 warnx("cannot read %s: %s", msg, kvm_geterr(kd)) 93 #define KGETN(idx, var) \ 94 KGET1N(idx, &var, sizeof(var), SVAR(var)) 95 #define KGET1N(idx, p, s, msg) \ 96 KGET2N(kvm_swap_nl[idx].n_value, p, s, msg) 97 #define KGET2N(addr, p, s, msg) \ 98 ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0) 99 #define KGETRET(addr, p, s, msg) \ 100 if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ 101 warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ 102 return (0); \ 103 } 104 105 #define GETSWDEVNAME(dev, str, flags) \ 106 if (dev == NODEV) { \ 107 strlcpy(str, "[NFS swap]", sizeof(str)); \ 108 } else { \ 109 snprintf( \ 110 str, sizeof(str), "%s%s", \ 111 ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), \ 112 devname(dev, S_IFCHR) \ 113 ); \ 114 } 115 116 int 117 kvm_getswapinfo( 118 kvm_t *kd, 119 struct kvm_swap *swap_ary, 120 int swap_max, 121 int flags 122 ) { 123 int i, ti, swi; 124 int ttl; 125 struct swdevt *sw; 126 struct swdevt swinfo; 127 128 /* 129 * clear cache 130 */ 131 if (kd == NULL) { 132 kvm_swap_nl_cached = 0; 133 return(0); 134 } 135 136 if (swap_max < 1) 137 return (-1); 138 139 /* 140 * Use sysctl if possible 141 */ 142 if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) { 143 ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags); 144 if (ti >= 0) 145 return(ti); 146 } 147 148 /* 149 * namelist 150 */ 151 if (!nlist_init(kd)) 152 return (-1); 153 154 swi = unswdev; 155 if (swi >= swap_max) 156 swi = swap_max - 1; 157 158 bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1)); 159 160 KGET(NL_SWDEVT, sw); 161 for (i = ti = 0; i < nswdev; ++i) { 162 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 163 164 if (swinfo.sw_nblks == 0) 165 continue; 166 167 /* 168 * The first dmmax is never allocated to avoid 169 * trashing the disklabels. 170 */ 171 ttl = swinfo.sw_nblks - dmmax; 172 if (ttl == 0) 173 continue; 174 175 swap_ary[swi].ksw_total += ttl; 176 swap_ary[swi].ksw_used += swinfo.sw_nused; 177 178 if (ti < swi) { 179 swap_ary[ti].ksw_total = ttl; 180 swap_ary[ti].ksw_used = swinfo.sw_nused; 181 swap_ary[ti].ksw_flags = swinfo.sw_flags; 182 GETSWDEVNAME(swinfo.sw_dev, swap_ary[ti].ksw_devname, 183 flags); 184 ++ti; 185 } 186 } 187 188 if (flags & SWIF_DUMP_TREE) 189 dump_blist(kd); 190 return (swi); 191 } 192 193 static int 194 nlist_init(kvm_t *kd) 195 { 196 int i; 197 struct swdevt *sw; 198 struct swdevt swinfo; 199 200 if (kvm_swap_nl_cached) 201 return (1); 202 203 if (kvm_nlist(kd, kvm_swap_nl) < 0) 204 return (0); 205 206 /* 207 * required entries 208 */ 209 if (kvm_swap_nl[NL_SWDEVT].n_value == 0 || 210 kvm_swap_nl[NL_NSWDEV].n_value == 0 || 211 kvm_swap_nl[NL_DMMAX].n_value == 0 || 212 kvm_swap_nl[NL_SWAPBLIST].n_type == 0) { 213 return (0); 214 } 215 216 /* 217 * get globals, type of swap 218 */ 219 KGET(NL_NSWDEV, nswdev); 220 KGET(NL_DMMAX, dmmax); 221 222 /* 223 * figure out how many actual swap devices are enabled 224 */ 225 KGET(NL_SWDEVT, sw); 226 for (i = unswdev = 0; i < nswdev; ++i) { 227 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 228 if (swinfo.sw_nblks) 229 ++unswdev; 230 231 } 232 233 kvm_swap_nl_cached = 1; 234 return (1); 235 } 236 237 /* 238 * scanradix() - support routine for radix scanner 239 */ 240 241 #define TABME tab, tab, "" 242 243 static int 244 scanradix( 245 blmeta_t *scan, 246 blmeta_t *scan_cache, 247 swblk_t blk, 248 int64_t radix, 249 swblk_t skip, 250 swblk_t count, 251 kvm_t *kd, 252 int dmmax, 253 int nswdev, 254 int64_t *availp, 255 int tab 256 ) { 257 blmeta_t meta; 258 blmeta_t scan_array[BLIST_BMAP_RADIX]; 259 int i; 260 261 if (scan_cache) { 262 meta = *scan_cache; 263 } else if (skip == BLIST_META_RADIX) { 264 if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) { 265 warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd)); 266 bzero(scan_array, sizeof(scan_array)); 267 } 268 meta = scan_array[0]; 269 } else { 270 KGET2(scan, &meta, sizeof(meta), "blmeta_t"); 271 } 272 273 /* 274 * Terminator 275 */ 276 if (meta.bm_bighint == (swblk_t)-1) { 277 printf("%*.*s(0x%06x,%lld) Terminator\n", 278 TABME, 279 blk, 280 (long long)radix 281 ); 282 return(-1); 283 } 284 285 if (radix == BLIST_BMAP_RADIX) { 286 /* 287 * Leaf bitmap 288 */ 289 printf("%*.*s(0x%06x,%lld) Bitmap %08x big=%d\n", 290 TABME, 291 blk, 292 (long long)radix, 293 (int)meta.u.bmu_bitmap, 294 meta.bm_bighint 295 ); 296 297 if (meta.u.bmu_bitmap) { 298 for (i = 0; i < BLIST_BMAP_RADIX; ++i) { 299 if (meta.u.bmu_bitmap & (1 << i)) 300 ++*availp; 301 } 302 } 303 } else if (meta.u.bmu_avail == radix) { 304 /* 305 * Meta node if all free 306 */ 307 printf("%*.*s(0x%06x,%lld) Submap ALL-FREE (big=%d) {\n", 308 TABME, 309 blk, 310 (long long)radix, 311 meta.bm_bighint 312 ); 313 *availp += radix; 314 } else if (meta.u.bmu_avail == 0) { 315 /* 316 * Meta node if all used 317 */ 318 printf("%*.*s(0x%06x,%lld) Submap ALL-ALLOCATED (big=%d)\n", 319 TABME, 320 blk, 321 (long long)radix, 322 meta.bm_bighint 323 ); 324 } else { 325 /* 326 * Meta node if not all free 327 */ 328 int i; 329 int next_skip; 330 int64_t avail_tmp = 0; 331 332 printf("%*.*s(0x%06x,%lld) Submap avail=%d big=%d {\n", 333 TABME, 334 blk, 335 (long long)radix, 336 (int)meta.u.bmu_avail, 337 meta.bm_bighint 338 ); 339 340 radix /= BLIST_META_RADIX; 341 next_skip = skip / BLIST_META_RADIX; 342 343 for (i = 1; i <= skip; i += next_skip) { 344 int r; 345 swblk_t vcount = (count > radix) ? 346 (swblk_t)radix : count; 347 348 r = scanradix( 349 &scan[i], 350 ((next_skip == 1) ? &scan_array[i] : NULL), 351 blk, 352 radix, 353 next_skip - 1, 354 vcount, 355 kd, 356 dmmax, 357 nswdev, 358 &avail_tmp, 359 tab + 4 360 ); 361 if (r < 0) 362 break; 363 blk += (swblk_t)radix; 364 } 365 *availp += avail_tmp; 366 if (avail_tmp == meta.u.bmu_avail) 367 printf("%*.*s}\n", TABME); 368 else 369 printf("%*.*s} (AVAIL MISMATCH %jd/%jd\n", 370 TABME, 371 (intmax_t)avail_tmp, 372 (intmax_t)meta.u.bmu_avail); 373 } 374 return(0); 375 } 376 377 static void 378 dump_blist(kvm_t *kd) 379 { 380 struct blist *swapblist = NULL; 381 struct blist blcopy = { 0 }; 382 int64_t avail = 0; 383 384 KGET(NL_SWAPBLIST, swapblist); 385 386 if (swapblist == NULL) { 387 printf("radix tree: NULL - no swap in system\n"); 388 return; 389 } 390 391 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 392 393 printf("radix tree: %d/%d/%lld blocks, %dK wired\n", 394 blcopy.bl_free, 395 blcopy.bl_blocks, 396 (long long)blcopy.bl_radix, 397 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 398 1024) 399 ); 400 401 scanradix( 402 blcopy.bl_root, 403 NULL, 404 0, 405 blcopy.bl_radix, 406 blcopy.bl_skip, 407 blcopy.bl_rootblks, 408 kd, 409 dmmax, 410 nswdev, 411 &avail, 412 0 413 ); 414 printf("final availability: %jd\n", (intmax_t)avail); 415 } 416 417 static 418 int 419 kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, 420 int swap_max, int flags) 421 { 422 size_t bytes = 0; 423 size_t ksize; 424 int ti; 425 int swi; 426 int n; 427 int i; 428 char *xswbuf; 429 struct xswdev *xsw; 430 431 if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) 432 return(-1); 433 if (bytes == 0) 434 return(-1); 435 436 xswbuf = malloc(bytes); 437 if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) { 438 free(xswbuf); 439 return(-1); 440 } 441 if (bytes == 0) { 442 free(xswbuf); 443 return(-1); 444 } 445 446 /* 447 * Calculate size of xsw entry returned by kernel (it can be larger 448 * than the one we have if there is a version mismatch). 449 */ 450 ksize = ((struct xswdev *)xswbuf)->xsw_size; 451 n = (int)(bytes / ksize); 452 453 /* 454 * Calculate the number of live swap devices and calculate 455 * the swap_ary[] index used for the cumulative result (swi) 456 */ 457 for (i = swi = 0; i < n; ++i) { 458 xsw = (void *)((char *)xswbuf + i * ksize); 459 if ((xsw->xsw_flags & SW_FREED) == 0) 460 continue; 461 ++swi; 462 } 463 if (swi >= swap_max) 464 swi = swap_max - 1; 465 466 bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1)); 467 468 /* 469 * Accumulate results. If the provided swap_ary[] is too 470 * small will only populate up to the available entries, 471 * but we always populate the cumulative results entry. 472 */ 473 for (i = ti = 0; i < n; ++i) { 474 xsw = (void *)((char *)xswbuf + i * ksize); 475 476 if ((xsw->xsw_flags & SW_FREED) == 0) 477 continue; 478 479 swap_ary[swi].ksw_total += xsw->xsw_nblks; 480 swap_ary[swi].ksw_used += xsw->xsw_used; 481 482 if (ti < swi) { 483 swap_ary[ti].ksw_total = xsw->xsw_nblks; 484 swap_ary[ti].ksw_used = xsw->xsw_used; 485 swap_ary[ti].ksw_flags = xsw->xsw_flags; 486 GETSWDEVNAME(xsw->xsw_dev, swap_ary[ti].ksw_devname, 487 flags); 488 ++ti; 489 } 490 } 491 492 free(xswbuf); 493 return(swi); 494 } 495