Commit 0042109a authored by Wanlong Gao's avatar Wanlong Gao Committed by Michael S. Tsirkin
Browse files

NUMA: convert -numa option to use OptsVisitor

parent 8c85901e
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -148,9 +148,10 @@ typedef struct node_info {
    DECLARE_BITMAP(node_cpu, MAX_CPUMASK_BITS);
} NodeInfo;
extern NodeInfo numa_info[MAX_NODES];
void numa_add(const char *optarg);
void set_numa_nodes(void);
void set_numa_modes(void);
extern QemuOptsList qemu_numa_opts;
int numa_init_func(QemuOpts *opts, void *opaque);

#define MAX_OPTION_ROMS 16
typedef struct QEMUOptionRom {
+70 −75
Original line number Diff line number Diff line
@@ -28,101 +28,96 @@
#include "qom/cpu.h"
#include "qemu/error-report.h"
#include "include/exec/cpu-common.h" /* for RAM_ADDR_FMT */

static void numa_node_parse_cpus(int nodenr, const char *cpus)
#include "qapi-visit.h"
#include "qapi/opts-visitor.h"
#include "qapi/dealloc-visitor.h"
#include "qapi/qmp/qerror.h"

QemuOptsList qemu_numa_opts = {
    .name = "numa",
    .implied_opt_name = "type",
    .head = QTAILQ_HEAD_INITIALIZER(qemu_numa_opts.head),
    .desc = { { 0 } } /* validated with OptsVisitor */
};

static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
{
    char *endptr;
    unsigned long long value, endvalue;
    uint16_t nodenr;
    uint16List *cpus = NULL;

    /* Empty CPU range strings will be considered valid, they will simply
     * not set any bit in the CPU bitmap.
     */
    if (!*cpus) {
        return;
    if (node->has_nodeid) {
        nodenr = node->nodeid;
    } else {
        nodenr = nb_numa_nodes;
    }

    if (parse_uint(cpus, &value, &endptr, 10) < 0) {
        goto error;
    if (nodenr >= MAX_NODES) {
        error_setg(errp, "Max number of NUMA nodes reached: %"
                   PRIu16 "\n", nodenr);
        return;
    }
    if (*endptr == '-') {
        if (parse_uint_full(endptr + 1, &endvalue, 10) < 0) {
            goto error;

    for (cpus = node->cpus; cpus; cpus = cpus->next) {
        if (cpus->value > MAX_CPUMASK_BITS) {
            error_setg(errp, "CPU number %" PRIu16 " is bigger than %d",
                       cpus->value, MAX_CPUMASK_BITS);
            return;
        }
    } else if (*endptr == '\0') {
        endvalue = value;
    } else {
        goto error;
        bitmap_set(numa_info[nodenr].node_cpu, cpus->value, 1);
    }

    if (endvalue >= MAX_CPUMASK_BITS) {
        endvalue = MAX_CPUMASK_BITS - 1;
        fprintf(stderr,
            "qemu: NUMA: A max of %d VCPUs are supported\n",
             MAX_CPUMASK_BITS);
    if (node->has_mem) {
        uint64_t mem_size = node->mem;
        const char *mem_str = qemu_opt_get(opts, "mem");
        /* Fix up legacy suffix-less format */
        if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) {
            mem_size <<= 20;
        }

    if (endvalue < value) {
        goto error;
        numa_info[nodenr].node_mem = mem_size;
    }

    bitmap_set(numa_info[nodenr].node_cpu, value, endvalue-value+1);
    return;

error:
    fprintf(stderr, "qemu: Invalid NUMA CPU range: %s\n", cpus);
    exit(1);
}

void numa_add(const char *optarg)
int numa_init_func(QemuOpts *opts, void *opaque)
{
    char option[128];
    char *endptr;
    unsigned long long nodenr;
    NumaOptions *object = NULL;
    Error *err = NULL;

    optarg = get_opt_name(option, 128, optarg, ',');
    if (*optarg == ',') {
        optarg++;
    {
        OptsVisitor *ov = opts_visitor_new(opts);
        visit_type_NumaOptions(opts_get_visitor(ov), &object, NULL, &err);
        opts_visitor_cleanup(ov);
    }
    if (!strcmp(option, "node")) {

        if (nb_numa_nodes >= MAX_NODES) {
            fprintf(stderr, "qemu: too many NUMA nodes\n");
            exit(1);
    if (err) {
        goto error;
    }

        if (get_param_value(option, 128, "nodeid", optarg) == 0) {
            nodenr = nb_numa_nodes;
        } else {
            if (parse_uint_full(option, &nodenr, 10) < 0) {
                fprintf(stderr, "qemu: Invalid NUMA nodeid: %s\n", option);
                exit(1);
    switch (object->kind) {
    case NUMA_OPTIONS_KIND_NODE:
        numa_node_parse(object->node, opts, &err);
        if (err) {
            goto error;
        }
        nb_numa_nodes++;
        break;
    default:
        abort();
    }

        if (nodenr >= MAX_NODES) {
            fprintf(stderr, "qemu: invalid NUMA nodeid: %llu\n", nodenr);
            exit(1);
        }
    return 0;

        if (get_param_value(option, 128, "mem", optarg) == 0) {
            numa_info[nodenr].node_mem = 0;
        } else {
            int64_t sval;
            sval = strtosz(option, &endptr);
            if (sval < 0 || *endptr) {
                fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg);
                exit(1);
            }
            numa_info[nodenr].node_mem = sval;
        }
        if (get_param_value(option, 128, "cpus", optarg) != 0) {
            numa_node_parse_cpus(nodenr, option);
        }
        nb_numa_nodes++;
    } else {
        fprintf(stderr, "Invalid -numa option: %s\n", option);
        exit(1);
error:
    qerror_report_err(err);
    error_free(err);

    if (object) {
        QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();
        visit_type_NumaOptions(qapi_dealloc_get_visitor(dv),
                               &object, NULL, NULL);
        qapi_dealloc_visitor_cleanup(dv);
    }

    return -1;
}

void set_numa_nodes(void)
+32 −0
Original line number Diff line number Diff line
@@ -3097,3 +3097,35 @@
              'btn'     : 'InputBtnEvent',
              'rel'     : 'InputMoveEvent',
              'abs'     : 'InputMoveEvent' } }

##
# @NumaOptions
#
# A discriminated record of NUMA options. (for OptsVisitor)
#
# Since 2.1
##
{ 'union': 'NumaOptions',
  'data': {
    'node': 'NumaNodeOptions' }}

##
# @NumaNodeOptions
#
# Create a guest NUMA node. (for OptsVisitor)
#
# @nodeid: #optional NUMA node ID (increase by 1 from 0 if omitted)
#
# @cpus: #optional VCPUs belonging to this node (assign VCPUS round-robin
#         if omitted)
#
# @mem: #optional memory size of this node (equally divide total memory among
#        nodes if omitted)
#
# Since: 2.1
##
{ 'type': 'NumaNodeOptions',
  'data': {
   '*nodeid': 'uint16',
   '*cpus':   ['uint16'],
   '*mem':    'size' }}
+10 −1
Original line number Diff line number Diff line
@@ -2938,6 +2938,7 @@ int main(int argc, char **argv, char **envp)
    qemu_add_opts(&qemu_realtime_opts);
    qemu_add_opts(&qemu_msg_opts);
    qemu_add_opts(&qemu_name_opts);
    qemu_add_opts(&qemu_numa_opts);

    runstate_init();

@@ -3133,7 +3134,10 @@ int main(int argc, char **argv, char **envp)
                }
                break;
            case QEMU_OPTION_numa:
                numa_add(optarg);
                opts = qemu_opts_parse(qemu_find_opts("numa"), optarg, 1);
                if (!opts) {
                    exit(1);
                }
                break;
            case QEMU_OPTION_display:
                display_type = select_display(optarg);
@@ -4303,6 +4307,11 @@ 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);

    if (qemu_opts_foreach(qemu_find_opts("numa"), numa_init_func,
                          NULL, 1) != 0) {
        exit(1);
    }

    set_numa_nodes();

    if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, 1) != 0) {