summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authordillon <dillon@FreeBSD.org>1999-01-22 10:36:04 +0000
committerdillon <dillon@FreeBSD.org>1999-01-22 10:36:04 +0000
commit247543f77e33d4ed1558543649a259fb164ab65a (patch)
treead5267c111f8234b301b3f32a11089d6ef0f8f07 /lib
parent416e7bfc5a6e4f73ffb52bd0875bc1d76b73ef9f (diff)
downloadFreeBSD-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/Makefile5
-rw-r--r--lib/libkvm/kvm.31
-rw-r--r--lib/libkvm/kvm_getswapinfo.361
-rw-r--r--lib/libkvm/kvm_getswapinfo.c486
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;
+ }
+}
+
OpenPOWER on IntegriCloud