All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F)
@ 2016-07-03  2:58 Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 01/16] net: use Netdev instead of NetClientOptions in client init Eric Blake
                   ` (16 more replies)
  0 siblings, 17 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

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 (including my clone visitor)

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

v7 was here:
https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg03569.html

Since then, I've addressed a lot of Markus' comments:
- drop 1/15
- split out a couple of patches
- wording tweaks, more justifications in some commit messages
- rebase to master
- allow boxed alternates, in addition to boxed unions
- forbid empty anonymous branches for now

001/16:[----] [--] 'net: use Netdev instead of NetClientOptions in client init'
002/16:[----] [--] 'qapi: Require all branches of flat union enum to be covered'
003/16:[----] [--] 'qapi: Hide tag_name data member of variants'
004/16:[0006] [FC] 'qapi: Add type.is_empty() helper'
005/16:[down] 'qapi: Drop useless gen_err_check()'
006/16:[down] 'qapi-event: Simplify visit of non-implicit data'
007/16:[0028] [FC] 'qapi: Plumb in 'box' to qapi generator lower levels'
008/16:[0121] [FC] 'qapi: Implement boxed types for commands/events'
009/16:[0002] [FC] 'block: Simplify block_set_io_throttle'
010/16:[0023] [FC] 'block: Simplify drive-mirror'
011/16:[0026] [FC] 'qapi-event: Reduce chance of collision with event data'
012/16:[0042] [FC] 'qapi: Change Netdev into a flat union'
013/16:[----] [--] 'net: Use correct type for bool flag'
014/16:[0008] [FC] 'net: Complete qapi-fication of netdev_add'
015/16:[0037] [FC] 'qapi: Allow anonymous branch types in flat union'
016/16:[----] [--] 'schema: Drop pointless empty type CpuInfoOther'

Other patches on my QAPI queue:
- posted, but needs another respin and might not make 2.7:
JSON output visitor
- not posted, and therefore most likely not 2.7 material:
anything we need to do to make 'blockdev-add' work smoothly
(we may want to allow a flat union with a default value for its
discriminator, and/or add a way to express mutually-exclusive
members in a qapi dict)

Other potential general QAPI patches, but not necessarily that I will write:
- Marc-Andre's cleanups
- convert more clients to use boxed commands
- get rid of has_FOO when FOO is pointer
- support default values (and get rid of has_FOO when FOO has default)
- QAPI-fy device_add
- ???

Eric Blake (15):
  qapi: Require all branches of flat union enum to be covered
  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 'box' to qapi generator lower levels
  qapi: Implement boxed types for commands/events
  block: Simplify block_set_io_throttle
  block: Simplify drive-mirror
  qapi-event: Reduce chance of collision with event data
  qapi: Change Netdev into a flat union
  net: Use correct type for bool flag
  net: Complete qapi-fication of netdev_add
  qapi: Allow anonymous branch types in flat union
  schema: Drop pointless empty type CpuInfoOther

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

 qapi-schema.json                                   |  76 +++++----
 qapi/block-core.json                               |  40 ++++-
 scripts/qapi.py                                    | 153 ++++++++++++-----
 scripts/qapi-commands.py                           |  31 ++--
 scripts/qapi-event.py                              |  71 +++++---
 scripts/qapi-introspect.py                         |   4 +-
 scripts/qapi-types.py                              |   2 +-
 scripts/qapi-visit.py                              |  22 ++-
 include/net/net.h                                  |   8 +-
 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 +--
 tests/test-qmp-commands.c                          |   8 +
 docs/qapi-code-gen.txt                             |  27 ++-
 qmp-commands.hx                                    |   2 +-
 tests/Makefile.include                             |   7 +
 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/flat-union-inline.err            |   2 +-
 tests/qapi-schema/flat-union-inline.json           |   5 +-
 tests/qapi-schema/ident-with-escape.out            |   2 +-
 tests/qapi-schema/indented-expr.out                |   4 +-
 tests/qapi-schema/qapi-schema-test.json            |  12 +-
 tests/qapi-schema/qapi-schema-test.out             |  44 ++++-
 tests/qapi-schema/test-qapi.py                     |  11 +-
 tests/qapi-schema/union-inline.err                 |   1 +
 tests/qapi-schema/union-inline.exit                |   1 +
 tests/qapi-schema/union-inline.json                |   4 +
 tests/qapi-schema/union-inline.out                 |   0
 100 files changed, 732 insertions(+), 526 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
 create mode 100644 tests/qapi-schema/union-inline.err
 create mode 100644 tests/qapi-schema/union-inline.exit
 create mode 100644 tests/qapi-schema/union-inline.json
 create mode 100644 tests/qapi-schema/union-inline.out

-- 
2.5.5

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

* [Qemu-devel] [PATCH v8 01/16] net: use Netdev instead of NetClientOptions in client init
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 02/16] qapi: Require all branches of flat union enum to be covered Eric Blake
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, 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>

---
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 49817c7..08e8350 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;
@@ -717,7 +717,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;
@@ -729,8 +729,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 92f4cfd..a650296 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -333,15 +333,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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 02/16] qapi: Require all branches of flat union enum to be covered
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 01/16] net: use Netdev instead of NetClientOptions in client init Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 03/16] qapi: Hide tag_name data member of variants Eric Blake
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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>

---
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 1163807..df232f6 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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 03/16] qapi: Hide tag_name data member of variants
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 01/16] net: use Netdev instead of NetClientOptions in client init Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 02/16] qapi: Require all branches of flat union enum to be covered Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 04/16] qapi: Add type.is_empty() helper Eric Blake
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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>

---
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 0635bbb..47be1c8 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1092,7 +1092,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

@@ -1102,8 +1102,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)
@@ -1133,7 +1133,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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 04/16] qapi: Add type.is_empty() helper
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (2 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 03/16] qapi: Hide tag_name data member of variants Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 05/16] qapi: Drop useless gen_err_check() Eric Blake
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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 47be1c8..a99a74a 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):
         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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 05/16] qapi: Drop useless gen_err_check()
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (3 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 04/16] qapi: Add type.is_empty() helper Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 06/16] qapi-event: Simplify visit of non-implicit data Eric Blake
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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>

---
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 a99a74a..86bd780 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1655,14 +1655,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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 06/16] qapi-event: Simplify visit of non-implicit data
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (4 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 05/16] qapi: Drop useless gen_err_check() Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 07/16] qapi: Plumb in 'box' to qapi generator lower levels Eric Blake
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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>

---
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..ef4a320 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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 07/16] qapi: Plumb in 'box' to qapi generator lower levels
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (5 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 06/16] qapi-event: Simplify visit of non-implicit data Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 08/16] qapi: Implement boxed types for commands/events Eric Blake
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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 'box' 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>

---
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 86bd780..f5e7697 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, box):
         pass

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


@@ -1164,7 +1164,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,
+                 box):
         QAPISchemaEntity.__init__(self, name, info)
         assert not arg_type or isinstance(arg_type, str)
         assert not ret_type or isinstance(ret_type, str)
@@ -1174,12 +1175,14 @@ class QAPISchemaCommand(QAPISchemaEntity):
         self.ret_type = None
         self.gen = gen
         self.success_response = success_response
+        self.box = box

     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.box                 # not implemented
         if self._ret_type_name:
             self.ret_type = schema.lookup_type(self._ret_type_name)
             assert isinstance(self.ret_type, QAPISchemaType)
@@ -1187,24 +1190,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.box)


 class QAPISchemaEvent(QAPISchemaEntity):
-    def __init__(self, name, info, arg_type):
+    def __init__(self, name, info, arg_type, box):
         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.box = box

     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.box                 # 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.box)


 class QAPISchema(object):
@@ -1380,6 +1385,7 @@ class QAPISchema(object):
         rets = expr.get('returns')
         gen = expr.get('gen', True)
         success_response = expr.get('success-response', True)
+        box = expr.get('box', False)
         if isinstance(data, OrderedDict):
             data = self._make_implicit_object_type(
                 name, info, 'arg', self._make_members(data, info))
@@ -1387,15 +1393,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, box))

     def _def_event(self, expr, info):
         name = expr['event']
         data = expr.get('data')
+        box = expr.get('box', 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, box))

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


-def gen_params(arg_type, extra):
+def gen_params(arg_type, box, 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 box:
+        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..598c4c7 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, box, 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, box, 'Error **errp'))


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

     argstr = ''
-    if arg_type:
+    if box:
+        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, box, 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, box, 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, box):
         if not gen:
             return
-        self.decl += gen_command_decl(name, arg_type, ret_type)
+        self.decl += gen_command_decl(name, arg_type, box, 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, box, 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 ef4a320..024be4d 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, box):
     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, box, 'Error **errp')}


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

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


 # 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, box):
     # 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, box))

     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, box):
+        self.decl += gen_event_send_decl(name, arg_type, box)
+        self.defn += gen_event_send(name, arg_type, box)
         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..5ca8fd2 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
+   box=False
 object q_empty
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index 382ce2f..27d45f3 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 box=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..a7cd10f 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 box=False
 object q_empty
 command zwei None -> None
-   gen=True success_response=True
+   gen=True success_response=True box=False
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index f34ecc7..754a926 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
+   box=False
 event EVENT_B None
+   box=False
 event EVENT_C q_obj_EVENT_C-arg
+   box=False
 event EVENT_D q_obj_EVENT_D-arg
+   box=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
+   box=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 box=False
 command guest-get-time q_obj_guest-get-time-arg -> int
-   gen=True success_response=True
+   gen=True success_response=True box=False
 command guest-sync q_obj_guest-sync-arg -> any
-   gen=True success_response=True
+   gen=True success_response=True box=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 box=False
 command user_def_cmd0 Empty2 -> Empty2
-   gen=True success_response=True
+   gen=True success_response=True box=False
 command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
-   gen=True success_response=True
+   gen=True success_response=True box=False
 command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
-   gen=True success_response=True
+   gen=True success_response=True box=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index bedd145..d82af59 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, box):
         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 box=%s' % (gen, success_response,
+                                                        box)

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

     @staticmethod
     def _print_variants(variants):
-- 
2.5.5

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

* [Qemu-devel] [PATCH v8 08/16] qapi: Implement boxed types for commands/events
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (6 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 07/16] qapi: Plumb in 'box' to qapi generator lower levels Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-07 10:52   ` Markus Armbruster
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 09/16] block: Simplify block_set_io_throttle Eric Blake
                   ` (8 subsequent siblings)
  16 siblings, 1 reply; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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.

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

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

---
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, 128 insertions(+), 21 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 f5e7697..48263c4 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']
+    box = expr.get('box', False)

+    args_meta = ['struct']
+    if box:
+        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 box, 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']
+    box = expr.get('box', False)
+    meta = ['struct']

+    if box:
+        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 box, 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 == 'box' 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', 'box'])
             add_name(expr['command'], info, 'command')
         elif 'event' in expr:
-            check_keys(expr_elem, 'event', [], ['data'])
+            check_keys(expr_elem, 'event', [], ['data', 'box'])
             add_name(expr['event'], info, 'event')
         else:
             raise QAPIExprError(expr_elem['info'],
@@ -1162,6 +1174,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,
@@ -1180,9 +1195,18 @@ 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.box                 # not implemented
+            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
+                    isinstance(self.arg_type, QAPISchemaAlternateType))
+            self.arg_type.check(schema)
+            if self.box:
+                if self.arg_type.is_empty():
+                    raise QAPIExprError(self.info,
+                                        "Cannot use 'box' with empty type")
+            else:
+                assert not self.arg_type.variants
+        elif self.box:
+            raise QAPIExprError(self.info,
+                                "Use of 'box' requires 'data'")
         if self._ret_type_name:
             self.ret_type = schema.lookup_type(self._ret_type_name)
             assert isinstance(self.ret_type, QAPISchemaType)
@@ -1204,9 +1228,18 @@ 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.box                 # not implemented
+            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
+                    isinstance(self.arg_type, QAPISchemaAlternateType))
+            self.arg_type.check(schema)
+            if self.box:
+                if self.arg_type.is_empty():
+                    raise QAPIExprError(self.info,
+                                        "Cannot use 'box' with empty type")
+            else:
+                assert not self.arg_type.variants
+        elif self.box:
+            raise QAPIExprError(self.info,
+                                "Use of 'box' requires 'data'")

     def visit(self, visitor):
         visitor.visit_event(self.name, self.info, self.arg_type, self.box)
@@ -1646,12 +1679,14 @@ extern const char *const %(c_name)s_lookup[];


 def gen_params(arg_type, box, extra):
-    if not arg_type:
+    if not arg_type or arg_type.is_empty():
+        assert not box
         return extra
     ret = ''
     sep = ''
     if box:
-        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 598c4c7..8d25701 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -30,7 +30,8 @@ def gen_call(name, arg_type, box, ret_type):

     argstr = ''
     if box:
-        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 024be4d..2cab588 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -79,7 +79,10 @@ def gen_event_send(name, arg_type, box):
     QObject *obj;
     Visitor *v;
 ''')
-        ret += gen_param_var(arg_type)
+        if not box:
+            ret += gen_param_var(arg_type)
+    else:
+        assert not box

     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..74171b7 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, '*box': 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 include a key 'box' 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,
+         '*box': 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 include a key
+'box' 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 df232f6..f73f350 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..16afe3c
--- /dev/null
+++ b/tests/qapi-schema/args-bad-box.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-bad-box.json:2: 'box' 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..8d5737a
--- /dev/null
+++ b/tests/qapi-schema/args-bad-box.json
@@ -0,0 +1,2 @@
+# 'box' should only appear with value true
+{ 'command': 'foo', 'box': 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..947e3c6
--- /dev/null
+++ b/tests/qapi-schema/args-box-anon.json
@@ -0,0 +1,2 @@
+# 'box' can only be used with named types
+{ 'command': 'foo', 'box': 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..9546a95
--- /dev/null
+++ b/tests/qapi-schema/args-box-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-box-empty.json:3: Cannot use 'box' 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..dda6355
--- /dev/null
+++ b/tests/qapi-schema/args-box-empty.json
@@ -0,0 +1,3 @@
+# 'box' requires a non-empty type
+{ 'struct': 'Empty', 'data': {} }
+{ 'command': 'foo', 'box': 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..85bafc2
--- /dev/null
+++ b/tests/qapi-schema/args-box-string.json
@@ -0,0 +1,2 @@
+# 'box' requires a complex (not built-in) type
+{ 'command': 'foo', 'box': 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..96b2bc3
--- /dev/null
+++ b/tests/qapi-schema/event-box-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/event-box-empty.json:2: Use of 'box' 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..0129918
--- /dev/null
+++ b/tests/qapi-schema/event-box-empty.json
@@ -0,0 +1,2 @@
+# 'box' requires a non-empty type
+{ 'event': 'FOO', 'box': 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..919dc097 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', 'box': true, 'data': 'UserDefZero' }
+{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'box': 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', 'box': true, 'data': 'UserDefZero' }
+{ 'event': 'EVENT_F', 'box': 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 754a926..e7ea242 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
    box=False
 event EVENT_D q_obj_EVENT_D-arg
    box=False
+event EVENT_E UserDefZero
+   box=True
+event EVENT_F UserDefAlternate
+   box=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 box=False
+command boxed-struct UserDefZero -> None
+   gen=True success_response=True box=True
+command boxed-union UserDefNativeListUnion -> None
+   gen=True success_response=True box=True
 command guest-get-time q_obj_guest-get-time-arg -> int
    gen=True success_response=True box=False
 command guest-sync q_obj_guest-sync-arg -> any
-- 
2.5.5

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

* [Qemu-devel] [PATCH v8 09/16] block: Simplify block_set_io_throttle
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (7 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 08/16] qapi: Implement boxed types for commands/events Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 10/16] block: Simplify drive-mirror Eric Blake
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, 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>

---
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 98a20d2..1bec29e 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', 'box': 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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 10/16] block: Simplify drive-mirror
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (8 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 09/16] block: Simplify block_set_io_throttle Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-05 20:27   ` [Qemu-devel] [Qemu-block] " John Snow
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 11/16] qapi-event: Reduce chance of collision with event data Eric Blake
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, 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>

