99cbc7
From 8953e599b13320239ced8e2422f95a1286847d93 Mon Sep 17 00:00:00 2001
99cbc7
Message-Id: <8953e599b13320239ced8e2422f95a1286847d93@dist-git>
99cbc7
From: Jiri Denemark <jdenemar@redhat.com>
99cbc7
Date: Tue, 4 Jun 2019 13:04:29 +0200
99cbc7
Subject: [PATCH] util: Add virHostCPUGetTscInfo
99cbc7
MIME-Version: 1.0
99cbc7
Content-Type: text/plain; charset=UTF-8
99cbc7
Content-Transfer-Encoding: 8bit
99cbc7
99cbc7
On a KVM x86_64 host which supports invariant TSC this function can be
99cbc7
used to detect the TSC frequency and the availability of TSC scaling.
99cbc7
99cbc7
The magic MSR numbers required to check if VMX scaling is supported on
99cbc7
the host are documented in Volume 3 of the Intel® 64 and IA-32
99cbc7
Architectures Software Developer’s Manual.
99cbc7
99cbc7
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
99cbc7
(cherry picked from commit f0f6faba63becfab38c928905ac6ed79f9a318b8)
99cbc7
99cbc7
https://bugzilla.redhat.com/show_bug.cgi?id=1641702
99cbc7
99cbc7
Conflicts:
99cbc7
	src/util/virhostcpu.h
99cbc7
            - virenum.h doesn't exist downstream, the content is still
99cbc7
              in virutil.h
99cbc7
            - the comment after #endif changed upstream
99cbc7
99cbc7
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
99cbc7
Message-Id: <b44a5f67b95fde34eacabf86c47c1c278d4cf03f.1559646067.git.jdenemar@redhat.com>
99cbc7
Reviewed-by: Ján Tomko <jtomko@redhat.com>
99cbc7
---
99cbc7
 src/util/virhostcpu.c | 71 +++++++++++++++++++++++++++++++++++++++++++
99cbc7
 src/util/virhostcpu.h | 11 +++++++
99cbc7
 2 files changed, 82 insertions(+)
99cbc7
99cbc7
diff --git a/src/util/virhostcpu.c b/src/util/virhostcpu.c
99cbc7
index 1b6570568c..0f84780e09 100644
99cbc7
--- a/src/util/virhostcpu.c
99cbc7
+++ b/src/util/virhostcpu.c
99cbc7
@@ -1364,6 +1364,69 @@ virHostCPUGetMSR(unsigned long index,
99cbc7
     return virHostCPUGetMSRFromKVM(index, msr);
99cbc7
 }
99cbc7
 
