[PATCH kvmtool v2 4/7] arm64: Initialise the PMU after the GIC
Alexandru Elisei
alexandru.elisei at arm.com
Thu Jun 18 08:49:58 PDT 2026
PMU initialisation is done in:
setup_fdt()
vcpu->generate_fdt_nodes()
pmu__generate_fdt_nodes()
The PMU ioctl KVM_ARM_VCPU_PMU_V3_INIT requires that the GIC has been
initialised, which is done in gic__init_gic().
gic__init_gic() and setup_fdt() are part of the same initialisation list.
The relative order on the list depends on the order of compilation: gic.c
is compiled after fdt.c and this places gic__init_gic() first on the
initialisation list.
If the compilation order changes and gic.c is compiled *before* fdt.c,
gic__init_gic() will be executed *after* setup_fdt() and PMU initialisation
will fail with an error message:
PMU KVM_SET_DEVICE_ATTR: No such device
This is fragile and hard to debug. Improve the situation by having
gic__init_gic() explicitely initialize the PMU.
Signed-off-by: Alexandru Elisei <alexandru.elisei at arm.com>
---
arm64/gic.c | 9 ++++++++-
arm64/include/asm/pmu.h | 3 +++
arm64/pmu.c | 28 ++++++++++++++++++----------
3 files changed, 29 insertions(+), 11 deletions(-)
diff --git a/arm64/gic.c b/arm64/gic.c
index b0be9e5766df..1e4009625595 100644
--- a/arm64/gic.c
+++ b/arm64/gic.c
@@ -9,6 +9,8 @@
#include <linux/kvm.h>
#include <linux/sizes.h>
+#include <asm/pmu.h>
+
#define IRQCHIP_GIC 0
#define GIC_MAINT_IRQ 9
@@ -355,7 +357,12 @@ static int gic__init_gic(struct kvm *kvm)
vgic_is_init = true;
- return irq__setup_irqfd_lines(kvm);
+ ret = irq__setup_irqfd_lines(kvm);
+ if (ret)
+ return ret;
+
+ /* The PMU requires that the GIC has been initialized. */
+ return pmu__init(kvm);
}
late_init(gic__init_gic)
diff --git a/arm64/include/asm/pmu.h b/arm64/include/asm/pmu.h
index 38e3154b11b7..74566943f355 100644
--- a/arm64/include/asm/pmu.h
+++ b/arm64/include/asm/pmu.h
@@ -3,6 +3,9 @@
#define KVM_ARM_PMUv3_PPI 23
+struct kvm;
+
void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm);
+int pmu__init(struct kvm *kvm);
#endif /* __ARM_PMU_H__ */
diff --git a/arm64/pmu.c b/arm64/pmu.c
index 5f31d6b6f346..92cacd62479e 100644
--- a/arm64/pmu.c
+++ b/arm64/pmu.c
@@ -193,21 +193,32 @@ static int find_pmu(struct kvm *kvm)
void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm)
{
const char compatible[] = "arm,armv8-pmuv3";
- int irq = KVM_ARM_PMUv3_PPI;
- struct kvm_cpu *vcpu;
- int pmu_id = -ENXIO;
- int i;
-
u32 cpu_mask = gic__get_fdt_irq_cpumask(kvm);
u32 irq_prop[] = {
cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI),
- cpu_to_fdt32(irq - 16),
+ cpu_to_fdt32(KVM_ARM_PMUv3_PPI - 16),
cpu_to_fdt32(cpu_mask | IRQ_TYPE_LEVEL_HIGH),
};
if (!kvm->cfg.arch.has_pmuv3)
return;
+ _FDT(fdt_begin_node(fdt, "pmu"));
+ _FDT(fdt_property(fdt, "compatible", compatible, sizeof(compatible)));
+ _FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
+ _FDT(fdt_end_node(fdt));
+}
+
+int pmu__init(struct kvm *kvm)
+{
+ int irq = KVM_ARM_PMUv3_PPI;
+ struct kvm_cpu *vcpu;
+ int pmu_id = -ENXIO;
+ int i;
+
+ if (!kvm->cfg.arch.has_pmuv3)
+ return 0;
+
if (pmu_has_attr(kvm->cpus[0], KVM_ARM_VCPU_PMU_V3_SET_PMU)) {
pmu_id = find_pmu(kvm);
if (pmu_id < 0) {
@@ -228,8 +239,5 @@ void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm)
set_pmu_attr(vcpu, NULL, KVM_ARM_VCPU_PMU_V3_INIT);
}
- _FDT(fdt_begin_node(fdt, "pmu"));
- _FDT(fdt_property(fdt, "compatible", compatible, sizeof(compatible)));
- _FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
- _FDT(fdt_end_node(fdt));
+ return 0;
}
--
2.54.0
More information about the linux-arm-kernel
mailing list