All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: MkfsSion <mkfssion@mkfssion.com>
Cc: Kevin Wolf <kwolf@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	qemu-devel@nongnu.org
Subject: Re: [PATCH] vl: Add -set options to device opts dict when using JSON syntax for -device
Date: Tue, 21 Dec 2021 12:26:42 +0100	[thread overview]
Message-ID: <877dbyjj0t.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <20211221071818.34731-1-mkfssion@mkfssion.com> (mkfssion@mkfssion.com's message of "Tue, 21 Dec 2021 15:18:18 +0800")

MkfsSion <mkfssion@mkfssion.com> writes:

> When using JSON syntax for -device, -set option can not find device
> specified in JSON by id field. The following commandline is an example:
>
> $ qemu-system-x86_64 -device '{"id":"foo"}' -set device.foo.bar=1
> qemu-system-x86_64: -set device.foo.bar=1: there is no device "foo" defined

Is this a regression?  I suspect commit 5dacda5167 "vl: Enable JSON
syntax for -device" (v6.2.0).

I believe any conversion away from QemuOpts loses -set.

> The patch adds -set options to JSON device opts dict for adding
> options to device by latter object_set_properties_from_keyval call.
>
> Signed-off-by: YuanYang Meng <mkfssion@mkfssion.com>
> ---
>  include/qemu/option.h |  4 ++++
>  softmmu/vl.c          | 28 ++++++++++++++++++++++++++++
>  util/qemu-option.c    |  2 +-
>  3 files changed, 33 insertions(+), 1 deletion(-)
>
> diff --git a/include/qemu/option.h b/include/qemu/option.h
> index 306bf07575..31fa9fdc25 100644
> --- a/include/qemu/option.h
> +++ b/include/qemu/option.h
> @@ -45,6 +45,10 @@ const char *get_opt_value(const char *p, char **value);
>  
>  bool parse_option_size(const char *name, const char *value,
>                         uint64_t *ret, Error **errp);
> +
> +bool parse_option_number(const char *name, const char *value,
> +                         uint64_t *ret, Error **errp);
> +
>  bool has_help_option(const char *param);
>  
>  enum QemuOptType {
> diff --git a/softmmu/vl.c b/softmmu/vl.c
> index 620a1f1367..feb3c49a65 100644
> --- a/softmmu/vl.c
> +++ b/softmmu/vl.c
> @@ -30,7 +30,9 @@
>  #include "hw/qdev-properties.h"
>  #include "qapi/compat-policy.h"
>  #include "qapi/error.h"
> +#include "qapi/qmp/qbool.h"
>  #include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qnum.h"
>  #include "qapi/qmp/qstring.h"
>  #include "qapi/qmp/qjson.h"
>  #include "qemu-version.h"
> @@ -2279,6 +2281,7 @@ static void qemu_set_option(const char *str, Error **errp)
>      char group[64], id[64], arg[64];
>      QemuOptsList *list;
>      QemuOpts *opts;
> +    DeviceOption *opt;
>      int rc, offset;
>  
>      rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset);
> @@ -2294,6 +2297,31 @@ static void qemu_set_option(const char *str, Error **errp)
>          if (list) {
>              opts = qemu_opts_find(list, id);
>              if (!opts) {
> +                QTAILQ_FOREACH(opt, &device_opts, next) {
> +                    const char *device_id = qdict_get_try_str(opt->opts, "id");
> +                    if (device_id && (strcmp(device_id, id) == 0)) {
> +                        if (qdict_get(opt->opts, arg)) {
> +                            qdict_del(opt->opts, arg);
> +                        }
> +                        const char *value = str + offset + 1;
> +                        QObject *obj = NULL;
> +                        bool boolean;
> +                        uint64_t num;
> +                        if (qapi_bool_parse(arg, value, &boolean, NULL)) {
> +                            obj = QOBJECT(qbool_from_bool(boolean));
> +                        } else if (parse_option_number(arg, value, &num, NULL)) {
> +                            obj = QOBJECT(qnum_from_uint(num));
> +                        } else if (parse_option_size(arg, value, &num, NULL)) {
> +                            obj = QOBJECT(qnum_from_uint(num));
> +                        } else {
> +                            obj = QOBJECT(qstring_from_str(value));
> +                        }
> +                        if (obj) {
> +                            qdict_put_obj(opt->opts, arg, obj);
> +                            return;
> +                        }
> +                    }
> +                }
>                  error_setg(errp, "there is no %s \"%s\" defined", group, id);
>                  return;
>              }
               qemu_opt_set(opts, arg, str + offset + 1, errp);
           }
       }
   }