---
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 1bec29e..b91b07c 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', 'box': 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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 11/16] qapi-event: Reduce chance of collision with event data
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (9 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 10/16] block: Simplify drive-mirror Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-07 11:37   ` Markus Armbruster
  2016-07-03  2:58   ` Eric Blake
                   ` (5 subsequent siblings)
  16 siblings, 1 reply; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

When an unboxed event has accompanying data, we are exposing all
of its members alongside our local variables in the generated
qapi_event_send_FOO() function.  So far, we haven't hit a
collision, but it may be a matter of time before someone wants
to name a QMP data element 'err' or similar.  We can separate
the names by making the public function (qapi_event_send_FOO())
a shell that boxes things up without having to worry about
collisions, then calls into a new worker function
(do_qapi_event_send_FOO()) that gets generated to look like what
we already output for boxed events.

There is still a chance for collision with 'errp' (if that happens,
tweak c_name() to rename a QMP member 'errp' into the C member
'q_errp'), and with 'param' (if that happens, tweak gen_event_send()
and gen_param_var() to name the temporary variable 'q_param').  But
with the division done here, the real worker function no longer has
to worry about collisions.

Adding the new wrapper now means that we need .c_type() for an
anonymous type given as data to an event, because that type is used
in the signature of the new helper function, so we have to relax
an assertion in QAPISchemaObjectType.

The generated file is unchanged for events without data, and for
boxed events; for unboxed events, the changes are as follows:

|-void qapi_event_send_migration(MigrationStatus status, Error **errp)
|+static void do_qapi_event_send_migration(q_obj_MIGRATION_arg *arg, Error **errp)
| {
|     QDict *qmp;
|     Error *err = NULL;
|     QMPEventFuncEmit emit;
|     QObject *obj;
|     Visitor *v;
|-    q_obj_MIGRATION_arg param = {
|-        status
|-    };
|
|     emit = qmp_event_get_func_emit();
|     if (!emit) {
...
|     if (err) {
|         goto out;
|     }
|-    visit_type_q_obj_MIGRATION_arg_members(v, &param, &err);
|+    visit_type_q_obj_MIGRATION_arg_members(v, arg, &err);
|     if (!err) {
|         visit_check_struct(v, &err);
...
|+void qapi_event_send_migration(MigrationStatus status, Error **errp)
|+{
|+    q_obj_MIGRATION_arg param = {
|+        status
|+    };
|+    do_qapi_event_send_migration(&param, errp);
|+}
|+

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

---
v8: don't open-code for boxed types, improve commit message,
s/intro/prefix/
v7: new patch
---
 scripts/qapi.py       |  1 -
 scripts/qapi-event.py | 45 ++++++++++++++++++++++++++-------------------
 2 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 48263c4..e051892 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1019,7 +1019,6 @@ class QAPISchemaObjectType(QAPISchemaType):
         return QAPISchemaType.c_name(self)

     def c_type(self):
-        assert not self.is_implicit()
         return c_name(self.name) + pointer_suffix

     def c_unboxed_type(self):
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 2cab588..b2da0a9 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -14,8 +14,13 @@
 from qapi import *


-def gen_event_send_proto(name, arg_type, box):
-    return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
+def gen_event_send_proto(name, arg_type, box, impl=False):
+    prefix='void '
+    if impl and arg_type and not box:
+        box = True
+        prefix='static void do_'
+    return '%(prefix)sqapi_event_send_%(c_name)s(%(param)s)' % {
+        'prefix': prefix,
         'c_name': c_name(name.lower()),
         'param': gen_params(arg_type, box, 'Error **errp')}

@@ -49,21 +54,12 @@ def gen_param_var(typ):

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


 def gen_event_send(name, arg_type, box):
-    # 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
-    # practice, we can rename our local variables with a leading _ prefix,
-    # or split the code into a wrapper function that creates a boxed
-    # 'param' object then calls another to do the real work.
+    if not arg_type or arg_type.is_empty():
+        assert not box
     ret = mcgen('''

 %(proto)s
@@ -72,17 +68,13 @@ def gen_event_send(name, arg_type, box):
     Error *err = NULL;
     QMPEventFuncEmit emit;
 ''',
-                proto=gen_event_send_proto(name, arg_type, box))
+                proto=gen_event_send_proto(name, arg_type, box, True))

     if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     QObject *obj;
     Visitor *v;
 ''')
-        if not box:
-            ret += gen_param_var(arg_type)
-    else:
-        assert not box

     ret += mcgen('''

@@ -112,7 +104,7 @@ def gen_event_send(name, arg_type, box):
     if (err) {
         goto out;
     }
-    visit_type_%(c_name)s_members(v, &param, &err);
+    visit_type_%(c_name)s_members(v, arg, &err);
     if (!err) {
         visit_check_struct(v, &err);
     }
@@ -144,6 +136,21 @@ out:
     QDECREF(qmp);
 }
 ''')
+
+    if arg_type and not box:
+        ret += mcgen('''
+
+%(proto)s
+{
+''',
+                     proto=gen_event_send_proto(name, arg_type, box))
+        ret += gen_param_var(arg_type)
+        ret += mcgen('''
+    do_qapi_event_send_%(c_name)s(&param, errp);
+}
+''',
+                     c_name=c_name(name.lower()))
+
     return ret


-- 
2.5.5

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

* [Qemu-devel] [PATCH v8 12/16] qapi: Change Netdev into a flat union
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
@ 2016-07-03  2:58   ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 02/16] qapi: Require all branches of flat union enum to be covered Eric Blake
                     ` (15 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, 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 ba3bf14..e8a015f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2805,16 +2805,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',
@@ -2849,23 +2865,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 891219a..579d31f 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 692283f..825f0ba 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -226,7 +226,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 50f4dcd..b3369ee 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 7e6a60a..528a919 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;
     }

