Machine core patches

- Clarify qdev_connect_gpio_out() documentation
 - Rework test-smp-parse tests following QOM style
 - Introduce CPU cluster topology support (Yanan Wang)
 - MAINTAINERS updates (Yanan Wang, Li Zhijian, myself)
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmHO+28ACgkQ4+MsLN6t
 wN53UhAAt4T6k2g4qiBTdvzU3QREGbpBedXsBQU8REykIBmHHG+9PH9QGOejZu9I
 a7i9V2FH1TgNsuIo4DdC01p8Pvrpyy6tJc4GNL8wrXHWKyfPn8e/pOgHFtgxLSWD
 msalW4Q5dQba2qTUXFNT9IBhAnRds56LBnKfseqWFEQyVahbep/MqN41nTJXLmbW
 W0FPivutMp9eKCMjivnjegrWW1Nht/01pMSCphEAsHjjICXjvzIpRJvJIB+kh/pK
 zG7hO+eynFlbbIGBgwU51ANEV7c+/8I0sLAI0O6cfswPdVZxY929DElgDjT7YxIl
 mQBdalEiaHX0cgzY91o/wwJ2lRk5xtVy+hV+PsBIP0RTrcJLAqel7Xsv9eXB1uCr
 /XtEvQbKDo0oP4z9/huxwJkXSZ4FX/UAyAPZQxYhqv3iVbtBhmUD1WN87WKC53Rk
 DbJn7jj+xmcA3SfwN8EdEFn87K72w3t1u7SJnP7w3naLTz5mREKB2K6Z2oCdvtAh
 XTKxIkAOQ9eWQPJYi/2SA8B2xwWfKV7vw2+PT8/eKdKgcmhbcZFqqSSrP7YX3QeW
 RtwCQB8FSS2kT1WCaNkKtKMZykZVYvX8gzzsGSww60GCqPDR33BRp7lqi/VlDdmV
 XBuKLm9vuBA+zJawcZKjv7diwlZTy2kaDOyf+wAnGZso/4HOSHU=
 =gP1z
 -----END PGP SIGNATURE-----

Merge tag 'machine-core-20211231' of https://github.com/philmd/qemu into staging

Machine core patches

- Clarify qdev_connect_gpio_out() documentation
- Rework test-smp-parse tests following QOM style
- Introduce CPU cluster topology support (Yanan Wang)
- MAINTAINERS updates (Yanan Wang, Li Zhijian, myself)

# gpg: Signature made Fri 31 Dec 2021 04:45:35 AM PST
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]

* tag 'machine-core-20211231' of https://github.com/philmd/qemu:
  MAINTAINERS: email address change
  MAINTAINERS: Change philmd's email address
  MAINTAINERS: Self-recommended as reviewer of "Machine core"
  tests/unit/test-smp-parse: Keep default MIN/MAX CPUs in machine_base_class_init
  tests/unit/test-smp-parse: No need to explicitly zero MachineClass members
  tests/unit/test-smp-parse: Add testcases for CPU clusters
  hw/core/machine: Introduce CPU cluster topology support
  qemu-options: Improve readability of SMP related Docs
  hw/core: Rename smp_parse() -> machine_parse_smp_config()
  tests/unit/test-smp-parse: Constify some pointer/struct
  tests/unit/test-smp-parse: Simplify pointer to compound literal use
  tests/unit/test-smp-parse: Add 'smp-generic-valid' machine type
  tests/unit/test-smp-parse: Add 'smp-generic-invalid' machine type
  tests/unit/test-smp-parse: Add 'smp-with-dies' machine type
  tests/unit/test-smp-parse: Split the 'generic' test in valid / invalid
  tests/unit/test-smp-parse: Pass machine type as argument to tests
  hw/qdev: Rename qdev_connect_gpio_out*() 'input_pin' parameter
  hw/qdev: Correct qdev_connect_gpio_out_named() documentation
  hw/qdev: Correct qdev_init_gpio_out_named() documentation
  hw/qdev: Cosmetic around documentation

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2021-12-31 09:33:56 -08:00
commit 814a050530
12 changed files with 358 additions and 131 deletions

View File

@ -3,7 +3,7 @@
#
FROM ubuntu:16.04
MAINTAINER Philippe Mathieu-Daudé <philmd@redhat.com>
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
# Install packages required to build EDK2
RUN apt update \

View File

@ -63,6 +63,7 @@ Paul Burton <paulburton@kernel.org> <paul.burton@mips.com>
Paul Burton <paulburton@kernel.org> <paul.burton@imgtec.com>
Paul Burton <paulburton@kernel.org> <paul@archlinuxmips.org>
Paul Burton <paulburton@kernel.org> <pburton@wavecomp.com>
Philippe Mathieu-Daudé <f4bug@amsat.org> <philmd@redhat.com>
Stefan Brankovic <stefan.brankovic@syrmia.com> <stefan.brankovic@rt-rk.com.com>
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>

View File

