NUMA queue, 2018-05-30

* New command-line option: --preconfig
   This option allows pausing QEMU and allow the configuration
   using QMP commands before running board initialization code.
 * New QMP set-numa-node, now made possible because of --preconfig
 * Small update on -numa error messages
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCAAGBQJbDy2jAAoJECgHk2+YTcWm8OgP/As0judZ7JwT5F5fR4nxtQPr
 EODeEI+e/IahGH58Bafx24Fvxy0HZIgwqBDnPgclA8d3ebwmDQIejgk5/00zq2xv
 cktRpzXGSBGJCVNctVz8xqN91m0lPtVgeRvXUJPn3hthXRSLO4p0vbyOW8g/C+O2
 +dEcGqifAQatyxs9gYmmWoUia8zle/v1bd2v/x/DwiFW9cd47yDr3+ChHh6+nx0W
 uh2zymD+ykVeNJ9WSxc3k8zTzQnuxbDK1fNbZsztk9KQDMWG3+u7KRNoJ86/7hKL
 RMUfRCUMBgemuZsrFF8wSsha27e9VgCw4oR8dQ5AnTgMjK6nch/839XuFWh7j5qu
 ntS4vt1v6IczdflKXX7IUILAgvVffLh2SCGWlKq9Q1eKo+CZTCea+iSbb2QobAE+
 9TyuqXVKCsGKLoqkbK39d1UjZFhDQJhMsyuOHreKEINmJRmLI5qrNbEaaysb+mMB
 DnFE1NiWjE+6X4w9U1l8lnKNUakOPNDG8cbgtuiEKDC9e3OZeiS8yyPLaQajELSR
 Ig7N2GxrLMSfS3LNMSwkACILzVeu03aFoHyCPrpUqD8mpxidN0mmFVup8dawtkJ1
 A5dHrKDmzlOT6DivJOgT8/Os/wMo3ErPcvyu69rTUO9PKSbwcFPwi6bMMDzgb4xO
 kAles9X7HrHxSeJiggRW
 =RHIj
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/ehabkost/tags/numa-next-pull-request' into staging

NUMA queue, 2018-05-30

* New command-line option: --preconfig
  This option allows pausing QEMU and allow the configuration
  using QMP commands before running board initialization code.
* New QMP set-numa-node, now made possible because of --preconfig
* Small update on -numa error messages

# gpg: Signature made Thu 31 May 2018 00:02:59 BST
# gpg:                using RSA key 2807936F984DC5A6
# gpg: Good signature from "Eduardo Habkost <ehabkost@redhat.com>"
# Primary key fingerprint: 5A32 2FD5 ABC4 D3DB ACCF  D1AA 2807 936F 984D C5A6

* remotes/ehabkost/tags/numa-next-pull-request:
  tests: functional tests for QMP command set-numa-node
  qmp: add set-numa-node command
  qmp: permit query-hotpluggable-cpus in preconfig state
  tests: extend qmp test with preconfig checks
  cli: add --preconfig option
  tests: qapi-schema tests for allow-preconfig
  qapi: introduce new cmd option "allow-preconfig"
  hmp: disable monitor in preconfig state
  qapi: introduce preconfig runstate
  numa: split out NumaOptions parsing into set_numa_options()
  numa: postpone options post-processing till machine_run_board_init()
  numa: clarify error message when node index is out of range in -numa dist, ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-05-31 11:12:36 +01:00
commit a3ac12fba0
35 changed files with 402 additions and 80 deletions

View File

@ -559,7 +559,7 @@ following example objects:
Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
'*returns': TYPE-NAME, '*boxed': true,
'*gen': false, '*success-response': false,
'*allow-oob': true }
'*allow-oob': true, '*allow-preconfig': true }
Commands are defined by using a dictionary containing several members,
where three members are most common. The 'command' member is a
@ -683,6 +683,15 @@ OOB command handlers must satisfy the following conditions:
If in doubt, do not implement OOB execution support.
A command may use the optional 'allow-preconfig' key to permit its execution
at early runtime configuration stage (preconfig runstate).
If not specified then a command defaults to 'allow-preconfig': false.
An example of declaring a command that is enabled during preconfig:
{ 'command': 'qmp_capabilities',
'data': { '*enable': [ 'QMPCapability' ] },
'allow-preconfig': true }
=== Events ===
Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,

