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