Skip to content

Commit 65e808a

Browse files
willdeacongregkh
authored andcommitted
vhost/vsock: Allocate nonlinear SKBs for handling large receive buffers
[Upstream commit ab9aa2f] When receiving a packet from a guest, vhost_vsock_handle_tx_kick() calls vhost_vsock_alloc_linear_skb() to allocate and fill an SKB with the receive data. Unfortunately, these are always linear allocations and can therefore result in significant pressure on kmalloc() considering that the maximum packet size (VIRTIO_VSOCK_MAX_PKT_BUF_SIZE + VIRTIO_VSOCK_SKB_HEADROOM) is a little over 64KiB, resulting in a 128KiB allocation for each packet. Rework the vsock SKB allocation so that, for sizes with page order greater than PAGE_ALLOC_COSTLY_ORDER, a nonlinear SKB is allocated instead with the packet header in the SKB and the receive data in the fragments. Finally, add a debug warning if virtio_vsock_skb_rx_put() is ever called on an SKB with a non-zero length, as this would be destructive for the nonlinear case. Reviewed-by: Stefano Garzarella <sgarzare@redhat.com> Signed-off-by: Will Deacon <will@kernel.org> Message-Id: <20250717090116.11987-8-will@kernel.org> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Heitor Alves de Siqueira <halves@igalia.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 2d651c3 commit 65e808a

2 files changed

Lines changed: 32 additions & 8 deletions

File tree

drivers/vhost/vsock.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ vhost_vsock_alloc_skb(struct vhost_virtqueue *vq,
350350
return NULL;
351351

352352
/* len contains both payload and hdr */
353-
skb = virtio_vsock_alloc_linear_skb(len, GFP_KERNEL);
353+
skb = virtio_vsock_alloc_skb(len, GFP_KERNEL);
354354
if (!skb)
355355
return NULL;
356356

@@ -379,10 +379,8 @@ vhost_vsock_alloc_skb(struct vhost_virtqueue *vq,
379379

380380
virtio_vsock_skb_put(skb, payload_len);
381381

382-
nbytes = copy_from_iter(skb->data, payload_len, &iov_iter);
383-
if (nbytes != payload_len) {
384-
vq_err(vq, "Expected %zu byte payload, got %zu bytes\n",
385-
payload_len, nbytes);
382+
if (skb_copy_datagram_from_iter(skb, 0, &iov_iter, payload_len)) {
383+
vq_err(vq, "Failed to copy %zu byte payload\n", payload_len);
386384
kfree_skb(skb);
387385
return NULL;
388386
}

include/linux/virtio_vsock.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,48 @@ static inline void virtio_vsock_skb_clear_tap_delivered(struct sk_buff *skb)
4949

5050
static inline void virtio_vsock_skb_put(struct sk_buff *skb, u32 len)
5151
{
52-
skb_put(skb, len);
52+
DEBUG_NET_WARN_ON_ONCE(skb->len);
53+
54+
if (skb_is_nonlinear(skb))
55+
skb->len = len;
56+
else
57+
skb_put(skb, len);
5358
}
5459

5560
static inline struct sk_buff *
56-
virtio_vsock_alloc_linear_skb(unsigned int size, gfp_t mask)
61+
__virtio_vsock_alloc_skb_with_frags(unsigned int header_len,
62+
unsigned int data_len,
63+
gfp_t mask)
5764
{
5865
struct sk_buff *skb;
66+
int err;
5967

60-
skb = alloc_skb(size, mask);
68+
skb = alloc_skb_with_frags(header_len, data_len,
69+
PAGE_ALLOC_COSTLY_ORDER, &err, mask);
6170
if (!skb)
6271
return NULL;
6372

6473
skb_reserve(skb, VIRTIO_VSOCK_SKB_HEADROOM);
74+
skb->data_len = data_len;
6575
return skb;
6676
}
6777

78+
static inline struct sk_buff *
79+
virtio_vsock_alloc_linear_skb(unsigned int size, gfp_t mask)
80+
{
81+
return __virtio_vsock_alloc_skb_with_frags(size, 0, mask);
82+
}
83+
84+
static inline struct sk_buff *virtio_vsock_alloc_skb(unsigned int size, gfp_t mask)
85+
{
86+
if (size <= SKB_WITH_OVERHEAD(PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER))
87+
return virtio_vsock_alloc_linear_skb(size, mask);
88+
89+
size -= VIRTIO_VSOCK_SKB_HEADROOM;
90+
return __virtio_vsock_alloc_skb_with_frags(VIRTIO_VSOCK_SKB_HEADROOM,
91+
size, mask);
92+
}
93+
6894
static inline void
6995
virtio_vsock_skb_queue_head(struct sk_buff_head *list, struct sk_buff *skb)
7096
{

0 commit comments

Comments
 (0)