View File

@ -737,7 +737,7 @@ static char *cpu_slot_to_string(const CPUArchId *cpu)
return g_string_free(s, false);
}
static void machine_numa_finish_init(MachineState *machine)
static void machine_numa_finish_cpu_init(MachineState *machine)
{
int i;
bool default_mapping;
@ -792,7 +792,8 @@ void machine_run_board_init(MachineState *machine)
MachineClass *machine_class = MACHINE_GET_CLASS(machine);
if (nb_numa_nodes) {
machine_numa_finish_init(machine);
numa_complete_configuration(machine);
machine_numa_finish_cpu_init(machine);
}
/* If the machine supports the valid_cpu_types check and the user

View File

@ -23,6 +23,7 @@ typedef enum QmpCommandOptions
QCO_NO_OPTIONS = 0x0,
QCO_NO_SUCCESS_RESP = (1U << 0),
QCO_ALLOW_OOB = (1U << 1),
QCO_ALLOW_PRECONFIG = (1U << 2),
} QmpCommandOptions;
typedef struct QmpCommand

View File

@ -22,7 +22,9 @@ struct NumaNodeMem {
};
extern NodeInfo numa_info[MAX_NODES];
int parse_numa(void *opaque, QemuOpts *opts, Error **errp);
void parse_numa_opts(MachineState *ms);
void numa_complete_configuration(MachineState *ms);
void query_numa_node_mem(NumaNodeMem node_mem[]);
extern QemuOptsList qemu_numa_opts;
void numa_legacy_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,

View File

@ -66,6 +66,7 @@ typedef enum WakeupReason {
QEMU_WAKEUP_REASON_OTHER,
} WakeupReason;
void qemu_exit_preconfig_request(void);
void qemu_system_reset_request(ShutdownCause reason);
void qemu_system_suspend_request(void);
void qemu_register_suspend_notifier(Notifier *notifier);

View File

@ -1179,8 +1179,7 @@ static void monitor_init_qmp_commands(void)
qmp_init_marshal(&qmp_commands);
qmp_register_command(&qmp_commands, "query-qmp-schema",
qmp_query_qmp_schema,
QCO_NO_OPTIONS);
qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG);
qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
QCO_NO_OPTIONS);
qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
@ -1190,7 +1189,7 @@ static void monitor_init_qmp_commands(void)
QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
}
static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap)
@ -3371,6 +3370,12 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline)
trace_handle_hmp_command(mon, cmdline);
if (runstate_check(RUN_STATE_PRECONFIG)) {
monitor_printf(mon, "HMP not available in preconfig state, "
"use QMP instead\n");
return;
}
cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
if (!cmd) {
return;

75
numa.c
View File

@ -141,9 +141,8 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp)
uint8_t val = dist->val;
if (src >= MAX_NODES || dst >= MAX_NODES) {
error_setg(errp,
"Invalid node %d, max possible could be %d",
MAX(src, dst), MAX_NODES);
error_setg(errp, "Parameter '%s' expects an integer between 0 and %d",
src >= MAX_NODES ? "src" : "dst", MAX_NODES - 1);
return;
}
@ -170,28 +169,11 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp)
have_numa_distance = true;
}
static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
static
void set_numa_options(MachineState *ms, NumaOptions *object, Error **errp)
{
NumaOptions *object = NULL;
MachineState *ms = opaque;
Error *err = NULL;
{
Visitor *v = opts_visitor_new(opts);
visit_type_NumaOptions(v, NULL, &object, &err);
visit_free(v);
}
if (err) {
goto end;
}
/* Fix up legacy suffix-less format */
if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) {
const char *mem_str = qemu_opt_get(opts, "mem");
qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem);
}
switch (object->type) {
case NUMA_OPTIONS_TYPE_NODE:
parse_numa_node(ms, &object->u.node, &err);
@ -224,6 +206,31 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
abort();
}
end:
error_propagate(errp, err);
}
int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
{
NumaOptions *object = NULL;
MachineState *ms = MACHINE(opaque);
Error *err = NULL;
Visitor *v = opts_visitor_new(opts);
visit_type_NumaOptions(v, NULL, &object, &err);
visit_free(v);
if (err) {
goto end;
}
/* Fix up legacy suffix-less format */
if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) {
const char *mem_str = qemu_opt_get(opts, "mem");
qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem);
}
set_numa_options(ms, object, &err);
end:
qapi_free_NumaOptions(object);
if (err) {
@ -339,15 +346,11 @@ void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
nodes[i].node_mem = size - usedmem;
}
void parse_numa_opts(MachineState *ms)
void numa_complete_configuration(MachineState *ms)
{
int i;
MachineClass *mc = MACHINE_GET_CLASS(ms);
if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) {
exit(1);
}
/*
* If memory hotplug is enabled (slots > 0) but without '-numa'
* options explicitly on CLI, guestes will break.
@ -434,6 +437,24 @@ void parse_numa_opts(MachineState *ms)
}
}
void parse_numa_opts(MachineState *ms)
{
if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) {
exit(1);
}
}
void qmp_set_numa_node(NumaOptions *cmd, Error **errp)
{
if (!runstate_check(RUN_STATE_PRECONFIG)) {
error_setg(errp, "The command is permitted only in '%s' state",
RunState_str(RUN_STATE_PRECONFIG));
return;
}
set_numa_options(MACHINE(qdev_get_machine()), cmd, errp);
}
void numa_cpu_pre_plug(const CPUArchId *slot, DeviceState *dev, Error **errp)
{
int node_id = object_property_get_int(OBJECT(dev), "node-id", &error_abort);

View File

@ -262,13 +262,16 @@
# @allow-oob: whether the command allows out-of-band execution.
# (Since: 2.12)
#
# @allow-preconfig: command can be executed in preconfig runstate,
# default: false (Since 3.0)
#
# TODO: @success-response (currently irrelevant, because it's QGA, not QMP)
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoCommand',
'data': { 'arg-type': 'str', 'ret-type': 'str',
'allow-oob': 'bool' } }
'allow-oob': 'bool', 'allow-preconfig': 'bool' } }
##
# @SchemaInfoEvent:

View File

@ -37,7 +37,8 @@
#
##
{ 'command': 'qmp_capabilities',
'data': { '*enable': [ 'QMPCapability' ] } }
'data': { '*enable': [ 'QMPCapability' ] },
'allow-preconfig': true }
##
# @QMPCapability:
@ -155,7 +156,8 @@
# Note: This example has been shortened as the real response is too long.
#
##
{ 'command': 'query-commands', 'returns': ['CommandInfo'] }
{ 'command': 'query-commands', 'returns': ['CommandInfo'],
'allow-preconfig': true }
##
# @LostTickPolicy:
@ -1242,6 +1244,29 @@
##
{ 'command': 'cont' }
##
# @exit-preconfig:
#
# Exit from "preconfig" state
#
# This command makes QEMU exit the preconfig state and proceed with
# VM initialization using configuration data provided on the command line
# and via the QMP monitor during the preconfig state. The command is only
# available during the preconfig state (i.e. when the --preconfig command
# line option was in use).
#
# Since 3.0
#
# Returns: nothing
#
# Example:
#
# -> { "execute": "exit-preconfig" }
# <- { "return": {} }
#
##
{ 'command': 'exit-preconfig', 'allow-preconfig': true }
##
# @system_wakeup:
#
@ -2648,7 +2673,8 @@
#
##
{'command': 'query-command-line-options', 'data': { '*option': 'str' },
'returns': ['CommandLineOptionInfo'] }
'returns': ['CommandLineOptionInfo'],
'allow-preconfig': true }
##
# @X86CPURegister32:
@ -3259,7 +3285,8 @@
# ]}
#
##
{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'] }
{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'],
'allow-preconfig': true }
##
# @GuidInfo:
@ -3483,3 +3510,17 @@
##
{ 'command': 'x-oob-test', 'data' : { 'lock': 'bool' },
'allow-oob': true }
##
# @set-numa-node:
#
# Runtime equivalent of '-numa' CLI option, available at
# preconfigure stage to configure numa mapping before initializing
# machine.
#
# Since 3.0
##
{ 'command': 'set-numa-node', 'boxed': true,
'data': 'NumaOptions',
'allow-preconfig': true
}

View File

@ -18,6 +18,7 @@
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qbool.h"
#include "sysemu/sysemu.h"
QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
{
@ -101,6 +102,13 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
return NULL;
}
if (runstate_check(RUN_STATE_PRECONFIG) &&
!(cmd->options & QCO_ALLOW_PRECONFIG)) {
error_setg(errp, "The command '%s' isn't permitted in '%s' state",
cmd->name, RunState_str(RUN_STATE_PRECONFIG));
return NULL;
}
if (!qdict_haskey(dict, "arguments")) {
args = qdict_new();
} else {

View File

@ -49,12 +49,15 @@
# @colo: guest is paused to save/restore VM state under colo checkpoint,
# VM can not get into this state unless colo capability is enabled
# for migration. (since 2.8)
# @preconfig: QEMU is paused before board specific init callback is executed.
# The state is reachable only if the --preconfig CLI option is used.
# (Since 3.0)
##
{ 'enum': 'RunState',
'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused',
'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm',
'running', 'save-vm', 'shutdown', 'suspended', 'watchdog',
'guest-panicked', 'colo' ] }
'guest-panicked', 'colo', 'preconfig' ] }
##
# @StatusInfo:
@ -91,7 +94,8 @@
# "status": "running" } }
#
##
{ 'command': 'query-status', 'returns': 'StatusInfo' }
{ 'command': 'query-status', 'returns': 'StatusInfo',
'allow-preconfig': true }
##
# @SHUTDOWN:

View File

@ -3299,6 +3299,19 @@ STEXI
Run the emulation in single step mode.
ETEXI
DEF("preconfig", 0, QEMU_OPTION_preconfig, \
"--preconfig pause QEMU before machine is initialized\n",
QEMU_ARCH_ALL)
STEXI
@item --preconfig
@findex --preconfig
Pause QEMU for interactive configuration before the machine is created,
which allows querying and configuring properties that will affect
machine initialization. Use the QMP command 'exit-preconfig' to exit
the preconfig state and move to the next state (ie. run guest if -S
isn't used or pause the second time if -S is used).
ETEXI
DEF("S", 0, QEMU_OPTION_S, \
"-S freeze CPU at startup (use 'c' to start execution)\n",
QEMU_ARCH_ALL)

View File

@ -5,6 +5,7 @@
* CPU emulation::
* Translator Internals::
* QEMU compared to other emulators::
* Managed start up options::
* Bibliography::
@end menu
@ -314,6 +315,45 @@ VirtualBox [9], Xen [10] and KVM [11] are based on QEMU. QEMU-SystemC
[12] uses QEMU to simulate a system where some hardware devices are
developed in SystemC.
@node Managed start up options
@section Managed start up options
In system mode emulation, it's possible to create a VM in a paused state using
the -S command line option. In this state the machine is completely initialized
according to command line options and ready to execute VM code but VCPU threads
are not executing any code. The VM state in this paused state depends on the way
QEMU was started. It could be in:
@table @asis
@item initial state (after reset/power on state)
@item with direct kernel loading, the initial state could be amended to execute
code loaded by QEMU in the VM's RAM and with incoming migration
@item with incoming migration, initial state will by amended with the migrated
machine state after migration completes.
@end table
This paused state is typically used by users to query machine state and/or
additionally configure the machine (by hotplugging devices) in runtime before
allowing VM code to run.
However, at the -S pause point, it's impossible to configure options that affect
initial VM creation (like: -smp/-m/-numa ...) or cold plug devices. That's
when the --preconfig command line option should be used. It allows pausing QEMU
before the initial VM creation, in a new preconfig state, where additional
queries and configuration can be performed via QMP before moving on to
the resulting configuration startup. In the preconfig state, QEMU only allows
a limited set of commands over the QMP monitor, where the commands do not
depend on an initialized machine, including but not limited to:
@table @asis
@item qmp_capabilities
@item query-qmp-schema
@item query-commands
@item query-status
@item exit-preconfig
@end table
The full list of commands is in QMP schema which could be queried with
query-qmp-schema, where commands supported at preconfig state have option
'allow-preconfig' set to true.
@node Bibliography
@section Bibliography

10
qmp.c
View File

@ -161,6 +161,16 @@ SpiceInfo *qmp_query_spice(Error **errp)
};
#endif
void qmp_exit_preconfig(Error **errp)
{
if (!runstate_check(RUN_STATE_PRECONFIG)) {
error_setg(errp, "The command is permitted only in '%s' state",
RunState_str(RUN_STATE_PRECONFIG));
return;
}
qemu_exit_preconfig_request();
}
void qmp_cont(Error **errp)
{
BlockBackend *blk;

View File

@ -193,13 +193,15 @@ out:
return ret
def gen_register_command(name, success_response, allow_oob):
def gen_register_command(name, success_response, allow_oob, allow_preconfig):
options = []
if not success_response:
options += ['QCO_NO_SUCCESS_RESP']
if allow_oob:
options += ['QCO_ALLOW_OOB']
if allow_preconfig:
options += ['QCO_ALLOW_PRECONFIG']
if not options:
options = ['QCO_NO_OPTIONS']
@ -275,8 +277,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
c_prefix=c_name(self._prefix, protect=False)))
genc.add(gen_registry(self._regy, self._prefix))
def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
if not gen:
return
self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
@ -285,7 +287,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
self._genc.add(gen_marshal_output(ret_type))
self._genh.add(gen_marshal_decl(name))
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
self._regy += gen_register_command(name, success_response, allow_oob)
self._regy += gen_register_command(name, success_response, allow_oob,
allow_preconfig)
def gen_commands(schema, output_dir, prefix):

View File

@ -872,7 +872,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
raise QAPISemError(info,
"'%s' of %s '%s' should only use false value"
% (key, meta, name))
if (key == 'boxed' or key == 'allow-oob') and value is not True:
if (key == 'boxed' or key == 'allow-oob' or
key == 'allow-preconfig') and value is not True:
raise QAPISemError(info,
"'%s' of %s '%s' should only use true value"
% (key, meta, name))
@ -922,7 +923,7 @@ def check_exprs(exprs):
meta = 'command'
check_keys(expr_elem, 'command', [],
['data', 'returns', 'gen', 'success-response',
'boxed', 'allow-oob'])
'boxed', 'allow-oob', 'allow-preconfig'])
elif 'event' in expr:
meta = 'event'
check_keys(expr_elem, 'event', [], ['data', 'boxed'])
@ -1044,8 +1045,8 @@ class QAPISchemaVisitor(object):
def visit_alternate_type(self, name, info, variants):
pass
def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
pass
def visit_event(self, name, info, arg_type, boxed):
@ -1422,7 +1423,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
class QAPISchemaCommand(QAPISchemaEntity):
def __init__(self, name, info, doc, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
gen, success_response, boxed, allow_oob, allow_preconfig):
QAPISchemaEntity.__init__(self, name, info, doc)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
@ -1434,6 +1435,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.success_response = success_response
self.boxed = boxed
self.allow_oob = allow_oob
self.allow_preconfig = allow_preconfig
def check(self, schema):
if self._arg_type_name:
@ -1458,7 +1460,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
visitor.visit_command(self.name, self.info,
self.arg_type, self.ret_type,
self.gen, self.success_response,
self.boxed, self.allow_oob)
self.boxed, self.allow_oob,
self.allow_preconfig)
class QAPISchemaEvent(QAPISchemaEntity):
@ -1678,6 +1681,7 @@ class QAPISchema(object):
success_response = expr.get('success-response', True)
boxed = expr.get('boxed', False)
allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, doc, 'arg', self._make_members(data, info))
@ -1686,7 +1690,7 @@ class QAPISchema(object):
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
gen, success_response,
boxed, allow_oob))
boxed, allow_oob, allow_preconfig))
def _def_event(self, expr, info, doc):
name = expr['event']

View File

@ -226,8 +226,8 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
name=doc.symbol,
body=texi_entity(doc, 'Members')))
def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
doc = self.cur_doc
if boxed:
body = texi_body(doc)

View File

@ -171,14 +171,15 @@ const QLitObject %(c_name)s = %(c_string)s;
{'members': [{'type': self._use_type(m.type)}
for m in variants.variants]})
def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
self._gen_qlit(name, 'command',
{'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type),
'allow-oob': allow_oob})
'allow-oob': allow_oob,
'allow-preconfig': allow_preconfig})
def visit_event(self, name, info, arg_type, boxed):
arg_type = arg_type or self._schema.the_empty_object_type

View File

@ -525,6 +525,7 @@ qapi-schema += missing-type.json
qapi-schema += nested-struct-data.json
qapi-schema += non-objects.json
qapi-schema += oob-test.json
qapi-schema += allow-preconfig-test.json
qapi-schema += pragma-doc-required-crap.json
qapi-schema += pragma-extra-junk.json
qapi-schema += pragma-name-case-whitelist-crap.json

View File

@ -1098,3 +1098,10 @@ void qtest_qmp_device_del(const char *id)
qobject_unref(response1);
qobject_unref(response2);
}
bool qmp_rsp_is_err(QDict *rsp)
{
QDict *error = qdict_get_qdict(rsp, "error");
qobject_unref(rsp);
return !!error;
}

View File

@ -972,4 +972,13 @@ void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt,
*/
void qtest_qmp_device_del(const char *id);
/**
* qmp_rsp_is_err:
* @rsp: QMP response to check for error
*
* Test @rsp for error and discard @rsp.
* Returns 'true' if there is error in @rsp and 'false' otherwise.
*/
bool qmp_rsp_is_err(QDict *rsp);
#endif

