76daa3
From e803b7229ed6f1d31d2892ad545be259c2b61004 Mon Sep 17 00:00:00 2001
76daa3
From: Igor Mammedov <imammedo@redhat.com>
76daa3
Date: Mon, 29 May 2017 07:52:54 +0200
76daa3
Subject: [PATCH 11/13] numa: Allow setting NUMA distance for different NUMA
76daa3
 nodes
76daa3
76daa3
RH-Author: Igor Mammedov <imammedo@redhat.com>
76daa3
Message-id: <1496044376-228722-2-git-send-email-imammedo@redhat.com>
76daa3
Patchwork-id: 75427
76daa3
O-Subject: [RHEL7.4 qemu-kvm-rhev 1/3] numa: Allow setting NUMA distance for different NUMA nodes
76daa3
Bugzilla: 1395339
76daa3
RH-Acked-by: Andrew Jones <drjones@redhat.com>
76daa3
RH-Acked-by: Eduardo Habkost <ehabkost@redhat.com>
76daa3
RH-Acked-by: Marcel Apfelbaum <marcel@redhat.com>
76daa3
76daa3
From: He Chen <he.chen@linux.intel.com>
76daa3
76daa3
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1395339
76daa3
Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=13293161
76daa3
76daa3
This patch is going to add SLIT table support in QEMU, and provides
76daa3
additional option `dist` for command `-numa` to allow user set vNUMA
76daa3
distance by QEMU command.
76daa3
76daa3
With this patch, when a user wants to create a guest that contains
76daa3
several vNUMA nodes and also wants to set distance among those nodes,
76daa3
the QEMU command would like:
76daa3
76daa3
```
76daa3
-numa node,nodeid=0,cpus=0 \
76daa3
-numa node,nodeid=1,cpus=1 \
76daa3
-numa node,nodeid=2,cpus=2 \
76daa3
-numa node,nodeid=3,cpus=3 \
76daa3
-numa dist,src=0,dst=1,val=21 \
76daa3
-numa dist,src=0,dst=2,val=31 \
76daa3
-numa dist,src=0,dst=3,val=41 \
76daa3
-numa dist,src=1,dst=2,val=21 \
76daa3
-numa dist,src=1,dst=3,val=31 \
76daa3
-numa dist,src=2,dst=3,val=21 \
76daa3
```
76daa3
76daa3
Signed-off-by: He Chen <he.chen@linux.intel.com>
76daa3
Message-Id: <1493260558-20728-1-git-send-email-he.chen@linux.intel.com>
76daa3
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
76daa3
Reviewed-by: Andrew Jones <drjones@redhat.com>
76daa3
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
76daa3
(cherry picked from commit 0f203430dd88cc6270310956ace58aca639edb59)
76daa3
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
76daa3
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
76daa3
---
76daa3
 hw/acpi/aml-build.c         |  26 +++++++++
76daa3
 hw/i386/acpi-build.c        |   4 ++
76daa3
 include/hw/acpi/aml-build.h |   1 +
76daa3
 include/sysemu/numa.h       |   2 +
76daa3
 include/sysemu/sysemu.h     |   4 ++
76daa3
 numa.c                      | 137 +++++++++++++++++++++++++++++++++++++++++++-
76daa3
 qapi-schema.json            |  30 +++++++++-
76daa3
 qemu-options.hx             |  16 +++++-
76daa3
 8 files changed, 215 insertions(+), 5 deletions(-)
76daa3
76daa3
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
76daa3
index c6f2032..be496c8 100644
76daa3
--- a/hw/acpi/aml-build.c
76daa3
+++ b/hw/acpi/aml-build.c
76daa3
@@ -24,6 +24,7 @@
76daa3
 #include "hw/acpi/aml-build.h"
76daa3
 #include "qemu/bswap.h"
76daa3
 #include "qemu/bitops.h"
76daa3
+#include "sysemu/numa.h"
76daa3
 
76daa3
 static GArray *build_alloc_array(void)
76daa3
 {
76daa3
@@ -1609,3 +1610,28 @@ void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
76daa3
     numamem->base_addr = cpu_to_le64(base);
76daa3
     numamem->range_length = cpu_to_le64(len);
76daa3
 }
76daa3
+
76daa3
+/*
76daa3
+ * ACPI spec 5.2.17 System Locality Distance Information Table
76daa3
+ * (Revision 2.0 or later)
76daa3
+ */
76daa3
+void build_slit(GArray *table_data, BIOSLinker *linker)
76daa3
+{
76daa3
+    int slit_start, i, j;
76daa3
+    slit_start = table_data->len;
76daa3
+
76daa3
+    acpi_data_push(table_data, sizeof(AcpiTableHeader));
76daa3
+
76daa3
+    build_append_int_noprefix(table_data, nb_numa_nodes, 8);
76daa3
+    for (i = 0; i < nb_numa_nodes; i++) {
76daa3
+        for (j = 0; j < nb_numa_nodes; j++) {
76daa3
+            assert(numa_info[i].distance[j]);
76daa3
+            build_append_int_noprefix(table_data, numa_info[i].distance[j], 1);
76daa3
+        }
76daa3
+    }
76daa3
+
76daa3
+    build_header(linker, table_data,
76daa3
+                 (void *)(table_data->data + slit_start),
76daa3
+                 "SLIT",
76daa3
+                 table_data->len - slit_start, 1, NULL, NULL);
76daa3
+}
76daa3
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
76daa3
index 2073108..2458ebc 100644
76daa3
--- a/hw/i386/acpi-build.c
76daa3
+++ b/hw/i386/acpi-build.c
76daa3
@@ -2678,6 +2678,10 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
76daa3
     if (pcms->numa_nodes) {
76daa3
         acpi_add_table(table_offsets, tables_blob);
76daa3
         build_srat(tables_blob, tables->linker, machine);
76daa3
+        if (have_numa_distance) {
76daa3
+            acpi_add_table(table_offsets, tables_blob);
76daa3
+            build_slit(tables_blob, tables->linker);
76daa3
+        }
76daa3
     }
76daa3
     if (acpi_get_mcfg(&mcfg)) {
76daa3
         acpi_add_table(table_offsets, tables_blob);
76daa3
diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
76daa3
index 00c21f1..329a0d0 100644
76daa3
--- a/include/hw/acpi/aml-build.h
76daa3
+++ b/include/hw/acpi/aml-build.h
76daa3
@@ -389,4 +389,5 @@ GCC_FMT_ATTR(2, 3);
76daa3
 void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
76daa3
                        uint64_t len, int node, MemoryAffinityFlags flags);
76daa3
 
76daa3
+void build_slit(GArray *table_data, BIOSLinker *linker);
76daa3
 #endif
76daa3
diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h
76daa3
index 8f09dcf..0ea1bc0 100644
76daa3
--- a/include/sysemu/numa.h
76daa3
+++ b/include/sysemu/numa.h
76daa3
@@ -8,6 +8,7 @@
76daa3
 #include "hw/boards.h"
76daa3
 
76daa3
 extern int nb_numa_nodes;   /* Number of NUMA nodes */
76daa3
+extern bool have_numa_distance;
76daa3
 
