All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F)
@ 2016-05-20 22:40 Eric Blake
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors Eric Blake
                   ` (16 more replies)
  0 siblings, 17 replies; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

The end is nearly in sight!  Last August, I started a process of
cleaning up qapi on top of Markus' introspection work.  Several
hundred emails later, and numerous intermediate series out of
the way, I'm finally at the end of my queue of patches that were
originally sent in v5:
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg05410.html

v6 was here (yes, 5 months ago):
https://www.mail-archive.com/qemu-devel@nongnu.org/msg343192.html

Prerequisites:
My clone/JSON visitor (weak; this series is mostly orthogonal to
that one, if we want to swap the order of the two based on review
complexity):
https://www.mail-archive.com/qemu-devel%40nongnu.org/msg371555.html

Once my series are in, we should focus on Marc-Andre's cleanups.
Ideas for further qapi work - which I have NOT coded yet:
- 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)
- finally QAPI-fy device_add
- consider swapping blockdev-add to use QAPI structs throughout the
code-base, rather than converting back into flat QDict and using QemuOpts
- introspection of command line options

diffstat to v6:
001/15:[down] 'qapi: Consolidate object visitors'
002/15:[0048] [FC] 'net: use Netdev instead of NetClientOptions in client init'
003/15:[down] 'qapi: Require all branches of flat union enum to be covered'
004/15:[----] [-C] 'qapi: Hide tag_name data member of variants'
005/15:[0001] [FC] 'qapi: Add type.is_empty() helper'
006/15:[0074] [FC] 'qapi: Plumb in 'box' to qapi generator lower levels'
007/15:[0031] [FC] 'qapi: Implement boxed types for commands/events'
008/15:[down] 'block: Simplify block_set_io_throttle'
009/15:[down] 'block: Simplify drive-mirror'
010/15:[down] 'qapi-event: Reduce chance of collision with event data'
011/15:[0084] [FC] 'qapi: Change Netdev into a flat union'
012/15:[----] [-C] 'net: Use correct type for bool flag'
013/15:[----] [--] 'net: Complete qapi-fication of netdev_add'
014/15:[down] 'qapi: Allow anonymous branch types in flat union'
015/15:[down] 'schema: Drop pointless empty type CpuInfoOther'

Eric Blake (13):
  qapi: Consolidate object visitors
  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: 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
  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 (2):
  net: use Netdev instead of NetClientOptions in client init
  qapi: Change Netdev into a flat union

 qapi-schema.json                                   |  74 +++++----
 qapi/block-core.json                               |  37 ++++-
 scripts/qapi.py                                    | 123 +++++++++-----
 scripts/qapi-commands.py                           |  21 ++-
 scripts/qapi-event.py                              |  67 +++++---
 scripts/qapi-introspect.py                         |  14 +-
 scripts/qapi-types.py                              |  25 +--
 scripts/qapi-visit.py                              |  26 +--
 include/net/net.h                                  |   7 +-
 net/clients.h                                      |  20 +--
 blockdev.c                                         | 183 ++++++++-------------
 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/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                                 |  18 +-
 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/hub.c                                          |  24 +--
 net/l2tpv3.c                                       |   8 +-
 net/net.c                                          | 161 +++++++++++-------
 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                                   |  20 +--
 tests/test-qmp-commands.c                          |  12 ++
 docs/qapi-code-gen.txt                             |  24 ++-
 qmp-commands.hx                                    |   2 +-
 tests/Makefile                                     |   4 +
 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-union.err                   |   2 +-
 tests/qapi-schema/args-union.json                  |   3 +-
 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            |  11 +-
 tests/qapi-schema/qapi-schema-test.out             |  45 ++++-
 tests/qapi-schema/test-qapi.py                     |  21 +--
 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
 86 files changed, 691 insertions(+), 534 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/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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 12:35   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 02/15] net: use Netdev instead of NetClientOptions in client init Eric Blake
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Rather than having two separate visitor callbacks with items
already broken out, pass the actual QAPISchemaObjectType object
to the visitor.  This lets the visitor access things like
type.is_implicit() without needing another parameter, resolving
a TODO from previous patches.

For convenience and consistency, the 'name' and 'info' parameters
are still provided, even though they are now redundant with
'typ.name' and 'typ.info'.

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

---
v7: no changes
[posted earlier as part of "cleanup subset E"]
v14: fix testsuite failures
[posted earlier as part of "easier unboxed visits/qapi implicit types"]
v6: new patch
---
 scripts/qapi.py                | 10 ++--------
 scripts/qapi-introspect.py     | 10 +++++-----
 scripts/qapi-types.py          | 13 ++++++-------
 scripts/qapi-visit.py          | 12 ++++++------
 tests/qapi-schema/test-qapi.py | 10 +++++-----
 5 files changed, 24 insertions(+), 31 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index b13ae47..4dde43a 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -808,10 +808,7 @@ class QAPISchemaVisitor(object):
     def visit_array_type(self, name, info, element_type):
         pass

-    def visit_object_type(self, name, info, base, members, variants):
-        pass
-
-    def visit_object_type_flat(self, name, info, members, variants):
+    def visit_object_type(self, name, info, typ):
         pass

     def visit_alternate_type(self, name, info, variants):
@@ -1005,10 +1002,7 @@ class QAPISchemaObjectType(QAPISchemaType):
         return 'object'

     def visit(self, visitor):
-        visitor.visit_object_type(self.name, self.info,
-                                  self.base, self.local_members, self.variants)
-        visitor.visit_object_type_flat(self.name, self.info,
-                                       self.members, self.variants)
+        visitor.visit_object_type(self.name, self.info, self)


 class QAPISchemaMember(object):
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index e0f926b..474eafd 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -141,11 +141,11 @@ const char %(c_name)s[] = %(c_string)s;
         element = self._use_type(element_type)
         self._gen_json('[' + element + ']', 'array', {'element-type': element})

-    def visit_object_type_flat(self, name, info, members, variants):
-        obj = {'members': [self._gen_member(m) for m in members]}
-        if variants:
-            obj.update(self._gen_variants(variants.tag_member.name,
-                                          variants.variants))
+    def visit_object_type(self, name, info, typ):
+        obj = {'members': [self._gen_member(m) for m in typ.members]}
+        if typ.variants:
+            obj.update(self._gen_variants(typ.variants.tag_member.name,
+                                          typ.variants.variants))
         self._gen_json(name, 'object', obj)

     def visit_alternate_type(self, name, info, variants):
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 5ace2cf..b3038e5 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -218,17 +218,16 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
             self.decl += gen_array(name, element_type)
             self._gen_type_cleanup(name)

-    def visit_object_type(self, name, info, base, members, variants):
+    def visit_object_type(self, name, info, typ):
         # Nothing to do for the special empty builtin
         if name == 'q_empty':
             return
         self._fwdecl += gen_fwd_object_or_array(name)
-        self.decl += gen_object(name, base, members, variants)
-        if base and not base.is_implicit():
-            self.decl += gen_upcast(name, base)
-        # TODO Worth changing the visitor signature, so we could
-        # directly use rather than repeat type.is_implicit()?
-        if not name.startswith('q_'):
+        self.decl += gen_object(name, typ.base, typ.local_members,
+                                typ.variants)
+        if typ.base and not typ.base.is_implicit():
+            self.decl += gen_upcast(name, typ.base)
+        if not typ.is_implicit():
             # implicit types won't be directly allocated/freed
             self._gen_type_cleanup(name)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 7b85d2b..07ae6d1 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -298,18 +298,18 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
             self.decl += decl
             self.defn += defn

-    def visit_object_type(self, name, info, base, members, variants):
+    def visit_object_type(self, name, info, typ):
         # Nothing to do for the special empty builtin
         if name == 'q_empty':
             return
         self.decl += gen_visit_members_decl(name)
-        self.defn += gen_visit_object_members(name, base, members, variants)
-        # TODO Worth changing the visitor signature, so we could
-        # directly use rather than repeat type.is_implicit()?
-        if not name.startswith('q_'):
+        self.defn += gen_visit_object_members(name, typ.base,
+                                              typ.local_members, typ.variants)
+        if not typ.is_implicit():
             # only explicit types need an allocating visit
             self.decl += gen_visit_decl(name)
-            self.defn += gen_visit_object(name, base, members, variants)
+            self.defn += gen_visit_object(name, typ.base, typ.local_members,
+                                          typ.variants)

     def visit_alternate_type(self, name, info, variants):
         self.decl += gen_visit_decl(name)
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 649677e..ccd1704 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -22,14 +22,14 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
         if prefix:
             print '    prefix %s' % prefix

-    def visit_object_type(self, name, info, base, members, variants):
+    def visit_object_type(self, name, info, typ):
         print 'object %s' % name
-        if base:
-            print '    base %s' % base.name
-        for m in members:
+        if typ.base:
+            print '    base %s' % typ.base.name
+        for m in typ.local_members:
             print '    member %s: %s optional=%s' % \
                 (m.name, m.type.name, m.optional)
-        self._print_variants(variants)
+        self._print_variants(typ.variants)

     def visit_alternate_type(self, name, info, variants):
         print 'alternate %s' % name
-- 
2.5.5

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

* [Qemu-devel] [PATCH v7 02/15] net: use Netdev instead of NetClientOptions in client init
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 13:11   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered Eric Blake
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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; rebase to qapi changes]
Signed-off-by: Eric Blake <eblake@redhat.com>

---
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 7a91e8f..ec4e700 100644
--- a/net/net.c
+++ b/net/net.c
@@ -864,15 +864,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) {
@@ -933,7 +933,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,
@@ -965,11 +965,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;

@@ -982,7 +984,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;

@@ -1009,7 +1013,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 6cc0db5..addeedd 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -419,10 +419,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 9fa2cd8..d258352 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -697,15 +697,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 740e8a2..9531880 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 1b9e73a..4607c7b 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -298,15 +298,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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors Eric Blake
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 02/15] net: use Netdev instead of NetClientOptions in client init Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 13:24   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 04/15] qapi: Hide tag_name data member of variants Eric Blake
                   ` (13 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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.

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

---
v7: new patch
---
 scripts/qapi.py                                     | 8 ++++++++
 tests/Makefile                                      | 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 4dde43a..82d96e2 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 b/tests/Makefile
index 0d06406..c608f9a 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -314,6 +314,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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 04/15] qapi: Hide tag_name data member of variants
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (2 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 13:32   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 05/15] qapi: Add type.is_empty() helper Eric Blake
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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.  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>

---
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 82d96e2..3554ab1 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1086,7 +1086,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

@@ -1096,8 +1096,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)
@@ -1127,7 +1127,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 ccd1704..65ca19a 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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 05/15] qapi: Add type.is_empty() helper
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (3 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 04/15] qapi: Hide tag_name data member of variants Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 14:01   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 06/15] qapi: Plumb in 'box' to qapi generator lower levels Eric Blake
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

And use it in qapi-types and qapi-event.  In the near future, we
want to lift our artificial restriction of no variants at the
top level of an event, at which point, inlining our check for
whether members is empty will no longer be sufficient; but
adding an inline check for variants everywhere we are also looking
at members adds verbosity.  For now, we already asserted that no
variants were present, so the conversion from 'if type.members'
to 'if not type.is_empty()' has no semantic change, but does
future-proof the code if we eventually do support variants in those
contexts.

No change to generated code.

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

---
v3: 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-event.py | 6 +++---
 scripts/qapi-types.py | 2 +-
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 3554ab1..90ea30c 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -996,6 +996,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-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 b3038e5..5a9e2da 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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 06/15] qapi: Plumb in 'box' to qapi generator lower levels
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (4 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 05/15] qapi: Add type.is_empty() helper Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 14:39   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 07/15] qapi: Implement boxed types for commands/events Eric Blake
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

A future 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.  This patch adds the
internal plubming of a 'box' flag associated with each
command and event.  For this patch, no behavior changes,
other than the testsuite outputting the value of the new
flag (always False for now).

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

---
v7: rebase to master
v6: rebase to earlier changes
---
 scripts/qapi.py                         | 46 ++++++++++++++++++++-------------
 scripts/qapi-commands.py                | 20 +++++++-------
 scripts/qapi-event.py                   | 37 +++++++++++++++-----------
 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, 84 insertions(+), 57 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 90ea30c..108363d 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -823,10 +823,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


@@ -1158,7 +1158,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)
@@ -1168,12 +1169,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
+            if not self.box:
+                assert not self.arg_type.variants
         if self._ret_type_name:
             self.ret_type = schema.lookup_type(self._ret_type_name)
             assert isinstance(self.ret_type, QAPISchemaType)
@@ -1181,24 +1184,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
+            if not self.box:
+                assert not self.arg_type.variants

     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):
@@ -1374,6 +1379,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))
@@ -1381,15 +1387,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:
@@ -1632,18 +1639,21 @@ 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:
+        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 34b6a3a..79d4eea 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:
@@ -92,7 +94,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
@@ -134,7 +136,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 arg_type.members) or ret_type:
@@ -210,16 +212,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 09c0a2a..fd953fe 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()
@@ -52,7 +52,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
@@ -67,14 +67,15 @@ 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('''
     QObject *obj;
     Visitor *v;
 ''')
-        ret += gen_param_var(arg_type)
+        if not box:
+            ret += gen_param_var(arg_type)

     ret += mcgen('''

@@ -92,6 +93,12 @@ def gen_event_send(name, arg_type):
         ret += mcgen('''
     v = qmp_output_visitor_new(&obj);

+''')
+
+        if box:
+            assert False     # not implemented
+        else:
+            ret += mcgen('''
     visit_start_struct(v, "%(name)s", NULL, 0, &err);
     if (err) {
         goto out;
@@ -101,14 +108,14 @@ def gen_event_send(name, arg_type):
         visit_check_struct(v, &err);
     }
     visit_end_struct(v, NULL);
-    if (err) {
-        goto out;
-    }
+''',
+                     name=name, c_name=arg_type.c_name())
+        ret += gen_err_check()
+        ret += mcgen('''

     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);
@@ -145,9 +152,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 474eafd..7646c89 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 65ca19a..fb899a1 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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 07/15] qapi: Implement boxed types for commands/events
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (5 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 06/15] qapi: Plumb in 'box' to qapi generator lower levels Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 15:27   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 08/15] block: Simplify block_set_io_throttle Eric Blake
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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.  For structs, it makes it possible
to pass a single qapi type instead of a breakout of all
struct members; for unions, it is now possible to use a
union as the data for a command or event.

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

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

---
v7: rebase to latest
v6: retitle, rebase, and merge v5 40/46 and 41/46 into one patch
---
 scripts/qapi.py                         | 29 +++++++++++++++++++++--------
 scripts/qapi-commands.py                |  3 ++-
 scripts/qapi-event.py                   |  5 ++++-
 tests/test-qmp-commands.c               | 12 ++++++++++++
 docs/qapi-code-gen.txt                  | 24 ++++++++++++++++++++++--
 tests/Makefile                          |  2 ++
 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-union.err        |  2 +-
 tests/qapi-schema/args-union.json       |  3 +--
 tests/qapi-schema/qapi-schema-test.json |  6 +++++-
 tests/qapi-schema/qapi-schema-test.out  | 10 +++++++++-
 18 files changed, 87 insertions(+), 17 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

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 108363d..6742e7a 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']
     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']
     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'],
@@ -1640,12 +1652,13 @@ 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():
         return extra
     ret = ''
     sep = ''
     if box:
-        assert False     # not implemented
+        ret += '%s arg' % arg_type.c_param_type()
+        sep = ', '
     else:
         for memb in arg_type.members:
             ret += sep
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 79d4eea..dc41fad 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
+        if 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 fd953fe..b8ca8c8 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -96,7 +96,10 @@ def gen_event_send(name, arg_type, box):
 ''')

         if box:
-            assert False     # not implemented
+            ret += mcgen('''
+    visit_type_%(c_name)s(v, NULL, &arg, &err);
+''',
+                         c_name=arg_type.c_name(), name=arg_type.name)
         else:
             ret += mcgen('''
     visit_start_struct(v, "%(name)s", NULL, 0, &err);
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 87fc759..007db37 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -60,6 +60,18 @@ QObject *qmp_guest_sync(QObject *arg, Error **errp)
     return arg;
 }

+void qmp_boxed_empty(Error **errp)
+{
+}
+
+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 aefc29e..40f050e 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,17 @@ which would validate this Client JSON Protocol transaction:
  => { "execute": "my-second-command" }
  <= { "return": [ { "value": "one" }, { } ] }

+By default, the generator creates a marshalling function that converts
+an input QDict into a function call implemented by the user, and
+declares a prototype for the user's function which has a parameter for
+each member of the argument struct, including boolean arguments that
+describe whether optional arguments were provided.  But if the QAPI
+description includes the key 'box' with the boolean value true, the
+user call prototype will have only a single parameter for the overall
+generated C structure.  The 'box' key is required in order to use a
+union as an input argument, since it is not possible to list all
+members of the union as separate parameters.
+
 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 +495,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 +517,14 @@ Resulting in this JSON object:
   "data": { "b": "test string" },
   "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }

+By default, the generator creates a C function that takes as
+parameters each member of the argument struct and turns it into the
+appropriate JSON Client event.  But if the QAPI description includes
+the key 'box' with the boolean value true, the event function will
+have only a single parameter for the overall generated C structure.
+The 'box' key is required in order to use a union as the data key,
+since it is not possible to list all members of the union as separate
+parameters.

 == Client JSON Protocol introspection ==

