On Thu, 9 Oct 2025 12:32:15 +0000
Miguel Luis <[email protected]> wrote:

> Hi Salil,
> 
> > On 1 Oct 2025, at 01:01, [email protected] wrote:
> > 
> > From: Salil Mehta <[email protected]>
> > 
> > To support a vCPU hot-add–like model on ARM, the virt machine may be setup 
> > with
> > more CPUs than are active at boot. These additional CPUs are fully realized 
> > in
> > KVM and listed in ACPI tables from the start, but begin in a disabled state.
> > They can later be brought online or taken offline under host or platform 
> > policy
> > control. The CPU topology is fixed at VM creation time and cannot change
> > dynamically on ARM. Therefore, we must determine precisely the 'maxcpus' 
> > value
> > that applies for the full lifetime of the VM.
> > 
> > On ARM, this deferred online-capable model is only valid if:
> >  - The GIC version is 3 or higher, and
> >  - Each non-boot CPU’s GIC CPU Interface is marked “online-capable” in its
> >    ACPI GICC structure (UEFI ACPI Specification 6.5, §5.2.12.14, Table 5.37
> >    “GICC CPU Interface Flags”), and
> >  - The chosen accelerator supports safe deferred CPU online:
> >      * TCG with multi-threaded TCG (MTTCG) enabled
> >      * KVM (on supported hosts)
> >      * Not HVF or QTest
> > 
> > This patch sizes the machine’s max-possible CPUs during VM init:
> >  - If all conditions are satisfied, retain the full set of CPUs 
> > corresponding
> >    to (`-smp cpus` + `-smp disabledcpus`), allowing the additional 
> > (initially
> >    disabled) CPUs to participate in later policy-driven online.
> >  - Otherwise, clamp the max-possible CPUs to the boot-enabled count
> >    (`-smp disabledcpus=0` equivalent) to avoid advertising CPUs the guest 
> > can
> >    never use.
> > 
> > A new MachineClass flag, `has_online_capable_cpus`, records whether the 
> > machine
> > supports deferred vCPU online. This is usable by other machine types as 
> > well.  
> 
> 
> By the definition of
> 
>  * @has_hotpluggable_cpus:
>  *    If true, board supports CPUs creation with -device/device_add.
> 
>  in include/hw/boards.h

It should be fine to rename it to has_pluggable_cpus.

But we should add support to arm/virt for -device/device_add cpu_foo,
to avoid awkward -device-set and mangling of -smp.

device_add in arm/virt case probably should be limited non hotplug usecase.

> seems one could take advantage of MachineClass's has_hotpluggable_cpus 
> variable
> instead of creating a new has_online_capable_cpus one.
> (Again, IMHO ‘online capable’ is ACPI nomenclature and doesn’t need to be 
> brought
> in MachineClass’s)

the issue with has_hotpluggable_cpus might be QMP ABI,
where libvirt migh use it to figure out if certain command are supported.

CCing libvirt to check if that would break something.

