* [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling @ 2011-03-14 17:48 Anthony Liguori 2011-03-14 19:52 ` Lluís ` (3 more replies) 0 siblings, 4 replies; 22+ messages in thread From: Anthony Liguori @ 2011-03-14 17:48 UTC (permalink / raw) To: qemu-devel Cc: Kevin Wolf, Chris Wright, Stefan Hajnoczi, Markus Armbruster, Adam Litke As I've been waiting for QAPI review, I've been working on the design of a new mechanism to replace our current command line option handling (QemuOpts) with something that reuses the QAPI infrastructure. The 'QemuOpts' syntax is just a way to encode complex data structures. 'nic,model=virtio,macaddress=00:01:02:03:04:05' can be mapped directly to a C data structure. This is exactly what QCFG does using the same JSON schema mechanism that QMP uses. The effect is that you describe a command line argument in JSON like so: { 'type': 'VncConfig', 'data': { 'address': 'str', '*password': 'bool', '*reverse': 'bool', '*no-lock-key-sync': 'bool', '*sasl': 'bool', '*tls': 'bool', '*x509': 'str', '*x509verify': 'str', '*acl': 'bool', '*lossy': 'bool' } } You then just implement a C function that gets called for each -vnc option specified: void qcfg_handle_vnc(VncConfig *option, Error **errp) { } And that's it. You can squirrel away the option such that they all can be processed later, you can perform additional validation and return an error, or you can implement the appropriate logic. The VncConfig structure is a proper C data structure. The advantages of this approach compared to QemuOpts are similar to QAPI: 1) Strong typing means less bugs with lack of command line validation. In many cases, a bad command line results in a SEGV today. 2) Every option is formally specified and documented in a way that is both rigorous and machine readable. This means we can generate high quality documentation in a variety of formats. 3) The command line parameters support full introspection. This should provide the same functionality as Dan's earlier introspection patches. 4) The 'VncConfig' structure also has JSON marshallers and the qcfg_handle_vnc() function can be trivially bridged to QMP. This means command line oriented interfaces (like device_add) are better integrated with QMP. 5) Very complex data types can be implemented. We had some discussion of supporting nested structures with -blockdev. This wouldn't work with QemuOpts but I've already implemented it with QCFG (blockdev syntax is my test case right now). The syntax I'm currently using is -blockdev cache=none,id=foo,format.qcow.protocol.nbd.hostname=localhost where '.' is used to reference sub structures. 6) Unions are supported which means complex options like -net can be specified in the schema and don't require hand validation. I've got a spec written up at http://wiki.qemu.org/Features/QCFG. Initial code is in my QAPI tree. I'm not going to start converting things until we get closer to the end of 0.15 and QAPI is fully merged. My plan is to focus on this for 0.16 and do a full conversion for the 0.16 time frame using the same approach as QAPI. That means that for 0.16, we would be able to set all command line options via QMP in a programmatic fashion with full support for introspection. I'm haven't yet closed on how to bridge this to qdev. qdev is a big consumer of QemuOpts today. I have some general ideas about what I'd like to do but so far, I haven't written anything down. I wanted to share these plans early hoping to get some feedback and also to maybe interest some folks in helping out. Regards, Anthony Liguori ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-14 17:48 [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling Anthony Liguori @ 2011-03-14 19:52 ` Lluís 2011-03-14 20:04 ` Anthony Liguori 2011-03-15 10:09 ` Kevin Wolf ` (2 subsequent siblings) 3 siblings, 1 reply; 22+ messages in thread From: Lluís @ 2011-03-14 19:52 UTC (permalink / raw) To: qemu-devel Anthony Liguori writes: > I've got a spec written up at http://wiki.qemu.org/Features/QCFG. Initial code > is in my QAPI tree. What about moving the documentation to a 'doc' attribute? Thus, instead of the example vnconfig: { 'type': 'VncConfig', 'doc': 'Configuration options for the built-in VNC server.', 'data': {'address': 'str', ...} } Still, it's not clear to me how attribute documentation shoul dbe provided: 'data': {'address': {'type': 'str', 'doc': 'The hostname to bind the VNC server to...'}, ... } Or maybe: 'data': {'address': 'str', 'address.doc': 'The hostname to bind the VNC server to...'}, ... } But as I suppose these documentation comments are automatically processes, this might just prove too verbose for no benefit at all, as introspecting down to the documentation might be already doable with the format on the example. You could also have a 'since' attribute, in case dynamic interface checks are necessary (e.g., the "Since: 0.14.0" in the example). Lluis -- "And it's much the same thing with knowledge, for whenever you learn something new, the whole world becomes that much richer." -- The Princess of Pure Reason, as told by Norton Juster in The Phantom Tollbooth ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-14 19:52 ` Lluís @ 2011-03-14 20:04 ` Anthony Liguori 0 siblings, 0 replies; 22+ messages in thread From: Anthony Liguori @ 2011-03-14 20:04 UTC (permalink / raw) To: qemu-devel On 03/14/2011 02:52 PM, Lluís wrote: > Anthony Liguori writes: > >> I've got a spec written up at http://wiki.qemu.org/Features/QCFG. Initial code >> is in my QAPI tree. > What about moving the documentation to a 'doc' attribute? > > Thus, instead of the example vnconfig: > > { 'type': 'VncConfig', > 'doc': 'Configuration options for the built-in VNC server.', > 'data': {'address': 'str', > ...} } > > Still, it's not clear to me how attribute documentation shoul dbe > provided: > > 'data': {'address': {'type': 'str', > 'doc': 'The hostname to bind the VNC server to...'}, > ... > } > > Or maybe: > > 'data': {'address': 'str', > 'address.doc': 'The hostname to bind the VNC server to...'}, > ... > } > > But as I suppose these documentation comments are automatically > processes, this might just prove too verbose for no benefit at all, as > introspecting down to the documentation might be already doable with the > format on the example. Exactly. This is the intention--to have the documentation be just as well structured as the JSON. > You could also have a 'since' attribute, in case dynamic interface > checks are necessary (e.g., the "Since: 0.14.0" in the example). Indeed. Regards, Anthony Liguori > Lluis > > -- > "And it's much the same thing with knowledge, for whenever you learn > something new, the whole world becomes that much richer." > -- The Princess of Pure Reason, as told by Norton Juster in The Phantom > Tollbooth > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-14 17:48 [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling Anthony Liguori 2011-03-14 19:52 ` Lluís @ 2011-03-15 10:09 ` Kevin Wolf 2011-03-15 13:27 ` Anthony Liguori 2011-03-15 11:21 ` [Qemu-devel] " Kevin Wolf 2011-03-17 15:22 ` [Qemu-devel] " Markus Armbruster 3 siblings, 1 reply; 22+ messages in thread From: Kevin Wolf @ 2011-03-15 10:09 UTC (permalink / raw) To: Anthony Liguori Cc: Chris Wright, Markus Armbruster, Adam Litke, qemu-devel, Stefan Hajnoczi Am 14.03.2011 18:48, schrieb Anthony Liguori: > As I've been waiting for QAPI review, I've been working on the design of > a new mechanism to replace our current command line option handling > (QemuOpts) with something that reuses the QAPI infrastructure. > > The 'QemuOpts' syntax is just a way to encode complex data structures. > 'nic,model=virtio,macaddress=00:01:02:03:04:05' can be mapped directly > to a C data structure. This is exactly what QCFG does using the same > JSON schema mechanism that QMP uses. > > The effect is that you describe a command line argument in JSON like so: > > { 'type': 'VncConfig', > 'data': { 'address': 'str', '*password': 'bool', '*reverse': 'bool', > '*no-lock-key-sync': 'bool', '*sasl': 'bool', '*tls': 'bool', > '*x509': 'str', '*x509verify': 'str', '*acl': 'bool', > '*lossy': 'bool' } } > > > You then just implement a C function that gets called for each -vnc > option specified: > > void qcfg_handle_vnc(VncConfig *option, Error **errp) > { > } > > And that's it. You can squirrel away the option such that they all can > be processed later, you can perform additional validation and return an > error, or you can implement the appropriate logic. > > The VncConfig structure is a proper C data structure. The advantages of > this approach compared to QemuOpts are similar to QAPI: > > 1) Strong typing means less bugs with lack of command line validation. > In many cases, a bad command line results in a SEGV today. > > 2) Every option is formally specified and documented in a way that is > both rigorous and machine readable. This means we can generate high > quality documentation in a variety of formats. > > 3) The command line parameters support full introspection. This should > provide the same functionality as Dan's earlier introspection patches. > > 4) The 'VncConfig' structure also has JSON marshallers and the > qcfg_handle_vnc() function can be trivially bridged to QMP. This means > command line oriented interfaces (like device_add) are better integrated > with QMP. > > 5) Very complex data types can be implemented. We had some discussion > of supporting nested structures with -blockdev. This wouldn't work with > QemuOpts but I've already implemented it with QCFG (blockdev syntax is > my test case right now). The syntax I'm currently using is -blockdev > cache=none,id=foo,format.qcow.protocol.nbd.hostname=localhost where '.' > is used to reference sub structures. Do you have an example from your implementation for this? I think the tricky part is that the valid fields depend on the block driver. qcow2 wants another BlockDriverState as its image file; file wants a file name; vvfat wants a directory name, FAT type and disk type; and NBD wants a host name and a port, except if it uses a UNIX socket. This is probably the most complex thing you can get, so I think it would make a better example than a VNC configuration. Kevin ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-15 10:09 ` Kevin Wolf @ 2011-03-15 13:27 ` Anthony Liguori 2011-03-15 13:45 ` Kevin Wolf 2011-03-18 18:12 ` Stefan Hajnoczi 0 siblings, 2 replies; 22+ messages in thread From: Anthony Liguori @ 2011-03-15 13:27 UTC (permalink / raw) To: Kevin Wolf Cc: Chris Wright, qemu-devel, Markus Armbruster, Stefan Hajnoczi, Adam Litke On 03/15/2011 05:09 AM, Kevin Wolf wrote: >> 5) Very complex data types can be implemented. We had some discussion >> of supporting nested structures with -blockdev. This wouldn't work with >> QemuOpts but I've already implemented it with QCFG (blockdev syntax is >> my test case right now). The syntax I'm currently using is -blockdev >> cache=none,id=foo,format.qcow.protocol.nbd.hostname=localhost where '.' >> is used to reference sub structures. > Do you have an example from your implementation for this? It's not exhaustive as I'm only using this for testing but here's what I've been working with: { 'type': 'ProbeProtocol', 'data': { 'unsafe': 'bool', 'filename': 'str' } } { 'type': 'FileProtocol', 'data': { 'filename': 'str' } } { 'type': 'HostDeviceProtocol', 'data': { 'device': 'str' } } { 'type': 'NbdProtocol', 'data': { 'hostname': 'str', 'port': 'int' } } { 'union': 'BlockdevProtocol', 'data': { 'probe': 'ProbeProtocol', 'file': 'FileProtocol', 'host-dev': 'HostDeviceProtocol', 'nbd': 'NbdProtocol' } } { 'type': 'ProbeFormat', 'data': { '*unsafe': 'bool', 'protocol': 'BlockdevProtocol' } } { 'type': 'RawFormat', 'data': { 'protocol': 'BlockdevProtocol' } } { 'type': 'Qcow2Format', 'data': { 'protocol': 'BlockdevProtocol', '*backing-file': 'BlockdevFormat' } } { 'type': 'QedFormat', 'data': { 'protocol': 'BlockdevProtocol', '*backing-file': 'BlockdevFormat', '*copy-on-read': 'bool' } } { 'union': 'BlockdevFormat', 'data': { 'probe': 'ProbeFormat', 'raw': 'RawFormat', 'qcow2': 'Qcow2Format', 'qed': 'QedFormat' } } { 'enum': 'BlockdevCacheSetting', 'data': [ 'none', 'writethrough', 'writeback' ] } { 'type': 'BlockdevConfig', 'data': { 'id': 'str', 'format': 'BlockdevFormat', '*cache': 'BlockdevCacheSetting', '*device': 'str' } } { 'option': 'blockdev', 'data': 'BlockdevConfig', 'implicit': 'id' } Choosing a union is implicit in selecting the union value. This was done to simplify the command line. Here are some examples: # create a blockdev using probing -blockdev my-image.qcow2,id=ide0-hd0 # create a blockdev using probing without relying on implicit keys and allowing unsafe probing -blockdev format.probe.unsafe=on,format.probe.protocol.file.filename=my-image.qcow2,id=ide0-hd0 # create a blockdev using qcow2 over NBD with a qed backing file -blockdev format.qcow2.protocol.nbd={hostname=localhost,port=1025},\ format.qcow2.backing-file.format.qed.protocol.nbd={hostname=localhost,port=1026},\ id=ide0-hd0 It looks less awkward in config file format: [blockdev] id = "ide0-hd0" format.qcow2.protocol.nbd.hostname = localhost format.qcow2.protocol.nbd.port = 1025 format.qcow2.backing-file.format.qed.protocol.nbd.hostname = localhost format.qcow2.backing-file.format.qed.protocol.nbd.port = 1026 And with a syntax this complex, errors are important. Here are some examples of Error messages: #./test-qcfg format.qcow2.file.filename="image.img",id=ide0-hd0 -blockdev: Parameter 'format.qcow2.protocol' is missing # ./test-qcfg format.qcow2.protocol.file.filename="image.img",format.qcow3.backing-file.format.qcow2.protocol.file.filename="foo.img",id=ide0-hd0 -blockdev: Invalid parameter 'format.qcow3.backing-file.format.qcow2.protocol.file.filename' #./test-qcfg format.qcow2.protocol.file.filename="image.img",id=ide0-hd0,cache=no-thank-you -blockdev: Enum 'cache' with value 'no-thank-you' is invalid for type 'BlockdevCacheSetting' > I think the tricky part is that the valid fields depend on the block > driver. qcow2 wants another BlockDriverState as its image file; file > wants a file name; vvfat wants a directory name, FAT type and disk type; > and NBD wants a host name and a port, except if it uses a UNIX socket. Yes, it's all handled with a new union type. > This is probably the most complex thing you can get, so I think it would > make a better example than a VNC configuration. Yup, that's been what I've been using to prototype all of this. I didn't it in the mail because it's rather complex :-) Regards, Anthony Liguori > Kevin > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-15 13:27 ` Anthony Liguori @ 2011-03-15 13:45 ` Kevin Wolf 2011-03-15 13:56 ` Anthony Liguori 2011-03-18 18:12 ` Stefan Hajnoczi 1 sibling, 1 reply; 22+ messages in thread From: Kevin Wolf @ 2011-03-15 13:45 UTC (permalink / raw) To: Anthony Liguori Cc: Chris Wright, qemu-devel, Markus Armbruster, Stefan Hajnoczi, Adam Litke Am 15.03.2011 14:27, schrieb Anthony Liguori: > On 03/15/2011 05:09 AM, Kevin Wolf wrote: >>> 5) Very complex data types can be implemented. We had some discussion >>> of supporting nested structures with -blockdev. This wouldn't work with >>> QemuOpts but I've already implemented it with QCFG (blockdev syntax is >>> my test case right now). The syntax I'm currently using is -blockdev >>> cache=none,id=foo,format.qcow.protocol.nbd.hostname=localhost where '.' >>> is used to reference sub structures. >> Do you have an example from your implementation for this? > > It's not exhaustive as I'm only using this for testing but here's what > I've been working with: > > { 'type': 'ProbeProtocol', 'data': { 'unsafe': 'bool', 'filename': 'str' } } > > { 'type': 'FileProtocol', 'data': { 'filename': 'str' } } > > { 'type': 'HostDeviceProtocol', 'data': { 'device': 'str' } } > > { 'type': 'NbdProtocol', 'data': { 'hostname': 'str', 'port': 'int' } } > > { 'union': 'BlockdevProtocol', > 'data': { 'probe': 'ProbeProtocol', 'file': 'FileProtocol', > 'host-dev': 'HostDeviceProtocol', 'nbd': 'NbdProtocol' } } What would this look like in the generated C code? A union of differently typed pointers? Are format drivers still contained in a single C file in block/ that is enabled just by compiling it in or does the block layer now have to know about all available drivers and the options they provide? >> This is probably the most complex thing you can get, so I think it would >> make a better example than a VNC configuration. > > Yup, that's been what I've been using to prototype all of this. I > didn't it in the mail because it's rather complex :-) This is exactly what makes it interesting. :-) Kevin ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-15 13:45 ` Kevin Wolf @ 2011-03-15 13:56 ` Anthony Liguori 0 siblings, 0 replies; 22+ messages in thread From: Anthony Liguori @ 2011-03-15 13:56 UTC (permalink / raw) To: Kevin Wolf Cc: Chris Wright, Adam Litke, qemu-devel, Stefan Hajnoczi, Markus Armbruster On 03/15/2011 08:45 AM, Kevin Wolf wrote: > Am 15.03.2011 14:27, schrieb Anthony Liguori: >> On 03/15/2011 05:09 AM, Kevin Wolf wrote: >>>> 5) Very complex data types can be implemented. We had some discussion >>>> of supporting nested structures with -blockdev. This wouldn't work with >>>> QemuOpts but I've already implemented it with QCFG (blockdev syntax is >>>> my test case right now). The syntax I'm currently using is -blockdev >>>> cache=none,id=foo,format.qcow.protocol.nbd.hostname=localhost where '.' >>>> is used to reference sub structures. >>> Do you have an example from your implementation for this? >> It's not exhaustive as I'm only using this for testing but here's what >> I've been working with: >> >> { 'type': 'ProbeProtocol', 'data': { 'unsafe': 'bool', 'filename': 'str' } } >> >> { 'type': 'FileProtocol', 'data': { 'filename': 'str' } } >> >> { 'type': 'HostDeviceProtocol', 'data': { 'device': 'str' } } >> >> { 'type': 'NbdProtocol', 'data': { 'hostname': 'str', 'port': 'int' } } >> >> { 'union': 'BlockdevProtocol', >> 'data': { 'probe': 'ProbeProtocol', 'file': 'FileProtocol', >> 'host-dev': 'HostDeviceProtocol', 'nbd': 'NbdProtocol' } } > What would this look like in the generated C code? A union of > differently typed pointers? Yes: typedef enum BlockdevFormatKind { BFK_PROBE = 0, BFK_RAW = 1, BFK_QCOW2 = 2, BFK_QED = 3, } BlockdevFormatKind; typedef struct BlockdevFormat { BlockdevFormatKind kind; union { struct ProbeFormat * probe; struct RawFormat * raw; struct Qcow2Format * qcow2; struct QedFormat * qed; }; struct BlockdevFormat * next; } BlockdevFormat; > Are format drivers still contained in a single C file in block/ that is > enabled just by compiling it in or does the block layer now have to know > about all available drivers and the options they provide? Yes, everything is contained within a single file. In terms of build dependencies, it's really just a call about what matters to you. You can have the block open take a BlockdevFormat which means the block layer doesn't need to know about specific formats. Regards, Anthony Liguori >>> This is probably the most complex thing you can get, so I think it would >>> make a better example than a VNC configuration. >> Yup, that's been what I've been using to prototype all of this. I >> didn't it in the mail because it's rather complex :-) > This is exactly what makes it interesting. :-) > > Kevin > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-15 13:27 ` Anthony Liguori 2011-03-15 13:45 ` Kevin Wolf @ 2011-03-18 18:12 ` Stefan Hajnoczi 1 sibling, 0 replies; 22+ messages in thread From: Stefan Hajnoczi @ 2011-03-18 18:12 UTC (permalink / raw) To: Anthony Liguori Cc: Kevin Wolf, Chris Wright, Stefan Hajnoczi, qemu-devel, Markus Armbruster, Adam Litke On Tue, Mar 15, 2011 at 1:27 PM, Anthony Liguori <anthony@codemonkey.ws> wrote: > # create a blockdev using probing without relying on implicit keys and > allowing unsafe probing > -blockdev > format.probe.unsafe=on,format.probe.protocol.file.filename=my-image.qcow2,id=ide0-hd0 This is a programmer's user interface :). I don't think typing format.probe.protocol.file.filename will make sense to users. I don't want to have to learn about 4 sub-objects in order to set the image filename. I would much prefer: -blockdev filename=my-image.qcow2,id=id0-hd0,format=probe > It looks less awkward in config file format: > > [blockdev] > id = "ide0-hd0" > format.qcow2.protocol.nbd.hostname = localhost > format.qcow2.protocol.nbd.port = 1025 > format.qcow2.backing-file.format.qed.protocol.nbd.hostname = localhost > format.qcow2.backing-file.format.qed.protocol.nbd.port = 1026 Here is the alternative if we don't support composition: [blockdev] id = "nbd-1025" protocol = "nbd" hostname = "localhost" port = 1025 [blockdev] id = "nbd-1026" protocol = "nbd" hostname = "localhost" port = 1026 [blockdev] id = "qed-nbd-1026" format = "qed" image = "nbd-1026" [blockdev] id = "ide0-hd0" format = "qcow2" image = "nbd-1025" backing_file = "qed-nbd-1026" I like the simplicity but it becomes so verbose that it is hard to see what is going on. Actually I think it's INI syntax's fault: blockdev id=nbd-1025,protocol=nbd,hostname=localhost,port=1025 blockdev id=nbd-1026,protocol=nbd,hostname=localhost,port=1026 blockdev id=qed-nbd-1026,format=qed,image=nbd-1026 blockdev id=ide0-hd0,format=qcow2,image=nbd-1025,backing_file=qed-nbd-1026 Perhaps the best compromise is to keep composition but implement shortcuts as suggested by Markus. Stefan ^ permalink raw reply [flat|nested] 22+ messages in thread
* [Qemu-devel] Re: [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-14 17:48 [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling Anthony Liguori 2011-03-14 19:52 ` Lluís 2011-03-15 10:09 ` Kevin Wolf @ 2011-03-15 11:21 ` Kevin Wolf 2011-03-15 13:37 ` Anthony Liguori 2011-03-17 15:22 ` [Qemu-devel] " Markus Armbruster 3 siblings, 1 reply; 22+ messages in thread From: Kevin Wolf @ 2011-03-15 11:21 UTC (permalink / raw) To: Anthony Liguori Cc: Chris Wright, Stefan Hajnoczi, qemu-devel, Markus Armbruster, Adam Litke Am 14.03.2011 18:48, schrieb Anthony Liguori: > I've got a spec written up at http://wiki.qemu.org/Features/QCFG. > Initial code is in my QAPI tree. One question about a small detail on this wiki page: > typedef struct BlockdevConfig { > char * file; > struct BlockdevConfig * backing_file; > > struct BlockdevConfig * next; > } BlockdevConfig; What is the 'next' pointer used for, are you going to store a list of all -blockdev options used? And why isn't it a QLIST or something? Kevin ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] Re: [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-15 11:21 ` [Qemu-devel] " Kevin Wolf @ 2011-03-15 13:37 ` Anthony Liguori 2011-03-15 13:51 ` Kevin Wolf 0 siblings, 1 reply; 22+ messages in thread From: Anthony Liguori @ 2011-03-15 13:37 UTC (permalink / raw) To: Kevin Wolf Cc: Chris Wright, Markus Armbruster, Adam Litke, Stefan Hajnoczi, qemu-devel On 03/15/2011 06:21 AM, Kevin Wolf wrote: > Am 14.03.2011 18:48, schrieb Anthony Liguori: >> I've got a spec written up at http://wiki.qemu.org/Features/QCFG. >> Initial code is in my QAPI tree. > One question about a small detail on this wiki page: > >> typedef struct BlockdevConfig { >> char * file; >> struct BlockdevConfig * backing_file; >> >> struct BlockdevConfig * next; >> } BlockdevConfig; > What is the 'next' pointer used for, This is a standard part of QAPI. All types get a next pointer added such that we can support lists of complex types. > are you going to store a list of > all -blockdev options used? And why isn't it a QLIST or something? Two reasons. QLIST requires another type for the head of the list which would complicate things overall. Second is that these types are part of the libqmp interface and I didn't want to force qemu-queue on any consumer of libqmp. Regards, Anthony Liguori > Kevin > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] Re: [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-15 13:37 ` Anthony Liguori @ 2011-03-15 13:51 ` Kevin Wolf 2011-03-17 15:26 ` Markus Armbruster 0 siblings, 1 reply; 22+ messages in thread From: Kevin Wolf @ 2011-03-15 13:51 UTC (permalink / raw) To: Anthony Liguori Cc: Chris Wright, Markus Armbruster, Adam Litke, Stefan Hajnoczi, qemu-devel Am 15.03.2011 14:37, schrieb Anthony Liguori: > On 03/15/2011 06:21 AM, Kevin Wolf wrote: >> Am 14.03.2011 18:48, schrieb Anthony Liguori: >>> I've got a spec written up at http://wiki.qemu.org/Features/QCFG. >>> Initial code is in my QAPI tree. >> One question about a small detail on this wiki page: >> >>> typedef struct BlockdevConfig { >>> char * file; >>> struct BlockdevConfig * backing_file; >>> >>> struct BlockdevConfig * next; >>> } BlockdevConfig; >> What is the 'next' pointer used for, > > This is a standard part of QAPI. All types get a next pointer added > such that we can support lists of complex types. Only a single list for each object. >> are you going to store a list of >> all -blockdev options used? And why isn't it a QLIST or something? > > Two reasons. QLIST requires another type for the head of the list which > would complicate things overall. Second is that these types are part of > the libqmp interface and I didn't want to force qemu-queue on any > consumer of libqmp. And now you force existing qemu code to go back to open coded lists, which is arguably a step backwards. I don't think this is any better than forcing the (non-existent) users of libqmp to include one additional header file. Kevin ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] Re: [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-15 13:51 ` Kevin Wolf @ 2011-03-17 15:26 ` Markus Armbruster 2011-03-18 4:12 ` Anthony Liguori 0 siblings, 1 reply; 22+ messages in thread From: Markus Armbruster @ 2011-03-17 15:26 UTC (permalink / raw) To: Kevin Wolf; +Cc: Chris Wright, qemu-devel, Stefan Hajnoczi, Adam Litke Kevin Wolf <kwolf@redhat.com> writes: > Am 15.03.2011 14:37, schrieb Anthony Liguori: >> On 03/15/2011 06:21 AM, Kevin Wolf wrote: >>> Am 14.03.2011 18:48, schrieb Anthony Liguori: >>>> I've got a spec written up at http://wiki.qemu.org/Features/QCFG. >>>> Initial code is in my QAPI tree. >>> One question about a small detail on this wiki page: >>> >>>> typedef struct BlockdevConfig { >>>> char * file; >>>> struct BlockdevConfig * backing_file; >>>> >>>> struct BlockdevConfig * next; >>>> } BlockdevConfig; >>> What is the 'next' pointer used for, >> >> This is a standard part of QAPI. All types get a next pointer added >> such that we can support lists of complex types. > > Only a single list for each object. Don't even think of trees. Yuck. >>> are you going to store a list of >>> all -blockdev options used? And why isn't it a QLIST or something? >> >> Two reasons. QLIST requires another type for the head of the list which >> would complicate things overall. Second is that these types are part of >> the libqmp interface and I didn't want to force qemu-queue on any >> consumer of libqmp. > > And now you force existing qemu code to go back to open coded lists, > which is arguably a step backwards. I don't think this is any better > than forcing the (non-existent) users of libqmp to include one > additional header file. Agree. Let's not screw up our internal interfaces for the sake of an external interface that may or may not turn out to be viable. ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] Re: [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-17 15:26 ` Markus Armbruster @ 2011-03-18 4:12 ` Anthony Liguori 2011-03-18 13:07 ` Markus Armbruster 0 siblings, 1 reply; 22+ messages in thread From: Anthony Liguori @ 2011-03-18 4:12 UTC (permalink / raw) To: Markus Armbruster Cc: Kevin Wolf, Chris Wright, Adam Litke, qemu-devel, Stefan Hajnoczi On 03/17/2011 10:26 AM, Markus Armbruster wrote: > Kevin Wolf<kwolf@redhat.com> writes: > >> Am 15.03.2011 14:37, schrieb Anthony Liguori: >>> On 03/15/2011 06:21 AM, Kevin Wolf wrote: >>>> Am 14.03.2011 18:48, schrieb Anthony Liguori: >>>>> I've got a spec written up at http://wiki.qemu.org/Features/QCFG. >>>>> Initial code is in my QAPI tree. >>>> One question about a small detail on this wiki page: >>>> >>>>> typedef struct BlockdevConfig { >>>>> char * file; >>>>> struct BlockdevConfig * backing_file; >>>>> >>>>> struct BlockdevConfig * next; >>>>> } BlockdevConfig; >>>> What is the 'next' pointer used for, >>> This is a standard part of QAPI. All types get a next pointer added >>> such that we can support lists of complex types. >> Only a single list for each object. > Don't even think of trees. Yuck. Sorry, don't fully understand. The above data structure is a tree. I haven't looked yet at converting the code generator to use the qemu-queue structures. I'm not sure I agree it's the right thing to do but I don't think it's all that hard. Regards, Anthony Liguori ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] Re: [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-18 4:12 ` Anthony Liguori @ 2011-03-18 13:07 ` Markus Armbruster 0 siblings, 0 replies; 22+ messages in thread From: Markus Armbruster @ 2011-03-18 13:07 UTC (permalink / raw) To: Anthony Liguori Cc: Kevin Wolf, Chris Wright, qemu-devel, Stefan Hajnoczi, Adam Litke Anthony Liguori <anthony@codemonkey.ws> writes: > On 03/17/2011 10:26 AM, Markus Armbruster wrote: >> Kevin Wolf<kwolf@redhat.com> writes: >> >>> Am 15.03.2011 14:37, schrieb Anthony Liguori: >>>> On 03/15/2011 06:21 AM, Kevin Wolf wrote: >>>>> Am 14.03.2011 18:48, schrieb Anthony Liguori: >>>>>> I've got a spec written up at http://wiki.qemu.org/Features/QCFG. >>>>>> Initial code is in my QAPI tree. >>>>> One question about a small detail on this wiki page: >>>>> >>>>>> typedef struct BlockdevConfig { >>>>>> char * file; >>>>>> struct BlockdevConfig * backing_file; >>>>>> >>>>>> struct BlockdevConfig * next; >>>>>> } BlockdevConfig; >>>>> What is the 'next' pointer used for, >>>> This is a standard part of QAPI. All types get a next pointer added >>>> such that we can support lists of complex types. >>> Only a single list for each object. >> Don't even think of trees. Yuck. > > Sorry, don't fully understand. The above data structure is a tree. The next pointer that is "a standard part of QAPI" provides for a single list membership, no more. The criticism isn't that you can't build trees in QAPI (you can). It's that there's one special list. Doesn't feel right for a general purpose interface. But it really depends on how it's used. Anyway, this belongs into the QAPI thread, not here. > I haven't looked yet at converting the code generator to use the > qemu-queue structures. I'm not sure I agree it's the right thing to > do but I don't think it's all that hard. Depends on how the list is used. If all uses are known (say because they're all behind the interface), and limited to trivial pointer chasing, then qemu-queue.h may well be more trouble than it's worth. ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-14 17:48 [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling Anthony Liguori ` (2 preceding siblings ...) 2011-03-15 11:21 ` [Qemu-devel] " Kevin Wolf @ 2011-03-17 15:22 ` Markus Armbruster 2011-03-17 18:28 ` Anthony Liguori 3 siblings, 1 reply; 22+ messages in thread From: Markus Armbruster @ 2011-03-17 15:22 UTC (permalink / raw) To: Anthony Liguori Cc: Kevin Wolf, Chris Wright, Adam Litke, qemu-devel, Stefan Hajnoczi Anthony Liguori <anthony@codemonkey.ws> writes: > As I've been waiting for QAPI review, I've been working on the design > of a new mechanism to replace our current command line option handling > (QemuOpts) with something that reuses the QAPI infrastructure. I'm ignoring the connection to QAPI, because I'm still ignorant there. > The 'QemuOpts' syntax is just a way to encode complex data structures. > nic,model=virtio,macaddress=00:01:02:03:04:05' can be mapped directly > to a C data structure. This is exactly what QCFG does using the same > JSON schema mechanism that QMP uses. > > The effect is that you describe a command line argument in JSON like so: > > { 'type': 'VncConfig', > 'data': { 'address': 'str', '*password': 'bool', '*reverse': 'bool', > '*no-lock-key-sync': 'bool', '*sasl': 'bool', '*tls': 'bool', > '*x509': 'str', '*x509verify': 'str', '*acl': 'bool', > '*lossy': 'bool' } } > > > You then just implement a C function that gets called for each -vnc > option specified: > > void qcfg_handle_vnc(VncConfig *option, Error **errp) > { > } > > And that's it. You can squirrel away the option such that they all > can be processed later, you can perform additional validation and > return an error, or you can implement the appropriate logic. > > The VncConfig structure is a proper C data structure. The advantages > of this approach compared to QemuOpts are similar to QAPI: > > 1) Strong typing means less bugs with lack of command line validation. > In many cases, a bad command line results in a SEGV today. Static typing, you mean. > 2) Every option is formally specified and documented in a way that is > both rigorous and machine readable. This means we can generate high > quality documentation in a variety of formats. You make it sound like QemuOpts wouldn't specify options. It does. Where your proposal differs from QemuOpts, in my opinion, is that it uses C types rather than QemuOpts' very own ad hoc type system, and is more expressive, see your 6) below. > 3) The command line parameters support full introspection. This > should provide the same functionality as Dan's earlier introspection > patches. Again, this isn't something QemuOpts could not do. We just neglected to make its option specification available outside QEMU, mostly because for lack of consensus on what we want to expose there. > 4) The 'VncConfig' structure also has JSON marshallers and the > qcfg_handle_vnc() function can be trivially bridged to QMP. This > means command line oriented interfaces (like device_add) are better > integrated with QMP. Yes. > 5) Very complex data types can be implemented. We had some discussion > of supporting nested structures with -blockdev. This wouldn't work > with QemuOpts but I've already implemented it with QCFG (blockdev > syntax is my test case right now). The syntax I'm currently using is > -blockdev > cache=none,id=foo,format.qcow.protocol.nbd.hostname=localhost where > .' is used to reference sub structures. We might want some syntactic sugar so that users don't have to repeat format.qcow.protocol.nbd.FOO for every FOO they want to configure. > 6) Unions are supported which means complex options like -net can be > specified in the schema and don't require hand validation. > > I've got a spec written up at http://wiki.qemu.org/Features/QCFG. Not reachable right now. > Initial code is in my QAPI tree. > > I'm not going to start converting things until we get closer to the > end of 0.15 and QAPI is fully merged. My plan is to focus on this for > 0.16 and do a full conversion for the 0.16 time frame using the same > approach as QAPI. That means that for 0.16, we would be able to set > all command line options via QMP in a programmatic fashion with full > support for introspection. > > I'm haven't yet closed on how to bridge this to qdev. qdev is a big > consumer of QemuOpts today. I have some general ideas about what I'd > like to do but so far, I haven't written anything down. Glad you mention qdev. qdev properties describe the configurable parts of qdev state objects. A state object is a C struct. The description is C data. Poor man's reflection. qdev needs to parse and print a device's properties. It uses QemuOpts to parse NAME=VALUE,... option strings into a list of (NAME, VALUE), and callbacks to parse the VALUEs. Awkward, especially when you go QMP -> option string -> qdev struct. Yet another one: vmstate, which describes migratable parts of qdev state objects. Unlike these two, QCFG doesn't describe C structs, it generates them from JSON specs. If I understand your proposal correctly. Hmm. Can we avoid defining our data types in JSON rather than C? Can we adopt *one* reflection mechanism? > I wanted to share these plans early hoping to get some feedback and > also to maybe interest some folks in helping out. There's also QEMUOptionParameter, thankfully limited to block code. Any plans to cover that as well? Hmm, my nitpicking and questioning may sound like I don't like the idea of replacing QemuOpts. That's actually not the case. ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-17 15:22 ` [Qemu-devel] " Markus Armbruster @ 2011-03-17 18:28 ` Anthony Liguori 2011-03-18 9:44 ` Kevin Wolf 2011-03-18 14:04 ` Markus Armbruster 0 siblings, 2 replies; 22+ messages in thread From: Anthony Liguori @ 2011-03-17 18:28 UTC (permalink / raw) To: Markus Armbruster Cc: Kevin Wolf, Chris Wright, qemu-devel, Stefan Hajnoczi, Adam Litke On 03/17/2011 10:22 AM, Markus Armbruster wrote: > >> void qcfg_handle_vnc(VncConfig *option, Error **errp) >> { >> } >> >> And that's it. You can squirrel away the option such that they all >> can be processed later, you can perform additional validation and >> return an error, or you can implement the appropriate logic. >> >> The VncConfig structure is a proper C data structure. The advantages >> of this approach compared to QemuOpts are similar to QAPI: >> >> 1) Strong typing means less bugs with lack of command line validation. >> In many cases, a bad command line results in a SEGV today. > Static typing, you mean. Both. We don't consistently check error returns when dealing with QemuOpts so while it does implement dynamic typing, the dynamic typing is not consistently enforced. >> 2) Every option is formally specified and documented in a way that is >> both rigorous and machine readable. This means we can generate high >> quality documentation in a variety of formats. > You make it sound like QemuOpts wouldn't specify options. It does. > Where your proposal differs from QemuOpts, in my opinion, is that it > uses C types rather than QemuOpts' very own ad hoc type system, and is > more expressive, see your 6) below. My point in that bullet is that the documentation is machine readable and highly structured. >> 3) The command line parameters support full introspection. This >> should provide the same functionality as Dan's earlier introspection >> patches. > Again, this isn't something QemuOpts could not do. We just neglected to > make its option specification available outside QEMU, mostly because for > lack of consensus on what we want to expose there. It's all just a matter of bits, right :-) But QemuOpts is just about parsing a string, it doesn't really explicitly tie to a command line. Another layer would be needed to clearly map that option foo is associated with this QemuOpts. So yeah, it could grow in that direction but there's a lot more to it then just returning a list of names and ties given a qemu opts definition. >> 5) Very complex data types can be implemented. We had some discussion >> of supporting nested structures with -blockdev. This wouldn't work >> with QemuOpts but I've already implemented it with QCFG (blockdev >> syntax is my test case right now). The syntax I'm currently using is >> -blockdev >> cache=none,id=foo,format.qcow.protocol.nbd.hostname=localhost where >> .' is used to reference sub structures. > We might want some syntactic sugar so that users don't have to repeat > format.qcow.protocol.nbd.FOO for every FOO they want to configure. -set will still function in this model as key/value parsing and translation to a C structure are two separate steps. But yeah, I think we ought to brain storm ways to simplify blockdev because the structure is very complex. But that's a separate discussion. >> Initial code is in my QAPI tree. >> >> I'm not going to start converting things until we get closer to the >> end of 0.15 and QAPI is fully merged. My plan is to focus on this for >> 0.16 and do a full conversion for the 0.16 time frame using the same >> approach as QAPI. That means that for 0.16, we would be able to set >> all command line options via QMP in a programmatic fashion with full >> support for introspection. >> >> I'm haven't yet closed on how to bridge this to qdev. qdev is a big >> consumer of QemuOpts today. I have some general ideas about what I'd >> like to do but so far, I haven't written anything down. > Glad you mention qdev. > > qdev properties describe the configurable parts of qdev state objects. > A state object is a C struct. The description is C data. Poor man's > reflection. Right. The problem is it's very hard to reflect C even if you parse it without additional annotations. For instance: typedef struct Foo { Bar *bar; } Foo; What the type of bar is is ambigious. It could be a pointer to a list of Bar's (if bar has an embedded pointer), it could be an array of Bars that is terminated using a field within Bar, it could be a pointer to a fixed size array of Bars, or it could be just a pointer to a single Bar object. So you end up needing additional annotations like: typedef struct Foo { size_t n_bar; Bar *bar sizeis(n_bar); } Foo; This is what most IDLs that use C style syntax do. > qdev needs to parse and print a device's properties. It uses QemuOpts > to parse NAME=VALUE,... option strings into a list of (NAME, VALUE), and > callbacks to parse the VALUEs. Awkward, especially when you go QMP -> > option string -> qdev struct. Indeed. You can do very weird things like pass a float through qdev and it appears as a (valid) string type with QemuOpts. But qdev properties are a little odder semantically. qdev properties are construct-only and read-only. They are construct-only because they are set implicitly in the object before its created as a sort of default value. It's easy to bridge QCFG/QMP to this but I've been thinking of going a step further. I've been thinking of taking the current qdev properties and making them proper construction properties and removing the magic involved in setting their default value. This would require the code generator to create a construction function that is called such as: DeviceState *isa_serial_init(int iobase, int irq, CharDriverState *chr) { ISADeviceState *dev = qdev_alloc(&isa_serial_desc); dev->iobase = iobase; dev->irq = irq; dev->chr = chr; // ... } There would be a separate interface for getting/setting properties. It might even be something as simple as, you have to implement: int isa_serial_get_iobase(ISADeviceState *dev); int isa_serial_get_irq(ISADeviceState *dev); ... This ends up being a powerful interface because you can easily get these properties within QEMU, but you also (transparently) map these to the wire. It also extends really nicely for setting properties which becomes an interesting way to implement dynamic device logic (like setting the link status of a network card). > Yet another one: vmstate, which describes migratable parts of qdev state > objects. Yes, for now, I'm ignoring vmstate. To really solve vmstate, I think you need to describe the complete object instead of just it's properties. > Unlike these two, QCFG doesn't describe C structs, it generates them > from JSON specs. If I understand your proposal correctly. Hmm. Correct. > Can we avoid defining our data types in JSON rather than C? I didn't start with describing them in JSON. I started by describing them in Python with the idea that I'd write a IDL parser using a C style syntax that would then generate the Python structures. Using the fixed Python data structures, I could play with code generation without all the trouble of writing a full blown IDL parser. But I never got around to writing that parser and have actually become fond of the Python/JSON style syntax. > Can we adopt *one* reflection mechanism? Yes, I think we should start with that being the JSON syntax I'm using for QAPI/QCFG and then down the road, we can introduce a totally new syntax if need be. But I think we need a central schema that describes all externally visible interfaces. I think this is really the key idea here. >> I wanted to share these plans early hoping to get some feedback and >> also to maybe interest some folks in helping out. > There's also QEMUOptionParameter, thankfully limited to block code. Any > plans to cover that as well? QCFG will replace this because the BlockdevConfig structure can be passed throughout the block layer to enable block format specific options to be implemented. QEMUOptionParameter is just used for bdrv_create today right? Do we want create options to be different than run time options or would a superset of the two be appropriate? Regards, Anthony Liguori ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-17 18:28 ` Anthony Liguori @ 2011-03-18 9:44 ` Kevin Wolf 2011-03-18 14:04 ` Markus Armbruster 1 sibling, 0 replies; 22+ messages in thread From: Kevin Wolf @ 2011-03-18 9:44 UTC (permalink / raw) To: Anthony Liguori Cc: Chris Wright, qemu-devel, Markus Armbruster, Stefan Hajnoczi, Adam Litke Am 17.03.2011 19:28, schrieb Anthony Liguori: > On 03/17/2011 10:22 AM, Markus Armbruster wrote: >>> I wanted to share these plans early hoping to get some feedback and >>> also to maybe interest some folks in helping out. >> There's also QEMUOptionParameter, thankfully limited to block code. Any >> plans to cover that as well? > > QCFG will replace this because the BlockdevConfig structure can be > passed throughout the block layer to enable block format specific > options to be implemented. > > QEMUOptionParameter is just used for bdrv_create today right? I think so. I created it for bdrv_create, and when Gerd needed something similar not much later, it turned out that we wanted to add some more features. So Gerd turned it into QemuOpts, but left the old interface instead of replacing it in the existing users. Everything newer than that should be using QemuOpts. > Do we > want create options to be different than run time options or would a > superset of the two be appropriate? Run time options would be things like cache mode etc.? I don't think we have an image format that can store these, so it doesn't make a lot of sense to provide them in bdrv_create. For opening an image, I think we have three levels of options, in this order: Defaults that are set by the block driver, options that are read from an image, options specified on the command line. It seems to make sense to allow this for any option. Kevin ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-17 18:28 ` Anthony Liguori 2011-03-18 9:44 ` Kevin Wolf @ 2011-03-18 14:04 ` Markus Armbruster 2011-03-18 22:39 ` Anthony Liguori 1 sibling, 1 reply; 22+ messages in thread From: Markus Armbruster @ 2011-03-18 14:04 UTC (permalink / raw) To: Anthony Liguori Cc: Kevin Wolf, Chris Wright, Adam Litke, qemu-devel, Stefan Hajnoczi Anthony Liguori <anthony@codemonkey.ws> writes: > On 03/17/2011 10:22 AM, Markus Armbruster wrote: >> >>> void qcfg_handle_vnc(VncConfig *option, Error **errp) >>> { >>> } >>> >>> And that's it. You can squirrel away the option such that they all >>> can be processed later, you can perform additional validation and >>> return an error, or you can implement the appropriate logic. >>> >>> The VncConfig structure is a proper C data structure. The advantages >>> of this approach compared to QemuOpts are similar to QAPI: >>> >>> 1) Strong typing means less bugs with lack of command line validation. >>> In many cases, a bad command line results in a SEGV today. >> Static typing, you mean. > > Both. We don't consistently check error returns when dealing with > QemuOpts so while it does implement dynamic typing, the dynamic typing > is not consistently enforced. Yes. Dynamic typing in C takes a lot of discipline. >>> 2) Every option is formally specified and documented in a way that is >>> both rigorous and machine readable. This means we can generate high >>> quality documentation in a variety of formats. >> You make it sound like QemuOpts wouldn't specify options. It does. >> Where your proposal differs from QemuOpts, in my opinion, is that it >> uses C types rather than QemuOpts' very own ad hoc type system, and is >> more expressive, see your 6) below. > > My point in that bullet is that the documentation is machine readable > and highly structured. > >>> 3) The command line parameters support full introspection. This >>> should provide the same functionality as Dan's earlier introspection >>> patches. >> Again, this isn't something QemuOpts could not do. We just neglected to >> make its option specification available outside QEMU, mostly because for >> lack of consensus on what we want to expose there. > > It's all just a matter of bits, right :-) > > But QemuOpts is just about parsing a string, it doesn't really > explicitly tie to a command line. Another layer would be needed to > clearly map that option foo is associated with this QemuOpts. > > So yeah, it could grow in that direction but there's a lot more to it > then just returning a list of names and ties given a qemu opts > definition. > > >>> 5) Very complex data types can be implemented. We had some discussion >>> of supporting nested structures with -blockdev. This wouldn't work >>> with QemuOpts but I've already implemented it with QCFG (blockdev >>> syntax is my test case right now). The syntax I'm currently using is >>> -blockdev >>> cache=none,id=foo,format.qcow.protocol.nbd.hostname=localhost where >>> .' is used to reference sub structures. >> We might want some syntactic sugar so that users don't have to repeat >> format.qcow.protocol.nbd.FOO for every FOO they want to configure. > > -set will still function in this model as key/value parsing and > translation to a C structure are two separate steps. > > But yeah, I think we ought to brain storm ways to simplify blockdev > because the structure is very complex. But that's a separate > discussion. Fair enough. >>> Initial code is in my QAPI tree. >>> >>> I'm not going to start converting things until we get closer to the >>> end of 0.15 and QAPI is fully merged. My plan is to focus on this for >>> 0.16 and do a full conversion for the 0.16 time frame using the same >>> approach as QAPI. That means that for 0.16, we would be able to set >>> all command line options via QMP in a programmatic fashion with full >>> support for introspection. >>> >>> I'm haven't yet closed on how to bridge this to qdev. qdev is a big >>> consumer of QemuOpts today. I have some general ideas about what I'd >>> like to do but so far, I haven't written anything down. >> Glad you mention qdev. >> >> qdev properties describe the configurable parts of qdev state objects. >> A state object is a C struct. The description is C data. Poor man's >> reflection. > > Right. The problem is it's very hard to reflect C even if you parse > it without additional annotations. For instance: > > typedef struct Foo { > Bar *bar; > } Foo; > > What the type of bar is is ambigious. It could be a pointer to a list > of Bar's (if bar has an embedded pointer), it could be an array of > Bars that is terminated using a field within Bar, it could be a > pointer to a fixed size array of Bars, or it could be just a pointer > to a single Bar object. > > So you end up needing additional annotations like: > > typedef struct Foo { > size_t n_bar; > Bar *bar sizeis(n_bar); > } Foo; > > This is what most IDLs that use C style syntax do. We currently use a more low-tech approach: define the struct in plain C, and the data describing the struct in plain C as well. Information about the type is in two places and in two formats (C type declaration and C data intializer). There's a bit of redundancy. Ensuring consistency requires preprocessor hackery. The data doesn't have to describe all struct members. I'm inclined to regard that as a feature. Avoids generating source code. >> qdev needs to parse and print a device's properties. It uses QemuOpts >> to parse NAME=VALUE,... option strings into a list of (NAME, VALUE), and >> callbacks to parse the VALUEs. Awkward, especially when you go QMP -> >> option string -> qdev struct. > > Indeed. You can do very weird things like pass a float through qdev > and it appears as a (valid) string type with QemuOpts. > > But qdev properties are a little odder semantically. qdev properties > are construct-only and read-only. They are construct-only because > they are set implicitly in the object before its created as a sort of > default value. > > It's easy to bridge QCFG/QMP to this but I've been thinking of going a > step further. I've been thinking of taking the current qdev > properties and making them proper construction properties and removing > the magic involved in setting their default value. This would require > the code generator to create a construction function that is called > such as: > > DeviceState *isa_serial_init(int iobase, int irq, CharDriverState *chr) > { > ISADeviceState *dev = qdev_alloc(&isa_serial_desc); > > dev->iobase = iobase; > dev->irq = irq; > dev->chr = chr; > // ... > } > > There would be a separate interface for getting/setting properties. > It might even be something as simple as, you have to implement: > > int isa_serial_get_iobase(ISADeviceState *dev); > int isa_serial_get_irq(ISADeviceState *dev); > ... To be honest, I'm wary of generating lots of C source. Prone to become a pain to debug and change. Generated code tends to be much worse than data in that regard. If you generate significant parts of your program with other programs, chances are that you're not using the right language for the job. > This ends up being a powerful interface because you can easily get > these properties within QEMU, but you also (transparently) map these > to the wire. It also extends really nicely for setting properties > which becomes an interesting way to implement dynamic device logic > (like setting the link status of a network card). Well, the traditional way to "get properties" within QEMU is the ol' -> operator. I'm not sure I get the rest of the paragraph. >> Yet another one: vmstate, which describes migratable parts of qdev state >> objects. > > Yes, for now, I'm ignoring vmstate. To really solve vmstate, I think > you need to describe the complete object instead of just it's > properties. Would be nice to have a solution that can cover all our reflection needs, including vmstate. But we need to watch out not to get bogged down in the Generality Swamps. >> Unlike these two, QCFG doesn't describe C structs, it generates them >> from JSON specs. If I understand your proposal correctly. Hmm. > > Correct. > >> Can we avoid defining our data types in JSON rather than C? > > I didn't start with describing them in JSON. I started by describing > them in Python with the idea that I'd write a IDL parser using a C > style syntax that would then generate the Python structures. Using > the fixed Python data structures, I could play with code generation > without all the trouble of writing a full blown IDL parser. > > But I never got around to writing that parser and have actually become > fond of the Python/JSON style syntax. Well, let's say "fondness" isn't what I'd expect ordinary people to feel when confonted with the idea to define half our structs in Python rather than C ;) >> Can we adopt *one* reflection mechanism? > > Yes, I think we should start with that being the JSON syntax I'm using > for QAPI/QCFG and then down the road, we can introduce a totally new > syntax if need be. But I think we need a central schema that > describes all externally visible interfaces. I think this is really > the key idea here. Yes, self-describing external interfaces are desirable. Speaking of external interfaces: we need to be careful when exposing internal data types externally. If we couple our external interfaces too closely to internals, we risk calcifying our internal interfaces. qdev has gotten that partly right: you can change the state structs pretty freely without affecting the externally visible properties. [...] ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-18 14:04 ` Markus Armbruster @ 2011-03-18 22:39 ` Anthony Liguori 2011-03-22 13:01 ` Markus Armbruster 0 siblings, 1 reply; 22+ messages in thread From: Anthony Liguori @ 2011-03-18 22:39 UTC (permalink / raw) To: Markus Armbruster Cc: Kevin Wolf, Chris Wright, qemu-devel, Stefan Hajnoczi, Adam Litke On 03/18/2011 09:04 AM, Markus Armbruster wrote: >>>> Initial code is in my QAPI tree. >>>> >>>> I'm not going to start converting things until we get closer to the >>>> end of 0.15 and QAPI is fully merged. My plan is to focus on this for >>>> 0.16 and do a full conversion for the 0.16 time frame using the same >>>> approach as QAPI. That means that for 0.16, we would be able to set >>>> all command line options via QMP in a programmatic fashion with full >>>> support for introspection. >>>> >>>> I'm haven't yet closed on how to bridge this to qdev. qdev is a big >>>> consumer of QemuOpts today. I have some general ideas about what I'd >>>> like to do but so far, I haven't written anything down. >>> Glad you mention qdev. >>> >>> qdev properties describe the configurable parts of qdev state objects. >>> A state object is a C struct. The description is C data. Poor man's >>> reflection. >> Right. The problem is it's very hard to reflect C even if you parse >> it without additional annotations. For instance: >> >> typedef struct Foo { >> Bar *bar; >> } Foo; >> >> What the type of bar is is ambigious. It could be a pointer to a list >> of Bar's (if bar has an embedded pointer), it could be an array of >> Bars that is terminated using a field within Bar, it could be a >> pointer to a fixed size array of Bars, or it could be just a pointer >> to a single Bar object. >> >> So you end up needing additional annotations like: >> >> typedef struct Foo { >> size_t n_bar; >> Bar *bar sizeis(n_bar); >> } Foo; >> >> This is what most IDLs that use C style syntax do. > We currently use a more low-tech approach: define the struct in plain C, > and the data describing the struct in plain C as well. > > Information about the type is in two places and in two formats (C type > declaration and C data intializer). There's a bit of redundancy. > Ensuring consistency requires preprocessor hackery. I've explored this to what I believe it's limit without crossing into extreme non-portability. I think the best you can possibly do is a scheme like the following: typedef struct MyStruct { int x; int y; MyOtherStruct *mo_list; MyStruct *next; } MyStruct; TypeInfo type_into_MyStruct = { .name = "MyStruct", .params = { DEFINE_PARAM(MyStruct, int, x), DEFINE_PARAM(MyStruct, int, y), DEFINE_LIST(MyStruct, MyOther, mo_list), DEFINE_NEXT(MyStruct, next), DEFINE_EOL(), }, }; But there is absolutely no type safety here. You can confuse the type of mo_list as an int and you'll get no errors. To get type safety, you need to have macros for each type. However, this makes it very difficult to support things like lists of lists or anything else that would basically require a non-concrete type. The basic problem here is that we're mixing how to transverse a data structure with how to describe a data structure. If you separate those two things, it becomes cleaner: void marshal_MyStruct(MyStruct *obj, const char *name) { marshal_start_struct("MyStruct", name); marshal_int(&obj->x, "x"); marshal_int(&obj->y, "y"); marshal_start_list("mo_list"); for (MyOtherStruct *i = obj->mo_list; i; i = i->next) { marshal_MyOtherStruct(i, ""); } marshal_end_list(); marshal_end_struct(); } This generalizes very well to almost arbitrarily complicated data structures and is really almost as compact as the data structure version. It also has very strong type safety. But it's not good enough because C doesn't have exceptions so if a marshal error occurs, you need to propagate it. So then you get: void marshal_MyStruct(MyStruct *obj, const char *name, Error **errp) { marshal_start_struct("MyStruct", name, errp); if (error_is_set(errp)) { return; } marshal_int(&obj->x, "x", errp); ... } Unwinding can be a bit challenging too without destructors assuming that some of the marshalling routines allocate state (they will for lists). But this provides an extremely nice marshaller/unmarshaller. You get high quality errors and you can support arbitrarily complex objects. This is what qmp-gen.py creates for the qmp-marshal-types module. But it creates quite a bit more. Even if you write the above by hand (or use struct to describe it), these types are complex and cannot be free'd with a simple free call. So you also need a function to free each type. If you plan to expose these types in a library, you need to either explicitly pad each structure and make sure that the padding is updated correctly each time a new member is added. Alternatively, you can add an allocation function that automatically pads each structure transparently. qmp-gen.py creates qmp-types.[ch] to do exactly the above and also generates the type declaration so that you don't have to duplicate the type marshalling code and the type declaration. Today, this is close to 2k LOCs so it's actually a significant amount of code code. There is also the code that takes the input (via QCFG or QMP) and calls an appropriate C function with a strongly typed argument. I've played with libffi here to try to do this dynamically but you still lose strong typing because there's no (obvious) way to check the types of a function pointer against a dynamic type description at compile time. I've tried to do some serious preprocessor-fu here to make it work but have failed. Finally, on the libqmp side, you need to code that takes C arguments and calls the appropriate marshalling routines to build the variant types. This may not seem relevant with QCFG but as we make command line options configurable via QMP, it becomes important. > The data doesn't have to describe all struct members. I'm inclined to > regard that as a feature. > > Avoids generating source code. I don't think we really can and still preserve type safety. All the techniques I see to do it without code generation also mean we still have to write a fair bit of code by hand. We could actually avoid it if we were using C++ because templates (particularly with specialization) are pretty much what the code generator is making up for. But yeah, I'm not going to even try to make that suggestion :-) It's still hard to do a proper libqmp even with C++ FWIW. >> There would be a separate interface for getting/setting properties. >> It might even be something as simple as, you have to implement: >> >> int isa_serial_get_iobase(ISADeviceState *dev); >> int isa_serial_get_irq(ISADeviceState *dev); >> ... > To be honest, I'm wary of generating lots of C source. Prone to become > a pain to debug and change. Generated code tends to be much worse than > data in that regard. > > If you generate significant parts of your program with other programs, > chances are that you're not using the right language for the job. Yeah, C really sucks when it comes to working with types in an abstract way. But the real question is, would we rather have C++ with fancy template code or C with some code generation to make up for C's incredibly limited type system? >> This ends up being a powerful interface because you can easily get >> these properties within QEMU, but you also (transparently) map these >> to the wire. It also extends really nicely for setting properties >> which becomes an interesting way to implement dynamic device logic >> (like setting the link status of a network card). > Well, the traditional way to "get properties" within QEMU is the ol' -> > operator. > > I'm not sure I get the rest of the paragraph. I mean: (qemu) device_get virtio-pci.0 link_status True (qemu) device_set virtio-pci.0 link_status False (qemu) device_get virtio-pci.0 link_status: False mac-address: 00:12:32:43:54:92 vectors: 1 ... We already have read-only and construct-only properties. A writable property is an obvious next step. >>> Yet another one: vmstate, which describes migratable parts of qdev state >>> objects. >> Yes, for now, I'm ignoring vmstate. To really solve vmstate, I think >> you need to describe the complete object instead of just it's >> properties. > Would be nice to have a solution that can cover all our reflection > needs, including vmstate. But we need to watch out not to get bogged > down in the Generality Swamps. The mechanism I described using the visitor pattern is really the right solution for vmstate. The hard problems to solve for vmstate are: 1) How to we support old versions in a robust way. There are fancy things we could do once we have a proper visitor mechanism. We could have special marshallers for old versions, we could treat the output of the visitor as an in memory tree and do XSLT style translations, etc. 2) How do we generate the visitor for each device. I don't think it's practical to describe devices in JSON. It certainly simplifies the problem but it seems ugly to me. I think we realistically need a C style IDL and adopt a style of treating it as a header. I think (1) is pretty straight forward but requires careful auditing of all of the weirdness we currently have. (2) isn't so hard but we need to make sure the syntax is right from the start. >>> Unlike these two, QCFG doesn't describe C structs, it generates them >>> from JSON specs. If I understand your proposal correctly. Hmm. >> Correct. >> >>> Can we avoid defining our data types in JSON rather than C? >> I didn't start with describing them in JSON. I started by describing >> them in Python with the idea that I'd write a IDL parser using a C >> style syntax that would then generate the Python structures. Using >> the fixed Python data structures, I could play with code generation >> without all the trouble of writing a full blown IDL parser. >> >> But I never got around to writing that parser and have actually become >> fond of the Python/JSON style syntax. > Well, let's say "fondness" isn't what I'd expect ordinary people to feel > when confonted with the idea to define half our structs in Python rather > than C ;) The thing I really like is that it limits you. With a C based approach, there is almost an uncanny valley effect where there's an expectation that since my type looks mostly like a C structure, I should be able to do anything that I would normally do in C. This is the real thing that I think frustrates most people with C style IDLs. But with a JSON IDL, you approach it differently. It's obviously not C and you start with the assumption that you can't do crazy things (like bitfields). >>> Can we adopt *one* reflection mechanism? >> Yes, I think we should start with that being the JSON syntax I'm using >> for QAPI/QCFG and then down the road, we can introduce a totally new >> syntax if need be. But I think we need a central schema that >> describes all externally visible interfaces. I think this is really >> the key idea here. > Yes, self-describing external interfaces are desirable. > > Speaking of external interfaces: we need to be careful when exposing > internal data types externally. If we couple our external interfaces > too closely to internals, we risk calcifying our internal interfaces. > > qdev has gotten that partly right: you can change the state structs > pretty freely without affecting the externally visible properties. Yeah, this is one of the big challenges with vmstate. There needs to be a clearer line between internal types and object models vs. the externally visible interfaces. Regards, Anthony Liguori > [...] > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-18 22:39 ` Anthony Liguori @ 2011-03-22 13:01 ` Markus Armbruster 2011-03-22 15:49 ` Anthony Liguori 0 siblings, 1 reply; 22+ messages in thread From: Markus Armbruster @ 2011-03-22 13:01 UTC (permalink / raw) To: Anthony Liguori Cc: Kevin Wolf, Chris Wright, Adam Litke, qemu-devel, Stefan Hajnoczi Anthony Liguori <anthony@codemonkey.ws> writes: > On 03/18/2011 09:04 AM, Markus Armbruster wrote: >>>>> Initial code is in my QAPI tree. >>>>> >>>>> I'm not going to start converting things until we get closer to the >>>>> end of 0.15 and QAPI is fully merged. My plan is to focus on this for >>>>> 0.16 and do a full conversion for the 0.16 time frame using the same >>>>> approach as QAPI. That means that for 0.16, we would be able to set >>>>> all command line options via QMP in a programmatic fashion with full >>>>> support for introspection. >>>>> >>>>> I'm haven't yet closed on how to bridge this to qdev. qdev is a big >>>>> consumer of QemuOpts today. I have some general ideas about what I'd >>>>> like to do but so far, I haven't written anything down. >>>> Glad you mention qdev. >>>> >>>> qdev properties describe the configurable parts of qdev state objects. >>>> A state object is a C struct. The description is C data. Poor man's >>>> reflection. >>> Right. The problem is it's very hard to reflect C even if you parse >>> it without additional annotations. For instance: >>> >>> typedef struct Foo { >>> Bar *bar; >>> } Foo; >>> >>> What the type of bar is is ambigious. It could be a pointer to a list >>> of Bar's (if bar has an embedded pointer), it could be an array of >>> Bars that is terminated using a field within Bar, it could be a >>> pointer to a fixed size array of Bars, or it could be just a pointer >>> to a single Bar object. >>> >>> So you end up needing additional annotations like: >>> >>> typedef struct Foo { >>> size_t n_bar; >>> Bar *bar sizeis(n_bar); >>> } Foo; >>> >>> This is what most IDLs that use C style syntax do. >> We currently use a more low-tech approach: define the struct in plain C, >> and the data describing the struct in plain C as well. >> >> Information about the type is in two places and in two formats (C type >> declaration and C data intializer). There's a bit of redundancy. >> Ensuring consistency requires preprocessor hackery. > > I've explored this to what I believe it's limit without crossing into > extreme non-portability. > > I think the best you can possibly do is a scheme like the following: > > typedef struct MyStruct { > int x; > int y; > MyOtherStruct *mo_list; > MyStruct *next; > } MyStruct; > > TypeInfo type_into_MyStruct = { > .name = "MyStruct", > .params = { > DEFINE_PARAM(MyStruct, int, x), > DEFINE_PARAM(MyStruct, int, y), > DEFINE_LIST(MyStruct, MyOther, mo_list), > DEFINE_NEXT(MyStruct, next), > DEFINE_EOL(), > }, > }; > > But there is absolutely no type safety here. You can confuse the type > of mo_list as an int and you'll get no errors. > > To get type safety, you need to have macros for each type. Type checking macros are feasible (see [*] for an existence proof), but things do get hairy, and the resulting error messages can be less than clear at times. > However, > this makes it very difficult to support things like lists of lists or > anything else that would basically require a non-concrete type. Sounds like you want a more expressive type system than C's, and propose to get it by building your own DSL. I'm not sure that's wise. > The basic problem here is that we're mixing how to transverse a data > structure with how to describe a data structure. If you separate > those two things, it becomes cleaner: > > void marshal_MyStruct(MyStruct *obj, const char *name) > { > marshal_start_struct("MyStruct", name); > marshal_int(&obj->x, "x"); > marshal_int(&obj->y, "y"); > marshal_start_list("mo_list"); > for (MyOtherStruct *i = obj->mo_list; i; i = i->next) { > marshal_MyOtherStruct(i, ""); > } > marshal_end_list(); > marshal_end_struct(); > } > > This generalizes very well to almost arbitrarily complicated data > structures and is really almost as compact as the data structure > version. It also has very strong type safety. But it's not good > enough because C doesn't have exceptions so if a marshal error occurs, > you need to propagate it. So then you get: > > void marshal_MyStruct(MyStruct *obj, const char *name, Error **errp) > { > marshal_start_struct("MyStruct", name, errp); > if (error_is_set(errp)) { > return; > } > marshal_int(&obj->x, "x", errp); > ... > } > > Unwinding can be a bit challenging too without destructors assuming > that some of the marshalling routines allocate state (they will for > lists). > > But this provides an extremely nice marshaller/unmarshaller. You get > high quality errors and you can support arbitrarily complex objects. > > This is what qmp-gen.py creates for the qmp-marshal-types module. > > But it creates quite a bit more. Even if you write the above by hand > (or use struct to describe it), these types are complex and cannot be > free'd with a simple free call. So you also need a function to free > each type. I'm not advocating hand-written marshallers. Data description should be data, not code. If you need a marshaller, generate it from the descriptive data, or write a generic one that interprets the descriptive data. > If you plan to expose these types in a library, you need to either > explicitly pad each structure and make sure that the padding is > updated correctly each time a new member is added. As long as the data description is data, writing a program to check that a new version is compatible with the old one shouldn't be hard. > Alternatively, you > can add an allocation function that automatically pads each structure > transparently. Weaker than a comprehensive check, but could be good enough. > qmp-gen.py creates qmp-types.[ch] to do exactly the above and also > generates the type declaration so that you don't have to duplicate the > type marshalling code and the type declaration. Today, this is close > to 2k LOCs so it's actually a significant amount of code code. > > There is also the code that takes the input (via QCFG or QMP) and > calls an appropriate C function with a strongly typed argument. I've Not sure I got you here. Perhaps an example could enlighten me :) > played with libffi here to try to do this dynamically but you still > lose strong typing because there's no (obvious) way to check the types > of a function pointer against a dynamic type description at compile > time. I've tried to do some serious preprocessor-fu here to make it > work but have failed. > > Finally, on the libqmp side, you need to code that takes C arguments > and calls the appropriate marshalling routines to build the variant > types. This may not seem relevant with QCFG but as we make command > line options configurable via QMP, it becomes important. > >> The data doesn't have to describe all struct members. I'm inclined to >> regard that as a feature. >> >> Avoids generating source code. > > I don't think we really can and still preserve type safety. All the > techniques I see to do it without code generation also mean we still > have to write a fair bit of code by hand. > > We could actually avoid it if we were using C++ because templates > (particularly with specialization) are pretty much what the code > generator is making up for. But yeah, I'm not going to even try to > make that suggestion :-) > > It's still hard to do a proper libqmp even with C++ FWIW. > >>> There would be a separate interface for getting/setting properties. >>> It might even be something as simple as, you have to implement: >>> >>> int isa_serial_get_iobase(ISADeviceState *dev); >>> int isa_serial_get_irq(ISADeviceState *dev); >>> ... >> To be honest, I'm wary of generating lots of C source. Prone to become >> a pain to debug and change. Generated code tends to be much worse than >> data in that regard. >> >> If you generate significant parts of your program with other programs, >> chances are that you're not using the right language for the job. > > Yeah, C really sucks when it comes to working with types in an > abstract way. But the real question is, would we rather have C++ with > fancy template code or C with some code generation to make up for C's > incredibly limited type system? Yes, C is limited. It makes you keep things simple & stupid, because as you stray away from that, pain increases sharply. Maybe what you try to do here simply isn't in C's application domain anymore. But I don't think C++ with fancy templating is a viable solution to the problem either. The set of people understanding fancy templating may not be empty, but it's certainly small. Much smaller than the set of people who think they understand fancy templating. >>> This ends up being a powerful interface because you can easily get >>> these properties within QEMU, but you also (transparently) map these >>> to the wire. It also extends really nicely for setting properties >>> which becomes an interesting way to implement dynamic device logic >>> (like setting the link status of a network card). >> Well, the traditional way to "get properties" within QEMU is the ol' -> >> operator. >> >> I'm not sure I get the rest of the paragraph. > > I mean: > > (qemu) device_get virtio-pci.0 link_status > True > (qemu) device_set virtio-pci.0 link_status False > (qemu) device_get virtio-pci.0 > link_status: False > mac-address: 00:12:32:43:54:92 > vectors: 1 > ... > > We already have read-only and construct-only properties. A writable > property is an obvious next step. Got it. >>>> Yet another one: vmstate, which describes migratable parts of qdev state >>>> objects. >>> Yes, for now, I'm ignoring vmstate. To really solve vmstate, I think >>> you need to describe the complete object instead of just it's >>> properties. >> Would be nice to have a solution that can cover all our reflection >> needs, including vmstate. But we need to watch out not to get bogged >> down in the Generality Swamps. > > The mechanism I described using the visitor pattern is really the > right solution for vmstate. The hard problems to solve for vmstate > are: > > 1) How to we support old versions in a robust way. There are fancy > things we could do once we have a proper visitor mechanism. We could > have special marshallers for old versions, we could treat the output > of the visitor as an in memory tree and do XSLT style translations, > etc. > > 2) How do we generate the visitor for each device. I don't think it's > practical to describe devices in JSON. It certainly simplifies the > problem but it seems ugly to me. I think we realistically need a C > style IDL and adopt a style of treating it as a header. Now I'm confused. Do you mean your JSON-based DSL won't cut it for vmstate? If yes, why is it wise to go down that route now? > I think (1) is pretty straight forward but requires careful auditing > of all of the weirdness we currently have. (2) isn't so hard but we > need to make sure the syntax is right from the start. > >>>> Unlike these two, QCFG doesn't describe C structs, it generates them >>>> from JSON specs. If I understand your proposal correctly. Hmm. >>> Correct. >>> >>>> Can we avoid defining our data types in JSON rather than C? >>> I didn't start with describing them in JSON. I started by describing >>> them in Python with the idea that I'd write a IDL parser using a C >>> style syntax that would then generate the Python structures. Using >>> the fixed Python data structures, I could play with code generation >>> without all the trouble of writing a full blown IDL parser. >>> >>> But I never got around to writing that parser and have actually become >>> fond of the Python/JSON style syntax. >> Well, let's say "fondness" isn't what I'd expect ordinary people to feel >> when confonted with the idea to define half our structs in Python rather >> than C ;) > > The thing I really like is that it limits you. With a C based > approach, there is almost an uncanny valley effect where there's an > expectation that since my type looks mostly like a C structure, I > should be able to do anything that I would normally do in C. This is > the real thing that I think frustrates most people with C style IDLs. > > But with a JSON IDL, you approach it differently. It's obviously not > C and you start with the assumption that you can't do crazy things > (like bitfields). > >>>> Can we adopt *one* reflection mechanism? >>> Yes, I think we should start with that being the JSON syntax I'm using >>> for QAPI/QCFG and then down the road, we can introduce a totally new >>> syntax if need be. But I think we need a central schema that >>> describes all externally visible interfaces. I think this is really >>> the key idea here. >> Yes, self-describing external interfaces are desirable. >> >> Speaking of external interfaces: we need to be careful when exposing >> internal data types externally. If we couple our external interfaces >> too closely to internals, we risk calcifying our internal interfaces. >> >> qdev has gotten that partly right: you can change the state structs >> pretty freely without affecting the externally visible properties. > > Yeah, this is one of the big challenges with vmstate. There needs to > be a clearer line between internal types and object models vs. the > externally visible interfaces. Not only with vmstate. If we couple QMP closely to internal interfaces, I'm afraid we'll end up in the same unhappy place we're now with vmstate. > Regards, > > Anthony Liguori > >> [...] >> [*] http://ccan.ozlabs.org/info/check_type.html ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-22 13:01 ` Markus Armbruster @ 2011-03-22 15:49 ` Anthony Liguori 2011-03-24 8:32 ` Markus Armbruster 0 siblings, 1 reply; 22+ messages in thread From: Anthony Liguori @ 2011-03-22 15:49 UTC (permalink / raw) To: Markus Armbruster Cc: Kevin Wolf, Chris Wright, qemu-devel, Stefan Hajnoczi, Adam Litke On 03/22/2011 08:01 AM, Markus Armbruster wrote: > Type checking macros are feasible (see [*] for an existence proof), but > things do get hairy, and the resulting error messages can be less than > clear at times. That just gives you a warning. You can do much better things with __builtin_types_compatible_p. But neither really solves the problem I'm talking about. I can go into it in depth but hopefully we both can agree that trying to build introspection macros is pretty difficult if not impossible :-) >> However, >> this makes it very difficult to support things like lists of lists or >> anything else that would basically require a non-concrete type. > Sounds like you want a more expressive type system than C's, and propose > to get it by building your own DSL. I'm not sure that's wise. It's an IDL. IDL based RPCs are pretty common with C. The IDL is purely declarative. >> If you plan to expose these types in a library, you need to either >> explicitly pad each structure and make sure that the padding is >> updated correctly each time a new member is added. > As long as the data description is data, writing a program to check that > a new version is compatible with the old one shouldn't be hard. If you define the structs on your own, you need to either have a data description of the padding or be very careful doing it yourself. >> Alternatively, you >> can add an allocation function that automatically pads each structure >> transparently. > Weaker than a comprehensive check, but could be good enough. > >> qmp-gen.py creates qmp-types.[ch] to do exactly the above and also >> generates the type declaration so that you don't have to duplicate the >> type marshalling code and the type declaration. Today, this is close >> to 2k LOCs so it's actually a significant amount of code code. >> >> There is also the code that takes the input (via QCFG or QMP) and >> calls an appropriate C function with a strongly typed argument. I've > Not sure I got you here. Perhaps an example could enlighten me :) void qapi_free_vnc_info(VncInfo *obj) { if (!obj) { return; } if (obj->has_host) { qemu_free(obj->host); } if (obj->has_family) { qemu_free(obj->family); } if (obj->has_service) { qemu_free(obj->service); } if (obj->has_auth) { qemu_free(obj->auth); } if (obj->has_clients) { qapi_free_vnc_client_info(obj->clients); } qapi_free_vnc_info(obj->next); qemu_free(obj); } It's pretty basic boiler plate code that could be written by hand, but why not generate it. It actually all adds up pretty quickly in terms of SLOCs. >> The mechanism I described using the visitor pattern is really the >> right solution for vmstate. The hard problems to solve for vmstate >> are: >> >> 1) How to we support old versions in a robust way. There are fancy >> things we could do once we have a proper visitor mechanism. We could >> have special marshallers for old versions, we could treat the output >> of the visitor as an in memory tree and do XSLT style translations, >> etc. >> >> 2) How do we generate the visitor for each device. I don't think it's >> practical to describe devices in JSON. It certainly simplifies the >> problem but it seems ugly to me. I think we realistically need a C >> style IDL and adopt a style of treating it as a header. > Now I'm confused. Do you mean your JSON-based DSL won't cut it for > vmstate? > > If yes, why is it wise to go down that route now? There are a few paths we could go. We can describe devices in JSON. This makes VMState introspectable with all of the nice properties of everything else. But the question is, do we describe the full device state and then use a separate mechanism to cull out the bits that can be recomputed. To we only describe the guest visible state and treat that as a separate structure? Is that embedded in the main state object or do we explicitly translate the main state object to this new type? We can pretty easily have a C-like IDL so I'm not terribly concerned about describing devices in JSON. It's about finding the right way to structure device marshalling in the long term. So yes, I think JSON is the right approach, but that doesn't mean I've figured out how to do VMState. >> Yeah, this is one of the big challenges with vmstate. There needs to >> be a clearer line between internal types and object models vs. the >> externally visible interfaces. > Not only with vmstate. If we couple QMP closely to internal interfaces, > I'm afraid we'll end up in the same unhappy place we're now with > vmstate. Yeah, it's a tough balance to strike. If you expose too much detail about internals, it's very difficult to maintain compatibility in the long term. If you have a pure external interface, it's difficult to make sure that the external interfaces are actually useful because there's no immediate feedback mechanism. Regards, Anthony Liguori >> Regards, >> >> Anthony Liguori >> >>> [...] >>> > [*] http://ccan.ozlabs.org/info/check_type.html > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling 2011-03-22 15:49 ` Anthony Liguori @ 2011-03-24 8:32 ` Markus Armbruster 0 siblings, 0 replies; 22+ messages in thread From: Markus Armbruster @ 2011-03-24 8:32 UTC (permalink / raw) To: Anthony Liguori Cc: Kevin Wolf, Chris Wright, Adam Litke, qemu-devel, Stefan Hajnoczi Anthony Liguori <anthony@codemonkey.ws> writes: > On 03/22/2011 08:01 AM, Markus Armbruster wrote: >> Type checking macros are feasible (see [*] for an existence proof), but >> things do get hairy, and the resulting error messages can be less than >> clear at times. > > That just gives you a warning. You can do much better things with > __builtin_types_compatible_p. Quote of existence proof != this is the best way to do it :) > But neither really solves the problem I'm talking about. I can go > into it in depth but hopefully we both can agree that trying to build > introspection macros is pretty difficult if not impossible :-) Depends on what you need from them. >>> However, >>> this makes it very difficult to support things like lists of lists or >>> anything else that would basically require a non-concrete type. >> Sounds like you want a more expressive type system than C's, and propose >> to get it by building your own DSL. I'm not sure that's wise. > > It's an IDL. IDL based RPCs are pretty common with C. The IDL is > purely declarative. Still sounds like you want a more expressive type system than C's, and propose to build it ourselves. First step would be to define the type system rigorously. >>> If you plan to expose these types in a library, you need to either >>> explicitly pad each structure and make sure that the padding is >>> updated correctly each time a new member is added. >> As long as the data description is data, writing a program to check that >> a new version is compatible with the old one shouldn't be hard. > > If you define the structs on your own, you need to either have a data > description of the padding or be very careful doing it yourself. With a program to check compatibility, you can easily make the build fail when you break compatibility. >>> Alternatively, you >>> can add an allocation function that automatically pads each structure >>> transparently. >> Weaker than a comprehensive check, but could be good enough. >> >>> qmp-gen.py creates qmp-types.[ch] to do exactly the above and also >>> generates the type declaration so that you don't have to duplicate the >>> type marshalling code and the type declaration. Today, this is close >>> to 2k LOCs so it's actually a significant amount of code code. >>> >>> There is also the code that takes the input (via QCFG or QMP) and >>> calls an appropriate C function with a strongly typed argument. I've >> Not sure I got you here. Perhaps an example could enlighten me :) > > void qapi_free_vnc_info(VncInfo *obj) > { > if (!obj) { > return; > } > if (obj->has_host) { > qemu_free(obj->host); > } > if (obj->has_family) { > qemu_free(obj->family); > } > if (obj->has_service) { > qemu_free(obj->service); > } > if (obj->has_auth) { > qemu_free(obj->auth); > } > if (obj->has_clients) { > qapi_free_vnc_client_info(obj->clients); > } > > > qapi_free_vnc_info(obj->next); > qemu_free(obj); > } > > It's pretty basic boiler plate code that could be written by hand, but > why not generate it. It actually all adds up pretty quickly in terms > of SLOCs. Since we have the data description anyway, why not for all members if member needs destruction destroy it Just because we can generate boiler-plate code doesn't mean we should have boiler-plate code. >>> The mechanism I described using the visitor pattern is really the >>> right solution for vmstate. The hard problems to solve for vmstate >>> are: >>> >>> 1) How to we support old versions in a robust way. There are fancy >>> things we could do once we have a proper visitor mechanism. We could >>> have special marshallers for old versions, we could treat the output >>> of the visitor as an in memory tree and do XSLT style translations, >>> etc. >>> >>> 2) How do we generate the visitor for each device. I don't think it's >>> practical to describe devices in JSON. It certainly simplifies the >>> problem but it seems ugly to me. I think we realistically need a C >>> style IDL and adopt a style of treating it as a header. >> Now I'm confused. Do you mean your JSON-based DSL won't cut it for >> vmstate? >> >> If yes, why is it wise to go down that route now? > > There are a few paths we could go. We can describe devices in JSON. > This makes VMState introspectable with all of the nice properties of > everything else. But the question is, do we describe the full device > state and then use a separate mechanism to cull out the bits that can > be recomputed. > > To we only describe the guest visible state and treat that as a > separate structure? Is that embedded in the main state object or do > we explicitly translate the main state object to this new type? Requiring fields of the externally visible vmstate wire format to be backed by members of the device state struct (whether they are right in the outermost struct or not) creates a potentially troublesome tight coupling between wire format and internal representation. You can avoid such coupling by permitting wire format fields to be backed by functions rather than struct members. > We can pretty easily have a C-like IDL so I'm not terribly concerned > about describing devices in JSON. It's about finding the right way to > structure device marshalling in the long term. > > So yes, I think JSON is the right approach, but that doesn't mean I've > figured out how to do VMState. > >>> Yeah, this is one of the big challenges with vmstate. There needs to >>> be a clearer line between internal types and object models vs. the >>> externally visible interfaces. >> Not only with vmstate. If we couple QMP closely to internal interfaces, >> I'm afraid we'll end up in the same unhappy place we're now with >> vmstate. > > Yeah, it's a tough balance to strike. If you expose too much detail > about internals, it's very difficult to maintain compatibility in the > long term. If you have a pure external interface, it's difficult to > make sure that the external interfaces are actually useful because > there's no immediate feedback mechanism. The only valid judges on the usefulness of external interfaces are external users. Coupling them tightly to internal interfaces just to shortcut that judgement doesn't strike me as a smart idea. Please note the "tightly" there. > > Regards, > > Anthony Liguori > >>> Regards, >>> >>> Anthony Liguori >>> >>>> [...] >>>> >> [*] http://ccan.ozlabs.org/info/check_type.html ^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2011-03-24 8:32 UTC | newest] Thread overview: 22+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-03-14 17:48 [Qemu-devel] [RFC] QCFG: a new mechanism to replace QemuOpts and option handling Anthony Liguori 2011-03-14 19:52 ` Lluís 2011-03-14 20:04 ` Anthony Liguori 2011-03-15 10:09 ` Kevin Wolf 2011-03-15 13:27 ` Anthony Liguori 2011-03-15 13:45 ` Kevin Wolf 2011-03-15 13:56 ` Anthony Liguori 2011-03-18 18:12 ` Stefan Hajnoczi 2011-03-15 11:21 ` [Qemu-devel] " Kevin Wolf 2011-03-15 13:37 ` Anthony Liguori 2011-03-15 13:51 ` Kevin Wolf 2011-03-17 15:26 ` Markus Armbruster 2011-03-18 4:12 ` Anthony Liguori 2011-03-18 13:07 ` Markus Armbruster 2011-03-17 15:22 ` [Qemu-devel] " Markus Armbruster 2011-03-17 18:28 ` Anthony Liguori 2011-03-18 9:44 ` Kevin Wolf 2011-03-18 14:04 ` Markus Armbruster 2011-03-18 22:39 ` Anthony Liguori 2011-03-22 13:01 ` Markus Armbruster 2011-03-22 15:49 ` Anthony Liguori 2011-03-24 8:32 ` Markus Armbruster
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).