qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] Send all the SVQ control commands in parallel
@ 2023-05-06 14:06 Hawkins Jiawei
  2023-05-06 14:06 ` [PATCH v2 1/2] vdpa: rename vhost_vdpa_net_cvq_add() Hawkins Jiawei
  2023-05-06 14:06 ` [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel Hawkins Jiawei
  0 siblings, 2 replies; 12+ messages in thread
From: Hawkins Jiawei @ 2023-05-06 14:06 UTC (permalink / raw)
  To: eperezma, jasowang; +Cc: yin31149, 18801353760, qemu-devel

This patchset allows QEMU to poll and check the device used buffer
after sending all SVQ control commands, instead of polling and checking
immediately after sending each SVQ control command, so that QEMU can
send all the SVQ control commands in parallel, which have better
performance improvement.

I use vdpa_sim_net to simulate vdpa device, refactor
vhost_vdpa_net_load() to call vhost_vdpa_net_load_mac() 30 times,
to build a test environment for sending multiple SVQ control commands.
The monotonic time to finish vhost_vdpa_net_load() is as follows:

    QEMU                            microseconds
--------------------------------------------------
not patched                              85.092
--------------------------------------------------
patched                                  79.222

So this is a save of (85.092 - 79.222)/30 = 0.2 ms per command.

This patchset resolves the GitLab issue at
https://gitlab.com/qemu-project/qemu/-/issues/1578.

v2:
  - recover accidentally deleted rows
  - remove extra newline
  - refactor `need_poll_len` to `cmds_in_flight`
  - return -EINVAL when vhost_svq_poll() return 0 or check
on buffers written by device fails
  - change the type of `in_cursor`, and refactor the
code for updating cursor
  - return directly when vhost_vdpa_net_load_{mac,mq}()
returns a failure in vhost_vdpa_net_load()

v1: https://lists.nongnu.org/archive/html/qemu-devel/2023-04/msg02668.html

Hawkins Jiawei (2):
  vdpa: rename vhost_vdpa_net_cvq_add()
  vdpa: send CVQ state load commands in parallel

 net/vhost-vdpa.c | 165 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 130 insertions(+), 35 deletions(-)

-- 
2.25.1



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

* [PATCH v2 1/2] vdpa: rename vhost_vdpa_net_cvq_add()
  2023-05-06 14:06 [PATCH v2 0/2] Send all the SVQ control commands in parallel Hawkins Jiawei
@ 2023-05-06 14:06 ` Hawkins Jiawei
  2023-05-17  4:12   ` Jason Wang
  2023-05-06 14:06 ` [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel Hawkins Jiawei
  1 sibling, 1 reply; 12+ messages in thread
From: Hawkins Jiawei @ 2023-05-06 14:06 UTC (permalink / raw)
  To: eperezma, jasowang; +Cc: yin31149, 18801353760, qemu-devel

We want to introduce a new version of vhost_vdpa_net_cvq_add() that
does not poll immediately after forwarding custom buffers
to the device, so that QEMU can send all the SVQ control commands
in parallel instead of serialized.

Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
---
 net/vhost-vdpa.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
index 99904a0da7..10804c7200 100644
--- a/net/vhost-vdpa.c
+++ b/net/vhost-vdpa.c
@@ -590,8 +590,14 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
     vhost_vdpa_net_client_stop(nc);
 }
 
-static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s, size_t out_len,
-                                      size_t in_len)
+/**
+ * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
+ * kicks the device and polls the device used buffers.
+ *
+ * Return the length written by the device.
+ */
+static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
+                                    size_t out_len, size_t in_len)
 {
     /* Buffers for the device */
     const struct iovec out = {
@@ -636,7 +642,7 @@ static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
     memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
     memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
 
-    return vhost_vdpa_net_cvq_add(s, sizeof(ctrl) + data_size,
+    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
                                   sizeof(virtio_net_ctrl_ack));
 }
 
@@ -753,7 +759,8 @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq,
         dev_written = sizeof(status);
         *s->status = VIRTIO_NET_OK;
     } else {
-        dev_written = vhost_vdpa_net_cvq_add(s, out.iov_len, sizeof(status));
+        dev_written = vhost_vdpa_net_cvq_add_and_wait(s, out.iov_len,
+                                                      sizeof(status));
         if (unlikely(dev_written < 0)) {
             goto out;
         }
-- 
2.25.1



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

* [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel
  2023-05-06 14:06 [PATCH v2 0/2] Send all the SVQ control commands in parallel Hawkins Jiawei
  2023-05-06 14:06 ` [PATCH v2 1/2] vdpa: rename vhost_vdpa_net_cvq_add() Hawkins Jiawei
@ 2023-05-06 14:06 ` Hawkins Jiawei
  2023-05-17  5:22   ` Jason Wang
  1 sibling, 1 reply; 12+ messages in thread
From: Hawkins Jiawei @ 2023-05-06 14:06 UTC (permalink / raw)
  To: eperezma, jasowang; +Cc: yin31149, 18801353760, qemu-devel

This patch introduces the vhost_vdpa_net_cvq_add() and
refactors the vhost_vdpa_net_load*(), so that QEMU can
send CVQ state load commands in parallel.

To be more specific, this patch introduces vhost_vdpa_net_cvq_add()
to add SVQ control commands to SVQ and kick the device,
but does not poll the device used buffers. QEMU will not
poll and check the device used buffers in vhost_vdpa_net_load()
until all CVQ state load commands have been sent to the device.

What's more, in order to avoid buffer overwriting caused by
using `svq->cvq_cmd_out_buffer` and `svq->status` as the
buffer for all CVQ state load commands when sending
CVQ state load commands in parallel, this patch introduces
`out_cursor` and `in_cursor` in vhost_vdpa_net_load(),
pointing to the available buffer for in descriptor and
out descriptor, so that different CVQ state load commands can
use their unique buffer.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
---
 net/vhost-vdpa.c | 152 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 120 insertions(+), 32 deletions(-)

diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
index 10804c7200..14e31ca5c5 100644
--- a/net/vhost-vdpa.c
+++ b/net/vhost-vdpa.c
@@ -590,6 +590,44 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
     vhost_vdpa_net_client_stop(nc);
 }
 
+/**
+ * vhost_vdpa_net_cvq_add() adds SVQ control commands to SVQ,
+ * kicks the device but does not poll the device used buffers.
+ *
+ * Return the number of elements added to SVQ if success.
+ */
+static int vhost_vdpa_net_cvq_add(VhostVDPAState *s,
+                                void **out_cursor, size_t out_len,
+                                virtio_net_ctrl_ack **in_cursor, size_t in_len)
+{
+    /* Buffers for the device */
+    const struct iovec out = {
+        .iov_base = *out_cursor,
+        .iov_len = out_len,
+    };
+    const struct iovec in = {
+        .iov_base = *in_cursor,
+        .iov_len = sizeof(virtio_net_ctrl_ack),
+    };
+    VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
+    int r;
+
+    r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
+    if (unlikely(r != 0)) {
+        if (unlikely(r == -ENOSPC)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
+                          __func__);
+        }
+        return r;
+    }
+
+    /* Update the cursor */
+    *out_cursor += out_len;
+    *in_cursor += 1;
+
+    return 1;
+}
+
 /**
  * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
  * kicks the device and polls the device used buffers.
@@ -628,69 +666,82 @@ static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
     return vhost_svq_poll(svq);
 }
 
-static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
-                                       uint8_t cmd, const void *data,
-                                       size_t data_size)
+
+/**
+ * vhost_vdpa_net_load_cmd() restores the NIC state through SVQ.
+ *
+ * Return the number of elements added to SVQ if success.
+ */
+static int vhost_vdpa_net_load_cmd(VhostVDPAState *s,
+                                void **out_cursor, uint8_t class, uint8_t cmd,
+                                const void *data, size_t data_size,
+                                virtio_net_ctrl_ack **in_cursor)
 {
     const struct virtio_net_ctrl_hdr ctrl = {
         .class = class,
         .cmd = cmd,
     };
 
-    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
+    assert(sizeof(ctrl) < vhost_vdpa_net_cvq_cmd_page_len() -
+                          (*out_cursor - s->cvq_cmd_out_buffer));
+    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl) -
+                       (*out_cursor - s->cvq_cmd_out_buffer));
 
-    memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
-    memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
+    memcpy(*out_cursor, &ctrl, sizeof(ctrl));
+    memcpy(*out_cursor + sizeof(ctrl), data, data_size);
 
-    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
-                                  sizeof(virtio_net_ctrl_ack));
+    return vhost_vdpa_net_cvq_add(s, out_cursor, sizeof(ctrl) + data_size,
+                                  in_cursor, sizeof(virtio_net_ctrl_ack));
 }
 
-static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
+/**
+ * vhost_vdpa_net_load_mac() restores the NIC mac through SVQ.
+ *
+ * Return the number of elements added to SVQ if success.
+ */
+static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
+                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
 {
     uint64_t features = n->parent_obj.guest_features;
     if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
-        ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
-                                                  VIRTIO_NET_CTRL_MAC_ADDR_SET,
-                                                  n->mac, sizeof(n->mac));
-        if (unlikely(dev_written < 0)) {
-            return dev_written;
-        }
-
-        return *s->status != VIRTIO_NET_OK;
+        return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MAC,
+                                       VIRTIO_NET_CTRL_MAC_ADDR_SET,
+                                       n->mac, sizeof(n->mac), in_cursor);
     }
 
     return 0;
 }
 
-static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
-                                  const VirtIONet *n)
+/**
+ * vhost_vdpa_net_load_mac() restores the NIC mq state through SVQ.
+ *
+ * Return the number of elements added to SVQ if success.
+ */
+static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n,
+                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
 {
     struct virtio_net_ctrl_mq mq;
     uint64_t features = n->parent_obj.guest_features;
-    ssize_t dev_written;
 
     if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
         return 0;
     }
 
     mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
-    dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
-                                          VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
-                                          sizeof(mq));
-    if (unlikely(dev_written < 0)) {
-        return dev_written;
-    }
-
-    return *s->status != VIRTIO_NET_OK;
+    return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MQ,
+                                   VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
+                                   &mq, sizeof(mq), in_cursor);
 }
 
 static int vhost_vdpa_net_load(NetClientState *nc)
 {
     VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
+    VhostShadowVirtqueue *svq;
+    void *out_cursor;
+    virtio_net_ctrl_ack *in_cursor;
     struct vhost_vdpa *v = &s->vhost_vdpa;
     const VirtIONet *n;
-    int r;
+    ssize_t cmds_in_flight = 0, dev_written, r;
 
     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
 
@@ -699,14 +750,51 @@ static int vhost_vdpa_net_load(NetClientState *nc)
     }
 
     n = VIRTIO_NET(v->dev->vdev);
-    r = vhost_vdpa_net_load_mac(s, n);
+    out_cursor = s->cvq_cmd_out_buffer;
+    in_cursor = s->status;
+
+    r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor);
     if (unlikely(r < 0)) {
         return r;
     }
-    r = vhost_vdpa_net_load_mq(s, n);
-    if (unlikely(r)) {
+    cmds_in_flight += r;
+
+    r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor);
+    if (unlikely(r < 0)) {
         return r;
     }
+    cmds_in_flight += r;
+
+    /* Poll for all used buffer from device */
+    svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
+    while (cmds_in_flight > 0) {
+        /*
+         * We can poll here since we've had BQL from the time we sent the
+         * descriptor. Also, we need to take the answer before SVQ pulls
+         * by itself, when BQL is released
+         */
+        dev_written = vhost_svq_poll(svq);
+
+        if (unlikely(!dev_written)) {
+            /*
+             * vhost_svq_poll() return 0 when something wrong, such as
+             * QEMU waits for too long time or no available used buffer
+             * from device, and there is no need to continue polling
+             * in this case.
+             */
+            return -EINVAL;
+        }
+
+        --cmds_in_flight;
+    }
+
+    /* Check the buffers written by device */
+    for (virtio_net_ctrl_ack *status = s->status; status < in_cursor;
+         ++status) {
+        if (*status != VIRTIO_NET_OK) {
+            return -EINVAL;
+        }
+    }
 
     return 0;
 }
-- 
2.25.1



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

