7ac1966f8ca8b64af75a04cfe45f5dd763122693
[dragonfly.git] / lib / libkvm / kvm_getswapinfo.c
1 /*
2  * Copyright (c) 1999, Matthew Dillon.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided under the terms of the BSD
6  * Copyright as found in /usr/src/COPYRIGHT in the FreeBSD source tree.
7  *
8  * @(#) Copyright (c) 1999
9  * $FreeBSD: src/lib/libkvm/kvm_getswapinfo.c,v 1.10.2.4 2003/01/12 09:23:13 dillon Exp $
10  * $DragonFly: src/lib/libkvm/kvm_getswapinfo.c,v 1.2 2003/06/17 04:26:49 dillon Exp $
11  */
12
13 #include <sys/param.h>
14 #include <sys/time.h>
15 #include <sys/ucred.h>
16 #include <sys/stat.h>
17 #include <sys/conf.h>
18 #include <sys/blist.h>
19
20 #include <err.h>
21 #include <fcntl.h>
22 #include <kvm.h>
23 #include <nlist.h>
24 #include <paths.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 static struct nlist kvm_swap_nl[] = {
31         { "_swapblist" },       /* new radix swap list          */
32         { "_swdevt" },          /* list of swap devices and sizes */
33         { "_nswdev" },          /* number of swap devices */
34         { "_dmmax" },           /* maximum size of a swap block */
35         { "" }
36 };
37
38 #define NL_SWAPBLIST    0
39 #define NL_SWDEVT       1
40 #define NL_NSWDEV       2
41 #define NL_DMMAX        3
42
43 static int kvm_swap_nl_cached = 0;
44 static int nswdev;
45 static int unswdev;
46 static int dmmax;
47
48 static void getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary,
49                               int swap_max, int flags);
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 int
72 kvm_getswapinfo(
73         kvm_t *kd, 
74         struct kvm_swap *swap_ary,
75         int swap_max, 
76         int flags
77 ) {
78         int ti = 0;
79
80         /*
81          * clear cache
82          */
83         if (kd == NULL) {
84                 kvm_swap_nl_cached = 0;
85                 return(0);
86         }
87
88         /*
89          * namelist
90          */
91         if (kvm_swap_nl_cached == 0) {
92                 struct swdevt *sw;
93
94                 if (kvm_nlist(kd, kvm_swap_nl) < 0)
95                         return(-1);
96
97                 /*
98                  * required entries
99                  */
100
101                 if (
102                     kvm_swap_nl[NL_SWDEVT].n_value == 0 ||
103                     kvm_swap_nl[NL_NSWDEV].n_value == 0 ||
104                     kvm_swap_nl[NL_DMMAX].n_value == 0 ||
105                     kvm_swap_nl[NL_SWAPBLIST].n_type == 0
106                 ) {
107                         return(-1);
108                 }
109
110                 /*
111                  * get globals, type of swap
112                  */
113
114                 KGET(NL_NSWDEV, nswdev);
115                 KGET(NL_DMMAX, dmmax);
116
117                 /*
118                  * figure out how many actual swap devices are enabled
119                  */
120
121                 KGET(NL_SWDEVT, sw);
122                 for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) {
123                         struct swdevt swinfo;
124
125                         KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo");
126                         if (swinfo.sw_nblks)
127                                 break;
128                 }
129                 ++unswdev;
130
131                 kvm_swap_nl_cached = 1;
132         }
133
134
135         {
136                 struct swdevt *sw;
137                 int i;
138
139                 ti = unswdev;
140                 if (ti >= swap_max)
141                         ti = swap_max - 1;
142
143                 if (ti >= 0)
144                         bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1));
145
146                 KGET(NL_SWDEVT, sw);
147                 for (i = 0; i < unswdev; ++i) {
148                         struct swdevt swinfo;
149                         int ttl;
150
151                         KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
152
153                         /*
154                          * old style: everything in DEV_BSIZE'd chunks,
155                          * convert to pages.
156                          *
157                          * new style: swinfo in DEV_BSIZE'd chunks but dmmax
158                          * in pages.
159                          *
160                          * The first dmmax is never allocating to avoid 
161                          * trashing the disklabels
162                          */
163
164                         ttl = swinfo.sw_nblks - dmmax;
165
166                         if (ttl == 0)
167                                 continue;
168
169                         if (i < ti) {
170                                 swap_ary[i].ksw_total = ttl;
171                                 swap_ary[i].ksw_used = ttl;
172                                 swap_ary[i].ksw_flags = swinfo.sw_flags;
173                                 if (swinfo.sw_dev == NODEV) {
174                                         snprintf(
175                                             swap_ary[i].ksw_devname,
176                                             sizeof(swap_ary[i].ksw_devname),
177                                             "%s",
178                                             "[NFS swap]"
179                                         );
180                                 } else {
181                                         snprintf(
182                                             swap_ary[i].ksw_devname,
183                                             sizeof(swap_ary[i].ksw_devname),
184                                             "%s%s",
185                                             ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""),
186                                             devname(swinfo.sw_dev, S_IFCHR)
187                                         );
188                                 }
189                         }
190                         if (ti >= 0) {
191                                 swap_ary[ti].ksw_total += ttl;
192                                 swap_ary[ti].ksw_used += ttl;
193                         }
194                 }
195         }
196
197         getswapinfo_radix(kd, swap_ary, swap_max, flags);
198         return(ti);
199 }
200
201 /*
202  * scanradix() - support routine for radix scanner
203  */
204
205 #define TABME   tab, tab, ""
206
207 static int
208 scanradix(
209         blmeta_t *scan, 
210         daddr_t blk,
211         daddr_t radix,
212         daddr_t skip, 
213         daddr_t count,
214         kvm_t *kd,
215         int dmmax, 
216         int nswdev,
217         struct kvm_swap *swap_ary,
218         int swap_max,
219         int tab,
220         int flags
221 ) {
222         blmeta_t meta;
223         int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev;
224
225         KGET2(scan, &meta, sizeof(meta), "blmeta_t");
226
227         /*
228          * Terminator
229          */
230         if (meta.bm_bighint == (daddr_t)-1) {
231                 if (flags & SWIF_DUMP_TREE) {
232                         printf("%*.*s(0x%06x,%d) Terminator\n", 
233                             TABME,
234                             blk, 
235                             radix
236                         );
237                 }
238                 return(-1);
239         }
240
241         if (radix == BLIST_BMAP_RADIX) {
242                 /*
243                  * Leaf bitmap
244                  */
245                 int i;
246
247                 if (flags & SWIF_DUMP_TREE) {
248                         printf("%*.*s(0x%06x,%d) Bitmap %08x big=%d\n", 
249                             TABME,
250                             blk, 
251                             radix,
252                             (int)meta.u.bmu_bitmap,
253                             meta.bm_bighint
254                         );
255                 }
256
257                 /*
258                  * If not all allocated, count.
259                  */
260                 if (meta.u.bmu_bitmap != 0) {
261                         for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) {
262                                 /*
263                                  * A 0 bit means allocated
264                                  */
265                                 if ((meta.u.bmu_bitmap & (1 << i))) {
266                                         int t = 0;
267
268                                         if (nswdev)
269                                                 t = (blk + i) / dmmax % nswdev;
270                                         if (t < ti)
271                                                 --swap_ary[t].ksw_used;
272                                         if (ti >= 0)
273                                                 --swap_ary[ti].ksw_used;
274                                 }
275                         }
276                 }
277         } else if (meta.u.bmu_avail == radix) {
278                 /*
279                  * Meta node if all free
280                  */
281                 if (flags & SWIF_DUMP_TREE) {
282                         printf("%*.*s(0x%06x,%d) Submap ALL-FREE {\n", 
283                             TABME,
284                             blk, 
285                             radix
286                         );
287                 }
288                 /*
289                  * Note: both dmmax and radix are powers of 2.  However, dmmax
290                  * may be larger then radix so use a smaller increment if
291                  * necessary.
292                  */
293                 {
294                         int t;
295                         int tinc = dmmax;
296
297                         while (tinc > radix)
298                                 tinc >>= 1;
299
300                         for (t = blk; t < blk + radix; t += tinc) {
301                                 int u = (nswdev) ? (t / dmmax % nswdev) : 0;
302
303                                 if (u < ti)
304                                         swap_ary[u].ksw_used -= tinc;
305                                 if (ti >= 0)
306                                         swap_ary[ti].ksw_used -= tinc;
307                         }
308                 }
309         } else if (meta.u.bmu_avail == 0) {
310                 /*
311                  * Meta node if all used
312                  */
313                 if (flags & SWIF_DUMP_TREE) {
314                         printf("%*.*s(0x%06x,%d) Submap ALL-ALLOCATED\n", 
315                             TABME,
316                             blk, 
317                             radix
318                         );
319                 }
320         } else {
321                 /*
322                  * Meta node if not all free
323                  */
324                 int i;
325                 int next_skip;
326
327                 if (flags & SWIF_DUMP_TREE) {
328                         printf("%*.*s(0x%06x,%d) Submap avail=%d big=%d {\n", 
329                             TABME,
330                             blk, 
331                             radix,
332                             (int)meta.u.bmu_avail,
333                             meta.bm_bighint
334                         );
335                 }
336
337                 radix /= BLIST_META_RADIX;
338                 next_skip = skip / BLIST_META_RADIX;
339
340                 for (i = 1; i <= skip; i += next_skip) {
341                         int r;
342                         daddr_t vcount = (count > radix) ? radix : count;
343
344                         r = scanradix(
345                             &scan[i],
346                             blk,
347                             radix,
348                             next_skip - 1,
349                             vcount,
350                             kd,
351                             dmmax,
352                             nswdev,
353                             swap_ary,
354                             swap_max,
355                             tab + 4,
356                             flags
357                         );
358                         if (r < 0)
359                                 break;
360                         blk += radix;
361                 }
362                 if (flags & SWIF_DUMP_TREE) {
363                         printf("%*.*s}\n", TABME);
364                 }
365         }
366         return(0);
367 }
368
369 static void
370 getswapinfo_radix(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags)
371 {
372         struct blist *swapblist = NULL;
373         struct blist blcopy = { 0 };
374
375         KGET(NL_SWAPBLIST, swapblist);
376
377         if (swapblist == NULL) {
378                 if (flags & SWIF_DUMP_TREE)
379                         printf("radix tree: NULL - no swap in system\n");
380                 return;
381         }
382
383         KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist");
384
385         if (flags & SWIF_DUMP_TREE) {
386                 printf("radix tree: %d/%d/%d blocks, %dK wired\n",
387                         blcopy.bl_free,
388                         blcopy.bl_blocks,
389                         blcopy.bl_radix,
390                         (int)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/
391                             1024)
392                 );
393         }
394         scanradix(
395             blcopy.bl_root, 
396             0, 
397             blcopy.bl_radix, 
398             blcopy.bl_skip, 
399             blcopy.bl_rootblks, 
400             kd,
401             dmmax,
402             nswdev, 
403             swap_ary,
404             swap_max,
405             0,
406             flags
407         );
408 }