All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/4] Introducing QMP query-netdev command
@ 2020-11-08 23:59 Alexey Kirillov
  2020-11-08 23:59 ` [PATCH v5 1/4] qapi: net: Add " Alexey Kirillov
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Alexey Kirillov @ 2020-11-08 23:59 UTC (permalink / raw)
  To: Jason Wang, Eric Blake, Markus Armbruster, Thomas Huth
  Cc: Laurent Vivier, Michael S. Tsirkin, Stefan Weil, qemu-devel,
	Vincenzo Maffione, yc-core, Paolo Bonzini, Samuel Thibault,
	Giuseppe Lettieri, Luigi Rizzo

This patch series introduces a new QMP command "query-netdev" to get
information about currently attached backend network devices (netdevs).
Also, since the "info_str" field of "NetClientState" is now deprecated,
we no longer use it for netdevs, only for NIC/hubports.
The HMP command "info network" now also uses the new QMP command inside.

Usage example:

-> { "execute": "query-netdev" }
<- { "return": [
         {
             "listen": "127.0.0.1:90",
             "type": "socket",
             "peer-id": "hub0port1",
             "id": "__org.qemu.net1"
         },
         {
             "script": "/etc/qemu-ifup",
             "downscript": "/etc/qemu-ifdown",
             "ifname": "tap0",
             "type": "tap",
             "peer-id": "net5",
             "vnet_hdr": true,
             "id": "tap0"
         },
         {
             "ipv6": true,
             "ipv4": true,
             "host": "10.0.2.2",
             "ipv6-dns": "fec0::3",
             "ipv6-prefix": "fec0::",
             "net": "10.0.2.0/255.255.255.0",
             "ipv6-host": "fec0::2",
             "type": "user",
             "peer-id": "net0",
             "dns": "10.0.2.3",
             "hostfwd": [
                 {
                     "str": "tcp::20004-:22"
                 }
             ],
             "ipv6-prefixlen": 64,
             "id": "netdev0",
             "restrict": false
         }
     ]
   }

v4->v5:
- Enable qtest of query-netdevs for AVR and RX archs.
- Bump "Since" version in QAPI to 6.0.

v3->v4:
- Rename "query-netdevs" to "query-netdev".
- Copy netdev drivers to new QAPI enum "NetBackend".

v2->v3:
- Remove NIC and hubports from query-netdevs.
- Remove several fields from NetdevInfo since they are unnecessary.
- Rename field @peer to @peer-id.
- Add support of vhost-vdpa.
- Keep "info_str" for NIC/hubports, but remove it for netdevs.

v1->v2:
- Rewrite HMP "info network" to get information from results of QMP command.
- Remove obsolete field "info_str" from "NetClientState".

Alexey Kirillov (4):
  qapi: net: Add query-netdev command
  tests: Add tests for query-netdev command
  hmp: Use QMP query-netdev in hmp_info_network
  net: Do not use legacy info_str for backends

 include/net/net.h               |   4 +-
 net/clients.h                   |   1 +
 net/hub.c                       |   4 +-
 net/hub.h                       |   2 +-
 net/l2tpv3.c                    |  21 +++-
 net/net.c                       | 213 +++++++++++++++++++++++++++++++-
 net/netmap.c                    |  13 ++
 net/slirp.c                     | 128 ++++++++++++++++++-
 net/socket.c                    |  91 ++++++++++----
 net/tap-win32.c                 |  10 +-
 net/tap.c                       | 107 ++++++++++++++--
 net/vde.c                       |  39 +++++-
 net/vhost-user.c                |  20 ++-
 net/vhost-vdpa.c                |  15 ++-
 qapi/net.json                   |  80 ++++++++++++
 tests/qtest/meson.build         |   3 +
 tests/qtest/test-query-netdev.c | 120 ++++++++++++++++++
 17 files changed, 812 insertions(+), 59 deletions(-)
 create mode 100644 tests/qtest/test-query-netdev.c

-- 
2.25.1



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

* [PATCH v5 1/4] qapi: net: Add query-netdev command
  2020-11-08 23:59 [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov
@ 2020-11-08 23:59 ` Alexey Kirillov
  2020-11-08 23:59 ` [PATCH v5 2/4] tests: Add tests for " Alexey Kirillov
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Alexey Kirillov @ 2020-11-08 23:59 UTC (permalink / raw)
  To: Jason Wang, Eric Blake, Markus Armbruster, Thomas Huth
  Cc: Laurent Vivier, Michael S. Tsirkin, Stefan Weil, qemu-devel,
	Vincenzo Maffione, yc-core, Paolo Bonzini, Samuel Thibault,
	Giuseppe Lettieri, Luigi Rizzo

Add a qmp command that provides information about currently attached
backend network devices and their configuration.

Signed-off-by: Alexey Kirillov <lekiravi@yandex-team.ru>
Acked-by: Markus Armbruster <armbru@redhat.com>
---
 include/net/net.h |   1 +
 net/l2tpv3.c      |  19 +++++++
 net/net.c         |  32 ++++++++++++
 net/netmap.c      |  13 +++++
 net/slirp.c       | 126 ++++++++++++++++++++++++++++++++++++++++++++++
 net/socket.c      |  71 ++++++++++++++++++++++++++
 net/tap-win32.c   |   9 ++++
 net/tap.c         | 103 +++++++++++++++++++++++++++++++++++--
 net/vde.c         |  26 ++++++++++
 net/vhost-user.c  |  18 +++++--
 net/vhost-vdpa.c  |  14 ++++++
 qapi/net.json     |  80 +++++++++++++++++++++++++++++
 12 files changed, 504 insertions(+), 8 deletions(-)

diff --git a/include/net/net.h b/include/net/net.h
index 897b2d7595..16700f715e 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -92,6 +92,7 @@ struct NetClientState {
     char *model;
     char *name;
     char info_str[256];
+    NetdevInfo *stored_config;
     unsigned receive_disabled : 1;
     NetClientDestructor *destructor;
     unsigned int queue_index;
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index 55fea17c0f..881fc6f7c1 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -535,6 +535,7 @@ int net_init_l2tpv3(const Netdev *netdev,
     struct addrinfo hints;
     struct addrinfo *result = NULL;
     char *srcport, *dstport;
+    NetdevL2TPv3Options *stored;
 
     nc = qemu_new_net_client(&net_l2tpv3_info, peer, "l2tpv3", name);
 
@@ -726,6 +727,24 @@ int net_init_l2tpv3(const Netdev *netdev,
 
     l2tpv3_read_poll(s, true);
 
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_L2TPV3;
+    stored = &nc->stored_config->u.l2tpv3;
+
+    memcpy(stored, l2tpv3, sizeof(NetdevL2TPv3Options));
+
+    stored->src = g_strdup(l2tpv3->src);
+    stored->dst = g_strdup(l2tpv3->dst);
+
+    if (l2tpv3->has_srcport) {
+        stored->srcport = g_strdup(l2tpv3->srcport);
+    }
+
+    if (l2tpv3->has_dstport) {
+        stored->dstport = g_strdup(l2tpv3->dstport);
+    }
+
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "l2tpv3: connected");
     return 0;
diff --git a/net/net.c b/net/net.c
index 794c652282..551acadabc 100644
--- a/net/net.c
+++ b/net/net.c
@@ -54,6 +54,7 @@
 #include "sysemu/sysemu.h"
 #include "net/filter.h"
 #include "qapi/string-output-visitor.h"
+#include "qapi/clone-visitor.h"
 
 /* Net bridge is currently not supported for W32. */
 #if !defined(_WIN32)
@@ -351,6 +352,7 @@ static void qemu_free_net_client(NetClientState *nc)
     }
     g_free(nc->name);
     g_free(nc->model);
+    qapi_free_NetdevInfo(nc->stored_config);
     if (nc->destructor) {
         nc->destructor(nc);
     }
@@ -1253,6 +1255,36 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
     return filter_list;
 }
 
+NetdevInfoList *qmp_query_netdev(Error **errp)
+{
+    NetdevInfoList *list = NULL;
+    NetClientState *nc;
+
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        /*
+         * Only look at netdevs (backend network devices), not for each queue
+         * or NIC / hubport
+         */
+        if (nc->stored_config) {
+            NetdevInfoList *node = g_new0(NetdevInfoList, 1);
+
+            node->value = QAPI_CLONE(NetdevInfo, nc->stored_config);
+            g_free(node->value->id); /* Need to dealloc default empty id */
+            node->value->id = g_strdup(nc->name);
+
+            node->value->has_peer_id = nc->peer != NULL;
+            if (node->value->has_peer_id) {
+                node->value->peer_id = g_strdup(nc->peer->name);
+            }
+
+            node->next = list;
+            list = node;
+        }
+    }
+
+    return list;
+}
+
 void hmp_info_network(Monitor *mon, const QDict *qdict)
 {
     NetClientState *nc, *peer;
diff --git a/net/netmap.c b/net/netmap.c
index 350f097f91..b6087fdb7c 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -411,6 +411,7 @@ int net_init_netmap(const Netdev *netdev,
     NetClientState *nc;
     Error *err = NULL;
     NetmapState *s;
+    NetdevNetmapOptions *stored;
 
     nmd = netmap_open(netmap_opts, &err);
     if (err) {
@@ -427,6 +428,18 @@ int net_init_netmap(const Netdev *netdev,
     pstrcpy(s->ifname, sizeof(s->ifname), netmap_opts->ifname);
     netmap_read_poll(s, true); /* Initially only poll for reads. */
 
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_NETMAP;
+    stored = &nc->stored_config->u.netmap;
+
+    stored->ifname = g_strdup(netmap_opts->ifname);
+
+    if (netmap_opts->has_devname) {
+        stored->has_devname = true;
+        stored->devname = g_strdup(netmap_opts->devname);
+    }
+
     return 0;
 }
 
diff --git a/net/slirp.c b/net/slirp.c
index 77042e6df7..65d3f2639c 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -346,6 +346,14 @@ static SaveVMHandlers savevm_slirp_state = {
     .load_state = net_slirp_state_load,
 };
 
+#define APPEND_STRINGLIST(tail, new_val) \
+    do { \
+        *(tail) = g_new0(StringList, 1); \
+        (*(tail))->value = g_new0(String, 1); \
+        (*(tail))->value->str = g_strdup((new_val)); \
+        (tail) = &((*(tail))->next); \
+    } while (0)
+
 static int net_slirp_init(NetClientState *peer, const char *model,
                           const char *name, int restricted,
                           bool ipv4, const char *vnetwork, const char *vhost,
@@ -378,6 +386,9 @@ static int net_slirp_init(NetClientState *peer, const char *model,
     int shift;
     char *end;
     struct slirp_config_str *config;
+    NetdevUserOptions *stored;
+    StringList **stored_hostfwd;
+    StringList **stored_guestfwd;
 
     if (!ipv4 && (vnetwork || vhost || vnameserver)) {
         error_setg(errp, "IPv4 disabled but netmask/host/dns provided");
@@ -553,6 +564,112 @@ static int net_slirp_init(NetClientState *peer, const char *model,
 
     nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
 
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_USER;
+    stored = &nc->stored_config->u.user;
+
+    if (vhostname) {
+        stored->has_hostname = true;
+        stored->hostname = g_strdup(vhostname);
+    }
+
+    stored->has_q_restrict = true;
+    stored->q_restrict = restricted;
+
+    stored->has_ipv4 = true;
+    stored->ipv4 = ipv4;
+
+    stored->has_ipv6 = true;
+    stored->ipv6 = ipv6;
+
+    if (ipv4) {
+        uint8_t *net_bytes = (uint8_t *)&net;
+        uint8_t *mask_bytes = (uint8_t *)&mask;
+
+        stored->has_net = true;
+        stored->net = g_strdup_printf("%d.%d.%d.%d/%d.%d.%d.%d",
+                                      net_bytes[0], net_bytes[1],
+                                      net_bytes[2], net_bytes[3],
+                                      mask_bytes[0], mask_bytes[1],
+                                      mask_bytes[2], mask_bytes[3]);
+
+        stored->has_host = true;
+        stored->host = g_strdup(inet_ntoa(host));
+    }
+
+    if (tftp_export) {
+        stored->has_tftp = true;
+        stored->tftp = g_strdup(tftp_export);
+    }
+
+    if (bootfile) {
+        stored->has_bootfile = true;
+        stored->bootfile = g_strdup(bootfile);
+    }
+
+    if (vdhcp_start) {
+        stored->has_dhcpstart = true;
+        stored->dhcpstart = g_strdup(vdhcp_start);
+    }
+
+    if (ipv4) {
+        stored->has_dns = true;
+        stored->dns = g_strdup(inet_ntoa(dns));
+    }
+
+    if (dnssearch) {
+        stored->has_dnssearch = true;
+        StringList **stored_list = &stored->dnssearch;
+
+        for (int i = 0; dnssearch[i]; i++) {
+            APPEND_STRINGLIST(stored_list, dnssearch[i]);
+        }
+    }
+
+    if (vdomainname) {
+        stored->has_domainname = true;
+        stored->domainname = g_strdup(vdomainname);
+    }
+
+    if (ipv6) {
+        char addrstr[INET6_ADDRSTRLEN];
+        const char *res;
+
+        stored->has_ipv6_prefix = true;
+        stored->ipv6_prefix = g_strdup(vprefix6);
+
+        stored->has_ipv6_prefixlen = true;
+        stored->ipv6_prefixlen = vprefix6_len;
+
+        res = inet_ntop(AF_INET6, &ip6_host,
+                        addrstr, sizeof(addrstr));
+
+        stored->has_ipv6_host = true;
+        stored->ipv6_host = g_strdup(res);
+
+        res = inet_ntop(AF_INET6, &ip6_dns,
+                        addrstr, sizeof(addrstr));
+
+        stored->has_ipv6_dns = true;
+        stored->ipv6_dns = g_strdup(res);
+    }
+
+    if (smb_export) {
+        stored->has_smb = true;
+        stored->smb = g_strdup(smb_export);
+    }
+
+    if (vsmbserver) {
+        stored->has_smbserver = true;
+        stored->smbserver = g_strdup(vsmbserver);
+    }
+
+    if (tftp_server_name) {
+        stored->has_tftp_server_name = true;
+        stored->tftp_server_name = g_strdup(tftp_server_name);
+    }
+
     snprintf(nc->info_str, sizeof(nc->info_str),
              "net=%s,restrict=%s", inet_ntoa(net),
              restricted ? "on" : "off");
@@ -582,14 +699,23 @@ static int net_slirp_init(NetClientState *peer, const char *model,
     s->poll_notifier.notify = net_slirp_poll_notify;
     main_loop_poll_add_notifier(&s->poll_notifier);
 
+    stored_hostfwd = &stored->hostfwd;
+    stored_guestfwd = &stored->guestfwd;
+
     for (config = slirp_configs; config; config = config->next) {
         if (config->flags & SLIRP_CFG_HOSTFWD) {
             if (slirp_hostfwd(s, config->str, errp) < 0) {
                 goto error;
+            } else {
+                stored->has_hostfwd = true;
+                APPEND_STRINGLIST(stored_hostfwd, config->str);
             }
         } else {
             if (slirp_guestfwd(s, config->str, errp) < 0) {
                 goto error;
+            } else {
+                stored->has_guestfwd = true;
+                APPEND_STRINGLIST(stored_guestfwd, config->str);
             }
         }
     }
diff --git a/net/socket.c b/net/socket.c
index 15b410e8d8..1614523b82 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -342,6 +342,7 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
     NetSocketState *s;
     SocketAddress *sa;
     SocketAddressType sa_type;
+    NetdevSocketOptions *stored;
 
     sa = socket_local_address(fd, errp);
     if (!sa) {
@@ -385,8 +386,19 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
     net_socket_rs_init(&s->rs, net_socket_rs_finalize, false);
     net_socket_read_poll(s, true);
 
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_SOCKET;
+    stored = &nc->stored_config->u.socket;
+
+    stored->has_fd = true;
+    stored->fd = g_strdup_printf("%d", fd);
+
     /* mcast: save bound address as dst */
     if (is_connected && mcast != NULL) {
+        stored->has_mcast = true;
+        stored->mcast = g_strdup(mcast);
+
         s->dgram_dst = saddr;
         snprintf(nc->info_str, sizeof(nc->info_str),
                  "socket: fd=%d (cloned mcast=%s:%d)",
@@ -428,6 +440,7 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
 {
     NetClientState *nc;
     NetSocketState *s;
+    NetdevSocketOptions *stored;
 
     nc = qemu_new_net_client(&net_socket_info, peer, model, name);
 
@@ -447,6 +460,15 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
     } else {
         qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
     }
+
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_SOCKET;
+    stored = &nc->stored_config->u.socket;
+
+    stored->has_fd = true;
+    stored->fd = g_strdup_printf("%d", fd);
+
     return s;
 }
 
@@ -483,6 +505,7 @@ static void net_socket_accept(void *opaque)
     struct sockaddr_in saddr;
     socklen_t len;
     int fd;
+    NetdevSocketOptions *stored;
 
     for(;;) {
         len = sizeof(saddr);
@@ -498,6 +521,13 @@ static void net_socket_accept(void *opaque)
     s->fd = fd;
     s->nc.link_down = false;
     net_socket_connect(s);
+
+    /* Store additional startup parameters (extend net_socket_listen_init) */
+    stored = &s->nc.stored_config->u.socket;
+
+    stored->has_fd = true;
+    stored->fd = g_strdup_printf("%d", fd);
+
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "socket: connection from %s:%d",
              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
@@ -513,6 +543,7 @@ static int net_socket_listen_init(NetClientState *peer,
     NetSocketState *s;
     struct sockaddr_in saddr;
     int fd, ret;
+    NetdevSocketOptions *stored;
 
     if (parse_host_port(&saddr, host_str, errp) < 0) {
         return -1;
@@ -549,6 +580,15 @@ static int net_socket_listen_init(NetClientState *peer,
     net_socket_rs_init(&s->rs, net_socket_rs_finalize, false);
 
     qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s);
+
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_SOCKET;
+    stored = &nc->stored_config->u.socket;
+
+    stored->has_listen = true;
+    stored->listen = g_strdup(host_str);
+
     return 0;
 }
 
@@ -561,6 +601,7 @@ static int net_socket_connect_init(NetClientState *peer,
     NetSocketState *s;
     int fd, connected, ret;
     struct sockaddr_in saddr;
+    NetdevSocketOptions *stored;
 
     if (parse_host_port(&saddr, host_str, errp) < 0) {
         return -1;
@@ -598,6 +639,12 @@ static int net_socket_connect_init(NetClientState *peer,
         return -1;
     }
 
+    /* Store additional startup parameters (extend net_socket_fd_init) */
+    stored = &s->nc.stored_config->u.socket;
+
+    stored->has_connect = true;
+    stored->connect = g_strdup(host_str);
+
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "socket: connect to %s:%d",
              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
@@ -615,6 +662,7 @@ static int net_socket_mcast_init(NetClientState *peer,
     int fd;
     struct sockaddr_in saddr;
     struct in_addr localaddr, *param_localaddr;
+    NetdevSocketOptions *stored;
 
     if (parse_host_port(&saddr, host_str, errp) < 0) {
         return -1;
@@ -643,6 +691,19 @@ static int net_socket_mcast_init(NetClientState *peer,
 
     s->dgram_dst = saddr;
 
+    /* Store additional startup parameters (extend net_socket_fd_init) */
+    stored = &s->nc.stored_config->u.socket;
+
+    if (!stored->has_mcast) {
+        stored->has_mcast = true;
+        stored->mcast = g_strdup(host_str);
+    }
+
+    if (localaddr_str) {
+        stored->has_localaddr = true;
+        stored->localaddr = g_strdup(localaddr_str);
+    }
+
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "socket: mcast=%s:%d",
              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
@@ -660,6 +721,7 @@ static int net_socket_udp_init(NetClientState *peer,
     NetSocketState *s;
     int fd, ret;
     struct sockaddr_in laddr, raddr;
+    NetdevSocketOptions *stored;
 
     if (parse_host_port(&laddr, lhost, errp) < 0) {
         return -1;
@@ -698,6 +760,15 @@ static int net_socket_udp_init(NetClientState *peer,
 
     s->dgram_dst = raddr;
 
+    /* Store additional startup parameters (extend net_socket_fd_init) */
+    stored = &s->nc.stored_config->u.socket;
+
+    stored->has_localaddr = true;
+    stored->localaddr = g_strdup(lhost);
+
+    stored->has_udp = true;
+    stored->udp = g_strdup(rhost);
+
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "socket: udp=%s:%d",
              inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port));
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 2b5dcda36e..b60933bd1a 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -768,6 +768,7 @@ static int tap_win32_init(NetClientState *peer, const char *model,
     NetClientState *nc;
     TAPState *s;
     tap_win32_overlapped_t *handle;
+    NetdevTapOptions *stored;
 
     if (tap_win32_open(&handle, ifname) < 0) {
         printf("tap: Could not open '%s'\n", ifname);
@@ -778,6 +779,14 @@ static int tap_win32_init(NetClientState *peer, const char *model,
 
     s = DO_UPCAST(TAPState, nc, nc);
 
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_TAP;
+    stored = &nc->stored_config->u.tap;
+
+    stored->has_ifname = true;
+    stored->ifname = g_strdup(ifname);
+
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "tap: ifname=%s", ifname);
 
diff --git a/net/tap.c b/net/tap.c
index c46ff66184..57af49ad51 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -590,6 +590,7 @@ int net_init_bridge(const Netdev *netdev, const char *name,
     const char *helper, *br;
     TAPState *s;
     int fd, vnet_hdr;
+    NetdevBridgeOptions *stored;
 
     assert(netdev->type == NET_CLIENT_DRIVER_BRIDGE);
     bridge = &netdev->u.bridge;
@@ -609,6 +610,21 @@ int net_init_bridge(const Netdev *netdev, const char *name,
     }
     s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr);
 
+    /* Store startup parameters */
+    s->nc.stored_config = g_new0(NetdevInfo, 1);
+    s->nc.stored_config->type = NET_BACKEND_BRIDGE;
+    stored = &s->nc.stored_config->u.bridge;
+
+    if (br) {
+        stored->has_br = true;
+        stored->br = g_strdup(br);
+    }
+
+    if (helper) {
+        stored->has_helper = true;
+        stored->helper = g_strdup(helper);
+    }
+
     snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper,
              br);
 
@@ -656,11 +672,13 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
                              const char *model, const char *name,
                              const char *ifname, const char *script,
                              const char *downscript, const char *vhostfdname,
-                             int vnet_hdr, int fd, Error **errp)
+                             int vnet_hdr, int fd, NetdevInfo **common_stored,
+                             Error **errp)
 {
     Error *err = NULL;
     TAPState *s = net_tap_fd_init(peer, model, name, fd, vnet_hdr);
     int vhostfd;
+    NetdevTapOptions *stored;
 
     tap_set_sndbuf(s->fd, tap, &err);
     if (err) {
@@ -668,12 +686,65 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
         return;
     }
 
+    /* Store startup parameters */
+    if (!*common_stored) {
+        *common_stored = g_new0(NetdevInfo, 1);
+        (*common_stored)->type = NET_BACKEND_TAP;
+        s->nc.stored_config = *common_stored;
+    }
+    stored = &(*common_stored)->u.tap;
+
+    if (tap->has_sndbuf && !stored->has_sndbuf) {
+        stored->has_sndbuf = true;
+        stored->sndbuf = tap->sndbuf;
+    }
+
+    if (vnet_hdr && !stored->has_vnet_hdr) {
+        stored->has_vnet_hdr = true;
+        stored->vnet_hdr = true;
+    }
+
     if (tap->has_fd || tap->has_fds) {
+        if (!stored->has_fds) {
+            stored->has_fds = true;
+            stored->fds = g_strdup_printf("%d", fd);
+        } else {
+            char *tmp_s = stored->fds;
+            stored->fds = g_strdup_printf("%s:%d", stored->fds, fd);
+            g_free(tmp_s);
+        }
+
         snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
     } else if (tap->has_helper) {
+        if (!stored->has_helper) {
+            stored->has_helper = true;
+            stored->helper = g_strdup(tap->helper);
+        }
+
+        if (!stored->has_br) {
+            stored->has_br = true;
+            stored->br = tap->has_br ? g_strdup(tap->br) :
+                                       g_strdup(DEFAULT_BRIDGE_INTERFACE);
+        }
+
         snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
                  tap->helper);
     } else {
+        if (ifname && !stored->has_ifname) {
+            stored->has_ifname = true;
+            stored->ifname = g_strdup(ifname);
+        }
+
+        if (script && !stored->has_script) {
+            stored->has_script = true;
+            stored->script = g_strdup(script);
+        }
+
+        if (downscript && !stored->has_downscript) {
+            stored->has_downscript = true;
+            stored->downscript = g_strdup(downscript);
+        }
+
         snprintf(s->nc.info_str, sizeof(s->nc.info_str),
                  "ifname=%s,script=%s,downscript=%s", ifname, script,
                  downscript);
@@ -689,9 +760,20 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
         vhostfdname || (tap->has_vhostforce && tap->vhostforce)) {
         VhostNetOptions options;
 
+        stored->has_vhost = true;
+        stored->vhost = true;
+
+        if (tap->has_vhostforce && tap->vhostforce) {
+            stored->has_vhostforce = true;
+            stored->vhostforce = true;
+        }
+
         options.backend_type = VHOST_BACKEND_TYPE_KERNEL;
         options.net_backend = &s->nc;
         if (tap->has_poll_us) {
+            stored->has_poll_us = true;
+            stored->poll_us = tap->poll_us;
+
             options.busyloop_timeout = tap->poll_us;
         } else {
             options.busyloop_timeout = 0;
@@ -731,6 +813,15 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
         }
         options.opaque = (void *)(uintptr_t)vhostfd;
 
+        if (!stored->has_vhostfds) {
+            stored->has_vhostfds = true;
+            stored->vhostfds = g_strdup_printf("%d", vhostfd);
+        } else {
+            char *tmp_s = stored->vhostfds;
+            stored->vhostfds = g_strdup_printf("%s:%d", stored->fds, vhostfd);
+            g_free(tmp_s);
+        }
+
         s->vhost_net = vhost_net_init(&options);
         if (!s->vhost_net) {
             if (tap->has_vhostforce && tap->vhostforce) {
@@ -783,6 +874,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
     const char *vhostfdname;
     char ifname[128];
     int ret = 0;
+    NetdevInfo *common_stored = NULL; /* will store configuration */
 
     assert(netdev->type == NET_CLIENT_DRIVER_TAP);
     tap = &netdev->u.tap;
@@ -828,7 +920,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
 
         net_init_tap_one(tap, peer, "tap", name, NULL,
                          script, downscript,
-                         vhostfdname, vnet_hdr, fd, &err);
+                         vhostfdname, vnet_hdr, fd, &common_stored, &err);
         if (err) {
             error_propagate(errp, err);
             return -1;
@@ -890,7 +982,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
             net_init_tap_one(tap, peer, "tap", name, ifname,
                              script, downscript,
                              tap->has_vhostfds ? vhost_fds[i] : NULL,
-                             vnet_hdr, fd, &err);
+                             vnet_hdr, fd, &common_stored, &err);
             if (err) {
                 error_propagate(errp, err);
                 ret = -1;
@@ -933,7 +1025,7 @@ free_fail:
 
         net_init_tap_one(tap, peer, "bridge", name, ifname,
                          script, downscript, vhostfdname,
-                         vnet_hdr, fd, &err);
+                         vnet_hdr, fd, &common_stored, &err);
         if (err) {
             error_propagate(errp, err);
             close(fd);
@@ -978,7 +1070,8 @@ free_fail:
             net_init_tap_one(tap, peer, "tap", name, ifname,
                              i >= 1 ? "no" : script,
                              i >= 1 ? "no" : downscript,
-                             vhostfdname, vnet_hdr, fd, &err);
+                             vhostfdname, vnet_hdr, fd,
+                             &common_stored, &err);
             if (err) {
                 error_propagate(errp, err);
                 close(fd);
diff --git a/net/vde.c b/net/vde.c
index 99189cccb6..1af9175060 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -84,6 +84,7 @@ static int net_vde_init(NetClientState *peer, const char *model,
     VDECONN *vde;
     char *init_group = (char *)group;
     char *init_sock = (char *)sock;
+    NetdevVdeOptions *stored;
 
     struct vde_open_args args = {
         .port = port,
@@ -108,6 +109,31 @@ static int net_vde_init(NetClientState *peer, const char *model,
 
     qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s);
 
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_VDE;
+    stored = &nc->stored_config->u.vde;
+
+    if (sock) {
+        stored->has_sock = true;
+        stored->sock = g_strdup(sock);
+    }
+
+    if (port) {
+        stored->has_port = true;
+        stored->port = port;
+    }
+
+    if (group) {
+        stored->has_group = true;
+        stored->group = g_strdup(group);
+    }
+
+    if (mode) {
+        stored->has_mode = true;
+        stored->mode = mode;
+    }
+
     return 0;
 }
 
diff --git a/net/vhost-user.c b/net/vhost-user.c
index ffbd94d944..5b7056be25 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -311,14 +311,15 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
 }
 
 static int net_vhost_user_init(NetClientState *peer, const char *device,
-                               const char *name, Chardev *chr,
-                               int queues)
+                               const char *name, const char *chardev,
+                               Chardev *chr, int queues)
 {
     Error *err = NULL;
     NetClientState *nc, *nc0 = NULL;
     NetVhostUserState *s = NULL;
     VhostUserState *user;
     int i;
+    NetdevVhostUserOptions *stored;
 
     assert(name);
     assert(queues > 0);
@@ -355,6 +356,16 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
 
     assert(s->vhost_net);
 
+    /* Store startup parameters */
+    nc0->stored_config = g_new0(NetdevInfo, 1);
+    nc0->stored_config->type = NET_BACKEND_VHOST_USER;
+    stored = &nc0->stored_config->u.vhost_user;
+
+    stored->chardev = g_strdup(chardev);
+
+    stored->has_queues = true;
+    stored->queues = queues;
+
     return 0;
 
 err:
@@ -446,5 +457,6 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
         return -1;
     }
 
-    return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
+    return net_vhost_user_init(peer, "vhost_user", name,
+                               vhost_user_opts->chardev, chr, queues);
 }
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
index fe659ec9e2..8c27ea0142 100644
--- a/net/vhost-vdpa.c
+++ b/net/vhost-vdpa.c
@@ -184,8 +184,22 @@ static int net_vhost_vdpa_init(NetClientState *peer, const char *device,
     VhostVDPAState *s;
     int vdpa_device_fd = -1;
     int ret = 0;
+    NetdevVhostVDPAOptions *stored;
+
     assert(name);
     nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name);
+
+    /* Store startup parameters */
+    nc->stored_config = g_new0(NetdevInfo, 1);
+    nc->stored_config->type = NET_BACKEND_VHOST_VDPA;
+    stored = &nc->stored_config->u.vhost_vdpa;
+
+    stored->has_vhostdev = true;
+    stored->vhostdev = g_strdup(vhostdev);
+
+    stored->has_queues = true;
+    stored->queues = 1; /* TODO: change when support multiqueue */
+
     snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA);
     nc->queue_index = 0;
     s = DO_UPCAST(VhostVDPAState, nc, nc);
diff --git a/qapi/net.json b/qapi/net.json
index a3a1336001..7f8261b042 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -714,3 +714,83 @@
 ##
 { 'event': 'FAILOVER_NEGOTIATED',
   'data': {'device-id': 'str'} }
+
+##
+# @NetBackend:
+#
+# Available netdev backend drivers.
+#
+# Since: 6.0
+##
+{ 'enum': 'NetBackend',
+  'data': [ 'user', 'tap', 'l2tpv3', 'socket', 'vde', 'bridge', 'netmap',
+            'vhost-user', 'vhost-vdpa' ] }
+
+##
+# @NetdevInfo:
+#
+# Configuration of a network backend device (netdev).
+#
+# @id: Device identifier.
+#
+# @type: Specify the driver used for interpreting remaining arguments.
+#
+# @peer-id: The connected frontend network device name (absent if no frontend
+#           is connected).
+#
+# Since: 6.0
+##
+{ 'union': 'NetdevInfo',
+  'base': { 'id': 'str',
+            'type': 'NetBackend',
+            '*peer-id': 'str' },
+  'discriminator': 'type',
+  'data': {
+      'user':       'NetdevUserOptions',
+      'tap':        'NetdevTapOptions',
+      'l2tpv3':     'NetdevL2TPv3Options',
+      'socket':     'NetdevSocketOptions',
+      'vde':        'NetdevVdeOptions',
+      'bridge':     'NetdevBridgeOptions',
+      'netmap':     'NetdevNetmapOptions',
+      'vhost-user': 'NetdevVhostUserOptions',
+      'vhost-vdpa': 'NetdevVhostVDPAOptions' } }
+
+##
+# @query-netdev:
+#
+# Get a list of @NetdevInfo for all virtual network backend devices (netdevs).
+#
+# Returns: a list of @NetdevInfo describing each netdev.
+#
+# Since: 6.0
+#
+# Example:
+#
+# -> { "execute": "query-netdev" }
+# <- { "return": [
+#          {
+#              "ipv6": true,
+#              "ipv4": true,
+#              "host": "10.0.2.2",
+#              "ipv6-dns": "fec0::3",
+#              "ipv6-prefix": "fec0::",
+#              "net": "10.0.2.0/255.255.255.0",
+#              "ipv6-host": "fec0::2",
+#              "type": "user",
+#              "peer-id": "net0",
+#              "dns": "10.0.2.3",
+#              "hostfwd": [
+#                  {
+#                      "str": "tcp::20004-:22"
+#                  }
+#              ],
+#              "ipv6-prefixlen": 64,
+#              "id": "netdev0",
+#              "restrict": false
+#          }
+#      ]
+#    }
+#
+##
+{ 'command': 'query-netdev', 'returns': ['NetdevInfo'] }
-- 
2.25.1



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

* [PATCH v5 2/4] tests: Add tests for query-netdev command
  2020-11-08 23:59 [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov
  2020-11-08 23:59 ` [PATCH v5 1/4] qapi: net: Add " Alexey Kirillov
