mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 16:23:55 -06:00
numa: Allow setting NUMA distance for different NUMA nodes
This patch is going to add SLIT table support in QEMU, and provides additional option `dist` for command `-numa` to allow user set vNUMA distance by QEMU command. With this patch, when a user wants to create a guest that contains several vNUMA nodes and also wants to set distance among those nodes, the QEMU command would like: ``` -numa node,nodeid=0,cpus=0 \ -numa node,nodeid=1,cpus=1 \ -numa node,nodeid=2,cpus=2 \ -numa node,nodeid=3,cpus=3 \ -numa dist,src=0,dst=1,val=21 \ -numa dist,src=0,dst=2,val=31 \ -numa dist,src=0,dst=3,val=41 \ -numa dist,src=1,dst=2,val=21 \ -numa dist,src=1,dst=3,val=31 \ -numa dist,src=2,dst=3,val=21 \ ``` Signed-off-by: He Chen <he.chen@linux.intel.com> Message-Id: <1493260558-20728-1-git-send-email-he.chen@linux.intel.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Andrew Jones <drjones@redhat.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
This commit is contained in:
parent
4ed3d478c6
commit
0f203430dd
8 changed files with 215 additions and 5 deletions
137
numa.c
137
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,7 +141,7 @@ 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(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
uint16_t nodenr;
|
||||
uint16List *cpus = NULL;
|
||||
|
@ -212,6 +213,43 @@ 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;
|
||||
|
@ -229,12 +267,18 @@ 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(&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;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -294,6 +338,75 @@ static void validate_numa_cpus(void)
|
|||
g_free(seen_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 src, dst;
|
||||
bool is_asymmetrical = 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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 parse_numa_opts(MachineClass *mc)
|
||||
{
|
||||
int i;
|
||||
|
@ -390,6 +503,26 @@ void parse_numa_opts(MachineClass *mc)
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue