diff mbox series

[RFC] qapi: net: fix -set parameter with modern style

Message ID 20230313114255.1206609-1-lvivier@redhat.com (mailing list archive)
State New, archived
Headers show
Series [RFC] qapi: net: fix -set parameter with modern style | expand

Commit Message

Laurent Vivier March 13, 2023, 11:42 a.m. UTC
With netdev modern style, parameters cannot be found using
qemu_find_opts_err() and then qemu_set_option() cannot find
them to update them with the new option.

To fix that, update the code to manage the modern style by
searching the parameter in nd_queue, and update the entry
using visit_type_Netdev_members().

Fixes: f3eedcddba36 ("qapi: net: introduce a way to bypass qemu_opts_parse_noisily()")
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---
 include/net/net.h |  2 ++
 net/net.c         | 35 +++++++++++++++++++++++++++++++++++
 softmmu/vl.c      |  8 ++++++++
 3 files changed, 45 insertions(+)

Comments

Markus Armbruster March 13, 2023, 1:07 p.m. UTC | #1
Laurent Vivier <lvivier@redhat.com> writes:

> With netdev modern style, parameters cannot be found using
> qemu_find_opts_err() and then qemu_set_option() cannot find
> them to update them with the new option.
>
> To fix that, update the code to manage the modern style by
> searching the parameter in nd_queue, and update the entry
> using visit_type_Netdev_members().
>
> Fixes: f3eedcddba36 ("qapi: net: introduce a way to bypass qemu_opts_parse_noisily()")
> Signed-off-by: Laurent Vivier <lvivier@redhat.com>

Let me add Some more background.

A long time ago, configuration was a bunch of global variables in
various places, and CLI options and monitor commands to set them, all
using ad hoc parsers.

QemuOpts was an attempt at proper configuration infrastructure: a single
configuration repository with a rigid structure.  It is a set of named
"groups" (fixed at compile time, C type QemuOptsList).  Each group is a
set of QemuOpts.  A QemuOpts has a user-defined ID and a list of
QemuOpt.  A QemuOpt is essentially a key-value pair.  Values can be
string, uint64_t, or bool.

This is a *flat* design: GROUP.ID.NAME=VALUE.  Good enough back when it
was created in 2009, but we've long outgrown "flat".  More on that in a
jiffie.

-set operates on this repository.  -set GROUP.ID.NAME=VALUE selects the
QemuOptsList GROUP, within that the QemuOpts ID, and within that sets
the key NAME to value VALUE.

For -set to work, the QemuOpts must be created with a CLI option, and
used only after CLI parsing is complete.  This is how QemuOpts wants to
be used (but we use (and abuse) it in other ways, too).

The other way to configure a VM is of course the monitor.  QMP uses its
own infrastructure: the QAPI schema (since 2011).  Considerably more
expressive than QemuOpts: tree vs. flat, more scalar types.

There's another fundamental difference between a QemuOpts-based CLI and
the monitor.

With QemuOpts (when using it the intended way), we separate parsing from
acting.  First we parse the CLI entirely, populating the configuration
repository.  Only then do we actually create the things demanded by the
configuration.  Some errors get detected during parsing, and some during
acting.

In the monitor, however, each command acts immediately.  There is no
configuration repository.  There is simply no space for something like
-set.

There's also -readconfig: it parses a configuration file in a .INI
syntax into the configuration repository.

Over time, we grew bits of configurations QemuOpts can't express.  In
particular, QMP object-add could do things -object could not.  This led
to

commit 155b5f8b8d3d5dedd7c57e5223e822dc1b5295c8
Author: Kevin Wolf <kwolf@redhat.com>
Date:   Fri Mar 12 14:19:21 2021 +0100

    qom: Support JSON in HMP object_add and tools --object
    
    Support JSON for --object in all tools and in HMP object_add in the same
    way as it is supported in qobject_input_visitor_new_str().
    
    Signed-off-by: Kevin Wolf <kwolf@redhat.com>
    Message-Id: <20210312131921.421023-1-kwolf@redhat.com>
    Reviewed-by: Eric Blake <eblake@redhat.com>
    Reviewed-by: Markus Armbruster <armbru@redhat.com>
    Signed-off-by: Kevin Wolf <kwolf@redhat.com>