@@ -1686,7 +1686,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 92236d3..f2c956b 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2089,7 +2089,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 ed09cdc..9498acd 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 08e8350..7e0ba46 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;
@@ -729,8 +729,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;

@@ -893,7 +893,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 a650296..7673a06 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)) {
@@ -162,20 +162,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,
@@ -206,7 +206,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);

@@ -340,8 +340,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] 28+ messages in thread

* [PATCH v8 12/16] qapi: Change Netdev into a flat union
@ 2016-07-03  2:58   ` Eric Blake
  0 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang, Vincenzo Maffione,
	Alexander Graf, Max Filippov, Gerd Hoffmann, Dmitry Fleytman,
	Edgar E. Iglesias, Rob Herring, Stefano Stabellini, armbru,
	Scott Feldman, Anthony Perard, Samuel Thibault, Jiri Pirko,
	Alistair Francis, Beniamino Galvani, open list:Musicpal,
	Jan Kiszka, Scott Wood, Giuseppe Lettieri, Luiz Capitulino

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 ba3bf14..e8a015f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2805,16 +2805,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',
@@ -2849,23 +2865,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 891219a..579d31f 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 692283f..825f0ba 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -226,7 +226,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 50f4dcd..b3369ee 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 7e6a60a..528a919 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;
     }

@@ -1686,7 +1686,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 92236d3..f2c956b 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2089,7 +2089,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 ed09cdc..9498acd 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 08e8350..7e0ba46 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;
@@ -729,8 +729,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;

@@ -893,7 +893,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 a650296..7673a06 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)) {
@@ -162,20 +162,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,
@@ -206,7 +206,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);

@@ -340,8 +340,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


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel

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

* [Qemu-devel] [PATCH v8 13/16] net: Use correct type for bool flag
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (11 preceding siblings ...)
  2016-07-03  2:58   ` Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 14/16] net: Complete qapi-fication of netdev_add Eric Blake
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Gerd Hoffmann, Jason Wang

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

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

---
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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 14/16] net: Complete qapi-fication of netdev_add
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (12 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 13/16] net: Use correct type for bool flag Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-07 12:57   ` Markus Armbruster
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 15/16] qapi: Allow anonymous branch types in flat union Eric Blake
                   ` (2 subsequent siblings)
  16 siblings, 1 reply; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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"}}

If that turns out to be problematic, we could resolve the problem
by either 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), or by using QAPI alternates.  But that work should be
in followup patches, if at all.

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>

---
v8: improve commit message, call out a change in QMP behavior,
inline netdev_add()
v7: no change
v6: don't lose qemu_opts handling
---
 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 e8a015f..3a7632b 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', 'box': 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] 28+ messages in thread