diff --git a/tests/Makefile b/tests/Makefile
index c608f9a..5cd6177 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -272,6 +272,8 @@ 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-int.json
 qapi-schema += args-invalid.json
 qapi-schema += args-member-array-bad.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-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/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index f571e1b..df91f3d 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -127,6 +127,9 @@
 { 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' },
   'returns': 'int' }
 { 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
+{ 'command': 'boxed-empty', 'box': true }
+{ '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:
@@ -147,13 +150,14 @@
 { 'struct': 'EventStructOne',
   'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }

-{ 'event': 'EVENT_A' }
+{ 'event': 'EVENT_A', 'box': true }
 { 'event': 'EVENT_B',
   'data': { } }
 { 'event': 'EVENT_C',
   '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' }

 # 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..8a00c6b 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -23,13 +23,15 @@ alternate AltStrNum
     case s: str
     case n: number
 event EVENT_A None
-   box=False
+   box=True
 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
+event EVENT_E UserDefZero
+   box=True
 object Empty1
 object Empty2
     base Empty1
@@ -153,6 +155,12 @@ 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-empty None -> None
+   gen=True success_response=True box=True
+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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 08/15] block: Simplify block_set_io_throttle
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (6 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 07/15] qapi: Implement boxed types for commands/events Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-05-24 15:21   ` [Qemu-devel] [Qemu-block] " Alberto Garcia
  2016-06-14 15:34   ` [Qemu-devel] " Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 09/15] block: Simplify drive-mirror Eric Blake
                   ` (8 subsequent siblings)
  16 siblings, 2 replies; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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>

---
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..26f7c0e 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
+#
+# The parameters for the block_set_io_throttle command.
+#
 # @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 cf5afa3..b8db496 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2625,49 +2625,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;
     }

@@ -2676,59 +2644,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)) {
@@ -2739,9 +2707,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 9836227..a0c3f4e 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1395,42 +1395,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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 09/15] block: Simplify drive-mirror
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (7 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 08/15] block: Simplify block_set_io_throttle Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 15:42   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 10/15] qapi-event: Reduce chance of collision with event data Eric Blake
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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>

---
v7: new patch
---
 qapi/block-core.json | 17 ++++++++++++-
 blockdev.c           | 72 ++++++++++++++++++++++------------------------------
 hmp.c                | 27 +++++++++-----------
 3 files changed, 59 insertions(+), 57 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index 26f7c0e..885a75a 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
+#
+# The parameters for the drive-mirror command.
+#
 # @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
@@ -1159,7 +1174,7 @@
 #
 # 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 b8db496..94850fd 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3457,19 +3457,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;
@@ -3480,11 +3468,12 @@ void qmp_drive_mirror(const char *device, const char *target,
     int flags;
     int64_t size;
     int ret;
+    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;
     }

@@ -3492,24 +3481,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;
     }

@@ -3519,18 +3509,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);
@@ -3549,20 +3539,20 @@ void qmp_drive_mirror(const char *device, const char *target,
         }
     }

-    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);
@@ -3578,8 +3568,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));
@@ -3589,7 +3579,7 @@ void qmp_drive_mirror(const char *device, const char *target,
      * file.
      */
     target_bs = NULL;
-    ret = bdrv_open(&target_bs, target, NULL, options,
+    ret = bdrv_open(&target_bs, arg->target, NULL, options,
                     flags | BDRV_O_NO_BACKING, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
@@ -3599,13 +3589,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,
-                           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,
+                           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);
     if (local_err) {
         error_propagate(errp, local_err);
diff --git a/hmp.c b/hmp.c
index a0c3f4e..c8f744b 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1059,31 +1059,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;
+    bool reuse = qdict_get_try_bool(qdict, "reuse", false);
     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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 10/15] qapi-event: Reduce chance of collision with event data
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (8 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 09/15] block: Simplify drive-mirror Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-14 16:28   ` Markus Armbruster
  2016-05-20 22:40   ` Eric Blake
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

When an event has data that is not boxed, we are exposing all of
its members alongside our local variables.  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 a shell that creates a
simple wrapper, then calls a worker that operates on only the
boxed version and thus has no user-supplied names to worry about
in naming its local variables.  For boxed events, we don't need
the wrapper.

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.

The generated file changes 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>

---
v7: new patch
---
 scripts/qapi.py       |  1 -
 scripts/qapi-event.py | 47 ++++++++++++++++++++++++++---------------------
 2 files changed, 26 insertions(+), 22 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 6742e7a..7d568d9 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1016,7 +1016,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 b8ca8c8..fe4e50d 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):
+    intro='void '
+    if impl and arg_type and not box:
+        box = True
+        intro='static void do_'
+    return '%(intro)sqapi_event_send_%(c_name)s(%(param)s)' % {
+        'intro': intro,
         'c_name': c_name(name.lower()),
         'param': gen_params(arg_type, box, 'Error **errp')}

@@ -53,12 +58,8 @@ def gen_param_var(typ):


 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():
+        box = False
     ret = mcgen('''

 %(proto)s
@@ -67,15 +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)

     ret += mcgen('''

@@ -93,20 +92,11 @@ def gen_event_send(name, arg_type, box):
         ret += mcgen('''
     v = qmp_output_visitor_new(&obj);

-''')
-
-        if box:
-            ret += mcgen('''
-    visit_type_%(c_name)s(v, NULL, &arg, &err);
-''',
-                         c_name=arg_type.c_name(), name=arg_type.name)
-        else:
-            ret += mcgen('''
     visit_start_struct(v, "%(name)s", NULL, 0, &err);
     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);
     }
@@ -136,6 +126,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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 11/15] qapi: Change Netdev into a flat union
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
@ 2016-05-20 22:40   ` Eric Blake
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 02/15] net: use Netdev instead of NetClientOptions in client init Eric Blake
                     ` (15 subsequent siblings)
  16 siblings, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kővágó, Zoltán, Kővágó,
	Zoltán, Jan Kiszka, Peter Maydell, Beniamino Galvani,
	Jason Wang, Alistair Francis, Peter Crosthwaite,
	Edgar E. Iglesias, Alexander Graf, Scott Wood, Peter Chubb,
	Michael Walle, Max Filippov, Scott Feldman, Jiri Pirko,
	David Gibson, Michael S. Tsirkin, Dmitry Fleytman,
	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

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

Except qapi-schema.json, this patch was generated by:

find . -name .git -prune -o -type f \! -name '*~' -print0 | \
  xargs -0 sed -i \
    -e 's/NetClientOptionsKind/NetClientDriver/g' \
    -e 's/NET_CLIENT_OPTIONS_KIND_/NET_CLIENT_DRIVER_/g' \
    -e 's/netdev->opts/netdev/g'

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com>

Additional changes:
Rebase the patch on top of an earlier change from netdev->kind to
netdev->type, so that tweak is no longer needed here.  Rebase to
latest master which enhanced multiqueue.

Rework so that NetdevLegacy doesn't pollute QMP command but is instead
copied piecewise into the new Netdev, which means that NetClientOptions
must still remain in qapi. Since legacy previously always rejected
'hubport', we can now make that explicit by having the two unions be
slightly different; but that means we must manually map between the
two structures.

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

---
v7: rebase to latest master
v6: rebase to latest master
---
 qapi-schema.json                 |  47 ++++++++++----
 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/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               |  18 +++---
 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/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                 |  18 +++---
 46 files changed, 225 insertions(+), 167 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 54634c4..4fee44b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2744,16 +2744,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',
@@ -2791,20 +2807,25 @@
     'opts':  'NetClientOptions' } }

 ##
-# @Netdev
+# @NetClientOptions
 #
-# 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': 'NetClientOptions',
   '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 73e4c46..9bdda40 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -59,7 +59,7 @@ typedef int (SetVnetLE)(NetClientState *, bool);
 typedef int (SetVnetBE)(NetClientState *, bool);

 typedef struct NetClientInfo {
-    NetClientOptionsKind type;
+    NetClientDriver type;
     size_t size;
     NetReceive *receive;
     NetReceive *receive_raw;
@@ -105,7 +105,7 @@ typedef struct NICState {
 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 0346f3e..07116c9 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1194,7 +1194,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 8e79b55..490007c 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -1785,7 +1785,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/eepro100.c b/hw/net/eepro100.c
index 9b4b9b5..0a28176 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 9055ea8..033e6e6 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -653,7 +653,7 @@ static void imx_fec_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_imx_fec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = imx_fec_can_receive,
     .receive = imx_fec_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 740cd98..0f2efab 100644
--- a/hw/net/mipsnet.c
+++ b/hw/net/mipsnet.c
@@ -219,7 +219,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 a7f5a94..294e30b 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 c6094fb..bdbee3b 100644
--- a/hw/net/opencores_eth.c
+++ b/hw/net/opencores_eth.c
@@ -474,7 +474,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 1e5ec14..739ca87 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -3411,7 +3411,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 a8266f8..4908ece 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -260,7 +260,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 6e1032f..2b7a3f1 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:
@@ -123,7 +123,7 @@ uint64_t vhost_net_get_max_queues(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");
@@ -219,7 +219,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) {
@@ -234,7 +234,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);
@@ -256,7 +256,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);
@@ -293,7 +293,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;
         }
      }
@@ -385,10 +385,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 a0ace28..2846520 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 20f26b7..e86f030 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2062,7 +2062,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 7281730..71c0c4e 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -270,7 +270,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 74306b5..72378a6 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1337,7 +1337,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 3e99006..2ca9692 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3044,8 +3044,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]);
     }
 }

@@ -3245,7 +3245,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;
@@ -3270,7 +3270,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;
@@ -3382,7 +3382,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;
@@ -3399,13 +3399,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/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 ec4e700..c49a295 100644
--- a/net/net.c
+++ b/net/net.c
@@ -291,7 +291,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);
@@ -362,13 +362,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);

@@ -377,7 +377,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;
@@ -433,7 +433,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);
             }
@@ -605,7 +605,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();
         }
@@ -779,7 +779,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;
@@ -790,7 +790,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;
@@ -871,8 +871,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) {
@@ -932,39 +932,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;
@@ -972,34 +971,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 NetClientOptions *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_CLIENT_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_CLIENT_OPTIONS_KIND_NIC:
+            legacy.type = NET_CLIENT_DRIVER_NIC;
+            legacy.u.nic = *opts->u.nic.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_USER:
+            legacy.type = NET_CLIENT_DRIVER_USER;
+            legacy.u.user = *opts->u.user.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_TAP:
+            legacy.type = NET_CLIENT_DRIVER_TAP;
+            legacy.u.tap = *opts->u.tap.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_L2TPV3:
+            legacy.type = NET_CLIENT_DRIVER_L2TPV3;
+            legacy.u.l2tpv3 = *opts->u.l2tpv3.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_SOCKET:
+            legacy.type = NET_CLIENT_DRIVER_SOCKET;
+            legacy.u.socket = *opts->u.socket.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_VDE:
+            legacy.type = NET_CLIENT_DRIVER_VDE;
+            legacy.u.vde = *opts->u.vde.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_DUMP:
+            legacy.type = NET_CLIENT_DRIVER_DUMP;
+            legacy.u.dump = *opts->u.dump.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_BRIDGE:
+            legacy.type = NET_CLIENT_DRIVER_BRIDGE;
+            legacy.u.bridge = *opts->u.bridge.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_NETMAP:
+            legacy.type = NET_CLIENT_DRIVER_NETMAP;
+            legacy.u.netmap = *opts->u.netmap.data;
+            break;
+        case NET_CLIENT_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)");
@@ -1007,17 +1044,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;
     }
@@ -1137,7 +1174,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;
     }
@@ -1228,7 +1265,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");
@@ -1258,7 +1295,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;
@@ -1304,7 +1341,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);

@@ -1317,10 +1354,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);
         }
@@ -1334,7 +1371,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) {
@@ -1361,7 +1398,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;
             }
@@ -1402,7 +1439,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);
@@ -1434,7 +1471,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 addeedd..aa2d34a 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -401,7 +401,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,
@@ -422,7 +422,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 d258352..b831afa 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -346,7 +346,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,
@@ -429,7 +429,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,
@@ -704,8 +704,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 9531880..8844d90 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;

@@ -891,7 +891,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 4607c7b..01b5453 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -32,7 +32,7 @@ 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;
 }

@@ -47,7 +47,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)) {
@@ -71,7 +71,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)) {
@@ -146,20 +146,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,
@@ -176,7 +176,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);

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

* [PATCH v7 11/15] qapi: Change Netdev into a flat union
@ 2016-05-20 22:40   ` Eric Blake
  0 siblings, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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, Kővágó,
	Zoltán, Anthony Perard, Samuel Thibault, Jiri Pirko,
	Alistair Francis, Beniamino Galvani, open list:Musicpal,
	Jan Kiszka, Scott Wood

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

Except qapi-schema.json, this patch was generated by:

find . -name .git -prune -o -type f \! -name '*~' -print0 | \
  xargs -0 sed -i \
    -e 's/NetClientOptionsKind/NetClientDriver/g' \
    -e 's/NET_CLIENT_OPTIONS_KIND_/NET_CLIENT_DRIVER_/g' \
    -e 's/netdev->opts/netdev/g'

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com>

Additional changes:
Rebase the patch on top of an earlier change from netdev->kind to
netdev->type, so that tweak is no longer needed here.  Rebase to
latest master which enhanced multiqueue.

Rework so that NetdevLegacy doesn't pollute QMP command but is instead
copied piecewise into the new Netdev, which means that NetClientOptions
must still remain in qapi. Since legacy previously always rejected
'hubport', we can now make that explicit by having the two unions be
slightly different; but that means we must manually map between the
two structures.

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

---
v7: rebase to latest master
v6: rebase to latest master
---
 qapi-schema.json                 |  47 ++++++++++----
 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/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               |  18 +++---
 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/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                 |  18 +++---
 46 files changed, 225 insertions(+), 167 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 54634c4..4fee44b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2744,16 +2744,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',
@@ -2791,20 +2807,25 @@
     'opts':  'NetClientOptions' } }

 ##
-# @Netdev
+# @NetClientOptions
 #
-# 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': 'NetClientOptions',
   '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 73e4c46..9bdda40 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -59,7 +59,7 @@ typedef int (SetVnetLE)(NetClientState *, bool);
 typedef int (SetVnetBE)(NetClientState *, bool);

 typedef struct NetClientInfo {
-    NetClientOptionsKind type;
+    NetClientDriver type;
     size_t size;
     NetReceive *receive;
     NetReceive *receive_raw;
@@ -105,7 +105,7 @@ typedef struct NICState {
 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 0346f3e..07116c9 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1194,7 +1194,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 8e79b55..490007c 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -1785,7 +1785,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/eepro100.c b/hw/net/eepro100.c
index 9b4b9b5..0a28176 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 9055ea8..033e6e6 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -653,7 +653,7 @@ static void imx_fec_cleanup(NetClientState *nc)
 }

 static NetClientInfo net_imx_fec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = imx_fec_can_receive,
     .receive = imx_fec_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 740cd98..0f2efab 100644
--- a/hw/net/mipsnet.c
+++ b/hw/net/mipsnet.c
@@ -219,7 +219,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 a7f5a94..294e30b 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 c6094fb..bdbee3b 100644
--- a/hw/net/opencores_eth.c
+++ b/hw/net/opencores_eth.c
@@ -474,7 +474,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 1e5ec14..739ca87 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -3411,7 +3411,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 a8266f8..4908ece 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -260,7 +260,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 6e1032f..2b7a3f1 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:
@@ -123,7 +123,7 @@ uint64_t vhost_net_get_max_queues(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");
@@ -219,7 +219,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) {
@@ -234,7 +234,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);
@@ -256,7 +256,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);
@@ -293,7 +293,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;
         }
      }
@@ -385,10 +385,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 a0ace28..2846520 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 20f26b7..e86f030 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2062,7 +2062,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 7281730..71c0c4e 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -270,7 +270,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 74306b5..72378a6 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1337,7 +1337,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 3e99006..2ca9692 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3044,8 +3044,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]);
     }
 }

@@ -3245,7 +3245,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;
@@ -3270,7 +3270,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;
@@ -3382,7 +3382,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;
@@ -3399,13 +3399,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/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 ec4e700..c49a295 100644
--- a/net/net.c
+++ b/net/net.c
@@ -291,7 +291,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);
@@ -362,13 +362,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);

@@ -377,7 +377,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;
@@ -433,7 +433,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);
             }
@@ -605,7 +605,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();
         }
@@ -779,7 +779,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;
@@ -790,7 +790,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;
@@ -871,8 +871,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) {
@@ -932,39 +932,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;
@@ -972,34 +971,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 NetClientOptions *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_CLIENT_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_CLIENT_OPTIONS_KIND_NIC:
+            legacy.type = NET_CLIENT_DRIVER_NIC;
+            legacy.u.nic = *opts->u.nic.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_USER:
+            legacy.type = NET_CLIENT_DRIVER_USER;
+            legacy.u.user = *opts->u.user.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_TAP:
+            legacy.type = NET_CLIENT_DRIVER_TAP;
+            legacy.u.tap = *opts->u.tap.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_L2TPV3:
+            legacy.type = NET_CLIENT_DRIVER_L2TPV3;
+            legacy.u.l2tpv3 = *opts->u.l2tpv3.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_SOCKET:
+            legacy.type = NET_CLIENT_DRIVER_SOCKET;
+            legacy.u.socket = *opts->u.socket.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_VDE:
+            legacy.type = NET_CLIENT_DRIVER_VDE;
+            legacy.u.vde = *opts->u.vde.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_DUMP:
+            legacy.type = NET_CLIENT_DRIVER_DUMP;
+            legacy.u.dump = *opts->u.dump.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_BRIDGE:
+            legacy.type = NET_CLIENT_DRIVER_BRIDGE;
+            legacy.u.bridge = *opts->u.bridge.data;
+            break;
+        case NET_CLIENT_OPTIONS_KIND_NETMAP:
+            legacy.type = NET_CLIENT_DRIVER_NETMAP;
+            legacy.u.netmap = *opts->u.netmap.data;
+            break;
+        case NET_CLIENT_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)");
@@ -1007,17 +1044,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;
     }
@@ -1137,7 +1174,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;
     }
@@ -1228,7 +1265,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");
@@ -1258,7 +1295,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;
@@ -1304,7 +1341,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);

@@ -1317,10 +1354,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);
         }
@@ -1334,7 +1371,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) {
@@ -1361,7 +1398,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;
             }
@@ -1402,7 +1439,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);
@@ -1434,7 +1471,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 addeedd..aa2d34a 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -401,7 +401,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,
@@ -422,7 +422,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 d258352..b831afa 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -346,7 +346,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,
@@ -429,7 +429,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,
@@ -704,8 +704,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 9531880..8844d90 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;

@@ -891,7 +891,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 4607c7b..01b5453 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -32,7 +32,7 @@ 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;
 }

@@ -47,7 +47,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)) {
@@ -71,7 +71,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)) {
@@ -146,20 +146,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,
@@ -176,7 +176,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);

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