The commit message is arguably bad: it doesn't explain why.  The point
is to make -object to work like QMP object-add when the option argument
is JSON.  Full expressive power then, no QemuOpts, and therefore no
-set and no -readconfig.  This led to:

commit 49e987695a1873a769a823604f9065aa88e00c55
Author: Paolo Bonzini <pbonzini@redhat.com>
Date:   Mon May 24 06:57:52 2021 -0400

    vl: plug -object back into -readconfig
    
    Commit bc2f4fcb1d ("qom: move user_creatable_add_opts logic to vl.c
    and QAPIfy it", 2021-03-19) switched the creation of objects from
    qemu_opts_foreach to a bespoke QTAILQ in preparation for supporting JSON
    syntax in -object.
    
    Unfortunately in doing so it lost support for [object] stanzas in
    configuration files and also for "-set object.ID.KEY=VAL".  The latter
    is hard to re-establish and probably best solved by deprecating -set.
    This patch uses the infrastructure introduced by the previous two
    patches in order to parse QOM objects correctly from configuration
    files.
    
    Cc: Markus Armbruster <armbru@redhat.com>
    Cc: qemu-stable@nongnu.org
    Reviewed-by: Kevin Wolf <kwolf@redhat.com>
    Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
    Message-Id: <20210524105752.3318299-4-pbonzini@redhat.com>
    Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

> ---
>  include/net/net.h |  2 ++
>  net/net.c         | 35 +++++++++++++++++++++++++++++++++++
>  softmmu/vl.c      |  8 ++++++++
>  3 files changed, 45 insertions(+)
>
> diff --git a/include/net/net.h b/include/net/net.h
> index 1448d00afbc6..be42ba96ee3d 100644
> --- a/include/net/net.h
> +++ b/include/net/net.h
> @@ -246,6 +246,8 @@ extern const char *host_net_devices[];
>  extern NetClientStateList net_clients;
>  bool netdev_is_modern(const char *optarg);
>  void netdev_parse_modern(const char *optarg);
> +Netdev *netdev_find_modern(const char *id);
> +void netdev_set_modern(Netdev *net, const char *arg, Error **errp);
>  void net_client_parse(QemuOptsList *opts_list, const char *str);
>  void show_netdevs(void);
>  void net_init_clients(void);
> diff --git a/net/net.c b/net/net.c
> index 6492ad530e21..9c384187255b 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -1624,6 +1624,41 @@ out:
>      return ret;
>  }
>  
> +Netdev *netdev_find_modern(const char *id)
> +{
> +    NetdevQueueEntry *e;
> +
> +    QSIMPLEQ_FOREACH(e, &nd_queue, entry) {
> +        if (strcmp(id, e->nd->id) == 0) {
> +            return e->nd;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +void netdev_set_modern(Netdev *net, const char *arg, Error **errp)
> +{
> +    Visitor *v;
> +    char *id = net->id;
> +    char *opts = g_strdup_printf("type=%s,id=%s,%s",
> +                                 NetClientDriver_lookup.array[net->type],
> +                                 id, arg);
> +
> +    v = qobject_input_visitor_new_str(opts, NULL, errp);
> +    if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
> +        goto out;
> +    }
> +    if (visit_type_Netdev_members(v, net, errp)) {
> +        g_free(id); /* re-allocated in visit_type_Netdev_members() */
> +        visit_check_struct(v, errp);
> +        visit_end_struct(v, NULL);
> +    }

This exploits the fact that visit_type_FOO_members() leaves alone the
members that are not in @opts.  It overwrites net->type and net->id with
identical values, and member NAME with VALUE, where NAME and value come
from -set netdev.ID.NAME=VALUE.

> +
> +out:
> +    visit_free(v);
> +    g_free(opts);
> +}
> +
>  static void netdev_init_modern(void)
>  {
>      while (!QSIMPLEQ_EMPTY(&nd_queue)) {
> diff --git a/softmmu/vl.c b/softmmu/vl.c
> index 3340f63c3764..c063857867e2 100644
> --- a/softmmu/vl.c
> +++ b/softmmu/vl.c
> @@ -2162,6 +2162,14 @@ static void qemu_set_option(const char *str, Error **errp)
>      if (!is_qemuopts_group(group)) {
>          error_setg(errp, "-set is not supported with %s", group);
>      } else {
> +        if (strcmp(group, "netdev") == 0) {
> +            Netdev *net = netdev_find_modern(id);
> +            if (net) {
> +                netdev_set_modern(net, str + strlen(group) + strlen(id) + 2,
> +                                  errp);
> +                return;
> +            }
> +        }
>          list = qemu_find_opts_err(group, errp);
>          if (list) {
>              opts = qemu_opts_find(list, id);

Do we really want to special-case -set netdev... this way?
diff mbox series

Patch

diff --git a/include/net/net.h b/include/net/net.h
index 1448d00afbc6..be42ba96ee3d 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -246,6 +246,8 @@  extern const char *host_net_devices[];
 extern NetClientStateList net_clients;
 bool netdev_is_modern(const char *optarg);
 void netdev_parse_modern(const char *optarg);
+Netdev *netdev_find_modern(const char *id);
+void netdev_set_modern(Netdev *net, const char *arg, Error **errp);
 void net_client_parse(QemuOptsList *opts_list, const char *str);
 void show_netdevs(void);
 void net_init_clients(void);
diff --git a/net/net.c b/net/net.c
index 6492ad530e21..9c384187255b 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1624,6 +1624,41 @@  out:
     return ret;
 }
 
+Netdev *netdev_find_modern(const char *id)
+{
+    NetdevQueueEntry *e;
+
+    QSIMPLEQ_FOREACH(e, &nd_queue, entry) {
+        if (strcmp(id, e->nd->id) == 0) {
+            return e->nd;
+        }
+    }
+    return NULL;
+}
+
+void netdev_set_modern(Netdev *net, const char *arg, Error **errp)
+{
+    Visitor *v;
+    char *id = net->id;
+    char *opts = g_strdup_printf("type=%s,id=%s,%s",
+                                 NetClientDriver_lookup.array[net->type],
+                                 id, arg);
+
+    v = qobject_input_visitor_new_str(opts, NULL, errp);
+    if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
+        goto out;
+    }
+    if (visit_type_Netdev_members(v, net, errp)) {
+        g_free(id); /* re-allocated in visit_type_Netdev_members() */
+        visit_check_struct(v, errp);
+        visit_end_struct(v, NULL);
+    }
+
+out:
+    visit_free(v);
+    g_free(opts);
+}
+
 static void netdev_init_modern(void)
 {
     while (!QSIMPLEQ_EMPTY(&nd_queue)) {
diff --git a/softmmu/vl.c b/softmmu/vl.c
index 3340f63c3764..c063857867e2 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -2162,6 +2162,14 @@  static void qemu_set_option(const char *str, Error **errp)
     if (!is_qemuopts_group(group)) {
         error_setg(errp, "-set is not supported with %s", group);
     } else {
+        if (strcmp(group, "netdev") == 0) {
+            Netdev *net = netdev_find_modern(id);
+            if (net) {
+                netdev_set_modern(net, str + strlen(group) + strlen(id) + 2,
+                                  errp);
+                return;
+            }
+        }
         list = qemu_find_opts_err(group, errp);
         if (list) {
             opts = qemu_opts_find(list, id);