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 --- 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 14adc82b65828..fd64ea0173b2f 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"); @@ -182,6 +180,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: @@ -209,9 +209,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