kvm_getswapinfo.c revision 77605
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
11static const char copyright[] =
12    "@(#) Copyright (c) 1999\n"
13    "Matthew Dillon.  All rights reserved.\n";
14#endif /* not lint */
15
16#ifndef lint
17static const char rcsid[] =
18  "$FreeBSD: head/lib/libkvm/kvm_getswapinfo.c 77605 2001-06-01 22:57:07Z tmm $";
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
46static 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
59static int kvm_swap_nl_cached = 0;
60static int nswdev;
61static int unswdev;  /* number of found swap dev's */
62static int dmmax;
63
64static void getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary,
65			      int swap_max, int flags);
66static int kvm_getswapinfo2(kvm_t *kd, struct kvm_swap *swap_ary,
67			    int swap_max, int flags);
68static int  kvm_getswapinfo_kvm(kvm_t *, struct kvm_swap *, int, int);
69static int  kvm_getswapinfo_sysctl(kvm_t *, struct kvm_swap *, int, int);
70static int  nlist_init(kvm_t *);
71static 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
104int
105kvm_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
159static int
160kvm_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
173int
174kvm_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
246static int
247scanradix(
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
416static void
417getswapinfo_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
468int
469kvm_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
534static int
535nlist_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
581static int
582getsysctl (
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