xref: /dflybsd-src/lib/libkvm/kvm_getswapinfo.c (revision c01f27eb865fd879f1ee80f5eeace159d26c9251)
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  * $DragonFly: src/lib/libkvm/kvm_getswapinfo.c,v 1.5 2006/03/18 17:15:35 dillon Exp $
36  */
37 
38 #define	_KERNEL_STRUCTURES
39 
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>
48 
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>
59 
60 #include "kvm_private.h"
61 
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 	{ "" }
68 };
69 
70 #define NL_SWAPBLIST	0
71 #define NL_SWDEVT	1
72 #define NL_NSWDEV	2
73 #define NL_DMMAX	3
74 
75 static int kvm_swap_nl_cached = 0;
76 static int nswdev;
77 static int unswdev;
78 static int dmmax;
79 
80 static void getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary,
81 			      int swap_max, int flags);
82 static int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
83 			      int swap_max, int flags);
84 
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);						\
103 	}
104 
105 int
106 kvm_getswapinfo(
107 	kvm_t *kd,
108 	struct kvm_swap *swap_ary,
109 	int swap_max,
110 	int flags
111 ) {
112 	int ti;
113 
114 	/*
115 	 * clear cache
116 	 */
117 	if (kd == NULL) {
118 		kvm_swap_nl_cached = 0;
119 		return(0);
120 	}
121 
122 	/*
123 	 * Use sysctl if possible
124 	 */
125 	if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) {
126 		ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
127 		if (ti >= 0)
128 			return(ti);
129 	}
130 
131 	/*
132 	 * namelist
133 	 */
134 	if (kvm_swap_nl_cached == 0) {
135 		struct swdevt *sw;
136 
137 		if (kvm_nlist(kd, kvm_swap_nl) < 0)
138 			return(-1);
139 
140 		/*
141 		 * required entries
142 		 */
143 
144 		if (
145 		    kvm_swap_nl[NL_SWDEVT].n_value == 0 ||
146 		    kvm_swap_nl[NL_NSWDEV].n_value == 0 ||
147 		    kvm_swap_nl[NL_DMMAX].n_value == 0 ||
148 		    kvm_swap_nl[NL_SWAPBLIST].n_type == 0
149 		) {
150 			return(-1);
151 		}
152 
153 		/*
154 		 * get globals, type of swap
155 		 */
156 
157 		KGET(NL_NSWDEV, nswdev);
158 		KGET(NL_DMMAX, dmmax);
159 
160 		/*
161 		 * figure out how many actual swap devices are enabled
162 		 */
163 
164 		KGET(NL_SWDEVT, sw);
165 		for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) {
166 			struct swdevt swinfo;
167 
168 			KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo");
169 			if (swinfo.sw_nblks)
170 				break;
171 		}
172 		++unswdev;
173 
174 		kvm_swap_nl_cached = 1;
175 	}
176 
177 	{
178 		struct swdevt *sw;
179 		int i;
180 
181 		ti = unswdev;
182 		if (ti >= swap_max)
183 			ti = swap_max - 1;
184 
185 		if (ti >= 0)
186 			bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1));
187 
188 		KGET(NL_SWDEVT, sw);
189 		for (i = 0; i < unswdev; ++i) {
190 			struct swdevt swinfo;
191 			int ttl;
192 
193 			KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
194 
195 			/*
196 			 * old style: everything in DEV_BSIZE'd chunks,
197 			 * convert to pages.
198 			 *
199 			 * new style: swinfo in DEV_BSIZE'd chunks but dmmax
200 			 * in pages.
201 			 *
202 			 * The first dmmax is never allocating to avoid
203 			 * trashing the disklabels
204 			 */
205 
206 			ttl = swinfo.sw_nblks - dmmax;
207 
208 			if (ttl == 0)
209 				continue;
210 
211 			if (i < ti) {
212 				swap_ary[i].ksw_total = ttl;
213 				swap_ary[i].ksw_used = ttl;
214 				swap_ary[i].ksw_flags = swinfo.sw_flags;
215 				if (swinfo.sw_dev == NODEV) {
216 					snprintf(
217 					    swap_ary[i].ksw_devname,
218 					    sizeof(swap_ary[i].ksw_devname),
219 					    "%s",
220 					    "[NFS swap]"
221 					);
222 				} else {
223 					snprintf(
224 					    swap_ary[i].ksw_devname,
225 					    sizeof(swap_ary[i].ksw_devname),
226 					    "%s%s",
227 					    ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),
228 					    devname(swinfo.sw_dev, S_IFCHR)
229 					);
230 				}
231 			}
232 			if (ti >= 0) {
233 				swap_ary[ti].ksw_total += ttl;
234 				swap_ary[ti].ksw_used += ttl;
235 			}
236 		}
237 	}
238 
239 	getswapinfo_radix(kd, swap_ary, swap_max, flags);
240 	return(ti);
241 }
242 
243 /*
244  * scanradix() - support routine for radix scanner
245  */
246 
247 #define TABME	tab, tab, ""
248 
249 static int
250 scanradix(
251 	blmeta_t *scan,
252 	blmeta_t *scan_cache,
253 	swblk_t blk,
254 	int64_t radix,
255 	swblk_t skip,
256 	swblk_t count,
257 	kvm_t *kd,
258 	int dmmax,
259 	int nswdev,
260 	struct kvm_swap *swap_ary,
261 	int swap_max,
262 	int tab,
263 	int flags
264 ) {
265 	blmeta_t meta;
266 	blmeta_t scan_array[BLIST_BMAP_RADIX];
267 	int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev;
268 
269 	if (scan_cache) {
270 		meta = *scan_cache;
271 	} else if (skip == BLIST_META_RADIX) {
272 		if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) {
273 			warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd));
274 			bzero(scan_array, sizeof(scan_array));
275 		}
276 		meta = scan_array[0];
277 	} else {
278 		KGET2(scan, &meta, sizeof(meta), "blmeta_t");
279 	}
280 
281 	/*
282 	 * Terminator
283 	 */
284 	if (meta.bm_bighint == (swblk_t)-1) {
285 		if (flags & SWIF_DUMP_TREE) {
286 			printf("%*.*s(0x%06x,%lld) Terminator\n",
287 			    TABME,
288 			    blk,
289 			    (long long)radix
290 			);
291 		}
292 		return(-1);
293 	}
294 
295 	if (radix == BLIST_BMAP_RADIX) {
296 		/*
297 		 * Leaf bitmap
298 		 */
299 		int i;
300 
301 		if (flags & SWIF_DUMP_TREE) {
302 			printf("%*.*s(0x%06x,%lld) Bitmap %08x big=%d\n",
303 			    TABME,
304 			    blk,
305 			    (long long)radix,
306 			    (int)meta.u.bmu_bitmap,
307 			    meta.bm_bighint
308 			);
309 		}
310 
311 		/*
312 		 * If not all allocated, count.
313 		 */
314 		if (meta.u.bmu_bitmap != 0) {
315 			for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) {
316 				/*
317 				 * A 0 bit means allocated
318 				 */
319 				if ((meta.u.bmu_bitmap & (1 << i))) {
320 					int t = 0;
321 
322 					if (nswdev)
323 						t = (blk + i) / dmmax % nswdev;
324 					if (t < ti)
325 						--swap_ary[t].ksw_used;
326 					if (ti >= 0)
327 						--swap_ary[ti].ksw_used;
328 				}
329 			}
330 		}
331 	} else if (meta.u.bmu_avail == radix) {
332 		/*
333 		 * Meta node if all free
334 		 */
335 		if (flags & SWIF_DUMP_TREE) {
336 			printf("%*.*s(0x%06x,%lld) Submap ALL-FREE {\n",
337 			    TABME,
338 			    blk,
339 			    (long long)radix
340 			);
341 		}
342 		/*
343 		 * Note: both dmmax and radix are powers of 2.  However, dmmax
344 		 * may be larger then radix so use a smaller increment if
345 		 * necessary.
346 		 */
347 		{
348 			int t;
349 			int tinc = dmmax;
350 
351 			while (tinc > radix)
352 				tinc >>= 1;
353 
354 			for (t = blk; t < blk + radix; t += tinc) {
355 				int u = (nswdev) ? (t / dmmax % nswdev) : 0;
356 
357 				if (u < ti)
358 					swap_ary[u].ksw_used -= tinc;
359 				if (ti >= 0)
360 					swap_ary[ti].ksw_used -= tinc;
361 			}
362 		}
363 	} else if (meta.u.bmu_avail == 0) {
364 		/*
365 		 * Meta node if all used
366 		 */
367 		if (flags & SWIF_DUMP_TREE) {
368 			printf("%*.*s(0x%06x,%lld) Submap ALL-ALLOCATED\n",
369 			    TABME,
370 			    blk,
371 			    (long long)radix
372 			);
373 		}
374 	} else {
375 		/*
376 		 * Meta node if not all free
377 		 */
378 		int i;
379 		int next_skip;
380 
381 		if (flags & SWIF_DUMP_TREE) {
382 			printf("%*.*s(0x%06x,%lld) Submap avail=%d big=%d {\n",
383 			    TABME,
384 			    blk,
385 			    (long long)radix,
386 			    (int)meta.u.bmu_avail,
387 			    meta.bm_bighint
388 			);
389 		}
390 
391 		radix /= BLIST_META_RADIX;
392 		next_skip = skip / BLIST_META_RADIX;
393 
394 		for (i = 1; i <= skip; i += next_skip) {
395 			int r;
396 			swblk_t vcount = (count > radix) ?
397 					(swblk_t)radix : count;
398 
399 			r = scanradix(
400 			    &scan[i],
401 			    ((next_skip == 1) ? &scan_array[i] : NULL),
402 			    blk,
403 			    radix,
404 			    next_skip - 1,
405 			    vcount,
406 			    kd,
407 			    dmmax,
408 			    nswdev,
409 			    swap_ary,
410 			    swap_max,
411 			    tab + 4,
412 			    flags
413 			);
414 			if (r < 0)
415 				break;
416 			blk += (swblk_t)radix;
417 		}
418 		if (flags & SWIF_DUMP_TREE) {
419 			printf("%*.*s}\n", TABME);
420 		}
421 	}
422 	return(0);
423 }
424 
425 static void
426 getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags)
427 {
428 	struct blist *swapblist = NULL;
429 	struct blist blcopy = { 0 };
430 
431 	KGET(NL_SWAPBLIST, swapblist);
432 
433 	if (swapblist == NULL) {
434 		if (flags & SWIF_DUMP_TREE)
435 			printf("radix tree: NULL - no swap in system\n");
436 		return;
437 	}
438 
439 	KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist");
440 
441 	if (flags & SWIF_DUMP_TREE) {
442 		printf("radix tree: %d/%d/%lld blocks, %dK wired\n",
443 			blcopy.bl_free,
444 			blcopy.bl_blocks,
445 			(long long)blcopy.bl_radix,
446 			(int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/
447 			    1024)
448 		);
449 	}
450 
451 	/*
452 	 * XXX Scan the radix tree in the kernel if we have more then one
453 	 *     swap device so we can get per-device statistics.  This can
454 	 *     get nasty because swap devices are interleaved based on the
455 	 *     maximum of (4), so the blist winds up not using any shortcuts.
456 	 *
457 	 *     Otherwise just pull the free count out of the blist header,
458 	 *     which is a billion times faster.
459 	 */
460 	if ((flags & SWIF_DUMP_TREE) || unswdev > 1) {
461 		scanradix(
462 		    blcopy.bl_root,
463 		    NULL,
464 		    0,
465 		    blcopy.bl_radix,
466 		    blcopy.bl_skip,
467 		    blcopy.bl_rootblks,
468 		    kd,
469 		    dmmax,
470 		    nswdev,
471 		    swap_ary,
472 		    swap_max,
473 		    0,
474 		    flags
475 		);
476 	} else {
477 		swap_ary[0].ksw_used -= blcopy.bl_free;
478 	}
479 }
480 
481 static
482 int
483 kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
484 		       int swap_max, int flags)
485 {
486 	size_t bytes = 0;
487 	size_t ksize;
488 	int ti;
489 	int n;
490 	int i;
491 	char *xswbuf;
492 	struct xswdev *xsw;
493 
494 	if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
495 		return(-1);
496 	if (bytes == 0)
497 		return(-1);
498 
499 	xswbuf = malloc(bytes);
500 	if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
501 		free(xswbuf);
502 		return(-1);
503 	}
504 	if (bytes == 0) {
505 		free(xswbuf);
506 		return(-1);
507 	}
508 
509 	bzero(swap_ary, sizeof(struct kvm_swap) * swap_max);
510 	--swap_max;
511 
512 	/*
513 	 * Calculate size of xsw entry returned by kernel (it can be larger
514 	 * than the one we have if there is a version mismatch).
515 	 *
516 	 * Then iterate the list looking for live swap devices.
517 	 */
518 	ksize = ((struct xswdev *)xswbuf)->xsw_size;
519 	n = (int)(bytes / ksize);
520 
521 	for (i = ti = 0; i < n && ti < swap_max; ++i) {
522 		xsw = (void *)((char *)xswbuf + i * ksize);
523 
524 		if ((xsw->xsw_flags & SW_FREED) == 0)
525 			continue;
526 
527 		swap_ary[ti].ksw_total = xsw->xsw_nblks;
528 		swap_ary[ti].ksw_used = xsw->xsw_used;
529 		swap_ary[ti].ksw_flags = xsw->xsw_flags;
530 
531 		if (xsw->xsw_dev == NODEV) {
532 			snprintf(
533 			    swap_ary[ti].ksw_devname,
534 			    sizeof(swap_ary[ti].ksw_devname),
535 			    "%s",
536 			    "[NFS swap]"
537 			);
538 		} else {
539 			snprintf(
540 			    swap_ary[ti].ksw_devname,
541 			    sizeof(swap_ary[ti].ksw_devname),
542 			    "%s%s",
543 			    ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),
544 			    devname(xsw->xsw_dev, S_IFCHR)
545 			);
546 		}
547 		++ti;
548 	}
549 	free(xswbuf);
550 	return(ti);
551 }
552