summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Zyngier <maz@kernel.org>2026-10-27 14:23:16 +0000
committerMarc Zyngier <maz@kernel.org>2026-10-27 14:23:16 +0000
commit79f3724ca89134f87f8e0e3f04cd330dc98d1654 (patch)
treeb7e82949adaf22ce044ea07c706f3a3d7f3c4522
parent9e9283e4c941e806d2735f09b66763487bc48b50 (diff)
parent99ec74a33d52219f57cc61a2bc7bd5114cc997b0 (diff)
downloadlinux-next-79f3724ca89134f87f8e0e3f04cd330dc98d1654.tar.gz
linux-next-79f3724ca89134f87f8e0e3f04cd330dc98d1654.zip
Merge branch kvm-arm64/vgic-fixes-7.4-1 into kvmarm-master/fixes
* kvm-arm64/vgic-fixes-7.4-1: : \ : Fix a bunch of low-impact issues with GICv4, avoiding leaks on : unlikely failure paths. : / KVM: arm64: vgic-v4: Restore nr_vpes before freeing the vPE resources KVM: arm64: vgic: Tear down what vgic_init() created when it fails irqchip/gic-v4: Unwind what its_alloc_vcpu_irqs() allocated on failure irqchip/gic-v4: Clear the domain and fwnode pointers after freeing them Signed-off-by: Marc Zyngier <maz@kernel.org>
-rw-r--r--arch/arm64/kvm/vgic/vgic-init.c21
-rw-r--r--arch/arm64/kvm/vgic/vgic-v4.c3
-rw-r--r--drivers/irqchip/irq-gic-v4.c34
3 files changed, 45 insertions, 13 deletions
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index c9991e24cb6b..7050e274b742 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -519,7 +519,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)
@@ -534,17 +534,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)
diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c
index ed236f083f0d..30e1de3fc7d9 100644
--- a/arch/arm64/kvm/vgic/vgic-v4.c
+++ b/arch/arm64/kvm/vgic/vgic-v4.c
@@ -333,6 +333,9 @@ void vgic_v4_teardown(struct kvm *kvm)
free_irq(irq, vcpu);
}
+ /* Make sure we free all VM-wide, per-CPU resources */
+ its_vm->nr_vpes = atomic_read(&kvm->online_vcpus);
+
its_free_vcpu_irqs(its_vm);
kfree(its_vm->vpes);
its_vm->nr_vpes = 0;
diff --git a/drivers/irqchip/irq-gic-v4.c b/drivers/irqchip/irq-gic-v4.c
index 8455b4a5fbb0..f707a3cb281a 100644
--- a/drivers/irqchip/irq-gic-v4.c
+++ b/drivers/irqchip/irq-gic-v4.c
@@ -147,14 +147,20 @@ static int its_alloc_vcpu_sgis(struct its_vpe *vpe, int idx)
return 0;
err:
- if (vpe->sgi_domain)
+ if (vpe->sgi_domain) {
irq_domain_remove(vpe->sgi_domain);
- if (vpe->fwnode)
+ vpe->sgi_domain = NULL;
+ }
+ if (vpe->fwnode) {
irq_domain_free_fwnode(vpe->fwnode);
+ vpe->fwnode = NULL;
+ }
kfree(name);
return -ENOMEM;
}
+static void its_free_sgi_irqs(struct its_vm *vm);
+
int its_alloc_vcpu_irqs(struct its_vm *vm)
{
int vpe_base_irq, i;
@@ -185,16 +191,23 @@ int its_alloc_vcpu_irqs(struct its_vm *vm)
vm->vpes[i]->irq = vpe_base_irq + i;
ret = its_alloc_vcpu_sgis(vm->vpes[i], i);
if (ret)
- goto err;
+ goto err_free_irqs;
}
return 0;
+err_free_irqs:
+ its_free_sgi_irqs(vm);
+ irq_domain_free_irqs(vpe_base_irq, vm->nr_vpes);
err:
- if (vm->domain)
+ if (vm->domain) {
irq_domain_remove(vm->domain);
- if (vm->fwnode)
+ vm->domain = NULL;
+ }
+ if (vm->fwnode) {
irq_domain_free_fwnode(vm->fwnode);
+ vm->fwnode = NULL;
+ }
return -ENOMEM;
}
@@ -207,14 +220,21 @@ static void its_free_sgi_irqs(struct its_vm *vm)
return;
for (i = 0; i < vm->nr_vpes; i++) {
- unsigned int irq = irq_find_mapping(vm->vpes[i]->sgi_domain, 0);
+ unsigned int irq;
+
+ /* irq_find_mapping() falls back to the default domain on NULL. */
+ if (!vm->vpes[i]->sgi_domain)
+ continue;
+ irq = irq_find_mapping(vm->vpes[i]->sgi_domain, 0);
if (WARN_ON(!irq))
continue;
irq_domain_free_irqs(irq, 16);
irq_domain_remove(vm->vpes[i]->sgi_domain);
irq_domain_free_fwnode(vm->vpes[i]->fwnode);
+ vm->vpes[i]->sgi_domain = NULL;
+ vm->vpes[i]->fwnode = NULL;
}
}
@@ -224,6 +244,8 @@ void its_free_vcpu_irqs(struct its_vm *vm)
irq_domain_free_irqs(vm->vpes[0]->irq, vm->nr_vpes);
irq_domain_remove(vm->domain);
irq_domain_free_fwnode(vm->fwnode);
+ vm->domain = NULL;
+ vm->fwnode = NULL;
}
static int its_send_vpe_cmd(struct its_vpe *vpe, struct its_cmd_info *info)