Skip to content

Commit ab9aa2f

Browse files
willdeaconmstsirkin
authored andcommitted
vhost/vsock: Allocate nonlinear SKBs for handling large receive buffers
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 <[email protected]> Signed-off-by: Will Deacon <[email protected]> Message-Id: <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]>
1 parent fac6b82 commit ab9aa2f

File tree

2 files changed

+32
-8
lines changed

2 files changed

+32
-8
lines changed

drivers/vhost/vsock.c

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

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

@@ -378,10 +378,8 @@ vhost_vsock_alloc_skb(struct vhost_virtqueue *vq,
378378

379379
virtio_vsock_skb_rx_put(skb, payload_len);
380380

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

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