qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F)
@ 2016-07-14  3:50 Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 01/17] net: use Netdev instead of NetClientOptions in client init Eric Blake
                   ` (17 more replies)
  0 siblings, 18 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange

It's time to expose QMP 'netdev_add' through introspection, and
to add boxed commands/events so that we can drastically reduce
the number of C parameters needed to implement a command that
matches a large QAPI type.

Prerequistes:
Markus' qapi-next branch (weak; series also applies on master)

available as a tag at:
git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv9f
or as part of my qapi branch:
git fetch git://repo.or.cz/qemu/ericb.git qapi

v8 was here:
https://lists.gnu.org/archive/html/qemu-devel/2016-07/msg00302.html

Since then, I've addressed Markus' comments:
- s/box/boxed/
- fix a useless Python override of c_name()
- comment and formatting tweaks
- defer event collision prevention until later; for 2.7, we are
focusing on the bare minimum needed to take advantage of boxing
- implement a promised followup that allows netdev_add to still
parse strings the way it did with QemuOpts (thanks Dan for starting
that effort)

The diffstat from v8 looks big, but a lot of it is mechanical
due to the spelling change.  It's late at night, and we're close
to hard freeze, so to maximize review time I'm posting now; if I
weren't feeling quite as rushed, I might would have swapped the
order of 14 vs. 15-17 to avoid even a temporary behavior change
in netdev_add.  On the other hand, 14 has had review, while 15-17
are effectively new, and it is feasible that we may be comfortable
enough with the new type-safety constraints of 14 to not want
to bother with 15-17 loosening things back up, given that we are
already in soft freeze.

001/17:[----] [--] 'net: use Netdev instead of NetClientOptions in client init'
002/17:[----] [--] 'qapi: Require all branches of flat union enum to be covered'
003/17:[down] 'qapi: Special case c_name() for empty type'
004/17:[----] [--] 'qapi: Hide tag_name data member of variants'
005/17:[----] [-C] 'qapi: Add type.is_empty() helper'
006/17:[----] [--] 'qapi: Drop useless gen_err_check()'
007/17:[0002] [FC] 'qapi-event: Simplify visit of non-implicit data'
008/17:[down] 'qapi: Plumb in 'boxed' to qapi generator lower levels'
009/17:[0100] [FC] 'qapi: Implement boxed types for commands/events'
010/17:[0002] [FC] 'block: Simplify block_set_io_throttle'
011/17:[0002] [FC] 'block: Simplify drive-mirror'
012/17:[----] [--] 'qapi: Change Netdev into a flat union'
013/17:[----] [--] 'net: Use correct type for bool flag'
014/17:[0002] [FC] 'net: Complete qapi-fication of netdev_add'
015/17:[down] 'option: make parse_option_bool/number non-static'
016/17:[down] 'qapi: Tweak QmpInputVisitor to optionally do string conversion'
017/17:[down] 'qapi: Restore autocast behavior in 'netdev_add''

Daniel P. Berrange (1):
  option: make parse_option_bool/number non-static

Eric Blake (15):
  qapi: Require all branches of flat union enum to be covered
  qapi: Special case c_name() for empty type
  qapi: Hide tag_name data member of variants
  qapi: Add type.is_empty() helper
  qapi: Drop useless gen_err_check()
  qapi-event: Simplify visit of non-implicit data
  qapi: Plumb in 'boxed' to qapi generator lower levels
  qapi: Implement boxed types for commands/events
  block: Simplify block_set_io_throttle
  block: Simplify drive-mirror
  qapi: Change Netdev into a flat union
  net: Use correct type for bool flag
  net: Complete qapi-fication of netdev_add
  qapi: Tweak QmpInputVisitor to optionally do string conversion
  qapi: Restore autocast behavior in 'netdev_add'

Kővágó, Zoltán (1):
  net: use Netdev instead of NetClientOptions in client init

 qapi-schema.json                                   |  66 +++++---
 qapi/block-core.json                               |  40 ++++-
 scripts/qapi.py                                    | 131 +++++++++++----
 scripts/qapi-commands.py                           |  36 ++--
 scripts/qapi-event.py                              |  48 ++++--
 scripts/qapi-introspect.py                         |   4 +-
 scripts/qapi-types.py                              |   2 +-
 scripts/qapi-visit.py                              |   8 +-
 include/net/net.h                                  |   8 +-
 include/qapi/qmp-input-visitor.h                   |  24 ++-
 include/qemu/option.h                              |   4 +
 net/clients.h                                      |  20 +--
 blockdev.c                                         | 187 ++++++++-------------
 hmp.c                                              |  72 +++-----
 hw/arm/musicpal.c                                  |   2 +-
 hw/core/qdev-properties-system.c                   |   2 +-
 hw/net/allwinner_emac.c                            |   2 +-
 hw/net/cadence_gem.c                               |   2 +-
 hw/net/dp8393x.c                                   |   2 +-
 hw/net/e1000.c                                     |   2 +-
 hw/net/e1000e.c                                    |   2 +-
 hw/net/eepro100.c                                  |   2 +-
 hw/net/etraxfs_eth.c                               |   2 +-
 hw/net/fsl_etsec/etsec.c                           |   2 +-
 hw/net/imx_fec.c                                   |   2 +-
 hw/net/lan9118.c                                   |   2 +-
 hw/net/lance.c                                     |   2 +-
 hw/net/mcf_fec.c                                   |   2 +-
 hw/net/milkymist-minimac2.c                        |   2 +-
 hw/net/mipsnet.c                                   |   2 +-
 hw/net/ne2000-isa.c                                |   2 +-
 hw/net/ne2000.c                                    |   2 +-
 hw/net/opencores_eth.c                             |   2 +-
 hw/net/pcnet-pci.c                                 |   2 +-
 hw/net/rocker/rocker_fp.c                          |   2 +-
 hw/net/rtl8139.c                                   |   2 +-
 hw/net/smc91c111.c                                 |   2 +-
 hw/net/spapr_llan.c                                |   2 +-
 hw/net/stellaris_enet.c                            |   2 +-
 hw/net/vhost_net.c                                 |  20 +--
 hw/net/virtio-net.c                                |  10 +-
 hw/net/vmxnet3.c                                   |   2 +-
 hw/net/xen_nic.c                                   |   2 +-
 hw/net/xgmac.c                                     |   2 +-
 hw/net/xilinx_axienet.c                            |   2 +-
 hw/net/xilinx_ethlite.c                            |   2 +-
 hw/usb/dev-network.c                               |   4 +-
 monitor.c                                          |  14 +-
 net/dump.c                                         |   8 +-
 net/filter.c                                       |   2 +-
 net/hub.c                                          |  24 +--
 net/l2tpv3.c                                       |   8 +-
 net/net.c                                          | 164 +++++++++++-------
 net/netmap.c                                       |   6 +-
 net/slirp.c                                        |   8 +-
 net/socket.c                                       |  10 +-
 net/tap-win32.c                                    |   8 +-
 net/tap.c                                          |  28 +--
 net/vde.c                                          |   8 +-
 net/vhost-user.c                                   |  22 +--
 qapi/opts-visitor.c                                |  19 +--
 qapi/qmp-input-visitor.c                           | 100 +++++++++--
 qmp.c                                              |   2 +-
 qom/qom-qobject.c                                  |   2 +-
 tests/check-qnull.c                                |   2 +-
 tests/test-qmp-commands.c                          |  10 +-
 tests/test-qmp-input-strict.c                      |   2 +-
 tests/test-qmp-input-visitor.c                     | 150 ++++++++++++++++-
 tests/test-visitor-serialization.c                 |   2 +-
 util/qemu-option.c                                 |  27 +--
 docs/qapi-code-gen.txt                             |  37 +++-
 qmp-commands.hx                                    |   2 +-
 tests/Makefile.include                             |   6 +
 tests/qapi-schema/args-bad-box.err                 |   1 +
 tests/qapi-schema/args-bad-box.exit                |   1 +
 tests/qapi-schema/args-bad-box.json                |   2 +
 tests/qapi-schema/args-bad-box.out                 |   0
 tests/qapi-schema/args-box-anon.err                |   1 +
 tests/qapi-schema/args-box-anon.exit               |   1 +
 tests/qapi-schema/args-box-anon.json               |   2 +
 tests/qapi-schema/args-box-anon.out                |   0
 tests/qapi-schema/args-box-empty.err               |   1 +
 tests/qapi-schema/args-box-empty.exit              |   1 +
 tests/qapi-schema/args-box-empty.json              |   3 +
 tests/qapi-schema/args-box-empty.out               |   0
 tests/qapi-schema/args-box-string.err              |   1 +
 tests/qapi-schema/args-box-string.exit             |   1 +
 tests/qapi-schema/args-box-string.json             |   2 +
 tests/qapi-schema/args-box-string.out              |   0
 tests/qapi-schema/args-union.err                   |   2 +-
 tests/qapi-schema/args-union.json                  |   3 +-
 tests/qapi-schema/event-box-empty.err              |   1 +
 tests/qapi-schema/event-box-empty.exit             |   1 +
 tests/qapi-schema/event-box-empty.json             |   2 +
 tests/qapi-schema/event-box-empty.out              |   0
 tests/qapi-schema/event-case.out                   |   1 +
 tests/qapi-schema/flat-union-incomplete-branch.err |   1 +
 .../qapi-schema/flat-union-incomplete-branch.exit  |   1 +
 .../qapi-schema/flat-union-incomplete-branch.json  |   9 +
 tests/qapi-schema/flat-union-incomplete-branch.out |   0
 tests/qapi-schema/ident-with-escape.out            |   2 +-
 tests/qapi-schema/indented-expr.out                |   4 +-
 tests/qapi-schema/qapi-schema-test.json            |   4 +
 tests/qapi-schema/qapi-schema-test.out             |  37 +++-
 tests/qapi-schema/test-qapi.py                     |  11 +-
 105 files changed, 968 insertions(+), 543 deletions(-)
 create mode 100644 tests/qapi-schema/args-bad-box.err
 create mode 100644 tests/qapi-schema/args-bad-box.exit
 create mode 100644 tests/qapi-schema/args-bad-box.json
 create mode 100644 tests/qapi-schema/args-bad-box.out
 create mode 100644 tests/qapi-schema/args-box-anon.err
 create mode 100644 tests/qapi-schema/args-box-anon.exit
 create mode 100644 tests/qapi-schema/args-box-anon.json
 create mode 100644 tests/qapi-schema/args-box-anon.out
 create mode 100644 tests/qapi-schema/args-box-empty.err
 create mode 100644 tests/qapi-schema/args-box-empty.exit
 create mode 100644 tests/qapi-schema/args-box-empty.json
 create mode 100644 tests/qapi-schema/args-box-empty.out
 create mode 100644 tests/qapi-schema/args-box-string.err
 create mode 100644 tests/qapi-schema/args-box-string.exit
 create mode 100644 tests/qapi-schema/args-box-string.json
 create mode 100644 tests/qapi-schema/args-box-string.out
 create mode 100644 tests/qapi-schema/event-box-empty.err
 create mode 100644 tests/qapi-schema/event-box-empty.exit
 create mode 100644 tests/qapi-schema/event-box-empty.json
 create mode 100644 tests/qapi-schema/event-box-empty.out
 create mode 100644 tests/qapi-schema/flat-union-incomplete-branch.err
 create mode 100644 tests/qapi-schema/flat-union-incomplete-branch.exit
 create mode 100644 tests/qapi-schema/flat-union-incomplete-branch.json
 create mode 100644 tests/qapi-schema/flat-union-incomplete-branch.out

-- 
2.5.5

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 01/17] net: use Netdev instead of NetClientOptions in client init
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 02/17] qapi: Require all branches of flat union enum to be covered Eric Blake
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, berrange, Kővágó, Zoltán,
	Kővágó, Zoltán, Jason Wang, Luigi Rizzo,
	Giuseppe Lettieri, Vincenzo Maffione, Samuel Thibault, Jan Kiszka,
	Michael S. Tsirkin

From: Kővágó, Zoltán <dirty.ice.hu@gmail.com>

This way we no longer need NetClientOptions and can convert Netdev
into a flat union.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <93ffdfed7054529635e6acb935150d95dc173a12.1441627176.git.DirtY.iCE.hu@gmail.com>

[rework net_client_init1() to pass Netdev by copying from NetdevLegacy,
rather than merging the two types - which means that we still need
NetClientOptions after all.  Rebase to qapi changes. The bulk of the
patch is mechanical, replacing 'opts' by 'netdev->opts', while
net_client_init1() takes care of converting between legacy and modern
types.]

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: no change
v8: tweak commit message
v7: rebase to master
v6: rebase
---
 net/clients.h    | 20 ++++++++++----------
 net/dump.c       |  6 +++---
 net/hub.c        |  6 +++---
 net/l2tpv3.c     |  6 +++---
 net/net.c        | 18 +++++++++++-------
 net/netmap.c     |  4 ++--
 net/slirp.c      |  6 +++---
 net/socket.c     |  6 +++---
 net/tap-win32.c  |  6 +++---
 net/tap.c        | 12 ++++++------
 net/vde.c        |  6 +++---
 net/vhost-user.c |  6 +++---
 12 files changed, 53 insertions(+), 49 deletions(-)

diff --git a/net/clients.h b/net/clients.h
index d47530e..5cae479 100644
--- a/net/clients.h
+++ b/net/clients.h
@@ -27,39 +27,39 @@
 #include "net/net.h"
 #include "qapi-types.h"

-int net_init_dump(const NetClientOptions *opts, const char *name,
+int net_init_dump(const Netdev *netdev, const char *name,
                   NetClientState *peer, Error **errp);

 #ifdef CONFIG_SLIRP
-int net_init_slirp(const NetClientOptions *opts, const char *name,
+int net_init_slirp(const Netdev *netdev, const char *name,
                    NetClientState *peer, Error **errp);
 #endif

-int net_init_hubport(const NetClientOptions *opts, const char *name,
+int net_init_hubport(const Netdev *netdev, const char *name,
                      NetClientState *peer, Error **errp);

-int net_init_socket(const NetClientOptions *opts, const char *name,
+int net_init_socket(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp);

-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp);

-int net_init_bridge(const NetClientOptions *opts, const char *name,
+int net_init_bridge(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp);

-int net_init_l2tpv3(const NetClientOptions *opts, const char *name,
+int net_init_l2tpv3(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp);
 #ifdef CONFIG_VDE
-int net_init_vde(const NetClientOptions *opts, const char *name,
+int net_init_vde(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp);
 #endif

 #ifdef CONFIG_NETMAP
-int net_init_netmap(const NetClientOptions *opts, const char *name,
+int net_init_netmap(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp);
 #endif

-int net_init_vhost_user(const NetClientOptions *opts, const char *name,
+int net_init_vhost_user(const Netdev *netdev, const char *name,
                         NetClientState *peer, Error **errp);

 #endif /* QEMU_NET_CLIENTS_H */
diff --git a/net/dump.c b/net/dump.c
index 41f7673..f8a500f 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -179,7 +179,7 @@ static NetClientInfo net_dump_info = {
     .cleanup = dumpclient_cleanup,
 };

-int net_init_dump(const NetClientOptions *opts, const char *name,
+int net_init_dump(const Netdev *netdev, const char *name,
                   NetClientState *peer, Error **errp)
 {
     int len, rc;
@@ -189,8 +189,8 @@ int net_init_dump(const NetClientOptions *opts, const char *name,
     NetClientState *nc;
     DumpNetClient *dnc;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_DUMP);
-    dump = opts->u.dump.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_DUMP);
+    dump = netdev->opts->u.dump.data;

     assert(peer);

diff --git a/net/hub.c b/net/hub.c
index 6d90c6e..ec4626f 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -281,14 +281,14 @@ int net_hub_id_for_client(NetClientState *nc, int *id)
     return 0;
 }

-int net_init_hubport(const NetClientOptions *opts, const char *name,
+int net_init_hubport(const Netdev *netdev, const char *name,
                      NetClientState *peer, Error **errp)
 {
     const NetdevHubPortOptions *hubport;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT);
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT);
     assert(!peer);
-    hubport = opts->u.hubport.data;
+    hubport = netdev->opts->u.hubport.data;

     net_hub_add_port(hubport->hubid, name);
     return 0;
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index 5c668f7..df02f5b 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -524,7 +524,7 @@ static NetClientInfo net_l2tpv3_info = {
     .cleanup = net_l2tpv3_cleanup,
 };

-int net_init_l2tpv3(const NetClientOptions *opts,
+int net_init_l2tpv3(const Netdev *netdev,
                     const char *name,
                     NetClientState *peer, Error **errp)
 {
@@ -545,8 +545,8 @@ int net_init_l2tpv3(const NetClientOptions *opts,
     s->queue_tail = 0;
     s->header_mismatch = false;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_L2TPV3);
-    l2tpv3 = opts->u.l2tpv3.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_L2TPV3);
+    l2tpv3 = netdev->opts->u.l2tpv3.data;

     if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
         s->ipv6 = l2tpv3->ipv6;
diff --git a/net/net.c b/net/net.c
index 019aaad..d80fd82 100644
--- a/net/net.c
+++ b/net/net.c
@@ -862,15 +862,15 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
     return -1;
 }

-static int net_init_nic(const NetClientOptions *opts, const char *name,
+static int net_init_nic(const Netdev *netdev, const char *name,
                         NetClientState *peer, Error **errp)
 {
     int idx;
     NICInfo *nd;
     const NetLegacyNicOptions *nic;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_NIC);
-    nic = opts->u.nic.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_NIC);
+    nic = netdev->opts->u.nic.data;

     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -931,7 +931,7 @@ static int net_init_nic(const NetClientOptions *opts, const char *name,


 static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND__MAX])(
-    const NetClientOptions *opts,
+    const Netdev *netdev,
     const char *name,
     NetClientState *peer, Error **errp) = {
         [NET_CLIENT_OPTIONS_KIND_NIC]       = net_init_nic,
@@ -963,11 +963,13 @@ static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND__MAX])(
 static int net_client_init1(const void *object, int is_netdev, Error **errp)
 {
     const NetClientOptions *opts;
+    Netdev legacy = {0};
+    const Netdev *netdev;
     const char *name;
     NetClientState *peer = NULL;

     if (is_netdev) {
-        const Netdev *netdev = object;
+        netdev = object;
         opts = netdev->opts;
         name = netdev->id;

@@ -980,7 +982,9 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         }
     } else {
         const NetLegacy *net = object;
-        opts = net->opts;
+        legacy.id = net->id;
+        opts = legacy.opts = net->opts;
+        netdev = &legacy;
         /* missing optional values have been initialized to "all bits zero" */
         name = net->has_id ? net->id : net->name;

@@ -1007,7 +1011,7 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         }
     }

-    if (net_client_init_fun[opts->type](opts, name, peer, errp) < 0) {
+    if (net_client_init_fun[opts->type](netdev, name, peer, errp) < 0) {
         /* FIXME drop when all init functions store an Error */
         if (errp && !*errp) {
             error_setg(errp, QERR_DEVICE_INIT_FAILED,
diff --git a/net/netmap.c b/net/netmap.c
index 64967b9..beb8d28 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -418,10 +418,10 @@ static NetClientInfo net_netmap_info = {
  *
  * ... -net netmap,ifname="..."
  */
-int net_init_netmap(const NetClientOptions *opts,
+int net_init_netmap(const Netdev *netdev,
                     const char *name, NetClientState *peer, Error **errp)
 {
-    const NetdevNetmapOptions *netmap_opts = opts->u.netmap.data;
+    const NetdevNetmapOptions *netmap_opts = netdev->opts->u.netmap.data;
     struct nm_desc *nmd;
     NetClientState *nc;
     Error *err = NULL;
diff --git a/net/slirp.c b/net/slirp.c
index 31630f0..bb49629 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -817,7 +817,7 @@ static const char **slirp_dnssearch(const StringList *dnsname)
     return ret;
 }

-int net_init_slirp(const NetClientOptions *opts, const char *name,
+int net_init_slirp(const Netdev *netdev, const char *name,
                    NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
@@ -828,8 +828,8 @@ int net_init_slirp(const NetClientOptions *opts, const char *name,
     const char **dnssearch;
     bool ipv4 = true, ipv6 = true;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_USER);
-    user = opts->u.user.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_USER);
+    user = netdev->opts->u.user.data;

     if ((user->has_ipv6 && user->ipv6 && !user->has_ipv4) ||
         (user->has_ipv4 && !user->ipv4)) {
diff --git a/net/socket.c b/net/socket.c
index ae6f921..6e5c902 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -663,15 +663,15 @@ static int net_socket_udp_init(NetClientState *peer,
     return 0;
 }

-int net_init_socket(const NetClientOptions *opts, const char *name,
+int net_init_socket(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
     Error *err = NULL;
     const NetdevSocketOptions *sock;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_SOCKET);
-    sock = opts->u.socket.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_SOCKET);
+    sock = netdev->opts->u.socket.data;

     if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
         sock->has_udp != 1) {
diff --git a/net/tap-win32.c b/net/tap-win32.c
index f1e142a..0f23b19 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -788,14 +788,14 @@ static int tap_win32_init(NetClientState *peer, const char *model,
     return 0;
 }

-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevTapOptions *tap;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->u.tap.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    tap = netdev->opts->u.tap.data;

     if (!tap->has_ifname) {
         error_report("tap: no interface name");
diff --git a/net/tap.c b/net/tap.c
index 676bad4..0e713b0 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -558,7 +558,7 @@ static int net_bridge_run_helper(const char *helper, const char *bridge,
     }
 }

-int net_init_bridge(const NetClientOptions *opts, const char *name,
+int net_init_bridge(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp)
 {
     const NetdevBridgeOptions *bridge;
@@ -566,8 +566,8 @@ int net_init_bridge(const NetClientOptions *opts, const char *name,
     TAPState *s;
     int fd, vnet_hdr;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_BRIDGE);
-    bridge = opts->u.bridge.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_BRIDGE);
+    bridge = netdev->opts->u.bridge.data;

     helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
     br     = bridge->has_br     ? bridge->br     : DEFAULT_BRIDGE_INTERFACE;
@@ -722,7 +722,7 @@ static int get_fds(char *str, char *fds[], int max)
     return i;
 }

-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp)
 {
     const NetdevTapOptions *tap;
@@ -734,8 +734,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
     const char *vhostfdname;
     char ifname[128];

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->u.tap.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    tap = netdev->opts->u.tap.data;
     queues = tap->has_queues ? tap->queues : 1;
     vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;

diff --git a/net/vde.c b/net/vde.c
index 9427eaa..53cdbbf 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -109,14 +109,14 @@ static int net_vde_init(NetClientState *peer, const char *model,
     return 0;
 }

-int net_init_vde(const NetClientOptions *opts, const char *name,
+int net_init_vde(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevVdeOptions *vde;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_VDE);
-    vde = opts->u.vde.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_VDE);
+    vde = netdev->opts->u.vde.data;

     /* missing optional values have been initialized to "all bits zero" */
     if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
diff --git a/net/vhost-user.c b/net/vhost-user.c
index a88dfe0..dfdcca2 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -334,15 +334,15 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
     return 0;
 }

-int net_init_vhost_user(const NetClientOptions *opts, const char *name,
+int net_init_vhost_user(const Netdev *netdev, const char *name,
                         NetClientState *peer, Error **errp)
 {
     int queues;
     const NetdevVhostUserOptions *vhost_user_opts;
     CharDriverState *chr;

-    assert(opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
-    vhost_user_opts = opts->u.vhost_user.data;
+    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    vhost_user_opts = netdev->opts->u.vhost_user.data;

     chr = net_vhost_parse_chardev(vhost_user_opts, errp);
     if (!chr) {
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 02/17] qapi: Require all branches of flat union enum to be covered
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 01/17] net: use Netdev instead of NetClientOptions in client init Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 03/17] qapi: Special case c_name() for empty type Eric Blake
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

We were previously enforcing that all flat union branches were
found in the corresponding enum, but not that all enum values
were covered by branches.  The resulting generated code would
abort() if the user passes the uncovered enum value.

We don't automatically treat non-present branches in a flat
union as empty types, for symmetry with simple unions (there,
the enum type is generated from the list of all branches, so
there is no way to omit a branch but still have it be part of
the union).

A later patch will add shorthand so that branches that are empty
in flat unions can be declared as 'branch':{} instead of
'branch':'Empty', to avoid the need for an otherwise useless
explicit empty type.  [Such shorthand for simple unions is a bit
harder to justify, since we would still have to generate a
wrapper type that parses 'data':{}, rather than truly being an
empty branch with no additional siblings to the 'type' member.]

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: no change
v8: enhance commit message
v7: new patch
---
 scripts/qapi.py                                     | 8 ++++++++
 tests/Makefile.include                              | 1 +
 tests/qapi-schema/flat-union-incomplete-branch.err  | 1 +
 tests/qapi-schema/flat-union-incomplete-branch.exit | 1 +
 tests/qapi-schema/flat-union-incomplete-branch.json | 9 +++++++++
 tests/qapi-schema/flat-union-incomplete-branch.out  | 0
 6 files changed, 20 insertions(+)
 create mode 100644 tests/qapi-schema/flat-union-incomplete-branch.err
 create mode 100644 tests/qapi-schema/flat-union-incomplete-branch.exit
 create mode 100644 tests/qapi-schema/flat-union-incomplete-branch.json
 create mode 100644 tests/qapi-schema/flat-union-incomplete-branch.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index b13ae47..0635bbb 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -612,6 +612,14 @@ def check_union(expr, expr_info):
                                     "enum '%s'" %
                                     (key, enum_define["enum_name"]))

+    # If discriminator is user-defined, ensure all values are covered
+    if enum_define:
+        for value in enum_define['enum_values']:
+            if value not in members.keys():
+                raise QAPIExprError(expr_info,
+                                    "Union '%s' data missing '%s' branch"
+                                    % (name, value))
+

 def check_alternate(expr, expr_info):
     name = expr['alternate']
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 2010b11..a04c199 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -326,6 +326,7 @@ qapi-schema += flat-union-base-any.json
 qapi-schema += flat-union-base-union.json
 qapi-schema += flat-union-clash-member.json
 qapi-schema += flat-union-empty.json
+qapi-schema += flat-union-incomplete-branch.json
 qapi-schema += flat-union-inline.json
 qapi-schema += flat-union-int-branch.json
 qapi-schema += flat-union-invalid-branch-key.json
diff --git a/tests/qapi-schema/flat-union-incomplete-branch.err b/tests/qapi-schema/flat-union-incomplete-branch.err
new file mode 100644
index 0000000..e826bf0
--- /dev/null
+++ b/tests/qapi-schema/flat-union-incomplete-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-incomplete-branch.json:6: Union 'TestUnion' data missing 'value2' branch
diff --git a/tests/qapi-schema/flat-union-incomplete-branch.exit b/tests/qapi-schema/flat-union-incomplete-branch.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/flat-union-incomplete-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-incomplete-branch.json b/tests/qapi-schema/flat-union-incomplete-branch.json
new file mode 100644
index 0000000..25a411b
--- /dev/null
+++ b/tests/qapi-schema/flat-union-incomplete-branch.json
@@ -0,0 +1,9 @@
+# we require all branches of the union to be covered
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestTypeA',
+  'data': { 'string': 'str' } }
+{ 'union': 'TestUnion',
+  'base': { 'type': 'TestEnum' },
+  'discriminator': 'type',
+  'data': { 'value1': 'TestTypeA' } }
diff --git a/tests/qapi-schema/flat-union-incomplete-branch.out b/tests/qapi-schema/flat-union-incomplete-branch.out
new file mode 100644
index 0000000..e69de29
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 03/17] qapi: Special case c_name() for empty type
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 01/17] net: use Netdev instead of NetClientOptions in client init Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 02/17] qapi: Require all branches of flat union enum to be covered Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 04/17] qapi: Hide tag_name data member of variants Eric Blake
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

Commit 7ce106a rendered QAPISchemaObjectType.c_name() redundant,
since it now does nothing more than delegate to its superclass.
However, rather than deleting it, we can restore part of the
assertion that was removed in that commit, to prove that we never
emit the empty type directly in generated code, but rather
special-case it as a built-in that makes other aspects of code
generation easier to reason about.

Reported-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: new patch
---
 scripts/qapi.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 0635bbb..68ee319 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1000,6 +1000,7 @@ class QAPISchemaObjectType(QAPISchemaType):
         return self.name.startswith('q_')

     def c_name(self):
+        assert self.name != 'q_empty'
         return QAPISchemaType.c_name(self)

     def c_type(self):
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 04/17] qapi: Hide tag_name data member of variants
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (2 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 03/17] qapi: Special case c_name() for empty type Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 05/17] qapi: Add type.is_empty() helper Eric Blake
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

Clean up the only remaining external use of the tag_name field of
QAPISchemaObjectTypeVariants, by explicitly listing the generated
'type' tag for all variants in the testsuite (you can still tell
simple unions by the -wrapper types).  Then we can mark the
tag_name field as private by adding a leading underscore to prevent
any further use.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: no change
v8: tweak commit wording
v7: no change
v6: rebase to earlier changes
---
 scripts/qapi.py                        |  8 ++++----
 tests/qapi-schema/qapi-schema-test.out | 10 ++++++++++
 tests/qapi-schema/test-qapi.py         |  3 +--
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 68ee319..400c4ef 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1093,7 +1093,7 @@ class QAPISchemaObjectTypeVariants(object):
         assert len(variants) > 0
         for v in variants:
             assert isinstance(v, QAPISchemaObjectTypeVariant)
-        self.tag_name = tag_name
+        self._tag_name = tag_name
         self.tag_member = tag_member
         self.variants = variants

@@ -1103,8 +1103,8 @@ class QAPISchemaObjectTypeVariants(object):

     def check(self, schema, seen):
         if not self.tag_member:    # flat union
-            self.tag_member = seen[c_name(self.tag_name)]
-            assert self.tag_name == self.tag_member.name
+            self.tag_member = seen[c_name(self._tag_name)]
+            assert self._tag_name == self.tag_member.name
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         for v in self.variants:
             v.check(schema)
@@ -1134,7 +1134,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
     def __init__(self, name, info, variants):
         QAPISchemaType.__init__(self, name, info)
         assert isinstance(variants, QAPISchemaObjectTypeVariants)
-        assert not variants.tag_name
+        assert variants.tag_member
         variants.set_owner(name)
         variants.tag_member.set_owner(self.name)
         self.variants = variants
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 19cd214..f34ecc7 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,19 +1,25 @@
 alternate AltIntNum
+    tag type
     case i: int
     case n: number
 alternate AltNumInt
+    tag type
     case n: number
     case i: int
 alternate AltNumStr
+    tag type
     case n: number
     case s: str
 alternate AltStrBool
+    tag type
     case s: str
     case b: bool
 alternate AltStrInt
+    tag type
     case s: str
     case i: int
 alternate AltStrNum
+    tag type
     case s: str
     case n: number
 event EVENT_A None
@@ -50,6 +56,7 @@ object UserDefA
     member boolean: bool optional=False
     member a_b: int optional=True
 alternate UserDefAlternate
+    tag type
     case udfu: UserDefFlatUnion
     case s: str
     case i: int
@@ -72,6 +79,7 @@ object UserDefFlatUnion2
     case value2: UserDefB
 object UserDefNativeListUnion
     member type: UserDefNativeListUnionKind optional=False
+    tag type
     case integer: q_obj_intList-wrapper
     case s8: q_obj_int8List-wrapper
     case s16: q_obj_int16List-wrapper
@@ -117,6 +125,7 @@ object WrapAlternate
     member alt: UserDefAlternate optional=False
 event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
 alternate __org.qemu_x-Alt
+    tag type
     case __org.qemu_x-branch: str
     case b: __org.qemu_x-Base
 object __org.qemu_x-Base
@@ -130,6 +139,7 @@ object __org.qemu_x-Struct2
     member array: __org.qemu_x-Union1List optional=False
 object __org.qemu_x-Union1
     member type: __org.qemu_x-Union1Kind optional=False
+    tag type
     case __org.qemu_x-branch: q_obj_str-wrapper
 enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch']
 object __org.qemu_x-Union2
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 649677e..bedd145 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -47,8 +47,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
     @staticmethod
     def _print_variants(variants):
         if variants:
-            if variants.tag_name:
-                print '    tag %s' % variants.tag_name
+            print '    tag %s' % variants.tag_member.name
             for v in variants.variants:
                 print '    case %s: %s' % (v.name, v.type.name)

-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 05/17] qapi: Add type.is_empty() helper
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (3 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 04/17] qapi: Hide tag_name data member of variants Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 06/17] qapi: Drop useless gen_err_check() Eric Blake
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

In the near future, we want to lift our artificial restriction of
no variants at the top level of an event, at which point the
currently open-coded check for empty members will become
insufficient.  Factor it out into a new helper method is_empty()
now, and future-proof it by checking variants, too, along with an
assert that it is not used prior to the completion of .check().
Update places that were checking for (non-)empty .members to use
the new helper.

All of the current callers assert that there are no variants (either
directly, or by qapi.py asserting that base types have no variants),
so this is not a semantic change.

No change to generated code.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v8: commit message tweaks, also fix qapi-commands.py to use it
v7: rebase to latest
[Previously posted as part of "easier unboxed visits/qapi implicit types"]
v2: no change
v1: add some asserts
[Previously posted as part of "qapi cleanup subset E"]
v9: improve commit message
v8: no change
v7: rebase to context change
v6: new patch
---
 scripts/qapi.py          | 4 ++++
 scripts/qapi-commands.py | 6 +++---
 scripts/qapi-event.py    | 6 +++---
 scripts/qapi-types.py    | 2 +-
 4 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 400c4ef..27284be 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -999,6 +999,10 @@ class QAPISchemaObjectType(QAPISchemaType):
         # _def_predefineds()
         return self.name.startswith('q_')

+    def is_empty(self):
+        assert self.members is not None
+        return not self.members and not self.variants
+
     def c_name(self):
         assert self.name != 'q_empty'
         return QAPISchemaType.c_name(self)
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 34b6a3a..c93470c 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -107,7 +107,7 @@ def gen_marshal(name, arg_type, ret_type):
 ''',
                      c_type=ret_type.c_type())

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     Visitor *v;
     %(c_name)s arg = {0};
@@ -137,7 +137,7 @@ def gen_marshal(name, arg_type, ret_type):
     ret += gen_call(name, arg_type, ret_type)

     # 'goto out' produced above for arg_type, and by gen_call() for ret_type
-    if (arg_type and arg_type.members) or ret_type:
+    if (arg_type and not arg_type.is_empty()) or ret_type:
         ret += mcgen('''

 out:
@@ -145,7 +145,7 @@ out:
     ret += mcgen('''
     error_propagate(errp, err);
 ''')
-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     visit_free(v);
     v = qapi_dealloc_visitor_new();
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 9c88627..09c0a2a 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -69,7 +69,7 @@ def gen_event_send(name, arg_type):
 ''',
                 proto=gen_event_send_proto(name, arg_type))

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     QObject *obj;
     Visitor *v;
@@ -88,7 +88,7 @@ def gen_event_send(name, arg_type):
 ''',
                  name=name)

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     v = qmp_output_visitor_new(&obj);

@@ -116,7 +116,7 @@ def gen_event_send(name, arg_type):
 ''',
                  c_enum=c_enum_const(event_enum_name, name))

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
 out:
     visit_free(v);
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 5ace2cf..dabc42e 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -91,7 +91,7 @@ struct %(c_name)s {
     # potential issues with attempting to malloc space for zero-length
     # structs in C, and also incompatibility with C++ (where an empty
     # struct is size 1).
-    if not (base and base.members) and not members and not variants:
+    if (not base or base.is_empty()) and not members and not variants:
         ret += mcgen('''
     char qapi_dummy_for_empty_struct;
 ''')
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 06/17] qapi: Drop useless gen_err_check()
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (4 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 05/17] qapi: Add type.is_empty() helper Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 07/17] qapi-event: Simplify visit of non-implicit data Eric Blake
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

Ever since commit 12f254f removed the last parameterization
of gen_err_check(), it no longer makes sense to hide the three
lines of generated C code behind a macro call. Just inline it
into the remaining users.

No change to generated code.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: new patch
v8: new patch
---
 scripts/qapi.py          | 8 --------
 scripts/qapi-commands.py | 4 +++-
 scripts/qapi-visit.py    | 8 ++++++--
 3 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 27284be..9c48f6d 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1656,14 +1656,6 @@ def gen_params(arg_type, extra):
     return ret


-def gen_err_check():
-    return mcgen('''
-    if (err) {
-        goto out;
-    }
-''')
-
-
 #
 # Common command line parsing
 #
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index c93470c..333a46f 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -46,8 +46,10 @@ def gen_call(name, arg_type, ret_type):
 ''',
                 c_name=c_name(name), args=argstr, lhs=lhs)
     if ret_type:
-        ret += gen_err_check()
         ret += mcgen('''
+    if (err) {
+        goto out;
+    }

     qmp_marshal_output_%(c_name)s(retval, ret, &err);
 ''',
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 0b9e298..96f2491 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -47,9 +47,11 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
     if base:
         ret += mcgen('''
     visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err);
+    if (err) {
+        goto out;
+    }
 ''',
                      c_type=base.c_name())
-        ret += gen_err_check()

     for memb in members:
         if memb.optional:
@@ -60,10 +62,12 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
             push_indent()
         ret += mcgen('''
     visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, &err);
+    if (err) {
+        goto out;
+    }
 ''',
                      c_type=memb.type.c_name(), name=memb.name,
                      c_name=c_name(memb.name))
-        ret += gen_err_check()
         if memb.optional:
             pop_indent()
             ret += mcgen('''
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 07/17] qapi-event: Simplify visit of non-implicit data
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (5 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 06/17] qapi: Drop useless gen_err_check() Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 08/17] qapi: Plumb in 'boxed' to qapi generator lower levels Eric Blake
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

Commit 7ce106a9 documented why we don't generated a visit_type_FOO()
for implicit types; and therefore events with an anonymous type for
'data' have to open-code a visit.  Note that the open-coded visit in
qapi-event.c is slightly different from what is done in
qapi-visit.c for normal types, in part because we don't have to
check for *obj being NULL or free things on error.  But where the
type is not implicit, it is nicer to reuse the normal visit instead
of open-coding a duplicate.

At the moment, the only event with a non-implicit 'data' is in the
testsuite, where test-qapi-event.c changes as follows:

|@@ -155,6 +155,7 @@ void qapi_event_send___org_qemu_x_event(
|     __org_qemu_x_Struct param = {
|         __org_qemu_x_member1, (char *)__org_qemu_x_member2, has_q_wchar_t, q_wchar_t
|     };
|+    __org_qemu_x_Struct *arg = &param;
|
|     emit = qmp_event_get_func_emit();
|     if (!emit) {
|@@ -164,16 +165,7 @@ void qapi_event_send___org_qemu_x_event(
|     qmp = qmp_event_build_dict("__ORG.QEMU_X-EVENT");
|
|     v = qmp_output_visitor_new(&obj);
|-
|-    visit_start_struct(v, "__ORG.QEMU_X-EVENT", NULL, 0, &err);
|-    if (err) {
|-        goto out;
|-    }
|-    visit_type___org_qemu_x_Struct_members(v, &param, &err);
|-    if (!err) {
|-    if (!err) {
|-        visit_check_struct(v, &err);
|-    }
|-    visit_end_struct(v, NULL);
|+    visit_type___org_qemu_x_Struct(v, "__ORG.QEMU_X-EVENT", &arg, &err);
|     if (err) {
|         goto out;
|     }

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: fix pep8 warning
v8: new patch
---
 scripts/qapi-event.py | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 09c0a2a..b9c6b6e 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -49,6 +49,11 @@ def gen_param_var(typ):

     };
 ''')
+    if not typ.is_implicit():
+        ret += mcgen('''
+    %(c_name)s *arg = &param;
+''',
+                     c_name=typ.c_name())
     return ret


@@ -91,6 +96,14 @@ def gen_event_send(name, arg_type):
     if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     v = qmp_output_visitor_new(&obj);
+''')
+        if not arg_type.is_implicit():
+            ret += mcgen('''
+    visit_type_%(c_name)s(v, "%(name)s", &arg, &err);
+''',
+                         name=name, c_name=arg_type.c_name())
+        else:
+            ret += mcgen('''

     visit_start_struct(v, "%(name)s", NULL, 0, &err);
     if (err) {
@@ -101,14 +114,16 @@ def gen_event_send(name, arg_type):
         visit_check_struct(v, &err);
     }
     visit_end_struct(v, NULL);
+''',
+                         name=name, c_name=arg_type.c_name())
+        ret += mcgen('''
     if (err) {
         goto out;
     }

     visit_complete(v, &obj);
     qdict_put_obj(qmp, "data", obj);
-''',
-                     name=name, c_name=arg_type.c_name())
+''')

     ret += mcgen('''
     emit(%(c_enum)s, qmp, &err);
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 08/17] qapi: Plumb in 'boxed' to qapi generator lower levels
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (6 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 07/17] qapi-event: Simplify visit of non-implicit data Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14 14:17   ` Markus Armbruster
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 09/17] qapi: Implement boxed types for commands/events Eric Blake
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

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

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

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

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: change title, s/box/boxed/
v8: improve commit message, defer some implementation, rebase without
gen_err_check()
v7: rebase to master
v6: rebase to earlier changes
---
 scripts/qapi.py                         | 43 +++++++++++++++++++++------------
 scripts/qapi-commands.py                | 20 ++++++++-------
 scripts/qapi-event.py                   | 18 +++++++-------
 scripts/qapi-introspect.py              |  4 +--
 tests/qapi-schema/event-case.out        |  1 +
 tests/qapi-schema/ident-with-escape.out |  2 +-
 tests/qapi-schema/indented-expr.out     |  4 +--
 tests/qapi-schema/qapi-schema-test.out  | 19 +++++++++------
 tests/qapi-schema/test-qapi.py          |  8 +++---
 9 files changed, 70 insertions(+), 49 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 9c48f6d..0c6159c 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -826,10 +826,10 @@ class QAPISchemaVisitor(object):
         pass

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response):
+                      gen, success_response, boxed):
         pass

