virtio_net: Add XDP frame size in two code paths
The virtio_net driver is running inside the guest-OS. There are two XDP receive code-paths in virtio_net, namely receive_small() and receive_mergeable(). The receive_big() function does not support XDP. In receive_small() the frame size is available in buflen. The buffer backing these frames are allocated in add_recvbuf_small() with same size, except for the headroom, but tailroom have reserved room for skb_shared_info. The headroom is encoded in ctx pointer as a value. In receive_mergeable() the frame size is more dynamic. There are two basic cases: (1) buffer size is based on a exponentially weighted moving average (see DECLARE_EWMA) of packet length. Or (2) in case virtnet_get_headroom() have any headroom then buffer size is PAGE_SIZE. The ctx pointer is this time used for encoding two values; the buffer len "truesize" and headroom. In case (1) if the rx buffer size is underestimated, the packet will have been split over more buffers (num_buf info in virtio_net_hdr_mrg_rxbuf placed in top of buffer area). If that happens the XDP path does a xdp_linearize_page operation. V3: Adjust frame_sz in receive_mergeable() case, spotted by Jason Wang. The code is really hard to follow, so some hints to reviewers. The receive_mergeable() case gets frames that were allocated in add_recvbuf_mergeable() which uses headroom=virtnet_get_headroom(), and 'buf' ptr is advanced this headroom. The headroom can only be 0 or VIRTIO_XDP_HEADROOM, as virtnet_get_headroom is really simple: static unsigned int virtnet_get_headroom(struct virtnet_info *vi) { return vi->xdp_queue_pairs ? VIRTIO_XDP_HEADROOM : 0; } As frame_sz is an offset size from xdp.data_hard_start, reviewers should notice how this is calculated in receive_mergeable(): int offset = buf - page_address(page); [...] data = page_address(xdp_page) + offset; xdp.data_hard_start = data - VIRTIO_XDP_HEADROOM + vi->hdr_len; The calculated offset will always be VIRTIO_XDP_HEADROOM when reaching this code. Thus, xdp.data_hard_start will be page-start address plus vi->hdr_len. Given this xdp.frame_sz need to be reduced with vi->hdr_len size. IMHO a followup patch should cleanup this code to make it easier to maintain and understand, but it is outside the scope of this patchset. Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Jason Wang <jasowang@redhat.com> Link: https://lore.kernel.org/bpf/158945344436.97035.9445115070189151680.stgit@firesoul
This commit is contained in:
parent
05afee298a
commit
9ce6146ec7
|
@ -689,6 +689,7 @@ static struct sk_buff *receive_small(struct net_device *dev,
|
||||||
xdp.data_end = xdp.data + len;
|
xdp.data_end = xdp.data + len;
|
||||||
xdp.data_meta = xdp.data;
|
xdp.data_meta = xdp.data;
|
||||||
xdp.rxq = &rq->xdp_rxq;
|
xdp.rxq = &rq->xdp_rxq;
|
||||||
|
xdp.frame_sz = buflen;
|
||||||
orig_data = xdp.data;
|
orig_data = xdp.data;
|
||||||
act = bpf_prog_run_xdp(xdp_prog, &xdp);
|
act = bpf_prog_run_xdp(xdp_prog, &xdp);
|
||||||
stats->xdp_packets++;
|
stats->xdp_packets++;
|
||||||
|
@ -797,10 +798,11 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
|
||||||
int offset = buf - page_address(page);
|
int offset = buf - page_address(page);
|
||||||
struct sk_buff *head_skb, *curr_skb;
|
struct sk_buff *head_skb, *curr_skb;
|
||||||
struct bpf_prog *xdp_prog;
|
struct bpf_prog *xdp_prog;
|
||||||
unsigned int truesize;
|
unsigned int truesize = mergeable_ctx_to_truesize(ctx);
|
||||||
unsigned int headroom = mergeable_ctx_to_headroom(ctx);
|
unsigned int headroom = mergeable_ctx_to_headroom(ctx);
|
||||||
int err;
|
|
||||||
unsigned int metasize = 0;
|
unsigned int metasize = 0;
|
||||||
|
unsigned int frame_sz;
|
||||||
|
int err;
|
||||||
|
|
||||||
head_skb = NULL;
|
head_skb = NULL;
|
||||||
stats->bytes += len - vi->hdr_len;
|
stats->bytes += len - vi->hdr_len;
|
||||||
|
@ -821,6 +823,11 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
|
||||||
if (unlikely(hdr->hdr.gso_type))
|
if (unlikely(hdr->hdr.gso_type))
|
||||||
goto err_xdp;
|
goto err_xdp;
|
||||||
|
|
||||||
|
/* Buffers with headroom use PAGE_SIZE as alloc size,
|
||||||
|
* see add_recvbuf_mergeable() + get_mergeable_buf_len()
|
||||||
|
*/
|
||||||
|
frame_sz = headroom ? PAGE_SIZE : truesize;
|
||||||
|
|
||||||
/* This happens when rx buffer size is underestimated
|
/* This happens when rx buffer size is underestimated
|
||||||
* or headroom is not enough because of the buffer
|
* or headroom is not enough because of the buffer
|
||||||
* was refilled before XDP is set. This should only
|
* was refilled before XDP is set. This should only
|
||||||
|
@ -834,6 +841,8 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
|
||||||
page, offset,
|
page, offset,
|
||||||
VIRTIO_XDP_HEADROOM,
|
VIRTIO_XDP_HEADROOM,
|
||||||
&len);
|
&len);
|
||||||
|
frame_sz = PAGE_SIZE;
|
||||||
|
|
||||||
if (!xdp_page)
|
if (!xdp_page)
|
||||||
goto err_xdp;
|
goto err_xdp;
|
||||||
offset = VIRTIO_XDP_HEADROOM;
|
offset = VIRTIO_XDP_HEADROOM;
|
||||||
|
@ -850,6 +859,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
|
||||||
xdp.data_end = xdp.data + (len - vi->hdr_len);
|
xdp.data_end = xdp.data + (len - vi->hdr_len);
|
||||||
xdp.data_meta = xdp.data;
|
xdp.data_meta = xdp.data;
|
||||||
xdp.rxq = &rq->xdp_rxq;
|
xdp.rxq = &rq->xdp_rxq;
|
||||||
|
xdp.frame_sz = frame_sz - vi->hdr_len;
|
||||||
|
|
||||||
act = bpf_prog_run_xdp(xdp_prog, &xdp);
|
act = bpf_prog_run_xdp(xdp_prog, &xdp);
|
||||||
stats->xdp_packets++;
|
stats->xdp_packets++;
|
||||||
|
@ -924,7 +934,6 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
truesize = mergeable_ctx_to_truesize(ctx);
|
|
||||||
if (unlikely(len > truesize)) {
|
if (unlikely(len > truesize)) {
|
||||||
pr_debug("%s: rx error: len %u exceeds truesize %lu\n",
|
pr_debug("%s: rx error: len %u exceeds truesize %lu\n",
|
||||||
dev->name, len, (unsigned long)ctx);
|
dev->name, len, (unsigned long)ctx);
|
||||||
|
|
Loading…
Reference in New Issue