x86 and machine queue, 2017-05-11
Highlights: * New "-numa cpu" option * NUMA distance configuration * migration/i386 vmstatification -----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJZFLh3AAoJECgHk2+YTcWmppQQAJe9Y5a3VwHXqvHbwBHX2ysn RZDAUPd9DWpbM+UUydyKOVIZ7u5RXbbVq4E0NeCD8VYYd+grZB5Wo1cAzy3b4U2j 2s+MDqaPMtZtGoqxTsyQOVoVxazT5Kf1zglK+iUEzik44J7LGdro+ty2Z7Ut2c11 q9rE/GNS78czBm7c4lxgkxXW4N95K/tEGlLtDQ7uct//3U/ZimF+mO6GcbVFlOWT 4iEbOz2sqvBVv22nLJRufiPgFNIW4hizAz5KBWxwGFCCKvT3N6yYNKKjzEpCw+jE lpjIRODU02yIZZZY841fLRtyrk7p4zORS8jRaHTdEJgb5bGc/YazxxVL8nzRQT1W VxFwAMd+UNrDkV24hpN++Ln2O+b3kwcGZ7uA/qu9d5WvSYUKXlHqcMJ35q6zuhAI /ecfYO7EZfVP86VjIt5IH04iV8RChA9Q6de+kQEFa6wHUxufeCOwCFqukGo8zj07 plX8NcjnzYmSXKnYjHOHao4rKT+DiJhRB60rFiMeKP/qvKbZPjtgsIeonhHm53qZ /QwkhowahHKkpAnetIl0QHm8KS4YudAofMi/Fl+he4gRkEbSQVAo6iQb2L4cjcLC LNSDDsIVWGem4gCR+vcsFqB3lggRDfltHXm15JKh92UMpOr6RI6s8pD55T7EdnPC CfdxWB5kYM6/lLbOHj94 =48wH -----END PGP SIGNATURE----- Merge remote-tracking branch 'ehabkost/tags/x86-and-machine-pull-request' into staging x86 and machine queue, 2017-05-11 Highlights: * New "-numa cpu" option * NUMA distance configuration * migration/i386 vmstatification # gpg: Signature made Thu 11 May 2017 08:16:07 PM BST # gpg: using RSA key 0x2807936F984DC5A6 # gpg: Good signature from "Eduardo Habkost <ehabkost@redhat.com>" # gpg: Note: This key has expired! # Primary key fingerprint: 5A32 2FD5 ABC4 D3DB ACCF D1AA 2807 936F 984D C5A6 * ehabkost/tags/x86-and-machine-pull-request: (29 commits) migration/i386: Remove support for pre-0.12 formats vmstatification: i386 FPReg migration/i386: Remove old non-softfloat 64bit FP support tests: check -numa node,cpu=props_list usecase numa: add '-numa cpu,...' option for property based node mapping numa: remove node_cpu bitmaps as they are no longer used numa: use possible_cpus for not mapped CPUs check machine: call machine init from wrapper numa: remove no longer need numa_post_machine_init() tests: numa: add case for QMP command query-cpus QMP: include CpuInstanceProperties into query_cpus output output virt-arm: get numa node mapping from possible_cpus instead of numa_get_node_for_cpu() spapr: get numa node mapping from possible_cpus instead of numa_get_node_for_cpu() pc: get numa node mapping from possible_cpus instead of numa_get_node_for_cpu() numa: do default mapping based on possible_cpus instead of node_cpu bitmaps numa: mirror cpu to node mapping in MachineState::possible_cpus numa: add check that board supports cpu_index to node mapping virt-arm: add node-id property to CPU pc: add node-id property to CPU spapr: add node-id property to sPAPR core ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
ba9915e1f8
10
cpus.c
10
cpus.c
@ -50,6 +50,7 @@
|
||||
#include "qapi-event.h"
|
||||
#include "hw/nmi.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
|
||||
@ -1865,6 +1866,8 @@ void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
|
||||
|
||||
CpuInfoList *qmp_query_cpus(Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
CpuInfoList *head = NULL, *cur_item = NULL;
|
||||
CPUState *cpu;
|
||||
|
||||
@ -1915,6 +1918,13 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
||||
#else
|
||||
info->value->arch = CPU_INFO_ARCH_OTHER;
|
||||
#endif
|
||||
info->value->has_props = !!mc->cpu_index_to_instance_props;
|
||||
if (info->value->has_props) {
|
||||
CpuInstanceProperties *props;
|
||||
props = g_malloc0(sizeof(*props));
|
||||
*props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index);
|
||||
info->value->props = props;
|
||||
}
|
||||
|
||||
/* XXX: waiting for the qapi to support GSList */
|
||||
if (!cur_item) {
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "sysemu/numa.h"
|
||||
|
||||
static GArray *build_alloc_array(void)
|
||||
{
|
||||
@ -1609,3 +1610,28 @@ void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
|
||||
numamem->base_addr = cpu_to_le64(base);
|
||||
numamem->range_length = cpu_to_le64(len);
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI spec 5.2.17 System Locality Distance Information Table
|
||||
* (Revision 2.0 or later)
|
||||
*/
|
||||
void build_slit(GArray *table_data, BIOSLinker *linker)
|
||||
{
|
||||
int slit_start, i, j;
|
||||
slit_start = table_data->len;
|
||||
|
||||
acpi_data_push(table_data, sizeof(AcpiTableHeader));
|
||||
|
||||
build_append_int_noprefix(table_data, nb_numa_nodes, 8);
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
for (j = 0; j < nb_numa_nodes; j++) {
|
||||
assert(numa_info[i].distance[j]);
|
||||
build_append_int_noprefix(table_data, numa_info[i].distance[j], 1);
|
||||
}
|
||||
}
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + slit_start),
|
||||
"SLIT",
|
||||
table_data->len - slit_start, 1, NULL, NULL);
|
||||
}
|
||||
|
@ -503,7 +503,6 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
||||
|
||||
/* build Processor object for each processor */
|
||||
for (i = 0; i < arch_ids->len; i++) {
|
||||
int j;
|
||||
Aml *dev;
|
||||
Aml *uid = aml_int(i);
|
||||
GArray *madt_buf = g_array_new(0, 1, 1);
|
||||
@ -557,9 +556,9 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
||||
* as a result _PXM is required for all CPUs which might
|
||||
* be hot-plugged. For simplicity, add it for all CPUs.
|
||||
*/
|
||||
j = numa_get_node_for_cpu(i);
|
||||
if (j < nb_numa_nodes) {
|
||||
aml_append(dev, aml_name_decl("_PXM", aml_int(j)));
|
||||
if (arch_ids->cpus[i].props.has_node_id) {
|
||||
aml_append(dev, aml_name_decl("_PXM",
|
||||
aml_int(arch_ids->cpus[i].props.node_id)));
|
||||
}
|
||||
|
||||
aml_append(cpus_dev, dev);
|
||||
|
@ -486,30 +486,25 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
AcpiSystemResourceAffinityTable *srat;
|
||||
AcpiSratProcessorGiccAffinity *core;
|
||||
AcpiSratMemoryAffinity *numamem;
|
||||
int i, j, srat_start;
|
||||
int i, srat_start;
|
||||
uint64_t mem_base;
|
||||
uint32_t *cpu_node = g_malloc0(vms->smp_cpus * sizeof(uint32_t));
|
||||
|
||||
for (i = 0; i < vms->smp_cpus; i++) {
|
||||
j = numa_get_node_for_cpu(i);
|
||||
if (j < nb_numa_nodes) {
|
||||
cpu_node[i] = j;
|
||||
}
|
||||
}
|
||||
MachineClass *mc = MACHINE_GET_CLASS(vms);
|
||||
const CPUArchIdList *cpu_list = mc->possible_cpu_arch_ids(MACHINE(vms));
|
||||
|
||||
srat_start = table_data->len;
|
||||
srat = acpi_data_push(table_data, sizeof(*srat));
|
||||
srat->reserved1 = cpu_to_le32(1);
|
||||
|
||||
for (i = 0; i < vms->smp_cpus; ++i) {
|
||||
for (i = 0; i < cpu_list->len; ++i) {
|
||||
int node_id = cpu_list->cpus[i].props.has_node_id ?
|
||||
cpu_list->cpus[i].props.node_id : 0;
|
||||
core = acpi_data_push(table_data, sizeof(*core));
|
||||
core->type = ACPI_SRAT_PROCESSOR_GICC;
|
||||
core->length = sizeof(*core);
|
||||
core->proximity = cpu_to_le32(cpu_node[i]);
|
||||
core->proximity = cpu_to_le32(node_id);
|
||||
core->acpi_processor_uid = cpu_to_le32(i);
|
||||
core->flags = cpu_to_le32(1);
|
||||
}
|
||||
g_free(cpu_node);
|
||||
|
||||
mem_base = vms->memmap[VIRT_MEM].base;
|
||||
for (i = 0; i < nb_numa_nodes; ++i) {
|
||||
|
123
hw/arm/virt.c
123
hw/arm/virt.c
@ -338,7 +338,7 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms)
|
||||
{
|
||||
int cpu;
|
||||
int addr_cells = 1;
|
||||
unsigned int i;
|
||||
const MachineState *ms = MACHINE(vms);
|
||||
|
||||
/*
|
||||
* From Documentation/devicetree/bindings/arm/cpus.txt
|
||||
@ -369,6 +369,7 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms)
|
||||
for (cpu = vms->smp_cpus - 1; cpu >= 0; cpu--) {
|
||||
char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
|
||||
CPUState *cs = CPU(armcpu);
|
||||
|
||||
qemu_fdt_add_subnode(vms->fdt, nodename);
|
||||
qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "cpu");
|
||||
@ -389,9 +390,9 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms)
|
||||
armcpu->mp_affinity);
|
||||
}
|
||||
|
||||
i = numa_get_node_for_cpu(cpu);
|
||||
if (i < nb_numa_nodes) {
|
||||
qemu_fdt_setprop_cell(vms->fdt, nodename, "numa-node-id", i);
|
||||
if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) {
|
||||
qemu_fdt_setprop_cell(vms->fdt, nodename, "numa-node-id",
|
||||
ms->possible_cpus->cpus[cs->cpu_index].props.node_id);
|
||||
}
|
||||
|
||||
g_free(nodename);
|
||||
@ -1194,10 +1195,35 @@ void virt_machine_done(Notifier *notifier, void *data)
|
||||
virt_build_smbios(vms);
|
||||
}
|
||||
|
||||
static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
|
||||
{
|
||||
uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER;
|
||||
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
||||
|
||||
if (!vmc->disallow_affinity_adjustment) {
|
||||
/* Adjust MPIDR like 64-bit KVM hosts, which incorporate the
|
||||
* GIC's target-list limitations. 32-bit KVM hosts currently
|
||||
* always create clusters of 4 CPUs, but that is expected to
|
||||
* change when they gain support for gicv3. When KVM is enabled
|
||||
* it will override the changes we make here, therefore our
|
||||
* purposes are to make TCG consistent (with 64-bit KVM hosts)
|
||||
* and to improve SGI efficiency.
|
||||
*/
|
||||
if (vms->gic_version == 3) {
|
||||
clustersz = GICV3_TARGETLIST_BITS;
|
||||
} else {
|
||||
clustersz = GIC_TARGETLIST_BITS;
|
||||
}
|
||||
}
|
||||
return arm_cpu_mp_affinity(idx, clustersz);
|
||||
}
|
||||
|
||||
static void machvirt_init(MachineState *machine)
|
||||
{
|
||||
VirtMachineState *vms = VIRT_MACHINE(machine);
|
||||
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
const CPUArchIdList *possible_cpus;
|
||||
qemu_irq pic[NUM_IRQS];
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *secure_sysmem = NULL;
|
||||
@ -1210,7 +1236,6 @@ static void machvirt_init(MachineState *machine)
|
||||
CPUClass *cc;
|
||||
Error *err = NULL;
|
||||
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
||||
uint8_t clustersz;
|
||||
|
||||
if (!cpu_model) {
|
||||
cpu_model = "cortex-a15";
|
||||
@ -1263,10 +1288,8 @@ static void machvirt_init(MachineState *machine)
|
||||
*/
|
||||
if (vms->gic_version == 3) {
|
||||
virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000;
|
||||
clustersz = GICV3_TARGETLIST_BITS;
|
||||
} else {
|
||||
virt_max_cpus = GIC_NCPU;
|
||||
clustersz = GIC_TARGETLIST_BITS;
|
||||
}
|
||||
|
||||
if (max_cpus > virt_max_cpus) {
|
||||
@ -1324,21 +1347,35 @@ static void machvirt_init(MachineState *machine)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
Object *cpuobj = object_new(typename);
|
||||
if (!vmc->disallow_affinity_adjustment) {
|
||||
/* Adjust MPIDR like 64-bit KVM hosts, which incorporate the
|
||||
* GIC's target-list limitations. 32-bit KVM hosts currently
|
||||
* always create clusters of 4 CPUs, but that is expected to
|
||||
* change when they gain support for gicv3. When KVM is enabled
|
||||
* it will override the changes we make here, therefore our
|
||||
* purposes are to make TCG consistent (with 64-bit KVM hosts)
|
||||
* and to improve SGI efficiency.
|
||||
*/
|
||||
uint8_t aff1 = n / clustersz;
|
||||
uint8_t aff0 = n % clustersz;
|
||||
object_property_set_int(cpuobj, (aff1 << ARM_AFF1_SHIFT) | aff0,
|
||||
"mp-affinity", NULL);
|
||||
possible_cpus = mc->possible_cpu_arch_ids(machine);
|
||||
for (n = 0; n < possible_cpus->len; n++) {
|
||||
Object *cpuobj;
|
||||
CPUState *cs;
|
||||
int node_id;
|
||||
|
||||
if (n >= smp_cpus) {
|
||||
break;
|
||||
}
|
||||
|
||||
cpuobj = object_new(typename);
|
||||
object_property_set_int(cpuobj, possible_cpus->cpus[n].arch_id,
|
||||
"mp-affinity", NULL);
|
||||
|
||||
cs = CPU(cpuobj);
|
||||
cs->cpu_index = n;
|
||||
|
||||
node_id = possible_cpus->cpus[cs->cpu_index].props.node_id;
|
||||
if (!possible_cpus->cpus[cs->cpu_index].props.has_node_id) {
|
||||
/* by default CPUState::numa_node was 0 if it's not set via CLI
|
||||
* keep it this way for now but in future we probably should
|
||||
* refuse to start up with incomplete numa mapping */
|
||||
node_id = 0;
|
||||
}
|
||||
if (cs->numa_node == CPU_UNSET_NUMA_NODE_ID) {
|
||||
cs->numa_node = node_id;
|
||||
} else {
|
||||
/* CPU isn't device_add compatible yet, this shouldn't happen */
|
||||
error_setg(&error_abort, "user set node-id not implemented");
|
||||
}
|
||||
|
||||
if (!vms->secure) {
|
||||
@ -1518,6 +1555,46 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static CpuInstanceProperties
|
||||
virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
|
||||
|
||||
assert(cpu_index < possible_cpus->len);
|
||||
return possible_cpus->cpus[cpu_index].props;
|
||||
}
|
||||
|
||||
static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
|
||||
{
|
||||
int n;
|
||||
VirtMachineState *vms = VIRT_MACHINE(ms);
|
||||
|
||||
if (ms->possible_cpus) {
|
||||
assert(ms->possible_cpus->len == max_cpus);
|
||||
return ms->possible_cpus;
|
||||
}
|
||||
|
||||
ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
|
||||
sizeof(CPUArchId) * max_cpus);
|
||||
ms->possible_cpus->len = max_cpus;
|
||||
for (n = 0; n < ms->possible_cpus->len; n++) {
|
||||
ms->possible_cpus->cpus[n].arch_id =
|
||||
virt_cpu_mp_affinity(vms, n);
|
||||
ms->possible_cpus->cpus[n].props.has_thread_id = true;
|
||||
ms->possible_cpus->cpus[n].props.thread_id = n;
|
||||
|
||||
/* default distribution of CPUs over NUMA nodes */
|
||||
if (nb_numa_nodes) {
|
||||
/* preset values but do not enable them i.e. 'has_node_id = false',
|
||||
* numa init code will enable them later if manual mapping wasn't
|
||||
* present on CLI */
|
||||
ms->possible_cpus->cpus[n].props.node_id = n % nb_numa_nodes;
|
||||
}
|
||||
}
|
||||
return ms->possible_cpus;
|
||||
}
|
||||
|
||||
static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
@ -1534,6 +1611,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||
mc->pci_allow_0_address = true;
|
||||
/* We know we will never create a pre-ARMv7 CPU which needs 1K pages */
|
||||
mc->minimum_page_bits = 12;
|
||||
mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids;
|
||||
mc->cpu_index_to_instance_props = virt_cpu_index_to_props;
|
||||
}
|
||||
|
||||
static const TypeInfo virt_machine_info = {
|
||||
|
@ -17,8 +17,10 @@
|
||||
#include "qapi/visitor.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/numa.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/numa.h"
|
||||
|
||||
static char *machine_get_accel(Object *obj, Error **errp)
|
||||
{
|
||||
@ -388,6 +390,102 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine)
|
||||
return head;
|
||||
}
|
||||
|
||||
/**
|
||||
* machine_set_cpu_numa_node:
|
||||
* @machine: machine object to modify
|
||||
* @props: specifies which cpu objects to assign to
|
||||
* numa node specified by @props.node_id
|
||||
* @errp: if an error occurs, a pointer to an area to store the error
|
||||
*
|
||||
* Associate NUMA node specified by @props.node_id with cpu slots that
|
||||
* match socket/core/thread-ids specified by @props. It's recommended to use
|
||||
* query-hotpluggable-cpus.props values to specify affected cpu slots,
|
||||
* which would lead to exact 1:1 mapping of cpu slots to NUMA node.
|
||||
*
|
||||
* However for CLI convenience it's possible to pass in subset of properties,
|
||||
* which would affect all cpu slots that match it.
|
||||
* Ex for pc machine:
|
||||
* -smp 4,cores=2,sockets=2 -numa node,nodeid=0 -numa node,nodeid=1 \
|
||||
* -numa cpu,node-id=0,socket_id=0 \
|
||||
* -numa cpu,node-id=1,socket_id=1
|
||||
* will assign all child cores of socket 0 to node 0 and
|
||||
* of socket 1 to node 1.
|
||||
*
|
||||
* On attempt of reassigning (already assigned) cpu slot to another NUMA node,
|
||||
* return error.
|
||||
* Empty subset is disallowed and function will return with error in this case.
|
||||
*/
|
||||
void machine_set_cpu_numa_node(MachineState *machine,
|
||||
const CpuInstanceProperties *props, Error **errp)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
bool match = false;
|
||||
int i;
|
||||
|
||||
if (!mc->possible_cpu_arch_ids) {
|
||||
error_setg(errp, "mapping of CPUs to NUMA node is not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
/* disabling node mapping is not supported, forbid it */
|
||||
assert(props->has_node_id);
|
||||
|
||||
/* force board to initialize possible_cpus if it hasn't been done yet */
|
||||
mc->possible_cpu_arch_ids(machine);
|
||||
|
||||
for (i = 0; i < machine->possible_cpus->len; i++) {
|
||||
CPUArchId *slot = &machine->possible_cpus->cpus[i];
|
||||
|
||||
/* reject unsupported by board properties */
|
||||
if (props->has_thread_id && !slot->props.has_thread_id) {
|
||||
error_setg(errp, "thread-id is not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
if (props->has_core_id && !slot->props.has_core_id) {
|
||||
error_setg(errp, "core-id is not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
if (props->has_socket_id && !slot->props.has_socket_id) {
|
||||
error_setg(errp, "socket-id is not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
/* skip slots with explicit mismatch */
|
||||
if (props->has_thread_id && props->thread_id != slot->props.thread_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (props->has_core_id && props->core_id != slot->props.core_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (props->has_socket_id && props->socket_id != slot->props.socket_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reject assignment if slot is already assigned, for compatibility
|
||||
* of legacy cpu_index mapping with SPAPR core based mapping do not
|
||||
* error out if cpu thread and matched core have the same node-id */
|
||||
if (slot->props.has_node_id &&
|
||||
slot->props.node_id != props->node_id) {
|
||||
error_setg(errp, "CPU is already assigned to node-id: %" PRId64,
|
||||
slot->props.node_id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* assign slot to node as it's matched '-numa cpu' key */
|
||||
match = true;
|
||||
slot->props.node_id = props->node_id;
|
||||
slot->props.has_node_id = props->has_node_id;
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
error_setg(errp, "no match found");
|
||||
}
|
||||
}
|
||||
|
||||
static void machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
@ -400,6 +498,7 @@ static void machine_class_init(ObjectClass *oc, void *data)
|
||||
* On Linux, each node's border has to be 8MB aligned
|
||||
*/
|
||||
mc->numa_mem_align_shift = 23;
|
||||
mc->numa_auto_assign_ram = numa_default_auto_assign_ram;
|
||||
|
||||
object_class_property_add_str(oc, "accel",
|
||||
machine_get_accel, machine_set_accel, &error_abort);
|
||||
@ -580,6 +679,69 @@ bool machine_mem_merge(MachineState *machine)
|
||||
return machine->mem_merge;
|
||||
}
|
||||
|
||||
static char *cpu_slot_to_string(const CPUArchId *cpu)
|
||||
{
|
||||
GString *s = g_string_new(NULL);
|
||||
if (cpu->props.has_socket_id) {
|
||||
g_string_append_printf(s, "socket-id: %"PRId64, cpu->props.socket_id);
|
||||
}
|
||||
if (cpu->props.has_core_id) {
|
||||
if (s->len) {
|
||||
g_string_append_printf(s, ", ");
|
||||
}
|
||||
g_string_append_printf(s, "core-id: %"PRId64, cpu->props.core_id);
|
||||
}
|
||||
if (cpu->props.has_thread_id) {
|
||||
if (s->len) {
|
||||
g_string_append_printf(s, ", ");
|
||||
}
|
||||
g_string_append_printf(s, "thread-id: %"PRId64, cpu->props.thread_id);
|
||||
}
|
||||
return g_string_free(s, false);
|
||||
}
|
||||
|
||||
static void machine_numa_validate(MachineState *machine)
|
||||
{
|
||||
int i;
|
||||
GString *s = g_string_new(NULL);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(machine);
|
||||
|
||||
assert(nb_numa_nodes);
|
||||
for (i = 0; i < possible_cpus->len; i++) {
|
||||
const CPUArchId *cpu_slot = &possible_cpus->cpus[i];
|
||||
|
||||
/* at this point numa mappings are initilized by CLI options
|
||||
* or with default mappings so it's sufficient to list
|
||||
* all not yet mapped CPUs here */
|
||||
/* TODO: make it hard error in future */
|
||||
if (!cpu_slot->props.has_node_id) {
|
||||
char *cpu_str = cpu_slot_to_string(cpu_slot);
|
||||
g_string_append_printf(s, "%sCPU %d [%s]", s->len ? ", " : "", i,
|
||||
cpu_str);
|
||||
g_free(cpu_str);
|
||||
}
|
||||
}
|
||||
if (s->len) {
|
||||
error_report("warning: CPU(s) not present in any NUMA nodes: %s",
|
||||
s->str);
|
||||
error_report("warning: All CPU(s) up to maxcpus should be described "
|
||||
"in NUMA config, ability to start up with partial NUMA "
|
||||
"mappings is obsoleted and will be removed in future");
|
||||
}
|
||||
g_string_free(s, true);
|
||||
}
|
||||
|
||||
void machine_run_board_init(MachineState *machine)
|
||||
{
|
||||
MachineClass *machine_class = MACHINE_GET_CLASS(machine);
|
||||
|
||||
if (nb_numa_nodes) {
|
||||
machine_numa_validate(machine);
|
||||
}
|
||||
machine_class->init(machine);
|
||||
}
|
||||
|
||||
static void machine_class_finalize(ObjectClass *klass, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(klass);
|
||||
|
@ -2335,7 +2335,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
|
||||
srat->reserved1 = cpu_to_le32(1);
|
||||
|
||||
for (i = 0; i < apic_ids->len; i++) {
|
||||
int j = numa_get_node_for_cpu(i);
|
||||
int node_id = apic_ids->cpus[i].props.has_node_id ?
|
||||
apic_ids->cpus[i].props.node_id : 0;
|
||||
uint32_t apic_id = apic_ids->cpus[i].arch_id;
|
||||
|
||||
if (apic_id < 255) {
|
||||
@ -2345,9 +2346,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
|
||||
core->type = ACPI_SRAT_PROCESSOR_APIC;
|
||||
core->length = sizeof(*core);
|
||||
core->local_apic_id = apic_id;
|
||||
if (j < nb_numa_nodes) {
|
||||
core->proximity_lo = j;
|
||||
}
|
||||
core->proximity_lo = node_id;
|
||||
memset(core->proximity_hi, 0, 3);
|
||||
core->local_sapic_eid = 0;
|
||||
core->flags = cpu_to_le32(1);
|
||||
@ -2358,9 +2357,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
|
||||
core->type = ACPI_SRAT_PROCESSOR_x2APIC;
|
||||
core->length = sizeof(*core);
|
||||
core->x2apic_id = cpu_to_le32(apic_id);
|
||||
if (j < nb_numa_nodes) {
|
||||
core->proximity_domain = cpu_to_le32(j);
|
||||
}
|
||||
core->proximity_domain = cpu_to_le32(node_id);
|
||||
core->flags = cpu_to_le32(1);
|
||||
}
|
||||
}
|
||||
@ -2707,6 +2704,10 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||
if (pcms->numa_nodes) {
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_srat(tables_blob, tables->linker, machine);
|
||||
if (have_numa_distance) {
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_slit(tables_blob, tables->linker);
|
||||
}
|
||||
}
|
||||
if (acpi_get_mcfg(&mcfg)) {
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
|
54
hw/i386/pc.c
54
hw/i386/pc.c
@ -747,7 +747,9 @@ static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms)
|
||||
{
|
||||
FWCfgState *fw_cfg;
|
||||
uint64_t *numa_fw_cfg;
|
||||
int i, j;
|
||||
int i;
|
||||
const CPUArchIdList *cpus;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(pcms);
|
||||
|
||||
fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4, as);
|
||||
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus);
|
||||
@ -782,12 +784,12 @@ static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms)
|
||||
*/
|
||||
numa_fw_cfg = g_new0(uint64_t, 1 + pcms->apic_id_limit + nb_numa_nodes);
|
||||
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
|
||||
for (i = 0; i < max_cpus; i++) {
|
||||
unsigned int apic_id = x86_cpu_apic_id_from_index(i);
|
||||
cpus = mc->possible_cpu_arch_ids(MACHINE(pcms));
|
||||
for (i = 0; i < cpus->len; i++) {
|
||||
unsigned int apic_id = cpus->cpus[i].arch_id;
|
||||
assert(apic_id < pcms->apic_id_limit);
|
||||
j = numa_get_node_for_cpu(i);
|
||||
if (j < nb_numa_nodes) {
|
||||
numa_fw_cfg[apic_id + 1] = cpu_to_le64(j);
|
||||
if (cpus->cpus[i].props.has_node_id) {
|
||||
numa_fw_cfg[apic_id + 1] = cpu_to_le64(cpus->cpus[i].props.node_id);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
@ -1893,6 +1895,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
int idx;
|
||||
int node_id;
|
||||
CPUState *cs;
|
||||
CPUArchId *cpu_slot;
|
||||
X86CPUTopoInfo topo;
|
||||
@ -1982,6 +1985,22 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
||||
|
||||
cs = CPU(cpu);
|
||||
cs->cpu_index = idx;
|
||||
|
||||
node_id = cpu_slot->props.node_id;
|
||||
if (!cpu_slot->props.has_node_id) {
|
||||
/* by default CPUState::numa_node was 0 if it's not set via CLI
|
||||
* keep it this way for now but in future we probably should
|
||||
* refuse to start up with incomplete numa mapping */
|
||||
node_id = 0;
|
||||
}
|
||||
if (cs->numa_node == CPU_UNSET_NUMA_NODE_ID) {
|
||||
cs->numa_node = node_id;
|
||||
} else if (cs->numa_node != node_id) {
|
||||
error_setg(errp, "node-id %d must match numa node specified"
|
||||
"with -numa option for cpu-index %d",
|
||||
cs->numa_node, cs->cpu_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
|
||||
@ -2243,12 +2262,14 @@ static void pc_machine_reset(void)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index)
|
||||
static CpuInstanceProperties
|
||||
pc_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
|
||||
{
|
||||
X86CPUTopoInfo topo;
|
||||
x86_topo_ids_from_idx(smp_cores, smp_threads, cpu_index,
|
||||
&topo);
|
||||
return topo.pkg_id;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
|
||||
|
||||
assert(cpu_index < possible_cpus->len);
|
||||
return possible_cpus->cpus[cpu_index].props;
|
||||
}
|
||||
|
||||
static const CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *ms)
|
||||
@ -2280,6 +2301,15 @@ static const CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *ms)
|
||||
ms->possible_cpus->cpus[i].props.core_id = topo.core_id;
|
||||
ms->possible_cpus->cpus[i].props.has_thread_id = true;
|
||||
ms->possible_cpus->cpus[i].props.thread_id = topo.smt_id;
|
||||
|
||||
/* default distribution of CPUs over NUMA nodes */
|
||||
if (nb_numa_nodes) {
|
||||
/* preset values but do not enable them i.e. 'has_node_id = false',
|
||||
* numa init code will enable them later if manual mapping wasn't
|
||||
* present on CLI */
|
||||
ms->possible_cpus->cpus[i].props.node_id =
|
||||
topo.pkg_id % nb_numa_nodes;
|
||||
}
|
||||
}
|
||||
return ms->possible_cpus;
|
||||
}
|
||||
@ -2322,7 +2352,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
|
||||
pcmc->acpi_data_size = 0x20000 + 0x8000;
|
||||
pcmc->save_tsc_khz = true;
|
||||
mc->get_hotplug_handler = pc_get_hotpug_handler;
|
||||
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
|
||||
mc->cpu_index_to_instance_props = pc_cpu_index_to_props;
|
||||
mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids;
|
||||
mc->has_hotpluggable_cpus = true;
|
||||
mc->default_boot_order = "cad";
|
||||
|
@ -54,6 +54,7 @@
|
||||
#endif
|
||||
#include "migration/migration.h"
|
||||
#include "kvm_i386.h"
|
||||
#include "sysemu/numa.h"
|
||||
|
||||
#define MAX_IDE_BUS 2
|
||||
|
||||
@ -442,6 +443,7 @@ static void pc_i440fx_2_9_machine_options(MachineClass *m)
|
||||
pc_i440fx_machine_options(m);
|
||||
m->alias = "pc";
|
||||
m->is_default = 1;
|
||||
m->numa_auto_assign_ram = numa_legacy_auto_assign_ram;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_9, "pc-i440fx-2.9", NULL,
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "hw/usb.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "migration/migration.h"
|
||||
#include "sysemu/numa.h"
|
||||
|
||||
/* ICH9 AHCI has 6 ports */
|
||||
#define MAX_SATA_PORTS 6
|
||||
@ -305,6 +306,7 @@ static void pc_q35_2_9_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_q35_machine_options(m);
|
||||
m->alias = "q35";
|
||||
m->numa_auto_assign_ram = numa_legacy_auto_assign_ram;
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v2_9, "pc-q35-2.9", NULL,
|
||||
|
@ -2831,9 +2831,11 @@ static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev);
|
||||
Error *local_err = NULL;
|
||||
CPUCore *cc = CPU_CORE(dev);
|
||||
sPAPRCPUCore *sc = SPAPR_CPU_CORE(dev);
|
||||
char *base_core_type = spapr_get_cpu_core_type(machine->cpu_model);
|
||||
const char *type = object_get_typename(OBJECT(dev));
|
||||
CPUArchId *core_slot;
|
||||
int node_id;
|
||||
int index;
|
||||
|
||||
if (dev->hotplugged && !mc->has_hotpluggable_cpus) {
|
||||
@ -2868,6 +2870,21 @@ static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
node_id = core_slot->props.node_id;
|
||||
if (!core_slot->props.has_node_id) {
|
||||
/* by default CPUState::numa_node was 0 if it's not set via CLI
|
||||
* keep it this way for now but in future we probably should
|
||||
* refuse to start up with incomplete numa mapping */
|
||||
node_id = 0;
|
||||
}
|
||||
if (sc->node_id == CPU_UNSET_NUMA_NODE_ID) {
|
||||
sc->node_id = node_id;
|
||||
} else if (sc->node_id != node_id) {
|
||||
error_setg(&local_err, "node-id %d must match numa node specified"
|
||||
"with -numa option for cpu-index %d", sc->node_id, cc->core_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(base_core_type);
|
||||
error_propagate(errp, local_err);
|
||||
@ -2988,11 +3005,18 @@ static HotplugHandler *spapr_get_hotplug_handler(MachineState *machine,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned spapr_cpu_index_to_socket_id(unsigned cpu_index)
|
||||
static CpuInstanceProperties
|
||||
spapr_cpu_index_to_props(MachineState *machine, unsigned cpu_index)
|
||||
{
|
||||
/* Allocate to NUMA nodes on a "socket" basis (not that concept of
|
||||
* socket means much for the paravirtualized PAPR platform) */
|
||||
return cpu_index / smp_threads / smp_cores;
|
||||
CPUArchId *core_slot;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
|
||||
/* make sure possible_cpu are intialized */
|
||||
mc->possible_cpu_arch_ids(machine);
|
||||
/* get CPU core slot containing thread that matches cpu_index */
|
||||
core_slot = spapr_find_cpu_slot(machine, cpu_index, NULL);
|
||||
assert(core_slot);
|
||||
return core_slot->props;
|
||||
}
|
||||
|
||||
static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine)
|
||||
@ -3019,8 +3043,15 @@ static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine)
|
||||
machine->possible_cpus->cpus[i].arch_id = core_id;
|
||||
machine->possible_cpus->cpus[i].props.has_core_id = true;
|
||||
machine->possible_cpus->cpus[i].props.core_id = core_id;
|
||||
/* TODO: add 'has_node/node' here to describe
|
||||
to which node core belongs */
|
||||
|
||||
/* default distribution of CPUs over NUMA nodes */
|
||||
if (nb_numa_nodes) {
|
||||
/* preset values but do not enable them i.e. 'has_node_id = false',
|
||||
* numa init code will enable them later if manual mapping wasn't
|
||||
* present on CLI */
|
||||
machine->possible_cpus->cpus[i].props.node_id =
|
||||
core_id / smp_threads / smp_cores % nb_numa_nodes;
|
||||
}
|
||||
}
|
||||
return machine->possible_cpus;
|
||||
}
|
||||
@ -3145,7 +3176,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
||||
hc->pre_plug = spapr_machine_device_pre_plug;
|
||||
hc->plug = spapr_machine_device_plug;
|
||||
hc->unplug = spapr_machine_device_unplug;
|
||||
mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id;
|
||||
mc->cpu_index_to_instance_props = spapr_cpu_index_to_props;
|
||||
mc->possible_cpu_arch_ids = spapr_possible_cpu_arch_ids;
|
||||
hc->unplug_request = spapr_machine_device_unplug_request;
|
||||
|
||||
@ -3249,6 +3280,7 @@ static void spapr_machine_2_9_class_options(MachineClass *mc)
|
||||
{
|
||||
spapr_machine_2_10_class_options(mc);
|
||||
SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_9);
|
||||
mc->numa_auto_assign_ram = numa_legacy_auto_assign_ram;
|
||||
}
|
||||
|
||||
DEFINE_SPAPR_MACHINE(2_9, "2.9", false);
|
||||
|
@ -176,13 +176,11 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
|
||||
const char *typename = object_class_get_name(scc->cpu_class);
|
||||
size_t size = object_type_get_instance_size(typename);
|
||||
Error *local_err = NULL;
|
||||
int core_node_id = numa_get_node_for_cpu(cc->core_id);;
|
||||
void *obj;
|
||||
int i, j;
|
||||
|
||||
sc->threads = g_malloc0(size * cc->nr_threads);
|
||||
for (i = 0; i < cc->nr_threads; i++) {
|
||||
int node_id;
|
||||
char id[32];
|
||||
CPUState *cs;
|
||||
|
||||
@ -192,17 +190,8 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
|
||||
cs = CPU(obj);
|
||||
cs->cpu_index = cc->core_id + i;
|
||||
|
||||
/* Set NUMA node for the added CPUs */
|
||||
node_id = numa_get_node_for_cpu(cs->cpu_index);
|
||||
if (node_id != core_node_id) {
|
||||
error_setg(&local_err, "Invalid node-id=%d of thread[cpu-index: %d]"
|
||||
" on CPU[core-id: %d, node-id: %d], node-id must be the same",
|
||||
node_id, cs->cpu_index, cc->core_id, core_node_id);
|
||||
goto err;
|
||||
}
|
||||
if (node_id < nb_numa_nodes) {
|
||||
cs->numa_node = node_id;
|
||||
}
|
||||
/* Set NUMA node for the threads belonged to core */
|
||||
cs->numa_node = sc->node_id;
|
||||
|
||||
snprintf(id, sizeof(id), "thread[%d]", i);
|
||||
object_property_add_child(OBJECT(sc), id, obj, &local_err);
|
||||
@ -263,6 +252,11 @@ static const char *spapr_core_models[] = {
|
||||
"POWER9_v1.0",
|
||||
};
|
||||
|
||||
static Property spapr_cpu_core_properties[] = {
|
||||
DEFINE_PROP_INT32("node-id", sPAPRCPUCore, node_id, CPU_UNSET_NUMA_NODE_ID),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
@ -270,6 +264,7 @@ void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
dc->realize = spapr_cpu_core_realize;
|
||||
dc->unrealize = spapr_cpu_core_unrealizefn;
|
||||
dc->props = spapr_cpu_core_properties;
|
||||
scc->cpu_class = cpu_class_by_name(TYPE_POWERPC_CPU, data);
|
||||
g_assert(scc->cpu_class);
|
||||
}
|
||||
|
@ -389,4 +389,5 @@ GCC_FMT_ATTR(2, 3);
|
||||
void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
|
||||
uint64_t len, int node, MemoryAffinityFlags flags);
|
||||
|
||||
void build_slit(GArray *table_data, BIOSLinker *linker);
|
||||
#endif
|
||||
|
@ -32,6 +32,7 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner,
|
||||
MachineClass *find_default_machine(void);
|
||||
extern MachineState *current_machine;
|
||||
|
||||
void machine_run_board_init(MachineState *machine);
|
||||
bool machine_usb(MachineState *machine);
|
||||
bool machine_kernel_irqchip_allowed(MachineState *machine);
|
||||
bool machine_kernel_irqchip_required(MachineState *machine);
|
||||
@ -42,6 +43,9 @@ bool machine_dump_guest_core(MachineState *machine);
|
||||
bool machine_mem_merge(MachineState *machine);
|
||||
void machine_register_compat_props(MachineState *machine);
|
||||
HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine);
|
||||
void machine_set_cpu_numa_node(MachineState *machine,
|
||||
const CpuInstanceProperties *props,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* CPUArchId:
|
||||
@ -74,7 +78,10 @@ typedef struct {
|
||||
* of HotplugHandler object, which handles hotplug operation
|
||||
* for a given @dev. It may return NULL if @dev doesn't require
|
||||
* any actions to be performed by hotplug handler.
|
||||
* @cpu_index_to_socket_id:
|
||||
* @cpu_index_to_instance_props:
|
||||
* used to provide @cpu_index to socket/core/thread number mapping, allowing
|
||||
* legacy code to perform maping from cpu_index to topology properties
|
||||
* Returns: tuple of socket/core/thread ids given cpu_index belongs to.
|
||||
* used to provide @cpu_index to socket number mapping, allowing
|
||||
* a machine to group CPU threads belonging to the same socket/package
|
||||
* Returns: socket number given cpu_index belongs to.
|
||||
@ -136,10 +143,13 @@ struct MachineClass {
|
||||
int minimum_page_bits;
|
||||
bool has_hotpluggable_cpus;
|
||||
int numa_mem_align_shift;
|
||||
void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes,
|
||||
int nb_nodes, ram_addr_t size);
|
||||
|
||||
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
|
||||
DeviceState *dev);
|
||||
unsigned (*cpu_index_to_socket_id)(unsigned cpu_index);
|
||||
CpuInstanceProperties (*cpu_index_to_instance_props)(MachineState *machine,
|
||||
unsigned cpu_index);
|
||||
const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine);
|
||||
};
|
||||
|
||||
|
@ -27,6 +27,7 @@ typedef struct sPAPRCPUCore {
|
||||
|
||||
/*< public >*/
|
||||
void *threads;
|
||||
int node_id;
|
||||
} sPAPRCPUCore;
|
||||
|
||||
typedef struct sPAPRCPUCoreClass {
|
||||
|
@ -97,5 +97,6 @@ typedef struct SSIBus SSIBus;
|
||||
typedef struct uWireSlave uWireSlave;
|
||||
typedef struct VirtIODevice VirtIODevice;
|
||||
typedef struct Visitor Visitor;
|
||||
typedef struct node_info NodeInfo;
|
||||
|
||||
#endif /* QEMU_TYPEDEFS_H */
|
||||
|
@ -258,6 +258,8 @@ typedef void (*run_on_cpu_func)(CPUState *cpu, run_on_cpu_data data);
|
||||
|
||||
struct qemu_work_item;
|
||||
|
||||
#define CPU_UNSET_NUMA_NODE_ID -1
|
||||
|
||||
/**
|
||||
* CPUState:
|
||||
* @cpu_index: CPU index (informative).
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "hw/boards.h"
|
||||
|
||||
extern int nb_numa_nodes; /* Number of NUMA nodes */
|
||||
extern bool have_numa_distance;
|
||||
|
||||
struct numa_addr_range {
|
||||
ram_addr_t mem_start;
|
||||
@ -15,24 +16,23 @@ struct numa_addr_range {
|
||||
QLIST_ENTRY(numa_addr_range) entry;
|
||||
};
|
||||
|
||||
typedef struct node_info {
|
||||
struct node_info {
|
||||
uint64_t node_mem;
|
||||
unsigned long *node_cpu;
|
||||
struct HostMemoryBackend *node_memdev;
|
||||
bool present;
|
||||
QLIST_HEAD(, numa_addr_range) addr; /* List to store address ranges */
|
||||
} NodeInfo;
|
||||
uint8_t distance[MAX_NODES];
|
||||
};
|
||||
|
||||
extern NodeInfo numa_info[MAX_NODES];
|
||||
void parse_numa_opts(MachineClass *mc);
|
||||
void numa_post_machine_init(void);
|
||||
void parse_numa_opts(MachineState *ms);
|
||||
void query_numa_node_mem(uint64_t node_mem[]);
|
||||
extern QemuOptsList qemu_numa_opts;
|
||||
void numa_set_mem_node_id(ram_addr_t addr, uint64_t size, uint32_t node);
|
||||
void numa_unset_mem_node_id(ram_addr_t addr, uint64_t size, uint32_t node);
|
||||
uint32_t numa_get_node(ram_addr_t addr, Error **errp);
|
||||
|
||||
/* on success returns node index in numa_info,
|
||||
* on failure returns nb_numa_nodes */
|
||||
int numa_get_node_for_cpu(int idx);
|
||||
void numa_legacy_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
|
||||
int nb_nodes, ram_addr_t size);
|
||||
void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
|
||||
int nb_nodes, ram_addr_t size);
|
||||
#endif
|
||||
|
@ -166,6 +166,10 @@ extern int mem_prealloc;
|
||||
|
||||
#define MAX_NODES 128
|
||||
#define NUMA_NODE_UNASSIGNED MAX_NODES
|
||||
#define NUMA_DISTANCE_MIN 10
|
||||
#define NUMA_DISTANCE_DEFAULT 20
|
||||
#define NUMA_DISTANCE_MAX 254
|
||||
#define NUMA_DISTANCE_UNREACHABLE 255
|
||||
|
||||
#define MAX_OPTION_ROMS 16
|
||||
typedef struct QEMUOptionRom {
|
||||
|
310
numa.c
310
numa.c
@ -51,6 +51,7 @@ static int max_numa_nodeid; /* Highest specified NUMA node ID, plus one.
|
||||
* For all nodes, nodeid < max_numa_nodeid
|
||||
*/
|
||||
int nb_numa_nodes;
|
||||
bool have_numa_distance;
|
||||
NodeInfo numa_info[MAX_NODES];
|
||||
|
||||
void numa_set_mem_node_id(ram_addr_t addr, uint64_t size, uint32_t node)
|
||||
@ -140,10 +141,12 @@ uint32_t numa_get_node(ram_addr_t addr, Error **errp)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
|
||||
static void parse_numa_node(MachineState *ms, NumaNodeOptions *node,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
uint16_t nodenr;
|
||||
uint16List *cpus = NULL;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
|
||||
if (node->has_nodeid) {
|
||||
nodenr = node->nodeid;
|
||||
@ -162,7 +165,12 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mc->cpu_index_to_instance_props) {
|
||||
error_report("NUMA is not supported by this machine-type");
|
||||
exit(1);
|
||||
}
|
||||
for (cpus = node->cpus; cpus; cpus = cpus->next) {
|
||||
CpuInstanceProperties props;
|
||||
if (cpus->value >= max_cpus) {
|
||||
error_setg(errp,
|
||||
"CPU index (%" PRIu16 ")"
|
||||
@ -170,7 +178,10 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
|
||||
cpus->value, max_cpus);
|
||||
return;
|
||||
}
|
||||
bitmap_set(numa_info[nodenr].node_cpu, cpus->value, 1);
|
||||
props = mc->cpu_index_to_instance_props(ms, cpus->value);
|
||||
props.node_id = nodenr;
|
||||
props.has_node_id = true;
|
||||
machine_set_cpu_numa_node(ms, &props, &error_fatal);
|
||||
}
|
||||
|
||||
if (node->has_mem && node->has_memdev) {
|
||||
@ -212,9 +223,47 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
|
||||
max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1);
|
||||
}
|
||||
|
||||
static void parse_numa_distance(NumaDistOptions *dist, Error **errp)
|
||||
{
|
||||
uint16_t src = dist->src;
|
||||
uint16_t dst = dist->dst;
|
||||
uint8_t val = dist->val;
|
||||
|
||||
if (src >= MAX_NODES || dst >= MAX_NODES) {
|
||||
error_setg(errp,
|
||||
"Invalid node %" PRIu16
|
||||
", max possible could be %" PRIu16,
|
||||
MAX(src, dst), MAX_NODES);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!numa_info[src].present || !numa_info[dst].present) {
|
||||
error_setg(errp, "Source/Destination NUMA node is missing. "
|
||||
"Please use '-numa node' option to declare it first.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (val < NUMA_DISTANCE_MIN) {
|
||||
error_setg(errp, "NUMA distance (%" PRIu8 ") is invalid, "
|
||||
"it shouldn't be less than %d.",
|
||||
val, NUMA_DISTANCE_MIN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src == dst && val != NUMA_DISTANCE_MIN) {
|
||||
error_setg(errp, "Local distance of node %d should be %d.",
|
||||
src, NUMA_DISTANCE_MIN);
|
||||
return;
|
||||
}
|
||||
|
||||
numa_info[src].distance[dst] = val;
|
||||
have_numa_distance = true;
|
||||
}
|
||||
|
||||
static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
NumaOptions *object = NULL;
|
||||
MachineState *ms = opaque;
|
||||
Error *err = NULL;
|
||||
|
||||
{
|
||||
@ -229,12 +278,33 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
|
||||
|
||||
switch (object->type) {
|
||||
case NUMA_OPTIONS_TYPE_NODE:
|
||||
numa_node_parse(&object->u.node, opts, &err);
|
||||
parse_numa_node(ms, &object->u.node, opts, &err);
|
||||
if (err) {
|
||||
goto end;
|
||||
}
|
||||
nb_numa_nodes++;
|
||||
break;
|
||||
case NUMA_OPTIONS_TYPE_DIST:
|
||||
parse_numa_distance(&object->u.dist, &err);
|
||||
if (err) {
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
case NUMA_OPTIONS_TYPE_CPU:
|
||||
if (!object->u.cpu.has_node_id) {
|
||||
error_setg(&err, "Missing mandatory node-id property");
|
||||
goto end;
|
||||
}
|
||||
if (!numa_info[object->u.cpu.node_id].present) {
|
||||
error_setg(&err, "Invalid node-id=%" PRId64 ", NUMA node must be "
|
||||
"defined with -numa node,nodeid=ID before it's used with "
|
||||
"-numa cpu,node-id=ID", object->u.cpu.node_id);
|
||||
goto end;
|
||||
}
|
||||
|
||||
machine_set_cpu_numa_node(ms, qapi_NumaCpuOptions_base(&object->u.cpu),
|
||||
&err);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
@ -249,60 +319,118 @@ end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *enumerate_cpus(unsigned long *cpus, int max_cpus)
|
||||
/* If all node pair distances are symmetric, then only distances
|
||||
* in one direction are enough. If there is even one asymmetric
|
||||
* pair, though, then all distances must be provided. The
|
||||
* distance from a node to itself is always NUMA_DISTANCE_MIN,
|
||||
* so providing it is never necessary.
|
||||
*/
|
||||
static void validate_numa_distance(void)
|
||||
{
|
||||
int cpu;
|
||||
bool first = true;
|
||||
GString *s = g_string_new(NULL);
|
||||
int src, dst;
|
||||
bool is_asymmetrical = false;
|
||||
|
||||
for (cpu = find_first_bit(cpus, max_cpus);
|
||||
cpu < max_cpus;
|
||||
cpu = find_next_bit(cpus, max_cpus, cpu + 1)) {
|
||||
g_string_append_printf(s, "%s%d", first ? "" : " ", cpu);
|
||||
first = false;
|
||||
}
|
||||
return g_string_free(s, FALSE);
|
||||
}
|
||||
for (src = 0; src < nb_numa_nodes; src++) {
|
||||
for (dst = src; dst < nb_numa_nodes; dst++) {
|
||||
if (numa_info[src].distance[dst] == 0 &&
|
||||
numa_info[dst].distance[src] == 0) {
|
||||
if (src != dst) {
|
||||
error_report("The distance between node %d and %d is "
|
||||
"missing, at least one distance value "
|
||||
"between each nodes should be provided.",
|
||||
src, dst);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void validate_numa_cpus(void)
|
||||
{
|
||||
int i;
|
||||
unsigned long *seen_cpus = bitmap_new(max_cpus);
|
||||
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
if (bitmap_intersects(seen_cpus, numa_info[i].node_cpu, max_cpus)) {
|
||||
bitmap_and(seen_cpus, seen_cpus,
|
||||
numa_info[i].node_cpu, max_cpus);
|
||||
error_report("CPU(s) present in multiple NUMA nodes: %s",
|
||||
enumerate_cpus(seen_cpus, max_cpus));
|
||||
g_free(seen_cpus);
|
||||
exit(EXIT_FAILURE);
|
||||
if (numa_info[src].distance[dst] != 0 &&
|
||||
numa_info[dst].distance[src] != 0 &&
|
||||
numa_info[src].distance[dst] !=
|
||||
numa_info[dst].distance[src]) {
|
||||
is_asymmetrical = true;
|
||||
}
|
||||
}
|
||||
bitmap_or(seen_cpus, seen_cpus,
|
||||
numa_info[i].node_cpu, max_cpus);
|
||||
}
|
||||
|
||||
if (!bitmap_full(seen_cpus, max_cpus)) {
|
||||
char *msg;
|
||||
bitmap_complement(seen_cpus, seen_cpus, max_cpus);
|
||||
msg = enumerate_cpus(seen_cpus, max_cpus);
|
||||
error_report("warning: CPU(s) not present in any NUMA nodes: %s", msg);
|
||||
error_report("warning: All CPU(s) up to maxcpus should be described "
|
||||
"in NUMA config");
|
||||
g_free(msg);
|
||||
if (is_asymmetrical) {
|
||||
for (src = 0; src < nb_numa_nodes; src++) {
|
||||
for (dst = 0; dst < nb_numa_nodes; dst++) {
|
||||
if (src != dst && numa_info[src].distance[dst] == 0) {
|
||||
error_report("At least one asymmetrical pair of "
|
||||
"distances is given, please provide distances "
|
||||
"for both directions of all node pairs.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
g_free(seen_cpus);
|
||||
}
|
||||
|
||||
void parse_numa_opts(MachineClass *mc)
|
||||
static void complete_init_numa_distance(void)
|
||||
{
|
||||
int src, dst;
|
||||
|
||||
/* Fixup NUMA distance by symmetric policy because if it is an
|
||||
* asymmetric distance table, it should be a complete table and
|
||||
* there would not be any missing distance except local node, which
|
||||
* is verified by validate_numa_distance above.
|
||||
*/
|
||||
for (src = 0; src < nb_numa_nodes; src++) {
|
||||
for (dst = 0; dst < nb_numa_nodes; dst++) {
|
||||
if (numa_info[src].distance[dst] == 0) {
|
||||
if (src == dst) {
|
||||
numa_info[src].distance[dst] = NUMA_DISTANCE_MIN;
|
||||
} else {
|
||||
numa_info[src].distance[dst] = numa_info[dst].distance[src];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void numa_legacy_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
|
||||
int nb_nodes, ram_addr_t size)
|
||||
{
|
||||
int i;
|
||||
uint64_t usedmem = 0;
|
||||
|
||||
for (i = 0; i < MAX_NODES; i++) {
|
||||
numa_info[i].node_cpu = bitmap_new(max_cpus);
|
||||
/* Align each node according to the alignment
|
||||
* requirements of the machine class
|
||||
*/
|
||||
|
||||
for (i = 0; i < nb_nodes - 1; i++) {
|
||||
nodes[i].node_mem = (size / nb_nodes) &
|
||||
~((1 << mc->numa_mem_align_shift) - 1);
|
||||
usedmem += nodes[i].node_mem;
|
||||
}
|
||||
nodes[i].node_mem = size - usedmem;
|
||||
}
|
||||
|
||||
if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, NULL, NULL)) {
|
||||
void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
|
||||
int nb_nodes, ram_addr_t size)
|
||||
{
|
||||
int i;
|
||||
uint64_t usedmem = 0, node_mem;
|
||||
uint64_t granularity = size / nb_nodes;
|
||||
uint64_t propagate = 0;
|
||||
|
||||
for (i = 0; i < nb_nodes - 1; i++) {
|
||||
node_mem = (granularity + propagate) &
|
||||
~((1 << mc->numa_mem_align_shift) - 1);
|
||||
propagate = granularity + propagate - node_mem;
|
||||
nodes[i].node_mem = node_mem;
|
||||
usedmem += node_mem;
|
||||
}
|
||||
nodes[i].node_mem = size - usedmem;
|
||||
}
|
||||
|
||||
void parse_numa_opts(MachineState *ms)
|
||||
{
|
||||
int i;
|
||||
const CPUArchIdList *possible_cpus;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
|
||||
if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -336,17 +464,8 @@ void parse_numa_opts(MachineClass *mc)
|
||||
}
|
||||
}
|
||||
if (i == nb_numa_nodes) {
|
||||
uint64_t usedmem = 0;
|
||||
|
||||
/* Align each node according to the alignment
|
||||
* requirements of the machine class
|
||||
*/
|
||||
for (i = 0; i < nb_numa_nodes - 1; i++) {
|
||||
numa_info[i].node_mem = (ram_size / nb_numa_nodes) &
|
||||
~((1 << mc->numa_mem_align_shift) - 1);
|
||||
usedmem += numa_info[i].node_mem;
|
||||
}
|
||||
numa_info[i].node_mem = ram_size - usedmem;
|
||||
assert(mc->numa_auto_assign_ram);
|
||||
mc->numa_auto_assign_ram(mc, numa_info, nb_numa_nodes, ram_size);
|
||||
}
|
||||
|
||||
numa_total = 0;
|
||||
@ -366,50 +485,55 @@ void parse_numa_opts(MachineClass *mc)
|
||||
|
||||
numa_set_mem_ranges();
|
||||
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
if (!bitmap_empty(numa_info[i].node_cpu, max_cpus)) {
|
||||
/* assign CPUs to nodes using board provided default mapping */
|
||||
if (!mc->cpu_index_to_instance_props || !mc->possible_cpu_arch_ids) {
|
||||
error_report("default CPUs to NUMA node mapping isn't supported");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
possible_cpus = mc->possible_cpu_arch_ids(ms);
|
||||
for (i = 0; i < possible_cpus->len; i++) {
|
||||
if (possible_cpus->cpus[i].props.has_node_id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Historically VCPUs were assigned in round-robin order to NUMA
|
||||
* nodes. However it causes issues with guest not handling it nice
|
||||
* in case where cores/threads from a multicore CPU appear on
|
||||
* different nodes. So allow boards to override default distribution
|
||||
* rule grouping VCPUs by socket so that VCPUs from the same socket
|
||||
* would be on the same node.
|
||||
*/
|
||||
if (i == nb_numa_nodes) {
|
||||
for (i = 0; i < max_cpus; i++) {
|
||||
unsigned node_id = i % nb_numa_nodes;
|
||||
if (mc->cpu_index_to_socket_id) {
|
||||
node_id = mc->cpu_index_to_socket_id(i) % nb_numa_nodes;
|
||||
}
|
||||
|
||||
set_bit(i, numa_info[node_id].node_cpu);
|
||||
/* no CPUs are assigned to NUMA nodes */
|
||||
if (i == possible_cpus->len) {
|
||||
for (i = 0; i < max_cpus; i++) {
|
||||
CpuInstanceProperties props;
|
||||
/* fetch default mapping from board and enable it */
|
||||
props = mc->cpu_index_to_instance_props(ms, i);
|
||||
props.has_node_id = true;
|
||||
|
||||
machine_set_cpu_numa_node(ms, &props, &error_fatal);
|
||||
}
|
||||
}
|
||||
|
||||
validate_numa_cpus();
|
||||
/* QEMU needs at least all unique node pair distances to build
|
||||
* the whole NUMA distance table. QEMU treats the distance table
|
||||
* as symmetric by default, i.e. distance A->B == distance B->A.
|
||||
* Thus, QEMU is able to complete the distance table
|
||||
* initialization even though only distance A->B is provided and
|
||||
* distance B->A is not. QEMU knows the distance of a node to
|
||||
* itself is always 10, so A->A distances may be omitted. When
|
||||
* the distances of two nodes of a pair differ, i.e. distance
|
||||
* A->B != distance B->A, then that means the distance table is
|
||||
* asymmetric. In this case, the distances for both directions
|
||||
* of all node pairs are required.
|
||||
*/
|
||||
if (have_numa_distance) {
|
||||
/* Validate enough NUMA distance information was provided. */
|
||||
validate_numa_distance();
|
||||
|
||||
/* Validation succeeded, now fill in any missing distances. */
|
||||
complete_init_numa_distance();
|
||||
}
|
||||
} else {
|
||||
numa_set_mem_node_id(0, ram_size, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void numa_post_machine_init(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
int i;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
assert(cpu->cpu_index < max_cpus);
|
||||
if (test_bit(cpu->cpu_index, numa_info[i].node_cpu)) {
|
||||
cpu->numa_node = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner,
|
||||
const char *name,
|
||||
uint64_t ram_size)
|
||||
@ -560,20 +684,6 @@ MemdevList *qmp_query_memdev(Error **errp)
|
||||
return list;
|
||||
}
|
||||
|
||||
int numa_get_node_for_cpu(int idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(idx < max_cpus);
|
||||
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
if (test_bit(idx, numa_info[i].node_cpu)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void ram_block_notifier_add(RAMBlockNotifier *n)
|
||||
{
|
||||
QLIST_INSERT_HEAD(&ram_list.ramblock_notifiers, n, next);
|
||||
|
@ -1325,6 +1325,9 @@
|
||||
#
|
||||
# @thread_id: ID of the underlying host thread
|
||||
#
|
||||
# @props: properties describing to which node/socket/core/thread
|
||||
# virtual CPU belongs to, provided if supported by board (since 2.10)
|
||||
#
|
||||
# @arch: architecture of the cpu, which determines which additional fields
|
||||
# will be listed (since 2.6)
|
||||
#
|
||||
@ -1335,7 +1338,8 @@
|
||||
##
|
||||
{ 'union': 'CpuInfo',
|
||||
'base': {'CPU': 'int', 'current': 'bool', 'halted': 'bool',
|
||||
'qom_path': 'str', 'thread_id': 'int', 'arch': 'CpuInfoArch' },
|
||||
'qom_path': 'str', 'thread_id': 'int',
|
||||
'*props': 'CpuInstanceProperties', 'arch': 'CpuInfoArch' },
|
||||
'discriminator': 'arch',
|
||||
'data': { 'x86': 'CpuInfoX86',
|
||||
'sparc': 'CpuInfoSPARC',
|
||||
@ -5682,10 +5686,16 @@
|
||||
##
|
||||
# @NumaOptionsType:
|
||||
#
|
||||
# @node: NUMA nodes configuration
|
||||
#
|
||||
# @dist: NUMA distance configuration (since 2.10)
|
||||
#
|
||||
# @cpu: property based CPU(s) to node mapping (Since: 2.10)
|
||||
#
|
||||
# Since: 2.1
|
||||
##
|
||||
{ 'enum': 'NumaOptionsType',
|
||||
'data': [ 'node' ] }
|
||||
'data': [ 'node', 'dist', 'cpu' ] }
|
||||
|
||||
##
|
||||
# @NumaOptions:
|
||||
@ -5698,7 +5708,9 @@
|
||||
'base': { 'type': 'NumaOptionsType' },
|
||||
'discriminator': 'type',
|
||||
'data': {
|
||||
'node': 'NumaNodeOptions' }}
|
||||
'node': 'NumaNodeOptions',
|
||||
'dist': 'NumaDistOptions',
|
||||
'cpu': 'NumaCpuOptions' }}
|
||||
|
||||
##
|
||||
# @NumaNodeOptions:
|
||||
@ -5726,6 +5738,41 @@
|
||||
'*mem': 'size',
|
||||
'*memdev': 'str' }}
|
||||
|
||||
##
|
||||
# @NumaDistOptions:
|
||||
#
|
||||
# Set the distance between 2 NUMA nodes.
|
||||
#
|
||||
# @src: source NUMA node.
|
||||
#
|
||||
# @dst: destination NUMA node.
|
||||
#
|
||||
# @val: NUMA distance from source node to destination node.
|
||||
# When a node is unreachable from another node, set the distance
|
||||
# between them to 255.
|
||||
#
|
||||
# Since: 2.10
|
||||
##
|
||||
{ 'struct': 'NumaDistOptions',
|
||||
'data': {
|
||||
'src': 'uint16',
|
||||
'dst': 'uint16',
|
||||
'val': 'uint8' }}
|
||||
|
||||
##
|
||||
# @NumaCpuOptions:
|
||||
#
|
||||
# Option "-numa cpu" overrides default cpu to node mapping.
|
||||
# It accepts the same set of cpu properties as returned by
|
||||
# query-hotpluggable-cpus[].props, where node-id could be used to
|
||||
# override default node mapping.
|
||||
#
|
||||
# Since: 2.10
|
||||
##
|
||||
{ 'struct': 'NumaCpuOptions',
|
||||
'base': 'CpuInstanceProperties',
|
||||
'data' : {} }
|
||||
|
||||
##
|
||||
# @HostMemPolicy:
|
||||
#
|
||||
|
@ -139,13 +139,18 @@ ETEXI
|
||||
|
||||
DEF("numa", HAS_ARG, QEMU_OPTION_numa,
|
||||
"-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n"
|
||||
"-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n", QEMU_ARCH_ALL)
|
||||
"-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n"
|
||||
"-numa dist,src=source,dst=destination,val=distance\n", QEMU_ARCH_ALL)
|
||||
STEXI
|
||||
@item -numa node[,mem=@var{size}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}]
|
||||
@itemx -numa node[,memdev=@var{id}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}]
|
||||
@itemx -numa dist,src=@var{source},dst=@var{destination},val=@var{distance}
|
||||
@itemx -numa cpu,node-id=@var{node}[,socket-id=@var{x}][,core-id=@var{y}][,thread-id=@var{z}]
|
||||
@findex -numa
|
||||
Define a NUMA node and assign RAM and VCPUs to it.
|
||||
Set the NUMA distance from a source node to a destination node.
|
||||
|
||||
Legacy VCPU assignment uses @samp{cpus} option where
|
||||
@var{firstcpu} and @var{lastcpu} are CPU indexes. Each
|
||||
@samp{cpus} option represent a contiguous range of CPU indexes
|
||||
(or a single VCPU if @var{lastcpu} is omitted). A non-contiguous
|
||||
@ -159,6 +164,24 @@ a NUMA node:
|
||||
-numa node,cpus=0-2,cpus=5
|
||||
@end example
|
||||
|
||||
@samp{cpu} option is a new alternative to @samp{cpus} option
|
||||
which uses @samp{socket-id|core-id|thread-id} properties to assign
|
||||
CPU objects to a @var{node} using topology layout properties of CPU.
|
||||
The set of properties is machine specific, and depends on used
|
||||
machine type/@samp{smp} options. It could be queried with
|
||||
@samp{hotpluggable-cpus} monitor command.
|
||||
@samp{node-id} property specifies @var{node} to which CPU object
|
||||
will be assigned, it's required for @var{node} to be declared
|
||||
with @samp{node} option before it's used with @samp{cpu} option.
|
||||
|
||||
For example:
|
||||
@example
|
||||
-M pc \
|
||||
-smp 1,sockets=2,maxcpus=2 \
|
||||
-numa node,nodeid=0 -numa node,nodeid=1 \
|
||||
-numa cpu,node-id=0,socket-id=0 -numa cpu,node-id=1,socket-id=1
|
||||
@end example
|
||||
|
||||
@samp{mem} assigns a given RAM amount to a node. @samp{memdev}
|
||||
assigns RAM from a given memory backend device to a node. If
|
||||
@samp{mem} and @samp{memdev} are omitted in all nodes, RAM is
|
||||
@ -167,6 +190,17 @@ split equally between them.
|
||||
@samp{mem} and @samp{memdev} are mutually exclusive. Furthermore,
|
||||
if one node uses @samp{memdev}, all of them have to use it.
|
||||
|
||||
@var{source} and @var{destination} are NUMA node IDs.
|
||||
@var{distance} is the NUMA distance from @var{source} to @var{destination}.
|
||||
The distance from a node to itself is always 10. If any pair of nodes is
|
||||
given a distance, then all pairs must be given distances. Although, when
|
||||
distances are only given in one direction for each pair of nodes, then
|
||||
the distances in the opposite directions are assumed to be the same. If,
|
||||
however, an asymmetrical pair of distances is given for even one node
|
||||
pair, then all node pairs must be provided distance values for both
|
||||
directions, even when they are symmetrical. When a node is unreachable
|
||||
from another node, set the pair's distance to 255.
|
||||
|
||||
Note that the -@option{numa} option doesn't allocate any of the
|
||||
specified resources, it just assigns existing resources to NUMA
|
||||
nodes. This means that one still has to use the @option{-m},
|
||||
|
@ -458,6 +458,13 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info)
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz)
|
||||
{
|
||||
uint32_t Aff1 = idx / clustersz;
|
||||
uint32_t Aff0 = idx % clustersz;
|
||||
return (Aff1 << ARM_AFF1_SHIFT) | Aff0;
|
||||
}
|
||||
|
||||
static void arm_cpu_initfn(Object *obj)
|
||||
{
|
||||
CPUState *cs = CPU(obj);
|
||||
@ -709,9 +716,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
* so these bits always RAZ.
|
||||
*/
|
||||
if (cpu->mp_affinity == ARM64_AFFINITY_INVALID) {
|
||||
uint32_t Aff1 = cs->cpu_index / ARM_DEFAULT_CPUS_PER_CLUSTER;
|
||||
uint32_t Aff0 = cs->cpu_index % ARM_DEFAULT_CPUS_PER_CLUSTER;
|
||||
cpu->mp_affinity = (Aff1 << ARM_AFF1_SHIFT) | Aff0;
|
||||
cpu->mp_affinity = arm_cpu_mp_affinity(cs->cpu_index,
|
||||
ARM_DEFAULT_CPUS_PER_CLUSTER);
|
||||
}
|
||||
|
||||
if (cpu->reset_hivecs) {
|
||||
@ -1567,6 +1573,7 @@ static Property arm_cpu_properties[] = {
|
||||
DEFINE_PROP_UINT32("midr", ARMCPU, midr, 0),
|
||||
DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
|
||||
mp_affinity, ARM64_AFFINITY_INVALID),
|
||||
DEFINE_PROP_INT32("node-id", CPUState, numa_node, CPU_UNSET_NUMA_NODE_ID),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
|
@ -710,6 +710,8 @@ static inline ARMCPU *arm_env_get_cpu(CPUARMState *env)
|
||||
return container_of(env, ARMCPU, env);
|
||||
}
|
||||
|
||||
uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz);
|
||||
|
||||
#define ENV_GET_CPU(e) CPU(arm_env_get_cpu(e))
|
||||
|
||||
#define ENV_OFFSET offsetof(ARMCPU, env)
|
||||
|
@ -2635,28 +2635,23 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||
X86CPU *cpu = x86_env_get_cpu(env);
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint32_t pkg_offset;
|
||||
uint32_t limit;
|
||||
|
||||
/* test if maximum index reached */
|
||||
if (index & 0x80000000) {
|
||||
if (index > env->cpuid_xlevel) {
|
||||
if (env->cpuid_xlevel2 > 0) {
|
||||
/* Handle the Centaur's CPUID instruction. */
|
||||
if (index > env->cpuid_xlevel2) {
|
||||
index = env->cpuid_xlevel2;
|
||||
} else if (index < 0xC0000000) {
|
||||
index = env->cpuid_xlevel;
|
||||
}
|
||||
} else {
|
||||
/* Intel documentation states that invalid EAX input will
|
||||
* return the same information as EAX=cpuid_level
|
||||
* (Intel SDM Vol. 2A - Instruction Set Reference - CPUID)
|
||||
*/
|
||||
index = env->cpuid_level;
|
||||
}
|
||||
}
|
||||
/* Calculate & apply limits for different index ranges */
|
||||
if (index >= 0xC0000000) {
|
||||
limit = env->cpuid_xlevel2;
|
||||
} else if (index >= 0x80000000) {
|
||||
limit = env->cpuid_xlevel;
|
||||
} else {
|
||||
if (index > env->cpuid_level)
|
||||
index = env->cpuid_level;
|
||||
limit = env->cpuid_level;
|
||||
}
|
||||
|
||||
if (index > limit) {
|
||||
/* Intel documentation states that invalid EAX input will
|
||||
* return the same information as EAX=cpuid_level
|
||||
* (Intel SDM Vol. 2A - Instruction Set Reference - CPUID)
|
||||
*/
|
||||
index = env->cpuid_level;
|
||||
}
|
||||
|
||||
switch(index) {
|
||||
@ -3991,6 +3986,7 @@ static Property x86_cpu_properties[] = {
|
||||
DEFINE_PROP_INT32("core-id", X86CPU, core_id, -1),
|
||||
DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, -1),
|
||||
#endif
|
||||
DEFINE_PROP_INT32("node-id", CPUState, numa_node, CPU_UNSET_NUMA_NODE_ID),
|
||||
DEFINE_PROP_BOOL("pmu", X86CPU, enable_pmu, false),
|
||||
{ .name = "hv-spinlocks", .info = &qdev_prop_spinlocks },
|
||||
DEFINE_PROP_BOOL("hv-relaxed", X86CPU, hyperv_relaxed_timing, false),
|
||||
|
@ -136,178 +136,48 @@ static const VMStateDescription vmstate_mtrr_var = {
|
||||
#define VMSTATE_MTRR_VARS(_field, _state, _n, _v) \
|
||||
VMSTATE_STRUCT_ARRAY(_field, _state, _n, _v, vmstate_mtrr_var, MTRRVar)
|
||||
|
||||
static int put_fpreg_error(QEMUFile *f, void *opaque, size_t size,
|
||||
VMStateField *field, QJSON *vmdesc)
|
||||
typedef struct x86_FPReg_tmp {
|
||||
FPReg *parent;
|
||||
uint64_t tmp_mant;
|
||||
uint16_t tmp_exp;
|
||||
} x86_FPReg_tmp;
|
||||
|
||||
static void fpreg_pre_save(void *opaque)
|
||||
{
|
||||
fprintf(stderr, "call put_fpreg() with invalid arguments\n");
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
x86_FPReg_tmp *tmp = opaque;
|
||||
|
||||
/* XXX: add that in a FPU generic layer */
|
||||
union x86_longdouble {
|
||||
uint64_t mant;
|
||||
uint16_t exp;
|
||||
};
|
||||
|
||||
#define MANTD1(fp) (fp & ((1LL << 52) - 1))
|
||||
#define EXPBIAS1 1023
|
||||
#define EXPD1(fp) ((fp >> 52) & 0x7FF)
|
||||
#define SIGND1(fp) ((fp >> 32) & 0x80000000)
|
||||
|
||||
static void fp64_to_fp80(union x86_longdouble *p, uint64_t temp)
|
||||
{
|
||||
int e;
|
||||
/* mantissa */
|
||||
p->mant = (MANTD1(temp) << 11) | (1LL << 63);
|
||||
/* exponent + sign */
|
||||
e = EXPD1(temp) - EXPBIAS1 + 16383;
|
||||
e |= SIGND1(temp) >> 16;
|
||||
p->exp = e;
|
||||
}
|
||||
|
||||
static int get_fpreg(QEMUFile *f, void *opaque, size_t size,
|
||||
VMStateField *field)
|
||||
{
|
||||
FPReg *fp_reg = opaque;
|
||||
uint64_t mant;
|
||||
uint16_t exp;
|
||||
|
||||
qemu_get_be64s(f, &mant);
|
||||
qemu_get_be16s(f, &exp);
|
||||
fp_reg->d = cpu_set_fp80(mant, exp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_fpreg(QEMUFile *f, void *opaque, size_t size,
|
||||
VMStateField *field, QJSON *vmdesc)
|
||||
{
|
||||
FPReg *fp_reg = opaque;
|
||||
uint64_t mant;
|
||||
uint16_t exp;
|
||||
/* we save the real CPU data (in case of MMX usage only 'mant'
|
||||
contains the MMX register */
|
||||
cpu_get_fp80(&mant, &exp, fp_reg->d);
|
||||
qemu_put_be64s(f, &mant);
|
||||
qemu_put_be16s(f, &exp);
|
||||
cpu_get_fp80(&tmp->tmp_mant, &tmp->tmp_exp, tmp->parent->d);
|
||||
}
|
||||
|
||||
static int fpreg_post_load(void *opaque, int version)
|
||||
{
|
||||
x86_FPReg_tmp *tmp = opaque;
|
||||
|
||||
tmp->parent->d = cpu_set_fp80(tmp->tmp_mant, tmp->tmp_exp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateInfo vmstate_fpreg = {
|
||||
static const VMStateDescription vmstate_fpreg_tmp = {
|
||||
.name = "fpreg_tmp",
|
||||
.post_load = fpreg_post_load,
|
||||
.pre_save = fpreg_pre_save,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(tmp_mant, x86_FPReg_tmp),
|
||||
VMSTATE_UINT16(tmp_exp, x86_FPReg_tmp),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_fpreg = {
|
||||
.name = "fpreg",
|
||||
.get = get_fpreg,
|
||||
.put = put_fpreg,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_WITH_TMP(FPReg, x86_FPReg_tmp, vmstate_fpreg_tmp),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static int get_fpreg_1_mmx(QEMUFile *f, void *opaque, size_t size,
|
||||
VMStateField *field)
|
||||
{
|
||||
union x86_longdouble *p = opaque;
|
||||
uint64_t mant;
|
||||
|
||||
qemu_get_be64s(f, &mant);
|
||||
p->mant = mant;
|
||||
p->exp = 0xffff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateInfo vmstate_fpreg_1_mmx = {
|
||||
.name = "fpreg_1_mmx",
|
||||
.get = get_fpreg_1_mmx,
|
||||
.put = put_fpreg_error,
|
||||
};
|
||||
|
||||
static int get_fpreg_1_no_mmx(QEMUFile *f, void *opaque, size_t size,
|
||||
VMStateField *field)
|
||||
{
|
||||
union x86_longdouble *p = opaque;
|
||||
uint64_t mant;
|
||||
|
||||
qemu_get_be64s(f, &mant);
|
||||
fp64_to_fp80(p, mant);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateInfo vmstate_fpreg_1_no_mmx = {
|
||||
.name = "fpreg_1_no_mmx",
|
||||
.get = get_fpreg_1_no_mmx,
|
||||
.put = put_fpreg_error,
|
||||
};
|
||||
|
||||
static bool fpregs_is_0(void *opaque, int version_id)
|
||||
{
|
||||
X86CPU *cpu = opaque;
|
||||
CPUX86State *env = &cpu->env;
|
||||
|
||||
return (env->fpregs_format_vmstate == 0);
|
||||
}
|
||||
|
||||
static bool fpregs_is_1_mmx(void *opaque, int version_id)
|
||||
{
|
||||
X86CPU *cpu = opaque;
|
||||
CPUX86State *env = &cpu->env;
|
||||
int guess_mmx;
|
||||
|
||||
guess_mmx = ((env->fptag_vmstate == 0xff) &&
|
||||
(env->fpus_vmstate & 0x3800) == 0);
|
||||
return (guess_mmx && (env->fpregs_format_vmstate == 1));
|
||||
}
|
||||
|
||||
static bool fpregs_is_1_no_mmx(void *opaque, int version_id)
|
||||
{
|
||||
X86CPU *cpu = opaque;
|
||||
CPUX86State *env = &cpu->env;
|
||||
int guess_mmx;
|
||||
|
||||
guess_mmx = ((env->fptag_vmstate == 0xff) &&
|
||||
(env->fpus_vmstate & 0x3800) == 0);
|
||||
return (!guess_mmx && (env->fpregs_format_vmstate == 1));
|
||||
}
|
||||
|
||||
#define VMSTATE_FP_REGS(_field, _state, _n) \
|
||||
VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_0, vmstate_fpreg, FPReg), \
|
||||
VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_mmx, vmstate_fpreg_1_mmx, FPReg), \
|
||||
VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_no_mmx, vmstate_fpreg_1_no_mmx, FPReg)
|
||||
|
||||
static bool version_is_5(void *opaque, int version_id)
|
||||
{
|
||||
return version_id == 5;
|
||||
}
|
||||
|
||||
#ifdef TARGET_X86_64
|
||||
static bool less_than_7(void *opaque, int version_id)
|
||||
{
|
||||
return version_id < 7;
|
||||
}
|
||||
|
||||
static int get_uint64_as_uint32(QEMUFile *f, void *pv, size_t size,
|
||||
VMStateField *field)
|
||||
{
|
||||
uint64_t *v = pv;
|
||||
*v = qemu_get_be32(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_uint64_as_uint32(QEMUFile *f, void *pv, size_t size,
|
||||
VMStateField *field, QJSON *vmdesc)
|
||||
{
|
||||
uint64_t *v = pv;
|
||||
qemu_put_be32(f, *v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateInfo vmstate_hack_uint64_as_uint32 = {
|
||||
.name = "uint64_as_uint32",
|
||||
.get = get_uint64_as_uint32,
|
||||
.put = put_uint64_as_uint32,
|
||||
};
|
||||
|
||||
#define VMSTATE_HACK_UINT32(_f, _s, _t) \
|
||||
VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint64_as_uint32, uint64_t)
|
||||
#endif
|
||||
|
||||
static void cpu_pre_save(void *opaque)
|
||||
{
|
||||
X86CPU *cpu = opaque;
|
||||
@ -356,6 +226,10 @@ static int cpu_post_load(void *opaque, int version_id)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (env->fpregs_format_vmstate) {
|
||||
error_report("Unsupported old non-softfloat CPU state");
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* Real mode guest segments register DPL should be zero.
|
||||
* Older KVM version were setting it wrongly.
|
||||
@ -930,7 +804,7 @@ static const VMStateDescription vmstate_mcg_ext_ctl = {
|
||||
VMStateDescription vmstate_x86_cpu = {
|
||||
.name = "cpu",
|
||||
.version_id = 12,
|
||||
.minimum_version_id = 3,
|
||||
.minimum_version_id = 11,
|
||||
.pre_save = cpu_pre_save,
|
||||
.post_load = cpu_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
@ -943,7 +817,8 @@ VMStateDescription vmstate_x86_cpu = {
|
||||
VMSTATE_UINT16(env.fpus_vmstate, X86CPU),
|
||||
VMSTATE_UINT16(env.fptag_vmstate, X86CPU),
|
||||
VMSTATE_UINT16(env.fpregs_format_vmstate, X86CPU),
|
||||
VMSTATE_FP_REGS(env.fpregs, X86CPU, 8),
|
||||
|
||||
VMSTATE_STRUCT_ARRAY(env.fpregs, X86CPU, 8, 0, vmstate_fpreg, FPReg),
|
||||
|
||||
VMSTATE_SEGMENT_ARRAY(env.segs, X86CPU, 6),
|
||||
VMSTATE_SEGMENT(env.ldt, X86CPU),
|
||||
@ -952,16 +827,8 @@ VMStateDescription vmstate_x86_cpu = {
|
||||
VMSTATE_SEGMENT(env.idt, X86CPU),
|
||||
|
||||
VMSTATE_UINT32(env.sysenter_cs, X86CPU),
|
||||
#ifdef TARGET_X86_64
|
||||
/* Hack: In v7 size changed from 32 to 64 bits on x86_64 */
|
||||
VMSTATE_HACK_UINT32(env.sysenter_esp, X86CPU, less_than_7),
|
||||
VMSTATE_HACK_UINT32(env.sysenter_eip, X86CPU, less_than_7),
|
||||
VMSTATE_UINTTL_V(env.sysenter_esp, X86CPU, 7),
|
||||
VMSTATE_UINTTL_V(env.sysenter_eip, X86CPU, 7),
|
||||
#else
|
||||
VMSTATE_UINTTL(env.sysenter_esp, X86CPU),
|
||||
VMSTATE_UINTTL(env.sysenter_eip, X86CPU),
|
||||
#endif
|
||||
|
||||
VMSTATE_UINTTL(env.cr[0], X86CPU),
|
||||
VMSTATE_UINTTL(env.cr[2], X86CPU),
|
||||
@ -982,46 +849,45 @@ VMStateDescription vmstate_x86_cpu = {
|
||||
VMSTATE_UINT64(env.fmask, X86CPU),
|
||||
VMSTATE_UINT64(env.kernelgsbase, X86CPU),
|
||||
#endif
|
||||
VMSTATE_UINT32_V(env.smbase, X86CPU, 4),
|
||||
VMSTATE_UINT32(env.smbase, X86CPU),
|
||||
|
||||
VMSTATE_UINT64_V(env.pat, X86CPU, 5),
|
||||
VMSTATE_UINT32_V(env.hflags2, X86CPU, 5),
|
||||
VMSTATE_UINT64(env.pat, X86CPU),
|
||||
VMSTATE_UINT32(env.hflags2, X86CPU),
|
||||
|
||||
VMSTATE_UINT32_TEST(parent_obj.halted, X86CPU, version_is_5),
|
||||
VMSTATE_UINT64_V(env.vm_hsave, X86CPU, 5),
|
||||
VMSTATE_UINT64_V(env.vm_vmcb, X86CPU, 5),
|
||||
VMSTATE_UINT64_V(env.tsc_offset, X86CPU, 5),
|
||||
VMSTATE_UINT64_V(env.intercept, X86CPU, 5),
|
||||
VMSTATE_UINT16_V(env.intercept_cr_read, X86CPU, 5),
|
||||
VMSTATE_UINT16_V(env.intercept_cr_write, X86CPU, 5),
|
||||
VMSTATE_UINT16_V(env.intercept_dr_read, X86CPU, 5),
|
||||
VMSTATE_UINT16_V(env.intercept_dr_write, X86CPU, 5),
|
||||
VMSTATE_UINT32_V(env.intercept_exceptions, X86CPU, 5),
|
||||
VMSTATE_UINT8_V(env.v_tpr, X86CPU, 5),
|
||||
VMSTATE_UINT64(env.vm_hsave, X86CPU),
|
||||
VMSTATE_UINT64(env.vm_vmcb, X86CPU),
|
||||
VMSTATE_UINT64(env.tsc_offset, X86CPU),
|
||||
VMSTATE_UINT64(env.intercept, X86CPU),
|
||||
VMSTATE_UINT16(env.intercept_cr_read, X86CPU),
|
||||
VMSTATE_UINT16(env.intercept_cr_write, X86CPU),
|
||||
VMSTATE_UINT16(env.intercept_dr_read, X86CPU),
|
||||
VMSTATE_UINT16(env.intercept_dr_write, X86CPU),
|
||||
VMSTATE_UINT32(env.intercept_exceptions, X86CPU),
|
||||
VMSTATE_UINT8(env.v_tpr, X86CPU),
|
||||
/* MTRRs */
|
||||
VMSTATE_UINT64_ARRAY_V(env.mtrr_fixed, X86CPU, 11, 8),
|
||||
VMSTATE_UINT64_V(env.mtrr_deftype, X86CPU, 8),
|
||||
VMSTATE_UINT64_ARRAY(env.mtrr_fixed, X86CPU, 11),
|
||||
VMSTATE_UINT64(env.mtrr_deftype, X86CPU),
|
||||
VMSTATE_MTRR_VARS(env.mtrr_var, X86CPU, MSR_MTRRcap_VCNT, 8),
|
||||
/* KVM-related states */
|
||||
VMSTATE_INT32_V(env.interrupt_injected, X86CPU, 9),
|
||||
VMSTATE_UINT32_V(env.mp_state, X86CPU, 9),
|
||||
VMSTATE_UINT64_V(env.tsc, X86CPU, 9),
|
||||
VMSTATE_INT32_V(env.exception_injected, X86CPU, 11),
|
||||
VMSTATE_UINT8_V(env.soft_interrupt, X86CPU, 11),
|
||||
VMSTATE_UINT8_V(env.nmi_injected, X86CPU, 11),
|
||||
VMSTATE_UINT8_V(env.nmi_pending, X86CPU, 11),
|
||||
VMSTATE_UINT8_V(env.has_error_code, X86CPU, 11),
|
||||
VMSTATE_UINT32_V(env.sipi_vector, X86CPU, 11),
|
||||
VMSTATE_INT32(env.interrupt_injected, X86CPU),
|
||||
VMSTATE_UINT32(env.mp_state, X86CPU),
|
||||
VMSTATE_UINT64(env.tsc, X86CPU),
|
||||
VMSTATE_INT32(env.exception_injected, X86CPU),
|
||||
VMSTATE_UINT8(env.soft_interrupt, X86CPU),
|
||||
VMSTATE_UINT8(env.nmi_injected, X86CPU),
|
||||
VMSTATE_UINT8(env.nmi_pending, X86CPU),
|
||||
VMSTATE_UINT8(env.has_error_code, X86CPU),
|
||||
VMSTATE_UINT32(env.sipi_vector, X86CPU),
|
||||
/* MCE */
|
||||
VMSTATE_UINT64_V(env.mcg_cap, X86CPU, 10),
|
||||
VMSTATE_UINT64_V(env.mcg_status, X86CPU, 10),
|
||||
VMSTATE_UINT64_V(env.mcg_ctl, X86CPU, 10),
|
||||
VMSTATE_UINT64_ARRAY_V(env.mce_banks, X86CPU, MCE_BANKS_DEF * 4, 10),
|
||||
VMSTATE_UINT64(env.mcg_cap, X86CPU),
|
||||
VMSTATE_UINT64(env.mcg_status, X86CPU),
|
||||
VMSTATE_UINT64(env.mcg_ctl, X86CPU),
|
||||
VMSTATE_UINT64_ARRAY(env.mce_banks, X86CPU, MCE_BANKS_DEF * 4),
|
||||
/* rdtscp */
|
||||
VMSTATE_UINT64_V(env.tsc_aux, X86CPU, 11),
|
||||
VMSTATE_UINT64(env.tsc_aux, X86CPU),
|
||||
/* KVM pvclock msr */
|
||||
VMSTATE_UINT64_V(env.system_time_msr, X86CPU, 11),
|
||||
VMSTATE_UINT64_V(env.wall_clock_msr, X86CPU, 11),
|
||||
VMSTATE_UINT64(env.system_time_msr, X86CPU),
|
||||
VMSTATE_UINT64(env.wall_clock_msr, X86CPU),
|
||||
/* XSAVE related fields */
|
||||
VMSTATE_UINT64_V(env.xcr0, X86CPU, 12),
|
||||
VMSTATE_UINT64_V(env.xstate_bv, X86CPU, 12),
|
||||
|
@ -260,6 +260,7 @@ check-qtest-i386-y += tests/test-filter-mirror$(EXESUF)
|
||||
check-qtest-i386-y += tests/test-filter-redirector$(EXESUF)
|
||||
check-qtest-i386-y += tests/postcopy-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF)
|
||||
check-qtest-i386-y += tests/numa-test$(EXESUF)
|
||||
check-qtest-x86_64-y += $(check-qtest-i386-y)
|
||||
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
|
||||
gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y))
|
||||
@ -300,6 +301,7 @@ check-qtest-ppc64-y += tests/test-netfilter$(EXESUF)
|
||||
check-qtest-ppc64-y += tests/test-filter-mirror$(EXESUF)
|
||||
check-qtest-ppc64-y += tests/test-filter-redirector$(EXESUF)
|
||||
check-qtest-ppc64-y += tests/display-vga-test$(EXESUF)
|
||||
check-qtest-ppc64-y += tests/numa-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF)
|
||||
|
||||
check-qtest-sh4-y = tests/endianness-test$(EXESUF)
|
||||
@ -324,6 +326,8 @@ gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c
|
||||
check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF)
|
||||
gcov-files-arm-y += hw/timer/arm_mptimer.c
|
||||
|
||||
check-qtest-aarch64-y = tests/numa-test$(EXESUF)
|
||||
|
||||
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
|
||||
|
||||
check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
|
||||
@ -753,6 +757,7 @@ tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o contrib/libvhost-use
|
||||
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
|
||||
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
|
||||
tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
|
||||
tests/numa-test$(EXESUF): tests/numa-test.o
|
||||
|
||||
tests/migration/stress$(EXESUF): tests/migration/stress.o
|
||||
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
|
||||
|
BIN
tests/acpi-test-data/pc/SLIT.cphp
Normal file
BIN
tests/acpi-test-data/pc/SLIT.cphp
Normal file
Binary file not shown.
BIN
tests/acpi-test-data/pc/SLIT.memhp
Normal file
BIN
tests/acpi-test-data/pc/SLIT.memhp
Normal file
Binary file not shown.
Binary file not shown.
BIN
tests/acpi-test-data/q35/SLIT.cphp
Normal file
BIN
tests/acpi-test-data/q35/SLIT.cphp
Normal file
Binary file not shown.
BIN
tests/acpi-test-data/q35/SLIT.memhp
Normal file
BIN
tests/acpi-test-data/q35/SLIT.memhp
Normal file
Binary file not shown.
Binary file not shown.
@ -723,7 +723,8 @@ static void test_acpi_piix4_tcg_cphp(void)
|
||||
data.machine = MACHINE_PC;
|
||||
data.variant = ".cphp";
|
||||
test_acpi_one("-smp 2,cores=3,sockets=2,maxcpus=6"
|
||||
" -numa node -numa node",
|
||||
" -numa node -numa node"
|
||||
" -numa dist,src=0,dst=1,val=21",
|
||||
&data);
|
||||
free_test_data(&data);
|
||||
}
|
||||
@ -736,7 +737,8 @@ static void test_acpi_q35_tcg_cphp(void)
|
||||
data.machine = MACHINE_Q35;
|
||||
data.variant = ".cphp";
|
||||
test_acpi_one(" -smp 2,cores=3,sockets=2,maxcpus=6"
|
||||
" -numa node -numa node",
|
||||
" -numa node -numa node"
|
||||
" -numa dist,src=0,dst=1,val=21",
|
||||
&data);
|
||||
free_test_data(&data);
|
||||
}
|
||||
@ -785,7 +787,10 @@ static void test_acpi_q35_tcg_memhp(void)
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.machine = MACHINE_Q35;
|
||||
data.variant = ".memhp";
|
||||
test_acpi_one(" -m 128,slots=3,maxmem=1G -numa node", &data);
|
||||
test_acpi_one(" -m 128,slots=3,maxmem=1G"
|
||||
" -numa node -numa node"
|
||||
" -numa dist,src=0,dst=1,val=21",
|
||||
&data);
|
||||
free_test_data(&data);
|
||||
}
|
||||
|
||||
@ -796,7 +801,10 @@ static void test_acpi_piix4_tcg_memhp(void)
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.machine = MACHINE_PC;
|
||||
data.variant = ".memhp";
|
||||
test_acpi_one(" -m 128,slots=3,maxmem=1G -numa node", &data);
|
||||
test_acpi_one(" -m 128,slots=3,maxmem=1G"
|
||||
" -numa node -numa node"
|
||||
" -numa dist,src=0,dst=1,val=21",
|
||||
&data);
|
||||
free_test_data(&data);
|
||||
}
|
||||
|
||||
|
302
tests/numa-test.c
Normal file
302
tests/numa-test.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* NUMA configuration test cases
|
||||
*
|
||||
* Copyright (c) 2017 Red Hat Inc.
|
||||
* Authors:
|
||||
* Igor Mammedov <imammedo@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
|
||||
static char *make_cli(const char *generic_cli, const char *test_cli)
|
||||
{
|
||||
return g_strdup_printf("%s %s", generic_cli ? generic_cli : "", test_cli);
|
||||
}
|
||||
|
||||
static char *hmp_info_numa(void)
|
||||
{
|
||||
QDict *resp;
|
||||
char *s;
|
||||
|
||||
resp = qmp("{ 'execute': 'human-monitor-command', 'arguments': "
|
||||
"{ 'command-line': 'info numa '} }");
|
||||
g_assert(resp);
|
||||
g_assert(qdict_haskey(resp, "return"));
|
||||
s = g_strdup(qdict_get_str(resp, "return"));
|
||||
g_assert(s);
|
||||
QDECREF(resp);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void test_mon_explicit(const void *data)
|
||||
{
|
||||
char *s;
|
||||
char *cli;
|
||||
|
||||
cli = make_cli(data, "-smp 8 "
|
||||
"-numa node,nodeid=0,cpus=0-3 "
|
||||
"-numa node,nodeid=1,cpus=4-7 ");
|
||||
qtest_start(cli);
|
||||
|
||||
s = hmp_info_numa();
|
||||
g_assert(strstr(s, "node 0 cpus: 0 1 2 3"));
|
||||
g_assert(strstr(s, "node 1 cpus: 4 5 6 7"));
|
||||
g_free(s);
|
||||
|
||||
qtest_end();
|
||||
g_free(cli);
|
||||
}
|
||||
|
||||
static void test_mon_default(const void *data)
|
||||
{
|
||||
char *s;
|
||||
char *cli;
|
||||
|
||||
cli = make_cli(data, "-smp 8 -numa node -numa node");
|
||||
qtest_start(cli);
|
||||
|
||||
s = hmp_info_numa();
|
||||
g_assert(strstr(s, "node 0 cpus: 0 2 4 6"));
|
||||
g_assert(strstr(s, "node 1 cpus: 1 3 5 7"));
|
||||
g_free(s);
|
||||
|
||||
qtest_end();
|
||||
g_free(cli);
|
||||
}
|
||||
|
||||
static void test_mon_partial(const void *data)
|
||||
{
|
||||
char *s;
|
||||
char *cli;
|
||||
|
||||
cli = make_cli(data, "-smp 8 "
|
||||
"-numa node,nodeid=0,cpus=0-1 "
|
||||
"-numa node,nodeid=1,cpus=4-5 ");
|
||||
qtest_start(cli);
|
||||
|
||||
s = hmp_info_numa();
|
||||
g_assert(strstr(s, "node 0 cpus: 0 1 2 3 6 7"));
|
||||
g_assert(strstr(s, "node 1 cpus: 4 5"));
|
||||
g_free(s);
|
||||
|
||||
qtest_end();
|
||||
g_free(cli);
|
||||
}
|
||||
|
||||
static QList *get_cpus(QDict **resp)
|
||||
{
|
||||
*resp = qmp("{ 'execute': 'query-cpus' }");
|
||||
g_assert(*resp);
|
||||
g_assert(qdict_haskey(*resp, "return"));
|
||||
return qdict_get_qlist(*resp, "return");
|
||||
}
|
||||
|
||||
static void test_query_cpus(const void *data)
|
||||
{
|
||||
char *cli;
|
||||
QDict *resp;
|
||||
QList *cpus;
|
||||
const QObject *e;
|
||||
|
||||
cli = make_cli(data, "-smp 8 -numa node,cpus=0-3 -numa node,cpus=4-7");
|
||||
qtest_start(cli);
|
||||
cpus = get_cpus(&resp);
|
||||
g_assert(cpus);
|
||||
|
||||
while ((e = qlist_pop(cpus))) {
|
||||
QDict *cpu, *props;
|
||||
int64_t cpu_idx, node;
|
||||
|
||||
cpu = qobject_to_qdict(e);
|
||||
g_assert(qdict_haskey(cpu, "CPU"));
|
||||
g_assert(qdict_haskey(cpu, "props"));
|
||||
|
||||
cpu_idx = qdict_get_int(cpu, "CPU");
|
||||
props = qdict_get_qdict(cpu, "props");
|
||||
g_assert(qdict_haskey(props, "node-id"));
|
||||
node = qdict_get_int(props, "node-id");
|
||||
if (cpu_idx >= 0 && cpu_idx < 4) {
|
||||
g_assert_cmpint(node, ==, 0);
|
||||
} else {
|
||||
g_assert_cmpint(node, ==, 1);
|
||||
}
|
||||
}
|
||||
|
||||
QDECREF(resp);
|
||||
qtest_end();
|
||||
g_free(cli);
|
||||
}
|
||||
|
||||
static void pc_numa_cpu(const void *data)
|
||||
{
|
||||
char *cli;
|
||||
QDict *resp;
|
||||
QList *cpus;
|
||||
const QObject *e;
|
||||
|
||||
cli = make_cli(data, "-cpu pentium -smp 8,sockets=2,cores=2,threads=2 "
|
||||
"-numa node,nodeid=0 -numa node,nodeid=1 "
|
||||
"-numa cpu,node-id=1,socket-id=0 "
|
||||
"-numa cpu,node-id=0,socket-id=1,core-id=0 "
|
||||
"-numa cpu,node-id=0,socket-id=1,core-id=1,thread-id=0 "
|
||||
"-numa cpu,node-id=1,socket-id=1,core-id=1,thread-id=1");
|
||||
qtest_start(cli);
|
||||
cpus = get_cpus(&resp);
|
||||
g_assert(cpus);
|
||||
|
||||
while ((e = qlist_pop(cpus))) {
|
||||
QDict *cpu, *props;
|
||||
int64_t socket, core, thread, node;
|
||||
|
||||
cpu = qobject_to_qdict(e);
|
||||
g_assert(qdict_haskey(cpu, "props"));
|
||||
props = qdict_get_qdict(cpu, "props");
|
||||
|
||||
g_assert(qdict_haskey(props, "node-id"));
|
||||
node = qdict_get_int(props, "node-id");
|
||||
g_assert(qdict_haskey(props, "socket-id"));
|
||||
socket = qdict_get_int(props, "socket-id");
|
||||
g_assert(qdict_haskey(props, "core-id"));
|
||||
core = qdict_get_int(props, "core-id");
|
||||
g_assert(qdict_haskey(props, "thread-id"));
|
||||
thread = qdict_get_int(props, "thread-id");
|
||||
|
||||
if (socket == 0) {
|
||||
g_assert_cmpint(node, ==, 1);
|
||||
} else if (socket == 1 && core == 0) {
|
||||
g_assert_cmpint(node, ==, 0);
|
||||
} else if (socket == 1 && core == 1 && thread == 0) {
|
||||
g_assert_cmpint(node, ==, 0);
|
||||
} else if (socket == 1 && core == 1 && thread == 1) {
|
||||
g_assert_cmpint(node, ==, 1);
|
||||
} else {
|
||||
g_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
QDECREF(resp);
|
||||
qtest_end();
|
||||
g_free(cli);
|
||||
}
|
||||
|
||||
static void spapr_numa_cpu(const void *data)
|
||||
{
|
||||
char *cli;
|
||||
QDict *resp;
|
||||
QList *cpus;
|
||||
const QObject *e;
|
||||
|
||||
cli = make_cli(data, "-smp 4,cores=4 "
|
||||
"-numa node,nodeid=0 -numa node,nodeid=1 "
|
||||
"-numa cpu,node-id=0,core-id=0 "
|
||||
"-numa cpu,node-id=0,core-id=1 "
|
||||
"-numa cpu,node-id=0,core-id=2 "
|
||||
"-numa cpu,node-id=1,core-id=3");
|
||||
qtest_start(cli);
|
||||
cpus = get_cpus(&resp);
|
||||
g_assert(cpus);
|
||||
|
||||
while ((e = qlist_pop(cpus))) {
|
||||
QDict *cpu, *props;
|
||||
int64_t core, node;
|
||||
|
||||
cpu = qobject_to_qdict(e);
|
||||
g_assert(qdict_haskey(cpu, "props"));
|
||||
props = qdict_get_qdict(cpu, "props");
|
||||
|
||||
g_assert(qdict_haskey(props, "node-id"));
|
||||
node = qdict_get_int(props, "node-id");
|
||||
g_assert(qdict_haskey(props, "core-id"));
|
||||
core = qdict_get_int(props, "core-id");
|
||||
|
||||
if (core >= 0 && core < 3) {
|
||||
g_assert_cmpint(node, ==, 0);
|
||||
} else if (core == 3) {
|
||||
g_assert_cmpint(node, ==, 1);
|
||||
} else {
|
||||
g_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
QDECREF(resp);
|
||||
qtest_end();
|
||||
g_free(cli);
|
||||
}
|
||||
|
||||
static void aarch64_numa_cpu(const void *data)
|
||||
{
|
||||
char *cli;
|
||||
QDict *resp;
|
||||
QList *cpus;
|
||||
const QObject *e;
|
||||
|
||||
cli = make_cli(data, "-smp 2 "
|
||||
"-numa node,nodeid=0 -numa node,nodeid=1 "
|
||||
"-numa cpu,node-id=1,thread-id=0 "
|
||||
"-numa cpu,node-id=0,thread-id=1");
|
||||
qtest_start(cli);
|
||||
cpus = get_cpus(&resp);
|
||||
g_assert(cpus);
|
||||
|
||||
while ((e = qlist_pop(cpus))) {
|
||||
QDict *cpu, *props;
|
||||
int64_t thread, node;
|
||||
|
||||
cpu = qobject_to_qdict(e);
|
||||
g_assert(qdict_haskey(cpu, "props"));
|
||||
props = qdict_get_qdict(cpu, "props");
|
||||
|
||||
g_assert(qdict_haskey(props, "node-id"));
|
||||
node = qdict_get_int(props, "node-id");
|
||||
g_assert(qdict_haskey(props, "thread-id"));
|
||||
thread = qdict_get_int(props, "thread-id");
|
||||
|
||||
if (thread == 0) {
|
||||
g_assert_cmpint(node, ==, 1);
|
||||
} else if (thread == 1) {
|
||||
g_assert_cmpint(node, ==, 0);
|
||||
} else {
|
||||
g_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
QDECREF(resp);
|
||||
qtest_end();
|
||||
g_free(cli);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *args = NULL;
|
||||
const char *arch = qtest_get_arch();
|
||||
|
||||
if (strcmp(arch, "aarch64") == 0) {
|
||||
args = "-machine virt";
|
||||
}
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
qtest_add_data_func("/numa/mon/default", args, test_mon_default);
|
||||
qtest_add_data_func("/numa/mon/cpus/explicit", args, test_mon_explicit);
|
||||
qtest_add_data_func("/numa/mon/cpus/partial", args, test_mon_partial);
|
||||
qtest_add_data_func("/numa/qmp/cpus/query-cpus", args, test_query_cpus);
|
||||
|
||||
if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) {
|
||||
qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu);
|
||||
}
|
||||
|
||||
if (!strcmp(arch, "ppc64")) {
|
||||
qtest_add_data_func("/numa/spapr/cpu/explicit", args, spapr_numa_cpu);
|
||||
}
|
||||
|
||||
if (!strcmp(arch, "aarch64")) {
|
||||
qtest_add_data_func("/numa/aarch64/cpu/explicit", args,
|
||||
aarch64_numa_cpu);
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
}
|
6
vl.c
6
vl.c
@ -4504,7 +4504,7 @@ int main(int argc, char **argv, char **envp)
|
||||
default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS);
|
||||
default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS);
|
||||
|
||||
parse_numa_opts(machine_class);
|
||||
parse_numa_opts(current_machine);
|
||||
|
||||
if (qemu_opts_foreach(qemu_find_opts("mon"),
|
||||
mon_init_func, NULL, NULL)) {
|
||||
@ -4560,7 +4560,7 @@ int main(int argc, char **argv, char **envp)
|
||||
current_machine->boot_order = boot_order;
|
||||
current_machine->cpu_model = cpu_model;
|
||||
|
||||
machine_class->init(current_machine);
|
||||
machine_run_board_init(current_machine);
|
||||
|
||||
realtime_init();
|
||||
|
||||
@ -4593,8 +4593,6 @@ int main(int argc, char **argv, char **envp)
|
||||
|
||||
cpu_synchronize_all_post_init();
|
||||
|
||||
numa_post_machine_init();
|
||||
|
||||
rom_reset_order_override();
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user