git: 93e81baa1c1b - main - vmm: Move duplicated stats code into a generic file

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 26 Aug 2024 18:58:30 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=93e81baa1c1baccf4eca9e3dbfe5ac9378e9623a

commit 93e81baa1c1baccf4eca9e3dbfe5ac9378e9623a
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2024-08-26 18:41:14 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2024-08-26 18:41:14 +0000

    vmm: Move duplicated stats code into a generic file
    
    There is a small difference between the arm64 and amd64 implementations:
    the latter makes use of a "scope" to exclude AMD-specific stats on Intel
    systems and vice-versa.  Replace this with a more generic predicate
    callback which can be used for the same purpose.
    
    No functional change intended.
    
    Reviewed by:    corvink, jhb
    Differential Revision:  https://reviews.freebsd.org/D46430
---
 sys/amd64/vmm/vmm.c               |  23 +++++
 sys/amd64/vmm/vmm_stat.c          | 177 --------------------------------------
 sys/amd64/vmm/vmm_stat.h          | 109 ++---------------------
 sys/arm64/vmm/vmm.c               |  19 +++-
 sys/arm64/vmm/vmm_stat.h          |  98 +--------------------
 sys/conf/files.arm64              |   2 +-
 sys/{arm64 => dev}/vmm/vmm_stat.c |  26 ++----
 sys/dev/vmm/vmm_stat.h            | 135 +++++++++++++++++++++++++++++
 sys/modules/vmm/Makefile          |   7 +-
 9 files changed, 196 insertions(+), 400 deletions(-)

diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c
index b0465ea84fcb..d0259b3b125f 100644
--- a/sys/amd64/vmm/vmm.c
+++ b/sys/amd64/vmm/vmm.c
@@ -297,6 +297,29 @@ static void vm_free_memmap(struct vm *vm, int ident);
 static bool sysmem_mapping(struct vm *vm, struct mem_map *mm);
 static void vcpu_notify_event_locked(struct vcpu *vcpu, bool lapic_intr);
 