* Re: [PATCH v2 1/2] vdpa: rename vhost_vdpa_net_cvq_add()
  2023-05-06 14:06 ` [PATCH v2 1/2] vdpa: rename vhost_vdpa_net_cvq_add() Hawkins Jiawei
@ 2023-05-17  4:12   ` Jason Wang
  2023-05-17 15:11     ` Hawkins Jiawei
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Wang @ 2023-05-17  4:12 UTC (permalink / raw)
  To: Hawkins Jiawei; +Cc: eperezma, 18801353760, qemu-devel

On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
>
> We want to introduce a new version of vhost_vdpa_net_cvq_add() that
> does not poll immediately after forwarding custom buffers
> to the device, so that QEMU can send all the SVQ control commands
> in parallel instead of serialized.
>
> Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
> ---
>  net/vhost-vdpa.c | 15 +++++++++++----
>  1 file changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
> index 99904a0da7..10804c7200 100644
> --- a/net/vhost-vdpa.c
> +++ b/net/vhost-vdpa.c
> @@ -590,8 +590,14 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
>      vhost_vdpa_net_client_stop(nc);
>  }
>
> -static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s, size_t out_len,
> -                                      size_t in_len)
> +/**
> + * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
> + * kicks the device and polls the device used buffers.
> + *
> + * Return the length written by the device.
> + */
> +static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,

Nit: is it better to use "poll" or "sync" other than wait?

Other than this:

Acked-by: Jason Wang <jasowang@redhat.com>

Thanks

> +                                    size_t out_len, size_t in_len)
>  {
>      /* Buffers for the device */
>      const struct iovec out = {
> @@ -636,7 +642,7 @@ static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
>      memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
>      memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
>
> -    return vhost_vdpa_net_cvq_add(s, sizeof(ctrl) + data_size,
> +    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
>                                    sizeof(virtio_net_ctrl_ack));
>  }
>
> @@ -753,7 +759,8 @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq,
>          dev_written = sizeof(status);
>          *s->status = VIRTIO_NET_OK;
>      } else {
> -        dev_written = vhost_vdpa_net_cvq_add(s, out.iov_len, sizeof(status));
> +        dev_written = vhost_vdpa_net_cvq_add_and_wait(s, out.iov_len,
> +                                                      sizeof(status));
>          if (unlikely(dev_written < 0)) {
>              goto out;
>          }
> --
> 2.25.1
>



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

* Re: [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel
  2023-05-06 14:06 ` [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel Hawkins Jiawei
@ 2023-05-17  5:22   ` Jason Wang
  2023-05-17  8:21     ` Eugenio Perez Martin
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Wang @ 2023-05-17  5:22 UTC (permalink / raw)
  To: Hawkins Jiawei; +Cc: eperezma, 18801353760, qemu-devel

On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
>
> This patch introduces the vhost_vdpa_net_cvq_add() and
> refactors the vhost_vdpa_net_load*(), so that QEMU can
> send CVQ state load commands in parallel.
>
> To be more specific, this patch introduces vhost_vdpa_net_cvq_add()
> to add SVQ control commands to SVQ and kick the device,
> but does not poll the device used buffers. QEMU will not
> poll and check the device used buffers in vhost_vdpa_net_load()
> until all CVQ state load commands have been sent to the device.
>
> What's more, in order to avoid buffer overwriting caused by
> using `svq->cvq_cmd_out_buffer` and `svq->status` as the
> buffer for all CVQ state load commands when sending
> CVQ state load commands in parallel, this patch introduces
> `out_cursor` and `in_cursor` in vhost_vdpa_net_load(),
> pointing to the available buffer for in descriptor and
> out descriptor, so that different CVQ state load commands can
> use their unique buffer.
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
> Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
> ---
>  net/vhost-vdpa.c | 152 +++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 120 insertions(+), 32 deletions(-)
>
> diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
> index 10804c7200..14e31ca5c5 100644
> --- a/net/vhost-vdpa.c
> +++ b/net/vhost-vdpa.c
> @@ -590,6 +590,44 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
>      vhost_vdpa_net_client_stop(nc);
>  }
>
> +/**
> + * vhost_vdpa_net_cvq_add() adds SVQ control commands to SVQ,
> + * kicks the device but does not poll the device used buffers.
> + *
> + * Return the number of elements added to SVQ if success.
> + */
> +static int vhost_vdpa_net_cvq_add(VhostVDPAState *s,
> +                                void **out_cursor, size_t out_len,

Can we track things like cursors in e.g VhostVDPAState ?

> +                                virtio_net_ctrl_ack **in_cursor, size_t in_len)
> +{
> +    /* Buffers for the device */
> +    const struct iovec out = {
> +        .iov_base = *out_cursor,
> +        .iov_len = out_len,
> +    };
> +    const struct iovec in = {
> +        .iov_base = *in_cursor,
> +        .iov_len = sizeof(virtio_net_ctrl_ack),
> +    };
> +    VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> +    int r;
> +
> +    r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
> +    if (unlikely(r != 0)) {
> +        if (unlikely(r == -ENOSPC)) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
> +                          __func__);
> +        }
> +        return r;
> +    }
> +
> +    /* Update the cursor */
> +    *out_cursor += out_len;
> +    *in_cursor += 1;
> +
> +    return 1;
> +}
> +
>  /**
>   * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
>   * kicks the device and polls the device used buffers.
> @@ -628,69 +666,82 @@ static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
>      return vhost_svq_poll(svq);
>  }
>
> -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
> -                                       uint8_t cmd, const void *data,
> -                                       size_t data_size)
> +
> +/**
> + * vhost_vdpa_net_load_cmd() restores the NIC state through SVQ.
> + *
> + * Return the number of elements added to SVQ if success.
> + */
> +static int vhost_vdpa_net_load_cmd(VhostVDPAState *s,
> +                                void **out_cursor, uint8_t class, uint8_t cmd,
> +                                const void *data, size_t data_size,
> +                                virtio_net_ctrl_ack **in_cursor)
>  {
>      const struct virtio_net_ctrl_hdr ctrl = {
>          .class = class,
>          .cmd = cmd,
>      };
>
> -    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
> +    assert(sizeof(ctrl) < vhost_vdpa_net_cvq_cmd_page_len() -
> +                          (*out_cursor - s->cvq_cmd_out_buffer));
> +    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl) -
> +                       (*out_cursor - s->cvq_cmd_out_buffer));
>
> -    memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
> -    memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
> +    memcpy(*out_cursor, &ctrl, sizeof(ctrl));
> +    memcpy(*out_cursor + sizeof(ctrl), data, data_size);
>
> -    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
> -                                  sizeof(virtio_net_ctrl_ack));
> +    return vhost_vdpa_net_cvq_add(s, out_cursor, sizeof(ctrl) + data_size,
> +                                  in_cursor, sizeof(virtio_net_ctrl_ack));
>  }
>
> -static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
> +/**
> + * vhost_vdpa_net_load_mac() restores the NIC mac through SVQ.
> + *
> + * Return the number of elements added to SVQ if success.
> + */
> +static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
> +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
>  {
>      uint64_t features = n->parent_obj.guest_features;
>      if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
> -        ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
> -                                                  VIRTIO_NET_CTRL_MAC_ADDR_SET,
> -                                                  n->mac, sizeof(n->mac));
> -        if (unlikely(dev_written < 0)) {
> -            return dev_written;
> -        }
> -
> -        return *s->status != VIRTIO_NET_OK;
> +        return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MAC,
> +                                       VIRTIO_NET_CTRL_MAC_ADDR_SET,
> +                                       n->mac, sizeof(n->mac), in_cursor);
>      }
>
>      return 0;
>  }
>
> -static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
> -                                  const VirtIONet *n)
> +/**
> + * vhost_vdpa_net_load_mac() restores the NIC mq state through SVQ.
> + *
> + * Return the number of elements added to SVQ if success.
> + */
> +static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n,
> +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
>  {
>      struct virtio_net_ctrl_mq mq;
>      uint64_t features = n->parent_obj.guest_features;
> -    ssize_t dev_written;
>
>      if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
>          return 0;
>      }
>
>      mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
> -    dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
> -                                          VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
> -                                          sizeof(mq));
> -    if (unlikely(dev_written < 0)) {
> -        return dev_written;
> -    }
> -
> -    return *s->status != VIRTIO_NET_OK;
> +    return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MQ,
> +                                   VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
> +                                   &mq, sizeof(mq), in_cursor);
>  }
>
>  static int vhost_vdpa_net_load(NetClientState *nc)
>  {
>      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
> +    VhostShadowVirtqueue *svq;
> +    void *out_cursor;
> +    virtio_net_ctrl_ack *in_cursor;
>      struct vhost_vdpa *v = &s->vhost_vdpa;
>      const VirtIONet *n;
> -    int r;
> +    ssize_t cmds_in_flight = 0, dev_written, r;
>
>      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
>
> @@ -699,14 +750,51 @@ static int vhost_vdpa_net_load(NetClientState *nc)
>      }
>
>      n = VIRTIO_NET(v->dev->vdev);
> -    r = vhost_vdpa_net_load_mac(s, n);
> +    out_cursor = s->cvq_cmd_out_buffer;
> +    in_cursor = s->status;
> +
> +    r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor);
>      if (unlikely(r < 0))
>          return r;
>      }
> -    r = vhost_vdpa_net_load_mq(s, n);
> -    if (unlikely(r)) {
> +    cmds_in_flight += r;
> +
> +    r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor);
> +    if (unlikely(r < 0)) {
>          return r;
>      }
> +    cmds_in_flight += r;
> +
> +    /* Poll for all used buffer from device */
> +    svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> +    while (cmds_in_flight > 0) {
> +        /*
> +         * We can poll here since we've had BQL from the time we sent the
> +         * descriptor. Also, we need to take the answer before SVQ pulls
> +         * by itself, when BQL is released
> +         */
> +        dev_written = vhost_svq_poll(svq);

I'd tweak vhost_svq_poll to accept cmds_in_flight.

Thanks

> +
> +        if (unlikely(!dev_written)) {
> +            /*
> +             * vhost_svq_poll() return 0 when something wrong, such as
> +             * QEMU waits for too long time or no available used buffer
> +             * from device, and there is no need to continue polling
> +             * in this case.
> +             */
> +            return -EINVAL;
> +        }
> +
> +        --cmds_in_flight;
> +    }
> +
> +    /* Check the buffers written by device */
> +    for (virtio_net_ctrl_ack *status = s->status; status < in_cursor;
> +         ++status) {
> +        if (*status != VIRTIO_NET_OK) {
> +            return -EINVAL;
> +        }
> +    }
>
>      return 0;
>  }
> --
> 2.25.1
>



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

