Patchwork [RFC,09/10] vl.c: Introduce QemuOpts-friendly "-numa-node" config option

login
register
mail settings
Submitter Eduardo Habkost
Date Jan. 11, 2013, 6:15 p.m.
Message ID <1357928108-21066-10-git-send-email-ehabkost@redhat.com>
Download mbox | patch
Permalink /patch/211408/
State New
Headers show

Comments

Eduardo Habkost - Jan. 11, 2013, 6:15 p.m.
This introduces both a "-numa-node" command-line option that is parsed
using a pure qemu_opts_parse() call, and a "numa-node" config file
section.

As the -numa option has a non-standard syntax, parse it manually and
translate it "numa-node" config options.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
---
 qemu-config.c   |  25 ++++++++++++
 qemu-options.hx |  50 ++++++++++++++++++++++-
 vl.c            | 120 ++++++++++++++++++++++++++++++++++++++++++--------------
 3 files changed, 164 insertions(+), 31 deletions(-)

Patch

diff --git a/qemu-config.c b/qemu-config.c
index 2188c3e..601dcda 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -647,6 +647,30 @@  static QemuOptsList qemu_object_opts = {
     },
 };
 
+static QemuOptsList qemu_numa_node_opts = {
+    .name = "numa-node",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_numa_node_opts.head),
+    .implied_opt_name = "type",
+    .desc = {
+        {
+            .name = "cpus",
+            .type = QEMU_OPT_STRING,
+            .help = "CPU range in the format N[-M]",
+        },
+        {
+            .name = "mem",
+            .type = QEMU_OPT_STRING,
+            .help = "RAM size for node, in MB",
+        },
+        {
+            .name = "nodeid",
+            .type = QEMU_OPT_NUMBER,
+            .help = "Node ID",
+        },
+        { /* end of list */ }
+    },
+};
+
 static QemuOptsList *vm_config_groups[32] = {
     &qemu_drive_opts,
     &qemu_chardev_opts,
@@ -664,6 +688,7 @@  static QemuOptsList *vm_config_groups[32] = {
     &qemu_sandbox_opts,
     &qemu_add_fd_opts,
     &qemu_object_opts,
+    &qemu_numa_node_opts,
     NULL,
 };
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 9df0cde..a1c1a87 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -99,8 +99,54 @@  DEF("numa", HAS_ARG, QEMU_OPTION_numa,
 STEXI
 @item -numa @var{opts}
 @findex -numa
-Simulate a multi node NUMA system. If mem and cpus are omitted, resources
-are split equally.
+Deprecated. See the @option{-numa-node} option.
+ETEXI
+
+DEF("numa-node", HAS_ARG, QEMU_OPTION_numa_node,
+    "-numa-node [nodeid=@var{nodeid}][,mem=@var{size}][,cpus=@var{cpuranges}\n", QEMU_ARCH_ALL)
+STEXI
+@item -numa-node @var{opts}
+@findex -numa-node
+
+Define a NUMA node.
+
+@table @option
+@item nodeid=@var{nodeid}
+Index of the NUMA node, starting with 0. If omitted, NUMA nodes will be defined
+in the order they appear.
+
+@item mem=@var{size}
+
+Sets the RAM size of the NUMA node (in MB, of no unit is specified). If size
+of all nodes is omitted, memory is split equally.
+
+@item cpus=@var{cpus}
+
+@var{cpus} is a list of CPU indexes or CPU index ranges in the format:
+@samp{@var{start}[-@var{end}]}, separated by commas or semicolons.
+
+Note that commas used in values in key=value options have to be escaped, using
+@samp{,,}.
+
+The @option{cpus} option may appear multiple times, to assign multiple CPUs or
+CPU ranges to a node.
+
+If no node has CPU ranges assigned, CPUs will be split equally between the
+nodes.
+@end table
+
+Examples:
+
+@example
+-numa-node nodeid=1,mem=1024,cpus=0,,2,,4,,6 \
+-numa-node nodeid=0,mem=1024,cpus=1,,3,,5,,7
+@end example
+
+@example
+-numa-node mem=1024,cpus=0-3,,8-11 \
+-numa-node mem=1024,cpus=4-7,cpus=12-15
+@end example
+
 ETEXI
 
 DEF("fda", HAS_ARG, QEMU_OPTION_fda,
diff --git a/vl.c b/vl.c
index d7337c1..14bf9b6 100644
--- a/vl.c
+++ b/vl.c
@@ -1052,7 +1052,7 @@  char *get_boot_devices_list(uint32_t *size)
     return list;
 }
 
-static void numa_node_parse_cpu_range(int nodenr, const char *cpus)
+static int numa_node_parse_cpu_range(int nodenr, const char *cpus)
 {
     char *endptr;
     unsigned long long value, endvalue;
@@ -1088,62 +1088,113 @@  static void numa_node_parse_cpu_range(int nodenr, const char *cpus)
     }
 
     bitmap_set(node_cpumask[nodenr], value, endvalue-value+1);
-    return;
+    return 0;
 
 error:
     fprintf(stderr, "qemu: Invalid NUMA CPU range: %s\n", cpus);
-    exit(1);
+    return -1;
 }
 
-static void numa_node_parse_cpus(int nodenr, const char *option)
+static int numa_node_parse_cpus(int nodenr, const char *option)
 {
     char **parts;
-    int i;
+    int i, r = 0;
 
     parts = g_strsplit_set(option, ",;", 0);
     for (i = 0; parts[i]; i++) {
-        numa_node_parse_cpu_range(nodenr, parts[i]);
+        r = numa_node_parse_cpu_range(nodenr, parts[i]);
+        if (r < 0) {
+            break;
+        }
     }
     g_strfreev(parts);
+    return r;
 }
 
-static void numa_node_add(const char *optarg)
+static int parse_numa_node_opt(const char *name, const char *value,
+                                void *opaque)
 {
-    char option[128];
-    char *endptr;
-    int nodenr;
+    uint64_t nodenr = *(uint64_t *)opaque;
+    if (!strcmp(name, "cpus")) {
+        return numa_node_parse_cpus(nodenr, value);
+    }
+    return 0;
+}
+
+static int parse_numa_node(QemuOpts *opts, void *opaque)
+{
+    uint64_t nodenr;
+    const char *memstr;
+    int64_t mem;
 
     if (nb_numa_nodes >= MAX_NODES) {
         fprintf(stderr, "qemu: too many NUMA nodes\n");
-        exit(1);
+        return -1;
+    }
+
+    nodenr = qemu_opt_get_number(opts, "nodeid", nb_numa_nodes);
+
+    if (nodenr >= MAX_NODES) {
+        fprintf(stderr, "qemu: invalid NUMA nodeid: %" PRIu64 "\n", nodenr);
+        return -1;
     }
 
-    if (get_param_value(option, 128, "nodeid", optarg) == 0) {
-        nodenr = nb_numa_nodes;
+    memstr = qemu_opt_get(opts, "mem");
+    if (memstr) {
+        char *endptr;
+        mem = strtosz(memstr, &endptr);
+        if (mem < 0 || *endptr) {
+            fprintf(stderr, "qemu: invalid numa mem size: %s\n", memstr);
+            return -1;
+        }
     } else {
-        nodenr = strtoull(option, NULL, 10);
+        mem = 0;
     }
 
-    if (nodenr >= MAX_NODES) {
-        fprintf(stderr, "qemu: invalid NUMA nodeid: %d\n", nodenr);
+    node_mem[nodenr] = mem;
+
+    /* There may be multiple "cpus" options set */
+    if (qemu_opt_foreach(opts, parse_numa_node_opt, &nodenr, 1) < 0) {
+        return -1;
+    }
+
+    nb_numa_nodes++;
+
+    return 0;
+}
+
+/* Parse legacy -numa option
+ *
+ * The option will be translated to a numa-node config option.
+ */
+static void parse_legacy_numa_node(const char *optarg)
+{
+    char option[128];
+    char value[128];
+    const char *p = optarg;
+    Error *error = NULL;
+    QemuOpts *opts = qemu_opts_create(qemu_find_opts("numa-node"),
+                                      NULL, 0, &error);
+    if (error) {
+        error_report("%s", error_get_pretty(error));
         exit(1);
     }
 
-    if (get_param_value(option, 128, "mem", optarg) == 0) {
-        node_mem[nodenr] = 0;
-    } else {
-        int64_t sval;
-        sval = strtosz(option, &endptr);
-        if (sval < 0 || *endptr) {
-            fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg);
+    while (*p) {
+        p = get_opt_name(option, 128, p, '=');
+        if (*p == '=') {
+            p++;
+        } else {
+            fprintf(stderr, "Invalid -numa option: %s\n", optarg);
             exit(1);
         }
-        node_mem[nodenr] = sval;
-    }
-    if (get_param_value(option, 128, "cpus", optarg) != 0) {
-        numa_node_parse_cpus(nodenr, option);
+
+        p = get_opt_value(value, 128, p);
+        if (*p == ',') {
+            p++;
+        }
+        qemu_opt_set(opts, option, value);
     }
-    nb_numa_nodes++;
 }
 
 static void numa_add(const char *optarg)
@@ -1155,7 +1206,7 @@  static void numa_add(const char *optarg)
         optarg++;
     }
     if (!strcmp(option, "node")) {
-        numa_node_add(optarg);
+        parse_legacy_numa_node(optarg);
     } else {
         fprintf(stderr, "Invalid -numa option: %s\n", option);
         exit(1);
@@ -2812,6 +2863,12 @@  int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_numa:
                 numa_add(optarg);
                 break;
+            case QEMU_OPTION_numa_node:
+                opts = qemu_opts_parse(qemu_find_opts("numa-node"), optarg, 0);
+                if (!opts) {
+                    exit(1);
+                }
+                break;
             case QEMU_OPTION_display:
                 display_type = select_display(optarg);
                 break;
@@ -3856,6 +3913,11 @@  int main(int argc, char **argv, char **envp)
 
     register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL);
 
+    if (qemu_opts_foreach(qemu_find_opts("numa-node"),
+                          parse_numa_node, NULL, 1) < 0) {
+        exit(1);
+    }
+
     if (nb_numa_nodes > 0) {
         int i;