diff --git a/src/util/virhostcpu.c b/src/util/virhostcpu.c index f4a62c74fa..8c00804b0e 100644 --- a/src/util/virhostcpu.c +++ b/src/util/virhostcpu.c @@ -1324,6 +1324,69 @@ virHostCPUGetMSR(unsigned long index, return virHostCPUGetMSRFromKVM(index, msr); } + +# define VMX_PROCBASED_CTLS2_MSR 0x48b +# define VMX_USE_TSC_SCALING (1 << 25) + +/* + * This function should only be called when the host CPU supports invariant TSC + * (invtsc CPUID feature). + * + * Returns pointer to the TSC info structure on success, + * NULL when TSC cannot be probed otherwise. + */ +virHostCPUTscInfoPtr +virHostCPUGetTscInfo(void) +{ + virHostCPUTscInfoPtr info; + VIR_AUTOCLOSE kvmFd = -1; + VIR_AUTOCLOSE vmFd = -1; + VIR_AUTOCLOSE vcpuFd = -1; + uint64_t msr = 0; + int rc; + + if ((kvmFd = open(KVM_DEVICE, O_RDONLY)) < 0) { + virReportSystemError(errno, _("Unable to open %s"), KVM_DEVICE); + return NULL; + } + + if ((vmFd = ioctl(kvmFd, KVM_CREATE_VM, 0)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to create KVM VM for TSC probing")); + return NULL; + } + + if ((vcpuFd = ioctl(vmFd, KVM_CREATE_VCPU, 0)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to create KVM vCPU for TSC probing")); + return NULL; + } + + if ((rc = ioctl(vcpuFd, KVM_GET_TSC_KHZ)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to probe TSC frequency")); + return NULL; + } + + if (VIR_ALLOC(info) < 0) + return NULL; + + info->frequency = rc * 1000ULL; + + if (virHostCPUGetMSR(VMX_PROCBASED_CTLS2_MSR, &msr) == 0) { + /* High 32 bits of the MSR value indicate whether specific control + * can be set to 1. */ + msr >>= 32; + + info->scaling = virTristateBoolFromBool(!!(msr & VMX_USE_TSC_SCALING)); + } + + VIR_DEBUG("Detected TSC frequency %llu Hz, scaling %s", + info->frequency, virTristateBoolTypeToString(info->scaling)); + + return info; +} + #else int @@ -1335,6 +1398,14 @@ virHostCPUGetMSR(unsigned long index ATTRIBUTE_UNUSED, return -1; } +virHostCPUTscInfoPtr +virHostCPUGetTscInfo(void) +{ + virReportSystemError(ENOSYS, "%s", + _("Probing TSC is not supported on this platform")); + return NULL; +} + #endif /* HAVE_LINUX_KVM_H && defined(KVM_GET_MSRS) && \ (defined(__i386__) || defined(__x86_64__)) && \ (defined(__linux__) || defined(__FreeBSD__)) */ diff --git a/src/util/virhostcpu.h b/src/util/virhostcpu.h index 0d20dbef61..b822bc11a8 100644 --- a/src/util/virhostcpu.h +++ b/src/util/virhostcpu.h @@ -25,6 +25,15 @@ # include "internal.h" # include "virarch.h" # include "virbitmap.h" +# include "virenum.h" + + +typedef struct _virHostCPUTscInfo virHostCPUTscInfo; +typedef virHostCPUTscInfo *virHostCPUTscInfoPtr; +struct _virHostCPUTscInfo { + unsigned long long frequency; + virTristateBool scaling; +}; int virHostCPUGetStats(int cpuNum, @@ -69,4 +78,6 @@ unsigned int virHostCPUGetMicrocodeVersion(void); int virHostCPUGetMSR(unsigned long index, uint64_t *msr); +virHostCPUTscInfoPtr virHostCPUGetTscInfo(void); + #endif /* LIBVIRT_VIRHOSTCPU_H */