-    def visit_event(self, name, info, arg_type):
+    def visit_event(self, name, info, arg_type, boxed):
         pass


@@ -1165,7 +1165,8 @@ class QAPISchemaAlternateType(QAPISchemaType):


 class QAPISchemaCommand(QAPISchemaEntity):
-    def __init__(self, name, info, arg_type, ret_type, gen, success_response):
+    def __init__(self, name, info, arg_type, ret_type, gen, success_response,
+                 boxed):
         QAPISchemaEntity.__init__(self, name, info)
         assert not arg_type or isinstance(arg_type, str)
         assert not ret_type or isinstance(ret_type, str)
@@ -1175,12 +1176,14 @@ class QAPISchemaCommand(QAPISchemaEntity):
         self.ret_type = None
         self.gen = gen
         self.success_response = success_response
+        self.boxed = boxed

     def check(self, schema):
         if self._arg_type_name:
             self.arg_type = schema.lookup_type(self._arg_type_name)
             assert isinstance(self.arg_type, QAPISchemaObjectType)
             assert not self.arg_type.variants   # not implemented
+            assert not self.boxed               # not implemented
         if self._ret_type_name:
             self.ret_type = schema.lookup_type(self._ret_type_name)
             assert isinstance(self.ret_type, QAPISchemaType)
@@ -1188,24 +1191,26 @@ class QAPISchemaCommand(QAPISchemaEntity):
     def visit(self, visitor):
         visitor.visit_command(self.name, self.info,
                               self.arg_type, self.ret_type,
-                              self.gen, self.success_response)
+                              self.gen, self.success_response, self.boxed)


 class QAPISchemaEvent(QAPISchemaEntity):
-    def __init__(self, name, info, arg_type):
+    def __init__(self, name, info, arg_type, boxed):
         QAPISchemaEntity.__init__(self, name, info)
         assert not arg_type or isinstance(arg_type, str)
         self._arg_type_name = arg_type
         self.arg_type = None
+        self.boxed = boxed

     def check(self, schema):
         if self._arg_type_name:
             self.arg_type = schema.lookup_type(self._arg_type_name)
             assert isinstance(self.arg_type, QAPISchemaObjectType)
             assert not self.arg_type.variants   # not implemented
+            assert not self.boxed               # not implemented

     def visit(self, visitor):
-        visitor.visit_event(self.name, self.info, self.arg_type)
+        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)


 class QAPISchema(object):
@@ -1381,6 +1386,7 @@ class QAPISchema(object):
         rets = expr.get('returns')
         gen = expr.get('gen', True)
         success_response = expr.get('success-response', True)
+        boxed = expr.get('boxed', False)
         if isinstance(data, OrderedDict):
             data = self._make_implicit_object_type(
                 name, info, 'arg', self._make_members(data, info))
@@ -1388,15 +1394,16 @@ class QAPISchema(object):
             assert len(rets) == 1
             rets = self._make_array_type(rets[0], info)
         self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
-                                           success_response))
+                                           success_response, boxed))

     def _def_event(self, expr, info):
         name = expr['event']
         data = expr.get('data')
+        boxed = expr.get('boxed', False)
         if isinstance(data, OrderedDict):
             data = self._make_implicit_object_type(
                 name, info, 'arg', self._make_members(data, info))
-        self._def_entity(QAPISchemaEvent(name, info, data))
+        self._def_entity(QAPISchemaEvent(name, info, data, boxed))

     def _def_exprs(self):
         for expr_elem in self.exprs:
@@ -1639,18 +1646,22 @@ extern const char *const %(c_name)s_lookup[];
     return ret


-def gen_params(arg_type, extra):
+def gen_params(arg_type, boxed, extra):
     if not arg_type:
         return extra
-    assert not arg_type.variants
     ret = ''
     sep = ''
-    for memb in arg_type.members:
-        ret += sep
-        sep = ', '
-        if memb.optional:
-            ret += 'bool has_%s, ' % c_name(memb.name)
-        ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name))
+    if boxed:
+        assert False     # not implemented
+    else:
+        assert not arg_type.variants
+        for memb in arg_type.members:
+            ret += sep
+            sep = ', '
+            if memb.optional:
+                ret += 'bool has_%s, ' % c_name(memb.name)
+            ret += '%s %s' % (memb.type.c_param_type(),
+                              c_name(memb.name))
     if extra:
         ret += sep + extra
     return ret
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 333a46f..0090fb4 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -16,20 +16,22 @@ from qapi import *
 import re


-def gen_command_decl(name, arg_type, ret_type):
+def gen_command_decl(name, arg_type, boxed, ret_type):
     return mcgen('''
 %(c_type)s qmp_%(c_name)s(%(params)s);
 ''',
                  c_type=(ret_type and ret_type.c_type()) or 'void',
                  c_name=c_name(name),
-                 params=gen_params(arg_type, 'Error **errp'))
+                 params=gen_params(arg_type, boxed, 'Error **errp'))


-def gen_call(name, arg_type, ret_type):
+def gen_call(name, arg_type, boxed, ret_type):
     ret = ''

     argstr = ''
-    if arg_type:
+    if boxed:
+        assert False    # not implemented
+    elif arg_type:
         assert not arg_type.variants
         for memb in arg_type.members:
             if memb.optional:
@@ -94,7 +96,7 @@ def gen_marshal_decl(name):
                  proto=gen_marshal_proto(name))


-def gen_marshal(name, arg_type, ret_type):
+def gen_marshal(name, arg_type, boxed, ret_type):
     ret = mcgen('''

 %(proto)s
@@ -136,7 +138,7 @@ def gen_marshal(name, arg_type, ret_type):
     (void)args;
 ''')

-    ret += gen_call(name, arg_type, ret_type)
+    ret += gen_call(name, arg_type, boxed, ret_type)

     # 'goto out' produced above for arg_type, and by gen_call() for ret_type
     if (arg_type and not arg_type.is_empty()) or ret_type:
@@ -212,16 +214,16 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         self._visited_ret_types = None

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response):
+                      gen, success_response, boxed):
         if not gen:
             return
-        self.decl += gen_command_decl(name, arg_type, ret_type)
+        self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
         if ret_type and ret_type not in self._visited_ret_types:
             self._visited_ret_types.add(ret_type)
             self.defn += gen_marshal_output(ret_type)
         if middle_mode:
             self.decl += gen_marshal_decl(name)
-        self.defn += gen_marshal(name, arg_type, ret_type)
+        self.defn += gen_marshal(name, arg_type, boxed, ret_type)
         if not middle_mode:
             self._regy += gen_register_command(name, success_response)

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index b9c6b6e..53410c2 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -14,18 +14,18 @@
 from qapi import *


-def gen_event_send_proto(name, arg_type):
+def gen_event_send_proto(name, arg_type, boxed):
     return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
         'c_name': c_name(name.lower()),
-        'param': gen_params(arg_type, 'Error **errp')}
+        'param': gen_params(arg_type, boxed, 'Error **errp')}


-def gen_event_send_decl(name, arg_type):
+def gen_event_send_decl(name, arg_type, boxed):
     return mcgen('''

 %(proto)s;
 ''',
-                 proto=gen_event_send_proto(name, arg_type))
+                 proto=gen_event_send_proto(name, arg_type, boxed))


 # Declare and initialize an object 'qapi' using parameters from gen_params()
@@ -57,7 +57,7 @@ def gen_param_var(typ):
     return ret


-def gen_event_send(name, arg_type):
+def gen_event_send(name, arg_type, boxed):
     # FIXME: Our declaration of local variables (and of 'errp' in the
     # parameter list) can collide with exploded members of the event's
     # data type passed in as parameters.  If this collision ever hits in
@@ -72,7 +72,7 @@ def gen_event_send(name, arg_type):
     Error *err = NULL;
     QMPEventFuncEmit emit;
 ''',
-                proto=gen_event_send_proto(name, arg_type))
+                proto=gen_event_send_proto(name, arg_type, boxed))

     if arg_type and not arg_type.is_empty():
         ret += mcgen('''
@@ -160,9 +160,9 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
         self.defn += gen_enum_lookup(event_enum_name, self._event_names)
         self._event_names = None

-    def visit_event(self, name, info, arg_type):
-        self.decl += gen_event_send_decl(name, arg_type)
-        self.defn += gen_event_send(name, arg_type)
+    def visit_event(self, name, info, arg_type, boxed):
+        self.decl += gen_event_send_decl(name, arg_type, boxed)
+        self.defn += gen_event_send(name, arg_type, boxed)
         self._event_names.append(name)


diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index e0f926b..60e9877 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -154,14 +154,14 @@ const char %(c_name)s[] = %(c_string)s;
                                     for m in variants.variants]})

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response):
+                      gen, success_response, box):
         arg_type = arg_type or self._schema.the_empty_object_type
         ret_type = ret_type or self._schema.the_empty_object_type
         self._gen_json(name, 'command',
                        {'arg-type': self._use_type(arg_type),
                         'ret-type': self._use_type(ret_type)})

-    def visit_event(self, name, info, arg_type):
+    def visit_event(self, name, info, arg_type, box):
         arg_type = arg_type or self._schema.the_empty_object_type
         self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})

diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
index b6b4134..5a0f2bf 100644
--- a/tests/qapi-schema/event-case.out
+++ b/tests/qapi-schema/event-case.out
@@ -1,4 +1,5 @@
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
 event oops None
+   boxed=False
 object q_empty
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index 382ce2f..1d2722c 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -1,7 +1,7 @@
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
 command fooA q_obj_fooA-arg -> None
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
 object q_empty
 object q_obj_fooA-arg
     member bar1: str optional=False
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index ae3293a..e8171c9 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,7 +1,7 @@
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
 command eins None -> None
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
 object q_empty
 command zwei None -> None
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index f34ecc7..1aace71 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -23,9 +23,13 @@ alternate AltStrNum
     case s: str
     case n: number
 event EVENT_A None
+   boxed=False
 event EVENT_B None
+   boxed=False
 event EVENT_C q_obj_EVENT_C-arg
+   boxed=False
 event EVENT_D q_obj_EVENT_D-arg
+   boxed=False
 object Empty1
 object Empty2
     base Empty1
@@ -124,6 +128,7 @@ object UserDefZero
 object WrapAlternate
     member alt: UserDefAlternate optional=False
 event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
+   boxed=False
 alternate __org.qemu_x-Alt
     tag type
     case __org.qemu_x-branch: str
@@ -147,11 +152,11 @@ object __org.qemu_x-Union2
     tag __org.qemu_x-member1
     case __org.qemu_x-value: __org.qemu_x-Struct2
 command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
 command guest-get-time q_obj_guest-get-time-arg -> int
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
 command guest-sync q_obj_guest-sync-arg -> any
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
 object q_empty
 object q_obj_EVENT_C-arg
     member a: int optional=True
@@ -212,10 +217,10 @@ object q_obj_user_def_cmd2-arg
     member ud1a: UserDefOne optional=False
     member ud1b: UserDefOne optional=True
 command user_def_cmd None -> None
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
 command user_def_cmd0 Empty2 -> Empty2
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
 command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
 command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
-   gen=True success_response=True
+   gen=True success_response=True boxed=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index bedd145..ef74e2c 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -36,13 +36,15 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
         self._print_variants(variants)

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response):
+                      gen, success_response, boxed):
         print 'command %s %s -> %s' % \
             (name, arg_type and arg_type.name, ret_type and ret_type.name)
-        print '   gen=%s success_response=%s' % (gen, success_response)
+        print '   gen=%s success_response=%s boxed=%s' % \
+            (gen, success_response, boxed)

-    def visit_event(self, name, info, arg_type):
+    def visit_event(self, name, info, arg_type, boxed):
         print 'event %s %s' % (name, arg_type and arg_type.name)
+        print '   boxed=%s' % boxed

     @staticmethod
     def _print_variants(variants):
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 09/17] qapi: Implement boxed types for commands/events
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (7 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 08/17] qapi: Plumb in 'boxed' to qapi generator lower levels Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14 14:26   ` Markus Armbruster
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 10/17] block: Simplify block_set_io_throttle Eric Blake
                   ` (8 subsequent siblings)
  17 siblings, 1 reply; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

Turn on the ability to pass command and event arguments in
a single boxed parameter, which must name a non-empty type
(although the type can be a struct with all optional members).
For structs, it makes it possible to pass a single qapi type
instead of a breakout of all struct members (useful if the
arguments are already in a struct or if the number of members
is large); for other complex types, it is now possible to use
a union or alternate as the data for a command or event.