* [Qemu-devel] [PATCH v7 12/15] net: Use correct type for bool flag
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (10 preceding siblings ...)
  2016-05-20 22:40   ` Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 13/15] net: Complete qapi-fication of netdev_add Eric Blake
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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>

---
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 9bdda40..74b32e0 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -185,7 +185,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 72378a6..2eda481 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1399,7 +1399,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 c49a295..e159506 100644
--- a/net/net.c
+++ b/net/net.c
@@ -962,7 +962,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;
@@ -1062,7 +1062,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;
@@ -1155,7 +1155,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);
@@ -1185,7 +1185,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)
@@ -1495,7 +1495,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;
@@ -1509,7 +1509,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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 13/15] net: Complete qapi-fication of netdev_add
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (11 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 12/15] net: Use correct type for bool flag Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-16 13:40   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 14/15] qapi: Allow anonymous branch types in flat union Eric Blake
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, 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, while
making no changes to the set of previously-valid QMP commands
that would work, and without breaking command line parsing.

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

---
v7: no change
v6: don't lose qemu_opts handling
---
 qapi-schema.json  | 15 +++------------
 include/net/net.h |  1 -
 net/net.c         |  6 +++---
 qmp-commands.hx   |  2 +-
 4 files changed, 7 insertions(+), 17 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 4fee44b..fee4d07 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2301,26 +2301,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 74b32e0..8f9be98 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -193,7 +193,6 @@ 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/net/net.c b/net/net.c
index e159506..b93ff00 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1188,7 +1188,7 @@ 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;
@@ -1199,12 +1199,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 94847e5..3804030 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -949,7 +949,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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 14/15] qapi: Allow anonymous branch types in flat union
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (12 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 13/15] net: Complete qapi-fication of netdev_add Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-06-16 14:33   ` Markus Armbruster
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 15/15] schema: Drop pointless empty type CpuInfoOther Eric Blake
                   ` (2 subsequent siblings)
  16 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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 '_empty' struct nor a 'visit_type__empty_members()'
corresponding to the special ':empty' type; but when the branch
is truly empty, there's nothing to do.

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>

---
v7: new patch
---
 scripts/qapi.py                          | 21 +++++++++++++++------
 scripts/qapi-types.py                    | 10 +++++++---
 scripts/qapi-visit.py                    | 14 ++++++++++----
 tests/Makefile                           |  1 +
 tests/qapi-schema/flat-union-inline.err  |  2 +-
 tests/qapi-schema/flat-union-inline.json |  5 ++---
 tests/qapi-schema/qapi-schema-test.json  |  5 +++--
 tests/qapi-schema/qapi-schema-test.out   |  8 ++++++--
 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
 12 files changed, 51 insertions(+), 21 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 7d568d9..4c531e7 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.
@@ -1061,6 +1063,9 @@ class QAPISchemaMember(object):
                 return '(parameter of %s)' % owner[:-4]
             elif owner.endswith('-base'):
                 return '(base of %s)' % owner[:-5]
+            elif owner.endswith('-branch'):
+                return ('(member of %s branch %s)'
+                        % tuple(owner[:-7].split(':')))
             else:
                 assert owner.endswith('-wrapper')
                 # Unreachable and not implemented
@@ -1335,7 +1340,11 @@ 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):
+            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):
@@ -1356,7 +1365,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:
@@ -1375,7 +1384,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(
@@ -1485,7 +1494,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-types.py b/scripts/qapi-types.py
index 5a9e2da..f1edab2 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -61,7 +61,8 @@ def gen_object(name, base, members, variants):
     ret = ''
     if variants:
         for v in variants.variants:
-            if isinstance(v.type, QAPISchemaObjectType):
+            if (isinstance(v.type, QAPISchemaObjectType)
+                    and not (v.type.is_implicit() and v.type.is_empty())):
                 ret += gen_object(v.type.name, v.type.base,
                                   v.type.local_members, v.type.variants)

@@ -123,11 +124,14 @@ def gen_variants(variants):
                 c_name=c_name(variants.tag_member.name))

     for var in variants.variants:
+        typ = var.type.c_unboxed_type()
+        if (isinstance(var.type, QAPISchemaObjectType) and
+                var.type.is_empty() and var.type.is_implicit()):
+            typ = 'char'
         ret += mcgen('''
         %(c_type)s %(c_name)s;
 ''',
-                     c_type=var.type.c_unboxed_type(),
-                     c_name=c_name(var.name))
+                     c_type=typ, c_name=c_name(var.name))

     ret += mcgen('''
     } u;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 07ae6d1..46f8b39 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -79,13 +79,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 b/tests/Makefile
index 5cd6177..d7d9597 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -379,6 +379,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 df91f3d..5128b49 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' ] }

 # for testing nested structs
 { 'struct': 'UserDefOne',
@@ -81,7 +81,8 @@
   'base': { '*integer': 'int', 'string': 'str', 'enum1': 'QEnumTwo' },
   'discriminator': 'enum1',
   'data': { 'value1' : 'UserDefC', # intentional forward reference
-            'value2' : 'UserDefB' } }
+            'value2' : { },
+            'value3' : { 'boolean': 'bool', '*number': 'number' } } }

 { 'struct': 'WrapAlternate',
   'data': { 'alt': 'UserDefAlternate' } }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 8a00c6b..7fac2da 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -50,7 +50,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']
     prefix QENUM_TWO
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
@@ -82,7 +82,8 @@ object UserDefFlatUnion2
     base q_obj_UserDefFlatUnion2-base
     tag enum1
     case value1: UserDefC
-    case value2: UserDefB
+    case value2: q_empty
+    case value3: q_obj_UserDefFlatUnion2:value3-branch
 object UserDefNativeListUnion
     member type: UserDefNativeListUnionKind optional=False
     tag type
@@ -179,6 +180,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:value3-branch
+    member boolean: bool optional=False
+    member number: number 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] 53+ messages in thread

* [Qemu-devel] [PATCH v7 15/15] schema: Drop pointless empty type CpuInfoOther
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (13 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 14/15] qapi: Allow anonymous branch types in flat union Eric Blake
@ 2016-05-20 22:40 ` Eric Blake
  2016-05-20 22:59 ` [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
  2016-06-16 14:57 ` Markus Armbruster
  16 siblings, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:40 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.

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

---
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 fee4d07..2afcbb9 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -785,7 +785,7 @@
             'ppc': 'CpuInfoPPC',
             'mips': 'CpuInfoMIPS',
             'tricore': 'CpuInfoTricore',
-            'other': 'CpuInfoOther' } }
+            'other': {} } }

 ##
 # @CpuInfoX86:
@@ -845,16 +845,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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F)
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (14 preceding siblings ...)
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 15/15] schema: Drop pointless empty type CpuInfoOther Eric Blake
@ 2016-05-20 22:59 ` Eric Blake
  2016-06-16 14:57 ` Markus Armbruster
  16 siblings, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-05-20 22:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

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

On 05/20/2016 04:40 PM, Eric Blake wrote:
> The end is nearly in sight!  Last August, I started a process of
> cleaning up qapi on top of Markus' introspection work.  Several
> hundred emails later, and numerous intermediate series out of
> the way, I'm finally at the end of my queue of patches that were
> originally sent in v5:
> https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg05410.html
> 
> v6 was here (yes, 5 months ago):
> https://www.mail-archive.com/qemu-devel@nongnu.org/msg343192.html
> 
> Prerequisites:
> My clone/JSON visitor (weak; this series is mostly orthogonal to
> that one, if we want to swap the order of the two based on review
> complexity):
> https://www.mail-archive.com/qemu-devel%40nongnu.org/msg371555.html

And I promptly forget to add:

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

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

* Re: [Qemu-devel] [Qemu-block] [PATCH v7 08/15] block: Simplify block_set_io_throttle
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 08/15] block: Simplify block_set_io_throttle Eric Blake
@ 2016-05-24 15:21   ` Alberto Garcia
  2016-06-14 15:34   ` [Qemu-devel] " Markus Armbruster
  1 sibling, 0 replies; 53+ messages in thread
From: Alberto Garcia @ 2016-05-24 15:21 UTC (permalink / raw)
  To: Eric Blake, qemu-devel
  Cc: Kevin Wolf, Luiz Capitulino, armbru, open list:Block layer core,
	Max Reitz

On Sat 21 May 2016 12:40:17 AM CEST, 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 throttle parameters.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

It looks much better now, thanks!

Reviewed-by: Alberto Garcia <berto@igalia.com>

Berto

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

* Re: [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors Eric Blake
@ 2016-06-14 12:35   ` Markus Armbruster
  2016-06-16 14:46     ` Markus Armbruster
  0 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 12:35 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Rather than having two separate visitor callbacks with items
> already broken out, pass the actual QAPISchemaObjectType object
> to the visitor.  This lets the visitor access things like
> type.is_implicit() without needing another parameter, resolving
> a TODO from previous patches.
>
> For convenience and consistency, the 'name' and 'info' parameters
> are still provided, even though they are now redundant with
> 'typ.name' and 'typ.info'.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

We've seen this one before :)

The previous review is relevant:
Message-ID: <878u0hn08l.fsf@dusky.pond.sub.org>
http://lists.nongnu.org/archive/html/qemu-devel/2016-04/msg02151.html

Quote its recap of pros and cons:

* The existing interface

      def visit_object_type(self, name, info, base, members, variants):
      def visit_object_type_flat(self, name, info, members, variants):

  is explicit and narrow, but when you need more information, you have
  to add parameters or functions.

* The new interface

     def visit_object_type(self, name, info, typ):

  avoids that, but now its users can access everything.

End quote.  Let's see how this series profits from the patch, and
whether we want to change the other visit methods as well for
consistency.

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

* Re: [Qemu-devel] [PATCH v7 02/15] net: use Netdev instead of NetClientOptions in client init
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 02/15] net: use Netdev instead of NetClientOptions in client init Eric Blake
@ 2016-06-14 13:11   ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 13:11 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Michael S. Tsirkin, Jan Kiszka, Jason Wang,
	Vincenzo Maffione, Kővágó,
	Zoltán, Samuel Thibault, Giuseppe Lettieri, Luigi Rizzo

Eric Blake <eblake@redhat.com> writes:

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

First posted almost a year ago.  I'm glad we're finally ready for this
work.  Zoltán, I'm sorry it has taken us so long.

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

That's work enabled by this patch, not done by it.  We won't actually
get rid of NetClientOptions, only confine it to legacy command line
support.

> 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; rebase to qapi changes]
> Signed-off-by: Eric Blake <eblake@redhat.com>

The patch is mostly mechanical: wherever we replace parameter
NetClientOptions *opts by Netdev *netdev, we replace opts by
netdev->opts, because netdev wraps opts:

    struct Netdev {
        char *id;
        NetClientOptions *opts;
    };

The interesting part is the change to net_client_init1(), which looks
sane.

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

* Re: [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered Eric Blake
@ 2016-06-14 13:24   ` Markus Armbruster
  2016-06-14 13:46     ` Eric Blake
  0 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 13:24 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> 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.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

I'd let the cases not mentioned default to the empty type (ample
precedence in other languages), but I can live with making the user ask
for the empty type explicitly.  But we should then make that less
cumbersome than now: you have to define an empty struct type, and use
that.  Examples of such hoop-jumping: CpuInfoOther, Abort,
NetdevNoneOptions.

Patch looks good.

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

* Re: [Qemu-devel] [PATCH v7 04/15] qapi: Hide tag_name data member of variants
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 04/15] qapi: Hide tag_name data member of variants Eric Blake
@ 2016-06-14 13:32   ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 13:32 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> 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.  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>
>
> ---
> 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 82d96e2..3554ab1 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -1086,7 +1086,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
>
> @@ -1096,8 +1096,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)
> @@ -1127,7 +1127,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

The implicit "tag type" is now shown.  You can still recognize simple
unions by their -wrapper types.  Okay.

> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index ccd1704..65ca19a 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)

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

* Re: [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered
  2016-06-14 13:24   ` Markus Armbruster
@ 2016-06-14 13:46     ` Eric Blake
  2016-06-28  1:52       ` Eric Blake
  2016-07-03  2:34       ` Eric Blake
  0 siblings, 2 replies; 53+ messages in thread
From: Eric Blake @ 2016-06-14 13:46 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/14/2016 07:24 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> 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.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> I'd let the cases not mentioned default to the empty type (ample
> precedence in other languages), but I can live with making the user ask
> for the empty type explicitly.  But we should then make that less
> cumbersome than now: you have to define an empty struct type, and use
> that.  Examples of such hoop-jumping: CpuInfoOther, Abort,
> NetdevNoneOptions.

Later in the series, I do just that, so that we can write 'other':{}
instead of 'other':'CpuInfoOther'.

What I did not do (but maybe should) is make that short syntax possible
on simple unions (so that we could do 'abort':{} rather than
'abort':'Abort') - and merely distinguish that simple unions cannot
stick anything within the {}, thus leaving non-empty anonymous branches
only for discriminated unions.  Can do that as a followup or if this
series needs a respin.

> 
> Patch looks good.
> 

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

* Re: [Qemu-devel] [PATCH v7 05/15] qapi: Add type.is_empty() helper
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 05/15] qapi: Add type.is_empty() helper Eric Blake
@ 2016-06-14 14:01   ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 14:01 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> And use it in qapi-types and qapi-event.  In the near future, we
> want to lift our artificial restriction of no variants at the
> top level of an event, at which point, inlining our check for
> whether members is empty will no longer be sufficient; but
> adding an inline check for variants everywhere we are also looking
> at members adds verbosity.  For now, we already asserted that no
> variants were present, so the conversion from 'if type.members'
> to 'if not type.is_empty()' has no semantic change, but does
> future-proof the code if we eventually do support variants in those
> contexts.

Suggest:

qapi: Add type.is_empty() helper

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 new helper method is_empty() now.  Future-proof it by checking
variants, too.  Since all current callers assert that there are no
variants, this is no semantic change.

>
> No change to generated code.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v3: 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-event.py | 6 +++---
>  scripts/qapi-types.py | 2 +-
>  3 files changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 3554ab1..90ea30c 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -996,6 +996,10 @@ class QAPISchemaObjectType(QAPISchemaType):
>          # _def_predefineds()
>          return self.name.startswith('q_')
>
> +    def is_empty(self):
> +        assert self.members is not None

Not to be called before .check().  Good.

> +        return not self.members and not self.variants
> +
>      def c_name(self):
>          return QAPISchemaType.c_name(self)
>
> 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):

Let's verify your claim "already asserted that no variants present".

>  ''',
>                  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;
   ''')
           ret += gen_param_var(arg_type)

gen_param_var() indeed asserts.

> @@ -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 b3038e5..5a9e2da 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;
>  ''')

I can't see an assertion here.  qapi.py should reject use of base types
with variants, though.  Unless there is an assertion I missed, the
commit message should be updated to be less specific.

To check patch completeness, let's examine the other tests of .members.
I can see just three more, all in gen_marshal().

Variants can't happen there (qapi.py should reject base types with
variants), the code isn't prepared for them, and it asserts in
gen_call().

Should we use .is_empty() there?

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

* Re: [Qemu-devel] [PATCH v7 06/15] qapi: Plumb in 'box' to qapi generator lower levels
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 06/15] qapi: Plumb in 'box' to qapi generator lower levels Eric Blake
@ 2016-06-14 14:39   ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 14:39 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

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

Passing arguments as a single C struct may also be convenient in other
cases, e.g. when the callers already have them in a struct, or when the
number of parameters is large.

>                                 This patch adds the
> internal plubming of a 'box' flag associated with each

plumbing

> command and event.  For this patch, no behavior changes,
> other than the testsuite outputting the value of the new
> flag (always False for now).
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v7: rebase to master
> v6: rebase to earlier changes
> ---
>  scripts/qapi.py                         | 46 ++++++++++++++++++++-------------
>  scripts/qapi-commands.py                | 20 +++++++-------
>  scripts/qapi-event.py                   | 37 +++++++++++++++-----------
>  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, 84 insertions(+), 57 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 90ea30c..108363d 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -823,10 +823,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
>
>
> @@ -1158,7 +1158,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)
> @@ -1168,12 +1169,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
> +            if not self.box:
> +                assert not self.arg_type.variants

This looks premature.  As long as box=True isn't implemented, I'd assert
not self.box here, with a "not implemented" comment.

>          if self._ret_type_name:
>              self.ret_type = schema.lookup_type(self._ret_type_name)
>              assert isinstance(self.ret_type, QAPISchemaType)
> @@ -1181,24 +1184,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
> +            if not self.box:
> +                assert not self.arg_type.variants

Likewise.

>
>      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):
> @@ -1374,6 +1379,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))
> @@ -1381,15 +1387,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:
> @@ -1632,18 +1639,21 @@ 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:
> +        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))

I understand you're rewriting the code this way so you can slot in the
implementation later.  The alternative is to simply assert not box for
now, like this:

  -def gen_params(arg_type, extra):
  +def gen_params(arg_type, box, extra):
       if not arg_type:
           return extra
  -    assert not arg_type.variants
  +    assert not arg_type.variants and not box    # not implemented
       ret = ''

Restricts this patch to propagating the box value to the places that
need it, and leaves the restructuring of the code those places need for
box to the patch that actually implements box.  Might come out neater.
Your choice.  If you choose to keep the restructuring here, please
prepare the reader for it in the commit message.

>      if extra:
>          ret += sep + extra
>      return ret
> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index 34b6a3a..79d4eea 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:

More restructuring, only less invasive.

> @@ -92,7 +94,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
> @@ -134,7 +136,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 arg_type.members) or ret_type:
> @@ -210,16 +212,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 09c0a2a..fd953fe 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()
> @@ -52,7 +52,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
> @@ -67,14 +67,15 @@ 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('''
>      QObject *obj;
>      Visitor *v;
>  ''')
> -        ret += gen_param_var(arg_type)
> +        if not box:
> +            ret += gen_param_var(arg_type)
>
>      ret += mcgen('''
>

More restructuring in this and the following hunks.

> @@ -92,6 +93,12 @@ def gen_event_send(name, arg_type):
>          ret += mcgen('''
>      v = qmp_output_visitor_new(&obj);
>
> +''')
> +
> +        if box:
> +            assert False     # not implemented
> +        else:
> +            ret += mcgen('''
>      visit_start_struct(v, "%(name)s", NULL, 0, &err);
>      if (err) {
>          goto out;

The need for part would probably be obvious when done together with the
implementation.  It isn't now.

> @@ -101,14 +108,14 @@ def gen_event_send(name, arg_type):
>          visit_check_struct(v, &err);
>      }
>      visit_end_struct(v, NULL);
> -    if (err) {
> -        goto out;
> -    }
> +''',
> +                     name=name, c_name=arg_type.c_name())
> +        ret += gen_err_check()

You're replacing the open-coded emission of the error check by
gen_err_check().  I feel that helper became pointless in commit
12f254f.  Eliminate it?

