kvm_getswapinfo.c revision 118280
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 118280 2003-07-31 21:30:28Z phk $");
12
13#include <sys/param.h>
14#include <sys/time.h>
15#include <sys/stat.h>
16#include <sys/blist.h>
17#include <sys/sysctl.h>
18
19#include <vm/vm_param.h>
20#include <vm/swap_pager.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
36static 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
49static int kvm_swap_nl_cached = 0;
50static int nswdev;
51static int unswdev;  /* number of found swap dev's */
52static int dmmax;
53
54static int kvm_getswapinfo2(kvm_t *kd, struct kvm_swap *swap_ary,
55			    int swap_max, int flags);
56static int  kvm_getswapinfo_kvm(kvm_t *, struct kvm_swap *, int, int);
57static int  kvm_getswapinfo_sysctl(kvm_t *, struct kvm_swap *, int, int);
58static int  nlist_init(kvm_t *);
59static int  getsysctl(kvm_t *, char *, void *, size_t);
60
61#define	SVAR(var) __STRING(var)	/* to force expansion */
62#define	KGET(idx, var)							\
63	KGET1(idx, &var, sizeof(var), SVAR(var))
64#define	KGET1(idx, p, s, msg)						\
65	KGET2(kvm_swap_nl[idx].n_value, p, s, msg)
66#define	KGET2(addr, p, s, msg)						\
67	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
68		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
69#define	KGETN(idx, var)							\
70	KGET1N(idx, &var, sizeof(var), SVAR(var))
71#define	KGET1N(idx, p, s, msg)						\
72	KGET2N(kvm_swap_nl[idx].n_value, p, s, msg)
73#define	KGET2N(addr, p, s, msg)						\
74	((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0)
75#define	KGETRET(addr, p, s, msg)					\
76	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
77		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
78		return (0);						\
79	}
80
81#define GETSWDEVNAME(dev, str, flags)					\
82	if (dev == NODEV) {						\
83		strlcpy(str, "[NFS swap]", sizeof(str));		\
84	} else {							\
85		snprintf(						\
86		    str, sizeof(str),"%s%s",				\
87		    ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),	\
88		    devname(dev, S_IFCHR)				\
89		);							\
90	}
91
92int
93kvm_getswapinfo(
94	kvm_t *kd,
95	struct kvm_swap *swap_ary,
96	int swap_max,
97	int flags
98) {
99	int rv;
100
101	/*
102	 * clear cache
103	 */
104	if (kd == NULL) {
105		kvm_swap_nl_cached = 0;
106		return(0);
107	}
108
109	if (ISALIVE(kd)) {
110		return kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
111	} else {
112		return kvm_getswapinfo_kvm(kd, swap_ary, swap_max, flags);
113	}
114}
115
116int
117kvm_getswapinfo_kvm(
118	kvm_t *kd,
119	struct kvm_swap *swap_ary,
120	int swap_max,
121	int flags
122) {
123	int ti = 0;
124
125	/*
126	 * namelist
127	 */
128	if (!nlist_init(kd))
129		return (-1);
130
131	{
132		struct swdevt *sw;
133		int i;
134
135		ti = unswdev;
136		if (ti >= swap_max)
137			ti = swap_max - 1;
138
139		if (ti >= 0)
140			bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1));
141
142		KGET(NL_SWDEVT, sw);
143		for (i = 0; i < unswdev; ++i) {
144			struct swdevt swinfo;
145			int ttl;
146
147			KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
148
149			/*
150			 * old style: everything in DEV_BSIZE'd chunks,
151			 * convert to pages.
152			 *
153			 * new style: swinfo in DEV_BSIZE'd chunks but dmmax
154			 * in pages.
155			 *
156			 * The first dmmax is never allocating to avoid
157			 * trashing the disklabels
158			 */
159
160			ttl = swinfo.sw_nblks - dmmax;
161
162			if (ttl == 0)
163				continue;
164
165			if (i < ti) {
166				swap_ary[i].ksw_total = ttl;
167				swap_ary[i].ksw_used = swinfo.sw_used;
168				swap_ary[i].ksw_flags = swinfo.sw_flags;
169				GETSWDEVNAME(swinfo.sw_dev,
170				    swap_ary[i].ksw_devname, flags
171				);
172			}
173			if (ti >= 0) {
174				swap_ary[ti].ksw_total += ttl;
175				swap_ary[ti].ksw_used += swinfo.sw_used;
176			}
177		}
178	}
179
180	return(ti);
181}
182
183#define	GETSYSCTL(kd, name, var)					\
184	    getsysctl(kd, name, &(var), sizeof(var))
185
186/* The maximum MIB length for vm.swap_info and an additional device number */
187#define	SWI_MAXMIB	3
188
189int
190kvm_getswapinfo_sysctl(
191	kvm_t *kd,
192	struct kvm_swap *swap_ary,
193	int swap_max,
194	int flags
195) {
196	int ti, ttl;
197	size_t mibi, len;
198	int soid[SWI_MAXMIB];
199	struct xswdev xsd;
200	struct kvm_swap tot;
201
202	if (!GETSYSCTL(kd, "vm.dmmax", dmmax))
203		return -1;
204
205	mibi = SWI_MAXMIB - 1;
206	if (sysctlnametomib("vm.swap_info", soid, &mibi) == -1) {
207		_kvm_err(kd, kd->program, "sysctlnametomib failed: %s",
208		    strerror(errno));
209		return -1;
210	}
211	bzero(&tot, sizeof(tot));
212	for (unswdev = 0;; unswdev++) {
213		soid[mibi] = unswdev;
214		len = sizeof(xsd);
215		if (sysctl(soid, mibi + 1, &xsd, &len, NULL, 0) == -1) {
216			if (errno == ENOENT)
217				break;
218			_kvm_err(kd, kd->program, "cannot read sysctl: %s.",
219			    strerror(errno));
220			return -1;
221		}
222		if (len != sizeof(xsd)) {
223			_kvm_err(kd, kd->program, "struct xswdev has unexpected "
224			    "size;  kernel and libkvm out of sync?");
225			return -1;
226		}
227		if (xsd.xsw_version != XSWDEV_VERSION) {
228			_kvm_err(kd, kd->program, "struct xswdev version "
229			    "mismatch; kernel and libkvm out of sync?");
230			return -1;
231		}
232
233		ttl = xsd.xsw_nblks - dmmax;
234		if (unswdev < swap_max - 1) {
235			bzero(&swap_ary[unswdev], sizeof(swap_ary[unswdev]));
236			swap_ary[unswdev].ksw_total = ttl;
237			swap_ary[unswdev].ksw_used = xsd.xsw_used;
238			swap_ary[unswdev].ksw_flags = xsd.xsw_flags;
239			GETSWDEVNAME(xsd.xsw_dev, swap_ary[unswdev].ksw_devname,
240			     flags);
241		}
242		tot.ksw_total += ttl;
243		tot.ksw_used += xsd.xsw_used;
244	}
245
246	ti = unswdev;
247	if (ti >= swap_max)
248		ti = swap_max - 1;
249	if (ti >= 0)
250		swap_ary[ti] = tot;
251
252        return(ti);
253}
254
255static int
256nlist_init (
257	kvm_t *kd
258) {
259	struct swdevt *sw;
260
261	if (kvm_swap_nl_cached)
262		return (1);
263
264	if (kvm_nlist(kd, kvm_swap_nl) < 0)
265		return (0);
266
267	/*
268	 * required entries
269	 */
270	if (
271	    kvm_swap_nl[NL_SWDEVT].n_value == 0 ||
272	    kvm_swap_nl[NL_NSWDEV].n_value == 0 ||
273	    kvm_swap_nl[NL_DMMAX].n_value == 0 ||
274	    kvm_swap_nl[NL_SWAPBLIST].n_type == 0
275	   ) {
276		return (0);
277	}
278
279	/*
280	 * get globals, type of swap
281	 */
282	KGET(NL_NSWDEV, nswdev);
283	KGET(NL_DMMAX, dmmax);
284
285	/*
286	 * figure out how many actual swap devices are enabled
287	 */
288	KGET(NL_SWDEVT, sw);
289	for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) {
290		struct swdevt swinfo;
291
292		KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo");
293		if (swinfo.sw_nblks)
294			break;
295	}
296	++unswdev;
297
298	kvm_swap_nl_cached = 1;
299	return (1);
300}
301
302static int
303getsysctl (
304	kvm_t *kd,
305	char *name,
306	void *ptr,
307	size_t len
308) {
309	size_t nlen = len;
310	if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
311		_kvm_err(kd, kd->program, "cannot read sysctl %s:%s", name,
312		    strerror(errno));
313		return (0);
314	}
315	if (nlen != len) {
316		_kvm_err(kd, kd->program, "sysctl %s has unexpected size", name);
317		return (0);
318	}
319	return (1);
320}
321