virtnet_xsk_pool_enable() DMA maps the static xsk_hdr, which lives in the kernel image. On arm64 the kernel image is mapped in the vmalloc area, so when the device uses the DMA API the mapping is rejected: virtio_net virtio0: rejecting DMA map of vmalloc memory WARNING: drivers/virtio/virtio_ring.c:3882 ... and binding an AF_XDP zero-copy socket fails with -ENOMEM. Even where the mapping succeeds, the header shares cache lines with unrelated data. Allocate the all-zero header per send queue when the pool is enabled and free it when it is disabled. Fixes: 21a4e3ce6dc7 ("virtio_net: xsk: bind/unbind xsk for tx") Signed-off-by: Haishuang Yan --- drivers/net/virtio_net.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index dd2c41ef6fbe..4f5d8812f75b 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -315,6 +315,8 @@ struct send_queue { struct xsk_buff_pool *xsk_pool; + /* All-zero header shared by the AF_XDP packets sent on this queue */ + struct virtio_net_common_hdr *xsk_hdr; dma_addr_t xsk_hdr_dma_addr; }; @@ -500,8 +502,6 @@ struct virtio_net_common_hdr { }; }; -static struct virtio_net_common_hdr xsk_hdr; - static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf); static void virtnet_sq_free_unused_buf_done(struct virtqueue *vq); static int virtnet_xdp_handler(struct bpf_prog *xdp_prog, struct xdp_buff *xdp, @@ -5971,11 +5971,21 @@ static int virtnet_xsk_pool_enable(struct net_device *dev, if (!rq->xsk_buffs) return -ENOMEM; - hdr_dma = virtqueue_map_single_attrs(sq->vq, &xsk_hdr, vi->hdr_len, + /* + * Don't map a static header: the kernel image is not in the linear + * map on all architectures (e.g. arm64), so it can't be DMA mapped. + */ + sq->xsk_hdr = kzalloc_obj(*sq->xsk_hdr); + if (!sq->xsk_hdr) { + err = -ENOMEM; + goto err_free_buffs; + } + + hdr_dma = virtqueue_map_single_attrs(sq->vq, sq->xsk_hdr, vi->hdr_len, DMA_TO_DEVICE, 0); if (virtqueue_map_mapping_error(sq->vq, hdr_dma)) { err = -ENOMEM; - goto err_free_buffs; + goto err_free_hdr; } err = xsk_pool_dma_map(pool, dma_dev, 0); @@ -6004,6 +6014,9 @@ static int virtnet_xsk_pool_enable(struct net_device *dev, err_xsk_map: virtqueue_unmap_single_attrs(rq->vq, hdr_dma, vi->hdr_len, DMA_TO_DEVICE, 0); +err_free_hdr: + kfree(sq->xsk_hdr); + sq->xsk_hdr = NULL; err_free_buffs: kvfree(rq->xsk_buffs); return err; @@ -6032,6 +6045,8 @@ static int virtnet_xsk_pool_disable(struct net_device *dev, u16 qid) virtqueue_unmap_single_attrs(sq->vq, sq->xsk_hdr_dma_addr, vi->hdr_len, DMA_TO_DEVICE, 0); + kfree(sq->xsk_hdr); + sq->xsk_hdr = NULL; kvfree(rq->xsk_buffs); return err; -- 2.43.0