Two issues, and only looks fixable.

-device accepts either a QemuOpts or a JSON argument.

It parses the former with qemu_opts_parse_noisily() into a QemuOpt
stored in @qemu_device_opts.

It parses the latter with qobject_from_json() into a QObject stored in
@device_opts.  Yes, the names are confusing.

-set parses its argument into @group, @id, and @arg (the value).

Before the patch, it uses @group and @id to look up the QemuOpt in
@qemu_device_opts.  If found, it updates it with qemu_opt_set().

By design, -set operates on the QemuOpts store.

Options stored in @device_opts are not found.  Your patch tries to fix
that.  Before I can explain what's wrong with it, I need more
background.

QemuOpts arguments are parsed as a set of (key, value) pairs, where the
values are all strings (because qemu_device_opts.desc[] is empty).  We
access them with a qobject_input_visitor_new_keyval() visitor.  This
parses the strings according to the types being visited.

Example: key=42 gets stored as {"key": "42"}.  If we visit it with
visit_type_str(), we use string value "42".  If we visit it with
visit_type_int() or similar, we use integer value 42.  If we visit it
with visit_type_number(), we use double value 42.0.  If we visit it with
something else, we error out.

JSON arguments are parsed as arbitrary JSON object.  We access them with
a qobject_input_visitor_new() visitor.  This expects the values to have
JSON types appropriate for the types being visited.

Example: {"key": "42"} gets stored as is.  Now everything but
visit_type_str() errors out.

Back to your patch.  It adds code to look up a QObject in @device_opts.
If found, it updates it.

Issue#1: it does so regardless of @group.

Example:

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device '{"driver": "usb-mouse", "id": "ms0"}' -set chardev.ms0.serial=456

Here, -set chardev... gets misinterpreted as -set device...

Issue#2 is the value to store in @device_opts.  Always storing a string,
like in the QemuOpts case, would be wrong, because it works only when
its accessed with visit_type_str(), i.e. the property is actually of
string type.

Example:

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device '{"driver": "usb-mouse", "id": "ms0"}' -set device.ms0.serial=123

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device '{"driver": "usb-mouse", "id": "ms0"}' -set device.ms0.msos-desc=off
    qemu-system-x86_64: -device {"driver": "usb-mouse", "id": "ms0"}: Invalid parameter type for 'msos-desc', expected: boolean

Your patch stores a boolean if possible, else a number if possible, else
a string.  This is differently wrong.

Example:

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device '{"driver": "usb-mouse", "id": "ms0"}'

Example:

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device '{"driver": "usb-mouse", "id": "ms0"}' -set device.ms0.serial=123
    qemu-system-x86_64: -device {"driver": "usb-mouse", "id": "ms0", "serial": "123"}: Invalid parameter type for 'serial', expected: string

I can't see how -set can store the right thing.

Aside: the error messages refer to -device instead of -set.  Known bug
in -set, hard to fix.

> diff --git a/util/qemu-option.c b/util/qemu-option.c
> index eedd08929b..b2427cba9f 100644
> --- a/util/qemu-option.c
> +++ b/util/qemu-option.c
> @@ -88,7 +88,7 @@ const char *get_opt_value(const char *p, char **value)
>      return offset;
>  }
>  
> -static bool parse_option_number(const char *name, const char *value,
> +bool parse_option_number(const char *name, const char *value,
>                                  uint64_t *ret, Error **errp)
>  {
>      uint64_t number;



  reply	other threads:[~2021-12-21 11:29 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-21  7:18 [PATCH] vl: Add -set options to device opts dict when using JSON syntax for -device MkfsSion
2021-12-21 11:26 ` Markus Armbruster [this message]
2021-12-21 12:58   ` Markus Armbruster
2021-12-21 14:47     ` Paolo Bonzini
2021-12-21 15:40       ` Markus Armbruster
2021-12-21 17:20         ` Damien Hedde
2021-12-21 17:31         ` Damien Hedde
2021-12-22  8:22         ` Gerd Hoffmann
2021-12-22  9:37           ` Damien Hedde
2021-12-22 10:47             ` Gerd Hoffmann
2022-01-04 13:14           ` Daniel P. Berrangé
2021-12-22  5:54   ` MkfsSion

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=877dbyjj0t.fsf@dusky.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=mkfssion@mkfssion.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 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.