> +        ret += mcgen('''
>
>      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);
> @@ -145,9 +152,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 474eafd..7646c89 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)})
>
[Boring tests/qapi-schema/*.out diffs snipped...]
> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index 65ca19a..fb899a1 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):

No test cases for the new box syntax.  I figure you'll have some in the
patches that implement box.

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

* Re: [Qemu-devel] [PATCH v7 07/15] qapi: Implement boxed types for commands/events
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 07/15] qapi: Implement boxed types for commands/events Eric Blake
@ 2016-06-14 15:27   ` Markus Armbruster
  2016-06-14 17:22     ` Eric Blake
  0 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 15:27 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.  For structs, it makes it possible
> to pass a single qapi type instead of a breakout of all
> struct members; for unions, it is now possible to use a
> union as the data for a command or event.
>
> Generated code is unchanged, as long as no client uses the
> new feature.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v7: rebase to latest
> v6: retitle, rebase, and merge v5 40/46 and 41/46 into one patch
> ---
>  scripts/qapi.py                         | 29 +++++++++++++++++++++--------
>  scripts/qapi-commands.py                |  3 ++-
>  scripts/qapi-event.py                   |  5 ++++-
>  tests/test-qmp-commands.c               | 12 ++++++++++++
>  docs/qapi-code-gen.txt                  | 24 ++++++++++++++++++++++--
>  tests/Makefile                          |  2 ++
>  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-union.err        |  2 +-
>  tests/qapi-schema/args-union.json       |  3 +--
>  tests/qapi-schema/qapi-schema-test.json |  6 +++++-
>  tests/qapi-schema/qapi-schema-test.out  | 10 +++++++++-
>  18 files changed, 87 insertions(+), 17 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
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 108363d..6742e7a 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']
>      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']
>      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'],
> @@ -1640,12 +1652,13 @@ 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():
>          return extra

When arg_type is empty, box gets ignored.  That's not wrong, but I'd
skin this cat differently: when box=true, pass a single arg_type
argument, no matter what the type is.

>      ret = ''
>      sep = ''
>      if box:
> -        assert False     # not implemented
> +        ret += '%s arg' % arg_type.c_param_type()
> +        sep = ', '
>      else:
>          for memb in arg_type.members:
>              ret += sep
> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index 79d4eea..dc41fad 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
> +        if arg_type and not arg_type.is_empty():
> +            argstr = '&arg, '

Related.

>      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 fd953fe..b8ca8c8 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -96,7 +96,10 @@ def gen_event_send(name, arg_type, box):
>  ''')
>
>          if box:
> -            assert False     # not implemented
> +            ret += mcgen('''
> +    visit_type_%(c_name)s(v, NULL, &arg, &err);
> +''',
> +                         c_name=arg_type.c_name(), name=arg_type.name)
>          else:
>              ret += mcgen('''
>      visit_start_struct(v, "%(name)s", NULL, 0, &err);

Same design alternative for empty types, only here it's not visible in
the patch, due to your split of the implementation between here and
the previous patch.

> diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
> index 87fc759..007db37 100644
> --- a/tests/test-qmp-commands.c
> +++ b/tests/test-qmp-commands.c
> @@ -60,6 +60,18 @@ QObject *qmp_guest_sync(QObject *arg, Error **errp)
>      return arg;
>  }
>
> +void qmp_boxed_empty(Error **errp)
> +{
> +}

Demontrates that 'box': true with an empty type isn't boxed.

> +
> +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 aefc29e..40f050e 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,17 @@ which would validate this Client JSON Protocol transaction:
>   => { "execute": "my-second-command" }
>   <= { "return": [ { "value": "one" }, { } ] }
>
> +By default, the generator creates a marshalling function that converts
> +an input QDict into a function call implemented by the user, and

Well, the called function is implemented by the user.

> +declares a prototype for the user's function which has a parameter for
> +each member of the argument struct, including boolean arguments that
> +describe whether optional arguments were provided.  But if the QAPI
> +description includes the key 'box' with the boolean value true, the
> +user call prototype will have only a single parameter for the overall
> +generated C structure.  The 'box' key is required in order to use a
> +union as an input argument, since it is not possible to list all
> +members of the union as separate parameters.
> +

Neglects to mention that 'data' is less restricted with 'box': true.

Suggest:

    The generator emits a prototype for the user's function implementing
    the command.  Normally, 'data' is or names a struct type, and its
    members are passed as separate arguments to this function.  If the
    command definition includes a key 'box' with the boolean value true,
    then the arguments are passed to the function as a single pointer to
    the QAPI type generated for 'data'.  'data' may name an arbitrary
    complex type then.

This still glosses over the has_ arguments, but it's no worse than
before.

Only then mention the marshalling:

    The generator 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 +495,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 +517,14 @@ Resulting in this JSON object:
>    "data": { "b": "test string" },
>    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
>
> +By default, the generator creates a C function that takes as
> +parameters each member of the argument struct and turns it into the
> +appropriate JSON Client event.  But if the QAPI description includes
> +the key 'box' with the boolean value true, the event function will
> +have only a single parameter for the overall generated C structure.
> +The 'box' key is required in order to use a union as the data key,
> +since it is not possible to list all members of the union as separate
> +parameters.

Suggest:

    The generator emits a function to send the event.  Normally, 'data'
    is or names a struct type, and its members are passed as separate
    arguments to this function.  If the event definition includes a key
    'box' with the boolean value true, then the arguments are passed to
    the function as a single pointer to the QAPI type generated for
    'data'.  'data' may name an arbitrary complex type then.

>
>  == Client JSON Protocol introspection ==
>
> diff --git a/tests/Makefile b/tests/Makefile
> index c608f9a..5cd6177 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -272,6 +272,8 @@ 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-int.json
>  qapi-schema += args-invalid.json
>  qapi-schema += args-member-array-bad.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-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/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index f571e1b..df91f3d 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -127,6 +127,9 @@
>  { 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' },
>    'returns': 'int' }
>  { 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
> +{ 'command': 'boxed-empty', 'box': true }
> +{ '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:
> @@ -147,13 +150,14 @@
>  { 'struct': 'EventStructOne',
>    'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
>
> -{ 'event': 'EVENT_A' }
> +{ 'event': 'EVENT_A', 'box': true }

This is case "empty".

Separate tests for both values of box would be cleaner, even though they
produce the exact same result.  If we decide to obey box even with empty
types, they don't.

>  { 'event': 'EVENT_B',
>    'data': { } }
>  { 'event': 'EVENT_C',
>    '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' }

This is case "struct".

Missing: case "union".

>
>  # 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..8a00c6b 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -23,13 +23,15 @@ alternate AltStrNum
>      case s: str
>      case n: number
>  event EVENT_A None
> -   box=False
> +   box=True
>  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
> +event EVENT_E UserDefZero
> +   box=True
>  object Empty1
>  object Empty2
>      base Empty1
> @@ -153,6 +155,12 @@ 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-empty None -> None
> +   gen=True success_response=True box=True
> +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

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

* Re: [Qemu-devel] [PATCH v7 08/15] block: Simplify block_set_io_throttle
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 08/15] block: Simplify block_set_io_throttle Eric Blake
  2016-05-24 15:21   ` [Qemu-devel] [Qemu-block] " Alberto Garcia
@ 2016-06-14 15:34   ` Markus Armbruster
  1 sibling, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 15:34 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, Luiz Capitulino,
	open list:Block layer core, Max Reitz

Eric Blake <eblake@redhat.com> writes:

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

This comment will be trivial as soon as we got used to the 'box' feature
:)

> +#
> +# 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
> +#
> +# The parameters for the block_set_io_throttle command.

This comment is prone to go stale.

> +#
>  # @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 cf5afa3..b8db496 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -2625,49 +2625,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)
>  {

This hunk together with the last one are worth a good chunk of the work
you had to do to get here :)

>      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;
>      }
>
> @@ -2676,59 +2644,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)) {
> @@ -2739,9 +2707,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 9836227..a0c3f4e 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1395,42 +1395,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);
>  }

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

* Re: [Qemu-devel] [PATCH v7 09/15] block: Simplify drive-mirror
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 09/15] block: Simplify drive-mirror Eric Blake
@ 2016-06-14 15:42   ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 15:42 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, Luiz Capitulino,
	open list:Block layer core, Max Reitz

Eric Blake <eblake@redhat.com> writes:

> 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>
>
> ---
> v7: new patch
> ---
>  qapi/block-core.json | 17 ++++++++++++-
>  blockdev.c           | 72 ++++++++++++++++++++++------------------------------
>  hmp.c                | 27 +++++++++-----------
>  3 files changed, 59 insertions(+), 57 deletions(-)
>
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 26f7c0e..885a75a 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
> +#
> +# The parameters for the drive-mirror command.
> +#
>  # @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
> @@ -1159,7 +1174,7 @@
   #
   # Returns: nothing on success
   #          If @device is not a valid block device, DeviceNotFound

You forgot to delete this part.

   #
>  #
>  # Since 1.3

Kind of (same in previous patch).

>  ##
> -{ '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 b8db496..94850fd 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -3457,19 +3457,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;
> @@ -3480,11 +3468,12 @@ void qmp_drive_mirror(const char *device, const char *target,
>      int flags;
>      int64_t size;
>      int ret;
> +    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;
>      }
>
> @@ -3492,24 +3481,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;
>      }
>
> @@ -3519,18 +3509,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);
> @@ -3549,20 +3539,20 @@ void qmp_drive_mirror(const char *device, const char *target,
>          }
>      }
>
> -    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);
> @@ -3578,8 +3568,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));
> @@ -3589,7 +3579,7 @@ void qmp_drive_mirror(const char *device, const char *target,
>       * file.
>       */
>      target_bs = NULL;
> -    ret = bdrv_open(&target_bs, target, NULL, options,
> +    ret = bdrv_open(&target_bs, arg->target, NULL, options,
>                      flags | BDRV_O_NO_BACKING, &local_err);
>      if (ret < 0) {
>          error_propagate(errp, local_err);
> @@ -3599,13 +3589,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,
> -                           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,
> +                           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);
>      if (local_err) {
>          error_propagate(errp, local_err);
> diff --git a/hmp.c b/hmp.c
> index a0c3f4e..c8f744b 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1059,31 +1059,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;
> +    bool reuse = qdict_get_try_bool(qdict, "reuse", false);

Any particular reason to swap reuse and full?

>      Error *err = NULL;
> +    DriveMirror mirror = {
> +        .device = (char *) qdict_get_str(qdict, "device"),
> +        .target = (char *) filename,

No space between the parenthesized type and the experession in type
casts, please.

> +        .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);
>  }

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

* Re: [Qemu-devel] [PATCH v7 10/15] qapi-event: Reduce chance of collision with event data
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 10/15] qapi-event: Reduce chance of collision with event data Eric Blake
@ 2016-06-14 16:28   ` Markus Armbruster
  2016-06-16 12:25     ` Markus Armbruster
  0 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-14 16:28 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> When an event has data that is not boxed, we are exposing all of
> its members alongside our local variables.  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 a shell that creates a
> simple wrapper, then calls a worker that operates on only the
> boxed version and thus has no user-supplied names to worry about
> in naming its local variables.  For boxed events, we don't need
> the wrapper.
>
> 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.
>
> The generated file changes 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>
>
> ---
> v7: new patch
> ---
>  scripts/qapi.py       |  1 -
>  scripts/qapi-event.py | 47 ++++++++++++++++++++++++++---------------------
>  2 files changed, 26 insertions(+), 22 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 6742e7a..7d568d9 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -1016,7 +1016,6 @@ class QAPISchemaObjectType(QAPISchemaType):
>          return QAPISchemaType.c_name(self)
>
>      def c_type(self):
> -        assert not self.is_implicit()

Huh?

>          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 b8ca8c8..fe4e50d 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):
> +    intro='void '
> +    if impl and arg_type and not box:
> +        box = True
> +        intro='static void do_'
> +    return '%(intro)sqapi_event_send_%(c_name)s(%(param)s)' % {
> +        'intro': intro,
>          'c_name': c_name(name.lower()),
>          'param': gen_params(arg_type, box, 'Error **errp')}
>

I'd call it prefix rather than intro.

> @@ -53,12 +58,8 @@ def gen_param_var(typ):
>
>
>  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():
> +        box = False
>      ret = mcgen('''
>
>  %(proto)s
> @@ -67,15 +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)

Because the not box case moves to the wrapper function (last hunk).

>
>      ret += mcgen('''
>
> @@ -93,20 +92,11 @@ def gen_event_send(name, arg_type, box):
>          ret += mcgen('''
>      v = qmp_output_visitor_new(&obj);
>
> -''')
> -
> -        if box:
> -            ret += mcgen('''
> -    visit_type_%(c_name)s(v, NULL, &arg, &err);
> -''',
> -                         c_name=arg_type.c_name(), name=arg_type.name)
> -        else:
> -            ret += mcgen('''
>      visit_start_struct(v, "%(name)s", NULL, 0, &err);
>      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);
>      }

Getting confused...  why are we getting rid of the box case here?

Too many conditionals...  gen_event_send() has three cases: empty
arg_type, non-empty arg_type and box, non-empty arg_type and not box.
The commit message shows the change to generated code for the second
case.  It doesn't show visit_type_%(c_name)s(v, NULL, &arg, &err) going
away.

> @@ -136,6 +126,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

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

* Re: [Qemu-devel] [PATCH v7 07/15] qapi: Implement boxed types for commands/events
  2016-06-14 15:27   ` Markus Armbruster
@ 2016-06-14 17:22     ` Eric Blake
  2016-06-15  6:22       ` Markus Armbruster
  0 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-06-14 17:22 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/14/2016 09:27 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.  For structs, it makes it possible
>> to pass a single qapi type instead of a breakout of all
>> struct members; for unions, it is now possible to use a
>> union as the data for a command or event.
>>
>> Generated code is unchanged, as long as no client uses the
>> new feature.
>>

>> @@ -1640,12 +1652,13 @@ 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():
>>          return extra
> 
> When arg_type is empty, box gets ignored.  That's not wrong, but I'd
> skin this cat differently: when box=true, pass a single arg_type
> argument, no matter what the type is.

Except that we don't have a visit function generated for the 'q_empty'
type; I'm worried that coming up with the right arg_type for an empty
box may be difficult.


>> +++ b/tests/test-qmp-commands.c
>> @@ -60,6 +60,18 @@ QObject *qmp_guest_sync(QObject *arg, Error **errp)
>>      return arg;
>>  }
>>
>> +void qmp_boxed_empty(Error **errp)
>> +{
>> +}
> 
> Demontrates that 'box': true with an empty type isn't boxed.

In other words, an empty type takes precedence over 'box':true, because
there is nothing to be passed.

I could go the other direction and make it a hard error to use
'box':true on an empty type, if that would be conceptually cleaner.

>>
>> +By default, the generator creates a marshalling function that converts
>> +an input QDict into a function call implemented by the user, and
> 
> Well, the called function is implemented by the user.
> 
>> +declares a prototype for the user's function which has a parameter for
>> +each member of the argument struct, including boolean arguments that
>> +describe whether optional arguments were provided.  But if the QAPI
>> +description includes the key 'box' with the boolean value true, the
>> +user call prototype will have only a single parameter for the overall
>> +generated C structure.  The 'box' key is required in order to use a
>> +union as an input argument, since it is not possible to list all
>> +members of the union as separate parameters.
>> +
> 
> Neglects to mention that 'data' is less restricted with 'box': true.
> 
> Suggest:
> 
>     The generator emits a prototype for the user's function implementing
>     the command.  Normally, 'data' is or names a struct type, and its
>     members are passed as separate arguments to this function.  If the
>     command definition includes a key 'box' with the boolean value true,
>     then the arguments are passed to the function as a single pointer to
>     the QAPI type generated for 'data'.  'data' may name an arbitrary
>     complex type then.

Or maybe arbitrary non-empty complex type, depending on what we decide
above.  And maybe I still need to make it clear that when using
'box':true, an anonymous type is no longer permitted.

> 
> This still glosses over the has_ arguments, but it's no worse than
> before.
> 
> Only then mention the marshalling:
> 
>     The generator 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.

Sure, I can reword along those lines.  (I may not state it enough, but
thanks for your wordsmithing help).


>> @@ -147,13 +150,14 @@
>>  { 'struct': 'EventStructOne',
>>    'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
>>
>> -{ 'event': 'EVENT_A' }
>> +{ 'event': 'EVENT_A', 'box': true }
> 
> This is case "empty".
> 
> Separate tests for both values of box would be cleaner, even though they
> produce the exact same result.  If we decide to obey box even with empty
> types, they don't.
> 

Or, if we decide to forbid 'box':true on an empty type, then this needs
tweaking anyway.

>>  { 'event': 'EVENT_B',
>>    'data': { } }
>>  { 'event': 'EVENT_C',
>>    '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' }
> 
> This is case "struct".
> 
> Missing: case "union".
> 


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

* Re: [Qemu-devel] [PATCH v7 07/15] qapi: Implement boxed types for commands/events
  2016-06-14 17:22     ` Eric Blake
@ 2016-06-15  6:22       ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-15  6:22 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 06/14/2016 09:27 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.  For structs, it makes it possible
>>> to pass a single qapi type instead of a breakout of all
>>> struct members; for unions, it is now possible to use a
>>> union as the data for a command or event.
>>>
>>> Generated code is unchanged, as long as no client uses the
>>> new feature.
>>>
>
>>> @@ -1640,12 +1652,13 @@ 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():
>>>          return extra
>> 
>> When arg_type is empty, box gets ignored.  That's not wrong, but I'd
>> skin this cat differently: when box=true, pass a single arg_type
>> argument, no matter what the type is.
>
> Except that we don't have a visit function generated for the 'q_empty'
> type; I'm worried that coming up with the right arg_type for an empty
> box may be difficult.
>
>
>>> +++ b/tests/test-qmp-commands.c
>>> @@ -60,6 +60,18 @@ QObject *qmp_guest_sync(QObject *arg, Error **errp)
>>>      return arg;
>>>  }
>>>
>>> +void qmp_boxed_empty(Error **errp)
>>> +{
>>> +}
>> 
>> Demontrates that 'box': true with an empty type isn't boxed.
>
> In other words, an empty type takes precedence over 'box':true, because
> there is nothing to be passed.
>
> I could go the other direction and make it a hard error to use
> 'box':true on an empty type, if that would be conceptually cleaner.

That's fine with me.  We can always relax the restriction if it turns
out bothersome.

>>> +By default, the generator creates a marshalling function that converts
>>> +an input QDict into a function call implemented by the user, and
>> 
>> Well, the called function is implemented by the user.
>> 
>>> +declares a prototype for the user's function which has a parameter for
>>> +each member of the argument struct, including boolean arguments that
>>> +describe whether optional arguments were provided.  But if the QAPI
>>> +description includes the key 'box' with the boolean value true, the
>>> +user call prototype will have only a single parameter for the overall
>>> +generated C structure.  The 'box' key is required in order to use a
>>> +union as an input argument, since it is not possible to list all
>>> +members of the union as separate parameters.
>>> +
>> 
>> Neglects to mention that 'data' is less restricted with 'box': true.
>> 
>> Suggest:
>> 
>>     The generator emits a prototype for the user's function implementing
>>     the command.  Normally, 'data' is or names a struct type, and its
>>     members are passed as separate arguments to this function.  If the
>>     command definition includes a key 'box' with the boolean value true,
>>     then the arguments are passed to the function as a single pointer to
>>     the QAPI type generated for 'data'.  'data' may name an arbitrary
>>     complex type then.
>
> Or maybe arbitrary non-empty complex type, depending on what we decide
> above.  And maybe I still need to make it clear that when using
> 'box':true, an anonymous type is no longer permitted.

My text kind of implies "anonymous not permitted": "'data' may *name* an
arbitrary complex type then" (emphasis added).  Stating it explictly
would be better.

>> This still glosses over the has_ arguments, but it's no worse than
>> before.
>> 
>> Only then mention the marshalling:
>> 
>>     The generator 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.
>
> Sure, I can reword along those lines.  (I may not state it enough, but
> thanks for your wordsmithing help).

Thanks for caring for it!

