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