View File

@ -260,6 +260,66 @@ static void aarch64_numa_cpu(const void *data)
g_free(cli);
}
static void pc_dynamic_cpu_cfg(const void *data)
{
QObject *e;
QDict *resp;
QList *cpus;
QTestState *qs;
qs = qtest_startf("%s %s", data ? (char *)data : "",
"-nodefaults --preconfig -smp 2");
/* create 2 numa nodes */
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node',"
" 'arguments': { 'type': 'node', 'nodeid': 0 } }")));
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node',"
" 'arguments': { 'type': 'node', 'nodeid': 1 } }")));
/* map 2 cpus in non default reverse order
* i.e socket1->node0, socket0->node1
*/
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node',"
" 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }")));
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node',"
" 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }")));
/* let machine initialization to complete and run */
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }")));
qtest_qmp_eventwait(qs, "RESUME");
/* check that CPUs are mapped as expected */
resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}");
g_assert(qdict_haskey(resp, "return"));
cpus = qdict_get_qlist(resp, "return");
g_assert(cpus);
while ((e = qlist_pop(cpus))) {
const QDict *cpu, *props;
int64_t socket, node;
cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "props"));
props = qdict_get_qdict(cpu, "props");
g_assert(qdict_haskey(props, "node-id"));
node = qdict_get_int(props, "node-id");
g_assert(qdict_haskey(props, "socket-id"));
socket = qdict_get_int(props, "socket-id");
if (socket == 0) {
g_assert_cmpint(node, ==, 1);
} else if (socket == 1) {
g_assert_cmpint(node, ==, 0);
} else {
g_assert(false);
}
qobject_unref(e);
}
qobject_unref(resp);
qtest_quit(qs);
}
int main(int argc, char **argv)
{
const char *args = NULL;
@ -278,6 +338,7 @@ int main(int argc, char **argv)
if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) {
qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu);
qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg);
}
if (!strcmp(arch, "ppc64")) {

View File

@ -0,0 +1 @@
tests/qapi-schema/allow-preconfig-test.json:2: 'allow-preconfig' of command 'allow-preconfig-test' should only use true value

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,2 @@
# Check against allow-preconfig illegal value
{ 'command': 'allow-preconfig-test', 'allow-preconfig': 'some-string' }

View File

@ -28,9 +28,9 @@ object q_obj_cmd-arg
member arg2: str optional=True
member arg3: bool optional=False
command cmd q_obj_cmd-arg -> Object
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False
command cmd-boxed Object -> None
gen=True success_response=True boxed=True oob=False
gen=True success_response=True boxed=True oob=False preconfig=False
doc freeform
body=
= Section

View File

@ -5,4 +5,4 @@ module ident-with-escape.json
object q_obj_fooA-arg
member bar1: str optional=False
command fooA q_obj_fooA-arg -> None
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False

View File

@ -3,6 +3,6 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
prefix QTYPE
module indented-expr.json
command eins None -> None
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False
command zwei None -> None
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False

View File

@ -139,8 +139,8 @@
{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }
# Smoke test on Out-Of-Band
{ 'command': 'an-oob-command', 'allow-oob': true }
# Smoke test on Out-Of-Band and allow-preconfig-test
{ 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true }
# For testing integer range flattening in opts-visitor. The following schema
# corresponds to the option format:

View File

@ -16,7 +16,7 @@ object Empty1
object Empty2
base Empty1
command user_def_cmd0 Empty2 -> Empty2
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False
enum QEnumTwo ['value1', 'value2']
prefix QENUM_TWO
object UserDefOne
@ -143,31 +143,31 @@ object UserDefNativeListUnion
case sizes: q_obj_sizeList-wrapper
case any: q_obj_anyList-wrapper
command user_def_cmd None -> None
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False
object q_obj_user_def_cmd1-arg
member ud1a: UserDefOne optional=False
command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False
object q_obj_user_def_cmd2-arg
member ud1a: UserDefOne optional=False
member ud1b: UserDefOne optional=True
command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False
object q_obj_guest-get-time-arg
member a: int optional=False
member b: int optional=True
command guest-get-time q_obj_guest-get-time-arg -> int
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False
object q_obj_guest-sync-arg
member arg: any optional=False
command guest-sync q_obj_guest-sync-arg -> any
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False
command boxed-struct UserDefZero -> None
gen=True success_response=True boxed=True oob=False
gen=True success_response=True boxed=True oob=False preconfig=False
command boxed-union UserDefNativeListUnion -> None
gen=True success_response=True boxed=True oob=False
command an-oob-command None -> None
gen=True success_response=True boxed=False oob=True
gen=True success_response=True boxed=True oob=False preconfig=False
command test-flags-command None -> None
gen=True success_response=True boxed=False oob=True preconfig=True
object UserDefOptions
member i64: intList optional=True
member u64: uint64List optional=True
@ -231,4 +231,4 @@ object q_obj___org.qemu_x-command-arg
member c: __org.qemu_x-Union2 optional=False
member d: __org.qemu_x-Alt optional=False
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
gen=True success_response=True boxed=False oob=False
gen=True success_response=True boxed=False oob=False preconfig=False

View File

@ -41,12 +41,12 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
print('alternate %s' % name)
self._print_variants(variants)
def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
print('command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name))
print(' gen=%s success_response=%s boxed=%s oob=%s' % \
(gen, success_response, boxed, allow_oob))
print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % \
(gen, success_response, boxed, allow_oob, allow_preconfig))
def visit_event(self, name, info, arg_type, boxed):
print('event %s %s' % (name, arg_type and arg_type.name))

View File

@ -392,6 +392,45 @@ static void add_query_tests(QmpSchema *schema)
}
}
static void test_qmp_preconfig(void)
{
QDict *rsp, *ret;
QTestState *qs = qtest_startf("%s --preconfig", common_args);
/* preconfig state */
/* enabled commands, no error expected */
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }")));
/* forbidden commands, expected error */
g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }")));
/* check that query-status returns preconfig state */
rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }");
ret = qdict_get_qdict(rsp, "return");
g_assert(ret);
g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig");
qobject_unref(rsp);
/* exit preconfig state */
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }")));
qtest_qmp_eventwait(qs, "RESUME");
/* check that query-status returns running state */
rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }");
ret = qdict_get_qdict(rsp, "return");
g_assert(ret);
g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running");
qobject_unref(rsp);
/* check that exit-preconfig returns error after exiting preconfig */
g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }")));
/* enabled commands, no error expected */
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }")));
qtest_quit(qs);
}
int main(int argc, char *argv[])
{
QmpSchema schema;
@ -403,6 +442,7 @@ int main(int argc, char *argv[])
qtest_add_func("qmp/oob", test_qmp_oob);
qmp_schema_init(&schema);
add_query_tests(&schema);
qtest_add_func("qmp/preconfig", test_qmp_preconfig);
ret = g_test_run();

View File

@ -16,7 +16,7 @@ void qmp_user_def_cmd(Error **errp)
{
}
void qmp_an_oob_command(Error **errp)
void qmp_test_flags_command(Error **errp)
{
}

35
vl.c
View File

@ -594,7 +594,7 @@ static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp)
/***********************************************************/
/* QEMU state */
static RunState current_run_state = RUN_STATE_PRELAUNCH;
static RunState current_run_state = RUN_STATE_PRECONFIG;
/* We use RUN_STATE__MAX but any invalid value will do */
static RunState vmstop_requested = RUN_STATE__MAX;
@ -607,6 +607,13 @@ typedef struct {
static const RunStateTransition runstate_transitions_def[] = {
/* from -> to */
{ RUN_STATE_PRECONFIG, RUN_STATE_PRELAUNCH },
/* Early switch to inmigrate state to allow -incoming CLI option work
* as it used to. TODO: delay actual switching to inmigrate state to
* the point after machine is built and remove this hack.
*/
{ RUN_STATE_PRECONFIG, RUN_STATE_INMIGRATE },
{ RUN_STATE_DEBUG, RUN_STATE_RUNNING },
{ RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE },
{ RUN_STATE_DEBUG, RUN_STATE_PRELAUNCH },
@ -1630,6 +1637,7 @@ static pid_t shutdown_pid;
static int powerdown_requested;
static int debug_requested;
static int suspend_requested;
static bool preconfig_exit_requested = true;
static WakeupReason wakeup_reason;
static NotifierList powerdown_notifiers =
NOTIFIER_LIST_INITIALIZER(powerdown_notifiers);
@ -1714,6 +1722,11 @@ static int qemu_debug_requested(void)
return r;
}
void qemu_exit_preconfig_request(void)
{
preconfig_exit_requested = true;
}
/*
* Reset the VM. Issue an event unless @reason is SHUTDOWN_CAUSE_NONE.
*/
@ -1887,6 +1900,13 @@ static bool main_loop_should_exit(void)
RunState r;
ShutdownCause request;
if (preconfig_exit_requested) {
if (runstate_check(RUN_STATE_PRECONFIG)) {
runstate_set(RUN_STATE_PRELAUNCH);
}
preconfig_exit_requested = false;
return true;
}
if (qemu_debug_requested()) {
vm_stop(RUN_STATE_DEBUG);
}
@ -3667,6 +3687,9 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
break;
case QEMU_OPTION_preconfig:
preconfig_exit_requested = false;
break;
case QEMU_OPTION_enable_kvm:
olist = qemu_find_opts("machine");
qemu_opts_parse_noisily(olist, "accel=kvm", false);
@ -4031,6 +4054,12 @@ int main(int argc, char **argv, char **envp)
replay_configure(icount_opts);
if (incoming && !preconfig_exit_requested) {
error_report("'preconfig' and 'incoming' options are "
"mutually exclusive");
exit(EXIT_FAILURE);
}
machine_class = select_machine();
set_memory_options(&ram_slots, &maxram_size, machine_class);
@ -4548,6 +4577,10 @@ int main(int argc, char **argv, char **envp)
}
parse_numa_opts(current_machine);
/* do monitor/qmp handling at preconfig state if requested */
main_loop();
/* from here on runstate is RUN_STATE_PRELAUNCH */
machine_run_board_init(current_machine);
realtime_init();