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