Only Intel CPUs support PerfMon masking, so this new parameter is Intel-specific. Mediated vPMU must be enabled for PerfMon masking. PerfMon masking lets a VMM partition PMU resources between host and guest: each mask bit determines whether the guest (set) or host (clear) owns a counter's MSR(s), RDPMC access, and the corresponding bit in the global MSRs (e.g. IA32_PERF_GLOBAL_CTRL) in non-root mode. The setting is system-wide and caps what any individual guest may be given; each guest may configure a subset of it. The parameter is configured with a text-based, semicolon-separated list of terms. Currently supported for a mediated vPMU guest: guest_gp= general purpose counters (0-based) owned by the guest guest_fixed= fixed counters (0-based) owned by the guest perf_metrics PERF_METRICS is owned by the guest For example: kvm-intel.perfmon_mask=guest_gp=0-3;guest_fixed=0,2-3;perf_metrics Internally, the parsed terms are folded into a 64-bit perfmon_mask variable that uses the same layout as the PERFMON_MASK VMCS field, i.e. the IA32_PERF_GLOBAL_STATUS layout. An empty (default) value behaves the same as a plain mediated vPMU. Using IA32_PERF_GLOBAL_CTRL in the generic VM-exit MSR-store area while the PerfMon masking VM-execution control is set is undefined, so PerfMon masking requires the dedicated Save-IA32_PERF_GLOBAL_CTRL VM-exit control. cpu_has_vmx_perfmon_mask() is hardcoded to false temporarily until later patches. Signed-off-by: Zide Chen --- .../admin-guide/kernel-parameters.txt | 30 ++++++++ arch/x86/kvm/pmu.c | 3 + arch/x86/kvm/pmu.h | 1 + arch/x86/kvm/vmx/capabilities.h | 5 ++ arch/x86/kvm/vmx/pmu_intel.c | 68 +++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 54 +++++++++++++++ arch/x86/kvm/vmx/vmx.h | 1 + arch/x86/kvm/x86.c | 2 +- 8 files changed, 163 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b5493a7f8f22..e0ff16746a08 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3284,6 +3284,36 @@ Kernel parameters [KVM,Intel] Control nested virtualization feature in KVM/VMX. Default is 1 (enabled). + kvm-intel.perfmon_mask= + [KVM,Intel] Defines the host-wide PMU resource + partition between guest and host. Resources assigned + to the guest are unavailable to the host, and vice + versa, while a guest is running. + + The value is a semicolon-separated list of terms: + + guest_gp= General purpose counters + (0-based) assigned to the guest. + guest_fixed= Fixed counters (0-based) assigned + to the guest. + perf_metrics PERF_METRICS is assigned to the + guest. Requires fixed counter 3 + to also be assigned to the + guest. + + is a comma-separated list of numbers and/or + ranges, e.g. "0-2,5". Terms and the resources they + don't mention default to being host-owned. For + example: + + kvm-intel.perfmon_mask=guest_gp=0-3;guest_fixed=0-1,3;perf_metrics + + assigns general purpose counters 0-3, fixed counters + 0, 1 and 3, and PERF_METRICS to the guest, while all + other resources remain with the host. + + Default is "" (disabled). + kvm-intel.unrestricted_guest= [KVM,Intel] Control KVM's use of unrestricted guest feature (virtualized real and unpaged mode). Default diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index c022337d0bec..92ff685d11b3 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -43,6 +43,9 @@ module_param(enable_pmu, bool, 0444); bool __read_mostly enable_mediated_pmu; EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_mediated_pmu); +u64 __read_mostly perfmon_mask; +EXPORT_SYMBOL_FOR_KVM_INTERNAL(perfmon_mask); + struct kvm_x86_pmu_event_filter { __u32 action; __u32 nevents; diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index 8322bbed2d64..2dc12e3f3af0 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -64,6 +64,7 @@ struct kvm_pmu_ops { extern bool enable_pmu; extern bool enable_mediated_pmu; +extern u64 perfmon_mask; void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops); diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index 810119167f79..d4c362093966 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -296,6 +296,11 @@ static inline bool cpu_has_vmx_ipiv(void) return vmcs_config.cpu_based_3rd_exec_ctrl & TERTIARY_EXEC_IPI_VIRT; } +static inline bool cpu_has_vmx_perfmon_mask(void) +{ + return false; +} + static inline bool cpu_has_vmx_flexpriority(void) { return cpu_has_vmx_tpr_shadow() && diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index e2e51006ca47..62e542eac05e 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -955,6 +955,74 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu) } } +static bool intel_pmu_validate_perfmon_mask(void) +{ + u64 guest_fixed_mask, guest_gp_mask; + + /* + * Combining VM-exit MSR-store with PerfMon masking produces + * undefined behavior, so it requires hardware support for this + * dedicated save control. + */ + if (!cpu_has_save_perf_global_ctrl()) + return false; + + guest_gp_mask = perfmon_mask & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0); + guest_fixed_mask = perfmon_mask >> INTEL_PMC_IDX_FIXED; + guest_fixed_mask &= GENMASK_ULL(INTEL_PMC_MAX_FIXED - 1, 0); + + if ((guest_fixed_mask & ~kvm_pmu_cap.fixed_cntr_mask64) || + (guest_gp_mask & ~kvm_pmu_cap.cntr_mask64)) + return false; + + /* + * Without KVM Arch PerfMon extension support, the guest cannot own + * non-contiguous GP counters. + */ + if (guest_gp_mask & (guest_gp_mask + 1)) + return false; + + if ((perfmon_mask & GLOBAL_STATUS_PERF_METRICS_OVF) && + !(kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS)) + return false; + + /* + * PERF_METRICS and fixed counter 3 must both be host-owned or both + * guest-owned. + */ + if (!!(perfmon_mask & BIT_ULL(GLOBAL_STATUS_PERF_METRICS_OVF_BIT)) != + !!(perfmon_mask & BIT_ULL(INTEL_PMC_IDX_FIXED + 3))) + return false; + + /* + * The guest must not own all PMU counters. Otherwise, the configuration + * degenerates into plain mediated vPMU and adds unnecessary complexity + * to the perf scheduler. + */ + if ((guest_fixed_mask == kvm_host_pmu.fixed_cntr_mask64) && + (guest_gp_mask == kvm_host_pmu.cntr_mask64)) + return false; + + return true; +} + +void intel_pmu_perfmon_mask_setup(void) +{ + if (!perfmon_mask) + return; + + if (!enable_mediated_pmu || !cpu_has_vmx_perfmon_mask()) { + perfmon_mask = 0; + return; + } + + if (!intel_pmu_validate_perfmon_mask()) { + pr_warn("Invalid perfmon_mask=%#llx, disabling PerfMon masking\n", + perfmon_mask); + perfmon_mask = 0; + } +} + struct kvm_pmu_ops intel_pmu_ops __initdata = { .emulate_rdpmc = intel_emulate_rdpmc, .msr_idx_to_pmc = intel_msr_idx_to_pmc, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index ded63e2e39ce..cdd141d22efa 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -14,6 +14,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -164,6 +166,56 @@ module_param(allow_smaller_maxphyaddr, bool, S_IRUGO); module_param(enable_mediated_pmu, bool, 0444); +/* + * See the "kvm-intel.perfmon_mask" entry in + * Documentation/admin-guide/kernel-parameters.txt for the full syntax. + * Example: kvm-intel.perfmon_mask=guest_gp=0-3;guest_fixed=0-1,3;perf_metrics + */ +static int perfmon_mask_set(const char *val, const struct kernel_param *kp) +{ + unsigned long gp_bitmap = 0, fixed_bitmap = 0; + char *buf, *orig, *tok; + size_t prefix_len; + u64 mask = 0; + int r = 0; + + buf = orig = kstrdup(val, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + while ((tok = strsep(&buf, ";")) != NULL) { + if (!*tok) + continue; + + if (!strcmp(tok, "perf_metrics")) { + mask |= GLOBAL_STATUS_PERF_METRICS_OVF; + } else if ((prefix_len = str_has_prefix(tok, "guest_gp="))) { + r = bitmap_parselist(tok + prefix_len, &gp_bitmap, + INTEL_PMC_MAX_GENERIC); + } else if ((prefix_len = str_has_prefix(tok, "guest_fixed="))) { + r = bitmap_parselist(tok + prefix_len, &fixed_bitmap, + INTEL_PMC_MAX_FIXED); + } else { + r = -EINVAL; + } + + if (r) + goto out; + } + + mask |= gp_bitmap | ((u64)fixed_bitmap << INTEL_PMC_IDX_FIXED); + *(u64 *)kp->arg = mask; +out: + kfree(orig); + return r; +} + +static const struct kernel_param_ops perfmon_mask_ops = { + .set = perfmon_mask_set, + .get = param_get_ullong, +}; +module_param_cb(perfmon_mask, &perfmon_mask_ops, &perfmon_mask, 0444); + #define KVM_VM_CR0_ALWAYS_OFF (X86_CR0_NW | X86_CR0_CD) #define KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST X86_CR0_NE #define KVM_VM_CR0_ALWAYS_ON \ @@ -8824,6 +8876,8 @@ __init int vmx_hardware_setup(void) else vt_init_ops.handle_intel_pt_intr = NULL; + intel_pmu_perfmon_mask_setup(); + setup_default_sgx_lepubkeyhash(); vmx_set_cpu_caps(); diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index dc8517f15bc4..ccda5c5c8c2c 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -667,6 +667,7 @@ static __always_inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu) void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu); int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu); void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu); +void intel_pmu_perfmon_mask_setup(void); struct vmcs *alloc_vmcs_cpu(bool shadow, int cpu, gfp_t flags); void free_vmcs(struct vmcs *vmcs); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 5f3215915c76..26a3b7a267b5 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9328,7 +9328,7 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) if (enable_mediated_pmu && kvm->arch.enable_pmu && !kvm->arch.created_mediated_pmu) { if (irqchip_in_kernel(kvm)) { - r = perf_create_mediated_pmu(0); + r = perf_create_mediated_pmu(perfmon_mask); if (r) { pr_warn_ratelimited(PERF_MEDIATED_PMU_MSG); return r; -- 2.55.0