+/* global statistics */
+VMM_STAT(VCPU_MIGRATIONS, "vcpu migration across host cpus");
+VMM_STAT(VMEXIT_COUNT, "total number of vm exits");
+VMM_STAT(VMEXIT_EXTINT, "vm exits due to external interrupt");
+VMM_STAT(VMEXIT_HLT, "number of times hlt was intercepted");
+VMM_STAT(VMEXIT_CR_ACCESS, "number of times %cr access was intercepted");
+VMM_STAT(VMEXIT_RDMSR, "number of times rdmsr was intercepted");
+VMM_STAT(VMEXIT_WRMSR, "number of times wrmsr was intercepted");
+VMM_STAT(VMEXIT_MTRAP, "number of monitor trap exits");
+VMM_STAT(VMEXIT_PAUSE, "number of times pause was intercepted");
+VMM_STAT(VMEXIT_INTR_WINDOW, "vm exits due to interrupt window opening");
+VMM_STAT(VMEXIT_NMI_WINDOW, "vm exits due to nmi window opening");
+VMM_STAT(VMEXIT_INOUT, "number of times in/out was intercepted");
+VMM_STAT(VMEXIT_CPUID, "number of times cpuid was intercepted");
+VMM_STAT(VMEXIT_NESTED_FAULT, "vm exits due to nested page fault");
+VMM_STAT(VMEXIT_INST_EMUL, "vm exits for instruction emulation");
+VMM_STAT(VMEXIT_UNKNOWN, "number of vm exits for unknown reason");
+VMM_STAT(VMEXIT_ASTPENDING, "number of times astpending at exit");
+VMM_STAT(VMEXIT_REQIDLE, "number of times idle requested at exit");
+VMM_STAT(VMEXIT_USERSPACE, "number of vm exits handled in userspace");
+VMM_STAT(VMEXIT_RENDEZVOUS, "number of times rendezvous pending at exit");
+VMM_STAT(VMEXIT_EXCEPTION, "number of vm exits due to exceptions");
+
 /*
  * Upper limit on vm_maxcpu.  Limited by use of uint16_t types for CPU
  * counts as well as range of vpid values for VT-x and by the capacity
diff --git a/sys/amd64/vmm/vmm_stat.c b/sys/amd64/vmm/vmm_stat.c
deleted file mode 100644
index eb394418b4a9..000000000000
--- a/sys/amd64/vmm/vmm_stat.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2011 NetApp, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/param.h>
-#include <sys/kernel.h>
-#include <sys/systm.h>
-#include <sys/malloc.h>
-
-#include <machine/vmm.h>
-#include "vmm_util.h"
-#include "vmm_stat.h"
-
-/*
- * 'vst_num_elems' is the total number of addressable statistic elements
- * 'vst_num_types' is the number of unique statistic types
- *
- * It is always true that 'vst_num_elems' is greater than or equal to
- * 'vst_num_types'. This is because a stat type may represent more than
- * one element (for e.g. VMM_STAT_ARRAY).
- */
-static int vst_num_elems, vst_num_types;
-static struct vmm_stat_type *vsttab[MAX_VMM_STAT_ELEMS];
-
-static MALLOC_DEFINE(M_VMM_STAT, "vmm stat", "vmm stat");
-
-#define	vst_size	((size_t)vst_num_elems * sizeof(uint64_t))
-
-void
-vmm_stat_register(void *arg)
-{
-	struct vmm_stat_type *vst = arg;
-
-	/* We require all stats to identify themselves with a description */
-	if (vst->desc == NULL)
-		return;
-
-	if (vst->scope == VMM_STAT_SCOPE_INTEL && !vmm_is_intel())
-		return;
-
-	if (vst->scope == VMM_STAT_SCOPE_AMD && !vmm_is_svm())
-		return;
-
-	if (vst_num_elems + vst->nelems >= MAX_VMM_STAT_ELEMS) {
-		printf("Cannot accommodate vmm stat type \"%s\"!\n", vst->desc);
-		return;
-	}
-
-	vst->index = vst_num_elems;
-	vst_num_elems += vst->nelems;
-
-	vsttab[vst_num_types++] = vst;
-}
-
-int
-vmm_stat_copy(struct vcpu *vcpu, int index, int count, int *num_stats,
-    uint64_t *buf)
-{
-	struct vmm_stat_type *vst;
-	uint64_t *stats;
-	int i, tocopy;
-
-	if (index < 0 || count < 0)
-		return (EINVAL);
-
-	if (index > vst_num_elems)
-		return (ENOENT);
-
-	if (index == vst_num_elems) {
-		*num_stats = 0;
-		return (0);
-	}
-
-	tocopy = min(vst_num_elems - index, count);
-
-	/* Let stats functions update their counters */
-	for (i = 0; i < vst_num_types; i++) {
-		vst = vsttab[i];
-		if (vst->func != NULL)
-			(*vst->func)(vcpu, vst);
-	}
-
-	/* Copy over the stats */
-	stats = vcpu_stats(vcpu);
-	memcpy(buf, stats + index, tocopy * sizeof(stats[0]));
-	*num_stats = tocopy;
-	return (0);
-}
-
-void *
-vmm_stat_alloc(void)
-{
-
-	return (malloc(vst_size, M_VMM_STAT, M_WAITOK));
-}
-
-void
-vmm_stat_init(void *vp)
-{
-
-	bzero(vp, vst_size);
-}
-
-void
-vmm_stat_free(void *vp)
-{
-	free(vp, M_VMM_STAT);
-}
-
-int
-vmm_stat_desc_copy(int index, char *buf, int bufsize)
-{
-	int i;
-	struct vmm_stat_type *vst;
-
-	for (i = 0; i < vst_num_types; i++) {
-		vst = vsttab[i];
-		if (index >= vst->index && index < vst->index + vst->nelems) {
-			if (vst->nelems > 1) {
-				snprintf(buf, bufsize, "%s[%d]",
-					 vst->desc, index - vst->index);
-			} else {
-				strlcpy(buf, vst->desc, bufsize);
-			}
-			return (0);	/* found it */
-		}
-	}
-
-	return (EINVAL);
-}
-
-/* global statistics */
-VMM_STAT(VCPU_MIGRATIONS, "vcpu migration across host cpus");
-VMM_STAT(VMEXIT_COUNT, "total number of vm exits");
-VMM_STAT(VMEXIT_EXTINT, "vm exits due to external interrupt");
-VMM_STAT(VMEXIT_HLT, "number of times hlt was intercepted");
-VMM_STAT(VMEXIT_CR_ACCESS, "number of times %cr access was intercepted");
-VMM_STAT(VMEXIT_RDMSR, "number of times rdmsr was intercepted");
-VMM_STAT(VMEXIT_WRMSR, "number of times wrmsr was intercepted");
-VMM_STAT(VMEXIT_MTRAP, "number of monitor trap exits");
-VMM_STAT(VMEXIT_PAUSE, "number of times pause was intercepted");
-VMM_STAT(VMEXIT_INTR_WINDOW, "vm exits due to interrupt window opening");
-VMM_STAT(VMEXIT_NMI_WINDOW, "vm exits due to nmi window opening");
-VMM_STAT(VMEXIT_INOUT, "number of times in/out was intercepted");
-VMM_STAT(VMEXIT_CPUID, "number of times cpuid was intercepted");
-VMM_STAT(VMEXIT_NESTED_FAULT, "vm exits due to nested page fault");
-VMM_STAT(VMEXIT_INST_EMUL, "vm exits for instruction emulation");
-VMM_STAT(VMEXIT_UNKNOWN, "number of vm exits for unknown reason");
-VMM_STAT(VMEXIT_ASTPENDING, "number of times astpending at exit");
-VMM_STAT(VMEXIT_REQIDLE, "number of times idle requested at exit");
-VMM_STAT(VMEXIT_USERSPACE, "number of vm exits handled in userspace");
-VMM_STAT(VMEXIT_RENDEZVOUS, "number of times rendezvous pending at exit");
-VMM_STAT(VMEXIT_EXCEPTION, "number of vm exits due to exceptions");
diff --git a/sys/amd64/vmm/vmm_stat.h b/sys/amd64/vmm/vmm_stat.h
index 7d3a4044dcce..cf3895001528 100644
--- a/sys/amd64/vmm/vmm_stat.h
+++ b/sys/amd64/vmm/vmm_stat.h
@@ -32,108 +32,9 @@
 #ifndef _VMM_STAT_H_
 #define	_VMM_STAT_H_
 
