Hi Salil, > On 1 Oct 2025, at 01:01, [email protected] wrote: > > From: Salil Mehta <[email protected]> > > Add support for a new SMP configuration parameter, 'disabledcpus', which > specifies the number of additional CPUs that are present in the virtual > machine but administratively disabled at boot. These CPUs are visible in > firmware (e.g. ACPI tables) yet unavailable to the guest until explicitly > enabled via QMP/HMP, or via the 'device_set' API (introduced in later > patches). > > This feature is intended for architectures that lack native CPU hotplug > support but can change the administrative power state of present CPUs. > It allows simulating CPU hot-add–like scenarios while all CPUs remain > physically present in the topology at boot time. > > Note: ARM is the first architecture to support this concept. > > Changes include: > - Extend CpuTopology with a 'disabledcpus' field. > - Update machine_parse_smp_config() to account for disabled CPUs when > computing 'cpus' and 'maxcpus'. > - Update SMPConfiguration in QAPI to accept 'disabledcpus'. > - Extend -smp option documentation to describe 'disabledcpus' usage and > behavior. >
Specifying a new parameter for the user seems unnecessary when the system could infer the number of present and disabled from (maxcpus - cpus) and those this patch calls "disabledcpus" could be obtained this way. Naming is hard although it is of my opinion that we shouldn't be calling 'disabledcpus' here; I understand that gets carried by previous administrative power state meanings but machine-smp level being at a different abstraction level the administrative power state concept could be decoupled from machine-smp realm. My suggestion would be calling those cpus 'inactive' and not carry previous patch's nomenclature. CPUs in 'inactive' state are still present in the virtual machine although this pre-condition may require post actions like being explicitly 'enabled'/active via [QH]MP. Overall, I believe the above should be all it takes to simplify acommodation of CPUs not to be brought online at boot time within this patch's context. Thanks Miguel > Signed-off-by: Salil Mehta <[email protected]> > --- > hw/core/machine-smp.c | 24 +++++++----- > include/hw/boards.h | 2 + > qapi/machine.json | 3 ++ > qemu-options.hx | 86 +++++++++++++++++++++++++++++++++---------- > system/vl.c | 3 ++ > 5 files changed, 89 insertions(+), 29 deletions(-) > > diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c > index 0be0ac044c..c1a09fdc3f 100644 > --- a/hw/core/machine-smp.c > +++ b/hw/core/machine-smp.c > @@ -87,6 +87,7 @@ void machine_parse_smp_config(MachineState *ms, > { > MachineClass *mc = MACHINE_GET_CLASS(ms); > unsigned cpus = config->has_cpus ? config->cpus : 0; > + unsigned disabledcpus = config->has_disabledcpus ? config->disabledcpus > : 0; > unsigned drawers = config->has_drawers ? config->drawers : 0; > unsigned books = config->has_books ? config->books : 0; > unsigned sockets = config->has_sockets ? config->sockets : 0; > @@ -166,8 +167,13 @@ void machine_parse_smp_config(MachineState *ms, > sockets = sockets > 0 ? sockets : 1; > cores = cores > 0 ? cores : 1; > threads = threads > 0 ? threads : 1; > + > + maxcpus = drawers * books * sockets * dies * clusters * > + modules * cores * threads; > + cpus = maxcpus - disabledcpus; > } else { > - maxcpus = maxcpus > 0 ? maxcpus : cpus; > + maxcpus = maxcpus > 0 ? maxcpus : cpus + disabledcpus; > + cpus = cpus > 0 ? cpus : maxcpus - disabledcpus; > > if (mc->smp_props.prefer_sockets) { > /* prefer sockets over cores before 6.2 */ > @@ -207,12 +213,8 @@ void machine_parse_smp_config(MachineState *ms, > } > } > > - total_cpus = drawers * books * sockets * dies * > - clusters * modules * cores * threads; > - maxcpus = maxcpus > 0 ? maxcpus : total_cpus; > - cpus = cpus > 0 ? cpus : maxcpus; > - > ms->smp.cpus = cpus; > + ms->smp.disabledcpus = disabledcpus; > ms->smp.drawers = drawers; > ms->smp.books = books; > ms->smp.sockets = sockets; > @@ -226,6 +228,8 @@ void machine_parse_smp_config(MachineState *ms, > mc->smp_props.has_clusters = config->has_clusters; > > /* sanity-check of the computed topology */ > + total_cpus = maxcpus = drawers * books * sockets * dies * clusters * > + modules * cores * threads; > if (total_cpus != maxcpus) { > g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); > error_setg(errp, "Invalid CPU topology: " > @@ -235,12 +239,12 @@ void machine_parse_smp_config(MachineState *ms, > return; > } > > - if (maxcpus < cpus) { > + if (maxcpus < (cpus + disabledcpus)) { > g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); > error_setg(errp, "Invalid CPU topology: " > - "maxcpus must be equal to or greater than smp: " > - "%s == maxcpus (%u) < smp_cpus (%u)", > - topo_msg, maxcpus, cpus); > + "maxcpus must be equal to or greater than > smp[+disabledcpus]:" > + "%s == maxcpus (%u) < smp_cpus (%u) [+ offline cpus > (%u)]", > + topo_msg, maxcpus, cpus, disabledcpus); > return; > } > > diff --git a/include/hw/boards.h b/include/hw/boards.h > index f94713e6e2..2b182d7817 100644 > --- a/include/hw/boards.h > +++ b/include/hw/boards.h > @@ -361,6 +361,7 @@ typedef struct DeviceMemoryState { > /** > * CpuTopology: > * @cpus: the number of present logical processors on the machine > + * @disabledcpus: the number additional present but admin disabled cpus > * @drawers: the number of drawers on the machine > * @books: the number of books in one drawer > * @sockets: the number of sockets in one book > @@ -373,6 +374,7 @@ typedef struct DeviceMemoryState { > */ > typedef struct CpuTopology { > unsigned int cpus; > + unsigned int disabledcpus; > unsigned int drawers; > unsigned int books; > unsigned int sockets; > diff --git a/qapi/machine.json b/qapi/machine.json > index 038eab281c..e45740da33 100644 > --- a/qapi/machine.json > +++ b/qapi/machine.json > @@ -1634,6 +1634,8 @@ > # > # @cpus: number of virtual CPUs in the virtual machine > # > +# @disabledcpus: number of additional present but disabled(or offline) CPUs > +# > # @maxcpus: maximum number of hotpluggable virtual CPUs in the virtual > # machine > # > @@ -1657,6 +1659,7 @@ > ## > { 'struct': 'SMPConfiguration', 'data': { > '*cpus': 'int', > + '*disabledcpus': 'int', > '*drawers': 'int', > '*books': 'int', > '*sockets': 'int', > diff --git a/qemu-options.hx b/qemu-options.hx > index ab23f14d21..83ccde341b 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -326,12 +326,15 @@ SRST > ERST > > DEF("smp", HAS_ARG, QEMU_OPTION_smp, > - "-smp > [[cpus=]n][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets]\n" > - " > [,dies=dies][,clusters=clusters][,modules=modules][,cores=cores]\n" > - " [,threads=threads]\n" > - " set the number of initial CPUs to 'n' [default=1]\n" > - " maxcpus= maximum number of total CPUs, including\n" > - " offline CPUs for hotplug, etc\n" > + "-smp > [[cpus=]n][,disabledcpus=disabledcpus][,maxcpus=maxcpus][,drawers=drawers][,books=books]\n" > + " > [,sockets=sockets][,dies=dies][,clusters=clusters][,modules=modules]\n" > + " [,cores=cores][,threads=threads]\n" > + " set the initial number of CPUs present and\n" > + " administratively enabled at boot time to 'n' > [default=1]\n" > + " disabledcpus= number of present but administratively\n" > + " disabled CPUs (unavailable to the guest at boot)\n" > + " maxcpus= maximum total CPUs (present + hotpluggable)\n" > + " on machines without CPU hotplug, defaults to n + > disabledcpus\n" > " drawers= number of drawers on the machine board\n" > " books= number of books in one drawer\n" > " sockets= number of sockets in one book\n" > @@ -351,22 +354,49 @@ DEF("smp", HAS_ARG, QEMU_OPTION_smp, > " For a particular machine type board, an expected CPU topology > hierarchy\n" > " can be defined through the supported sub-option. Unsupported > parameters\n" > " can also be provided in addition to the sub-option, but their > values\n" > - " must be set as 1 in the purpose of correct parsing.\n", > + " must be set as 1 in the purpose of correct parsing.\n" > + " \n" > + " Administratively disabled CPUs: Some machine types do not support > vCPU\n" > + " hotplug but their CPUs can be marked disabled (powered off) and > kept\n" > + " unavailable to the guest. Later, such CPUs can be enabled via > QMP/HMP\n" > + " (e.g., 'device_set ... admin-state=enable'). This is similar to > hotplug,\n" > + " except all disabled CPUs are already present at boot. Useful on\n" > + " architectures that lack architectural CPU hotplug.\n", > QEMU_ARCH_ALL) > SRST > -``-smp > [[cpus=]n][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets][,dies=dies][,clusters=clusters][,modules=modules][,cores=cores][,threads=threads]`` > - Simulate a SMP system with '\ ``n``\ ' CPUs initially present on > - the machine type board. On boards supporting CPU hotplug, the optional > - '\ ``maxcpus``\ ' parameter can be set to enable further CPUs to be > - added at runtime. When both parameters are omitted, the maximum number > +``-smp > [[cpus=]n][,disabledcpus=disabledcpus][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets][,dies=dies][,clusters=clusters][,modules=modules][,cores=cores][,threads=threads]`` > + Simulate a SMP system with '\ ``n``\ ' CPUs initially present & enabled > on > + the machine type board. Furthermore, on architectures that support > changing > + the administrative power state of CPUs, optional '\ ``disabledcpus``\ ' > + parameter specifies *additional* CPUs that are present in firmware (e.g., > + ACPI) but are administratively disabled (i.e., not usable by the guest at > + boot time). > + > + This is different from CPU hotplug where additional CPUs are not even > + present in the system description. Administratively disabled CPUs appear > in > + ACPI tables i.e. are provisioned, but cannot be used until explicitly > + enabled via QMP/HMP or the deviceset API. > + > + On boards supporting CPU hotplug, the optional '\ ``maxcpus``\ ' > parameter > + can be set to enable further CPUs to be added at runtime. When both > + '\ ``n``\ ' & '\ ``maxcpus``\ ' parameters are omitted, the maximum > number > of CPUs will be calculated from the provided topology members and the > - initial CPU count will match the maximum number. When only one of them > - is given then the omitted one will be set to its counterpart's value. > - Both parameters may be specified, but the maximum number of CPUs must > - be equal to or greater than the initial CPU count. Product of the > - CPU topology hierarchy must be equal to the maximum number of CPUs. > - Both parameters are subject to an upper limit that is determined by > - the specific machine type chosen. > + initial CPU count will match the maximum number. When only one of them is > + given then the omitted one will be set to its counterpart's value. Both > + parameters may be specified, but the maximum number of CPUs must be equal > + to or greater than the initial CPU count. Product of the CPU topology > + hierarchy must be equal to the maximum number of CPUs. Both parameters > are > + subject to an upper limit that is determined by the specific machine type > + chosen. Boards that support administratively disabled CPUs but do *not* > + support CPU hotplug derive the maximum number of CPUs implicitly: > + '\ ``maxcpus``\ ' is treated as '\ ``n + disabledcpus``\ ' (the total > CPUs > + present in firmware). If '\ ``maxcpus``\ ' is provided, it must equal > + '\ ``n + disabledcpus``\ '. The topology product must equal this derived > + maximum as well. > + > + Note: Administratively disabled CPUs will appear to the guest as > + unavailable, and any attempt to bring them online must go through QMP/HMP > + commands like 'device_set'. > > To control reporting of CPU topology information, values of the topology > parameters can be specified. Machines may only support a subset of the > @@ -425,6 +455,24 @@ SRST > > -smp 2 > > + Examples using 'disabledcpus': > + > + For a board without CPU hotplug, enable 4 CPUs at boot and provision > + 2 additional administratively disabled CPUs (maximum is derived > + implicitly as 6 = 4 + 2): > + > + :: > + > + -smp cpus=4,disabledcpus=2 > + > + For a board that supports CPU hotplug and 'disabledcpus', enable 4 CPUs > + at boot, provision 2 administratively disabled CPUs, and allow hotplug of > + 2 more CPUs (for a maximum of 8): > + > + :: > + > + -smp cpus=4,disabledcpus=2,maxcpus=8 > + > Note: The cluster topology will only be generated in ACPI and exposed > to guest if it's explicitly specified in -smp. > ERST > diff --git a/system/vl.c b/system/vl.c > index 3b7057e6c6..2f0fd21a1f 100644 > --- a/system/vl.c > +++ b/system/vl.c > @@ -736,6 +736,9 @@ static QemuOptsList qemu_smp_opts = { > { > .name = "cpus", > .type = QEMU_OPT_NUMBER, > + }, { > + .name = "disabledcpus", > + .type = QEMU_OPT_NUMBER, > }, { > .name = "drawers", > .type = QEMU_OPT_NUMBER, > -- > 2.34.1 >
