From: Salil Mehta <salil.mehta@huawei.com>
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.
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
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
Hi Salil,
On 10/1/25 11:01 AM, salil.mehta@opnsrc.net wrote:
> From: Salil Mehta <salil.mehta@huawei.com>
>
> 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.
>
> Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
> ---
> 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: "
It's enforced that total_cpus is equal to maxcpus. The followup check
"if (total_cpus != maxcpus)" becomes unnecessary. Also, does this makes
"-smp maxcpus=x" unnecessary?
Thanks,
Gavin
> @@ -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,
salil.mehta@opnsrc.net writes:
> From: Salil Mehta <salil.mehta@huawei.com>
>
> 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.
>
> Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
[...]
> 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',
We prefer words-with-dashes to wordsruntogether in QAPI/QMP, for
readability: disabled-cpus.
Missing here even before the patch: how these values are related. That
information appears to be buried in machine_parse_smp_config(), which is
160 lines long and with too many conditionals for me to make sense of
now.
> 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
> +
So, maxcpus = cpus + disabledcpus?
Is drawers * books * sockets * dies * clusters * modules * cores *
threads equal or greater than maxcpus?
> 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,
Hi Salil,
> On 1 Oct 2025, at 01:01, salil.mehta@opnsrc.net wrote:
>
> From: Salil Mehta <salil.mehta@huawei.com>
>
> 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 <salil.mehta@huawei.com>
> ---
> 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
>
On Thu, 9 Oct 2025 11:28:40 +0000
Miguel Luis <miguel.luis@oracle.com> wrote:
> Hi Salil,
>
> > On 1 Oct 2025, at 01:01, salil.mehta@opnsrc.net wrote:
> >
> > From: Salil Mehta <salil.mehta@huawei.com>
> >
> > 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.
See my reply to cover letter, we shouldn't touch -smp at all.
And make -device/device_add work with virt/arm cpus instead,
that solves a number of issues that this and -deviceset patches introduce.
> Thanks
> Miguel
>
>
> > Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
> > ---
> > 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
> >
>
© 2016 - 2025 Red Hat, Inc.