xref: /dragonfly/lib/libkvm/kvm_getswapinfo.c (revision dda92f98)
1 /*
2  * Copyright (c) 1999 The DragonFly Project.  All rights reserved.
3  *
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:
10  *
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.
20  *
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.
33  *
34  * $FreeBSD: src/lib/libkvm/kvm_getswapinfo.c,v 1.10.2.4 2003/01/12 09:23:13 dillon Exp $
35  */
36 
37 #define	_KERNEL_STRUCTURES
38 
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <sys/ucred.h>
42 #include <sys/stat.h>
43 #include <sys/conf.h>
44 #include <sys/blist.h>
45 #include <sys/sysctl.h>
46 #include <vm/vm_param.h>
47 
48 #include <err.h>
49 #include <fcntl.h>
50 #include <nlist.h>
51 #include <paths.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <limits.h>
57 
58 #include "kvm.h"
59 #include "kvm_private.h"
60 
61 static struct nlist kvm_swap_nl[] = {
62 	{ .n_name = "_swapblist" },	/* new radix swap list */
63 	{ .n_name = "_swdevt" },	/* list of swap devices and sizes */
64 	{ .n_name = "_nswdev" },	/* number of swap devices */
65 	{ .n_name = "_dmmax" },		/* maximum size of a swap block */
66 	{ .n_name = "" }
67 };
68 
69 #define NL_SWAPBLIST	0
70 #define NL_SWDEVT	1
71 #define NL_NSWDEV	2
72 #define NL_DMMAX	3
73 
74 static int kvm_swap_nl_cached = 0;
75 static int nswdev;
76 static int unswdev;
77 static int dmmax;
78 
79 static int nlist_init(kvm_t *kd);
80 static void dump_blist(kvm_t *kd);
81 static int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
82 			      int swap_max, int flags);
83 
84 #define	SVAR(var) __STRING(var)	/* to force expansion */
85 #define	KGET(idx, var)							\
86 	KGET1(idx, &var, sizeof(var), SVAR(var))
87 #define	KGET1(idx, p, s, msg)						\
88 	KGET2(kvm_swap_nl[idx].n_value, p, s, msg)
89 #define	KGET2(addr, p, s, msg)						\
90 	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
91 		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
92 #define	KGETN(idx, var)							\
93 	KGET1N(idx, &var, sizeof(var), SVAR(var))
94 #define	KGET1N(idx, p, s, msg)						\
95 	KGET2N(kvm_swap_nl[idx].n_value, p, s, msg)
96 #define	KGET2N(addr, p, s, msg)						\
97 	((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0)
98 #define	KGETRET(addr, p, s, msg)					\
99 	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
100 		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
101 		return (0);						\
102 	}
103 
104 #define GETSWDEVNAME(dev, str, flags)					\
105 	if (dev == NODEV) {						\
106 		strlcpy(str, "[NFS swap]", sizeof(str));		\
107 	} else {							\
108 		snprintf(						\
109 		    str, sizeof(str), "%s%s",				\
110 		    ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),	\
111 		    devname(dev, S_IFCHR)				\
112 		);							\
113 	}
114 
115 int
116 kvm_getswapinfo(
117 	kvm_t *kd,
118 	struct kvm_swap *swap_ary,
119 	int swap_max,
120 	int flags
121 ) {
122 	int i, ti, swi;
123 	swblk_t ttl;
124 	struct swdevt *sw;
125 	struct swdevt swinfo;
126 
127 	/*
128 	 * clear cache
129 	 */
130 	if (kd == NULL) {
131 		kvm_swap_nl_cached = 0;
132 		return(0);
133 	}
134 
135 	if (swap_max < 1)
136 		return (-1);
137 
138 	/*
139 	 * Use sysctl if possible
140 	 */
141 	if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) {
142 		ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
143 		if (ti >= 0)
144 			return(ti);
145 	}
146 
147 	/*
148 	 * namelist
149 	 */
150 	if (!nlist_init(kd))
151 		return (-1);
152 
153 	swi = unswdev;
154 	if (swi >= swap_max)
155 		swi = swap_max - 1;
156 
157 	bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
158 
159 	KGET(NL_SWDEVT, sw);
160 	for (i = ti = 0; i < nswdev; ++i) {
161 		KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
162 
163 		if (swinfo.sw_nblks == 0)
164 			continue;
165 
166 		/*
167 		 * The first dmmax is never allocated to avoid
168 		 * trashing the disklabels.
169 		 */
170 		ttl = swinfo.sw_nblks - dmmax;
171 		if (ttl == 0)
172 			continue;
173 
174 		swap_ary[swi].ksw_total += ttl;
175 		swap_ary[swi].ksw_used += swinfo.sw_nused;
176 
177 		if (ti < swi) {
178 			swap_ary[ti].ksw_total = ttl;
179 			swap_ary[ti].ksw_used = swinfo.sw_nused;
180 			swap_ary[ti].ksw_flags = swinfo.sw_flags;
181 			GETSWDEVNAME(swinfo.sw_dev, swap_ary[ti].ksw_devname,
182 			    flags);
183 			++ti;
184 		}
185 	}
186 
187 	if (flags & SWIF_DUMP_TREE)
188 		dump_blist(kd);
189 	return (swi);
190 }
191 
192 static int
193 nlist_init(kvm_t *kd)
194 {
195 	int i;
196 	struct swdevt *sw;
197 	struct swdevt swinfo;
198 
199 	if (kvm_swap_nl_cached)
200 		return (1);
201 
202 	if (kvm_nlist(kd, kvm_swap_nl) < 0)
203 		return (0);
204 
205 	/*
206 	 * required entries
207 	 */
208 	if (kvm_swap_nl[NL_SWDEVT].n_value == 0 ||
209 	    kvm_swap_nl[NL_NSWDEV].n_value == 0 ||
210 	    kvm_swap_nl[NL_DMMAX].n_value == 0 ||
211 	    kvm_swap_nl[NL_SWAPBLIST].n_type == 0) {
212 		return (0);
213 	}
214 
215 	/*
216 	 * get globals, type of swap
217 	 */
218 	KGET(NL_NSWDEV, nswdev);
219 	KGET(NL_DMMAX, dmmax);
220 
221 	/*
222 	 * figure out how many actual swap devices are enabled
223 	 */
224 	KGET(NL_SWDEVT, sw);
225 	for (i = unswdev = 0; i < nswdev; ++i) {
226 		KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
227 		if (swinfo.sw_nblks)
228 			++unswdev;
229 
230 	}
231 
232 	kvm_swap_nl_cached = 1;
233 	return (1);
234 }
235 
236 /*
237  * scanradix() - support routine for radix scanner
238  */
239 
240 #define TABME	tab, tab, ""
241 
242 static int
243 scanradix(
244 	blmeta_t *scan,
245 	blmeta_t *scan_cache,
246 	swblk_t blk,
247 	int64_t radix,
248 	swblk_t skip,
249 	swblk_t count,
250 	kvm_t *kd,
251 	int dmmaxr,
252 	int nswdevr,
253 	int64_t *availp,
254 	int tab
255 ) {
256 	blmeta_t meta;
257 	blmeta_t scan_array[BLIST_BMAP_RADIX];
258 	int64_t avail_tmp = 0;
259 	unsigned int iu;
260 	int im;
261 	int next_skip;
262 
263 	if (scan_cache) {
264 		meta = *scan_cache;
265 	} else if (skip == BLIST_META_RADIX) {
266 		if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) {
267 			warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd));
268 			bzero(scan_array, sizeof(scan_array));
269 		}
270 		meta = scan_array[0];
271 	} else {
272 		KGET2(scan, &meta, sizeof(meta), "blmeta_t");
273 	}
274 
275 	/*
276 	 * Terminator
277 	 */
278 	if (meta.bm_bighint == (swblk_t)-1) {
279 		printf("%*.*s(0x%06jx,%jd) Terminator\n",
280 		    TABME,
281 		    (intmax_t)blk,
282 		    (intmax_t)radix
283 		);
284 		return(-1);
285 	}
286 
287 	if (radix == BLIST_BMAP_RADIX) {
288 		/*
289 		 * Leaf bitmap
290 		 */
291 		printf("%*.*s(0x%06jx,%jd) Bitmap %016jx big=%jd\n",
292 		    TABME,
293 		    (intmax_t)blk,
294 		    (intmax_t)radix,
295 		    (intmax_t)meta.u.bmu_bitmap,
296 		    (intmax_t)meta.bm_bighint
297 		);
298 
299 		if (meta.u.bmu_bitmap) {
300 			for (iu = 0; iu < BLIST_BMAP_RADIX; ++iu) {
301 				if (meta.u.bmu_bitmap & (1LU << iu))
302 					++*availp;
303 			}
304 		}
305 	} else if (meta.u.bmu_avail == radix) {
306 		/*
307 		 * Meta node if all free
308 		 */
309 		printf("%*.*s(0x%06jx,%jd) Submap ALL-FREE (big=%jd) {\n",
310 		    TABME,
311 		    (intmax_t)blk,
312 		    (intmax_t)radix,
313 		    (intmax_t)meta.bm_bighint
314 		);
315 		*availp += radix;
316 	} else if (meta.u.bmu_avail == 0) {
317 		/*
318 		 * Meta node if all used
319 		 */
320 		printf("%*.*s(0x%06jx,%jd) Submap ALL-ALLOCATED (big=%jd)\n",
321 		    TABME,
322 		    (intmax_t)blk,
323 		    (intmax_t)radix,
324 		    (intmax_t)meta.bm_bighint
325 		);
326 	} else {
327 		/*
328 		 * Meta node if not all free
329 		 */
330 		printf("%*.*s(0x%06jx,%jd) Submap avail=%jd big=%jd {\n",
331 		    TABME,
332 		    (intmax_t)blk,
333 		    (intmax_t)radix,
334 		    (intmax_t)meta.u.bmu_avail,
335 		    (intmax_t)meta.bm_bighint
336 		);
337 
338 		radix /= BLIST_META_RADIX;
339 		next_skip = skip / BLIST_META_RADIX;
340 
341 		for (im = 1; im <= skip; im += next_skip) {
342 			int r;
343 			swblk_t vcount = (count > radix) ?
344 					(swblk_t)radix : count;
345 
346 			r = scanradix(
347 			    &scan[im],
348 			    ((next_skip == 1) ? &scan_array[im] : NULL),
349 			    blk,
350 			    radix,
351 			    next_skip - 1,
352 			    vcount,
353 			    kd,
354 			    dmmaxr,
355 			    nswdevr,
356 			    &avail_tmp,
357 			    tab + 4
358 			);
359 			if (r < 0)
360 				break;
361 			blk += (swblk_t)radix;
362 		}
363 		*availp += avail_tmp;
364 		if (avail_tmp == meta.u.bmu_avail)
365 			printf("%*.*s}\n", TABME);
366 		else
367 			printf("%*.*s} (AVAIL MISMATCH %jd/%jd\n",
368 				TABME,
369 				(intmax_t)avail_tmp,
370 				(intmax_t)meta.u.bmu_avail);
371 	}
372 	return(0);
373 }
374 
375 static void
376 dump_blist(kvm_t *kd)
377 {
378 	struct blist *swapblist = NULL;
379 	struct blist blcopy = { 0 };
380 	int64_t avail = 0;
381 
382 	KGET(NL_SWAPBLIST, swapblist);
383 
384 	if (swapblist == NULL) {
385 		printf("radix tree: NULL - no swap in system\n");
386 		return;
387 	}
388 
389 	KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist");
390 
391 	printf("radix tree: %jd/%jd/%jd blocks, %jdK wired\n",
392 		(intmax_t)blcopy.bl_free,
393 		(intmax_t)blcopy.bl_blocks,
394 		(intmax_t)blcopy.bl_radix,
395 		(intmax_t)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/
396 		    1024)
397 	);
398 
399 	scanradix(
400 	    blcopy.bl_root,
401 	    NULL,
402 	    0,
403 	    blcopy.bl_radix,
404 	    blcopy.bl_skip,
405 	    blcopy.bl_rootblks,
406 	    kd,
407 	    dmmax,
408 	    nswdev,
409 	    &avail,
410 	    0
411 	);
412 	printf("final availability: %jd\n", (intmax_t)avail);
413 }
414 
415 static
416 int
417 kvm_getswapinfo_sysctl(kvm_t *kd __unused, struct kvm_swap *swap_ary,
418 		       int swap_max, int flags)
419 {
420 	size_t bytes = 0;
421 	size_t ksize;
422 	int ti;
423 	int swi;
424 	int n;
425 	int i;
426 	char *xswbuf;
427 	struct xswdev *xsw;
428 
429 	if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
430 		return(-1);
431 	if (bytes == 0)
432 		return(-1);
433 
434 	xswbuf = malloc(bytes);
435 	if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
436 		free(xswbuf);
437 		return(-1);
438 	}
439 	if (bytes == 0) {
440 		free(xswbuf);
441 		return(-1);
442 	}
443 
444 	/*
445 	 * Calculate size of xsw entry returned by kernel (it can be larger
446 	 * than the one we have if there is a version mismatch).
447 	 */
448 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
449 	n = (int)(bytes / ksize);
450 
451 	/*
452 	 * Calculate the number of live swap devices and calculate
453 	 * the swap_ary[] index used for the cumulative result (swi)
454 	 */
455 	for (i = swi = 0; i < n; ++i) {
456 		xsw = (void *)((char *)xswbuf + i * ksize);
457 		if ((xsw->xsw_flags & SW_FREED) == 0)
458 			continue;
459 		++swi;
460 	}
461 	if (swi >= swap_max)
462 		swi = swap_max - 1;
463 
464 	bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
465 
466 	/*
467 	 * Accumulate results.  If the provided swap_ary[] is too
468 	 * small will only populate up to the available entries,
469 	 * but we always populate the cumulative results entry.
470 	 */
471 	for (i = ti = 0; i < n; ++i) {
472 		xsw = (void *)((char *)xswbuf + i * ksize);
473 
474 		if ((xsw->xsw_flags & SW_FREED) == 0)
475 			continue;
476 
477 		swap_ary[swi].ksw_total += xsw->xsw_nblks;
478 		swap_ary[swi].ksw_used += xsw->xsw_used;
479 
480 		if (ti < swi) {
481 			swap_ary[ti].ksw_total = xsw->xsw_nblks;
482 			swap_ary[ti].ksw_used = xsw->xsw_used;
483 			swap_ary[ti].ksw_flags = xsw->xsw_flags;
484 			GETSWDEVNAME(xsw->xsw_dev, swap_ary[ti].ksw_devname,
485 			    flags);
486 			++ti;
487 		}
488 	}
489 
490 	free(xswbuf);
491 	return(swi);
492 }
493