git: 64269786170f - main - Extend the VMM stats interface to support a dynamic count of statistics.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Mon, 07 Feb 2022 22:14:06 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=64269786170ffd8e3348edea0fc5f5b09b79391e

commit 64269786170ffd8e3348edea0fc5f5b09b79391e
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-02-07 22:11:10 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-02-07 22:11:10 +0000

    Extend the VMM stats interface to support a dynamic count of statistics.
    
    - Add a starting index to 'struct vmstats' and change the
      VM_STATS ioctl to fetch the 64 stats starting at that index.
      A compat shim for <= 13 continues to fetch only the first 64
      stats.
    
    - Extend vm_get_stats() in libvmmapi to use a loop and a static
      thread local buffer which grows to hold the stats needed.
    
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D27463
---
 lib/libvmmapi/vmmapi.c      | 41 +++++++++++++++++++++++++++++++++--------
 sys/amd64/include/vmm_dev.h |  1 +
 sys/amd64/vmm/vmm_dev.c     | 29 +++++++++++++++++++++++++++--
 sys/amd64/vmm/vmm_stat.c    | 23 ++++++++++++++++++-----
 sys/amd64/vmm/vmm_stat.h    |  6 ++----
 5 files changed, 81 insertions(+), 19 deletions(-)

diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c
index a2d26b1b33b9..0291e4a10c33 100644
--- a/lib/libvmmapi/vmmapi.c
+++ b/lib/libvmmapi/vmmapi.c
@@ -1066,19 +1066,44 @@ uint64_t *
 vm_get_stats(struct vmctx *ctx, int vcpu, struct timeval *ret_tv,
 	     int *ret_entries)
 {
-	int error;
-
-	static struct vm_stats vmstats;
-
+	static _Thread_local uint64_t *stats_buf;
+	static _Thread_local u_int stats_count;
+	uint64_t *new_stats;
+	struct vm_stats vmstats;
+	u_int count, index;
+	bool have_stats;
+
+	have_stats = false;
 	vmstats.cpuid = vcpu;
+	count = 0;
+	for (index = 0;; index += nitems(vmstats.statbuf)) {
+		vmstats.index = index;
+		if (ioctl(ctx->fd, VM_STATS, &vmstats) != 0)
+			break;
+		if (stats_count < index + vmstats.num_entries) {
+			new_stats = realloc(stats_buf,
+			    (index + vmstats.num_entries) * sizeof(uint64_t));
+			if (new_stats == NULL) {
+				errno = ENOMEM;
+				return (NULL);
+			}
+			stats_count = index + vmstats.num_entries;
+			stats_buf = new_stats;
+		}
+		memcpy(stats_buf + index, vmstats.statbuf,
+		    vmstats.num_entries * sizeof(uint64_t));
+		count += vmstats.num_entries;
+		have_stats = true;
 
-	error = ioctl(ctx->fd, VM_STATS, &vmstats);
-	if (error == 0) {
+		if (vmstats.num_entries != nitems(vmstats.statbuf))
+			break;
+	}
+	if (have_stats) {
 		if (ret_entries)
-			*ret_entries = vmstats.num_entries;
+			*ret_entries = count;
 		if (ret_tv)
 			*ret_tv = vmstats.tv;
-		return (vmstats.statbuf);
+		return (stats_buf);
 	} else
 		return (NULL);
 }
diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h
index a048e05d4b7c..9ed8f32302ae 100644
--- a/sys/amd64/include/vmm_dev.h
+++ b/sys/amd64/include/vmm_dev.h
@@ -174,6 +174,7 @@ struct vm_nmi {
 #define	MAX_VM_STATS	64
 struct vm_stats {
 	int		cpuid;				/* in */
+	int		index;				/* in */
 	int		num_entries;			/* out */
 	struct timeval	tv;
 	uint64_t	statbuf[MAX_VM_STATS];
diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c
index 2ce9470cf6dd..a83c74219fee 100644
--- a/sys/amd64/vmm/vmm_dev.c
+++ b/sys/amd64/vmm/vmm_dev.c
@@ -69,6 +69,18 @@ __FBSDID("$FreeBSD$");
 #include "io/vhpet.h"
 #include "io/vrtc.h"
 
+#ifdef COMPAT_FREEBSD13
+struct vm_stats_old {
+	int		cpuid;				/* in */
+	int		num_entries;			/* out */
+	struct timeval	tv;
+	uint64_t	statbuf[MAX_VM_STATS];
+};
+
+#define	VM_STATS_OLD \
+	_IOWR('v', IOCNUM_VM_STATS, struct vm_stats_old)
+#endif
+
 struct devmem_softc {
 	int	segid;
 	char	*name;
@@ -376,6 +388,9 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
 	struct vm_pptdev_msi *pptmsi;
 	struct vm_pptdev_msix *pptmsix;
 	struct vm_nmi *vmnmi;
+#ifdef COMPAT_FREEBSD13
+	struct vm_stats_old *vmstats_old;
+#endif
 	struct vm_stats *vmstats;
 	struct vm_stat_desc *statdesc;
 	struct vm_x2apic *x2apic;
@@ -501,11 +516,21 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
 					statdesc->desc, sizeof(statdesc->desc));
 		break;
 	}
+#ifdef COMPAT_FREEBSD13
+	case VM_STATS_OLD:
+		vmstats_old = (struct vm_stats_old *)data;
+		getmicrotime(&vmstats_old->tv);
+		error = vmm_stat_copy(sc->vm, vmstats_old->cpuid, 0,
+				      nitems(vmstats_old->statbuf),
+				      &vmstats_old->num_entries,
+				      vmstats_old->statbuf);
+		break;
+#endif
 	case VM_STATS: {
-		CTASSERT(MAX_VM_STATS >= MAX_VMM_STAT_ELEMS);
 		vmstats = (struct vm_stats *)data;
 		getmicrotime(&vmstats->tv);
-		error = vmm_stat_copy(sc->vm, vmstats->cpuid,
+		error = vmm_stat_copy(sc->vm, vmstats->cpuid, vmstats->index,
+				      nitems(vmstats->statbuf),
 				      &vmstats->num_entries, vmstats->statbuf);
 		break;
 	}
diff --git a/sys/amd64/vmm/vmm_stat.c b/sys/amd64/vmm/vmm_stat.c
index 89133d4b3868..497db4452f3b 100644
--- a/sys/amd64/vmm/vmm_stat.c
+++ b/sys/amd64/vmm/vmm_stat.c
@@ -82,15 +82,29 @@ vmm_stat_register(void *arg)
 }
 
 int
-vmm_stat_copy(struct vm *vm, int vcpu, int *num_stats, uint64_t *buf)
+vmm_stat_copy(struct vm *vm, int vcpu, int index, int count, int *num_stats,
+    uint64_t *buf)
 {
 	struct vmm_stat_type *vst;
 	uint64_t *stats;
-	int i;
+	int i, tocopy;
 
 	if (vcpu < 0 || vcpu >= vm_get_maxcpus(vm))
 		return (EINVAL);
 
+	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];
@@ -100,9 +114,8 @@ vmm_stat_copy(struct vm *vm, int vcpu, int *num_stats, uint64_t *buf)
 
 	/* Copy over the stats */
 	stats = vcpu_stats(vm, vcpu);
-	for (i = 0; i < vst_num_elems; i++)
-		buf[i] = stats[i];
-	*num_stats = vst_num_elems;
+	memcpy(buf, stats + index, tocopy * sizeof(stats[0]));
+	*num_stats = tocopy;
 	return (0);
 }
 
diff --git a/sys/amd64/vmm/vmm_stat.h b/sys/amd64/vmm/vmm_stat.h
index f97743e77fe7..0e9c8db8429d 100644
--- a/sys/amd64/vmm/vmm_stat.h
+++ b/sys/amd64/vmm/vmm_stat.h
@@ -87,10 +87,8 @@ void	*vmm_stat_alloc(void);
 void	vmm_stat_init(void *vp);
 void 	vmm_stat_free(void *vp);
 
-/*
- * 'buf' should be at least fit 'MAX_VMM_STAT_TYPES' entries
- */
-int	vmm_stat_copy(struct vm *vm, int vcpu, int *num_stats, uint64_t *buf);
+int	vmm_stat_copy(struct vm *vm, int 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