From adda0ad56bd28d5a809051cbd190fda5798ec4e4 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 30 May 2025 14:18:53 +0900 Subject: virtio-net: Add queues for RSS during migration virtio_net_pre_load_queues() inspects vdev->guest_features to tell if VIRTIO_NET_F_RSS or VIRTIO_NET_F_MQ is enabled to infer the required number of queues. This works for VIRTIO_NET_F_MQ but it doesn't for VIRTIO_NET_F_RSS because only the lowest 32 bits of vdev->guest_features is set at the point and VIRTIO_NET_F_RSS uses bit 60 while VIRTIO_NET_F_MQ uses bit 22. Instead of inferring the required number of queues from vdev->guest_features, use the number loaded from the vm state. This change also has a nice side effect to remove a duplicate peer queue pair change by circumventing virtio_net_set_multiqueue(). Also update the comment in include/hw/virtio/virtio.h to prevent an implementation of pre_load_queues() from refering to any fields being loaded during migration by accident in the future. Fixes: 8c49756825da ("virtio-net: Add only one queue pair when realizing") Tested-by: Lei Yang Cc: qemu-stable@nongnu.org Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'hw/net/virtio-net.c') diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index eb93607b8c..351377c025 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3022,11 +3022,10 @@ static void virtio_net_del_queue(VirtIONet *n, int index) virtio_del_queue(vdev, index * 2 + 1); } -static void virtio_net_change_num_queue_pairs(VirtIONet *n, int new_max_queue_pairs) +static void virtio_net_change_num_queues(VirtIONet *n, int new_num_queues) { VirtIODevice *vdev = VIRTIO_DEVICE(n); int old_num_queues = virtio_get_num_queues(vdev); - int new_num_queues = new_max_queue_pairs * 2 + 1; int i; assert(old_num_queues >= 3); @@ -3062,16 +3061,14 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) int max = multiqueue ? n->max_queue_pairs : 1; n->multiqueue = multiqueue; - virtio_net_change_num_queue_pairs(n, max); + virtio_net_change_num_queues(n, max * 2 + 1); virtio_net_set_queue_pairs(n); } -static int virtio_net_pre_load_queues(VirtIODevice *vdev) +static int virtio_net_pre_load_queues(VirtIODevice *vdev, uint32_t n) { - virtio_net_set_multiqueue(VIRTIO_NET(vdev), - virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_RSS) || - virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MQ)); + virtio_net_change_num_queues(VIRTIO_NET(vdev), n); return 0; } -- cgit 1.4.1 From 7136352b40631b058dd0fe731a0d404e761e799f Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:17 +0200 Subject: vhost_net: Rename vhost_set_vring_enable() for clarity This is a cosmetic change with no functional impact. The function vhost_set_vring_enable() is specific to vhost_net and is used outside of vhost_net.c (specifically, in hw/net/virtio-net.c). To prevent confusion with other similarly named vhost functions, such as the one found in cryptodev-vhost.c, it has been renamed to vhost_net_set_vring_enable(). This clarifies that the function belongs to the vhost_net module. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net-stub.c | 2 +- hw/net/vhost_net.c | 4 ++-- hw/net/virtio-net.c | 4 ++-- include/net/vhost_net.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'hw/net/virtio-net.c') diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index 72df6d757e..7bed0bf92b 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -101,7 +101,7 @@ VHostNetState *get_vhost_net(NetClientState *nc) return 0; } -int vhost_set_vring_enable(NetClientState *nc, int enable) +int vhost_net_set_vring_enable(NetClientState *nc, int enable) { return 0; } diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 891f235a0a..cb87056397 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -551,7 +551,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, if (peer->vring_enable) { /* restore vring enable state */ - r = vhost_set_vring_enable(peer, peer->vring_enable); + r = vhost_net_set_vring_enable(peer, peer->vring_enable); if (r < 0) { goto err_guest_notifiers; @@ -686,7 +686,7 @@ VHostNetState *get_vhost_net(NetClientState *nc) return vhost_net; } -int vhost_set_vring_enable(NetClientState *nc, int enable) +int vhost_net_set_vring_enable(NetClientState *nc, int enable) { VHostNetState *net = get_vhost_net(nc); const VhostOps *vhost_ops = net->dev.vhost_ops; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 351377c025..e3400f18c8 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -697,7 +697,7 @@ static int peer_attach(VirtIONet *n, int index) } if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - vhost_set_vring_enable(nc->peer, 1); + vhost_net_set_vring_enable(nc->peer, 1); } if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { @@ -720,7 +720,7 @@ static int peer_detach(VirtIONet *n, int index) } if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - vhost_set_vring_enable(nc->peer, 0); + vhost_net_set_vring_enable(nc->peer, 0); } if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index c6a5361a2a..0f40049f34 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -41,7 +41,7 @@ void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask); int vhost_net_notify_migration_done(VHostNetState *net, char* mac_addr); VHostNetState *get_vhost_net(NetClientState *nc); -int vhost_set_vring_enable(NetClientState * nc, int enable); +int vhost_net_set_vring_enable(NetClientState *nc, int enable); uint64_t vhost_net_get_acked_features(VHostNetState *net); -- cgit 1.4.1 From 33b78a30a3e8e1cf16ef423bf2e78caf3d560985 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:22 +0200 Subject: net: Allow network backends to advertise max TX queue size This commit refactors how the maximum transmit queue size for virtio-net devices is determined, making the mechanism more generic and extensible. Previously, virtio_net_max_tx_queue_size() contained hardcoded checks for specific network backend types (vhost-user and vhost-vdpa) to determine their supported maximum queue size. This created direct dependencies and would require modifications for every new backend that supports variable queue sizes. To improve flexibility, a new max_tx_queue_size field is added to the vhost_net structure. This allows each network backend to advertise its supported maximum transmit queue size directly. The virtio_net_max_tx_queue_size() function now retrieves the max TX queue size from the vhost_net struct, if available and set. Otherwise, it defaults to VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net.c | 1 + hw/net/virtio-net.c | 24 ++++++++++++------------ include/hw/virtio/vhost.h | 1 + include/net/vhost_net.h | 1 + net/tap.c | 1 + net/vhost-user.c | 1 + net/vhost-vdpa.c | 1 + 7 files changed, 18 insertions(+), 12 deletions(-) (limited to 'hw/net/virtio-net.c') diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 976d2b315a..74d2e3ed90 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -245,6 +245,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->dev.nvqs = options->nvqs; net->feature_bits = options->feature_bits; net->save_acked_features = options->save_acked_features; + net->max_tx_queue_size = options->max_tx_queue_size; net->dev.max_queues = 1; net->dev.vqs = net->vqs; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index e3400f18c8..39fc280839 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -670,22 +670,22 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, static int virtio_net_max_tx_queue_size(VirtIONet *n) { NetClientState *peer = n->nic_conf.peers.ncs[0]; + struct vhost_net *net; - /* - * Backends other than vhost-user or vhost-vdpa don't support max queue - * size. - */ if (!peer) { - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + goto default_value; } - switch(peer->info->type) { - case NET_CLIENT_DRIVER_VHOST_USER: - case NET_CLIENT_DRIVER_VHOST_VDPA: - return VIRTQUEUE_MAX_SIZE; - default: - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; - }; + net = get_vhost_net(peer); + + if (!net || !net->max_tx_queue_size) { + goto default_value; + } + + return net->max_tx_queue_size; + +default_value: + return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; } static int peer_attach(VirtIONet *n, int index) diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index b0830bac79..a62992c819 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -145,6 +145,7 @@ struct vhost_net { struct vhost_virtqueue vqs[2]; int backend; const int *feature_bits; + int max_tx_queue_size; SaveAcketFeatures *save_acked_features; NetClientState *nc; }; diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index eb26ed9bdc..8f4fddfb69 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -16,6 +16,7 @@ typedef struct VhostNetOptions { uint32_t busyloop_timeout; unsigned int nvqs; const int *feature_bits; + int max_tx_queue_size; GetAckedFeatures *get_acked_features; SaveAcketFeatures *save_acked_features; void *opaque; diff --git a/net/tap.c b/net/tap.c index 79fa02a65c..2f0cb55c9a 100644 --- a/net/tap.c +++ b/net/tap.c @@ -746,6 +746,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.feature_bits = kernel_feature_bits; options.get_acked_features = NULL; options.save_acked_features = NULL; + options.max_tx_queue_size = 0; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { diff --git a/net/vhost-user.c b/net/vhost-user.c index 8a3df27b02..bf892915de 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -138,6 +138,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], options.busyloop_timeout = 0; options.nvqs = 2; options.feature_bits = user_feature_bits; + options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; options.get_acked_features = vhost_user_get_acked_features; options.save_acked_features = vhost_user_save_acked_features; diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index c63225d3d2..353392b3d7 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -204,6 +204,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, options.feature_bits = vdpa_feature_bits; options.get_acked_features = NULL; options.save_acked_features = NULL; + options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; net = vhost_net_init(&options); if (!net) { -- cgit 1.4.1 From ba5acc5d6e0bc9ab86619c92cb76493aa197d7a2 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:23 +0200 Subject: net: Add is_vhost_user flag to vhost_net struct Introduce a boolean is_vhost_user field to the vhost_net structure. This flag is initialized during vhost_net_init based on whether the backend is vhost-user. This refactoring simplifies checks for vhost-user specific behavior, replacing direct comparisons of 'net->nc->info->type' with the new flag. It improves readability and encapsulates the backend type information directly within the vhost_net instance. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net.c | 3 ++- hw/net/virtio-net.c | 8 ++++++-- include/hw/virtio/vhost.h | 1 + include/net/vhost_net.h | 1 + net/tap.c | 1 + net/vhost-user.c | 1 + net/vhost-vdpa.c | 1 + 7 files changed, 13 insertions(+), 3 deletions(-) (limited to 'hw/net/virtio-net.c') diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 74d2e3ed90..540492b37d 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -246,6 +246,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->feature_bits = options->feature_bits; net->save_acked_features = options->save_acked_features; net->max_tx_queue_size = options->max_tx_queue_size; + net->is_vhost_user = options->is_vhost_user; net->dev.max_queues = 1; net->dev.vqs = net->vqs; @@ -440,7 +441,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, * because vhost user doesn't interrupt masking/unmasking * properly. */ - if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + if (net->is_vhost_user) { dev->use_guest_notifier_mask = false; } } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 39fc280839..00df5fd6cd 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -691,12 +691,14 @@ default_value: static int peer_attach(VirtIONet *n, int index) { NetClientState *nc = qemu_get_subqueue(n->nic, index); + struct vhost_net *net; if (!nc->peer) { return 0; } - if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + net = get_vhost_net(nc->peer); + if (net && net->is_vhost_user) { vhost_net_set_vring_enable(nc->peer, 1); } @@ -714,12 +716,14 @@ static int peer_attach(VirtIONet *n, int index) static int peer_detach(VirtIONet *n, int index) { NetClientState *nc = qemu_get_subqueue(n->nic, index); + struct vhost_net *net; if (!nc->peer) { return 0; } - if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + net = get_vhost_net(nc->peer); + if (net && net->is_vhost_user) { vhost_net_set_vring_enable(nc->peer, 0); } diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index a62992c819..f178cf9e1d 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -147,6 +147,7 @@ struct vhost_net { const int *feature_bits; int max_tx_queue_size; SaveAcketFeatures *save_acked_features; + bool is_vhost_user; NetClientState *nc; }; diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 8f4fddfb69..879781dad7 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -17,6 +17,7 @@ typedef struct VhostNetOptions { unsigned int nvqs; const int *feature_bits; int max_tx_queue_size; + bool is_vhost_user; GetAckedFeatures *get_acked_features; SaveAcketFeatures *save_acked_features; void *opaque; diff --git a/net/tap.c b/net/tap.c index 2f0cb55c9a..23536c09b4 100644 --- a/net/tap.c +++ b/net/tap.c @@ -747,6 +747,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.get_acked_features = NULL; options.save_acked_features = NULL; options.max_tx_queue_size = 0; + options.is_vhost_user = false; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { diff --git a/net/vhost-user.c b/net/vhost-user.c index bf892915de..1c3b8b36f3 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -141,6 +141,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; options.get_acked_features = vhost_user_get_acked_features; options.save_acked_features = vhost_user_save_acked_features; + options.is_vhost_user = true; net = vhost_net_init(&options); if (!net) { diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 353392b3d7..943e9c585c 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -205,6 +205,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, options.get_acked_features = NULL; options.save_acked_features = NULL; options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; + options.is_vhost_user = false; net = vhost_net_init(&options); if (!net) { -- cgit 1.4.1