Move the ACPI PM timer emulation into vmm.ko.

This reduces variability during timer calibration by keeping the emulation
"close" to the guest. Additionally having all timer emulations in the kernel
will ease the transition to a per-VM clock source (as opposed to using the
host's uptime keep track of time).

Discussed with:	grehan
This commit is contained in:
Neel Natu 2014-10-26 04:44:28 +00:00
parent 31b117bec9
commit 160ef77abf
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=273683
8 changed files with 162 additions and 175 deletions

View File

@ -285,6 +285,7 @@ int vm_assign_pptdev(struct vm *vm, int bus, int slot, int func);
int vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func);
struct vatpic *vm_atpic(struct vm *vm);
struct vatpit *vm_atpit(struct vm *vm);
struct vpmtmr *vm_pmtmr(struct vm *vm);
/*
* Inject exception 'vme' into the guest vcpu. This function returns 0 on

104
sys/amd64/vmm/io/vpmtmr.c Normal file
View File

@ -0,0 +1,104 @@
/*-
* Copyright (c) 2014, Neel Natu (neel@freebsd.org)
* 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 unmodified, 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 THE AUTHOR ``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 AUTHOR 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/cpuset.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <machine/vmm.h>
#include "vpmtmr.h"
/*
* The ACPI Power Management timer is a free-running 24- or 32-bit
* timer with a frequency of 3.579545MHz
*
* This implementation will be 32-bits
*/
#define PMTMR_FREQ 3579545 /* 3.579545MHz */
struct vpmtmr {
sbintime_t freq_sbt;
sbintime_t baseuptime;
uint32_t baseval;
};
static MALLOC_DEFINE(M_VPMTMR, "vpmtmr", "bhyve virtual acpi timer");
struct vpmtmr *
vpmtmr_init(struct vm *vm)
{
struct vpmtmr *vpmtmr;
struct bintime bt;
vpmtmr = malloc(sizeof(struct vpmtmr), M_VPMTMR, M_WAITOK | M_ZERO);
vpmtmr->baseuptime = sbinuptime();
vpmtmr->baseval = 0;
FREQ2BT(PMTMR_FREQ, &bt);
vpmtmr->freq_sbt = bttosbt(bt);
return (vpmtmr);
}
void
vpmtmr_cleanup(struct vpmtmr *vpmtmr)
{
free(vpmtmr, M_VPMTMR);
}
int
vpmtmr_handler(void *vm, int vcpuid, bool in, int port, int bytes,
uint32_t *val)
{
struct vpmtmr *vpmtmr;
sbintime_t now, delta;
if (!in || bytes != 4)
return (-1);
vpmtmr = vm_pmtmr(vm);
/*
* No locking needed because 'baseuptime' and 'baseval' are
* written only during initialization.
*/
now = sbinuptime();
delta = now - vpmtmr->baseuptime;
KASSERT(delta >= 0, ("vpmtmr_handler: uptime went backwards: "
"%#lx to %#lx", vpmtmr->baseuptime, now));
*val = vpmtmr->baseval + delta / vpmtmr->freq_sbt;
return (0);
}

42
sys/amd64/vmm/io/vpmtmr.h Normal file
View File

@ -0,0 +1,42 @@
/*-
* Copyright (c) 2014 Neel Natu (neel@freebsd.org)
* 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 unmodified, 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 THE AUTHOR ``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 AUTHOR 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.
*
* $FreeBSD$
*/
#ifndef _VPMTMR_H_
#define _VPMTMR_H_
#define IO_PMTMR 0x408
struct vpmtmr;
struct vpmtmr *vpmtmr_init(struct vm *vm);
void vpmtmr_cleanup(struct vpmtmr *pmtmr);
int vpmtmr_handler(void *vm, int vcpuid, bool in, int port, int bytes,
uint32_t *val);
#endif

View File