> 
> Variable which would be initialized in machvirt_init on an assignment based on
> GIC version and/or wether there's inactive CPUs and proceed from there 
> anyways,
> making the default assignment in machine_virt_class_init superfluous.
> 
> We're at hw/arm/virt and we know these CPUs are administratively power state
> coordinated so admin_power_state_supported can still be set there in the
> presence of inactive CPUs.
> 
> Thanks
> Miguel
> 
> > 
> > Signed-off-by: Salil Mehta <[email protected]>
> > ---
> > hw/arm/virt.c       | 84 ++++++++++++++++++++++++++++++---------------
> > include/hw/boards.h |  1 +
> > 2 files changed, 57 insertions(+), 28 deletions(-)
> > 
> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index ef6be3660f..76f21bd56a 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -2168,8 +2168,7 @@ static void machvirt_init(MachineState *machine)
> >     bool has_ged = !vmc->no_ged;
> >     unsigned int smp_cpus = machine->smp.cpus;
> >     unsigned int max_cpus = machine->smp.max_cpus;
> > -
> > -    possible_cpus = mc->possible_cpu_arch_ids(machine);
> > +    DeviceClass *dc;
> > 
> >     /*
> >      * In accelerated mode, the memory map is computed earlier in kvm_type()
> > @@ -2186,7 +2185,7 @@ static void machvirt_init(MachineState *machine)
> >          * we are about to deal with. Once this is done, get rid of
> >          * the object.
> >          */
> > -        cpuobj = object_new(possible_cpus->cpus[0].type);
> > +        cpuobj = object_new(machine->cpu_type);
> >         armcpu = ARM_CPU(cpuobj);
> > 
> >         pa_bits = arm_pamax(armcpu);
> > @@ -2201,6 +2200,57 @@ static void machvirt_init(MachineState *machine)
> >      */
> >     finalize_gic_version(vms);
> > 
> > +    /*
> > +     * The maximum number of CPUs depends on the GIC version, or on how
> > +     * many redistributors we can fit into the memory map (which in turn
> > +     * depends on whether this is a GICv3 or v4).
> > +     */
> > +    if (vms->gic_version == VIRT_GIC_VERSION_2) {
> > +        virt_max_cpus = GIC_NCPU;
> > +    } else {
> > +        virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST);
> > +        if (vms->highmem_redists) {
> > +            virt_max_cpus += virt_redist_capacity(vms, 
> > VIRT_HIGH_GIC_REDIST2);
> > +        }
> > +    }
> > +
> > +    if ((tcg_enabled() && !qemu_tcg_mttcg_enabled()) || hvf_enabled() ||
> > +        qtest_enabled() || vms->gic_version == VIRT_GIC_VERSION_2) {
> > +        max_cpus = machine->smp.max_cpus = smp_cpus;
> > +        if (mc->has_online_capable_cpus) {
> > +            if (vms->gic_version == VIRT_GIC_VERSION_2) {
> > +                warn_report("GICv2 does not support online-capable CPUs");
> > +            }
> > +            mc->has_online_capable_cpus = false;
> > +        }
> > +    }
> > +
> > +    if (mc->has_online_capable_cpus) {
> > +        max_cpus = smp_cpus + machine->smp.disabledcpus;
> > +        machine->smp.max_cpus = max_cpus;
> > +    }
> > +
> > +    if (max_cpus > virt_max_cpus) {
> > +        error_report("Number of SMP CPUs requested (%d) exceeds max CPUs "
> > +                     "supported by machine 'mach-virt' (%d)",
> > +                     max_cpus, virt_max_cpus);
> > +        if (vms->gic_version != VIRT_GIC_VERSION_2 && 
> > !vms->highmem_redists) {
> > +            error_printf("Try 'highmem-redists=on' for more CPUs\n");
> > +        }
> > +
> > +        exit(1);
> > +    }
> > +
> > +    dc = DEVICE_CLASS(object_class_by_name(machine->cpu_type));
> > +    if (!dc) {
> > +        error_report("CPU type '%s' not registered", machine->cpu_type);
> > +        exit(1);
> > +    }
> > +    dc->admin_power_state_supported = mc->has_online_capable_cpus;
> > +
> > +    /* uses smp.max_cpus to initialize all possible vCPUs */
> > +    possible_cpus = mc->possible_cpu_arch_ids(machine);
> > +
> >     if (vms->secure) {
> >         /*
> >          * The Secure view of the world is the same as the NonSecure,
> > @@ -2235,31 +2285,6 @@ static void machvirt_init(MachineState *machine)
> >         vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC;
> >     }
> > 
> > -    /*
> > -     * The maximum number of CPUs depends on the GIC version, or on how
> > -     * many redistributors we can fit into the memory map (which in turn
> > -     * depends on whether this is a GICv3 or v4).
> > -     */
> > -    if (vms->gic_version == VIRT_GIC_VERSION_2) {
> > -        virt_max_cpus = GIC_NCPU;
> > -    } else {
> > -        virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST);
> > -        if (vms->highmem_redists) {
> > -            virt_max_cpus += virt_redist_capacity(vms, 
> > VIRT_HIGH_GIC_REDIST2);
> > -        }
> > -    }
> > -
> > -    if (max_cpus > virt_max_cpus) {
> > -        error_report("Number of SMP CPUs requested (%d) exceeds max CPUs "
> > -                     "supported by machine 'mach-virt' (%d)",
> > -                     max_cpus, virt_max_cpus);
> > -        if (vms->gic_version != VIRT_GIC_VERSION_2 && 
> > !vms->highmem_redists) {
> > -            error_printf("Try 'highmem-redists=on' for more CPUs\n");
> > -        }
> > -
> > -        exit(1);
> > -    }
> > -
> >     if (vms->secure && !tcg_enabled() && !qtest_enabled()) {
> >         error_report("mach-virt: %s does not support providing "
> >                      "Security extensions (TrustZone) to the guest CPU",
> > @@ -3245,6 +3270,9 @@ static void virt_machine_class_init(ObjectClass *oc, 
> > const void *data)
> >     hc->plug = virt_machine_device_plug_cb;
> >     hc->unplug_request = virt_machine_device_unplug_request_cb;
> >     hc->unplug = virt_machine_device_unplug_cb;
> > +
> > +    mc->has_online_capable_cpus = true;
> > +
> >     mc->nvdimm_supported = true;
> >     mc->smp_props.clusters_supported = true;
> >     mc->auto_enable_numa_with_memhp = true;
> > diff --git a/include/hw/boards.h b/include/hw/boards.h
> > index 2b182d7817..b27c2326a2 100644
> > --- a/include/hw/boards.h
> > +++ b/include/hw/boards.h
> > @@ -302,6 +302,7 @@ struct MachineClass {
> >     bool rom_file_has_mr;
> >     int minimum_page_bits;
> >     bool has_hotpluggable_cpus;
> > +    bool has_online_capable_cpus;
> >     bool ignore_memory_transaction_failures;
> >     int numa_mem_align_shift;
> >     const char * const *valid_cpu_types;
> > -- 
> > 2.34.1
> >   
> 


Reply via email to