kvm_getswapinfo.c revision 118282
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 118282 2003-07-31 21:38:32Z 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
21#include <err.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <kvm.h>
25#include <nlist.h>
26#include <paths.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <limits.h>
32
33#include "kvm_private.h"
34
35#define NL_SWAPBLIST	0
36#define NL_SWDEVT	1
37#define NL_NSWDEV	2
38#define NL_DMMAX	3
39
40static int kvm_swap_nl_cached = 0;
41static int nswdev;
42static int unswdev;  /* number of found swap dev's */
43static int dmmax;
44
45static int kvm_getswapinfo2(kvm_t *kd, struct kvm_swap *swap_ary,
46			    int swap_max, int flags);
47static int  kvm_getswapinfo_kvm(kvm_t *, struct kvm_swap *, int, int);
48static int  kvm_getswapinfo_sysctl(kvm_t *, struct kvm_swap *, int, int);
49static int  getsysctl(kvm_t *, char *, void *, size_t);
50
51#define	SVAR(var) __STRING(var)	/* to force expansion */
52#define	KGET(idx, var)							\
53	KGET1(idx, &var, sizeof(var), SVAR(var))
54#define	KGET1(idx, p, s, msg)						\
55	KGET2(kvm_swap_nl[idx].n_value, p, s, msg)
56#define	KGET2(addr, p, s, msg)						\
57	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
58		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
59#define	KGETN(idx, var)							\
60	KGET1N(idx, &var, sizeof(var), SVAR(var))
61#define	KGET1N(idx, p, s, msg)						\
62	KGET2N(kvm_swap_nl[idx].n_value, p, s, msg)
63#define	KGET2N(addr, p, s, msg)						\
64	((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0)
65#define	KGETRET(addr, p, s, msg)					\
66	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
67		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
68		return (0);						\
69	}
70
71#define GETSWDEVNAME(dev, str, flags)					\
72	if (dev == NODEV) {						\
73		strlcpy(str, "[NFS swap]", sizeof(str));		\
74	} else {							\
75		snprintf(						\
76		    str, sizeof(str),"%s%s",				\
77		    ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),	\
78		    devname(dev, S_IFCHR)				\
79		);							\
80	}
81
82int
83kvm_getswapinfo(
84	kvm_t *kd,
85	struct kvm_swap *swap_ary,
86	int swap_max,
87	int flags
88) {
89	int rv;
90
91	/*
92	 * clear cache
93	 */
94	if (kd == NULL) {
95		kvm_swap_nl_cached = 0;
96		return(0);
97	}
98
99	if (ISALIVE(kd)) {
100		return kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
101	} else {
102		return -1;
103	}
104}
105
106#define	GETSYSCTL(kd, name, var)					\
107	    getsysctl(kd, name, &(var), sizeof(var))
108
109/* The maximum MIB length for vm.swap_info and an additional device number */
110#define	SWI_MAXMIB	3
111
112int
113kvm_getswapinfo_sysctl(
114	kvm_t *kd,
115	struct kvm_swap *swap_ary,
116	int swap_max,
117	int flags
118) {
119	int ti, ttl;
120	size_t mibi, len;
121	int soid[SWI_MAXMIB];
122	struct xswdev xsd;
123	struct kvm_swap tot;
124
125	if (!GETSYSCTL(kd, "vm.dmmax", dmmax))
126		return -1;
127
128	mibi = SWI_MAXMIB - 1;
129	if (sysctlnametomib("vm.swap_info", soid, &mibi) == -1) {
130		_kvm_err(kd, kd->program, "sysctlnametomib failed: %s",
131		    strerror(errno));
132		return -1;
133	}
134	bzero(&tot, sizeof(tot));
135	for (unswdev = 0;; unswdev++) {
136		soid[mibi] = unswdev;
137		len = sizeof(xsd);
138		if (sysctl(soid, mibi + 1, &xsd, &len, NULL, 0) == -1) {
139			if (errno == ENOENT)
140				break;
141			_kvm_err(kd, kd->program, "cannot read sysctl: %s.",
142			    strerror(errno));
143			return -1;
144		}
145		if (len != sizeof(xsd)) {
146			_kvm_err(kd, kd->program, "struct xswdev has unexpected "
147			    "size;  kernel and libkvm out of sync?");
148			return -1;
149		}
150		if (xsd.xsw_version != XSWDEV_VERSION) {
151			_kvm_err(kd, kd->program, "struct xswdev version "
152			    "mismatch; kernel and libkvm out of sync?");
153			return -1;
154		}
155
156		ttl = xsd.xsw_nblks - dmmax;
157		if (unswdev < swap_max - 1) {
158			bzero(&swap_ary[unswdev], sizeof(swap_ary[unswdev]));
159			swap_ary[unswdev].ksw_total = ttl;
160			swap_ary[unswdev].ksw_used = xsd.xsw_used;
161			swap_ary[unswdev].ksw_flags = xsd.xsw_flags;
162			GETSWDEVNAME(xsd.xsw_dev, swap_ary[unswdev].ksw_devname,
163			     flags);
164		}
165		tot.ksw_total += ttl;
166		tot.ksw_used += xsd.xsw_used;
167	}
168
169	ti = unswdev;
170	if (ti >= swap_max)
171		ti = swap_max - 1;
172	if (ti >= 0)
173		swap_ary[ti] = tot;
174
175        return(ti);
176}
177
178static int
179getsysctl (
180	kvm_t *kd,
181	char *name,
182	void *ptr,
183	size_t len
184) {
185	size_t nlen = len;
186	if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
187		_kvm_err(kd, kd->program, "cannot read sysctl %s:%s", name,
188		    strerror(errno));
189		return (0);
190	}
191	if (nlen != len) {
192		_kvm_err(kd, kd->program, "sysctl %s has unexpected size", name);
193		return (0);
194	}
195	return (1);
196}
197