[PATCH v3 3/4] KVM: arm64: vgic: Tear down what vgic_init() created when it fails
From: Fuad Tabba
Date: Mon Sep 21 2026 - 03:30:53 EST
Once kvm_vgic_dist_init() has succeeded, every later failure in
vgic_init() returns with the SPI array still allocated. A failure after
vgic_v4_init() has also succeeded, which means only
kvm_vgic_setup_default_irq_routing(), leaves the vPE array behind as
well.
A failed vgic_init() leaves kvm_arch_vcpu_precreate() admitting new
vCPUs, so a retry of KVM_DEV_ARM_VGIC_CTRL_INIT reaches
vgic_v4_init()'s early return with an array that no longer covers every
vCPU, and vgic_v3_load()'s WARN_ON(vgic_v4_load()) fires on the first
one it misses.
Release each on the paths that can reach it, so the ioctl is all or
nothing and a retry starts from scratch. vgic_v4_init() unwinds its own
state on every error return, so its failure leaves only the SPI array
to free. dist->nr_spis stays frozen, since the SPI count cannot change
once vgic_init() has supplied it.
Fixes: 180ae7b11823 ("KVM: arm/arm64: Enable irqchip routing")
Fixes: 74fe55dc9ab7 ("KVM: arm/arm64: GICv4: Add init/teardown of the per-VM vPE irq domain")
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Closes: https://lore.kernel.org/all/20260807105558.73D701F000E9@xxxxxxxxxxxxxxx/
Signed-off-by: Fuad Tabba <fuad.tabba@xxxxxxxxx>
---
arch/arm64/kvm/vgic/vgic-init.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 1537f4d20318e..67d1e8ce2aa2f 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -513,7 +513,7 @@ int vgic_init(struct kvm *kvm)
if (vgic_supports_direct_irqs(kvm)) {
ret = vgic_v4_init(kvm);
if (ret)
- return ret;
+ goto out_free_spis;
}
} else {
if (!dist->nr_spis)
@@ -528,17 +528,24 @@ int vgic_init(struct kvm *kvm)
kvm_vgic_vcpu_reset(vcpu);
ret = kvm_vgic_setup_default_irq_routing(kvm);
- if (ret) {
- if (vgic_is_v5(kvm))
- vgic_v5_teardown(kvm);
-
- return ret;
- }
+ if (ret)
+ goto out_teardown;
vgic_debug_init(kvm);
dist->initialized = true;
return 0;
+
+out_teardown:
+ if (vgic_is_v5(kvm))
+ vgic_v5_teardown(kvm);
+ else
+ vgic_v4_teardown(kvm);
+out_free_spis:
+ kfree(dist->spis);
+ dist->spis = NULL;
+
+ return ret;
}
static void kvm_vgic_dist_destroy(struct kvm *kvm)
--
2.39.5