* [Qemu-devel] [PATCH v8 15/16] qapi: Allow anonymous branch types in flat union
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (13 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 14/16] net: Complete qapi-fication of netdev_add Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 16/16] schema: Drop pointless empty type CpuInfoOther Eric Blake
  2016-07-07 13:40 ` [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Markus Armbruster
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Recent commits added support for an anonymous type as the base
of a flat union; with a bit more work, we can also allow an
anonymous struct as a branch of a flat union.  This probably
most useful when a branch adds no additional members beyond the
common elements of the base (that is, the branch struct is '{}'),
but can be used for any struct in the same way we allow for an
anonymous struct for a command.

The generator has to do a bit of special-casing for the fact that
we do not emit a 'q_empty' struct nor a 'visit_type_q_empty_members()'
corresponding to the special 'q_empty' type (see commit 7ce106a9 for
more reasons why); but when the branch is truly empty, there's nothing
to do.  This is easiest by declaring that an empty type member of
another QAPI type is emitted as a 'char' rather than bothering with a
named type, via c_unboxed_type() (this change also affects empty types
used as the branch of a simple union).

In QAPISchemaObjectTypeMember._pretty_owner(), we want to distinguish
which branch introduces any duplicate member with the base of a flat
union. We can't use just the branch name (otherwise, two flat unions
with the same branch name will create a collision in C types), so
we have to include the QAPI type name as well - but since both types
and branches can contain '-' (or even '_'), we need to introduce yet
another character for robust parsing of the generated name back to
the sources, so we use the character ':' in _make_variant() and
transliterate it to '_' in c_name_trans.

The testsuite gets an update to use the new feature, and to ensure
that we can still detect invalid collisions of QMP names.

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

---
v8: tweak commit message, drop unneeded hunks, change c_unboxed_type()
handling of empty types, don't lose test of flat union backref
v7: new patch
---
 scripts/qapi.py                          | 26 ++++++++++++++++++++------
 scripts/qapi-visit.py                    | 14 ++++++++++----
 tests/Makefile.include                   |  1 +
 tests/qapi-schema/flat-union-inline.err  |  2 +-
 tests/qapi-schema/flat-union-inline.json |  5 ++---
 tests/qapi-schema/qapi-schema-test.json  |  8 ++++++--
 tests/qapi-schema/qapi-schema-test.out   |  7 ++++++-
 tests/qapi-schema/union-inline.err       |  1 +
 tests/qapi-schema/union-inline.exit      |  1 +
 tests/qapi-schema/union-inline.json      |  4 ++++
 tests/qapi-schema/union-inline.out       |  0
 11 files changed, 52 insertions(+), 17 deletions(-)
 create mode 100644 tests/qapi-schema/union-inline.err
 create mode 100644 tests/qapi-schema/union-inline.exit
 create mode 100644 tests/qapi-schema/union-inline.json
 create mode 100644 tests/qapi-schema/union-inline.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index e051892..db115eb 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -607,9 +607,11 @@ def check_union(expr, expr_info):
     for (key, value) in members.items():
         check_name(expr_info, "Member of union '%s'" % name, key)

-        # Each value must name a known type
+        # Each value must name a type; although the type may be anonymous
+        # for a flat union.
         check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
-                   value, allow_array=not base, allow_metas=allow_metas)
+                   value, allow_array=not base, allow_dict=base is not None,
+                   allow_optional=True, allow_metas=allow_metas)

         # If the discriminator names an enum type, then all members
         # of 'data' must also be members of the enum type.
@@ -1022,6 +1024,8 @@ class QAPISchemaObjectType(QAPISchemaType):
         return c_name(self.name) + pointer_suffix

     def c_unboxed_type(self):
+        if self.is_empty():
+            return 'char'
         return c_name(self.name)

     def json_type(self):
@@ -1067,6 +1071,11 @@ class QAPISchemaMember(object):
                 return '(parameter of %s)' % owner[:-4]
             elif owner.endswith('-base'):
                 return '(base of %s)' % owner[:-5]
+            elif owner.endswith('-branch'):
+                # See QAPISchema._make_variant() for the further division
+                # of the owner
+                return ('(member of %s branch %s)'
+                        % tuple(owner[:-7].split(':')))
             else:
                 assert owner.endswith('-wrapper')
                 # Unreachable and not implemented
@@ -1362,7 +1371,12 @@ class QAPISchema(object):
                                               self._make_members(data, info),
                                               None))

-    def _make_variant(self, case, typ):
+    def _make_variant(self, case, typ, info, owner):
+        if isinstance(typ, dict):
+            # See also QAPISchemaObjectTypeMember._pretty_owner()
+            typ = self._make_implicit_object_type(
+                "%s:%s" % (owner, case), info, 'branch',
+                self._make_members(typ, info)) or 'q_empty'
         return QAPISchemaObjectTypeVariant(case, typ)

     def _make_simple_variant(self, case, typ, info):
@@ -1383,7 +1397,7 @@ class QAPISchema(object):
             base = (self._make_implicit_object_type(
                     name, info, 'base', self._make_members(base, info)))
         if tag_name:
-            variants = [self._make_variant(key, value)
+            variants = [self._make_variant(key, value, info, name)
                         for (key, value) in data.iteritems()]
             members = []
         else:
@@ -1402,7 +1416,7 @@ class QAPISchema(object):
     def _def_alternate_type(self, expr, info):
         name = expr['alternate']
         data = expr['data']
-        variants = [self._make_variant(key, value)
+        variants = [self._make_variant(key, value, info, name)
                     for (key, value) in data.iteritems()]
         tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
         self._def_entity(
@@ -1512,7 +1526,7 @@ def c_enum_const(type_name, const_name, prefix=None):
         type_name = prefix
     return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()

-c_name_trans = string.maketrans('.-', '__')
+c_name_trans = string.maketrans('.-:', '___')


 # Map @name to a valid C identifier.
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 96f2491..9fa1f04 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -83,13 +83,19 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
         for var in variants.variants:
             ret += mcgen('''
     case %(case)s:
-        visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
-        break;
 ''',
                          case=c_enum_const(variants.tag_member.type.name,
                                            var.name,
-                                           variants.tag_member.type.prefix),
-                         c_type=var.type.c_name(), c_name=c_name(var.name))
+                                           variants.tag_member.type.prefix))
+            if (not isinstance(var.type, QAPISchemaObjectType) or
+                    not var.type.is_empty()):
+                ret += mcgen('''
+        visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
+''',
+                             c_type=var.type.c_name(), c_name=c_name(var.name))
+            ret += mcgen('''
+        break;
+''')

         ret += mcgen('''
     default:
diff --git a/tests/Makefile.include b/tests/Makefile.include
index f73f350..8a4c60d 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -394,6 +394,7 @@ qapi-schema += union-base-no-discriminator.json
 qapi-schema += union-branch-case.json
 qapi-schema += union-clash-branches.json
 qapi-schema += union-empty.json
+qapi-schema += union-inline.json
 qapi-schema += union-invalid-base.json
 qapi-schema += union-optional-branch.json
 qapi-schema += union-unknown.json
diff --git a/tests/qapi-schema/flat-union-inline.err b/tests/qapi-schema/flat-union-inline.err
index 2333358..efcafec 100644
--- a/tests/qapi-schema/flat-union-inline.err
+++ b/tests/qapi-schema/flat-union-inline.err
@@ -1 +1 @@
-tests/qapi-schema/flat-union-inline.json:7: Member 'value1' of union 'TestUnion' should be a type name
+tests/qapi-schema/flat-union-inline.json:6: 'kind' (member of TestUnion branch value2) collides with 'kind' (member of Base)
diff --git a/tests/qapi-schema/flat-union-inline.json b/tests/qapi-schema/flat-union-inline.json
index 62c7cda..a049ec8 100644
--- a/tests/qapi-schema/flat-union-inline.json
+++ b/tests/qapi-schema/flat-union-inline.json
@@ -1,5 +1,4 @@
-# we require branches to be a struct name
-# TODO: should we allow anonymous inline branch types?
+# we allow anonymous union branches, but they must not have clashing names
 { 'enum': 'TestEnum',
   'data': [ 'value1', 'value2' ] }
 { 'struct': 'Base',
@@ -8,4 +7,4 @@
   'base': 'Base',
   'discriminator': 'enum1',
   'data': { 'value1': { 'string': 'str' },
-            'value2': { 'integer': 'int' } } }
+            'value2': { 'kind': 'int' } } }
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 919dc097..c527af0 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -23,7 +23,7 @@
 # for testing override of default naming heuristic
 { 'enum': 'QEnumTwo',
   'prefix': 'QENUM_TWO',
-  'data': [ 'value1', 'value2' ] }
+  'data': [ 'value1', 'value2', 'value3', 'value4' ] }

 # for testing nested structs
 { 'struct': 'UserDefOne',
@@ -81,7 +81,11 @@
   'base': { '*integer': 'int', 'string': 'str', 'enum1': 'QEnumTwo' },
   'discriminator': 'enum1',
   'data': { 'value1' : 'UserDefC', # intentional forward reference
-            'value2' : 'UserDefB' } }
+            'value2' : 'UserDefB', # intentional back reference
+            'value3' : { },
+            'value4' : { 'intb': 'int', '*a-b': 'bool' }
+            # value2 and value4 have same members, but do not collide
+  } }

 { 'struct': 'WrapAlternate',
   'data': { 'alt': 'UserDefAlternate' } }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index e7ea242..fb4ca88 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -52,7 +52,7 @@ object NestedEnumsOne
     member enum2: EnumOne optional=True
     member enum3: EnumOne optional=False
     member enum4: EnumOne optional=True
-enum QEnumTwo ['value1', 'value2']
+enum QEnumTwo ['value1', 'value2', 'value3', 'value4']
     prefix QENUM_TWO
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
@@ -85,6 +85,8 @@ object UserDefFlatUnion2
     tag enum1
     case value1: UserDefC
     case value2: UserDefB
+    case value3: q_empty
+    case value4: q_obj_UserDefFlatUnion2:value4-branch
 object UserDefNativeListUnion
     member type: UserDefNativeListUnionKind optional=False
     tag type
@@ -179,6 +181,9 @@ object q_obj_UserDefFlatUnion2-base
     member integer: int optional=True
     member string: str optional=False
     member enum1: QEnumTwo optional=False
+object q_obj_UserDefFlatUnion2:value4-branch
+    member intb: int optional=False
+    member a-b: bool optional=True
 object q_obj___org.qemu_x-command-arg
     member a: __org.qemu_x-EnumList optional=False
     member b: __org.qemu_x-StructList optional=False
diff --git a/tests/qapi-schema/union-inline.err b/tests/qapi-schema/union-inline.err
new file mode 100644
index 0000000..6c5389a
--- /dev/null
+++ b/tests/qapi-schema/union-inline.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-inline.json:2: Member 'value1' of union 'TestUnion' should be a type name
diff --git a/tests/qapi-schema/union-inline.exit b/tests/qapi-schema/union-inline.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/union-inline.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-inline.json b/tests/qapi-schema/union-inline.json
new file mode 100644
index 0000000..b8c5df6
--- /dev/null
+++ b/tests/qapi-schema/union-inline.json
@@ -0,0 +1,4 @@
+# simple unions cannot have anonymous branches (only flat unions can)
+{ 'union': 'TestUnion',
+  'data': { 'value1': { 'string': 'str' },
+            'value2': { 'kind': 'int' } } }
diff --git a/tests/qapi-schema/union-inline.out b/tests/qapi-schema/union-inline.out
new file mode 100644
index 0000000..e69de29
-- 
2.5.5

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

* [Qemu-devel] [PATCH v8 16/16] schema: Drop pointless empty type CpuInfoOther
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (14 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 15/16] qapi: Allow anonymous branch types in flat union Eric Blake
@ 2016-07-03  2:58 ` Eric Blake
  2016-07-07 13:40 ` [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Markus Armbruster
  16 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-03  2:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

Now that we can represent an empty type anonymously in a flat
union, we can drop one such empty types that existed solely
for CpuInfo.  However, we can't drop Abort or NetdevNoneOptions,
as those were used by simple unions (remember, for simple unions,
we still have to create a wrapper around a type in order to
parse 'data':{}, rather than being a branch that parses nothing).

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

---
v8: tweak commit message
v7: new patch
---
 qapi-schema.json | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 3a7632b..e4b0838 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -846,7 +846,7 @@
             'ppc': 'CpuInfoPPC',
             'mips': 'CpuInfoMIPS',
             'tricore': 'CpuInfoTricore',
-            'other': 'CpuInfoOther' } }
+            'other': {} } }

 ##
 # @CpuInfoX86:
@@ -906,16 +906,6 @@
 { 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } }

 ##
-# @CpuInfoOther:
-#
-# No additional information is available about the virtual CPU
-#
-# Since 2.6
-#
-##
-{ 'struct': 'CpuInfoOther', 'data': { } }
-
-##
 # @query-cpus:
 #
 # Returns a list of information about each virtual CPU.
-- 
2.5.5

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

* Re: [Qemu-devel] [Qemu-block] [PATCH v8 10/16] block: Simplify drive-mirror
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 10/16] block: Simplify drive-mirror Eric Blake
@ 2016-07-05 20:27   ` John Snow
  2016-07-05 22:16     ` Eric Blake
  0 siblings, 1 reply; 28+ messages in thread
From: John Snow @ 2016-07-05 20:27 UTC (permalink / raw)
  To: Eric Blake, qemu-devel
  Cc: Kevin Wolf, Luiz Capitulino, armbru, open list:Block layer core,
	Max Reitz



On 07/02/2016 10:58 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>
> 
> ---
> 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 1bec29e..b91b07c 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', 'box': 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

Should this still be "Since 1.3" for DriveMirror as a structure, since
it's being newly created?

(What color of shed would you like? Any color is fine for me.)

>  ##
> -{ '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)

It's like a symphony!

>  {
>      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);
>  }
> 

Looks good to me.

Reviewed-by: John Snow <jsnow@redhat.com>

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

* Re: [Qemu-devel] [Qemu-block] [PATCH v8 10/16] block: Simplify drive-mirror
  2016-07-05 20:27   ` [Qemu-devel] [Qemu-block] " John Snow