* Re: [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel
  2023-05-17  5:22   ` Jason Wang
@ 2023-05-17  8:21     ` Eugenio Perez Martin
  2023-05-17 15:01       ` Hawkins Jiawei
  0 siblings, 1 reply; 12+ messages in thread
From: Eugenio Perez Martin @ 2023-05-17  8:21 UTC (permalink / raw)
  To: Jason Wang; +Cc: Hawkins Jiawei, 18801353760, qemu-devel

On Wed, May 17, 2023 at 7:22 AM Jason Wang <jasowang@redhat.com> wrote:
>
> On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
> >
> > This patch introduces the vhost_vdpa_net_cvq_add() and
> > refactors the vhost_vdpa_net_load*(), so that QEMU can
> > send CVQ state load commands in parallel.
> >
> > To be more specific, this patch introduces vhost_vdpa_net_cvq_add()
> > to add SVQ control commands to SVQ and kick the device,
> > but does not poll the device used buffers. QEMU will not
> > poll and check the device used buffers in vhost_vdpa_net_load()
> > until all CVQ state load commands have been sent to the device.
> >
> > What's more, in order to avoid buffer overwriting caused by
> > using `svq->cvq_cmd_out_buffer` and `svq->status` as the
> > buffer for all CVQ state load commands when sending
> > CVQ state load commands in parallel, this patch introduces
> > `out_cursor` and `in_cursor` in vhost_vdpa_net_load(),
> > pointing to the available buffer for in descriptor and
> > out descriptor, so that different CVQ state load commands can
> > use their unique buffer.
> >
> > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
> > Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
> > ---
> >  net/vhost-vdpa.c | 152 +++++++++++++++++++++++++++++++++++++----------
> >  1 file changed, 120 insertions(+), 32 deletions(-)
> >
> > diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
> > index 10804c7200..14e31ca5c5 100644
> > --- a/net/vhost-vdpa.c
> > +++ b/net/vhost-vdpa.c
> > @@ -590,6 +590,44 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
> >      vhost_vdpa_net_client_stop(nc);
> >  }
> >
> > +/**
> > + * vhost_vdpa_net_cvq_add() adds SVQ control commands to SVQ,
> > + * kicks the device but does not poll the device used buffers.
> > + *
> > + * Return the number of elements added to SVQ if success.
> > + */
> > +static int vhost_vdpa_net_cvq_add(VhostVDPAState *s,
> > +                                void **out_cursor, size_t out_len,
>
> Can we track things like cursors in e.g VhostVDPAState ?
>

Cursors will only be used at device startup. After that, cursors are
always the full buffer. Do we want to "pollute" VhostVDPAState with
things that will not be used after the startup?

Maybe we can create a struct for them and then pass just this struct?

Thanks!

> > +                                virtio_net_ctrl_ack **in_cursor, size_t in_len)
> > +{
> > +    /* Buffers for the device */
> > +    const struct iovec out = {
> > +        .iov_base = *out_cursor,
> > +        .iov_len = out_len,
> > +    };
> > +    const struct iovec in = {
> > +        .iov_base = *in_cursor,
> > +        .iov_len = sizeof(virtio_net_ctrl_ack),
> > +    };
> > +    VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > +    int r;
> > +
> > +    r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
> > +    if (unlikely(r != 0)) {
> > +        if (unlikely(r == -ENOSPC)) {
> > +            qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
> > +                          __func__);
> > +        }
> > +        return r;
> > +    }
> > +
> > +    /* Update the cursor */
> > +    *out_cursor += out_len;
> > +    *in_cursor += 1;
> > +
> > +    return 1;
> > +}
> > +
> >  /**
> >   * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
> >   * kicks the device and polls the device used buffers.
> > @@ -628,69 +666,82 @@ static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
> >      return vhost_svq_poll(svq);
> >  }
> >
> > -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
> > -                                       uint8_t cmd, const void *data,
> > -                                       size_t data_size)
> > +
> > +/**
> > + * vhost_vdpa_net_load_cmd() restores the NIC state through SVQ.
> > + *
> > + * Return the number of elements added to SVQ if success.
> > + */
> > +static int vhost_vdpa_net_load_cmd(VhostVDPAState *s,
> > +                                void **out_cursor, uint8_t class, uint8_t cmd,
> > +                                const void *data, size_t data_size,
> > +                                virtio_net_ctrl_ack **in_cursor)
> >  {
> >      const struct virtio_net_ctrl_hdr ctrl = {
> >          .class = class,
> >          .cmd = cmd,
> >      };
> >
> > -    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
> > +    assert(sizeof(ctrl) < vhost_vdpa_net_cvq_cmd_page_len() -
> > +                          (*out_cursor - s->cvq_cmd_out_buffer));
> > +    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl) -
> > +                       (*out_cursor - s->cvq_cmd_out_buffer));
> >
> > -    memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
> > -    memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
> > +    memcpy(*out_cursor, &ctrl, sizeof(ctrl));
> > +    memcpy(*out_cursor + sizeof(ctrl), data, data_size);
> >
> > -    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
> > -                                  sizeof(virtio_net_ctrl_ack));
> > +    return vhost_vdpa_net_cvq_add(s, out_cursor, sizeof(ctrl) + data_size,
> > +                                  in_cursor, sizeof(virtio_net_ctrl_ack));
> >  }
> >
> > -static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
> > +/**
> > + * vhost_vdpa_net_load_mac() restores the NIC mac through SVQ.
> > + *
> > + * Return the number of elements added to SVQ if success.
> > + */
> > +static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
> > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> >  {
> >      uint64_t features = n->parent_obj.guest_features;
> >      if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
> > -        ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
> > -                                                  VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > -                                                  n->mac, sizeof(n->mac));
> > -        if (unlikely(dev_written < 0)) {
> > -            return dev_written;
> > -        }
> > -
> > -        return *s->status != VIRTIO_NET_OK;
> > +        return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MAC,
> > +                                       VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > +                                       n->mac, sizeof(n->mac), in_cursor);
> >      }
> >
> >      return 0;
> >  }
> >
> > -static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
> > -                                  const VirtIONet *n)
> > +/**
> > + * vhost_vdpa_net_load_mac() restores the NIC mq state through SVQ.
> > + *
> > + * Return the number of elements added to SVQ if success.
> > + */
> > +static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n,
> > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> >  {
> >      struct virtio_net_ctrl_mq mq;
> >      uint64_t features = n->parent_obj.guest_features;
> > -    ssize_t dev_written;
> >
> >      if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
> >          return 0;
> >      }
> >
> >      mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
> > -    dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
> > -                                          VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
> > -                                          sizeof(mq));
> > -    if (unlikely(dev_written < 0)) {
> > -        return dev_written;
> > -    }
> > -
> > -    return *s->status != VIRTIO_NET_OK;
> > +    return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MQ,
> > +                                   VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
> > +                                   &mq, sizeof(mq), in_cursor);
> >  }
> >
> >  static int vhost_vdpa_net_load(NetClientState *nc)
> >  {
> >      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
> > +    VhostShadowVirtqueue *svq;
> > +    void *out_cursor;
> > +    virtio_net_ctrl_ack *in_cursor;
> >      struct vhost_vdpa *v = &s->vhost_vdpa;
> >      const VirtIONet *n;
> > -    int r;
> > +    ssize_t cmds_in_flight = 0, dev_written, r;
> >
> >      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
> >
> > @@ -699,14 +750,51 @@ static int vhost_vdpa_net_load(NetClientState *nc)
> >      }
> >
> >      n = VIRTIO_NET(v->dev->vdev);
> > -    r = vhost_vdpa_net_load_mac(s, n);
> > +    out_cursor = s->cvq_cmd_out_buffer;
> > +    in_cursor = s->status;
> > +
> > +    r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor);
> >      if (unlikely(r < 0))
> >          return r;
> >      }
> > -    r = vhost_vdpa_net_load_mq(s, n);
> > -    if (unlikely(r)) {
> > +    cmds_in_flight += r;
> > +
> > +    r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor);
> > +    if (unlikely(r < 0)) {
> >          return r;
> >      }
> > +    cmds_in_flight += r;
> > +
> > +    /* Poll for all used buffer from device */
> > +    svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > +    while (cmds_in_flight > 0) {
> > +        /*
> > +         * We can poll here since we've had BQL from the time we sent the
> > +         * descriptor. Also, we need to take the answer before SVQ pulls
> > +         * by itself, when BQL is released
> > +         */
> > +        dev_written = vhost_svq_poll(svq);
>
> I'd tweak vhost_svq_poll to accept cmds_in_flight.
>
> Thanks
>
> > +
> > +        if (unlikely(!dev_written)) {
> > +            /*
> > +             * vhost_svq_poll() return 0 when something wrong, such as
> > +             * QEMU waits for too long time or no available used buffer
> > +             * from device, and there is no need to continue polling
> > +             * in this case.
> > +             */
> > +            return -EINVAL;
> > +        }
> > +
> > +        --cmds_in_flight;
> > +    }
> > +
> > +    /* Check the buffers written by device */
> > +    for (virtio_net_ctrl_ack *status = s->status; status < in_cursor;
> > +         ++status) {
> > +        if (*status != VIRTIO_NET_OK) {
> > +            return -EINVAL;
> > +        }
> > +    }
> >
> >      return 0;
> >  }
> > --
> > 2.25.1
> >
>



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

* Re: [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel
  2023-05-17  8:21     ` Eugenio Perez Martin
@ 2023-05-17 15:01       ` Hawkins Jiawei
  2023-05-18  5:46         ` Jason Wang
  0 siblings, 1 reply; 12+ messages in thread
From: Hawkins Jiawei @ 2023-05-17 15:01 UTC (permalink / raw)
  To: Eugenio Perez Martin; +Cc: Jason Wang, 18801353760, qemu-devel

Sorry for forgetting cc when replying to the email.

On Wed, 17 May 2023 at 16:22, Eugenio Perez Martin <eperezma@redhat.com> wrote:
>
> On Wed, May 17, 2023 at 7:22 AM Jason Wang <jasowang@redhat.com> wrote:
> >
> > On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
> > >
> > > This patch introduces the vhost_vdpa_net_cvq_add() and
> > > refactors the vhost_vdpa_net_load*(), so that QEMU can
> > > send CVQ state load commands in parallel.
> > >
> > > To be more specific, this patch introduces vhost_vdpa_net_cvq_add()
> > > to add SVQ control commands to SVQ and kick the device,
> > > but does not poll the device used buffers. QEMU will not
> > > poll and check the device used buffers in vhost_vdpa_net_load()
> > > until all CVQ state load commands have been sent to the device.
> > >
> > > What's more, in order to avoid buffer overwriting caused by
> > > using `svq->cvq_cmd_out_buffer` and `svq->status` as the
> > > buffer for all CVQ state load commands when sending
> > > CVQ state load commands in parallel, this patch introduces
> > > `out_cursor` and `in_cursor` in vhost_vdpa_net_load(),
> > > pointing to the available buffer for in descriptor and
> > > out descriptor, so that different CVQ state load commands can
> > > use their unique buffer.
> > >
> > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
> > > Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
> > > ---
> > >  net/vhost-vdpa.c | 152 +++++++++++++++++++++++++++++++++++++----------
> > >  1 file changed, 120 insertions(+), 32 deletions(-)
> > >
> > > diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
> > > index 10804c7200..14e31ca5c5 100644
> > > --- a/net/vhost-vdpa.c
> > > +++ b/net/vhost-vdpa.c
> > > @@ -590,6 +590,44 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
> > >      vhost_vdpa_net_client_stop(nc);
> > >  }
> > >
> > > +/**
> > > + * vhost_vdpa_net_cvq_add() adds SVQ control commands to SVQ,
> > > + * kicks the device but does not poll the device used buffers.
> > > + *
> > > + * Return the number of elements added to SVQ if success.
> > > + */
> > > +static int vhost_vdpa_net_cvq_add(VhostVDPAState *s,
> > > +                                void **out_cursor, size_t out_len,
> >
> > Can we track things like cursors in e.g VhostVDPAState ?
> >
>
> Cursors will only be used at device startup. After that, cursors are
> always the full buffer. Do we want to "pollute" VhostVDPAState with
> things that will not be used after the startup?
>
> Maybe we can create a struct for them and then pass just this struct?

Yes, Currently, the new version of vhost_vdpa_net_cvq_add() is only
called in vhost_vdpa_net_load() at startup, so these cursors will not be
used after startup.

If needed, we can create a `VhostVDPACursor` as suggested by Eugenio.

>
> Thanks!
>
> > > +                                virtio_net_ctrl_ack **in_cursor, size_t in_len)
> > > +{
> > > +    /* Buffers for the device */
> > > +    const struct iovec out = {
> > > +        .iov_base = *out_cursor,
> > > +        .iov_len = out_len,
> > > +    };
> > > +    const struct iovec in = {
> > > +        .iov_base = *in_cursor,
> > > +        .iov_len = sizeof(virtio_net_ctrl_ack),
> > > +    };
> > > +    VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > > +    int r;
> > > +
> > > +    r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
> > > +    if (unlikely(r != 0)) {
> > > +        if (unlikely(r == -ENOSPC)) {
> > > +            qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
> > > +                          __func__);
> > > +        }
> > > +        return r;
> > > +    }
> > > +
> > > +    /* Update the cursor */
> > > +    *out_cursor += out_len;
> > > +    *in_cursor += 1;
> > > +
> > > +    return 1;
> > > +}
> > > +
> > >  /**
> > >   * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
> > >   * kicks the device and polls the device used buffers.
> > > @@ -628,69 +666,82 @@ static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
> > >      return vhost_svq_poll(svq);
> > >  }
> > >
> > > -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
> > > -                                       uint8_t cmd, const void *data,
> > > -                                       size_t data_size)
> > > +
> > > +/**
> > > + * vhost_vdpa_net_load_cmd() restores the NIC state through SVQ.
> > > + *
> > > + * Return the number of elements added to SVQ if success.
> > > + */
> > > +static int vhost_vdpa_net_load_cmd(VhostVDPAState *s,
> > > +                                void **out_cursor, uint8_t class, uint8_t cmd,
> > > +                                const void *data, size_t data_size,
> > > +                                virtio_net_ctrl_ack **in_cursor)
> > >  {
> > >      const struct virtio_net_ctrl_hdr ctrl = {
> > >          .class = class,
> > >          .cmd = cmd,
> > >      };
> > >
> > > -    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
> > > +    assert(sizeof(ctrl) < vhost_vdpa_net_cvq_cmd_page_len() -
> > > +                          (*out_cursor - s->cvq_cmd_out_buffer));
> > > +    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl) -
> > > +                       (*out_cursor - s->cvq_cmd_out_buffer));
> > >
> > > -    memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
> > > -    memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
> > > +    memcpy(*out_cursor, &ctrl, sizeof(ctrl));
> > > +    memcpy(*out_cursor + sizeof(ctrl), data, data_size);
> > >
> > > -    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
> > > -                                  sizeof(virtio_net_ctrl_ack));
> > > +    return vhost_vdpa_net_cvq_add(s, out_cursor, sizeof(ctrl) + data_size,
> > > +                                  in_cursor, sizeof(virtio_net_ctrl_ack));
> > >  }
> > >
> > > -static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
> > > +/**
> > > + * vhost_vdpa_net_load_mac() restores the NIC mac through SVQ.
> > > + *
> > > + * Return the number of elements added to SVQ if success.
> > > + */
> > > +static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
> > > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> > >  {
> > >      uint64_t features = n->parent_obj.guest_features;
> > >      if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
> > > -        ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
> > > -                                                  VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > > -                                                  n->mac, sizeof(n->mac));
> > > -        if (unlikely(dev_written < 0)) {
> > > -            return dev_written;
> > > -        }
> > > -
> > > -        return *s->status != VIRTIO_NET_OK;
> > > +        return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MAC,
> > > +                                       VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > > +                                       n->mac, sizeof(n->mac), in_cursor);
> > >      }
> > >
> > >      return 0;
> > >  }
> > >
> > > -static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
> > > -                                  const VirtIONet *n)
> > > +/**
> > > + * vhost_vdpa_net_load_mac() restores the NIC mq state through SVQ.
> > > + *
> > > + * Return the number of elements added to SVQ if success.
> > > + */
> > > +static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n,
> > > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> > >  {
> > >      struct virtio_net_ctrl_mq mq;
> > >      uint64_t features = n->parent_obj.guest_features;
> > > -    ssize_t dev_written;
> > >
> > >      if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
> > >          return 0;
> > >      }
> > >
> > >      mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
> > > -    dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
> > > -                                          VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
> > > -                                          sizeof(mq));
> > > -    if (unlikely(dev_written < 0)) {
> > > -        return dev_written;
> > > -    }
> > > -
> > > -    return *s->status != VIRTIO_NET_OK;
> > > +    return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MQ,
> > > +                                   VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
> > > +                                   &mq, sizeof(mq), in_cursor);
> > >  }
> > >
> > >  static int vhost_vdpa_net_load(NetClientState *nc)
> > >  {
> > >      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
> > > +    VhostShadowVirtqueue *svq;
> > > +    void *out_cursor;
> > > +    virtio_net_ctrl_ack *in_cursor;
> > >      struct vhost_vdpa *v = &s->vhost_vdpa;
> > >      const VirtIONet *n;
> > > -    int r;
> > > +    ssize_t cmds_in_flight = 0, dev_written, r;
> > >
> > >      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
> > >
> > > @@ -699,14 +750,51 @@ static int vhost_vdpa_net_load(NetClientState *nc)
> > >      }
> > >
> > >      n = VIRTIO_NET(v->dev->vdev);
> > > -    r = vhost_vdpa_net_load_mac(s, n);
> > > +    out_cursor = s->cvq_cmd_out_buffer;
> > > +    in_cursor = s->status;
> > > +
> > > +    r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor);
> > >      if (unlikely(r < 0))
> > >          return r;
> > >      }
> > > -    r = vhost_vdpa_net_load_mq(s, n);
> > > -    if (unlikely(r)) {
> > > +    cmds_in_flight += r;
> > > +
> > > +    r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor);
> > > +    if (unlikely(r < 0)) {
> > >          return r;
> > >      }
> > > +    cmds_in_flight += r;
> > > +
> > > +    /* Poll for all used buffer from device */
> > > +    svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > > +    while (cmds_in_flight > 0) {
> > > +        /*
> > > +         * We can poll here since we've had BQL from the time we sent the
> > > +         * descriptor. Also, we need to take the answer before SVQ pulls
> > > +         * by itself, when BQL is released
> > > +         */
> > > +        dev_written = vhost_svq_poll(svq);
> >
> > I'd tweak vhost_svq_poll to accept cmds_in_flight.

That sounds great!
I will refactor the code here and send the v2 patch after
your patch.

Thanks!

> >
> > Thanks
> >
> > > +
> > > +        if (unlikely(!dev_written)) {
> > > +            /*
> > > +             * vhost_svq_poll() return 0 when something wrong, such as
> > > +             * QEMU waits for too long time or no available used buffer
> > > +             * from device, and there is no need to continue polling
> > > +             * in this case.
> > > +             */
> > > +            return -EINVAL;
> > > +        }
> > > +
> > > +        --cmds_in_flight;
> > > +    }
> > > +
> > > +    /* Check the buffers written by device */
> > > +    for (virtio_net_ctrl_ack *status = s->status; status < in_cursor;
> > > +         ++status) {
> > > +        if (*status != VIRTIO_NET_OK) {
> > > +            return -EINVAL;
> > > +        }
> > > +    }
> > >
> > >      return 0;
> > >  }
> > > --
> > > 2.25.1
> > >
> >
>


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