99cbc7
+
99cbc7
+# define VMX_PROCBASED_CTLS2_MSR 0x48b
99cbc7
+# define VMX_USE_TSC_SCALING (1 << 25)
99cbc7
+
99cbc7
+/*
99cbc7
+ * This function should only be called when the host CPU supports invariant TSC
99cbc7
+ * (invtsc CPUID feature).
99cbc7
+ *
99cbc7
+ * Returns pointer to the TSC info structure on success,
99cbc7
+ *         NULL when TSC cannot be probed otherwise.
99cbc7
+ */
99cbc7
+virHostCPUTscInfoPtr
99cbc7
+virHostCPUGetTscInfo(void)
99cbc7
+{
99cbc7
+    virHostCPUTscInfoPtr info;
99cbc7
+    VIR_AUTOCLOSE kvmFd = -1;
99cbc7
+    VIR_AUTOCLOSE vmFd = -1;
99cbc7
+    VIR_AUTOCLOSE vcpuFd = -1;
99cbc7
+    uint64_t msr = 0;
99cbc7
+    int rc;
99cbc7
+
99cbc7
+    if ((kvmFd = open(KVM_DEVICE, O_RDONLY)) < 0) {
99cbc7
+        virReportSystemError(errno, _("Unable to open %s"), KVM_DEVICE);
99cbc7
+        return NULL;
99cbc7
+    }
99cbc7
+
99cbc7
+    if ((vmFd = ioctl(kvmFd, KVM_CREATE_VM, 0)) < 0) {
99cbc7
+        virReportSystemError(errno, "%s",
99cbc7
+                             _("Unable to create KVM VM for TSC probing"));
99cbc7
+        return NULL;
99cbc7
+    }
99cbc7
+
99cbc7
+    if ((vcpuFd = ioctl(vmFd, KVM_CREATE_VCPU, 0)) < 0) {
99cbc7
+        virReportSystemError(errno, "%s",
99cbc7
+                             _("Unable to create KVM vCPU for TSC probing"));
99cbc7
+        return NULL;
99cbc7
+    }
99cbc7
+
99cbc7
+    if ((rc = ioctl(vcpuFd, KVM_GET_TSC_KHZ)) < 0) {
99cbc7
+        virReportSystemError(errno, "%s",
99cbc7
+                             _("Unable to probe TSC frequency"));
99cbc7
+        return NULL;
99cbc7
+    }
99cbc7
+
99cbc7
+    if (VIR_ALLOC(info) < 0)
99cbc7
+        return NULL;
99cbc7
+
99cbc7
+    info->frequency = rc * 1000ULL;
99cbc7
+
99cbc7
+    if (virHostCPUGetMSR(VMX_PROCBASED_CTLS2_MSR, &msr) == 0) {
99cbc7
+        /* High 32 bits of the MSR value indicate whether specific control
99cbc7
+         * can be set to 1. */
99cbc7
+        msr >>= 32;
99cbc7
+
99cbc7
+        info->scaling = virTristateBoolFromBool(!!(msr & VMX_USE_TSC_SCALING));
99cbc7
+    }
99cbc7
+
99cbc7
+    VIR_DEBUG("Detected TSC frequency %llu Hz, scaling %s",
99cbc7
+              info->frequency, virTristateBoolTypeToString(info->scaling));
99cbc7
+
99cbc7
+    return info;
99cbc7
+}
99cbc7
+
99cbc7
 #else
99cbc7
 
99cbc7
 int
99cbc7
@@ -1375,6 +1438,14 @@ virHostCPUGetMSR(unsigned long index ATTRIBUTE_UNUSED,
99cbc7
     return -1;
99cbc7
 }
99cbc7
 
99cbc7
+virHostCPUTscInfoPtr
99cbc7
+virHostCPUGetTscInfo(void)
99cbc7
+{
99cbc7
+    virReportSystemError(ENOSYS, "%s",
99cbc7
+                         _("Probing TSC is not supported on this platform"));
99cbc7
+    return NULL;
99cbc7
+}
99cbc7
+
99cbc7
 #endif /* HAVE_LINUX_KVM_H && defined(KVM_GET_MSRS) && \
99cbc7
           (defined(__i386__) || defined(__x86_64__)) && \
99cbc7
           (defined(__linux__) || defined(__FreeBSD__)) */
99cbc7
diff --git a/src/util/virhostcpu.h b/src/util/virhostcpu.h
99cbc7
index e705623d4f..ee9d755c83 100644
99cbc7
--- a/src/util/virhostcpu.h
99cbc7
+++ b/src/util/virhostcpu.h
99cbc7
@@ -27,6 +27,15 @@
99cbc7
 # include "internal.h"
99cbc7
 # include "virarch.h"
99cbc7
 # include "virbitmap.h"
99cbc7
+# include "virutil.h"
99cbc7
+
99cbc7
+
99cbc7
+typedef struct _virHostCPUTscInfo virHostCPUTscInfo;
99cbc7
+typedef virHostCPUTscInfo *virHostCPUTscInfoPtr;
99cbc7
+struct _virHostCPUTscInfo {
99cbc7
+    unsigned long long frequency;
99cbc7
+    virTristateBool scaling;
99cbc7
+};
99cbc7
 
99cbc7
 
99cbc7
 int virHostCPUGetStats(int cpuNum,
99cbc7
@@ -71,4 +80,6 @@ unsigned int virHostCPUGetMicrocodeVersion(void);
99cbc7
 int virHostCPUGetMSR(unsigned long index,
99cbc7
                      uint64_t *msr);
99cbc7
 
99cbc7
+virHostCPUTscInfoPtr virHostCPUGetTscInfo(void);
99cbc7
+
99cbc7
 #endif /* __VIR_HOSTCPU_H__*/
99cbc7
-- 
99cbc7
2.21.0
99cbc7