qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

qapi: Plumb in 'boxed' to qapi generator lower levels

The next patch will add support for passing a qapi union type
as the 'data' of a command. But to do that, the user function
for implementing the command, as called by the generated
marshal command, must take the corresponding C struct as a
single boxed pointer, rather than a breakdown into one
parameter per member. Even without a union, being able to use
a C struct rather than a list of parameters can make it much
easier to handle coding with QAPI.

This patch adds the internal plumbing of a 'boxed' flag
associated with each command and event. In several cases,
this means adding indentation, with one new dead branch and
the remaining branch being the original code more deeply
nested; this was done so that the new implementation in the
next patch is easier to review without also being mixed with
indentation changes.

For this patch, no behavior or generated output changes, other
than the testsuite outputting the value of the new flag
(always False for now).

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1468468228-27827-9-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
[Identifier box renamed to boxed in two places]
Signed-off-by: Markus Armbruster <armbru@redhat.com>

authored by

Eric Blake and committed by
Markus Armbruster
48825ca4 4d0b268f

+70 -49
+11 -9
scripts/qapi-commands.py
··· 16 16 import re 17 17 18 18 19 - def gen_command_decl(name, arg_type, ret_type): 19 + def gen_command_decl(name, arg_type, boxed, ret_type): 20 20 return mcgen(''' 21 21 %(c_type)s qmp_%(c_name)s(%(params)s); 22 22 ''', 23 23 c_type=(ret_type and ret_type.c_type()) or 'void', 24 24 c_name=c_name(name), 25 - params=gen_params(arg_type, 'Error **errp')) 25 + params=gen_params(arg_type, boxed, 'Error **errp')) 26 26 27 27 28 - def gen_call(name, arg_type, ret_type): 28 + def gen_call(name, arg_type, boxed, ret_type): 29 29 ret = '' 30 30 31 31 argstr = '' 32 - if arg_type: 32 + if boxed: 33 + assert False # not implemented 34 + elif arg_type: 33 35 assert not arg_type.variants 34 36 for memb in arg_type.members: 35 37 if memb.optional: ··· 94 96 proto=gen_marshal_proto(name)) 95 97 96 98 97 - def gen_marshal(name, arg_type, ret_type): 99 + def gen_marshal(name, arg_type, boxed, ret_type): 98 100 ret = mcgen(''' 99 101 100 102 %(proto)s ··· 136 138 (void)args; 137 139 ''') 138 140 139 - ret += gen_call(name, arg_type, ret_type) 141 + ret += gen_call(name, arg_type, boxed, ret_type) 140 142 141 143 # 'goto out' produced above for arg_type, and by gen_call() for ret_type 142 144 if (arg_type and not arg_type.is_empty()) or ret_type: ··· 212 214 self._visited_ret_types = None 213 215 214 216 def visit_command(self, name, info, arg_type, ret_type, 215 - gen, success_response): 217 + gen, success_response, boxed): 216 218 if not gen: 217 219 return 218 - self.decl += gen_command_decl(name, arg_type, ret_type) 220 + self.decl += gen_command_decl(name, arg_type, boxed, ret_type) 219 221 if ret_type and ret_type not in self._visited_ret_types: 220 222 self._visited_ret_types.add(ret_type) 221 223 self.defn += gen_marshal_output(ret_type) 222 224 if middle_mode: 223 225 self.decl += gen_marshal_decl(name) 224 - self.defn += gen_marshal(name, arg_type, ret_type) 226 + self.defn += gen_marshal(name, arg_type, boxed, ret_type) 225 227 if not middle_mode: 226 228 self._regy += gen_register_command(name, success_response) 227 229
+9 -9
scripts/qapi-event.py
··· 14 14 from qapi import * 15 15 16 16 17 - def gen_event_send_proto(name, arg_type): 17 + def gen_event_send_proto(name, arg_type, boxed): 18 18 return 'void qapi_event_send_%(c_name)s(%(param)s)' % { 19 19 'c_name': c_name(name.lower()), 20 - 'param': gen_params(arg_type, 'Error **errp')} 20 + 'param': gen_params(arg_type, boxed, 'Error **errp')} 21 21 22 22 23 - def gen_event_send_decl(name, arg_type): 23 + def gen_event_send_decl(name, arg_type, boxed): 24 24 return mcgen(''' 25 25 26 26 %(proto)s; 27 27 ''', 28 - proto=gen_event_send_proto(name, arg_type)) 28 + proto=gen_event_send_proto(name, arg_type, boxed)) 29 29 30 30 31 31 # Declare and initialize an object 'qapi' using parameters from gen_params() ··· 57 57 return ret 58 58 59 59 60 - def gen_event_send(name, arg_type): 60 + def gen_event_send(name, arg_type, boxed): 61 61 # FIXME: Our declaration of local variables (and of 'errp' in the 62 62 # parameter list) can collide with exploded members of the event's 63 63 # data type passed in as parameters. If this collision ever hits in ··· 72 72 Error *err = NULL; 73 73 QMPEventFuncEmit emit; 74 74 ''', 75 - proto=gen_event_send_proto(name, arg_type)) 75 + proto=gen_event_send_proto(name, arg_type, boxed)) 76 76 77 77 if arg_type and not arg_type.is_empty(): 78 78 ret += mcgen(''' ··· 160 160 self.defn += gen_enum_lookup(event_enum_name, self._event_names) 161 161 self._event_names = None 162 162 163 - def visit_event(self, name, info, arg_type): 164 - self.decl += gen_event_send_decl(name, arg_type) 165 - self.defn += gen_event_send(name, arg_type) 163 + def visit_event(self, name, info, arg_type, boxed): 164 + self.decl += gen_event_send_decl(name, arg_type, boxed) 165 + self.defn += gen_event_send(name, arg_type, boxed) 166 166 self._event_names.append(name) 167 167 168 168
+2 -2
scripts/qapi-introspect.py
··· 154 154 for m in variants.variants]}) 155 155 156 156 def visit_command(self, name, info, arg_type, ret_type, 157 - gen, success_response): 157 + gen, success_response, boxed): 158 158 arg_type = arg_type or self._schema.the_empty_object_type 159 159 ret_type = ret_type or self._schema.the_empty_object_type 160 160 self._gen_json(name, 'command', 161 161 {'arg-type': self._use_type(arg_type), 162 162 'ret-type': self._use_type(ret_type)}) 163 163 164 - def visit_event(self, name, info, arg_type): 164 + def visit_event(self, name, info, arg_type, boxed): 165 165 arg_type = arg_type or self._schema.the_empty_object_type 166 166 self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) 167 167
+27 -16
scripts/qapi.py
··· 826 826 pass 827 827 828 828 def visit_command(self, name, info, arg_type, ret_type, 829 - gen, success_response): 829 + gen, success_response, boxed): 830 830 pass 831 831 832 - def visit_event(self, name, info, arg_type): 832 + def visit_event(self, name, info, arg_type, boxed): 833 833 pass 834 834 835 835 ··· 1165 1165 1166 1166 1167 1167 class QAPISchemaCommand(QAPISchemaEntity): 1168 - def __init__(self, name, info, arg_type, ret_type, gen, success_response): 1168 + def __init__(self, name, info, arg_type, ret_type, gen, success_response, 1169 + boxed): 1169 1170 QAPISchemaEntity.__init__(self, name, info) 1170 1171 assert not arg_type or isinstance(arg_type, str) 1171 1172 assert not ret_type or isinstance(ret_type, str) ··· 1175 1176 self.ret_type = None 1176 1177 self.gen = gen 1177 1178 self.success_response = success_response 1179 + self.boxed = boxed 1178 1180 1179 1181 def check(self, schema): 1180 1182 if self._arg_type_name: 1181 1183 self.arg_type = schema.lookup_type(self._arg_type_name) 1182 1184 assert isinstance(self.arg_type, QAPISchemaObjectType) 1183 1185 assert not self.arg_type.variants # not implemented 1186 + assert not self.boxed # not implemented 1184 1187 if self._ret_type_name: 1185 1188 self.ret_type = schema.lookup_type(self._ret_type_name) 1186 1189 assert isinstance(self.ret_type, QAPISchemaType) ··· 1188 1191 def visit(self, visitor): 1189 1192 visitor.visit_command(self.name, self.info, 1190 1193 self.arg_type, self.ret_type, 1191 - self.gen, self.success_response) 1194 + self.gen, self.success_response, self.boxed) 1192 1195 1193 1196 1194 1197 class QAPISchemaEvent(QAPISchemaEntity): 1195 - def __init__(self, name, info, arg_type): 1198 + def __init__(self, name, info, arg_type, boxed): 1196 1199 QAPISchemaEntity.__init__(self, name, info) 1197 1200 assert not arg_type or isinstance(arg_type, str) 1198 1201 self._arg_type_name = arg_type 1199 1202 self.arg_type = None 1203 + self.boxed = boxed 1200 1204 1201 1205 def check(self, schema): 1202 1206 if self._arg_type_name: 1203 1207 self.arg_type = schema.lookup_type(self._arg_type_name) 1204 1208 assert isinstance(self.arg_type, QAPISchemaObjectType) 1205 1209 assert not self.arg_type.variants # not implemented 1210 + assert not self.boxed # not implemented 1206 1211 1207 1212 def visit(self, visitor): 1208 - visitor.visit_event(self.name, self.info, self.arg_type) 1213 + visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) 1209 1214 1210 1215 1211 1216 class QAPISchema(object): ··· 1381 1386 rets = expr.get('returns') 1382 1387 gen = expr.get('gen', True) 1383 1388 success_response = expr.get('success-response', True) 1389 + boxed = expr.get('boxed', False) 1384 1390 if isinstance(data, OrderedDict): 1385 1391 data = self._make_implicit_object_type( 1386 1392 name, info, 'arg', self._make_members(data, info)) ··· 1388 1394 assert len(rets) == 1 1389 1395 rets = self._make_array_type(rets[0], info) 1390 1396 self._def_entity(QAPISchemaCommand(name, info, data, rets, gen, 1391 - success_response)) 1397 + success_response, boxed)) 1392 1398 1393 1399 def _def_event(self, expr, info): 1394 1400 name = expr['event'] 1395 1401 data = expr.get('data') 1402 + boxed = expr.get('boxed', False) 1396 1403 if isinstance(data, OrderedDict): 1397 1404 data = self._make_implicit_object_type( 1398 1405 name, info, 'arg', self._make_members(data, info)) 1399 - self._def_entity(QAPISchemaEvent(name, info, data)) 1406 + self._def_entity(QAPISchemaEvent(name, info, data, boxed)) 1400 1407 1401 1408 def _def_exprs(self): 1402 1409 for expr_elem in self.exprs: ··· 1639 1646 return ret 1640 1647 1641 1648 1642 - def gen_params(arg_type, extra): 1649 + def gen_params(arg_type, boxed, extra): 1643 1650 if not arg_type: 1644 1651 return extra 1645 - assert not arg_type.variants 1646 1652 ret = '' 1647 1653 sep = '' 1648 - for memb in arg_type.members: 1649 - ret += sep 1650 - sep = ', ' 1651 - if memb.optional: 1652 - ret += 'bool has_%s, ' % c_name(memb.name) 1653 - ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name)) 1654 + if boxed: 1655 + assert False # not implemented 1656 + else: 1657 + assert not arg_type.variants 1658 + for memb in arg_type.members: 1659 + ret += sep 1660 + sep = ', ' 1661 + if memb.optional: 1662 + ret += 'bool has_%s, ' % c_name(memb.name) 1663 + ret += '%s %s' % (memb.type.c_param_type(), 1664 + c_name(memb.name)) 1654 1665 if extra: 1655 1666 ret += sep + extra 1656 1667 return ret
+1
tests/qapi-schema/event-case.out
··· 1 1 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] 2 2 prefix QTYPE 3 3 event oops None 4 + boxed=False 4 5 object q_empty
+1 -1
tests/qapi-schema/ident-with-escape.out
··· 1 1 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] 2 2 prefix QTYPE 3 3 command fooA q_obj_fooA-arg -> None 4 - gen=True success_response=True 4 + gen=True success_response=True boxed=False 5 5 object q_empty 6 6 object q_obj_fooA-arg 7 7 member bar1: str optional=False
+2 -2
tests/qapi-schema/indented-expr.out
··· 1 1 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] 2 2 prefix QTYPE 3 3 command eins None -> None 4 - gen=True success_response=True 4 + gen=True success_response=True boxed=False 5 5 object q_empty 6 6 command zwei None -> None 7 - gen=True success_response=True 7 + gen=True success_response=True boxed=False
+12 -7
tests/qapi-schema/qapi-schema-test.out
··· 23 23 case s: str 24 24 case n: number 25 25 event EVENT_A None 26 + boxed=False 26 27 event EVENT_B None 28 + boxed=False 27 29 event EVENT_C q_obj_EVENT_C-arg 30 + boxed=False 28 31 event EVENT_D q_obj_EVENT_D-arg 32 + boxed=False 29 33 object Empty1 30 34 object Empty2 31 35 base Empty1 ··· 124 128 object WrapAlternate 125 129 member alt: UserDefAlternate optional=False 126 130 event __ORG.QEMU_X-EVENT __org.qemu_x-Struct 131 + boxed=False 127 132 alternate __org.qemu_x-Alt 128 133 tag type 129 134 case __org.qemu_x-branch: str ··· 147 152 tag __org.qemu_x-member1 148 153 case __org.qemu_x-value: __org.qemu_x-Struct2 149 154 command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 150 - gen=True success_response=True 155 + gen=True success_response=True boxed=False 151 156 command guest-get-time q_obj_guest-get-time-arg -> int 152 - gen=True success_response=True 157 + gen=True success_response=True boxed=False 153 158 command guest-sync q_obj_guest-sync-arg -> any 154 - gen=True success_response=True 159 + gen=True success_response=True boxed=False 155 160 object q_empty 156 161 object q_obj_EVENT_C-arg 157 162 member a: int optional=True ··· 212 217 member ud1a: UserDefOne optional=False 213 218 member ud1b: UserDefOne optional=True 214 219 command user_def_cmd None -> None 215 - gen=True success_response=True 220 + gen=True success_response=True boxed=False 216 221 command user_def_cmd0 Empty2 -> Empty2 217 - gen=True success_response=True 222 + gen=True success_response=True boxed=False 218 223 command user_def_cmd1 q_obj_user_def_cmd1-arg -> None 219 - gen=True success_response=True 224 + gen=True success_response=True boxed=False 220 225 command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo 221 - gen=True success_response=True 226 + gen=True success_response=True boxed=False
+5 -3
tests/qapi-schema/test-qapi.py
··· 36 36 self._print_variants(variants) 37 37 38 38 def visit_command(self, name, info, arg_type, ret_type, 39 - gen, success_response): 39 + gen, success_response, boxed): 40 40 print 'command %s %s -> %s' % \ 41 41 (name, arg_type and arg_type.name, ret_type and ret_type.name) 42 - print ' gen=%s success_response=%s' % (gen, success_response) 42 + print ' gen=%s success_response=%s boxed=%s' % \ 43 + (gen, success_response, boxed) 43 44 44 - def visit_event(self, name, info, arg_type): 45 + def visit_event(self, name, info, arg_type, boxed): 45 46 print 'event %s %s' % (name, arg_type and arg_type.name) 47 + print ' boxed=%s' % boxed 46 48 47 49 @staticmethod 48 50 def _print_variants(variants):