From: Laszlo Ersek <lersek@redhat.com>
To: Luiz Capitulino <lcapitulino@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>, qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH v2 05/17] qapi: introduce OptsVisitor
Date: Sat, 14 Jul 2012 00:48:58 +0200 [thread overview]
Message-ID: <5000A5DA.9050904@redhat.com> (raw)
In-Reply-To: <20120713135136.049b9e54@doriath.home>
[-- Attachment #1: Type: text/plain, Size: 6752 bytes --]
On 07/13/12 18:51, Luiz Capitulino wrote:
> On Wed, 13 Jun 2012 10:22:36 +0200
> Laszlo Ersek <lersek@redhat.com> wrote:
>> Repeating an optarg is supported;
>
> I see that the current code supports this too, but why? Something
> like this should fail:
>
> -netdev type=tap,vhost=on,vhost=off,id=guest1,script=qemu-ifup-switch
> Also, you're using a queue to support the repeating of optargs,
> right? I think this could be simplified if we just don't support
> that.
I hate repeated options with a passion, but SLIRP's hostfwd and guestfwd
depend on repetition.
When the outermost opts_start_struct() is invoked and I shovel the
optargs into the queues, I can't yet know what's going to be used in
repeated form and what not.
If you prefer I can change lookup_scalar() as follows. For reference:
>> +static GQueue *
>> +lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
>> +{
>> + GQueue *list;
>> +
>> + list = g_hash_table_lookup(ov->unprocessed_opts, name);
>> + if (!list) {
>> + error_set(errp, QERR_MISSING_PARAMETER, name);
>> + }
>> + return list;
>> +}
>> +static void
>> +opts_start_optional(Visitor *v, bool *present, const char *name,
>> + Error **errp)
>> +{
>> + OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>> +
>> + /* we only support a single mandatory scalar field in a list node */
>> + assert(ov->repeated_opts == NULL);
>> + *present = (lookup_distinct(ov, name, NULL) != NULL);
>> +}
>> +static const QemuOpt *
>> +lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
>> +{
>> + if (ov->repeated_opts == NULL) {
>> + GQueue *list;
>> +
>> + /* the last occurrence of any QemuOpt takes effect when queried by name
>> + */
>> + list = lookup_distinct(ov, name, errp;
>> + return list ? g_queue_peek_tail(list) : NULL;
We're outside of list traversal in this branch, meaning the optarg is
allowed exactly once. (Optional optargs are first handled by
opts_start_optional().) If lookup_distinct() succeeds here, then rather
than returning the last occurrence, I could check the depth of the queue
(== 1 or > 1), and set an error for > 1.
However QemuOpts definitely supports repeated optargs now (otherwise
slirp hostfwd/guestfwd wouldn't work). qemu_opt_foreach() is used for
iteration (with QTAILQ_FOREACH()), while qemu_opt_find() -- and thus its
direct callers -- rely on QTAILQ_FOREACH_REVERSE() and the first match.
Optargs of an option are apparently chained like this:
qemu_opts_parse() [qemu-option.c]
opts_parse(..., defaults=false)
opts_do_parse(..., prepend=false)
opt_set(..., prepend=false, ...)
QTAILQ_INSERT_TAIL()
"-option arg=val1,arg=val2,arg=val3" is therefore linked into the
corresponding QemuOpts instance in the same order, and qemu_opt_find()
will return "arg=val3". I also use g_queue_push_tail() and
g_queue_peek_tail(), so I think we're compatible.
>> + }
>> + return g_queue_peek_head(ov->repeated_opts);
>> +}
Continuing slightly out of order:
>> +/* mimics qemu-option.c::parse_option_bool() */
>> +static void
>> +opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
>> +{
>> + OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>> + const QemuOpt *opt;
>> +
>> + opt = lookup_scalar(ov, name, errp);
>> + if (!opt) {
>> + return;
>> + }
>> +
>> + if (opt->str) {
>> + if (strcmp(opt->str, "on") == 0 ||
>> + strcmp(opt->str, "yes") == 0 ||
>> + strcmp(opt->str, "y") == 0) {
>> + *obj = true;
>> + } else if (strcmp(opt->str, "off") == 0 ||
>> + strcmp(opt->str, "no") == 0 ||
>> + strcmp(opt->str, "n") == 0) {
>> + *obj = false;
>
> The current code only accepts 'on' or 'off', no reason to change that.
>
>> + } else {
>> + error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>> + "on|yes|y|off|no|n");
>> + return;
>> + }
>> + } else {
>> + *obj = true;
>> + }
>> +
>> + processed(ov, name);
>> +}
This function is used for "bool" generally. The following optargs were
all unified as "bool":
- slirp/restrict: originally QEMU_OPT_STRING, net_init_slirp() accepting
all of "on|yes|y|off|no|n"
- tap/vnet_hdr: originally QEMU_OPT_BOOL, parse_option_bool() accepting
"on|off".
- tap/vhost: ditto
- tap/vhostforce: ditto
So I took the union (nothing should break that used to work).
The leading comment rather means that the structure of
parse_option_bool() is followed:
- optarg values meaning "true": true
- optarg values meaning "false": false
- other optarg values: error
- no optarg value at all: true
>> +static void
>> +opts_start_struct(Visitor *v, void **obj, const char *kind,
>> + const char *name, size_t size, Error **errp)
>> +{
>> + OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>> + const QemuOpt *opt;
>> +
>> + *obj = g_malloc0(size);
>> + if (ov->depth++ > 0) {
>> + return;
>> + }
>> +
>> + ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
>> + NULL, &destroy_list);
>> + QTAILQ_FOREACH(opt, &ov->opts_root->head, next) {
>> + GQueue *list;
>> +
>> + list = g_hash_table_lookup(ov->unprocessed_opts, opt->name);
>> + if (list == NULL) {
>> + list = g_queue_new();
>> +
>> + /* GHashTable will never try to free the keys -- we supplied NULL
>> + * as "key_destroy_func" above. Thus cast away key const-ness in
>> + * order to suppress gcc's warning. */
>> + g_hash_table_insert(ov->unprocessed_opts, (gpointer)opt->name,
>> + list);
>> + }
>> +
>> + /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
>> + g_queue_push_tail(list, (gpointer)opt);
>> + }
>> +}
>
> This doesn't insert the opts id into the hash, so opts_type_str()
> will fail to find the id when the generated code visits it here:
>
> void visit_type_Netdev(Visitor *m, Netdev ** obj, const char *name, Error **errp)
> {
> if (!error_is_set(errp)) {
> Error *err = NULL;
> visit_start_struct(m, (void **)obj, "Netdev", name, sizeof(Netdev), &err);
> if (!err) {
> assert(!obj || *obj);
> visit_type_str(m, obj ? &(*obj)->id : NULL, "id", &err); <---------
> [...]
>
*groan*
You're right. opts_do_parse() makes an exception with "id" and doesn't
call opt_set() for any occurrence of it. Would you accept the attached
fix, split up and squashed into previous parts appropriately?
Thanks!
Laszlo
[-- Attachment #2: 0001-support-NetLegacy-id-and-clean-up-QemuOpts-id-usage.patch --]
[-- Type: text/plain, Size: 4686 bytes --]
>From 6839ead5ac4a77ac82aee2fe1365e72e276aa89d Mon Sep 17 00:00:00 2001
From: Laszlo Ersek <lersek@redhat.com>
Date: Fri, 13 Jul 2012 23:49:09 +0200
Subject: [PATCH] support NetLegacy::id and clean up QemuOpts::id usage
NetLegacy::id is actually allowed and takes precedence over
NetLegacy::name.
Factor opts_visitor_insert() out of opts_start_struct() and call it
separately for opts_root->id if there's any.
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
net.c | 2 +-
qapi-schema.json | 5 ++++-
qapi/opts-visitor.c | 49 +++++++++++++++++++++++++++++++++++++------------
3 files changed, 42 insertions(+), 14 deletions(-)
diff --git a/net.c b/net.c
index 1612f64..dbca77b 100644
--- a/net.c
+++ b/net.c
@@ -869,7 +869,7 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
u.net = object;
opts = u.net->opts;
/* missing optional values have been initialized to "all bits zero" */
- name = u.net->name;
+ name = u.net->has_id ? u.net->id : u.net->name;
}
if (net_client_init_fun[opts->kind]) {
diff --git a/qapi-schema.json b/qapi-schema.json
index ed345ee..cc48127 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2116,7 +2116,9 @@
#
# @vlan: #optional vlan number
#
-# @name: #optional identifier for monitor commands
+# @id: #optional identifier for monitor commands
+#
+# @name: #optional identifier for monitor commands, ignored if @id is present
#
# @traits: device type specific properties (legacy)
#
@@ -2125,6 +2127,7 @@
{ 'type': 'NetLegacy',
'data': {
'*vlan': 'int32',
+ '*id': 'str',
'*name': 'str',
'opts': 'NetClientOptions' } }
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 9187c86..a261cf3 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -35,6 +35,12 @@ struct OptsVisitor
* schema, with a single mandatory scalar member. */
GQueue *repeated_opts;
bool repeated_opts_first;
+
+ /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for
+ * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does
+ * not survive or escape the OptsVisitor object.
+ */
+ QemuOpt *fake_id_opt;
};
@@ -46,6 +52,27 @@ destroy_list(gpointer list)
static void
+opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
+{
+ GQueue *list;
+
+ list = g_hash_table_lookup(unprocessed_opts, opt->name);
+ if (list == NULL) {
+ list = g_queue_new();
+
+ /* GHashTable will never try to free the keys -- we supply NULL as
+ * "key_destroy_func" in opts_start_struct(). Thus cast away key
+ * const-ness in order to suppress gcc's warning.
+ */
+ g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list);
+ }
+
+ /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
+ g_queue_push_tail(list, (gpointer)opt);
+}
+
+
+static void
opts_start_struct(Visitor *v, void **obj, const char *kind,
const char *name, size_t size, Error **errp)
{
@@ -60,21 +87,18 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
NULL, &destroy_list);
QTAILQ_FOREACH(opt, &ov->opts_root->head, next) {
- GQueue *list;
+ /* ensured by qemu-option.c::opts_do_parse() */
+ assert(strcmp(opt->name, "id") != 0);
- list = g_hash_table_lookup(ov->unprocessed_opts, opt->name);
- if (list == NULL) {
- list = g_queue_new();
+ opts_visitor_insert(ov->unprocessed_opts, opt);
+ }
- /* GHashTable will never try to free the keys -- we supplied NULL
- * as "key_destroy_func" above. Thus cast away key const-ness in
- * order to suppress gcc's warning. */
- g_hash_table_insert(ov->unprocessed_opts, (gpointer)opt->name,
- list);
- }
+ if (ov->opts_root->id != NULL) {
+ ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);
- /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
- g_queue_push_tail(list, (gpointer)opt);
+ ov->fake_id_opt->name = "id";
+ ov->fake_id_opt->str = ov->opts_root->id;
+ opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
}
}
@@ -390,6 +414,7 @@ opts_visitor_cleanup(OptsVisitor *ov)
if (ov->unprocessed_opts != NULL) {
g_hash_table_destroy(ov->unprocessed_opts);
}
+ g_free(ov->fake_id_opt);
memset(ov, '\0', sizeof *ov);
}
--
1.7.1
next prev parent reply other threads:[~2012-07-13 22:47 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-06-13 8:22 [Qemu-devel] [PATCH v2 00/17] introduce OptsVisitor, rebase -net/-netdev parsing Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 01/17] qapi: fix error propagation Laszlo Ersek
2012-07-13 16:38 ` Luiz Capitulino
2012-07-13 17:30 ` Laszlo Ersek
2012-07-13 19:11 ` Paolo Bonzini
2012-07-13 20:11 ` Laszlo Ersek
2012-07-16 17:12 ` Luiz Capitulino
2012-07-16 20:31 ` Laszlo Ersek
2012-07-16 20:44 ` Luiz Capitulino
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 02/17] qapi: generate C types for fixed-width integers Laszlo Ersek
2012-06-13 10:48 ` Paolo Bonzini
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 03/17] qapi: introduce "size" type Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 04/17] expose QemuOpt and QemuOpts struct definitions to interested parties Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 05/17] qapi: introduce OptsVisitor Laszlo Ersek
2012-06-13 10:50 ` Paolo Bonzini
2012-06-13 14:03 ` Laszlo Ersek
2012-07-13 16:51 ` Luiz Capitulino
2012-07-13 22:48 ` Laszlo Ersek [this message]
2012-07-13 23:09 ` Laszlo Ersek
2012-07-16 17:30 ` Luiz Capitulino
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 06/17] qapi schema: remove trailing whitespace Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 07/17] qapi schema: add Netdev types Laszlo Ersek
2012-06-13 10:50 ` Paolo Bonzini
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 08/17] hw, net: "net_client_type" -> "NetClientOptionsKind" (qapi-generated) Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 09/17] convert net_client_init() to OptsVisitor Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 10/17] convert net_init_nic() to NetClientOptions Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 11/17] convert net_init_dump() " Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 12/17] convert net_init_slirp() " Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 13/17] convert net_init_socket() " Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 14/17] convert net_init_vde() " Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 15/17] convert net_init_tap() " Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 16/17] convert net_init_bridge() " Laszlo Ersek
2012-06-13 8:22 ` [Qemu-devel] [PATCH v2 17/17] remove unused QemuOpts parameter from net init functions Laszlo Ersek
2012-06-13 10:54 ` [Qemu-devel] [PATCH v2 00/17] introduce OptsVisitor, rebase -net/-netdev parsing Paolo Bonzini
2012-07-01 13:33 ` Paolo Bonzini
2012-07-02 13:17 ` Luiz Capitulino
2012-07-13 16:46 ` Luiz Capitulino
2012-07-13 19:28 ` Laszlo Ersek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5000A5DA.9050904@redhat.com \
--to=lersek@redhat.com \
--cc=lcapitulino@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).