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 <[email protected]>
---
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