[BACK]Return to uvm.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / systat

File: [local] / src / usr.bin / systat / uvm.c (download)

Revision 1.9, Wed May 1 12:54:27 2024 UTC (2 weeks, 4 days ago) by mpi
Branch: MAIN
Changes since 1.8: +6 -7 lines

Add per-CPU caches to the pmemrange allocator.

The caches are used primarily to reduce contention on uvm_lock_fpageq() during
concurrent page faults.  For the moment only uvm_pagealloc() tries to get a
page from the current CPU's cache.  So on some architectures the caches are
also used by the pmap layer.

Each cache is composed of two magazines, design is borrowed from jeff bonwick
vmem's paper and the implementation is similar to the one of pool_cache from
dlg@.  However there is no depot layer and magazines are refilled directly by
the pmemrange allocator.

This version includes splvm()/splx() dances because the buffer cache flips
buffers in interrupt context.  So we have to prevent recursive accesses to
per-CPU magazines.

Tested by naddy@, solene@, krw@, robert@, claudio@ and Laurence Tratt.

ok claudio@, kettenis@

/*	$OpenBSD: uvm.c,v 1.9 2024/05/01 12:54:27 mpi Exp $	*/
/*
 * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
 * Copyright (c) 2018 Kenneth R Westerback <krw@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/signal.h>
#include <sys/sysctl.h>
#include <sys/pool.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#include "systat.h"

#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif

void print_uvm(void);
int  read_uvm(void);
int  select_uvm(void);

void print_uvmexp_field(field_def *, field_def *, int *, int *, const char *);
void print_uvmexp_line(int);

struct uvmexp uvmexp;
struct uvmexp last_uvmexp;

struct uvmline {
	int	*v1;
	int	*ov1;
	char	*n1;
	int	*v2;
	int	*ov2;
	char	*n2;
	int	*v3;
	int	*ov3;
	char	*n3;
};

struct uvmline uvmline[] = {
	{ NULL, NULL, "Page Counters",
	  NULL, NULL, "Stats Counters",
	  NULL, NULL, "Fault Counters" },
	{ &uvmexp.npages, &last_uvmexp.npages, "npages",
	  &uvmexp.faults, &last_uvmexp.faults, "faults",
	  &uvmexp.fltnoram, &last_uvmexp.fltnoram, "fltnoram" },
	{ &uvmexp.free, &last_uvmexp.free, "free",
	  &uvmexp.traps, &last_uvmexp.traps, "traps",
	  &uvmexp.fltnoanon, &last_uvmexp.fltnoanon, "fltnoanon" },
	{ &uvmexp.active, &last_uvmexp.active, "active",
	  &uvmexp.intrs, &last_uvmexp.intrs, "intrs",
	  &uvmexp.fltnoamap, &last_uvmexp.fltnoamap, "fltnoamap" },
	{ &uvmexp.inactive, &last_uvmexp.inactive, "inactive",
	  &uvmexp.swtch, &last_uvmexp.swtch, "swtch",
	  &uvmexp.fltpgwait, &last_uvmexp.fltpgwait, "fltpgwait" },
	{ &uvmexp.paging, &last_uvmexp.paging, "paging",
	  &uvmexp.softs, &last_uvmexp.softs, "softs",
	  &uvmexp.fltpgrele, &last_uvmexp.fltpgrele, "fltpgrele" },
	{ &uvmexp.wired, &last_uvmexp.wired, "wired",
	  &uvmexp.syscalls, &last_uvmexp.syscalls, "syscalls",
	  &uvmexp.fltrelck, &last_uvmexp.fltrelck, "fltrelck" },
	{ &uvmexp.zeropages, &last_uvmexp.zeropages, "zeropages",
	  &uvmexp.pageins, &last_uvmexp.pageins, "pageins",
	  &uvmexp.fltrelckok, &last_uvmexp.fltrelckok, "fltrelckok" },
	{ &uvmexp.percpucaches, &last_uvmexp.percpucaches, "percpucaches",
	  &uvmexp.pgswapin, &last_uvmexp.pgswapin, "pgswapin",
	  &uvmexp.fltanget, &last_uvmexp.fltanget, "fltanget" },
	{ NULL, NULL, NULL,
	  &uvmexp.pgswapout, &last_uvmexp.pgswapout, "pgswapout",
	  &uvmexp.fltanretry, &last_uvmexp.fltanretry, "fltanretry" },
	{ NULL, NULL, NULL,
	  &uvmexp.forks, &last_uvmexp.forks, "forks",
	  &uvmexp.fltamcopy, &last_uvmexp.fltamcopy, "fltamcopy" },
	{ NULL, NULL, "Pageout Params",
	  &uvmexp.forks_ppwait, &last_uvmexp.forks_ppwait, "forks_ppwait",
	  &uvmexp.fltnamap, &last_uvmexp.fltnamap, "fltnamap" },
	{ &uvmexp.freemin, &last_uvmexp.freemin, "freemin",
	  &uvmexp.forks_sharevm, &last_uvmexp.forks_sharevm, "forks_sharevm",
	  &uvmexp.fltnomap, &last_uvmexp.fltnomap, "fltnomap" },
	{ &uvmexp.freetarg, &last_uvmexp.freetarg, "freetarg",
	  &uvmexp.pga_zerohit, &last_uvmexp.pga_zerohit, "pga_zerohit",
	  &uvmexp.fltlget, &last_uvmexp.fltlget, "fltlget" },
	{ &uvmexp.inactarg, &last_uvmexp.inactarg, "inactarg",
	  &uvmexp.pga_zeromiss, &last_uvmexp.pga_zeromiss, "pga_zeromiss",
	  &uvmexp.fltget, &last_uvmexp.fltget, "fltget" },
	{ &uvmexp.wiredmax, &last_uvmexp.wiredmax, "wiredmax",
	  NULL, NULL, NULL,
	  &uvmexp.flt_anon, &last_uvmexp.flt_anon, "flt_anon" },
	{ &uvmexp.anonmin, &last_uvmexp.anonmin, "anonmin",
	  NULL, NULL, "Daemon Counters",
	  &uvmexp.flt_acow, &last_uvmexp.flt_acow, "flt_acow" },
	{ &uvmexp.vtextmin, &last_uvmexp.vtextmin, "vtextmin",
	  &uvmexp.pdwoke, &last_uvmexp.pdwoke, "pdwoke",
	  &uvmexp.flt_obj, &last_uvmexp.flt_obj, "flt_obj" },
	{ &uvmexp.vnodemin, &last_uvmexp.vnodemin, "vnodemin",
	  &uvmexp.pdrevs, &last_uvmexp.pdrevs, "pdrevs",
	  &uvmexp.flt_prcopy, &last_uvmexp.flt_prcopy, "flt_prcopy" },
	{ &uvmexp.anonminpct, &last_uvmexp.anonminpct, "anonminpct",
	  &uvmexp.pdswout, &last_uvmexp.pdswout, "pdswout",
	  &uvmexp.flt_przero, &last_uvmexp.flt_przero, "flt_przero" },
	{ &uvmexp.vtextminpct, &last_uvmexp.vtextminpct, "vtextminpct",
	  &uvmexp.swpgonly, &last_uvmexp.swpgonly, "swpgonly",
	  NULL, NULL, NULL },
	{ &uvmexp.vnodeminpct, &last_uvmexp.vnodeminpct, "vnodeminpct",
	  &uvmexp.pdfreed, &last_uvmexp.pdfreed, "pdfreed",
	  NULL, NULL, "Swap Counters" },
	{ NULL, NULL, NULL,
	  &uvmexp.pdscans, &last_uvmexp.pdscans, "pdscans",
	  &uvmexp.nswapdev, &last_uvmexp.nswapdev, "nswapdev" },
	{ NULL, NULL, "Misc Counters",
	  &uvmexp.pdanscan, &last_uvmexp.pdanscan, "pdanscan",
	  &uvmexp.swpages, &last_uvmexp.swpages, "swpages" },
	{ &uvmexp.fpswtch, &last_uvmexp.fpswtch, "fpswtch",
	  &uvmexp.pdobscan, &last_uvmexp.pdobscan, "pdobscan",
	  &uvmexp.swpginuse, &last_uvmexp.swpginuse, "swpginuse" },
	{ &uvmexp.kmapent, &last_uvmexp.kmapent, "kmapent",
	  &uvmexp.pdreact, &last_uvmexp.pdreact, "pdreact",
	  &uvmexp.swpgonly, &last_uvmexp.swpgonly, "swpgonly" },
	{ NULL, NULL, NULL,
	  &uvmexp.pdbusy, &last_uvmexp.pdbusy, "pdbusy",
	  &uvmexp.nswget, &last_uvmexp.nswget, "nswget" },
	{ NULL, NULL, "Constants",
	  &uvmexp.pdpageouts, &last_uvmexp.pdpageouts, "pdpageouts",
	  NULL, NULL, NULL },
	{ &uvmexp.pagesize, &last_uvmexp.pagesize, "pagesize",
	  &uvmexp.pdpending, &last_uvmexp.pdpending, "pdpending",
	  NULL, NULL, "Per-CPU Counters" },
	{ &uvmexp.pagemask, &last_uvmexp.pagemask, "pagemask",
	  &uvmexp.pddeact, &last_uvmexp.pddeact, "pddeact",
	  &uvmexp.pcphit, &last_uvmexp.pcphit, "pcphit" },
	{ &uvmexp.pageshift, &last_uvmexp.pageshift, "pageshift",
	  NULL, NULL, NULL,
	  &uvmexp.pcpmiss, &last_uvmexp.pcpmiss, "pcpmiss" }
};

field_def fields_uvm[] = {
	{"",	 5,10,1, FLD_ALIGN_RIGHT,	-1,0,0,0 },
	{"",	18,19,1, FLD_ALIGN_LEFT,	-1,0,0,0 },
	{"",	 5,10,1, FLD_ALIGN_RIGHT,	-1,0,0,0 },
	{"",	18,19,1, FLD_ALIGN_LEFT,	-1,0,0,0 },
	{"",	 5,10,1, FLD_ALIGN_RIGHT,	-1,0,0,0 },
	{"",	18,19,1, FLD_ALIGN_LEFT,	-1,0,0,0 },
};

#define	FLD_VALUE1		FIELD_ADDR(fields_uvm,  0)
#define	FLD_NAME1		FIELD_ADDR(fields_uvm,  1)
#define	FLD_VALUE2		FIELD_ADDR(fields_uvm,  2)
#define	FLD_NAME2		FIELD_ADDR(fields_uvm,  3)
#define	FLD_VALUE3		FIELD_ADDR(fields_uvm,  4)
#define	FLD_NAME3		FIELD_ADDR(fields_uvm,  5)

/* Define views */
field_def *view_uvm_0[] = {
	FLD_VALUE1, FLD_NAME1,
	FLD_VALUE2, FLD_NAME2,
	FLD_VALUE3, FLD_NAME3,
	NULL
};

