| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/20 06:14 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"KVM",
"PERF_EVENTS"
],
"FocusSymbols": [
"kvm_pmu_handle_mdcr",
"kvm_vcpu_load_pmu",
"kvm_pmu_create_perf_event_with_pmu",
"kvm_arm_pmu_v3_set_pmu_fixed_counters_only",
"kvm_arm_pmu_v3_set_nr_counters",
"kvm_pmu_probe_armpmu"
],
"Reasoning": "The patch introduces a new KVM ARM64 PMU emulation feature (KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY) and modifies PMU event creation, loading, and reloading logic. This is reachable from userspace via KVM ioctls and modifies core KVM PMU emulation logic.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/20 06:14 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit e3a8c7074b50b165a55190fe29bac604faf26de6\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Jul 20 06:14:43 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst\nindex 66e714f2fcfa7f..1d592e70bdea3d 100644\n--- a/Documentation/virt/kvm/devices/vcpu.rst\n+++ b/Documentation/virt/kvm/devices/vcpu.rst\n@@ -74,7 +74,8 @@ irqchip.\n \t -ENODEV PMUv3 not supported or GIC not initialized\n \t -ENXIO PMUv3 not properly configured or in-kernel irqchip not\n \t \t configured as required prior to calling this attribute\n-\t -EBUSY PMUv3 already initialized or a VCPU has already run\n+\t -EBUSY PMUv3 already initialized, a VCPU has already run or\n+\t\t FIXED_COUNTERS_ONLY has already been set\n \t -EINVAL Invalid filter range\n \t ======= ======================================================\n \n@@ -116,14 +117,14 @@ using event 0x11 (CPU_CYCLES).\n \n :Returns:\n \n-\t ======= ====================================================\n+\t ======= ===========================================================\n \t -EBUSY PMUv3 already initialized, a VCPU has already run or\n- an event filter has already been set\n+ an event filter or FIXED_COUNTERS_ONLY has already been set\n \t -EFAULT Error accessing the PMU identifier\n \t -ENXIO PMU not found\n \t -ENODEV PMUv3 not supported or GIC not initialized\n \t -ENOMEM Could not allocate memory\n-\t ======= ====================================================\n+\t ======= ===========================================================\n \n Request that the VCPU uses the specified hardware PMU when creating guest events\n for the purpose of PMU emulation. The PMU identifier can be read from the \"type\"\n@@ -165,6 +166,27 @@ explicitly selected, or the number of counters is out of range for the\n selected PMU. Selecting a new PMU cancels the effect of setting this\n attribute.\n \n+1.6 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY\n+------------------------------------------------------\n+\n+:Parameters: no additional parameter in kvm_device_attr.addr\n+\n+:Returns:\n+\n+\t ======= ==================================================\n+\t -EBUSY PMUv3 already initialized, a VCPU has already run,\n+\t\t an event filter has already been set or\n+\t\t a hardware PMU has already been specified\n+\t -ENXIO Attempted to get before setting\n+\t -ENODEV Attempted to set while PMUv3 not supported\n+\t ======= ==================================================\n+\n+If set, KVM emulates PMUv3 without programmable event counters.\n+\n+When this attribute is enabled, the vCPU can run on any physical CPU\n+that has a PMU, regardless of the underlying implementation. This\n+attribute is VM-scoped.\n+\n 2. GROUP: KVM_ARM_VCPU_TIMER_CTRL\n =================================\n \ndiff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h\nindex bae2c4f92ef5c0..3998e66c657150 100644\n--- a/arch/arm64/include/asm/kvm_host.h\n+++ b/arch/arm64/include/asm/kvm_host.h\n@@ -367,6 +367,10 @@ struct kvm_arch {\n #define KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS\t\t10\n \t/* Unhandled SEAs are taken to userspace */\n #define KVM_ARCH_FLAG_EXIT_SEA\t\t\t\t11\n+\t/* PMUv3 is emulated with an explicitly specified hardware PMU */\n+#define KVM_ARCH_FLAG_PMU_V3_EXPLICIT\t\t\t12\n+\t/* PMUv3 is emulated without programmable event counters */\n+#define KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY\t13\n \tunsigned long flags;\n \n \t/* VM-wide vCPU feature set */\ndiff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h\nindex 1c13bfa2d38aad..39a1a1e412e66f 100644\n--- a/arch/arm64/include/uapi/asm/kvm.h\n+++ b/arch/arm64/include/uapi/asm/kvm.h\n@@ -437,6 +437,7 @@ enum {\n #define KVM_ARM_VCPU_PMU_V3_FILTER\t\t2\n #define KVM_ARM_VCPU_PMU_V3_SET_PMU\t\t3\n #define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS\t4\n+#define KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY\t5\n #define KVM_ARM_VCPU_TIMER_CTRL\t\t1\n #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER\t\t0\n #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER\t\t1\ndiff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c\nindex 1995e1198648e8..a5ae628204a122 100644\n--- a/arch/arm64/kernel/cpu_errata.c\n+++ b/arch/arm64/kernel/cpu_errata.c\n@@ -1005,7 +1005,7 @@ const struct arm64_cpu_capabilities arm64_errata[] = {\n \t{\n \t\t.desc = \"Apple IMPDEF PMUv3 Traps\",\n \t\t.capability = ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS,\n-\t\t.type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,\n+\t\t.type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE,\n \t\t.matches = has_impdef_pmuv3,\n \t\t.cpu_enable = cpu_enable_impdef_pmuv3_traps,\n \t},\ndiff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c\nindex 50adfff75be82e..a866beb8d873d3 100644\n--- a/arch/arm64/kvm/arm.c\n+++ b/arch/arm64/kvm/arm.c\n@@ -651,6 +651,7 @@ static bool kvm_vcpu_should_clear_twe(struct kvm_vcpu *vcpu)\n void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n {\n \tstruct kvm_s2_mmu *mmu;\n+\tint last_cpu = vcpu-\u003ecpu;\n \tint *last_ran;\n \n \tif (is_protected_kvm_enabled())\n@@ -700,6 +701,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n \tif (has_vhe())\n \t\tkvm_vcpu_load_vhe(vcpu);\n \tkvm_arch_vcpu_load_fp(vcpu);\n+\tkvm_vcpu_load_pmu(vcpu, last_cpu);\n \tkvm_vcpu_pmu_restore_guest(vcpu);\n \tif (kvm_arm_is_pvtime_enabled(\u0026vcpu-\u003earch))\n \t\tkvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);\n@@ -749,7 +751,6 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)\n \tkvm_arm_vmid_clear_active();\n \n \tvcpu_clear_on_unsupported_cpu(vcpu);\n-\tvcpu-\u003ecpu = -1;\n }\n \n static void __kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu)\n@@ -1652,29 +1653,26 @@ static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu,\n {\n \tunsigned long features = init-\u003efeatures[0];\n \tstruct kvm *kvm = vcpu-\u003ekvm;\n-\tint ret = -EINVAL;\n+\tint ret;\n \n-\tmutex_lock(\u0026kvm-\u003earch.config_lock);\n+\tlockdep_assert_held(\u0026kvm-\u003earch.config_lock);\n \n \tif (test_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, \u0026kvm-\u003earch.flags) \u0026\u0026\n \t kvm_vcpu_init_changed(vcpu, init))\n-\t\tgoto out_unlock;\n+\t\treturn -EINVAL;\n \n \tbitmap_copy(kvm-\u003earch.vcpu_features, \u0026features, KVM_VCPU_MAX_FEATURES);\n \n \tret = kvm_setup_vcpu(vcpu);\n \tif (ret)\n-\t\tgoto out_unlock;\n+\t\treturn ret;\n \n \t/* Now we know what it is, we can reset it. */\n \tkvm_reset_vcpu(vcpu);\n \n \tset_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, \u0026kvm-\u003earch.flags);\n \tvcpu_set_flag(vcpu, VCPU_INITIALIZED);\n-\tret = 0;\n-out_unlock:\n-\tmutex_unlock(\u0026kvm-\u003earch.config_lock);\n-\treturn ret;\n+\treturn 0;\n }\n \n static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,\n@@ -1690,6 +1688,8 @@ static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,\n \tif (ret)\n \t\treturn ret;\n \n+\tguard(mutex)(\u0026vcpu-\u003ekvm-\u003earch.config_lock);\n+\n \tif (!kvm_vcpu_initialized(vcpu))\n \t\treturn __kvm_vcpu_set_target(vcpu, init);\n \ndiff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c\nindex 98305bbfc095ab..ff5d5f66a9c09f 100644\n--- a/arch/arm64/kvm/pmu-emul.c\n+++ b/arch/arm64/kvm/pmu-emul.c\n@@ -7,9 +7,9 @@\n #include \u003clinux/cpu.h\u003e\n #include \u003clinux/kvm.h\u003e\n #include \u003clinux/kvm_host.h\u003e\n-#include \u003clinux/list.h\u003e\n #include \u003clinux/perf_event.h\u003e\n #include \u003clinux/perf/arm_pmu.h\u003e\n+#include \u003clinux/rculist.h\u003e\n #include \u003clinux/uaccess.h\u003e\n #include \u003casm/kvm_emulate.h\u003e\n #include \u003ckvm/arm_pmu.h\u003e\n@@ -17,6 +17,13 @@\n \n #define PERF_ATTR_CFG1_COUNTER_64BIT\tBIT(0)\n \n+#define MDCR_EL2_PMU_RECREATE_MASK\t(MDCR_EL2_HPMN | MDCR_EL2_HPMD | \\\n+\t\t\t\t\t MDCR_EL2_HLP)\n+\n+/*\n+ * arm_pmus is append-only. kvm_supports_guest_pmuv3() feeds persistent\n+ * VM state, so a true result must remain valid after the check.\n+ */\n static LIST_HEAD(arm_pmus);\n static DEFINE_MUTEX(arm_pmus_lock);\n \n@@ -26,7 +33,6 @@ static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc);\n \n bool kvm_supports_guest_pmuv3(void)\n {\n-\tguard(mutex)(\u0026arm_pmus_lock);\n \treturn !list_empty(\u0026arm_pmus);\n }\n \n@@ -80,6 +86,11 @@ u64 kvm_pmu_evtyper_mask(struct kvm *kvm)\n \treturn mask;\n }\n \n+static bool kvm_pmu_fixed_counters_only(struct kvm *kvm)\n+{\n+\treturn test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, \u0026kvm-\u003earch.flags);\n+}\n+\n /**\n * kvm_pmc_is_64bit - determine if counter is 64bit\n * @pmc: counter context\n@@ -317,17 +328,22 @@ u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu)\n \n u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu)\n {\n-\tu64 val = FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu));\n+\tu64 n = vcpu-\u003ekvm-\u003earch.nr_pmu_counters;\n \n-\tif (val == 0)\n+\tif (n == 0)\n \t\treturn BIT(ARMV8_PMU_CYCLE_IDX);\n \telse\n-\t\treturn GENMASK(val - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX);\n+\t\treturn GENMASK(n - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX);\n }\n \n static void kvm_pmc_enable_perf_event(struct kvm_pmc *pmc)\n {\n-\tif (!pmc-\u003eperf_event) {\n+\tstruct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);\n+\n+\tif (!pmc-\u003eperf_event ||\n+\t (kvm_pmu_fixed_counters_only(vcpu-\u003ekvm) \u0026\u0026\n+\t !cpumask_test_cpu(READ_ONCE(vcpu-\u003ecpu),\n+\t\t\t \u0026to_arm_pmu(pmc-\u003eperf_event-\u003epmu)-\u003esupported_cpus))) {\n \t\tkvm_pmu_create_perf_event(pmc);\n \t\treturn;\n \t}\n@@ -606,6 +622,21 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)\n \t}\n }\n \n+void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val)\n+{\n+\tu64 changed = old ^ val;\n+\n+\t/*\n+\t * HPMN determines which counters HPMD and HLP apply to. Changes to\n+\t * these fields require new perf event filters and sample periods.\n+\t */\n+\tif (changed \u0026 MDCR_EL2_PMU_RECREATE_MASK)\n+\t\tvcpu-\u003earch.pmu.events_need_recreate = true;\n+\n+\tif (changed \u0026 (MDCR_EL2_HPME | MDCR_EL2_PMU_RECREATE_MASK))\n+\t\tkvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);\n+}\n+\n static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc)\n {\n \tstruct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);\n@@ -649,10 +680,8 @@ static bool kvm_pmc_counts_at_el2(struct kvm_pmc *pmc)\n \treturn kvm_pmc_read_evtreg(pmc) \u0026 ARMV8_PMU_INCLUDE_EL2;\n }\n \n-static int kvm_map_pmu_event(struct kvm *kvm, unsigned int eventsel)\n+static int kvm_map_pmu_event(struct arm_pmu *pmu, unsigned int eventsel)\n {\n-\tstruct arm_pmu *pmu = kvm-\u003earch.arm_pmu;\n-\n \t/*\n \t * The CPU PMU likely isn't PMUv3; let the driver provide a mapping\n \t * for the guest's PMUv3 event ID.\n@@ -663,14 +692,27 @@ static int kvm_map_pmu_event(struct kvm *kvm, unsigned int eventsel)\n \treturn eventsel;\n }\n \n-/**\n- * kvm_pmu_create_perf_event - create a perf event for a counter\n- * @pmc: Counter context\n- */\n-static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)\n+static struct arm_pmu *kvm_pmu_probe_armpmu(int cpu)\n+{\n+\tstruct arm_pmu_entry *entry;\n+\tstruct arm_pmu *pmu;\n+\n+\tguard(rcu)();\n+\n+\tlist_for_each_entry_rcu(entry, \u0026arm_pmus, entry) {\n+\t\tpmu = entry-\u003earm_pmu;\n+\n+\t\tif (cpumask_test_cpu(cpu, \u0026pmu-\u003esupported_cpus))\n+\t\t\treturn pmu;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static void kvm_pmu_create_perf_event_with_pmu(struct kvm_pmc *pmc,\n+\t\t\t\t\t struct arm_pmu *arm_pmu)\n {\n \tstruct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);\n-\tstruct arm_pmu *arm_pmu = vcpu-\u003ekvm-\u003earch.arm_pmu;\n \tstruct perf_event *event;\n \tstruct perf_event_attr attr;\n \tint eventsel;\n@@ -704,7 +746,7 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)\n \t * Don't create an event if we're running on hardware that requires\n \t * PMUv3 event translation and we couldn't find a valid mapping.\n \t */\n-\teventsel = kvm_map_pmu_event(vcpu-\u003ekvm, eventsel);\n+\teventsel = kvm_map_pmu_event(arm_pmu, eventsel);\n \tif (eventsel \u003c 0)\n \t\treturn;\n \n@@ -749,6 +791,32 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)\n \tpmc-\u003eperf_event = event;\n }\n \n+/**\n+ * kvm_pmu_create_perf_event - create a perf event for a counter\n+ * @pmc: Counter context\n+ */\n+static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)\n+{\n+\tstruct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);\n+\tstruct arm_pmu *arm_pmu = vcpu-\u003ekvm-\u003earch.arm_pmu;\n+\n+\tif (kvm_pmu_fixed_counters_only(vcpu-\u003ekvm)) {\n+\t\tdo {\n+\t\t\tarm_pmu = kvm_pmu_probe_armpmu(READ_ONCE(vcpu-\u003ecpu));\n+\n+\t\t\tif (!arm_pmu) {\n+\t\t\t\tpr_warn_once(\"kvm: Unsupported PMU variation detected.\\n\");\n+\t\t\t\tadd_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);\n+\t\t\t\treturn;\n+\t\t\t}\n+\n+\t\t\tkvm_pmu_create_perf_event_with_pmu(pmc, arm_pmu);\n+\t\t} while (!cpumask_test_cpu(READ_ONCE(vcpu-\u003ecpu), \u0026arm_pmu-\u003esupported_cpus));\n+\t} else {\n+\t\tkvm_pmu_create_perf_event_with_pmu(pmc, arm_pmu);\n+\t}\n+}\n+\n /**\n * kvm_pmu_set_counter_event_type - set selected counter to monitor some event\n * @vcpu: The vcpu pointer\n@@ -789,43 +857,7 @@ void kvm_host_pmu_init(struct arm_pmu *pmu)\n \t\treturn;\n \n \tentry-\u003earm_pmu = pmu;\n-\tlist_add_tail(\u0026entry-\u003eentry, \u0026arm_pmus);\n-}\n-\n-static struct arm_pmu *kvm_pmu_probe_armpmu(void)\n-{\n-\tstruct arm_pmu_entry *entry;\n-\tstruct arm_pmu *pmu;\n-\tint cpu;\n-\n-\tguard(mutex)(\u0026arm_pmus_lock);\n-\n-\t/*\n-\t * It is safe to use a stale cpu to iterate the list of PMUs so long as\n-\t * the same value is used for the entirety of the loop. Given this, and\n-\t * the fact that no percpu data is used for the lookup there is no need\n-\t * to disable preemption.\n-\t *\n-\t * It is still necessary to get a valid cpu, though, to probe for the\n-\t * default PMU instance as userspace is not required to specify a PMU\n-\t * type. In order to uphold the preexisting behavior KVM selects the\n-\t * PMU instance for the core during vcpu init. A dependent use\n-\t * case would be a user with disdain of all things big.LITTLE that\n-\t * affines the VMM to a particular cluster of cores.\n-\t *\n-\t * In any case, userspace should just do the sane thing and use the UAPI\n-\t * to select a PMU type directly. But, be wary of the baggage being\n-\t * carried here.\n-\t */\n-\tcpu = raw_smp_processor_id();\n-\tlist_for_each_entry(entry, \u0026arm_pmus, entry) {\n-\t\tpmu = entry-\u003earm_pmu;\n-\n-\t\tif (cpumask_test_cpu(cpu, \u0026pmu-\u003esupported_cpus))\n-\t\t\treturn pmu;\n-\t}\n-\n-\treturn NULL;\n+\tlist_add_tail_rcu(\u0026entry-\u003eentry, \u0026arm_pmus);\n }\n \n static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1)\n@@ -870,6 +902,13 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)\n \tu64 val, mask = 0;\n \tint base, i, nr_events;\n \n+\t/*\n+\t * Hide the hardware PMU's event set to keep PMCEID stable across\n+\t * physical CPU migration.\n+\t */\n+\tif (kvm_pmu_fixed_counters_only(vcpu-\u003ekvm))\n+\t\treturn 0;\n+\n \tif (!pmceid1) {\n \t\tval = compute_pmceid0(cpu_pmu);\n \t\tbase = 0;\n@@ -897,9 +936,27 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)\n \treturn val \u0026 mask;\n }\n \n+void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu, int last_cpu)\n+{\n+\tif (!kvm_pmu_fixed_counters_only(vcpu-\u003ekvm) || vcpu-\u003ecpu == last_cpu || last_cpu == -1)\n+\t\treturn;\n+\n+\tif (kvm_pmu_probe_armpmu(vcpu-\u003ecpu) != kvm_pmu_probe_armpmu(last_cpu))\n+\t\tkvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);\n+}\n+\n void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)\n {\n+\tstruct kvm_pmu *pmu = \u0026vcpu-\u003earch.pmu;\n \tu64 mask = kvm_pmu_implemented_counter_mask(vcpu);\n+\tint i;\n+\n+\tif (pmu-\u003eevents_need_recreate) {\n+\t\tfor (i = 0; i \u003c KVM_ARMV8_PMU_MAX_COUNTERS; i++)\n+\t\t\tkvm_pmu_stop_counter(kvm_vcpu_idx_to_pmc(vcpu, i));\n+\n+\t\tpmu-\u003eevents_need_recreate = false;\n+\t}\n \n \t__vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, \u0026=, mask);\n \t__vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, \u0026=, mask);\n@@ -1008,6 +1065,9 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)\n {\n \tstruct arm_pmu *arm_pmu = kvm-\u003earch.arm_pmu;\n \n+\tif (kvm_pmu_fixed_counters_only(kvm))\n+\t\treturn 0;\n+\n \t/*\n \t * PMUv3 requires that all event counters are capable of counting any\n \t * event, though the same may not be true of non-PMUv3 hardware.\n@@ -1022,30 +1082,12 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)\n \treturn bitmap_weight(arm_pmu-\u003ecntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS);\n }\n \n-static void kvm_arm_set_nr_counters(struct kvm *kvm, unsigned int nr)\n-{\n-\tkvm-\u003earch.nr_pmu_counters = nr;\n-\n-\t/* Reset MDCR_EL2.HPMN behind the vcpus' back... */\n-\tif (test_bit(KVM_ARM_VCPU_HAS_EL2, kvm-\u003earch.vcpu_features)) {\n-\t\tstruct kvm_vcpu *vcpu;\n-\t\tunsigned long i;\n-\n-\t\tkvm_for_each_vcpu(i, vcpu, kvm) {\n-\t\t\tu64 val = __vcpu_sys_reg(vcpu, MDCR_EL2);\n-\t\t\tval \u0026= ~MDCR_EL2_HPMN;\n-\t\t\tval |= FIELD_PREP(MDCR_EL2_HPMN, kvm-\u003earch.nr_pmu_counters);\n-\t\t\t__vcpu_assign_sys_reg(vcpu, MDCR_EL2, val);\n-\t\t}\n-\t}\n-}\n-\n static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)\n {\n \tlockdep_assert_held(\u0026kvm-\u003earch.config_lock);\n \n \tkvm-\u003earch.arm_pmu = arm_pmu;\n-\tkvm_arm_set_nr_counters(kvm, kvm_arm_pmu_get_max_counters(kvm));\n+\tkvm-\u003earch.nr_pmu_counters = kvm_arm_pmu_get_max_counters(kvm);\n }\n \n /**\n@@ -1062,7 +1104,24 @@ static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)\n */\n int kvm_arm_set_default_pmu(struct kvm *kvm)\n {\n-\tstruct arm_pmu *arm_pmu = kvm_pmu_probe_armpmu();\n+\t/*\n+\t * It is safe to use a stale cpu to iterate the list of PMUs so long as\n+\t * the same value is used for the entirety of the loop. Given this, and\n+\t * the fact that no percpu data is used for the lookup there is no need\n+\t * to disable preemption.\n+\t *\n+\t * It is still necessary to get a valid cpu, though, to probe for the\n+\t * default PMU instance as userspace is not required to specify a PMU\n+\t * type. In order to uphold the preexisting behavior KVM selects the\n+\t * PMU instance for the core during vcpu init. A dependent use\n+\t * case would be a user with disdain of all things big.LITTLE that\n+\t * affines the VMM to a particular cluster of cores.\n+\t *\n+\t * In any case, userspace should just do the sane thing and use the UAPI\n+\t * to select a PMU type directly, or request fixed-counters-only\n+\t * emulation. But, be wary of the baggage being carried here.\n+\t */\n+\tstruct arm_pmu *arm_pmu = kvm_pmu_probe_armpmu(raw_smp_processor_id());\n \n \tif (!arm_pmu)\n \t\treturn -ENODEV;\n@@ -1079,17 +1138,19 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)\n \tint ret = -ENXIO;\n \n \tlockdep_assert_held(\u0026kvm-\u003earch.config_lock);\n-\tmutex_lock(\u0026arm_pmus_lock);\n+\tguard(rcu)();\n \n-\tlist_for_each_entry(entry, \u0026arm_pmus, entry) {\n+\tlist_for_each_entry_rcu(entry, \u0026arm_pmus, entry) {\n \t\tarm_pmu = entry-\u003earm_pmu;\n \t\tif (arm_pmu-\u003epmu.type == pmu_id) {\n \t\t\tif (kvm_vm_has_ran_once(kvm) ||\n+\t\t\t kvm_pmu_fixed_counters_only(kvm) ||\n \t\t\t (kvm-\u003earch.pmu_filter \u0026\u0026 kvm-\u003earch.arm_pmu != arm_pmu)) {\n \t\t\t\tret = -EBUSY;\n \t\t\t\tbreak;\n \t\t\t}\n \n+\t\t\tset_bit(KVM_ARCH_FLAG_PMU_V3_EXPLICIT, \u0026kvm-\u003earch.flags);\n \t\t\tkvm_arm_set_pmu(kvm, arm_pmu);\n \t\t\tcpumask_copy(kvm-\u003earch.supported_cpus, \u0026arm_pmu-\u003esupported_cpus);\n \t\t\tret = 0;\n@@ -1097,21 +1158,39 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)\n \t\t}\n \t}\n \n-\tmutex_unlock(\u0026arm_pmus_lock);\n \treturn ret;\n }\n \n+static int kvm_arm_pmu_v3_set_pmu_fixed_counters_only(struct kvm_vcpu *vcpu)\n+{\n+\tstruct kvm *kvm = vcpu-\u003ekvm;\n+\n+\tlockdep_assert_held(\u0026kvm-\u003earch.config_lock);\n+\n+\tif (kvm_vm_has_ran_once(kvm) || kvm-\u003earch.pmu_filter ||\n+\t test_bit(KVM_ARCH_FLAG_PMU_V3_EXPLICIT, \u0026kvm-\u003earch.flags))\n+\t\treturn -EBUSY;\n+\n+\tset_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, \u0026kvm-\u003earch.flags);\n+\tkvm-\u003earch.nr_pmu_counters = 0;\n+\n+\treturn 0;\n+}\n+\n static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned int n)\n {\n \tstruct kvm *kvm = vcpu-\u003ekvm;\n \n+\tif (kvm_vm_has_ran_once(kvm))\n+\t\treturn -EBUSY;\n+\n \tif (!kvm-\u003earch.arm_pmu)\n \t\treturn -EINVAL;\n \n \tif (n \u003e kvm_arm_pmu_get_max_counters(kvm))\n \t\treturn -EINVAL;\n \n-\tkvm_arm_set_nr_counters(kvm, n);\n+\tkvm-\u003earch.nr_pmu_counters = n;\n \treturn 0;\n }\n \n@@ -1175,7 +1254,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)\n \t\t filter.action != KVM_PMU_EVENT_DENY))\n \t\t\treturn -EINVAL;\n \n-\t\tif (kvm_vm_has_ran_once(kvm))\n+\t\tif (kvm_vm_has_ran_once(kvm) || kvm_pmu_fixed_counters_only(kvm))\n \t\t\treturn -EBUSY;\n \n \t\tif (!kvm-\u003earch.pmu_filter) {\n@@ -1220,6 +1299,8 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)\n \n \t\treturn kvm_arm_pmu_v3_set_nr_counters(vcpu, n);\n \t}\n+\tcase KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY:\n+\t\treturn kvm_arm_pmu_v3_set_pmu_fixed_counters_only(vcpu);\n \tcase KVM_ARM_VCPU_PMU_V3_INIT:\n \t\treturn kvm_arm_pmu_v3_init(vcpu);\n \t}\n@@ -1246,6 +1327,9 @@ int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)\n \t\tirq = vcpu-\u003earch.pmu.irq_num;\n \t\treturn put_user(irq, uaddr);\n \t}\n+\tcase KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY:\n+\t\tif (kvm_pmu_fixed_counters_only(vcpu-\u003ekvm))\n+\t\t\treturn 0;\n \t}\n \n \treturn -ENXIO;\n@@ -1259,6 +1343,7 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)\n \tcase KVM_ARM_VCPU_PMU_V3_FILTER:\n \tcase KVM_ARM_VCPU_PMU_V3_SET_PMU:\n \tcase KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS:\n+\tcase KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY:\n \t\tif (kvm_vcpu_has_pmu(vcpu))\n \t\t\treturn 0;\n \t}\ndiff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c\nindex b963fd975aacaf..6bfb21f0044424 100644\n--- a/arch/arm64/kvm/reset.c\n+++ b/arch/arm64/kvm/reset.c\n@@ -190,7 +190,8 @@ static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)\n void kvm_reset_vcpu(struct kvm_vcpu *vcpu)\n {\n \tstruct vcpu_reset_state reset_state;\n-\tbool loaded;\n+\tstruct kvm_vcpu *running;\n+\tbool loaded = false;\n \tu32 pstate;\n \n \tspin_lock(\u0026vcpu-\u003earch.mp_state_lock);\n@@ -198,10 +199,16 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu)\n \tvcpu-\u003earch.reset_state.reset = false;\n \tspin_unlock(\u0026vcpu-\u003earch.mp_state_lock);\n \n-\tpreempt_disable();\n-\tloaded = (vcpu-\u003ecpu != -1);\n-\tif (loaded)\n+\tguard(preempt)();\n+\n+\trunning = kvm_get_running_vcpu();\n+\tif (running) {\n+\t\tif (KVM_BUG_ON(running != vcpu, vcpu-\u003ekvm))\n+\t\t\treturn;\n+\n+\t\tloaded = true;\n \t\tkvm_arch_vcpu_put(vcpu);\n+\t}\n \n \tif (!kvm_arm_vcpu_sve_finalized(vcpu)) {\n \t\tif (vcpu_has_feature(vcpu, KVM_ARM_VCPU_SVE))\n@@ -269,7 +276,6 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu)\n \n \tif (loaded)\n \t\tkvm_arch_vcpu_load(vcpu, smp_processor_id());\n-\tpreempt_enable();\n }\n \n u32 kvm_get_pa_bits(struct kvm *kvm)\ndiff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c\nindex 5d5c579d457902..9e905d9be9f67e 100644\n--- a/arch/arm64/kvm/sys_regs.c\n+++ b/arch/arm64/kvm/sys_regs.c\n@@ -3020,17 +3020,22 @@ static bool access_mdcr(struct kvm_vcpu *vcpu,\n \t}\n \n \t__vcpu_assign_sys_reg(vcpu, MDCR_EL2, val);\n-\n-\t/*\n-\t * Request a reload of the PMU to enable/disable the counters\n-\t * affected by HPME.\n-\t */\n-\tif ((old ^ val) \u0026 MDCR_EL2_HPME)\n-\t\tkvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);\n+\tkvm_pmu_handle_mdcr(vcpu, old, val);\n \n \treturn true;\n }\n \n+static int set_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,\n+\t\t u64 val)\n+{\n+\tu64 old = __vcpu_sys_reg(vcpu, MDCR_EL2);\n+\n+\t__vcpu_assign_sys_reg(vcpu, MDCR_EL2, val);\n+\tkvm_pmu_handle_mdcr(vcpu, old, val);\n+\n+\treturn 0;\n+}\n+\n static bool access_ras(struct kvm_vcpu *vcpu,\n \t\t struct sys_reg_params *p,\n \t\t const struct sys_reg_desc *r)\n@@ -3177,8 +3182,13 @@ static int set_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,\n \n static u64 reset_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)\n {\n-\t__vcpu_assign_sys_reg(vcpu, r-\u003ereg, vcpu-\u003ekvm-\u003earch.nr_pmu_counters);\n-\treturn vcpu-\u003ekvm-\u003earch.nr_pmu_counters;\n+\tu64 old = __vcpu_sys_reg(vcpu, r-\u003ereg);\n+\tu64 val = vcpu-\u003ekvm-\u003earch.nr_pmu_counters;\n+\n+\t__vcpu_assign_sys_reg(vcpu, r-\u003ereg, val);\n+\tkvm_pmu_handle_mdcr(vcpu, old, val);\n+\n+\treturn val;\n }\n \n /*\n@@ -3741,7 +3751,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {\n \tEL2_REG_FILTERED(SCTLR2_EL2, access_vm_reg, reset_val, 0,\n \t\t\t sctlr2_el2_visibility),\n \tEL2_REG_VNCR(HCR_EL2, reset_hcr, 0),\n-\tEL2_REG(MDCR_EL2, access_mdcr, reset_mdcr, 0),\n+\tSYS_REG_USER_FILTER(MDCR_EL2, access_mdcr, reset_mdcr, 0,\n+\t\t\t NULL, set_mdcr, el2_visibility),\n \tEL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1),\n \tEL2_REG_VNCR(HSTR_EL2, reset_val, 0),\n \tEL2_REG_VNCR_FILT(HFGRTR_EL2, fgt_visibility),\ndiff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h\nindex b5e5942204fc69..0c31b72e3435a5 100644\n--- a/include/kvm/arm_pmu.h\n+++ b/include/kvm/arm_pmu.h\n@@ -32,6 +32,7 @@ struct kvm_pmu {\n \tstruct kvm_pmc pmc[KVM_ARMV8_PMU_MAX_COUNTERS];\n \tint irq_num;\n \tbool created;\n+\tbool events_need_recreate;\n };\n \n struct arm_pmu_entry {\n@@ -56,8 +57,10 @@ bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu);\n bool kvm_pmu_update_run(struct kvm_vcpu *vcpu);\n void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val);\n void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val);\n+void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val);\n void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,\n \t\t\t\t u64 select_idx);\n+void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu, int last_cpu);\n void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu);\n int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu,\n \t\t\t struct kvm_device_attr *attr);\n@@ -133,6 +136,7 @@ static inline bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu)\n static inline bool kvm_pmu_update_run(struct kvm_vcpu *vcpu) { return false; }\n static inline void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val) {}\n static inline void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) {}\n+static inline void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val) {}\n static inline void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu,\n \t\t\t\t\t\t u64 data, u64 select_idx) {}\n static inline int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu,\n@@ -163,6 +167,7 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)\n static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {}\n static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {}\n static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {}\n+static inline void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu, int last_cpu) {}\n static inline void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) {}\n static inline u8 kvm_arm_pmu_get_pmuver_limit(void)\n {\ndiff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h\nindex 1c13bfa2d38aad..39a1a1e412e66f 100644\n--- a/tools/arch/arm64/include/uapi/asm/kvm.h\n+++ b/tools/arch/arm64/include/uapi/asm/kvm.h\n@@ -437,6 +437,7 @@ enum {\n #define KVM_ARM_VCPU_PMU_V3_FILTER\t\t2\n #define KVM_ARM_VCPU_PMU_V3_SET_PMU\t\t3\n #define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS\t4\n+#define KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY\t5\n #define KVM_ARM_VCPU_TIMER_CTRL\t\t1\n #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER\t\t0\n #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER\t\t1\ndiff --git a/tools/include/linux/bitfield.h b/tools/include/linux/bitfield.h\nindex ddf81f24956ba0..1c633343d57668 100644\n--- a/tools/include/linux/bitfield.h\n+++ b/tools/include/linux/bitfield.h\n@@ -142,32 +142,32 @@ static __always_inline u64 field_mask(u64 field)\n \treturn field / field_multiplier(field);\n }\n #define field_max(field)\t((typeof(field))field_mask(field))\n-#define ____MAKE_OP(type,base,to,from)\t\t\t\t\t\\\n-static __always_inline __##type type##_encode_bits(base v, base field)\t\\\n+#define ____MAKE_OP(name,type,base,to,from)\t\t\t\t\\\n+static __always_inline type name##_encode_bits(base v, base field)\t\\\n {\t\t\t\t\t\t\t\t\t\\\n \tif (__builtin_constant_p(v) \u0026\u0026 (v \u0026 ~field_mask(field)))\t\\\n \t\t__field_overflow();\t\t\t\t\t\\\n \treturn to((v \u0026 field_mask(field)) * field_multiplier(field));\t\\\n }\t\t\t\t\t\t\t\t\t\\\n-static __always_inline __##type type##_replace_bits(__##type old,\t\\\n-\t\t\t\t\tbase val, base field)\t\t\\\n+static __always_inline type name##_replace_bits(type old,\t\t\\\n+\t\t\t\t\t\tbase val, base field)\t\\\n {\t\t\t\t\t\t\t\t\t\\\n-\treturn (old \u0026 ~to(field)) | type##_encode_bits(val, field);\t\\\n+\treturn (old \u0026 ~to(field)) | name##_encode_bits(val, field);\t\\\n }\t\t\t\t\t\t\t\t\t\\\n-static __always_inline void type##p_replace_bits(__##type *p,\t\t\\\n+static __always_inline void name##p_replace_bits(type *p,\t\t\\\n \t\t\t\t\tbase val, base field)\t\t\\\n {\t\t\t\t\t\t\t\t\t\\\n-\t*p = (*p \u0026 ~to(field)) | type##_encode_bits(val, field);\t\\\n+\t*p = (*p \u0026 ~to(field)) | name##_encode_bits(val, field);\t\\\n }\t\t\t\t\t\t\t\t\t\\\n-static __always_inline base type##_get_bits(__##type v, base field)\t\\\n+static __always_inline base name##_get_bits(type v, base field)\t\\\n {\t\t\t\t\t\t\t\t\t\\\n \treturn (from(v) \u0026 field)/field_multiplier(field);\t\t\\\n }\n-#define __MAKE_OP(size)\t\t\t\t\t\t\t\\\n-\t____MAKE_OP(le##size,u##size,cpu_to_le##size,le##size##_to_cpu)\t\\\n-\t____MAKE_OP(be##size,u##size,cpu_to_be##size,be##size##_to_cpu)\t\\\n-\t____MAKE_OP(u##size,u##size,,)\n-____MAKE_OP(u8,u8,,)\n+#define __MAKE_OP(size)\t\t\t\t\t\t\t\t\t\\\n+\t____MAKE_OP(le##size,__le##size,u##size,cpu_to_le##size,le##size##_to_cpu)\t\\\n+\t____MAKE_OP(be##size,__be##size,u##size,cpu_to_be##size,be##size##_to_cpu)\t\\\n+\t____MAKE_OP(u##size,u##size,u##size,,)\n+____MAKE_OP(u8,u8,u8,,)\n __MAKE_OP(16)\n __MAKE_OP(32)\n __MAKE_OP(64)\ndiff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c\nindex 22223395969e0f..b5f8ce8d3451f2 100644\n--- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c\n+++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c\n@@ -25,6 +25,14 @@\n /* The cycle counter bit position that's common among the PMU registers */\n #define ARMV8_PMU_CYCLE_IDX\t\t31\n \n+#ifndef MDCR_EL2_HPMN\n+#define MDCR_EL2_HPMN\t\t\tGENMASK_ULL(4, 0)\n+#endif\n+\n+#ifndef MDCR_EL2_HPME\n+#define MDCR_EL2_HPME\t\t\tBIT_ULL(7)\n+#endif\n+\n struct vpmu_vm {\n \tstruct kvm_vm *vm;\n \tstruct kvm_vcpu *vcpu;\n@@ -403,12 +411,7 @@ static void create_vpmu_vm(void *guest_code)\n {\n \tstruct kvm_vcpu_init init;\n \tu8 pmuver, ec;\n-\tu64 dfr0, irq = 23;\n-\tstruct kvm_device_attr irq_attr = {\n-\t\t.group = KVM_ARM_VCPU_PMU_V3_CTRL,\n-\t\t.attr = KVM_ARM_VCPU_PMU_V3_IRQ,\n-\t\t.addr = (u64)\u0026irq,\n-\t};\n+\tu64 dfr0;\n \n \t/* The test creates the vpmu_vm multiple times. Ensure a clean state */\n \tmemset(\u0026vpmu_vm, 0, sizeof(vpmu_vm));\n@@ -434,8 +437,6 @@ static void create_vpmu_vm(void *guest_code)\n \tTEST_ASSERT(pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF \u0026\u0026\n \t\t pmuver \u003e= ID_AA64DFR0_EL1_PMUVer_IMP,\n \t\t \"Unexpected PMUVER (0x%x) on the vCPU with PMUv3\", pmuver);\n-\n-\tvcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, \u0026irq_attr);\n }\n \n static void destroy_vpmu_vm(void)\n@@ -461,15 +462,17 @@ static void run_vcpu(struct kvm_vcpu *vcpu, u64 pmcr_n)\n \t}\n }\n \n-static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool expect_fail)\n+static void guest_code_done(void)\n+{\n+\tGUEST_DONE();\n+}\n+\n+static void set_nr_counters(struct kvm_vcpu *vcpu,\n+\t\t\t unsigned int nr_counters, bool expect_fail)\n {\n-\tstruct kvm_vcpu *vcpu;\n \tunsigned int prev;\n \tint ret;\n \n-\tcreate_vpmu_vm(guest_code);\n-\tvcpu = vpmu_vm.vcpu;\n-\n \tprev = get_pmcr_n(vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0)));\n \n \tret = __vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n@@ -481,6 +484,26 @@ static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool\n \t\t\t nr_counters, prev);\n \telse\n \t\tTEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));\n+}\n+\n+static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters,\n+\t\t\t\t\t\t bool fixed_counters_only,\n+\t\t\t\t\t\t bool expect_fail)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tu64 irq = 23;\n+\n+\tcreate_vpmu_vm(guest_code);\n+\tvcpu = vpmu_vm.vcpu;\n+\n+\tif (fixed_counters_only)\n+\t\tvcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t\t KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);\n+\n+\tvcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_IRQ, \u0026irq);\n+\n+\tset_nr_counters(vcpu, nr_counters, expect_fail);\n \n \tvcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, NULL);\n }\n@@ -489,15 +512,15 @@ static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool\n * Create a guest with one vCPU, set the PMCR_EL0.N for the vCPU to @pmcr_n,\n * and run the test.\n */\n-static void run_access_test(u64 pmcr_n)\n+static void run_access_test(u64 pmcr_n, bool fixed_counters_only)\n {\n \tu64 sp;\n \tstruct kvm_vcpu *vcpu;\n \tstruct kvm_vcpu_init init;\n \n-\tpr_debug(\"Test with pmcr_n %lu\\n\", pmcr_n);\n+\tpr_debug(\"Test with pmcr_n %lu, fixed_counters_only %d\\n\", pmcr_n, fixed_counters_only);\n \n-\ttest_create_vpmu_vm_with_nr_counters(pmcr_n, false);\n+\ttest_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, false);\n \tvcpu = vpmu_vm.vcpu;\n \n \t/* Save the initial sp to restore them later to run the guest again */\n@@ -531,14 +554,14 @@ static struct pmreg_sets validity_check_reg_sets[] = {\n * Create a VM, and check if KVM handles the userspace accesses of\n * the PMU register sets in @validity_check_reg_sets[] correctly.\n */\n-static void run_pmregs_validity_test(u64 pmcr_n)\n+static void run_pmregs_validity_test(u64 pmcr_n, bool fixed_counters_only)\n {\n \tint i;\n \tstruct kvm_vcpu *vcpu;\n \tu64 set_reg_id, clr_reg_id, reg_val;\n \tu64 valid_counters_mask, max_counters_mask;\n \n-\ttest_create_vpmu_vm_with_nr_counters(pmcr_n, false);\n+\ttest_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, false);\n \tvcpu = vpmu_vm.vcpu;\n \n \tvalid_counters_mask = get_counters_mask(pmcr_n);\n@@ -583,16 +606,94 @@ static void run_pmregs_validity_test(u64 pmcr_n)\n \tdestroy_vpmu_vm();\n }\n \n+static void run_mdcr_el2_validity_test(u64 pmcr_n, bool fixed_counters_only)\n+{\n+\tstruct kvm_vcpu_init init;\n+\tstruct kvm_vcpu *vcpu;\n+\tu64 expected_mdcr, mdcr;\n+\tu64 irq = 23;\n+\n+\tpr_debug(\"MDCR_EL2 test with pmcr_n %lu, fixed_counters_only %d\\n\",\n+\t\t pmcr_n, fixed_counters_only);\n+\n+\tcreate_vpmu_vm(guest_code);\n+\tif (!vm_supports_el2(vpmu_vm.vm)) {\n+\t\tdestroy_vpmu_vm();\n+\t\treturn;\n+\t}\n+\n+\tvcpu = vpmu_vm.vcpu;\n+\n+\tmdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));\n+\texpected_mdcr = u64_replace_bits(mdcr, FIELD_MAX(MDCR_EL2_HPMN),\n+\t\t\t\t\t MDCR_EL2_HPMN);\n+\tvcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),\n+\t\t expected_mdcr);\n+\n+\tmdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));\n+\tTEST_ASSERT(mdcr == expected_mdcr,\n+\t\t \"MDCR_EL2 was not properly updated after HPMN write (expected 0x%lx, got 0x%lx)\",\n+\t\t expected_mdcr, mdcr);\n+\n+\tif (fixed_counters_only) {\n+\t\tvcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t\t KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);\n+\n+\t\tmdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));\n+\t\tTEST_ASSERT(mdcr == expected_mdcr,\n+\t\t\t \"MDCR_EL2 changed after PMU_V3_FIXED_COUNTERS_ONLY (expected 0x%lx, got 0x%lx)\",\n+\t\t\t expected_mdcr, mdcr);\n+\t}\n+\n+\tset_nr_counters(vcpu, pmcr_n, false);\n+\n+\tmdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));\n+\tTEST_ASSERT(mdcr == expected_mdcr,\n+\t\t \"MDCR_EL2 changed after PMU_V3_SET_NR_COUNTERS (expected 0x%lx, got 0x%lx)\",\n+\t\t expected_mdcr, mdcr);\n+\n+\tvcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_IRQ, \u0026irq);\n+\n+\tvcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_INIT, NULL);\n+\n+\tmdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));\n+\tTEST_ASSERT(mdcr == expected_mdcr,\n+\t\t \"MDCR_EL2 changed after PMU_V3_INIT (expected 0x%lx, got 0x%lx)\",\n+\t\t expected_mdcr, mdcr);\n+\n+\texpected_mdcr = mdcr ^ MDCR_EL2_HPME;\n+\tvcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),\n+\t\t expected_mdcr);\n+\n+\tmdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));\n+\tTEST_ASSERT(mdcr == expected_mdcr,\n+\t\t \"MDCR_EL2 was not properly updated after HPME write (expected 0x%lx, got 0x%lx)\",\n+\t\t expected_mdcr, mdcr);\n+\n+\tkvm_get_default_vcpu_target(vpmu_vm.vm, \u0026init);\n+\tinit.features[0] |= (1 \u003c\u003c KVM_ARM_VCPU_PMU_V3);\n+\taarch64_vcpu_setup(vcpu, \u0026init);\n+\n+\tmdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));\n+\tTEST_ASSERT(FIELD_GET(MDCR_EL2_HPMN, mdcr) == pmcr_n,\n+\t\t \"MDCR_EL2.HPMN is not reset after INIT (expected %lu, got %lu)\",\n+\t\t pmcr_n, FIELD_GET(MDCR_EL2_HPMN, mdcr));\n+\n+\tdestroy_vpmu_vm();\n+}\n+\n /*\n * Create a guest with one vCPU, and attempt to set the PMCR_EL0.N for\n * the vCPU to @pmcr_n, which is larger than the host value.\n * The attempt should fail as @pmcr_n is too big to set for the vCPU.\n */\n-static void run_error_test(u64 pmcr_n)\n+static void run_error_test(u64 pmcr_n, bool fixed_counters_only)\n {\n \tpr_debug(\"Error test with pmcr_n %lu (larger than the host)\\n\", pmcr_n);\n \n-\ttest_create_vpmu_vm_with_nr_counters(pmcr_n, true);\n+\ttest_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, true);\n \tdestroy_vpmu_vm();\n }\n \n@@ -622,22 +723,147 @@ static bool kvm_supports_nr_counters_attr(void)\n \treturn supported;\n }\n \n-int main(void)\n+static void test_set_nr_counters_after_vcpu_run(void)\n {\n-\tu64 i, pmcr_n;\n+\tstruct kvm_vcpu *running_vcpu, *stopped_vcpu;\n+\tunsigned int nr_counters = 0;\n+\tstruct kvm_vcpu_init init;\n+\tstruct kvm_vm *vm;\n+\tint ret;\n+\tu64 irq = 23;\n+\n+\tvm = vm_create(2);\n+\tvm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, \u0026init);\n+\tinit.features[0] |= BIT(KVM_ARM_VCPU_PMU_V3);\n+\trunning_vcpu = aarch64_vcpu_add(vm, 0, \u0026init, guest_code_done);\n+\tstopped_vcpu = aarch64_vcpu_add(vm, 1, \u0026init, guest_code_done);\n+\tkvm_arch_vm_finalize_vcpus(vm);\n+\n+\tvcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_IRQ, \u0026irq);\n+\tvcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_INIT, NULL);\n+\tvcpu_run(running_vcpu);\n+\n+\tret = __vcpu_device_attr_set(stopped_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t\t KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS,\n+\t\t\t\t \u0026nr_counters);\n+\tTEST_ASSERT(ret == -1 \u0026\u0026 errno == EBUSY,\n+\t\t KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));\n+\n+\tkvm_vm_free(vm);\n+}\n \n-\tTEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3));\n-\tTEST_REQUIRE(kvm_supports_vgic_v3());\n-\tTEST_REQUIRE(kvm_supports_nr_counters_attr());\n+static void test_config(u64 pmcr_n, bool fixed_counters_only)\n+{\n+\tu64 i;\n \n-\tpmcr_n = get_pmcr_n_limit();\n \tfor (i = 0; i \u003c= pmcr_n; i++) {\n-\t\trun_access_test(i);\n-\t\trun_pmregs_validity_test(i);\n+\t\trun_access_test(i, fixed_counters_only);\n+\t\trun_pmregs_validity_test(i, fixed_counters_only);\n+\t\trun_mdcr_el2_validity_test(i, fixed_counters_only);\n \t}\n \n \tfor (i = pmcr_n + 1; i \u003c ARMV8_PMU_MAX_COUNTERS; i++)\n-\t\trun_error_test(i);\n+\t\trun_error_test(i, fixed_counters_only);\n+}\n+\n+static void test_fixed_counters_only(void)\n+{\n+\tstruct kvm_pmu_event_filter filter = { .nevents = 0 };\n+\tstruct kvm_vm *vm;\n+\tstruct kvm_vcpu *running_vcpu;\n+\tstruct kvm_vcpu *stopped_vcpu;\n+\tstruct kvm_vcpu_init init;\n+\tint ret;\n+\tu64 irq = 23;\n+\n+\tcreate_vpmu_vm(guest_code);\n+\tret = __vcpu_has_device_attr(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t\t KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY);\n+\tif (ret) {\n+\t\tTEST_ASSERT(ret == -1 \u0026\u0026 errno == ENXIO,\n+\t\t\t KVM_IOCTL_ERROR(KVM_HAS_DEVICE_ATTR, ret));\n+\t\tdestroy_vpmu_vm();\n+\t\treturn;\n+\t}\n+\n+\t/* Assert that FIXED_COUNTERS_ONLY is unset at initialization. */\n+\tret = __vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t\t KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);\n+\tTEST_ASSERT(ret == -1 \u0026\u0026 errno == ENXIO,\n+\t\t KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret));\n+\n+\t/* Assert that setting FIXED_COUNTERS_ONLY succeeds. */\n+\tvcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);\n+\n+\t/* Assert that FIXED_COUNTERS_ONLY is set. */\n+\tvcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);\n+\n+\t/*\n+\t * Setting an event filter when FIXED_COUNTERS_ONLY has already been set\n+\t * results in EBUSY.\n+\t */\n+\tret = __vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t\t KVM_ARM_VCPU_PMU_V3_FILTER, \u0026filter);\n+\tTEST_ASSERT(ret == -1 \u0026\u0026 errno == EBUSY,\n+\t\t KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));\n+\n+\tdestroy_vpmu_vm();\n+\n+\tcreate_vpmu_vm(guest_code);\n+\n+\t/*\n+\t * Assert that setting FIXED_COUNTERS_ONLY when an event filter has\n+\t * already been set results in EBUSY.\n+\t */\n+\tvcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_FILTER, \u0026filter);\n+\n+\tret = __vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t\t KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);\n+\tTEST_ASSERT(ret == -1 \u0026\u0026 errno == EBUSY,\n+\t\t KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));\n+\n+\tdestroy_vpmu_vm();\n+\n+\t/*\n+\t * Assert that setting FIXED_COUNTERS_ONLY when a VCPU has already run\n+\t * results in EBUSY.\n+\t */\n+\tvm = vm_create(2);\n+\tvm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, \u0026init);\n+\tinit.features[0] |= (1 \u003c\u003c KVM_ARM_VCPU_PMU_V3);\n+\trunning_vcpu = aarch64_vcpu_add(vm, 0, \u0026init, guest_code_done);\n+\tstopped_vcpu = aarch64_vcpu_add(vm, 1, \u0026init, guest_code_done);\n+\tkvm_arch_vm_finalize_vcpus(vm);\n+\tvcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_IRQ, \u0026irq);\n+\tvcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t KVM_ARM_VCPU_PMU_V3_INIT, NULL);\n+\tvcpu_run(running_vcpu);\n+\n+\tret = __vcpu_device_attr_set(stopped_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,\n+\t\t\t\t KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);\n+\tTEST_ASSERT(ret == -1 \u0026\u0026 errno == EBUSY,\n+\t\t KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));\n+\n+\tkvm_vm_free(vm);\n+\n+\ttest_config(0, true);\n+}\n+\n+int main(void)\n+{\n+\tTEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3));\n+\tTEST_REQUIRE(kvm_supports_vgic_v3());\n+\tTEST_REQUIRE(kvm_supports_nr_counters_attr());\n+\n+\ttest_set_nr_counters_after_vcpu_run();\n+\ttest_config(get_pmcr_n_limit(), false);\n+\ttest_fixed_counters_only();\n \n \treturn 0;\n }\n"
} |
| 2/1 | 2026/07/20 06:14 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"KVM",
"PERF_EVENTS"
],
"FocusSymbols": [
"kvm_pmu_handle_mdcr",
"kvm_vcpu_load_pmu",
"kvm_pmu_create_perf_event_with_pmu",
"kvm_arm_pmu_v3_set_pmu_fixed_counters_only",
"kvm_arm_pmu_v3_set_nr_counters",
"kvm_pmu_probe_armpmu"
],
"Reasoning": "The patch introduces a new KVM ARM64 PMU emulation feature (KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY) and modifies PMU event creation, loading, and reloading logic. This is reachable from userspace via KVM ioctls and modifies core KVM PMU emulation logic.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit e3a8c7074b50b165a55190fe29bac604faf26de6
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Jul 20 06:14:43 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst
index 66e714f2fcfa7f..1d592e70bdea3d 100644
--- a/Documentation/virt/kvm/devices/vcpu.rst
+++ b/Documentation/virt/kvm/devices/vcpu.rst
@@ -74,7 +74,8 @@ irqchip.
-ENODEV PMUv3 not supported or GIC not initialized
-ENXIO PMUv3 not properly configured or in-kernel irqchip not
configured as required prior to calling this attribute
- -EBUSY PMUv3 already initialized or a VCPU has already run
+ -EBUSY PMUv3 already initialized, a VCPU has already run or
+ FIXED_COUNTERS_ONLY has already been set
-EINVAL Invalid filter range
======= ======================================================
@@ -116,14 +117,14 @@ using event 0x11 (CPU_CYCLES).
:Returns:
- ======= ====================================================
+ ======= ===========================================================
-EBUSY PMUv3 already initialized, a VCPU has already run or
- an event filter has already been set
+ an event filter or FIXED_COUNTERS_ONLY has already been set
-EFAULT Error accessing the PMU identifier
-ENXIO PMU not found
-ENODEV PMUv3 not supported or GIC not initialized
-ENOMEM Could not allocate memory
- ======= ====================================================
+ ======= ===========================================================
Request that the VCPU uses the specified hardware PMU when creating guest events
for the purpose of PMU emulation. The PMU identifier can be read from the "type"
@@ -165,6 +166,27 @@ explicitly selected, or the number of counters is out of range for the
selected PMU. Selecting a new PMU cancels the effect of setting this
attribute.
+1.6 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY
+------------------------------------------------------
+
+:Parameters: no additional parameter in kvm_device_attr.addr
+
+:Returns:
+
+ ======= ==================================================
+ -EBUSY PMUv3 already initialized, a VCPU has already run,
+ an event filter has already been set or
+ a hardware PMU has already been specified
+ -ENXIO Attempted to get before setting
+ -ENODEV Attempted to set while PMUv3 not supported
+ ======= ==================================================
+
+If set, KVM emulates PMUv3 without programmable event counters.
+
+When this attribute is enabled, the vCPU can run on any physical CPU
+that has a PMU, regardless of the underlying implementation. This
+attribute is VM-scoped.
+
2. GROUP: KVM_ARM_VCPU_TIMER_CTRL
=================================
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bae2c4f92ef5c0..3998e66c657150 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -367,6 +367,10 @@ struct kvm_arch {
#define KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS 10
/* Unhandled SEAs are taken to userspace */
#define KVM_ARCH_FLAG_EXIT_SEA 11
+ /* PMUv3 is emulated with an explicitly specified hardware PMU */
+#define KVM_ARCH_FLAG_PMU_V3_EXPLICIT 12
+ /* PMUv3 is emulated without programmable event counters */
+#define KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY 13
unsigned long flags;
/* VM-wide vCPU feature set */
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 1c13bfa2d38aad..39a1a1e412e66f 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -437,6 +437,7 @@ enum {
#define KVM_ARM_VCPU_PMU_V3_FILTER 2
#define KVM_ARM_VCPU_PMU_V3_SET_PMU 3
#define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4
+#define KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY 5
#define KVM_ARM_VCPU_TIMER_CTRL 1
#define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0
#define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 1995e1198648e8..a5ae628204a122 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -1005,7 +1005,7 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
{
.desc = "Apple IMPDEF PMUv3 Traps",
.capability = ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS,
- .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
+ .type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE,
.matches = has_impdef_pmuv3,
.cpu_enable = cpu_enable_impdef_pmuv3_traps,
},
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 50adfff75be82e..a866beb8d873d3 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -651,6 +651,7 @@ static bool kvm_vcpu_should_clear_twe(struct kvm_vcpu *vcpu)
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
struct kvm_s2_mmu *mmu;
+ int last_cpu = vcpu->cpu;
int *last_ran;
if (is_protected_kvm_enabled())
@@ -700,6 +701,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
if (has_vhe())
kvm_vcpu_load_vhe(vcpu);
kvm_arch_vcpu_load_fp(vcpu);
+ kvm_vcpu_load_pmu(vcpu, last_cpu);
kvm_vcpu_pmu_restore_guest(vcpu);
if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
@@ -749,7 +751,6 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
kvm_arm_vmid_clear_active();
vcpu_clear_on_unsupported_cpu(vcpu);
- vcpu->cpu = -1;
}
static void __kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu)
@@ -1652,29 +1653,26 @@ static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
{
unsigned long features = init->features[0];
struct kvm *kvm = vcpu->kvm;
- int ret = -EINVAL;
+ int ret;
- mutex_lock(&kvm->arch.config_lock);
+ lockdep_assert_held(&kvm->arch.config_lock);
if (test_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags) &&
kvm_vcpu_init_changed(vcpu, init))
- goto out_unlock;
+ return -EINVAL;
bitmap_copy(kvm->arch.vcpu_features, &features, KVM_VCPU_MAX_FEATURES);
ret = kvm_setup_vcpu(vcpu);
if (ret)
- goto out_unlock;
+ return ret;
/* Now we know what it is, we can reset it. */
kvm_reset_vcpu(vcpu);
set_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags);
vcpu_set_flag(vcpu, VCPU_INITIALIZED);
- ret = 0;
-out_unlock:
- mutex_unlock(&kvm->arch.config_lock);
- return ret;
+ return 0;
}
static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
@@ -1690,6 +1688,8 @@ static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
if (ret)
return ret;
+ guard(mutex)(&vcpu->kvm->arch.config_lock);
+
if (!kvm_vcpu_initialized(vcpu))
return __kvm_vcpu_set_target(vcpu, init);
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 98305bbfc095ab..ff5d5f66a9c09f 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -7,9 +7,9 @@
#include <linux/cpu.h>
#include <linux/kvm.h>
#include <linux/kvm_host.h>
-#include <linux/list.h>
#include <linux/perf_event.h>
#include <linux/perf/arm_pmu.h>
+#include <linux/rculist.h>
#include <linux/uaccess.h>
#include <asm/kvm_emulate.h>
#include <kvm/arm_pmu.h>
@@ -17,6 +17,13 @@
#define PERF_ATTR_CFG1_COUNTER_64BIT BIT(0)
+#define MDCR_EL2_PMU_RECREATE_MASK (MDCR_EL2_HPMN | MDCR_EL2_HPMD | \
+ MDCR_EL2_HLP)
+
+/*
+ * arm_pmus is append-only. kvm_supports_guest_pmuv3() feeds persistent
+ * VM state, so a true result must remain valid after the check.
+ */
static LIST_HEAD(arm_pmus);
static DEFINE_MUTEX(arm_pmus_lock);
@@ -26,7 +33,6 @@ static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc);
bool kvm_supports_guest_pmuv3(void)
{
- guard(mutex)(&arm_pmus_lock);
return !list_empty(&arm_pmus);
}
@@ -80,6 +86,11 @@ u64 kvm_pmu_evtyper_mask(struct kvm *kvm)
return mask;
}
+static bool kvm_pmu_fixed_counters_only(struct kvm *kvm)
+{
+ return test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &kvm->arch.flags);
+}
+
/**
* kvm_pmc_is_64bit - determine if counter is 64bit
* @pmc: counter context
@@ -317,17 +328,22 @@ u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu)
u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu)
{
- u64 val = FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu));
+ u64 n = vcpu->kvm->arch.nr_pmu_counters;
- if (val == 0)
+ if (n == 0)
return BIT(ARMV8_PMU_CYCLE_IDX);
else
- return GENMASK(val - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX);
+ return GENMASK(n - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX);
}
static void kvm_pmc_enable_perf_event(struct kvm_pmc *pmc)
{
- if (!pmc->perf_event) {
+ struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
+
+ if (!pmc->perf_event ||
+ (kvm_pmu_fixed_counters_only(vcpu->kvm) &&
+ !cpumask_test_cpu(READ_ONCE(vcpu->cpu),
+ &to_arm_pmu(pmc->perf_event->pmu)->supported_cpus))) {
kvm_pmu_create_perf_event(pmc);
return;
}
@@ -606,6 +622,21 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
}
}
+void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val)
+{
+ u64 changed = old ^ val;
+
+ /*
+ * HPMN determines which counters HPMD and HLP apply to. Changes to
+ * these fields require new perf event filters and sample periods.
+ */
+ if (changed & MDCR_EL2_PMU_RECREATE_MASK)
+ vcpu->arch.pmu.events_need_recreate = true;
+
+ if (changed & (MDCR_EL2_HPME | MDCR_EL2_PMU_RECREATE_MASK))
+ kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
+}
+
static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc)
{
struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
@@ -649,10 +680,8 @@ static bool kvm_pmc_counts_at_el2(struct kvm_pmc *pmc)
return kvm_pmc_read_evtreg(pmc) & ARMV8_PMU_INCLUDE_EL2;
}
-static int kvm_map_pmu_event(struct kvm *kvm, unsigned int eventsel)
+static int kvm_map_pmu_event(struct arm_pmu *pmu, unsigned int eventsel)
{
- struct arm_pmu *pmu = kvm->arch.arm_pmu;
-
/*
* The CPU PMU likely isn't PMUv3; let the driver provide a mapping
* for the guest's PMUv3 event ID.
@@ -663,14 +692,27 @@ static int kvm_map_pmu_event(struct kvm *kvm, unsigned int eventsel)
return eventsel;
}
-/**
- * kvm_pmu_create_perf_event - create a perf event for a counter
- * @pmc: Counter context
- */
-static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
+static struct arm_pmu *kvm_pmu_probe_armpmu(int cpu)
+{
+ struct arm_pmu_entry *entry;
+ struct arm_pmu *pmu;
+
+ guard(rcu)();
+
+ list_for_each_entry_rcu(entry, &arm_pmus, entry) {
+ pmu = entry->arm_pmu;
+
+ if (cpumask_test_cpu(cpu, &pmu->supported_cpus))
+ return pmu;
+ }
+
+ return NULL;
+}
+
+static void kvm_pmu_create_perf_event_with_pmu(struct kvm_pmc *pmc,
+ struct arm_pmu *arm_pmu)
{
struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
- struct arm_pmu *arm_pmu = vcpu->kvm->arch.arm_pmu;
struct perf_event *event;
struct perf_event_attr attr;
int eventsel;
@@ -704,7 +746,7 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
* Don't create an event if we're running on hardware that requires
* PMUv3 event translation and we couldn't find a valid mapping.
*/
- eventsel = kvm_map_pmu_event(vcpu->kvm, eventsel);
+ eventsel = kvm_map_pmu_event(arm_pmu, eventsel);
if (eventsel < 0)
return;
@@ -749,6 +791,32 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
pmc->perf_event = event;
}
+/**
+ * kvm_pmu_create_perf_event - create a perf event for a counter
+ * @pmc: Counter context
+ */
+static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
+{
+ struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
+ struct arm_pmu *arm_pmu = vcpu->kvm->arch.arm_pmu;
+
+ if (kvm_pmu_fixed_counters_only(vcpu->kvm)) {
+ do {
+ arm_pmu = kvm_pmu_probe_armpmu(READ_ONCE(vcpu->cpu));
+
+ if (!arm_pmu) {
+ pr_warn_once("kvm: Unsupported PMU variation detected.\n");
+ add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+ return;
+ }
+
+ kvm_pmu_create_perf_event_with_pmu(pmc, arm_pmu);
+ } while (!cpumask_test_cpu(READ_ONCE(vcpu->cpu), &arm_pmu->supported_cpus));
+ } else {
+ kvm_pmu_create_perf_event_with_pmu(pmc, arm_pmu);
+ }
+}
+
/**
* kvm_pmu_set_counter_event_type - set selected counter to monitor some event
* @vcpu: The vcpu pointer
@@ -789,43 +857,7 @@ void kvm_host_pmu_init(struct arm_pmu *pmu)
return;
entry->arm_pmu = pmu;
- list_add_tail(&entry->entry, &arm_pmus);
-}
-
-static struct arm_pmu *kvm_pmu_probe_armpmu(void)
-{
- struct arm_pmu_entry *entry;
- struct arm_pmu *pmu;
- int cpu;
-
- guard(mutex)(&arm_pmus_lock);
-
- /*
- * It is safe to use a stale cpu to iterate the list of PMUs so long as
- * the same value is used for the entirety of the loop. Given this, and
- * the fact that no percpu data is used for the lookup there is no need
- * to disable preemption.
- *
- * It is still necessary to get a valid cpu, though, to probe for the
- * default PMU instance as userspace is not required to specify a PMU
- * type. In order to uphold the preexisting behavior KVM selects the
- * PMU instance for the core during vcpu init. A dependent use
- * case would be a user with disdain of all things big.LITTLE that
- * affines the VMM to a particular cluster of cores.
- *
- * In any case, userspace should just do the sane thing and use the UAPI
- * to select a PMU type directly. But, be wary of the baggage being
- * carried here.
- */
- cpu = raw_smp_processor_id();
- list_for_each_entry(entry, &arm_pmus, entry) {
- pmu = entry->arm_pmu;
-
- if (cpumask_test_cpu(cpu, &pmu->supported_cpus))
- return pmu;
- }
-
- return NULL;
+ list_add_tail_rcu(&entry->entry, &arm_pmus);
}
static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1)
@@ -870,6 +902,13 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
u64 val, mask = 0;
int base, i, nr_events;
+ /*
+ * Hide the hardware PMU's event set to keep PMCEID stable across
+ * physical CPU migration.
+ */
+ if (kvm_pmu_fixed_counters_only(vcpu->kvm))
+ return 0;
+
if (!pmceid1) {
val = compute_pmceid0(cpu_pmu);
base = 0;
@@ -897,9 +936,27 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
return val & mask;
}
+void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu, int last_cpu)
+{
+ if (!kvm_pmu_fixed_counters_only(vcpu->kvm) || vcpu->cpu == last_cpu || last_cpu == -1)
+ return;
+
+ if (kvm_pmu_probe_armpmu(vcpu->cpu) != kvm_pmu_probe_armpmu(last_cpu))
+ kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
+}
+
void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
{
+ struct kvm_pmu *pmu = &vcpu->arch.pmu;
u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
+ int i;
+
+ if (pmu->events_need_recreate) {
+ for (i = 0; i < KVM_ARMV8_PMU_MAX_COUNTERS; i++)
+ kvm_pmu_stop_counter(kvm_vcpu_idx_to_pmc(vcpu, i));
+
+ pmu->events_need_recreate = false;
+ }
__vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, &=, mask);
__vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, &=, mask);
@@ -1008,6 +1065,9 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
{
struct arm_pmu *arm_pmu = kvm->arch.arm_pmu;
+ if (kvm_pmu_fixed_counters_only(kvm))
+ return 0;
+
/*
* PMUv3 requires that all event counters are capable of counting any
* event, though the same may not be true of non-PMUv3 hardware.
@@ -1022,30 +1082,12 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
return bitmap_weight(arm_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS);
}
-static void kvm_arm_set_nr_counters(struct kvm *kvm, unsigned int nr)
-{
- kvm->arch.nr_pmu_counters = nr;
-
- /* Reset MDCR_EL2.HPMN behind the vcpus' back... */
- if (test_bit(KVM_ARM_VCPU_HAS_EL2, kvm->arch.vcpu_features)) {
- struct kvm_vcpu *vcpu;
- unsigned long i;
-
- kvm_for_each_vcpu(i, vcpu, kvm) {
- u64 val = __vcpu_sys_reg(vcpu, MDCR_EL2);
- val &= ~MDCR_EL2_HPMN;
- val |= FIELD_PREP(MDCR_EL2_HPMN, kvm->arch.nr_pmu_counters);
- __vcpu_assign_sys_reg(vcpu, MDCR_EL2, val);
- }
- }
-}
-
static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)
{
lockdep_assert_held(&kvm->arch.config_lock);
kvm->arch.arm_pmu = arm_pmu;
- kvm_arm_set_nr_counters(kvm, kvm_arm_pmu_get_max_counters(kvm));
+ kvm->arch.nr_pmu_counters = kvm_arm_pmu_get_max_counters(kvm);
}
/**
@@ -1062,7 +1104,24 @@ static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)
*/
int kvm_arm_set_default_pmu(struct kvm *kvm)
{
- struct arm_pmu *arm_pmu = kvm_pmu_probe_armpmu();
+ /*
+ * It is safe to use a stale cpu to iterate the list of PMUs so long as
+ * the same value is used for the entirety of the loop. Given this, and
+ * the fact that no percpu data is used for the lookup there is no need
+ * to disable preemption.
+ *
+ * It is still necessary to get a valid cpu, though, to probe for the
+ * default PMU instance as userspace is not required to specify a PMU
+ * type. In order to uphold the preexisting behavior KVM selects the
+ * PMU instance for the core during vcpu init. A dependent use
+ * case would be a user with disdain of all things big.LITTLE that
+ * affines the VMM to a particular cluster of cores.
+ *
+ * In any case, userspace should just do the sane thing and use the UAPI
+ * to select a PMU type directly, or request fixed-counters-only
+ * emulation. But, be wary of the baggage being carried here.
+ */
+ struct arm_pmu *arm_pmu = kvm_pmu_probe_armpmu(raw_smp_processor_id());
if (!arm_pmu)
return -ENODEV;
@@ -1079,17 +1138,19 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
int ret = -ENXIO;
lockdep_assert_held(&kvm->arch.config_lock);
- mutex_lock(&arm_pmus_lock);
+ guard(rcu)();
- list_for_each_entry(entry, &arm_pmus, entry) {
+ list_for_each_entry_rcu(entry, &arm_pmus, entry) {
arm_pmu = entry->arm_pmu;
if (arm_pmu->pmu.type == pmu_id) {
if (kvm_vm_has_ran_once(kvm) ||
+ kvm_pmu_fixed_counters_only(kvm) ||
(kvm->arch.pmu_filter && kvm->arch.arm_pmu != arm_pmu)) {
ret = -EBUSY;
break;
}
+ set_bit(KVM_ARCH_FLAG_PMU_V3_EXPLICIT, &kvm->arch.flags);
kvm_arm_set_pmu(kvm, arm_pmu);
cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
ret = 0;
@@ -1097,21 +1158,39 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
}
}
- mutex_unlock(&arm_pmus_lock);
return ret;
}
+static int kvm_arm_pmu_v3_set_pmu_fixed_counters_only(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = vcpu->kvm;
+
+ lockdep_assert_held(&kvm->arch.config_lock);
+
+ if (kvm_vm_has_ran_once(kvm) || kvm->arch.pmu_filter ||
+ test_bit(KVM_ARCH_FLAG_PMU_V3_EXPLICIT, &kvm->arch.flags))
+ return -EBUSY;
+
+ set_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &kvm->arch.flags);
+ kvm->arch.nr_pmu_counters = 0;
+
+ return 0;
+}
+
static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned int n)
{
struct kvm *kvm = vcpu->kvm;
+ if (kvm_vm_has_ran_once(kvm))
+ return -EBUSY;
+
if (!kvm->arch.arm_pmu)
return -EINVAL;
if (n > kvm_arm_pmu_get_max_counters(kvm))
return -EINVAL;
- kvm_arm_set_nr_counters(kvm, n);
+ kvm->arch.nr_pmu_counters = n;
return 0;
}
@@ -1175,7 +1254,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
filter.action != KVM_PMU_EVENT_DENY))
return -EINVAL;
- if (kvm_vm_has_ran_once(kvm))
+ if (kvm_vm_has_ran_once(kvm) || kvm_pmu_fixed_counters_only(kvm))
return -EBUSY;
if (!kvm->arch.pmu_filter) {
@@ -1220,6 +1299,8 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
return kvm_arm_pmu_v3_set_nr_counters(vcpu, n);
}
+ case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY:
+ return kvm_arm_pmu_v3_set_pmu_fixed_counters_only(vcpu);
case KVM_ARM_VCPU_PMU_V3_INIT:
return kvm_arm_pmu_v3_init(vcpu);
}
@@ -1246,6 +1327,9 @@ int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
irq = vcpu->arch.pmu.irq_num;
return put_user(irq, uaddr);
}
+ case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY:
+ if (kvm_pmu_fixed_counters_only(vcpu->kvm))
+ return 0;
}
return -ENXIO;
@@ -1259,6 +1343,7 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
case KVM_ARM_VCPU_PMU_V3_FILTER:
case KVM_ARM_VCPU_PMU_V3_SET_PMU:
case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS:
+ case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY:
if (kvm_vcpu_has_pmu(vcpu))
return 0;
}
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index b963fd975aacaf..6bfb21f0044424 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -190,7 +190,8 @@ static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
void kvm_reset_vcpu(struct kvm_vcpu *vcpu)
{
struct vcpu_reset_state reset_state;
- bool loaded;
+ struct kvm_vcpu *running;
+ bool loaded = false;
u32 pstate;
spin_lock(&vcpu->arch.mp_state_lock);
@@ -198,10 +199,16 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu)
vcpu->arch.reset_state.reset = false;
spin_unlock(&vcpu->arch.mp_state_lock);
- preempt_disable();
- loaded = (vcpu->cpu != -1);
- if (loaded)
+ guard(preempt)();
+
+ running = kvm_get_running_vcpu();
+ if (running) {
+ if (KVM_BUG_ON(running != vcpu, vcpu->kvm))
+ return;
+
+ loaded = true;
kvm_arch_vcpu_put(vcpu);
+ }
if (!kvm_arm_vcpu_sve_finalized(vcpu)) {
if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_SVE))
@@ -269,7 +276,6 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu)
if (loaded)
kvm_arch_vcpu_load(vcpu, smp_processor_id());
- preempt_enable();
}
u32 kvm_get_pa_bits(struct kvm *kvm)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 5d5c579d457902..9e905d9be9f67e 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -3020,17 +3020,22 @@ static bool access_mdcr(struct kvm_vcpu *vcpu,
}
__vcpu_assign_sys_reg(vcpu, MDCR_EL2, val);
-
- /*
- * Request a reload of the PMU to enable/disable the counters
- * affected by HPME.
- */
- if ((old ^ val) & MDCR_EL2_HPME)
- kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
+ kvm_pmu_handle_mdcr(vcpu, old, val);
return true;
}
+static int set_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
+ u64 val)
+{
+ u64 old = __vcpu_sys_reg(vcpu, MDCR_EL2);
+
+ __vcpu_assign_sys_reg(vcpu, MDCR_EL2, val);
+ kvm_pmu_handle_mdcr(vcpu, old, val);
+
+ return 0;
+}
+
static bool access_ras(struct kvm_vcpu *vcpu,
struct sys_reg_params *p,
const struct sys_reg_desc *r)
@@ -3177,8 +3182,13 @@ static int set_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
static u64 reset_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
{
- __vcpu_assign_sys_reg(vcpu, r->reg, vcpu->kvm->arch.nr_pmu_counters);
- return vcpu->kvm->arch.nr_pmu_counters;
+ u64 old = __vcpu_sys_reg(vcpu, r->reg);
+ u64 val = vcpu->kvm->arch.nr_pmu_counters;
+
+ __vcpu_assign_sys_reg(vcpu, r->reg, val);
+ kvm_pmu_handle_mdcr(vcpu, old, val);
+
+ return val;
}
/*
@@ -3741,7 +3751,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
EL2_REG_FILTERED(SCTLR2_EL2, access_vm_reg, reset_val, 0,
sctlr2_el2_visibility),
EL2_REG_VNCR(HCR_EL2, reset_hcr, 0),
- EL2_REG(MDCR_EL2, access_mdcr, reset_mdcr, 0),
+ SYS_REG_USER_FILTER(MDCR_EL2, access_mdcr, reset_mdcr, 0,
+ NULL, set_mdcr, el2_visibility),
EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1),
EL2_REG_VNCR(HSTR_EL2, reset_val, 0),
EL2_REG_VNCR_FILT(HFGRTR_EL2, fgt_visibility),
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index b5e5942204fc69..0c31b72e3435a5 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -32,6 +32,7 @@ struct kvm_pmu {
struct kvm_pmc pmc[KVM_ARMV8_PMU_MAX_COUNTERS];
int irq_num;
bool created;
+ bool events_need_recreate;
};
struct arm_pmu_entry {
@@ -56,8 +57,10 @@ bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu);
bool kvm_pmu_update_run(struct kvm_vcpu *vcpu);
void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val);
void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val);
+void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val);
void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
u64 select_idx);
+void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu, int last_cpu);
void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu);
int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu,
struct kvm_device_attr *attr);
@@ -133,6 +136,7 @@ static inline bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu)
static inline bool kvm_pmu_update_run(struct kvm_vcpu *vcpu) { return false; }
static inline void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val) {}
static inline void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) {}
+static inline void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val) {}
static inline void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu,
u64 data, u64 select_idx) {}
static inline int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu,
@@ -163,6 +167,7 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {}
static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {}
static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {}
+static inline void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu, int last_cpu) {}
static inline void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) {}
static inline u8 kvm_arm_pmu_get_pmuver_limit(void)
{
diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h
index 1c13bfa2d38aad..39a1a1e412e66f 100644
--- a/tools/arch/arm64/include/uapi/asm/kvm.h
+++ b/tools/arch/arm64/include/uapi/asm/kvm.h
@@ -437,6 +437,7 @@ enum {
#define KVM_ARM_VCPU_PMU_V3_FILTER 2
#define KVM_ARM_VCPU_PMU_V3_SET_PMU 3
#define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4
+#define KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY 5
#define KVM_ARM_VCPU_TIMER_CTRL 1
#define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0
#define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1
diff --git a/tools/include/linux/bitfield.h b/tools/include/linux/bitfield.h
index ddf81f24956ba0..1c633343d57668 100644
--- a/tools/include/linux/bitfield.h
+++ b/tools/include/linux/bitfield.h
@@ -142,32 +142,32 @@ static __always_inline u64 field_mask(u64 field)
return field / field_multiplier(field);
}
#define field_max(field) ((typeof(field))field_mask(field))
-#define ____MAKE_OP(type,base,to,from) \
-static __always_inline __##type type##_encode_bits(base v, base field) \
+#define ____MAKE_OP(name,type,base,to,from) \
+static __always_inline type name##_encode_bits(base v, base field) \
{ \
if (__builtin_constant_p(v) && (v & ~field_mask(field))) \
__field_overflow(); \
return to((v & field_mask(field)) * field_multiplier(field)); \
} \
-static __always_inline __##type type##_replace_bits(__##type old, \
- base val, base field) \
+static __always_inline type name##_replace_bits(type old, \
+ base val, base field) \
{ \
- return (old & ~to(field)) | type##_encode_bits(val, field); \
+ return (old & ~to(field)) | name##_encode_bits(val, field); \
} \
-static __always_inline void type##p_replace_bits(__##type *p, \
+static __always_inline void name##p_replace_bits(type *p, \
base val, base field) \
{ \
- *p = (*p & ~to(field)) | type##_encode_bits(val, field); \
+ *p = (*p & ~to(field)) | name##_encode_bits(val, field); \
} \
-static __always_inline base type##_get_bits(__##type v, base field) \
+static __always_inline base name##_get_bits(type v, base field) \
{ \
return (from(v) & field)/field_multiplier(field); \
}
-#define __MAKE_OP(size) \
- ____MAKE_OP(le##size,u##size,cpu_to_le##size,le##size##_to_cpu) \
- ____MAKE_OP(be##size,u##size,cpu_to_be##size,be##size##_to_cpu) \
- ____MAKE_OP(u##size,u##size,,)
-____MAKE_OP(u8,u8,,)
+#define __MAKE_OP(size) \
+ ____MAKE_OP(le##size,__le##size,u##size,cpu_to_le##size,le##size##_to_cpu) \
+ ____MAKE_OP(be##size,__be##size,u##size,cpu_to_be##size,be##size##_to_cpu) \
+ ____MAKE_OP(u##size,u##size,u##size,,)
+____MAKE_OP(u8,u8,u8,,)
__MAKE_OP(16)
__MAKE_OP(32)
__MAKE_OP(64)
diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
index 22223395969e0f..b5f8ce8d3451f2 100644
--- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
+++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
@@ -25,6 +25,14 @@
/* The cycle counter bit position that's common among the PMU registers */
#define ARMV8_PMU_CYCLE_IDX 31
+#ifndef MDCR_EL2_HPMN
+#define MDCR_EL2_HPMN GENMASK_ULL(4, 0)
+#endif
+
+#ifndef MDCR_EL2_HPME
+#define MDCR_EL2_HPME BIT_ULL(7)
+#endif
+
struct vpmu_vm {
struct kvm_vm *vm;
struct kvm_vcpu *vcpu;
@@ -403,12 +411,7 @@ static void create_vpmu_vm(void *guest_code)
{
struct kvm_vcpu_init init;
u8 pmuver, ec;
- u64 dfr0, irq = 23;
- struct kvm_device_attr irq_attr = {
- .group = KVM_ARM_VCPU_PMU_V3_CTRL,
- .attr = KVM_ARM_VCPU_PMU_V3_IRQ,
- .addr = (u64)&irq,
- };
+ u64 dfr0;
/* The test creates the vpmu_vm multiple times. Ensure a clean state */
memset(&vpmu_vm, 0, sizeof(vpmu_vm));
@@ -434,8 +437,6 @@ static void create_vpmu_vm(void *guest_code)
TEST_ASSERT(pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF &&
pmuver >= ID_AA64DFR0_EL1_PMUVer_IMP,
"Unexpected PMUVER (0x%x) on the vCPU with PMUv3", pmuver);
-
- vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &irq_attr);
}
static void destroy_vpmu_vm(void)
@@ -461,15 +462,17 @@ static void run_vcpu(struct kvm_vcpu *vcpu, u64 pmcr_n)
}
}
-static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool expect_fail)
+static void guest_code_done(void)
+{
+ GUEST_DONE();
+}
+
+static void set_nr_counters(struct kvm_vcpu *vcpu,
+ unsigned int nr_counters, bool expect_fail)
{
- struct kvm_vcpu *vcpu;
unsigned int prev;
int ret;
- create_vpmu_vm(guest_code);
- vcpu = vpmu_vm.vcpu;
-
prev = get_pmcr_n(vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0)));
ret = __vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
@@ -481,6 +484,26 @@ static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool
nr_counters, prev);
else
TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));
+}
+
+static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters,
+ bool fixed_counters_only,
+ bool expect_fail)
+{
+ struct kvm_vcpu *vcpu;
+ u64 irq = 23;
+
+ create_vpmu_vm(guest_code);
+ vcpu = vpmu_vm.vcpu;
+
+ if (fixed_counters_only)
+ vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);
+
+ vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_IRQ, &irq);
+
+ set_nr_counters(vcpu, nr_counters, expect_fail);
vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, NULL);
}
@@ -489,15 +512,15 @@ static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool
* Create a guest with one vCPU, set the PMCR_EL0.N for the vCPU to @pmcr_n,
* and run the test.
*/
-static void run_access_test(u64 pmcr_n)
+static void run_access_test(u64 pmcr_n, bool fixed_counters_only)
{
u64 sp;
struct kvm_vcpu *vcpu;
struct kvm_vcpu_init init;
- pr_debug("Test with pmcr_n %lu\n", pmcr_n);
+ pr_debug("Test with pmcr_n %lu, fixed_counters_only %d\n", pmcr_n, fixed_counters_only);
- test_create_vpmu_vm_with_nr_counters(pmcr_n, false);
+ test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, false);
vcpu = vpmu_vm.vcpu;
/* Save the initial sp to restore them later to run the guest again */
@@ -531,14 +554,14 @@ static struct pmreg_sets validity_check_reg_sets[] = {
* Create a VM, and check if KVM handles the userspace accesses of
* the PMU register sets in @validity_check_reg_sets[] correctly.
*/
-static void run_pmregs_validity_test(u64 pmcr_n)
+static void run_pmregs_validity_test(u64 pmcr_n, bool fixed_counters_only)
{
int i;
struct kvm_vcpu *vcpu;
u64 set_reg_id, clr_reg_id, reg_val;
u64 valid_counters_mask, max_counters_mask;
- test_create_vpmu_vm_with_nr_counters(pmcr_n, false);
+ test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, false);
vcpu = vpmu_vm.vcpu;
valid_counters_mask = get_counters_mask(pmcr_n);
@@ -583,16 +606,94 @@ static void run_pmregs_validity_test(u64 pmcr_n)
destroy_vpmu_vm();
}
+static void run_mdcr_el2_validity_test(u64 pmcr_n, bool fixed_counters_only)
+{
+ struct kvm_vcpu_init init;
+ struct kvm_vcpu *vcpu;
+ u64 expected_mdcr, mdcr;
+ u64 irq = 23;
+
+ pr_debug("MDCR_EL2 test with pmcr_n %lu, fixed_counters_only %d\n",
+ pmcr_n, fixed_counters_only);
+
+ create_vpmu_vm(guest_code);
+ if (!vm_supports_el2(vpmu_vm.vm)) {
+ destroy_vpmu_vm();
+ return;
+ }
+
+ vcpu = vpmu_vm.vcpu;
+
+ mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+ expected_mdcr = u64_replace_bits(mdcr, FIELD_MAX(MDCR_EL2_HPMN),
+ MDCR_EL2_HPMN);
+ vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),
+ expected_mdcr);
+
+ mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+ TEST_ASSERT(mdcr == expected_mdcr,
+ "MDCR_EL2 was not properly updated after HPMN write (expected 0x%lx, got 0x%lx)",
+ expected_mdcr, mdcr);
+
+ if (fixed_counters_only) {
+ vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);
+
+ mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+ TEST_ASSERT(mdcr == expected_mdcr,
+ "MDCR_EL2 changed after PMU_V3_FIXED_COUNTERS_ONLY (expected 0x%lx, got 0x%lx)",
+ expected_mdcr, mdcr);
+ }
+
+ set_nr_counters(vcpu, pmcr_n, false);
+
+ mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+ TEST_ASSERT(mdcr == expected_mdcr,
+ "MDCR_EL2 changed after PMU_V3_SET_NR_COUNTERS (expected 0x%lx, got 0x%lx)",
+ expected_mdcr, mdcr);
+
+ vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_IRQ, &irq);
+
+ vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_INIT, NULL);
+
+ mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+ TEST_ASSERT(mdcr == expected_mdcr,
+ "MDCR_EL2 changed after PMU_V3_INIT (expected 0x%lx, got 0x%lx)",
+ expected_mdcr, mdcr);
+
+ expected_mdcr = mdcr ^ MDCR_EL2_HPME;
+ vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),
+ expected_mdcr);
+
+ mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+ TEST_ASSERT(mdcr == expected_mdcr,
+ "MDCR_EL2 was not properly updated after HPME write (expected 0x%lx, got 0x%lx)",
+ expected_mdcr, mdcr);
+
+ kvm_get_default_vcpu_target(vpmu_vm.vm, &init);
+ init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
+ aarch64_vcpu_setup(vcpu, &init);
+
+ mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+ TEST_ASSERT(FIELD_GET(MDCR_EL2_HPMN, mdcr) == pmcr_n,
+ "MDCR_EL2.HPMN is not reset after INIT (expected %lu, got %lu)",
+ pmcr_n, FIELD_GET(MDCR_EL2_HPMN, mdcr));
+
+ destroy_vpmu_vm();
+}
+
/*
* Create a guest with one vCPU, and attempt to set the PMCR_EL0.N for
* the vCPU to @pmcr_n, which is larger than the host value.
* The attempt should fail as @pmcr_n is too big to set for the vCPU.
*/
-static void run_error_test(u64 pmcr_n)
+static void run_error_test(u64 pmcr_n, bool fixed_counters_only)
{
pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n);
- test_create_vpmu_vm_with_nr_counters(pmcr_n, true);
+ test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, true);
destroy_vpmu_vm();
}
@@ -622,22 +723,147 @@ static bool kvm_supports_nr_counters_attr(void)
return supported;
}
-int main(void)
+static void test_set_nr_counters_after_vcpu_run(void)
{
- u64 i, pmcr_n;
+ struct kvm_vcpu *running_vcpu, *stopped_vcpu;
+ unsigned int nr_counters = 0;
+ struct kvm_vcpu_init init;
+ struct kvm_vm *vm;
+ int ret;
+ u64 irq = 23;
+
+ vm = vm_create(2);
+ vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
+ init.features[0] |= BIT(KVM_ARM_VCPU_PMU_V3);
+ running_vcpu = aarch64_vcpu_add(vm, 0, &init, guest_code_done);
+ stopped_vcpu = aarch64_vcpu_add(vm, 1, &init, guest_code_done);
+ kvm_arch_vm_finalize_vcpus(vm);
+
+ vcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_IRQ, &irq);
+ vcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_INIT, NULL);
+ vcpu_run(running_vcpu);
+
+ ret = __vcpu_device_attr_set(stopped_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS,
+ &nr_counters);
+ TEST_ASSERT(ret == -1 && errno == EBUSY,
+ KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));
+
+ kvm_vm_free(vm);
+}
- TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3));
- TEST_REQUIRE(kvm_supports_vgic_v3());
- TEST_REQUIRE(kvm_supports_nr_counters_attr());
+static void test_config(u64 pmcr_n, bool fixed_counters_only)
+{
+ u64 i;
- pmcr_n = get_pmcr_n_limit();
for (i = 0; i <= pmcr_n; i++) {
- run_access_test(i);
- run_pmregs_validity_test(i);
+ run_access_test(i, fixed_counters_only);
+ run_pmregs_validity_test(i, fixed_counters_only);
+ run_mdcr_el2_validity_test(i, fixed_counters_only);
}
for (i = pmcr_n + 1; i < ARMV8_PMU_MAX_COUNTERS; i++)
- run_error_test(i);
+ run_error_test(i, fixed_counters_only);
+}
+
+static void test_fixed_counters_only(void)
+{
+ struct kvm_pmu_event_filter filter = { .nevents = 0 };
+ struct kvm_vm *vm;
+ struct kvm_vcpu *running_vcpu;
+ struct kvm_vcpu *stopped_vcpu;
+ struct kvm_vcpu_init init;
+ int ret;
+ u64 irq = 23;
+
+ create_vpmu_vm(guest_code);
+ ret = __vcpu_has_device_attr(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY);
+ if (ret) {
+ TEST_ASSERT(ret == -1 && errno == ENXIO,
+ KVM_IOCTL_ERROR(KVM_HAS_DEVICE_ATTR, ret));
+ destroy_vpmu_vm();
+ return;
+ }
+
+ /* Assert that FIXED_COUNTERS_ONLY is unset at initialization. */
+ ret = __vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);
+ TEST_ASSERT(ret == -1 && errno == ENXIO,
+ KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret));
+
+ /* Assert that setting FIXED_COUNTERS_ONLY succeeds. */
+ vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);
+
+ /* Assert that FIXED_COUNTERS_ONLY is set. */
+ vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);
+
+ /*
+ * Setting an event filter when FIXED_COUNTERS_ONLY has already been set
+ * results in EBUSY.
+ */
+ ret = __vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FILTER, &filter);
+ TEST_ASSERT(ret == -1 && errno == EBUSY,
+ KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));
+
+ destroy_vpmu_vm();
+
+ create_vpmu_vm(guest_code);
+
+ /*
+ * Assert that setting FIXED_COUNTERS_ONLY when an event filter has
+ * already been set results in EBUSY.
+ */
+ vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FILTER, &filter);
+
+ ret = __vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);
+ TEST_ASSERT(ret == -1 && errno == EBUSY,
+ KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));
+
+ destroy_vpmu_vm();
+
+ /*
+ * Assert that setting FIXED_COUNTERS_ONLY when a VCPU has already run
+ * results in EBUSY.
+ */
+ vm = vm_create(2);
+ vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
+ init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
+ running_vcpu = aarch64_vcpu_add(vm, 0, &init, guest_code_done);
+ stopped_vcpu = aarch64_vcpu_add(vm, 1, &init, guest_code_done);
+ kvm_arch_vm_finalize_vcpus(vm);
+ vcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_IRQ, &irq);
+ vcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_INIT, NULL);
+ vcpu_run(running_vcpu);
+
+ ret = __vcpu_device_attr_set(stopped_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+ KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL);
+ TEST_ASSERT(ret == -1 && errno == EBUSY,
+ KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));
+
+ kvm_vm_free(vm);
+
+ test_config(0, true);
+}
+
+int main(void)
+{
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3));
+ TEST_REQUIRE(kvm_supports_vgic_v3());
+ TEST_REQUIRE(kvm_supports_nr_counters_attr());
+
+ test_set_nr_counters_after_vcpu_run();
+ test_config(get_pmcr_n_limit(), false);
+ test_fixed_counters_only();
return 0;
}
|
| 3/2 | 2026/07/20 06:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20139 output: 36 thoughts: 569 Thoughts: **My Assessment of the KVM ARM64 PMU Emulation Patch Series** Okay, so I've been tasked with reviewing this patch series and determining if it introduces functional changes in the kernel that warrant fuzzing attention. The patch specifically tweaks the KVM ARM64 PMU emulation. I see it introduces a new attribute, `KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY`, and alters how PMU events are recreated and the overall PMU loading/reloading process. Looking at the modified files – `arch/arm64/kvm/pmu-emul.c`, `arch/arm64/kvm/arm.c`, `arch/arm64/kvm/sys_regs.c`, and `arch/arm64/kvm/reset.c` – this is undoubtedly a functional change to KVM on ARM64. And, importantly, since this is KVM, this can definitely be accessed and influenced from userspace via KVM ioctls. I'm thinking about `KVM_SET_DEVICE_ATTR` calls with the new attribute `KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY`, and also `KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS` immediately come to mind. Now, let's nail down the configuration dependencies. The code resides within `arch/arm64/kvm/`, so `CONFIG_KVM` is obviously a prerequisite. It's also ARM64-specific, so we need to ensure this is an ARM64 system. To focus the fuzzing effort, I need to identify the crucial functions affected. Let's list these out: `kvm_pmu_handle_mdcr`, `kvm_vcpu_load_pmu`, `kvm_vcpu_reload_pmu`, `kvm_arm_pmu_v3_set_pmu_fixed_counters_only`, `kvm_arm_pmu_v3_set_nr_counters`, `kvm_arm_pmu_v3_set_attr`, `kvm_arm_pmu_v3_get_attr`, `kvm_arm_pmu_v3_has_attr`, `kvm_pmu_create_perf_event`, `kvm_pmu_create_perf_event_with_pmu`, and `kvm_pmu_probe_armpmu`. These are the key entry points and the functions which are handling the core changes. Finally, I'll do a quick grep across the `arch/arm64/kvm/` directory to double-check for any other relevant configuration options. Scanning `arch/arm64/kvm/Kconfig` shows `CONFIG_KVM` is indeed the main requirement. I suspect `CONFIG_HW_PERF_EVENTS` might also be needed, since PMU emulation often relies on performance monitoring infrastructure. Let me take another look at `arch/arm64/kvm/pmu-emul.c` to confirm my suspicion. |
| 4/2 | 2026/07/20 06:14 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_HW_PERF_EVENTS",
"PathPrefix": "arch/arm64/kvm"
}Results: {
"Output": "arch/arm64/kvm/Makefile=16=kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \\\n--\narch/arm64/kvm/Makefile-28-\narch/arm64/kvm/Makefile:29:kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o\narch/arm64/kvm/Makefile-30-kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o\n--\narch/arm64/kvm/hyp/nvhe/switch.c=129=static void __hyp_vgic_restore_state(struct kvm_vcpu *vcpu)\n--\narch/arm64/kvm/hyp/nvhe/switch.c-145- */\narch/arm64/kvm/hyp/nvhe/switch.c:146:#ifdef CONFIG_HW_PERF_EVENTS\narch/arm64/kvm/hyp/nvhe/switch.c-147-static bool __pmu_switch_to_guest(struct kvm_vcpu *vcpu)\n"
} |
| 5/2 | 2026/07/20 06:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21730 output: 84 thoughts: 197 Thoughts: Here's my thought process, distilled: **KVM PMU Emulation Dependencies** Okay, so it looks like `pmu-emul.o` necessitates `CONFIG_HW_PERF_EVENTS`. My initial assumption is that we need both `KVM` and `HW_PERF_EVENTS`, but let's just confirm that `CONFIG_KVM` is indeed sufficient. Yes, `KVM` and `HW_PERF_EVENTS` are both necessary. Now, let's get a handle on the function calls this introduces. I need to understand the scope of the changes. I'll need to review: `kvm_pmu_handle_mdcr`, `kvm_vcpu_load_pmu`, `kvm_vcpu_reload_pmu`, `kvm_pmu_create_perf_event_with_pmu`, `kvm_arm_pmu_v3_set_pmu_fixed_counters_only`, and `kvm_arm_pmu_v3_set_nr_counters`. Finally, to get a truly complete picture, I should check `arch/arm64/kvm/pmu-emul.c` directly to see what other functions are introduced or modified, and also to understand the overall context. |
| 6/2 | 2026/07/20 06:15 | tool | grepper |
0mArgs:{
"Expression": "^void kvm_pmu_handle_mdcr|^void kvm_vcpu_load_pmu|^static int kvm_arm_pmu_v3_set_pmu_fixed_counters_only",
"PathPrefix": "arch/arm64/kvm/pmu-emul.c"
}Results: {
"Output": "arch/arm64/kvm/pmu-emul.c=595=void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)\n--\narch/arm64/kvm/pmu-emul.c-624-\narch/arm64/kvm/pmu-emul.c:625:void kvm_pmu_handle_mdcr(struct kvm_vcpu *vcpu, u64 old, u64 val)\narch/arm64/kvm/pmu-emul.c-626-{\n--\narch/arm64/kvm/pmu-emul.c=898=u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)\n--\narch/arm64/kvm/pmu-emul.c-938-\narch/arm64/kvm/pmu-emul.c:939:void kvm_vcpu_load_pmu(struct kvm_vcpu *vcpu, int last_cpu)\narch/arm64/kvm/pmu-emul.c-940-{\n--\narch/arm64/kvm/pmu-emul.c=1133=static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)\n--\narch/arm64/kvm/pmu-emul.c-1163-\narch/arm64/kvm/pmu-emul.c:1164:static int kvm_arm_pmu_v3_set_pmu_fixed_counters_only(struct kvm_vcpu *vcpu)\narch/arm64/kvm/pmu-emul.c-1165-{\n"
} |
| 7/2 | 2026/07/20 06:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 22769 output: 68 thoughts: 271 Thoughts: **Fuzzing Plan for KVM PMU Emulation on ARM64** Okay, so this patch introduces some functional changes to KVM's PMU emulation for ARM64, which means it's definitely worth taking a closer look at fuzzing. My initial thought is to zero in on these symbols: `kvm_pmu_handle_mdcr`, `kvm_vcpu_load_pmu`, `kvm_vcpu_reload_pmu`, `kvm_pmu_create_perf_event_with_pmu`, `kvm_arm_pmu_v3_set_pmu_fixed_counters_only`, `kvm_arm_pmu_v3_set_nr_counters`, `kvm_arm_pmu_v3_set_attr`, and `kvm_pmu_probe_armpmu`. Those seem like the key areas that need thorough testing. I need to make sure I get the configuration right for this. I need `KVM` and `HW_PERF_EVENTS`. Just quickly, let me double-check those config names. Yep, `CONFIG_HW_PERF_EVENTS` and `CONFIG_KVM` are the right ones. And now I will quickly check `kvm_arm_pmu_v3_set_attr` and `kvm_arm_pmu_v3_get_attr` to get a better understanding of how the attributes are handled. |
| 8/2 | 2026/07/20 06:15 | tool | grepper |
0mArgs:{
"Expression": "^int kvm_arm_pmu_v3_set_attr|^int kvm_arm_pmu_v3_get_attr",
"PathPrefix": "arch/arm64/kvm/pmu-emul.c"
}Results: {
"Output": "arch/arm64/kvm/pmu-emul.c=1180=static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned int n)\n--\narch/arm64/kvm/pmu-emul.c-1196-\narch/arm64/kvm/pmu-emul.c:1197:int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)\narch/arm64/kvm/pmu-emul.c-1198-{\n--\narch/arm64/kvm/pmu-emul.c-1310-\narch/arm64/kvm/pmu-emul.c:1311:int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)\narch/arm64/kvm/pmu-emul.c-1312-{\n"
} |
| 9/2 | 2026/07/20 06:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23710 output: 217 thoughts: 289 Thoughts: **My Assessment of the KVM ARM64 PMU Emulation Change** Okay, so this patch is introducing a new capability for the KVM ARM64 PMU emulation: `KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY`. My initial thought is to immediately consider how this impacts existing userspace interaction and KVM's core PMU emulation logic. It looks like it's going to affect the `kvm_arm_pmu_v3_set_attr`, `kvm_arm_pmu_v3_get_attr`, and `kvm_arm_pmu_v3_has_attr` functions, which is where the new flag is being integrated. Now, let's nail down the critical symbols affected by this. I need to understand the full scope, so I'm focusing on these: `kvm_pmu_handle_mdcr`, `kvm_vcpu_load_pmu`, `kvm_pmu_create_perf_event_with_pmu`, `kvm_arm_pmu_v3_set_pmu_fixed_counters_only`, `kvm_arm_pmu_v3_set_nr_counters`, and `kvm_pmu_probe_armpmu`. I need to review these specific functions carefully; the PMU event creation, loading, and reloading are key points here because they're directly impacted by this new functionality. These probably need some extra scrutiny, as they are likely to interact with the new capability. From a functional perspective, I see that this is a feature related to PMU emulation, accessible from userspace through KVM ioctls. That means any change here has the potential to impact the user-visible behavior and needs to be analyzed with that in mind. The mention of `KVM` and `HW_PERF_EVENTS` in the configs is important. Finally, I'll need to figure out what the "set-results" are, to check how this function will actually work. |
| 10/2 | 2026/07/20 06:15 | tool | set-results |
0mError:the following configs do not exist in the kernel tree: HW_PERF_EVENTS Args: {
"EnableConfigs": [
"KVM",
"HW_PERF_EVENTS"
],
"FocusSymbols": [
"kvm_pmu_handle_mdcr",
"kvm_vcpu_load_pmu",
"kvm_pmu_create_perf_event_with_pmu",
"kvm_arm_pmu_v3_set_pmu_fixed_counters_only",
"kvm_arm_pmu_v3_set_nr_counters",
"kvm_pmu_probe_armpmu"
],
"Reasoning": "The patch introduces a new KVM ARM64 PMU emulation feature (KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY) and modifies PMU event creation, loading, and reloading logic. This is reachable from userspace via KVM ioctls and modifies core KVM PMU emulation logic.",
"WorthFuzzing": true
}Results: {
"error": "the following configs do not exist in the kernel tree: HW_PERF_EVENTS"
} |
| 11/2 | 2026/07/20 06:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24689 output: 27 thoughts: 44 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Investigating the ARM64 Hardware Performance Events Configuration** Right, so `HW_PERF_EVENTS`... that immediately flags ARM64. Time to dive into the kernel config files. I need to figure out where this feature is controlled and how it's enabled. My gut tells me it's either going to be tucked away in `arch/arm64/Kconfig` – makes the most sense – or perhaps linked in `init/Kconfig` if it's got some broader dependency or global influence. Gotta quickly check both. It's about knowing the expected structure and common locations. No point in wasting time with a blind search if I can narrow it down with this basic knowledge of the tree. Let's see what's actually under the hood. |
| 12/2 | 2026/07/20 06:15 | tool | grepper |
0mArgs:{
"Expression": "config HW_PERF_EVENTS",
"PathPrefix": ""
}Results: {
"Output": "arch/arm/Kconfig=1253=config CPU_TTBR0_PAN\n--\narch/arm/Kconfig-1259-\narch/arm/Kconfig:1260:config HW_PERF_EVENTS\narch/arm/Kconfig-1261-\tdef_bool y\n--\narch/arm64/Kconfig=1606=config ARCH_SPARSEMEM_ENABLE\n--\narch/arm64/Kconfig-1609-\narch/arm64/Kconfig:1610:config HW_PERF_EVENTS\narch/arm64/Kconfig-1611-\tdef_bool y\n--\narch/mips/Kconfig=2680=config NODES_SHIFT\n--\narch/mips/Kconfig-2684-\narch/mips/Kconfig:2685:config HW_PERF_EVENTS\narch/mips/Kconfig-2686-\tbool \"Enable hardware performance counter support for perf events\"\n--\narch/sh/Kconfig=626=config GUSA_RB\n--\narch/sh/Kconfig-635-\narch/sh/Kconfig:636:config HW_PERF_EVENTS\narch/sh/Kconfig-637-\tbool \"Enable hardware performance counter support for perf events\"\n"
} |
| 13/2 | 2026/07/20 06:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25272 output: 25 thoughts: 57 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, as if they were my own: **Investigating a Potentially Implicit Feature** Alright, so I'm seeing something that *feels* like it should be there, but the tool isn't explicitly showing it. My first instinct is to consider whether this functionality, let's call it the elusive "it," is actually implicitly enabled by default, perhaps as `def_bool y`, and the current setup just doesn't surface it. The tool might simply not be configured to recognize it correctly, maybe because it's not a standard prompt configuration option. So, I need to verify that. My next step is to drill down and check the specific definitions, and the likely candidate, at least based on my initial assessment, is `PERF_EVENTS`. I need to confirm whether `PERF_EVENTS` is the key to unlocking or understanding the behavior I'm observing. It’s the obvious first thing to check, so let's start there. |
| 14/2 | 2026/07/20 06:15 | tool | grepper |
0mArgs:{
"Expression": "config PERF_EVENTS",
"PathPrefix": ""
}Results: {
"Output": "arch/x86/events/Kconfig=2=menu \"Performance monitoring\"\narch/x86/events/Kconfig-3-\narch/x86/events/Kconfig:4:config PERF_EVENTS_INTEL_UNCORE\narch/x86/events/Kconfig-5-\ttristate \"Intel uncore performance events\"\n--\narch/x86/events/Kconfig-11-\narch/x86/events/Kconfig:12:config PERF_EVENTS_INTEL_RAPL\narch/x86/events/Kconfig-13-\ttristate \"Intel/AMD rapl performance events\"\n--\narch/x86/events/Kconfig-19-\narch/x86/events/Kconfig:20:config PERF_EVENTS_INTEL_CSTATE\narch/x86/events/Kconfig-21-\ttristate \"Intel cstate performance events\"\n--\narch/x86/events/Kconfig-27-\narch/x86/events/Kconfig:28:config PERF_EVENTS_AMD_POWER\narch/x86/events/Kconfig-29-\tdepends on PERF_EVENTS \u0026\u0026 CPU_SUP_AMD\n--\narch/x86/events/Kconfig-36-\narch/x86/events/Kconfig:37:config PERF_EVENTS_AMD_UNCORE\narch/x86/events/Kconfig-38-\ttristate \"AMD Uncore performance events\"\n--\narch/x86/events/Kconfig-47-\narch/x86/events/Kconfig:48:config PERF_EVENTS_AMD_BRS\narch/x86/events/Kconfig-49-\tdepends on PERF_EVENTS \u0026\u0026 CPU_SUP_AMD\n--\ninit/Kconfig=2138=menu \"Kernel Performance Events And Counters\"\ninit/Kconfig-2139-\ninit/Kconfig:2140:config PERF_EVENTS\ninit/Kconfig-2141-\tbool \"Kernel performance events and counters\"\n"
} |
| 15/2 | 2026/07/20 06:15 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 26082 output: 215 thoughts: 16 Thoughts: Okay, here's my take on those "thoughts," reflecting my understanding as an expert: **Focusing on KVM and Performance Monitoring** Alright, given the context, the core thought here is pretty straightforward: let's immediately narrow the scope to two key tools, `KVM` and `PERF_EVENTS`. The implication is that we're likely dealing with a performance-related investigation within a virtualized environment. The use of `KVM` almost certainly means we're dealing with a hypervisor-based virtualization setup, meaning we're directly dealing with virtual machines (VMs) and their interaction with the host hardware. And, the inclusion of `PERF_EVENTS` is key because it means we'll be diving deep into hardware performance counters. We're talking detailed measurements of CPU cycles, cache misses, branch predictions, memory access patterns – the nitty-gritty of what's happening *inside* those VMs. This approach allows for detailed and nuanced evaluation of specific performance issues. It’s an efficient way to cut through the noise and get right to the source of bottlenecks or inefficiencies. This isn't just a high-level overview; this is a diagnostic deep dive. |
| 16/2 | 2026/07/20 06:15 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"KVM",
"PERF_EVENTS"
],
"FocusSymbols": [
"kvm_pmu_handle_mdcr",
"kvm_vcpu_load_pmu",
"kvm_pmu_create_perf_event_with_pmu",
"kvm_arm_pmu_v3_set_pmu_fixed_counters_only",
"kvm_arm_pmu_v3_set_nr_counters",
"kvm_pmu_probe_armpmu"
],
"Reasoning": "The patch introduces a new KVM ARM64 PMU emulation feature (KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY) and modifies PMU event creation, loading, and reloading logic. This is reachable from userspace via KVM ioctls and modifies core KVM PMU emulation logic.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"KVM",
"PERF_EVENTS"
],
"FocusSymbols": [
"kvm_pmu_handle_mdcr",
"kvm_vcpu_load_pmu",
"kvm_pmu_create_perf_event_with_pmu",
"kvm_arm_pmu_v3_set_pmu_fixed_counters_only",
"kvm_arm_pmu_v3_set_nr_counters",
"kvm_pmu_probe_armpmu"
],
"Reasoning": "The patch introduces a new KVM ARM64 PMU emulation feature (KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY) and modifies PMU event creation, loading, and reloading logic. This is reachable from userspace via KVM ioctls and modifies core KVM PMU emulation logic.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|