With PMU partitioning, the host is allowed to create !exclude_guest events because it no longer yields all PMU resources to the guest while the guest is running. However, Perf Metrics, LBR, BTS, and PEBS cannot be shared between host and guest. Host events that rely on these exclusive facilities must not be scheduled in if the facilities are guest-owned. Intel PT PMU is special: Since it can't be partitioned, supporting Intel PT passthrough requires heterogeneous mediated vPMUs. Additional work is needed to handle nr_include_guest_events accounting. Signed-off-by: Zide Chen --- arch/x86/events/intel/core.c | 35 ++++++++++++++++++++++++++++++- arch/x86/include/asm/perf_event.h | 1 + kernel/events/core.c | 16 ++++++++++++-- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 0f76e56fd2db..2928c8262fef 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -4455,10 +4455,40 @@ dyn_constraint(struct cpu_hw_events *cpuc, struct event_constraint *c, int idx) return c; } +static bool event_uses_guest_owned_facility(struct perf_event *event) +{ + /* + * For Intel platforms, PMU partition mask shares the same bit layout + * as IA32_PERF_GLOBAL_STATUS. + */ + u64 partition_mask = x86_pmu_current_partition_mask(); + + if ((partition_mask & GLOBAL_STATUS_PERF_METRICS_OVF) && + is_topdown_event(event)) + return true; + + if ((partition_mask & GLOBAL_STATUS_LBRS_FROZEN) && + needs_branch_stack(event)) + return true; + + if (partition_mask & + (GLOBAL_STATUS_BUFFER_OVF | GLOBAL_STATUS_ARCH_PEBS_THRESHOLD)) { + if (event->attr.precise_ip || is_pebs_counter_event_group(event)) + return true; + + if ((partition_mask & GLOBAL_STATUS_BUFFER_OVF) && + intel_pmu_has_bts(event)) + return true; + } + + return false; +} + /* * Mask out guest-owned counters from a constraint when PMU partition has been * entered, so !exclude_guest host events are not scheduled onto them while - * the CPU is in non-root mode. + * the CPU is in non-root mode. Reject the event when a guest is currently + * loaded and it needs a guest-owned exclusive facility. * * This is also used by PMU-specific get_event_constraints() wrappers * that hard-code a static, counter-specific constraint. @@ -4475,6 +4505,9 @@ part_constraint(struct cpu_hw_events *cpuc, int idx, return c; if (x86_pmu_partition_loaded(cpuc)) { + if (event_uses_guest_owned_facility(event)) + return &emptyconstraint; + c = dyn_constraint(cpuc, c, idx); c->idxmsk64 &= ~x86_pmu_current_partition_mask(); c->weight = hweight64(c->idxmsk64); diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 18f1ac5e008b..aaaa34062f8c 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -453,6 +453,7 @@ static inline bool is_topdown_idx(int idx) #define GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT 54 #define GLOBAL_STATUS_ARCH_PEBS_THRESHOLD BIT_ULL(GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT) #define GLOBAL_STATUS_PERF_METRICS_OVF_BIT 48 +#define GLOBAL_STATUS_PERF_METRICS_OVF BIT_ULL(GLOBAL_STATUS_PERF_METRICS_OVF_BIT) #define GLOBAL_CTRL_EN_PERF_METRICS BIT_ULL(48) /* diff --git a/kernel/events/core.c b/kernel/events/core.c index c9e7a2f0edc8..1ae52a0ce234 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6381,11 +6381,23 @@ static int mediated_pmu_account_event(struct perf_event *event) if (!is_include_guest_event(event)) return 0; + /* + * This lockless fast path assumes that heterogeneous mediated vPMUs + * are not supported, i.e. a mix of PERF_PMU_CAP_MEDIATED_VPMU PMUs + * with and without PERF_PMU_CAP_PMU_PARTITION. + */ if (atomic_inc_not_zero(&nr_include_guest_events)) return 0; guard(mutex)(&perf_mediated_pmu_mutex); - if (atomic_read(&nr_mediated_pmu_vms)) + + /* + * PMU partitioning allows scheduling !exclude_guest events while a + * guest is running. However, it is up to the PMU driver to validate + * whether the facilities needed by the event are available on the host. + */ + if (atomic_read(&nr_mediated_pmu_vms) && + !(event->pmu->capabilities & PERF_PMU_CAP_PMU_PARTITION)) return -EOPNOTSUPP; atomic_inc(&nr_include_guest_events); @@ -6418,7 +6430,7 @@ int perf_create_mediated_pmu(u64 partition_mask) int ret; guard(mutex)(&perf_mediated_pmu_mutex); - if (atomic_read(&nr_include_guest_events)) + if (atomic_read(&nr_include_guest_events) && !partition_mask) return -EBUSY; ret = arch_perf_set_pmu_partition_mask(partition_mask); -- 2.55.0