Merge branch 'vsock-virtio-fix-skb-overhead-accounting-to-preserve-full-buf_alloc'

Stefano Garzarella says:

====================
vsock/virtio: fix skb overhead accounting to preserve full buf_alloc

Patch 1 resets the connection when we can no longer queue packets,
this prevents silent data loss, and both peers are notified.

Patch 2 increases the total budget to `buf_alloc * 2` for payload
plus skb overhead similar to how SO_RCVBUF is doubled to reserve
space for sk_buff metadata. This preserves the full buf_alloc for
payload under normal operation, while still bounding the skb queue
growth.

In the future, we plan to improve how we handle the merging of packets
to minimize overhead and avoid closing connections.

v3: https://lore.kernel.org/netdev/20260513105417.56761-1-sgarzare@redhat.com/
v2: https://lore.kernel.org/netdev/20260512080737.36787-1-sgarzare@redhat.com/
v1: https://lore.kernel.org/netdev/20260508092330.69690-1-sgarzare@redhat.com/
====================

Link: https://patch.msgid.link/20260518090656.134588-1-sgarzare@redhat.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-05-21 13:14:04 +02:00
commit 94e3dd6874

View File

@ -419,7 +419,14 @@ static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs,
{
u64 skb_overhead = (skb_queue_len(&vvs->rx_queue) + 1) * SKB_TRUESIZE(0);
if (skb_overhead + vvs->buf_used + len > vvs->buf_alloc)
/* Allow at most buf_alloc * 2 total budget (payload + overhead),
* similar to how SO_RCVBUF is doubled to reserve space for sk_buff
* metadata. Check payload against buf_alloc to be sure the other
* peer is respecting the credit, and sk_buff overhead to bound
* queue growth.
*/
if ((u64)vvs->buf_used + len > vvs->buf_alloc ||
skb_overhead > vvs->buf_alloc)
return false;
vvs->rx_bytes += len;
@ -1335,7 +1342,7 @@ virtio_transport_recv_connecting(struct sock *sk,
return err;
}
static void
static bool
virtio_transport_recv_enqueue(struct vsock_sock *vsk,
struct sk_buff *skb)
{
@ -1350,10 +1357,8 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk,
spin_lock_bh(&vvs->rx_lock);
can_enqueue = virtio_transport_inc_rx_pkt(vvs, len);
if (!can_enqueue) {
free_pkt = true;
if (!can_enqueue)
goto out;
}
if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SEQ_EOM)
vvs->msg_count++;
@ -1393,6 +1398,8 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk,
spin_unlock_bh(&vvs->rx_lock);
if (free_pkt)
kfree_skb(skb);
return can_enqueue;
}
static int
@ -1405,7 +1412,17 @@ virtio_transport_recv_connected(struct sock *sk,
switch (le16_to_cpu(hdr->op)) {
case VIRTIO_VSOCK_OP_RW:
virtio_transport_recv_enqueue(vsk, skb);
if (!virtio_transport_recv_enqueue(vsk, skb)) {
/* There is no more space to queue the packet, so let's
* close the connection; otherwise, we'll lose data.
*/
(void)virtio_transport_reset(vsk, skb);
virtio_transport_do_close(vsk, true);
sk->sk_err = ENOBUFS;
sk_error_report(sk);
vsock_remove_sock(vsk);
break;
}
vsock_data_ready(sk);
return err;
case VIRTIO_VSOCK_OP_CREDIT_REQUEST: