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 10 static const char copyright[] = 11 "@(#) Copyright (c) 1999\n" 12 "Matthew Dillon. All rights reserved.\n"; 13 #endif /* not lint */ 14 15 #ifndef lint 16 static const char rcsid[] = 17 "$FreeBSD$"; 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/blist.h> 27 28 #include <err.h> 29 #include <fcntl.h> 30 #include <kvm.h> 31 #include <limits.h> 32 #include <nlist.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 static struct nlist kvm_swap_nl[] = { 39 { "_swapblist" }, /* new radix swap list */ 40 { "_swdevt" }, /* list of swap devices and sizes */ 41 { "_nswdev" }, /* number of swap devices */ 42 { "_dmmax" }, /* maximum size of a swap block */ 43 { "" } 44 }; 45 46 #define NL_SWAPBLIST 0 47 #define NL_SWDEVT 1 48 #define NL_NSWDEV 2 49 #define NL_DMMAX 3 50 51 static int kvm_swap_nl_cached = 0; 52 static int nswdev; 53 static int unswdev; 54 static int dmmax; 55 56 static void getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, 57 int swap_max, int flags); 58 59 #define SVAR(var) __STRING(var) /* to force expansion */ 60 #define KGET(idx, var) \ 61 KGET1(idx, &var, sizeof(var), SVAR(var)) 62 #define KGET1(idx, p, s, msg) \ 63 KGET2(kvm_swap_nl[idx].n_value, p, s, msg) 64 #define KGET2(addr, p, s, msg) \ 65 if (kvm_read(kd, (u_long)(addr), p, s) != s) \ 66 warnx("cannot read %s: %s", msg, kvm_geterr(kd)) 67 #define KGETN(idx, var) \ 68 KGET1N(idx, &var, sizeof(var), SVAR(var)) 69 #define KGET1N(idx, p, s, msg) \ 70 KGET2N(kvm_swap_nl[idx].n_value, p, s, msg) 71 #define KGET2N(addr, p, s, msg) \ 72 ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0) 73 #define KGETRET(addr, p, s, msg) \ 74 if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ 75 warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ 76 return (0); \ 77 } 78 79 int 80 kvm_getswapinfo( 81 kvm_t *kd, 82 struct kvm_swap *swap_ary, 83 int swap_max, 84 int flags 85 ) { 86 int ti = 0; 87 88 /* 89 * clear cache 90 */ 91 if (kd == NULL) { 92 kvm_swap_nl_cached = 0; 93 return(0); 94 } 95 96 /* 97 * namelist 98 */ 99 if (kvm_swap_nl_cached == 0) { 100 struct swdevt *sw; 101 102 if (kvm_nlist(kd, kvm_swap_nl) < 0) 103 return(-1); 104 105 /* 106 * required entries 107 */ 108 109 if ( 110 kvm_swap_nl[NL_SWDEVT].n_value == 0 || 111 kvm_swap_nl[NL_NSWDEV].n_value == 0 || 112 kvm_swap_nl[NL_DMMAX].n_value == 0 || 113 kvm_swap_nl[NL_SWAPBLIST].n_type == 0 114 ) { 115 return(-1); 116 } 117 118 /* 119 * get globals, type of swap 120 */ 121 122 KGET(NL_NSWDEV, nswdev); 123 KGET(NL_DMMAX, dmmax); 124 125 /* 126 * figure out how many actual swap devices are enabled 127 */ 128 129 KGET(NL_SWDEVT, sw); 130 for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) { 131 struct swdevt swinfo; 132 133 KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo"); 134 if (swinfo.sw_nblks) 135 break; 136 } 137 ++unswdev; 138 139 kvm_swap_nl_cached = 1; 140 } 141 142 143 { 144 struct swdevt *sw; 145 int i; 146 147 ti = unswdev; 148 if (ti >= swap_max) 149 ti = swap_max - 1; 150 151 if (ti >= 0) 152 bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1)); 153 154 KGET(NL_SWDEVT, sw); 155 for (i = 0; i < unswdev; ++i) { 156 struct swdevt swinfo; 157 int ttl; 158 159 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); 160 161 /* 162 * old style: everything in DEV_BSIZE'd chunks, 163 * convert to pages. 164 * 165 * new style: swinfo in DEV_BSIZE'd chunks but dmmax 166 * in pages. 167 * 168 * The first dmmax is never allocating to avoid 169 * trashing the disklabels 170 */ 171 172 ttl = swinfo.sw_nblks - dmmax; 173 174 if (ttl == 0) 175 continue; 176 177 if (i < ti) { 178 swap_ary[i].ksw_total = ttl; 179 swap_ary[i].ksw_used = ttl; 180 swap_ary[i].ksw_flags = swinfo.sw_flags; 181 if (swinfo.sw_dev == NODEV) { 182 snprintf( 183 swap_ary[i].ksw_devname, 184 sizeof(swap_ary[i].ksw_devname), 185 "%s", 186 "[NFS swap]" 187 ); 188 } else { 189 snprintf( 190 swap_ary[i].ksw_devname, 191 sizeof(swap_ary[i].ksw_devname), 192 "%s%s", 193 ((flags & SWIF_DEV_PREFIX) ? "/dev/" : ""), 194 devname(swinfo.sw_dev, S_IFBLK) 195 ); 196 } 197 } 198 if (ti >= 0) { 199 swap_ary[ti].ksw_total += ttl; 200 swap_ary[ti].ksw_used += ttl; 201 } 202 } 203 } 204 205 getswapinfo_radix(kd, swap_ary, swap_max, flags); 206 return(ti); 207 } 208 209 /* 210 * scanradix() - support routine for radix scanner 211 */ 212 213 #define TABME tab, tab, "" 214 215 static int 216 scanradix( 217 blmeta_t *scan, 218 daddr_t blk, 219 daddr_t radix, 220 daddr_t skip, 221 daddr_t count, 222 kvm_t *kd, 223 int dmmax, 224 int nswdev, 225 struct kvm_swap *swap_ary, 226 int swap_max, 227 int tab, 228 int flags 229 ) { 230 blmeta_t meta; 231 int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev; 232 233 KGET2(scan, &meta, sizeof(meta), "blmeta_t"); 234 235 /* 236 * Terminator 237 */ 238 if (meta.bm_bighint == (daddr_t)-1) { 239 if (flags & SWIF_DUMP_TREE) { 240 printf("%*.*s(0x%06x,%d) Terminator\n", 241 TABME, 242 blk, 243 radix 244 ); 245 } 246 return(-1); 247 } 248 249 if (radix == BLIST_BMAP_RADIX) { 250 /* 251 * Leaf bitmap 252 */ 253 int i; 254 255 if (flags & SWIF_DUMP_TREE) { 256 printf("%*.*s(0x%06x,%d) Bitmap %08x big=%d\n", 257 TABME, 258 blk, 259 radix, 260 (int)meta.u.bmu_bitmap, 261 meta.bm_bighint 262 ); 263 } 264 265 /* 266 * If not all allocated, count. 267 */ 268 if (meta.u.bmu_bitmap != 0) { 269 for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) { 270 /* 271 * A 0 bit means allocated 272 */ 273 if ((meta.u.bmu_bitmap & (1 << i))) { 274 int t = 0; 275 276 if (nswdev) 277 t = (blk + i) / dmmax % nswdev; 278 if (t < ti) 279 --swap_ary[t].ksw_used; 280 if (ti >= 0) 281 --swap_ary[ti].ksw_used; 282 } 283 } 284 } 285 } else if (meta.u.bmu_avail == radix) { 286 /* 287 * Meta node if all free 288 */ 289 if (flags & SWIF_DUMP_TREE) { 290 printf("%*.*s(0x%06x,%d) Submap ALL-FREE {\n", 291 TABME, 292 blk, 293 radix, 294 (int)meta.u.bmu_avail, 295 meta.bm_bighint 296 ); 297 } 298 /* 299 * Note: both dmmax and radix are powers of 2. However, dmmax 300 * may be larger then radix so use a smaller increment if 301 * necessary. 302 */ 303 { 304 int t; 305 int tinc = dmmax; 306 307 while (tinc > radix) 308 tinc >>= 1; 309 310 for (t = blk; t < blk + radix; t += tinc) { 311 int u = (nswdev) ? (t / dmmax % nswdev) : 0; 312 313 if (u < ti) 314 swap_ary[u].ksw_used -= tinc; 315 if (ti >= 0) 316 swap_ary[ti].ksw_used -= tinc; 317 } 318 } 319 } else if (meta.u.bmu_avail == 0) { 320 /* 321 * Meta node if all used 322 */ 323 if (flags & SWIF_DUMP_TREE) { 324 printf("%*.*s(0x%06x,%d) Submap ALL-ALLOCATED\n", 325 TABME, 326 blk, 327 radix, 328 (int)meta.u.bmu_avail, 329 meta.bm_bighint 330 ); 331 } 332 } else { 333 /* 334 * Meta node if not all free 335 */ 336 int i; 337 int next_skip; 338 339 if (flags & SWIF_DUMP_TREE) { 340 printf("%*.*s(0x%06x,%d) Submap avail=%d big=%d {\n", 341 TABME, 342 blk, 343 radix, 344 (int)meta.u.bmu_avail, 345 meta.bm_bighint 346 ); 347 } 348 349 radix >>= BLIST_META_RADIX_SHIFT; 350 next_skip = skip >> BLIST_META_RADIX_SHIFT; 351 352 for (i = 1; i <= skip; i += next_skip) { 353 int r; 354 daddr_t vcount = (count > radix) ? radix : count; 355 356 r = scanradix( 357 &scan[i], 358 blk, 359 radix, 360 next_skip - 1, 361 vcount, 362 kd, 363 dmmax, 364 nswdev, 365 swap_ary, 366 swap_max, 367 tab + 4, 368 flags 369 ); 370 if (r < 0) 371 break; 372 blk += radix; 373 } 374 if (flags & SWIF_DUMP_TREE) { 375 printf("%*.*s}\n", TABME); 376 } 377 } 378 return(0); 379 } 380 381 static void 382 getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags) 383 { 384 struct blist *swapblist = NULL; 385 struct blist blcopy = { 0 }; 386 387 KGET(NL_SWAPBLIST, swapblist); 388 389 if (swapblist == NULL) { 390 if (flags & SWIF_DUMP_TREE) 391 printf("radix tree: NULL - no swap in system\n"); 392 return; 393 } 394 395 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); 396 397 if (flags & SWIF_DUMP_TREE) { 398 printf("radix tree: %d/%d/%d blocks, %dK wired\n", 399 blcopy.bl_free, 400 blcopy.bl_blocks, 401 blcopy.bl_radix, 402 (blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ 403 1024 404 ); 405 } 406 scanradix( 407 blcopy.bl_root, 408 0, 409 blcopy.bl_radix, 410 blcopy.bl_skip, 411 blcopy.bl_rootblks, 412 kd, 413 dmmax, 414 nswdev, 415 swap_ary, 416 swap_max, 417 0, 418 flags 419 ); 420 } 421