xref: /freebsd/lib/libkvm/kvm_getswapinfo.c (revision ae83180158c4c937f170e31eff311b18c0286a93)
1 /*
2  * Copyright (c) 1999, Matthew Dillon.  All Rights Reserved.
3  * Copyright (c) 2001, Thomas Moestl
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided under the terms of the BSD
7  * Copyright as found in /usr/src/COPYRIGHT in the FreeBSD source tree.
8  */
9 
10 #include <sys/cdefs.h>
11 __FBSDID("$FreeBSD$");
12 
13 #include <sys/param.h>
14 #include <sys/time.h>
15 #include <sys/stat.h>
16 #include <sys/conf.h>
17 #include <sys/blist.h>
18 #include <sys/sysctl.h>
19 
20 #include <vm/vm_param.h>
21 
22 #include <err.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <kvm.h>
26 #include <nlist.h>
27 #include <paths.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <limits.h>
33 
34 #include "kvm_private.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;  /* number of found swap dev's */
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 static int kvm_getswapinfo2(kvm_t *kd, struct kvm_swap *swap_ary,
57 			    int swap_max, int flags);
58 static int  kvm_getswapinfo_kvm(kvm_t *, struct kvm_swap *, int, int);
59 static int  kvm_getswapinfo_sysctl(kvm_t *, struct kvm_swap *, int, int);
60 static int  nlist_init(kvm_t *);
61 static int  getsysctl(kvm_t *, char *, void *, size_t);
62 
63 #define	SVAR(var) __STRING(var)	/* to force expansion */
64 #define	KGET(idx, var)							\
65 	KGET1(idx, &var, sizeof(var), SVAR(var))
66 #define	KGET1(idx, p, s, msg)						\
67 	KGET2(kvm_swap_nl[idx].n_value, p, s, msg)
68 #define	KGET2(addr, p, s, msg)						\
69 	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
70 		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
71 #define	KGETN(idx, var)							\
72 	KGET1N(idx, &var, sizeof(var), SVAR(var))
73 #define	KGET1N(idx, p, s, msg)						\
74 	KGET2N(kvm_swap_nl[idx].n_value, p, s, msg)
75 #define	KGET2N(addr, p, s, msg)						\
76 	((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0)
77 #define	KGETRET(addr, p, s, msg)					\
78 	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
79 		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
80 		return (0);						\
81 	}
82 
83 #define GETSWDEVNAME(dev, str, flags)					\
84 	if (dev == NODEV) {						\
85 		strlcpy(str, "[NFS swap]", sizeof(str));		\
86 	} else {							\
87 		snprintf(						\
88 		    str, sizeof(str),"%s%s",				\
89 		    ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),	\
90 		    devname(dev, S_IFCHR)				\
91 		);							\
92 	}
93 
94 int
95 kvm_getswapinfo(
96 	kvm_t *kd,
97 	struct kvm_swap *swap_ary,
98 	int swap_max,
99 	int flags
100 ) {
101 	int rv;
102 #ifdef DEBUG_SWAPINFO
103 	int i;
104 #endif
105 
106 	/*
107 	 * clear cache
108 	 */
109 	if (kd == NULL) {
110 		kvm_swap_nl_cached = 0;
111 		return(0);
112 	}
113 
114 	rv = kvm_getswapinfo2(kd, swap_ary, swap_max, flags);
115 
116 	/* This is only called when the tree shall be dumped. It needs kvm. */
117 	if (flags & SWIF_DUMP_TREE) {
118 #ifdef DEBUG_SWAPINFO
119 		/*
120 		 * sanity check: Sizes must be equal - used field must be
121 		 * 0 after this. Fill it with total-used before, where
122 		 * getswapinfo_radix will subtrat total-used.
123 		 * This will of course only work if there is no swap activity
124 		 * while we are working, so this code is normally not active.
125 		 */
126 		for (i = 0; i < unswdev; i++) {
127 			swap_ary[i].ksw_used =  swap_ary[i].ksw_total -
128 			    swap_ary[i].ksw_used;
129 		}
130 #endif
131 		getswapinfo_radix(kd, swap_ary, swap_max, flags);
132 #ifdef DEBUG_SWAPINFO
133 		for (i = 0; i < unswdev; i++) {
134 			if (swap_ary[i].ksw_used != 0) {
135 				fprintf(stderr, "kvm_getswapinfo: swap size "
136 				    "mismatch (%d blocks)!\n",
137 				    swap_ary[i].ksw_used
138 				);
139 			}
140 		}
141 		/* This is fast enough now, so just do it again. */
142 		rv = kvm_getswapinfo2(kd, swap_ary, swap_max, flags);
143 #endif
144 	}
145 
146 	return rv;
147 }
148 
149 static int
150 kvm_getswapinfo2(
151 	kvm_t *kd,
152 	struct kvm_swap *swap_ary,
153 	int swap_max,
154 	int flags
155 ) {
156 	if (ISALIVE(kd)) {
157 		return kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
158 	} else {
159 		return kvm_getswapinfo_kvm(kd, swap_ary, swap_max, flags);
160 	}
161 }
162 
163 int
164 kvm_getswapinfo_kvm(
165 	kvm_t *kd,
166 	struct kvm_swap *swap_ary,
167 	int swap_max,
168 	int flags
169 ) {
170 	int ti = 0;
171 
172 	/*
173 	 * namelist
174 	 */
175 	if (!nlist_init(kd))
176 		return (-1);
177 
178 	{
179 		struct swdevt *sw;
180 		int i;
181 
182 		ti = unswdev;
183 		if (ti >= swap_max)
184 			ti = swap_max - 1;
185 
186 		if (ti >= 0)
187 			bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1));
188 
189 		KGET(NL_SWDEVT, sw);
190 		for (i = 0; i < unswdev; ++i) {
191 			struct swdevt swinfo;
192 			int ttl;
193 
194 			KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
195 
196 			/*
197 			 * old style: everything in DEV_BSIZE'd chunks,
198 			 * convert to pages.
199 			 *
200 			 * new style: swinfo in DEV_BSIZE'd chunks but dmmax
201 			 * in pages.
202 			 *
203 			 * The first dmmax is never allocating to avoid
204 			 * trashing the disklabels
205 			 */
206 
207 			ttl = swinfo.sw_nblks - dmmax;
208 
209 			if (ttl == 0)
210 				continue;
211 
212 			if (i < ti) {
213 				swap_ary[i].ksw_total = ttl;
214 				swap_ary[i].ksw_used = swinfo.sw_used;
215 				swap_ary[i].ksw_flags = swinfo.sw_flags;
216 				GETSWDEVNAME(swinfo.sw_dev,
217 				    swap_ary[i].ksw_devname, flags
218 				);
219 			}
220 			if (ti >= 0) {
221 				swap_ary[ti].ksw_total += ttl;
222 				swap_ary[ti].ksw_used += swinfo.sw_used;
223 			}
224 		}
225 	}
226 
227 	return(ti);
228 }
229 
230 /*
231  * scanradix() - support routine for radix scanner
232  */
233 
234 #define TABME	tab, tab, ""
235 
236 static int
237 scanradix(
238 	blmeta_t *scan,
239 	daddr_t blk,
240 	daddr_t radix,
241 	daddr_t skip,
242 	daddr_t count,
243 	kvm_t *kd,
244 	int dmmax,
245 	int nswdev,
246 	struct kvm_swap *swap_ary,
247 	int swap_max,
248 	int tab,
249 	int flags
250 ) {
251 	blmeta_t meta;
252 #ifdef DEBUG_SWAPINFO
253 	int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev;
254 #endif
255 
256 	KGET2(scan, &meta, sizeof(meta), "blmeta_t");
257 
258 	/*
259 	 * Terminator
260 	 */
261 	if (meta.bm_bighint == (daddr_t)-1) {
262 		if (flags & SWIF_DUMP_TREE) {
263 			printf("%*.*s(0x%06x,%d) Terminator\n",
264 			    TABME,
265 			    blk,
266 			    radix
267 			);
268 		}
269 		return(-1);
270 	}
271 
272 	if (radix == BLIST_BMAP_RADIX) {
273 		/*
274 		 * Leaf bitmap
275 		 */
276 #ifdef DEBUG_SWAPINFO
277 		int i;
278 #endif
279 
280 		if (flags & SWIF_DUMP_TREE) {
281 			printf("%*.*s(0x%06x,%d) Bitmap %08x big=%d\n",
282 			    TABME,
283 			    blk,
284 			    radix,
285 			    (int)meta.u.bmu_bitmap,
286 			    meta.bm_bighint
287 			);
288 		}
289 
290 #ifdef DEBUG_SWAPINFO
291 		/*
292 		 * If not all allocated, count.
293 		 */
294 		if (meta.u.bmu_bitmap != 0) {
295 			for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) {
296 				/*
297 				 * A 0 bit means allocated
298 				 */
299 				if ((meta.u.bmu_bitmap & (1 << i))) {
300 					int t = 0;
301 
302 					if (nswdev)
303 						t = (blk + i) / dmmax % nswdev;
304 					if (t < ti)
305 						--swap_ary[t].ksw_used;
306 					if (ti >= 0)
307 						--swap_ary[ti].ksw_used;
308 				}
309 			}
310 		}
311 #endif
312 	} else if (meta.u.bmu_avail == radix) {
313 		/*
314 		 * Meta node if all free
315 		 */
316 		if (flags & SWIF_DUMP_TREE) {
317 			printf("%*.*s(0x%06x,%d) Submap ALL-FREE {\n",
318 			    TABME,
319 			    blk,
320 			    radix
321 			);
322 		}
323 #ifdef DEBUG_SWAPINFO
324 		/*
325 		 * Note: both dmmax and radix are powers of 2.  However, dmmax
326 		 * may be larger then radix so use a smaller increment if
327 		 * necessary.
328 		 */
329 		{
330 			int t;
331 			int tinc = dmmax;
332 
333 			while (tinc > radix)
334 				tinc >>= 1;
335 
336 			for (t = blk; t < blk + radix; t += tinc) {
337 				int u = (nswdev) ? (t / dmmax % nswdev) : 0;
338 
339 				if (u < ti)
340 					swap_ary[u].ksw_used -= tinc;
341 				if (ti >= 0)
342 					swap_ary[ti].ksw_used -= tinc;
343 			}
344 		}
345 #endif
346 	} else if (meta.u.bmu_avail == 0) {
347 		/*
348 		 * Meta node if all used
349 		 */
350 		if (flags & SWIF_DUMP_TREE) {
351 			printf("%*.*s(0x%06x,%d) Submap ALL-ALLOCATED\n",
352 			    TABME,
353 			    blk,
354 			    radix
355 			);
356 		}
357 	} else {
358 		/*
359 		 * Meta node if not all free
360 		 */
361 		int i;
362 		int next_skip;
363 
364 		if (flags & SWIF_DUMP_TREE) {
365 			printf("%*.*s(0x%06x,%d) Submap avail=%d big=%d {\n",
366 			    TABME,
367 			    blk,
368 			    radix,
369 			    (int)meta.u.bmu_avail,
370 			    meta.bm_bighint
371 			);
372 		}
373 
374 		radix >>= BLIST_META_RADIX_SHIFT;
375 		next_skip = skip >> BLIST_META_RADIX_SHIFT;
376 
377 		for (i = 1; i <= skip; i += next_skip) {
378 			int r;
379 			daddr_t vcount = (count > radix) ? radix : count;
380 
381 			r = scanradix(
382 			    &scan[i],
383 			    blk,
384 			    radix,
385 			    next_skip - 1,
386 			    vcount,
387 			    kd,
388 			    dmmax,
389 			    nswdev,
390 			    swap_ary,
391 			    swap_max,
392 			    tab + 4,
393 			    flags
394 			);
395 			if (r < 0)
396 				break;
397 			blk += radix;
398 		}
399 		if (flags & SWIF_DUMP_TREE) {
400 			printf("%*.*s}\n", TABME);
401 		}
402 	}
403 	return(0);
404 }
405 
406 static void
407 getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags)
408 {
409 	struct blist *swapblist = NULL;
410 	struct blist blcopy = { 0 };
411 
412 	if (!nlist_init(kd)) {
413 		fprintf(stderr, "radix tree: nlist_init failed!\n");
414 		return;
415 	}
416 
417 	KGET(NL_SWAPBLIST, swapblist);
418 
419 	if (swapblist == NULL) {
420 		if (flags & SWIF_DUMP_TREE)
421 			printf("radix tree: NULL - no swap in system\n");
422 		return;
423 	}
424 
425 	KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist");
426 
427 	if (flags & SWIF_DUMP_TREE) {
428 		printf("radix tree: %d/%d/%d blocks, %dK wired\n",
429 			blcopy.bl_free,
430 			blcopy.bl_blocks,
431 			blcopy.bl_radix,
432 			(int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/
433 			    1024)
434 		);
435 	}
436 	scanradix(
437 	    blcopy.bl_root,
438 	    0,
439 	    blcopy.bl_radix,
440 	    blcopy.bl_skip,
441 	    blcopy.bl_rootblks,
442 	    kd,
443 	    dmmax,
444 	    nswdev,
445 	    swap_ary,
446 	    swap_max,
447 	    0,
448 	    flags
449 	);
450 }
451 
452 #define	GETSYSCTL(kd, name, var)					\
453 	    getsysctl(kd, name, &(var), sizeof(var))
454 
455 /* The maximum MIB length for vm.swap_info and an additional device number */
456 #define	SWI_MAXMIB	3
457 
458 int
459 kvm_getswapinfo_sysctl(
460 	kvm_t *kd,
461 	struct kvm_swap *swap_ary,
462 	int swap_max,
463 	int flags
464 ) {
465 	int ti, ttl;
466 	size_t mibi, len;
467 	int soid[SWI_MAXMIB];
468 	struct xswdev xsd;
469 	struct kvm_swap tot;
470 
471 	if (!GETSYSCTL(kd, "vm.dmmax", dmmax))
472 		return -1;
473 
474 	mibi = SWI_MAXMIB - 1;
475 	if (sysctlnametomib("vm.swap_info", soid, &mibi) == -1) {
476 		_kvm_err(kd, kd->program, "sysctlnametomib failed: %s",
477 		    strerror(errno));
478 		return -1;
479 	}
480 	bzero(&tot, sizeof(tot));
481 	for (unswdev = 0;; unswdev++) {
482 		soid[mibi] = unswdev;
483 		len = sizeof(xsd);
484 		if (sysctl(soid, mibi + 1, &xsd, &len, NULL, 0) == -1) {
485 			if (errno == ENOENT)
486 				break;
487 			_kvm_err(kd, kd->program, "cannot read sysctl: %s.",
488 			    strerror(errno));
489 			return -1;
490 		}
491 		if (len != sizeof(xsd)) {
492 			_kvm_err(kd, kd->program, "struct xswdev has unexpected "
493 			    "size;  kernel and libkvm out of sync?");
494 			return -1;
495 		}
496 		if (xsd.xsw_version != XSWDEV_VERSION) {
497 			_kvm_err(kd, kd->program, "struct xswdev version "
498 			    "mismatch; kernel and libkvm out of sync?");
499 			return -1;
500 		}
501 
502 		ttl = xsd.xsw_nblks - dmmax;
503 		if (unswdev < swap_max - 1) {
504 			bzero(&swap_ary[unswdev], sizeof(swap_ary[unswdev]));
505 			swap_ary[unswdev].ksw_total = ttl;
506 			swap_ary[unswdev].ksw_used = xsd.xsw_used;
507 			swap_ary[unswdev].ksw_flags = xsd.xsw_flags;
508 			GETSWDEVNAME(xsd.xsw_dev, swap_ary[unswdev].ksw_devname,
509 			     flags);
510 		}
511 		tot.ksw_total += ttl;
512 		tot.ksw_used += xsd.xsw_used;
513 	}
514 
515 	ti = unswdev;
516 	if (ti >= swap_max)
517 		ti = swap_max - 1;
518 	if (ti >= 0)
519 		swap_ary[ti] = tot;
520 
521         return(ti);
522 }
523 
524 static int
525 nlist_init (
526 	kvm_t *kd
527 ) {
528 	struct swdevt *sw;
529 
530 	if (kvm_swap_nl_cached)
531 		return (1);
532 
533 	if (kvm_nlist(kd, kvm_swap_nl) < 0)
534 		return (0);
535 
536 	/*
537 	 * required entries
538 	 */
539 	if (
540 	    kvm_swap_nl[NL_SWDEVT].n_value == 0 ||
541 	    kvm_swap_nl[NL_NSWDEV].n_value == 0 ||
542 	    kvm_swap_nl[NL_DMMAX].n_value == 0 ||
543 	    kvm_swap_nl[NL_SWAPBLIST].n_type == 0
544 	   ) {
545 		return (0);
546 	}
547 
548 	/*
549 	 * get globals, type of swap
550 	 */
551 	KGET(NL_NSWDEV, nswdev);
552 	KGET(NL_DMMAX, dmmax);
553 
554 	/*
555 	 * figure out how many actual swap devices are enabled
556 	 */
557 	KGET(NL_SWDEVT, sw);
558 	for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) {
559 		struct swdevt swinfo;
560 
561 		KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo");
562 		if (swinfo.sw_nblks)
563 			break;
564 	}
565 	++unswdev;
566 
567 	kvm_swap_nl_cached = 1;
568 	return (1);
569 }
570 
571 static int
572 getsysctl (
573 	kvm_t *kd,
574 	char *name,
575 	void *ptr,
576 	size_t len
577 ) {
578 	size_t nlen = len;
579 	if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
580 		_kvm_err(kd, kd->program, "cannot read sysctl %s:%s", name,
581 		    strerror(errno));
582 		return (0);
583 	}
584 	if (nlen != len) {
585 		_kvm_err(kd, kd->program, "sysctl %s has unexpected size", name);
586 		return (0);
587 	}
588 	return (1);
589 }
590