diff options
author | dillon <dillon@FreeBSD.org> | 1999-01-22 10:36:04 +0000 |
---|---|---|
committer | dillon <dillon@FreeBSD.org> | 1999-01-22 10:36:04 +0000 |
commit | 247543f77e33d4ed1558543649a259fb164ab65a (patch) | |
tree | ad5267c111f8234b301b3f32a11089d6ef0f8f07 /lib | |
parent | 416e7bfc5a6e4f73ffb52bd0875bc1d76b73ef9f (diff) | |
download | FreeBSD-src-247543f77e33d4ed1558543649a259fb164ab65a.zip FreeBSD-src-247543f77e33d4ed1558543649a259fb164ab65a.tar.gz |
Implement kvm_getswapinfo() libkvm function. Will be used by
pstat, top, and systat.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libkvm/Makefile | 5 | ||||
-rw-r--r-- | lib/libkvm/kvm.3 | 1 | ||||
-rw-r--r-- | lib/libkvm/kvm_getswapinfo.3 | 61 | ||||
-rw-r--r-- | lib/libkvm/kvm_getswapinfo.c | 486 |
4 files changed, 551 insertions, 2 deletions
diff --git a/lib/libkvm/Makefile b/lib/libkvm/Makefile index c31992d..0428895 100644 --- a/lib/libkvm/Makefile +++ b/lib/libkvm/Makefile @@ -2,10 +2,11 @@ LIB= kvm CFLAGS+=-DLIBC_SCCS -I${.CURDIR}/../../sys -SRCS= kvm.c kvm_${MACHINE_ARCH}.c kvm_file.c kvm_getloadavg.c kvm_proc.c +SRCS= kvm.c kvm_${MACHINE_ARCH}.c kvm_file.c kvm_getloadavg.c kvm_proc.c \ + kvm_getswapinfo.c MAN3= kvm.3 kvm_geterr.3 kvm_getfiles.3 kvm_getloadavg.3 kvm_getprocs.3 \ - kvm_nlist.3 kvm_open.3 kvm_read.3 + kvm_nlist.3 kvm_open.3 kvm_read.3 kvm_getswapinfo.3 MLINKS+=kvm_getprocs.3 kvm_getargv.3 kvm_getprocs.3 kvm_getenvv.3 MLINKS+=kvm_open.3 kvm_openfiles.3 kvm_open.3 kvm_close.3 diff --git a/lib/libkvm/kvm.3 b/lib/libkvm/kvm.3 index e0b12eb..c6f0e51 100644 --- a/lib/libkvm/kvm.3 +++ b/lib/libkvm/kvm.3 @@ -93,6 +93,7 @@ given descriptor. .Xr kvm_getenvv 3 , .Xr kvm_geterr 3 , .Xr kvm_getloadavg 3 , +.Xr kvm_getswapinfo 3 , .Xr kvm_getprocs 3 , .Xr kvm_nlist 3 , .Xr kvm_open 3 , diff --git a/lib/libkvm/kvm_getswapinfo.3 b/lib/libkvm/kvm_getswapinfo.3 new file mode 100644 index 0000000..ea5ccaa --- /dev/null +++ b/lib/libkvm/kvm_getswapinfo.3 @@ -0,0 +1,61 @@ +.\" Copyright (c) 1999, Matthew Dillon. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided under the terms of the BSD +.\" Copyright as found in /usr/src/COPYRIGHT in the FreeBSD source tree. +.\" +.\" +.Dd January 22, 1999 +.Dt KVM_SWAPINFO 3 +.Os +.Sh NAME +.Nm kvm_swapinfo +.Nd return swap summary statistics for the system +.Sh SYNOPSIS +.Fd #include <kvm.h> +.Ft int +.Fn kvm_swapinfo "kvm_t *kd" "struct kvm_swap *" "int maxswap" "int flags" +.Sh DESCRIPTION +The +.Fn kvm_swapinfo +function fills an array of kvm_swap structures with swap summary +information for each swap device, for up to maxswap - 1 devices. +The number of devices, up to maxswap - 1, is returned. A grand +total of all swap devices ( including any devices that go beyond +maxswap - 1 ) is returned in one additional array entry. This +entry is not counted in the return value. Thus, if you specify +a maxswap value of 1, the function will typically return the +value 0 and the single kvm_swap structure will be filled with +the grand total over all swap devices. The grand total is calculated +from all available swap devices whether or not you made room +for them all in the array. +the grant total is returned. +.Pp +The flags argument is currently unused and must be passed as 0. +.Pp +If an error occurs, -1 is returned. +.Pp +Each swap partition and the grand total is summarized in the kvm_swap +structure. This structure contains the following fields: +.Bl -inset -width indent +.It char ksw_devname[]; +.It int ksw_total; +.It int ksw_used; +.It int ksw_flags; +.El +.Pp +Values are in PAGE_SIZE'd chunks ( see getpagesize() ). ksw_flags contains +a copy of the swap device flags. +.PP +.Sh CACHING +This function caches the nlist values for various kernel variables which +it reuses in successive calls. You may call the function with kd == NULL +to clear the cache. +.Sh DIAGNOSTICS +If the load average was unobtainable, \-1 is returned; otherwise, +the number of swap devices actually retrieved is returned. +.Pp +If the name of the swap device does not fit in the static char buffer +in the structure, it is truncated. The buffer is always zero terminated. +.Sh SEE ALSO +.Xr kvm 3 diff --git a/lib/libkvm/kvm_getswapinfo.c b/lib/libkvm/kvm_getswapinfo.c new file mode 100644 index 0000000..54663e4 --- /dev/null +++ b/lib/libkvm/kvm_getswapinfo.c @@ -0,0 +1,486 @@ +/* + * Copyright (c) 1999, Matthew Dillon. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided under the terms of the BSD + * Copyright as found in /usr/src/COPYRIGHT in the FreeBSD source tree. + */ + +#ifndef lint +static const char copyright[] = + "@(#) Copyright (c) 1999\n" + "Matthew Dillon. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static const char rcsid[] = + "$Id: kvm_getswapinfo.c,v 1.38 1999/01/21 08:08:55 dillon Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/ucred.h> +#include <sys/stat.h> +#include <sys/conf.h> +#include <sys/rlist.h> +#include <sys/blist.h> + +#include <err.h> +#include <fcntl.h> +#include <kvm.h> +#include <limits.h> +#include <nlist.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static struct nlist kvm_swap_nl[] = { + { "_swaplist" }, /* old style swap list */ + { "_swapblist" }, /* new radix swap list */ + { "_swdevt" }, /* list of swap devices and sizes */ + { "_nswdev" }, /* number of swap devices */ + { "_dmmax" }, /* maximum size of a swap block */ + { "" } +}; + +#define NL_SWAPLIST 0 +#define NL_SWAPBLIST 1 +#define NL_SWDEVT 2 +#define NL_NSWDEV 3 +#define NL_DMMAX 4 + +static int kvm_swap_nl_cached = 0; +static int nswdev; +static int unswdev; +static int dmmax; +static int type; + +static void getswapinfo_old(kvm_t *kd, kvm_swap_t swap_ary, int swap_max, int flags); +static void getswapinfo_radix(kvm_t *kd, kvm_swap_t swap_ary, int swap_max, int flags); + +#define SVAR(var) __STRING(var) /* to force expansion */ +#define KGET(idx, var) \ + KGET1(idx, &var, sizeof(var), SVAR(var)) +#define KGET1(idx, p, s, msg) \ + KGET2(kvm_swap_nl[idx].n_value, p, s, msg) +#define KGET2(addr, p, s, msg) \ + if (kvm_read(kd, (u_long)(addr), p, s) != s) \ + warnx("cannot read %s: %s", msg, kvm_geterr(kd)) +#define KGETN(idx, var) \ + KGET1N(idx, &var, sizeof(var), SVAR(var)) +#define KGET1N(idx, p, s, msg) \ + KGET2N(kvm_swap_nl[idx].n_value, p, s, msg) +#define KGET2N(addr, p, s, msg) \ + ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0) +#define KGETRET(addr, p, s, msg) \ + if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ + warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ + return (0); \ + } + +int +kvm_getswapinfo( + kvm_t *kd, + struct kvm_swap *swap_ary, + int swap_max, + int flags +) { + int ti = 0; + + /* + * clear cache + */ + if (kd == NULL) { + kvm_swap_nl_cached = 0; + return(0); + } + + /* + * namelist + */ + if (kvm_swap_nl_cached == 0) { + struct swdevt *sw; + + if (kvm_nlist(kd, kvm_swap_nl) < 0) + return(-1); + + /* + * required entries + */ + + if ( + kvm_swap_nl[NL_SWDEVT].n_value == 0 || + kvm_swap_nl[NL_NSWDEV].n_value == 0 || + kvm_swap_nl[NL_DMMAX].n_value == 0 + ) { + return(-1); + } + + /* + * get globals, type of swap + */ + + KGET(NL_NSWDEV, nswdev); + KGET(NL_DMMAX, dmmax); + + if (kvm_swap_nl[NL_SWAPLIST].n_value) + type = 1; + + if (kvm_swap_nl[NL_SWAPBLIST].n_value) + type = 2; + + /* + * figure out how many actual swap devices are enabled + */ + + KGET(NL_SWDEVT, sw); + for (unswdev = nswdev - 1; unswdev >= 0; --unswdev) { + struct swdevt swinfo; + + KGET2(&sw[unswdev], &swinfo, sizeof(swinfo), "swinfo"); + if (swinfo.sw_nblks) + break; + } + ++unswdev; + + kvm_swap_nl_cached = 1; + } + + + { + struct swdevt *sw; + int i; + + ti = unswdev; + if (ti >= swap_max) + ti = swap_max - 1; + + if (ti >= 0) + bzero(swap_ary, sizeof(struct kvm_swap) * (ti + 1)); + + KGET(NL_SWDEVT, sw); + for (i = 0; i < unswdev; ++i) { + struct swdevt swinfo; + int ttl; + + KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo"); + + /* + * old style: everything in DEV_BSIZE'd chunks, + * convert to pages. + * + * new style: swinfo in DEV_BSIZE'd chunks but dmmax + * in pages. + */ + + if (type == 1) + ttl = dbtoc(swinfo.sw_nblks); + else + ttl = swinfo.sw_nblks; + + if (ttl == 0) + continue; + + if (i < ti) { + swap_ary[i].ksw_total = ttl; + swap_ary[i].ksw_used = ttl; + swap_ary[i].ksw_flags = swinfo.sw_flags; + if (swinfo.sw_dev == NODEV) { + snprintf( + swap_ary[i].ksw_devname, + sizeof(swap_ary[i].ksw_devname), + "%s", + "[NFS swap]" + ); + } else { + snprintf( + swap_ary[i].ksw_devname, + sizeof(swap_ary[i].ksw_devname), + "%s", + devname(swinfo.sw_dev, S_IFBLK) + ); + } + } + if (ti >= 0) { + swap_ary[ti].ksw_total += ttl; + swap_ary[ti].ksw_used += ttl; + } + } + } + + switch(type) { + case 1: + getswapinfo_old(kd, swap_ary, swap_max, flags); + break; + case 2: + getswapinfo_radix(kd, swap_ary, swap_max, flags); + break; + default: + ti = -1; + break; + } + return(ti); +} + +/* + * scanradix() - support routine for radix scanner + */ + +#define TABME tab, tab, "" + +static int +scanradix( + blmeta_t *scan, + daddr_t blk, + daddr_t radix, + daddr_t skip, + daddr_t count, + kvm_t *kd, + int dmmax, + int nswdev, + kvm_swap_t swap_ary, + int swap_max, + int tab, + int flags +) { + blmeta_t meta; + int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev; + + KGET2(scan, &meta, sizeof(meta), "blmeta_t"); + + /* + * Terminator + */ + if (meta.bm_bighint == (daddr_t)-1) { + if (flags & SWIF_DUMP_TREE) { + printf("%*.*s(0x%06x,%d) Terminator\n", + TABME, + blk, + radix + ); + } + return(-1); + } + + if (radix == BLIST_BMAP_RADIX) { + /* + * Leaf bitmap + */ + int i; + + if (flags & SWIF_DUMP_TREE) { + printf("%*.*s(0x%06x,%d) Bitmap %08x big=%d\n", + TABME, + blk, + radix, + (int)meta.u.bmu_bitmap, + meta.bm_bighint + ); + } + + /* + * If not all allocated, count. + */ + if (meta.u.bmu_bitmap != 0) { + for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) { + /* + * A 0 bit means allocated + */ + if ((meta.u.bmu_bitmap & (1 << i))) { + int t = 0; + + if (nswdev) + t = (blk + i) / dmmax % nswdev; + if (t < ti) + --swap_ary[t].ksw_used; + if (ti >= 0) + --swap_ary[ti].ksw_used; + } + } + } + } else if (meta.u.bmu_avail == radix) { + /* + * Meta node if all free + */ + if (flags & SWIF_DUMP_TREE) { + printf("%*.*s(0x%06x,%d) Submap ALL-FREE {\n", + TABME, + blk, + radix, + (int)meta.u.bmu_avail, + meta.bm_bighint + ); + } + /* + * Note: both dmmax and radix are powers of 2. However, dmmax + * may be larger then radix so use a smaller increment if + * necessary. + */ + { + int t; + int tinc = dmmax; + + while (tinc > radix) + tinc >>= 1; + + for (t = blk; t < blk + radix; t += tinc) { + int u = (nswdev) ? (t / dmmax % nswdev) : 0; + + if (u < ti) + swap_ary[u].ksw_used -= tinc; + if (ti >= 0) + swap_ary[ti].ksw_used -= tinc; + } + } + } else if (meta.u.bmu_avail == 0) { + /* + * Meta node if all used + */ + if (flags & SWIF_DUMP_TREE) { + printf("%*.*s(0x%06x,%d) Submap ALL-ALLOCATED\n", + TABME, + blk, + radix, + (int)meta.u.bmu_avail, + meta.bm_bighint + ); + } + } else { + /* + * Meta node if not all free + */ + int i; + int next_skip; + + radix >>= BLIST_META_RADIX_SHIFT; + next_skip = skip >> BLIST_META_RADIX_SHIFT; + + if (flags & SWIF_DUMP_TREE) { + printf("%*.*s(0x%06x,%d) Submap avail=%d big=%d {\n", + TABME, + blk, + radix, + (int)meta.u.bmu_avail, + meta.bm_bighint + ); + } + + for (i = 1; i <= skip; i += next_skip) { + int r; + daddr_t vcount = (count > radix) ? radix : count; + + r = scanradix( + &scan[i], + blk, + radix, + next_skip - 1, + vcount, + kd, + dmmax, + nswdev, + swap_ary, + swap_max, + tab + 4, + flags + ); + if (r < 0) + break; + blk += radix; + } + if (flags & SWIF_DUMP_TREE) { + printf("%*.*s}\n", TABME); + } + } + return(0); +} + +static void +getswapinfo_radix(kvm_t *kd, kvm_swap_t swap_ary, int swap_max, int flags) +{ + struct blist *swapblist = NULL; + struct blist blcopy = { 0 }; + + KGET(NL_SWAPBLIST, swapblist); + KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist"); + + if (flags & SWIF_DUMP_TREE) { + printf("radix tree: %d/%d/%d blocks, %dK wired\n", + blcopy.bl_free, + blcopy.bl_blocks, + blcopy.bl_radix, + (blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/ + 1024 + ); + } + scanradix( + blcopy.bl_root, + 0, + blcopy.bl_radix, + blcopy.bl_skip, + blcopy.bl_rootblks, + kd, + dmmax, + nswdev, + swap_ary, + swap_max, + 0, + flags + ); +} + +static void +getswapinfo_old(kvm_t *kd, kvm_swap_t swap_ary, int swap_max, int flags) +{ + struct rlist *swapptr; + struct rlisthdr swaplist; + int ti = (unswdev >= swap_max) ? swap_max - 1 : unswdev; + + KGET(NL_SWAPLIST, swaplist); + + swapptr = swaplist.rlh_list; + + while (swapptr) { + int top; + int bottom; + int next_block; + int t; + int v; + struct rlist head; + + KGET2(swapptr, &head, sizeof(head), "swapptr"); + + top = head.rl_end; + bottom = head.rl_start; + + /* + * Handle interleave indexing + */ + + while (top / dmmax != bottom / dmmax) { + next_block = ((bottom + dmmax) / dmmax); + + t = (bottom / dmmax) % nswdev; + v = next_block * dmmax - bottom; + + if (t < ti) + swap_ary[t].ksw_used -= dbtoc(v); + if (ti >= 0) + swap_ary[ti].ksw_used -= dbtoc(v); + + bottom = next_block * dmmax; + } + + t = (bottom / dmmax) % nswdev; + v = top - bottom + 1; + + if (t < ti) + swap_ary[t].ksw_used -= dbtoc(v); + if (ti >= 0) + swap_ary[ti].ksw_used -= dbtoc(v); + + swapptr = head.rl_next; + } +} + |