From: Zide Chen <zide.chen@intel.com>
To: Sean Christopherson <seanjc@google.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Peter Zijlstra <peterz@infradead.org>
Cc: kvm@vger.kernel.org, Andi Kleen <ak@linux.intel.com>,
Jim Mattson <jmattson@google.com>,
Stephane Eranian <eranian@google.com>,
linux-kernel@vger.kernel.org, Mingwei Zhang <mizhang@google.com>,
Zide Chen <zide.chen@intel.com>,
Das Sandipan <Sandipan.Das@amd.com>,
Shukla Manali <Manali.Shukla@amd.com>,
Dapeng Mi <dapeng1.mi@linux.intel.com>,
Xudong Hao <xudong.hao@intel.com>
Subject: [PATCH 12/23] KVM: x86/pmu: Add the perfmon_mask module parameter
Date: Fri, 21 Aug 2026 15:19:51 -0700 [thread overview]
Message-ID: <20260821222002.54907-13-zide.chen@intel.com> (raw)
In-Reply-To: <20260821222002.54907-1-zide.chen@intel.com>
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=<list> general purpose counters (0-based) owned by the guest
guest_fixed=<list> 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 <zide.chen@intel.com>
---
.../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=<list> General purpose counters
+ (0-based) assigned to the guest.
+ guest_fixed=<list> 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.
+
+ <list> 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 <linux/bitmap.h>
#include <linux/highmem.h>
#include <linux/hrtimer.h>
#include <linux/kernel.h>
@@ -25,6 +26,7 @@
#include <linux/sched.h>
#include <linux/sched/smt.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/tboot.h>
#include <linux/trace_events.h>
@@ -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
next prev parent reply other threads:[~2026-08-21 22:31 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-21 22:19 [PATCH 00/23] perf/KVM: Support PMU partitioning for x86 platforms Zide Chen
2026-08-21 22:19 ` [PATCH 01/23] perf/x86/intel: Guard counter masks against zero counters Zide Chen
2026-08-21 22:19 ` [PATCH 02/23] perf, perf/x86: Pass partition mask from KVM to perf/x86 Zide Chen
2026-08-21 22:19 ` [PATCH 03/23] perf/x86: Add GUEST_PMU states for PMU partitioning Zide Chen
2026-08-21 22:19 ` [PATCH 04/23] perf/x86: Split host/guest PMI handling under " Zide Chen
2026-08-21 22:19 ` [PATCH 05/23] perf/x86: Allow exclude_host events to run in non-root mode Zide Chen
2026-08-21 22:19 ` [PATCH 06/23] perf/x86: Restrict !exclude_guest events to host-owned counters Zide Chen
2026-08-21 22:19 ` [PATCH 07/23] perf/x86: Apply PMU partition mask on static constraints Zide Chen
2026-08-21 22:19 ` [PATCH 08/23] perf/x86: Export available PMU counters to sysfs Zide Chen
2026-08-21 22:19 ` [PATCH 09/23] perf: Skip exclude_guest events on PMU partitioned counters Zide Chen
2026-08-21 22:19 ` [PATCH 10/23] perf: Reschedule events across PMU partition transitions Zide Chen
2026-08-21 22:19 ` [PATCH 11/23] perf, perf/x86: Allow host !exclude_guest events in PMU partitioning Zide Chen
2026-08-21 22:19 ` Zide Chen [this message]
2026-08-21 22:19 ` [PATCH 13/23] KVM: x86/pmu: Set up the PERFMON_MASK VMCS field Zide Chen
2026-08-21 22:19 ` [PATCH 14/23] KVM: x86/pmu, perf/x86: Update effective PMU partition mask Zide Chen
2026-08-21 22:19 ` [PATCH 15/23] KVM: x86/pmu: Relax MSR intercept policy under PerfMon masking Zide Chen
2026-08-21 22:19 ` [PATCH 16/23] KVM: x86/pmu: Handle FIXED_CTR_CTRL " Zide Chen
2026-08-21 22:19 ` [PATCH 17/23] KVM: x86/pmu: Handle GLOBAL_CTRL " Zide Chen
2026-08-21 22:19 ` [PATCH 18/23] KVM: x86/pmu: Handle GLOBAL_STATUS MSRs " Zide Chen
2026-08-21 22:19 ` [PATCH 19/23] KVM: x86/pmu: Always intercept GLOBAL_INUSE " Zide Chen
2026-08-21 22:19 ` [PATCH 20/23] KVM: x86/pmu: Request guest PMI for guest-induced PMIs Zide Chen
2026-08-21 22:20 ` [PATCH 21/23] KVM: x86/pmu: Enable PerfMon masking Zide Chen
2026-08-21 22:20 ` [PATCH 22/23] KVM: selftests: Fix PERF_METRICS test by checking FC3 availability Zide Chen
2026-08-21 22:20 ` [PATCH 23/23] KVM: selftests: Allow no general purpose counters on the host Zide Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260821222002.54907-13-zide.chen@intel.com \
--to=zide.chen@intel.com \
--cc=Manali.Shukla@amd.com \
--cc=Sandipan.Das@amd.com \
--cc=ak@linux.intel.com \
--cc=dapeng1.mi@linux.intel.com \
--cc=eranian@google.com \
--cc=jmattson@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mizhang@google.com \
--cc=pbonzini@redhat.com \
--cc=peterz@infradead.org \
--cc=seanjc@google.com \
--cc=xudong.hao@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.