diff mbox series

[v3,2/2] target/i386: add "-cpu, lbr-fmt=*" support to enable guest LBR

Message ID 20210508055259.128025-2-like.xu@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series [v3,1/2] qdev-properties: Add a new macro to validate bitmask for setter | expand

Commit Message

Like Xu May 8, 2021, 5:52 a.m. UTC
The last branch recording (LBR) is a performance monitor unit (PMU)
feature on Intel processors that records a running trace of the most
recent branches taken by the processor in the LBR stack. The QEMU
could configure whether it's enabled or not for each guest via CLI.

The LBR feature would be enabled on the guest if:
- the KVM is enabled and the PMU is enabled and,
- the msr-based-feature IA32_PERF_CAPABILITIES is supporterd on KVM and,
- the supported returned value for lbr_fmt from this msr is not zero and,
- the requested guest vcpu model does support FEAT_1_ECX.CPUID_EXT_PDCM,
- the user-provided lbr-fmt value should not violate its bitmask (0x3f)
  and it should be the same as the host lbr_fmt value or just use the
  QEMU option "-cpu host,migratable=no" to enable guest LBR.

Signed-off-by: Like Xu <like.xu@linux.intel.com>
---
v2-v3 Changelog:
- Add a new generic property macro to validate its bitmask;
- Differentiate "lbr-fmt=0" from "lbr-fmt not set";
- Do what the user asked for whenever possible;
- Treat mismatch or violatation as an error rather than warning;

Testcases for a lbr-fmt=5 host:

 "-cpu host" --> "Disable LBR"
 "-cpu host,lbr-fmt=0" --> "Disable LBR"
 "-cpu host,lbr-fmt=5" --> "Enable LBR"
 "-cpu host,lbr-fmt=6" --> "Error out, lbr mismatch"
 "-cpu host,lbr-fmt=0xff" --> "Error out, bitmask violatation"
 "-cpu host,migratable=no" --> "Enable LBR"
 "-cpu host,migratable=no,lbr-fmt=0" --> "Disable LBR"
 "-cpu host,migratable=no,lbr-fmt=5" --> "Enable LBR"
 "-cpu host,migratable=no,lbr-fmt=6" --> "Error out, lbr mismatch"
 "-cpu host,migratable=no,lbr-fmt=0xff" --> "Error out, bitmask violatation"

 target/i386/cpu.c | 39 +++++++++++++++++++++++++++++++++++++++
 target/i386/cpu.h | 10 ++++++++++
 2 files changed, 49 insertions(+)
diff mbox series

Patch

diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index ad99cad0e7..d03306179a 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -6748,6 +6748,41 @@  static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
         goto out;
     }
 
+    /*
+     * Override env->features[FEAT_PERF_CAPABILITIES]
+     * with explicit user-provided settings.
+     */
+    if (cpu->lbr_fmt != ~PERF_CAP_LBR_FMT) {
+        if ((cpu->lbr_fmt & PERF_CAP_LBR_FMT) != cpu->lbr_fmt) {
+            error_setg(errp, "invalid lbr-fmt");
+            return;
+        }
+        env->features[FEAT_PERF_CAPABILITIES] &= ~PERF_CAP_LBR_FMT;
+        env->features[FEAT_PERF_CAPABILITIES] |= cpu->lbr_fmt;
+    }
+
+    /*
+     * We can always validate env->features[FEAT_PERF_CAPABILITIES],
+     * no matter how it was initialized:
+     */
+    uint64_t requested_lbr_fmt =
+        env->features[FEAT_PERF_CAPABILITIES] & PERF_CAP_LBR_FMT;
+    if (requested_lbr_fmt && kvm_enabled()) {
+        uint64_t host_perf_cap =
+            x86_cpu_get_supported_feature_word(FEAT_PERF_CAPABILITIES, false);
+        uint64_t host_lbr_fmt = host_perf_cap & PERF_CAP_LBR_FMT;
+        if (!cpu->enable_pmu) {
+            error_setg(errp, "vPMU: LBR is unsupported without pmu=on");
+            return;
+        }
+        if (requested_lbr_fmt != host_lbr_fmt) {
+            error_setg(errp, "vPMU: the lbr-fmt value (0x%lx) mismatches "
+                        "the host supported value (0x%lx).",
+                        requested_lbr_fmt, host_lbr_fmt);
+            return;
+        }
+    }
+
     x86_cpu_filter_features(cpu, cpu->check_cpuid || cpu->enforce_cpuid);
 
     if (cpu->enforce_cpuid && x86_cpu_have_filtered_features(cpu)) {
@@ -7150,6 +7185,9 @@  static void x86_cpu_initfn(Object *obj)
     object_property_add_alias(obj, "sse4_1", obj, "sse4.1");
     object_property_add_alias(obj, "sse4_2", obj, "sse4.2");
 
+    cpu->lbr_fmt = ~PERF_CAP_LBR_FMT;
+    object_property_add_alias(obj, "lbr_fmt", obj, "lbr-fmt");
+
     if (xcc->model) {
         x86_cpu_load_model(cpu, xcc->model);
     }
@@ -7300,6 +7338,7 @@  static Property x86_cpu_properties[] = {
 #endif
     DEFINE_PROP_INT32("node-id", X86CPU, node_id, CPU_UNSET_NUMA_NODE_ID),
     DEFINE_PROP_BOOL("pmu", X86CPU, enable_pmu, false),
+    DEFINE_PROP_BITMASK_UINT64("lbr-fmt", X86CPU, lbr_fmt, PERF_CAP_LBR_FMT),
 
     DEFINE_PROP_UINT32("hv-spinlocks", X86CPU, hyperv_spinlock_attempts,
                        HYPERV_SPINLOCK_NEVER_NOTIFY),
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 1bc300ce85..bab394e18e 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -354,6 +354,7 @@  typedef enum X86Seg {
 #define ARCH_CAP_TSX_CTRL_MSR		(1<<7)
 
 #define MSR_IA32_PERF_CAPABILITIES      0x345
+#define PERF_CAP_LBR_FMT                0x3f
 
 #define MSR_IA32_TSX_CTRL		0x122
 #define MSR_IA32_TSCDEADLINE            0x6e0
@@ -1726,6 +1727,15 @@  struct X86CPU {
      */
     bool enable_pmu;
 
+    /*
+     * Configure LBR_FMT bits on IA32_PERF_CAPABILITIES MSR.
+     * This can't be enabled by default yet because it doesn't have
+     * ABI stability guarantees, as it is only allowed to pass all
+     * LBR_FMT bits returned by kvm_arch_get_supported_msr_feature()
+     * (that depends on host CPU and kernel capabilities) to the guest.
+     */
+    uint64_t lbr_fmt;
+
     /* LMCE support can be enabled/disabled via cpu option 'lmce=on/off'. It is
      * disabled by default to avoid breaking migration between QEMU with
      * different LMCE configurations.