From: "Michael S. Tsirkin" <mst@redhat.com>
To: zuoboqun <zuoboqun@baidu.com>
Cc: qemu-devel <qemu-devel@nongnu.org>,
Stefano Garzarella <sgarzare@redhat.com>,
Jason Wang <jasowang@redhat.com>
Subject: Re: [PATCH] vhost_net: configure all host notifiers in a single MR transaction
Date: Fri, 16 Aug 2024 03:36:13 -0400 [thread overview]
Message-ID: <20240816033545-mutt-send-email-mst@kernel.org> (raw)
In-Reply-To: <20240816070835.8309-1-zuoboqun@baidu.com>
On Fri, Aug 16, 2024 at 03:08:35PM +0800, zuoboqun wrote:
> This allows the vhost_net device which has multiple virtqueues to batch
> the setup of all its host notifiers. This significantly reduces the
> vhost_net device starting and stoping time, e.g. the time spend
> on enabling notifiers reduce from 630ms to 75ms and the time spend on
> disabling notifiers reduce from 441ms to 45ms for a VM with 192 vCPUs
> and 15 vhost-user-net devices (64vq per device) in our case.
>
> Signed-off-by: zuoboqun <zuoboqun@baidu.com>
Looks good, tagged for past the release.
> ---
> hw/net/vhost_net.c | 155 +++++++++++++++++++++++++++++++++++---
> hw/virtio/vhost.c | 6 +-
> include/hw/virtio/vhost.h | 4 +
> 3 files changed, 150 insertions(+), 15 deletions(-)
>
> diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
> index a788e6937e..28a9aca1a7 100644
> --- a/hw/net/vhost_net.c
> +++ b/hw/net/vhost_net.c
> @@ -160,6 +160,135 @@ void vhost_net_save_acked_features(NetClientState *nc)
> #endif
> }
>
> +static void vhost_net_disable_notifiers_nvhosts(VirtIODevice *dev,
> + NetClientState *ncs, int data_queue_pairs, int nvhosts)
> +{
> + VirtIONet *n = VIRTIO_NET(dev);
> + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
> + struct vhost_net *net;
> + struct vhost_dev *hdev;
> + int r, i, j;
> + NetClientState *peer;
> +
> + /*
> + * Batch all the host notifiers in a single transaction to avoid
> + * quadratic time complexity in address_space_update_ioeventfds().
> + */
> + memory_region_transaction_begin();
> +
> + for (i = 0; i < nvhosts; i++) {
> + if (i < data_queue_pairs) {
> + peer = qemu_get_peer(ncs, i);
> + } else {
> + peer = qemu_get_peer(ncs, n->max_queue_pairs);
> + }
> +
> + net = get_vhost_net(peer);
> + hdev = &net->dev;
> + for (j = 0; j < hdev->nvqs; j++) {
> + r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus),
> + hdev->vq_index + j,
> + false);
> + if (r < 0) {
> + error_report("vhost %d VQ %d notifier cleanup failed: %d",
> + i, j, -r);
> + }
> + assert(r >= 0);
> + }
> + }
> + /*
> + * The transaction expects the ioeventfds to be open when it
> + * commits. Do it now, before the cleanup loop.
> + */
> + memory_region_transaction_commit();
> +
> + for (i = 0; i < nvhosts; i++) {
> + if (i < data_queue_pairs) {
> + peer = qemu_get_peer(ncs, i);
> + } else {
> + peer = qemu_get_peer(ncs, n->max_queue_pairs);
> + }
> +
> + net = get_vhost_net(peer);
> + hdev = &net->dev;
> + for (j = 0; j < hdev->nvqs; j++) {
> + virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus),
> + hdev->vq_index + j);
> + }
> + virtio_device_release_ioeventfd(dev);
> + }
> +}
> +
> +static int vhost_net_enable_notifiers(VirtIODevice *dev,
> + NetClientState *ncs, int data_queue_pairs, int cvq)
> +{
> + VirtIONet *n = VIRTIO_NET(dev);
> + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
> + int nvhosts = data_queue_pairs + cvq;
> + struct vhost_net *net;
> + struct vhost_dev *hdev;
> + int r, i, j;
> + NetClientState *peer;
> +
> + /*
> + * Batch all the host notifiers in a single transaction to avoid
> + * quadratic time complexity in address_space_update_ioeventfds().
> + */
> + memory_region_transaction_begin();
> +
> + for (i = 0; i < nvhosts; i++) {
> + if (i < data_queue_pairs) {
> + peer = qemu_get_peer(ncs, i);
> + } else {
> + peer = qemu_get_peer(ncs, n->max_queue_pairs);
> + }
> +
> + net = get_vhost_net(peer);
> + hdev = &net->dev;
> + /*
> + * We will pass the notifiers to the kernel, make sure that QEMU
> + * doesn't interfere.
> + */
> + r = virtio_device_grab_ioeventfd(dev);
> + if (r < 0) {
> + error_report("binding does not support host notifiers");
> + memory_region_transaction_commit();
> + goto fail_nvhosts;
> + }
> +
> + for (j = 0; j < hdev->nvqs; j++) {
> + r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus),
> + hdev->vq_index + j,
> + true);
> + if (r < 0) {
> + error_report("vhost %d VQ %d notifier binding failed: %d",
> + i, j, -r);
> + memory_region_transaction_commit();
> + vhost_dev_disable_notifiers_nvqs(hdev, dev, j);
> + goto fail_nvhosts;
> + }
> + }
> + }
> +
> + memory_region_transaction_commit();
> +
> + return 0;
> +fail_nvhosts:
> + vhost_net_disable_notifiers_nvhosts(dev, ncs, data_queue_pairs, i);
> + return r;
> +}
> +
> +/*
> + * Stop processing guest IO notifications in qemu.
> + * Start processing them in vhost in kernel.
> + */
> +static void vhost_net_disable_notifiers(VirtIODevice *dev,
> + NetClientState *ncs, int data_queue_pairs, int cvq)
> +{
> + vhost_net_disable_notifiers_nvhosts(dev, ncs, data_queue_pairs,
> + data_queue_pairs + cvq);
> +}
> +
> static int vhost_net_get_fd(NetClientState *backend)
> {
> switch (backend->info->type) {
> @@ -270,11 +399,6 @@ static int vhost_net_start_one(struct vhost_net *net,
> }
> }
>
> - r = vhost_dev_enable_notifiers(&net->dev, dev);
> - if (r < 0) {
> - goto fail_notifiers;
> - }
> -
> r = vhost_dev_start(&net->dev, dev, false);
> if (r < 0) {
> goto fail_start;
> @@ -326,8 +450,6 @@ fail:
> }
> vhost_dev_stop(&net->dev, dev, false);
> fail_start:
> - vhost_dev_disable_notifiers(&net->dev, dev);
> -fail_notifiers:
> return r;
> }
>
> @@ -349,7 +471,6 @@ static void vhost_net_stop_one(struct vhost_net *net,
> if (net->nc->info->stop) {
> net->nc->info->stop(net->nc);
> }
> - vhost_dev_disable_notifiers(&net->dev, dev);
> }
>
> int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
> @@ -394,10 +515,16 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
> }
> }
>
> + r = vhost_net_enable_notifiers(dev, ncs, data_queue_pairs, cvq);
> + if (r < 0) {
> + error_report("Error enabling host notifiers: %d", -r);
> + goto err;
> + }
> +
> r = k->set_guest_notifiers(qbus->parent, total_notifiers, true);
> if (r < 0) {
> error_report("Error binding guest notifier: %d", -r);
> - goto err;
> + goto err_host_notifiers;
> }
>
> for (i = 0; i < nvhosts; i++) {
> @@ -412,19 +539,19 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
> r = vhost_set_vring_enable(peer, peer->vring_enable);
>
> if (r < 0) {
> - goto err_start;
> + goto err_guest_notifiers;
> }
> }
>
> r = vhost_net_start_one(get_vhost_net(peer), dev);
> if (r < 0) {
> - goto err_start;
> + goto err_guest_notifiers;
> }
> }
>
> return 0;
>
> -err_start:
> +err_guest_notifiers:
> while (--i >= 0) {
> peer = qemu_get_peer(ncs, i < data_queue_pairs ?
> i : n->max_queue_pairs);
> @@ -435,6 +562,8 @@ err_start:
> fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e);
> fflush(stderr);
> }
> +err_host_notifiers:
> + vhost_net_disable_notifiers(dev, ncs, data_queue_pairs, cvq);
> err:
> return r;
> }
> @@ -466,6 +595,8 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs,
> fflush(stderr);
> }
> assert(r >= 0);
> +
> + vhost_net_disable_notifiers(dev, ncs, data_queue_pairs, cvq);
> }
>
> void vhost_net_cleanup(struct vhost_net *net)
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index 06fc71746e..7c5ef81b55 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -1682,9 +1682,9 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
> memset(hdev, 0, sizeof(struct vhost_dev));
> }
>
> -static void vhost_dev_disable_notifiers_nvqs(struct vhost_dev *hdev,
> - VirtIODevice *vdev,
> - unsigned int nvqs)
> +void vhost_dev_disable_notifiers_nvqs(struct vhost_dev *hdev,
> + VirtIODevice *vdev,
> + unsigned int nvqs)
> {
> BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
> int i, r;
> diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> index d75faf46e9..c75be46c06 100644
> --- a/include/hw/virtio/vhost.h
> +++ b/include/hw/virtio/vhost.h
> @@ -171,6 +171,10 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
> */
> void vhost_dev_cleanup(struct vhost_dev *hdev);
>
> +void vhost_dev_disable_notifiers_nvqs(struct vhost_dev *hdev,
> + VirtIODevice *vdev,
> + unsigned int nvqs);
> +
> /**
> * vhost_dev_enable_notifiers() - enable event notifiers
> * @hdev: common vhost_dev structure
> --
> 2.42.0.windows.2
prev parent reply other threads:[~2024-08-16 7:36 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-08-16 7:08 [PATCH] vhost_net: configure all host notifiers in a single MR transaction zuoboqun via
2024-08-16 7:36 ` Michael S. Tsirkin [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240816033545-mutt-send-email-mst@kernel.org \
--to=mst@redhat.com \
--cc=jasowang@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=sgarzare@redhat.com \
--cc=zuoboqun@baidu.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).