From nobody Sun Feb 8 23:05:16 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1505376053088583.1490164536954; Thu, 14 Sep 2017 01:00:53 -0700 (PDT) Received: from localhost ([::1]:46228 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dsP4u-0007PC-99 for importer@patchew.org; Thu, 14 Sep 2017 04:00:52 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:52830) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dsOwe-0007S9-6n for qemu-devel@nongnu.org; Thu, 14 Sep 2017 03:52:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dsOwb-0008Cg-O1 for qemu-devel@nongnu.org; Thu, 14 Sep 2017 03:52:20 -0400 Received: from mx1.redhat.com ([209.132.183.28]:40734) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dsOwb-0008Am-Fm for qemu-devel@nongnu.org; Thu, 14 Sep 2017 03:52:17 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 71450883AB; Thu, 14 Sep 2017 07:52:16 +0000 (UTC) Received: from pxdev.xzpeter.org.com (dhcp-15-224.nay.redhat.com [10.66.15.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id E3934619BE; Thu, 14 Sep 2017 07:52:11 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 71450883AB Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=peterx@redhat.com From: Peter Xu To: qemu-devel@nongnu.org Date: Thu, 14 Sep 2017 15:50:34 +0800 Message-Id: <1505375436-28439-14-git-send-email-peterx@redhat.com> In-Reply-To: <1505375436-28439-1-git-send-email-peterx@redhat.com> References: <1505375436-28439-1-git-send-email-peterx@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Thu, 14 Sep 2017 07:52:16 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [RFC 13/15] qapi: introduce new cmd option "allow-oob" X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Laurent Vivier , Fam Zheng , Juan Quintela , Markus Armbruster , peterx@redhat.com, mdroth@linux.vnet.ibm.com, Stefan Hajnoczi , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , "Dr . David Alan Gilbert" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Here "oob" stands for "Out-Of-Band". When "allow-oob" is set, it means the command allows out-of-band execution. Please see the spec update for more details. The "oob" idea is proposed by Markus Armbruster in following thread: https://lists.gnu.org/archive/html/qemu-devel/2017-09/msg02057.html This new "allow-oob" boolean will be exposed by "query-qmp-schema" as well for command entries, so that QMP clients can know which command can be used as out-of-band calls. For example the command "migrate" originally looks like: {"name": "migrate", "ret-type": "17", "meta-type": "command", "arg-type": "86"} And it'll be changed into: {"name": "migrate", "ret-type": "17", "allow-oob": false, "meta-type": "command", "arg-type": "86"} This patch only provides the QMP interface level changes. It does not contains the real out-of-band execution implementation yet. Suggested-by: Markus Armbruster Signed-off-by: Peter Xu --- docs/devel/qapi-code-gen.txt | 41 ++++++++++++++++++++++++++++++++++++--= --- include/qapi/qmp/dispatch.h | 1 + qapi/introspect.json | 6 +++++- scripts/qapi-commands.py | 19 ++++++++++++++----- scripts/qapi-introspect.py | 10 ++++++++-- scripts/qapi.py | 15 ++++++++++----- scripts/qapi2texi.py | 2 +- tests/qapi-schema/test-qapi.py | 2 +- 8 files changed, 76 insertions(+), 20 deletions(-) diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index f04c63f..61fa167 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -556,7 +556,8 @@ following example objects: =20 Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, '*returns': TYPE-NAME, '*boxed': true, - '*gen': false, '*success-response': false } + '*gen': false, '*success-response': false, + '*allow-oob': false } =20 Commands are defined by using a dictionary containing several members, where three members are most common. The 'command' member is a @@ -636,6 +637,34 @@ possible, the command expression should include the op= tional key 'success-response' with boolean value false. So far, only QGA makes use of this member. =20 +Most of the QMP commands are handled sequentially in such a order. +Firstly, the JSON Parser parses the command request into some internal +message, then it delivers the message to QMP dispatchers; secondly, +the QMP dispatchers will handle the commands one by one in time order, +respond when necessary. For some commands that always complete +"quickly" can instead be executed directly during parsing, at the QMP +client's request. This kind of commands that allow direct execution +is called "out-of-band" ("oob" as shortcut) commands. the response can +overtake prior in-band commands' responses. By default, commands are +always in-band. We need to explicitly specify "allow-oob" to "True" +to show that one command can be run out-of-band. + +One thing to mention is that, although out-of-band execution of +commands benefit from quick and asynchronous execution, it need to +satisfy at least the following: + +(1) It is extremely quick and never blocks, so that its execution will + not block parsing routine of any other monitors. + +(2) It does not need BQL, since the parser can be run without BQL, + while the dispatcher is always with BQL held. + +If not, the command is not suitable to be allowed to run out-of-band, +and it should set its "allow-oob" to "False". Whether a command is +allowed to run out-of-band can also be introspected using +query-qmp-schema command. Please see the section "Client JSON +Protocol introspection" for more information. + =20 =3D=3D=3D Events =3D=3D=3D =20 @@ -739,10 +768,12 @@ references by name. QAPI schema definitions not reachable that way are omitted. =20 The SchemaInfo for a command has meta-type "command", and variant -members "arg-type" and "ret-type". On the wire, the "arguments" -member of a client's "execute" command must conform to the object type -named by "arg-type". The "return" member that the server passes in a -success response conforms to the type named by "ret-type". +members "arg-type", "ret-type" and "allow-oob". On the wire, the +"arguments" member of a client's "execute" command must conform to the +object type named by "arg-type". The "return" member that the server +passes in a success response conforms to the type named by +"ret-type". When "allow-oob" is set, it means the command supports +out-of-band execution. =20 If the command takes no arguments, "arg-type" names an object type without members. Likewise, if the command returns nothing, "ret-type" diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 20578dc..b767988 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -23,6 +23,7 @@ typedef enum QmpCommandOptions { QCO_NO_OPTIONS =3D 0x0, QCO_NO_SUCCESS_RESP =3D 0x1, + QCO_ALLOW_OOB =3D 0x2, } QmpCommandOptions; =20 typedef struct QmpCommand diff --git a/qapi/introspect.json b/qapi/introspect.json index 5b3e6e9..57cc137 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -259,12 +259,16 @@ # # @ret-type: the name of the command's result type. # +# @allow-oob: whether the command allows out-of-band execution. +# (Since: 2.11) +# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', - 'data': { 'arg-type': 'str', 'ret-type': 'str' } } + 'data': { 'arg-type': 'str', 'ret-type': 'str', + 'allow-oob': 'bool' } } =20 ## # @SchemaInfoEvent: diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 974d0a4..b2b0bc0 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -192,10 +192,18 @@ out: return ret =20 =20 -def gen_register_command(name, success_response): - options =3D 'QCO_NO_OPTIONS' +def gen_register_command(name, success_response, allow_oob): + options =3D [] + if not success_response: - options =3D 'QCO_NO_SUCCESS_RESP' + options +=3D ['QCO_NO_SUCCESS_RESP'] + if allow_oob: + options +=3D ['QCO_ALLOW_OOB'] + + if not options: + options =3D ['QCO_NO_OPTIONS'] + + options =3D " | ".join(options) =20 ret =3D mcgen(''' qmp_register_command(cmds, "%(name)s", @@ -241,7 +249,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): self._visited_ret_types =3D None =20 def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): + gen, success_response, boxed, allow_oob): if not gen: return self.decl +=3D gen_command_decl(name, arg_type, boxed, ret_type) @@ -250,7 +258,8 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): self.defn +=3D gen_marshal_output(ret_type) self.decl +=3D gen_marshal_decl(name) self.defn +=3D gen_marshal(name, arg_type, boxed, ret_type) - self._regy +=3D gen_register_command(name, success_response) + self._regy +=3D gen_register_command(name, success_response, + allow_oob) =20 =20 (input_file, output_dir, do_c, do_h, prefix, opts) =3D parse_command_line() diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py index 032bcea..9fbf88b 100644 --- a/scripts/qapi-introspect.py +++ b/scripts/qapi-introspect.py @@ -28,6 +28,11 @@ def to_json(obj, level=3D0): to_json(obj[key], level + 1)) for key in sorted(obj.keys())] ret =3D '{' + ', '.join(elts) + '}' + elif isinstance(obj, bool): + if obj: + ret =3D 'true' + else: + ret =3D 'false' else: assert False # not implemented if level =3D=3D 1: @@ -154,12 +159,13 @@ const char %(c_name)s[] =3D %(c_string)s; for m in variants.variants]}) =20 def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): + gen, success_response, boxed, allow_oob): arg_type =3D arg_type or self._schema.the_empty_object_type ret_type =3D ret_type or self._schema.the_empty_object_type self._gen_json(name, 'command', {'arg-type': self._use_type(arg_type), - 'ret-type': self._use_type(ret_type)}) + 'ret-type': self._use_type(ret_type), + 'allow-oob': allow_oob}) =20 def visit_event(self, name, info, arg_type, boxed): arg_type =3D arg_type or self._schema.the_empty_object_type diff --git a/scripts/qapi.py b/scripts/qapi.py index 62dc52e..f411b8f 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -920,7 +920,8 @@ def check_exprs(exprs): elif 'command' in expr: meta =3D 'command' check_keys(expr_elem, 'command', [], - ['data', 'returns', 'gen', 'success-response', 'box= ed']) + ['data', 'returns', 'gen', 'success-response', + 'boxed', 'allow-oob']) elif 'event' in expr: meta =3D 'event' check_keys(expr_elem, 'event', [], ['data', 'boxed']) @@ -1031,7 +1032,7 @@ class QAPISchemaVisitor(object): pass =20 def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): + gen, success_response, boxed, allow_oob): pass =20 def visit_event(self, name, info, arg_type, boxed): @@ -1398,7 +1399,7 @@ class QAPISchemaAlternateType(QAPISchemaType): =20 class QAPISchemaCommand(QAPISchemaEntity): def __init__(self, name, info, doc, arg_type, ret_type, - gen, success_response, boxed): + gen, success_response, boxed, allow_oob): QAPISchemaEntity.__init__(self, name, info, doc) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) @@ -1409,6 +1410,7 @@ class QAPISchemaCommand(QAPISchemaEntity): self.gen =3D gen self.success_response =3D success_response self.boxed =3D boxed + self.allow_oob =3D allow_oob =20 def check(self, schema): if self._arg_type_name: @@ -1432,7 +1434,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.allow_oob) =20 =20 class QAPISchemaEvent(QAPISchemaEntity): @@ -1640,6 +1643,7 @@ class QAPISchema(object): gen =3D expr.get('gen', True) success_response =3D expr.get('success-response', True) boxed =3D expr.get('boxed', False) + allow_oob =3D expr.get('allow-oob', False) if isinstance(data, OrderedDict): data =3D self._make_implicit_object_type( name, info, doc, 'arg', self._make_members(data, info)) @@ -1647,7 +1651,8 @@ class QAPISchema(object): assert len(rets) =3D=3D 1 rets =3D self._make_array_type(rets[0], info) self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, - gen, success_response, boxed)) + gen, success_response, + boxed, allow_oob)) =20 def _def_event(self, expr, info, doc): name =3D expr['event'] diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index a317526..0ac0df5 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -236,7 +236,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): body=3Dtexi_entity(doc, 'Members')) =20 def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): + gen, success_response, boxed, allow_oob): doc =3D self.cur_doc if self.out: self.out +=3D '\n' diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index c7724d3..6749e9e 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -36,7 +36,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_variants(variants) =20 def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): + gen, success_response, boxed, allow_oob): print 'command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name) print ' gen=3D%s success_response=3D%s boxed=3D%s' % \ --=20 2.7.4