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 and array capacity. Flush when the batch reaches its limit or before 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: The test used vsock_perf with a fresh Guest for each state and 20 paired runs. The Guest receiver used: vsock_perf --port PORT --buf-size 64M --vsk-size 64M --rcvlowat 1 The Host sender used: vsock_perf --sender 3 --port PORT --bytes 1G \ --buf-size SEND_BUF --vsk-size 64M The table reports geometric mean Guest RX throughput. SEND_BUF baseline RX batching RX change (Gbit/s) (Gbit/s) 256 B 0.0795724 0.0831509 +4.497% 512 B 0.1194885 0.1210297 +1.290% 4 KiB 0.7208273 0.7242053 +0.469% 64 KiB 2.1712797 2.1951941 +1.101% As supplementary data, perf stat measured the vhost worker cycles and instructions per GiB in 10 paired runs. The patched implementation reduced cycles by 3.846%, 2.162%, and 4.109%, and instructions by 2.548%, 2.084%, and 4.637% for 256 B, 4 KiB, and 64 KiB, respectively. An AF_VSOCK request-response latency test with 10 AB/BA pairs showed no consistent RTT change: -0.102% for 256-byte messages (4/10 pairs lower) and +3.281% for 4-KiB messages (5/10 pairs lower), using arithmetic mean RTTs. Signed-off-by: Jia Jia Acked-by: Eugenio Pérez --- Changes in v4: - Simplify the performance description. - Add a brief AF_VSOCK RTT measurement. - Keep the batch limit within the used ring and scratch arrays, without duplicating the worker weight limit. - Flush after adding a used entry when the batch reaches the limit, and remove the redundant flush in the empty-queue path. --- drivers/vhost/vsock.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c index 9aaab6bb8061..7a13abe73345 100644 --- a/drivers/vhost/vsock.c +++ b/drivers/vhost/vsock.c @@ -103,12 +103,35 @@ 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); + return true; +} + +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; +} + 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 +143,11 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock, if (!vq_meta_prefetch(vq)) goto out; + /* Keep the batch within the used ring and the scratch arrays. */ + used_limit = min_t(unsigned int, vq->num, vq->dev->iov_limit); + if (unlikely(!used_limit)) + goto out; + /* Avoid further vmexits, we're already processing the virtqueue */ vhost_disable_notify(&vsock->dev, vq); @@ -150,9 +178,16 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock, if (head == vq->num) { virtio_vsock_skb_queue_head(&vsock->send_pkt_queue, skb); + + /* Flush completed buffers before re-enabling notifications. */ + if (vhost_vsock_flush_used(vq, used_count)) { + added = true; + used_count = 0; + } + /* We cannot finish yet if more buffers snuck in while * re-enabling notify. */ if (unlikely(vhost_enable_notify(&vsock->dev, vq))) { vhost_disable_notify(&vsock->dev, vq); continue; @@ -230,8 +265,13 @@ 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); + used_count++; + if (used_count == used_limit) { + added |= vhost_vsock_flush_used(vq, used_count); + used_count = 0; + } VIRTIO_VSOCK_SKB_CB(skb)->offset += payload_len; total_len += payload_len; @@ -264,6 +304,9 @@ 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.34.1