Merge branch 'vendor/MDOCML'
[dragonfly.git] / lib / libkvm / kvm_getswapinfo.c
1 /*
2  * Copyright (c) 1999 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $FreeBSD: src/lib/libkvm/kvm_getswapinfo.c,v 1.10.2.4 2003/01/12 09:23:13 dillon Exp $
35  * $DragonFly: src/lib/libkvm/kvm_getswapinfo.c,v 1.5 2006/03/18 17:15:35 dillon Exp $
36  */
37
38 #define _KERNEL_STRUCTURES
39
40 #include <sys/param.h>
41 #include <sys/time.h>
42 #include <sys/ucred.h>
43 #include <sys/stat.h>
44 #include <sys/conf.h>
45 #include <sys/blist.h>
46 #include <sys/sysctl.h>
47 #include <vm/vm_param.h>
48
49 #include <err.h>
50 #include <fcntl.h>
51 #include <kvm.h>
52 #include <nlist.h>
53 #include <paths.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <limits.h>
59
60 #include "kvm_private.h"
61
62 static struct nlist kvm_swap_nl[] = {
63         { "_swapblist" },       /* new radix swap list          */
64         { "_swdevt" },          /* list of swap devices and sizes */
65         { "_nswdev" },          /* number of swap devices */
66         { "_dmmax" },           /* maximum size of a swap block */
67         { "" }
68 };
69
70 #define NL_SWAPBLIST    0
71 #define NL_SWDEVT       1
72 #define NL_NSWDEV       2
73 #define NL_DMMAX        3
74
75 static int kvm_swap_nl_cached = 0;
76 static int nswdev;
77 static int unswdev;
78 static int dmmax;
79
80 static int nlist_init(kvm_t *kd);
81 static void dump_blist(kvm_t *kd);
82 static int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
83                               int swap_max, int flags);
84
85 #define SVAR(var) __STRING(var) /* to force expansion */
86 #define KGET(idx, var)                                                  \
87         KGET1(idx, &var, sizeof(var), SVAR(var))
88 #define KGET1(idx, p, s, msg)                                           \
89         KGET2(kvm_swap_nl[idx].n_value, p, s, msg)
90 #define KGET2(addr, p, s, msg)                                          \
91         if (kvm_read(kd, (u_long)(addr), p, s) != s)                    \
92                 warnx("cannot read %s: %s", msg, kvm_geterr(kd))
93 #define KGETN(idx, var)                                                 \
94         KGET1N(idx, &var, sizeof(var), SVAR(var))
95 #define KGET1N(idx, p, s, msg)                                          \
96         KGET2N(kvm_swap_nl[idx].n_value, p, s, msg)
97 #define KGET2N(addr, p, s, msg)                                         \
98         ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0)
99 #define KGETRET(addr, p, s, msg)                                        \
100         if (kvm_read(kd, (u_long)(addr), p, s) != s) {                  \
101                 warnx("cannot read %s: %s", msg, kvm_geterr(kd));       \
102                 return (0);                                             \
103         }
104
105 #define GETSWDEVNAME(dev, str, flags)                                   \
106         if (dev == NODEV) {                                             \
107                 strlcpy(str, "[NFS swap]", sizeof(str));                \
108         } else {                                                        \
109                 snprintf(                                               \
110                     str, sizeof(str), "%s%s",                           \
111                     ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),       \
112                     devname(dev, S_IFCHR)                               \
113                 );                                                      \
114         }
115
116 int
117 kvm_getswapinfo(
118         kvm_t *kd, 
119         struct kvm_swap *swap_ary,
120         int swap_max, 
121         int flags
122 ) {
123         int i, ti, swi;
124         int ttl;
125         struct swdevt *sw;
126         struct swdevt swinfo;
127
128         /*
129          * clear cache
130          */
131         if (kd == NULL) {
132                 kvm_swap_nl_cached = 0;
133                 return(0);
134         }
135
136         if (swap_max < 1)
137                 return (-1);
138
139         /*
140          * Use sysctl if possible
141          */
142         if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) {
143                 ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
144                 if (ti >= 0)
145                         return(ti);
146         }
147
148         /*
149          * namelist
150          */
151         if (!nlist_init(kd))
152                 return (-1);
153
154         swi = unswdev;
155         if (swi >= swap_max)
156                 swi = swap_max - 1;
157
158         bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
159
160         KGET(NL_SWDEVT, sw);
161         for (i = ti = 0; i < nswdev; ++i) {
162                 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
163
164                 if (swinfo.sw_nblks == 0)
165                         continue;
166
167                 /*
168                  * The first dmmax is never allocated to avoid
169                  * trashing the disklabels.
170                  */
171                 ttl = swinfo.sw_nblks - dmmax;
172                 if (ttl == 0)
173                         continue;
174
175                 swap_ary[swi].ksw_total += ttl;
176                 swap_ary[swi].ksw_used += swinfo.sw_nused;
177
178                 if (ti < swi) {
179                         swap_ary[ti].ksw_total = ttl;
180                         swap_ary[ti].ksw_used = swinfo.sw_nused;
181                         swap_ary[ti].ksw_flags = swinfo.sw_flags;
182                         GETSWDEVNAME(swinfo.sw_dev, swap_ary[ti].ksw_devname,
183                             flags);
184                         ++ti;
185                 }
186         }
187
188         if (flags & SWIF_DUMP_TREE)
189                 dump_blist(kd);
190         return (swi);
191 }
192
193 static int
194 nlist_init(kvm_t *kd)
195 {
196         int i;
197         struct swdevt *sw;
198         struct swdevt swinfo;
199
200         if (kvm_swap_nl_cached)
201                 return (1);
202
203         if (kvm_nlist(kd, kvm_swap_nl) < 0)
204                 return (0);
205
206         /*
207          * required entries
208          */
209         if (kvm_swap_nl[NL_SWDEVT].n_value == 0 ||
210             kvm_swap_nl[NL_NSWDEV].n_value == 0 ||
211             kvm_swap_nl[NL_DMMAX].n_value == 0 ||
212             kvm_swap_nl[NL_SWAPBLIST].n_type == 0) {
213                 return (0);
214         }
215
216         /*
217          * get globals, type of swap
218          */
219         KGET(NL_NSWDEV, nswdev);
220         KGET(NL_DMMAX, dmmax);
221
222         /*
223          * figure out how many actual swap devices are enabled
224          */
225         KGET(NL_SWDEVT, sw);
226         for (i = unswdev = 0; i < nswdev; ++i) {
227                 KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
228                 if (swinfo.sw_nblks)
229                         ++unswdev;
230
231         }
232
233         kvm_swap_nl_cached = 1;
234         return (1);
235 }
236
237 /*
238  * scanradix() - support routine for radix scanner
239  */
240
241 #define TABME   tab, tab, ""
242
243 static int
244 scanradix(
245         blmeta_t *scan, 
246         blmeta_t *scan_cache,
247         swblk_t blk,
248         int64_t radix,
249         swblk_t skip,
250         swblk_t count,
251         kvm_t *kd,
252         int dmmax, 
253         int nswdev,
254         int tab
255 ) {
256         blmeta_t meta;
257         blmeta_t scan_array[BLIST_BMAP_RADIX];
258
259         if (scan_cache) {
260                 meta = *scan_cache;
261         } else if (skip == BLIST_META_RADIX) {
262                 if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) {
263                         warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd));
264                         bzero(scan_array, sizeof(scan_array));
265                 }
266                 meta = scan_array[0];
267         } else {
268                 KGET2(scan, &meta, sizeof(meta), "blmeta_t");
269         }
270
271         /*
272          * Terminator
273          */
274         if (meta.bm_bighint == (swblk_t)-1) {
275                 printf("%*.*s(0x%06x,%lld) Terminator\n",
276                     TABME,
277                     blk,
278                     (long long)radix
279                 );
280                 return(-1);
281         }
282
283         if (radix == BLIST_BMAP_RADIX) {
284                 /*
285                  * Leaf bitmap
286                  */
287                 printf("%*.*s(0x%06x,%lld) Bitmap %08x big=%d\n",
288                     TABME,
289                     blk,
290                     (long long)radix,
291                     (int)meta.u.bmu_bitmap,
292                     meta.bm_bighint
293                 );
294
295         } else if (meta.u.bmu_avail == radix) {
296                 /*
297                  * Meta node if all free
298                  */
299                 printf("%*.*s(0x%06x,%lld) Submap ALL-FREE {\n",
300                     TABME,
301                     blk,
302                     (long long)radix
303                 );
304
305         } else if (meta.u.bmu_avail == 0) {
306                 /*
307                  * Meta node if all used
308                  */
309                 printf("%*.*s(0x%06x,%lld) Submap ALL-ALLOCATED\n",
310                     TABME,
311                     blk,
312                     (long long)radix
313                 );
314         } else {
315                 /*
316                  * Meta node if not all free
317                  */
318                 int i;
319                 int next_skip;
320
321                 printf("%*.*s(0x%06x,%lld) Submap avail=%d big=%d {\n",
322                     TABME,
323                     blk,
324                     (long long)radix,
325                     (int)meta.u.bmu_avail,
326                     meta.bm_bighint
327                 );
328
329                 radix /= BLIST_META_RADIX;
330                 next_skip = skip / BLIST_META_RADIX;
331
332                 for (i = 1; i <= skip; i += next_skip) {
333                         int r;
334                         swblk_t vcount = (count > radix) ?
335                                         (swblk_t)radix : count;
336
337                         r = scanradix(
338                             &scan[i],
339                             ((next_skip == 1) ? &scan_array[i] : NULL),
340                             blk,
341                             radix,
342                             next_skip - 1,
343                             vcount,
344                             kd,
345                             dmmax,
346                             nswdev,
347                             tab + 4
348                         );
349                         if (r < 0)
350                                 break;
351                         blk += (swblk_t)radix;
352                 }
353                 printf("%*.*s}\n", TABME);
354         }
355         return(0);
356 }
357
358 static void
359 dump_blist(kvm_t *kd)
360 {
361         struct blist *swapblist = NULL;
362         struct blist blcopy = { 0 };
363
364         KGET(NL_SWAPBLIST, swapblist);
365
366         if (swapblist == NULL) {
367                 printf("radix tree: NULL - no swap in system\n");
368                 return;
369         }
370
371         KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist");
372
373         printf("radix tree: %d/%d/%lld blocks, %dK wired\n",
374                 blcopy.bl_free,
375                 blcopy.bl_blocks,
376                 (long long)blcopy.bl_radix,
377                 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/
378                     1024)
379         );
380
381         scanradix(
382             blcopy.bl_root,
383             NULL,
384             0,
385             blcopy.bl_radix,
386             blcopy.bl_skip,
387             blcopy.bl_rootblks,
388             kd,
389             dmmax,
390             nswdev,
391             0
392         );
393 }
394
395 static
396 int
397 kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
398                        int swap_max, int flags)
399 {
400         size_t bytes = 0;
401         size_t ksize;
402         int ti;
403         int swi;
404         int n;
405         int i;
406         char *xswbuf;
407         struct xswdev *xsw;
408
409         if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
410                 return(-1);
411         if (bytes == 0)
412                 return(-1);
413
414         xswbuf = malloc(bytes);
415         if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
416                 free(xswbuf);
417                 return(-1);
418         }
419         if (bytes == 0) {
420                 free(xswbuf);
421                 return(-1);
422         }
423
424         /*
425          * Calculate size of xsw entry returned by kernel (it can be larger
426          * than the one we have if there is a version mismatch).
427          */
428         ksize = ((struct xswdev *)xswbuf)->xsw_size;
429         n = (int)(bytes / ksize);
430
431         /*
432          * Calculate the number of live swap devices and calculate
433          * the swap_ary[] index used for the cumulative result (swi)
434          */
435         for (i = swi = 0; i < n; ++i) {
436                 xsw = (void *)((char *)xswbuf + i * ksize);
437                 if ((xsw->xsw_flags & SW_FREED) == 0)
438                         continue;
439                 ++swi;
440         }
441         if (swi >= swap_max)
442                 swi = swap_max - 1;
443
444         bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
445
446         /*
447          * Accumulate results.  If the provided swap_ary[] is too
448          * small will only populate up to the available entries,
449          * but we always populate the cumulative results entry.
450          */
451         for (i = ti = 0; i < n; ++i) {
452                 xsw = (void *)((char *)xswbuf + i * ksize);
453
454                 if ((xsw->xsw_flags & SW_FREED) == 0)
455                         continue;
456
457                 swap_ary[swi].ksw_total += xsw->xsw_nblks;
458                 swap_ary[swi].ksw_used += xsw->xsw_used;
459
460                 if (ti < swi) {
461                         swap_ary[ti].ksw_total = xsw->xsw_nblks;
462                         swap_ary[ti].ksw_used = xsw->xsw_used;
463                         swap_ary[ti].ksw_flags = xsw->xsw_flags;
464                         GETSWDEVNAME(xsw->xsw_dev, swap_ary[ti].ksw_devname,
465                             flags);
466                         ++ti;
467                 }
468         }
469
470         free(xswbuf);
471         return(swi);
472 }