>>> @@ -147,13 +150,14 @@
>>>  { 'struct': 'EventStructOne',
>>>    'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
>>>
>>> -{ 'event': 'EVENT_A' }
>>> +{ 'event': 'EVENT_A', 'box': true }
>> 
>> This is case "empty".
>> 
>> Separate tests for both values of box would be cleaner, even though they
>> produce the exact same result.  If we decide to obey box even with empty
>> types, they don't.
>> 
>
> Or, if we decide to forbid 'box':true on an empty type, then this needs
> tweaking anyway.
>
>>>  { 'event': 'EVENT_B',
>>>    'data': { } }
>>>  { 'event': 'EVENT_C',
>>>    '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' }
>> 
>> This is case "struct".
>> 
>> Missing: case "union".
>> 

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

* Re: [Qemu-devel] [PATCH v7 10/15] qapi-event: Reduce chance of collision with event data
  2016-06-14 16:28   ` Markus Armbruster
@ 2016-06-16 12:25     ` Markus Armbruster
  2016-06-28  3:20       ` Eric Blake
  0 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-16 12:25 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Markus Armbruster <armbru@redhat.com> writes:

> Eric Blake <eblake@redhat.com> writes:
>
>> When an event has data that is not boxed, we are exposing all of
>> its members alongside our local variables.  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 a shell that creates a
>> simple wrapper, then calls a worker that operates on only the
>> boxed version and thus has no user-supplied names to worry about
>> in naming its local variables.  For boxed events, we don't need
>> the wrapper.
>>
>> 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.
>>
>> The generated file changes 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>
>>
>> ---
>> v7: new patch
>> ---
>>  scripts/qapi.py       |  1 -
>>  scripts/qapi-event.py | 47 ++++++++++++++++++++++++++---------------------
>>  2 files changed, 26 insertions(+), 22 deletions(-)
>>
>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>> index 6742e7a..7d568d9 100644
>> --- a/scripts/qapi.py
>> +++ b/scripts/qapi.py
>> @@ -1016,7 +1016,6 @@ class QAPISchemaObjectType(QAPISchemaType):
>>          return QAPISchemaType.c_name(self)
>>
>>      def c_type(self):
>> -        assert not self.is_implicit()
>
> Huh?
>
>>          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 b8ca8c8..fe4e50d 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):
>> +    intro='void '
>> +    if impl and arg_type and not box:
>> +        box = True
>> +        intro='static void do_'
>> +    return '%(intro)sqapi_event_send_%(c_name)s(%(param)s)' % {
>> +        'intro': intro,
>>          'c_name': c_name(name.lower()),
>>          'param': gen_params(arg_type, box, 'Error **errp')}
>>
>
> I'd call it prefix rather than intro.
>
>> @@ -53,12 +58,8 @@ def gen_param_var(typ):
>>
>>
>>  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():
>> +        box = False
>>      ret = mcgen('''
>>
>>  %(proto)s
>> @@ -67,15 +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)
>
> Because the not box case moves to the wrapper function (last hunk).
>
>>
>>      ret += mcgen('''
>>
>> @@ -93,20 +92,11 @@ def gen_event_send(name, arg_type, box):
>>          ret += mcgen('''
>>      v = qmp_output_visitor_new(&obj);
>>
>> -''')
>> -
>> -        if box:
>> -            ret += mcgen('''
>> -    visit_type_%(c_name)s(v, NULL, &arg, &err);
>> -''',
>> -                         c_name=arg_type.c_name(), name=arg_type.name)
>> -        else:
>> -            ret += mcgen('''
>>      visit_start_struct(v, "%(name)s", NULL, 0, &err);
>>      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);
>>      }
>
> Getting confused...  why are we getting rid of the box case here?
>
> Too many conditionals...  gen_event_send() has three cases: empty
> arg_type, non-empty arg_type and box, non-empty arg_type and not box.
> The commit message shows the change to generated code for the second
> case.  It doesn't show visit_type_%(c_name)s(v, NULL, &arg, &err) going
> away.

Case empty arg_type: no change
Example: POWERDOWN

Case non-empty arg_type and box: visit gets open-coded
Example: EVENT_E

     void qapi_event_send_event_e(UserDefZero *arg, Error **errp)
     {
         QDict *qmp;
         Error *err = NULL;
         QMPEventFuncEmit emit;
         QObject *obj;
         Visitor *v;

         emit = qmp_event_get_func_emit();
         if (!emit) {
             return;
         }

         qmp = qmp_event_build_dict("EVENT_E");

         v = qmp_output_visitor_new(&obj);

    -    visit_type_UserDefZero(v, NULL, &arg, &err);
    +    visit_start_struct(v, "EVENT_E", NULL, 0, &err);
    +    if (err) {
    +        goto out;
    +    }
    +    visit_type_UserDefZero_members(v, arg, &err);
    +    if (!err) {
    +        visit_check_struct(v, &err);
    +    }
    +    visit_end_struct(v, NULL);
         if (err) {
             goto out;
         }

         visit_complete(v, &obj);
         qdict_put_obj(qmp, "data", obj);
         emit(TEST_QAPI_EVENT_EVENT_E, qmp, &err);

     out:
         visit_free(v);
         error_propagate(errp, err);
         QDECREF(qmp);
     }

Compare:

     void visit_type_UserDefZero(Visitor *v, const char *name, UserDefZero **obj, Error **errp)
     {
         Error *err = NULL;

         visit_start_struct(v, name, (void **)obj, sizeof(UserDefZero), &err);
         if (err) {
             goto out;
         }
--->     if (!*obj) {
--->         goto out_obj;
--->     }
         visit_type_UserDefZero_members(v, *obj, &err);
--->     if (err) {
--->         goto out_obj;
--->     }
--->     visit_check_struct(v, &err);
     out_obj:
         visit_end_struct(v, (void **)obj);
--->     if (err && visit_is_input(v)) {
--->         qapi_free_UserDefZero(*obj);
--->         *obj = NULL;
--->     }
     out:
         error_propagate(errp, err);
     }

The open-coded visit drops the !*obj check (okay, @arg isn't going
anywhere), skips the visit_check_struct() differently, and drops the
qapi_free_FOO() (okay, condition is always false here).

So this isn't wrong.  But why open-code?

Case non-empty arg_type and not box:
Example: ACPI_DEVICE_OST

    -void qapi_event_send_acpi_device_ost(ACPIOSTInfo *info, Error **errp)
    +static void do_qapi_event_send_acpi_device_ost(q_obj_ACPI_DEVICE_OST_arg *arg, Error **errp)
     {
         QDict *qmp;
         Error *err = NULL;
         QMPEventFuncEmit emit;
         QObject *obj;
         Visitor *v;
    -    q_obj_ACPI_DEVICE_OST_arg param = {
    -        info
    -    };

         emit = qmp_event_get_func_emit();
         if (!emit) {
             return;
         }

         qmp = qmp_event_build_dict("ACPI_DEVICE_OST");

         v = qmp_output_visitor_new(&obj);

         visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err);
         if (err) {
             goto out;
         }
    -    visit_type_q_obj_ACPI_DEVICE_OST_arg_members(v, &param, &err);
    +    visit_type_q_obj_ACPI_DEVICE_OST_arg_members(v, arg, &err);
         if (!err) {
             visit_check_struct(v, &err);
         }
         visit_end_struct(v, NULL);
         if (err) {
             goto out;
         }

         visit_complete(v, &obj);
         qdict_put_obj(qmp, "data", obj);
         emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);

     out:
         visit_free(v);
         error_propagate(errp, err);
         QDECREF(qmp);
     }

    -void qapi_event_send_balloon_change(int64_t actual, Error **errp)
    +void qapi_event_send_acpi_device_ost(ACPIOSTInfo *info, Error **errp)
    +{
    +    q_obj_ACPI_DEVICE_OST_arg param = {
    +        info
    +    };
    +    do_qapi_event_send_acpi_device_ost(&param, errp);
    +}
    +

This is the case the commit message advertises.

There is no visit_type_FOO() we could compare too, since FOO is an
implicit type

>> @@ -136,6 +126,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

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

* Re: [Qemu-devel] [PATCH v7 11/15] qapi: Change Netdev into a flat union
  2016-05-20 22:40   ` Eric Blake
@ 2016-06-16 13:15     ` Markus Armbruster
  -1 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-16 13:15 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Peter Maydell, Michael S. Tsirkin, Jason Wang,
	Vincenzo Maffione, Alexander Graf, Max Filippov, Gerd Hoffmann,
	Dmitry Fleytman, Edgar E. Iglesias, Rob Herring,
	Stefano Stabellini, Scott Feldman, Kővágó,
	Zoltán, Anthony Perard, Samuel Thibault, Jiri Pirko,
	Alistair Francis, Beniamino Galvani, open list:Musicpal,
	Jan Kiszka, Scott Wood, Giuseppe Lettieri, Luiz Capitulino,
	Luigi Rizzo, David Gibson, Peter Crosthwaite, Michael Walle,
	open list:ppce500, Peter Chubb, open list:X86

Eric Blake <eblake@redhat.com> writes:

> From: Kővágó, Zoltán <dirty.ice.hu@gmail.com>
>
> Except qapi-schema.json, this patch was generated by:
>
> find . -name .git -prune -o -type f \! -name '*~' -print0 | \
>   xargs -0 sed -i \
>     -e 's/NetClientOptionsKind/NetClientDriver/g' \
>     -e 's/NET_CLIENT_OPTIONS_KIND_/NET_CLIENT_DRIVER_/g' \
>     -e 's/netdev->opts/netdev/g'

Uh, this is prone to descend into build trees and edit random crap.  I
used

    $ sed -i -e ... `git-ls-tree -r HEAD | awk '$2 == "blob" { print $4 }'`

to verify this commit.  Differences noted inline.

>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com>
>
> Additional changes:
> Rebase the patch on top of an earlier change from netdev->kind to
> netdev->type, so that tweak is no longer needed here.

I guess this is the "-e 's/netdev->kind/netdev->type/g'" you deleted
from Zoltán's commit message.

>                                                        Rebase to
> latest master which enhanced multiqueue.
>
> Rework so that NetdevLegacy doesn't pollute QMP command but is instead
> copied piecewise into the new Netdev, which means that NetClientOptions
> must still remain in qapi. Since legacy previously always rejected
> 'hubport', we can now make that explicit by having the two unions be
> slightly different; but that means we must manually map between the
> two structures.

I suspect this explains most of the differences between the sed script's
results and the actual patch.  Would it be possible to split the patch
into a mechanical and a manual part, for easier review?  If not, could
explain your manual edits in more detail?

> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> index 8e79b55..490007c 100644
> --- a/hw/net/e1000.c
> +++ b/hw/net/e1000.c
> @@ -1785,7 +1785,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,

I additionally get:

  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,

Rebase needed?

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

sed turns this into

       dump = netdev->u.dump.data;

Is this part of the manual changes?  More of the same below.

>
>      assert(peer);
>

Another possible case of "rebase needed":

  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;

sed:
  +    hubport = netdev->u.hubport.data;

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

sed:
  +    l2tpv3 = netdev->u.l2tpv3.data;

>
>      if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
>          s->ipv6 = l2tpv3->ipv6;
> diff --git a/net/net.c b/net/net.c
> index ec4e700..c49a295 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -291,7 +291,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);
> @@ -362,13 +362,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);
>
> @@ -377,7 +377,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;
> @@ -433,7 +433,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);
>              }
> @@ -605,7 +605,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();
>          }
> @@ -779,7 +779,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;
> @@ -790,7 +790,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;
> @@ -871,8 +871,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;

sed:
  +    nic = netdev->u.nic.data;

>
>      idx = nic_get_free_idx();
>      if (idx == -1 || nb_nics >= MAX_NICS) {
> @@ -932,39 +932,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)
>  {

Multiple differences in this function.  Manual?

> -    const NetClientOptions *opts;
>      Netdev legacy = {0};
>      const Netdev *netdev;
>      const char *name;
> @@ -972,34 +971,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 NetClientOptions *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_CLIENT_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_CLIENT_OPTIONS_KIND_NIC:
> +            legacy.type = NET_CLIENT_DRIVER_NIC;
> +            legacy.u.nic = *opts->u.nic.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_USER:
> +            legacy.type = NET_CLIENT_DRIVER_USER;
> +            legacy.u.user = *opts->u.user.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_TAP:
> +            legacy.type = NET_CLIENT_DRIVER_TAP;
> +            legacy.u.tap = *opts->u.tap.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_L2TPV3:
> +            legacy.type = NET_CLIENT_DRIVER_L2TPV3;
> +            legacy.u.l2tpv3 = *opts->u.l2tpv3.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_SOCKET:
> +            legacy.type = NET_CLIENT_DRIVER_SOCKET;
> +            legacy.u.socket = *opts->u.socket.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_VDE:
> +            legacy.type = NET_CLIENT_DRIVER_VDE;
> +            legacy.u.vde = *opts->u.vde.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_DUMP:
> +            legacy.type = NET_CLIENT_DRIVER_DUMP;
> +            legacy.u.dump = *opts->u.dump.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_BRIDGE:
> +            legacy.type = NET_CLIENT_DRIVER_BRIDGE;
> +            legacy.u.bridge = *opts->u.bridge.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_NETMAP:
> +            legacy.type = NET_CLIENT_DRIVER_NETMAP;
> +            legacy.u.netmap = *opts->u.netmap.data;
> +            break;
> +        case NET_CLIENT_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)");
> @@ -1007,17 +1044,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;
>      }
> @@ -1137,7 +1174,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;
>      }
> @@ -1228,7 +1265,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");
> @@ -1258,7 +1295,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;
> @@ -1304,7 +1341,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);
>
> @@ -1317,10 +1354,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);
>          }
> @@ -1334,7 +1371,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) {
> @@ -1361,7 +1398,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;
>              }
> @@ -1402,7 +1439,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);
> @@ -1434,7 +1471,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 addeedd..aa2d34a 100644
> --- a/net/netmap.c
> +++ b/net/netmap.c
> @@ -401,7 +401,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,
> @@ -422,7 +422,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;

sed:
  +    const NetdevNetmapOptions *netmap_opts = netdev->u.netmap.data;

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

sed:
  +    user = netdev->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 d258352..b831afa 100644
> --- a/net/socket.c
> +++ b/net/socket.c
> @@ -346,7 +346,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,
> @@ -429,7 +429,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,
> @@ -704,8 +704,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;

sed:
  +    sock = netdev->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 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;

sed:
  +    tap = netdev->u.tap.data;

>
>      if (!tap->has_ifname) {
>          error_report("tap: no interface name");
> diff --git a/net/tap.c b/net/tap.c
> index 9531880..8844d90 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;

sed:
  +    bridge = netdev->u.bridge.data;

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

sed:
  +    tap = netdev->u.tap.data;

>      queues = tap->has_queues ? tap->queues : 1;
>      vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;
>
> @@ -891,7 +891,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;

sed:
  +    vde = netdev->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 4607c7b..01b5453 100644
> --- a/net/vhost-user.c
> +++ b/net/vhost-user.c
> @@ -32,7 +32,7 @@ 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;
>  }
>
> @@ -47,7 +47,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);

Manual whitespace cleanup.  Okay.

>
>          s = DO_UPCAST(VhostUserState, nc, ncs[i]);
>          if (!vhost_user_running(s)) {
> @@ -71,7 +71,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);

Likewise.

>
>          s = DO_UPCAST(VhostUserState, nc, ncs[i]);
>          if (vhost_user_running(s)) {
> @@ -146,20 +146,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,
> @@ -176,7 +176,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);
>
> @@ -305,8 +305,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;

sed:
  +    vhost_user_opts = netdev->u.vhost_user.data;

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

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

* Re: [PATCH v7 11/15] qapi: Change Netdev into a flat union
@ 2016-06-16 13:15     ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-16 13:15 UTC (permalink / raw)
  To: Eric Blake
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang, qemu-devel,
	Vincenzo Maffione, Luiz Capitulino, Max Filippov, Gerd Hoffmann,
	Dmitry Fleytman, Edgar E. Iglesias, Rob Herring,
	Stefano Stabellini, Alexander Graf, Scott Feldman,
	Kővágó,
	Zoltán, Anthony Perard, Samuel Thibault, Jiri Pirko,
	Alistair Francis, Beniamino Galvani, open list:Musicpal,
	Jan Kiszka

Eric Blake <eblake@redhat.com> writes:

> From: Kővágó, Zoltán <dirty.ice.hu@gmail.com>
>
> Except qapi-schema.json, this patch was generated by:
>
> find . -name .git -prune -o -type f \! -name '*~' -print0 | \
>   xargs -0 sed -i \
>     -e 's/NetClientOptionsKind/NetClientDriver/g' \
>     -e 's/NET_CLIENT_OPTIONS_KIND_/NET_CLIENT_DRIVER_/g' \
>     -e 's/netdev->opts/netdev/g'

Uh, this is prone to descend into build trees and edit random crap.  I
used

    $ sed -i -e ... `git-ls-tree -r HEAD | awk '$2 == "blob" { print $4 }'`

to verify this commit.  Differences noted inline.

>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com>
>
> Additional changes:
> Rebase the patch on top of an earlier change from netdev->kind to
> netdev->type, so that tweak is no longer needed here.

I guess this is the "-e 's/netdev->kind/netdev->type/g'" you deleted
from Zoltán's commit message.

>                                                        Rebase to
> latest master which enhanced multiqueue.
>
> Rework so that NetdevLegacy doesn't pollute QMP command but is instead
> copied piecewise into the new Netdev, which means that NetClientOptions
> must still remain in qapi. Since legacy previously always rejected
> 'hubport', we can now make that explicit by having the two unions be
> slightly different; but that means we must manually map between the
> two structures.

I suspect this explains most of the differences between the sed script's
results and the actual patch.  Would it be possible to split the patch
into a mechanical and a manual part, for easier review?  If not, could
explain your manual edits in more detail?

> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> index 8e79b55..490007c 100644
> --- a/hw/net/e1000.c
> +++ b/hw/net/e1000.c
> @@ -1785,7 +1785,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,

I additionally get:

  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,

Rebase needed?

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

sed turns this into

       dump = netdev->u.dump.data;

Is this part of the manual changes?  More of the same below.

>
>      assert(peer);
>

Another possible case of "rebase needed":

  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;

sed:
  +    hubport = netdev->u.hubport.data;

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

sed:
  +    l2tpv3 = netdev->u.l2tpv3.data;

>
>      if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
>          s->ipv6 = l2tpv3->ipv6;
> diff --git a/net/net.c b/net/net.c
> index ec4e700..c49a295 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -291,7 +291,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);
> @@ -362,13 +362,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);
>
> @@ -377,7 +377,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;
> @@ -433,7 +433,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);
>              }
> @@ -605,7 +605,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();
>          }
> @@ -779,7 +779,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;
> @@ -790,7 +790,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;
> @@ -871,8 +871,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;

sed:
  +    nic = netdev->u.nic.data;

>
>      idx = nic_get_free_idx();
>      if (idx == -1 || nb_nics >= MAX_NICS) {
> @@ -932,39 +932,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)
>  {

Multiple differences in this function.  Manual?

> -    const NetClientOptions *opts;
>      Netdev legacy = {0};
>      const Netdev *netdev;
>      const char *name;
> @@ -972,34 +971,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 NetClientOptions *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_CLIENT_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_CLIENT_OPTIONS_KIND_NIC:
> +            legacy.type = NET_CLIENT_DRIVER_NIC;
> +            legacy.u.nic = *opts->u.nic.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_USER:
> +            legacy.type = NET_CLIENT_DRIVER_USER;
> +            legacy.u.user = *opts->u.user.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_TAP:
> +            legacy.type = NET_CLIENT_DRIVER_TAP;
> +            legacy.u.tap = *opts->u.tap.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_L2TPV3:
> +            legacy.type = NET_CLIENT_DRIVER_L2TPV3;
> +            legacy.u.l2tpv3 = *opts->u.l2tpv3.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_SOCKET:
> +            legacy.type = NET_CLIENT_DRIVER_SOCKET;
> +            legacy.u.socket = *opts->u.socket.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_VDE:
> +            legacy.type = NET_CLIENT_DRIVER_VDE;
> +            legacy.u.vde = *opts->u.vde.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_DUMP:
> +            legacy.type = NET_CLIENT_DRIVER_DUMP;
> +            legacy.u.dump = *opts->u.dump.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_BRIDGE:
> +            legacy.type = NET_CLIENT_DRIVER_BRIDGE;
> +            legacy.u.bridge = *opts->u.bridge.data;
> +            break;
> +        case NET_CLIENT_OPTIONS_KIND_NETMAP:
> +            legacy.type = NET_CLIENT_DRIVER_NETMAP;
> +            legacy.u.netmap = *opts->u.netmap.data;
> +            break;
> +        case NET_CLIENT_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)");
> @@ -1007,17 +1044,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;
>      }
> @@ -1137,7 +1174,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;
>      }
> @@ -1228,7 +1265,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");
> @@ -1258,7 +1295,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;
> @@ -1304,7 +1341,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);
>
> @@ -1317,10 +1354,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);
>          }
> @@ -1334,7 +1371,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) {
> @@ -1361,7 +1398,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;
>              }
> @@ -1402,7 +1439,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);
> @@ -1434,7 +1471,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 addeedd..aa2d34a 100644
> --- a/net/netmap.c
> +++ b/net/netmap.c
> @@ -401,7 +401,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,
> @@ -422,7 +422,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;

sed:
  +    const NetdevNetmapOptions *netmap_opts = netdev->u.netmap.data;

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

sed:
  +    user = netdev->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 d258352..b831afa 100644
> --- a/net/socket.c
> +++ b/net/socket.c
> @@ -346,7 +346,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,
> @@ -429,7 +429,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,
> @@ -704,8 +704,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;

sed:
  +    sock = netdev->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 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;

sed:
  +    tap = netdev->u.tap.data;

>
>      if (!tap->has_ifname) {
>          error_report("tap: no interface name");
> diff --git a/net/tap.c b/net/tap.c
> index 9531880..8844d90 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;

sed:
  +    bridge = netdev->u.bridge.data;

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

sed:
  +    tap = netdev->u.tap.data;

>      queues = tap->has_queues ? tap->queues : 1;
>      vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;
>
> @@ -891,7 +891,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;

sed:
  +    vde = netdev->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 4607c7b..01b5453 100644
> --- a/net/vhost-user.c
> +++ b/net/vhost-user.c
> @@ -32,7 +32,7 @@ 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;
>  }
>
> @@ -47,7 +47,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);

Manual whitespace cleanup.  Okay.

>
>          s = DO_UPCAST(VhostUserState, nc, ncs[i]);
>          if (!vhost_user_running(s)) {
> @@ -71,7 +71,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);

Likewise.

>
>          s = DO_UPCAST(VhostUserState, nc, ncs[i]);
>          if (vhost_user_running(s)) {
> @@ -146,20 +146,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,
> @@ -176,7 +176,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);
>
> @@ -305,8 +305,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;

sed:
  +    vhost_user_opts = netdev->u.vhost_user.data;

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

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

* Re: [Qemu-devel] [PATCH v7 13/15] net: Complete qapi-fication of netdev_add
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 13/15] net: Complete qapi-fication of netdev_add Eric Blake
@ 2016-06-16 13:40   ` Markus Armbruster
  2016-07-02 22:58     ` Eric Blake
  0 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-16 13:40 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Jason Wang

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, while
> making no changes to the set of previously-valid QMP commands
> that would work, and without breaking command line parsing.

Isn't it amazing that you pulled this off without a compatibility break?

>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v7: no change
> v6: don't lose qemu_opts handling
> ---
>  qapi-schema.json  | 15 +++------------
>  include/net/net.h |  1 -
>  net/net.c         |  6 +++---
>  qmp-commands.hx   |  2 +-
>  4 files changed, 7 insertions(+), 17 deletions(-)
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 4fee44b..fee4d07 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -2301,26 +2301,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 74b32e0..8f9be98 100644
> --- a/include/net/net.h
> +++ b/include/net/net.h
> @@ -193,7 +193,6 @@ 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);

Only a dead QemuOpts is a good QemuOpts.

>
>  int net_hub_id_for_client(NetClientState *nc, int *id);
>  NetClientState *net_hub_port_find(int hub_id);
> diff --git a/net/net.c b/net/net.c
> index e159506..b93ff00 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -1188,7 +1188,7 @@ 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;
> @@ -1199,12 +1199,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);

We keep creating a QemuOpts for the network backend, but its opts->head
remains empty.  We need it for qmp_netdev_del().  Possibly more, but I
doubt it.

I think this is worth a mention in the commit message.  

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

Suggest to inline netdev_add() into its only remaining caller
hmp_netdev_add().

> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 94847e5..3804030 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -949,7 +949,7 @@ EQMP
>      {
>          .name       = "netdev_add",
>          .args_type  = "netdev:O",
> -        .mhandler.cmd_new = qmp_netdev_add,
> +        .mhandler.cmd_new = qmp_marshal_netdev_add,
>      },
>
>  SQMP

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

* Re: [Qemu-devel] [PATCH v7 14/15] qapi: Allow anonymous branch types in flat union
  2016-05-20 22:40 ` [Qemu-devel] [PATCH v7 14/15] qapi: Allow anonymous branch types in flat union Eric Blake
@ 2016-06-16 14:33   ` Markus Armbruster
  2016-07-01 22:59     ` Eric Blake
  0 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-16 14:33 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> 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 '_empty' struct nor a 'visit_type__empty_members()'
> corresponding to the special ':empty' type; but when the branch
> is truly empty, there's nothing to do.

Well, it could emit them, if it makes things easier.

> 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>
>
> ---
> v7: new patch
> ---
>  scripts/qapi.py                          | 21 +++++++++++++++------
>  scripts/qapi-types.py                    | 10 +++++++---
>  scripts/qapi-visit.py                    | 14 ++++++++++----
>  tests/Makefile                           |  1 +
>  tests/qapi-schema/flat-union-inline.err  |  2 +-
>  tests/qapi-schema/flat-union-inline.json |  5 ++---
>  tests/qapi-schema/qapi-schema-test.json  |  5 +++--
>  tests/qapi-schema/qapi-schema-test.out   |  8 ++++++--
>  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
>  12 files changed, 51 insertions(+), 21 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 7d568d9..4c531e7 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.
> @@ -1061,6 +1063,9 @@ class QAPISchemaMember(object):
>                  return '(parameter of %s)' % owner[:-4]
>              elif owner.endswith('-base'):
>                  return '(base of %s)' % owner[:-5]
> +            elif owner.endswith('-branch'):
> +                return ('(member of %s branch %s)'
> +                        % tuple(owner[:-7].split(':')))

I think we should point to the spot that puts in the colon, and back.

Do we really need the "of %s" part?

>              else:
>                  assert owner.endswith('-wrapper')
>                  # Unreachable and not implemented
> @@ -1335,7 +1340,11 @@ 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):
> +            typ = self._make_implicit_object_type(
> +                "%s:%s" % (owner, case), info, 'branch',
> +                self._make_members(typ, info)) or 'q_empty'