-struct vm;
+#include <dev/vmm/vmm_stat.h>
 
-#define	MAX_VMM_STAT_ELEMS	64		/* arbitrary */
-
-enum vmm_stat_scope {
-	VMM_STAT_SCOPE_ANY,
-	VMM_STAT_SCOPE_INTEL,		/* Intel VMX specific statistic */
-	VMM_STAT_SCOPE_AMD,		/* AMD SVM specific statistic */
-};
-
-struct vmm_stat_type;
-typedef void (*vmm_stat_func_t)(struct vcpu *vcpu,
-    struct vmm_stat_type *stat);
-
-struct vmm_stat_type {
-	int	index;			/* position in the stats buffer */
-	int	nelems;			/* standalone or array */
-	const char *desc;		/* description of statistic */
-	vmm_stat_func_t func;
-	enum vmm_stat_scope scope;
-};
-
-void	vmm_stat_register(void *arg);
-
-#define	VMM_STAT_FDEFINE(type, nelems, desc, func, scope)		\
-	struct vmm_stat_type type[1] = {				\
-		{ -1, nelems, desc, func, scope }			\
-	};								\
-	SYSINIT(type##_stat, SI_SUB_KLD, SI_ORDER_ANY, vmm_stat_register, type)
-
-#define VMM_STAT_DEFINE(type, nelems, desc, scope) 			\
-	VMM_STAT_FDEFINE(type, nelems, desc, NULL, scope)
-
-#define	VMM_STAT_DECLARE(type)						\
-	extern struct vmm_stat_type type[1]
-
-#define	VMM_STAT(type, desc)		\
-	VMM_STAT_DEFINE(type, 1, desc, VMM_STAT_SCOPE_ANY)
-#define	VMM_STAT_INTEL(type, desc)	\
-	VMM_STAT_DEFINE(type, 1, desc, VMM_STAT_SCOPE_INTEL)
-#define	VMM_STAT_AMD(type, desc)	\
-	VMM_STAT_DEFINE(type, 1, desc, VMM_STAT_SCOPE_AMD)
-
-#define	VMM_STAT_FUNC(type, desc, func)	\
-	VMM_STAT_FDEFINE(type, 1, desc, func, VMM_STAT_SCOPE_ANY)
-
-#define	VMM_STAT_ARRAY(type, nelems, desc)	\
-	VMM_STAT_DEFINE(type, nelems, desc, VMM_STAT_SCOPE_ANY)
-
-void	*vmm_stat_alloc(void);
-void	vmm_stat_init(void *vp);
-void 	vmm_stat_free(void *vp);
-
-int	vmm_stat_copy(struct vcpu *vcpu, int index, int count,
-	    int *num_stats, uint64_t *buf);
-int	vmm_stat_desc_copy(int index, char *buf, int buflen);
-
-static void __inline
-vmm_stat_array_incr(struct vcpu *vcpu, struct vmm_stat_type *vst, int statidx,
-    uint64_t x)
-{
-#ifdef VMM_KEEP_STATS
-	uint64_t *stats;
-
-	stats = vcpu_stats(vcpu);
-
-	if (vst->index >= 0 && statidx < vst->nelems)
-		stats[vst->index + statidx] += x;
-#endif
-}
-
-static void __inline
-vmm_stat_array_set(struct vcpu *vcpu, struct vmm_stat_type *vst, int statidx,
-    uint64_t val)
-{
-#ifdef VMM_KEEP_STATS
-	uint64_t *stats;
-
-	stats = vcpu_stats(vcpu);
-
-	if (vst->index >= 0 && statidx < vst->nelems)
-		stats[vst->index + statidx] = val;
-#endif
-}
-		   
-static void __inline
-vmm_stat_incr(struct vcpu *vcpu, struct vmm_stat_type *vst, uint64_t x)
-{
-
-#ifdef VMM_KEEP_STATS
-	vmm_stat_array_incr(vcpu, vst, 0, x);
-#endif
-}
-
-static void __inline
-vmm_stat_set(struct vcpu *vcpu, struct vmm_stat_type *vst, uint64_t val)
-{
-
-#ifdef VMM_KEEP_STATS
-	vmm_stat_array_set(vcpu, vst, 0, val);
-#endif
-}
+#include "vmm_util.h"
 
 VMM_STAT_DECLARE(VCPU_MIGRATIONS);
 VMM_STAT_DECLARE(VMEXIT_COUNT);
@@ -156,4 +57,10 @@ VMM_STAT_DECLARE(VMEXIT_USERSPACE);
 VMM_STAT_DECLARE(VMEXIT_RENDEZVOUS);
 VMM_STAT_DECLARE(VMEXIT_EXCEPTION);
 VMM_STAT_DECLARE(VMEXIT_REQIDLE);
+
+#define	VMM_STAT_INTEL(type, desc)	\
+	VMM_STAT_DEFINE(type, 1, desc, vmm_is_intel)
+#define	VMM_STAT_AMD(type, desc)	\
+	VMM_STAT_DEFINE(type, 1, desc, vmm_is_svm)
+
 #endif
diff --git a/sys/arm64/vmm/vmm.c b/sys/arm64/vmm/vmm.c
index f5173c4152bd..425e44f81500 100644
--- a/sys/arm64/vmm/vmm.c
+++ b/sys/arm64/vmm/vmm.c
@@ -65,8 +65,8 @@
 
 #include <dev/pci/pcireg.h>
 #include <dev/vmm/vmm_ktr.h>
+#include <dev/vmm/vmm_stat.h>
 
-#include "vmm_stat.h"
 #include "arm64.h"
 #include "mmu.h"
 
@@ -239,6 +239,23 @@ static void vm_free_memmap(struct vm *vm, int ident);
 static bool sysmem_mapping(struct vm *vm, struct mem_map *mm);
 static void vcpu_notify_event_locked(struct vcpu *vcpu);
 
+/* global statistics */
+VMM_STAT(VMEXIT_COUNT, "total number of vm exits");
+VMM_STAT(VMEXIT_UNKNOWN, "number of vmexits for the unknown exception");
+VMM_STAT(VMEXIT_WFI, "number of times wfi was intercepted");
+VMM_STAT(VMEXIT_WFE, "number of times wfe was intercepted");
+VMM_STAT(VMEXIT_HVC, "number of times hvc was intercepted");
+VMM_STAT(VMEXIT_MSR, "number of times msr/mrs was intercepted");
+VMM_STAT(VMEXIT_DATA_ABORT, "number of vmexits for a data abort");
+VMM_STAT(VMEXIT_INSN_ABORT, "number of vmexits for an instruction abort");
+VMM_STAT(VMEXIT_UNHANDLED_SYNC, "number of vmexits for an unhandled synchronous exception");
+VMM_STAT(VMEXIT_IRQ, "number of vmexits for an irq");
+VMM_STAT(VMEXIT_FIQ, "number of vmexits for an interrupt");
+VMM_STAT(VMEXIT_BRK, "number of vmexits for a breakpoint exception");
+VMM_STAT(VMEXIT_SS, "number of vmexits for a single-step exception");
+VMM_STAT(VMEXIT_UNHANDLED_EL2, "number of vmexits for an unhandled EL2 exception");
+VMM_STAT(VMEXIT_UNHANDLED, "number of vmexits for an unhandled exception");
+
 /*
  * Upper limit on vm_maxcpu. We could increase this to 28 bits, but this
  * is a safe value for now.
diff --git a/sys/arm64/vmm/vmm_stat.h b/sys/arm64/vmm/vmm_stat.h
index 402fa2f1b1e4..0dc3eeced603 100644
--- a/sys/arm64/vmm/vmm_stat.h
+++ b/sys/arm64/vmm/vmm_stat.h
@@ -32,102 +32,7 @@
 #ifndef _VMM_STAT_H_
 #define	_VMM_STAT_H_
 
-struct vm;
-
-#define	MAX_VMM_STAT_ELEMS	64		/* arbitrary */
-
-enum vmm_stat_scope {
-	VMM_STAT_SCOPE_ANY,
-};
-
-struct vmm_stat_type;
-typedef void (*vmm_stat_func_t)(struct vcpu *vcpu,
-    struct vmm_stat_type *stat);
-
-struct vmm_stat_type {
-	int	index;			/* position in the stats buffer */
-	int	nelems;			/* standalone or array */
-	const char *desc;		/* description of statistic */
-	vmm_stat_func_t func;
-	enum vmm_stat_scope scope;
-};
-
-void	vmm_stat_register(void *arg);
-
-#define	VMM_STAT_FDEFINE(type, nelems, desc, func, scope)		\
-	struct vmm_stat_type type[1] = {				\
-		{ -1, nelems, desc, func, scope }			\
-	};								\
-	SYSINIT(type##_stat, SI_SUB_KLD, SI_ORDER_ANY, vmm_stat_register, type)
-
-#define VMM_STAT_DEFINE(type, nelems, desc, scope) 			\
-	VMM_STAT_FDEFINE(type, nelems, desc, NULL, scope)
-
-#define	VMM_STAT_DECLARE(type)						\
-	extern struct vmm_stat_type type[1]
-
-#define	VMM_STAT(type, desc)		\
-	VMM_STAT_DEFINE(type, 1, desc, VMM_STAT_SCOPE_ANY)
-
-#define	VMM_STAT_FUNC(type, desc, func)	\
-	VMM_STAT_FDEFINE(type, 1, desc, func, VMM_STAT_SCOPE_ANY)
-
-#define	VMM_STAT_ARRAY(type, nelems, desc)	\
-	VMM_STAT_DEFINE(type, nelems, desc, VMM_STAT_SCOPE_ANY)
-
-void	*vmm_stat_alloc(void);
-void	vmm_stat_init(void *vp);
-void 	vmm_stat_free(void *vp);
-
-int	vmm_stat_copy(struct vcpu *vcpu, int index, int count,
-	    int *num_stats, uint64_t *buf);
-int	vmm_stat_desc_copy(int index, char *buf, int buflen);
-
-static void __inline
-vmm_stat_array_incr(struct vcpu *vcpu, struct vmm_stat_type *vst, int statidx,
-    uint64_t x)
-{
-#ifdef VMM_KEEP_STATS
-	uint64_t *stats;
-
-	stats = vcpu_stats(vcpu);
-
-	if (vst->index >= 0 && statidx < vst->nelems)
-		stats[vst->index + statidx] += x;
-#endif
-}
-
-static void __inline
-vmm_stat_array_set(struct vcpu *vcpu, struct vmm_stat_type *vst, int statidx,
-    uint64_t val)
-{
-#ifdef VMM_KEEP_STATS
-	uint64_t *stats;
-
-	stats = vcpu_stats(vcpu);
-
-	if (vst->index >= 0 && statidx < vst->nelems)
-		stats[vst->index + statidx] = val;
-#endif
-}
-
-static void __inline
-vmm_stat_incr(struct vcpu *vcpu, struct vmm_stat_type *vst, uint64_t x)
-{
-
-#ifdef VMM_KEEP_STATS
-	vmm_stat_array_incr(vcpu, vst, 0, x);
-#endif
-}
-
-static void __inline
-vmm_stat_set(struct vcpu *vcpu, struct vmm_stat_type *vst, uint64_t val)
-{
-
-#ifdef VMM_KEEP_STATS
-	vmm_stat_array_set(vcpu, vst, 0, val);
-#endif
-}
+#include <dev/vmm/vmm_stat.h>
 
 VMM_STAT_DECLARE(VMEXIT_COUNT);
 VMM_STAT_DECLARE(VMEXIT_UNKNOWN);
@@ -144,4 +49,5 @@ VMM_STAT_DECLARE(VMEXIT_BRK);
 VMM_STAT_DECLARE(VMEXIT_SS);
 VMM_STAT_DECLARE(VMEXIT_UNHANDLED_EL2);
 VMM_STAT_DECLARE(VMEXIT_UNHANDLED);
+
 #endif
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
index 1c863ff98998..3a8818340f75 100644
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -119,7 +119,6 @@ dev/iommu/iommu_gas.c				optional iommu
 arm64/vmm/vmm.c					optional vmm
 arm64/vmm/vmm_dev.c				optional vmm
 arm64/vmm/vmm_instruction_emul.c		optional vmm
-arm64/vmm/vmm_stat.c				optional vmm
 arm64/vmm/vmm_arm64.c				optional vmm
 arm64/vmm/vmm_reset.c				optional vmm
 arm64/vmm/vmm_handlers.c			optional vmm
@@ -151,6 +150,7 @@ arm64/vmm/io/vgic.c				optional vmm
 arm64/vmm/io/vgic_v3.c				optional vmm
 arm64/vmm/io/vgic_if.m				optional vmm
 arm64/vmm/io/vtimer.c				optional vmm
+dev/vmm/vmm_stat.c				optional vmm
 
 crypto/armv8/armv8_crypto.c			optional armv8crypto
 armv8_crypto_wrap.o				optional armv8crypto	\
diff --git a/sys/arm64/vmm/vmm_stat.c b/sys/dev/vmm/vmm_stat.c
similarity index 77%
rename from sys/arm64/vmm/vmm_stat.c
rename to sys/dev/vmm/vmm_stat.c
index 05ece6f30579..44edd6af85dd 100644
--- a/sys/arm64/vmm/vmm_stat.c
+++ b/sys/dev/vmm/vmm_stat.c
@@ -26,16 +26,14 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 
-#include <machine/machdep.h>
 #include <machine/vmm.h>
-#include "vmm_stat.h"
+
+#include <dev/vmm/vmm_stat.h>
 
 /*
  * 'vst_num_elems' is the total number of addressable statistic elements
@@ -61,6 +59,9 @@ vmm_stat_register(void *arg)
 	if (vst->desc == NULL)
 		return;
 
+	if (vst->pred != NULL && !vst->pred())
+		return;
+
 	if (vst_num_elems + vst->nelems >= MAX_VMM_STAT_ELEMS) {
 		printf("Cannot accommodate vmm stat type \"%s\"!\n", vst->desc);
 		return;
@@ -148,20 +149,3 @@ vmm_stat_desc_copy(int index, char *buf, int bufsize)
 
 	return (EINVAL);
 }
-
-/* global statistics */
-VMM_STAT(VMEXIT_COUNT, "total number of vm exits");
-VMM_STAT(VMEXIT_UNKNOWN, "number of vmexits for the unknown exception");
-VMM_STAT(VMEXIT_WFI, "number of times wfi was intercepted");
-VMM_STAT(VMEXIT_WFE, "number of times wfe was intercepted");
-VMM_STAT(VMEXIT_HVC, "number of times hvc was intercepted");
-VMM_STAT(VMEXIT_MSR, "number of times msr/mrs was intercepted");
-VMM_STAT(VMEXIT_DATA_ABORT, "number of vmexits for a data abort");
-VMM_STAT(VMEXIT_INSN_ABORT, "number of vmexits for an instruction abort");
-VMM_STAT(VMEXIT_UNHANDLED_SYNC, "number of vmexits for an unhandled synchronous exception");
-VMM_STAT(VMEXIT_IRQ, "number of vmexits for an irq");
-VMM_STAT(VMEXIT_FIQ, "number of vmexits for an interrupt");
-VMM_STAT(VMEXIT_BRK, "number of vmexits for a breakpoint exception");
-VMM_STAT(VMEXIT_SS, "number of vmexits for a single-step exception");
-VMM_STAT(VMEXIT_UNHANDLED_EL2, "number of vmexits for an unhandled EL2 exception");
-VMM_STAT(VMEXIT_UNHANDLED, "number of vmexits for an unhandled exception");
diff --git a/sys/dev/vmm/vmm_stat.h b/sys/dev/vmm/vmm_stat.h
new file mode 100644
index 000000000000..471afd0dd827
--- /dev/null
+++ b/sys/dev/vmm/vmm_stat.h
@@ -0,0 +1,135 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_VMM_STAT_H_
+#define	_DEV_VMM_STAT_H_
+
+struct vm;
+
+#define	MAX_VMM_STAT_ELEMS	64		/* arbitrary */
+
+struct vmm_stat_type;
+typedef void (*vmm_stat_func_t)(struct vcpu *vcpu,
+    struct vmm_stat_type *stat);
+typedef bool (*vmm_stat_func_pred_t)(void);
+
+struct vmm_stat_type {
+	int	index;			/* position in the stats buffer */
+	int	nelems;			/* standalone or array */
+	const char *desc;		/* description of statistic */
+	vmm_stat_func_t func;
+	vmm_stat_func_pred_t pred;	/* predicate to check during registration */
+};
+
+void	vmm_stat_register(void *arg);
+
+#define	VMM_STAT_FDEFINE(type, _nelems, _desc, _func, _pred)		\
+	struct vmm_stat_type type[1] = {				\
+		{							\
+			.index = -1,					\
+			.nelems = _nelems,				\
+			.desc = _desc,					\
+			.func = _func,					\
+			.pred = _pred,					\
+		}							\
+	};								\
+	SYSINIT(type##_stat, SI_SUB_KLD, SI_ORDER_ANY, vmm_stat_register, type)
+
+#define VMM_STAT_DEFINE(type, nelems, desc, pred) 			\
+	VMM_STAT_FDEFINE(type, nelems, desc, NULL, pred)
+
+#define	VMM_STAT_DECLARE(type)						\
+	extern struct vmm_stat_type type[1]
+
+#define	VMM_STAT(type, desc)		\
+	VMM_STAT_DEFINE(type, 1, desc, NULL)
+
+#define	VMM_STAT_FUNC(type, desc, func)	\
+	VMM_STAT_FDEFINE(type, 1, desc, func, NULL)
+
+#define	VMM_STAT_ARRAY(type, nelems, desc)	\
+	VMM_STAT_DEFINE(type, nelems, desc, NULL)
+
+void	*vmm_stat_alloc(void);
+void	vmm_stat_init(void *vp);
+void 	vmm_stat_free(void *vp);
+
+int	vmm_stat_copy(struct vcpu *vcpu, int index, int count,
+	    int *num_stats, uint64_t *buf);
+int	vmm_stat_desc_copy(int index, char *buf, int buflen);
+
+static void __inline
+vmm_stat_array_incr(struct vcpu *vcpu, struct vmm_stat_type *vst, int statidx,
+    uint64_t x)
+{
+#ifdef VMM_KEEP_STATS
+	uint64_t *stats;
+
+	stats = vcpu_stats(vcpu);
+
+	if (vst->index >= 0 && statidx < vst->nelems)
+		stats[vst->index + statidx] += x;
+#endif
+}
+
+static void __inline
+vmm_stat_array_set(struct vcpu *vcpu, struct vmm_stat_type *vst, int statidx,
+    uint64_t val)
+{
+#ifdef VMM_KEEP_STATS
+	uint64_t *stats;
+
+	stats = vcpu_stats(vcpu);
+
+	if (vst->index >= 0 && statidx < vst->nelems)
+		stats[vst->index + statidx] = val;
+#endif
+}
+
+static void __inline
+vmm_stat_incr(struct vcpu *vcpu, struct vmm_stat_type *vst, uint64_t x)
+{
+
+#ifdef VMM_KEEP_STATS
+	vmm_stat_array_incr(vcpu, vst, 0, x);
+#endif
+}
+
+static void __inline
+vmm_stat_set(struct vcpu *vcpu, struct vmm_stat_type *vst, uint64_t val)
+{
+
+#ifdef VMM_KEEP_STATS
+	vmm_stat_array_set(vcpu, vst, 0, val);
+#endif
+}
+
+#endif /* !_DEV_VMM_STAT_H_ */
diff --git a/sys/modules/vmm/Makefile b/sys/modules/vmm/Makefile
index 0604a34690e5..92986a364883 100644
--- a/sys/modules/vmm/Makefile
+++ b/sys/modules/vmm/Makefile
@@ -15,9 +15,10 @@ CFLAGS+= -I${SRCTOP}/sys/${MACHINE}/vmm
 CFLAGS+= -I${SRCTOP}/sys/${MACHINE}/vmm/io
 
 # generic vmm support
-.PATH: ${SRCTOP}/sys/${MACHINE}/vmm
-SRCS+=	vmm.c		\
-	vmm_dev.c	\
+.PATH: ${SRCTOP}/sys/dev/vmm ${SRCTOP}/sys/${MACHINE}/vmm
+
+SRCS+=	vmm.c			\
+	vmm_dev.c		\
 	vmm_instruction_emul.c	\
 	vmm_stat.c