* Re: [PATCH v2 1/2] vdpa: rename vhost_vdpa_net_cvq_add()
  2023-05-17  4:12   ` Jason Wang
@ 2023-05-17 15:11     ` Hawkins Jiawei
  0 siblings, 0 replies; 12+ messages in thread
From: Hawkins Jiawei @ 2023-05-17 15:11 UTC (permalink / raw)
  To: Jason Wang; +Cc: eperezma, 18801353760, qemu-devel

Sorry for forgetting cc when replying to the email.
I will resend this email with cc.

On Wed, 17 May 2023 at 12:12, Jason Wang <jasowang@redhat.com> wrote:
>
> On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
> >
> > We want to introduce a new version of vhost_vdpa_net_cvq_add() that
> > does not poll immediately after forwarding custom buffers
> > to the device, so that QEMU can send all the SVQ control commands
> > in parallel instead of serialized.
> >
> > Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
> > ---
> >  net/vhost-vdpa.c | 15 +++++++++++----
> >  1 file changed, 11 insertions(+), 4 deletions(-)
> >
> > diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
> > index 99904a0da7..10804c7200 100644
> > --- a/net/vhost-vdpa.c
> > +++ b/net/vhost-vdpa.c
> > @@ -590,8 +590,14 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
> >      vhost_vdpa_net_client_stop(nc);
> >  }
> >
> > -static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s, size_t out_len,
> > -                                      size_t in_len)
> > +/**
> > + * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
> > + * kicks the device and polls the device used buffers.
> > + *
> > + * Return the length written by the device.
> > + */
> > +static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
>
> Nit: is it better to use "poll" or "sync" other than wait?
>
> Other than this:
>
> Acked-by: Jason Wang <jasowang@redhat.com>

Hi Jason,

Thanks for your suggestion. I prefer 'poll', which makes it clearer
that this function will poll immediately compared to the new
version of vhost_vdpa_net_cvq_add().

I will refactor this in the v2 patch with the Acked-by tag on.

Thanks!