@ 2016-07-05 22:16     ` Eric Blake
  2016-07-05 22:17       ` John Snow
  0 siblings, 1 reply; 28+ messages in thread
From: Eric Blake @ 2016-07-05 22:16 UTC (permalink / raw)
  To: John Snow, qemu-devel
  Cc: Kevin Wolf, Luiz Capitulino, armbru, open list:Block layer core,
	Max Reitz

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

On 07/05/2016 02:27 PM, John Snow wrote:
> 
> 
> On 07/02/2016 10:58 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>
>>

>> @@ -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
> 
> Should this still be "Since 1.3" for DriveMirror as a structure, since
> it's being newly created?
> 
> (What color of shed would you like? Any color is fine for me.)

Introspection output is unchanged, and doesn't include 'since:'
information (we intentionally made introspection agnostic to
backwards-compatible changes, such as creating a named type in place of
an inline one).  So unless Markus has any particular advice on a better
number to use, I think leaving the version number associated with the
first time the associated command was around is as close to the truth as
is necessary.


-- 
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] 28+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [PATCH v8 10/16] block: Simplify drive-mirror
  2016-07-05 22:16     ` Eric Blake
@ 2016-07-05 22:17       ` John Snow
  0 siblings, 0 replies; 28+ messages in thread
From: John Snow @ 2016-07-05 22:17 UTC (permalink / raw)
  To: Eric Blake, qemu-devel
  Cc: Kevin Wolf, Luiz Capitulino, armbru, open list:Block layer core,
	Max Reitz



On 07/05/2016 06:16 PM, Eric Blake wrote:
> On 07/05/2016 02:27 PM, John Snow wrote:
>>
>>
>> On 07/02/2016 10:58 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>
>>>
> 
>>> @@ -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
>>
>> Should this still be "Since 1.3" for DriveMirror as a structure, since
>> it's being newly created?
>>
>> (What color of shed would you like? Any color is fine for me.)
> 
> Introspection output is unchanged, and doesn't include 'since:'
> information (we intentionally made introspection agnostic to
> backwards-compatible changes, such as creating a named type in place of
> an inline one).  So unless Markus has any particular advice on a better
> number to use, I think leaving the version number associated with the
> first time the associated command was around is as close to the truth as
> is necessary.
> 
> 

Suits me just fine, it simply caught my eye is all.

--js

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

* Re: [Qemu-devel] [PATCH v8 08/16] qapi: Implement boxed types for commands/events
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 08/16] qapi: Implement boxed types for commands/events Eric Blake
@ 2016-07-07 10:52   ` Markus Armbruster
  2016-07-07 16:02     ` Eric Blake
  0 siblings, 1 reply; 28+ messages in thread
From: Markus Armbruster @ 2016-07-07 10:52 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.
>
> Generated code is unchanged, as long as no client uses the
> new feature.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> 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, 128 insertions(+), 21 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 f5e7697..48263c4 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']
> +    box = expr.get('box', False)
>
> +    args_meta = ['struct']
> +    if box:
> +        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 box, allow_optional=True,
> +               allow_metas=args_meta)
>      returns_meta = ['union', 'struct']
>      if name in returns_whitelist:
>          returns_meta += ['built-in', 'alternate', 'enum']

Leaves enforcing "if 'box':true then 'data' is mandatory" to
QAPISchemaCommand.check().  Okay.  Just a reminder that we we still have
this ugly split of the semantic checking to clean up.

> @@ -537,11 +541,15 @@ def check_command(expr, expr_info):
>  def check_event(expr, expr_info):
>      global events
>      name = expr['event']
> +    box = expr.get('box', False)
> +    meta = ['struct']
>
> +    if box:
> +        meta += ['union', 'alternate']

Let's not separate the initialization of meta with a blank line here.
Separating it from box, like you did in check_command() is fine.  Can
touch up on commit.

