1 /* Copyright (C) 1998-99 Martin Baulig
2    This file is part of LibGTop 1.0.
3 
4    Contributed by Martin Baulig <martin@home-of-linux.org>, April 1998.
5 
6    LibGTop is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License,
9    or (at your option) any later version.
10 
11    LibGTop is distributed in the hope that it will be useful, but WITHOUT
12    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14    for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with LibGTop; see the file COPYING. If not, write to the
18    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19    Boston, MA 02110-1301, USA.
20 */
21 
22 #include <config.h>
23 #include <glibtop.h>
24 #include <glibtop/error.h>
25 #include <glibtop/swap.h>
26 
27 #include <glibtop_suid.h>
28 
29 static const unsigned long _glibtop_sysdeps_swap =
30 (1L << GLIBTOP_SWAP_TOTAL) + (1L << GLIBTOP_SWAP_USED) +
31 (1L << GLIBTOP_SWAP_FREE) + (1L << GLIBTOP_SWAP_PAGEIN) +
32 (1L << GLIBTOP_SWAP_PAGEOUT);
33 
34 #if defined(__FreeBSD__) || defined(__bsdi__) || defined(__FreeBSD_kernel__)
35 
36 #include <sys/conf.h>
37 #ifdef __bsdi__
38 #include <vm/swap_pager.h>
39 #else
40 #if (__FreeBSD_version < 400005) && !defined(__FreeBSD_kernel__)
41 #include <sys/rlist.h>
42 #endif
43 #endif
44 #include <sys/vmmeter.h>
45 
46 /* nlist structure for kernel access */
47 
48 #if defined(__bsdi__)
49 static struct nlist nlst [] = {
50 	{ "_swapstats" }, /* general swap info */
51 	{ 0 }
52 };
53 #elif __FreeBSD__ < 4
54 static struct nlist nlst [] = {
55 #define VM_SWAPLIST	0
56 	{ "_swaplist" },/* list of free swap areas */
57 #define VM_SWDEVT	1
58 	{ "_swdevt" },	/* list of swap devices and sizes */
59 #define VM_NSWAP	2
60 	{ "_nswap" },	/* size of largest swap device */
61 #define VM_NSWDEV	3
62 	{ "_nswdev" },	/* number of swap devices */
63 #define VM_DMMAX	4
64 	{ "_dmmax" },	/* maximum size of a swap block */
65 	{ 0 }
66 };
67 #endif
68 
69 #elif defined(__NetBSD__) || defined(__OpenBSD__)
70 
71 #if (__NetBSD_Version__ >= 104000000) || defined(__OpenBSD__)
72 #include <uvm/uvm_extern.h>
73 #include <sys/swap.h>
74 #else
75 #include <vm/vm_swap.h>
76 #endif
77 
78 #endif
79 
80 #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) || defined(__OpenBSD__)
81 static int mib_uvmexp [] = { CTL_VM, VM_UVMEXP };
82 #else
83 /* nlist structure for kernel access */
84 static struct nlist nlst2 [] = {
85 	{ "_cnt" },
86 	{ 0 }
87 };
88 #endif
89 
90 /* Init function. */
91 
92 void
_glibtop_init_swap_p(glibtop * server)93 _glibtop_init_swap_p (glibtop *server)
94 {
95 #if defined(__FreeBSD__) || defined(__bsdi__) || defined(__FreeBSD_kernel__)
96 #if __FreeBSD__ < 4 || defined(__bsdi__)
97 	if (kvm_nlist (server->machine->kd, nlst) < 0) {
98 		glibtop_warn_io_r (server, "kvm_nlist (swap)");
99 		return;
100 	}
101 #else
102 	struct kvm_swap dummy;
103 
104 	if (kvm_getswapinfo (server->machine->kd, &dummy, 1, 0) != 0) {
105 		glibtop_warn_io_r (server, "kvm_swap (swap)");
106 		return;
107 	}
108 #endif
109 #endif
110 
111 #if !(defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000)) && !defined(__OpenBSD__)
112 	if (kvm_nlist (server->machine->kd, nlst2) < 0) {
113 		glibtop_warn_io_r (server, "kvm_nlist (cnt)");
114 		return;
115 	}
116 #endif
117 
118 	server->sysdeps.swap = _glibtop_sysdeps_swap;
119 }
120 
121 /* Provides information about swap usage. */
122 
123 /*
124  * This function is based on a program called swapinfo written
125  * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
126  */
127 
128 void
glibtop_get_swap_p(glibtop * server,glibtop_swap * buf)129 glibtop_get_swap_p (glibtop *server, glibtop_swap *buf)
130 {
131 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
132 
133 # if (__FreeBSD__ < 4) && !defined(__FreeBSD_kernel__)
134 	char *header;
135 	int hlen, nswdev, dmmax;
136 	int div, nfree, npfree;
137 	struct swdevt *sw;
138 	long blocksize, *perdev;
139 	struct rlist head;
140 	struct rlisthdr swaplist;
141 	struct rlist *swapptr;
142 	size_t sw_size;
143 	u_long ptr;
144 # else
145 	int nswdev;
146 	struct kvm_swap kvmsw[16];
147 # endif
148 
149 #elif defined(__bsdi__)
150 	struct swapstats swap;
151 #elif defined(__NetBSD__) || defined(__OpenBSD__)
152 	struct swapent *swaplist;
153 #endif
154 
155 	int nswap, i;
156 	guint64 avail = 0, inuse = 0;
157 
158 #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) || defined(__OpenBSD__)
159 	struct uvmexp uvmexp;
160 	size_t length_uvmexp;
161 #else
162 	/* To get `pagein' and `pageout'. */
163 	struct vmmeter vmm;
164 #endif
165         static int swappgsin = -1;
166 	static int swappgsout = -1;
167 
168 	glibtop_init_p (server, (1L << GLIBTOP_SYSDEPS_SWAP), 0);
169 
170 	memset (buf, 0, sizeof (glibtop_swap));
171 
172 	if (server->sysdeps.swap == 0)
173 		return;
174 
175 #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) || defined(__OpenBSD__)
176 	length_uvmexp = sizeof (uvmexp);
177 	if (sysctl (mib_uvmexp, 2, &uvmexp, &length_uvmexp, NULL, 0)) {
178 		glibtop_warn_io_r (server, "sysctl (uvmexp)");
179 		return;
180 	}
181 #else
182 	/* This is used to get the `pagein' and `pageout' members. */
183 
184 	if (kvm_read (server->machine->kd, nlst2[0].n_value,
185 		      &vmm, sizeof (vmm)) != sizeof (vmm)) {
186 		glibtop_warn_io_r (server, "kvm_read (cnt)");
187 		return;
188 	}
189 #endif
190 
191         if (swappgsin < 0) {
192 		buf->pagein = 0;
193 		buf->pageout = 0;
194 	} else {
195 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
196 		buf->pagein = vmm.v_swappgsin - swappgsin;
197 		buf->pageout = vmm.v_swappgsout - swappgsout;
198 #elif defined(__NetBSD__) && (__NetBSD_Version__ >= 599002100)
199 		/* no uvmexp.swap{ins,outs} */
200 		buf->pagein = 0;
201 		buf->pageout = 0;
202 #elif defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) || defined(__OpenBSD__)
203 		buf->pagein = uvmexp.swapins - swappgsin;
204 		buf->pageout = uvmexp.swapouts - swappgsout;
205 #else
206 		buf->pagein = vmm.v_swpin - swappgsin;
207 		buf->pageout = vmm.v_swpout - swappgsout;
208 #endif
209 	}
210 
211 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
212         swappgsin = vmm.v_swappgsin;
213 	swappgsout = vmm.v_swappgsout;
214 #elif defined(__NetBSD__) && (__NetBSD_Version__ >= 599002100)
215 	swappgsin = 0;
216 	swappgsout = 0;
217 #elif defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) || defined(__OpenBSD__)
218 	swappgsin = uvmexp.swapins;
219 	swappgsout = uvmexp.swapouts;
220 #else
221 	swappgsin = vmm.v_swpin;
222 	swappgsout = vmm.v_swpout;
223 #endif
224 
225 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
226 
227 #if (__FreeBSD__ < 4) && !defined(__FreeBSD_kernel__)
228 
229 	/* Size of largest swap device. */
230 
231 	if (kvm_read (server->machine->kd, nlst[VM_NSWAP].n_value,
232 		      &nswap, sizeof (nswap)) != sizeof (nswap)) {
233 		glibtop_warn_io_r (server, "kvm_read (nswap)");
234 		return;
235 	}
236 
237 	/* Number of swap devices. */
238 
239 	if (kvm_read (server->machine->kd, nlst[VM_NSWDEV].n_value,
240 		      &nswdev, sizeof (nswdev)) != sizeof (nswdev)) {
241 		glibtop_warn_io_r (server, "kvm_read (nswdev)");
242 		return;
243 	}
244 
245 	/* Maximum size of a swap block. */
246 
247 	if (kvm_read (server->machine->kd, nlst[VM_DMMAX].n_value,
248 		      &dmmax, sizeof (dmmax)) != sizeof (dmmax)) {
249 		glibtop_warn_io_r (server, "kvm_read (dmmax)");
250 		return;
251 	}
252 
253 	/* List of free swap areas. */
254 
255 	if (kvm_read (server->machine->kd, nlst[VM_SWAPLIST].n_value,
256 		      &swaplist, sizeof (swaplist)) != sizeof (swaplist)) {
257 		glibtop_warn_io_r (server, "kvm_read (swaplist)");
258 		return;
259 	}
260 
261 	/* Kernel offset of list of swap devices and sizes. */
262 
263 	if (kvm_read (server->machine->kd, nlst[VM_SWDEVT].n_value,
264 		      &ptr, sizeof (ptr)) != sizeof (ptr)) {
265 		glibtop_warn_io_r (server, "kvm_read (swdevt)");
266 		return;
267 	}
268 
269 	/* List of swap devices and sizes. */
270 
271 	sw_size = nswdev * sizeof (*sw);
272 	sw = g_malloc (sw_size);
273 
274 	if (kvm_read (server->machine->kd, ptr, sw, sw_size) != (ssize_t)sw_size) {
275 		glibtop_warn_io_r (server, "kvm_read (*swdevt)");
276 		return;
277 	}
278 
279 	perdev = g_malloc (nswdev * sizeof (*perdev));
280 
281 	/* Count up swap space. */
282 
283 	nfree = 0;
284 	memset (perdev, 0, nswdev * sizeof(*perdev));
285 
286 	swapptr = swaplist.rlh_list;
287 
288 	while (swapptr) {
289 		int	top, bottom, next_block;
290 
291 		if (kvm_read (server->machine->kd, (int) swapptr, &head,
292 			      sizeof (struct rlist)) != sizeof (struct rlist)) {
293 			glibtop_warn_io_r (server, "kvm_read (swapptr)");
294 			return;
295 		}
296 
297 		top = head.rl_end;
298 		bottom = head.rl_start;
299 
300 		nfree += top - bottom + 1;
301 
302 		/*
303 		 * Swap space is split up among the configured disks.
304 		 *
305 		 * For interleaved swap devices, the first dmmax blocks
306 		 * of swap space some from the first disk, the next dmmax
307 		 * blocks from the next, and so on up to nswap blocks.
308 		 *
309 		 * The list of free space joins adjacent free blocks,
310 		 * ignoring device boundries.  If we want to keep track
311 		 * of this information per device, we'll just have to
312 		 * extract it ourselves.
313 		 */
314 		while (top / dmmax != bottom / dmmax) {
315 			next_block = ((bottom + dmmax) / dmmax);
316 			perdev[(bottom / dmmax) % nswdev] +=
317 				next_block * dmmax - bottom;
318 			bottom = next_block * dmmax;
319 		}
320 		perdev[(bottom / dmmax) % nswdev] +=
321 			top - bottom + 1;
322 
323 		swapptr = head.rl_next;
324 	}
325 
326 	header = getbsize (&hlen, &blocksize);
327 
328 	div = blocksize / 512;
329 	avail = npfree = 0;
330 	for (i = 0; i < nswdev; i++) {
331 		int xsize, xfree;
332 
333 		/*
334 		 * Don't report statistics for partitions which have not
335 		 * yet been activated via swapon(8).
336 		 */
337 		if (!(sw[i].sw_flags & SW_FREED))
338 			continue;
339 
340 		/* The first dmmax is never allocated to avoid trashing of
341 		 * disklabels
342 		 */
343 		xsize = sw[i].sw_nblks - dmmax;
344 		xfree = perdev[i];
345 		inuse = xsize - xfree;
346 		npfree++;
347 		avail += xsize;
348 	}
349 
350 	/*
351 	 * If only one partition has been set up via swapon(8), we don't
352 	 * need to bother with totals.
353 	 */
354 	inuse = avail - nfree;
355 
356 	g_free (sw);
357 	g_free (perdev);
358 
359 	buf->flags = _glibtop_sysdeps_swap;
360 
361 	buf->used = inuse;
362 	buf->free = avail;
363 
364 	buf->total = inuse + avail;
365 
366 #else
367 
368 	nswdev = kvm_getswapinfo(server->machine->kd, kvmsw, 16, 0);
369 
370 	buf->flags = _glibtop_sysdeps_swap;
371 
372 	buf->used = kvmsw[nswdev].ksw_used * getpagesize();
373 	buf->total = kvmsw[nswdev].ksw_total * getpagesize();
374 
375 	buf->free = buf->total - buf->used;
376 
377 #endif
378 
379 #elif defined(__bsdi__)
380 
381 	/* General info about swap devices. */
382 
383 	if (kvm_read (server->machine->kd, nlst[0].n_value,
384 		      &swap, sizeof (swap)) != sizeof (swap)) {
385 		glibtop_warn_io_r (server, "kvm_read (swap)");
386 		return;
387 	}
388 
389 	buf->flags = _glibtop_sysdeps_swap;
390 
391 	buf->used = swap.swap_total - swap.swap_free;
392 	buf->free = swap.swap_free;
393 
394 	buf->total = swap.swap_total;
395 
396 #elif defined(__NetBSD__) || defined(__OpenBSD__)
397 
398 	nswap = swapctl (SWAP_NSWAP, NULL, 0);
399 	if (nswap < 0) {
400 		glibtop_warn_io_r (server, "swapctl (SWAP_NSWAP)");
401 		return;
402 	}
403 
404 	swaplist = g_malloc (nswap * sizeof (struct swapent));
405 
406 	if (swapctl (SWAP_STATS, swaplist, nswap) != nswap) {
407 		glibtop_warn_io_r (server, "swapctl (SWAP_STATS)");
408 		g_free (swaplist);
409 		return;
410 	}
411 
412 	for (i = 0; i < nswap; i++) {
413 		avail += swaplist[i].se_nblks;
414 		inuse += swaplist[i].se_inuse;
415 	}
416 
417 	g_free (swaplist);
418 
419 	buf->flags = _glibtop_sysdeps_swap;
420 
421 	buf->used = inuse;
422 	buf->free = avail;
423 
424 	buf->total = inuse + avail;
425 #endif
426 }
427