>
> Thanks
>
> > +                                    size_t out_len, size_t in_len)
> >  {
> >      /* Buffers for the device */
> >      const struct iovec out = {
> > @@ -636,7 +642,7 @@ static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
> >      memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
> >      memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
> >
> > -    return vhost_vdpa_net_cvq_add(s, sizeof(ctrl) + data_size,
> > +    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
> >                                    sizeof(virtio_net_ctrl_ack));
> >  }
> >
> > @@ -753,7 +759,8 @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq,
> >          dev_written = sizeof(status);
> >          *s->status = VIRTIO_NET_OK;
> >      } else {
> > -        dev_written = vhost_vdpa_net_cvq_add(s, out.iov_len, sizeof(status));
> > +        dev_written = vhost_vdpa_net_cvq_add_and_wait(s, out.iov_len,
> > +                                                      sizeof(status));
> >          if (unlikely(dev_written < 0)) {
> >              goto out;
> >          }
> > --
> > 2.25.1
> >
>


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

* Re: [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel
  2023-05-17 15:01       ` Hawkins Jiawei
@ 2023-05-18  5:46         ` Jason Wang
  2023-05-18  6:00           ` Eugenio Perez Martin
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Wang @ 2023-05-18  5:46 UTC (permalink / raw)
  To: Hawkins Jiawei; +Cc: Eugenio Perez Martin, 18801353760, qemu-devel

On Wed, May 17, 2023 at 11:02 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
>
> Sorry for forgetting cc when replying to the email.
>
> On Wed, 17 May 2023 at 16:22, Eugenio Perez Martin <eperezma@redhat.com> wrote:
> >
> > On Wed, May 17, 2023 at 7:22 AM Jason Wang <jasowang@redhat.com> wrote:
> > >
> > > On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
> > > >
> > > > This patch introduces the vhost_vdpa_net_cvq_add() and
> > > > refactors the vhost_vdpa_net_load*(), so that QEMU can
> > > > send CVQ state load commands in parallel.
> > > >
> > > > To be more specific, this patch introduces vhost_vdpa_net_cvq_add()
> > > > to add SVQ control commands to SVQ and kick the device,
> > > > but does not poll the device used buffers. QEMU will not
> > > > poll and check the device used buffers in vhost_vdpa_net_load()
> > > > until all CVQ state load commands have been sent to the device.
> > > >
> > > > What's more, in order to avoid buffer overwriting caused by
> > > > using `svq->cvq_cmd_out_buffer` and `svq->status` as the
> > > > buffer for all CVQ state load commands when sending
> > > > CVQ state load commands in parallel, this patch introduces
> > > > `out_cursor` and `in_cursor` in vhost_vdpa_net_load(),
> > > > pointing to the available buffer for in descriptor and
> > > > out descriptor, so that different CVQ state load commands can
> > > > use their unique buffer.
> > > >
> > > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
> > > > Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
> > > > ---
> > > >  net/vhost-vdpa.c | 152 +++++++++++++++++++++++++++++++++++++----------
> > > >  1 file changed, 120 insertions(+), 32 deletions(-)
> > > >
> > > > diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
> > > > index 10804c7200..14e31ca5c5 100644
> > > > --- a/net/vhost-vdpa.c
> > > > +++ b/net/vhost-vdpa.c
> > > > @@ -590,6 +590,44 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
> > > >      vhost_vdpa_net_client_stop(nc);
> > > >  }
> > > >
> > > > +/**
> > > > + * vhost_vdpa_net_cvq_add() adds SVQ control commands to SVQ,
> > > > + * kicks the device but does not poll the device used buffers.
> > > > + *
> > > > + * Return the number of elements added to SVQ if success.
> > > > + */
> > > > +static int vhost_vdpa_net_cvq_add(VhostVDPAState *s,
> > > > +                                void **out_cursor, size_t out_len,
> > >
> > > Can we track things like cursors in e.g VhostVDPAState ?
> > >
> >
> > Cursors will only be used at device startup. After that, cursors are
> > always the full buffer. Do we want to "pollute" VhostVDPAState with
> > things that will not be used after the startup?

So it's the cursor of the cvq_cmd_out_buffer, it would be more elegant
to keep it with where the cvq_cmd_out_buffer is placed. It can avoid
passing cursors in several levels.

Or it would be even better to have some buffer allocation helpers to
alloc and free in and out buffers.

Thanks

> >
> > Maybe we can create a struct for them and then pass just this struct?
>
> Yes, Currently, the new version of vhost_vdpa_net_cvq_add() is only
> called in vhost_vdpa_net_load() at startup, so these cursors will not be
> used after startup.
>
> If needed, we can create a `VhostVDPACursor` as suggested by Eugenio.
>
> >
> > Thanks!
> >
> > > > +                                virtio_net_ctrl_ack **in_cursor, size_t in_len)
> > > > +{
> > > > +    /* Buffers for the device */
> > > > +    const struct iovec out = {
> > > > +        .iov_base = *out_cursor,
> > > > +        .iov_len = out_len,
> > > > +    };
> > > > +    const struct iovec in = {
> > > > +        .iov_base = *in_cursor,
> > > > +        .iov_len = sizeof(virtio_net_ctrl_ack),
> > > > +    };
> > > > +    VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > > > +    int r;
> > > > +
> > > > +    r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
> > > > +    if (unlikely(r != 0)) {
> > > > +        if (unlikely(r == -ENOSPC)) {
> > > > +            qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
> > > > +                          __func__);
> > > > +        }
> > > > +        return r;
> > > > +    }
> > > > +
> > > > +    /* Update the cursor */
> > > > +    *out_cursor += out_len;
> > > > +    *in_cursor += 1;
> > > > +
> > > > +    return 1;
> > > > +}
> > > > +
> > > >  /**
> > > >   * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
> > > >   * kicks the device and polls the device used buffers.
> > > > @@ -628,69 +666,82 @@ static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
> > > >      return vhost_svq_poll(svq);
> > > >  }
> > > >
> > > > -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
> > > > -                                       uint8_t cmd, const void *data,
> > > > -                                       size_t data_size)
> > > > +
> > > > +/**
> > > > + * vhost_vdpa_net_load_cmd() restores the NIC state through SVQ.
> > > > + *
> > > > + * Return the number of elements added to SVQ if success.
> > > > + */
> > > > +static int vhost_vdpa_net_load_cmd(VhostVDPAState *s,
> > > > +                                void **out_cursor, uint8_t class, uint8_t cmd,
> > > > +                                const void *data, size_t data_size,
> > > > +                                virtio_net_ctrl_ack **in_cursor)
> > > >  {
> > > >      const struct virtio_net_ctrl_hdr ctrl = {
> > > >          .class = class,
> > > >          .cmd = cmd,
> > > >      };
> > > >
> > > > -    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
> > > > +    assert(sizeof(ctrl) < vhost_vdpa_net_cvq_cmd_page_len() -
> > > > +                          (*out_cursor - s->cvq_cmd_out_buffer));
> > > > +    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl) -
> > > > +                       (*out_cursor - s->cvq_cmd_out_buffer));
> > > >
> > > > -    memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
> > > > -    memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
> > > > +    memcpy(*out_cursor, &ctrl, sizeof(ctrl));
> > > > +    memcpy(*out_cursor + sizeof(ctrl), data, data_size);
> > > >
> > > > -    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
> > > > -                                  sizeof(virtio_net_ctrl_ack));
> > > > +    return vhost_vdpa_net_cvq_add(s, out_cursor, sizeof(ctrl) + data_size,
> > > > +                                  in_cursor, sizeof(virtio_net_ctrl_ack));
> > > >  }
> > > >
> > > > -static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
> > > > +/**
> > > > + * vhost_vdpa_net_load_mac() restores the NIC mac through SVQ.
> > > > + *
> > > > + * Return the number of elements added to SVQ if success.
> > > > + */
> > > > +static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
> > > > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> > > >  {
> > > >      uint64_t features = n->parent_obj.guest_features;
> > > >      if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
> > > > -        ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
> > > > -                                                  VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > > > -                                                  n->mac, sizeof(n->mac));
> > > > -        if (unlikely(dev_written < 0)) {
> > > > -            return dev_written;
> > > > -        }
> > > > -
> > > > -        return *s->status != VIRTIO_NET_OK;
> > > > +        return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MAC,
> > > > +                                       VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > > > +                                       n->mac, sizeof(n->mac), in_cursor);
> > > >      }
> > > >
> > > >      return 0;
> > > >  }
> > > >
> > > > -static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
> > > > -                                  const VirtIONet *n)
> > > > +/**
> > > > + * vhost_vdpa_net_load_mac() restores the NIC mq state through SVQ.
> > > > + *
> > > > + * Return the number of elements added to SVQ if success.
> > > > + */
> > > > +static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n,
> > > > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> > > >  {
> > > >      struct virtio_net_ctrl_mq mq;
> > > >      uint64_t features = n->parent_obj.guest_features;
> > > > -    ssize_t dev_written;
> > > >
> > > >      if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
> > > >          return 0;
> > > >      }
> > > >
> > > >      mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
> > > > -    dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
> > > > -                                          VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
> > > > -                                          sizeof(mq));
> > > > -    if (unlikely(dev_written < 0)) {
> > > > -        return dev_written;
> > > > -    }
> > > > -
> > > > -    return *s->status != VIRTIO_NET_OK;
> > > > +    return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MQ,
> > > > +                                   VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
> > > > +                                   &mq, sizeof(mq), in_cursor);
> > > >  }
> > > >
> > > >  static int vhost_vdpa_net_load(NetClientState *nc)
> > > >  {
> > > >      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
> > > > +    VhostShadowVirtqueue *svq;
> > > > +    void *out_cursor;
> > > > +    virtio_net_ctrl_ack *in_cursor;
> > > >      struct vhost_vdpa *v = &s->vhost_vdpa;
> > > >      const VirtIONet *n;
> > > > -    int r;
> > > > +    ssize_t cmds_in_flight = 0, dev_written, r;
> > > >
> > > >      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
> > > >
> > > > @@ -699,14 +750,51 @@ static int vhost_vdpa_net_load(NetClientState *nc)
> > > >      }
> > > >
> > > >      n = VIRTIO_NET(v->dev->vdev);
> > > > -    r = vhost_vdpa_net_load_mac(s, n);
> > > > +    out_cursor = s->cvq_cmd_out_buffer;
> > > > +    in_cursor = s->status;
> > > > +
> > > > +    r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor);
> > > >      if (unlikely(r < 0))
> > > >          return r;
> > > >      }
> > > > -    r = vhost_vdpa_net_load_mq(s, n);
> > > > -    if (unlikely(r)) {
> > > > +    cmds_in_flight += r;
> > > > +
> > > > +    r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor);
> > > > +    if (unlikely(r < 0)) {
> > > >          return r;
> > > >      }
> > > > +    cmds_in_flight += r;
> > > > +
> > > > +    /* Poll for all used buffer from device */
> > > > +    svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > > > +    while (cmds_in_flight > 0) {
> > > > +        /*
> > > > +         * We can poll here since we've had BQL from the time we sent the
> > > > +         * descriptor. Also, we need to take the answer before SVQ pulls
> > > > +         * by itself, when BQL is released
> > > > +         */
> > > > +        dev_written = vhost_svq_poll(svq);
> > >
> > > I'd tweak vhost_svq_poll to accept cmds_in_flight.
>
> That sounds great!
> I will refactor the code here and send the v2 patch after
> your patch.
>
> Thanks!
>
> > >
> > > Thanks
> > >
> > > > +
> > > > +        if (unlikely(!dev_written)) {
> > > > +            /*
> > > > +             * vhost_svq_poll() return 0 when something wrong, such as
> > > > +             * QEMU waits for too long time or no available used buffer
> > > > +             * from device, and there is no need to continue polling
> > > > +             * in this case.
> > > > +             */
> > > > +            return -EINVAL;
> > > > +        }
> > > > +
> > > > +        --cmds_in_flight;
> > > > +    }
> > > > +
> > > > +    /* Check the buffers written by device */
> > > > +    for (virtio_net_ctrl_ack *status = s->status; status < in_cursor;
> > > > +         ++status) {
> > > > +        if (*status != VIRTIO_NET_OK) {
> > > > +            return -EINVAL;
> > > > +        }
> > > > +    }
> > > >
> > > >      return 0;
> > > >  }
> > > > --
> > > > 2.25.1
> > > >
> > >
> >
>



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

* Re: [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel
  2023-05-18  5:46         ` Jason Wang
@ 2023-05-18  6:00           ` Eugenio Perez Martin
  2023-05-18  6:12             ` Jason Wang
  0 siblings, 1 reply; 12+ messages in thread
From: Eugenio Perez Martin @ 2023-05-18  6:00 UTC (permalink / raw)
  To: Jason Wang; +Cc: Hawkins Jiawei, 18801353760, qemu-devel

On Thu, May 18, 2023 at 7:47 AM Jason Wang <jasowang@redhat.com> wrote:
>
> On Wed, May 17, 2023 at 11:02 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
> >
> > Sorry for forgetting cc when replying to the email.
> >
> > On Wed, 17 May 2023 at 16:22, Eugenio Perez Martin <eperezma@redhat.com> wrote:
> > >
> > > On Wed, May 17, 2023 at 7:22 AM Jason Wang <jasowang@redhat.com> wrote:
> > > >
> > > > On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
> > > > >
> > > > > This patch introduces the vhost_vdpa_net_cvq_add() and
> > > > > refactors the vhost_vdpa_net_load*(), so that QEMU can
> > > > > send CVQ state load commands in parallel.
> > > > >
> > > > > To be more specific, this patch introduces vhost_vdpa_net_cvq_add()
> > > > > to add SVQ control commands to SVQ and kick the device,
> > > > > but does not poll the device used buffers. QEMU will not
> > > > > poll and check the device used buffers in vhost_vdpa_net_load()
> > > > > until all CVQ state load commands have been sent to the device.
> > > > >
> > > > > What's more, in order to avoid buffer overwriting caused by
> > > > > using `svq->cvq_cmd_out_buffer` and `svq->status` as the
> > > > > buffer for all CVQ state load commands when sending
> > > > > CVQ state load commands in parallel, this patch introduces
> > > > > `out_cursor` and `in_cursor` in vhost_vdpa_net_load(),
> > > > > pointing to the available buffer for in descriptor and
> > > > > out descriptor, so that different CVQ state load commands can
> > > > > use their unique buffer.
> > > > >
> > > > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
> > > > > Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
> > > > > ---
> > > > >  net/vhost-vdpa.c | 152 +++++++++++++++++++++++++++++++++++++----------
> > > > >  1 file changed, 120 insertions(+), 32 deletions(-)
> > > > >
> > > > > diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
> > > > > index 10804c7200..14e31ca5c5 100644
> > > > > --- a/net/vhost-vdpa.c
> > > > > +++ b/net/vhost-vdpa.c
> > > > > @@ -590,6 +590,44 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
> > > > >      vhost_vdpa_net_client_stop(nc);
> > > > >  }
> > > > >
> > > > > +/**
> > > > > + * vhost_vdpa_net_cvq_add() adds SVQ control commands to SVQ,
> > > > > + * kicks the device but does not poll the device used buffers.
> > > > > + *
> > > > > + * Return the number of elements added to SVQ if success.
> > > > > + */
> > > > > +static int vhost_vdpa_net_cvq_add(VhostVDPAState *s,
> > > > > +                                void **out_cursor, size_t out_len,
> > > >
> > > > Can we track things like cursors in e.g VhostVDPAState ?
> > > >
> > >
> > > Cursors will only be used at device startup. After that, cursors are
> > > always the full buffer. Do we want to "pollute" VhostVDPAState with
> > > things that will not be used after the startup?
>
> So it's the cursor of the cvq_cmd_out_buffer, it would be more elegant
> to keep it with where the cvq_cmd_out_buffer is placed. It can avoid
> passing cursors in several levels.
>

That's right, there is no reason to update at vhost_vdpa_net_cvq_add.
It can be done at the caller.

> Or it would be even better to have some buffer allocation helpers to
> alloc and free in and out buffers.
>

I think that's overkill, as the buffers are always the in and out CVQ
buffers. They are allocated at qemu initialization, and mapped /
unmapped at device start / stop for now.

To manage them dynamically would need code to map them at any time etc.

Thanks!

> Thanks
>
> > >
> > > Maybe we can create a struct for them and then pass just this struct?
> >
> > Yes, Currently, the new version of vhost_vdpa_net_cvq_add() is only
> > called in vhost_vdpa_net_load() at startup, so these cursors will not be
> > used after startup.
> >
> > If needed, we can create a `VhostVDPACursor` as suggested by Eugenio.
> >
> > >
> > > Thanks!
> > >
> > > > > +                                virtio_net_ctrl_ack **in_cursor, size_t in_len)
> > > > > +{
> > > > > +    /* Buffers for the device */
> > > > > +    const struct iovec out = {
> > > > > +        .iov_base = *out_cursor,
> > > > > +        .iov_len = out_len,
> > > > > +    };
> > > > > +    const struct iovec in = {
> > > > > +        .iov_base = *in_cursor,
> > > > > +        .iov_len = sizeof(virtio_net_ctrl_ack),
> > > > > +    };
> > > > > +    VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > > > > +    int r;
> > > > > +
> > > > > +    r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
> > > > > +    if (unlikely(r != 0)) {
> > > > > +        if (unlikely(r == -ENOSPC)) {
> > > > > +            qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
> > > > > +                          __func__);
> > > > > +        }
> > > > > +        return r;
> > > > > +    }
> > > > > +
> > > > > +    /* Update the cursor */
> > > > > +    *out_cursor += out_len;
> > > > > +    *in_cursor += 1;
> > > > > +
> > > > > +    return 1;
> > > > > +}
> > > > > +
> > > > >  /**
> > > > >   * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
> > > > >   * kicks the device and polls the device used buffers.
> > > > > @@ -628,69 +666,82 @@ static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
> > > > >      return vhost_svq_poll(svq);
> > > > >  }
> > > > >
> > > > > -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
> > > > > -                                       uint8_t cmd, const void *data,
> > > > > -                                       size_t data_size)
> > > > > +
> > > > > +/**
> > > > > + * vhost_vdpa_net_load_cmd() restores the NIC state through SVQ.
> > > > > + *
> > > > > + * Return the number of elements added to SVQ if success.
> > > > > + */
> > > > > +static int vhost_vdpa_net_load_cmd(VhostVDPAState *s,
> > > > > +                                void **out_cursor, uint8_t class, uint8_t cmd,
> > > > > +                                const void *data, size_t data_size,
> > > > > +                                virtio_net_ctrl_ack **in_cursor)
> > > > >  {
> > > > >      const struct virtio_net_ctrl_hdr ctrl = {
> > > > >          .class = class,
> > > > >          .cmd = cmd,
> > > > >      };
> > > > >
> > > > > -    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
> > > > > +    assert(sizeof(ctrl) < vhost_vdpa_net_cvq_cmd_page_len() -
> > > > > +                          (*out_cursor - s->cvq_cmd_out_buffer));
> > > > > +    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl) -
> > > > > +                       (*out_cursor - s->cvq_cmd_out_buffer));
> > > > >
> > > > > -    memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
> > > > > -    memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
> > > > > +    memcpy(*out_cursor, &ctrl, sizeof(ctrl));
> > > > > +    memcpy(*out_cursor + sizeof(ctrl), data, data_size);
> > > > >
> > > > > -    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
> > > > > -                                  sizeof(virtio_net_ctrl_ack));
> > > > > +    return vhost_vdpa_net_cvq_add(s, out_cursor, sizeof(ctrl) + data_size,
> > > > > +                                  in_cursor, sizeof(virtio_net_ctrl_ack));
> > > > >  }
> > > > >
> > > > > -static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
> > > > > +/**
> > > > > + * vhost_vdpa_net_load_mac() restores the NIC mac through SVQ.
> > > > > + *
> > > > > + * Return the number of elements added to SVQ if success.
> > > > > + */
> > > > > +static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
> > > > > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> > > > >  {
> > > > >      uint64_t features = n->parent_obj.guest_features;
> > > > >      if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
> > > > > -        ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
> > > > > -                                                  VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > > > > -                                                  n->mac, sizeof(n->mac));
> > > > > -        if (unlikely(dev_written < 0)) {
> > > > > -            return dev_written;
> > > > > -        }
> > > > > -
> > > > > -        return *s->status != VIRTIO_NET_OK;
> > > > > +        return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MAC,
> > > > > +                                       VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > > > > +                                       n->mac, sizeof(n->mac), in_cursor);
> > > > >      }
> > > > >
> > > > >      return 0;
> > > > >  }
> > > > >
> > > > > -static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
> > > > > -                                  const VirtIONet *n)
> > > > > +/**
> > > > > + * vhost_vdpa_net_load_mac() restores the NIC mq state through SVQ.
> > > > > + *
> > > > > + * Return the number of elements added to SVQ if success.
> > > > > + */
> > > > > +static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n,
> > > > > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> > > > >  {
> > > > >      struct virtio_net_ctrl_mq mq;
> > > > >      uint64_t features = n->parent_obj.guest_features;
> > > > > -    ssize_t dev_written;
> > > > >
> > > > >      if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
> > > > >          return 0;
> > > > >      }
> > > > >
> > > > >      mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
> > > > > -    dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
> > > > > -                                          VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
> > > > > -                                          sizeof(mq));
> > > > > -    if (unlikely(dev_written < 0)) {
> > > > > -        return dev_written;
> > > > > -    }
> > > > > -
> > > > > -    return *s->status != VIRTIO_NET_OK;
> > > > > +    return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MQ,
> > > > > +                                   VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
> > > > > +                                   &mq, sizeof(mq), in_cursor);
> > > > >  }
> > > > >
> > > > >  static int vhost_vdpa_net_load(NetClientState *nc)
> > > > >  {
> > > > >      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
> > > > > +    VhostShadowVirtqueue *svq;
> > > > > +    void *out_cursor;
> > > > > +    virtio_net_ctrl_ack *in_cursor;
> > > > >      struct vhost_vdpa *v = &s->vhost_vdpa;
> > > > >      const VirtIONet *n;
> > > > > -    int r;
> > > > > +    ssize_t cmds_in_flight = 0, dev_written, r;
> > > > >
> > > > >      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
> > > > >
> > > > > @@ -699,14 +750,51 @@ static int vhost_vdpa_net_load(NetClientState *nc)
> > > > >      }
> > > > >
> > > > >      n = VIRTIO_NET(v->dev->vdev);
> > > > > -    r = vhost_vdpa_net_load_mac(s, n);
> > > > > +    out_cursor = s->cvq_cmd_out_buffer;
> > > > > +    in_cursor = s->status;
> > > > > +
> > > > > +    r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor);
> > > > >      if (unlikely(r < 0))
> > > > >          return r;
> > > > >      }
> > > > > -    r = vhost_vdpa_net_load_mq(s, n);
> > > > > -    if (unlikely(r)) {
> > > > > +    cmds_in_flight += r;
> > > > > +
> > > > > +    r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor);
> > > > > +    if (unlikely(r < 0)) {
> > > > >          return r;
> > > > >      }
> > > > > +    cmds_in_flight += r;
> > > > > +
> > > > > +    /* Poll for all used buffer from device */
> > > > > +    svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > > > > +    while (cmds_in_flight > 0) {
> > > > > +        /*
> > > > > +         * We can poll here since we've had BQL from the time we sent the
> > > > > +         * descriptor. Also, we need to take the answer before SVQ pulls
> > > > > +         * by itself, when BQL is released
> > > > > +         */
> > > > > +        dev_written = vhost_svq_poll(svq);
> > > >
> > > > I'd tweak vhost_svq_poll to accept cmds_in_flight.
> >
> > That sounds great!
> > I will refactor the code here and send the v2 patch after
> > your patch.
> >
> > Thanks!
> >
> > > >
> > > > Thanks
> > > >
> > > > > +
> > > > > +        if (unlikely(!dev_written)) {
> > > > > +            /*
> > > > > +             * vhost_svq_poll() return 0 when something wrong, such as
> > > > > +             * QEMU waits for too long time or no available used buffer
> > > > > +             * from device, and there is no need to continue polling
> > > > > +             * in this case.
> > > > > +             */
> > > > > +            return -EINVAL;
> > > > > +        }
> > > > > +
> > > > > +        --cmds_in_flight;
> > > > > +    }
> > > > > +
> > > > > +    /* Check the buffers written by device */
> > > > > +    for (virtio_net_ctrl_ack *status = s->status; status < in_cursor;
> > > > > +         ++status) {
> > > > > +        if (*status != VIRTIO_NET_OK) {
> > > > > +            return -EINVAL;
> > > > > +        }
> > > > > +    }
> > > > >
> > > > >      return 0;
> > > > >  }
> > > > > --
> > > > > 2.25.1
> > > > >
> > > >
> > >
> >
>



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