76daa3
 struct numa_addr_range {
76daa3
     ram_addr_t mem_start;
76daa3
@@ -21,6 +22,7 @@ typedef struct node_info {
76daa3
     struct HostMemoryBackend *node_memdev;
76daa3
     bool present;
76daa3
     QLIST_HEAD(, numa_addr_range) addr; /* List to store address ranges */
76daa3
+    uint8_t distance[MAX_NODES];
76daa3
 } NodeInfo;
76daa3
 
76daa3
 extern NodeInfo numa_info[MAX_NODES];
76daa3
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
76daa3
index 22c0460..1fd51a8 100644
76daa3
--- a/include/sysemu/sysemu.h
76daa3
+++ b/include/sysemu/sysemu.h
76daa3
@@ -170,6 +170,10 @@ extern int mem_prealloc;
76daa3
 
76daa3
 #define MAX_NODES 128
76daa3
 #define NUMA_NODE_UNASSIGNED MAX_NODES
76daa3
+#define NUMA_DISTANCE_MIN         10
76daa3
+#define NUMA_DISTANCE_DEFAULT     20
76daa3
+#define NUMA_DISTANCE_MAX         254
76daa3
+#define NUMA_DISTANCE_UNREACHABLE 255
76daa3
 
76daa3
 #define MAX_OPTION_ROMS 16
76daa3
 typedef struct QEMUOptionRom {
76daa3
diff --git a/numa.c b/numa.c
76daa3
index 80d9029..1ca2a11 100644
76daa3
--- a/numa.c
76daa3
+++ b/numa.c
76daa3
@@ -51,6 +51,7 @@ static int max_numa_nodeid; /* Highest specified NUMA node ID, plus one.
76daa3
                              * For all nodes, nodeid < max_numa_nodeid
76daa3
                              */
76daa3
 int nb_numa_nodes;
76daa3
+bool have_numa_distance;
76daa3
 NodeInfo numa_info[MAX_NODES];
76daa3
 
76daa3
 void numa_set_mem_node_id(ram_addr_t addr, uint64_t size, uint32_t node)
76daa3
@@ -140,7 +141,7 @@ uint32_t numa_get_node(ram_addr_t addr, Error **errp)
76daa3
     return -1;
76daa3
 }
76daa3
 
76daa3
-static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
76daa3
+static void parse_numa_node(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
76daa3
 {
76daa3
     uint16_t nodenr;
76daa3
     uint16List *cpus = NULL;
76daa3
@@ -212,6 +213,43 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
76daa3
     max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1);
76daa3
 }
76daa3
 
76daa3
+static void parse_numa_distance(NumaDistOptions *dist, Error **errp)
76daa3
+{
76daa3
+    uint16_t src = dist->src;
76daa3
+    uint16_t dst = dist->dst;
76daa3
+    uint8_t val = dist->val;
76daa3
+
76daa3
+    if (src >= MAX_NODES || dst >= MAX_NODES) {
76daa3
+        error_setg(errp,
76daa3
+                   "Invalid node %" PRIu16
76daa3
+                   ", max possible could be %" PRIu16,
76daa3
+                   MAX(src, dst), MAX_NODES);
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    if (!numa_info[src].present || !numa_info[dst].present) {
76daa3
+        error_setg(errp, "Source/Destination NUMA node is missing. "
76daa3
+                   "Please use '-numa node' option to declare it first.");
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    if (val < NUMA_DISTANCE_MIN) {
76daa3
+        error_setg(errp, "NUMA distance (%" PRIu8 ") is invalid, "
76daa3
+                   "it shouldn't be less than %d.",
76daa3
+                   val, NUMA_DISTANCE_MIN);
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    if (src == dst && val != NUMA_DISTANCE_MIN) {
76daa3
+        error_setg(errp, "Local distance of node %d should be %d.",
76daa3
+                   src, NUMA_DISTANCE_MIN);
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    numa_info[src].distance[dst] = val;
76daa3
+    have_numa_distance = true;
76daa3
+}
76daa3
+
76daa3
 static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
76daa3
 {
76daa3
     NumaOptions *object = NULL;
76daa3
@@ -229,12 +267,18 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
76daa3
 
76daa3
     switch (object->type) {
76daa3
     case NUMA_OPTIONS_TYPE_NODE:
76daa3
-        numa_node_parse(&object->u.node, opts, &err;;
76daa3
+        parse_numa_node(&object->u.node, opts, &err;;
76daa3
         if (err) {
76daa3
             goto end;
76daa3
         }
76daa3
         nb_numa_nodes++;
76daa3
         break;
76daa3
+    case NUMA_OPTIONS_TYPE_DIST:
76daa3
+        parse_numa_distance(&object->u.dist, &err;;
76daa3
+        if (err) {
76daa3
+            goto end;
76daa3
+        }
76daa3
+        break;
76daa3
     default:
76daa3
         abort();
76daa3
     }
76daa3
@@ -294,6 +338,75 @@ static void validate_numa_cpus(void)
76daa3
     g_free(seen_cpus);
76daa3
 }
76daa3
 
76daa3
+/* If all node pair distances are symmetric, then only distances
76daa3
+ * in one direction are enough. If there is even one asymmetric
76daa3
+ * pair, though, then all distances must be provided. The
76daa3
+ * distance from a node to itself is always NUMA_DISTANCE_MIN,
76daa3
+ * so providing it is never necessary.
76daa3
+ */
76daa3
+static void validate_numa_distance(void)
76daa3
+{
76daa3
+    int src, dst;
76daa3
+    bool is_asymmetrical = false;
76daa3
+
76daa3
+    for (src = 0; src < nb_numa_nodes; src++) {
76daa3
+        for (dst = src; dst < nb_numa_nodes; dst++) {
76daa3
+            if (numa_info[src].distance[dst] == 0 &&
76daa3
+                numa_info[dst].distance[src] == 0) {
76daa3
+                if (src != dst) {
76daa3
+                    error_report("The distance between node %d and %d is "
76daa3
+                                 "missing, at least one distance value "
76daa3
+                                 "between each nodes should be provided.",
76daa3
+                                 src, dst);
76daa3
+                    exit(EXIT_FAILURE);
76daa3
+                }
76daa3
+            }
76daa3
+
76daa3
+            if (numa_info[src].distance[dst] != 0 &&
76daa3
+                numa_info[dst].distance[src] != 0 &&
76daa3
+                numa_info[src].distance[dst] !=
76daa3
+                numa_info[dst].distance[src]) {
76daa3
+                is_asymmetrical = true;
76daa3
+            }
76daa3
+        }
76daa3
+    }
76daa3
+
76daa3
+    if (is_asymmetrical) {
76daa3
+        for (src = 0; src < nb_numa_nodes; src++) {
76daa3
+            for (dst = 0; dst < nb_numa_nodes; dst++) {
76daa3
+                if (src != dst && numa_info[src].distance[dst] == 0) {
76daa3
+                    error_report("At least one asymmetrical pair of "
76daa3
+                            "distances is given, please provide distances "
76daa3
+                            "for both directions of all node pairs.");
76daa3
+                    exit(EXIT_FAILURE);
76daa3
+                }
76daa3
+            }
76daa3
+        }
76daa3
+    }
76daa3
+}
76daa3
+
76daa3
+static void complete_init_numa_distance(void)
76daa3
+{
76daa3
+    int src, dst;
76daa3
+
76daa3
+    /* Fixup NUMA distance by symmetric policy because if it is an
76daa3
+     * asymmetric distance table, it should be a complete table and
76daa3
+     * there would not be any missing distance except local node, which
76daa3
+     * is verified by validate_numa_distance above.
76daa3
+     */
76daa3
+    for (src = 0; src < nb_numa_nodes; src++) {
76daa3
+        for (dst = 0; dst < nb_numa_nodes; dst++) {
76daa3
+            if (numa_info[src].distance[dst] == 0) {
76daa3
+                if (src == dst) {
76daa3
+                    numa_info[src].distance[dst] = NUMA_DISTANCE_MIN;
76daa3
+                } else {
76daa3
+                    numa_info[src].distance[dst] = numa_info[dst].distance[src];
76daa3
+                }
76daa3
+            }
76daa3
+        }
76daa3
+    }
76daa3
+}
76daa3
+
76daa3
 void parse_numa_opts(MachineClass *mc)
76daa3
 {
76daa3
     int i;
76daa3
@@ -390,6 +503,26 @@ void parse_numa_opts(MachineClass *mc)
76daa3
         }
76daa3
 
76daa3
         validate_numa_cpus();
76daa3
+
76daa3
+        /* QEMU needs at least all unique node pair distances to build
76daa3
+         * the whole NUMA distance table. QEMU treats the distance table
76daa3
+         * as symmetric by default, i.e. distance A->B == distance B->A.
76daa3
+         * Thus, QEMU is able to complete the distance table
76daa3
+         * initialization even though only distance A->B is provided and
76daa3
+         * distance B->A is not. QEMU knows the distance of a node to
76daa3
+         * itself is always 10, so A->A distances may be omitted. When
76daa3
+         * the distances of two nodes of a pair differ, i.e. distance
76daa3
+         * A->B != distance B->A, then that means the distance table is
76daa3
+         * asymmetric. In this case, the distances for both directions
76daa3
+         * of all node pairs are required.
76daa3
+         */
76daa3
+        if (have_numa_distance) {
76daa3
+            /* Validate enough NUMA distance information was provided. */
76daa3
+            validate_numa_distance();
76daa3
+
76daa3
+            /* Validation succeeded, now fill in any missing distances. */
76daa3
+            complete_init_numa_distance();
76daa3
+        }
76daa3
     } else {
76daa3
         numa_set_mem_node_id(0, ram_size, 0);
76daa3
     }
76daa3
diff --git a/qapi-schema.json b/qapi-schema.json
76daa3
index cefbaf5..fa71429 100644
76daa3
--- a/qapi-schema.json
76daa3
+++ b/qapi-schema.json
76daa3
@@ -5695,10 +5695,14 @@
76daa3
 ##
76daa3
 # @NumaOptionsType:
76daa3
 #
76daa3
+# @node: NUMA nodes configuration
76daa3
+#
76daa3
+# @dist: NUMA distance configuration (since 2.10)
76daa3
+#
76daa3
 # Since: 2.1
76daa3
 ##
76daa3
 { 'enum': 'NumaOptionsType',
76daa3
-  'data': [ 'node' ] }
76daa3
+  'data': [ 'node', 'dist' ] }
76daa3
 
76daa3
 ##
76daa3
 # @NumaOptions:
76daa3
@@ -5711,7 +5715,8 @@
76daa3
   'base': { 'type': 'NumaOptionsType' },
76daa3
   'discriminator': 'type',
76daa3
   'data': {
76daa3
-    'node': 'NumaNodeOptions' }}
76daa3
+    'node': 'NumaNodeOptions',
76daa3
+    'dist': 'NumaDistOptions' }}
76daa3
 
76daa3
 ##
76daa3
 # @NumaNodeOptions:
76daa3
@@ -5740,6 +5745,27 @@
76daa3
    '*memdev': 'str' }}
76daa3
 
76daa3
 ##
76daa3
+# @NumaDistOptions:
76daa3
+#
76daa3
+# Set the distance between 2 NUMA nodes.
76daa3
+#
76daa3
+# @src: source NUMA node.
76daa3
+#
76daa3
+# @dst: destination NUMA node.
76daa3
+#
76daa3
+# @val: NUMA distance from source node to destination node.
76daa3
+#       When a node is unreachable from another node, set the distance
76daa3
+#       between them to 255.
76daa3
+#
76daa3
+# Since: 2.10
76daa3
+##
76daa3
+{ 'struct': 'NumaDistOptions',
76daa3
+  'data': {
76daa3
+   'src': 'uint16',
76daa3
+   'dst': 'uint16',
76daa3
+   'val': 'uint8' }}
76daa3
+
76daa3
+##
76daa3
 # @HostMemPolicy:
76daa3
 #
76daa3
 # Host memory policy types
76daa3
diff --git a/qemu-options.hx b/qemu-options.hx
76daa3
index b5544ac..2f25547 100644
76daa3
--- a/qemu-options.hx
76daa3
+++ b/qemu-options.hx
76daa3
@@ -139,12 +139,15 @@ ETEXI
76daa3
 
76daa3
 DEF("numa", HAS_ARG, QEMU_OPTION_numa,
76daa3
     "-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n"
76daa3
-    "-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n", QEMU_ARCH_ALL)
76daa3
+    "-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n"
76daa3
+    "-numa dist,src=source,dst=destination,val=distance\n", QEMU_ARCH_ALL)
76daa3
 STEXI
