Merge branch 'vendor/TNFTP'
[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         int64_t *availp,
255         int tab
256 ) {
257         blmeta_t meta;
258         blmeta_t scan_array[BLIST_BMAP_RADIX];
259         int i;
260
261         if (scan_cache) {
262                 meta = *scan_cache;
263         } else if (skip == BLIST_META_RADIX) {
264                 if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) {
265                         warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd));
266                         bzero(scan_array, sizeof(scan_array));
267                 }
268                 meta = scan_array[0];
269         } else {
270                 KGET2(scan, &meta, sizeof(meta), "blmeta_t");
271         }
272
273         /*
274          * Terminator
275          */
276         if (meta.bm_bighint == (swblk_t)-1) {
277                 printf("%*.*s(0x%06x,%lld) Terminator\n",
278                     TABME,
279                     blk,
280                     (long long)radix
281                 );
282                 return(-1);
283         }
284
285         if (radix == BLIST_BMAP_RADIX) {
286                 /*
287                  * Leaf bitmap
288                  */
289                 printf("%*.*s(0x%06x,%lld) Bitmap %08x big=%d\n",
290                     TABME,
291                     blk,
292                     (long long)radix,
293                     (int)meta.u.bmu_bitmap,
294                     meta.bm_bighint
295                 );
296
297                 if (meta.u.bmu_bitmap) {
298                         for (i = 0; i < BLIST_BMAP_RADIX; ++i) {
299                                 if (meta.u.bmu_bitmap & (1 << i))
300                                         ++*availp;
301                         }
302                 }
303         } else if (meta.u.bmu_avail == radix) {
304                 /*
305                  * Meta node if all free
306                  */
307                 printf("%*.*s(0x%06x,%lld) Submap ALL-FREE (big=%d) {\n",
308                     TABME,
309                     blk,
310                     (long long)radix,
311                     meta.bm_bighint
312                 );
313                 *availp += radix;
314         } else if (meta.u.bmu_avail == 0) {
315                 /*
316                  * Meta node if all used
317                  */
318                 printf("%*.*s(0x%06x,%lld) Submap ALL-ALLOCATED (big=%d)\n",
319                     TABME,
320                     blk,
321                     (long long)radix,
322                     meta.bm_bighint
323                 );
324         } else {
325                 /*
326                  * Meta node if not all free
327                  */
328                 int i;
329                 int next_skip;
330                 int64_t avail_tmp = 0;
331
332                 printf("%*.*s(0x%06x,%lld) Submap avail=%d big=%d {\n",
333                     TABME,
334                     blk,
335                     (long long)radix,
336                     (int)meta.u.bmu_avail,
337                     meta.bm_bighint
338                 );
339
340                 radix /= BLIST_META_RADIX;
341                 next_skip = skip / BLIST_META_RADIX;
342
343                 for (i = 1; i <= skip; i += next_skip) {
344                         int r;
345                         swblk_t vcount = (count > radix) ?
346                                         (swblk_t)radix : count;
347
348                         r = scanradix(
349                             &scan[i],
350                             ((next_skip == 1) ? &scan_array[i] : NULL),
351                             blk,
352                             radix,
353                             next_skip - 1,
354                             vcount,
355                             kd,
356                             dmmax,
357                             nswdev,
358                             &avail_tmp,
359                             tab + 4
360                         );
361                         if (r < 0)
362                                 break;
363                         blk += (swblk_t)radix;
364                 }
365                 *availp += avail_tmp;
366                 if (avail_tmp == meta.u.bmu_avail)
367                         printf("%*.*s}\n", TABME);
368                 else
369                         printf("%*.*s} (AVAIL MISMATCH %jd/%jd\n",
370                                 TABME,
371                                 (intmax_t)avail_tmp,
372                                 (intmax_t)meta.u.bmu_avail);
373         }
374         return(0);
375 }
376
377 static void
378 dump_blist(kvm_t *kd)
379 {
380         struct blist *swapblist = NULL;
381         struct blist blcopy = { 0 };
382         int64_t avail = 0;
383
384         KGET(NL_SWAPBLIST, swapblist);
385
386         if (swapblist == NULL) {
387                 printf("radix tree: NULL - no swap in system\n");
388                 return;
389         }
390
391         KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist");
392
393         printf("radix tree: %d/%d/%lld blocks, %dK wired\n",
394                 blcopy.bl_free,
395                 blcopy.bl_blocks,
396                 (long long)blcopy.bl_radix,
397                 (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/
398                     1024)
399         );
400
401         scanradix(
402             blcopy.bl_root,
403             NULL,
404             0,
405             blcopy.bl_radix,
406             blcopy.bl_skip,
407             blcopy.bl_rootblks,
408             kd,
409             dmmax,
410             nswdev,
411             &avail,
412             0
413         );
414         printf("final availability: %jd\n", (intmax_t)avail);
415 }
416
417 static
418 int
419 kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
420                        int swap_max, int flags)
421 {
422         size_t bytes = 0;
423         size_t ksize;
424         int ti;
425         int swi;
426         int n;
427         int i;
428         char *xswbuf;
429         struct xswdev *xsw;
430
431         if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
432                 return(-1);
433         if (bytes == 0)
434                 return(-1);
435
436         xswbuf = malloc(bytes);
437         if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
438                 free(xswbuf);
439                 return(-1);
440         }
441         if (bytes == 0) {
442                 free(xswbuf);
443                 return(-1);
444         }
445
446         /*
447          * Calculate size of xsw entry returned by kernel (it can be larger
448          * than the one we have if there is a version mismatch).
449          */
450         ksize = ((struct xswdev *)xswbuf)->xsw_size;
451         n = (int)(bytes / ksize);
452
453         /*
454          * Calculate the number of live swap devices and calculate
455          * the swap_ary[] index used for the cumulative result (swi)
456          */
457         for (i = swi = 0; i < n; ++i) {
458                 xsw = (void *)((char *)xswbuf + i * ksize);
459                 if ((xsw->xsw_flags & SW_FREED) == 0)
460                         continue;
461                 ++swi;
462         }
463         if (swi >= swap_max)
464                 swi = swap_max - 1;
465
466         bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
467
468         /*
469          * Accumulate results.  If the provided swap_ary[] is too
470          * small will only populate up to the available entries,
471          * but we always populate the cumulative results entry.
472          */
473         for (i = ti = 0; i < n; ++i) {
474                 xsw = (void *)((char *)xswbuf + i * ksize);
475
476                 if ((xsw->xsw_flags & SW_FREED) == 0)
477                         continue;
478
479                 swap_ary[swi].ksw_total += xsw->xsw_nblks;
480                 swap_ary[swi].ksw_used += xsw->xsw_used;
481
482                 if (ti < swi) {
483                         swap_ary[ti].ksw_total = xsw->xsw_nblks;
484                         swap_ary[ti].ksw_used = xsw->xsw_used;
485                         swap_ary[ti].ksw_flags = xsw->xsw_flags;
486                         GETSWDEVNAME(xsw->xsw_dev, swap_ary[ti].ksw_devname,
487                             flags);
488                         ++ti;
489                 }
490         }
491
492         free(xswbuf);
493         return(swi);
494 }