The empty type may be technically feasible if needed down the
road, but it's easier to forbid it now and relax things to allow
it later, than it is to allow it now and have to special case
how the generated 'q_empty' type is handled (see commit 7ce106a9
for reasons why nothing is generated for the empty type).  An
alternate type is never considered empty, but now that a boxed
type can be either an object or an alternate, we have to provide
a trivial QAPISchemaAlternateType.is_empty().  The new call to
arg_type.is_empty() during QAPISchemaCommand.check() requires
that we first check the type in question; but there is no chance
of introducing a cycle since objects do not refer back to commands.

We still have a split in syntax checking between ad-hoc parsing
up front (merely validates that 'boxed' has a sane value) and
during .check() methods (if 'boxed' is set, then 'data' must name
a non-empty user-defined type).

Generated code is unchanged, as long as no client uses the
new feature.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: s/box/boxed/, formatting tweak, commit message improvements,
restore assertions rather than relying on implicit checking
v8: forbid empty type, allow alternates, improve docs
v7: rebase to latest
v6: retitle, rebase, and merge v5 40/46 and 41/46 into one patch
---
 scripts/qapi.py                         | 63 ++++++++++++++++++++++++++-------
 scripts/qapi-commands.py                |  3 +-
 scripts/qapi-event.py                   |  5 ++-
 tests/test-qmp-commands.c               |  8 +++++
 docs/qapi-code-gen.txt                  | 27 ++++++++++++--
 tests/Makefile.include                  |  5 +++
 tests/qapi-schema/args-bad-box.err      |  1 +
 tests/qapi-schema/args-bad-box.exit     |  1 +
 tests/qapi-schema/args-bad-box.json     |  2 ++
 tests/qapi-schema/args-bad-box.out      |  0
 tests/qapi-schema/args-box-anon.err     |  1 +
 tests/qapi-schema/args-box-anon.exit    |  1 +
 tests/qapi-schema/args-box-anon.json    |  2 ++
 tests/qapi-schema/args-box-anon.out     |  0
 tests/qapi-schema/args-box-empty.err    |  1 +
 tests/qapi-schema/args-box-empty.exit   |  1 +
 tests/qapi-schema/args-box-empty.json   |  3 ++
 tests/qapi-schema/args-box-empty.out    |  0
 tests/qapi-schema/args-box-string.err   |  1 +
 tests/qapi-schema/args-box-string.exit  |  1 +
 tests/qapi-schema/args-box-string.json  |  2 ++
 tests/qapi-schema/args-box-string.out   |  0
 tests/qapi-schema/args-union.err        |  2 +-
 tests/qapi-schema/args-union.json       |  3 +-
 tests/qapi-schema/event-box-empty.err   |  1 +
 tests/qapi-schema/event-box-empty.exit  |  1 +
 tests/qapi-schema/event-box-empty.json  |  2 ++
 tests/qapi-schema/event-box-empty.out   |  0
 tests/qapi-schema/qapi-schema-test.json |  4 +++
 tests/qapi-schema/qapi-schema-test.out  |  8 +++++
 30 files changed, 129 insertions(+), 20 deletions(-)
 create mode 100644 tests/qapi-schema/args-bad-box.err
 create mode 100644 tests/qapi-schema/args-bad-box.exit
 create mode 100644 tests/qapi-schema/args-bad-box.json
 create mode 100644 tests/qapi-schema/args-bad-box.out
 create mode 100644 tests/qapi-schema/args-box-anon.err
 create mode 100644 tests/qapi-schema/args-box-anon.exit
 create mode 100644 tests/qapi-schema/args-box-anon.json
 create mode 100644 tests/qapi-schema/args-box-anon.out
 create mode 100644 tests/qapi-schema/args-box-empty.err
 create mode 100644 tests/qapi-schema/args-box-empty.exit
 create mode 100644 tests/qapi-schema/args-box-empty.json
 create mode 100644 tests/qapi-schema/args-box-empty.out
 create mode 100644 tests/qapi-schema/args-box-string.err
 create mode 100644 tests/qapi-schema/args-box-string.exit
 create mode 100644 tests/qapi-schema/args-box-string.json
 create mode 100644 tests/qapi-schema/args-box-string.out
 create mode 100644 tests/qapi-schema/event-box-empty.err
 create mode 100644 tests/qapi-schema/event-box-empty.exit
 create mode 100644 tests/qapi-schema/event-box-empty.json
 create mode 100644 tests/qapi-schema/event-box-empty.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 0c6159c..21bc32f 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -522,10 +522,14 @@ def check_type(expr_info, source, value, allow_array=False,

 def check_command(expr, expr_info):
     name = expr['command']
+    boxed = expr.get('boxed', False)

+    args_meta = ['struct']
+    if boxed:
+        args_meta += ['union', 'alternate']
     check_type(expr_info, "'data' for command '%s'" % name,
-               expr.get('data'), allow_dict=True, allow_optional=True,
-               allow_metas=['struct'])
+               expr.get('data'), allow_dict=not boxed, allow_optional=True,
+               allow_metas=args_meta)
     returns_meta = ['union', 'struct']
     if name in returns_whitelist:
         returns_meta += ['built-in', 'alternate', 'enum']
@@ -537,11 +541,15 @@ def check_command(expr, expr_info):
 def check_event(expr, expr_info):
     global events
     name = expr['event']
+    boxed = expr.get('boxed', False)

+    meta = ['struct']
+    if boxed:
+        meta += ['union', 'alternate']
     events.append(name)
     check_type(expr_info, "'data' for event '%s'" % name,
-               expr.get('data'), allow_dict=True, allow_optional=True,
-               allow_metas=['struct'])
+               expr.get('data'), allow_dict=not boxed, allow_optional=True,
+               allow_metas=meta)


 def check_union(expr, expr_info):
@@ -694,6 +702,10 @@ def check_keys(expr_elem, meta, required, optional=[]):
             raise QAPIExprError(info,
                                 "'%s' of %s '%s' should only use false value"
                                 % (key, meta, name))
+        if key == 'boxed' and value is not True:
+            raise QAPIExprError(info,
+                                "'%s' of %s '%s' should only use true value"
+                                % (key, meta, name))
     for key in required:
         if key not in expr:
             raise QAPIExprError(info,
@@ -725,10 +737,10 @@ def check_exprs(exprs):
             add_struct(expr, info)
         elif 'command' in expr:
             check_keys(expr_elem, 'command', [],
-                       ['data', 'returns', 'gen', 'success-response'])
+                       ['data', 'returns', 'gen', 'success-response', 'boxed'])
             add_name(expr['command'], info, 'command')
         elif 'event' in expr:
-            check_keys(expr_elem, 'event', [], ['data'])
+            check_keys(expr_elem, 'event', [], ['data', 'boxed'])
             add_name(expr['event'], info, 'event')
         else:
             raise QAPIExprError(expr_elem['info'],
@@ -1163,6 +1175,9 @@ class QAPISchemaAlternateType(QAPISchemaType):
     def visit(self, visitor):
         visitor.visit_alternate_type(self.name, self.info, self.variants)

+    def is_empty(self):
+        return False
+

 class QAPISchemaCommand(QAPISchemaEntity):
     def __init__(self, name, info, arg_type, ret_type, gen, success_response,
@@ -1181,9 +1196,19 @@ class QAPISchemaCommand(QAPISchemaEntity):
     def check(self, schema):
         if self._arg_type_name:
             self.arg_type = schema.lookup_type(self._arg_type_name)
-            assert isinstance(self.arg_type, QAPISchemaObjectType)
-            assert not self.arg_type.variants   # not implemented
-            assert not self.boxed               # not implemented
+            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
+                    isinstance(self.arg_type, QAPISchemaAlternateType))
+            self.arg_type.check(schema)
+            if self.boxed:
+                if self.arg_type.is_empty():
+                    raise QAPIExprError(self.info,
+                                        "Cannot use 'boxed' with empty type")
+            else:
+                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
+                assert not self.arg_type.variants
+        elif self.boxed:
+            raise QAPIExprError(self.info,
+                                "Use of 'boxed' requires 'data'")
         if self._ret_type_name:
             self.ret_type = schema.lookup_type(self._ret_type_name)
             assert isinstance(self.ret_type, QAPISchemaType)
@@ -1205,9 +1230,19 @@ class QAPISchemaEvent(QAPISchemaEntity):
     def check(self, schema):
         if self._arg_type_name:
             self.arg_type = schema.lookup_type(self._arg_type_name)
-            assert isinstance(self.arg_type, QAPISchemaObjectType)
-            assert not self.arg_type.variants   # not implemented
-            assert not self.boxed               # not implemented
+            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
+                    isinstance(self.arg_type, QAPISchemaAlternateType))
+            self.arg_type.check(schema)
+            if self.boxed:
+                if self.arg_type.is_empty():
+                    raise QAPIExprError(self.info,
+                                        "Cannot use 'boxed' with empty type")
+            else:
+                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
+                assert not self.arg_type.variants
+        elif self.boxed:
+            raise QAPIExprError(self.info,
+                                "Use of 'boxed' requires 'data'")

     def visit(self, visitor):
         visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
@@ -1648,11 +1683,13 @@ extern const char *const %(c_name)s_lookup[];

 def gen_params(arg_type, boxed, extra):
     if not arg_type:
+        assert not boxed
         return extra
     ret = ''
     sep = ''
     if boxed:
-        assert False     # not implemented
+        ret += '%s arg' % arg_type.c_param_type()
+        sep = ', '
     else:
         assert not arg_type.variants
         for memb in arg_type.members:
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 0090fb4..a06a2c4 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -30,7 +30,8 @@ def gen_call(name, arg_type, boxed, ret_type):

     argstr = ''
     if boxed:
-        assert False    # not implemented
+        assert arg_type and not arg_type.is_empty()
+        argstr = '&arg, '
     elif arg_type:
         assert not arg_type.variants
         for memb in arg_type.members:
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 53410c2..38d8211 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -79,7 +79,10 @@ def gen_event_send(name, arg_type, boxed):
     QObject *obj;
     Visitor *v;
 ''')
-        ret += gen_param_var(arg_type)
+        if not boxed:
+            ret += gen_param_var(arg_type)
+    else:
+        assert not boxed

     ret += mcgen('''

diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 8ffeb04..5af1a46 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -59,6 +59,14 @@ QObject *qmp_guest_sync(QObject *arg, Error **errp)
     return arg;
 }

+void qmp_boxed_struct(UserDefZero *arg, Error **errp)
+{
+}
+
+void qmp_boxed_union(UserDefNativeListUnion *arg, Error **errp)
+{
+}
+
 __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
                                               __org_qemu_x_StructList *b,
                                               __org_qemu_x_Union2 *c,
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 48b0b31..de298dc 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -410,7 +410,7 @@ following example objects:
 === Commands ===

 Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
-         '*returns': TYPE-NAME,
+         '*returns': TYPE-NAME, '*boxed': true,
          '*gen': false, '*success-response': false }

 Commands are defined by using a dictionary containing several members,
@@ -461,6 +461,20 @@ which would validate this Client JSON Protocol transaction:
  => { "execute": "my-second-command" }
  <= { "return": [ { "value": "one" }, { } ] }

+The generator emits a prototype for the user's function implementing
+the command.  Normally, 'data' is a dictionary for an anonymous type,
+or names a struct type (possibly empty, but not a union), and its
+members are passed as separate arguments to this function.  If the
+command definition includes a key 'boxed' with the boolean value true,
+then 'data' is instead the name of any non-empty complex type
+(struct, union, or alternate), and a pointer to that QAPI type is
+passed as a single argument.
+
+The generator also emits a marshalling function that extracts
+arguments for the user's function out of an input QDict, calls the
+user's function, and if it succeeded, builds an output QObject from
+its return value.
+
 In rare cases, QAPI cannot express a type-safe representation of a
 corresponding Client JSON Protocol command.  You then have to suppress
 generation of a marshalling function by including a key 'gen' with
@@ -484,7 +498,8 @@ use of this member.

 === Events ===

-Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT }
+Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
+         '*boxed': true }

 Events are defined with the keyword 'event'.  It is not allowed to
 name an event 'MAX', since the generator also produces a C enumeration
@@ -505,6 +520,14 @@ Resulting in this JSON object:
   "data": { "b": "test string" },
   "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }

+The generator emits a function to send the event.  Normally, 'data' is
+a dictionary for an anonymous type, or names a struct type (possibly
+empty, but not a union), and its members are passed as separate
+arguments to this function.  If the event definition includes a key
+'boxed' with the boolean value true, then 'data' is instead the name of
+any non-empty complex type (struct, union, or alternate), and a
+pointer to that QAPI type is passed as a single argument.
+

 == Client JSON Protocol introspection ==

diff --git a/tests/Makefile.include b/tests/Makefile.include
index a04c199..829e58e 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -284,6 +284,10 @@ qapi-schema += args-alternate.json
 qapi-schema += args-any.json
 qapi-schema += args-array-empty.json
 qapi-schema += args-array-unknown.json
+qapi-schema += args-bad-box.json
+qapi-schema += args-box-anon.json
+qapi-schema += args-box-empty.json
+qapi-schema += args-box-string.json
 qapi-schema += args-int.json
 qapi-schema += args-invalid.json
 qapi-schema += args-member-array-bad.json
@@ -317,6 +321,7 @@ qapi-schema += enum-wrong-data.json
 qapi-schema += escape-outside-string.json
 qapi-schema += escape-too-big.json
 qapi-schema += escape-too-short.json
+qapi-schema += event-box-empty.json
 qapi-schema += event-case.json
 qapi-schema += event-nest-struct.json
 qapi-schema += flat-union-array-branch.json
diff --git a/tests/qapi-schema/args-bad-box.err b/tests/qapi-schema/args-bad-box.err
new file mode 100644
index 0000000..0a326a8
--- /dev/null
+++ b/tests/qapi-schema/args-bad-box.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-bad-box.json:2: 'boxed' of command 'foo' should only use true value
diff --git a/tests/qapi-schema/args-bad-box.exit b/tests/qapi-schema/args-bad-box.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/args-bad-box.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-bad-box.json b/tests/qapi-schema/args-bad-box.json
new file mode 100644
index 0000000..dea0cd0
--- /dev/null
+++ b/tests/qapi-schema/args-bad-box.json
@@ -0,0 +1,2 @@
+# 'boxed' should only appear with value true
+{ 'command': 'foo', 'boxed': false }
diff --git a/tests/qapi-schema/args-bad-box.out b/tests/qapi-schema/args-bad-box.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/args-box-anon.err b/tests/qapi-schema/args-box-anon.err
new file mode 100644
index 0000000..11eaefc
--- /dev/null
+++ b/tests/qapi-schema/args-box-anon.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-box-anon.json:2: 'data' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/args-box-anon.exit b/tests/qapi-schema/args-box-anon.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/args-box-anon.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-box-anon.json b/tests/qapi-schema/args-box-anon.json
new file mode 100644
index 0000000..95f60da
--- /dev/null
+++ b/tests/qapi-schema/args-box-anon.json
@@ -0,0 +1,2 @@
+# 'boxed' can only be used with named types
+{ 'command': 'foo', 'boxed': true, 'data': { 'string': 'str' } }
diff --git a/tests/qapi-schema/args-box-anon.out b/tests/qapi-schema/args-box-anon.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/args-box-empty.err b/tests/qapi-schema/args-box-empty.err
new file mode 100644
index 0000000..14831c2
--- /dev/null
+++ b/tests/qapi-schema/args-box-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-box-empty.json:3: Cannot use 'boxed' with empty type
diff --git a/tests/qapi-schema/args-box-empty.exit b/tests/qapi-schema/args-box-empty.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/args-box-empty.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-box-empty.json b/tests/qapi-schema/args-box-empty.json
new file mode 100644
index 0000000..52717e0
--- /dev/null
+++ b/tests/qapi-schema/args-box-empty.json
@@ -0,0 +1,3 @@
+# 'boxed' requires a non-empty type
+{ 'struct': 'Empty', 'data': {} }
+{ 'command': 'foo', 'boxed': true, 'data': 'Empty' }
diff --git a/tests/qapi-schema/args-box-empty.out b/tests/qapi-schema/args-box-empty.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/args-box-string.err b/tests/qapi-schema/args-box-string.err
new file mode 100644
index 0000000..4c12d96
--- /dev/null
+++ b/tests/qapi-schema/args-box-string.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-box-string.json:2: 'data' for command 'foo' cannot use built-in type 'str'
diff --git a/tests/qapi-schema/args-box-string.exit b/tests/qapi-schema/args-box-string.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/args-box-string.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-box-string.json b/tests/qapi-schema/args-box-string.json
new file mode 100644
index 0000000..f91a150
--- /dev/null
+++ b/tests/qapi-schema/args-box-string.json
@@ -0,0 +1,2 @@
+# 'boxed' requires a complex (not built-in) type
+{ 'command': 'foo', 'boxed': true, 'data': 'str' }
diff --git a/tests/qapi-schema/args-box-string.out b/tests/qapi-schema/args-box-string.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/args-union.err b/tests/qapi-schema/args-union.err
index 1d693d7..f8ad223 100644
--- a/tests/qapi-schema/args-union.err
+++ b/tests/qapi-schema/args-union.err
@@ -1 +1 @@
-tests/qapi-schema/args-union.json:4: 'data' for command 'oops' cannot use union type 'Uni'
+tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni'
diff --git a/tests/qapi-schema/args-union.json b/tests/qapi-schema/args-union.json
index 7bdcbb7..c0ce091 100644
--- a/tests/qapi-schema/args-union.json
+++ b/tests/qapi-schema/args-union.json
@@ -1,4 +1,3 @@
-# we do not allow union arguments
-# TODO should we support this?
+# use of union arguments requires 'box':true
 { 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } }
 { 'command': 'oops', 'data': 'Uni' }
diff --git a/tests/qapi-schema/event-box-empty.err b/tests/qapi-schema/event-box-empty.err
new file mode 100644
index 0000000..aa7909e
--- /dev/null
+++ b/tests/qapi-schema/event-box-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/event-box-empty.json:2: Use of 'boxed' requires 'data'
diff --git a/tests/qapi-schema/event-box-empty.exit b/tests/qapi-schema/event-box-empty.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/event-box-empty.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/event-box-empty.json b/tests/qapi-schema/event-box-empty.json
new file mode 100644
index 0000000..cb145f1
--- /dev/null
+++ b/tests/qapi-schema/event-box-empty.json
@@ -0,0 +1,2 @@
+# 'boxed' requires a non-empty type
+{ 'event': 'FOO', 'boxed': true }
diff --git a/tests/qapi-schema/event-box-empty.out b/tests/qapi-schema/event-box-empty.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index f571e1b..1719463 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -127,6 +127,8 @@
 { 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' },
   'returns': 'int' }
 { 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
+{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
+{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }

 # For testing integer range flattening in opts-visitor. The following schema
 # corresponds to the option format:
@@ -154,6 +156,8 @@
   'data': { '*a': 'int', '*b': 'UserDefOne', 'c': 'str' } }
 { 'event': 'EVENT_D',
   'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } }
+{ 'event': 'EVENT_E', 'boxed': true, 'data': 'UserDefZero' }
+{ 'event': 'EVENT_F', 'boxed': true, 'data': 'UserDefAlternate' }

 # test that we correctly compile downstream extensions, as well as munge
 # ticklish names
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 1aace71..9d99c4e 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -30,6 +30,10 @@ event EVENT_C q_obj_EVENT_C-arg
    boxed=False
 event EVENT_D q_obj_EVENT_D-arg
    boxed=False
+event EVENT_E UserDefZero
+   boxed=True
+event EVENT_F UserDefAlternate
+   boxed=True
 object Empty1
 object Empty2
     base Empty1
@@ -153,6 +157,10 @@ object __org.qemu_x-Union2
     case __org.qemu_x-value: __org.qemu_x-Struct2
 command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
    gen=True success_response=True boxed=False
+command boxed-struct UserDefZero -> None
+   gen=True success_response=True boxed=True
+command boxed-union UserDefNativeListUnion -> None
+   gen=True success_response=True boxed=True
 command guest-get-time q_obj_guest-get-time-arg -> int
    gen=True success_response=True boxed=False
 command guest-sync q_obj_guest-sync-arg -> any
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 10/17] block: Simplify block_set_io_throttle
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (8 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 09/17] qapi: Implement boxed types for commands/events Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 11/17] block: Simplify drive-mirror Eric Blake
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, berrange, Kevin Wolf, Max Reitz, Luiz Capitulino,
	open list:Block layer core

Now that we can support boxed commands, use it to greatly
reduce the number of parameters (and likelihood of getting
out of sync) when adjusting throttle parameters.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Alberto Garcia <berto@igalia.com>

---
v9: s/box/boxed/, trivial enough to keep R-b
v8: tweak doc wording
v7: new patch
---
 qapi/block-core.json |  20 ++++++++--
 blockdev.c           | 111 +++++++++++++++++++--------------------------------
 hmp.c                |  45 +++++----------------
 3 files changed, 66 insertions(+), 110 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index ac8f5f6..cbf4410 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1312,6 +1312,21 @@
 # the device will be removed from its group and the rest of its
 # members will not be affected. The 'group' parameter is ignored.
 #
+# See BlockIOThrottle for parameter descriptions.
+#
+# Returns: Nothing on success
+#          If @device is not a valid block device, DeviceNotFound
+#
+# Since: 1.1
+##
+{ 'command': 'block_set_io_throttle', 'boxed': true,
+  'data': 'BlockIOThrottle' }
+
+##
+# BlockIOThrottle
+#
+# A set of parameters describing block throttling.
+#
 # @device: The name of the device
 #
 # @bps: total throughput limit in bytes per second
@@ -1378,12 +1393,9 @@
 #
 # @group: #optional throttle group name (Since 2.4)
 #
-# Returns: Nothing on success
-#          If @device is not a valid block device, DeviceNotFound
-#
 # Since: 1.1
 ##
-{ 'command': 'block_set_io_throttle',
+{ 'struct': 'BlockIOThrottle',
   'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
             'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
             '*bps_max': 'int', '*bps_rd_max': 'int',
diff --git a/blockdev.c b/blockdev.c
index 0f8065c..ddf30e1 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2631,49 +2631,17 @@ fail:
 }

 /* throttling disk I/O limits */
-void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
-                               int64_t bps_wr,
-                               int64_t iops,
-                               int64_t iops_rd,
-                               int64_t iops_wr,
-                               bool has_bps_max,
-                               int64_t bps_max,
-                               bool has_bps_rd_max,
-                               int64_t bps_rd_max,
-                               bool has_bps_wr_max,
-                               int64_t bps_wr_max,
-                               bool has_iops_max,
-                               int64_t iops_max,
-                               bool has_iops_rd_max,
-                               int64_t iops_rd_max,
-                               bool has_iops_wr_max,
-                               int64_t iops_wr_max,
-                               bool has_bps_max_length,
-                               int64_t bps_max_length,
-                               bool has_bps_rd_max_length,
-                               int64_t bps_rd_max_length,
-                               bool has_bps_wr_max_length,
-                               int64_t bps_wr_max_length,
-                               bool has_iops_max_length,
-                               int64_t iops_max_length,
-                               bool has_iops_rd_max_length,
-                               int64_t iops_rd_max_length,
-                               bool has_iops_wr_max_length,
-                               int64_t iops_wr_max_length,
-                               bool has_iops_size,
-                               int64_t iops_size,
-                               bool has_group,
-                               const char *group, Error **errp)
+void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
 {
     ThrottleConfig cfg;
     BlockDriverState *bs;
     BlockBackend *blk;
     AioContext *aio_context;

-    blk = blk_by_name(device);
+    blk = blk_by_name(arg->device);
     if (!blk) {
         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-                  "Device '%s' not found", device);
+                  "Device '%s' not found", arg->device);
         return;
     }

@@ -2682,59 +2650,59 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,

     bs = blk_bs(blk);
     if (!bs) {
-        error_setg(errp, "Device '%s' has no medium", device);
+        error_setg(errp, "Device '%s' has no medium", arg->device);
         goto out;
     }

     throttle_config_init(&cfg);
-    cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
-    cfg.buckets[THROTTLE_BPS_READ].avg  = bps_rd;
-    cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr;
+    cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
+    cfg.buckets[THROTTLE_BPS_READ].avg  = arg->bps_rd;
+    cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;

-    cfg.buckets[THROTTLE_OPS_TOTAL].avg = iops;
-    cfg.buckets[THROTTLE_OPS_READ].avg  = iops_rd;
-    cfg.buckets[THROTTLE_OPS_WRITE].avg = iops_wr;
+    cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
+    cfg.buckets[THROTTLE_OPS_READ].avg  = arg->iops_rd;
+    cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;