* Re: [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel
  2023-05-18  6:00           ` Eugenio Perez Martin
@ 2023-05-18  6:12             ` Jason Wang
  2023-05-18  6:54               ` Hawkins Jiawei
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Wang @ 2023-05-18  6:12 UTC (permalink / raw)
  To: Eugenio Perez Martin; +Cc: Hawkins Jiawei, 18801353760, qemu-devel

On Thu, May 18, 2023 at 2:00 PM Eugenio Perez Martin
<eperezma@redhat.com> wrote:
>
> On Thu, May 18, 2023 at 7:47 AM Jason Wang <jasowang@redhat.com> wrote:
> >
> > On Wed, May 17, 2023 at 11:02 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
> > >
> > > Sorry for forgetting cc when replying to the email.
> > >
> > > On Wed, 17 May 2023 at 16:22, Eugenio Perez Martin <eperezma@redhat.com> wrote:
> > > >
> > > > On Wed, May 17, 2023 at 7:22 AM Jason Wang <jasowang@redhat.com> wrote:
> > > > >
> > > > > On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
> > > > > >
> > > > > > This patch introduces the vhost_vdpa_net_cvq_add() and
> > > > > > refactors the vhost_vdpa_net_load*(), so that QEMU can
> > > > > > send CVQ state load commands in parallel.
> > > > > >
> > > > > > To be more specific, this patch introduces vhost_vdpa_net_cvq_add()
> > > > > > to add SVQ control commands to SVQ and kick the device,
> > > > > > but does not poll the device used buffers. QEMU will not
> > > > > > poll and check the device used buffers in vhost_vdpa_net_load()
> > > > > > until all CVQ state load commands have been sent to the device.
> > > > > >
> > > > > > What's more, in order to avoid buffer overwriting caused by
> > > > > > using `svq->cvq_cmd_out_buffer` and `svq->status` as the
> > > > > > buffer for all CVQ state load commands when sending
> > > > > > CVQ state load commands in parallel, this patch introduces
> > > > > > `out_cursor` and `in_cursor` in vhost_vdpa_net_load(),
> > > > > > pointing to the available buffer for in descriptor and
> > > > > > out descriptor, so that different CVQ state load commands can
> > > > > > use their unique buffer.
> > > > > >
> > > > > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
> > > > > > Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
> > > > > > ---
> > > > > >  net/vhost-vdpa.c | 152 +++++++++++++++++++++++++++++++++++++----------
> > > > > >  1 file changed, 120 insertions(+), 32 deletions(-)
> > > > > >
> > > > > > diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
> > > > > > index 10804c7200..14e31ca5c5 100644
> > > > > > --- a/net/vhost-vdpa.c
> > > > > > +++ b/net/vhost-vdpa.c
> > > > > > @@ -590,6 +590,44 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
> > > > > >      vhost_vdpa_net_client_stop(nc);
> > > > > >  }
> > > > > >
> > > > > > +/**
> > > > > > + * vhost_vdpa_net_cvq_add() adds SVQ control commands to SVQ,
> > > > > > + * kicks the device but does not poll the device used buffers.
> > > > > > + *
> > > > > > + * Return the number of elements added to SVQ if success.
> > > > > > + */
> > > > > > +static int vhost_vdpa_net_cvq_add(VhostVDPAState *s,
> > > > > > +                                void **out_cursor, size_t out_len,
> > > > >
> > > > > Can we track things like cursors in e.g VhostVDPAState ?
> > > > >
> > > >
> > > > Cursors will only be used at device startup. After that, cursors are
> > > > always the full buffer. Do we want to "pollute" VhostVDPAState with
> > > > things that will not be used after the startup?
> >
> > So it's the cursor of the cvq_cmd_out_buffer, it would be more elegant
> > to keep it with where the cvq_cmd_out_buffer is placed. It can avoid
> > passing cursors in several levels.
> >
>
> That's right, there is no reason to update at vhost_vdpa_net_cvq_add.
> It can be done at the caller.
>
> > Or it would be even better to have some buffer allocation helpers to
> > alloc and free in and out buffers.
> >
>
> I think that's overkill, as the buffers are always the in and out CVQ
> buffers. They are allocated at qemu initialization, and mapped /
> unmapped at device start / stop for now.

It's not a must, we can do it if it has more users for sure.

>
> To manage them dynamically would need code to map them at any time etc.

Thanks

>
> Thanks!
>
> > Thanks
> >
> > > >
> > > > Maybe we can create a struct for them and then pass just this struct?
> > >
> > > Yes, Currently, the new version of vhost_vdpa_net_cvq_add() is only
> > > called in vhost_vdpa_net_load() at startup, so these cursors will not be
> > > used after startup.
> > >
> > > If needed, we can create a `VhostVDPACursor` as suggested by Eugenio.
> > >
> > > >
> > > > Thanks!
> > > >
> > > > > > +                                virtio_net_ctrl_ack **in_cursor, size_t in_len)
> > > > > > +{
> > > > > > +    /* Buffers for the device */
> > > > > > +    const struct iovec out = {
> > > > > > +        .iov_base = *out_cursor,
> > > > > > +        .iov_len = out_len,
> > > > > > +    };
> > > > > > +    const struct iovec in = {
> > > > > > +        .iov_base = *in_cursor,
> > > > > > +        .iov_len = sizeof(virtio_net_ctrl_ack),
> > > > > > +    };
> > > > > > +    VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > > > > > +    int r;
> > > > > > +
> > > > > > +    r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
> > > > > > +    if (unlikely(r != 0)) {
> > > > > > +        if (unlikely(r == -ENOSPC)) {
> > > > > > +            qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
> > > > > > +                          __func__);
> > > > > > +        }
> > > > > > +        return r;
> > > > > > +    }
> > > > > > +
> > > > > > +    /* Update the cursor */
> > > > > > +    *out_cursor += out_len;
> > > > > > +    *in_cursor += 1;
> > > > > > +
> > > > > > +    return 1;
> > > > > > +}
> > > > > > +
> > > > > >  /**
> > > > > >   * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
> > > > > >   * kicks the device and polls the device used buffers.
> > > > > > @@ -628,69 +666,82 @@ static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
> > > > > >      return vhost_svq_poll(svq);
> > > > > >  }
> > > > > >
> > > > > > -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
> > > > > > -                                       uint8_t cmd, const void *data,
> > > > > > -                                       size_t data_size)
> > > > > > +
> > > > > > +/**
> > > > > > + * vhost_vdpa_net_load_cmd() restores the NIC state through SVQ.
> > > > > > + *
> > > > > > + * Return the number of elements added to SVQ if success.
> > > > > > + */
> > > > > > +static int vhost_vdpa_net_load_cmd(VhostVDPAState *s,
> > > > > > +                                void **out_cursor, uint8_t class, uint8_t cmd,
> > > > > > +                                const void *data, size_t data_size,
> > > > > > +                                virtio_net_ctrl_ack **in_cursor)
> > > > > >  {
> > > > > >      const struct virtio_net_ctrl_hdr ctrl = {
> > > > > >          .class = class,
> > > > > >          .cmd = cmd,
> > > > > >      };
> > > > > >
> > > > > > -    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
> > > > > > +    assert(sizeof(ctrl) < vhost_vdpa_net_cvq_cmd_page_len() -
> > > > > > +                          (*out_cursor - s->cvq_cmd_out_buffer));
> > > > > > +    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl) -
> > > > > > +                       (*out_cursor - s->cvq_cmd_out_buffer));
> > > > > >
> > > > > > -    memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
> > > > > > -    memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
> > > > > > +    memcpy(*out_cursor, &ctrl, sizeof(ctrl));
> > > > > > +    memcpy(*out_cursor + sizeof(ctrl), data, data_size);
> > > > > >
> > > > > > -    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
> > > > > > -                                  sizeof(virtio_net_ctrl_ack));
> > > > > > +    return vhost_vdpa_net_cvq_add(s, out_cursor, sizeof(ctrl) + data_size,
> > > > > > +                                  in_cursor, sizeof(virtio_net_ctrl_ack));
> > > > > >  }
> > > > > >
> > > > > > -static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
> > > > > > +/**
> > > > > > + * vhost_vdpa_net_load_mac() restores the NIC mac through SVQ.
> > > > > > + *
> > > > > > + * Return the number of elements added to SVQ if success.
> > > > > > + */
> > > > > > +static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
> > > > > > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> > > > > >  {
> > > > > >      uint64_t features = n->parent_obj.guest_features;
> > > > > >      if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
> > > > > > -        ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
> > > > > > -                                                  VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > > > > > -                                                  n->mac, sizeof(n->mac));
> > > > > > -        if (unlikely(dev_written < 0)) {
> > > > > > -            return dev_written;
> > > > > > -        }
> > > > > > -
> > > > > > -        return *s->status != VIRTIO_NET_OK;
> > > > > > +        return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MAC,
> > > > > > +                                       VIRTIO_NET_CTRL_MAC_ADDR_SET,
> > > > > > +                                       n->mac, sizeof(n->mac), in_cursor);
> > > > > >      }
> > > > > >
> > > > > >      return 0;
> > > > > >  }
> > > > > >
> > > > > > -static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
> > > > > > -                                  const VirtIONet *n)
> > > > > > +/**
> > > > > > + * vhost_vdpa_net_load_mac() restores the NIC mq state through SVQ.
> > > > > > + *
> > > > > > + * Return the number of elements added to SVQ if success.
> > > > > > + */
> > > > > > +static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n,
> > > > > > +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
> > > > > >  {
> > > > > >      struct virtio_net_ctrl_mq mq;
> > > > > >      uint64_t features = n->parent_obj.guest_features;
> > > > > > -    ssize_t dev_written;
> > > > > >
> > > > > >      if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
> > > > > >          return 0;
> > > > > >      }
> > > > > >
> > > > > >      mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
> > > > > > -    dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
> > > > > > -                                          VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
> > > > > > -                                          sizeof(mq));
> > > > > > -    if (unlikely(dev_written < 0)) {
> > > > > > -        return dev_written;
> > > > > > -    }
> > > > > > -
> > > > > > -    return *s->status != VIRTIO_NET_OK;
> > > > > > +    return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MQ,
> > > > > > +                                   VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
> > > > > > +                                   &mq, sizeof(mq), in_cursor);
> > > > > >  }
> > > > > >
> > > > > >  static int vhost_vdpa_net_load(NetClientState *nc)
> > > > > >  {
> > > > > >      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
> > > > > > +    VhostShadowVirtqueue *svq;
> > > > > > +    void *out_cursor;
> > > > > > +    virtio_net_ctrl_ack *in_cursor;
> > > > > >      struct vhost_vdpa *v = &s->vhost_vdpa;
> > > > > >      const VirtIONet *n;
> > > > > > -    int r;
> > > > > > +    ssize_t cmds_in_flight = 0, dev_written, r;
> > > > > >
> > > > > >      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
> > > > > >
> > > > > > @@ -699,14 +750,51 @@ static int vhost_vdpa_net_load(NetClientState *nc)
> > > > > >      }
> > > > > >
> > > > > >      n = VIRTIO_NET(v->dev->vdev);
> > > > > > -    r = vhost_vdpa_net_load_mac(s, n);
> > > > > > +    out_cursor = s->cvq_cmd_out_buffer;
> > > > > > +    in_cursor = s->status;
> > > > > > +
> > > > > > +    r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor);
> > > > > >      if (unlikely(r < 0))
> > > > > >          return r;
> > > > > >      }
> > > > > > -    r = vhost_vdpa_net_load_mq(s, n);
> > > > > > -    if (unlikely(r)) {
> > > > > > +    cmds_in_flight += r;
> > > > > > +
> > > > > > +    r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor);
> > > > > > +    if (unlikely(r < 0)) {
> > > > > >          return r;
> > > > > >      }
> > > > > > +    cmds_in_flight += r;
> > > > > > +
> > > > > > +    /* Poll for all used buffer from device */
> > > > > > +    svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
> > > > > > +    while (cmds_in_flight > 0) {
> > > > > > +        /*
> > > > > > +         * We can poll here since we've had BQL from the time we sent the
> > > > > > +         * descriptor. Also, we need to take the answer before SVQ pulls
> > > > > > +         * by itself, when BQL is released
> > > > > > +         */
> > > > > > +        dev_written = vhost_svq_poll(svq);
> > > > >
> > > > > I'd tweak vhost_svq_poll to accept cmds_in_flight.
> > >
> > > That sounds great!
> > > I will refactor the code here and send the v2 patch after
> > > your patch.
> > >
> > > Thanks!
> > >
> > > > >
> > > > > Thanks
> > > > >
> > > > > > +
> > > > > > +        if (unlikely(!dev_written)) {
> > > > > > +            /*
> > > > > > +             * vhost_svq_poll() return 0 when something wrong, such as
> > > > > > +             * QEMU waits for too long time or no available used buffer
> > > > > > +             * from device, and there is no need to continue polling
> > > > > > +             * in this case.
> > > > > > +             */
> > > > > > +            return -EINVAL;
> > > > > > +        }
> > > > > > +
> > > > > > +        --cmds_in_flight;
> > > > > > +    }
> > > > > > +
> > > > > > +    /* Check the buffers written by device */
> > > > > > +    for (virtio_net_ctrl_ack *status = s->status; status < in_cursor;
> > > > > > +         ++status) {
> > > > > > +        if (*status != VIRTIO_NET_OK) {
> > > > > > +            return -EINVAL;
> > > > > > +        }
> > > > > > +    }
> > > > > >
> > > > > >      return 0;
> > > > > >  }
> > > > > > --
> > > > > > 2.25.1
> > > > > >
> > > > >
> > > >
> > >
> >
>



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

* Re: [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel
  2023-05-18  6:12             ` Jason Wang
@ 2023-05-18  6:54               ` Hawkins Jiawei
  0 siblings, 0 replies; 12+ messages in thread
From: Hawkins Jiawei @ 2023-05-18  6:54 UTC (permalink / raw)
  To: Jason Wang, Eugenio Perez Martin; +Cc: 18801353760, qemu-devel

On 2023/5/18 14:12, Jason Wang wrote:
> On Thu, May 18, 2023 at 2:00 PM Eugenio Perez Martin
> <eperezma@redhat.com> wrote:
>>
>> On Thu, May 18, 2023 at 7:47 AM Jason Wang <jasowang@redhat.com> wrote:
>>>
>>> On Wed, May 17, 2023 at 11:02 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
>>>>
>>>> Sorry for forgetting cc when replying to the email.
>>>>
>>>> On Wed, 17 May 2023 at 16:22, Eugenio Perez Martin <eperezma@redhat.com> wrote:
>>>>>
>>>>> On Wed, May 17, 2023 at 7:22 AM Jason Wang <jasowang@redhat.com> wrote:
>>>>>>
>>>>>> On Sat, May 6, 2023 at 10:07 PM Hawkins Jiawei <yin31149@gmail.com> wrote:
>>>>>>>
>>>>>>> This patch introduces the vhost_vdpa_net_cvq_add() and
>>>>>>> refactors the vhost_vdpa_net_load*(), so that QEMU can
>>>>>>> send CVQ state load commands in parallel.
>>>>>>>
>>>>>>> To be more specific, this patch introduces vhost_vdpa_net_cvq_add()
>>>>>>> to add SVQ control commands to SVQ and kick the device,
>>>>>>> but does not poll the device used buffers. QEMU will not
>>>>>>> poll and check the device used buffers in vhost_vdpa_net_load()
>>>>>>> until all CVQ state load commands have been sent to the device.
>>>>>>>
>>>>>>> What's more, in order to avoid buffer overwriting caused by
>>>>>>> using `svq->cvq_cmd_out_buffer` and `svq->status` as the
>>>>>>> buffer for all CVQ state load commands when sending
>>>>>>> CVQ state load commands in parallel, this patch introduces
>>>>>>> `out_cursor` and `in_cursor` in vhost_vdpa_net_load(),
>>>>>>> pointing to the available buffer for in descriptor and
>>>>>>> out descriptor, so that different CVQ state load commands can
>>>>>>> use their unique buffer.
>>>>>>>
>>>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
>>>>>>> Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
>>>>>>> ---
>>>>>>>   net/vhost-vdpa.c | 152 +++++++++++++++++++++++++++++++++++++----------
>>>>>>>   1 file changed, 120 insertions(+), 32 deletions(-)
>>>>>>>
>>>>>>> diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
>>>>>>> index 10804c7200..14e31ca5c5 100644
>>>>>>> --- a/net/vhost-vdpa.c
>>>>>>> +++ b/net/vhost-vdpa.c
>>>>>>> @@ -590,6 +590,44 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
>>>>>>>       vhost_vdpa_net_client_stop(nc);
>>>>>>>   }
>>>>>>>
>>>>>>> +/**
>>>>>>> + * vhost_vdpa_net_cvq_add() adds SVQ control commands to SVQ,
>>>>>>> + * kicks the device but does not poll the device used buffers.
>>>>>>> + *
>>>>>>> + * Return the number of elements added to SVQ if success.
>>>>>>> + */
>>>>>>> +static int vhost_vdpa_net_cvq_add(VhostVDPAState *s,
>>>>>>> +                                void **out_cursor, size_t out_len,
>>>>>>
>>>>>> Can we track things like cursors in e.g VhostVDPAState ?
>>>>>>
>>>>>
>>>>> Cursors will only be used at device startup. After that, cursors are
>>>>> always the full buffer. Do we want to "pollute" VhostVDPAState with
>>>>> things that will not be used after the startup?
>>>
>>> So it's the cursor of the cvq_cmd_out_buffer, it would be more elegant
>>> to keep it with where the cvq_cmd_out_buffer is placed. It can avoid
>>> passing cursors in several levels.
>>> >>
>> That's right, there is no reason to update at vhost_vdpa_net_cvq_add.
>> It can be done at the caller.

But in any case, these cursors need to be passed as alone parameters to
vhost_vdpa_net_cvq_add(), so that they can be accessed for
`struct iovec` `iov_base` field, right? Considering that
we always pass these parameters, so I also update them together in
vhost_vdpa_net_cvq_add() in this patch.

If we want to avoid passing cursors in several levels, is it okay
to backup `cvq_cmd_out_buffer` and `status` in vhost_vdpa_net_load(),
access and update them through `VhostVDPAState` directly during loading,
and restore them when finishing loading.

>>
>>> Or it would be even better to have some buffer allocation helpers to
>>> alloc and free in and out buffers.
>>>
>>
>> I think that's overkill, as the buffers are always the in and out CVQ
>> buffers. They are allocated at qemu initialization, and mapped /
>> unmapped at device start / stop for now.
>
> It's not a must, we can do it if it has more users for sure.
>
>>
>> To manage them dynamically would need code to map them at any time etc.
>
> Thanks
>
>>
>> Thanks!
>>
>>> Thanks
>>>
>>>>>
>>>>> Maybe we can create a struct for them and then pass just this struct?
>>>>
>>>> Yes, Currently, the new version of vhost_vdpa_net_cvq_add() is only
>>>> called in vhost_vdpa_net_load() at startup, so these cursors will not be
>>>> used after startup.
>>>>
>>>> If needed, we can create a `VhostVDPACursor` as suggested by Eugenio.
>>>>
>>>>>
>>>>> Thanks!
>>>>>
>>>>>>> +                                virtio_net_ctrl_ack **in_cursor, size_t in_len)
>>>>>>> +{
>>>>>>> +    /* Buffers for the device */
>>>>>>> +    const struct iovec out = {
>>>>>>> +        .iov_base = *out_cursor,
>>>>>>> +        .iov_len = out_len,
>>>>>>> +    };
>>>>>>> +    const struct iovec in = {
>>>>>>> +        .iov_base = *in_cursor,
>>>>>>> +        .iov_len = sizeof(virtio_net_ctrl_ack),
>>>>>>> +    };
>>>>>>> +    VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
>>>>>>> +    int r;
>>>>>>> +
>>>>>>> +    r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
>>>>>>> +    if (unlikely(r != 0)) {
>>>>>>> +        if (unlikely(r == -ENOSPC)) {
>>>>>>> +            qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
>>>>>>> +                          __func__);
>>>>>>> +        }
>>>>>>> +        return r;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    /* Update the cursor */
>>>>>>> +    *out_cursor += out_len;
>>>>>>> +    *in_cursor += 1;
>>>>>>> +
>>>>>>> +    return 1;
>>>>>>> +}
>>>>>>> +
>>>>>>>   /**
>>>>>>>    * vhost_vdpa_net_cvq_add_and_wait() adds SVQ control commands to SVQ,
>>>>>>>    * kicks the device and polls the device used buffers.
>>>>>>> @@ -628,69 +666,82 @@ static ssize_t vhost_vdpa_net_cvq_add_and_wait(VhostVDPAState *s,
>>>>>>>       return vhost_svq_poll(svq);
>>>>>>>   }
>>>>>>>
>>>>>>> -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
>>>>>>> -                                       uint8_t cmd, const void *data,
>>>>>>> -                                       size_t data_size)
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * vhost_vdpa_net_load_cmd() restores the NIC state through SVQ.
>>>>>>> + *
>>>>>>> + * Return the number of elements added to SVQ if success.
>>>>>>> + */
>>>>>>> +static int vhost_vdpa_net_load_cmd(VhostVDPAState *s,
>>>>>>> +                                void **out_cursor, uint8_t class, uint8_t cmd,
>>>>>>> +                                const void *data, size_t data_size,
>>>>>>> +                                virtio_net_ctrl_ack **in_cursor)
>>>>>>>   {
>>>>>>>       const struct virtio_net_ctrl_hdr ctrl = {
>>>>>>>           .class = class,
>>>>>>>           .cmd = cmd,
>>>>>>>       };
>>>>>>>
>>>>>>> -    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
>>>>>>> +    assert(sizeof(ctrl) < vhost_vdpa_net_cvq_cmd_page_len() -
>>>>>>> +                          (*out_cursor - s->cvq_cmd_out_buffer));
>>>>>>> +    assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl) -
>>>>>>> +                       (*out_cursor - s->cvq_cmd_out_buffer));
>>>>>>>
>>>>>>> -    memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
>>>>>>> -    memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
>>>>>>> +    memcpy(*out_cursor, &ctrl, sizeof(ctrl));
>>>>>>> +    memcpy(*out_cursor + sizeof(ctrl), data, data_size);
>>>>>>>
>>>>>>> -    return vhost_vdpa_net_cvq_add_and_wait(s, sizeof(ctrl) + data_size,
>>>>>>> -                                  sizeof(virtio_net_ctrl_ack));
>>>>>>> +    return vhost_vdpa_net_cvq_add(s, out_cursor, sizeof(ctrl) + data_size,
>>>>>>> +                                  in_cursor, sizeof(virtio_net_ctrl_ack));
>>>>>>>   }
>>>>>>>
>>>>>>> -static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
>>>>>>> +/**
>>>>>>> + * vhost_vdpa_net_load_mac() restores the NIC mac through SVQ.
>>>>>>> + *
>>>>>>> + * Return the number of elements added to SVQ if success.
>>>>>>> + */
>>>>>>> +static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
>>>>>>> +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
>>>>>>>   {
>>>>>>>       uint64_t features = n->parent_obj.guest_features;
>>>>>>>       if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
>>>>>>> -        ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
>>>>>>> -                                                  VIRTIO_NET_CTRL_MAC_ADDR_SET,
>>>>>>> -                                                  n->mac, sizeof(n->mac));
>>>>>>> -        if (unlikely(dev_written < 0)) {
>>>>>>> -            return dev_written;
>>>>>>> -        }
>>>>>>> -
>>>>>>> -        return *s->status != VIRTIO_NET_OK;
>>>>>>> +        return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MAC,
>>>>>>> +                                       VIRTIO_NET_CTRL_MAC_ADDR_SET,
>>>>>>> +                                       n->mac, sizeof(n->mac), in_cursor);
>>>>>>>       }
>>>>>>>
>>>>>>>       return 0;
>>>>>>>   }
>>>>>>>
>>>>>>> -static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
>>>>>>> -                                  const VirtIONet *n)
>>>>>>> +/**
>>>>>>> + * vhost_vdpa_net_load_mac() restores the NIC mq state through SVQ.
>>>>>>> + *
>>>>>>> + * Return the number of elements added to SVQ if success.
>>>>>>> + */
>>>>>>> +static int vhost_vdpa_net_load_mq(VhostVDPAState *s, const VirtIONet *n,
>>>>>>> +                            void **out_cursor, virtio_net_ctrl_ack **in_cursor)
>>>>>>>   {
>>>>>>>       struct virtio_net_ctrl_mq mq;
>>>>>>>       uint64_t features = n->parent_obj.guest_features;
>>>>>>> -    ssize_t dev_written;
>>>>>>>
>>>>>>>       if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
>>>>>>>           return 0;
>>>>>>>       }
>>>>>>>
>>>>>>>       mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
>>>>>>> -    dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
>>>>>>> -                                          VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
>>>>>>> -                                          sizeof(mq));
>>>>>>> -    if (unlikely(dev_written < 0)) {
>>>>>>> -        return dev_written;
>>>>>>> -    }
>>>>>>> -
>>>>>>> -    return *s->status != VIRTIO_NET_OK;
>>>>>>> +    return vhost_vdpa_net_load_cmd(s, out_cursor, VIRTIO_NET_CTRL_MQ,
>>>>>>> +                                   VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
>>>>>>> +                                   &mq, sizeof(mq), in_cursor);
>>>>>>>   }
>>>>>>>
>>>>>>>   static int vhost_vdpa_net_load(NetClientState *nc)
>>>>>>>   {
>>>>>>>       VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
>>>>>>> +    VhostShadowVirtqueue *svq;
>>>>>>> +    void *out_cursor;
>>>>>>> +    virtio_net_ctrl_ack *in_cursor;
>>>>>>>       struct vhost_vdpa *v = &s->vhost_vdpa;
>>>>>>>       const VirtIONet *n;
>>>>>>> -    int r;
>>>>>>> +    ssize_t cmds_in_flight = 0, dev_written, r;
>>>>>>>
>>>>>>>       assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
>>>>>>>
>>>>>>> @@ -699,14 +750,51 @@ static int vhost_vdpa_net_load(NetClientState *nc)
>>>>>>>       }
>>>>>>>
>>>>>>>       n = VIRTIO_NET(v->dev->vdev);
>>>>>>> -    r = vhost_vdpa_net_load_mac(s, n);
>>>>>>> +    out_cursor = s->cvq_cmd_out_buffer;
>>>>>>> +    in_cursor = s->status;
>>>>>>> +
>>>>>>> +    r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor);
>>>>>>>       if (unlikely(r < 0))
>>>>>>>           return r;
>>>>>>>       }
>>>>>>> -    r = vhost_vdpa_net_load_mq(s, n);
>>>>>>> -    if (unlikely(r)) {
>>>>>>> +    cmds_in_flight += r;
>>>>>>> +
>>>>>>> +    r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor);
>>>>>>> +    if (unlikely(r < 0)) {
>>>>>>>           return r;
>>>>>>>       }
>>>>>>> +    cmds_in_flight += r;
>>>>>>> +
>>>>>>> +    /* Poll for all used buffer from device */
>>>>>>> +    svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
>>>>>>> +    while (cmds_in_flight > 0) {
>>>>>>> +        /*
>>>>>>> +         * We can poll here since we've had BQL from the time we sent the
>>>>>>> +         * descriptor. Also, we need to take the answer before SVQ pulls
>>>>>>> +         * by itself, when BQL is released
>>>>>>> +         */
>>>>>>> +        dev_written = vhost_svq_poll(svq);
>>>>>>
>>>>>> I'd tweak vhost_svq_poll to accept cmds_in_flight.
>>>>
>>>> That sounds great!
>>>> I will refactor the code here and send the v2 patch after
>>>> your patch.
>>>>
>>>> Thanks!
>>>>
>>>>>>
>>>>>> Thanks
>>>>>>
>>>>>>> +
>>>>>>> +        if (unlikely(!dev_written)) {
>>>>>>> +            /*
>>>>>>> +             * vhost_svq_poll() return 0 when something wrong, such as
>>>>>>> +             * QEMU waits for too long time or no available used buffer
>>>>>>> +             * from device, and there is no need to continue polling
>>>>>>> +             * in this case.
>>>>>>> +             */
>>>>>>> +            return -EINVAL;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        --cmds_in_flight;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    /* Check the buffers written by device */
>>>>>>> +    for (virtio_net_ctrl_ack *status = s->status; status < in_cursor;
>>>>>>> +         ++status) {
>>>>>>> +        if (*status != VIRTIO_NET_OK) {
>>>>>>> +            return -EINVAL;
>>>>>>> +        }
>>>>>>> +    }
>>>>>>>
>>>>>>>       return 0;
>>>>>>>   }
>>>>>>> --
>>>>>>> 2.25.1
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>


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

end of thread, other threads:[~2023-05-18  6:55 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-06 14:06 [PATCH v2 0/2] Send all the SVQ control commands in parallel Hawkins Jiawei
2023-05-06 14:06 ` [PATCH v2 1/2] vdpa: rename vhost_vdpa_net_cvq_add() Hawkins Jiawei
2023-05-17  4:12   ` Jason Wang
2023-05-17 15:11     ` Hawkins Jiawei
2023-05-06 14:06 ` [PATCH v2 2/2] vdpa: send CVQ state load commands in parallel Hawkins Jiawei
2023-05-17  5:22   ` Jason Wang
2023-05-17  8:21     ` Eugenio Perez Martin
2023-05-17 15:01       ` Hawkins Jiawei
2023-05-18  5:46         ` Jason Wang
2023-05-18  6:00           ` Eugenio Perez Martin
2023-05-18  6:12             ` Jason Wang
2023-05-18  6:54               ` Hawkins Jiawei

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).