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