From c6941b3b9b7445f7760c462882f8397b9dc51e30 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 10 Nov 2022 13:52:22 +0100 Subject: [PATCH 1/9] net: Move the code to collect available NIC models to a separate function The code that collects the available NIC models is not really specific to PCI anymore and will be required in the next patch, too, so let's move this into a new separate function in net.c instead. Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- hw/pci/pci.c | 29 +---------------------------- include/net/net.h | 14 ++++++++++++++ net/net.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 208c16f450..cc51f98593 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1789,7 +1789,6 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, const char *default_devaddr) { const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; - GSList *list; GPtrArray *pci_nic_models; PCIBus *bus; PCIDevice *pci_dev; @@ -1804,33 +1803,7 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, nd->model = g_strdup("virtio-net-pci"); } - list = object_class_get_list_sorted(TYPE_PCI_DEVICE, false); - pci_nic_models = g_ptr_array_new(); - while (list) { - DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data, - TYPE_DEVICE); - GSList *next; - if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) && - dc->user_creatable) { - const char *name = object_class_get_name(list->data); - /* - * A network device might also be something else than a NIC, see - * e.g. the "rocker" device. Thus we have to look for the "netdev" - * property, too. Unfortunately, some devices like virtio-net only - * create this property during instance_init, so we have to create - * a temporary instance here to be able to check it. - */ - Object *obj = object_new_with_class(OBJECT_CLASS(dc)); - if (object_property_find(obj, "netdev")) { - g_ptr_array_add(pci_nic_models, (gpointer)name); - } - object_unref(obj); - } - next = list->next; - g_slist_free_1(list); - list = next; - } - g_ptr_array_add(pci_nic_models, NULL); + pci_nic_models = qemu_get_nic_models(TYPE_PCI_DEVICE); if (qemu_show_nic_models(nd->model, (const char **)pci_nic_models->pdata)) { exit(0); diff --git a/include/net/net.h b/include/net/net.h index fad589cc1d..1d88621c12 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -203,6 +203,20 @@ void net_socket_rs_init(SocketReadState *rs, bool vnet_hdr); NetClientState *qemu_get_peer(NetClientState *nc, int queue_index); +/** + * qemu_get_nic_models: + * @device_type: Defines which devices should be taken into consideration + * (e.g. TYPE_DEVICE for all devices, or TYPE_PCI_DEVICE for PCI) + * + * Get an array of pointers to names of NIC devices that are available in + * the QEMU binary. The array is terminated with a NULL pointer entry. + * The caller is responsible for freeing the memory when it is not required + * anymore, e.g. with g_ptr_array_free(..., true). + * + * Returns: Pointer to the array that contains the pointers to the names. + */ +GPtrArray *qemu_get_nic_models(const char *device_type); + /* NIC info */ #define MAX_NICS 8 diff --git a/net/net.c b/net/net.c index 251fc5ab55..476a4b71cc 100644 --- a/net/net.c +++ b/net/net.c @@ -899,6 +899,40 @@ static int nic_get_free_idx(void) return -1; } +GPtrArray *qemu_get_nic_models(const char *device_type) +{ + GPtrArray *nic_models = g_ptr_array_new(); + GSList *list = object_class_get_list_sorted(device_type, false); + + while (list) { + DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data, + TYPE_DEVICE); + GSList *next; + if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) && + dc->user_creatable) { + const char *name = object_class_get_name(list->data); + /* + * A network device might also be something else than a NIC, see + * e.g. the "rocker" device. Thus we have to look for the "netdev" + * property, too. Unfortunately, some devices like virtio-net only + * create this property during instance_init, so we have to create + * a temporary instance here to be able to check it. + */ + Object *obj = object_new_with_class(OBJECT_CLASS(dc)); + if (object_property_find(obj, "netdev")) { + g_ptr_array_add(nic_models, (gpointer)name); + } + object_unref(obj); + } + next = list->next; + g_slist_free_1(list); + list = next; + } + g_ptr_array_add(nic_models, NULL); + + return nic_models; +} + int qemu_show_nic_models(const char *arg, const char *const *models) { int i; From 27c819244b8129a4742bfe43d255cdaa8528765d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 10 Nov 2022 13:52:23 +0100 Subject: [PATCH 2/9] net: Restore printing of the help text with "-nic help" Running QEMU with "-nic help" used to work in QEMU 5.2 and earlier versions (it showed the available netdev backends), but this feature got broken during some refactoring in version 6.0. Let's restore the old behavior, and while we're at it, let's also print the available NIC models here now since this option can be used to configure both, netdev backend and model in one go. Fixes: ad6f932fe8 ("net: do not exit on "netdev_add help" monitor command") Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- net/net.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/net.c b/net/net.c index 476a4b71cc..e8cd95cb7e 100644 --- a/net/net.c +++ b/net/net.c @@ -1542,8 +1542,18 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) const char *type; type = qemu_opt_get(opts, "type"); - if (type && g_str_equal(type, "none")) { - return 0; /* Nothing to do, default_net is cleared in vl.c */ + if (type) { + if (g_str_equal(type, "none")) { + return 0; /* Nothing to do, default_net is cleared in vl.c */ + } + if (is_help_option(type)) { + GPtrArray *nic_models = qemu_get_nic_models(TYPE_DEVICE); + show_netdevs(); + printf("\n"); + qemu_show_nic_models(type, (const char **)nic_models->pdata); + g_ptr_array_free(nic_models, true); + exit(0); + } } idx = nic_get_free_idx(); From 3b0cca8e4e674bda3457435208c3268767b6b085 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 10 Nov 2022 13:52:24 +0100 Subject: [PATCH 3/9] net: Replace "Supported NIC models" with "Available NIC models" Just because a NIC model is compiled into the QEMU binary does not necessary mean that it can be used with each and every machine. So let's rather talk about "available" models instead of "supported" models, just to avoid confusion. Reviewed-by: Claudio Fontana Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- net/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/net.c b/net/net.c index e8cd95cb7e..ebc7ce0231 100644 --- a/net/net.c +++ b/net/net.c @@ -941,7 +941,7 @@ int qemu_show_nic_models(const char *arg, const char *const *models) return 0; } - printf("Supported NIC models:\n"); + printf("Available NIC models:\n"); for (i = 0 ; models[i]; i++) { printf("%s\n", models[i]); } From 44c94cdb21cd1d1fb9aa6554585b94aa6de7ed9d Mon Sep 17 00:00:00 2001 From: Qiang Liu Date: Mon, 16 Jan 2023 11:14:31 +0800 Subject: [PATCH 4/9] hw/net/lan9118: log [read|write]b when mode_16bit is enabled rather than abort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch replaces hw_error to guest error log for [read|write]b accesses when mode_16bit is enabled. This avoids aborting qemu. Fixes: 1248f8d4cbc3 ("hw/lan9118: Add basic 16-bit mode support.") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1433 Reported-by: Qiang Liu Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Qiang Liu Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Jason Wang --- hw/net/lan9118.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index f1cba55967..e5c4af182d 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -15,7 +15,6 @@ #include "migration/vmstate.h" #include "net/net.h" #include "net/eth.h" -#include "hw/hw.h" #include "hw/irq.h" #include "hw/net/lan9118.h" #include "hw/ptimer.h" @@ -32,12 +31,8 @@ #ifdef DEBUG_LAN9118 #define DPRINTF(fmt, ...) \ do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { hw_error("lan9118: error: " fmt , ## __VA_ARGS__);} while (0) #else #define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0) #endif /* The tx and rx fifo ports are a range of aliased 32-bit registers */ @@ -848,7 +843,8 @@ static uint32_t do_phy_read(lan9118_state *s, int reg) case 30: /* Interrupt mask */ return s->phy_int_mask; default: - BADF("PHY read reg %d\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, + "do_phy_read: PHY read reg %d\n", reg); return 0; } } @@ -876,7 +872,8 @@ static void do_phy_write(lan9118_state *s, int reg, uint32_t val) phy_update_irq(s); break; default: - BADF("PHY write reg %d = 0x%04x\n", reg, val); + qemu_log_mask(LOG_GUEST_ERROR, + "do_phy_write: PHY write reg %d = 0x%04x\n", reg, val); } } @@ -1209,7 +1206,8 @@ static void lan9118_16bit_mode_write(void *opaque, hwaddr offset, return; } - hw_error("lan9118_write: Bad size 0x%x\n", size); + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118_16bit_mode_write: Bad size 0x%x\n", size); } static uint64_t lan9118_readl(void *opaque, hwaddr offset, @@ -1324,7 +1322,8 @@ static uint64_t lan9118_16bit_mode_read(void *opaque, hwaddr offset, return lan9118_readl(opaque, offset, size); } - hw_error("lan9118_read: Bad size 0x%x\n", size); + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118_16bit_mode_read: Bad size 0x%x\n", size); return 0; } From 099a63828130843741d317cb28e936f468b2b53b Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Thu, 25 Aug 2022 11:29:10 +0200 Subject: [PATCH 5/9] hw/net/vmxnet3: allow VMXNET3_MAX_MTU itself as a value Currently, VMXNET3_MAX_MTU itself (being 9000) is not considered a valid value for the MTU, but a guest running ESXi 7.0 might try to set it and fail the assert [0]. In the Linux kernel, dev->max_mtu itself is a valid value for the MTU and for the vmxnet3 driver it's 9000, so a guest running Linux will also fail the assert when trying to set an MTU of 9000. VMXNET3_MAX_MTU and s->mtu don't seem to be used in relation to buffer allocations/accesses, so allowing the upper limit itself as a value should be fine. [0]: https://forum.proxmox.com/threads/114011/ Fixes: d05dcd94ae ("net: vmxnet3: validate configuration values during activate (CVE-2021-20203)") Signed-off-by: Fiona Ebner Signed-off-by: Jason Wang --- hw/net/vmxnet3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index d2ab527ef4..56559cda24 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1441,7 +1441,7 @@ static void vmxnet3_activate_device(VMXNET3State *s) vmxnet3_setup_rx_filtering(s); /* Cache fields from shared memory */ s->mtu = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, devRead.misc.mtu); - assert(VMXNET3_MIN_MTU <= s->mtu && s->mtu < VMXNET3_MAX_MTU); + assert(VMXNET3_MIN_MTU <= s->mtu && s->mtu <= VMXNET3_MAX_MTU); VMW_CFPRN("MTU is %u", s->mtu); s->max_rx_frags = From 0c65ef4fbbf3d3c1c4435f06db7648ab67935a19 Mon Sep 17 00:00:00 2001 From: Christian Svensson Date: Fri, 30 Dec 2022 21:27:10 +0100 Subject: [PATCH 6/9] net: Increase L2TPv3 buffer to fit jumboframes Increase the allocated buffer size to fit larger packets. Given that jumboframes can commonly be up to 9000 bytes the closest suitable value seems to be 16 KiB. Tested by running qemu towards a Linux L2TPv3 endpoint and pushing jumboframe traffic through the interfaces. Signed-off-by: Christian Svensson Signed-off-by: Jason Wang --- net/l2tpv3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/l2tpv3.c b/net/l2tpv3.c index 53b2d32573..b5547cb917 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -42,7 +42,7 @@ */ #define BUFFER_ALIGN sysconf(_SC_PAGESIZE) -#define BUFFER_SIZE 2048 +#define BUFFER_SIZE 16384 #define IOVSIZE 2 #define MAX_L2TPV3_MSGCNT 64 #define MAX_L2TPV3_IOVCNT (MAX_L2TPV3_MSGCNT * IOVSIZE) From 993f71ee3360450c2758964adbdfb13f4d460162 Mon Sep 17 00:00:00 2001 From: Joelle van Dyne Date: Sun, 1 Jan 2023 17:08:21 -0800 Subject: [PATCH 7/9] vmnet: stop recieving events when VM is stopped When the VM is stopped using the HMP command "stop", soon the handler will stop reading from the vmnet interface. This causes a flood of `VMNET_INTERFACE_PACKETS_AVAILABLE` events to arrive and puts the host CPU at 100%. We fix this by removing the event handler from vmnet when the VM is no longer in a running state and restore it when we return to a running state. Signed-off-by: Joelle van Dyne Signed-off-by: Jason Wang --- net/vmnet-common.m | 48 +++++++++++++++++++++++++++++++++------------- net/vmnet_int.h | 2 ++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/net/vmnet-common.m b/net/vmnet-common.m index 2cb60b9ddd..2958283485 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -17,6 +17,7 @@ #include "clients.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "sysemu/runstate.h" #include #include @@ -242,6 +243,35 @@ static void vmnet_bufs_init(VmnetState *s) } } +/** + * Called on state change to un-register/re-register handlers + */ +static void vmnet_vm_state_change_cb(void *opaque, bool running, RunState state) +{ + VmnetState *s = opaque; + + if (running) { + vmnet_interface_set_event_callback( + s->vmnet_if, + VMNET_INTERFACE_PACKETS_AVAILABLE, + s->if_queue, + ^(interface_event_t event_id, xpc_object_t event) { + assert(event_id == VMNET_INTERFACE_PACKETS_AVAILABLE); + /* + * This function is being called from a non qemu thread, so + * we only schedule a BH, and do the rest of the io completion + * handling from vmnet_send_bh() which runs in a qemu context. + */ + qemu_bh_schedule(s->send_bh); + }); + } else { + vmnet_interface_set_event_callback( + s->vmnet_if, + VMNET_INTERFACE_PACKETS_AVAILABLE, + NULL, + NULL); + } +} int vmnet_if_create(NetClientState *nc, xpc_object_t if_desc, @@ -329,19 +359,9 @@ int vmnet_if_create(NetClientState *nc, s->packets_send_current_pos = 0; s->packets_send_end_pos = 0; - vmnet_interface_set_event_callback( - s->vmnet_if, - VMNET_INTERFACE_PACKETS_AVAILABLE, - s->if_queue, - ^(interface_event_t event_id, xpc_object_t event) { - assert(event_id == VMNET_INTERFACE_PACKETS_AVAILABLE); - /* - * This function is being called from a non qemu thread, so - * we only schedule a BH, and do the rest of the io completion - * handling from vmnet_send_bh() which runs in a qemu context. - */ - qemu_bh_schedule(s->send_bh); - }); + vmnet_vm_state_change_cb(s, 1, RUN_STATE_RUNNING); + + s->change = qemu_add_vm_change_state_handler(vmnet_vm_state_change_cb, s); return 0; } @@ -356,6 +376,8 @@ void vmnet_cleanup_common(NetClientState *nc) return; } + vmnet_vm_state_change_cb(s, 0, RUN_STATE_SHUTDOWN); + qemu_del_vm_change_state_handler(s->change); if_stopped_sem = dispatch_semaphore_create(0); vmnet_stop_interface( s->vmnet_if, diff --git a/net/vmnet_int.h b/net/vmnet_int.h index d0b90594f2..a8a033dc96 100644 --- a/net/vmnet_int.h +++ b/net/vmnet_int.h @@ -45,6 +45,8 @@ typedef struct VmnetState { int packets_send_end_pos; struct iovec iov_buf[VMNET_PACKETS_LIMIT]; + + VMChangeStateEntry *change; } VmnetState; const char *vmnet_status_map_str(vmnet_return_t status); From 148fbf0d58a6fa9c6881db28fced8c071c3be100 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 19 Jan 2023 11:16:45 +0100 Subject: [PATCH 8/9] net: stream: add a new option to automatically reconnect In stream mode, if the server shuts down there is currently no way to reconnect the client to a new server without removing the NIC device and the netdev backend (or to reboot). This patch introduces a reconnect option that specifies a delay to try to reconnect with the same parameters. Add a new test in qtest to test the reconnect option and the connect/disconnect events. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- net/stream.c | 53 ++++++++++++++++++- qapi/net.json | 7 ++- qemu-options.hx | 6 +-- tests/qtest/netdev-socket.c | 101 ++++++++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+), 5 deletions(-) diff --git a/net/stream.c b/net/stream.c index 37ff727e0c..9204b4c96e 100644 --- a/net/stream.c +++ b/net/stream.c @@ -39,6 +39,8 @@ #include "io/channel-socket.h" #include "io/net-listener.h" #include "qapi/qapi-events-net.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/clone-visitor.h" typedef struct NetStreamState { NetClientState nc; @@ -49,11 +51,15 @@ typedef struct NetStreamState { guint ioc_write_tag; SocketReadState rs; unsigned int send_index; /* number of bytes sent*/ + uint32_t reconnect; + guint timer_tag; + SocketAddress *addr; } NetStreamState; static void net_stream_listen(QIONetListener *listener, QIOChannelSocket *cioc, void *opaque); +static void net_stream_arm_reconnect(NetStreamState *s); static gboolean net_stream_writable(QIOChannel *ioc, GIOCondition condition, @@ -170,6 +176,7 @@ static gboolean net_stream_send(QIOChannel *ioc, qemu_set_info_str(&s->nc, "%s", ""); qapi_event_send_netdev_stream_disconnected(s->nc.name); + net_stream_arm_reconnect(s); return G_SOURCE_REMOVE; } @@ -187,6 +194,14 @@ static gboolean net_stream_send(QIOChannel *ioc, static void net_stream_cleanup(NetClientState *nc) { NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); + if (s->timer_tag) { + g_source_remove(s->timer_tag); + s->timer_tag = 0; + } + if (s->addr) { + qapi_free_SocketAddress(s->addr); + s->addr = NULL; + } if (s->ioc) { if (QIO_CHANNEL_SOCKET(s->ioc)->fd != -1) { if (s->ioc_read_tag) { @@ -346,12 +361,37 @@ static void net_stream_client_connected(QIOTask *task, gpointer opaque) error: object_unref(OBJECT(s->ioc)); s->ioc = NULL; + net_stream_arm_reconnect(s); +} + +static gboolean net_stream_reconnect(gpointer data) +{ + NetStreamState *s = data; + QIOChannelSocket *sioc; + + s->timer_tag = 0; + + sioc = qio_channel_socket_new(); + s->ioc = QIO_CHANNEL(sioc); + qio_channel_socket_connect_async(sioc, s->addr, + net_stream_client_connected, s, + NULL, NULL); + return G_SOURCE_REMOVE; +} + +static void net_stream_arm_reconnect(NetStreamState *s) +{ + if (s->reconnect && s->timer_tag == 0) { + s->timer_tag = g_timeout_add_seconds(s->reconnect, + net_stream_reconnect, s); + } } static int net_stream_client_init(NetClientState *peer, const char *model, const char *name, SocketAddress *addr, + uint32_t reconnect, Error **errp) { NetStreamState *s; @@ -364,6 +404,10 @@ static int net_stream_client_init(NetClientState *peer, s->ioc = QIO_CHANNEL(sioc); s->nc.link_down = true; + s->reconnect = reconnect; + if (reconnect) { + s->addr = QAPI_CLONE(SocketAddress, addr); + } qio_channel_socket_connect_async(sioc, addr, net_stream_client_connected, s, NULL, NULL); @@ -380,7 +424,14 @@ int net_init_stream(const Netdev *netdev, const char *name, sock = &netdev->u.stream; if (!sock->has_server || !sock->server) { - return net_stream_client_init(peer, "stream", name, sock->addr, errp); + return net_stream_client_init(peer, "stream", name, sock->addr, + sock->has_reconnect ? sock->reconnect : 0, + errp); + } + if (sock->has_reconnect) { + error_setg(errp, "'reconnect' option is incompatible with " + "socket in server mode"); + return -1; } return net_stream_server_init(peer, "stream", name, sock->addr, errp); } diff --git a/qapi/net.json b/qapi/net.json index 522ac582ed..d6eb30008b 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -585,6 +585,10 @@ # @addr: socket address to listen on (server=true) # or connect to (server=false) # @server: create server socket (default: false) +# @reconnect: For a client socket, if a socket is disconnected, +# then attempt a reconnect after the given number of seconds. +# Setting this to zero disables this function. (default: 0) +# (since 8.0) # # Only SocketAddress types 'unix', 'inet' and 'fd' are supported. # @@ -593,7 +597,8 @@ { 'struct': 'NetdevStreamOptions', 'data': { 'addr': 'SocketAddress', - '*server': 'bool' } } + '*server': 'bool', + '*reconnect': 'uint32' } } ## # @NetdevDgramOptions: diff --git a/qemu-options.hx b/qemu-options.hx index cafd8be8ed..beeb4475ba 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2762,9 +2762,9 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev socket,id=str[,fd=h][,udp=host:port][,localaddr=host:port]\n" " configure a network backend to connect to another network\n" " using an UDP tunnel\n" - "-netdev stream,id=str[,server=on|off],addr.type=inet,addr.host=host,addr.port=port[,to=maxport][,numeric=on|off][,keep-alive=on|off][,mptcp=on|off][,addr.ipv4=on|off][,addr.ipv6=on|off]\n" - "-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off]\n" - "-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor\n" + "-netdev stream,id=str[,server=on|off],addr.type=inet,addr.host=host,addr.port=port[,to=maxport][,numeric=on|off][,keep-alive=on|off][,mptcp=on|off][,addr.ipv4=on|off][,addr.ipv6=on|off][,reconnect=seconds]\n" + "-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off][,reconnect=seconds]\n" + "-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor[,reconnect=seconds]\n" " configure a network backend to connect to another network\n" " using a socket connection in stream mode.\n" "-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=inet,local.host=addr]\n" diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c index 1d98dca821..270e424bee 100644 --- a/tests/qtest/netdev-socket.c +++ b/tests/qtest/netdev-socket.c @@ -11,6 +11,10 @@ #include #include "../unit/socket-helpers.h" #include "libqtest.h" +#include "qapi/qmp/qstring.h" +#include "qemu/sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-sockets.h" #define CONNECTION_TIMEOUT 60 @@ -142,6 +146,101 @@ static void test_stream_inet_ipv4(void) qtest_quit(qts0); } +static void wait_stream_connected(QTestState *qts, const char *id, + SocketAddress **addr) +{ + QDict *resp, *data; + QString *qstr; + QObject *obj; + Visitor *v = NULL; + + resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_CONNECTED"); + g_assert_nonnull(resp); + data = qdict_get_qdict(resp, "data"); + g_assert_nonnull(data); + + qstr = qobject_to(QString, qdict_get(data, "netdev-id")); + g_assert_nonnull(data); + + g_assert(!strcmp(qstring_get_str(qstr), id)); + + obj = qdict_get(data, "addr"); + + v = qobject_input_visitor_new(obj); + visit_type_SocketAddress(v, NULL, addr, NULL); + visit_free(v); + qobject_unref(resp); +} + +static void wait_stream_disconnected(QTestState *qts, const char *id) +{ + QDict *resp, *data; + QString *qstr; + + resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_DISCONNECTED"); + g_assert_nonnull(resp); + data = qdict_get_qdict(resp, "data"); + g_assert_nonnull(data); + + qstr = qobject_to(QString, qdict_get(data, "netdev-id")); + g_assert_nonnull(data); + + g_assert(!strcmp(qstring_get_str(qstr), id)); + qobject_unref(resp); +} + +static void test_stream_inet_reconnect(void) +{ + QTestState *qts0, *qts1; + int port; + SocketAddress *addr; + + port = inet_get_free_port(false); + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true,addr.type=inet," + "addr.ipv4=on,addr.ipv6=off," + "addr.host=127.0.0.1,addr.port=%d", port); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,server=false,id=st0,addr.type=inet," + "addr.ipv4=on,addr.ipv6=off,reconnect=1," + "addr.host=127.0.0.1,addr.port=%d", port); + + wait_stream_connected(qts0, "st0", &addr); + g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_INET); + g_assert_cmpstr(addr->u.inet.host, ==, "127.0.0.1"); + qapi_free_SocketAddress(addr); + + /* kill server */ + qtest_quit(qts0); + + /* check client has been disconnected */ + wait_stream_disconnected(qts1, "st0"); + + /* restart server */ + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true,addr.type=inet," + "addr.ipv4=on,addr.ipv6=off," + "addr.host=127.0.0.1,addr.port=%d", port); + + /* wait connection events*/ + wait_stream_connected(qts0, "st0", &addr); + g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_INET); + g_assert_cmpstr(addr->u.inet.host, ==, "127.0.0.1"); + qapi_free_SocketAddress(addr); + + wait_stream_connected(qts1, "st0", &addr); + g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_INET); + g_assert_cmpstr(addr->u.inet.host, ==, "127.0.0.1"); + g_assert_cmpint(atoi(addr->u.inet.port), ==, port); + qapi_free_SocketAddress(addr); + + qtest_quit(qts1); + qtest_quit(qts0); +} + static void test_stream_inet_ipv6(void) { QTestState *qts0, *qts1; @@ -418,6 +517,8 @@ int main(int argc, char **argv) #ifndef _WIN32 qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast); #endif + qtest_add_func("/netdev/stream/inet/reconnect", + test_stream_inet_reconnect); } if (has_ipv6) { qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6); From 525ae115222f0b0b6de7f9665976f640d18c200a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Tue, 17 Jan 2023 11:53:08 +0100 Subject: [PATCH 9/9] vdpa: fix VHOST_BACKEND_F_IOTLB_ASID flag check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VHOST_BACKEND_F_IOTLB_ASID is the feature bit, not the bitmask. Since the device under test also provided VHOST_BACKEND_F_IOTLB_MSG_V2 and VHOST_BACKEND_F_IOTLB_BATCH, this went unnoticed. Fixes: c1a1008685 ("vdpa: always start CVQ in SVQ mode if possible") Signed-off-by: Eugenio Pérez Reviewed-by: Michael S. Tsirkin Acked-by: Jason Wang Signed-off-by: Jason Wang --- net/vhost-vdpa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 1a13a34d35..de5ed8ff22 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -384,7 +384,7 @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc) g_strerror(errno), errno); return -1; } - if (!(backend_features & VHOST_BACKEND_F_IOTLB_ASID) || + if (!(backend_features & BIT_ULL(VHOST_BACKEND_F_IOTLB_ASID)) || !vhost_vdpa_net_valid_svq_features(v->dev->features, NULL)) { return 0; }