@ -1630,7 +1630,8 @@ F: pc-bios/bios-microvm.bin
Machine core
M: Eduardo Habkost <eduardo@habkost.net>
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
R: Philippe Mathieu-Daudé <philmd@redhat.com>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
R: Yanan Wang <wangyanan55@huawei.com>
S: Supported
F: cpu.c
F: hw/core/cpu.c
@ -1810,7 +1811,7 @@ F: docs/virtio-net-failover.rst
T: git https://github.com/jasowang/qemu.git net
Parallel NOR Flash devices
M: Philippe Mathieu-Daudé <philmd@redhat.com>
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
T: git https://gitlab.com/philmd/qemu.git pflash-next
S: Maintained
F: hw/block/pflash_cfi*.c
@ -2226,7 +2227,7 @@ F: hw/isa/piix4.c
F: include/hw/southbridge/piix.h
Firmware configuration (fw_cfg)
M: Philippe Mathieu-Daudé <philmd@redhat.com>
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
R: Gerd Hoffmann <kraxel@redhat.com>
S: Supported
F: docs/specs/fw_cfg.txt
@ -2524,7 +2525,7 @@ F: scripts/coccinelle/errp-guard.cocci
GDB stub
M: Alex Bennée <alex.bennee@linaro.org>
R: Philippe Mathieu-Daudé <philmd@redhat.com>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
S: Maintained
F: gdbstub*
F: include/exec/gdbstub.h
@ -2535,7 +2536,7 @@ Memory API
M: Paolo Bonzini <pbonzini@redhat.com>
M: Peter Xu <peterx@redhat.com>
M: David Hildenbrand <david@redhat.com>
R: Philippe Mathieu-Daudé <philmd@redhat.com>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
S: Supported
F: include/exec/ioport.h
F: include/exec/memop.h
@ -2981,7 +2982,7 @@ F: docs/COLO-FT.txt
COLO Proxy
M: Zhang Chen <chen.zhang@intel.com>
M: Li Zhijian <lizhijian@cn.fujitsu.com>
M: Li Zhijian <lizhijian@fujitsu.com>
S: Supported
F: docs/colo-proxy.txt
F: net/colo*
@ -3029,14 +3030,14 @@ F: include/hw/i2c/smbus_slave.h
F: include/hw/i2c/smbus_eeprom.h
Firmware schema specifications
M: Philippe Mathieu-Daudé <philmd@redhat.com>
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
R: Daniel P. Berrange <berrange@redhat.com>
R: Kashyap Chamarthy <kchamart@redhat.com>
S: Maintained
F: docs/interop/firmware.json
EDK2 Firmware
M: Philippe Mathieu-Daudé <philmd@redhat.com>
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
R: Gerd Hoffmann <kraxel@redhat.com>
S: Supported
F: hw/i386/*ovmf*
@ -3274,7 +3275,7 @@ F: block/null.c
NVMe Block Driver
M: Stefan Hajnoczi <stefanha@redhat.com>
R: Fam Zheng <fam@euphon.net>
R: Philippe Mathieu-Daudé <philmd@redhat.com>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
L: qemu-block@nongnu.org
S: Supported
F: block/nvme*
@ -3517,7 +3518,7 @@ F: tests/tcg/Makefile.include
Integration Testing with the Avocado framework
W: https://trello.com/b/6Qi1pxVn/avocado-qemu
R: Cleber Rosa <crosa@redhat.com>
R: Philippe Mathieu-Daudé <philmd@redhat.com>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
R: Wainer dos Santos Moschetta <wainersm@redhat.com>
R: Beraldo Leal <bleal@redhat.com>
S: Odd Fixes

View File

@ -115,17 +115,18 @@ qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
}
void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
qemu_irq pin)
qemu_irq input_pin)
{
char *propname = g_strdup_printf("%s[%d]",
name ? name : "unnamed-gpio-out", n);
if (pin && !OBJECT(pin)->parent) {
if (input_pin && !OBJECT(input_pin)->parent) {
/* We need a name for object_property_set_link to work */
object_property_add_child(container_get(qdev_get_machine(),
"/unattached"),
"non-qdev-gpio[*]", OBJECT(pin));
"non-qdev-gpio[*]", OBJECT(input_pin));
}
object_property_set_link(OBJECT(dev), propname, OBJECT(pin), &error_abort);
object_property_set_link(OBJECT(dev), propname,
OBJECT(input_pin), &error_abort);
g_free(propname);
}
@ -165,9 +166,9 @@ qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
return disconnected;
}
void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin)
void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq input_pin)
{
qdev_connect_gpio_out_named(dev, NULL, n, pin);
qdev_connect_gpio_out_named(dev, NULL, n, input_pin);
}
void qdev_pass_gpios(DeviceState *dev, DeviceState *container,

View File

@ -37,6 +37,10 @@ static char *cpu_hierarchy_to_string(MachineState *ms)
g_string_append_printf(s, " * dies (%u)", ms->smp.dies);
}
if (mc->smp_props.clusters_supported) {
g_string_append_printf(s, " * clusters (%u)", ms->smp.clusters);
}
g_string_append_printf(s, " * cores (%u)", ms->smp.cores);
g_string_append_printf(s, " * threads (%u)", ms->smp.threads);
@ -44,7 +48,8 @@ static char *cpu_hierarchy_to_string(MachineState *ms)
}
/*
* smp_parse - Generic function used to parse the given SMP configuration
* machine_parse_smp_config: Generic function used to parse the given
* SMP configuration
*
* Any missing parameter in "cpus/maxcpus/sockets/cores/threads" will be
* automatically computed based on the provided ones.
@ -63,12 +68,14 @@ static char *cpu_hierarchy_to_string(MachineState *ms)
* introduced topology members which are likely to be target specific should
* be directly set as 1 if they are omitted (e.g. dies for PC since 4.1).
*/
void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
void machine_parse_smp_config(MachineState *ms,
const SMPConfiguration *config, Error **errp)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
unsigned cpus = config->has_cpus ? config->cpus : 0;
unsigned sockets = config->has_sockets ? config->sockets : 0;
unsigned dies = config->has_dies ? config->dies : 0;
unsigned clusters = config->has_clusters ? config->clusters : 0;
unsigned cores = config->has_cores ? config->cores : 0;
unsigned threads = config->has_threads ? config->threads : 0;
unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
@ -80,6 +87,7 @@ void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
if ((config->has_cpus && config->cpus == 0) ||
(config->has_sockets && config->sockets == 0) ||
(config->has_dies && config->dies == 0) ||
(config->has_clusters && config->clusters == 0) ||
(config->has_cores && config->cores == 0) ||
(config->has_threads && config->threads == 0) ||
(config->has_maxcpus && config->maxcpus == 0)) {
@ -95,8 +103,13 @@ void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
error_setg(errp, "dies not supported by this machine's CPU topology");
return;
}
if (!mc->smp_props.clusters_supported && clusters > 1) {
error_setg(errp, "clusters not supported by this machine's CPU topology");
return;
}
dies = dies > 0 ? dies : 1;
clusters = clusters > 0 ? clusters : 1;
/* compute missing values based on the provided ones */
if (cpus == 0 && maxcpus == 0) {
@ -111,41 +124,42 @@ void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
if (sockets == 0) {
cores = cores > 0 ? cores : 1;
threads = threads > 0 ? threads : 1;
sockets = maxcpus / (dies * cores * threads);
sockets = maxcpus / (dies * clusters * cores * threads);
} else if (cores == 0) {
threads = threads > 0 ? threads : 1;
cores = maxcpus / (sockets * dies * threads);
cores = maxcpus / (sockets * dies * clusters * threads);
}
} else {
/* prefer cores over sockets since 6.2 */
if (cores == 0) {
sockets = sockets > 0 ? sockets : 1;
threads = threads > 0 ? threads : 1;
cores = maxcpus / (sockets * dies * threads);
cores = maxcpus / (sockets * dies * clusters * threads);
} else if (sockets == 0) {
threads = threads > 0 ? threads : 1;
sockets = maxcpus / (dies * cores * threads);
sockets = maxcpus / (dies * clusters * cores * threads);
}
}
/* try to calculate omitted threads at last */
if (threads == 0) {
threads = maxcpus / (sockets * dies * cores);
threads = maxcpus / (sockets * dies * clusters * cores);
}
}
maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads;
maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * clusters * cores * threads;
cpus = cpus > 0 ? cpus : maxcpus;
ms->smp.cpus = cpus;
ms->smp.sockets = sockets;
ms->smp.dies = dies;
ms->smp.clusters = clusters;
ms->smp.cores = cores;
ms->smp.threads = threads;
ms->smp.max_cpus = maxcpus;
/* sanity-check of the computed topology */
if (sockets * dies * cores * threads != maxcpus) {
if (sockets * dies * clusters * cores * threads != maxcpus) {
g_autofree char *topo_msg = cpu_hierarchy_to_string(ms);
error_setg(errp, "Invalid CPU topology: "
"product of the hierarchy must match maxcpus: "

View File

@ -742,10 +742,12 @@ static void machine_get_smp(Object *obj, Visitor *v, const char *name,
.has_cpus = true, .cpus = ms->smp.cpus,
.has_sockets = true, .sockets = ms->smp.sockets,
.has_dies = true, .dies = ms->smp.dies,
.has_clusters = true, .clusters = ms->smp.clusters,
.has_cores = true, .cores = ms->smp.cores,
.has_threads = true, .threads = ms->smp.threads,
.has_maxcpus = true, .maxcpus = ms->smp.max_cpus,
};
if (!visit_type_SMPConfiguration(v, name, &config, &error_abort)) {
return;
}
@ -761,7 +763,7 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name,
return;
}
smp_parse(ms, config, errp);
machine_parse_smp_config(ms, config, errp);
}
static void machine_class_init(ObjectClass *oc, void *data)
@ -932,6 +934,7 @@ static void machine_initfn(Object *obj)
ms->smp.max_cpus = mc->default_cpus;
ms->smp.sockets = 1;
ms->smp.dies = 1;
ms->smp.clusters = 1;
ms->smp.cores = 1;
ms->smp.threads = 1;
}

