If the xsk is enabling, the xsk tx will share the send queue. But the xsk requires that the send queue use the premapped mode. So the send queue must support premapped mode. Signed-off-by: Xuan Zhuo <xuanzhuo@xxxxxxxxxxxxxxxxx> --- drivers/net/virtio/main.c | 114 ++++++++++++++++++++++++++++---- drivers/net/virtio/virtio_net.h | 2 + 2 files changed, 102 insertions(+), 14 deletions(-) diff --git a/drivers/net/virtio/main.c b/drivers/net/virtio/main.c index 57cbd449637a..0766fed28afe 100644 --- a/drivers/net/virtio/main.c +++ b/drivers/net/virtio/main.c @@ -167,13 +167,46 @@ static struct xdp_frame *ptr_to_xdp(void *ptr) return (struct xdp_frame *)((unsigned long)ptr & ~VIRTIO_XDP_FLAG); } +static void virtnet_sq_unmap(struct virtnet_sq *sq) +{ + struct scatterlist *sg; + + for (sg = sq->sg; ; sg = sg_next(sg)) { + virtqueue_dma_unmap_single_attrs(sq->vq, + sg->dma_address, + sg->length, + DMA_TO_DEVICE, 0); + + if (sg_is_last(sg)) { + /* clear the end mark setted by virtio core. */ + sg_unmark_end(sg); + break; + } + } + + /* mark the last one of sq->sg[] as the end for reuse by + * virtqueue_get_buf_ctx_dma(). + */ + sg_mark_end(&sq->sg[ARRAY_SIZE(sq->sg) - 1]); +} + static void __free_old_xmit(struct virtnet_sq *sq, bool in_napi, u64 *bytes, u64 *packets) { + struct scatterlist *sg = NULL; unsigned int len; void *ptr; - while ((ptr = virtqueue_get_buf(sq->vq, &len)) != NULL) { + /* clear the sg table */ + if (sq->do_dma) { + sg_init_table(sq->sg, ARRAY_SIZE(sq->sg)); + sg = sq->sg; + } + + while ((ptr = virtqueue_get_buf_ctx_dma(sq->vq, &len, sg, NULL)) != NULL) { + if (sq->do_dma) + virtnet_sq_unmap(sq); + if (!is_xdp_frame(ptr)) { struct sk_buff *skb = ptr; @@ -567,22 +600,61 @@ static void *virtnet_rq_alloc(struct virtnet_rq *rq, u32 size, gfp_t gfp) return buf; } -static void virtnet_rq_set_premapped(struct virtnet_info *vi) +static void virtnet_set_premapped(struct virtnet_info *vi) { int i; - /* disable for big mode */ - if (!vi->mergeable_rx_bufs && vi->big_packets) - return; - for (i = 0; i < vi->max_queue_pairs; i++) { - if (virtqueue_set_dma_premapped(vi->rq[i].vq)) - continue; + if (!virtqueue_set_dma_premapped(vi->sq[i].vq)) + vi->sq[i].do_dma = true; - vi->rq[i].do_dma = true; + /* disable for big mode */ + if (vi->mergeable_rx_bufs || !vi->big_packets) { + if (!virtqueue_set_dma_premapped(vi->rq[i].vq)) + vi->rq[i].do_dma = true; + } } } +static int virtnet_sq_map_sg(struct virtnet_sq *sq) +{ + struct scatterlist *sg; + + for (sg = sq->sg; sg; sg = sg_next(sg)) { + sg->dma_address = virtqueue_dma_map_single_attrs(sq->vq, sg_virt(sg), + sg->length, + DMA_TO_DEVICE, 0); + if (virtqueue_dma_mapping_error(sq->vq, sg->dma_address)) + goto err; + } + + return 0; + +err: + if (sg != sq->sg) { + sg_mark_end(sg - 1); + virtnet_sq_unmap(sq); + } + return -ENOMEM; +} + +static int virtnet_add_outbuf(struct virtnet_sq *sq, u32 num, void *data) +{ + int ret; + + if (sq->do_dma) { + ret = virtnet_sq_map_sg(sq); + if (ret) + return -ENOMEM; + } + + ret = virtqueue_add_outbuf(sq->vq, sq->sg, num, data, GFP_ATOMIC); + if (ret && sq->do_dma) + virtnet_sq_unmap(sq); + + return ret; +} + static void free_old_xmit(struct virtnet_sq *sq, bool in_napi) { u64 bytes, packets = 0; @@ -686,8 +758,7 @@ static int __virtnet_xdp_xmit_one(struct virtnet_info *vi, skb_frag_size(frag), skb_frag_off(frag)); } - err = virtqueue_add_outbuf(sq->vq, sq->sg, nr_frags + 1, - xdp_to_ptr(xdpf), GFP_ATOMIC); + err = virtnet_add_outbuf(sq, nr_frags + 1, xdp_to_ptr(xdpf)); if (unlikely(err)) return -ENOSPC; /* Caller handle free/refcnt */ @@ -2126,7 +2197,7 @@ static int xmit_skb(struct virtnet_sq *sq, struct sk_buff *skb) return num_sg; num_sg++; } - return virtqueue_add_outbuf(sq->vq, sq->sg, num_sg, skb, GFP_ATOMIC); + return virtnet_add_outbuf(sq, num_sg, skb); } static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) @@ -3852,10 +3923,25 @@ static void virtnet_rq_free_unused_buf(struct virtqueue *vq, void *buf) static void virtnet_sq_free_unused_bufs(struct virtqueue *vq) { + struct virtnet_info *vi = vq->vdev->priv; + struct scatterlist *sg = NULL; + struct virtnet_sq *sq; + int i = vq2txq(vq); void *buf; - while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) + sq = &vi->sq[i]; + + if (sq->do_dma) { + sg_init_table(sq->sg, ARRAY_SIZE(sq->sg)); + sg = sq->sg; + } + + while ((buf = virtqueue_detach_unused_buf_premapped(vq, sg)) != NULL) { + if (sq->do_dma) + virtnet_sq_unmap(sq); + virtnet_sq_free_unused_buf(vq, buf); + } } static void virtnet_rq_free_unused_bufs(struct virtqueue *vq) @@ -4052,7 +4138,7 @@ static int init_vqs(struct virtnet_info *vi) if (ret) goto err_free; - virtnet_rq_set_premapped(vi); + virtnet_set_premapped(vi); cpus_read_lock(); virtnet_set_affinity(vi); diff --git a/drivers/net/virtio/virtio_net.h b/drivers/net/virtio/virtio_net.h index ebf9f344648a..870855a829a0 100644 --- a/drivers/net/virtio/virtio_net.h +++ b/drivers/net/virtio/virtio_net.h @@ -67,6 +67,8 @@ struct virtnet_sq { /* Record whether sq is in reset state. */ bool reset; + + bool do_dma; }; /* Internal representation of a receive virtqueue */ -- 2.32.0.3.g01195cf9f