Merge branch 'master' of ssh://crater.dragonflybsd.org/repository/git/dragonfly
[dragonfly.git] / lib / libkvm / kvm_getswapinfo.c
blob98cb5e1baef25df3be764d820fc28ea7026d34d1
1 /*
2 * Copyright (c) 1999 The DragonFly Project. All rights reserved.
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:
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.
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.
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 $
38 #define _KERNEL_STRUCTURES
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 #include <sys/sysctl.h>
47 #include <vm/vm_param.h>
49 #include <err.h>
50 #include <fcntl.h>
51 #include <kvm.h>
52 #include <nlist.h>
53 #include <paths.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <limits.h>
60 #include "kvm_private.h"
62 static struct nlist kvm_swap_nl[] = {
63 { "_swapblist" }, /* new radix swap list */
64 { "_swdevt" }, /* list of swap devices and sizes */
65 { "_nswdev" }, /* number of swap devices */
66 { "_dmmax" }, /* maximum size of a swap block */
67 { "" }
70 #define NL_SWAPBLIST 0
71 #define NL_SWDEVT 1
72 #define NL_NSWDEV 2
73 #define NL_DMMAX 3
75 static int kvm_swap_nl_cached = 0;
76 static int nswdev;
77 static int unswdev;
78 static int dmmax;
80 static int nlist_init(kvm_t *kd);
81 static void dump_blist(kvm_t *kd);
82 static int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
83 int swap_max, int flags);
85 #define SVAR(var) __STRING(var) /* to force expansion */
86 #define KGET(idx, var) \
87 KGET1(idx, &var, sizeof(var), SVAR(var))
88 #define KGET1(idx, p, s, msg) \
89 KGET2(kvm_swap_nl[idx].n_value, p, s, msg)
90 #define KGET2(addr, p, s, msg) \
91 if (kvm_read(kd, (u_long)(addr), p, s) != s) \
92 warnx("cannot read %s: %s", msg, kvm_geterr(kd))
93 #define KGETN(idx, var) \
94 KGET1N(idx, &var, sizeof(var), SVAR(var))
95 #define KGET1N(idx, p, s, msg) \
96 KGET2N(kvm_swap_nl[idx].n_value, p, s, msg)
97 #define KGET2N(addr, p, s, msg) \
98 ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0)
99 #define KGETRET(addr, p, s, msg) \
100 if (kvm_read(kd, (u_long)(addr), p, s) != s) { \
101 warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \
102 return (0); \
105 #define GETSWDEVNAME(dev, str, flags) \
106 if (dev == NODEV) { \
107 strlcpy(str, "[NFS swap]", sizeof(str)); \
108 } else { \
109 snprintf( \
110 str, sizeof(str), "%s%s", \
111 ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), \
112 devname(dev, S_IFCHR) \
113 ); \
117 kvm_getswapinfo(
118 kvm_t *kd,
119 struct kvm_swap *swap_ary,
120 int swap_max,
121 int flags
123 int i, ti, swi;
124 int ttl;
125 struct swdevt *sw;
126 struct swdevt swinfo;
129 * clear cache
131 if (kd == NULL) {
132 kvm_swap_nl_cached = 0;
133 return(0);
136 if (swap_max < 1)
137 return (-1);
140 * Use sysctl if possible
142 if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) {
143 ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
144 if (ti >= 0)
145 return(ti);
149 * namelist
151 if (!nlist_init(kd))
152 return (-1);
154 swi = unswdev;
155 if (swi >= swap_max)
156 swi = swap_max - 1;
158 bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
160 KGET(NL_SWDEVT, sw);
161 for (i = ti = 0; i < nswdev; ++i) {
162 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
164 if (swinfo.sw_nblks == 0)
165 continue;
168 * The first dmmax is never allocated to avoid
169 * trashing the disklabels.
171 ttl = swinfo.sw_nblks - dmmax;
172 if (ttl == 0)
173 continue;
175 swap_ary[swi].ksw_total += ttl;
176 swap_ary[swi].ksw_used += swinfo.sw_nused;
178 if (ti < swi) {
179 swap_ary[ti].ksw_total = ttl;
180 swap_ary[ti].ksw_used = swinfo.sw_nused;
181 swap_ary[ti].ksw_flags = swinfo.sw_flags;
182 GETSWDEVNAME(swinfo.sw_dev, swap_ary[ti].ksw_devname,
183 flags);
184 ++ti;
188 if (flags & SWIF_DUMP_TREE)
189 dump_blist(kd);
190 return (swi);
193 static int
194 nlist_init(kvm_t *kd)
196 int i;
197 struct swdevt *sw;
198 struct swdevt swinfo;
200 if (kvm_swap_nl_cached)
201 return (1);
203 if (kvm_nlist(kd, kvm_swap_nl) < 0)
204 return (0);
207 * required entries
209 if (kvm_swap_nl[NL_SWDEVT].n_value == 0 ||
210 kvm_swap_nl[NL_NSWDEV].n_value == 0 ||
211 kvm_swap_nl[NL_DMMAX].n_value == 0 ||
212 kvm_swap_nl[NL_SWAPBLIST].n_type == 0) {
213 return (0);
217 * get globals, type of swap
219 KGET(NL_NSWDEV, nswdev);
220 KGET(NL_DMMAX, dmmax);
223 * figure out how many actual swap devices are enabled
225 KGET(NL_SWDEVT, sw);
226 for (i = unswdev = 0; i < nswdev; ++i) {
227 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
228 if (swinfo.sw_nblks)
229 ++unswdev;
233 kvm_swap_nl_cached = 1;
234 return (1);
238 * scanradix() - support routine for radix scanner
241 #define TABME tab, tab, ""
243 static int
244 scanradix(
245 blmeta_t *scan,
246 blmeta_t *scan_cache,
247 swblk_t blk,
248 int64_t radix,
249 swblk_t skip,
250 swblk_t count,
251 kvm_t *kd,
252 int dmmax,
253 int nswdev,
254 int tab
256 blmeta_t meta;
257 blmeta_t scan_array[BLIST_BMAP_RADIX];
259 if (scan_cache) {
260 meta = *scan_cache;
261 } else if (skip == BLIST_META_RADIX) {
262 if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) {
263 warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd));
264 bzero(scan_array, sizeof(scan_array));
266 meta = scan_array[0];
267 } else {
268 KGET2(scan, &meta, sizeof(meta), "blmeta_t");
272 * Terminator
274 if (meta.bm_bighint == (swblk_t)-1) {
275 printf("%*.*s(0x%06x,%lld) Terminator\n",
276 TABME,
277 blk,
278 (long long)radix
280 return(-1);
283 if (radix == BLIST_BMAP_RADIX) {
285 * Leaf bitmap
287 printf("%*.*s(0x%06x,%lld) Bitmap %08x big=%d\n",
288 TABME,
289 blk,
290 (long long)radix,
291 (int)meta.u.bmu_bitmap,
292 meta.bm_bighint
295 } else if (meta.u.bmu_avail == radix) {
297 * Meta node if all free
299 printf("%*.*s(0x%06x,%lld) Submap ALL-FREE {\n",
300 TABME,
301 blk,
302 (long long)radix
305 } else if (meta.u.bmu_avail == 0) {
307 * Meta node if all used
309 printf("%*.*s(0x%06x,%lld) Submap ALL-ALLOCATED\n",
310 TABME,
311 blk,
312 (long long)radix
314 } else {
316 * Meta node if not all free
318 int i;
319 int next_skip;
321 printf("%*.*s(0x%06x,%lld) Submap avail=%d big=%d {\n",
322 TABME,
323 blk,
324 (long long)radix,
325 (int)meta.u.bmu_avail,
326 meta.bm_bighint
329 radix /= BLIST_META_RADIX;
330 next_skip = skip / BLIST_META_RADIX;
332 for (i = 1; i <= skip; i += next_skip) {
333 int r;
334 swblk_t vcount = (count > radix) ?
335 (swblk_t)radix : count;
337 r = scanradix(
338 &scan[i],
339 ((next_skip == 1) ? &scan_array[i] : NULL),
340 blk,
341 radix,
342 next_skip - 1,
343 vcount,
345 dmmax,
346 nswdev,
347 tab + 4
349 if (r < 0)
350 break;
351 blk += (swblk_t)radix;
353 printf("%*.*s}\n", TABME);
355 return(0);
358 static void
359 dump_blist(kvm_t *kd)
361 struct blist *swapblist = NULL;
362 struct blist blcopy = { 0 };
364 KGET(NL_SWAPBLIST, swapblist);
366 if (swapblist == NULL) {
367 printf("radix tree: NULL - no swap in system\n");
368 return;
371 KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist");
373 printf("radix tree: %d/%d/%lld blocks, %dK wired\n",
374 blcopy.bl_free,
375 blcopy.bl_blocks,
376 (long long)blcopy.bl_radix,
377 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/
378 1024)
381 scanradix(
382 blcopy.bl_root,
383 NULL,
385 blcopy.bl_radix,
386 blcopy.bl_skip,
387 blcopy.bl_rootblks,
389 dmmax,
390 nswdev,
395 static
397 kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
398 int swap_max, int flags)
400 size_t bytes = 0;
401 size_t ksize;
402 int ti;
403 int swi;
404 int n;
405 int i;
406 char *xswbuf;
407 struct xswdev *xsw;
409 if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
410 return(-1);
411 if (bytes == 0)
412 return(-1);
414 xswbuf = malloc(bytes);
415 if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
416 free(xswbuf);
417 return(-1);
419 if (bytes == 0) {
420 free(xswbuf);
421 return(-1);
425 * Calculate size of xsw entry returned by kernel (it can be larger
426 * than the one we have if there is a version mismatch).
428 ksize = ((struct xswdev *)xswbuf)->xsw_size;
429 n = (int)(bytes / ksize);
432 * Calculate the number of live swap devices and calculate
433 * the swap_ary[] index used for the cumulative result (swi)
435 for (i = swi = 0; i < n; ++i) {
436 xsw = (void *)((char *)xswbuf + i * ksize);
437 if ((xsw->xsw_flags & SW_FREED) == 0)
438 continue;
439 ++swi;
441 if (swi >= swap_max)
442 swi = swap_max - 1;
444 bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
447 * Accumulate results. If the provided swap_ary[] is too
448 * small will only populate up to the available entries,
449 * but we always populate the cumulative results entry.
451 for (i = ti = 0; i < n; ++i) {
452 xsw = (void *)((char *)xswbuf + i * ksize);
454 if ((xsw->xsw_flags & SW_FREED) == 0)
455 continue;
457 swap_ary[swi].ksw_total += xsw->xsw_nblks;
458 swap_ary[swi].ksw_used += xsw->xsw_used;
460 if (ti < swi) {
461 swap_ary[ti].ksw_total = xsw->xsw_nblks;
462 swap_ary[ti].ksw_used = xsw->xsw_used;
463 swap_ary[ti].ksw_flags = xsw->xsw_flags;
464 GETSWDEVNAME(xsw->xsw_dev, swap_ary[ti].ksw_devname,
465 flags);
466 ++ti;
470 free(xswbuf);
471 return(swi);