diff options
| author | Marc Zyngier <maz@kernel.org> | 2026-10-27 14:23:16 +0000 |
|---|---|---|
| committer | Marc Zyngier <maz@kernel.org> | 2026-10-27 14:23:16 +0000 |
| commit | 79f3724ca89134f87f8e0e3f04cd330dc98d1654 (patch) | |
| tree | b7e82949adaf22ce044ea07c706f3a3d7f3c4522 | |
| parent | 9e9283e4c941e806d2735f09b66763487bc48b50 (diff) | |
| parent | 99ec74a33d52219f57cc61a2bc7bd5114cc997b0 (diff) | |
| download | linux-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.c | 21 | ||||
| -rw-r--r-- | arch/arm64/kvm/vgic/vgic-v4.c | 3 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v4.c | 34 |
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) |