View File

@ -34,7 +34,8 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine);
void machine_set_cpu_numa_node(MachineState *machine,
const CpuInstanceProperties *props,
Error **errp);
void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp);
void machine_parse_smp_config(MachineState *ms,
const SMPConfiguration *config, Error **errp);
/**
* machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices
@ -128,10 +129,12 @@ typedef struct {
* SMPCompatProps:
* @prefer_sockets - whether sockets are preferred over cores in smp parsing
* @dies_supported - whether dies are supported by the machine
* @clusters_supported - whether clusters are supported by the machine
*/
typedef struct {
bool prefer_sockets;
bool dies_supported;
bool clusters_supported;
} SMPCompatProps;
/**
@ -298,7 +301,8 @@ typedef struct DeviceMemoryState {
* @cpus: the number of present logical processors on the machine
* @sockets: the number of sockets on the machine
* @dies: the number of dies in one socket
* @cores: the number of cores in one die
* @clusters: the number of clusters in one die
* @cores: the number of cores in one cluster
* @threads: the number of threads in one core
* @max_cpus: the maximum number of logical processors on the machine
*/
@ -306,6 +310,7 @@ typedef struct CpuTopology {
unsigned int cpus;
unsigned int sockets;
unsigned int dies;
unsigned int clusters;
unsigned int cores;
unsigned int threads;
unsigned int max_cpus;

View File

@ -321,6 +321,7 @@ compat_props_add(GPtrArray *arr,
* The returned object has a reference count of 1.
*/
DeviceState *qdev_new(const char *name);
/**
* qdev_try_new: Try to create a device on the heap
* @name: device type to create
@ -329,6 +330,7 @@ DeviceState *qdev_new(const char *name);
* does not exist, rather than asserting.
*/
DeviceState *qdev_try_new(const char *name);
/**
* qdev_realize: Realize @dev.
* @dev: device to realize
@ -347,6 +349,7 @@ DeviceState *qdev_try_new(const char *name);
* qdev_realize_and_unref() instead.
*/
bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp);
/**
* qdev_realize_and_unref: Realize @dev and drop a reference
* @dev: device to realize
@ -372,6 +375,7 @@ bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp);
* would be incorrect. For that use case you want qdev_realize().
*/
bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp);
/**
* qdev_unrealize: Unrealize a device
* @dev: device to unrealize
@ -450,6 +454,7 @@ typedef enum {
* For named input GPIO lines, use qdev_get_gpio_in_named().
*/
qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
/**
* qdev_get_gpio_in_named: Get one of a device's named input GPIO lines
* @dev: Device whose GPIO we want
@ -471,7 +476,7 @@ qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n);
* qdev_connect_gpio_out: Connect one of a device's anonymous output GPIO lines
* @dev: Device whose GPIO to connect
* @n: Number of the anonymous output GPIO line (which must be in range)
* @pin: qemu_irq to connect the output line to
* @input_pin: qemu_irq to connect the output line to
*
* This function connects an anonymous output GPIO line on a device
* up to an arbitrary qemu_irq, so that when the device asserts that
@ -497,12 +502,14 @@ qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n);
* For named output GPIO lines, use qdev_connect_gpio_out_named().
*/
void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin);
/**
* qdev_connect_gpio_out: Connect one of a device's anonymous output GPIO lines
* qdev_connect_gpio_out_named: Connect one of a device's named output
* GPIO lines
* @dev: Device whose GPIO to connect
* @name: Name of the output GPIO array
* @n: Number of the anonymous output GPIO line (which must be in range)
* @pin: qemu_irq to connect the output line to
* @input_pin: qemu_irq to connect the output line to
*
* This function connects an anonymous output GPIO line on a device
* up to an arbitrary qemu_irq, so that when the device asserts that
@ -520,10 +527,11 @@ void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin);
* qemu_irqs at once, or to connect multiple outbound GPIOs to the
* same qemu_irq; see qdev_connect_gpio_out() for details.
*
* For named output GPIO lines, use qdev_connect_gpio_out_named().
* For anonymous output GPIO lines, use qdev_connect_gpio_out().
*/
void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
qemu_irq pin);
qemu_irq input_pin);
/**
* qdev_get_gpio_out_connector: Get the qemu_irq connected to an output GPIO
* @dev: Device whose output GPIO we are interested in
@ -541,6 +549,7 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
* by the platform-bus subsystem.
*/
qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n);
/**
* qdev_intercept_gpio_out: Intercept an existing GPIO connection
* @dev: Device to intercept the outbound GPIO line from
@ -582,6 +591,7 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name);
* hold of an input GPIO line to manipulate it.
*/
void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n);
/**
* qdev_init_gpio_out: create an array of anonymous output GPIO lines
* @dev: Device to create output GPIOs for
@ -610,8 +620,9 @@ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n);
* handler.
*/
void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n);
/**
* qdev_init_gpio_out: create an array of named output GPIO lines
* qdev_init_gpio_out_named: create an array of named output GPIO lines
* @dev: Device to create output GPIOs for
* @pins: Pointer to qemu_irq or qemu_irq array for the GPIO lines
* @name: Name to give this array of GPIO lines
@ -623,6 +634,7 @@ void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n);
*/
void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
const char *name, int n);
/**
* qdev_init_gpio_in_named_with_opaque: create an array of input GPIO lines
* for the specified device

View File

@ -1404,7 +1404,9 @@
#
# @dies: number of dies per socket in the CPU topology
#
# @cores: number of cores per die in the CPU topology
# @clusters: number of clusters per die in the CPU topology (since 7.0)
#
# @cores: number of cores per cluster in the CPU topology
#
# @threads: number of threads per core in the CPU topology
#
@ -1416,6 +1418,7 @@
'*cpus': 'int',
'*sockets': 'int',
'*dies': 'int',
'*clusters': 'int',
'*cores': 'int',
'*threads': 'int',
'*maxcpus': 'int' } }

View File

@ -206,17 +206,30 @@ SRST
ERST
DEF("smp", HAS_ARG, QEMU_OPTION_smp,
"-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]\n"
" set the number of CPUs to 'n' [default=1]\n"
"-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,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"
" sockets= number of discrete sockets in the system\n"
" dies= number of CPU dies on one socket (for PC only)\n"
" cores= number of CPU cores on one socket (for PC, it's on one die)\n"
" threads= number of threads on one CPU core\n",
QEMU_ARCH_ALL)
" sockets= number of sockets on the machine board\n"
" dies= number of dies in one socket\n"
" clusters= number of clusters in one die\n"
" cores= number of cores in one cluster\n"
" threads= number of threads in one core\n"
"Note: Different machines may have different subsets of the CPU topology\n"
" parameters supported, so the actual meaning of the supported parameters\n"
" will vary accordingly. For example, for a machine type that supports a\n"
" three-level CPU hierarchy of sockets/cores/threads, the parameters will\n"
" sequentially mean as below:\n"
" sockets means the number of sockets on the machine board\n"
" cores means the number of cores in one socket\n"
" threads means the number of threads in one core\n"
" 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",
QEMU_ARCH_ALL)
SRST
``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]``
``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,clusters=clusters][,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
@ -225,27 +238,57 @@ SRST
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. Both parameters are
subject to an upper limit that is determined by the specific machine
type chosen.
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.
To control reporting of CPU topology information, the number of sockets,
dies per socket, cores per die, and threads per core can be specified.
The sum `` sockets * cores * dies * threads `` must be equal to the
maximum CPU count. CPU targets may only support a subset of the topology
parameters. Where a CPU target does not support use of a particular
topology parameter, its value should be assumed to be 1 for the purpose
of computing the CPU maximum count.
To control reporting of CPU topology information, values of the topology
parameters can be specified. Machines may only support a subset of the
parameters and different machines may have different subsets supported
which vary depending on capacity of the corresponding CPU targets. So
for a particular machine type board, an expected topology hierarchy can
be defined through the supported sub-option. Unsupported parameters can
also be provided in addition to the sub-option, but their values must be
set as 1 in the purpose of correct parsing.
Either the initial CPU count, or at least one of the topology parameters
must be specified. The specified parameters must be greater than zero,
explicit configuration like "cpus=0" is not allowed. Values for any
omitted parameters will be computed from those which are given.
For example, the following sub-option defines a CPU topology hierarchy
(2 sockets totally on the machine, 2 cores per socket, 2 threads per
core) for a machine that only supports sockets/cores/threads.
Some members of the option can be omitted but their values will be
automatically computed:
::
-smp 8,sockets=2,cores=2,threads=2,maxcpus=8
The following sub-option defines a CPU topology hierarchy (2 sockets
totally on the machine, 2 dies per socket, 2 cores per die, 2 threads
per core) for PC machines which support sockets/dies/cores/threads.
Some members of the option can be omitted but their values will be
automatically computed:
::
-smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16
Historically preference was given to the coarsest topology parameters
when computing missing values (ie sockets preferred over cores, which
were preferred over threads), however, this behaviour is considered
liable to change. Prior to 6.2 the preference was sockets over cores
over threads. Since 6.2 the preference is cores over sockets over threads.
For example, the following option defines a machine board with 2 sockets
of 1 core before 6.2 and 1 socket of 2 cores after 6.2:
::
-smp 2
ERST
DEF("numa", HAS_ARG, QEMU_OPTION_numa,

View File

@ -726,6 +726,9 @@ static QemuOptsList qemu_smp_opts = {
}, {
.name = "dies",
.type = QEMU_OPT_NUMBER,
}, {
.name = "clusters",
.type = QEMU_OPT_NUMBER,
}, {
.name = "cores",
.type = QEMU_OPT_NUMBER,

View File

@ -61,6 +61,20 @@
.has_maxcpus = hf, .maxcpus = f, \
}
/*
* Currently a 4-level topology hierarchy is supported on ARM virt machines
* -sockets/clusters/cores/threads
*/
#define SMP_CONFIG_WITH_CLUSTERS(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \
{ \
.has_cpus = ha, .cpus = a, \
.has_sockets = hb, .sockets = b, \
.has_clusters = hc, .clusters = c, \
.has_cores = hd, .cores = d, \
.has_threads = he, .threads = e, \
.has_maxcpus = hf, .maxcpus = f, \
}
/**
* @config - the given SMP configuration
* @expect_prefer_sockets - the expected parsing result for the
@ -83,7 +97,7 @@ typedef struct SMPTestData {
* then test the automatic calculation algorithm of the missing
* values in the parser.
*/
static struct SMPTestData data_generic_valid[] = {
static const struct SMPTestData data_generic_valid[] = {
{
/* config: no configuration provided
* expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */
@ -285,11 +299,15 @@ static struct SMPTestData data_generic_valid[] = {
},
};
static struct SMPTestData data_generic_invalid[] = {
static const struct SMPTestData data_generic_invalid[] = {
{
/* config: -smp 2,dies=2 */
.config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
.expect_error = "dies not supported by this machine's CPU topology",
}, {
/* config: -smp 2,clusters=2 */
.config = SMP_CONFIG_WITH_CLUSTERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
.expect_error = "clusters not supported by this machine's CPU topology",
}, {
/* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */
.config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8),
@ -319,7 +337,7 @@ static struct SMPTestData data_generic_invalid[] = {
},
};
static struct SMPTestData data_with_dies_invalid[] = {
static const struct SMPTestData data_with_dies_invalid[] = {
{
/* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */
.config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
@ -337,42 +355,63 @@ static struct SMPTestData data_with_dies_invalid[] = {
},
};
static char *smp_config_to_string(SMPConfiguration *config)
static const struct SMPTestData data_with_clusters_invalid[] = {
{
/* config: -smp 16,sockets=2,clusters=2,cores=4,threads=2,maxcpus=16 */
.config = SMP_CONFIG_WITH_CLUSTERS(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
.expect_error = "Invalid CPU topology: "
"product of the hierarchy must match maxcpus: "
"sockets (2) * clusters (2) * cores (4) * threads (2) "
"!= maxcpus (16)",
}, {
/* config: -smp 34,sockets=2,clusters=2,cores=4,threads=2,maxcpus=32 */
.config = SMP_CONFIG_WITH_CLUSTERS(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
.expect_error = "Invalid CPU topology: "
"maxcpus must be equal to or greater than smp: "
"sockets (2) * clusters (2) * cores (4) * threads (2) "
"== maxcpus (32) < smp_cpus (34)",
},
};
static char *smp_config_to_string(const SMPConfiguration *config)
{
return g_strdup_printf(
"(SMPConfiguration) {\n"
" .has_cpus = %5s, cpus = %" PRId64 ",\n"
" .has_sockets = %5s, sockets = %" PRId64 ",\n"
" .has_dies = %5s, dies = %" PRId64 ",\n"
" .has_cores = %5s, cores = %" PRId64 ",\n"
" .has_threads = %5s, threads = %" PRId64 ",\n"
" .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n"
" .has_cpus = %5s, cpus = %" PRId64 ",\n"
" .has_sockets = %5s, sockets = %" PRId64 ",\n"
" .has_dies = %5s, dies = %" PRId64 ",\n"
" .has_clusters = %5s, clusters = %" PRId64 ",\n"
" .has_cores = %5s, cores = %" PRId64 ",\n"
" .has_threads = %5s, threads = %" PRId64 ",\n"
" .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n"
"}",
config->has_cpus ? "true" : "false", config->cpus,
config->has_sockets ? "true" : "false", config->sockets,
config->has_dies ? "true" : "false", config->dies,
config->has_clusters ? "true" : "false", config->clusters,
config->has_cores ? "true" : "false", config->cores,
config->has_threads ? "true" : "false", config->threads,
config->has_maxcpus ? "true" : "false", config->maxcpus);
}
static char *cpu_topology_to_string(CpuTopology *topo)
static char *cpu_topology_to_string(const CpuTopology *topo)
{
return g_strdup_printf(
"(CpuTopology) {\n"
" .cpus = %u,\n"
" .sockets = %u,\n"
" .dies = %u,\n"
" .clusters = %u,\n"
" .cores = %u,\n"
" .threads = %u,\n"
" .max_cpus = %u,\n"
"}",
topo->cpus, topo->sockets, topo->dies,
topo->cpus, topo->sockets, topo->dies, topo->clusters,
topo->cores, topo->threads, topo->max_cpus);
}
static void check_parse(MachineState *ms, SMPConfiguration *config,
CpuTopology *expect_topo, const char *expect_err,
static void check_parse(MachineState *ms, const SMPConfiguration *config,
const CpuTopology *expect_topo, const char *expect_err,
bool is_valid)
{
g_autofree char *config_str = smp_config_to_string(config);
@ -380,8 +419,8 @@ static void check_parse(MachineState *ms, SMPConfiguration *config,
g_autofree char *output_topo_str = NULL;
Error *err = NULL;
/* call the generic parser smp_parse() */
smp_parse(ms, config, &err);
/* call the generic parser */
machine_parse_smp_config(ms, config, &err);
output_topo_str = cpu_topology_to_string(&ms->smp);
@ -391,6 +430,7 @@ static void check_parse(MachineState *ms, SMPConfiguration *config,
(ms->smp.cpus == expect_topo->cpus) &&
(ms->smp.sockets == expect_topo->sockets) &&
(ms->smp.dies == expect_topo->dies) &&
(ms->smp.clusters == expect_topo->clusters) &&
(ms->smp.cores == expect_topo->cores) &&
(ms->smp.threads == expect_topo->threads) &&
(ms->smp.max_cpus == expect_topo->max_cpus)) {
@ -466,12 +506,17 @@ static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid)
}
/* The parsed results of the unsupported parameters should be 1 */
static void unsupported_params_init(MachineClass *mc, SMPTestData *data)
static void unsupported_params_init(const MachineClass *mc, SMPTestData *data)
{
if (!mc->smp_props.dies_supported) {
data->expect_prefer_sockets.dies = 1;
data->expect_prefer_cores.dies = 1;
}
if (!mc->smp_props.clusters_supported) {
data->expect_prefer_sockets.clusters = 1;
data->expect_prefer_cores.clusters = 1;
}
}
static void machine_base_class_init(ObjectClass *oc, void *data)
@ -481,101 +526,171 @@ static void machine_base_class_init(ObjectClass *oc, void *data)
mc->min_cpus = MIN_CPUS;
mc->max_cpus = MAX_CPUS;
mc->smp_props.prefer_sockets = true;
mc->smp_props.dies_supported = false;
mc->name = g_strdup(SMP_MACHINE_NAME);
}
static void test_generic(void)
static void machine_generic_invalid_class_init(ObjectClass *oc, void *data)
{
Object *obj = object_new(TYPE_MACHINE);
MachineState *ms = MACHINE(obj);
MachineClass *mc = MACHINE_GET_CLASS(obj);
SMPTestData *data = &(SMPTestData){{ }};
int i;
for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
*data = data_generic_valid[i];
unsupported_params_init(mc, data);
smp_parse_test(ms, data, true);
/* Unsupported parameters can be provided with their values as 1 */
data->config.has_dies = true;
data->config.dies = 1;
smp_parse_test(ms, data, true);
}
MachineClass *mc = MACHINE_CLASS(oc);
/* Force invalid min CPUs and max CPUs */
mc->min_cpus = 2;
mc->max_cpus = 511;
}
for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
*data = data_generic_invalid[i];
unsupported_params_init(mc, data);
static void machine_with_dies_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
smp_parse_test(ms, data, false);
mc->smp_props.dies_supported = true;
}
static void machine_with_clusters_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->smp_props.clusters_supported = true;
}
static void test_generic_valid(const void *opaque)
{
const char *machine_type = opaque;
Object *obj = object_new(machine_type);
MachineState *ms = MACHINE(obj);
MachineClass *mc = MACHINE_GET_CLASS(obj);
SMPTestData data = {};
int i;
for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
data = data_generic_valid[i];
unsupported_params_init(mc, &data);
smp_parse_test(ms, &data, true);
/* Unsupported parameters can be provided with their values as 1 */
data.config.has_dies = true;
data.config.dies = 1;
smp_parse_test(ms, &data, true);
}
/* Reset the supported min CPUs and max CPUs */
mc->min_cpus = MIN_CPUS;
mc->max_cpus = MAX_CPUS;
object_unref(obj);
}
static void test_with_dies(void)
static void test_generic_invalid(const void *opaque)
{
Object *obj = object_new(TYPE_MACHINE);
const char *machine_type = opaque;
Object *obj = object_new(machine_type);
MachineState *ms = MACHINE(obj);
MachineClass *mc = MACHINE_GET_CLASS(obj);
SMPTestData *data = &(SMPTestData){{ }};
SMPTestData data = {};
int i;
for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
data = data_generic_invalid[i];
unsupported_params_init(mc, &data);
smp_parse_test(ms, &data, false);
}
object_unref(obj);
}
static void test_with_dies(const void *opaque)
{
const char *machine_type = opaque;
Object *obj = object_new(machine_type);
MachineState *ms = MACHINE(obj);
MachineClass *mc = MACHINE_GET_CLASS(obj);
SMPTestData data = {};
unsigned int num_dies = 2;
int i;
/* Force the SMP compat properties */
mc->smp_props.dies_supported = true;
for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
*data = data_generic_valid[i];
unsupported_params_init(mc, data);
data = data_generic_valid[i];
unsupported_params_init(mc, &data);
/* when dies parameter is omitted, it will be set as 1 */
data->expect_prefer_sockets.dies = 1;
data->expect_prefer_cores.dies = 1;
data.expect_prefer_sockets.dies = 1;
data.expect_prefer_cores.dies = 1;
smp_parse_test(ms, data, true);
smp_parse_test(ms, &data, true);
/* when dies parameter is specified */
data->config.has_dies = true;
data->config.dies = num_dies;
if (data->config.has_cpus) {
data->config.cpus *= num_dies;
data.config.has_dies = true;
data.config.dies = num_dies;
if (data.config.has_cpus) {
data.config.cpus *= num_dies;
}
if (data->config.has_maxcpus) {
data->config.maxcpus *= num_dies;
if (data.config.has_maxcpus) {
data.config.maxcpus *= num_dies;
}
data->expect_prefer_sockets.dies = num_dies;
data->expect_prefer_sockets.cpus *= num_dies;
data->expect_prefer_sockets.max_cpus *= num_dies;
data->expect_prefer_cores.dies = num_dies;
data->expect_prefer_cores.cpus *= num_dies;
data->expect_prefer_cores.max_cpus *= num_dies;
data.expect_prefer_sockets.dies = num_dies;
data.expect_prefer_sockets.cpus *= num_dies;
data.expect_prefer_sockets.max_cpus *= num_dies;
data.expect_prefer_cores.dies = num_dies;
data.expect_prefer_cores.cpus *= num_dies;
data.expect_prefer_cores.max_cpus *= num_dies;
smp_parse_test(ms, data, true);
smp_parse_test(ms, &data, true);
}
for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) {
*data = data_with_dies_invalid[i];
unsupported_params_init(mc, data);
data = data_with_dies_invalid[i];
unsupported_params_init(mc, &data);
smp_parse_test(ms, data, false);
smp_parse_test(ms, &data, false);
}
/* Restore the SMP compat properties */
mc->smp_props.dies_supported = false;
object_unref(obj);
}
static void test_with_clusters(const void *opaque)
{
const char *machine_type = opaque;
Object *obj = object_new(machine_type);
MachineState *ms = MACHINE(obj);
MachineClass *mc = MACHINE_GET_CLASS(obj);
SMPTestData data = {};
unsigned int num_clusters = 2;
int i;
for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
data = data_generic_valid[i];
unsupported_params_init(mc, &data);
/* when clusters parameter is omitted, it will be set as 1 */
data.expect_prefer_sockets.clusters = 1;
data.expect_prefer_cores.clusters = 1;
smp_parse_test(ms, &data, true);
/* when clusters parameter is specified */
data.config.has_clusters = true;
data.config.clusters = num_clusters;
if (data.config.has_cpus) {
data.config.cpus *= num_clusters;
}
if (data.config.has_maxcpus) {
data.config.maxcpus *= num_clusters;
}
data.expect_prefer_sockets.clusters = num_clusters;
data.expect_prefer_sockets.cpus *= num_clusters;
data.expect_prefer_sockets.max_cpus *= num_clusters;
data.expect_prefer_cores.clusters = num_clusters;
data.expect_prefer_cores.cpus *= num_clusters;
data.expect_prefer_cores.max_cpus *= num_clusters;
smp_parse_test(ms, &data, true);
}
for (i = 0; i < ARRAY_SIZE(data_with_clusters_invalid); i++) {
data = data_with_clusters_invalid[i];
unsupported_params_init(mc, &data);
smp_parse_test(ms, &data, false);
}
object_unref(obj);
}
@ -585,9 +700,25 @@ static const TypeInfo smp_machine_types[] = {
{
.name = TYPE_MACHINE,
.parent = TYPE_OBJECT,
.abstract = true,
.class_init = machine_base_class_init,
.class_size = sizeof(MachineClass),
.instance_size = sizeof(MachineState),
}, {
.name = MACHINE_TYPE_NAME("smp-generic-valid"),
.parent = TYPE_MACHINE,
}, {
.name = MACHINE_TYPE_NAME("smp-generic-invalid"),
.parent = TYPE_MACHINE,
.class_init = machine_generic_invalid_class_init,
}, {
.name = MACHINE_TYPE_NAME("smp-with-dies"),
.parent = TYPE_MACHINE,
.class_init = machine_with_dies_class_init,
}, {
.name = MACHINE_TYPE_NAME("smp-with-clusters"),
.parent = TYPE_MACHINE,
.class_init = machine_with_clusters_class_init,
}
};
@ -599,8 +730,18 @@ int main(int argc, char *argv[])
g_test_init(&argc, &argv, NULL);
g_test_add_func("/test-smp-parse/generic", test_generic);
g_test_add_func("/test-smp-parse/with_dies", test_with_dies);
g_test_add_data_func("/test-smp-parse/generic/valid",
MACHINE_TYPE_NAME("smp-generic-valid"),
test_generic_valid);
g_test_add_data_func("/test-smp-parse/generic/invalid",
MACHINE_TYPE_NAME("smp-generic-invalid"),
test_generic_invalid);
g_test_add_data_func("/test-smp-parse/with_dies",
MACHINE_TYPE_NAME("smp-with-dies"),
test_with_dies);
g_test_add_data_func("/test-smp-parse/with_clusters",
MACHINE_TYPE_NAME("smp-with-clusters"),
test_with_clusters);
g_test_run();