-    if (has_bps_max) {
-        cfg.buckets[THROTTLE_BPS_TOTAL].max = bps_max;
+    if (arg->has_bps_max) {
+        cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
     }
-    if (has_bps_rd_max) {
-        cfg.buckets[THROTTLE_BPS_READ].max = bps_rd_max;
+    if (arg->has_bps_rd_max) {
+        cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
     }
-    if (has_bps_wr_max) {
-        cfg.buckets[THROTTLE_BPS_WRITE].max = bps_wr_max;
+    if (arg->has_bps_wr_max) {
+        cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
     }
-    if (has_iops_max) {
-        cfg.buckets[THROTTLE_OPS_TOTAL].max = iops_max;
+    if (arg->has_iops_max) {
+        cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
     }
-    if (has_iops_rd_max) {
-        cfg.buckets[THROTTLE_OPS_READ].max = iops_rd_max;
+    if (arg->has_iops_rd_max) {
+        cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
     }
-    if (has_iops_wr_max) {
-        cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max;
+    if (arg->has_iops_wr_max) {
+        cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
     }

-    if (has_bps_max_length) {
-        cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = bps_max_length;
+    if (arg->has_bps_max_length) {
+        cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
     }
-    if (has_bps_rd_max_length) {
-        cfg.buckets[THROTTLE_BPS_READ].burst_length = bps_rd_max_length;
+    if (arg->has_bps_rd_max_length) {
+        cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
     }
-    if (has_bps_wr_max_length) {
-        cfg.buckets[THROTTLE_BPS_WRITE].burst_length = bps_wr_max_length;
+    if (arg->has_bps_wr_max_length) {
+        cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
     }
-    if (has_iops_max_length) {
-        cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = iops_max_length;
+    if (arg->has_iops_max_length) {
+        cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
     }
-    if (has_iops_rd_max_length) {
-        cfg.buckets[THROTTLE_OPS_READ].burst_length = iops_rd_max_length;
+    if (arg->has_iops_rd_max_length) {
+        cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
     }
-    if (has_iops_wr_max_length) {
-        cfg.buckets[THROTTLE_OPS_WRITE].burst_length = iops_wr_max_length;
+    if (arg->has_iops_wr_max_length) {
+        cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
     }

-    if (has_iops_size) {
-        cfg.op_size = iops_size;
+    if (arg->has_iops_size) {
+        cfg.op_size = arg->iops_size;
     }

     if (!throttle_is_valid(&cfg, errp)) {
@@ -2745,9 +2713,10 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
         /* Enable I/O limits if they're not enabled yet, otherwise
          * just update the throttling group. */
         if (!blk_get_public(blk)->throttle_state) {
-            blk_io_limits_enable(blk, has_group ? group : device);
-        } else if (has_group) {
-            blk_io_limits_update_group(blk, group);
+            blk_io_limits_enable(blk,
+                                 arg->has_group ? arg->group : arg->device);
+        } else if (arg->has_group) {
+            blk_io_limits_update_group(blk, arg->group);
         }
         /* Set the new throttling configuration */
         blk_set_io_limits(blk, &cfg);
diff --git a/hmp.c b/hmp.c
index 0cf5baa..c7ca776 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1439,42 +1439,17 @@ void hmp_change(Monitor *mon, const QDict *qdict)
 void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
 {
     Error *err = NULL;
+    BlockIOThrottle throttle = {
+        .device = (char *) qdict_get_str(qdict, "device"),
+        .bps = qdict_get_int(qdict, "bps"),
+        .bps_rd = qdict_get_int(qdict, "bps_rd"),
+        .bps_wr = qdict_get_int(qdict, "bps_wr"),
+        .iops = qdict_get_int(qdict, "iops"),
+        .iops_rd = qdict_get_int(qdict, "iops_rd"),
+        .iops_wr = qdict_get_int(qdict, "iops_wr"),
+    };

-    qmp_block_set_io_throttle(qdict_get_str(qdict, "device"),
-                              qdict_get_int(qdict, "bps"),
-                              qdict_get_int(qdict, "bps_rd"),
-                              qdict_get_int(qdict, "bps_wr"),
-                              qdict_get_int(qdict, "iops"),
-                              qdict_get_int(qdict, "iops_rd"),
-                              qdict_get_int(qdict, "iops_wr"),
-                              false, /* no burst max via HMP */
-                              0,
-                              false,
-                              0,
-                              false,
-                              0,
-                              false,
-                              0,
-                              false,
-                              0,
-                              false,
-                              0,
-                              false, /* no burst length via HMP */
-                              0,
-                              false,
-                              0,
-                              false,
-                              0,
-                              false,
-                              0,
-                              false,
-                              0,
-                              false,
-                              0,
-                              false, /* No default I/O size */
-                              0,
-                              false,
-                              NULL, &err);
+    qmp_block_set_io_throttle(&throttle, &err);
     hmp_handle_error(mon, &err);
 }

-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 11/17] block: Simplify drive-mirror
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (9 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 10/17] block: Simplify block_set_io_throttle Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14 22:28   ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 12/17] qapi: Change Netdev into a flat union Eric Blake
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, berrange, Kevin Wolf, Max Reitz, Luiz Capitulino,
	open list:Block layer core

Now that we can support boxed commands, use it to greatly
reduce the number of parameters (and likelihood of getting
out of sync) when adjusting drive-mirror parameters.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>

---
v9: s/box/boxed/, trivial enough to keep R-b
v8: rebase, drop stale sentence in docs, don't rearrange initialiation
v7: new patch
---
 qapi/block-core.json | 20 +++++++++++---
 blockdev.c           | 76 +++++++++++++++++++++++-----------------------------
 hmp.c                | 25 ++++++++---------
 3 files changed, 60 insertions(+), 61 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index cbf4410..a085c85 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1108,6 +1108,21 @@
 #
 # Start mirroring a block device's writes to a new destination.
 #
+# See DriveMirror for parameter descriptions
+#
+# Returns: nothing on success
+#          If @device is not a valid block device, DeviceNotFound
+#
+# Since 1.3
+##
+{ 'command': 'drive-mirror', 'boxed': true,
+  'data': 'DriveMirror' }
+
+##
+# DriveMirror
+#
+# A set of parameters describing drive mirror setup.
+#
 # @device:  the name of the device whose writes should be mirrored.
 #
 # @target: the target of the new image. If the file exists, or if it
@@ -1154,12 +1169,9 @@
 #         written. Both will result in identical contents.
 #         Default is true. (Since 2.4)
 #
-# Returns: nothing on success
-#          If @device is not a valid block device, DeviceNotFound
-#
 # Since 1.3
 ##
-{ 'command': 'drive-mirror',
+{ 'struct': 'DriveMirror',
   'data': { 'device': 'str', 'target': 'str', '*format': 'str',
             '*node-name': 'str', '*replaces': 'str',
             'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
diff --git a/blockdev.c b/blockdev.c
index ddf30e1..f23bf99 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3458,19 +3458,7 @@ static void blockdev_mirror_common(BlockDriverState *bs,
                  block_job_cb, bs, errp);
 }

-void qmp_drive_mirror(const char *device, const char *target,
-                      bool has_format, const char *format,
-                      bool has_node_name, const char *node_name,
-                      bool has_replaces, const char *replaces,
-                      enum MirrorSyncMode sync,
-                      bool has_mode, enum NewImageMode mode,
-                      bool has_speed, int64_t speed,
-                      bool has_granularity, uint32_t granularity,
-                      bool has_buf_size, int64_t buf_size,
-                      bool has_on_source_error, BlockdevOnError on_source_error,
-                      bool has_on_target_error, BlockdevOnError on_target_error,
-                      bool has_unmap, bool unmap,
-                      Error **errp)
+void qmp_drive_mirror(DriveMirror *arg, Error **errp)
 {
     BlockDriverState *bs;
     BlockBackend *blk;
@@ -3481,11 +3469,12 @@ void qmp_drive_mirror(const char *device, const char *target,
     QDict *options = NULL;
     int flags;
     int64_t size;
+    const char *format = arg->format;

-    blk = blk_by_name(device);
+    blk = blk_by_name(arg->device);
     if (!blk) {
         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-                  "Device '%s' not found", device);
+                  "Device '%s' not found", arg->device);
         return;
     }

@@ -3493,24 +3482,25 @@ void qmp_drive_mirror(const char *device, const char *target,
     aio_context_acquire(aio_context);

     if (!blk_is_available(blk)) {
-        error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+        error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, arg->device);
         goto out;
     }
     bs = blk_bs(blk);
-    if (!has_mode) {
-        mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+    if (!arg->has_mode) {
+        arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
     }

-    if (!has_format) {
-        format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
+    if (!arg->has_format) {
+        format = (arg->mode == NEW_IMAGE_MODE_EXISTING
+                  ? NULL : bs->drv->format_name);
     }

     flags = bs->open_flags | BDRV_O_RDWR;
     source = backing_bs(bs);
-    if (!source && sync == MIRROR_SYNC_MODE_TOP) {
-        sync = MIRROR_SYNC_MODE_FULL;
+    if (!source && arg->sync == MIRROR_SYNC_MODE_TOP) {
+        arg->sync = MIRROR_SYNC_MODE_FULL;
     }
-    if (sync == MIRROR_SYNC_MODE_NONE) {
+    if (arg->sync == MIRROR_SYNC_MODE_NONE) {
         source = bs;
     }

@@ -3520,18 +3510,18 @@ void qmp_drive_mirror(const char *device, const char *target,
         goto out;
     }

-    if (has_replaces) {
+    if (arg->has_replaces) {
         BlockDriverState *to_replace_bs;
         AioContext *replace_aio_context;
         int64_t replace_size;

-        if (!has_node_name) {
+        if (!arg->has_node_name) {
             error_setg(errp, "a node-name must be provided when replacing a"
                              " named node of the graph");
             goto out;
         }

-        to_replace_bs = check_to_replace_node(bs, replaces, &local_err);
+        to_replace_bs = check_to_replace_node(bs, arg->replaces, &local_err);

         if (!to_replace_bs) {
             error_propagate(errp, local_err);
@@ -3550,26 +3540,26 @@ void qmp_drive_mirror(const char *device, const char *target,
         }
     }

-    if (mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
+    if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
         backing_mode = MIRROR_SOURCE_BACKING_CHAIN;
     } else {
         backing_mode = MIRROR_OPEN_BACKING_CHAIN;
     }

-    if ((sync == MIRROR_SYNC_MODE_FULL || !source)
-        && mode != NEW_IMAGE_MODE_EXISTING)
+    if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source)
+        && arg->mode != NEW_IMAGE_MODE_EXISTING)
     {
         /* create new image w/o backing file */
         assert(format);
-        bdrv_img_create(target, format,
+        bdrv_img_create(arg->target, format,
                         NULL, NULL, NULL, size, flags, &local_err, false);
     } else {
-        switch (mode) {
+        switch (arg->mode) {
         case NEW_IMAGE_MODE_EXISTING:
             break;
         case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
             /* create new image with backing file */
-            bdrv_img_create(target, format,
+            bdrv_img_create(arg->target, format,
                             source->filename,
                             source->drv->format_name,
                             NULL, size, flags, &local_err, false);
@@ -3585,8 +3575,8 @@ void qmp_drive_mirror(const char *device, const char *target,
     }

     options = qdict_new();
-    if (has_node_name) {
-        qdict_put(options, "node-name", qstring_from_str(node_name));
+    if (arg->has_node_name) {
+        qdict_put(options, "node-name", qstring_from_str(arg->node_name));
     }
     if (format) {
         qdict_put(options, "driver", qstring_from_str(format));
@@ -3595,8 +3585,8 @@ void qmp_drive_mirror(const char *device, const char *target,
     /* Mirroring takes care of copy-on-write using the source's backing
      * file.
      */
-    target_bs = bdrv_open(target, NULL, options, flags | BDRV_O_NO_BACKING,
-                          errp);
+    target_bs = bdrv_open(arg->target, NULL, options,
+                          flags | BDRV_O_NO_BACKING, errp);
     if (!target_bs) {
         goto out;
     }
@@ -3604,13 +3594,13 @@ void qmp_drive_mirror(const char *device, const char *target,
     bdrv_set_aio_context(target_bs, aio_context);

     blockdev_mirror_common(bs, target_bs,
-                           has_replaces, replaces, sync, backing_mode,
-                           has_speed, speed,
-                           has_granularity, granularity,
-                           has_buf_size, buf_size,
-                           has_on_source_error, on_source_error,
-                           has_on_target_error, on_target_error,
-                           has_unmap, unmap,
+                           arg->has_replaces, arg->replaces, arg->sync,
+                           backing_mode, arg->has_speed, arg->speed,
+                           arg->has_granularity, arg->granularity,
+                           arg->has_buf_size, arg->buf_size,
+                           arg->has_on_source_error, arg->on_source_error,
+                           arg->has_on_target_error, arg->on_target_error,
+                           arg->has_unmap, arg->unmap,
                            &local_err);
     bdrv_unref(target_bs);
     error_propagate(errp, local_err);
diff --git a/hmp.c b/hmp.c
index c7ca776..4819abc 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1077,31 +1077,28 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict)

 void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
 {
-    const char *device = qdict_get_str(qdict, "device");
     const char *filename = qdict_get_str(qdict, "target");
     const char *format = qdict_get_try_str(qdict, "format");
     bool reuse = qdict_get_try_bool(qdict, "reuse", false);
     bool full = qdict_get_try_bool(qdict, "full", false);
-    enum NewImageMode mode;
     Error *err = NULL;
+    DriveMirror mirror = {
+        .device = (char *)qdict_get_str(qdict, "device"),
+        .target = (char *)filename,
+        .has_format = !!format,
+        .format = (char *)format,
+        .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
+        .has_mode = true,
+        .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS,
+        .unmap = true,
+    };

     if (!filename) {
         error_setg(&err, QERR_MISSING_PARAMETER, "target");
         hmp_handle_error(mon, &err);
         return;
     }
-
-    if (reuse) {
-        mode = NEW_IMAGE_MODE_EXISTING;
-    } else {
-        mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
-    }
-
-    qmp_drive_mirror(device, filename, !!format, format,
-                     false, NULL, false, NULL,
-                     full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
-                     true, mode, false, 0, false, 0, false, 0,
-                     false, 0, false, 0, false, true, &err);
+    qmp_drive_mirror(&mirror, &err);
     hmp_handle_error(mon, &err);
 }

-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 12/17] qapi: Change Netdev into a flat union
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (10 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 11/17] block: Simplify drive-mirror Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 13/17] net: Use correct type for bool flag Eric Blake
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, berrange, Jan Kiszka, Peter Maydell, Beniamino Galvani,
	Jason Wang, Alistair Francis, Peter Crosthwaite, Dmitry Fleytman,
	Edgar E. Iglesias, Alexander Graf, Scott Wood, Peter Chubb,
	Michael Walle, Max Filippov, Scott Feldman, Jiri Pirko,
	David Gibson, Michael S. Tsirkin, Stefano Stabellini,
	Anthony Perard, Rob Herring, Gerd Hoffmann, Luiz Capitulino,
	Luigi Rizzo, Giuseppe Lettieri, Vincenzo Maffione,
	Samuel Thibault, open list:Musicpal, open list:ppce500,
	open list:X86

This is a mostly-mechanical conversion that creates a new flat
union 'Netdev' QAPI type that covers all the branches of the
former 'NetClientOptions' simple union, where the branches are
now listed in a new 'NetClientDriver' enum rather than generated
from the simple union.  The existence of a flat union has no
change to the command line syntax accepted for new code, and
will make it possible for a future patch to switch the QMP
command to parse a boxed union for no change to valid QMP; but
it does have some ripple effect on the C code when dealing with
the new types.

While making the conversion, note that the 'NetLegacy' type
remains unchanged: it applies only to legacy command line options,
and will not be ported to QMP, so it should remain a wrapper
around a simple union; to avoid confusion, the type named
'NetClientOptions' is now gone, and we introduce 'NetLegacyOptions'
in its place.  Then, in the C code, we convert from NetLegacy to
Netdev as soon as possible, so that the bulk of the net stack
only has to deal with one QAPI type, not two.  Note that since
the old legacy code always rejected 'hubport', we can just omit
that branch from the new 'NetLegacyOptions' simple union.

Based on an idea originally by Zoltán Kővágó <DirtY.iCE.hu@gmail.com>:
Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com>
although the sed script in that patch no longer applies due to
other changes in the tree since then, and I also did some manual
cleanups (such as fixing whitespace to keep checkpatch happy).

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v8: rewrite commit message, claim authorship, rebase to latest
v7: rebase to latest master
v6: rebase to latest master
---
 qapi-schema.json                 |  49 ++++++++++-----
 include/net/net.h                |   4 +-
 hw/arm/musicpal.c                |   2 +-
 hw/core/qdev-properties-system.c |   2 +-
 hw/net/allwinner_emac.c          |   2 +-
 hw/net/cadence_gem.c             |   2 +-
 hw/net/dp8393x.c                 |   2 +-
 hw/net/e1000.c                   |   2 +-
 hw/net/e1000e.c                  |   2 +-
 hw/net/eepro100.c                |   2 +-
 hw/net/etraxfs_eth.c             |   2 +-
 hw/net/fsl_etsec/etsec.c         |   2 +-
 hw/net/imx_fec.c                 |   2 +-
 hw/net/lan9118.c                 |   2 +-
 hw/net/lance.c                   |   2 +-
 hw/net/mcf_fec.c                 |   2 +-
 hw/net/milkymist-minimac2.c      |   2 +-
 hw/net/mipsnet.c                 |   2 +-
 hw/net/ne2000-isa.c              |   2 +-
 hw/net/ne2000.c                  |   2 +-
 hw/net/opencores_eth.c           |   2 +-
 hw/net/pcnet-pci.c               |   2 +-
 hw/net/rocker/rocker_fp.c        |   2 +-
 hw/net/rtl8139.c                 |   2 +-
 hw/net/smc91c111.c               |   2 +-
 hw/net/spapr_llan.c              |   2 +-
 hw/net/stellaris_enet.c          |   2 +-
 hw/net/vhost_net.c               |  20 +++---
 hw/net/virtio-net.c              |  10 +--
 hw/net/vmxnet3.c                 |   2 +-
 hw/net/xen_nic.c                 |   2 +-
 hw/net/xgmac.c                   |   2 +-
 hw/net/xilinx_axienet.c          |   2 +-
 hw/net/xilinx_ethlite.c          |   2 +-
 hw/usb/dev-network.c             |   2 +-
 monitor.c                        |  14 ++---
 net/dump.c                       |   6 +-
 net/filter.c                     |   2 +-
 net/hub.c                        |  22 +++----
 net/l2tpv3.c                     |   6 +-
 net/net.c                        | 133 +++++++++++++++++++++++++--------------
 net/netmap.c                     |   4 +-
 net/slirp.c                      |   6 +-
 net/socket.c                     |   8 +--
 net/tap-win32.c                  |   6 +-
 net/tap.c                        |  24 +++----
 net/vde.c                        |   6 +-
 net/vhost-user.c                 |  20 +++---
 48 files changed, 230 insertions(+), 172 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index d2d6506..5658723 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2809,16 +2809,32 @@
     '*queues':        'int' } }

 ##
-# @NetClientOptions
+# @NetClientDriver
 #
-# A discriminated record of network device traits.
+# Available netdev drivers.
+#
+# Since 2.7
+##
+{ 'enum': 'NetClientDriver',
+  'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', 'dump',
+            'bridge', 'hubport', 'netmap', 'vhost-user' ] }
+
+##
+# @Netdev
+#
+# Captures the configuration of a network device.
+#
+# @id: identifier for monitor commands.
+#
+# @type: Specify the driver used for interpreting remaining arguments.
 #
 # Since 1.2
 #
 # 'l2tpv3' - since 2.1
-#
 ##
-{ 'union': 'NetClientOptions',
+{ 'union': 'Netdev',
+  'base': { 'id': 'str', 'type': 'NetClientDriver' },
+  'discriminator': 'type',
   'data': {
     'none':     'NetdevNoneOptions',
     'nic':      'NetLegacyNicOptions',
@@ -2853,23 +2869,28 @@
     '*vlan': 'int32',
     '*id':   'str',
     '*name': 'str',
-    'opts':  'NetClientOptions' } }
+    'opts':  'NetLegacyOptions' } }

 ##
-# @Netdev
+# @NetLegacyOptions
 #
-# Captures the configuration of a network device.
-#
-# @id: identifier for monitor commands.
-#
-# @opts: device type specific properties
+# Like Netdev, but for use only by the legacy command line options
 #
 # Since 1.2
 ##
-{ 'struct': 'Netdev',
+{ 'union': 'NetLegacyOptions',
   'data': {
-    'id':   'str',
-    'opts': 'NetClientOptions' } }
+    'none':     'NetdevNoneOptions',
+    'nic':      'NetLegacyNicOptions',
+    'user':     'NetdevUserOptions',
+    'tap':      'NetdevTapOptions',
+    'l2tpv3':   'NetdevL2TPv3Options',
+    'socket':   'NetdevSocketOptions',
+    'vde':      'NetdevVdeOptions',
+    'dump':     'NetdevDumpOptions',
+    'bridge':   'NetdevBridgeOptions',
+    'netmap':   'NetdevNetmapOptions',
+    'vhost-user': 'NetdevVhostUserOptions' } }

 ##
 # @NetFilterDirection
diff --git a/include/net/net.h b/include/net/net.h
index a5c5095..48382c3 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -66,7 +66,7 @@ typedef struct SocketReadState SocketReadState;
 typedef void (SocketReadStateFinalize)(SocketReadState *rs);

 typedef struct NetClientInfo {
-    NetClientOptionsKind type;
+    NetClientDriver type;
     size_t size;
     NetReceive *receive;
     NetReceive *receive_raw;
@@ -122,7 +122,7 @@ int net_fill_rstate(SocketReadState *rs, const uint8_t *buf, int size);
 char *qemu_mac_strdup_printf(const uint8_t *macaddr);
 NetClientState *qemu_find_netdev(const char *id);
 int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
-                                 NetClientOptionsKind type, int max);
+                                 NetClientDriver type, int max);
 NetClientState *qemu_new_net_client(NetClientInfo *info,
                                     NetClientState *peer,
                                     const char *model,
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 7a4cc07..cc50ace 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -378,7 +378,7 @@ static void eth_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_mv88w8618_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = eth_receive,
     .cleanup = eth_cleanup,
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 65d9fa9..fa54d0b 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -231,7 +231,7 @@ static void set_netdev(Object *obj, Visitor *v, const char *name,
     }

     queues = qemu_find_net_clients_except(str, peers,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          NET_CLIENT_DRIVER_NIC,
                                           MAX_QUEUE_NUM);
     if (queues == 0) {
         err = -ENOENT;
diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
index d575023..50e8361 100644
--- a/hw/net/allwinner_emac.c
+++ b/hw/net/allwinner_emac.c
@@ -424,7 +424,7 @@ static const MemoryRegionOps aw_emac_mem_ops = {
 };

 static NetClientInfo net_aw_emac_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = aw_emac_can_receive,
     .receive = aw_emac_receive,
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 8a4be1e..db1b301 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1207,7 +1207,7 @@ static void gem_set_link(NetClientState *nc)
 }

 static NetClientInfo net_gem_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = gem_can_receive,
     .receive = gem_receive,
diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c
index 0fa652c..17f0338 100644
--- a/hw/net/dp8393x.c
+++ b/hw/net/dp8393x.c
@@ -812,7 +812,7 @@ static void dp8393x_reset(DeviceState *dev)
 }

 static NetClientInfo net_dp83932_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = dp8393x_can_receive,
     .receive = dp8393x_receive,
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 06ca7b2..9324949 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -1563,7 +1563,7 @@ pci_e1000_uninit(PCIDevice *dev)
 }

 static NetClientInfo net_e1000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = e1000_can_receive,
     .receive = e1000_receive,
diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
index b4758bc..d001c96 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -225,7 +225,7 @@ e1000e_set_link_status(NetClientState *nc)
 }

 static NetClientInfo net_e1000e_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = e1000e_nc_can_receive,
     .receive = e1000e_nc_receive,
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index b10c419..bab4dbf 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -1848,7 +1848,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev)
 }

 static NetClientInfo net_eepro100_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = nic_receive,
 };
diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c
index 05495ec..efaa49f 100644
--- a/hw/net/etraxfs_eth.c
+++ b/hw/net/etraxfs_eth.c
@@ -578,7 +578,7 @@ static const MemoryRegionOps eth_ops = {
 };

 static NetClientInfo net_etraxfs_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = eth_receive,
     .link_status_changed = eth_set_link,
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index 98250e0..b5c777f 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -371,7 +371,7 @@ static void etsec_set_link_status(NetClientState *nc)
 }

 static NetClientInfo net_etsec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = etsec_receive,
     .link_status_changed = etsec_set_link_status,
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
index d91e029..1c415ab 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -1147,7 +1147,7 @@ static void imx_eth_cleanup(NetClientState *nc)
 }

 static NetClientInfo imx_eth_net_info = {
-    .type                = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type                = NET_CLIENT_DRIVER_NIC,
     .size                = sizeof(NICState),
     .can_receive         = imx_eth_can_receive,
     .receive             = imx_eth_receive,
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index 2052073..4615d87 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -1313,7 +1313,7 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = {
 };

 static NetClientInfo net_lan9118_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = lan9118_receive,
     .link_status_changed = lan9118_set_link,
diff --git a/hw/net/lance.c b/hw/net/lance.c
index 6253d21..573d724 100644
--- a/hw/net/lance.c
+++ b/hw/net/lance.c
@@ -93,7 +93,7 @@ static const MemoryRegionOps lance_mem_ops = {
 };

 static NetClientInfo net_lance_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = pcnet_receive,
     .link_status_changed = pcnet_set_link_status,
diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c
index 7c0398e..0ee8ad9 100644
--- a/hw/net/mcf_fec.c
+++ b/hw/net/mcf_fec.c
@@ -507,7 +507,7 @@ static const MemoryRegionOps mcf_fec_ops = {
 };

 static NetClientInfo net_mcf_fec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = mcf_fec_receive,
 };
diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c
index 1e147c3..c3a12e1 100644
--- a/hw/net/milkymist-minimac2.c
+++ b/hw/net/milkymist-minimac2.c
@@ -447,7 +447,7 @@ static void milkymist_minimac2_reset(DeviceState *d)
 }

 static NetClientInfo net_milkymist_minimac2_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = minimac2_rx,
 };
diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c
index 5115adc..5a63df7 100644
--- a/hw/net/mipsnet.c
+++ b/hw/net/mipsnet.c
@@ -224,7 +224,7 @@ static const VMStateDescription vmstate_mipsnet = {
 };

 static NetClientInfo net_mipsnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = mipsnet_receive,
 };
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
index 8fab7ae..f345533 100644
--- a/hw/net/ne2000-isa.c
+++ b/hw/net/ne2000-isa.c
@@ -44,7 +44,7 @@ typedef struct ISANE2000State {
 } ISANE2000State;

 static NetClientInfo net_ne2000_isa_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = ne2000_receive,
 };
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
index f0feaf9..798d681 100644
--- a/hw/net/ne2000.c
+++ b/hw/net/ne2000.c
@@ -712,7 +712,7 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size)
 }

 static NetClientInfo net_ne2000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = ne2000_receive,
 };
diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c
index 484d113..268d6a7 100644
--- a/hw/net/opencores_eth.c
+++ b/hw/net/opencores_eth.c
@@ -473,7 +473,7 @@ static ssize_t open_eth_receive(NetClientState *nc,
 }

 static NetClientInfo net_open_eth_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = open_eth_can_receive,
     .receive = open_eth_receive,
diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c
index 595439a..0acf8a4 100644
--- a/hw/net/pcnet-pci.c
+++ b/hw/net/pcnet-pci.c
@@ -272,7 +272,7 @@ static void pci_pcnet_uninit(PCIDevice *dev)
 }

 static NetClientInfo net_pci_pcnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = pcnet_receive,
     .link_status_changed = pcnet_set_link_status,
diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c
index 0149899..1305ac3 100644
--- a/hw/net/rocker/rocker_fp.c
+++ b/hw/net/rocker/rocker_fp.c
@@ -167,7 +167,7 @@ static void fp_port_set_link_status(NetClientState *nc)
 }

 static NetClientInfo fp_port_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = fp_port_receive,
     .receive_iov = fp_port_receive_iov,
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index 07297cb..3345bc6 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -3393,7 +3393,7 @@ static void rtl8139_set_link_status(NetClientState *nc)
 }

 static NetClientInfo net_rtl8139_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = rtl8139_can_receive,
     .receive = rtl8139_receive,
diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c
index 21c1b8f..3b16dcf 100644
--- a/hw/net/smc91c111.c
+++ b/hw/net/smc91c111.c
@@ -755,7 +755,7 @@ static const MemoryRegionOps smc91c111_mem_ops = {
 };

 static NetClientInfo net_smc91c111_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = smc91c111_can_receive_nc,
     .receive = smc91c111_receive,
diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c
index 8b2eebd..b273eda 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -278,7 +278,7 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
 }

 static NetClientInfo net_spapr_vlan_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = spapr_vlan_can_receive,
     .receive = spapr_vlan_receive,
diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
index 6880894..957730e 100644
--- a/hw/net/stellaris_enet.c
+++ b/hw/net/stellaris_enet.c
@@ -460,7 +460,7 @@ static void stellaris_enet_reset(stellaris_enet_state *s)
 }

 static NetClientInfo net_stellaris_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = stellaris_enet_receive,
 };
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index 11fabc0..f92d3f8 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -88,10 +88,10 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net)
     const int *feature_bits = 0;

     switch (net->nc->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         feature_bits = kernel_feature_bits;
         break;
-    case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+    case NET_CLIENT_DRIVER_VHOST_USER:
         feature_bits = user_feature_bits;
         break;
     default:
@@ -128,7 +128,7 @@ uint64_t vhost_net_get_acked_features(VHostNetState *net)
 static int vhost_net_get_fd(NetClientState *backend)
 {
     switch (backend->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         return tap_get_fd(backend);
     default:
         fprintf(stderr, "vhost-net requires tap backend\n");
@@ -191,7 +191,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
     }

     /* Set sane init value. Override when guest acks. */
-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
         features = vhost_user_get_acked_features(net->nc);
         if (~net->dev.features & features) {
             fprintf(stderr, "vhost lacks feature mask %" PRIu64
@@ -238,7 +238,7 @@ static int vhost_net_start_one(struct vhost_net *net,
         net->nc->info->poll(net->nc, false);
     }

-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         qemu_set_fd_handler(net->backend, NULL, NULL, NULL);
         file.fd = net->backend;
         for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
@@ -253,7 +253,7 @@ static int vhost_net_start_one(struct vhost_net *net,
     return 0;
 fail:
     file.fd = -1;
-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         while (file.index-- > 0) {
             const VhostOps *vhost_ops = net->dev.vhost_ops;
             int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
@@ -275,7 +275,7 @@ static void vhost_net_stop_one(struct vhost_net *net,
 {
     struct vhost_vring_file file = { .fd = -1 };

-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
             const VhostOps *vhost_ops = net->dev.vhost_ops;
             int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
@@ -312,7 +312,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
          * because vhost user doesn't interrupt masking/unmasking
          * properly.
          */
-        if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+        if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
                 dev->use_guest_notifier_mask = false;
         }
      }
@@ -413,10 +413,10 @@ VHostNetState *get_vhost_net(NetClientState *nc)
     }

     switch (nc->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         vhost_net = tap_get_vhost_net(nc);
         break;
-    case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+    case NET_CLIENT_DRIVER_VHOST_USER:
         vhost_net = vhost_user_get_vhost_net(nc);
         break;
     default:
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 9999899..56d8506 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -468,11 +468,11 @@ static int peer_attach(VirtIONet *n, int index)
         return 0;
     }

-    if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+    if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
         vhost_set_vring_enable(nc->peer, 1);
     }

-    if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
         return 0;
     }

@@ -487,11 +487,11 @@ static int peer_detach(VirtIONet *n, int index)
         return 0;
     }

-    if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+    if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
         vhost_set_vring_enable(nc->peer, 0);
     }

-    if (nc->peer->info->type !=  NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
         return 0;
     }

@@ -1680,7 +1680,7 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
 }

 static NetClientInfo net_virtio_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = virtio_net_can_receive,
     .receive = virtio_net_receive,
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index e767fc6..bbf44ad 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2087,7 +2087,7 @@ static void vmxnet3_set_link_status(NetClientState *nc)
 }

 static NetClientInfo net_vmxnet3_info = {
-        .type = NET_CLIENT_OPTIONS_KIND_NIC,
+        .type = NET_CLIENT_DRIVER_NIC,
         .size = sizeof(NICState),
         .receive = vmxnet3_receive,
         .link_status_changed = vmxnet3_set_link_status,
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index 0b4ddae..6856b52 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -269,7 +269,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
 /* ------------------------------------------------------------- */

 static NetClientInfo net_xen_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = net_rx_packet,
 };
diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c
index 0c5f793..46b1aa1 100644
--- a/hw/net/xgmac.c
+++ b/hw/net/xgmac.c
@@ -371,7 +371,7 @@ out:
 }

 static NetClientInfo net_xgmac_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = eth_rx,
 };
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index de23ab5..b670184 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -935,7 +935,7 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size)
 }

 static NetClientInfo net_xilinx_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = eth_rx,
 };
diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
index bc846e7..54db2b8 100644
--- a/hw/net/xilinx_ethlite.c
+++ b/hw/net/xilinx_ethlite.c
@@ -217,7 +217,7 @@ static void xilinx_ethlite_reset(DeviceState *dev)
 }

 static NetClientInfo net_xilinx_ethlite_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = eth_can_rx,
     .receive = eth_rx,
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 5c18198..3ced4ba 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1334,7 +1334,7 @@ static void usb_net_handle_destroy(USBDevice *dev)
 }

 static NetClientInfo net_usbnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = usbnet_receive,
     .cleanup = usbnet_cleanup,
diff --git a/monitor.c b/monitor.c
index d0ff246..0e055f8 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3055,8 +3055,8 @@ void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
     }
     len = strlen(str);
     readline_set_completion_index(rs, len);
-    for (i = 0; NetClientOptionsKind_lookup[i]; i++) {
-        add_completion_option(rs, str, NetClientOptionsKind_lookup[i]);
+    for (i = 0; NetClientDriver_lookup[i]; i++) {
+        add_completion_option(rs, str, NetClientDriver_lookup[i]);
     }
 }

@@ -3256,7 +3256,7 @@ void set_link_completion(ReadLineState *rs, int nb_args, const char *str)
         NetClientState *ncs[MAX_QUEUE_NUM];
         int count, i;
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NONE,
+                                             NET_CLIENT_DRIVER_NONE,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             const char *name = ncs[i]->name;
@@ -3281,7 +3281,7 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)

     len = strlen(str);
     readline_set_completion_index(rs, len);
-    count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC,
+    count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC,
                                          MAX_QUEUE_NUM);
     for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
         QemuOpts *opts;
@@ -3393,7 +3393,7 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str)
     readline_set_completion_index(rs, len);
     if (nb_args == 2) {
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NONE,
+                                             NET_CLIENT_DRIVER_NONE,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             int id;
@@ -3410,13 +3410,13 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str)
         return;
     } else if (nb_args == 3) {
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NIC,
+                                             NET_CLIENT_DRIVER_NIC,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             int id;
             const char *name;

-            if (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT ||
+            if (ncs[i]->info->type == NET_CLIENT_DRIVER_HUBPORT ||
                 net_hub_id_for_client(ncs[i], &id)) {
                 continue;
             }
diff --git a/net/dump.c b/net/dump.c
index f8a500f..89a149b 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -172,7 +172,7 @@ static void dumpclient_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_dump_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_DUMP,
+    .type = NET_CLIENT_DRIVER_DUMP,
     .size = sizeof(DumpNetClient),
     .receive = dumpclient_receive,
     .receive_iov = dumpclient_receive_iov,
@@ -189,8 +189,8 @@ int net_init_dump(const Netdev *netdev, const char *name,
     NetClientState *nc;
     DumpNetClient *dnc;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_DUMP);
-    dump = netdev->opts->u.dump.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_DUMP);
+    dump = &netdev->u.dump;

     assert(peer);

diff --git a/net/filter.c b/net/filter.c
index 8ac79f3..888fe6d 100644
--- a/net/filter.c
+++ b/net/filter.c
@@ -201,7 +201,7 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
     }

     queues = qemu_find_net_clients_except(nf->netdev_id, ncs,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          NET_CLIENT_DRIVER_NIC,
                                           MAX_QUEUE_NUM);
     if (queues < 1) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev",
diff --git a/net/hub.c b/net/hub.c
index ec4626f..32d8cf5 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -131,7 +131,7 @@ static void net_hub_port_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_hub_port_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
+    .type = NET_CLIENT_DRIVER_HUBPORT,
     .size = sizeof(NetHubPort),
     .can_receive = net_hub_port_can_receive,
     .receive = net_hub_port_receive,
@@ -266,10 +266,10 @@ int net_hub_id_for_client(NetClientState *nc, int *id)
 {
     NetHubPort *port;

-    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+    if (nc->info->type == NET_CLIENT_DRIVER_HUBPORT) {
         port = DO_UPCAST(NetHubPort, nc, nc);
     } else if (nc->peer != NULL && nc->peer->info->type ==
-            NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+            NET_CLIENT_DRIVER_HUBPORT) {
         port = DO_UPCAST(NetHubPort, nc, nc->peer);
     } else {
         return -ENOENT;
@@ -286,9 +286,9 @@ int net_init_hubport(const Netdev *netdev, const char *name,
 {
     const NetdevHubPortOptions *hubport;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT);
+    assert(netdev->type == NET_CLIENT_DRIVER_HUBPORT);
     assert(!peer);
-    hubport = netdev->opts->u.hubport.data;
+    hubport = &netdev->u.hubport;

     net_hub_add_port(hubport->hubid, name);
     return 0;
@@ -315,14 +315,14 @@ void net_hub_check_clients(void)
             }

             switch (peer->info->type) {
-            case NET_CLIENT_OPTIONS_KIND_NIC:
+            case NET_CLIENT_DRIVER_NIC:
                 has_nic = 1;
                 break;
-            case NET_CLIENT_OPTIONS_KIND_USER:
-            case NET_CLIENT_OPTIONS_KIND_TAP:
-            case NET_CLIENT_OPTIONS_KIND_SOCKET:
-            case NET_CLIENT_OPTIONS_KIND_VDE:
-            case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+            case NET_CLIENT_DRIVER_USER:
+            case NET_CLIENT_DRIVER_TAP:
+            case NET_CLIENT_DRIVER_SOCKET:
+            case NET_CLIENT_DRIVER_VDE:
+            case NET_CLIENT_DRIVER_VHOST_USER:
                 has_host_dev = 1;
                 break;
             default:
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index df02f5b..6745b78 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -516,7 +516,7 @@ static void net_l2tpv3_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_l2tpv3_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_L2TPV3,
+    .type = NET_CLIENT_DRIVER_L2TPV3,
     .size = sizeof(NetL2TPV3State),
     .receive = net_l2tpv3_receive_dgram,
     .receive_iov = net_l2tpv3_receive_dgram_iov,
@@ -545,8 +545,8 @@ int net_init_l2tpv3(const Netdev *netdev,
     s->queue_tail = 0;
     s->header_mismatch = false;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_L2TPV3);
-    l2tpv3 = netdev->opts->u.l2tpv3.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_L2TPV3);
+    l2tpv3 = &netdev->u.l2tpv3;

     if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
         s->ipv6 = l2tpv3->ipv6;
diff --git a/net/net.c b/net/net.c
index d80fd82..4a7f392 100644
--- a/net/net.c
+++ b/net/net.c
@@ -289,7 +289,7 @@ NICState *qemu_new_nic(NetClientInfo *info,
     NICState *nic;
     int i, queues = MAX(1, conf->peers.queues);

-    assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
+    assert(info->type == NET_CLIENT_DRIVER_NIC);
     assert(info->size >= sizeof(NICState));

     nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
@@ -360,13 +360,13 @@ void qemu_del_net_client(NetClientState *nc)
     int queues, i;
     NetFilterState *nf, *next;

-    assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
+    assert(nc->info->type != NET_CLIENT_DRIVER_NIC);

     /* If the NetClientState belongs to a multiqueue backend, we will change all
      * other NetClientStates also.
      */
     queues = qemu_find_net_clients_except(nc->name, ncs,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          NET_CLIENT_DRIVER_NIC,
                                           MAX_QUEUE_NUM);
     assert(queues != 0);

@@ -375,7 +375,7 @@ void qemu_del_net_client(NetClientState *nc)
     }

     /* If there is a peer NIC, delete and cleanup client, but do not free. */
-    if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+    if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
         NICState *nic = qemu_get_nic(nc->peer);
         if (nic->peer_deleted) {
             return;
@@ -431,7 +431,7 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
     NetClientState *nc;

     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
             if (nc->queue_index == 0) {
                 func(qemu_get_nic(nc), opaque);
             }
@@ -603,7 +603,7 @@ void qemu_flush_or_purge_queued_packets(NetClientState *nc, bool purge)
 {
     nc->receive_disabled = 0;

-    if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+    if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_HUBPORT) {
         if (net_hub_flush(nc->peer)) {
             qemu_notify_event();
         }
@@ -777,7 +777,7 @@ NetClientState *qemu_find_netdev(const char *id)
     NetClientState *nc;

     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC)
             continue;
         if (!strcmp(nc->name, id)) {
             return nc;
@@ -788,7 +788,7 @@ NetClientState *qemu_find_netdev(const char *id)
 }

 int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
-                                 NetClientOptionsKind type, int max)
+                                 NetClientDriver type, int max)
 {
     NetClientState *nc;
     int ret = 0;
@@ -869,8 +869,8 @@ static int net_init_nic(const Netdev *netdev, const char *name,
     NICInfo *nd;
     const NetLegacyNicOptions *nic;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_NIC);
-    nic = netdev->opts->u.nic.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_NIC);
+    nic = &netdev->u.nic;

     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -930,39 +930,38 @@ static int net_init_nic(const Netdev *netdev, const char *name,
 }


-static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND__MAX])(
+static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])(
     const Netdev *netdev,
     const char *name,
     NetClientState *peer, Error **errp) = {
-        [NET_CLIENT_OPTIONS_KIND_NIC]       = net_init_nic,
+        [NET_CLIENT_DRIVER_NIC]       = net_init_nic,
 #ifdef CONFIG_SLIRP
-        [NET_CLIENT_OPTIONS_KIND_USER]      = net_init_slirp,
+        [NET_CLIENT_DRIVER_USER]      = net_init_slirp,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_TAP]       = net_init_tap,
-        [NET_CLIENT_OPTIONS_KIND_SOCKET]    = net_init_socket,
+        [NET_CLIENT_DRIVER_TAP]       = net_init_tap,
+        [NET_CLIENT_DRIVER_SOCKET]    = net_init_socket,
 #ifdef CONFIG_VDE
-        [NET_CLIENT_OPTIONS_KIND_VDE]       = net_init_vde,
+        [NET_CLIENT_DRIVER_VDE]       = net_init_vde,
 #endif
 #ifdef CONFIG_NETMAP
-        [NET_CLIENT_OPTIONS_KIND_NETMAP]    = net_init_netmap,
+        [NET_CLIENT_DRIVER_NETMAP]    = net_init_netmap,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_DUMP]      = net_init_dump,
+        [NET_CLIENT_DRIVER_DUMP]      = net_init_dump,
 #ifdef CONFIG_NET_BRIDGE
-        [NET_CLIENT_OPTIONS_KIND_BRIDGE]    = net_init_bridge,
+        [NET_CLIENT_DRIVER_BRIDGE]    = net_init_bridge,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_HUBPORT]   = net_init_hubport,
+        [NET_CLIENT_DRIVER_HUBPORT]   = net_init_hubport,
 #ifdef CONFIG_VHOST_NET_USED
-        [NET_CLIENT_OPTIONS_KIND_VHOST_USER] = net_init_vhost_user,
+        [NET_CLIENT_DRIVER_VHOST_USER] = net_init_vhost_user,
 #endif
 #ifdef CONFIG_L2TPV3
-        [NET_CLIENT_OPTIONS_KIND_L2TPV3]    = net_init_l2tpv3,
+        [NET_CLIENT_DRIVER_L2TPV3]    = net_init_l2tpv3,
 #endif
 };


 static int net_client_init1(const void *object, int is_netdev, Error **errp)
 {
-    const NetClientOptions *opts;
     Netdev legacy = {0};
     const Netdev *netdev;
     const char *name;
@@ -970,34 +969,72 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)

     if (is_netdev) {
         netdev = object;
-        opts = netdev->opts;
         name = netdev->id;

-        if (opts->type == NET_CLIENT_OPTIONS_KIND_DUMP ||
-            opts->type == NET_CLIENT_OPTIONS_KIND_NIC ||
-            !net_client_init_fun[opts->type]) {
+        if (netdev->type == NET_CLIENT_DRIVER_DUMP ||
+            netdev->type == NET_CLIENT_DRIVER_NIC ||
+            !net_client_init_fun[netdev->type]) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a netdev backend type");
             return -1;
         }
     } else {
         const NetLegacy *net = object;
+        const NetLegacyOptions *opts = net->opts;
         legacy.id = net->id;
-        opts = legacy.opts = net->opts;
         netdev = &legacy;
         /* missing optional values have been initialized to "all bits zero" */
         name = net->has_id ? net->id : net->name;

-        if (opts->type == NET_CLIENT_OPTIONS_KIND_NONE) {
+        /* Map the old options to the new flat type */
+        switch (opts->type) {
+        case NET_LEGACY_OPTIONS_KIND_NONE:
             return 0; /* nothing to do */
-        }
-        if (opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
-                       "a net type");
-            return -1;
+        case NET_LEGACY_OPTIONS_KIND_NIC:
+            legacy.type = NET_CLIENT_DRIVER_NIC;
+            legacy.u.nic = *opts->u.nic.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_USER:
+            legacy.type = NET_CLIENT_DRIVER_USER;
+            legacy.u.user = *opts->u.user.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_TAP:
+            legacy.type = NET_CLIENT_DRIVER_TAP;
+            legacy.u.tap = *opts->u.tap.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_L2TPV3:
+            legacy.type = NET_CLIENT_DRIVER_L2TPV3;
+            legacy.u.l2tpv3 = *opts->u.l2tpv3.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_SOCKET:
+            legacy.type = NET_CLIENT_DRIVER_SOCKET;
+            legacy.u.socket = *opts->u.socket.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_VDE:
+            legacy.type = NET_CLIENT_DRIVER_VDE;
+            legacy.u.vde = *opts->u.vde.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_DUMP:
+            legacy.type = NET_CLIENT_DRIVER_DUMP;
+            legacy.u.dump = *opts->u.dump.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_BRIDGE:
+            legacy.type = NET_CLIENT_DRIVER_BRIDGE;
+            legacy.u.bridge = *opts->u.bridge.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_NETMAP:
+            legacy.type = NET_CLIENT_DRIVER_NETMAP;
+            legacy.u.netmap = *opts->u.netmap.data;
+            break;
+        case NET_LEGACY_OPTIONS_KIND_VHOST_USER:
+            legacy.type = NET_CLIENT_DRIVER_VHOST_USER;
+            legacy.u.vhost_user = *opts->u.vhost_user.data;
+            break;
+        default:
+            abort();
         }

-        if (!net_client_init_fun[opts->type]) {
+        if (!net_client_init_fun[netdev->type]) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a net backend type (maybe it is not compiled "
                        "into this binary)");
@@ -1005,17 +1042,17 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         }

         /* Do not add to a vlan if it's a nic with a netdev= parameter. */
-        if (opts->type != NET_CLIENT_OPTIONS_KIND_NIC ||
+        if (netdev->type != NET_CLIENT_DRIVER_NIC ||
             !opts->u.nic.data->has_netdev) {
             peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL);
         }
     }

-    if (net_client_init_fun[opts->type](netdev, name, peer, errp) < 0) {
+    if (net_client_init_fun[netdev->type](netdev, name, peer, errp) < 0) {
         /* FIXME drop when all init functions store an Error */
         if (errp && !*errp) {
             error_setg(errp, QERR_DEVICE_INIT_FAILED,
-                       NetClientOptionsKind_lookup[opts->type]);
+                       NetClientDriver_lookup[netdev->type]);
         }
         return -1;
     }
@@ -1135,7 +1172,7 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict)
                      device, vlan_id);
         return;
     }
-    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+    if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
         error_report("invalid host network device '%s'", device);
         return;
     }
@@ -1226,7 +1263,7 @@ void print_net_client(Monitor *mon, NetClientState *nc)

     monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
                    nc->queue_index,
-                   NetClientOptionsKind_lookup[nc->info->type],
+                   NetClientDriver_lookup[nc->info->type],
                    nc->info_str);
     if (!QTAILQ_EMPTY(&nc->filters)) {
         monitor_printf(mon, "filters:\n");
@@ -1256,7 +1293,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
         }

         /* only query rx-filter information of NIC */
-        if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type != NET_CLIENT_DRIVER_NIC) {
             if (has_name) {
                 error_setg(errp, "net client(%s) isn't a NIC", name);
                 return NULL;
@@ -1302,7 +1339,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
 void hmp_info_network(Monitor *mon, const QDict *qdict)
 {
     NetClientState *nc, *peer;
-    NetClientOptionsKind type;
+    NetClientDriver type;

     net_hub_info(mon);

@@ -1315,10 +1352,10 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
             continue;
         }

-        if (!peer || type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (!peer || type == NET_CLIENT_DRIVER_NIC) {
             print_net_client(mon, nc);
         } /* else it's a netdev connected to a NIC, printed with the NIC */
-        if (peer && type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (peer && type == NET_CLIENT_DRIVER_NIC) {
             monitor_printf(mon, " \\ ");
             print_net_client(mon, peer);
         }
@@ -1332,7 +1369,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
     int queues, i;

     queues = qemu_find_net_clients_except(name, ncs,
-                                          NET_CLIENT_OPTIONS_KIND__MAX,
+                                          NET_CLIENT_DRIVER__MAX,
                                           MAX_QUEUE_NUM);

     if (queues == 0) {
@@ -1359,7 +1396,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
          * multiple clients that can still communicate with each other in
          * disconnected mode. For now maintain this compatibility.
          */
-        if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
             for (i = 0; i < queues; i++) {
                 ncs[i]->peer->link_down = !up;
             }
@@ -1400,7 +1437,7 @@ void net_cleanup(void)
      */
     while (!QTAILQ_EMPTY(&net_clients)) {
         nc = QTAILQ_FIRST(&net_clients);
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
             qemu_del_nic(qemu_get_nic(nc));
         } else {
             qemu_del_net_client(nc);
@@ -1420,7 +1457,7 @@ void net_check_clients(void)
     QTAILQ_FOREACH(nc, &net_clients, next) {
         if (!nc->peer) {
             fprintf(stderr, "Warning: %s %s has no peer\n",
-                    nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC ?
+                    nc->info->type == NET_CLIENT_DRIVER_NIC ?
                     "nic" : "netdev", nc->name);
         }
     }
diff --git a/net/netmap.c b/net/netmap.c
index beb8d28..2d11a8f 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -400,7 +400,7 @@ static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6,

 /* NetClientInfo methods */
 static NetClientInfo net_netmap_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NETMAP,
+    .type = NET_CLIENT_DRIVER_NETMAP,
     .size = sizeof(NetmapState),
     .receive = netmap_receive,
     .receive_iov = netmap_receive_iov,
@@ -421,7 +421,7 @@ static NetClientInfo net_netmap_info = {
 int net_init_netmap(const Netdev *netdev,
                     const char *name, NetClientState *peer, Error **errp)
 {
-    const NetdevNetmapOptions *netmap_opts = netdev->opts->u.netmap.data;
+    const NetdevNetmapOptions *netmap_opts = &netdev->u.netmap;
     struct nm_desc *nmd;
     NetClientState *nc;
     Error *err = NULL;
diff --git a/net/slirp.c b/net/slirp.c
index bb49629..5aafbf3 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -128,7 +128,7 @@ static void net_slirp_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_slirp_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_USER,
+    .type = NET_CLIENT_DRIVER_USER,
     .size = sizeof(SlirpState),
     .receive = net_slirp_receive,
     .cleanup = net_slirp_cleanup,
@@ -828,8 +828,8 @@ int net_init_slirp(const Netdev *netdev, const char *name,
     const char **dnssearch;
     bool ipv4 = true, ipv6 = true;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_USER);
-    user = netdev->opts->u.user.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_USER);
+    user = &netdev->u.user;

     if ((user->has_ipv6 && user->ipv6 && !user->has_ipv4) ||
         (user->has_ipv4 && !user->ipv4)) {
diff --git a/net/socket.c b/net/socket.c
index 6e5c902..17e635d 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -311,7 +311,7 @@ static void net_socket_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_dgram_socket_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+    .type = NET_CLIENT_DRIVER_SOCKET,
     .size = sizeof(NetSocketState),
     .receive = net_socket_receive_dgram,
     .cleanup = net_socket_cleanup,
@@ -395,7 +395,7 @@ static void net_socket_connect(void *opaque)
 }

 static NetClientInfo net_socket_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+    .type = NET_CLIENT_DRIVER_SOCKET,
     .size = sizeof(NetSocketState),
     .receive = net_socket_receive,
     .cleanup = net_socket_cleanup,
@@ -670,8 +670,8 @@ int net_init_socket(const Netdev *netdev, const char *name,
     Error *err = NULL;
     const NetdevSocketOptions *sock;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_SOCKET);
-    sock = netdev->opts->u.socket.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_SOCKET);
+    sock = &netdev->u.socket;

     if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
         sock->has_udp != 1) {
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 0f23b19..729309d 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -750,7 +750,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 }

 static NetClientInfo net_tap_win32_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_TAP,
+    .type = NET_CLIENT_DRIVER_TAP,
     .size = sizeof(TAPState),
     .receive = tap_receive,
     .cleanup = tap_cleanup,
@@ -794,8 +794,8 @@ int net_init_tap(const Netdev *netdev, const char *name,
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevTapOptions *tap;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = netdev->opts->u.tap.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_TAP);
+    tap = netdev->u.tap;

     if (!tap->has_ifname) {
         error_report("tap: no interface name");
diff --git a/net/tap.c b/net/tap.c
index 0e713b0..0015e7b 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -222,7 +222,7 @@ static bool tap_has_ufo(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);

-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);

     return s->has_ufo;
 }
@@ -231,7 +231,7 @@ static bool tap_has_vnet_hdr(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);

-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);

     return !!s->host_vnet_hdr_len;
 }
@@ -240,7 +240,7 @@ static bool tap_has_vnet_hdr_len(NetClientState *nc, int len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);

-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);

     return !!tap_probe_vnet_hdr_len(s->fd, len);
 }
@@ -249,7 +249,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);

-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
            len == sizeof(struct virtio_net_hdr));

@@ -261,7 +261,7 @@ static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);

-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     assert(!!s->host_vnet_hdr_len == using_vnet_hdr);

     s->using_vnet_hdr = using_vnet_hdr;
@@ -327,14 +327,14 @@ static void tap_poll(NetClientState *nc, bool enable)
 int tap_get_fd(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     return s->fd;
 }

 /* fd support */

 static NetClientInfo net_tap_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_TAP,
+    .type = NET_CLIENT_DRIVER_TAP,
     .size = sizeof(TAPState),
     .receive = tap_receive,
     .receive_raw = tap_receive_raw,
@@ -566,8 +566,8 @@ int net_init_bridge(const Netdev *netdev, const char *name,
     TAPState *s;
     int fd, vnet_hdr;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_BRIDGE);
-    bridge = netdev->opts->u.bridge.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_BRIDGE);
+    bridge = &netdev->u.bridge;

     helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
     br     = bridge->has_br     ? bridge->br     : DEFAULT_BRIDGE_INTERFACE;
@@ -734,8 +734,8 @@ int net_init_tap(const Netdev *netdev, const char *name,
     const char *vhostfdname;
     char ifname[128];

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = netdev->opts->u.tap.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_TAP);
+    tap = &netdev->u.tap;
     queues = tap->has_queues ? tap->queues : 1;
     vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;

@@ -898,7 +898,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
 VHostNetState *tap_get_vhost_net(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     return s->vhost_net;
 }

diff --git a/net/vde.c b/net/vde.c
index 53cdbbf..b8725f8 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -68,7 +68,7 @@ static void vde_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_vde_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_VDE,
+    .type = NET_CLIENT_DRIVER_VDE,
     .size = sizeof(VDEState),
     .receive = vde_receive,
     .cleanup = vde_cleanup,
@@ -115,8 +115,8 @@ int net_init_vde(const Netdev *netdev, const char *name,
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevVdeOptions *vde;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_VDE);
-    vde = netdev->opts->u.vde.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_VDE);
+    vde = netdev->u.vde;

     /* missing optional values have been initialized to "all bits zero" */
     if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