This is the spot.

Is this the best spot for creating the type?  Precedent:
_make_simple_variant() creates a wrapper type.  Because of that, I'm
inclined to answer yes.  But let's examine the caller.  They're visible
right below.

>          return QAPISchemaObjectTypeVariant(case, typ)
>
>      def _make_simple_variant(self, case, typ, info):
> @@ -1356,7 +1365,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:

This is the caller we need to extend to cope with an anonymous type.
The anonymous type arrives in form of @value being a dict rather than a
type name.  Possible, because the change to check_union() now permits
dict in addition to string.

> @@ -1375,7 +1384,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(

Here, dict still can't happen, but passing more argument's isn't exactly
a burden.

> @@ -1485,7 +1494,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('.-:', '___')

Because you use the colon as separator.  Hmm.

>
>
>  # Map @name to a valid C identifier.
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 5a9e2da..f1edab2 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -61,7 +61,8 @@ def gen_object(name, base, members, variants):
   def gen_object(name, base, members, variants):
       if name in objects_seen:
           return ''
       objects_seen.add(name)

>      ret = ''
>      if variants:
>          for v in variants.variants:
> -            if isinstance(v.type, QAPISchemaObjectType):
> +            if (isinstance(v.type, QAPISchemaObjectType)
> +                    and not (v.type.is_implicit() and v.type.is_empty())):
>                  ret += gen_object(v.type.name, v.type.base,
>                                    v.type.local_members, v.type.variants)
>

This is the recursion that ensures an object type's variant member types
are emitted before the object type.

We can't simply .type == schema.the_empty_object_type like
qapi-introspect.py does, because we don't have schema handy here.  Hmm.

Do we really need this change?  Note that gen_object() does nothing for
name in objects_seen, and we do this in visit_begin():

        # gen_object() is recursive, ensure it doesn't visit the empty type
        objects_seen.add(schema.the_empty_object_type.name)

> @@ -123,11 +124,14 @@ def gen_variants(variants):
>                  c_name=c_name(variants.tag_member.name))
>
>      for var in variants.variants:

Here, we emit the C union member for a variant:

> +        typ = var.type.c_unboxed_type()
> +        if (isinstance(var.type, QAPISchemaObjectType) and
> +                var.type.is_empty() and var.type.is_implicit()):
> +            typ = 'char'
>          ret += mcgen('''
>          %(c_type)s %(c_name)s;
>  ''',
> -                     c_type=var.type.c_unboxed_type(),
> -                     c_name=c_name(var.name))
> +                     c_type=typ, c_name=c_name(var.name))

Your change replaces the C type when var.type is the empty type.
Without that, we'd get 'q_empty', I guess.

Do we need the union member?  Hmm, we may want to avoid empty unions, to
avoid pedantic warnings.

Should we make the_empty_object_type.c_unboxed_type() return a suitable
C type instead of 'q_empty'?

>
>      ret += mcgen('''
>      } u;
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 07ae6d1..46f8b39 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -79,13 +79,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:

Here, we emit the visit of the "active" C union member:

> -        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;
> +''')

Your change suppresses the visit_type_FOO_members() call when var.type
is the empty type.  Without that, we'd get visit_type_q_empty_members(),
which doesn't exist.

Why not make it exist?

>
>          ret += mcgen('''
>      default:
> diff --git a/tests/Makefile b/tests/Makefile
> index 5cd6177..d7d9597 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -379,6 +379,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 df91f3d..5128b49 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' ] }
>
>  # for testing nested structs
>  { 'struct': 'UserDefOne',
> @@ -81,7 +81,8 @@
>    'base': { '*integer': 'int', 'string': 'str', 'enum1': 'QEnumTwo' },
>    'discriminator': 'enum1',
>    'data': { 'value1' : 'UserDefC', # intentional forward reference
> -            'value2' : 'UserDefB' } }

You're deleting the equally intentional case of a backward reference :)

> +            'value2' : { },
> +            'value3' : { 'boolean': 'bool', '*number': 'number' } } }
>
>  { 'struct': 'WrapAlternate',
>    'data': { 'alt': 'UserDefAlternate' } }
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 8a00c6b..7fac2da 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -50,7 +50,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']
>      prefix QENUM_TWO
>  enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
>      prefix QTYPE
> @@ -82,7 +82,8 @@ object UserDefFlatUnion2
>      base q_obj_UserDefFlatUnion2-base
>      tag enum1
>      case value1: UserDefC
> -    case value2: UserDefB
> +    case value2: q_empty
> +    case value3: q_obj_UserDefFlatUnion2:value3-branch
>  object UserDefNativeListUnion
>      member type: UserDefNativeListUnionKind optional=False
>      tag type
> @@ -179,6 +180,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:value3-branch
> +    member boolean: bool optional=False
> +    member number: number 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

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

* Re: [Qemu-devel] [PATCH v7 11/15] qapi: Change Netdev into a flat union
  2016-06-16 13:15     ` Markus Armbruster
@ 2016-06-16 14:35       ` Eric Blake
  -1 siblings, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-06-16 14:35 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Peter Maydell, Michael S. Tsirkin, Jason Wang,
	Vincenzo Maffione, Alexander Graf, Max Filippov, Gerd Hoffmann,
	Dmitry Fleytman, Edgar E. Iglesias, Rob Herring,
	Stefano Stabellini, Scott Feldman, Kővágó,
	Zoltán, Anthony Perard, Samuel Thibault, Jiri Pirko,
	Alistair Francis, Beniamino Galvani, open list:Musicpal,
	Jan Kiszka, Scott Wood, Giuseppe Lettieri, Luiz Capitulino,
	Luigi Rizzo, David Gibson, Peter Crosthwaite, Michael Walle,
	open list:ppce500, Peter Chubb, open list:X86

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

On 06/16/2016 07:15 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> From: Kővágó, Zoltán <dirty.ice.hu@gmail.com>
>>
>> Except qapi-schema.json, this patch was generated by:
>>
>> find . -name .git -prune -o -type f \! -name '*~' -print0 | \
>>   xargs -0 sed -i \
>>     -e 's/NetClientOptionsKind/NetClientDriver/g' \
>>     -e 's/NET_CLIENT_OPTIONS_KIND_/NET_CLIENT_DRIVER_/g' \
>>     -e 's/netdev->opts/netdev/g'
> 
> Uh, this is prone to descend into build trees and edit random crap.  I
> used
> 
>     $ sed -i -e ... `git-ls-tree -r HEAD | awk '$2 == "blob" { print $4 }'`
> 
> to verify this commit.  Differences noted inline.
> 

> I additionally get:
> 
>   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,
> 
> Rebase needed?

Unfortunately yes. This patch has been under a LOT of churn since it was
first written; it may be better to just redo it from scratch and claim
ownership myself, since it hardly resembles Zoltán's original submission
(but of course, give him credit for the idea).

> 
>> 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;
> 
> sed turns this into
> 
>        dump = netdev->u.dump.data;
> 
> Is this part of the manual changes?  More of the same below.
> 

The original sed script is so distant from the actual changes that it's
not worth mentioning the sed script in the commit message any more.

>>
>>      assert(peer);
>>
> 
> Another possible case of "rebase needed":
> 
>   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);

Yep.  Thanks for researching.

>>
>>  static int net_client_init1(const void *object, int is_netdev, Error **errp)
>>  {
> 
> Multiple differences in this function.  Manual?

Yes.


>> @@ -47,7 +47,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);
> 
> Manual whitespace cleanup.  Okay.
> 

And necessary to shut up checkpatch.  I really get to rewrite the commit
message to something better for v8, don't I.

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

* Re: [Qemu-devel] [PATCH v7 11/15] qapi: Change Netdev into a flat union
@ 2016-06-16 14:35       ` Eric Blake
  0 siblings, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-06-16 14:35 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang, qemu-devel,
	Vincenzo Maffione, Luiz Capitulino, Max Filippov, Gerd Hoffmann,
	Dmitry Fleytman, Edgar E. Iglesias, Rob Herring,
	Stefano Stabellini, Alexander Graf, Scott Feldman,
	Kővágó,
	Zoltán, Anthony Perard, Samuel Thibault, Jiri Pirko,
	Alistair Francis, Beniamino Galvani, open list:Musicpal,
	Jan Kiszka


[-- Attachment #1.1: Type: text/plain, Size: 3924 bytes --]

On 06/16/2016 07:15 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> From: Kővágó, Zoltán <dirty.ice.hu@gmail.com>
>>
>> Except qapi-schema.json, this patch was generated by:
>>
>> find . -name .git -prune -o -type f \! -name '*~' -print0 | \
>>   xargs -0 sed -i \
>>     -e 's/NetClientOptionsKind/NetClientDriver/g' \
>>     -e 's/NET_CLIENT_OPTIONS_KIND_/NET_CLIENT_DRIVER_/g' \
>>     -e 's/netdev->opts/netdev/g'
> 
> Uh, this is prone to descend into build trees and edit random crap.  I
> used
> 
>     $ sed -i -e ... `git-ls-tree -r HEAD | awk '$2 == "blob" { print $4 }'`
> 
> to verify this commit.  Differences noted inline.
> 

> I additionally get:
> 
>   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,
> 
> Rebase needed?

Unfortunately yes. This patch has been under a LOT of churn since it was
first written; it may be better to just redo it from scratch and claim
ownership myself, since it hardly resembles Zoltán's original submission
(but of course, give him credit for the idea).

> 
>> 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;
> 
> sed turns this into
> 
>        dump = netdev->u.dump.data;
> 
> Is this part of the manual changes?  More of the same below.
> 

The original sed script is so distant from the actual changes that it's
not worth mentioning the sed script in the commit message any more.

>>
>>      assert(peer);
>>
> 
> Another possible case of "rebase needed":
> 
>   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);

Yep.  Thanks for researching.

>>
>>  static int net_client_init1(const void *object, int is_netdev, Error **errp)
>>  {
> 
> Multiple differences in this function.  Manual?

Yes.


>> @@ -47,7 +47,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);
> 
> Manual whitespace cleanup.  Okay.
> 

And necessary to shut up checkpatch.  I really get to rewrite the commit
message to something better for v8, don't I.

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


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

[-- Attachment #2: Type: text/plain, Size: 126 bytes --]

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

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

* Re: [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors
  2016-06-14 12:35   ` Markus Armbruster
@ 2016-06-16 14:46     ` Markus Armbruster
  2016-06-16 17:20       ` Eric Blake
  0 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-16 14:46 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Markus Armbruster <armbru@redhat.com> writes:

> Eric Blake <eblake@redhat.com> writes:
>
>> Rather than having two separate visitor callbacks with items
>> already broken out, pass the actual QAPISchemaObjectType object
>> to the visitor.  This lets the visitor access things like
>> type.is_implicit() without needing another parameter, resolving
>> a TODO from previous patches.
>>
>> For convenience and consistency, the 'name' and 'info' parameters
>> are still provided, even though they are now redundant with
>> 'typ.name' and 'typ.info'.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> We've seen this one before :)
>
> The previous review is relevant:
> Message-ID: <878u0hn08l.fsf@dusky.pond.sub.org>
> http://lists.nongnu.org/archive/html/qemu-devel/2016-04/msg02151.html
>
> Quote its recap of pros and cons:
>
> * The existing interface
>
>       def visit_object_type(self, name, info, base, members, variants):
>       def visit_object_type_flat(self, name, info, members, variants):
>
>   is explicit and narrow, but when you need more information, you have
>   to add parameters or functions.
>
> * The new interface
>
>      def visit_object_type(self, name, info, typ):
>
>   avoids that, but now its users can access everything.
>
> End quote.  Let's see how this series profits from the patch, and
> whether we want to change the other visit methods as well for
> consistency.

Where is this used in the rest of the series?

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

* Re: [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F)
  2016-05-20 22:40 [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
                   ` (15 preceding siblings ...)
  2016-05-20 22:59 ` [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F) Eric Blake
@ 2016-06-16 14:57 ` Markus Armbruster
  2016-06-28 18:14   ` Eric Blake
  16 siblings, 1 reply; 53+ messages in thread
From: Markus Armbruster @ 2016-06-16 14:57 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Eric Blake <eblake@redhat.com> writes:

> The end is nearly in sight!  Last August, I started a process of
> cleaning up qapi on top of Markus' introspection work.  Several
> hundred emails later, and numerous intermediate series out of
> the way, I'm finally at the end of my queue of patches that were
> originally sent in v5:
> https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg05410.html

So your QAPI queue is not in fact infinite?  ;-)

The series looks pretty close.  I'm confident we can get it into 2.7.

> v6 was here (yes, 5 months ago):
> https://www.mail-archive.com/qemu-devel@nongnu.org/msg343192.html
>
> Prerequisites:
> My clone/JSON visitor (weak; this series is mostly orthogonal to
> that one, if we want to swap the order of the two based on review
> complexity):
> https://www.mail-archive.com/qemu-devel%40nongnu.org/msg371555.html
>
> Once my series are in, we should focus on Marc-Andre's cleanups.

Yes.

> Ideas for further qapi work - which I have NOT coded yet:
> - 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)
> - finally QAPI-fy device_add
> - consider swapping blockdev-add to use QAPI structs throughout the
> code-base, rather than converting back into flat QDict and using QemuOpts
> - introspection of command line options

Worthy projects.

I appreciate your patches very much, but if you keep making them faster
than I can review them, I'll get tired of QAPI.  I'd love to get around
to some serious development once in a while...

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

* Re: [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors
  2016-06-16 14:46     ` Markus Armbruster
@ 2016-06-16 17:20       ` Eric Blake
  2016-06-17  7:39         ` Markus Armbruster
  0 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-06-16 17:20 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/16/2016 08:46 AM, Markus Armbruster wrote:
> Markus Armbruster <armbru@redhat.com> writes:
> 
>> Eric Blake <eblake@redhat.com> writes:
>>
>>> Rather than having two separate visitor callbacks with items
>>> already broken out, pass the actual QAPISchemaObjectType object
>>> to the visitor.  This lets the visitor access things like
>>> type.is_implicit() without needing another parameter, resolving
>>> a TODO from previous patches.
>>>
>>> For convenience and consistency, the 'name' and 'info' parameters
>>> are still provided, even though they are now redundant with
>>> 'typ.name' and 'typ.info'.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>
>> We've seen this one before :)
>>

>>
>> End quote.  Let's see how this series profits from the patch, and
>> whether we want to change the other visit methods as well for
>> consistency.
> 
> Where is this used in the rest of the series?

Hmm, I don't know that it actually makes a difference, unless we expand
its scope to also do the same things for commands and events (rather
than adding a 'box' parameter to those callbacks).

And deferring it doesn't break things anywhere else in this series.  I
guess we drop it.

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

* Re: [Qemu-devel] [PATCH v7 01/15] qapi: Consolidate object visitors
  2016-06-16 17:20       ` Eric Blake
@ 2016-06-17  7:39         ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-17  7:39 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 06/16/2016 08:46 AM, Markus Armbruster wrote:
>> Markus Armbruster <armbru@redhat.com> writes:
>> 
>>> Eric Blake <eblake@redhat.com> writes:
>>>
>>>> Rather than having two separate visitor callbacks with items
>>>> already broken out, pass the actual QAPISchemaObjectType object
>>>> to the visitor.  This lets the visitor access things like
>>>> type.is_implicit() without needing another parameter, resolving
>>>> a TODO from previous patches.
>>>>
>>>> For convenience and consistency, the 'name' and 'info' parameters
>>>> are still provided, even though they are now redundant with
>>>> 'typ.name' and 'typ.info'.
>>>>
>>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>>> We've seen this one before :)
>>>
>
>>>
>>> End quote.  Let's see how this series profits from the patch, and
>>> whether we want to change the other visit methods as well for
>>> consistency.
>> 
>> Where is this used in the rest of the series?
>
> Hmm, I don't know that it actually makes a difference, unless we expand
> its scope to also do the same things for commands and events (rather
> than adding a 'box' parameter to those callbacks).
>
> And deferring it doesn't break things anywhere else in this series.  I
> guess we drop it.

Let's keep it on the shelf for now.

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

* Re: [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered
  2016-06-14 13:46     ` Eric Blake
@ 2016-06-28  1:52       ` Eric Blake
  2016-06-28  7:57         ` Markus Armbruster
  2016-07-03  2:34       ` Eric Blake
  1 sibling, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-06-28  1:52 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/14/2016 07:46 AM, Eric Blake wrote:
> On 06/14/2016 07:24 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>>
>>> 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.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>
>> I'd let the cases not mentioned default to the empty type (ample
>> precedence in other languages), but I can live with making the user ask
>> for the empty type explicitly.  But we should then make that less
>> cumbersome than now: you have to define an empty struct type, and use
>> that.  Examples of such hoop-jumping: CpuInfoOther, Abort,
>> NetdevNoneOptions.
> 
> Later in the series, I do just that, so that we can write 'other':{}
> instead of 'other':'CpuInfoOther'.
> 
> What I did not do (but maybe should) is make that short syntax possible
> on simple unions (so that we could do 'abort':{} rather than
> 'abort':'Abort') - and merely distinguish that simple unions cannot
> stick anything within the {}, thus leaving non-empty anonymous branches
> only for discriminated unions.  Can do that as a followup or if this
> series needs a respin.

In fact, simple unions can't omit branches (the enum is generated from
the branch names that are explicitly mentioned); so for symmetry, an
explicit empty branch in a flat union is nicer than an omitted branch.

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

* Re: [Qemu-devel] [PATCH v7 10/15] qapi-event: Reduce chance of collision with event data
  2016-06-16 12:25     ` Markus Armbruster
@ 2016-06-28  3:20       ` Eric Blake
  2016-06-28  8:06         ` Markus Armbruster
  0 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-06-28  3:20 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/16/2016 06:25 AM, Markus Armbruster wrote:
> Markus Armbruster <armbru@redhat.com> writes:
> 
>> Eric Blake <eblake@redhat.com> writes:
>>
>>> When an event has data that is not boxed, we are exposing all of
>>> its members alongside our local variables.  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 a shell that creates a
>>> simple wrapper, then calls a worker that operates on only the
>>> boxed version and thus has no user-supplied names to worry about
>>> in naming its local variables.  For boxed events, we don't need
>>> the wrapper.
>>>
>>> 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.
>>>

>>> +++ b/scripts/qapi.py
>>> @@ -1016,7 +1016,6 @@ class QAPISchemaObjectType(QAPISchemaType):
>>>          return QAPISchemaType.c_name(self)
>>>
>>>      def c_type(self):
>>> -        assert not self.is_implicit()
>>
>> Huh?

Required, because we now pass a pointer to an implicit type from
qapi_event_send_FOO() to do_qapi_event_send_FOO(), so the c_type() of
that implicit type is required for generating the C type for that
parameter.  Will document it better in the commit message.


>>> @@ -93,20 +92,11 @@ def gen_event_send(name, arg_type, box):
>>>          ret += mcgen('''
>>>      v = qmp_output_visitor_new(&obj);
>>>
>>> -''')
>>> -
>>> -        if box:
>>> -            ret += mcgen('''
>>> -    visit_type_%(c_name)s(v, NULL, &arg, &err);
>>> -''',
>>> -                         c_name=arg_type.c_name(), name=arg_type.name)
>>> -        else:
>>> -            ret += mcgen('''
>>>      visit_start_struct(v, "%(name)s", NULL, 0, &err);
>>>      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);
>>>      }
>>
>> Getting confused...  why are we getting rid of the box case here?

