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