diff --git a/net/vhost-user.c b/net/vhost-user.c
index dfdcca2..c4d63e0 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -34,14 +34,14 @@ typedef struct VhostUserChardevProps {
 VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
 {
     VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
     return s->vhost_net;
 }

 uint64_t vhost_user_get_acked_features(NetClientState *nc)
 {
     VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
     return s->acked_features;
 }

@@ -56,7 +56,7 @@ static void vhost_user_stop(int queues, NetClientState *ncs[])
     int i;

     for (i = 0; i < queues; i++) {
-        assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+        assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);

         s = DO_UPCAST(VhostUserState, nc, ncs[i]);
         if (!vhost_user_running(s)) {
@@ -82,7 +82,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[])
     options.backend_type = VHOST_BACKEND_TYPE_USER;

     for (i = 0; i < queues; i++) {
-        assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+        assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);

         s = DO_UPCAST(VhostUserState, nc, ncs[i]);
         if (vhost_user_running(s)) {
@@ -163,20 +163,20 @@ static void vhost_user_cleanup(NetClientState *nc)

 static bool vhost_user_has_vnet_hdr(NetClientState *nc)
 {
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);

     return true;
 }

 static bool vhost_user_has_ufo(NetClientState *nc)
 {
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);

     return true;
 }

 static NetClientInfo net_vhost_user_info = {
-        .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
+        .type = NET_CLIENT_DRIVER_VHOST_USER,
         .size = sizeof(VhostUserState),
         .receive = vhost_user_receive,
         .cleanup = vhost_user_cleanup,
@@ -207,7 +207,7 @@ static void net_vhost_user_event(void *opaque, int event)
     int queues;

     queues = qemu_find_net_clients_except(name, ncs,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          NET_CLIENT_DRIVER_NIC,
                                           MAX_QUEUE_NUM);
     assert(queues < MAX_QUEUE_NUM);

@@ -341,8 +341,8 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
     const NetdevVhostUserOptions *vhost_user_opts;
     CharDriverState *chr;

-    assert(netdev->opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
-    vhost_user_opts = netdev->opts->u.vhost_user.data;
+    assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
+    vhost_user_opts = &netdev->u.vhost_user;

     chr = net_vhost_parse_chardev(vhost_user_opts, errp);
     if (!chr) {
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 13/17] net: Use correct type for bool flag
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (11 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 12/17] qapi: Change Netdev into a flat union Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 14/17] net: Complete qapi-fication of netdev_add Eric Blake
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Gerd Hoffmann, Jason Wang

is_netdev is only used as a bool, so make it one.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: no change
v8: no change
v7: no change
v6: rebase to latest context
---
 include/net/net.h    |  2 +-
 hw/usb/dev-network.c |  2 +-
 net/net.c            | 12 ++++++------
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/include/net/net.h b/include/net/net.h
index 48382c3..e8d9e9e 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -203,7 +203,7 @@ extern const char *host_net_devices[];
 extern const char *legacy_tftp_prefix;
 extern const char *legacy_bootp_filename;

-int net_client_init(QemuOpts *opts, int is_netdev, Error **errp);
+int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp);
 int net_client_parse(QemuOptsList *opts_list, const char *str);
 int net_init_clients(void);
 void net_check_clients(void);
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 3ced4ba..c0f1193 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1396,7 +1396,7 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
     qemu_opt_set(opts, "type", "nic", &error_abort);
     qemu_opt_set(opts, "model", "usb", &error_abort);

-    idx = net_client_init(opts, 0, &local_err);
+    idx = net_client_init(opts, false, &local_err);
     if (local_err) {
         error_report_err(local_err);
         return NULL;
diff --git a/net/net.c b/net/net.c
index 4a7f392..c124b11 100644
--- a/net/net.c
+++ b/net/net.c
@@ -960,7 +960,7 @@ static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])(
 };


-static int net_client_init1(const void *object, int is_netdev, Error **errp)
+static int net_client_init1(const void *object, bool is_netdev, Error **errp)
 {
     Netdev legacy = {0};
     const Netdev *netdev;
@@ -1060,7 +1060,7 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
 }


-int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
+int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
 {
     void *object = NULL;
     Error *err = NULL;
@@ -1153,7 +1153,7 @@ void hmp_host_net_add(Monitor *mon, const QDict *qdict)

     qemu_opt_set(opts, "type", device, &error_abort);

-    net_client_init(opts, 0, &local_err);
+    net_client_init(opts, false, &local_err);
     if (local_err) {
         error_report_err(local_err);
         monitor_printf(mon, "adding host network device %s failed\n", device);
@@ -1183,7 +1183,7 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict)

 void netdev_add(QemuOpts *opts, Error **errp)
 {
-    net_client_init(opts, 1, errp);
+    net_client_init(opts, true, errp);
 }

 void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp)
@@ -1481,7 +1481,7 @@ static int net_init_client(void *dummy, QemuOpts *opts, Error **errp)
 {
     Error *local_err = NULL;

-    net_client_init(opts, 0, &local_err);
+    net_client_init(opts, false, &local_err);
     if (local_err) {
         error_report_err(local_err);
         return -1;
@@ -1495,7 +1495,7 @@ static int net_init_netdev(void *dummy, QemuOpts *opts, Error **errp)
     Error *local_err = NULL;
     int ret;

-    ret = net_client_init(opts, 1, &local_err);
+    ret = net_client_init(opts, true, &local_err);
     if (local_err) {
         error_report_err(local_err);
         return -1;
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 14/17] net: Complete qapi-fication of netdev_add
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (12 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 13/17] net: Use correct type for bool flag Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 15/17] option: make parse_option_bool/number non-static Eric Blake
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Luiz Capitulino, Jason Wang

We finally have all the required pieces for doing a type-safe
representation of netdev_add as a flat union, where the
discriminator 'type' now selects which additional members may
appear in the "arguments" JSON object sent over QMP, and exposes
those types through introspection, and without breaking command
line parsing.

Inline the function netdev_add() into its lone remaining caller.

There are a few places where the QMP 'netdev_add' command is now
more strict: anywhere that the QAPI lists an integer member, we
now require a strict JSON integer (previously, we allowed both
integers and strings, because the conversion from QMP to QemuOpts
back to QObject collapsed them into integers).  For example,
pre-patch, both of these examples succeed, but post-patch, the
second example fails:

{'execute':'netdev_add',
  'arguments':{'id':'net1', 'type':'hubport', 'hubid':1}}
{"return": {}}
{'execute':'netdev_add',
  'arguments':{'id':'net2', 'type':'hubport', 'hubid':"2"}}
{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'hubid', expected: integer"}}

A later patch will resolve the problem by adding a loose parsing mode
to qmp-input-visitor (teaching it to accept a string encoding of an
integer in place of an actual integer).

In qmp_netdev_add(), we still have to create a QemuOpts object
so that qmp_netdev_del() will be able to remove a hotplugged
network device; but the opts->head remains empty since we now
manage all parsing through the QAPI object rather than QemuOpts.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: s/box/boxed/, tweak commit message
v8: improve commit message, call out a change in QMP behavior,
inline netdev_add()
v7: no change
v6: don't lose qemu_opts handling

If you like the approach of the later patches, we can rearrange
things in the series to avoid the temporary regression
---
 qapi-schema.json  | 15 +++------------
 include/net/net.h |  2 --
 hmp.c             |  2 +-
 net/net.c         | 11 +++--------
 qmp-commands.hx   |  2 +-
 5 files changed, 8 insertions(+), 24 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 5658723..7ec01e1 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2362,26 +2362,17 @@
 #
 # Add a network backend.
 #
-# @type: the type of network backend.  Current valid values are 'user', 'tap',
-#        'vde', 'socket', 'dump' and 'bridge'
+# @type: the type of network backend; determines which additional arguments
+#        are valid. See Netdev documentation for more details.
 #
 # @id: the name of the new network backend
 #
-# Additional arguments depend on the type.
-#
-# TODO This command effectively bypasses QAPI completely due to its
-# "additional arguments" business.  It shouldn't have been added to
-# the schema in this form.  It should be qapified properly, or
-# replaced by a properly qapified command.
-#
 # Since: 0.14.0
 #
 # Returns: Nothing on success
 #          If @type is not a valid network backend, DeviceNotFound
 ##
-{ 'command': 'netdev_add',
-  'data': {'type': 'str', 'id': 'str'},
-  'gen': false }                # so we can get the additional arguments
+{ 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true }

 ##
 # @netdev_del:
diff --git a/include/net/net.h b/include/net/net.h
index e8d9e9e..820f880 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -210,8 +210,6 @@ void net_check_clients(void);
 void net_cleanup(void);
 void hmp_host_net_add(Monitor *mon, const QDict *qdict);
 void hmp_host_net_remove(Monitor *mon, const QDict *qdict);
-void netdev_add(QemuOpts *opts, Error **errp);
-void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp);

 int net_hub_id_for_client(NetClientState *nc, int *id);
 NetClientState *net_hub_port_find(int hub_id);
diff --git a/hmp.c b/hmp.c
index 4819abc..3502abf 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1672,7 +1672,7 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict)
         goto out;
     }

-    netdev_add(opts, &err);
+    net_client_init(opts, true, &err);
     if (err) {
         qemu_opts_del(opts);
     }
diff --git a/net/net.c b/net/net.c
index c124b11..1bd7936 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1181,12 +1181,7 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict)
     qemu_del_net_client(nc);
 }

-void netdev_add(QemuOpts *opts, Error **errp)
-{
-    net_client_init(opts, true, errp);
-}
-
-void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp)
+void qmp_netdev_add(Netdev *netdev, Error **errp)
 {
     Error *local_err = NULL;
     QemuOptsList *opts_list;
@@ -1197,12 +1192,12 @@ void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp)
         goto out;
     }

-    opts = qemu_opts_from_qdict(opts_list, qdict, &local_err);
+    opts = qemu_opts_create(opts_list, netdev->id, 1, &local_err);
     if (local_err) {
         goto out;
     }

-    netdev_add(opts, &local_err);
+    net_client_init1(netdev, true, &local_err);
     if (local_err) {
         qemu_opts_del(opts);
         goto out;
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 6937e83..cfc4086 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -976,7 +976,7 @@ EQMP
     {
         .name       = "netdev_add",
         .args_type  = "netdev:O",
-        .mhandler.cmd_new = qmp_netdev_add,
+        .mhandler.cmd_new = qmp_marshal_netdev_add,
     },

 SQMP
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 15/17] option: make parse_option_bool/number non-static
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (13 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 14/17] net: Complete qapi-fication of netdev_add Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion Eric Blake
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

From: "Daniel P. Berrange" <berrange@redhat.com>

The opts-visitor.c opts_type_bool() method has code for
parsing a string to set a bool value, as does the
qemu-option.c parse_option_bool() method, except it
handles fewer cases.

To enable consistency across the codebase, extend
parse_option_bool() to handle "yes", "no", "y" and
"n", and make it non-static. Convert the opts
visitor to call this method directly.

Also make parse_option_number() non-static to allow
for similar reuse later.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-Id: <1467724312-9378-3-git-send-email-berrange@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: reuse in my series
Previously in Dan's v7 series: Provide a QOM-based authorization API
---
 include/qemu/option.h |  4 ++++
 qapi/opts-visitor.c   | 19 +------------------
 util/qemu-option.c    | 27 ++++++++++++++++-----------
 3 files changed, 21 insertions(+), 29 deletions(-)

diff --git a/include/qemu/option.h b/include/qemu/option.h
index 1f9e3f9..2a5266f 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -37,6 +37,10 @@ int get_param_value(char *buf, int buf_size,
                     const char *tag, const char *str);


+void parse_option_bool(const char *name, const char *value, bool *ret,
+                       Error **errp);
+void parse_option_number(const char *name, const char *value,
+                         uint64_t *ret, Error **errp);
 void parse_option_size(const char *name, const char *value,
                        uint64_t *ret, Error **errp);
 bool has_help_option(const char *param);
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 1048bbc..084f7cc 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -334,7 +334,6 @@ opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 }


-/* mimics qemu-option.c::parse_option_bool() */
 static void
 opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
 {
@@ -346,23 +345,7 @@ opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
         return;
     }

-    if (opt->str) {
-        if (strcmp(opt->str, "on") == 0 ||
-            strcmp(opt->str, "yes") == 0 ||
-            strcmp(opt->str, "y") == 0) {
-            *obj = true;
-        } else if (strcmp(opt->str, "off") == 0 ||
-            strcmp(opt->str, "no") == 0 ||
-            strcmp(opt->str, "n") == 0) {
-            *obj = false;
-        } else {
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
-                       "on|yes|y|off|no|n");
-            return;
-        }
-    } else {
-        *obj = true;
-    }
+    parse_option_bool(opt->name, opt->str, obj, errp);

     processed(ov, name);
 }
diff --git a/util/qemu-option.c b/util/qemu-option.c
index 3467dc2..6fc9ccb 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -125,25 +125,30 @@ int get_param_value(char *buf, int buf_size,
     return get_next_param_value(buf, buf_size, tag, &str);
 }

-static void parse_option_bool(const char *name, const char *value, bool *ret,
-                              Error **errp)
+void parse_option_bool(const char *name, const char *value, bool *ret,
+                       Error **errp)
 {
     if (value != NULL) {
-        if (!strcmp(value, "on")) {
-            *ret = 1;
-        } else if (!strcmp(value, "off")) {
-            *ret = 0;
+        if (strcmp(value, "on") == 0 ||
+            strcmp(value, "yes") == 0 ||
+            strcmp(value, "y") == 0) {
+            *ret = true;
+        } else if (strcmp(value, "off") == 0 ||
+            strcmp(value, "no") == 0 ||
+            strcmp(value, "n") == 0) {
+            *ret = false;
         } else {
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                       name, "'on' or 'off'");
+            error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
+                       "on|yes|y|off|no|n");
+            return;
         }
     } else {
-        *ret = 1;
+        *ret = true;
     }
 }

-static void parse_option_number(const char *name, const char *value,
-                                uint64_t *ret, Error **errp)
+void parse_option_number(const char *name, const char *value,
+                         uint64_t *ret, Error **errp)
 {
     char *postfix;
     uint64_t number;
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (14 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 15/17] option: make parse_option_bool/number non-static Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14  8:04   ` Daniel P. Berrange
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 17/17] qapi: Restore autocast behavior in 'netdev_add' Eric Blake
  2016-07-14 16:42 ` [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Markus Armbruster
  17 siblings, 1 reply; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth, Andreas Färber

Currently the QmpInputVisitor assumes that all scalar
values are directly represented as their final types.
ie it assumes an 'int' is using QInt, and a 'bool' is
using QBool.

This adds an alternative mode where a QString can also
be parsed in place of the native type, by adding a parameter
and updating all callers.  For now, only the testsuite sets
the new autocast parameter.

This makes it possible to use QmpInputVisitor with a QDict
produced from QemuOpts, where everything is in string format.
It also makes it possible for the next patch to support
back-compat in legacy commands like 'netdev_add' that no
longer use QemuOpts, but must parse the same set of QMP
constructs that QemuOpts would historically allow.

Based heavily on a patch by Daniel P. Berrange <berrange@redhat.com>:
Message-Id: <1467724312-9378-4-git-send-email-berrange@redhat.com>

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: new patch (but heavily draws from 3/7 in Dan's v7 series,
Provide a QOM-based authorization API)
---
 scripts/qapi-commands.py           |   2 +-
 include/qapi/qmp-input-visitor.h   |  24 ++++--
 qapi/qmp-input-visitor.c           | 100 ++++++++++++++++++++++---
 qmp.c                              |   2 +-
 qom/qom-qobject.c                  |   2 +-
 tests/check-qnull.c                |   2 +-
 tests/test-qmp-commands.c          |   2 +-
 tests/test-qmp-input-strict.c      |   2 +-
 tests/test-qmp-input-visitor.c     | 150 ++++++++++++++++++++++++++++++++++++-
 tests/test-visitor-serialization.c |   2 +-
 docs/qapi-code-gen.txt             |   2 +-
 11 files changed, 264 insertions(+), 26 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index a06a2c4..f526c13 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -117,7 +117,7 @@ def gen_marshal(name, arg_type, boxed, ret_type):
     Visitor *v;
     %(c_name)s arg = {0};

-    v = qmp_input_visitor_new(QOBJECT(args), true);
+    v = qmp_input_visitor_new(QOBJECT(args), true, false);
     visit_start_struct(v, NULL, NULL, 0, &err);
     if (err) {
         goto out;
diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qmp-input-visitor.h
index f3ff5f3..275a167 100644
--- a/include/qapi/qmp-input-visitor.h
+++ b/include/qapi/qmp-input-visitor.h
@@ -19,12 +19,26 @@

 typedef struct QmpInputVisitor QmpInputVisitor;

-/*
- * Return a new input visitor that converts QMP to QAPI.
+/**
+ * qmp_input_visitor_new:
+ * @obj: the input object to visit
+ * @strict: whether to require that all input keys are consumed
+ * @autocast: whether to allow strings in place of other scalars
  *
- * Set @strict to reject a parse that doesn't consume all keys of a
- * dictionary; otherwise excess input is ignored.
+ * Create a new input visitor that converts QMP to QAPI.
+ *
+ * If @strict is set to true, then an error will be reported if any
+ * dict keys are not consumed during visitation.
+ *
+ * When @autocast is false, any scalar values in the @obj input data
+ * structure should be in the required type already. ie if visiting a
+ * bool, the value should already be a QBool instance.  Otherwise, a
+ * string value that can be parsed as the scalar is acceptable;
+ * however, it is not possible to parse QAPI alternates in autocast
+ * mode.
+ *
+ * Returns: a new input visitor
  */
-Visitor *qmp_input_visitor_new(QObject *obj, bool strict);
+Visitor *qmp_input_visitor_new(QObject *obj, bool strict, bool autocast);

 #endif
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 64dd392..7a1b93a 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -20,6 +20,7 @@
 #include "qemu-common.h"
 #include "qapi/qmp/types.h"
 #include "qapi/qmp/qerror.h"
+#include "qemu/cutils.h"

 #define QIV_STACK_SIZE 1024

@@ -47,6 +48,9 @@ struct QmpInputVisitor

     /* True to reject parse in visit_end_struct() if unvisited keys remain. */
     bool strict;
+
+    /* True to allow strings in place of native scalars. */
+    bool autocast;
 };

 static QmpInputVisitor *to_qiv(Visitor *v)
@@ -234,6 +238,8 @@ static void qmp_input_start_alternate(Visitor *v, const char *name,
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, false);

+    assert(!qiv->autocast);
+
     if (!qobj) {
         *obj = NULL;
         error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
@@ -250,11 +256,21 @@ static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj,
                                  Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
-    QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QInt *qint = qobject_to_qint(qobj);

     if (!qint) {
-        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
-                   "integer");
+        QString *qstr = qobject_to_qstring(qobj);
+
+        if (qiv->autocast && qstr) {
+            uint64_t ret = *obj;
+
+            parse_option_number(name, qstr->string, &ret, errp);
+            *obj = ret;
+        } else {
+            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                       "integer");
+        }
         return;
     }

@@ -266,11 +282,18 @@ static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj,
 {
     /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
     QmpInputVisitor *qiv = to_qiv(v);
-    QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QInt *qint = qobject_to_qint(qobj);

     if (!qint) {
-        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
-                   "integer");
+        QString *qstr = qobject_to_qstring(qobj);
+
+        if (qiv->autocast && qstr) {
+            parse_option_number(name, qstr->string, obj, errp);
+        } else {
+            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                       "integer");
+        }
         return;
     }

@@ -281,11 +304,18 @@ static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj,
                                 Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
-    QBool *qbool = qobject_to_qbool(qmp_input_get_object(qiv, name, true));
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QBool *qbool = qobject_to_qbool(qobj);

     if (!qbool) {
-        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
-                   "boolean");
+        QString *qstr = qobject_to_qstring(qobj);
+
+        if (qiv->autocast && qstr) {
+            parse_option_bool(name, qstr->string, obj, errp);
+        } else {
+            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                       "boolean");
+        }
         return;
     }

@@ -315,6 +345,7 @@ static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
     QObject *qobj = qmp_input_get_object(qiv, name, true);
     QInt *qint;
     QFloat *qfloat;
+    QString *qstr;

     qint = qobject_to_qint(qobj);
     if (qint) {
@@ -328,6 +359,19 @@ static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
         return;
     }

+    qstr = qobject_to_qstring(qobj);
+    if (qiv->autocast && qstr) {
+        char *endp;
+        double value;
+
+        errno = 0;
+        value = strtod(qstr->string, &endp);
+        if (errno == 0 && endp != qstr->string && *endp == '\0') {
+            *obj = value;
+            return;
+        }
+    }
+
     error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                "number");
 }
@@ -353,6 +397,40 @@ static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
     }
 }

+static void qmp_input_type_size(Visitor *v, const char *name, uint64_t *obj,
+                                Error **errp)
+{
+    /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
+    QmpInputVisitor *qiv = to_qiv(v);
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QInt *qint = qobject_to_qint(qobj);
+
+    if (!qint) {
+        QString *qstr = qobject_to_qstring(qobj);
+
+        if (qiv->autocast && qstr) {
+            int64_t val;
+            char *endptr;
+
+            val = qemu_strtosz_suffix(qstr->string, &endptr,
+                                      QEMU_STRTOSZ_DEFSUFFIX_B);
+            if (val < 0 || *endptr) {
+                error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
+                           "a size value representible as a non-negative"
+                           " int64");
+            } else {
+                *obj = val;
+            }
+        } else {
+            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                       "integer");
+        }
+        return;
+    }
+
+    *obj = qint_get_int(qint);
+}
+
 static void qmp_input_optional(Visitor *v, const char *name, bool *present)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -380,7 +458,7 @@ static void qmp_input_free(Visitor *v)
     g_free(qiv);
 }

-Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
+Visitor *qmp_input_visitor_new(QObject *obj, bool strict, bool autocast)
 {
     QmpInputVisitor *v;

@@ -401,9 +479,11 @@ Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
     v->visitor.type_number = qmp_input_type_number;
     v->visitor.type_any = qmp_input_type_any;
     v->visitor.type_null = qmp_input_type_null;
+    v->visitor.type_size = qmp_input_type_size;
     v->visitor.optional = qmp_input_optional;
     v->visitor.free = qmp_input_free;
     v->strict = strict;
+    v->autocast = autocast;

     v->root = obj;
     qobject_incref(obj);
diff --git a/qmp.c b/qmp.c
index b6d531e..ff93890 100644
--- a/qmp.c
+++ b/qmp.c
@@ -666,7 +666,7 @@ void qmp_object_add(const char *type, const char *id,
         }
     }

-    v = qmp_input_visitor_new(props, true);
+    v = qmp_input_visitor_new(props, true, false);
     obj = user_creatable_add_type(type, id, pdict, v, errp);
     visit_free(v);
     if (obj) {
diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index c225abc..974c7ea 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -23,7 +23,7 @@ void object_property_set_qobject(Object *obj, QObject *value,
 {
     Visitor *v;
     /* TODO: Should we reject, rather than ignore, excess input? */
-    v = qmp_input_visitor_new(value, false);
+    v = qmp_input_visitor_new(value, false, false);
     object_property_set(obj, v, name, errp);
     visit_free(v);
 }
diff --git a/tests/check-qnull.c b/tests/check-qnull.c
index dc906b1..483ed6d 100644
--- a/tests/check-qnull.c
+++ b/tests/check-qnull.c
@@ -47,7 +47,7 @@ static void qnull_visit_test(void)

     g_assert(qnull_.refcnt == 1);
     obj = qnull();
-    v = qmp_input_visitor_new(obj, true);
+    v = qmp_input_visitor_new(obj, true, false);
     qobject_decref(obj);
     visit_type_null(v, NULL, &error_abort);
     visit_free(v);
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 5af1a46..974841c 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -229,7 +229,7 @@ static void test_dealloc_partial(void)
         ud2_dict = qdict_new();
         qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));

-        v = qmp_input_visitor_new(QOBJECT(ud2_dict), true);
+        v = qmp_input_visitor_new(QOBJECT(ud2_dict), true, false);
         visit_type_UserDefTwo(v, NULL, &ud2, &err);
         visit_free(v);
         QDECREF(ud2_dict);
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index 814550a..93ee137 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -53,7 +53,7 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data,
     data->obj = qobject_from_jsonv(json_string, ap);
     g_assert(data->obj);

-    data->qiv = qmp_input_visitor_new(data->obj, true);
+    data->qiv = qmp_input_visitor_new(data->obj, true, false);
     g_assert(data->qiv);
     return data->qiv;
 }
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index f583dce..29c4562 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -41,6 +41,7 @@ static void visitor_input_teardown(TestInputVisitorData *data,
    function so that the JSON string used by the tests are kept in the test
    functions (and not in main()). */
 static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
+                                                 bool strict, bool autocast,
                                                  const char *json_string,
                                                  va_list *ap)
 {
@@ -49,11 +50,26 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
     data->obj = qobject_from_jsonv(json_string, ap);
     g_assert(data->obj);

-    data->qiv = qmp_input_visitor_new(data->obj, false);
+    data->qiv = qmp_input_visitor_new(data->obj, strict, autocast);
     g_assert(data->qiv);
     return data->qiv;
 }

+static GCC_FMT_ATTR(4, 5)
+Visitor *visitor_input_test_init_full(TestInputVisitorData *data,
+                                      bool strict, bool autocast,
+                                      const char *json_string, ...)
+{
+    Visitor *v;
+    va_list ap;
+
+    va_start(ap, json_string);
+    v = visitor_input_test_init_internal(data, strict, autocast,
+                                         json_string, &ap);
+    va_end(ap);
+    return v;
+}
+
 static GCC_FMT_ATTR(2, 3)
 Visitor *visitor_input_test_init(TestInputVisitorData *data,
                                  const char *json_string, ...)
@@ -62,7 +78,8 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data,
     va_list ap;

     va_start(ap, json_string);
-    v = visitor_input_test_init_internal(data, json_string, &ap);
+    v = visitor_input_test_init_internal(data, false, false,
+                                         json_string, &ap);
     va_end(ap);
     return v;
 }