/* Define view managers */
struct view_manager uvm_mgr = {
	"UVM", select_uvm, read_uvm, NULL, print_header,
	print_uvm, keyboard_callback, NULL, NULL
};

field_view uvm_view = {
	view_uvm_0,
	"uvm",
	'5',
	&uvm_mgr
};

int
select_uvm(void)
{
	return (0);
}

int
read_uvm(void)
{
	static int uvmexp_mib[2] = { CTL_VM, VM_UVMEXP };
	size_t size;

	num_disp = nitems(uvmline);
	memcpy(&last_uvmexp, &uvmexp, sizeof(uvmexp));

	size = sizeof(uvmexp);
	if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) == -1) {
		error("Can't get VM_UVMEXP: %s\n", strerror(errno));
		memset(&uvmexp, 0, sizeof(uvmexp));
	}

	return 0;
}

void
print_uvmexp_field(field_def *fvalue, field_def *fname, int *new, int *old,
    const char *name)
{
	char *uppername;
	size_t len, i;

	if (new == NULL && name == NULL)
		return;

	if (new == NULL) {
		print_fld_str(fvalue, "=====");
		print_fld_str(fname, name);
		return;
	}

	if (*new != 0)
		print_fld_ssize(fvalue, *new);
	if (*new == *old) {
		print_fld_str(fname, name);
		return;
	}
	len = strlen(name);
	uppername = malloc(len + 1);
	if (uppername == NULL)
		err(1, "malloc");
	for (i = 0; i < len; i++)
		uppername[i] = toupper(name[i]);
	uppername[len] = '\0';
	print_fld_str(fname, uppername);
	free(uppername);
}

void
print_uvm(void)
{
	struct uvmline *l;
	int i, maxline;

	maxline = nitems(uvmline);
	if (maxline > (dispstart + maxprint))
		maxline = dispstart + maxprint;

	for (i = dispstart; i < nitems(uvmline); i++) {
		l = &uvmline[i];
		print_uvmexp_field(FLD_VALUE1, FLD_NAME1, l->v1, l->ov1, l->n1);
		print_uvmexp_field(FLD_VALUE2, FLD_NAME2, l->v2, l->ov2, l->n2);
		print_uvmexp_field(FLD_VALUE3, FLD_NAME3, l->v3, l->ov3, l->n3);
		end_line();
	}
}

int
inituvm(void)
{
	add_view(&uvm_view);
	read_uvm();

	return(0);
}