76daa3
 @item -numa node[,mem=@var{size}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}]
76daa3
 @itemx -numa node[,memdev=@var{id}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}]
76daa3
+@itemx -numa dist,src=@var{source},dst=@var{destination},val=@var{distance}
76daa3
 @findex -numa
76daa3
 Define a NUMA node and assign RAM and VCPUs to it.
76daa3
+Set the NUMA distance from a source node to a destination node.
76daa3
 
76daa3
 @var{firstcpu} and @var{lastcpu} are CPU indexes. Each
76daa3
 @samp{cpus} option represent a contiguous range of CPU indexes
76daa3
@@ -167,6 +170,17 @@ split equally between them.
76daa3
 @samp{mem} and @samp{memdev} are mutually exclusive. Furthermore,
76daa3
 if one node uses @samp{memdev}, all of them have to use it.
76daa3
 
76daa3
+@var{source} and @var{destination} are NUMA node IDs.
76daa3
+@var{distance} is the NUMA distance from @var{source} to @var{destination}.
76daa3
+The distance from a node to itself is always 10. If any pair of nodes is
76daa3
+given a distance, then all pairs must be given distances. Although, when
76daa3
+distances are only given in one direction for each pair of nodes, then
76daa3
+the distances in the opposite directions are assumed to be the same. If,
76daa3
+however, an asymmetrical pair of distances is given for even one node
76daa3
+pair, then all node pairs must be provided distance values for both
76daa3
+directions, even when they are symmetrical. When a node is unreachable
76daa3
+from another node, set the pair's distance to 255.
76daa3
+
76daa3
 Note that the -@option{numa} option doesn't allocate any of the
76daa3
 specified resources, it just assigns existing resources to NUMA
76daa3
 nodes. This means that one still has to use the @option{-m},
76daa3
-- 
76daa3
1.8.3.1
76daa3