On Tue, Jan 30, 2024 at 7:43 PM Xuan Zhuo <xuanzhuo@xxxxxxxxxxxxxxxxx> wrote: > > 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_net.c | 167 ++++++++++++++++++++++++++++++++++++++- > 1 file changed, 163 insertions(+), 4 deletions(-) > > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c > index 226ab830870e..cf0c67380b07 100644 > --- a/drivers/net/virtio_net.c > +++ b/drivers/net/virtio_net.c > @@ -46,6 +46,7 @@ module_param(napi_tx, bool, 0644); > #define VIRTIO_XDP_REDIR BIT(1) > > #define VIRTIO_XDP_FLAG BIT(0) > +#define VIRTIO_DMA_FLAG BIT(1) > > /* RX packet size EWMA. The average packet size is used to determine the packet > * buffer size when refilling RX rings. As the entire RX ring may be refilled > @@ -140,6 +141,21 @@ struct virtnet_rq_dma { > u16 need_sync; > }; > > +struct virtnet_sq_dma { > + union { > + struct virtnet_sq_dma *next; > + void *data; > + }; > + dma_addr_t addr; > + u32 len; > + bool is_tail; > +}; > + > +struct virtnet_sq_dma_head { > + struct virtnet_sq_dma *free; > + struct virtnet_sq_dma *head; Any reason the head must be a pointer instead of a simple index? > +}; > + > /* Internal representation of a send virtqueue */ > struct send_queue { > /* Virtqueue associated with this send _queue */ > @@ -159,6 +175,8 @@ struct send_queue { > > /* Record whether sq is in reset state. */ > bool reset; > + > + struct virtnet_sq_dma_head dmainfo; > }; > > /* Internal representation of a receive virtqueue */ > @@ -348,6 +366,131 @@ static struct xdp_frame *ptr_to_xdp(void *ptr) > return (struct xdp_frame *)((unsigned long)ptr & ~VIRTIO_XDP_FLAG); > } > > +static inline void *virtnet_sq_unmap(struct send_queue *sq, void *data) > +{ > + struct virtnet_sq_dma *head, *tail; > + > + if (!((unsigned long)data & VIRTIO_DMA_FLAG)) > + return data; > + > + head = (void *)((unsigned long)data & ~VIRTIO_DMA_FLAG); > + > + tail = head; > + > + while (true) { > + virtqueue_dma_unmap_page_attrs(sq->vq, tail->addr, tail->len, > + DMA_TO_DEVICE, 0); > + > + if (tail->is_tail) > + break; > + > + tail = tail->next; > + } > + > + data = tail->data; > + tail->is_tail = false; > + > + tail->next = sq->dmainfo.free; > + sq->dmainfo.free = head; > + > + return data; > +} > + > +static void *virtnet_sq_dma_splice(struct send_queue *sq, > + struct virtnet_sq_dma *head, > + struct virtnet_sq_dma *tail, > + void *data) > +{ > + sq->dmainfo.free = tail->next; > + > + tail->is_tail = true; > + tail->data = data; > + > + head = (void *)((unsigned long)head | VIRTIO_DMA_FLAG); > + > + return head; > +} > + > +static struct virtnet_sq_dma *virtnet_sq_map_sg(struct send_queue *sq, int nents, void *data) > +{ > + struct virtnet_sq_dma *head, *tail, *p; > + struct scatterlist *sg; > + dma_addr_t addr; > + int i; > + > + head = sq->dmainfo.free; > + p = head; > + > + tail = NULL; > + > + for_each_sg(sq->sg, sg, nents, i) { > + addr = virtqueue_dma_map_page_attrs(sq->vq, sg_page(sg), > + sg->offset, sg->length, > + DMA_TO_DEVICE, 0); > + if (virtqueue_dma_mapping_error(sq->vq, addr)) > + goto err; > + > + sg->dma_address = addr; > + > + tail = p; > + tail->addr = sg->dma_address; > + tail->len = sg->length; > + > + p = p->next; > + } > + > + return virtnet_sq_dma_splice(sq, head, tail, data); > + > +err: > + if (tail) > + virtnet_sq_unmap(sq, virtnet_sq_dma_splice(sq, head, tail, data)); > + > + return NULL; > +} > + > +static int virtnet_add_outbuf(struct send_queue *sq, u32 num, void *data) > +{ > + int ret; > + > + if (sq->vq->premapped) { > + data = virtnet_sq_map_sg(sq, num, data); > + if (!data) > + return -ENOMEM; > + } > + > + ret = virtqueue_add_outbuf(sq->vq, sq->sg, num, data, GFP_ATOMIC); > + if (ret && sq->vq->premapped) > + virtnet_sq_unmap(sq, data); > + > + return ret; > +} > + > +static int virtnet_sq_init_dma_mate(struct send_queue *sq) > +{ > + struct virtnet_sq_dma *d; > + int size, i; > + > + size = virtqueue_get_vring_size(sq->vq); > + > + size += MAX_SKB_FRAGS + 2; Is this enough for the case where an indirect descriptor is used? Thanks