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 */ 36 37 #define _KERNEL_STRUCTURES 38 39 #include <sys/param.h> 40 #include <sys/time.h> 41 #include <sys/ucred.h> 42 #include <sys/stat.h> 43 #include <sys/conf.h> 44 #include <sys/blist.h> 45 #include <sys/sysctl.h> 46 #include <vm/vm_param.h> 47 48 #include <err.h> 49 #include <fcntl.h> 50 #include <nlist.h> 51 #include <paths.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include <limits.h> 57 58 #include "kvm.h" 59 #include "kvm_private.h" 60 61 static struct nlist kvm_swap_nl[] = { 62 { .n_name = "_swapblist" }, /* new radix swap list */ 63 { .n_name = "_swdevt" }, /* list of swap devices and sizes */ 64 { .n_name = "_nswdev" }, /* number of swap devices */ 65 { .n_name = "_dmmax" }, /* maximum size of a swap block */ 66 { .n_name = "" } 67 }; 68 69 #define NL_SWAPBLIST 0 70 #define NL_SWDEVT 1 71 #define NL_NSWDEV 2 72 #define NL_DMMAX 3 73 74 static int kvm_swap_nl_cached = 0; 75 static int nswdev; 76 static int unswdev; 77 static int dmmax; 78 79 static int nlist_init(kvm_t *kd); 80 static void dump_blist(kvm_t *kd); 81 static int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, 82 int swap_max, int flags); 83 84 #define SVAR(var) __STRING(var) /* to force expansion */ 85 #define KGET(idx, var) \ 86 KGET1(idx, &var, sizeof(var), SVAR(var)) 87 #define KGET1(idx, p, s, msg) \ 88 KGET2(kvm_swap_nl[idx].n_value, p, s, msg) 89 #define KGET2(addr, p, s, msg) \ 90 if (kvm_read(kd, (u_long)(addr), p, s) != s) \ 91 warnx("cannot read %s: %s", msg, kvm_geterr(kd)) 92 #define KGETN(idx, var) \ 93 KGET1N(idx, &var, sizeof(var), SVAR(var)) 94 #define KGET1N(idx, p, s, msg) \ 95 KGET2N(kvm_swap_nl[idx].n_value, p, s, msg) 96 #define KGET2N(addr, p, s, msg) \ 97 ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0) 98 #define KGETRET(addr, p, s, msg) \ 99 if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ 100 warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ 101 return (0); \ 102 } 103 104 #define GETSWDEVNAME(dev, str, flags) \ 105 if (dev == NODEV) { \ 106 strlcpy(str, "[NFS swap]", sizeof(str)); \ 107 } else { \ 108 snprintf( \ 109 str, sizeof(str), "%s%s", \ 110 ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), \ 111 devname(dev, S_IFCHR) \ 112 ); \ 113 } 114 115 int 116 kvm_getswapinfo( 117 kvm_t *kd, 118 struct kvm_swap *swap_ary, 119 int swap_max, 120 int flags 121 ) { 122 int i, ti, swi; 123 swblk_t ttl; 124 struct swdevt *sw; 125 struct swdevt swinfo; 126 127 /* 128 * clear cache 129 */ 130 if (kd == NULL) { 131 kvm_swap_nl_cached = 0; 132 return(0); 133 } 134 135 if (swap_max < 1) 136 return (-1); 137 138 /* 139 * Use sysctl if possible 140 */ 141 if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) { 142 ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags); 143 if (ti >= 0) 144 return(ti); 145 } 146 147 /* 148 * namelist 149 */ 150 if (!nlist_init(kd)) 151 return (-1); 152 153 swi = unswdev; 154 if (swi >= swap_max) 155 swi = swap_max - 1; 156 157 bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1)); 158 159 KGET(NL_SWDEVT, sw); 160 for (i = ti = 0; i < nswdev; ++i) { 161 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 162 163 if (swinfo.sw_nblks == 0) 164 continue; 165 166 /* 167 * The first dmmax is never allocated to avoid 168 * trashing the disklabels. 169 */ 170 ttl = swinfo.sw_nblks - dmmax; 171 if (ttl == 0) 172 continue; 173 174 swap_ary[swi].ksw_total += ttl; 175 swap_ary[swi].ksw_used += swinfo.sw_nused; 176 177 if (ti < swi) { 178 swap_ary[ti].ksw_total = ttl; 179 swap_ary[ti].ksw_used = swinfo.sw_nused; 180 swap_ary[ti].ksw_flags = swinfo.sw_flags; 181 GETSWDEVNAME(swinfo.sw_dev, swap_ary[ti].ksw_devname, 182 flags); 183 ++ti; 184 } 185 } 186 187 if (flags & SWIF_DUMP_TREE) 188 dump_blist(kd); 189 return (swi); 190 } 191 192 static int 193 nlist_init(kvm_t *kd) 194 { 195 int i; 196 struct swdevt *sw; 197 struct swdevt swinfo; 198 199 if (kvm_swap_nl_cached) 200 return (1); 201 202 if (kvm_nlist(kd, kvm_swap_nl) < 0) 203 return (0); 204 205 /* 206 * required entries 207 */ 208 if (kvm_swap_nl[NL_SWDEVT].n_value == 0 || 209 kvm_swap_nl[NL_NSWDEV].n_value == 0 || 210 kvm_swap_nl[NL_DMMAX].n_value == 0 || 211 kvm_swap_nl[NL_SWAPBLIST].n_type == 0) { 212 return (0); 213 } 214 215 /* 216 * get globals, type of swap 217 */ 218 KGET(NL_NSWDEV, nswdev); 219 KGET(NL_DMMAX, dmmax); 220 221 /* 222 * figure out how many actual swap devices are enabled 223 */ 224 KGET(NL_SWDEVT, sw); 225 for (i = unswdev = 0; i < nswdev; ++i) { 226 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 227 if (swinfo.sw_nblks) 228 ++unswdev; 229 230 } 231 232 kvm_swap_nl_cached = 1; 233 return (1); 234 } 235 236 /* 237 * scanradix() - support routine for radix scanner 238 */ 239 240 #define TABME tab, tab, "" 241 242 static int 243 scanradix( 244 blmeta_t *scan, 245 blmeta_t *scan_cache, 246 swblk_t blk, 247 int64_t radix, 248 swblk_t skip, 249 swblk_t count, 250 kvm_t *kd, 251 int dmmaxr, 252 int nswdevr, 253 int64_t *availp, 254 int tab 255 ) { 256 blmeta_t meta; 257 blmeta_t scan_array[BLIST_BMAP_RADIX]; 258 int64_t avail_tmp = 0; 259 unsigned int iu; 260 int im; 261 int next_skip; 262 263 if (scan_cache) { 264 meta = *scan_cache; 265 } else if (skip == BLIST_META_RADIX) { 266 if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) { 267 warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd)); 268 bzero(scan_array, sizeof(scan_array)); 269 } 270 meta = scan_array[0]; 271 } else { 272 KGET2(scan, &meta, sizeof(meta), "blmeta_t"); 273 } 274 275 /* 276 * Terminator 277 */ 278 if (meta.bm_bighint == (swblk_t)-1) { 279 printf("%*.*s(0x%06jx,%jd) Terminator\n", 280 TABME, 281 (intmax_t)blk, 282 (intmax_t)radix 283 ); 284 return(-1); 285 } 286 287 if (radix == BLIST_BMAP_RADIX) { 288 /* 289 * Leaf bitmap 290 */ 291 printf("%*.*s(0x%06jx,%jd) Bitmap %016jx big=%jd\n", 292 TABME, 293 (intmax_t)blk, 294 (intmax_t)radix, 295 (intmax_t)meta.u.bmu_bitmap, 296 (intmax_t)meta.bm_bighint 297 ); 298 299 if (meta.u.bmu_bitmap) { 300 for (iu = 0; iu < BLIST_BMAP_RADIX; ++iu) { 301 if (meta.u.bmu_bitmap & (1 << iu)) 302 ++*availp; 303 } 304 } 305 } else if (meta.u.bmu_avail == radix) { 306 /* 307 * Meta node if all free 308 */ 309 printf("%*.*s(0x%06jx,%jd) Submap ALL-FREE (big=%jd) {\n", 310 TABME, 311 (intmax_t)blk, 312 (intmax_t)radix, 313 (intmax_t)meta.bm_bighint 314 ); 315 *availp += radix; 316 } else if (meta.u.bmu_avail == 0) { 317 /* 318 * Meta node if all used 319 */ 320 printf("%*.*s(0x%06jx,%jd) Submap ALL-ALLOCATED (big=%jd)\n", 321 TABME, 322 (intmax_t)blk, 323 (intmax_t)radix, 324 (intmax_t)meta.bm_bighint 325 ); 326 } else { 327 /* 328 * Meta node if not all free 329 */ 330 printf("%*.*s(0x%06jx,%jd) Submap avail=%jd big=%jd {\n", 331 TABME, 332 (intmax_t)blk, 333 (intmax_t)radix, 334 (intmax_t)meta.u.bmu_avail, 335 (intmax_t)meta.bm_bighint 336 ); 337 338 radix /= BLIST_META_RADIX; 339 next_skip = skip / BLIST_META_RADIX; 340 341 for (im = 1; im <= skip; im += next_skip) { 342 int r; 343 swblk_t vcount = (count > radix) ? 344 (swblk_t)radix : count; 345 346 r = scanradix( 347 &scan[im], 348 ((next_skip == 1) ? &scan_array[im] : NULL), 349 blk, 350 radix, 351 next_skip - 1, 352 vcount, 353 kd, 354 dmmaxr, 355 nswdevr, 356 &avail_tmp, 357 tab + 4 358 ); 359 if (r < 0) 360 break; 361 blk += (swblk_t)radix; 362 } 363 *availp += avail_tmp; 364 if (avail_tmp == meta.u.bmu_avail) 365 printf("%*.*s}\n", TABME); 366 else 367 printf("%*.*s} (AVAIL MISMATCH %jd/%jd\n", 368 TABME, 369 (intmax_t)avail_tmp, 370 (intmax_t)meta.u.bmu_avail); 371 } 372 return(0); 373 } 374 375 static void 376 dump_blist(kvm_t *kd) 377 { 378 struct blist *swapblist = NULL; 379 struct blist blcopy = { 0 }; 380 int64_t avail = 0; 381 382 KGET(NL_SWAPBLIST, swapblist); 383 384 if (swapblist == NULL) { 385 printf("radix tree: NULL - no swap in system\n"); 386 return; 387 } 388 389 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 390 391 printf("radix tree: %jd/%jd/%jd blocks, %jdK wired\n", 392 (intmax_t)blcopy.bl_free, 393 (intmax_t)blcopy.bl_blocks, 394 (intmax_t)blcopy.bl_radix, 395 (intmax_t)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 396 1024) 397 ); 398 399 scanradix( 400 blcopy.bl_root, 401 NULL, 402 0, 403 blcopy.bl_radix, 404 blcopy.bl_skip, 405 blcopy.bl_rootblks, 406 kd, 407 dmmax, 408 nswdev, 409 &avail, 410 0 411 ); 412 printf("final availability: %jd\n", (intmax_t)avail); 413 } 414 415 static 416 int 417 kvm_getswapinfo_sysctl(kvm_t *kd __unused, struct kvm_swap *swap_ary, 418 int swap_max, int flags) 419 { 420 size_t bytes = 0; 421 size_t ksize; 422 int ti; 423 int swi; 424 int n; 425 int i; 426 char *xswbuf; 427 struct xswdev *xsw; 428 429 if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) 430 return(-1); 431 if (bytes == 0) 432 return(-1); 433 434 xswbuf = malloc(bytes); 435 if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) { 436 free(xswbuf); 437 return(-1); 438 } 439 if (bytes == 0) { 440 free(xswbuf); 441 return(-1); 442 } 443 444 /* 445 * Calculate size of xsw entry returned by kernel (it can be larger 446 * than the one we have if there is a version mismatch). 447 */ 448 ksize = ((struct xswdev *)xswbuf)->xsw_size; 449 n = (int)(bytes / ksize); 450 451 /* 452 * Calculate the number of live swap devices and calculate 453 * the swap_ary[] index used for the cumulative result (swi) 454 */ 455 for (i = swi = 0; i < n; ++i) { 456 xsw = (void *)((char *)xswbuf + i * ksize); 457 if ((xsw->xsw_flags & SW_FREED) == 0) 458 continue; 459 ++swi; 460 } 461 if (swi >= swap_max) 462 swi = swap_max - 1; 463 464 bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1)); 465 466 /* 467 * Accumulate results. If the provided swap_ary[] is too 468 * small will only populate up to the available entries, 469 * but we always populate the cumulative results entry. 470 */ 471 for (i = ti = 0; i < n; ++i) { 472 xsw = (void *)((char *)xswbuf + i * ksize); 473 474 if ((xsw->xsw_flags & SW_FREED) == 0) 475 continue; 476 477 swap_ary[swi].ksw_total += xsw->xsw_nblks; 478 swap_ary[swi].ksw_used += xsw->xsw_used; 479 480 if (ti < swi) { 481 swap_ary[ti].ksw_total = xsw->xsw_nblks; 482 swap_ary[ti].ksw_used = xsw->xsw_used; 483 swap_ary[ti].ksw_flags = xsw->xsw_flags; 484 GETSWDEVNAME(xsw->xsw_dev, swap_ary[ti].ksw_devname, 485 flags); 486 ++ti; 487 } 488 } 489 490 free(xswbuf); 491 return(swi); 492 } 493