@ -74,6 +74,7 @@ __FBSDID("$FreeBSD$");
#include "vhpet.h"
#include "vioapic.h"
#include "vlapic.h"
#include "vpmtmr.h"
#include "vmm_ipi.h"
#include "vmm_stat.h"
#include "vmm_lapic.h"
@ -134,6 +135,7 @@ struct vm {
struct vioapic *vioapic; /* (i) virtual ioapic */
struct vatpic *vatpic; /* (i) virtual atpic */
struct vatpit *vatpit; /* (i) virtual atpit */
struct vpmtmr *vpmtmr; /* (i) virtual ACPI PM timer */
volatile cpuset_t active_cpus; /* (i) active vcpus */
int suspend; /* (i) stop VM execution */
volatile cpuset_t suspended_cpus; /* (i) suspended vcpus */
@ -360,6 +362,7 @@ vm_init(struct vm *vm, bool create)
vm->vhpet = vhpet_init(vm);
vm->vatpic = vatpic_init(vm);
vm->vatpit = vatpit_init(vm);
vm->vpmtmr = vpmtmr_init(vm);
CPU_ZERO(&vm->active_cpus);
@ -422,6 +425,7 @@ vm_cleanup(struct vm *vm, bool destroy)
if (vm->iommu != NULL)
iommu_destroy_domain(vm->iommu);
vpmtmr_cleanup(vm->vpmtmr);
vatpit_cleanup(vm->vatpit);
vhpet_cleanup(vm->vhpet);
vatpic_cleanup(vm->vatpic);
@ -2199,6 +2203,13 @@ vm_atpit(struct vm *vm)
return (vm->vatpit);
}
struct vpmtmr *
vm_pmtmr(struct vm *vm)
{
return (vm->vpmtmr);
}
enum vm_reg_name
vm_segment_name(int seg)
{

View File

@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
#include "vatpic.h"
#include "vatpit.h"
#include "vpmtmr.h"
#include "vmm_ioport.h"
#include "vmm_ktr.h"
@ -58,6 +59,7 @@ ioport_handler_func_t ioport_handler[MAX_IOPORTS] = {
[IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler,
[IO_ELCR1] = vatpic_elc_handler,
[IO_ELCR2] = vatpic_elc_handler,
[IO_PMTMR] = vpmtmr_handler,
};
#ifdef KTR

View File

@ -33,7 +33,8 @@ SRCS+= iommu.c \
vatpit.c \
vhpet.c \
vioapic.c \
vlapic.c
vlapic.c \
vpmtmr.c
# intel-specific files
.PATH: ${.CURDIR}/../../amd64/vmm/intel

View File

@ -31,7 +31,6 @@ SRCS= \
pci_virtio_rnd.c \
pci_uart.c \
pm.c \
pmtmr.c \
post.c \
rtc.c \
smbiostbl.c \

View File

@ -1,173 +0,0 @@
/*-
* Copyright (c) 2012 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.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <machine/cpufunc.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <pthread.h>
#include "acpi.h"
#include "inout.h"
/*
* The ACPI Power Management timer is a free-running 24- or 32-bit
* timer with a frequency of 3.579545MHz
*
* This implementation will be 32-bits
*/
#define PMTMR_FREQ 3579545 /* 3.579545MHz */
static pthread_mutex_t pmtmr_mtx;
static pthread_once_t pmtmr_once = PTHREAD_ONCE_INIT;
static uint64_t pmtmr_old;
static uint64_t pmtmr_tscf;
static uint64_t pmtmr_tsc_old;
static clockid_t clockid = CLOCK_UPTIME_FAST;
static struct timespec pmtmr_uptime_old;
#define timespecsub(vvp, uvp) \
do { \
(vvp)->tv_sec -= (uvp)->tv_sec; \
(vvp)->tv_nsec -= (uvp)->tv_nsec; \
if ((vvp)->tv_nsec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_nsec += 1000000000; \
} \
} while (0)
static uint64_t
timespec_to_pmtmr(const struct timespec *tsnew, const struct timespec *tsold)
{
struct timespec tsdiff;
int64_t nsecs;
tsdiff = *tsnew;
timespecsub(&tsdiff, tsold);
nsecs = tsdiff.tv_sec * 1000000000 + tsdiff.tv_nsec;
assert(nsecs >= 0);
return (nsecs * PMTMR_FREQ / 1000000000 + pmtmr_old);
}
static uint64_t
tsc_to_pmtmr(uint64_t tsc_new, uint64_t tsc_old)
{
return ((tsc_new - tsc_old) * PMTMR_FREQ / pmtmr_tscf + pmtmr_old);
}
static void
pmtmr_init(void)
{
size_t len;
int smp_tsc, err;
struct timespec tsnew, tsold = { 0 };
len = sizeof(smp_tsc);
err = sysctlbyname("kern.timecounter.smp_tsc", &smp_tsc, &len, NULL, 0);
assert(err == 0);
if (smp_tsc) {
len = sizeof(pmtmr_tscf);
err = sysctlbyname("machdep.tsc_freq", &pmtmr_tscf, &len,
NULL, 0);
assert(err == 0);
pmtmr_tsc_old = rdtsc();
pmtmr_old = tsc_to_pmtmr(pmtmr_tsc_old, 0);
} else {
if (getenv("BHYVE_PMTMR_PRECISE") != NULL)
clockid = CLOCK_UPTIME;
err = clock_gettime(clockid, &tsnew);
assert(err == 0);
pmtmr_uptime_old = tsnew;
pmtmr_old = timespec_to_pmtmr(&tsnew, &tsold);
}
pthread_mutex_init(&pmtmr_mtx, NULL);
}
static uint32_t
pmtmr_val(void)
{
struct timespec tsnew;
uint64_t pmtmr_tsc_new;
uint64_t pmtmr_new;
int error;
pthread_once(&pmtmr_once, pmtmr_init);
pthread_mutex_lock(&pmtmr_mtx);
if (pmtmr_tscf) {
pmtmr_tsc_new = rdtsc();
pmtmr_new = tsc_to_pmtmr(pmtmr_tsc_new, pmtmr_tsc_old);
pmtmr_tsc_old = pmtmr_tsc_new;
} else {
error = clock_gettime(clockid, &tsnew);
assert(error == 0);
pmtmr_new = timespec_to_pmtmr(&tsnew, &pmtmr_uptime_old);
pmtmr_uptime_old = tsnew;
}
pmtmr_old = pmtmr_new;
pthread_mutex_unlock(&pmtmr_mtx);
return (pmtmr_new);
}
static int
pmtmr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
assert(in == 1);
if (bytes != 4)
return (-1);
*eax = pmtmr_val();
return (0);
}
INOUT_PORT(pmtmr, IO_PMTMR, IOPORT_F_IN, pmtmr_handler);