@ 2020-11-08 23:59 ` Alexey Kirillov
  2020-11-08 23:59 ` [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network Alexey Kirillov
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Alexey Kirillov @ 2020-11-08 23:59 UTC (permalink / raw)
  To: Jason Wang, Eric Blake, Markus Armbruster, Thomas Huth
  Cc: Laurent Vivier, Michael S. Tsirkin, Stefan Weil, qemu-devel,
	Vincenzo Maffione, yc-core, Paolo Bonzini, Samuel Thibault,
	Giuseppe Lettieri, Luigi Rizzo

Signed-off-by: Alexey Kirillov <lekiravi@yandex-team.ru>
Acked-by: Thomas Huth <thuth@redhat.com>
---
 tests/qtest/meson.build         |   3 +
 tests/qtest/test-query-netdev.c | 120 ++++++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+)
 create mode 100644 tests/qtest/test-query-netdev.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index c19f1c8503..2d2c597364 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -17,6 +17,9 @@ qtests_generic = [
 if config_host.has_key('CONFIG_MODULES')
   qtests_generic += [ 'modules-test' ]
 endif
+if config_host.has_key('CONFIG_SLIRP')
+  qtests_generic += [ 'test-query-netdev' ]
+endif
 
 qtests_pci = \
   (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) +                  \
diff --git a/tests/qtest/test-query-netdev.c b/tests/qtest/test-query-netdev.c
new file mode 100644
index 0000000000..1118537a9f
--- /dev/null
+++ b/tests/qtest/test-query-netdev.c
@@ -0,0 +1,120 @@
+/*
+ * QTest testcase for the query-netdev
+ *
+ * Copyright Yandex N.V., 2019
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "libqos/libqtest.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qlist.h"
+
+/*
+ * Events can get in the way of responses we are actually waiting for.
+ */
+GCC_FMT_ATTR(2, 3)
+static QObject *wait_command(QTestState *who, const char *command, ...)
+{
+    va_list ap;
+    QDict *response;
+    QObject *result;
+
+    va_start(ap, command);
+    qtest_qmp_vsend(who, command, ap);
+    va_end(ap);
+
+    response = qtest_qmp_receive(who);
+
+    result = qdict_get(response, "return");
+    g_assert(result);
+    qobject_ref(result);
+    qobject_unref(response);
+
+    return result;
+}
+
+static void qmp_query_netdev_no_error(QTestState *qts, size_t netdevs_count)
+{
+    QObject *resp;
+    QList *netdevs;
+
+    resp = wait_command(qts, "{'execute': 'query-netdev'}");
+
+    netdevs = qobject_to(QList, resp);
+    g_assert(netdevs);
+    g_assert(qlist_size(netdevs) == netdevs_count);
+
+    qobject_unref(resp);
+}
+
+static void test_query_netdev(void)
+{
+    const char *arch = qtest_get_arch();
+    QObject *resp;
+    QTestState *state;
+
+    /* Choosing machine for platforms without default one */
+    if (g_str_equal(arch, "arm") ||
+        g_str_equal(arch, "aarch64")) {
+        state = qtest_init(
+            "-nodefaults "
+            "-M virt "
+            "-netdev user,id=slirp0");
+    } else if (g_str_equal(arch, "tricore")) {
+        state = qtest_init(
+            "-nodefaults "
+            "-M tricore_testboard "
+            "-netdev user,id=slirp0");
+    } else if (g_str_equal(arch, "avr")) {
+        state = qtest_init(
+            "-nodefaults "
+            "-M mega2560 "
+            "-netdev user,id=slirp0");
+    } else if (g_str_equal(arch, "rx")) {
+        state = qtest_init(
+            "-nodefaults "
+            "-M gdbsim-r5f562n8 "
+            "-netdev user,id=slirp0");
+    } else {
+        state = qtest_init(
+            "-nodefaults "
+            "-netdev user,id=slirp0");
+    }
+    g_assert(state);
+
+    qmp_query_netdev_no_error(state, 1);
+
+    resp = wait_command(state,
+        "{'execute': 'netdev_add', 'arguments': {"
+        " 'id': 'slirp1',"
+        " 'type': 'user'}}");
+    qobject_unref(resp);
+
+    qmp_query_netdev_no_error(state, 2);
+
+    resp = wait_command(state,
+        "{'execute': 'netdev_del', 'arguments': {"
+        " 'id': 'slirp1'}}");
+    qobject_unref(resp);
+
+    qmp_query_netdev_no_error(state, 1);
+
+    qtest_quit(state);
+}
+
+int main(int argc, char **argv)
+{
+    int ret = 0;
+    g_test_init(&argc, &argv, NULL);
+
+    qtest_add_func("/net/qapi/query_netdev", test_query_netdev);
+
+    ret = g_test_run();
+
+    return ret;
+}
-- 
2.25.1



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

* [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network
  2020-11-08 23:59 [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov
  2020-11-08 23:59 ` [PATCH v5 1/4] qapi: net: Add " Alexey Kirillov
  2020-11-08 23:59 ` [PATCH v5 2/4] tests: Add tests for " Alexey Kirillov
@ 2020-11-08 23:59 ` Alexey Kirillov
  2020-12-07  5:52   ` Jason Wang
  2020-11-08 23:59 ` [PATCH v5 4/4] net: Do not use legacy info_str for backends Alexey Kirillov
  2020-11-23 15:19 ` [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov
  4 siblings, 1 reply; 12+ messages in thread
From: Alexey Kirillov @ 2020-11-08 23:59 UTC (permalink / raw)
  To: Jason Wang, Eric Blake, Markus Armbruster, Thomas Huth
  Cc: Laurent Vivier, Michael S. Tsirkin, Stefan Weil, qemu-devel,
	Vincenzo Maffione, yc-core, Paolo Bonzini, Samuel Thibault,
	Giuseppe Lettieri, Luigi Rizzo

Replace usage of legacy field info_str of NetClientState for
backend network devices with result of QMP command query-netdev.
NIC and hubports still use legacy info_str field.

Signed-off-by: Alexey Kirillov <lekiravi@yandex-team.ru>
---
 include/net/net.h |   3 +-
 net/clients.h     |   1 +
 net/hub.c         |   4 +-
 net/hub.h         |   2 +-
 net/net.c         | 181 ++++++++++++++++++++++++++++++++++++++++++++--
 net/vde.c         |  10 +++
 6 files changed, 192 insertions(+), 9 deletions(-)

diff --git a/include/net/net.h b/include/net/net.h
index 16700f715e..ed77f444de 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -172,7 +172,8 @@ void qemu_check_nic_model(NICInfo *nd, const char *model);
 int qemu_find_nic_model(NICInfo *nd, const char * const *models,
                         const char *default_model);
 
-void print_net_client(Monitor *mon, NetClientState *nc);
+void print_net_client(Monitor *mon, NetClientState *nc,
+                      NetdevInfoList *ni_list);
 void hmp_info_network(Monitor *mon, const QDict *qdict);
 void net_socket_rs_init(SocketReadState *rs,
                         SocketReadStateFinalize *finalize,
diff --git a/net/clients.h b/net/clients.h
index 92f9b59aed..fdf257f641 100644
--- a/net/clients.h
+++ b/net/clients.h
@@ -51,6 +51,7 @@ int net_init_l2tpv3(const Netdev *netdev, const char *name,
 #ifdef CONFIG_VDE
 int net_init_vde(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp);
+int net_vde_get_fd(const NetClientState *nc);
 #endif
 
 #ifdef CONFIG_NETMAP
diff --git a/net/hub.c b/net/hub.c
index 1375738bf1..7815248650 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -221,7 +221,7 @@ NetClientState *net_hub_port_find(int hub_id)
 /**
  * Print hub configuration
  */
-void net_hub_info(Monitor *mon)
+void net_hub_info(Monitor *mon, NetdevInfoList *ni_list)
 {
     NetHub *hub;
     NetHubPort *port;
@@ -232,7 +232,7 @@ void net_hub_info(Monitor *mon)
             monitor_printf(mon, " \\ %s", port->nc.name);
             if (port->nc.peer) {
                 monitor_printf(mon, ": ");
-                print_net_client(mon, port->nc.peer);
+                print_net_client(mon, port->nc.peer, ni_list);
             } else {
                 monitor_printf(mon, "\n");
             }
diff --git a/net/hub.h b/net/hub.h
index ce45f7b399..2c9a384077 100644
--- a/net/hub.h
+++ b/net/hub.h
@@ -17,7 +17,7 @@
 
 NetClientState *net_hub_add_port(int hub_id, const char *name,
                                  NetClientState *hubpeer);
-void net_hub_info(Monitor *mon);
+void net_hub_info(Monitor *mon, NetdevInfoList *ninfo);
 void net_hub_check_clients(void);
 bool net_hub_flush(NetClientState *nc);
 
diff --git a/net/net.c b/net/net.c
index 551acadabc..4d3719434c 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1178,14 +1178,182 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
     monitor_printf(mon, "\n");
 }
 
-void print_net_client(Monitor *mon, NetClientState *nc)
+static NetdevInfo *get_netdev_info(NetdevInfoList *ni_list, char *name)
+{
+    NetdevInfo *ni;
+
+    while (ni_list) {
+        ni = ni_list->value;
+        if (g_str_equal(ni->id, name)) {
+            return ni;
+        }
+        ni_list = ni_list->next;
+    }
+
+    return NULL;
+}
+
+static char *generate_info_str(NetdevInfo *ni, NetClientState *nc)
+{
+    char *info_str;
+
+    /* Use legacy field info_str for NIC and hubports */
+    if ((nc->info->type == NET_CLIENT_DRIVER_NIC) ||
+        (nc->info->type == NET_CLIENT_DRIVER_HUBPORT)) {
+        return g_strdup(nc->info_str);
+    }
+
+    if (!ni) {
+        return g_malloc0(1);
+    }
+
+    switch (ni->type) {
+#ifdef CONFIG_SLIRP
+        case NET_BACKEND_USER: {
+            size_t len = strchr(ni->u.user.net, '/') - ni->u.user.net;
+            char *net = g_strndup(ni->u.user.net, len);
+
+            info_str = g_strdup_printf("net=%s,restrict=%s",
+                                       net,
+                                       ni->u.user.q_restrict ? "on" : "off");
+            g_free(net);
+            break;
+        }
+#endif /* CONFIG_SLIRP */
+        case NET_BACKEND_TAP: {
+#ifndef _WIN32
+            if (ni->u.tap.has_fds) {
+                char **fds = g_strsplit(ni->u.tap.fds, ":", -1);
+
+                info_str = g_strdup_printf("fd=%s", fds[nc->queue_index]);
+                g_strfreev(fds);
+            } else if (ni->u.tap.has_helper) {
+                info_str = g_strdup_printf("helper=%s", ni->u.tap.helper);
+            } else {
+                info_str = g_strdup_printf("ifname=%s,script=%s,downscript=%s",
+                    ni->u.tap.ifname,
+                    nc->queue_index == 0 ? ni->u.tap.script : "no",
+                    nc->queue_index == 0 ? ni->u.tap.downscript : "no");
+            }
+#else
+            info_str = g_strdup_printf("tap: ifname=%s", ni->u.tap.ifname);
+#endif /* _WIN32 */
+            break;
+        }
+#ifdef CONFIG_L2TPV3
+        case NET_BACKEND_L2TPV3: {
+            info_str = g_strdup_printf("l2tpv3: connected");
+            break;
+        }
+#endif /* CONFIG_L2TPV3 */
+        case NET_BACKEND_SOCKET: {
+            if (ni->u.socket.has_listen) {
+                if (ni->u.socket.has_fd) {
+                    info_str = g_strdup_printf("socket: connection from %s",
+                                               ni->u.socket.listen);
+                } else {
+                    info_str = g_strdup_printf("socket: wait from %s",
+                                               ni->u.socket.listen);
+                }
+            } else if (ni->u.socket.has_connect && ni->u.socket.has_fd) {
+                info_str = g_strdup_printf("socket: connect to %s",
+                                           ni->u.socket.connect);
+            } else if (ni->u.socket.has_mcast && ni->u.socket.has_fd) {
+                info_str = g_strdup_printf("socket: mcast=%s",
+                                           ni->u.socket.mcast);
+            } else if (ni->u.socket.has_udp && ni->u.socket.has_fd) {
+                info_str = g_strdup_printf("socket: udp=%s", ni->u.socket.udp);
+            } else {
+                g_assert(ni->u.socket.has_fd);
+                int so_type = -1;
+                int optlen = sizeof(so_type);
+                int fd = atoi(ni->u.socket.fd);
+
+                getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
+                           (socklen_t *)&optlen);
+                if (so_type == SOCK_STREAM) {
+                    info_str = g_strdup_printf("socket: fd=%s",
+                                               ni->u.socket.fd);
+                } else {
+                    if (ni->u.socket.has_mcast) {
+                        /*
+                         * This branch is unreachable, according to how it is in
+                         * net/socket.c at this moment
+                         */
+                        info_str = g_strdup_printf("socket: fd=%s "
+                                                   "(cloned mcast=%s)",
+                                                   ni->u.socket.fd,
+                                                   ni->u.socket.mcast);
+                    } else {
+                        SocketAddress *sa = socket_local_address(fd, NULL);
+
+                        info_str = g_strdup_printf("socket: fd=%s %s",
+                            ni->u.socket.fd,
+                            SocketAddressType_str(sa->type));
+                        qapi_free_SocketAddress(sa);
+                    }
+                }
+            }
+            break;
+        }
+#ifdef CONFIG_VDE
+        case NET_BACKEND_VDE: {
+            info_str = g_strdup_printf("sock=%s,fd=%d",
+                                       ni->u.vde.sock,
+                                       net_vde_get_fd(nc));
+            break;
+        }
+#endif /* CONFIG_VDE */
+#ifdef CONFIG_NET_BRIDGE
+        case NET_BACKEND_BRIDGE: {
+            info_str = g_strdup_printf("helper=%s,br=%s",
+                                       ni->u.bridge.helper,
+                                       ni->u.bridge.br);
+            break;
+        }
+#endif /* CONFIG_NET_BRIDGE */
+#ifdef CONFIG_NETMAP
+        case NET_BACKEND_NETMAP: {
+            info_str = g_strdup_printf("netmap: ifname=%s",
+                                       ni->u.netmap.ifname);
+            break;
+        }
+#endif /* CONFIG_NETMAP */
+#ifdef CONFIG_VHOST_NET_USER
+        case NET_BACKEND_VHOST_USER: {
+            info_str = g_strdup_printf("vhost-user%d to %s",
+                                       nc->queue_index,
+                                       ni->u.vhost_user.chardev);
+            break;
+        }
+#endif /* CONFIG_VHOST_NET_USER */
+#ifdef CONFIG_VHOST_NET_VDPA
+        case NET_BACKEND_VHOST_VDPA: {
+            info_str = g_strdup("vhost-vdpa");
+            break;
+        }
+#endif /* CONFIG_VHOST_NET_VDPA */
+        default: {
+            info_str = g_malloc0(1);
+            break;
+        }
+    }
+
+    return info_str;
+}
+
+void print_net_client(Monitor *mon, NetClientState *nc, NetdevInfoList *ni_list)
 {
     NetFilterState *nf;
+    NetdevInfo *ni = get_netdev_info(ni_list, nc->name);
+    char *info_str = generate_info_str(ni, nc);
 
     monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
                    nc->queue_index,
                    NetClientDriver_str(nc->info->type),
-                   nc->info_str);
+                   info_str);
+    g_free(info_str);
+
     if (!QTAILQ_EMPTY(&nc->filters)) {
         monitor_printf(mon, "filters:\n");
     }
@@ -1289,8 +1457,9 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
 {
     NetClientState *nc, *peer;
     NetClientDriver type;
+    NetdevInfoList *ni_list = qmp_query_netdev(NULL);
 
-    net_hub_info(mon);
+    net_hub_info(mon, ni_list);
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
         peer = nc->peer;
@@ -1302,13 +1471,15 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
         }
 
         if (!peer || type == NET_CLIENT_DRIVER_NIC) {
-            print_net_client(mon, nc);
+            print_net_client(mon, nc, ni_list);
         } /* else it's a netdev connected to a NIC, printed with the NIC */
         if (peer && type == NET_CLIENT_DRIVER_NIC) {
             monitor_printf(mon, " \\ ");
-            print_net_client(mon, peer);
+            print_net_client(mon, peer, ni_list);
         }
     }
+
+    qapi_free_NetdevInfoList(ni_list);
 }
 
 void colo_notify_filters_event(int event, Error **errp)
diff --git a/net/vde.c b/net/vde.c
index 1af9175060..041eadbcf9 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -153,3 +153,13 @@ int net_init_vde(const Netdev *netdev, const char *name,
 
     return 0;
 }
+
+int net_vde_get_fd(const NetClientState *nc)
+{
+    VDEState *s;
+    assert(nc->info->type == NET_CLIENT_DRIVER_VDE);
+
+    s = DO_UPCAST(VDEState, nc, nc);
+
+    return vde_datafd(s->vde);
+}
-- 
2.25.1



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

* [PATCH v5 4/4] net: Do not use legacy info_str for backends
  2020-11-08 23:59 [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov
                   ` (2 preceding siblings ...)
  2020-11-08 23:59 ` [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network Alexey Kirillov
@ 2020-11-08 23:59 ` Alexey Kirillov
  2020-11-23 15:19 ` [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov
  4 siblings, 0 replies; 12+ messages in thread
From: Alexey Kirillov @ 2020-11-08 23:59 UTC (permalink / raw)
  To: Jason Wang, Eric Blake, Markus Armbruster, Thomas Huth
  Cc: Laurent Vivier, Michael S. Tsirkin, Stefan Weil, qemu-devel,
	Vincenzo Maffione, yc-core, Paolo Bonzini, Samuel Thibault,
	Giuseppe Lettieri, Luigi Rizzo

As we use QMP query-netdev to store and get information about
backend network devices, we can drop usage legacy field info_str
for them.
We still use this field for NIC and hubports, so we can not
completely remove it.

Signed-off-by: Alexey Kirillov <lekiravi@yandex-team.ru>
---
 net/l2tpv3.c     |  2 --
 net/slirp.c      |  4 ----
 net/socket.c     | 22 ----------------------
 net/tap-win32.c  |  3 ---
 net/tap.c        | 12 ------------
 net/vde.c        |  3 ---
 net/vhost-user.c |  2 --
 net/vhost-vdpa.c |  1 -
 8 files changed, 49 deletions(-)

diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index 881fc6f7c1..7f2a590155 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -745,8 +745,6 @@ int net_init_l2tpv3(const Netdev *netdev,
         stored->dstport = g_strdup(l2tpv3->dstport);
     }
 
-    snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-             "l2tpv3: connected");
     return 0;
 outerr:
     qemu_del_net_client(nc);
diff --git a/net/slirp.c b/net/slirp.c
index 65d3f2639c..14fa64227d 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -670,10 +670,6 @@ static int net_slirp_init(NetClientState *peer, const char *model,
         stored->tftp_server_name = g_strdup(tftp_server_name);
     }
 
-    snprintf(nc->info_str, sizeof(nc->info_str),
-             "net=%s,restrict=%s", inet_ntoa(net),
-             restricted ? "on" : "off");
-
     s = DO_UPCAST(SlirpState, nc, nc);
 
     s->slirp = slirp_init(restricted, ipv4, net, mask, host,
diff --git a/net/socket.c b/net/socket.c
index 1614523b82..93b5163f0e 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -180,7 +180,6 @@ static void net_socket_send(void *opaque)
         s->fd = -1;
         net_socket_rs_init(&s->rs, net_socket_rs_finalize, false);
         s->nc.link_down = true;
-        memset(s->nc.info_str, 0, sizeof(s->nc.info_str));
 
         return;
     }
@@ -400,16 +399,10 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
         stored->mcast = g_strdup(mcast);
 
         s->dgram_dst = saddr;
-        snprintf(nc->info_str, sizeof(nc->info_str),
-                 "socket: fd=%d (cloned mcast=%s:%d)",
-                 fd, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
     } else {
         if (sa_type == SOCKET_ADDRESS_TYPE_UNIX) {
             s->dgram_dst.sin_family = AF_UNIX;
         }
-
-        snprintf(nc->info_str, sizeof(nc->info_str),
-                 "socket: fd=%d %s", fd, SocketAddressType_str(sa_type));
     }
 
     return s;
@@ -444,8 +437,6 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
 
     nc = qemu_new_net_client(&net_socket_info, peer, model, name);
 
-    snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd);
-
     s = DO_UPCAST(NetSocketState, nc, nc);
 
     s->fd = fd;
@@ -527,10 +518,6 @@ static void net_socket_accept(void *opaque)
 
     stored->has_fd = true;
     stored->fd = g_strdup_printf("%d", fd);
-
-    snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-             "socket: connection from %s:%d",
-             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
 }
 
 static int net_socket_listen_init(NetClientState *peer,
@@ -645,9 +632,6 @@ static int net_socket_connect_init(NetClientState *peer,
     stored->has_connect = true;
     stored->connect = g_strdup(host_str);
 
-    snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-             "socket: connect to %s:%d",
-             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
     return 0;
 }
 
@@ -704,9 +688,6 @@ static int net_socket_mcast_init(NetClientState *peer,
         stored->localaddr = g_strdup(localaddr_str);
     }
 
-    snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-             "socket: mcast=%s:%d",
-             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
     return 0;
 
 }
@@ -769,9 +750,6 @@ static int net_socket_udp_init(NetClientState *peer,
     stored->has_udp = true;
     stored->udp = g_strdup(rhost);
 
-    snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-             "socket: udp=%s:%d",
-             inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port));
     return 0;
 }
 
