>
> On Thu, Aug 27, 2026 at 7:59 AM Jia Jia <[email protected]> wrote:
> >
> > vhost_transport_do_send_pkt() calls vhost_add_used() for every Guest RX
> > buffer even though it delays the Guest signal until the worker finishes.
> > Each call publishes one used entry and updates the used index separately.
> >
> > Collect the completed buffer heads in the arrays already allocated for the
> > virtqueue and publish them with vhost_add_used_n().  Bound the batch by the
> > ring size, array capacity, and worker packet budget.  Flush before
> > re-enabling notifications or leaving the worker.
> >
> > Each used entry describes one completed RX buffer and keeps its actual used
> > length, so set nheads to 1 for every entry.  This patch does not change
> > negotiated features or compress multiple buffers into one used entry.
> >
> > This patch is limited to the current skb-based vhost-vsock RX path.
> >
> > Performance:
> >
> > Tested with a QEMU/KVM guest on a host with 4 online CPUs, using 2 vCPUs
> > pinned to host CPUs 2 and 3, QEMU 10.2.1, q35, 1536 MiB, and Linux
> > 7.2.0-rc3-next-20260713-next-debug-kasan.  The vhost-vsock source is based
> > on linux-next master at 49362394dad7df66c274c867a271394c10ca2bb8.
> >
> > Current vhost-vsock does not implement VIRTIO_F_IN_ORDER or
> > VIRTIO_F_RING_PACKED, so both configurations used packed=off and
> > in_order=off:
> >
> >   baseline:                 RX batching=off
> >   vhost-vsock RX batching:  RX batching=on
> >
> > The test used vsock_perf.  The Guest receiver was started with:
> >
> >   vsock_perf --port PORT --buf-size 64M --vsk-size 64M --rcvlowat 1
> >
> > The Host sender was started with:
> >
> >   vsock_perf --sender 3 --port PORT --bytes BYTES \
> >       --buf-size SEND_BUF --vsk-size 64M
> >
> > Each workload transferred BYTES=1 GiB.  The SEND_BUF values were 256 B
> > (SEND_BUF=256), 512 B (SEND_BUF=512), 4 KiB (SEND_BUF=4K), and 64 KiB
> > (SEND_BUF=64K).
> > Each state used a fresh Guest.  Each workload uses 20 paired runs, with 10
> > runs in each order.  The reported values are
> > Guest RX throughput in Gbits/s.  The baseline and batching columns are the
> > geometric means over the 20 runs; change is batching / baseline - 1,
> > computed from the unrounded values:
> >
> >   workload   baseline RX    batching RX   change   faster
> >   256 B      0.0795724      0.0831509      +4.497%  20/20
> >   512 B      0.1194885      0.1210297      +1.290%  14/20
> >   4 KiB      0.7208273      0.7242053      +0.469%  11/20
> >   64 KiB     2.1712797      2.1951941      +1.101%  13/20
> >
> > For reference, the table below gives the 95% normal-approximation intervals
> > obtained from the 20 paired log(batching / baseline) values:
> >
> >   workload   paired 95% interval
> >   256 B      +3.985% to +5.011%
> >   512 B      +0.206% to +2.385%
> >   4 KiB      -1.442% to +2.416%
> >   64 KiB     -1.474% to +3.745%
> >
> > All transfers passed byte-count checks, and no kernel errors were observed
> > in the logs.  The 256-byte workload improved in every pair.  The 512 B
> > workload was faster in 14 of 20 pairs, with a small gain.  The 4 KiB and
> > 64 KiB workloads showed no material throughput change; the difference
> > between their results may be due to scheduling and execution variation.
> >
> > Link: 
> > https://lore.kernel.org/r/[email protected]
> > Link: 
> > https://lore.kernel.org/r/[email protected]
> > Signed-off-by: Jia Jia <[email protected]>
> > ---
> >  drivers/vhost/vsock.c | 41 +++++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 39 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
> > index 9aaab6bb8..d8050cca2 100644
> > --- a/drivers/vhost/vsock.c
> > +++ b/drivers/vhost/vsock.c
> > @@ -103,12 +103,37 @@ static bool vhost_transport_has_remote_cid(struct 
> > vsock_sock *vsk, u32 cid)
> >         return found;
> >  }
> >
> > +static bool vhost_vsock_flush_used(struct vhost_virtqueue *vq,
> > +                                  unsigned int *used_count)
> > +{
> > +       if (!*used_count)
> > +               return false;
> > +
> > +       vhost_add_used_n(vq, vq->heads, vq->nheads, *used_count);
> > +       *used_count = 0;
> > +       return true;
> > +}
> > +
>
> I think that the used_count should be updated by the caller based on
> the boolean returned by this function.
>