>      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 box, 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 == 'box' 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', 'box'])
>              add_name(expr['command'], info, 'command')
>          elif 'event' in expr:
> -            check_keys(expr_elem, 'event', [], ['data'])
> +            check_keys(expr_elem, 'event', [], ['data', 'box'])
>              add_name(expr['event'], info, 'event')
>          else:
>              raise QAPIExprError(expr_elem['info'],
> @@ -1162,6 +1174,9 @@ class QAPISchemaAlternateType(QAPISchemaType):
>      def visit(self, visitor):
>          visitor.visit_alternate_type(self.name, self.info, self.variants)
>
> +    def is_empty(self):
> +        return False
> +

You want to .is_empty() the boxed type, which can either be
QAPISchemaObjectType (already has this method) or
QAPISchemaAlternateType (doesn't, so you add it).

Duck typing at work :)

>
>  class QAPISchemaCommand(QAPISchemaEntity):
>      def __init__(self, name, info, arg_type, ret_type, gen, success_response,
> @@ -1180,9 +1195,18 @@ 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.box                 # not implemented
> +            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
> +                    isinstance(self.arg_type, QAPISchemaAlternateType))
> +            self.arg_type.check(schema)

qapi.py recurses .check() only when necessary, because undisciplined
recursion can easily become cyclic.

I think you need self.arg_type.check() here so you can
self.arg_type.is_empty() below.  Correct?

> +            if self.box:
> +                if self.arg_type.is_empty():
> +                    raise QAPIExprError(self.info,
> +                                        "Cannot use 'box' with empty type")
> +            else:
> +                assert not self.arg_type.variants

Lost: assert isinstance(self.arg_type, QAPISchemaObjectType), or the
equivalent assert not isinstance(self.arg_type, QAPISchemaAlternateType).

> +        elif self.box:
> +            raise QAPIExprError(self.info,
> +                                "Use of 'box' requires 'data'")
>          if self._ret_type_name:
>              self.ret_type = schema.lookup_type(self._ret_type_name)
>              assert isinstance(self.ret_type, QAPISchemaType)
> @@ -1204,9 +1228,18 @@ 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.box                 # not implemented
> +            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
> +                    isinstance(self.arg_type, QAPISchemaAlternateType))
> +            self.arg_type.check(schema)
> +            if self.box:
> +                if self.arg_type.is_empty():
> +                    raise QAPIExprError(self.info,
> +                                        "Cannot use 'box' with empty type")
> +            else:
> +                assert not self.arg_type.variants

Likewise.

> +        elif self.box:
> +            raise QAPIExprError(self.info,
> +                                "Use of 'box' requires 'data'")
>
>      def visit(self, visitor):
>          visitor.visit_event(self.name, self.info, self.arg_type, self.box)
> @@ -1646,12 +1679,14 @@ extern const char *const %(c_name)s_lookup[];
>
>
>  def gen_params(arg_type, box, extra):
> -    if not arg_type:
> +    if not arg_type or arg_type.is_empty():
> +        assert not box
>          return extra
>      ret = ''
>      sep = ''
>      if box:
> -        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:

Having two representations of "no arguments" (null arg_type and empty
arg_type) is ugly.  Not this patch's fault.

> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index 598c4c7..8d25701 100644
> --- a/scripts/qapi-commands.py
> +++ b/scripts/qapi-commands.py
> @@ -30,7 +30,8 @@ def gen_call(name, arg_type, box, ret_type):
>
>      argstr = ''
>      if box:
> -        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 024be4d..2cab588 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -79,7 +79,10 @@ def gen_event_send(name, arg_type, box):
>      QObject *obj;
>      Visitor *v;
>  ''')
> -        ret += gen_param_var(arg_type)
> +        if not box:
> +            ret += gen_param_var(arg_type)
> +    else:
> +        assert not box
>
>      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..74171b7 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, '*box': 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 include a key 'box' 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,
> +         '*box': 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 include a key
> +'box' 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 ==
>
[Tests snipped, they look good...]

Having read PATCH 07+08 another time, I got one more stylistic remark:
I'd name the flag @boxed, not @box.  It's not a box, it's a flag that
tells us that whatever it applies to is boxed.

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

* Re: [Qemu-devel] [PATCH v8 11/16] qapi-event: Reduce chance of collision with event data
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 11/16] qapi-event: Reduce chance of collision with event data Eric Blake
@ 2016-07-07 11:37   ` Markus Armbruster
  2016-07-13 21:05     ` Eric Blake
  0 siblings, 1 reply; 28+ messages in thread
From: Markus Armbruster @ 2016-07-07 11:37 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> When an unboxed event has accompanying data, we are exposing all
> of its members alongside our local variables in the generated
> qapi_event_send_FOO() function.  So far, we haven't hit a
> collision, but it may be a matter of time before someone wants
> to name a QMP data element 'err' or similar.  We can separate
> the names by making the public function (qapi_event_send_FOO())
> a shell that boxes things up without having to worry about
> collisions, then calls into a new worker function
> (do_qapi_event_send_FOO()) that gets generated to look like what
> we already output for boxed events.
>
> There is still a chance for collision with 'errp' (if that happens,
> tweak c_name() to rename a QMP member 'errp' into the C member
> 'q_errp'), and with 'param' (if that happens, tweak gen_event_send()
> and gen_param_var() to name the temporary variable 'q_param').  But
> with the division done here, the real worker function no longer has
> to worry about collisions.
>
> Adding the new wrapper now means that we need .c_type() for an
> anonymous type given as data to an event, because that type is used
> in the signature of the new helper function, so we have to relax
> an assertion in QAPISchemaObjectType.
>
> The generated file is unchanged for events without data, and for
> boxed events; for unboxed events, the changes are as follows:
>
> |-void qapi_event_send_migration(MigrationStatus status, Error **errp)
> |+static void do_qapi_event_send_migration(q_obj_MIGRATION_arg *arg, Error **errp)
> | {
> |     QDict *qmp;
> |     Error *err = NULL;
> |     QMPEventFuncEmit emit;
> |     QObject *obj;
> |     Visitor *v;
> |-    q_obj_MIGRATION_arg param = {
> |-        status
> |-    };
> |
> |     emit = qmp_event_get_func_emit();
> |     if (!emit) {
> ...
> |     if (err) {
> |         goto out;
> |     }
> |-    visit_type_q_obj_MIGRATION_arg_members(v, &param, &err);
> |+    visit_type_q_obj_MIGRATION_arg_members(v, arg, &err);
> |     if (!err) {
> |         visit_check_struct(v, &err);
> ...
> |+void qapi_event_send_migration(MigrationStatus status, Error **errp)
> |+{
> |+    q_obj_MIGRATION_arg param = {
> |+        status
> |+    };
> |+    do_qapi_event_send_migration(&param, errp);
> |+}
> |+
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v8: don't open-code for boxed types, improve commit message,
> s/intro/prefix/
> v7: new patch
> ---
>  scripts/qapi.py       |  1 -
>  scripts/qapi-event.py | 45 ++++++++++++++++++++++++++-------------------
>  2 files changed, 26 insertions(+), 20 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 48263c4..e051892 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -1019,7 +1019,6 @@ class QAPISchemaObjectType(QAPISchemaType):
       def c_name(self):
>          return QAPISchemaType.c_name(self)

Aside: this method became pointless in commit 7ce106a.

>
>      def c_type(self):
> -        assert not self.is_implicit()
>          return c_name(self.name) + pointer_suffix

Correct if we emit a C type with this name for *any* object type.  I
don't think we do for the empty object type.  Any others?

The assertion should be relaxed to reject exactly the object types for
which we don't emit a C type.

>
>      def c_unboxed_type(self):
> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> index 2cab588..b2da0a9 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -14,8 +14,13 @@
>  from qapi import *
>
>
> -def gen_event_send_proto(name, arg_type, box):
> -    return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
> +def gen_event_send_proto(name, arg_type, box, impl=False):

The function has changed from one that does just one thing to one that
can do many things depending on arg_type, box and impl.  It's not even
obvious which combinations are valid.  Either split it into simple,
self-documenting functions, or explain this complex one with a function
comment.

> +    prefix='void '
> +    if impl and arg_type and not box:
> +        box = True
> +        prefix='static void do_'
> +    return '%(prefix)sqapi_event_send_%(c_name)s(%(param)s)' % {
> +        'prefix': prefix,
>          'c_name': c_name(name.lower()),
>          'param': gen_params(arg_type, box, 'Error **errp')}
>
> @@ -49,21 +54,12 @@ def gen_param_var(typ):
>
>      };
>  ''')
> -    if not typ.is_implicit():
> -        ret += mcgen('''
> -    %(c_name)s *arg = &param;
> -''',
> -                c_name=typ.c_name())
>      return ret
>
>
>  def gen_event_send(name, arg_type, box):
> -    # 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
> -    # practice, we can rename our local variables with a leading _ prefix,
> -    # or split the code into a wrapper function that creates a boxed
> -    # 'param' object then calls another to do the real work.
> +    if not arg_type or arg_type.is_empty():
> +        assert not box
>      ret = mcgen('''
>
>  %(proto)s
> @@ -72,17 +68,13 @@ def gen_event_send(name, arg_type, box):
>      Error *err = NULL;
>      QMPEventFuncEmit emit;
>  ''',
> -                proto=gen_event_send_proto(name, arg_type, box))
> +                proto=gen_event_send_proto(name, arg_type, box, True))
>
>      if arg_type and not arg_type.is_empty():
>          ret += mcgen('''
>      QObject *obj;
>      Visitor *v;
>  ''')
> -        if not box:
> -            ret += gen_param_var(arg_type)
> -    else:
> -        assert not box
>
>      ret += mcgen('''
>
> @@ -112,7 +104,7 @@ def gen_event_send(name, arg_type, box):
>      if (err) {
>          goto out;
>      }
> -    visit_type_%(c_name)s_members(v, &param, &err);
> +    visit_type_%(c_name)s_members(v, arg, &err);
>      if (!err) {
>          visit_check_struct(v, &err);
>      }
> @@ -144,6 +136,21 @@ out:
>      QDECREF(qmp);
>  }
>  ''')
> +
> +    if arg_type and not box:
> +        ret += mcgen('''
> +
> +%(proto)s
> +{
> +''',
> +                     proto=gen_event_send_proto(name, arg_type, box))
> +        ret += gen_param_var(arg_type)
> +        ret += mcgen('''
> +    do_qapi_event_send_%(c_name)s(&param, errp);
> +}
> +''',
> +                     c_name=c_name(name.lower()))
> +
>      return ret