No good reason. The visit_type_FOO() is more compact than
visit_type_FOO_members(), but only when we have a non-implicit type.  So
for v8, I'm switching the conditional from 'if box:' to 'if
arg_type.is_implicit():', more or less.

>>
>> Too many conditionals...  gen_event_send() has three cases: empty
>> arg_type, non-empty arg_type and box, non-empty arg_type and not box.
>> The commit message shows the change to generated code for the second
>> case.  It doesn't show visit_type_%(c_name)s(v, NULL, &arg, &err) going
>> away.
> 
> Case empty arg_type: no change
> Example: POWERDOWN

Good.

> 
> Case non-empty arg_type and box: visit gets open-coded
> Example: EVENT_E

Fixed in v8 so that it no longer changes.

> The open-coded visit drops the !*obj check (okay, @arg isn't going
> anywhere), skips the visit_check_struct() differently, and drops the
> qapi_free_FOO() (okay, condition is always false here).
> 
> So this isn't wrong.  But why open-code?

No need to add new open-coding, but we already had existing open-coding
for anonymous non-boxed 'data' (in part because commit 7ce106a9
intentionally chose not to create visit_type_FOO() for implicit types).

> 
> Case non-empty arg_type and not box:
> Example: ACPI_DEVICE_OST
> 

> 
> This is the case the commit message advertises.
> 
> There is no visit_type_FOO() we could compare too, since FOO is an
> implicit type

And in reviewing your message, I realize we have NO testsuite coverage of:

{ 'event': 'EVENT', 'data': 'NamedStruct' }

Guess I get to add that first.  Such a usage will then be improved by
using visit_type_NamedStruct() rather than open-coding around
visit_type_NamedStruct_members().

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

