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