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 47 #include <err.h> 48 #include <fcntl.h> 49 #include <kvm.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 57 static struct nlist kvm_swap_nl[] = { 58 { "_swapblist" }, /* new radix swap list */ 59 { "_swdevt" }, /* list of swap devices and sizes */ 60 { "_nswdev" }, /* number of swap devices */ 61 { "_dmmax" }, /* maximum size of a swap block */ 62 { "" } 63 }; 64 65 #define NL_SWAPBLIST 0 66 #define NL_SWDEVT 1 67 #define NL_NSWDEV 2 68 #define NL_DMMAX 3 69 70 static int kvm_swap_nl_cached = 0; 71 static int nswdev; 72 static int unswdev; 73 static int dmmax; 74 75 static void getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, 76 int swap_max, int flags); 77 78 #define SVAR(var) __STRING(var) /* to force expansion */ 79 #define KGET(idx, var) \ 80 KGET1(idx, &var, sizeof(var), SVAR(var)) 81 #define KGET1(idx, p, s, msg) \ 82 KGET2(kvm_swap_nl[idx].n_value, p, s, msg) 83 #define KGET2(addr, p, s, msg) \ 84 if (kvm_read(kd, (u_long)(addr), p, s) != s) \ 85 warnx("cannot read %s: %s", msg, kvm_geterr(kd)) 86 #define KGETN(idx, var) \ 87 KGET1N(idx, &var, sizeof(var), SVAR(var)) 88 #define KGET1N(idx, p, s, msg) \ 89 KGET2N(kvm_swap_nl[idx].n_value, p, s, msg) 90 #define KGET2N(addr, p, s, msg) \ 91 ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0) 92 #define KGETRET(addr, p, s, msg) \ 93 if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ 94 warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ 95 return (0); \ 96 } 97 98 int 99 kvm_getswapinfo( 100 kvm_t *kd, 101 struct kvm_swap *swap_ary, 102 int swap_max, 103 int flags 104 ) { 105 int ti = 0; 106 107 /* 108 * clear cache 109 */ 110 if (kd == NULL) { 111 kvm_swap_nl_cached = 0; 112 return(0); 113 } 114 115 /* 116 * namelist 117 */ 118 if (kvm_swap_nl_cached == 0) { 119 struct swdevt *sw; 120 121 if (kvm_nlist(kd, kvm_swap_nl) < 0) 122 return(-1); 123 124 /* 125 * required entries 126 */ 127 128 if ( 129 kvm_swap_nl[NL_SWDEVT].n_value == 0 || 130 kvm_swap_nl[NL_NSWDEV].n_value == 0 || 131 kvm_swap_nl[NL_DMMAX].n_value == 0 || 132 kvm_swap_nl[NL_SWAPBLIST].n_type == 0 133 ) { 134 return(-1); 135 } 136 137 /* 138 * get globals, type of swap 139 */ 140 141 KGET(NL_NSWDEV, nswdev); 142 KGET(NL_DMMAX, dmmax); 143 144 /* 145 * figure out how many actual swap devices are enabled 146 */ 147 148 KGET(NL_SWDEVT, sw); 149 for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) { 150 struct swdevt swinfo; 151 152 KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo"); 153 if (swinfo.sw_nblks) 154 break; 155 } 156 ++unswdev; 157 158 kvm_swap_nl_cached = 1; 159 } 160 161 162 { 163 struct swdevt *sw; 164 int i; 165 166 ti = unswdev; 167 if (ti >= swap_max) 168 ti = swap_max - 1; 169 170 if (ti >= 0) 171 bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1)); 172 173 KGET(NL_SWDEVT, sw); 174 for (i = 0; i < unswdev; ++i) { 175 struct swdevt swinfo; 176 int ttl; 177 178 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 179 180 /* 181 * old style: everything in DEV_BSIZE'd chunks, 182 * convert to pages. 183 * 184 * new style: swinfo in DEV_BSIZE'd chunks but dmmax 185 * in pages. 186 * 187 * The first dmmax is never allocating to avoid 188 * trashing the disklabels 189 */ 190 191 ttl = swinfo.sw_nblks - dmmax; 192 193 if (ttl == 0) 194 continue; 195 196 if (i < ti) { 197 swap_ary[i].ksw_total = ttl; 198 swap_ary[i].ksw_used = ttl; 199 swap_ary[i].ksw_flags = swinfo.sw_flags; 200 if (swinfo.sw_dev == NODEV) { 201 snprintf( 202 swap_ary[i].ksw_devname, 203 sizeof(swap_ary[i].ksw_devname), 204 "%s", 205 "[NFS swap]" 206 ); 207 } else { 208 snprintf( 209 swap_ary[i].ksw_devname, 210 sizeof(swap_ary[i].ksw_devname), 211 "%s%s", 212 ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), 213 devname(swinfo.sw_dev, S_IFCHR) 214 ); 215 } 216 } 217 if (ti >= 0) { 218 swap_ary[ti].ksw_total += ttl; 219 swap_ary[ti].ksw_used += ttl; 220 } 221 } 222 } 223 224 getswapinfo_radix(kd, swap_ary, swap_max, flags); 225 return(ti); 226 } 227 228 /* 229 * scanradix() - support routine for radix scanner 230 */ 231 232 #define TABME tab, tab, "" 233 234 static int 235 scanradix( 236 blmeta_t *scan, 237 blmeta_t *scan_cache, 238 swblk_t blk, 239 int64_t radix, 240 swblk_t skip, 241 swblk_t count, 242 kvm_t *kd, 243 int dmmax, 244 int nswdev, 245 struct kvm_swap *swap_ary, 246 int swap_max, 247 int tab, 248 int flags 249 ) { 250 blmeta_t meta; 251 blmeta_t scan_array[BLIST_BMAP_RADIX]; 252 int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev; 253 254 if (scan_cache) { 255 meta = *scan_cache; 256 } else if (skip == BLIST_META_RADIX) { 257 if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) { 258 warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd)); 259 bzero(scan_array, sizeof(scan_array)); 260 } 261 meta = scan_array[0]; 262 } else { 263 KGET2(scan, &meta, sizeof(meta), "blmeta_t"); 264 } 265 266 /* 267 * Terminator 268 */ 269 if (meta.bm_bighint == (swblk_t)-1) { 270 if (flags & SWIF_DUMP_TREE) { 271 printf("%*.*s(0x%06x,%lld) Terminator\n", 272 TABME, 273 blk, 274 (long long)radix 275 ); 276 } 277 return(-1); 278 } 279 280 if (radix == BLIST_BMAP_RADIX) { 281 /* 282 * Leaf bitmap 283 */ 284 int i; 285 286 if (flags & SWIF_DUMP_TREE) { 287 printf("%*.*s(0x%06x,%lld) Bitmap %08x big=%d\n", 288 TABME, 289 blk, 290 (long long)radix, 291 (int)meta.u.bmu_bitmap, 292 meta.bm_bighint 293 ); 294 } 295 296 /* 297 * If not all allocated, count. 298 */ 299 if (meta.u.bmu_bitmap != 0) { 300 for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) { 301 /* 302 * A 0 bit means allocated 303 */ 304 if ((meta.u.bmu_bitmap & (1 << i))) { 305 int t = 0; 306 307 if (nswdev) 308 t = (blk + i) / dmmax % nswdev; 309 if (t < ti) 310 --swap_ary[t].ksw_used; 311 if (ti >= 0) 312 --swap_ary[ti].ksw_used; 313 } 314 } 315 } 316 } else if (meta.u.bmu_avail == radix) { 317 /* 318 * Meta node if all free 319 */ 320 if (flags & SWIF_DUMP_TREE) { 321 printf("%*.*s(0x%06x,%lld) Submap ALL-FREE {\n", 322 TABME, 323 blk, 324 (long long)radix 325 ); 326 } 327 /* 328 * Note: both dmmax and radix are powers of 2. However, dmmax 329 * may be larger then radix so use a smaller increment if 330 * necessary. 331 */ 332 { 333 int t; 334 int tinc = dmmax; 335 336 while (tinc > radix) 337 tinc >>= 1; 338 339 for (t = blk; t < blk + radix; t += tinc) { 340 int u = (nswdev) ? (t / dmmax % nswdev) : 0; 341 342 if (u < ti) 343 swap_ary[u].ksw_used -= tinc; 344 if (ti >= 0) 345 swap_ary[ti].ksw_used -= tinc; 346 } 347 } 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,%lld) Submap ALL-ALLOCATED\n", 354 TABME, 355 blk, 356 (long long)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,%lld) Submap avail=%d big=%d {\n", 368 TABME, 369 blk, 370 (long long)radix, 371 (int)meta.u.bmu_avail, 372 meta.bm_bighint 373 ); 374 } 375 376 radix /= BLIST_META_RADIX; 377 next_skip = skip / BLIST_META_RADIX; 378 379 for (i = 1; i <= skip; i += next_skip) { 380 int r; 381 swblk_t vcount = (count > radix) ? 382 (swblk_t)radix : count; 383 384 r = scanradix( 385 &scan[i], 386 ((next_skip == 1) ? &scan_array[i] : NULL), 387 blk, 388 radix, 389 next_skip - 1, 390 vcount, 391 kd, 392 dmmax, 393 nswdev, 394 swap_ary, 395 swap_max, 396 tab + 4, 397 flags 398 ); 399 if (r < 0) 400 break; 401 blk += (swblk_t)radix; 402 } 403 if (flags & SWIF_DUMP_TREE) { 404 printf("%*.*s}\n", TABME); 405 } 406 } 407 return(0); 408 } 409 410 static void 411 getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags) 412 { 413 struct blist *swapblist = NULL; 414 struct blist blcopy = { 0 }; 415 416 KGET(NL_SWAPBLIST, swapblist); 417 418 if (swapblist == NULL) { 419 if (flags & SWIF_DUMP_TREE) 420 printf("radix tree: NULL - no swap in system\n"); 421 return; 422 } 423 424 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 425 426 if (flags & SWIF_DUMP_TREE) { 427 printf("radix tree: %d/%d/%lld blocks, %dK wired\n", 428 blcopy.bl_free, 429 blcopy.bl_blocks, 430 (long long)blcopy.bl_radix, 431 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 432 1024) 433 ); 434 } 435 436 /* 437 * XXX Scan the radix tree in the kernel if we have more then one 438 * swap device so we can get per-device statistics. This can 439 * get nasty because swap devices are interleaved based on the 440 * maximum of (4), so the blist winds up not using any shortcuts. 441 * 442 * Otherwise just pull the free count out of the blist header, 443 * which is a billion times faster. 444 */ 445 if ((flags & SWIF_DUMP_TREE) || unswdev > 1) { 446 scanradix( 447 blcopy.bl_root, 448 NULL, 449 0, 450 blcopy.bl_radix, 451 blcopy.bl_skip, 452 blcopy.bl_rootblks, 453 kd, 454 dmmax, 455 nswdev, 456 swap_ary, 457 swap_max, 458 0, 459 flags 460 ); 461 } else { 462 swap_ary[0].ksw_used -= blcopy.bl_free; 463 } 464 } 465