[PATCH v2 12/39] KVM: arm64: gic-v5: Keep GICv5 vCPU limit model-specific
Sascha Bischoff
Sascha.Bischoff at arm.com
Thu May 21 07:53:14 PDT 2026
A GICv5 host with FEAT_GCIE_LEGACY can expose both a native vGICv5 or
a vGICv3 device. These models do not necessarily have the same vCPU
limit: the native GICv5 limit is probed from the IRS VPE capacity,
while the GICv3 limit remains the fixed KVM vGICv3 limit.
Keep the IRS-derived limit separately for vGICv5 creation. The
pre-VGIC KVM_CAP_MAX_VCPUS value continues to expose the largest limit
among the still-selectable models, and kvm_vgic_create() clamps the VM
to the limit of the VGIC model userspace actually selected.
Signed-off-by: Sascha Bischoff <sascha.bischoff at arm.com>
---
arch/arm64/kvm/vgic/vgic-init.c | 14 +++++++++-----
arch/arm64/kvm/vgic/vgic-v5.c | 19 +++++++++----------
include/kvm/arm_vgic.h | 16 ++++++++++++----
3 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 079a57c2b18f6..94632fd90b728 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -129,13 +129,17 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
}
ret = 0;
- if (type == KVM_DEV_TYPE_ARM_VGIC_V2)
+ switch (type) {
+ case KVM_DEV_TYPE_ARM_VGIC_V2:
kvm->max_vcpus = VGIC_V2_MAX_CPUS;
- else if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
+ break;
+ case KVM_DEV_TYPE_ARM_VGIC_V3:
kvm->max_vcpus = VGIC_V3_MAX_CPUS;
- else if (type == KVM_DEV_TYPE_ARM_VGIC_V5)
- kvm->max_vcpus = min(VGIC_V5_MAX_CPUS,
- kvm_vgic_global_state.max_gic_vcpus);
+ break;
+ case KVM_DEV_TYPE_ARM_VGIC_V5:
+ kvm->max_vcpus = kvm_vgic_global_state.max_gicv5_vcpus;
+ break;
+ }
if (atomic_read(&kvm->online_vcpus) > kvm->max_vcpus) {
ret = -E2BIG;
diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c
index f9578c2a634a4..909cef5f31afa 100644
--- a/arch/arm64/kvm/vgic/vgic-v5.c
+++ b/arch/arm64/kvm/vgic/vgic-v5.c
@@ -110,7 +110,8 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
int ret;
kvm_vgic_global_state.type = VGIC_V5;
- kvm_vgic_global_state.max_gic_vcpus = VGIC_V5_MAX_CPUS;
+ kvm_vgic_global_state.max_gic_vcpus = 0;
+ kvm_vgic_global_state.max_gicv5_vcpus = 0;
kvm_vgic_global_state.vcpu_base = 0;
kvm_vgic_global_state.vctrl_base = NULL;
@@ -135,8 +136,8 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
* Even if the HW supports more per-VM vCPUs, artificially cap as we
* can't use them all.
*/
- kvm_vgic_global_state.max_gic_vcpus = min(irs_caps.max_vpes,
- VGIC_V5_MAX_CPUS);
+ kvm_vgic_global_state.max_gicv5_vcpus = min(irs_caps.max_vpes,
+ VGIC_V5_MAX_CPUS);
/*
* GICv5 requires a set of tables to be allocated in order to manage
@@ -145,7 +146,7 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
* we want to run. For now, we match the maximum number offered by the
* hardware, but this might not be a wise choice in the long term.
*/
- ret = vgic_v5_vmt_allocate(kvm_vgic_global_state.max_gic_vcpus);
+ ret = vgic_v5_vmt_allocate(kvm_vgic_global_state.max_gicv5_vcpus);
if (ret) {
kvm_err("Failed to allocate the GICv5 VM tables; no GICv5 support\n");
return -ENODEV;
@@ -166,9 +167,6 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
return -ENODEV;
}
- kvm_vgic_global_state.max_gic_vcpus = min(irs_caps.max_vpes,
- VGIC_V5_MAX_CPUS);
-
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V5);
if (ret) {
kvm_err("Cannot register GICv5 KVM device.\n");
@@ -178,6 +176,8 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
}
v5_registered = true;
+ kvm_vgic_global_state.max_gic_vcpus =
+ kvm_vgic_global_state.max_gicv5_vcpus;
kvm_info("GCIE system register CPU interface\n");
skip_v5:
@@ -205,9 +205,8 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
return v5_registered ? 0 : ret;
}
- /* We potentially limit the max VCPUs further than we need to here */
- kvm_vgic_global_state.max_gic_vcpus = min(VGIC_V3_MAX_CPUS,
- kvm_vgic_global_state.max_gic_vcpus);
+ kvm_vgic_global_state.max_gic_vcpus = max(kvm_vgic_global_state.max_gic_vcpus,
+ VGIC_V3_MAX_CPUS);
static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif);
kvm_info("GCIE legacy system register CPU interface\n");
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index ba32cd71fe0a7..6f736094a0e7e 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -157,9 +157,16 @@ struct vgic_global {
/* Maintenance IRQ number */
unsigned int maint_irq;
- /* maximum number of VCPUs allowed (GICv2 limits us to 8) */
+ /*
+ * Maximum number of VCPUs exposed before userspace has selected a
+ * VGIC model. Individual VGIC models can impose a lower limit
+ * (GICv2 limits us to 8).
+ */
int max_gic_vcpus;
+ /* Maximum number of VCPUs allowed for a GICv5 VM. */
+ int max_gicv5_vcpus;
+
/* Only needed for the legacy KVM_CREATE_IRQCHIP */
bool can_emulate_gicv2;
@@ -635,10 +642,11 @@ void kvm_vgic_process_async_update(struct kvm_vcpu *vcpu);
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1);
/**
- * kvm_vgic_get_max_vcpus - Get the maximum number of VCPUs allowed by HW
+ * kvm_vgic_get_max_vcpus - Get the pre-VGIC-selection VCPU limit
*
- * The host's GIC naturally limits the maximum amount of VCPUs a guest
- * can use.
+ * Userspace can query KVM_CAP_MAX_VCPUS before selecting a VGIC model, so
+ * expose the highest model-specific limit and let kvm_vgic_create() enforce
+ * the selected model's actual limit.
*/
static inline int kvm_vgic_get_max_vcpus(void)
{
--
2.34.1
More information about the linux-arm-kernel
mailing list