This function spans 95 lines, and you have to read to the end to see
that it first emits a function that can either be the
qapi_event_send_FOO() function or a helper, and if it's the latter, then
emits the former.  Either split it up into parts that can be more easily
understood, or add suitable comments to the complex function.

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

* Re: [Qemu-devel] [PATCH v8 14/16] net: Complete qapi-fication of netdev_add
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 14/16] net: Complete qapi-fication of netdev_add Eric Blake
@ 2016-07-07 12:57   ` Markus Armbruster
  0 siblings, 0 replies; 28+ messages in thread
From: Markus Armbruster @ 2016-07-07 12:57 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Jason Wang, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> 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"}}

Needs to be covered in release notes.

> If that turns out to be problematic, we could resolve the problem
> by either 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), or by using QAPI alternates.  But that work should be
> in followup patches, if at all.

The sloppy typing is an accidental misfeature that is at odds with QMP's
design.  As far as I can tell, it's not documented anywhere.  But lots
of things aren't documented anywhere; the questions is whether they're
used.

If we find we must keep netdev_add misfeature-compatible, I want it
deprecated in favor of a new command that adheres to the QMP design.

Do we have to keep it misfeature-compatible proactively?  How
comfortable are we with "wait see whether anything breaks" here?

The scenarios I want to avoid are "OMG, hold the release while we
improvise a misfeature-compatibility hack", and "OMG, we need a stable
release for misfeature compatibility a.s.a.p."

I'm okay with breaking misfeature compatibility in this patch, and
unbreak it separately if necessary.

> 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.

This use of QemuOpts as a network backend directory has always been
somewhat questionable.  More so now the QemuOpts is empty.  But it's not
this patch's business.

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

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

* Re: [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F)
  2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (15 preceding siblings ...)
  2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 16/16] schema: Drop pointless empty type CpuInfoOther Eric Blake
@ 2016-07-07 13:40 ` Markus Armbruster
  16 siblings, 0 replies; 28+ messages in thread
From: Markus Armbruster @ 2016-07-07 13:40 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 (including my clone visitor)
>
> available as a tag at:
> git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv8f
> or as part of my qapi branch:
> git fetch git://repo.or.cz/qemu/ericb.git qapi
>
> v7 was here:
> https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg03569.html
>
> Since then, I've addressed a lot of Markus' comments:
> - drop 1/15
> - split out a couple of patches
> - wording tweaks, more justifications in some commit messages
> - rebase to master
> - allow boxed alternates, in addition to boxed unions
> - forbid empty anonymous branches for now
>
> 001/16:[----] [--] 'net: use Netdev instead of NetClientOptions in client init'
> 002/16:[----] [--] 'qapi: Require all branches of flat union enum to be covered'
> 003/16:[----] [--] 'qapi: Hide tag_name data member of variants'
> 004/16:[0006] [FC] 'qapi: Add type.is_empty() helper'
> 005/16:[down] 'qapi: Drop useless gen_err_check()'
> 006/16:[down] 'qapi-event: Simplify visit of non-implicit data'
> 007/16:[0028] [FC] 'qapi: Plumb in 'box' to qapi generator lower levels'
> 008/16:[0121] [FC] 'qapi: Implement boxed types for commands/events'
> 009/16:[0002] [FC] 'block: Simplify block_set_io_throttle'
> 010/16:[0023] [FC] 'block: Simplify drive-mirror'
> 011/16:[0026] [FC] 'qapi-event: Reduce chance of collision with event data'
> 012/16:[0042] [FC] 'qapi: Change Netdev into a flat union'
> 013/16:[----] [--] 'net: Use correct type for bool flag'
> 014/16:[0008] [FC] 'net: Complete qapi-fication of netdev_add'
> 015/16:[0037] [FC] 'qapi: Allow anonymous branch types in flat union'
> 016/16:[----] [--] 'schema: Drop pointless empty type CpuInfoOther'

Let's focus on PATCH 01-14 for now.  I think we have a decent chance to
merge them quickly.  15 and 16 provide a nice extra, but I need to stare
at 15 a bit more to decide whether I like it, or how to do it
differently.

> Other patches on my QAPI queue:
> - posted, but needs another respin and might not make 2.7:
> JSON output visitor

I don't think this one's blocking anything.

> - not posted, and therefore most likely not 2.7 material:
> anything we need to do to make 'blockdev-add' work smoothly
> (we may want to allow a flat union with a default value for its
> discriminator, and/or add a way to express mutually-exclusive
> members in a qapi dict)

I'd like us to think through the needs blockdev-add may have before
rushing to add more QAPI features.

> Other potential general QAPI patches, but not necessarily that I will write:
> - Marc-Andre's cleanups

Next in line :)

> - convert more clients to use boxed commands

Can be done in parallel.

> - get rid of has_FOO when FOO is pointer

Shouldn't be hard.

> - support default values (and get rid of has_FOO when FOO has default)

I like the idea, but I think we should do some of the other things
first.

> - QAPI-fy device_add

Need something to talk about in Toronto ;)

> - ???

- Fold semantic analysis into QAPISchema (a TODO in qapi.py)

This is just paying back technical debt.

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

* Re: [Qemu-devel] [PATCH v8 08/16] qapi: Implement boxed types for commands/events
  2016-07-07 10:52   ` Markus Armbruster
@ 2016-07-07 16:02     ` Eric Blake
  2016-07-08  7:06       ` Markus Armbruster
  0 siblings, 1 reply; 28+ messages in thread
From: Eric Blake @ 2016-07-07 16:02 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 07/07/2016 04:52 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.
>>
>> 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.
>>
>> Generated code is unchanged, as long as no client uses the
>> new feature.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>

>> @@ -1180,9 +1195,18 @@ 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.box                 # not implemented
>> +            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
>> +                    isinstance(self.arg_type, QAPISchemaAlternateType))
>> +            self.arg_type.check(schema)
> 
> qapi.py recurses .check() only when necessary, because undisciplined
> recursion can easily become cyclic.
> 
> I think you need self.arg_type.check() here so you can
> self.arg_type.is_empty() below.  Correct?

Correct.  And should not be unbounded - a command depends on a type, but
no type depends on a command, so this does not introduce new recursion.

> 
>> +            if self.box:
>> +                if self.arg_type.is_empty():
>> +                    raise QAPIExprError(self.info,
>> +                                        "Cannot use 'box' with empty type")
>> +            else:
>> +                assert not self.arg_type.variants
> 
> Lost: assert isinstance(self.arg_type, QAPISchemaObjectType), or the
> equivalent assert not isinstance(self.arg_type, QAPISchemaAlternateType).

Or rather, implicitly hidden.  Only QAPISchemaObjectType and
QAPISchemaAlternateType have a .is_empty() or .variants, so if any other
type is passed, python will complain about a missing attribute (which is
just as effective as the assert() used to be).

> 
>> +        elif self.box:
>> +            raise QAPIExprError(self.info,
>> +                                "Use of 'box' requires 'data'")
>>          if self._ret_type_name:
>>              self.ret_type = schema.lookup_type(self._ret_type_name)
>>              assert isinstance(self.ret_type, QAPISchemaType)
>> @@ -1204,9 +1228,18 @@ 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.box                 # not implemented
>> +            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
>> +                    isinstance(self.arg_type, QAPISchemaAlternateType))
>> +            self.arg_type.check(schema)
>> +            if self.box:
>> +                if self.arg_type.is_empty():
>> +                    raise QAPIExprError(self.info,
>> +                                        "Cannot use 'box' with empty type")
>> +            else:
>> +                assert not self.arg_type.variants
> 
> Likewise.

And same argument about being implicitly correct.  I can either document
this in the commit message (to call it out as intentional) or restore
the asserts; up to you which is cleaner.


>>  == Client JSON Protocol introspection ==
>>
> [Tests snipped, they look good...]
> 
> Having read PATCH 07+08 another time, I got one more stylistic remark:
> I'd name the flag @boxed, not @box.  It's not a box, it's a flag that
> tells us that whatever it applies to is boxed.

Sounds reasonable, so looks like a v9 is worth posting.

-- 
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] 28+ messages in thread

* Re: [Qemu-devel] [PATCH v8 08/16] qapi: Implement boxed types for commands/events
  2016-07-07 16:02     ` Eric Blake
