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 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 struct kvm_swap *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 ); 314 } 315 /* 316 * Note: both dmmax and radix are powers of 2. However, dmmax 317 * may be larger then radix so use a smaller increment if 318 * necessary. 319 */ 320 { 321 int t; 322 int tinc = dmmax; 323 324 while (tinc > radix) 325 tinc >>= 1; 326 327 for (t = blk; t < blk + radix; t += tinc) { 328 int u = (nswdev) ? (t / dmmax % nswdev) : 0; 329 330 if (u < ti) 331 swap_ary[u].ksw_used -= tinc; 332 if (ti >= 0) 333 swap_ary[ti].ksw_used -= tinc; 334 } 335 } 336 } else if (meta.u.bmu_avail == 0) { 337 /* 338 * Meta node if all used 339 */ 340 if (flags & SWIF_DUMP_TREE) { 341 printf("%*.*s(0x%06x,%d) Submap ALL-ALLOCATED\n", 342 TABME, 343 blk, 344 radix 345 ); 346 } 347 } else { 348 /* 349 * Meta node if not all free 350 */ 351 int i; 352 int next_skip; 353 354 if (flags & SWIF_DUMP_TREE) { 355 printf("%*.*s(0x%06x,%d) Submap avail=%d big=%d {\n", 356 TABME, 357 blk, 358 radix, 359 (int)meta.u.bmu_avail, 360 meta.bm_bighint 361 ); 362 } 363 364 radix /= BLIST_META_RADIX; 365 next_skip = skip / BLIST_META_RADIX; 366 367 for (i = 1; i <= skip; i += next_skip) { 368 int r; 369 daddr_t vcount = (count > radix) ? radix : count; 370 371 r = scanradix( 372 &scan[i], 373 blk, 374 radix, 375 next_skip - 1, 376 vcount, 377 kd, 378 dmmax, 379 nswdev, 380 swap_ary, 381 swap_max, 382 tab + 4, 383 flags 384 ); 385 if (r < 0) 386 break; 387 blk += radix; 388 } 389 if (flags & SWIF_DUMP_TREE) { 390 printf("%*.*s}\n", TABME); 391 } 392 } 393 return(0); 394 } 395 396 static void 397 getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags) 398 { 399 struct blist *swapblist = NULL; 400 struct blist blcopy = { 0 }; 401 402 KGET(NL_SWAPBLIST, swapblist); 403 404 if (swapblist == NULL) { 405 if (flags & SWIF_DUMP_TREE) 406 printf("radix tree: NULL - no swap in system\n"); 407 return; 408 } 409 410 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 411 412 if (flags & SWIF_DUMP_TREE) { 413 printf("radix tree: %d/%d/%d blocks, %dK wired\n", 414 blcopy.bl_free, 415 blcopy.bl_blocks, 416 blcopy.bl_radix, 417 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 418 1024) 419 ); 420 } 421 scanradix( 422 blcopy.bl_root, 423 0, 424 blcopy.bl_radix, 425 blcopy.bl_skip, 426 blcopy.bl_rootblks, 427 kd, 428 dmmax, 429 nswdev, 430 swap_ary, 431 swap_max, 432 0, 433 flags 434 ); 435 } 436