@@ -77,7 +94,8 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data,
 static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data,
                                             const char *json_string)
 {
-    return visitor_input_test_init_internal(data, json_string, NULL);
+    return visitor_input_test_init_internal(data, false, false,
+                                            json_string, NULL);
 }

 static void test_visitor_in_int(TestInputVisitorData *data,
@@ -109,6 +127,33 @@ static void test_visitor_in_int_overflow(TestInputVisitorData *data,
     error_free_or_abort(&err);
 }

+static void test_visitor_in_int_autocast(TestInputVisitorData *data,
+                                         const void *unused)
+{
+    int64_t res = 0, value = -42;
+    Visitor *v;
+
+    v = visitor_input_test_init_full(data, false, true,
+                                     "\"-42\"");
+
+    visit_type_int(v, NULL, &res, &error_abort);
+    g_assert_cmpint(res, ==, value);
+}
+
+static void test_visitor_in_int_noautocast(TestInputVisitorData *data,
+                                           const void *unused)
+{
+    int64_t res = 0;
+    Visitor *v;
+    Error *err = NULL;
+
+    v = visitor_input_test_init(data, "\"-42\"");
+
+    visit_type_int(v, NULL, &res, &err);
+    g_assert(err != NULL);
+    error_free(err);
+}
+
 static void test_visitor_in_bool(TestInputVisitorData *data,
                                  const void *unused)
 {
@@ -121,6 +166,32 @@ static void test_visitor_in_bool(TestInputVisitorData *data,
     g_assert_cmpint(res, ==, true);
 }

+static void test_visitor_in_bool_autocast(TestInputVisitorData *data,
+                                          const void *unused)
+{
+    bool res = false;
+    Visitor *v;
+
+    v = visitor_input_test_init_full(data, false, true, "\"yes\"");
+
+    visit_type_bool(v, NULL, &res, &error_abort);
+    g_assert_cmpint(res, ==, true);
+}
+
+static void test_visitor_in_bool_noautocast(TestInputVisitorData *data,
+                                          const void *unused)
+{
+    bool res = false;
+    Visitor *v;
+    Error *err = NULL;
+
+    v = visitor_input_test_init(data, "\"true\"");
+
+    visit_type_bool(v, NULL, &res, &err);
+    g_assert(err != NULL);
+    error_free(err);
+}
+
 static void test_visitor_in_number(TestInputVisitorData *data,
                                    const void *unused)
 {
@@ -133,6 +204,63 @@ static void test_visitor_in_number(TestInputVisitorData *data,
     g_assert_cmpfloat(res, ==, value);
 }

+static void test_visitor_in_number_autocast(TestInputVisitorData *data,
+                                            const void *unused)
+{
+    double res = 0, value = 3.14;
+    Visitor *v;
+
+    v = visitor_input_test_init_full(data, false, true, "\"3.14\"");
+
+    visit_type_number(v, NULL, &res, &error_abort);
+    g_assert_cmpfloat(res, ==, value);
+}
+
+static void test_visitor_in_number_noautocast(TestInputVisitorData *data,
+                                              const void *unused)
+{
+    double res = 0;
+    Visitor *v;
+    Error *err = NULL;
+
+    v = visitor_input_test_init(data, "\"3.14\"");
+
+    visit_type_number(v, NULL, &res, &err);
+    g_assert(err != NULL);
+    error_free(err);
+}
+
+static void test_visitor_in_size_autocast(TestInputVisitorData *data,
+                                            const void *unused)
+{
+    uint64_t res, value = 500 * 1024 * 1024;
+    Visitor *v;
+
+    v = visitor_input_test_init_full(data, false, true, "\"500M\"");
+
+    visit_type_size(v, NULL, &res, &error_abort);
+    g_assert_cmpfloat(res, ==, value);
+
+    v = visitor_input_test_init_full(data, false, true, "524288000");
+
+    visit_type_size(v, NULL, &res, &error_abort);
+    g_assert_cmpfloat(res, ==, value);
+}
+
+static void test_visitor_in_size_noautocast(TestInputVisitorData *data,
+                                            const void *unused)
+{
+    uint64_t res = 0;
+    Visitor *v;
+    Error *err = NULL;
+
+    v = visitor_input_test_init(data, "\"500M\"");
+
+    visit_type_size(v, NULL, &res, &err);
+    g_assert(err != NULL);
+    error_free(err);
+}
+
 static void test_visitor_in_string(TestInputVisitorData *data,
                                    const void *unused)
 {
@@ -841,10 +969,26 @@ int main(int argc, char **argv)
                            &in_visitor_data, test_visitor_in_int);
     input_visitor_test_add("/visitor/input/int_overflow",
                            &in_visitor_data, test_visitor_in_int_overflow);
+    input_visitor_test_add("/visitor/input/int_autocast",
+                           &in_visitor_data, test_visitor_in_int_autocast);
+    input_visitor_test_add("/visitor/input/int_noautocast",
+                           &in_visitor_data, test_visitor_in_int_noautocast);
     input_visitor_test_add("/visitor/input/bool",
                            &in_visitor_data, test_visitor_in_bool);
+    input_visitor_test_add("/visitor/input/bool_autocast",
+                           &in_visitor_data, test_visitor_in_bool_autocast);
+    input_visitor_test_add("/visitor/input/bool_noautocast",
+                           &in_visitor_data, test_visitor_in_bool_noautocast);
     input_visitor_test_add("/visitor/input/number",
                            &in_visitor_data, test_visitor_in_number);
+    input_visitor_test_add("/visitor/input/number_autocast",
+                           &in_visitor_data, test_visitor_in_number_autocast);
+    input_visitor_test_add("/visitor/input/number_noautocast",
+                           &in_visitor_data, test_visitor_in_number_noautocast);
+    input_visitor_test_add("/visitor/input/size_autocast",
+                           &in_visitor_data, test_visitor_in_size_autocast);
+    input_visitor_test_add("/visitor/input/size_noautocast",
+                           &in_visitor_data, test_visitor_in_size_noautocast);
     input_visitor_test_add("/visitor/input/string",
                            &in_visitor_data, test_visitor_in_string);
     input_visitor_test_add("/visitor/input/enum",
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index dba4670..ddd5837 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1040,7 +1040,7 @@ static void qmp_deserialize(void **native_out, void *datap,
     obj = qobject_from_json(qstring_get_str(output_json));

     QDECREF(output_json);
-    d->qiv = qmp_input_visitor_new(obj, true);
+    d->qiv = qmp_input_visitor_new(obj, true, false);
     qobject_decref(obj_orig);
     qobject_decref(obj);
     visit(d->qiv, native_out, errp);
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index de298dc..961015c 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -1024,7 +1024,7 @@ Example:
         Visitor *v;
         UserDefOneList *arg1 = NULL;

-        v = qmp_input_visitor_new(QOBJECT(args), true);
+        v = qmp_input_visitor_new(QOBJECT(args), true, false);
         visit_start_struct(v, NULL, NULL, 0, &err);
         if (err) {
             goto out;
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9 17/17] qapi: Restore autocast behavior in 'netdev_add'
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (15 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion Eric Blake
@ 2016-07-14  3:50 ` Eric Blake
  2016-07-14 16:42 ` [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Markus Armbruster
  17 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14  3:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, berrange, Michael Roth

When we converted 'netdev_add' to a fully-introspectible QMP,
we temporarily lost the ability to parse strings in place of
integers, the way the old QemuOpts code had done.  But now that
we have autocast, we can restore that behavior, by adding a
new 'autocast':true marker in QAPI.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: new patch

If we like this patch, one alternative would be to rearrange
the series to get autocast working first, prior to converting
netdev_add to drop 'gen':false, so that we don't have any
temporary regression.
---
 qapi-schema.json               |  6 +++++-
 scripts/qapi.py                | 16 ++++++++++------
 scripts/qapi-commands.py       | 11 ++++++-----
 scripts/qapi-introspect.py     |  2 +-
 docs/qapi-code-gen.txt         | 10 +++++++++-
 tests/qapi-schema/test-qapi.py |  2 +-
 6 files changed, 32 insertions(+), 15 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 7ec01e1..4dccc9b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2367,12 +2367,16 @@
 #
 # @id: the name of the new network backend
 #
+# For backward compatibility, this command accepts strings where introspection
+# says that integers are expected; however, clients should not rely on this
+# and should obey the types visible in introspection.
+#
 # Since: 0.14.0
 #
 # Returns: Nothing on success
 #          If @type is not a valid network backend, DeviceNotFound
 ##
-{ 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true }
+{ 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true, 'autocast': true }

 ##
 # @netdev_del:
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 21bc32f..0e33f72 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -702,7 +702,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
             raise QAPIExprError(info,
                                 "'%s' of %s '%s' should only use false value"
                                 % (key, meta, name))
-        if key == 'boxed' and value is not True:
+        if (key == 'boxed' or key == 'autocast') and value is not True:
             raise QAPIExprError(info,
                                 "'%s' of %s '%s' should only use true value"
                                 % (key, meta, name))
@@ -737,7 +737,8 @@ def check_exprs(exprs):
             add_struct(expr, info)
         elif 'command' in expr:
             check_keys(expr_elem, 'command', [],
-                       ['data', 'returns', 'gen', 'success-response', 'boxed'])
+                       ['data', 'returns', 'gen', 'success-response',
+                        'boxed', 'autocast'])
             add_name(expr['command'], info, 'command')
         elif 'event' in expr:
             check_keys(expr_elem, 'event', [], ['data', 'boxed'])
@@ -838,7 +839,7 @@ class QAPISchemaVisitor(object):
         pass

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, autocast):
         pass

     def visit_event(self, name, info, arg_type, boxed):
@@ -1181,7 +1182,7 @@ class QAPISchemaAlternateType(QAPISchemaType):

 class QAPISchemaCommand(QAPISchemaEntity):
     def __init__(self, name, info, arg_type, ret_type, gen, success_response,
-                 boxed):
+                 boxed, autocast):
         QAPISchemaEntity.__init__(self, name, info)
         assert not arg_type or isinstance(arg_type, str)
         assert not ret_type or isinstance(ret_type, str)
@@ -1192,6 +1193,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
         self.gen = gen
         self.success_response = success_response
         self.boxed = boxed
+        self.autocast = autocast

     def check(self, schema):
         if self._arg_type_name:
@@ -1216,7 +1218,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
     def visit(self, visitor):
         visitor.visit_command(self.name, self.info,
                               self.arg_type, self.ret_type,
-                              self.gen, self.success_response, self.boxed)
+                              self.gen, self.success_response, self.boxed,
+                              self.autocast)


 class QAPISchemaEvent(QAPISchemaEntity):
@@ -1422,6 +1425,7 @@ class QAPISchema(object):
         gen = expr.get('gen', True)
         success_response = expr.get('success-response', True)
         boxed = expr.get('boxed', False)
+        autocast = expr.get('autocast', False)
         if isinstance(data, OrderedDict):
             data = self._make_implicit_object_type(
                 name, info, 'arg', self._make_members(data, info))
@@ -1429,7 +1433,7 @@ class QAPISchema(object):
             assert len(rets) == 1
             rets = self._make_array_type(rets[0], info)
         self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
-                                           success_response, boxed))
+                                           success_response, boxed, autocast))

     def _def_event(self, expr, info):
         name = expr['event']
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index f526c13..bf99d60 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -97,7 +97,7 @@ def gen_marshal_decl(name):
                  proto=gen_marshal_proto(name))