@ 2016-07-08  7:06       ` Markus Armbruster
  0 siblings, 0 replies; 28+ messages in thread
From: Markus Armbruster @ 2016-07-08  7:06 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 07/07/2016 04:52 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.
>>>
>>> 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.
>>>
>>> Generated code is unchanged, as long as no client uses the
>>> new feature.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>
>>> @@ -1180,9 +1195,18 @@ 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.box                 # not implemented
>>> +            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
>>> +                    isinstance(self.arg_type, QAPISchemaAlternateType))
>>> +            self.arg_type.check(schema)
>> 
>> qapi.py recurses .check() only when necessary, because undisciplined
>> recursion can easily become cyclic.
>> 
>> I think you need self.arg_type.check() here so you can
>> self.arg_type.is_empty() below.  Correct?
>
> Correct.  And should not be unbounded - a command depends on a type, but
> no type depends on a command, so this does not introduce new recursion.

Yes.

I like to avoid .check() recursion whenever practical, to keep the
termination argument simple, but since I can't see how to avoid it here,
we'll go with your code.

>>> +            if self.box:
>>> +                if self.arg_type.is_empty():
>>> +                    raise QAPIExprError(self.info,
>>> +                                        "Cannot use 'box' with empty type")
>>> +            else:
>>> +                assert not self.arg_type.variants
>> 
>> Lost: assert isinstance(self.arg_type, QAPISchemaObjectType), or the
>> equivalent assert not isinstance(self.arg_type, QAPISchemaAlternateType).
>
> Or rather, implicitly hidden.  Only QAPISchemaObjectType and
> QAPISchemaAlternateType have a .is_empty() or .variants, so if any other
> type is passed, python will complain about a missing attribute (which is
> just as effective as the assert() used to be).

You're right.

>>> +        elif self.box:
>>> +            raise QAPIExprError(self.info,
>>> +                                "Use of 'box' requires 'data'")
>>>          if self._ret_type_name:
>>>              self.ret_type = schema.lookup_type(self._ret_type_name)
>>>              assert isinstance(self.ret_type, QAPISchemaType)
>>> @@ -1204,9 +1228,18 @@ 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.box                 # not implemented
>>> +            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
>>> +                    isinstance(self.arg_type, QAPISchemaAlternateType))
>>> +            self.arg_type.check(schema)
>>> +            if self.box:
>>> +                if self.arg_type.is_empty():
>>> +                    raise QAPIExprError(self.info,
>>> +                                        "Cannot use 'box' with empty type")
>>> +            else:
>>> +                assert not self.arg_type.variants
>> 
>> Likewise.
>
> And same argument about being implicitly correct.  I can either document
> this in the commit message (to call it out as intentional) or restore
> the asserts; up to you which is cleaner.

Let's restore the assertions, because they do double-duty documenting
intent here.

>>>  == Client JSON Protocol introspection ==
>>>
>> [Tests snipped, they look good...]
>> 
>> Having read PATCH 07+08 another time, I got one more stylistic remark:
>> I'd name the flag @boxed, not @box.  It's not a box, it's a flag that
>> tells us that whatever it applies to is boxed.
>
> Sounds reasonable, so looks like a v9 is worth posting.

Yes, but it should be few and simple changes, thus quick to review.

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

* Re: [Qemu-devel] [PATCH v8 11/16] qapi-event: Reduce chance of collision with event data
  2016-07-07 11:37   ` Markus Armbruster
@ 2016-07-13 21:05     ` Eric Blake
  0 siblings, 0 replies; 28+ messages in thread
From: Eric Blake @ 2016-07-13 21:05 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 07/07/2016 05:37 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> When an unboxed event has accompanying data, we are exposing all
>> of its members alongside our local variables in the generated
>> qapi_event_send_FOO() function.  So far, we haven't hit a
>> collision, but it may be a matter of time before someone wants
>> to name a QMP data element 'err' or similar.  We can separate
>> the names by making the public function (qapi_event_send_FOO())
>> a shell that boxes things up without having to worry about
>> collisions, then calls into a new worker function
>> (do_qapi_event_send_FOO()) that gets generated to look like what
>> we already output for boxed events.
>>

>> +++ b/scripts/qapi.py
>> @@ -1019,7 +1019,6 @@ class QAPISchemaObjectType(QAPISchemaType):
>        def c_name(self):
>>          return QAPISchemaType.c_name(self)
> 
> Aside: this method became pointless in commit 7ce106a.
> 

Sounds like an easy separate cleanup.

>>
>>      def c_type(self):
>> -        assert not self.is_implicit()
>>          return c_name(self.name) + pointer_suffix
> 
> Correct if we emit a C type with this name for *any* object type.  I
> don't think we do for the empty object type.  Any others?
> 
> The assertion should be relaxed to reject exactly the object types for
> which we don't emit a C type.

Will fix.

> 
>>
>>      def c_unboxed_type(self):
>> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
>> index 2cab588..b2da0a9 100644
>> --- a/scripts/qapi-event.py
>> +++ b/scripts/qapi-event.py
>> @@ -14,8 +14,13 @@
>>  from qapi import *
>>
>>
>> -def gen_event_send_proto(name, arg_type, box):
>> -    return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
>> +def gen_event_send_proto(name, arg_type, box, impl=False):
> 
> The function has changed from one that does just one thing to one that
> can do many things depending on arg_type, box and impl.  It's not even
> obvious which combinations are valid.  Either split it into simple,
> self-documenting functions, or explain this complex one with a function
> comment.

At this point, this cleanup is separate from the main work to get
'boxed' commands working, so I'll focus on getting the important first
half of the series posted today, and leave this particular patch for
when I have more time to get the refactoring done right.


> 
> This function spans 95 lines, and you have to read to the end to see
> that it first emits a function that can either be the
> qapi_event_send_FOO() function or a helper, and if it's the latter, then
> emits the former.  Either split it up into parts that can be more easily
> understood, or add suitable comments to the complex function.

Indeed.  The changes were all made piecemeal with minimal churn, but the
end result is messier than if we step back and do it right.

-- 
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] 28+ messages in thread

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

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-03  2:58 [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 01/16] net: use Netdev instead of NetClientOptions in client init Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 02/16] qapi: Require all branches of flat union enum to be covered Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 03/16] qapi: Hide tag_name data member of variants Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 04/16] qapi: Add type.is_empty() helper Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 05/16] qapi: Drop useless gen_err_check() Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 06/16] qapi-event: Simplify visit of non-implicit data Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 07/16] qapi: Plumb in 'box' to qapi generator lower levels Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 08/16] qapi: Implement boxed types for commands/events Eric Blake
2016-07-07 10:52   ` Markus Armbruster
2016-07-07 16:02     ` Eric Blake
2016-07-08  7:06       ` Markus Armbruster
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 09/16] block: Simplify block_set_io_throttle Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 10/16] block: Simplify drive-mirror Eric Blake
2016-07-05 20:27   ` [Qemu-devel] [Qemu-block] " John Snow
2016-07-05 22:16     ` Eric Blake
2016-07-05 22:17       ` John Snow
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 11/16] qapi-event: Reduce chance of collision with event data Eric Blake
2016-07-07 11:37   ` Markus Armbruster
2016-07-13 21:05     ` Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 12/16] qapi: Change Netdev into a flat union Eric Blake
2016-07-03  2:58   ` Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 13/16] net: Use correct type for bool flag Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 14/16] net: Complete qapi-fication of netdev_add Eric Blake
2016-07-07 12:57   ` Markus Armbruster
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 15/16] qapi: Allow anonymous branch types in flat union Eric Blake
2016-07-03  2:58 ` [Qemu-devel] [PATCH v8 16/16] schema: Drop pointless empty type CpuInfoOther Eric Blake
2016-07-07 13:40 ` [Qemu-devel] [PATCH for-2.7 v8 00/16] qapi netdev_add introspection (post-introspection cleanups subset F) Markus Armbruster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.