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