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@xxxxxxxxxx> --- qemu-config.c | 25 ++++++++++++ qemu-options.hx | 50 ++++++++++++++++++++++- vl.c | 120 ++++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 164 insertions(+), 31 deletions(-) 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; -- 1.7.11.7 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list