diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c --- a/lib/libvmmapi/vmmapi.c +++ b/lib/libvmmapi/vmmapi.c @@ -1066,19 +1066,44 @@ 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 --- a/sys/amd64/include/vmm_dev.h +++ b/sys/amd64/include/vmm_dev.h @@ -174,6 +174,7 @@ #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 --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -69,6 +69,18 @@ #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 @@ 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 @@ 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.h b/sys/amd64/vmm/vmm_stat.h --- a/sys/amd64/vmm/vmm_stat.h +++ b/sys/amd64/vmm/vmm_stat.h @@ -87,10 +87,8 @@ 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 diff --git a/sys/amd64/vmm/vmm_stat.c b/sys/amd64/vmm/vmm_stat.c --- a/sys/amd64/vmm/vmm_stat.c +++ b/sys/amd64/vmm/vmm_stat.c @@ -82,15 +82,29 @@ } 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 @@ /* 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); }