veth_xdp_rcv_one() runs the XDP program on frames queued by ndo_xdp_xmit(), i.e. redirected to veth or sent back by the peer with XDP_TX. The xdp_buff points at rq->xdp_rxq, whose mem type is whatever the previous packet left there: the queue's MEM_TYPE_PAGE_SHARED, or the type of an earlier frame or skb. frame->mem_type is copied in only for XDP_TX and XDP_REDIRECT, after the program has run. bpf_xdp_adjust_tail() and bpf_xdp_pull_data() release whole frags with the rxq mem type. For a MEM_TYPE_PAGE_POOL frame and a stale MEM_TYPE_PAGE_SHARED rxq, page_frag_free() drops the reference owned by the page_pool: BUG: Bad page state in process ping pfn:2244a page dumped because: page_pool leak bad_page.cold+0x58/0x6b __free_frozen_pages+0x837/0x1280 page_frag_free+0x1a4/0x200 __xdp_return+0x3a8/0x9c0 bpf_xdp_adjust_tail+0x8a0/0xd10 bpf_prog_007aa02d10594afa_xdp_trim_drop+0x8f/0xd8 veth_xdp_rcv_one+0x389/0x10e0 veth_xdp_rcv+0x1c5/0xb20 veth_poll+0x33d/0xe00 ... kernel BUG at ./include/linux/mm.h:1820! The reverse, a MEM_TYPE_PAGE_SHARED frame after a page_pool one, hands pages that are not page_pool backed to page_pool_put_full_netmem(). Set the rxq mem type from the frame before running the program, as cpumap and devmap do, and drop the now redundant updates in the XDP_TX and XDP_REDIRECT paths. The splat above is from two veth peers with the next patch applied: one bounces multi-buffer packets with XDP_TX, the other trims all frags of the resulting frames. Page pool backed frames from other drivers can hit the same path today. Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb") Cc: Signed-off-by: Slawomir Stepien --- drivers/net/veth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 3eca92d3dc60..afae2c6533b9 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -658,6 +658,8 @@ static struct xdp_frame *veth_xdp_rcv_one(struct veth_rq *rq, xdp_convert_frame_to_buff(frame, xdp); xdp->rxq = &rq->xdp_rxq; + /* The program may release frags, do it the frame's way */ + xdp->rxq->mem.type = frame->mem_type; vxbuf.skb = NULL; act = bpf_prog_run_xdp(xdp_prog, xdp); @@ -669,7 +671,6 @@ static struct xdp_frame *veth_xdp_rcv_one(struct veth_rq *rq, break; case XDP_TX: orig_frame = *frame; - xdp->rxq->mem.type = frame->mem_type; if (unlikely(veth_xdp_tx(rq, xdp, bq) < 0)) { trace_xdp_exception(rq->dev, xdp_prog, act); frame = &orig_frame; @@ -681,7 +682,6 @@ static struct xdp_frame *veth_xdp_rcv_one(struct veth_rq *rq, goto xdp_xmit; case XDP_REDIRECT: orig_frame = *frame; - xdp->rxq->mem.type = frame->mem_type; if (xdp_do_redirect(rq->dev, xdp, xdp_prog)) { frame = &orig_frame; stats->rx_drops++; -- 2.56.0