#define _KERNEL_STRUCTURES
#include <sys/param.h>
#include <sys/time.h>
#include <sys/ucred.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/blist.h>
#include <sys/sysctl.h>
#include <vm/vm_param.h>
#include <err.h>
#include <fcntl.h>
#include <nlist.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include "kvm.h"
#include "kvm_private.h"
static struct nlist kvm_swap_nl[] = {
{ .n_name = "_swapblist" },
{ .n_name = "_swdevt" },
{ .n_name = "_nswdev" },
{ .n_name = "_dmmax" },
{ .n_name = "" }
};
#define NL_SWAPBLIST 0
#define NL_SWDEVT 1
#define NL_NSWDEV 2
#define NL_DMMAX 3
static int kvm_swap_nl_cached = 0;
static int nswdev;
static int unswdev;
static int dmmax;
static int nlist_init(kvm_t *kd);
static void dump_blist(kvm_t *kd);
static int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary,
int swap_max, int flags);
#define SVAR(var) __STRING(var)
#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); \
}
#define GETSWDEVNAME(dev, str, flags) \
if (dev == NODEV) { \
strlcpy(str, "[NFS swap]", sizeof(str)); \
} else { \
snprintf( \
str, sizeof(str), "%s%s", \
((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), \
devname(dev, S_IFCHR) \
); \
}
int
kvm_getswapinfo(
kvm_t *kd,
struct kvm_swap *swap_ary,
int swap_max,
int flags
) {
int i, ti, swi;
swblk_t ttl;
struct swdevt *sw;
struct swdevt swinfo;
if (kd == NULL) {
kvm_swap_nl_cached = 0;
return(0);
}
if (swap_max < 1)
return (-1);
if (kvm_ishost(kd) && (flags & SWIF_DUMP_TREE) == 0) {
ti = kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags);
if (ti >= 0)
return(ti);
}
if (!nlist_init(kd))
return (-1);
swi = unswdev;
if (swi >= swap_max)
swi = swap_max - 1;
bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
KGET(NL_SWDEVT, sw);
for (i = ti = 0; i < nswdev; ++i) {
KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
if (swinfo.sw_nblks == 0)
continue;
ttl = swinfo.sw_nblks - dmmax;
if (ttl == 0)
continue;
swap_ary[swi].ksw_total += ttl;
swap_ary[swi].ksw_used += swinfo.sw_nused;
if (ti < swi) {
swap_ary[ti].ksw_total = ttl;
swap_ary[ti].ksw_used = swinfo.sw_nused;
swap_ary[ti].ksw_flags = swinfo.sw_flags;
GETSWDEVNAME(swinfo.sw_dev, swap_ary[ti].ksw_devname,
flags);
++ti;
}
}
if (flags & SWIF_DUMP_TREE)
dump_blist(kd);
return (swi);
}
static int
nlist_init(kvm_t *kd)
{
int i;
struct swdevt *sw;
struct swdevt swinfo;
if (kvm_swap_nl_cached)
return (1);
if (kvm_nlist(kd, kvm_swap_nl) < 0)
return (0);
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 ||
kvm_swap_nl[NL_SWAPBLIST].n_type == 0) {
return (0);
}
KGET(NL_NSWDEV, nswdev);
KGET(NL_DMMAX, dmmax);
KGET(NL_SWDEVT, sw);
for (i = unswdev = 0; i < nswdev; ++i) {
KGET2(&sw[i], &swinfo, sizeof(swinfo), "swinfo");
if (swinfo.sw_nblks)
++unswdev;
}
kvm_swap_nl_cached = 1;
return (1);
}
#define TABME tab, tab, ""
static int
scanradix(
blmeta_t *scan,
blmeta_t *scan_cache,
swblk_t blk,
int64_t radix,
swblk_t skip,
swblk_t count,
kvm_t *kd,
int dmmaxr,
int nswdevr,
int64_t *availp,
int tab
) {
blmeta_t meta;
blmeta_t scan_array[BLIST_BMAP_RADIX];
int64_t avail_tmp = 0;
unsigned int iu;
int im;
int next_skip;
if (scan_cache) {
meta = *scan_cache;
} else if (skip == BLIST_META_RADIX) {
if (kvm_read(kd, (u_long)scan, scan_array, sizeof(scan_array)) != sizeof(scan_array)) {
warnx("cannot read %s: %s", "blmeta_t", kvm_geterr(kd));
bzero(scan_array, sizeof(scan_array));
}
meta = scan_array[0];
} else {
KGET2(scan, &meta, sizeof(meta), "blmeta_t");
}
if (meta.bm_bighint == (swblk_t)-1) {
printf("%*.*s(0x%06jx,%jd) Terminator\n",
TABME,
(intmax_t)blk,
(intmax_t)radix
);
return(-1);
}
if (radix == BLIST_BMAP_RADIX) {
printf("%*.*s(0x%06jx,%jd) Bitmap %016jx big=%jd\n",
TABME,
(intmax_t)blk,
(intmax_t)radix,
(intmax_t)meta.u.bmu_bitmap,
(intmax_t)meta.bm_bighint
);
if (meta.u.bmu_bitmap) {
for (iu = 0; iu < BLIST_BMAP_RADIX; ++iu) {
if (meta.u.bmu_bitmap & (1LU << iu))
++*availp;
}
}
} else if (meta.u.bmu_avail == radix) {
printf("%*.*s(0x%06jx,%jd) Submap ALL-FREE (big=%jd) {\n",
TABME,
(intmax_t)blk,
(intmax_t)radix,
(intmax_t)meta.bm_bighint
);
*availp += radix;
} else if (meta.u.bmu_avail == 0) {
printf("%*.*s(0x%06jx,%jd) Submap ALL-ALLOCATED (big=%jd)\n",
TABME,
(intmax_t)blk,
(intmax_t)radix,
(intmax_t)meta.bm_bighint
);
} else {
printf("%*.*s(0x%06jx,%jd) Submap avail=%jd big=%jd {\n",
TABME,
(intmax_t)blk,
(intmax_t)radix,
(intmax_t)meta.u.bmu_avail,
(intmax_t)meta.bm_bighint
);
radix /= BLIST_META_RADIX;
next_skip = skip / BLIST_META_RADIX;
for (im = 1; im <= skip; im += next_skip) {
int r;
swblk_t vcount = (count > radix) ?
(swblk_t)radix : count;
r = scanradix(
&scan[im],
((next_skip == 1) ? &scan_array[im] : NULL),
blk,
radix,
next_skip - 1,
vcount,
kd,
dmmaxr,
nswdevr,
&avail_tmp,
tab + 4
);
if (r < 0)
break;
blk += (swblk_t)radix;
}
*availp += avail_tmp;
if (avail_tmp == meta.u.bmu_avail)
printf("%*.*s}\n", TABME);
else
printf("%*.*s} (AVAIL MISMATCH %jd/%jd\n",
TABME,
(intmax_t)avail_tmp,
(intmax_t)meta.u.bmu_avail);
}
return(0);
}
static void
dump_blist(kvm_t *kd)
{
struct blist *swapblist = NULL;
struct blist blcopy = { 0 };
int64_t avail = 0;
KGET(NL_SWAPBLIST, swapblist);
if (swapblist == NULL) {
printf("radix tree: NULL - no swap in system\n");
return;
}
KGET2(swapblist, &blcopy, sizeof(blcopy), "*swapblist");
printf("radix tree: %jd/%jd/%jd blocks, %jdK wired\n",
(intmax_t)blcopy.bl_free,
(intmax_t)blcopy.bl_blocks,
(intmax_t)blcopy.bl_radix,
(intmax_t)((blcopy.bl_rootblks * sizeof(blmeta_t) + 1023)/
1024)
);
scanradix(
blcopy.bl_root,
NULL,
0,
blcopy.bl_radix,
blcopy.bl_skip,
blcopy.bl_rootblks,
kd,
dmmax,
nswdev,
&avail,
0
);
printf("final availability: %jd\n", (intmax_t)avail);
}
static
int
kvm_getswapinfo_sysctl(kvm_t *kd __unused, struct kvm_swap *swap_ary,
int swap_max, int flags)
{
size_t bytes = 0;
size_t ksize;
int ti;
int swi;
int n;
int i;
char *xswbuf;
struct xswdev *xsw;
if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
return(-1);
if (bytes == 0)
return(-1);
xswbuf = malloc(bytes);
if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
free(xswbuf);
return(-1);
}
if (bytes == 0) {
free(xswbuf);
return(-1);
}
ksize = ((struct xswdev *)xswbuf)->xsw_size;
n = (int)(bytes / ksize);
for (i = swi = 0; i < n; ++i) {
xsw = (void *)((char *)xswbuf + i * ksize);
if ((xsw->xsw_flags & SW_FREED) == 0)
continue;
++swi;
}
if (swi >= swap_max)
swi = swap_max - 1;
bzero(swap_ary, sizeof(struct kvm_swap) * (swi + 1));
for (i = ti = 0; i < n; ++i) {
xsw = (void *)((char *)xswbuf + i * ksize);
if ((xsw->xsw_flags & SW_FREED) == 0)
continue;
swap_ary[swi].ksw_total += xsw->xsw_nblks;
swap_ary[swi].ksw_used += xsw->xsw_used;
if (ti < swi) {
swap_ary[ti].ksw_total = xsw->xsw_nblks;
swap_ary[ti].ksw_used = xsw->xsw_used;
swap_ary[ti].ksw_flags = xsw->xsw_flags;
GETSWDEVNAME(xsw->xsw_dev, swap_ary[ti].ksw_devname,
flags);
++ti;
}
}
free(xswbuf);
return(swi);
}