Thanks for the review. I will fix this in v2.

> > +static void vhost_vsock_add_used(struct vhost_virtqueue *vq,
> > +                                unsigned int *used_count,
> > +                                unsigned int head, unsigned int len)
> > +{
> > +       struct vring_used_elem *used = &vq->heads[*used_count];
> > +
> > +       used->id = cpu_to_vhost32(vq, head);
> > +       used->len = cpu_to_vhost32(vq, len);
> > +       vq->nheads[*used_count] = 1;
> > +       (*used_count)++;
> > +}
>
> Same here, why pass it as an argument? The caller knows way better.

I will fix this in v2.

>
> Other than that, the patch looks good to me,
>
> Acked-by: Eugenio Pérez <[email protected]>
>
> Thanks!
>

Thanks!

> > +
> >  static void
> >  vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
> >                             struct vhost_virtqueue *vq)
> >  {
> >         struct vhost_virtqueue *tx_vq = &vsock->vqs[VSOCK_VQ_TX];
> >         int pkts = 0, total_len = 0;
> > +       unsigned int used_count = 0;
> > +       unsigned int used_limit;
> >         bool added = false;
> >         bool restart_tx = false;
> >
> > @@ -120,6 +145,12 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
> >         if (!vq_meta_prefetch(vq))
> >                 goto out;
> >
> > +       used_limit = min_t(unsigned int, vq->num,
> > +                          min_t(unsigned int, vq->dev->iov_limit,
> > +                                vq->dev->weight));
> > +       if (unlikely(!used_limit))
> > +               goto out;
> > +
> >         /* Avoid further vmexits, we're already processing the virtqueue */
> >         vhost_disable_notify(&vsock->dev, vq);
> >
> > @@ -134,9 +165,13 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
> >                 u32 offset;
> >                 int head;
> >
> > +               if (used_count == used_limit)
> > +                       added |= vhost_vsock_flush_used(vq, &used_count);
> > +
> >                 skb = virtio_vsock_skb_dequeue(&vsock->send_pkt_queue);
> >
> >                 if (!skb) {
> > +                       added |= vhost_vsock_flush_used(vq, &used_count);
> >                         vhost_enable_notify(&vsock->dev, vq);
> >                         break;
> >                 }
> > @@ -153,6 +188,7 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
> >                         /* We cannot finish yet if more buffers snuck in 
> > while
> >                          * re-enabling notify.
> >                          */
> > +                       added |= vhost_vsock_flush_used(vq, &used_count);
> >                         if (unlikely(vhost_enable_notify(&vsock->dev, vq))) 
> > {
> >                                 vhost_disable_notify(&vsock->dev, vq);
> >                                 continue;
> > @@ -230,8 +266,8 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
> >                  */
> >                 virtio_transport_deliver_tap_pkt(skb);
> >
> > -               vhost_add_used(vq, head, sizeof(*hdr) + payload_len);
> > -               added = true;
> > +               vhost_vsock_add_used(vq, &used_count, head,
> > +                                    sizeof(*hdr) + payload_len);
> >
> >                 VIRTIO_VSOCK_SKB_CB(skb)->offset += payload_len;
> >                 total_len += payload_len;
> > @@ -264,6 +300,7 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
> >                         virtio_transport_consume_skb_sent(skb, true);
> >                 }
> >         } while(likely(!vhost_exceeds_weight(vq, ++pkts, total_len)));
> > +       added |= vhost_vsock_flush_used(vq, &used_count);
> >         if (added)
> >                 vhost_signal(&vsock->dev, vq);
> >
> > --
> > 2.53.0
> >
>

Reply via email to