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