[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v3 5/9] QAPI: allow to specify valid runstates p
From: |
Dr. David Alan Gilbert |
Subject: |
Re: [Qemu-devel] [PATCH v3 5/9] QAPI: allow to specify valid runstates per command |
Date: |
Wed, 7 Mar 2018 14:16:50 +0000 |
User-agent: |
Mutt/1.9.2 (2017-12-15) |
* Igor Mammedov (address@hidden) wrote:
> Add optional 'runstates' parameter in QAPI command definition,
> which will permit to specify RunState variations in which
> a command could be exectuted via QMP monitor.
>
> For compatibility reasons, commands, that don't use
> 'runstates' explicitly, are not permited to run in
> preconfig state but allowed in all other states.
>
> New option will be used to allow commands, which are
> prepared/need to run this early, to run in preconfig state.
> It will include query-hotpluggable-cpus and new set-numa-node
> commands. Other commands that should be able to run in
> preconfig state, should be ammeded to not expect machine
> in initialized state.
>
> Signed-off-by: Igor Mammedov <address@hidden>
> ---
> include/qapi/qmp/dispatch.h | 5 +++-
> monitor.c | 28 +++++++++++++++++---
> qapi-schema.json | 12 +++++++--
> qapi/qmp-dispatch.c | 39 ++++++++++++++++++++++++++++
> qapi/qmp-registry.c | 4 ++-
> qapi/run-state.json | 6 ++++-
> scripts/qapi-commands.py | 46
> ++++++++++++++++++++++++++-------
> scripts/qapi-introspect.py | 2 +-
> scripts/qapi.py | 15 +++++++----
> scripts/qapi2texi.py | 2 +-
> tests/qapi-schema/doc-good.out | 4 +--
> tests/qapi-schema/ident-with-escape.out | 2 +-
> tests/qapi-schema/indented-expr.out | 4 +--
> tests/qapi-schema/qapi-schema-test.out | 18 ++++++-------
> tests/qapi-schema/test-qapi.py | 6 ++---
> 15 files changed, 151 insertions(+), 42 deletions(-)
>
> diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
> index 1e694b5..f821781 100644
> --- a/include/qapi/qmp/dispatch.h
> +++ b/include/qapi/qmp/dispatch.h
> @@ -15,6 +15,7 @@
> #define QAPI_QMP_DISPATCH_H
>
> #include "qemu/queue.h"
> +#include "qapi-types.h"
>
> typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);
>
> @@ -31,12 +32,14 @@ typedef struct QmpCommand
> QmpCommandOptions options;
> QTAILQ_ENTRY(QmpCommand) node;
> bool enabled;
> + const RunState *valid_runstates;
> } QmpCommand;
>
> typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList;
>
> void qmp_register_command(QmpCommandList *cmds, const char *name,
> - QmpCommandFunc *fn, QmpCommandOptions options);
> + QmpCommandFunc *fn, QmpCommandOptions options,
> + const RunState valid_runstates[]);
> void qmp_unregister_command(QmpCommandList *cmds, const char *name);
> QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name);
> QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request);
> diff --git a/monitor.c b/monitor.c
> index fcb5386..2edc96c 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -224,6 +224,25 @@ static mon_cmd_t mon_cmds[];
> static mon_cmd_t info_cmds[];
>
> QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
> +const RunState cap_negotiation_valid_runstates[] = {
> + RUN_STATE_DEBUG,
> + RUN_STATE_INMIGRATE,
> + RUN_STATE_INTERNAL_ERROR,
> + RUN_STATE_IO_ERROR,
> + RUN_STATE_PAUSED,
> + RUN_STATE_POSTMIGRATE,
> + RUN_STATE_PRELAUNCH,
> + RUN_STATE_FINISH_MIGRATE,
> + RUN_STATE_RESTORE_VM,
> + RUN_STATE_RUNNING,
> + RUN_STATE_SAVE_VM,
> + RUN_STATE_SHUTDOWN,
> + RUN_STATE_SUSPENDED,
> + RUN_STATE_WATCHDOG,
> + RUN_STATE_GUEST_PANICKED,
> + RUN_STATE_COLO,
> + RUN_STATE_PRECONFIG,
> +};
It's a shame this is such a manual copy.
While you're taking a big hammer to HMP in the preconfig case,
it's worth noting this more specific code is only protecting QMP
commands.
It's a shame in all the uses below we can't be working with bitmasks
of run-state's; it would feel a lot easier to have a default and mask
out or or in extra states.
Dave
> Monitor *cur_mon;
>
> @@ -1016,17 +1035,18 @@ void monitor_init_qmp_commands(void)
>
> qmp_register_command(&qmp_commands, "query-qmp-schema",
> qmp_query_qmp_schema,
> - QCO_NO_OPTIONS);
> + QCO_NO_OPTIONS, NULL);
> qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
> - QCO_NO_OPTIONS);
> + QCO_NO_OPTIONS, NULL);
> qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
> - QCO_NO_OPTIONS);
> + QCO_NO_OPTIONS, NULL);
>
> qmp_unregister_commands_hack();
>
> 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_NO_OPTIONS,
> + cap_negotiation_valid_runstates);
> }
>
> void qmp_qmp_capabilities(Error **errp)
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 0262b9f..ee1053d 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -219,7 +219,11 @@
> # Note: This example has been shortened as the real response is too long.
> #
> ##
> -{ 'command': 'query-commands', 'returns': ['CommandInfo'] }
> +{ 'command': 'query-commands', 'returns': ['CommandInfo'],
> + 'runstates': [ 'debug', 'inmigrate', 'internal-error', 'io-error',
> 'paused',
> + 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm',
> + 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog',
> + 'guest-panicked', 'colo', 'preconfig' ] }
That's going to get to be a pain to update as we add more states.
> ##
> # @LostTickPolicy:
> @@ -1146,7 +1150,11 @@
> # <- { "return": {} }
> #
> ##
> -{ 'command': 'cont' }
> +{ 'command': 'cont',
> + 'runstates': [ 'debug', 'inmigrate', 'internal-error', 'io-error',
> 'paused',
> + 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm',
> + 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog',
> + 'guest-panicked', 'colo', 'preconfig' ] }
>
> ##
> # @system_wakeup:
> diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
> index e31ac4b..26cba19 100644
> --- a/qapi/qmp-dispatch.c
> +++ b/qapi/qmp-dispatch.c
> @@ -17,6 +17,7 @@
> #include "qapi/qmp/json-parser.h"
> #include "qapi/qmp/qdict.h"
> #include "qapi/qmp/qjson.h"
> +#include "sysemu/sysemu.h"
>
> static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
> {
> @@ -65,6 +66,40 @@ static QDict *qmp_dispatch_check_obj(const QObject
> *request, Error **errp)
> return dict;
> }
>
> +static bool is_cmd_permited(const QmpCommand *cmd, Error **errp)
> +{
> + int i;
> + char *list = NULL;
> +
> + /* Old commands that don't have explicit runstate in schema
> + * are permited to run except of at PRECONFIG stage
> + */
> + if (!cmd->valid_runstates) {
> + if (runstate_check(RUN_STATE_PRECONFIG)) {
> + const char *state = RunState_str(RUN_STATE_PRECONFIG);
> + error_setg(errp, "The command '%s' isn't valid in '%s'",
> + cmd->name, state);
> + return false;
> + }
> + return true;
> + }
> +
> + for (i = 0; cmd->valid_runstates[i] != RUN_STATE__MAX; i++) {
> + if (runstate_check(cmd->valid_runstates[i])) {
> + return true;
> + }
> + }
> +
> + for (i = 0; cmd->valid_runstates[i] != RUN_STATE__MAX; i++) {
> + const char *state = RunState_str(cmd->valid_runstates[i]);
> + list = g_strjoin(", ", state, list, NULL);
> + }
> + error_setg(errp, "The command '%s' is valid only in '%s'", cmd->name,
> list);
> + g_free(list);
> +
> + return false;
> +}
> +
> static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
> Error **errp)
> {
> @@ -92,6 +127,10 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds,
> QObject *request,
> return NULL;
> }
>
> + if (!is_cmd_permited(cmd, errp)) {
> + return NULL;
> + }
> +
> if (!qdict_haskey(dict, "arguments")) {
> args = qdict_new();
> } else {
> diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c
> index 5af484c..59a5b85 100644
> --- a/qapi/qmp-registry.c
> +++ b/qapi/qmp-registry.c
> @@ -16,7 +16,8 @@
> #include "qapi/qmp/dispatch.h"
>
> void qmp_register_command(QmpCommandList *cmds, const char *name,
> - QmpCommandFunc *fn, QmpCommandOptions options)
> + QmpCommandFunc *fn, QmpCommandOptions options,
> + const RunState valid_runstates[])
> {
> QmpCommand *cmd = g_malloc0(sizeof(*cmd));
>
> @@ -24,6 +25,7 @@ void qmp_register_command(QmpCommandList *cmds, const char
> *name,
> cmd->fn = fn;
> cmd->enabled = true;
> cmd->options = options;
> + cmd->valid_runstates = valid_runstates;
> QTAILQ_INSERT_TAIL(cmds, cmd, node);
> }
>
> diff --git a/qapi/run-state.json b/qapi/run-state.json
> index d24a4e8..1c1c9b8 100644
> --- a/qapi/run-state.json
> +++ b/qapi/run-state.json
> @@ -92,7 +92,11 @@
> # "status": "running" } }
> #
> ##
> -{ 'command': 'query-status', 'returns': 'StatusInfo' }
> +{ 'command': 'query-status', 'returns': 'StatusInfo',
> + 'runstates': [ 'debug', 'inmigrate', 'internal-error', 'io-error',
> 'paused',
> + 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm',
> + 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog',
> + 'guest-panicked', 'colo', 'preconfig' ] }
>
> ##
> # @SHUTDOWN:
> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index f89d748..659e167 100644
> --- a/scripts/qapi-commands.py
> +++ b/scripts/qapi-commands.py
> @@ -192,17 +192,45 @@ out:
> return ret
>
>
> -def gen_register_command(name, success_response):
> +def gen_register_command(name, success_response, runstates):
> options = 'QCO_NO_OPTIONS'
> if not success_response:
> options = 'QCO_NO_SUCCESS_RESP'
>
> - ret = mcgen('''
> - qmp_register_command(cmds, "%(name)s",
> - qmp_marshal_%(c_name)s, %(opts)s);
> -''',
> - name=name, c_name=c_name(name),
> - opts=options)
> + if runstates is None:
> + ret = mcgen('''
> + qmp_register_command(cmds, "%(name)s",
> + qmp_marshal_%(c_name)s, %(opts)s,
> + NULL);
> + ''',
> + name=name, c_name=c_name(name),
> + opts=options)
> + else:
> + ret = mcgen('''
> + static const RunState qmp_valid_states_%(c_name)s[] = {
> +'''
> + , c_name=c_name(name))
> +
> + for value in runstates:
> + ret += mcgen('''
> + %(c_enum)s,
> +'''
> + , c_enum=c_enum_const('RunState', value))
> +
> + ret += mcgen('''
> + %(c_enum)s,
> + };
> +'''
> + , c_enum=c_enum_const('RunState', "_MAX"))
> +
> + ret += mcgen('''
> + qmp_register_command(cmds, "%(name)s",
> + qmp_marshal_%(c_name)s, %(opts)s,
> + qmp_valid_states_%(c_name)s);
> + ''',
> + name=name, c_name=c_name(name),
> + opts=options)
> +
> return ret
>
>
> @@ -241,7 +269,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
> self._visited_ret_types = None
>
> def visit_command(self, name, info, arg_type, ret_type,
> - gen, success_response, boxed):
> + gen, success_response, boxed, runstates):
> if not gen:
> return
> self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
> @@ -250,7 +278,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
> self.defn += gen_marshal_output(ret_type)
> self.decl += gen_marshal_decl(name)
> self.defn += gen_marshal(name, arg_type, boxed, ret_type)
> - self._regy += gen_register_command(name, success_response)
> + self._regy += gen_register_command(name, success_response, runstates)
>
>
> (input_file, output_dir, do_c, do_h, prefix, opts) = parse_command_line()
> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
> index 032bcea..ede86ac 100644
> --- a/scripts/qapi-introspect.py
> +++ b/scripts/qapi-introspect.py
> @@ -154,7 +154,7 @@ const char %(c_name)s[] = %(c_string)s;
> for m in variants.variants]})
>
> def visit_command(self, name, info, arg_type, ret_type,
> - gen, success_response, boxed):
> + gen, success_response, boxed, runstates):
> arg_type = arg_type or self._schema.the_empty_object_type
> ret_type = ret_type or self._schema.the_empty_object_type
> self._gen_json(name, 'command',
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 58f995b..5c5037e 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -919,7 +919,8 @@ def check_exprs(exprs):
> elif 'command' in expr:
> meta = 'command'
> check_keys(expr_elem, 'command', [],
> - ['data', 'returns', 'gen', 'success-response',
> 'boxed'])
> + ['data', 'returns', 'gen', 'success-response',
> 'boxed',
> + 'runstates'])
> elif 'event' in expr:
> meta = 'event'
> check_keys(expr_elem, 'event', [], ['data', 'boxed'])
> @@ -1030,7 +1031,7 @@ class QAPISchemaVisitor(object):
> pass
>
> def visit_command(self, name, info, arg_type, ret_type,
> - gen, success_response, boxed):
> + gen, success_response, boxed, runstates):
> pass
>
> def visit_event(self, name, info, arg_type, boxed):
> @@ -1397,7 +1398,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
>
> class QAPISchemaCommand(QAPISchemaEntity):
> def __init__(self, name, info, doc, arg_type, ret_type,
> - gen, success_response, boxed):
> + gen, success_response, boxed, runstates):
> QAPISchemaEntity.__init__(self, name, info, doc)
> assert not arg_type or isinstance(arg_type, str)
> assert not ret_type or isinstance(ret_type, str)
> @@ -1408,6 +1409,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
> self.gen = gen
> self.success_response = success_response
> self.boxed = boxed
> + self.runstates = runstates
>
> def check(self, schema):
> if self._arg_type_name:
> @@ -1431,7 +1433,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
> def visit(self, visitor):
> visitor.visit_command(self.name, self.info,
> self.arg_type, self.ret_type,
> - self.gen, self.success_response, self.boxed)
> + self.gen, self.success_response, self.boxed,
> + self.runstates)
>
>
> class QAPISchemaEvent(QAPISchemaEntity):
> @@ -1639,6 +1642,7 @@ class QAPISchema(object):
> gen = expr.get('gen', True)
> success_response = expr.get('success-response', True)
> boxed = expr.get('boxed', False)
> + runstates = expr.get('runstates')
> if isinstance(data, OrderedDict):
> data = self._make_implicit_object_type(
> name, info, doc, 'arg', self._make_members(data, info))
> @@ -1646,7 +1650,8 @@ class QAPISchema(object):
> assert len(rets) == 1
> rets = self._make_array_type(rets[0], info)
> self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
> - gen, success_response, boxed))
> + gen, success_response, boxed,
> + runstates))
>
> def _def_event(self, expr, info, doc):
> name = expr['event']
> diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
> index bf1c57b..d28594c 100755
> --- a/scripts/qapi2texi.py
> +++ b/scripts/qapi2texi.py
> @@ -227,7 +227,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
> body=texi_entity(doc, 'Members'))
>
> def visit_command(self, name, info, arg_type, ret_type,
> - gen, success_response, boxed):
> + gen, success_response, boxed, runstates):
> doc = self.cur_doc
> if boxed:
> body = texi_body(doc)
> diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
> index 1d2c250..a47d6f6 100644
> --- a/tests/qapi-schema/doc-good.out
> +++ b/tests/qapi-schema/doc-good.out
> @@ -18,9 +18,9 @@ object Variant1
> member var1: str optional=False
> object Variant2
> command cmd q_obj_cmd-arg -> Object
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> command cmd-boxed Object -> None
> - gen=True success_response=True boxed=True
> + gen=True success_response=True boxed=True, runstates=None
> object q_empty
> object q_obj_Variant1-wrapper
> member data: Variant1 optional=False
> diff --git a/tests/qapi-schema/ident-with-escape.out
> b/tests/qapi-schema/ident-with-escape.out
> index b5637cb..d9a272b 100644
> --- a/tests/qapi-schema/ident-with-escape.out
> +++ b/tests/qapi-schema/ident-with-escape.out
> @@ -1,7 +1,7 @@
> enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
> prefix QTYPE
> command fooA q_obj_fooA-arg -> None
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> object q_empty
> object q_obj_fooA-arg
> member bar1: str optional=False
> diff --git a/tests/qapi-schema/indented-expr.out
> b/tests/qapi-schema/indented-expr.out
> index 586795f..4b128d5 100644
> --- a/tests/qapi-schema/indented-expr.out
> +++ b/tests/qapi-schema/indented-expr.out
> @@ -1,7 +1,7 @@
> enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
> prefix QTYPE
> command eins None -> None
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> object q_empty
> command zwei None -> None
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> diff --git a/tests/qapi-schema/qapi-schema-test.out
> b/tests/qapi-schema/qapi-schema-test.out
> index 3b1e908..787c228 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -153,15 +153,15 @@ object __org.qemu_x-Union2
> tag __org.qemu_x-member1
> case __org.qemu_x-value: __org.qemu_x-Struct2
> command __org.qemu_x-command q_obj___org.qemu_x-command-arg ->
> __org.qemu_x-Union1
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> command boxed-struct UserDefZero -> None
> - gen=True success_response=True boxed=True
> + gen=True success_response=True boxed=True, runstates=None
> command boxed-union UserDefNativeListUnion -> None
> - gen=True success_response=True boxed=True
> + gen=True success_response=True boxed=True, runstates=None
> command guest-get-time q_obj_guest-get-time-arg -> int
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> command guest-sync q_obj_guest-sync-arg -> any
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> object q_empty
> object q_obj_EVENT_C-arg
> member a: int optional=True
> @@ -222,10 +222,10 @@ object q_obj_user_def_cmd2-arg
> member ud1a: UserDefOne optional=False
> member ud1b: UserDefOne optional=True
> command user_def_cmd None -> None
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> command user_def_cmd0 Empty2 -> Empty2
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
> - gen=True success_response=True boxed=False
> + gen=True success_response=True boxed=False, runstates=None
> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index ac43d34..958576d 100644
> --- a/tests/qapi-schema/test-qapi.py
> +++ b/tests/qapi-schema/test-qapi.py
> @@ -37,11 +37,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
> self._print_variants(variants)
>
> def visit_command(self, name, info, arg_type, ret_type,
> - gen, success_response, boxed):
> + gen, success_response, boxed, runstates):
> 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' % \
> - (gen, success_response, boxed))
> + print(' gen=%s success_response=%s boxed=%s, runstates=%s' % \
> + (gen, success_response, boxed, runstates))
>
> def visit_event(self, name, info, arg_type, boxed):
> print('event %s %s' % (name, arg_type and arg_type.name))
> --
> 2.7.4
>
>
--
Dr. David Alan Gilbert / address@hidden / Manchester, UK
- Re: [Qemu-devel] [PATCH v3 5/9] QAPI: allow to specify valid runstates per command,
Dr. David Alan Gilbert <=