diff --git a/net/tap-win32.c b/net/tap-win32.c
index b60933bd1a..21e451107b 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -787,9 +787,6 @@ static int tap_win32_init(NetClientState *peer, const char *model,
     stored->has_ifname = true;
     stored->ifname = g_strdup(ifname);
 
-    snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-             "tap: ifname=%s", ifname);
-
     s->handle = handle;
 
     qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s);
diff --git a/net/tap.c b/net/tap.c
index 57af49ad51..908cb8e7bd 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -625,9 +625,6 @@ int net_init_bridge(const Netdev *netdev, const char *name,
         stored->helper = g_strdup(helper);
     }
 
-    snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper,
-             br);
-
     return 0;
 }
 
@@ -713,8 +710,6 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
             stored->fds = g_strdup_printf("%s:%d", stored->fds, fd);
             g_free(tmp_s);
         }
-
-        snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
     } else if (tap->has_helper) {
         if (!stored->has_helper) {
             stored->has_helper = true;
@@ -726,9 +721,6 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
             stored->br = tap->has_br ? g_strdup(tap->br) :
                                        g_strdup(DEFAULT_BRIDGE_INTERFACE);
         }
-
-        snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
-                 tap->helper);
     } else {
         if (ifname && !stored->has_ifname) {
             stored->has_ifname = true;
@@ -745,10 +737,6 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
             stored->downscript = g_strdup(downscript);
         }
 
-        snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-                 "ifname=%s,script=%s,downscript=%s", ifname, script,
-                 downscript);
-
         if (strcmp(downscript, "no") != 0) {
             snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
             snprintf(s->down_script_arg, sizeof(s->down_script_arg),
diff --git a/net/vde.c b/net/vde.c
index 041eadbcf9..bda8444d1a 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -100,9 +100,6 @@ static int net_vde_init(NetClientState *peer, const char *model,
 
     nc = qemu_new_net_client(&net_vde_info, peer, model, name);
 
-    snprintf(nc->info_str, sizeof(nc->info_str), "sock=%s,fd=%d",
-             sock, vde_datafd(vde));
-
     s = DO_UPCAST(VDEState, nc, nc);
 
     s->vde = vde;
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 5b7056be25..e443c4b2b5 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -327,8 +327,6 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
     user = g_new0(struct VhostUserState, 1);
     for (i = 0; i < queues; i++) {
         nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
-        snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
-                 i, chr->label);
         nc->queue_index = i;
         if (!nc0) {
             nc0 = nc;
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
index 8c27ea0142..5a28bbcd7b 100644
--- a/net/vhost-vdpa.c
+++ b/net/vhost-vdpa.c
@@ -200,7 +200,6 @@ static int net_vhost_vdpa_init(NetClientState *peer, const char *device,
     stored->has_queues = true;
     stored->queues = 1; /* TODO: change when support multiqueue */
 
-    snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA);
     nc->queue_index = 0;
     s = DO_UPCAST(VhostVDPAState, nc, nc);
     vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR);
-- 
2.25.1



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

* Re: [PATCH v5 0/4] Introducing QMP query-netdev command
  2020-11-08 23:59 [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov
                   ` (3 preceding siblings ...)
  2020-11-08 23:59 ` [PATCH v5 4/4] net: Do not use legacy info_str for backends Alexey Kirillov
@ 2020-11-23 15:19 ` Alexey Kirillov
  4 siblings, 0 replies; 12+ messages in thread
From: Alexey Kirillov @ 2020-11-23 15:19 UTC (permalink / raw)
  To: Jason Wang, Eric Blake, Markus Armbruster, Thomas Huth
  Cc: Laurent Vivier, Michael S. Tsirkin, Stefan Weil, qemu-devel,
	Vincenzo Maffione, yc-core, Samuel Thibault, Paolo Bonzini,
	Giuseppe Lettieri, Luigi Rizzo

ping

Patchwork page: http://patchwork.ozlabs.org/project/qemu-devel/list/?series=212983

09.11.2020, 03:02, "Alexey Kirillov" <lekiravi@yandex-team.ru>:
> This patch series introduces a new QMP command "query-netdev" to get
> information about currently attached backend network devices (netdevs).
> Also, since the "info_str" field of "NetClientState" is now deprecated,
> we no longer use it for netdevs, only for NIC/hubports.
> The HMP command "info network" now also uses the new QMP command inside.
>
> Usage example:
>
> -> { "execute": "query-netdev" }
> <- { "return": [
>          {
>              "listen": "127.0.0.1:90",
>              "type": "socket",
>              "peer-id": "hub0port1",
>              "id": "__org.qemu.net1"
>          },
>          {
>              "script": "/etc/qemu-ifup",
>              "downscript": "/etc/qemu-ifdown",
>              "ifname": "tap0",
>              "type": "tap",
>              "peer-id": "net5",
>              "vnet_hdr": true,
>              "id": "tap0"
>          },
>          {
>              "ipv6": true,
>              "ipv4": true,
>              "host": "10.0.2.2",
>              "ipv6-dns": "fec0::3",
>              "ipv6-prefix": "fec0::",
>              "net": "10.0.2.0/255.255.255.0",
>              "ipv6-host": "fec0::2",
>              "type": "user",
>              "peer-id": "net0",
>              "dns": "10.0.2.3",
>              "hostfwd": [
>                  {
>                      "str": "tcp::20004-:22"
>                  }
>              ],
>              "ipv6-prefixlen": 64,
>              "id": "netdev0",
>              "restrict": false
>          }
>      ]
>    }
>
> v4->v5:
> - Enable qtest of query-netdevs for AVR and RX archs.
> - Bump "Since" version in QAPI to 6.0.
>
> v3->v4:
> - Rename "query-netdevs" to "query-netdev".
> - Copy netdev drivers to new QAPI enum "NetBackend".
>
> v2->v3:
> - Remove NIC and hubports from query-netdevs.
> - Remove several fields from NetdevInfo since they are unnecessary.
> - Rename field @peer to @peer-id.
> - Add support of vhost-vdpa.
> - Keep "info_str" for NIC/hubports, but remove it for netdevs.
>
> v1->v2:
> - Rewrite HMP "info network" to get information from results of QMP command.
> - Remove obsolete field "info_str" from "NetClientState".
>
> Alexey Kirillov (4):
>   qapi: net: Add query-netdev command
>   tests: Add tests for query-netdev command
>   hmp: Use QMP query-netdev in hmp_info_network
>   net: Do not use legacy info_str for backends
>
>  include/net/net.h | 4 +-
>  net/clients.h | 1 +
>  net/hub.c | 4 +-
>  net/hub.h | 2 +-
>  net/l2tpv3.c | 21 +++-
>  net/net.c | 213 +++++++++++++++++++++++++++++++-
>  net/netmap.c | 13 ++
>  net/slirp.c | 128 ++++++++++++++++++-
>  net/socket.c | 91 ++++++++++----
>  net/tap-win32.c | 10 +-
>  net/tap.c | 107 ++++++++++++++--
>  net/vde.c | 39 +++++-
>  net/vhost-user.c | 20 ++-
>  net/vhost-vdpa.c | 15 ++-
>  qapi/net.json | 80 ++++++++++++
>  tests/qtest/meson.build | 3 +
>  tests/qtest/test-query-netdev.c | 120 ++++++++++++++++++
>  17 files changed, 812 insertions(+), 59 deletions(-)
>  create mode 100644 tests/qtest/test-query-netdev.c
>
> --
> 2.25.1


-- 
Alexey Kirillov
Yandex.Cloud


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

* Re: [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network
  2020-11-08 23:59 ` [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network Alexey Kirillov
@ 2020-12-07  5:52   ` Jason Wang
  2020-12-14 17:14     ` Alexey Kirillov
  2020-12-15  8:39     ` Markus Armbruster
  0 siblings, 2 replies; 12+ messages in thread
From: Jason Wang @ 2020-12-07  5:52 UTC (permalink / raw)
  To: Alexey Kirillov, Eric Blake, Markus Armbruster, Thomas Huth
  Cc: Laurent Vivier, Michael S. Tsirkin, Stefan Weil, qemu-devel,
	Vincenzo Maffione, yc-core, Samuel Thibault, Paolo Bonzini,
	Giuseppe Lettieri, Luigi Rizzo


On 2020/11/9 上午7:59, Alexey Kirillov wrote:
> +#ifdef CONFIG_SLIRP
> +        case NET_BACKEND_USER: {
> +            size_t len = strchr(ni->u.user.net, '/') - ni->u.user.net;
> +            char *net = g_strndup(ni->u.user.net, len);
> +
> +            info_str = g_strdup_printf("net=%s,restrict=%s",
> +                                       net,
> +                                       ni->u.user.q_restrict ? "on" : "off");
> +            g_free(net);
> +            break;
> +        }
> +#endif /* CONFIG_SLIRP */
> +        case NET_BACKEND_TAP: {
> +#ifndef _WIN32
> +            if (ni->u.tap.has_fds) {
> +                char **fds = g_strsplit(ni->u.tap.fds, ":", -1);
> +
> +                info_str = g_strdup_printf("fd=%s", fds[nc->queue_index]);
> +                g_strfreev(fds);
> +            } else if (ni->u.tap.has_helper) {
> +                info_str = g_strdup_printf("helper=%s", ni->u.tap.helper);
> +            } else {
> +                info_str = g_strdup_printf("ifname=%s,script=%s,downscript=%s",
> +                    ni->u.tap.ifname,
> +                    nc->queue_index == 0 ? ni->u.tap.script : "no",
> +                    nc->queue_index == 0 ? ni->u.tap.downscript : "no");
> +            }
> +#else
> +            info_str = g_strdup_printf("tap: ifname=%s", ni->u.tap.ifname);
> +#endif /* _WIN32 */
> +            break;
> +        }
> +#ifdef CONFIG_L2TPV3
> +        case NET_BACKEND_L2TPV3: {
> +            info_str = g_strdup_printf("l2tpv3: connected");
> +            break;
> +        }
> +#endif /* CONFIG_L2TPV3 */
> +        case NET_BACKEND_SOCKET: {
> +            if (ni->u.socket.has_listen) {
> +                if (ni->u.socket.has_fd) {
> +                    info_str = g_strdup_printf("socket: connection from %s",
> +                                               ni->u.socket.listen);
> +                } else {
> +                    info_str = g_strdup_printf("socket: wait from %s",
> +                                               ni->u.socket.listen);
> +                }
> +            } else if (ni->u.socket.has_connect && ni->u.socket.has_fd) {
> +                info_str = g_strdup_printf("socket: connect to %s",
> +                                           ni->u.socket.connect);
> +            } else if (ni->u.socket.has_mcast && ni->u.socket.has_fd) {
> +                info_str = g_strdup_printf("socket: mcast=%s",
> +                                           ni->u.socket.mcast);
> +            } else if (ni->u.socket.has_udp && ni->u.socket.has_fd) {
> +                info_str = g_strdup_printf("socket: udp=%s", ni->u.socket.udp);
> +            } else {
> +                g_assert(ni->u.socket.has_fd);
> +                int so_type = -1;
> +                int optlen = sizeof(so_type);
> +                int fd = atoi(ni->u.socket.fd);
> +
> +                getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
> +                           (socklen_t *)&optlen);
> +                if (so_type == SOCK_STREAM) {
> +                    info_str = g_strdup_printf("socket: fd=%s",
> +                                               ni->u.socket.fd);
> +                } else {
> +                    if (ni->u.socket.has_mcast) {
> +                        /*
> +                         * This branch is unreachable, according to how it is in
> +                         * net/socket.c at this moment
> +                         */
> +                        info_str = g_strdup_printf("socket: fd=%s "
> +                                                   "(cloned mcast=%s)",
> +                                                   ni->u.socket.fd,
> +                                                   ni->u.socket.mcast);
> +                    } else {
> +                        SocketAddress *sa = socket_local_address(fd, NULL);
> +
> +                        info_str = g_strdup_printf("socket: fd=%s %s",
> +                            ni->u.socket.fd,
> +                            SocketAddressType_str(sa->type));
> +                        qapi_free_SocketAddress(sa);
> +                    }
> +                }
> +            }
> +            break;
> +        }
> +#ifdef CONFIG_VDE
> +        case NET_BACKEND_VDE: {
> +            info_str = g_strdup_printf("sock=%s,fd=%d",
> +                                       ni->u.vde.sock,
> +                                       net_vde_get_fd(nc));
> +            break;
> +        }
> +#endif /* CONFIG_VDE */
> +#ifdef CONFIG_NET_BRIDGE
> +        case NET_BACKEND_BRIDGE: {
> +            info_str = g_strdup_printf("helper=%s,br=%s",
> +                                       ni->u.bridge.helper,
> +                                       ni->u.bridge.br);
> +            break;
> +        }
> +#endif /* CONFIG_NET_BRIDGE */
> +#ifdef CONFIG_NETMAP
> +        case NET_BACKEND_NETMAP: {
> +            info_str = g_strdup_printf("netmap: ifname=%s",
> +                                       ni->u.netmap.ifname);
> +            break;
> +        }
> +#endif /* CONFIG_NETMAP */
> +#ifdef CONFIG_VHOST_NET_USER
> +        case NET_BACKEND_VHOST_USER: {
> +            info_str = g_strdup_printf("vhost-user%d to %s",
> +                                       nc->queue_index,
> +                                       ni->u.vhost_user.chardev);
> +            break;
> +        }
> +#endif /* CONFIG_VHOST_NET_USER */
> +#ifdef CONFIG_VHOST_NET_VDPA
> +        case NET_BACKEND_VHOST_VDPA: {
> +            info_str = g_strdup("vhost-vdpa");
> +            break;
> +        }
> +#endif /* CONFIG_VHOST_NET_VDPA */


This will introduce burdens for new netdevs or new attributes since 
people can easily forget to add the routine here.

I think at least we need introduce callbacks for this.

One more stupid question, instead of generating the string via hard 
codes, is there any method (dict?) to iterate all the key/values 
automatically?

Thanks




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

* Re: [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network
  2020-12-07  5:52   ` Jason Wang
@ 2020-12-14 17:14     ` Alexey Kirillov
  2020-12-15  4:16       ` Jason Wang
  2020-12-15  8:39     ` Markus Armbruster
  1 sibling, 1 reply; 12+ messages in thread
From: Alexey Kirillov @ 2020-12-14 17:14 UTC (permalink / raw)
  To: Jason Wang, Eric Blake, Markus Armbruster, Thomas Huth
  Cc: Laurent Vivier, Michael S. Tsirkin, Stefan Weil, qemu-devel,
	Vincenzo Maffione, yc-core, Samuel Thibault, Paolo Bonzini,
	Giuseppe Lettieri, Luigi Rizzo

Hi!

07.12.2020, 08:52, "Jason Wang" <jasowang@redhat.com>:
> On 2020/11/9 上午7:59, Alexey Kirillov wrote:
>>  +#ifdef CONFIG_SLIRP
>>  + case NET_BACKEND_USER: {
>>  + size_t len = strchr(ni->u.user.net, '/') - ni->u.user.net;
>>  + char *net = g_strndup(ni->u.user.net, len);
>>  +
>>  + info_str = g_strdup_printf("net=%s,restrict=%s",
>>  + net,
>>  + ni->u.user.q_restrict ? "on" : "off");
>>  + g_free(net);
>>  + break;
>>  + }
>>  +#endif /* CONFIG_SLIRP */
>>  + case NET_BACKEND_TAP: {
>>  +#ifndef _WIN32
>>  + if (ni->u.tap.has_fds) {
>>  + char **fds = g_strsplit(ni->u.tap.fds, ":", -1);
>>  +
>>  + info_str = g_strdup_printf("fd=%s", fds[nc->queue_index]);
>>  + g_strfreev(fds);
>>  + } else if (ni->u.tap.has_helper) {
>>  + info_str = g_strdup_printf("helper=%s", ni->u.tap.helper);
>>  + } else {
>>  + info_str = g_strdup_printf("ifname=%s,script=%s,downscript=%s",
>>  + ni->u.tap.ifname,
>>  + nc->queue_index == 0 ? ni->u.tap.script : "no",
>>  + nc->queue_index == 0 ? ni->u.tap.downscript : "no");
>>  + }
>>  +#else
>>  + info_str = g_strdup_printf("tap: ifname=%s", ni->u.tap.ifname);
>>  +#endif /* _WIN32 */
>>  + break;
>>  + }
>>  +#ifdef CONFIG_L2TPV3
>>  + case NET_BACKEND_L2TPV3: {
>>  + info_str = g_strdup_printf("l2tpv3: connected");
>>  + break;
>>  + }
>>  +#endif /* CONFIG_L2TPV3 */
>>  + case NET_BACKEND_SOCKET: {
>>  + if (ni->u.socket.has_listen) {
>>  + if (ni->u.socket.has_fd) {
>>  + info_str = g_strdup_printf("socket: connection from %s",
>>  + ni->u.socket.listen);
>>  + } else {
>>  + info_str = g_strdup_printf("socket: wait from %s",
>>  + ni->u.socket.listen);
>>  + }
>>  + } else if (ni->u.socket.has_connect && ni->u.socket.has_fd) {
>>  + info_str = g_strdup_printf("socket: connect to %s",
>>  + ni->u.socket.connect);
>>  + } else if (ni->u.socket.has_mcast && ni->u.socket.has_fd) {
>>  + info_str = g_strdup_printf("socket: mcast=%s",
>>  + ni->u.socket.mcast);
>>  + } else if (ni->u.socket.has_udp && ni->u.socket.has_fd) {
>>  + info_str = g_strdup_printf("socket: udp=%s", ni->u.socket.udp);
>>  + } else {
>>  + g_assert(ni->u.socket.has_fd);
>>  + int so_type = -1;
>>  + int optlen = sizeof(so_type);
>>  + int fd = atoi(ni->u.socket.fd);
>>  +
>>  + getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
>>  + (socklen_t *)&optlen);
>>  + if (so_type == SOCK_STREAM) {
>>  + info_str = g_strdup_printf("socket: fd=%s",
>>  + ni->u.socket.fd);
>>  + } else {
>>  + if (ni->u.socket.has_mcast) {
>>  + /*
>>  + * This branch is unreachable, according to how it is in
>>  + * net/socket.c at this moment
>>  + */
>>  + info_str = g_strdup_printf("socket: fd=%s "
>>  + "(cloned mcast=%s)",
>>  + ni->u.socket.fd,
>>  + ni->u.socket.mcast);
>>  + } else {
>>  + SocketAddress *sa = socket_local_address(fd, NULL);
>>  +
>>  + info_str = g_strdup_printf("socket: fd=%s %s",
>>  + ni->u.socket.fd,
>>  + SocketAddressType_str(sa->type));
>>  + qapi_free_SocketAddress(sa);
>>  + }
>>  + }
>>  + }
>>  + break;
>>  + }
>>  +#ifdef CONFIG_VDE
>>  + case NET_BACKEND_VDE: {
>>  + info_str = g_strdup_printf("sock=%s,fd=%d",
>>  + ni->u.vde.sock,
>>  + net_vde_get_fd(nc));
>>  + break;
>>  + }
>>  +#endif /* CONFIG_VDE */
>>  +#ifdef CONFIG_NET_BRIDGE
>>  + case NET_BACKEND_BRIDGE: {
>>  + info_str = g_strdup_printf("helper=%s,br=%s",
>>  + ni->u.bridge.helper,
>>  + ni->u.bridge.br);
>>  + break;
>>  + }
>>  +#endif /* CONFIG_NET_BRIDGE */
>>  +#ifdef CONFIG_NETMAP
>>  + case NET_BACKEND_NETMAP: {
>>  + info_str = g_strdup_printf("netmap: ifname=%s",
>>  + ni->u.netmap.ifname);
>>  + break;
>>  + }
>>  +#endif /* CONFIG_NETMAP */
>>  +#ifdef CONFIG_VHOST_NET_USER
>>  + case NET_BACKEND_VHOST_USER: {
>>  + info_str = g_strdup_printf("vhost-user%d to %s",
>>  + nc->queue_index,
>>  + ni->u.vhost_user.chardev);
>>  + break;
>>  + }
>>  +#endif /* CONFIG_VHOST_NET_USER */
>>  +#ifdef CONFIG_VHOST_NET_VDPA
>>  + case NET_BACKEND_VHOST_VDPA: {
>>  + info_str = g_strdup("vhost-vdpa");
>>  + break;
>>  + }
>>  +#endif /* CONFIG_VHOST_NET_VDPA */
>
> This will introduce burdens for new netdevs or new attributes since
> people can easily forget to add the routine here.
>
> I think at least we need introduce callbacks for this.

Thanks for pointing. I can't remember why exactly I chose to not do it.
So it's definitely better to split this chunk to several callbacks.
I'll do it in the next version of series.

> One more stupid question, instead of generating the string via hard
> codes, is there any method (dict?) to iterate all the key/values
> automatically?
>
> Thanks

Oh yes, that the point.
Now there are no common format for info_str.
This patch is aimed to keep old HMP command mostly untouched.
But if we can drop old format, all this mess can be generalized as JSON
lines replacing old info_str stuff.

What do you think about that?

Originally I wanted to completely drop old info_str and use
QAPI to store and provide information about netdevs (and NICs too).

Thanks!

-- 
Alexey Kirillov
Yandex.Cloud


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

* Re: [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network
  2020-12-14 17:14     ` Alexey Kirillov
@ 2020-12-15  4:16       ` Jason Wang
  0 siblings, 0 replies; 12+ messages in thread
From: Jason Wang @ 2020-12-15  4:16 UTC (permalink / raw)
  To: Alexey Kirillov
  Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Stefan Weil,
	Markus Armbruster, Vincenzo Maffione, qemu-devel, Luigi Rizzo,
	yc-core, Paolo Bonzini, Samuel Thibault, Giuseppe Lettieri



----- Original Message -----
> Hi!
> 
> 07.12.2020, 08:52, "Jason Wang" <jasowang@redhat.com>:
> > On 2020/11/9 上午7:59, Alexey Kirillov wrote:
> >>  +#ifdef CONFIG_SLIRP
> >>  + case NET_BACKEND_USER: {
> >>  + size_t len = strchr(ni->u.user.net, '/') - ni->u.user.net;
> >>  + char *net = g_strndup(ni->u.user.net, len);
> >>  +
> >>  + info_str = g_strdup_printf("net=%s,restrict=%s",
> >>  + net,
> >>  + ni->u.user.q_restrict ? "on" : "off");
> >>  + g_free(net);
> >>  + break;
> >>  + }
> >>  +#endif /* CONFIG_SLIRP */
> >>  + case NET_BACKEND_TAP: {
> >>  +#ifndef _WIN32
> >>  + if (ni->u.tap.has_fds) {
> >>  + char **fds = g_strsplit(ni->u.tap.fds, ":", -1);
> >>  +
> >>  + info_str = g_strdup_printf("fd=%s", fds[nc->queue_index]);
> >>  + g_strfreev(fds);
> >>  + } else if (ni->u.tap.has_helper) {
> >>  + info_str = g_strdup_printf("helper=%s", ni->u.tap.helper);
> >>  + } else {
> >>  + info_str = g_strdup_printf("ifname=%s,script=%s,downscript=%s",
> >>  + ni->u.tap.ifname,
> >>  + nc->queue_index == 0 ? ni->u.tap.script : "no",
> >>  + nc->queue_index == 0 ? ni->u.tap.downscript : "no");
> >>  + }
> >>  +#else
> >>  + info_str = g_strdup_printf("tap: ifname=%s", ni->u.tap.ifname);
> >>  +#endif /* _WIN32 */
> >>  + break;
> >>  + }
> >>  +#ifdef CONFIG_L2TPV3
> >>  + case NET_BACKEND_L2TPV3: {
> >>  + info_str = g_strdup_printf("l2tpv3: connected");
> >>  + break;
> >>  + }
> >>  +#endif /* CONFIG_L2TPV3 */
> >>  + case NET_BACKEND_SOCKET: {
> >>  + if (ni->u.socket.has_listen) {
> >>  + if (ni->u.socket.has_fd) {
> >>  + info_str = g_strdup_printf("socket: connection from %s",
> >>  + ni->u.socket.listen);
> >>  + } else {
> >>  + info_str = g_strdup_printf("socket: wait from %s",
> >>  + ni->u.socket.listen);
> >>  + }
> >>  + } else if (ni->u.socket.has_connect && ni->u.socket.has_fd) {
> >>  + info_str = g_strdup_printf("socket: connect to %s",
> >>  + ni->u.socket.connect);
> >>  + } else if (ni->u.socket.has_mcast && ni->u.socket.has_fd) {
> >>  + info_str = g_strdup_printf("socket: mcast=%s",
> >>  + ni->u.socket.mcast);
> >>  + } else if (ni->u.socket.has_udp && ni->u.socket.has_fd) {
> >>  + info_str = g_strdup_printf("socket: udp=%s", ni->u.socket.udp);
> >>  + } else {
> >>  + g_assert(ni->u.socket.has_fd);
> >>  + int so_type = -1;
> >>  + int optlen = sizeof(so_type);
> >>  + int fd = atoi(ni->u.socket.fd);
> >>  +
> >>  + getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
> >>  + (socklen_t *)&optlen);
> >>  + if (so_type == SOCK_STREAM) {
> >>  + info_str = g_strdup_printf("socket: fd=%s",
> >>  + ni->u.socket.fd);
> >>  + } else {
> >>  + if (ni->u.socket.has_mcast) {
> >>  + /*
> >>  + * This branch is unreachable, according to how it is in
> >>  + * net/socket.c at this moment
> >>  + */
> >>  + info_str = g_strdup_printf("socket: fd=%s "
> >>  + "(cloned mcast=%s)",
> >>  + ni->u.socket.fd,
> >>  + ni->u.socket.mcast);
> >>  + } else {
> >>  + SocketAddress *sa = socket_local_address(fd, NULL);
> >>  +
> >>  + info_str = g_strdup_printf("socket: fd=%s %s",
> >>  + ni->u.socket.fd,
> >>  + SocketAddressType_str(sa->type));
> >>  + qapi_free_SocketAddress(sa);
> >>  + }
> >>  + }
> >>  + }
> >>  + break;
> >>  + }
> >>  +#ifdef CONFIG_VDE
> >>  + case NET_BACKEND_VDE: {
> >>  + info_str = g_strdup_printf("sock=%s,fd=%d",
> >>  + ni->u.vde.sock,
> >>  + net_vde_get_fd(nc));
> >>  + break;
> >>  + }
> >>  +#endif /* CONFIG_VDE */
> >>  +#ifdef CONFIG_NET_BRIDGE
> >>  + case NET_BACKEND_BRIDGE: {
> >>  + info_str = g_strdup_printf("helper=%s,br=%s",
> >>  + ni->u.bridge.helper,
> >>  + ni->u.bridge.br);
> >>  + break;
> >>  + }
> >>  +#endif /* CONFIG_NET_BRIDGE */
> >>  +#ifdef CONFIG_NETMAP
> >>  + case NET_BACKEND_NETMAP: {
> >>  + info_str = g_strdup_printf("netmap: ifname=%s",
> >>  + ni->u.netmap.ifname);
> >>  + break;
> >>  + }
> >>  +#endif /* CONFIG_NETMAP */
> >>  +#ifdef CONFIG_VHOST_NET_USER
> >>  + case NET_BACKEND_VHOST_USER: {
> >>  + info_str = g_strdup_printf("vhost-user%d to %s",
> >>  + nc->queue_index,
> >>  + ni->u.vhost_user.chardev);
> >>  + break;
> >>  + }
> >>  +#endif /* CONFIG_VHOST_NET_USER */
> >>  +#ifdef CONFIG_VHOST_NET_VDPA
> >>  + case NET_BACKEND_VHOST_VDPA: {
> >>  + info_str = g_strdup("vhost-vdpa");
> >>  + break;
> >>  + }
> >>  +#endif /* CONFIG_VHOST_NET_VDPA */
> >
> > This will introduce burdens for new netdevs or new attributes since
> > people can easily forget to add the routine here.
> >
> > I think at least we need introduce callbacks for this.
> 
> Thanks for pointing. I can't remember why exactly I chose to not do it.
> So it's definitely better to split this chunk to several callbacks.
> I'll do it in the next version of series.
> 
> > One more stupid question, instead of generating the string via hard
> > codes, is there any method (dict?) to iterate all the key/values
> > automatically?
> >
> > Thanks
> 
> Oh yes, that the point.
> Now there are no common format for info_str.
> This patch is aimed to keep old HMP command mostly untouched.
> But if we can drop old format, all this mess can be generalized as JSON
> lines replacing old info_str stuff.
> 
> What do you think about that?

I think it works and there's no need for sticking to the old HMP
format since it's not an ABI. And it would be better if you can
generate more human readable format from JSON.

Thanks


> 
> Originally I wanted to completely drop old info_str and use
> QAPI to store and provide information about netdevs (and NICs too).
> 
> Thanks!
> 
> --
> Alexey Kirillov
> Yandex.Cloud
> 
> 



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

* Re: [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network
  2020-12-07  5:52   ` Jason Wang
  2020-12-14 17:14     ` Alexey Kirillov
@ 2020-12-15  8:39     ` Markus Armbruster
  2020-12-16  5:58       ` Jason Wang
  1 sibling, 1 reply; 12+ messages in thread
From: Markus Armbruster @ 2020-12-15  8:39 UTC (permalink / raw)
  To: Jason Wang
  Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Stefan Weil,
	qemu-devel, Vincenzo Maffione, Luigi Rizzo, yc-core,
	Paolo Bonzini, Alexey Kirillov, Samuel Thibault,
	Giuseppe Lettieri

Jason Wang <jasowang@redhat.com> writes:

[...]
> One more stupid question, instead of generating the string via hard
> codes, is there any method (dict?) to iterate all the key/values 
> automatically?

QAPI visitors.

The lazy way: use the QObject output visitor to convert the QAPI type
(here: NetdevInfo) to QObject, then qobject_to_json() to convert to
JSON text.

If you don't want JSON, replace qobject_to_json().  Perhaps you can
create something that's generally useful for HMP, not just "info
network".  I'd pick keyval_parse() syntax.

The detour through QObject creates and destroys a rather fat temporary
data structure.  Tolerable when the amount of data is small.  An output
visitor that directly creates the string is more efficient.  Takes a bit
more code, though.  I intend to post one for JSON, to reduce QMP's
malloc gluttony.



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

* Re: [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network
  2020-12-15  8:39     ` Markus Armbruster
@ 2020-12-16  5:58       ` Jason Wang
  2020-12-16  9:16         ` Alexey Kirillov
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Wang @ 2020-12-16  5:58 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Stefan Weil,
	qemu-devel, Vincenzo Maffione, yc-core, Samuel Thibault,
	Alexey Kirillov, Paolo Bonzini, Giuseppe Lettieri, Luigi Rizzo



----- Original Message -----
> Jason Wang <jasowang@redhat.com> writes:
> 
> [...]
> > One more stupid question, instead of generating the string via hard
> > codes, is there any method (dict?) to iterate all the key/values
> > automatically?
> 
> QAPI visitors.
> 
> The lazy way: use the QObject output visitor to convert the QAPI type
> (here: NetdevInfo) to QObject, then qobject_to_json() to convert to
> JSON text.
> 
> If you don't want JSON, replace qobject_to_json().  Perhaps you can
> create something that's generally useful for HMP, not just "info
> network".  I'd pick keyval_parse() syntax.
> 
> The detour through QObject creates and destroys a rather fat temporary
> data structure.  Tolerable when the amount of data is small.  An output
> visitor that directly creates the string is more efficient.  Takes a bit
> more code, though.  I intend to post one for JSON, to reduce QMP's
> malloc gluttony.
> 

Thanks a lot for the answer.

Alexey, let's try what Markus suggested here.



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

* Re: [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network
  2020-12-16  5:58       ` Jason Wang
@ 2020-12-16  9:16         ` Alexey Kirillov
  0 siblings, 0 replies; 12+ messages in thread
From: Alexey Kirillov @ 2020-12-16  9:16 UTC (permalink / raw)
  To: Jason Wang, Markus Armbruster
  Cc: Laurent Vivier, Thomas Huth, Michael S. Tsirkin, Stefan Weil,
	qemu-devel, Vincenzo Maffione, yc-core, Samuel Thibault,
	Paolo Bonzini, Giuseppe Lettieri, Luigi Rizzo


16.12.2020, 08:58, "Jason Wang" <jasowang@redhat.com>:
> ----- Original Message -----
>>  Jason Wang <jasowang@redhat.com> writes:
>>
>>  [...]
>>  > One more stupid question, instead of generating the string via hard
>>  > codes, is there any method (dict?) to iterate all the key/values
>>  > automatically?
>>
>>  QAPI visitors.
>>
>>  The lazy way: use the QObject output visitor to convert the QAPI type
>>  (here: NetdevInfo) to QObject, then qobject_to_json() to convert to
>>  JSON text.
>>
>>  If you don't want JSON, replace qobject_to_json(). Perhaps you can
>>  create something that's generally useful for HMP, not just "info
>>  network". I'd pick keyval_parse() syntax.
>>
>>  The detour through QObject creates and destroys a rather fat temporary
>>  data structure. Tolerable when the amount of data is small. An output
>>  visitor that directly creates the string is more efficient. Takes a bit
>>  more code, though. I intend to post one for JSON, to reduce QMP's
>>  malloc gluttony.
>
> Thanks a lot for the answer.
>
> Alexey, let's try what Markus suggested here.

Thanks for information.
I will do so and send a new version of the patchset.

-- 
Alexey Kirillov
Yandex.Cloud


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

end of thread, other threads:[~2020-12-16  9:18 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-08 23:59 [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov
2020-11-08 23:59 ` [PATCH v5 1/4] qapi: net: Add " Alexey Kirillov
2020-11-08 23:59 ` [PATCH v5 2/4] tests: Add tests for " Alexey Kirillov
2020-11-08 23:59 ` [PATCH v5 3/4] hmp: Use QMP query-netdev in hmp_info_network Alexey Kirillov
2020-12-07  5:52   ` Jason Wang
2020-12-14 17:14     ` Alexey Kirillov
2020-12-15  4:16       ` Jason Wang
2020-12-15  8:39     ` Markus Armbruster
2020-12-16  5:58       ` Jason Wang
2020-12-16  9:16         ` Alexey Kirillov
2020-11-08 23:59 ` [PATCH v5 4/4] net: Do not use legacy info_str for backends Alexey Kirillov
2020-11-23 15:19 ` [PATCH v5 0/4] Introducing QMP query-netdev command Alexey Kirillov

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.