-def gen_marshal(name, arg_type, boxed, ret_type):
+def gen_marshal(name, arg_type, boxed, autocast, ret_type):
     ret = mcgen('''

 %(proto)s
@@ -117,7 +117,7 @@ def gen_marshal(name, arg_type, boxed, ret_type):
     Visitor *v;
     %(c_name)s arg = {0};

-    v = qmp_input_visitor_new(QOBJECT(args), true, false);
+    v = qmp_input_visitor_new(QOBJECT(args), true, %(autocast)s);
     visit_start_struct(v, NULL, NULL, 0, &err);
     if (err) {
         goto out;
@@ -131,7 +131,8 @@ def gen_marshal(name, arg_type, boxed, ret_type):
         goto out;
     }
 ''',
-                     c_name=arg_type.c_name())
+                     c_name=arg_type.c_name(),
+                     autocast='true' if autocast else 'false')

     else:
         ret += mcgen('''
@@ -215,7 +216,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         self._visited_ret_types = None

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, autocast):
         if not gen:
             return
         self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
@@ -224,7 +225,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
             self.defn += gen_marshal_output(ret_type)
         if middle_mode:
             self.decl += gen_marshal_decl(name)
-        self.defn += gen_marshal(name, arg_type, boxed, ret_type)
+        self.defn += gen_marshal(name, arg_type, boxed, autocast, ret_type)
         if not middle_mode:
             self._regy += gen_register_command(name, success_response)

diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index 60e9877..0e878e2 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -154,7 +154,7 @@ const char %(c_name)s[] = %(c_string)s;
                                     for m in variants.variants]})

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, box):
+                      gen, success_response, box, autocast):
         arg_type = arg_type or self._schema.the_empty_object_type
         ret_type = ret_type or self._schema.the_empty_object_type
         self._gen_json(name, 'command',
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 961015c..fc462bf 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -410,7 +410,7 @@ following example objects:
 === Commands ===

 Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
-         '*returns': TYPE-NAME, '*boxed': true,
+         '*returns': TYPE-NAME, '*boxed': true, '*autocast': true,
          '*gen': false, '*success-response': false }

 Commands are defined by using a dictionary containing several members,
@@ -475,6 +475,14 @@ arguments for the user's function out of an input QDict, calls the
 user's function, and if it succeeded, builds an output QObject from
 its return value.

+By default, the marshalling function enforces that the QDict built
+from the Client JSON Protocol is strictly typed (if the QAPI calls for
+an 'int', the client must have passed a JSON number).  But if the
+command definition includes a key 'autocast' with the boolean value
+true, the command will also accept JSON strings that can be intepreted
+as the corresponding scalar type.  Autocast should only be used for
+preserving backwards compatibility, and avoided on new commands.
+
 In rare cases, QAPI cannot express a type-safe representation of a
 corresponding Client JSON Protocol command.  You then have to suppress
 generation of a marshalling function by including a key 'gen' with
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index ef74e2c..d5f675f 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -36,7 +36,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
         self._print_variants(variants)

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, autocast):
         print 'command %s %s -> %s' % \
             (name, arg_type and arg_type.name, ret_type and ret_type.name)
         print '   gen=%s success_response=%s boxed=%s' % \
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion Eric Blake
@ 2016-07-14  8:04   ` Daniel P. Berrange
  2016-07-14 12:43     ` Eric Blake
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel P. Berrange @ 2016-07-14  8:04 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, armbru, Michael Roth, Andreas Färber

On Wed, Jul 13, 2016 at 09:50:27PM -0600, Eric Blake wrote:
> Currently the QmpInputVisitor assumes that all scalar
> values are directly represented as their final types.
> ie it assumes an 'int' is using QInt, and a 'bool' is
> using QBool.
> 
> This adds an alternative mode where a QString can also
> be parsed in place of the native type, by adding a parameter
> and updating all callers.  For now, only the testsuite sets
> the new autocast parameter.
> 
> This makes it possible to use QmpInputVisitor with a QDict
> produced from QemuOpts, where everything is in string format.
> It also makes it possible for the next patch to support
> back-compat in legacy commands like 'netdev_add' that no
> longer use QemuOpts, but must parse the same set of QMP
> constructs that QemuOpts would historically allow.

I'm not really a huge fan of the approach there. IMHO the
input visitor should strictly operate in "all native types"
or "all string types" mode. The way you've done it here
means that users can actually mix & match strings vs native
types for each individual parameter :-(

IMHO, I'd suggest you try to parse netdev_add args with
it in "all native types" mode, if that fails, then re-parse
in "all string types" mode.

For that you'd not have to modify my patch at all.

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion
  2016-07-14  8:04   ` Daniel P. Berrange
@ 2016-07-14 12:43     ` Eric Blake
  2016-07-14 13:03       ` Daniel P. Berrange
  0 siblings, 1 reply; 31+ messages in thread
From: Eric Blake @ 2016-07-14 12:43 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: qemu-devel, armbru, Michael Roth, Andreas Färber

[-- Attachment #1: Type: text/plain, Size: 2457 bytes --]

On 07/14/2016 02:04 AM, Daniel P. Berrange wrote:
> On Wed, Jul 13, 2016 at 09:50:27PM -0600, Eric Blake wrote:
>> Currently the QmpInputVisitor assumes that all scalar
>> values are directly represented as their final types.
>> ie it assumes an 'int' is using QInt, and a 'bool' is
>> using QBool.
>>
>> This adds an alternative mode where a QString can also
>> be parsed in place of the native type, by adding a parameter
>> and updating all callers.  For now, only the testsuite sets
>> the new autocast parameter.
>>
>> This makes it possible to use QmpInputVisitor with a QDict
>> produced from QemuOpts, where everything is in string format.
>> It also makes it possible for the next patch to support
>> back-compat in legacy commands like 'netdev_add' that no
>> longer use QemuOpts, but must parse the same set of QMP
>> constructs that QemuOpts would historically allow.
> 
> I'm not really a huge fan of the approach there. IMHO the
> input visitor should strictly operate in "all native types"
> or "all string types" mode. The way you've done it here
> means that users can actually mix & match strings vs native
> types for each individual parameter :-(
> 
> IMHO, I'd suggest you try to parse netdev_add args with
> it in "all native types" mode, if that fails, then re-parse
> in "all string types" mode.

Sadly, this idea won't work. Without this series, the existing QMP
command 'netdev_add' takes mixed integers and strings in a single call,
by virtue of converting QObject into QemuOpts and back into QObject.
Forcing the parser to allow only integers or only strings would reject
current uses of netdev_add, and we don't yet have a good idea whether
any existing clients were depending on that behavior.

Also, see patch 17/17 - the easiest way to implement a relaxed QMP parse
is by setting a single flag which controls which visitor constructor is
used.  Your idea of parsing once with integer-only, then falling back to
parse a second time with string-only, would complicate the generator to
have to create two different visitors, rather than passing a single
boolean parameter through to the single visitor constructor.

> 
> For that you'd not have to modify my patch at all.
> 

Also not quite true - your patch no longer applies to master, so it
needed rebasing anyway.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion
  2016-07-14 12:43     ` Eric Blake
@ 2016-07-14 13:03       ` Daniel P. Berrange
  2016-07-14 14:04         ` Eric Blake
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel P. Berrange @ 2016-07-14 13:03 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, armbru, Michael Roth, Andreas Färber

On Thu, Jul 14, 2016 at 06:43:16AM -0600, Eric Blake wrote:
> On 07/14/2016 02:04 AM, Daniel P. Berrange wrote:
> > On Wed, Jul 13, 2016 at 09:50:27PM -0600, Eric Blake wrote:
> >> Currently the QmpInputVisitor assumes that all scalar
> >> values are directly represented as their final types.
> >> ie it assumes an 'int' is using QInt, and a 'bool' is
> >> using QBool.
> >>
> >> This adds an alternative mode where a QString can also
> >> be parsed in place of the native type, by adding a parameter
> >> and updating all callers.  For now, only the testsuite sets
> >> the new autocast parameter.
> >>
> >> This makes it possible to use QmpInputVisitor with a QDict
> >> produced from QemuOpts, where everything is in string format.
> >> It also makes it possible for the next patch to support
> >> back-compat in legacy commands like 'netdev_add' that no
> >> longer use QemuOpts, but must parse the same set of QMP
> >> constructs that QemuOpts would historically allow.
> > 
> > I'm not really a huge fan of the approach there. IMHO the
> > input visitor should strictly operate in "all native types"
> > or "all string types" mode. The way you've done it here
> > means that users can actually mix & match strings vs native
> > types for each individual parameter :-(
> > 
> > IMHO, I'd suggest you try to parse netdev_add args with
> > it in "all native types" mode, if that fails, then re-parse
> > in "all string types" mode.
> 
> Sadly, this idea won't work. Without this series, the existing QMP
> command 'netdev_add' takes mixed integers and strings in a single call,
> by virtue of converting QObject into QemuOpts and back into QObject.

Ok, so lemme check I understand. The original QObject has properties
which are integers, but with existing code QMP allows them to be passed
as strings or ints, converts them to QemuOpts which makes them all
strings and converts them back to QObject leaving them all as strings.

You've now got rid of the QObject -> QemuOpts conversion, so we're
not string-ifying everything, and so have to cope with arbitrary
mix directly.

> Forcing the parser to allow only integers or only strings would reject
> current uses of netdev_add, and we don't yet have a good idea whether
> any existing clients were depending on that behavior.
> 
> Also, see patch 17/17 - the easiest way to implement a relaxed QMP parse
> is by setting a single flag which controls which visitor constructor is
> used.  Your idea of parsing once with integer-only, then falling back to
> parse a second time with string-only, would complicate the generator to
> have to create two different visitors, rather than passing a single
> boolean parameter through to the single visitor constructor.

So I don't think a simple boolean flag is desirable, as we have 3 distinct
scenarios:

 1. Strongly typed only
 2. String conversion only
 3. Strongly typed, with string conversion fallback

My current patch provides 1 + 2, your alternative patch provides 1 + 3.
If we use option 3, in cases which only actually need option 2, then we
are potentially introducing more scenarios where arbitrarily mixed
types are accepted.  IOW, I think we must implement 1, 2 and 3 and avoid
using 3 except where absolutely needed.

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion
  2016-07-14 13:03       ` Daniel P. Berrange
@ 2016-07-14 14:04         ` Eric Blake
  2016-07-14 14:16           ` Daniel P. Berrange
  0 siblings, 1 reply; 31+ messages in thread
From: Eric Blake @ 2016-07-14 14:04 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: qemu-devel, armbru, Michael Roth, Andreas Färber

[-- Attachment #1: Type: text/plain, Size: 3576 bytes --]

On 07/14/2016 07:03 AM, Daniel P. Berrange wrote:

>>> I'm not really a huge fan of the approach there. IMHO the
>>> input visitor should strictly operate in "all native types"
>>> or "all string types" mode. The way you've done it here
>>> means that users can actually mix & match strings vs native
>>> types for each individual parameter :-(
>>>
>>> IMHO, I'd suggest you try to parse netdev_add args with
>>> it in "all native types" mode, if that fails, then re-parse
>>> in "all string types" mode.
>>
>> Sadly, this idea won't work. Without this series, the existing QMP
>> command 'netdev_add' takes mixed integers and strings in a single call,
>> by virtue of converting QObject into QemuOpts and back into QObject.
> 
> Ok, so lemme check I understand. The original QObject has properties
> which are integers, but with existing code QMP allows them to be passed
> as strings or ints, converts them to QemuOpts which makes them all
> strings and converts them back to QObject leaving them all as strings.

Yes, the existing code (using 'gen':false) just passes an entire QObject
to hand-written code; the hand-written code converted everything to
QemuOpts (which flattens both integers and strings into options), then
expanded QemuOpts back into QObject (strings-only).

> 
> You've now got rid of the QObject -> QemuOpts conversion, so we're
> not string-ifying everything, and so have to cope with arbitrary
> mix directly.

Yes.

> 
>> Forcing the parser to allow only integers or only strings would reject
>> current uses of netdev_add, and we don't yet have a good idea whether
>> any existing clients were depending on that behavior.
>>
>> Also, see patch 17/17 - the easiest way to implement a relaxed QMP parse
>> is by setting a single flag which controls which visitor constructor is
>> used.  Your idea of parsing once with integer-only, then falling back to
>> parse a second time with string-only, would complicate the generator to
>> have to create two different visitors, rather than passing a single
>> boolean parameter through to the single visitor constructor.
> 
> So I don't think a simple boolean flag is desirable, as we have 3 distinct
> scenarios:
> 
>  1. Strongly typed only
>  2. String conversion only
>  3. Strongly typed, with string conversion fallback
> 
> My current patch provides 1 + 2, your alternative patch provides 1 + 3.
> If we use option 3, in cases which only actually need option 2, then we
> are potentially introducing more scenarios where arbitrarily mixed
> types are accepted.  IOW, I think we must implement 1, 2 and 3 and avoid
> using 3 except where absolutely needed.

3 is a superset of 2, and your command-line conversion is the only case
where we can achieve 2.  That is, code dealing with QMP can only choose
between 1 and 3, based on whether the QAPI .json file used
'autocast':true for back-compat reasons (the only candidates are
'netdev_add' and 'device_add').  And code dealing with command line
parsing can only choose 2 (QemuOpts is string-only), but parsing
string-only via 2 is no different than the result achieved from parsing
strongly-typed with string fallback via 3.

I still don't buy the fact that we need a string-only parser at the
moment, but it would not be hard to change the 'bool autocast' into a
tri-state enum, and then make the implementation specifically honor 1,
2, or 3 based on the enum value.


-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion
  2016-07-14 14:04         ` Eric Blake
@ 2016-07-14 14:16           ` Daniel P. Berrange
  2016-07-14 14:25             ` Eric Blake
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel P. Berrange @ 2016-07-14 14:16 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, armbru, Michael Roth, Andreas Färber

On Thu, Jul 14, 2016 at 08:04:29AM -0600, Eric Blake wrote:
> On 07/14/2016 07:03 AM, Daniel P. Berrange wrote:
> 
> >>> I'm not really a huge fan of the approach there. IMHO the
> >>> input visitor should strictly operate in "all native types"
> >>> or "all string types" mode. The way you've done it here
> >>> means that users can actually mix & match strings vs native
> >>> types for each individual parameter :-(
> >>>
> >>> IMHO, I'd suggest you try to parse netdev_add args with
> >>> it in "all native types" mode, if that fails, then re-parse
> >>> in "all string types" mode.
> >>
> >> Sadly, this idea won't work. Without this series, the existing QMP
> >> command 'netdev_add' takes mixed integers and strings in a single call,
> >> by virtue of converting QObject into QemuOpts and back into QObject.
> > 
> > Ok, so lemme check I understand. The original QObject has properties
> > which are integers, but with existing code QMP allows them to be passed
> > as strings or ints, converts them to QemuOpts which makes them all
> > strings and converts them back to QObject leaving them all as strings.
> 
> Yes, the existing code (using 'gen':false) just passes an entire QObject
> to hand-written code; the hand-written code converted everything to
> QemuOpts (which flattens both integers and strings into options), then
> expanded QemuOpts back into QObject (strings-only).
> 
> > 
> > You've now got rid of the QObject -> QemuOpts conversion, so we're
> > not string-ifying everything, and so have to cope with arbitrary
> > mix directly.
> 
> Yes.
> 
> > 
> >> Forcing the parser to allow only integers or only strings would reject
> >> current uses of netdev_add, and we don't yet have a good idea whether
> >> any existing clients were depending on that behavior.
> >>
> >> Also, see patch 17/17 - the easiest way to implement a relaxed QMP parse
> >> is by setting a single flag which controls which visitor constructor is
> >> used.  Your idea of parsing once with integer-only, then falling back to
> >> parse a second time with string-only, would complicate the generator to
> >> have to create two different visitors, rather than passing a single
> >> boolean parameter through to the single visitor constructor.
> > 
> > So I don't think a simple boolean flag is desirable, as we have 3 distinct
> > scenarios:
> > 
> >  1. Strongly typed only
> >  2. String conversion only
> >  3. Strongly typed, with string conversion fallback
> > 
> > My current patch provides 1 + 2, your alternative patch provides 1 + 3.
> > If we use option 3, in cases which only actually need option 2, then we
> > are potentially introducing more scenarios where arbitrarily mixed
> > types are accepted.  IOW, I think we must implement 1, 2 and 3 and avoid
> > using 3 except where absolutely needed.
> 
> 3 is a superset of 2, and your command-line conversion is the only case
> where we can achieve 2.  That is, code dealing with QMP can only choose
> between 1 and 3, based on whether the QAPI .json file used
> 'autocast':true for back-compat reasons (the only candidates are
> 'netdev_add' and 'device_add').  And code dealing with command line
> parsing can only choose 2 (QemuOpts is string-only), but parsing
> string-only via 2 is no different than the result achieved from parsing
> strongly-typed with string fallback via 3.
> 
> I still don't buy the fact that we need a string-only parser at the
> moment, but it would not be hard to change the 'bool autocast' into a
> tri-state enum, and then make the implementation specifically honor 1,
> 2, or 3 based on the enum value.

FYI, I just copied you on a patch that enables us to easily support
all 3 options.


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 08/17] qapi: Plumb in 'boxed' to qapi generator lower levels
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 08/17] qapi: Plumb in 'boxed' to qapi generator lower levels Eric Blake
@ 2016-07-14 14:17   ` Markus Armbruster
  0 siblings, 0 replies; 31+ messages in thread
From: Markus Armbruster @ 2016-07-14 14:17 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The next patch will add support for passing a qapi union type
> as the 'data' of a command.  But to do that, the user function
> for implementing the command, as called by the generated
> marshal command, must take the corresponding C struct as a
> single boxed pointer, rather than a breakdown into one
> parameter per member.  Even without a union, being able to use
> a C struct rather than a list of parameters can make it much
> easier to handle coding with QAPI.
>
> This patch adds the internal plumbing of a 'boxed' flag
> associated with each command and event.  In several cases,
> this means adding indentation, with one new dead branch and
> the remaining branch being the original code more deeply
> nested; this was done so that the new implementation in the
> next patch is easier to review without also being mixed with
> indentation changes.
>
> For this patch, no behavior or generated output changes, other
> than the testsuite outputting the value of the new flag
> (always False for now).
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: change title, s/box/boxed/
> v8: improve commit message, defer some implementation, rebase without
> gen_err_check()
> v7: rebase to master
> v6: rebase to earlier changes
[...]
> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
> index e0f926b..60e9877 100644
> --- a/scripts/qapi-introspect.py
> +++ b/scripts/qapi-introspect.py
> @@ -154,14 +154,14 @@ const char %(c_name)s[] = %(c_string)s;
>                                      for m in variants.variants]})
>
>      def visit_command(self, name, info, arg_type, ret_type,
> -                      gen, success_response):
> +                      gen, success_response, box):

You missed a box here.  Can touch up on commit.

>          arg_type = arg_type or self._schema.the_empty_object_type
>          ret_type = ret_type or self._schema.the_empty_object_type
>          self._gen_json(name, 'command',
>                         {'arg-type': self._use_type(arg_type),
>                          'ret-type': self._use_type(ret_type)})
>
> -    def visit_event(self, name, info, arg_type):
> +    def visit_event(self, name, info, arg_type, box):

Likewise.

>          arg_type = arg_type or self._schema.the_empty_object_type
>          self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
>
[...]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion
  2016-07-14 14:16           ` Daniel P. Berrange
@ 2016-07-14 14:25             ` Eric Blake
  0 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14 14:25 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: qemu-devel, armbru, Michael Roth, Andreas Färber

[-- Attachment #1: Type: text/plain, Size: 1391 bytes --]

On 07/14/2016 08:16 AM, Daniel P. Berrange wrote:
> On Thu, Jul 14, 2016 at 08:04:29AM -0600, Eric Blake wrote:
>> On 07/14/2016 07:03 AM, Daniel P. Berrange wrote:
>>

>> 3 is a superset of 2, and your command-line conversion is the only case
>> where we can achieve 2.  That is, code dealing with QMP can only choose
>> between 1 and 3, based on whether the QAPI .json file used
>> 'autocast':true for back-compat reasons (the only candidates are
>> 'netdev_add' and 'device_add').  And code dealing with command line
>> parsing can only choose 2 (QemuOpts is string-only), but parsing
>> string-only via 2 is no different than the result achieved from parsing
>> strongly-typed with string fallback via 3.
>>
>> I still don't buy the fact that we need a string-only parser at the
>> moment, but it would not be hard to change the 'bool autocast' into a
>> tri-state enum, and then make the implementation specifically honor 1,
>> 2, or 3 based on the enum value.
> 
> FYI, I just copied you on a patch that enables us to easily support
> all 3 options.

Yours achieves that by having three separate constructors, instead of
one constructor with a tri-state enum parameter.  Either approach works;
Markus, do you have any opinions on which looks nicer?

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 09/17] qapi: Implement boxed types for commands/events
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 09/17] qapi: Implement boxed types for commands/events Eric Blake
@ 2016-07-14 14:26   ` Markus Armbruster
  2016-07-14 16:23     ` Eric Blake
  0 siblings, 1 reply; 31+ messages in thread
From: Markus Armbruster @ 2016-07-14 14:26 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Turn on the ability to pass command and event arguments in
> a single boxed parameter, which must name a non-empty type
> (although the type can be a struct with all optional members).
> For structs, it makes it possible to pass a single qapi type
> instead of a breakout of all struct members (useful if the
> arguments are already in a struct or if the number of members
> is large); for other complex types, it is now possible to use
> a union or alternate as the data for a command or event.
>
> The empty type may be technically feasible if needed down the
> road, but it's easier to forbid it now and relax things to allow
> it later, than it is to allow it now and have to special case
> how the generated 'q_empty' type is handled (see commit 7ce106a9
> for reasons why nothing is generated for the empty type).  An
> alternate type is never considered empty, but now that a boxed
> type can be either an object or an alternate, we have to provide
> a trivial QAPISchemaAlternateType.is_empty().  The new call to
> arg_type.is_empty() during QAPISchemaCommand.check() requires
> that we first check the type in question; but there is no chance
> of introducing a cycle since objects do not refer back to commands.
>
> We still have a split in syntax checking between ad-hoc parsing
> up front (merely validates that 'boxed' has a sane value) and
> during .check() methods (if 'boxed' is set, then 'data' must name
> a non-empty user-defined type).
>
> Generated code is unchanged, as long as no client uses the
> new feature.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: s/box/boxed/, formatting tweak, commit message improvements,
> restore assertions rather than relying on implicit checking
> v8: forbid empty type, allow alternates, improve docs
> v7: rebase to latest
> v6: retitle, rebase, and merge v5 40/46 and 41/46 into one patch
> ---
>  scripts/qapi.py                         | 63 ++++++++++++++++++++++++++-------
>  scripts/qapi-commands.py                |  3 +-
>  scripts/qapi-event.py                   |  5 ++-
>  tests/test-qmp-commands.c               |  8 +++++
>  docs/qapi-code-gen.txt                  | 27 ++++++++++++--
>  tests/Makefile.include                  |  5 +++
>  tests/qapi-schema/args-bad-box.err      |  1 +
>  tests/qapi-schema/args-bad-box.exit     |  1 +
>  tests/qapi-schema/args-bad-box.json     |  2 ++
>  tests/qapi-schema/args-bad-box.out      |  0
>  tests/qapi-schema/args-box-anon.err     |  1 +
>  tests/qapi-schema/args-box-anon.exit    |  1 +
>  tests/qapi-schema/args-box-anon.json    |  2 ++
>  tests/qapi-schema/args-box-anon.out     |  0
>  tests/qapi-schema/args-box-empty.err    |  1 +
>  tests/qapi-schema/args-box-empty.exit   |  1 +
>  tests/qapi-schema/args-box-empty.json   |  3 ++
>  tests/qapi-schema/args-box-empty.out    |  0
>  tests/qapi-schema/args-box-string.err   |  1 +
>  tests/qapi-schema/args-box-string.exit  |  1 +
>  tests/qapi-schema/args-box-string.json  |  2 ++
>  tests/qapi-schema/args-box-string.out   |  0
>  tests/qapi-schema/args-union.err        |  2 +-
>  tests/qapi-schema/args-union.json       |  3 +-
>  tests/qapi-schema/event-box-empty.err   |  1 +
>  tests/qapi-schema/event-box-empty.exit  |  1 +
>  tests/qapi-schema/event-box-empty.json  |  2 ++
>  tests/qapi-schema/event-box-empty.out   |  0
>  tests/qapi-schema/qapi-schema-test.json |  4 +++
>  tests/qapi-schema/qapi-schema-test.out  |  8 +++++
>  30 files changed, 129 insertions(+), 20 deletions(-)
>  create mode 100644 tests/qapi-schema/args-bad-box.err
>  create mode 100644 tests/qapi-schema/args-bad-box.exit
>  create mode 100644 tests/qapi-schema/args-bad-box.json
>  create mode 100644 tests/qapi-schema/args-bad-box.out
>  create mode 100644 tests/qapi-schema/args-box-anon.err
>  create mode 100644 tests/qapi-schema/args-box-anon.exit
>  create mode 100644 tests/qapi-schema/args-box-anon.json
>  create mode 100644 tests/qapi-schema/args-box-anon.out
>  create mode 100644 tests/qapi-schema/args-box-empty.err
>  create mode 100644 tests/qapi-schema/args-box-empty.exit
>  create mode 100644 tests/qapi-schema/args-box-empty.json
>  create mode 100644 tests/qapi-schema/args-box-empty.out
>  create mode 100644 tests/qapi-schema/args-box-string.err
>  create mode 100644 tests/qapi-schema/args-box-string.exit
>  create mode 100644 tests/qapi-schema/args-box-string.json
>  create mode 100644 tests/qapi-schema/args-box-string.out
>  create mode 100644 tests/qapi-schema/event-box-empty.err
>  create mode 100644 tests/qapi-schema/event-box-empty.exit
>  create mode 100644 tests/qapi-schema/event-box-empty.json
>  create mode 100644 tests/qapi-schema/event-box-empty.out

I'm inclined to change box to boxed in the test file names on commit.
What do you think?

[...]
> diff --git a/tests/qapi-schema/args-union.json b/tests/qapi-schema/args-union.json
> index 7bdcbb7..c0ce091 100644
> --- a/tests/qapi-schema/args-union.json
> +++ b/tests/qapi-schema/args-union.json
> @@ -1,4 +1,3 @@
> -# we do not allow union arguments
> -# TODO should we support this?
> +# use of union arguments requires 'box':true

You missed a box here.  Can touch up on commit.

>  { 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } }
>  { 'command': 'oops', 'data': 'Uni' }
[...]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 09/17] qapi: Implement boxed types for commands/events
  2016-07-14 14:26   ` Markus Armbruster
@ 2016-07-14 16:23     ` Eric Blake
  0 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14 16:23 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1839 bytes --]

On 07/14/2016 08:26 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Turn on the ability to pass command and event arguments in
>> a single boxed parameter, which must name a non-empty type
>> (although the type can be a struct with all optional members).
>> For structs, it makes it possible to pass a single qapi type
>> instead of a breakout of all struct members (useful if the
>> arguments are already in a struct or if the number of members
>> is large); for other complex types, it is now possible to use
>> a union or alternate as the data for a command or event.
>>
...
>>  create mode 100644 tests/qapi-schema/args-box-string.json
>>  create mode 100644 tests/qapi-schema/args-box-string.out
>>  create mode 100644 tests/qapi-schema/event-box-empty.err
>>  create mode 100644 tests/qapi-schema/event-box-empty.exit
>>  create mode 100644 tests/qapi-schema/event-box-empty.json
>>  create mode 100644 tests/qapi-schema/event-box-empty.out
> 
> I'm inclined to change box to boxed in the test file names on commit.
> What do you think?
> 

Fine by me ('git mv' is up to the task; you'll have to tweak the
Makefile, but that's not too onerous).

> [...]
>> diff --git a/tests/qapi-schema/args-union.json b/tests/qapi-schema/args-union.json
>> index 7bdcbb7..c0ce091 100644
>> --- a/tests/qapi-schema/args-union.json
>> +++ b/tests/qapi-schema/args-union.json
>> @@ -1,4 +1,3 @@
>> -# we do not allow union arguments
>> -# TODO should we support this?
>> +# use of union arguments requires 'box':true
> 
> You missed a box here.  Can touch up on commit.

Thanks for spotting them all.  Yeah, the stragglers are trivial enough
that you can fix on commit.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F)
  2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (16 preceding siblings ...)
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 17/17] qapi: Restore autocast behavior in 'netdev_add' Eric Blake
@ 2016-07-14 16:42 ` Markus Armbruster
  2016-07-15  7:13   ` Markus Armbruster
  17 siblings, 1 reply; 31+ messages in thread
From: Markus Armbruster @ 2016-07-14 16:42 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Eric Blake <eblake@redhat.com> writes:

> It's time to expose QMP 'netdev_add' through introspection, and
> to add boxed commands/events so that we can drastically reduce
> the number of C parameters needed to implement a command that
> matches a large QAPI type.
>
> Prerequistes:
> Markus' qapi-next branch (weak; series also applies on master)
>
> available as a tag at:
> git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv9f
> or as part of my qapi branch:
> git fetch git://repo.or.cz/qemu/ericb.git qapi
>
> v8 was here:
> https://lists.gnu.org/archive/html/qemu-devel/2016-07/msg00302.html
>
> Since then, I've addressed Markus' comments:
> - s/box/boxed/
> - fix a useless Python override of c_name()
> - comment and formatting tweaks
> - defer event collision prevention until later; for 2.7, we are
> focusing on the bare minimum needed to take advantage of boxing
> - implement a promised followup that allows netdev_add to still
> parse strings the way it did with QemuOpts (thanks Dan for starting
> that effort)
>
> The diffstat from v8 looks big, but a lot of it is mechanical
> due to the spelling change.  It's late at night, and we're close
> to hard freeze, so to maximize review time I'm posting now; if I
> weren't feeling quite as rushed, I might would have swapped the
> order of 14 vs. 15-17 to avoid even a temporary behavior change
> in netdev_add.  On the other hand, 14 has had review, while 15-17
> are effectively new, and it is feasible that we may be comfortable
> enough with the new type-safety constraints of 14 to not want
> to bother with 15-17 loosening things back up, given that we are
> already in soft freeze.

PATCH 01-13 with the few overlooked instanced of 'box' corrected:
Reviewed-by: Markus Armbruster <armbru@redhat.com>

PATCH 14-17 need careful consideration.  It's very late in the cycle,
which means there's little time to correct mistakes before they become
ABI.  Risks upsetting the release with last minute corrections.

The safest option is to punt to the next cycle.  Would be a shame,
though.

I need to read your discussion of the backward compatibility patches,
and the patches themselves, carefully.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH v9 11/17] block: Simplify drive-mirror
  2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 11/17] block: Simplify drive-mirror Eric Blake
@ 2016-07-14 22:28   ` Eric Blake
  2016-07-14 22:37     ` [Qemu-devel] [PATCH v9.5 " Eric Blake
  0 siblings, 1 reply; 31+ messages in thread
From: Eric Blake @ 2016-07-14 22:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, open list:Block layer core, armbru, Luiz Capitulino,
	Max Reitz

[-- Attachment #1: Type: text/plain, Size: 693 bytes --]

On 07/13/2016 09:50 PM, Eric Blake wrote:
> Now that we can support boxed commands, use it to greatly
> reduce the number of parameters (and likelihood of getting
> out of sync) when adjusting drive-mirror parameters.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: John Snow <jsnow@redhat.com>
> 
> ---
> v9: s/box/boxed/, trivial enough to keep R-b
> v8: rebase, drop stale sentence in docs, don't rearrange initialiation
> v7: new patch

You'll get a merge conflict for this one in comparison to current
qemu.git. I'll repost an updated version.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Qemu-devel] [PATCH v9.5 11/17] block: Simplify drive-mirror
  2016-07-14 22:28   ` Eric Blake
@ 2016-07-14 22:37     ` Eric Blake
  0 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2016-07-14 22:37 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, jsnow, berto, Kevin Wolf, Max Reitz, Luiz Capitulino,
	open list:Block layer core

Now that we can support boxed commands, use it to greatly
reduce the number of parameters (and likelihood of getting
out of sync) when adjusting drive-mirror parameters.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>

---
v9.5: Rebase to commit 71aa9867 adding job-id
v9: s/box/boxed/, trivial enough to keep R-b
v8: rebase, drop stale sentence in docs, don't rearrange initialiation
v7: new patch
---
 qapi/block-core.json | 20 +++++++++++---
 blockdev.c           | 78 +++++++++++++++++++++++-----------------------------
 hmp.c                | 25 ++++++++---------
 3 files changed, 61 insertions(+), 62 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index 3d4b071..f4f3ef9 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1120,6 +1120,21 @@
 #
 # Start mirroring a block device's writes to a new destination.
 #
+# See DriveMirror for parameter descriptions
+#
+# Returns: nothing on success
+#          If @device is not a valid block device, DeviceNotFound
+#
+# Since 1.3
+##
+{ 'command': 'drive-mirror', 'boxed': true,
+  'data': 'DriveMirror' }
+
+##
+# DriveMirror
+#
+# A set of parameters describing drive mirror setup.
+#
 # @job-id: #optional identifier for the newly-created block job. If
 #          omitted, the device name will be used. (Since 2.7)
 #
@@ -1169,12 +1184,9 @@
 #         written. Both will result in identical contents.
 #         Default is true. (Since 2.4)
 #
-# Returns: nothing on success
-#          If @device is not a valid block device, DeviceNotFound
-#
 # Since 1.3
 ##
-{ 'command': 'drive-mirror',
+{ 'struct': 'DriveMirror',
   'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
             '*format': 'str', '*node-name': 'str', '*replaces': 'str',
             'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
diff --git a/blockdev.c b/blockdev.c
index c5443c1..eafeba9 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3466,19 +3466,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
                  block_job_cb, bs, errp);
 }

-void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
-                      const char *target, bool has_format, const char *format,
-                      bool has_node_name, const char *node_name,
-                      bool has_replaces, const char *replaces,
-                      enum MirrorSyncMode sync,
-                      bool has_mode, enum NewImageMode mode,
-                      bool has_speed, int64_t speed,
-                      bool has_granularity, uint32_t granularity,
-                      bool has_buf_size, int64_t buf_size,
-                      bool has_on_source_error, BlockdevOnError on_source_error,
-                      bool has_on_target_error, BlockdevOnError on_target_error,
-                      bool has_unmap, bool unmap,
-                      Error **errp)
+void qmp_drive_mirror(DriveMirror *arg, Error **errp)
 {
     BlockDriverState *bs;
     BlockBackend *blk;
@@ -3489,11 +3477,12 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
     QDict *options = NULL;
     int flags;
     int64_t size;
+    const char *format = arg->format;

-    blk = blk_by_name(device);
+    blk = blk_by_name(arg->device);
     if (!blk) {
         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-                  "Device '%s' not found", device);
+                  "Device '%s' not found", arg->device);
         return;
     }

@@ -3501,24 +3490,25 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
     aio_context_acquire(aio_context);

     if (!blk_is_available(blk)) {
-        error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+        error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, arg->device);
         goto out;
     }
     bs = blk_bs(blk);
-    if (!has_mode) {
-        mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+    if (!arg->has_mode) {
+        arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
     }

-    if (!has_format) {
-        format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
+    if (!arg->has_format) {
+        format = (arg->mode == NEW_IMAGE_MODE_EXISTING
+                  ? NULL : bs->drv->format_name);
     }

     flags = bs->open_flags | BDRV_O_RDWR;
     source = backing_bs(bs);
-    if (!source && sync == MIRROR_SYNC_MODE_TOP) {
-        sync = MIRROR_SYNC_MODE_FULL;
+    if (!source && arg->sync == MIRROR_SYNC_MODE_TOP) {
+        arg->sync = MIRROR_SYNC_MODE_FULL;
     }
-    if (sync == MIRROR_SYNC_MODE_NONE) {
+    if (arg->sync == MIRROR_SYNC_MODE_NONE) {
         source = bs;
     }

@@ -3528,18 +3518,18 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
         goto out;
     }

-    if (has_replaces) {
+    if (arg->has_replaces) {
         BlockDriverState *to_replace_bs;
         AioContext *replace_aio_context;
         int64_t replace_size;

-        if (!has_node_name) {
+        if (!arg->has_node_name) {
             error_setg(errp, "a node-name must be provided when replacing a"
                              " named node of the graph");
             goto out;
         }

-        to_replace_bs = check_to_replace_node(bs, replaces, &local_err);
+        to_replace_bs = check_to_replace_node(bs, arg->replaces, &local_err);

         if (!to_replace_bs) {
             error_propagate(errp, local_err);
@@ -3558,26 +3548,26 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
         }
     }

-    if (mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
+    if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
         backing_mode = MIRROR_SOURCE_BACKING_CHAIN;
     } else {
         backing_mode = MIRROR_OPEN_BACKING_CHAIN;
     }

-    if ((sync == MIRROR_SYNC_MODE_FULL || !source)
-        && mode != NEW_IMAGE_MODE_EXISTING)
+    if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source)
+        && arg->mode != NEW_IMAGE_MODE_EXISTING)
     {
         /* create new image w/o backing file */
         assert(format);
-        bdrv_img_create(target, format,
+        bdrv_img_create(arg->target, format,
                         NULL, NULL, NULL, size, flags, &local_err, false);
     } else {
-        switch (mode) {
+        switch (arg->mode) {
         case NEW_IMAGE_MODE_EXISTING:
             break;
         case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
             /* create new image with backing file */
-            bdrv_img_create(target, format,
+            bdrv_img_create(arg->target, format,
                             source->filename,
                             source->drv->format_name,
                             NULL, size, flags, &local_err, false);
@@ -3593,8 +3583,8 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
     }

     options = qdict_new();
-    if (has_node_name) {
-        qdict_put(options, "node-name", qstring_from_str(node_name));
+    if (arg->has_node_name) {
+        qdict_put(options, "node-name", qstring_from_str(arg->node_name));
     }
     if (format) {
         qdict_put(options, "driver", qstring_from_str(format));
@@ -3603,22 +3593,22 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
     /* Mirroring takes care of copy-on-write using the source's backing
      * file.
      */
-    target_bs = bdrv_open(target, NULL, options, flags | BDRV_O_NO_BACKING,
-                          errp);
+    target_bs = bdrv_open(arg->target, NULL, options,
+                          flags | BDRV_O_NO_BACKING, errp);
     if (!target_bs) {
         goto out;
     }

     bdrv_set_aio_context(target_bs, aio_context);

-    blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs,
-                           has_replaces, replaces, sync, backing_mode,
-                           has_speed, speed,
-                           has_granularity, granularity,
-                           has_buf_size, buf_size,
-                           has_on_source_error, on_source_error,
-                           has_on_target_error, on_target_error,
-                           has_unmap, unmap,
+    blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs,
+                           arg->has_replaces, arg->replaces, arg->sync,
+                           backing_mode, arg->has_speed, arg->speed,
+                           arg->has_granularity, arg->granularity,
+                           arg->has_buf_size, arg->buf_size,
+                           arg->has_on_source_error, arg->on_source_error,
+                           arg->has_on_target_error, arg->on_target_error,
+                           arg->has_unmap, arg->unmap,
                            &local_err);
     bdrv_unref(target_bs);
     error_propagate(errp, local_err);
diff --git a/hmp.c b/hmp.c
index ea17d49..cc2056e 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1077,31 +1077,28 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict)

 void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
 {
-    const char *device = qdict_get_str(qdict, "device");
     const char *filename = qdict_get_str(qdict, "target");
     const char *format = qdict_get_try_str(qdict, "format");
     bool reuse = qdict_get_try_bool(qdict, "reuse", false);
     bool full = qdict_get_try_bool(qdict, "full", false);
-    enum NewImageMode mode;
     Error *err = NULL;
+    DriveMirror mirror = {
+        .device = (char *)qdict_get_str(qdict, "device"),
+        .target = (char *)filename,
+        .has_format = !!format,
+        .format = (char *)format,
+        .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
+        .has_mode = true,
+        .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS,
+        .unmap = true,
+    };

     if (!filename) {
         error_setg(&err, QERR_MISSING_PARAMETER, "target");
         hmp_handle_error(mon, &err);
         return;
     }
-
-    if (reuse) {
-        mode = NEW_IMAGE_MODE_EXISTING;
-    } else {
-        mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
-    }
-
-    qmp_drive_mirror(false, NULL, device, filename, !!format, format,
-                     false, NULL, false, NULL,
-                     full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
-                     true, mode, false, 0, false, 0, false, 0,
-                     false, 0, false, 0, false, true, &err);
+    qmp_drive_mirror(&mirror, &err);
     hmp_handle_error(mon, &err);
 }

-- 
2.5.5

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F)
  2016-07-14 16:42 ` [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Markus Armbruster
@ 2016-07-15  7:13   ` Markus Armbruster
  0 siblings, 0 replies; 31+ messages in thread
From: Markus Armbruster @ 2016-07-15  7:13 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Markus Armbruster <armbru@redhat.com> writes:

> Eric Blake <eblake@redhat.com> writes:
>
>> It's time to expose QMP 'netdev_add' through introspection, and
>> to add boxed commands/events so that we can drastically reduce
>> the number of C parameters needed to implement a command that
>> matches a large QAPI type.
>>
>> Prerequistes:
>> Markus' qapi-next branch (weak; series also applies on master)
>>
>> available as a tag at:
>> git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv9f
>> or as part of my qapi branch:
>> git fetch git://repo.or.cz/qemu/ericb.git qapi
>>
>> v8 was here:
>> https://lists.gnu.org/archive/html/qemu-devel/2016-07/msg00302.html
>>
>> Since then, I've addressed Markus' comments:
>> - s/box/boxed/
>> - fix a useless Python override of c_name()
>> - comment and formatting tweaks
>> - defer event collision prevention until later; for 2.7, we are
>> focusing on the bare minimum needed to take advantage of boxing
>> - implement a promised followup that allows netdev_add to still
>> parse strings the way it did with QemuOpts (thanks Dan for starting
>> that effort)
>>
>> The diffstat from v8 looks big, but a lot of it is mechanical
>> due to the spelling change.  It's late at night, and we're close
>> to hard freeze, so to maximize review time I'm posting now; if I
>> weren't feeling quite as rushed, I might would have swapped the
>> order of 14 vs. 15-17 to avoid even a temporary behavior change
>> in netdev_add.  On the other hand, 14 has had review, while 15-17
>> are effectively new, and it is feasible that we may be comfortable
>> enough with the new type-safety constraints of 14 to not want
>> to bother with 15-17 loosening things back up, given that we are
>> already in soft freeze.
>
> PATCH 01-13 with the few overlooked instanced of 'box' corrected:
> Reviewed-by: Markus Armbruster <armbru@redhat.com>

Pushed to qapi-next with PATCH 11 replaced by your update.

> PATCH 14-17 need careful consideration.  It's very late in the cycle,
> which means there's little time to correct mistakes before they become
> ABI.  Risks upsetting the release with last minute corrections.

Pushed to qapi-not-next for now.

> The safest option is to punt to the next cycle.  Would be a shame,
> though.
>
> I need to read your discussion of the backward compatibility patches,
> and the patches themselves, carefully.

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2016-07-15  7:13 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-07-14  3:50 [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 01/17] net: use Netdev instead of NetClientOptions in client init Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 02/17] qapi: Require all branches of flat union enum to be covered Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 03/17] qapi: Special case c_name() for empty type Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 04/17] qapi: Hide tag_name data member of variants Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 05/17] qapi: Add type.is_empty() helper Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 06/17] qapi: Drop useless gen_err_check() Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 07/17] qapi-event: Simplify visit of non-implicit data Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 08/17] qapi: Plumb in 'boxed' to qapi generator lower levels Eric Blake
2016-07-14 14:17   ` Markus Armbruster
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 09/17] qapi: Implement boxed types for commands/events Eric Blake
2016-07-14 14:26   ` Markus Armbruster
2016-07-14 16:23     ` Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 10/17] block: Simplify block_set_io_throttle Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 11/17] block: Simplify drive-mirror Eric Blake
2016-07-14 22:28   ` Eric Blake
2016-07-14 22:37     ` [Qemu-devel] [PATCH v9.5 " Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 12/17] qapi: Change Netdev into a flat union Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 13/17] net: Use correct type for bool flag Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 14/17] net: Complete qapi-fication of netdev_add Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 15/17] option: make parse_option_bool/number non-static Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion Eric Blake
2016-07-14  8:04   ` Daniel P. Berrange
2016-07-14 12:43     ` Eric Blake
2016-07-14 13:03       ` Daniel P. Berrange
2016-07-14 14:04         ` Eric Blake
2016-07-14 14:16           ` Daniel P. Berrange
2016-07-14 14:25             ` Eric Blake
2016-07-14  3:50 ` [Qemu-devel] [PATCH v9 17/17] qapi: Restore autocast behavior in 'netdev_add' Eric Blake
2016-07-14 16:42 ` [Qemu-devel] [PATCH for-2.7 v9 00/17] qapi netdev_add introspection (post-introspection cleanups subset F) Markus Armbruster
2016-07-15  7:13   ` 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).