[PATCH v11 04/21] KVM: arm64: selftests: Test SET_NR_COUNTERS after first run

From: Akihiko Odaki

Date: Sun Sep 20 2026 - 07:22:08 EST


Add a regression test that runs one vCPU and then tries to change the
VM-scoped PMU counter count through an idle sibling. Verify that
SET_NR_COUNTERS fails with EBUSY once any vCPU has run.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Akihiko Odaki <odaki@xxxxxxxxxxxxxxxxxxxxxx>
---
.../selftests/kvm/arm64/vpmu_counter_access.c | 47 ++++++++++++++++++++--
1 file changed, 43 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
index 22223395969e..05135bcdb462 100644
--- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
+++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
@@ -443,11 +443,10 @@ static void destroy_vpmu_vm(void)
kvm_vm_free(vpmu_vm.vm);
}

-static void run_vcpu(struct kvm_vcpu *vcpu, u64 pmcr_n)
+static void run_vcpu(struct kvm_vcpu *vcpu)
{
struct ucall uc;

- vcpu_args_set(vcpu, 1, pmcr_n);
vcpu_run(vcpu);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
@@ -461,6 +460,11 @@ static void run_vcpu(struct kvm_vcpu *vcpu, u64 pmcr_n)
}
}

+static void guest_code_done(void)
+{
+ GUEST_DONE();
+}
+
static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool expect_fail)
{
struct kvm_vcpu *vcpu;
@@ -503,7 +507,8 @@ static void run_access_test(u64 pmcr_n)
/* Save the initial sp to restore them later to run the guest again */
sp = vcpu_get_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1));

- run_vcpu(vcpu, pmcr_n);
+ vcpu_args_set(vcpu, 1, pmcr_n);
+ run_vcpu(vcpu);

/*
* Reset and re-initialize the vCPU, and run the guest code again to
@@ -516,7 +521,8 @@ static void run_access_test(u64 pmcr_n)
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1), sp);
vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (u64)guest_code);

- run_vcpu(vcpu, pmcr_n);
+ vcpu_args_set(vcpu, 1, pmcr_n);
+ run_vcpu(vcpu);

destroy_vpmu_vm();
}
@@ -622,6 +628,37 @@ static bool kvm_supports_nr_counters_attr(void)
return supported;
}

+static void test_set_nr_counters_after_vcpu_run(void)
+{
+ 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);
+ run_vcpu(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);
+}
+
int main(void)
{
u64 i, pmcr_n;
@@ -630,6 +667,8 @@ int main(void)
TEST_REQUIRE(kvm_supports_vgic_v3());
TEST_REQUIRE(kvm_supports_nr_counters_attr());

+ test_set_nr_counters_after_vcpu_run();
+
pmcr_n = get_pmcr_n_limit();
for (i = 0; i <= pmcr_n; i++) {
run_access_test(i);

--
2.55.0