hyperv: Add method to read 64bit Hyper-V specific time value.

MFC after:	1 week
Sponsored by:	Microsoft
Differential Revision:	https://reviews.freebsd.org/D9057
This commit is contained in:
sephe 2017-01-09 03:38:41 +00:00
parent acaa6ff02f
commit 5f62cf7ec4
5 changed files with 50 additions and 17 deletions

View File

@ -79,9 +79,17 @@ struct hyperv_guid {
#define HYPERV_GUID_STRLEN 40
int hyperv_guid2str(const struct hyperv_guid *, char *, size_t);
typedef uint64_t (*hyperv_tc64_t)(void);
extern u_int hyperv_features; /* CPUID_HV_MSR_ */
int hyperv_guid2str(const struct hyperv_guid *, char *,
size_t);
/*
* hyperv_tc64 could be NULL, if there were no suitable Hyper-V
* specific timecounter.
*/
extern hyperv_tc64_t hyperv_tc64;
extern u_int hyperv_features; /* CPUID_HV_MSR_ */
#endif /* _KERNEL */

View File

@ -52,8 +52,7 @@ __FBSDID("$FreeBSD$");
VMBUS_ICVER_LE(VMBUS_IC_VERSION(4, 0), (sc)->ic_msgver)
#define VMBUS_TIMESYNC_DORTT(sc) \
(VMBUS_TIMESYNC_MSGVER4((sc)) &&\
(hyperv_features & CPUID_HV_MSR_TIME_REFCNT))
(VMBUS_TIMESYNC_MSGVER4((sc)) && hyperv_tc64 != NULL)
static int vmbus_timesync_probe(device_t);
static int vmbus_timesync_attach(device_t);
@ -117,7 +116,7 @@ vmbus_timesync(struct vmbus_ic_softc *sc, uint64_t hvtime, uint64_t sent_tc,
uint64_t hv_ns, vm_ns, rtt = 0;
if (VMBUS_TIMESYNC_DORTT(sc))
rtt = rdmsr(MSR_HV_TIME_REF_COUNT) - sent_tc;
rtt = hyperv_tc64() - sent_tc;
hv_ns = (hvtime - VMBUS_ICMSG_TS_BASE + rtt) * HYPERV_TIMER_NS_FACTOR;
nanotime(&vm_ts);

View File

@ -133,8 +133,8 @@ hyperv_tsc_vdso_timehands(struct vdso_timehands *vdso_th,
}
#define HYPERV_TSC_TIMECOUNT(fence) \
static u_int \
hyperv_tsc_timecount_##fence(struct timecounter *tc) \
static uint64_t \
hyperv_tc64_tsc_##fence(void) \
{ \
struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref; \
uint32_t seq; \
@ -161,6 +161,13 @@ hyperv_tsc_timecount_##fence(struct timecounter *tc) \
} \
/* Fallback to the generic timecounter, i.e. rdmsr. */ \
return (rdmsr(MSR_HV_TIME_REF_COUNT)); \
} \
\
static u_int \
hyperv_tsc_timecount_##fence(struct timecounter *tc __unused) \
{ \
\
return (hyperv_tc64_tsc_##fence()); \
} \
struct __hack
@ -170,6 +177,7 @@ HYPERV_TSC_TIMECOUNT(mfence);
static void
hyperv_tsc_tcinit(void *dummy __unused)
{
hyperv_tc64_t tc64 = NULL;
uint64_t val, orig;
if ((hyperv_features &
@ -182,11 +190,13 @@ hyperv_tsc_tcinit(void *dummy __unused)
case CPU_VENDOR_AMD:
hyperv_tsc_timecounter.tc_get_timecount =
hyperv_tsc_timecount_mfence;
tc64 = hyperv_tc64_tsc_mfence;
break;
case CPU_VENDOR_INTEL:
hyperv_tsc_timecounter.tc_get_timecount =
hyperv_tsc_timecount_lfence;
tc64 = hyperv_tc64_tsc_lfence;
break;
default:
@ -211,6 +221,10 @@ hyperv_tsc_tcinit(void *dummy __unused)
/* Register "enlightened" timecounter. */
tc_init(&hyperv_tsc_timecounter);
/* Install 64 bits timecounter method for other modules to use. */
KASSERT(tc64 != NULL, ("tc64 is not set"));
hyperv_tc64 = tc64;
/* Add device for mmap(2). */
make_dev(&hyperv_tsc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0444,
HYPERV_REFTSC_DEVNAME);

View File

@ -77,6 +77,8 @@ u_int hyperv_recommends;
static u_int hyperv_pm_features;
static u_int hyperv_features3;
hyperv_tc64_t hyperv_tc64;
static struct timecounter hyperv_timecounter = {
.tc_get_timecount = hyperv_get_timecount,
.tc_poll_pps = NULL,
@ -96,6 +98,13 @@ hyperv_get_timecount(struct timecounter *tc __unused)
return rdmsr(MSR_HV_TIME_REF_COUNT);
}
static uint64_t
hyperv_tc64_rdmsr(void)
{
return (rdmsr(MSR_HV_TIME_REF_COUNT));
}
uint64_t
hypercall_post_message(bus_addr_t msg_paddr)
{
@ -232,6 +241,12 @@ hyperv_init(void *dummy __unused)
if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) {
/* Register Hyper-V timecounter */
tc_init(&hyperv_timecounter);
/*
* Install 64 bits timecounter method for other modules
* to use.
*/
hyperv_tc64 = hyperv_tc64_rdmsr;
}
}
SYSINIT(hyperv_initialize, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, hyperv_init,

View File

@ -48,13 +48,10 @@ __FBSDID("$FreeBSD$");
MSR_HV_STIMER_CFG_SINT_MASK)
/*
* Two additionally required features:
* Additionally required feature:
* - SynIC is needed for interrupt generation.
* - Time reference counter is needed to set ABS reference count to
* STIMER0_COUNT.
*/
#define CPUID_HV_ET_MASK (CPUID_HV_MSR_TIME_REFCNT | \
CPUID_HV_MSR_SYNIC | \
#define CPUID_HV_ET_MASK (CPUID_HV_MSR_SYNIC | \
CPUID_HV_MSR_SYNTIMER)
static void vmbus_et_identify(driver_t *, device_t);
@ -102,7 +99,7 @@ vmbus_et_start(struct eventtimer *et __unused, sbintime_t first,
{
uint64_t current;
current = rdmsr(MSR_HV_TIME_REF_COUNT);
current = hyperv_tc64();
current += hyperv_sbintime2count(first);
wrmsr(MSR_HV_STIMER0_COUNT, current);
@ -131,7 +128,8 @@ vmbus_et_identify(driver_t *driver, device_t parent)
{
if (device_get_unit(parent) != 0 ||
device_find_child(parent, VMBUS_ET_NAME, -1) != NULL ||
(hyperv_features & CPUID_HV_ET_MASK) != CPUID_HV_ET_MASK)
(hyperv_features & CPUID_HV_ET_MASK) != CPUID_HV_ET_MASK ||
hyperv_tc64 == NULL)
return;
device_add_child(parent, VMBUS_ET_NAME, -1);
@ -187,9 +185,8 @@ vmbus_et_attach(device_t dev)
vmbus_et.et_start = vmbus_et_start;
/*
* Delay a bit to make sure that MSR_HV_TIME_REF_COUNT will
* not return 0, since writing 0 to STIMER0_COUNT will disable
* STIMER0.
* Delay a bit to make sure that hyperv_tc64 will not return 0,
* since writing 0 to STIMER0_COUNT will disable STIMER0.
*/
DELAY(100);
smp_rendezvous(NULL, vmbus_et_config, NULL, NULL);