* Re: [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered
  2016-06-28  1:52       ` Eric Blake
@ 2016-06-28  7:57         ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-28  7:57 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 06/14/2016 07:46 AM, Eric Blake wrote:
>> On 06/14/2016 07:24 AM, Markus Armbruster wrote:
>>> Eric Blake <eblake@redhat.com> writes:
>>>
>>>> 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.
>>>>
>>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>>> I'd let the cases not mentioned default to the empty type (ample
>>> precedence in other languages), but I can live with making the user ask
>>> for the empty type explicitly.  But we should then make that less
>>> cumbersome than now: you have to define an empty struct type, and use
>>> that.  Examples of such hoop-jumping: CpuInfoOther, Abort,
>>> NetdevNoneOptions.
>> 
>> Later in the series, I do just that, so that we can write 'other':{}
>> instead of 'other':'CpuInfoOther'.
>> 
>> What I did not do (but maybe should) is make that short syntax possible
>> on simple unions (so that we could do 'abort':{} rather than
>> 'abort':'Abort') - and merely distinguish that simple unions cannot
>> stick anything within the {}, thus leaving non-empty anonymous branches
>> only for discriminated unions.  Can do that as a followup or if this
>> series needs a respin.
>
> In fact, simple unions can't omit branches (the enum is generated from
> the branch names that are explicitly mentioned); so for symmetry, an
> explicit empty branch in a flat union is nicer than an omitted branch.

Fair enough.

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

* Re: [Qemu-devel] [PATCH v7 10/15] qapi-event: Reduce chance of collision with event data
  2016-06-28  3:20       ` Eric Blake
@ 2016-06-28  8:06         ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-06-28  8:06 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 06/16/2016 06:25 AM, Markus Armbruster wrote:
>> Markus Armbruster <armbru@redhat.com> writes:
>> 
>>> Eric Blake <eblake@redhat.com> writes:
>>>
>>>> When an event has data that is not boxed, we are exposing all of
>>>> its members alongside our local variables.  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 a shell that creates a
>>>> simple wrapper, then calls a worker that operates on only the
>>>> boxed version and thus has no user-supplied names to worry about
>>>> in naming its local variables.  For boxed events, we don't need
>>>> the wrapper.
>>>>
>>>> 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.
>>>>
>
>>>> +++ b/scripts/qapi.py
>>>> @@ -1016,7 +1016,6 @@ class QAPISchemaObjectType(QAPISchemaType):
>>>>          return QAPISchemaType.c_name(self)
>>>>
>>>>      def c_type(self):
>>>> -        assert not self.is_implicit()
>>>
>>> Huh?
>
> Required, because we now pass a pointer to an implicit type from
> qapi_event_send_FOO() to do_qapi_event_send_FOO(), so the c_type() of
> that implicit type is required for generating the C type for that
> parameter.  Will document it better in the commit message.

Hmm.  The real assertion here is "we generate a C type for this QAPI
object type."  Can we express that in code?

>>>> @@ -93,20 +92,11 @@ def gen_event_send(name, arg_type, box):
>>>>          ret += mcgen('''
>>>>      v = qmp_output_visitor_new(&obj);
>>>>
>>>> -''')
>>>> -
>>>> -        if box:
>>>> -            ret += mcgen('''
>>>> -    visit_type_%(c_name)s(v, NULL, &arg, &err);
>>>> -''',
>>>> -                         c_name=arg_type.c_name(), name=arg_type.name)
>>>> -        else:
>>>> -            ret += mcgen('''
>>>>      visit_start_struct(v, "%(name)s", NULL, 0, &err);
>>>>      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);
>>>>      }
>>>
>>> Getting confused...  why are we getting rid of the box case here?
>
> No good reason. The visit_type_FOO() is more compact than
> visit_type_FOO_members(), but only when we have a non-implicit type.  So
> for v8, I'm switching the conditional from 'if box:' to 'if
> arg_type.is_implicit():', more or less.
>
>>>
>>> Too many conditionals...  gen_event_send() has three cases: empty
>>> arg_type, non-empty arg_type and box, non-empty arg_type and not box.
>>> The commit message shows the change to generated code for the second
>>> case.  It doesn't show visit_type_%(c_name)s(v, NULL, &arg, &err) going
>>> away.
>> 
>> Case empty arg_type: no change
>> Example: POWERDOWN
>
> Good.
>
>> 
>> Case non-empty arg_type and box: visit gets open-coded
>> Example: EVENT_E
>
> Fixed in v8 so that it no longer changes.
>
>> The open-coded visit drops the !*obj check (okay, @arg isn't going
>> anywhere), skips the visit_check_struct() differently, and drops the
>> qapi_free_FOO() (okay, condition is always false here).
>> 
>> So this isn't wrong.  But why open-code?
>
> No need to add new open-coding, but we already had existing open-coding
> for anonymous non-boxed 'data' (in part because commit 7ce106a9
> intentionally chose not to create visit_type_FOO() for implicit types).
>
>> 
>> Case non-empty arg_type and not box:
>> Example: ACPI_DEVICE_OST
>> 
>
>> 
>> This is the case the commit message advertises.
>> 
>> There is no visit_type_FOO() we could compare too, since FOO is an
>> implicit type
>
> And in reviewing your message, I realize we have NO testsuite coverage of:
>
> { 'event': 'EVENT', 'data': 'NamedStruct' }
>
> Guess I get to add that first.  Such a usage will then be improved by
> using visit_type_NamedStruct() rather than open-coding around
> visit_type_NamedStruct_members().

Makes sense.

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

* Re: [Qemu-devel] [PATCH v7 00/15] qapi netdev_add introspection (post-introspection cleanups subset F)
  2016-06-16 14:57 ` Markus Armbruster
@ 2016-06-28 18:14   ` Eric Blake
  0 siblings, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-06-28 18:14 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

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

On 06/16/2016 08:57 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> The end is nearly in sight!  Last August, I started a process of
>> cleaning up qapi on top of Markus' introspection work.  Several
>> hundred emails later, and numerous intermediate series out of
>> the way, I'm finally at the end of my queue of patches that were
>> originally sent in v5:
>> https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg05410.html
> 
> So your QAPI queue is not in fact infinite?  ;-)
> 
> The series looks pretty close.  I'm confident we can get it into 2.7.

I'm still polishing this up for a v8, and while the repost will probably
miss today's softfreeze deadline, at least the release policy permits
the inclusion of patches after softfreeze that were posted prior to
softfreeze, so I think this will qualify under that definition.

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

* Re: [Qemu-devel] [PATCH v7 14/15] qapi: Allow anonymous branch types in flat union
  2016-06-16 14:33   ` Markus Armbruster
@ 2016-07-01 22:59     ` Eric Blake
  2016-07-04 13:13       ` Markus Armbruster
  0 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-07-01 22:59 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/16/2016 08:33 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> 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 '_empty' struct nor a 'visit_type__empty_members()'
>> corresponding to the special ':empty' type; but when the branch
>> is truly empty, there's nothing to do.
> 
> Well, it could emit them, if it makes things easier.
> 
>> 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>
>>

>> @@ -1061,6 +1063,9 @@ class QAPISchemaMember(object):
>>                  return '(parameter of %s)' % owner[:-4]
>>              elif owner.endswith('-base'):
>>                  return '(base of %s)' % owner[:-5]
>> +            elif owner.endswith('-branch'):
>> +                return ('(member of %s branch %s)'
>> +                        % tuple(owner[:-7].split(':')))
> 
> I think we should point to the spot that puts in the colon, and back.
> 
> Do we really need the "of %s" part?
> 

If you think the message reads okay without it, then we can avoid...

>>              else:
>>                  assert owner.endswith('-wrapper')
>>                  # Unreachable and not implemented
>> @@ -1335,7 +1340,11 @@ 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):
>> +            typ = self._make_implicit_object_type(
>> +                "%s:%s" % (owner, case), info, 'branch',
>> +                self._make_members(typ, info)) or 'q_empty'
> 
> This is the spot.
> 

>> @@ -1485,7 +1494,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('.-:', '___')
> 
> Because you use the colon as separator.  Hmm.
> 

...the need to transliterate : into _.  More below[1]


>> +++ b/scripts/qapi-types.py
>> @@ -61,7 +61,8 @@ def gen_object(name, base, members, variants):
>    def gen_object(name, base, members, variants):
>        if name in objects_seen:
>            return ''
>        objects_seen.add(name)
> 
>>      ret = ''
>>      if variants:
>>          for v in variants.variants:
>> -            if isinstance(v.type, QAPISchemaObjectType):
>> +            if (isinstance(v.type, QAPISchemaObjectType)
>> +                    and not (v.type.is_implicit() and v.type.is_empty())):
>>                  ret += gen_object(v.type.name, v.type.base,
>>                                    v.type.local_members, v.type.variants)
>>
> 
> This is the recursion that ensures an object type's variant member types
> are emitted before the object type.
> 
> We can't simply .type == schema.the_empty_object_type like
> qapi-introspect.py does, because we don't have schema handy here.  Hmm.
> 
> Do we really need this change?  Note that gen_object() does nothing for
> name in objects_seen, and we do this in visit_begin():
> 
>         # gen_object() is recursive, ensure it doesn't visit the empty type
>         objects_seen.add(schema.the_empty_object_type.name)

Cool! Remember, these patches have been through a lot of rebase churn -
I did indeed originally have to add this check to work around the empty
type (back when it was based on v5 of the "unboxed visits" series), but
that was before incorporating your suggestions in commit 7ce106a9 of the
nicer recursion prevention (v6 of the "unboxed visits", which is what
got pulled).  You are indeed correct that even without this hunk, things
still work just fine - I just never noticed that across all the rebasing.

> 
>> @@ -123,11 +124,14 @@ def gen_variants(variants):
>>                  c_name=c_name(variants.tag_member.name))
>>
>>      for var in variants.variants:
> 
> Here, we emit the C union member for a variant:
> 
>> +        typ = var.type.c_unboxed_type()
>> +        if (isinstance(var.type, QAPISchemaObjectType) and
>> +                var.type.is_empty() and var.type.is_implicit()):
>> +            typ = 'char'
>>          ret += mcgen('''
>>          %(c_type)s %(c_name)s;
>>  ''',
>> -                     c_type=var.type.c_unboxed_type(),
>> -                     c_name=c_name(var.name))
>> +                     c_type=typ, c_name=c_name(var.name))
> 
> Your change replaces the C type when var.type is the empty type.
> Without that, we'd get 'q_empty', I guess.
> 
> Do we need the union member?  Hmm, we may want to avoid empty unions, to
> avoid pedantic warnings.
> 
> Should we make the_empty_object_type.c_unboxed_type() return a suitable
> C type instead of 'q_empty'?

Sounds like something worthwhile to try, and simpler to handle if it works.

> 
>>
>>      ret += mcgen('''
>>      } u;
>> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
>> index 07ae6d1..46f8b39 100644
>> --- a/scripts/qapi-visit.py
>> +++ b/scripts/qapi-visit.py
>> @@ -79,13 +79,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:
> 
> Here, we emit the visit of the "active" C union member:
> 
>> -        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;
>> +''')
> 
> Your change suppresses the visit_type_FOO_members() call when var.type
> is the empty type.  Without that, we'd get visit_type_q_empty_members(),
> which doesn't exist.
> 
> Why not make it exist?

If .c_unboxed_type() returns 'char' instead of the name of an empty
type, then we are not only avoiding 'q_empty branch;' where we need to
avoid a missing visit_type_q_empty_members(), but we would also be
changing CpuInfo's 'CpuInfoOther other' to 'char other', and would want
to avoid generating visit_type_CpuInfoOther_members().  Then again, one
of the end goals is getting rid of the need for an explicit empty type
in the .json files (CpuInfoOther disappears, to be replaced by an
anonymous branch).

I guess it's a tradeoff for whether special casing the empty type makes
things easier or harder for the rest of the code.  But commit 7ce106a9
documented some reasons for why making visit_type_q_empty_members()
exist is rather hard to achieve - as a built-in type, we would have to
solve problems on how to avoid it having multiple declarations between
the main QMP use and the QGA use.


>> +++ 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' } } }

[1] Here's an example of the error message that would be changed if we
simplify the '%s:%s' string notation mentioned above.  Let's try the
shorter version:

'kind' (member of branch value2) collides with 'kind' (member of Base)

Okay, that might work.  But the other thing we have to worry about is
collision prevention.  When a branch type is anonymous, we synthesize a
visit_type_FOO_members() call - and so we must ensure FOO is unique.  If
two different flat unions both have a branch named 'bar', the current
patch proposal gives:

visit_type_q_obj_Foo1_bar_branch_members()
visit_type_q_obj_Foo2_bar_branch_members()

where the type owning the branch is the only thing different; but
changing the generated name to avoid the colon would produce collisions:

visit_type_q_obj_bar_branch_members() // from Foo1
visit_type_q_obj_bar_branch_members() // from Foo2

So we really DO want both the type name and the branch name included in
the generated name; what's more, since both type names AND branch names
can include '-' (and even '_'), we need a third character to split on if
we are going to have _pretty_owner() do a proper split back into legible
error messages.


>> @@ -81,7 +81,8 @@
>>    'base': { '*integer': 'int', 'string': 'str', 'enum1': 'QEnumTwo' },
>>    'discriminator': 'enum1',
>>    'data': { 'value1' : 'UserDefC', # intentional forward reference
>> -            'value2' : 'UserDefB' } }
> 
> You're deleting the equally intentional case of a backward reference :)

Okay, I'll add the new code as a new branch, rather than deleting an
existing one.

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

* Re: [Qemu-devel] [PATCH v7 13/15] net: Complete qapi-fication of netdev_add
  2016-06-16 13:40   ` Markus Armbruster
@ 2016-07-02 22:58     ` Eric Blake
  2016-07-04 13:46       ` Markus Armbruster
  0 siblings, 1 reply; 53+ messages in thread
From: Eric Blake @ 2016-07-02 22:58 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Jason Wang, Daniel P. Berrange

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

On 06/16/2016 07:40 AM, Markus Armbruster wrote:
> 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, while
>> making no changes to the set of previously-valid QMP commands
>> that would work, and without breaking command line parsing.
> 
> Isn't it amazing that you pulled this off without a compatibility break?

No command line compatibility break, but in testing, I _did_ notice a
potential QMP break [it's hard to argue whether it is a break, given
that it was previously undocumented - I don't know if any QMP clients
were actually relying on loose behavior]:

Pre-patch:
    {'execute':'netdev_add',
      'arguments':{'id':'net1', 'type':'hubport', 'hubid':1}}
    {"return": {}}
    {'execute':'netdev_add',
      'arguments':{'id':'net2', 'type':'hubport', 'hubid':"2"}}
    {"return": {}}

Post-patch:
    {'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"}}

I'm half-tempted to claim that we should update the QMP spec to say that
our parser is ALWAYS loose (anywhere a built-in scalar type is listed in
introspection, whether bool or integer, the parser will always accept an
equivalent string on input - but output will always be the named type),
and then relax qmp-input-visitor accordingly.  In fact, danpb has
already proposed patches that allow "parse-string-as-int" as intentional
behavior, although under the guise of a new visitor rather than tweaking
qmp-input-visitor - so it just becomes a question of do we do it in
limited situations, or always.  "Be liberal in what you accept" comes to
mind.

And as a followon thought: if we DO update the QMP spec to state that we
always accept a string in place of an integer, then we also have the
luxury of stating that accepting a string "inf" for a QAPI 'number' is
valid (even though strict JSON will not let us pass a bare-word inf) -
and that hits back on my proposal of whether we want to accept bare-word
inf on input as an extension, and whether outputting a string "inf" when
we specified a QAPI type of 'number' would be acceptable (since we would
be canonicalizing input "2" into output 2, going the other direction and
canonicalizing input inf into output "inf" is a bit easier to justify).

But given that it is soft freeze time, I guess we need to be
conservative at what changes we want to support at this phase of
development.

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

* Re: [Qemu-devel] [PATCH v7 03/15] qapi: Require all branches of flat union enum to be covered
  2016-06-14 13:46     ` Eric Blake
  2016-06-28  1:52       ` Eric Blake
@ 2016-07-03  2:34       ` Eric Blake
  1 sibling, 0 replies; 53+ messages in thread
From: Eric Blake @ 2016-07-03  2:34 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/14/2016 07:46 AM, Eric Blake wrote:

>> I'd let the cases not mentioned default to the empty type (ample
>> precedence in other languages), but I can live with making the user ask
>> for the empty type explicitly.  But we should then make that less
>> cumbersome than now: you have to define an empty struct type, and use
>> that.  Examples of such hoop-jumping: CpuInfoOther, Abort,
>> NetdevNoneOptions.
> 
> Later in the series, I do just that, so that we can write 'other':{}
> instead of 'other':'CpuInfoOther'.
> 
> What I did not do (but maybe should) is make that short syntax possible
> on simple unions (so that we could do 'abort':{} rather than
> 'abort':'Abort') - and merely distinguish that simple unions cannot
> stick anything within the {}, thus leaving non-empty anonymous branches
> only for discriminated unions.  Can do that as a followup or if this
> series needs a respin.

In fact, it turns out that allowing 'abort':{} for 'TransactionAction'
is harder than I thought - we still have to create a wrapper type, and
the wrapper type must still be used to parse:

'action': { 'type':'abort', 'data': {} }

and qapi-types.h would look roughly like:

+struct q_obj_q_empty_wrapper {
+    q_empty *data;
+};
...
 struct TransactionAction {
     TransactionActionKind type;
     union { /* union tag is @type */
         q_obj_BlockdevSnapshot_wrapper blockdev_snapshot;
         q_obj_BlockdevSnapshotSync_wrapper blockdev_snapshot_sync;
         q_obj_DriveBackup_wrapper drive_backup;
         q_obj_BlockdevBackup_wrapper blockdev_backup;
-        q_obj_Abort_wrapper abort;
+        q_obj_q_empty_wrapper abort;

but as I said elsewhere in this thread, we then have to figure out how
to emit 'q_empty' and 'visit_type_q_empty_members()' without running
into duplicate definitions.

For that matter, it might be nice if the parser would allow:

'action': { 'type':'abort' }

that is, treat 'data' as optional if the only thing data would contain
is an empty dictionary - but that would be even trickier to code.

So for v8, I'm not getting rid of any empty types for simple unions.

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

* Re: [Qemu-devel] [PATCH v7 14/15] qapi: Allow anonymous branch types in flat union
  2016-07-01 22:59     ` Eric Blake
@ 2016-07-04 13:13       ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-07-04 13:13 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 06/16/2016 08:33 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> 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 '_empty' struct nor a 'visit_type__empty_members()'
>>> corresponding to the special ':empty' type; but when the branch
>>> is truly empty, there's nothing to do.
>> 
>> Well, it could emit them, if it makes things easier.
>> 
>>> 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>
>>>
>
>>> @@ -1061,6 +1063,9 @@ class QAPISchemaMember(object):
>>>                  return '(parameter of %s)' % owner[:-4]
>>>              elif owner.endswith('-base'):
>>>                  return '(base of %s)' % owner[:-5]
>>> +            elif owner.endswith('-branch'):
>>> +                return ('(member of %s branch %s)'
>>> +                        % tuple(owner[:-7].split(':')))
>> 
>> I think we should point to the spot that puts in the colon, and back.
>> 
>> Do we really need the "of %s" part?
>> 
>
> If you think the message reads okay without it, then we can avoid...
>
>>>              else:
>>>                  assert owner.endswith('-wrapper')
>>>                  # Unreachable and not implemented
>>> @@ -1335,7 +1340,11 @@ 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):
>>> +            typ = self._make_implicit_object_type(
>>> +                "%s:%s" % (owner, case), info, 'branch',
>>> +                self._make_members(typ, info)) or 'q_empty'
>> 
>> This is the spot.
>> 
>
>>> @@ -1485,7 +1494,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('.-:', '___')
>> 
>> Because you use the colon as separator.  Hmm.
>> 
>
> ...the need to transliterate : into _.  More below[1]
>
>
>>> +++ b/scripts/qapi-types.py
>>> @@ -61,7 +61,8 @@ def gen_object(name, base, members, variants):
>>    def gen_object(name, base, members, variants):
>>        if name in objects_seen:
>>            return ''
>>        objects_seen.add(name)
>> 
>>>      ret = ''
>>>      if variants:
>>>          for v in variants.variants:
>>> -            if isinstance(v.type, QAPISchemaObjectType):
>>> +            if (isinstance(v.type, QAPISchemaObjectType)
>>> +                    and not (v.type.is_implicit() and v.type.is_empty())):
>>>                  ret += gen_object(v.type.name, v.type.base,
>>>                                    v.type.local_members, v.type.variants)
>>>
>> 
>> This is the recursion that ensures an object type's variant member types
>> are emitted before the object type.
>> 
>> We can't simply .type == schema.the_empty_object_type like
>> qapi-introspect.py does, because we don't have schema handy here.  Hmm.
>> 
>> Do we really need this change?  Note that gen_object() does nothing for
>> name in objects_seen, and we do this in visit_begin():
>> 
>>         # gen_object() is recursive, ensure it doesn't visit the empty type
>>         objects_seen.add(schema.the_empty_object_type.name)
>
> Cool! Remember, these patches have been through a lot of rebase churn -
> I did indeed originally have to add this check to work around the empty
> type (back when it was based on v5 of the "unboxed visits" series), but
> that was before incorporating your suggestions in commit 7ce106a9 of the
> nicer recursion prevention (v6 of the "unboxed visits", which is what
> got pulled).  You are indeed correct that even without this hunk, things
> still work just fine - I just never noticed that across all the rebasing.
>
>> 
>>> @@ -123,11 +124,14 @@ def gen_variants(variants):
>>>                  c_name=c_name(variants.tag_member.name))
>>>
>>>      for var in variants.variants:
>> 
>> Here, we emit the C union member for a variant:
>> 
>>> +        typ = var.type.c_unboxed_type()
>>> +        if (isinstance(var.type, QAPISchemaObjectType) and
>>> +                var.type.is_empty() and var.type.is_implicit()):
>>> +            typ = 'char'
>>>          ret += mcgen('''
>>>          %(c_type)s %(c_name)s;
>>>  ''',
>>> -                     c_type=var.type.c_unboxed_type(),
>>> -                     c_name=c_name(var.name))
>>> +                     c_type=typ, c_name=c_name(var.name))
>> 
>> Your change replaces the C type when var.type is the empty type.
>> Without that, we'd get 'q_empty', I guess.
>> 
>> Do we need the union member?  Hmm, we may want to avoid empty unions, to
>> avoid pedantic warnings.
>> 
>> Should we make the_empty_object_type.c_unboxed_type() return a suitable
>> C type instead of 'q_empty'?
>
> Sounds like something worthwhile to try, and simpler to handle if it works.
>
>> 
>>>
>>>      ret += mcgen('''
>>>      } u;
>>> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
>>> index 07ae6d1..46f8b39 100644
>>> --- a/scripts/qapi-visit.py
>>> +++ b/scripts/qapi-visit.py
>>> @@ -79,13 +79,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:
>> 
>> Here, we emit the visit of the "active" C union member:
>> 
>>> -        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;
>>> +''')
>> 
>> Your change suppresses the visit_type_FOO_members() call when var.type
>> is the empty type.  Without that, we'd get visit_type_q_empty_members(),
>> which doesn't exist.
>> 
>> Why not make it exist?
>
> If .c_unboxed_type() returns 'char' instead of the name of an empty
> type, then we are not only avoiding 'q_empty branch;' where we need to
> avoid a missing visit_type_q_empty_members(), but we would also be
> changing CpuInfo's 'CpuInfoOther other' to 'char other', and would want
> to avoid generating visit_type_CpuInfoOther_members().  Then again, one
> of the end goals is getting rid of the need for an explicit empty type
> in the .json files (CpuInfoOther disappears, to be replaced by an
> anonymous branch).
>
> I guess it's a tradeoff for whether special casing the empty type makes
> things easier or harder for the rest of the code.  But commit 7ce106a9
> documented some reasons for why making visit_type_q_empty_members()
> exist is rather hard to achieve - as a built-in type, we would have to
> solve problems on how to avoid it having multiple declarations between
> the main QMP use and the QGA use.

We already emit code for built-in types that is to be shared between QMP
and QGA, both to .h and .c.  The way we do it isn't exactly pretty (I
dislike it in fact), but it works.

>>> +++ 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' } } }
>
> [1] Here's an example of the error message that would be changed if we
> simplify the '%s:%s' string notation mentioned above.  Let's try the
> shorter version:
>
> 'kind' (member of branch value2) collides with 'kind' (member of Base)
>
> Okay, that might work.  But the other thing we have to worry about is
> collision prevention.  When a branch type is anonymous, we synthesize a
> visit_type_FOO_members() call - and so we must ensure FOO is unique.  If
> two different flat unions both have a branch named 'bar', the current
> patch proposal gives:
>
> visit_type_q_obj_Foo1_bar_branch_members()
> visit_type_q_obj_Foo2_bar_branch_members()
>
> where the type owning the branch is the only thing different; but
> changing the generated name to avoid the colon would produce collisions:
>
> visit_type_q_obj_bar_branch_members() // from Foo1
> visit_type_q_obj_bar_branch_members() // from Foo2
>
> So we really DO want both the type name and the branch name included in
> the generated name; what's more, since both type names AND branch names
> can include '-' (and even '_'), we need a third character to split on if
> we are going to have _pretty_owner() do a proper split back into legible
> error messages.

I'm afraid we're once again growing beyond what our hacks can reasonably
support.  Having _pretty_owner key on a few suffixes is a tolerable
hack.  Taking off the suffix then splitting the remainder is a bit much.

We use .owner as key for case_whitelist, and for _pretty_owner().
Should it be an object instead of a string?

>>> @@ -81,7 +81,8 @@
>>>    'base': { '*integer': 'int', 'string': 'str', 'enum1': 'QEnumTwo' },
>>>    'discriminator': 'enum1',
>>>    'data': { 'value1' : 'UserDefC', # intentional forward reference
>>> -            'value2' : 'UserDefB' } }
>> 
>> You're deleting the equally intentional case of a backward reference :)
>
> Okay, I'll add the new code as a new branch, rather than deleting an
> existing one.

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

* Re: [Qemu-devel] [PATCH v7 13/15] net: Complete qapi-fication of netdev_add
  2016-07-02 22:58     ` Eric Blake
@ 2016-07-04 13:46       ` Markus Armbruster
  0 siblings, 0 replies; 53+ messages in thread
From: Markus Armbruster @ 2016-07-04 13:46 UTC (permalink / raw)
  To: Eric Blake; +Cc: Jason Wang, qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 06/16/2016 07:40 AM, Markus Armbruster wrote:
>> 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, while
>>> making no changes to the set of previously-valid QMP commands
>>> that would work, and without breaking command line parsing.
>> 
>> Isn't it amazing that you pulled this off without a compatibility break?
>
> No command line compatibility break, but in testing, I _did_ notice a
> potential QMP break [it's hard to argue whether it is a break, given
> that it was previously undocumented - I don't know if any QMP clients
> were actually relying on loose behavior]:
>
> Pre-patch:
>     {'execute':'netdev_add',
>       'arguments':{'id':'net1', 'type':'hubport', 'hubid':1}}
>     {"return": {}}
>     {'execute':'netdev_add',
>       'arguments':{'id':'net2', 'type':'hubport', 'hubid':"2"}}
>     {"return": {}}
>
> Post-patch:
>     {'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"}}

You're right.  My brain keeps suppressing this ugly case.

> I'm half-tempted to claim that we should update the QMP spec to say that
> our parser is ALWAYS loose (anywhere a built-in scalar type is listed in
> introspection, whether bool or integer, the parser will always accept an
> equivalent string on input - but output will always be the named type),
> and then relax qmp-input-visitor accordingly.  In fact, danpb has
> already proposed patches that allow "parse-string-as-int" as intentional
> behavior, although under the guise of a new visitor rather than tweaking
> qmp-input-visitor - so it just becomes a question of do we do it in
> limited situations, or always.  "Be liberal in what you accept" comes to
> mind.

I'm afraid we may indeed need a lose mode for backward compatibility of
QMP commands that used to take a detour through the not-really-typed
parts of the command line code (netdev_add now, device_add later).

Nevertheless, I'm reluctant to adopt it wholesale.  With a few decades
of being "liberal in what you accept" under our belts, it doesn't sound
like good advice anymore.  All too often we've ended up with differently
liberal implementations (both on the accepting and the sending side) to
the point where nobody really knows what crap you have to accept for
interoperability.

Instead, I'd prefer a way for a QMP client to opt into a type-correct
version of the command.  Easiest way to do it is to add a new one, and
deprecate the old one.

> And as a followon thought: if we DO update the QMP spec to state that we
> always accept a string in place of an integer, then we also have the
> luxury of stating that accepting a string "inf" for a QAPI 'number' is
> valid (even though strict JSON will not let us pass a bare-word inf) -
> and that hits back on my proposal of whether we want to accept bare-word
> inf on input as an extension, and whether outputting a string "inf" when
> we specified a QAPI type of 'number' would be acceptable (since we would
> be canonicalizing input "2" into output 2, going the other direction and
> canonicalizing input inf into output "inf" is a bit easier to justify).

I've come to the conclusion that the "can't express non-finite numbers
in QMP" problem is basically irrelevant now.  Solving it wouldn't really
accomplish much, as we'd nevertheless want to avoid non-finite numbers
for compatibility with older clients.  There are very few places that
can conceivably emit them anyway.

Besides, if you don't like JSON as a transport encoding, then switching
to one you like better makes a whole lot more sense to me than using it
in unnatural ways like "if the string contents looks like a number (my
definition of 'number', not JSON's), then it can also be a number, and
whether it is depends on the context."

> But given that it is soft freeze time, I guess we need to be
> conservative at what changes we want to support at this phase of
> development.

Yup.

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

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

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

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.