Commit e9f39624 authored by Xuan Zhuo's avatar Xuan Zhuo Committed by Jakub Kicinski
Browse files

virtio_net: xsk: rx: support fill with xsk buffer

parent 19a5a771
Loading
Loading
Loading
Loading
+66 −4
Original line number Diff line number Diff line
@@ -354,6 +354,8 @@ struct receive_queue {

	/* xdp rxq used by xsk */
	struct xdp_rxq_info xsk_rxq_info;

	struct xdp_buff **xsk_buffs;
};

/* This structure can contain rss message with maximum settings for indirection table and keysize
@@ -1054,6 +1056,53 @@ static void check_sq_full_and_disable(struct virtnet_info *vi,
	}
}

static void sg_fill_dma(struct scatterlist *sg, dma_addr_t addr, u32 len)
{
	sg->dma_address = addr;
	sg->length = len;
}

static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue *rq,
				   struct xsk_buff_pool *pool, gfp_t gfp)
{
	struct xdp_buff **xsk_buffs;
	dma_addr_t addr;
	int err = 0;
	u32 len, i;
	int num;

	xsk_buffs = rq->xsk_buffs;

	num = xsk_buff_alloc_batch(pool, xsk_buffs, rq->vq->num_free);
	if (!num)
		return -ENOMEM;

	len = xsk_pool_get_rx_frame_size(pool) + vi->hdr_len;

	for (i = 0; i < num; ++i) {
		/* Use the part of XDP_PACKET_HEADROOM as the virtnet hdr space.
		 * We assume XDP_PACKET_HEADROOM is larger than hdr->len.
		 * (see function virtnet_xsk_pool_enable)
		 */
		addr = xsk_buff_xdp_get_dma(xsk_buffs[i]) - vi->hdr_len;

		sg_init_table(rq->sg, 1);
		sg_fill_dma(rq->sg, addr, len);

		err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, xsk_buffs[i], gfp);
		if (err)
			goto err;
	}

	return num;

err:
	for (; i < num; ++i)
		xsk_buff_free(xsk_buffs[i]);

	return err;
}

static int virtnet_xsk_wakeup(struct net_device *dev, u32 qid, u32 flag)
{
	struct virtnet_info *vi = netdev_priv(dev);
@@ -2245,7 +2294,11 @@ static bool try_fill_recv(struct virtnet_info *vi, struct receive_queue *rq,
			  gfp_t gfp)
{
	int err;
	bool oom;

	if (rq->xsk_pool) {
		err = virtnet_add_recvbuf_xsk(vi, rq, rq->xsk_pool, gfp);
		goto kick;
	}

	do {
		if (vi->mergeable_rx_bufs)
@@ -2255,10 +2308,11 @@ static bool try_fill_recv(struct virtnet_info *vi, struct receive_queue *rq,
		else
			err = add_recvbuf_small(vi, rq, gfp);

		oom = err == -ENOMEM;
		if (err)
			break;
	} while (rq->vq->num_free);

kick:
	if (virtqueue_kick_prepare(rq->vq) && virtqueue_notify(rq->vq)) {
		unsigned long flags;

@@ -2267,7 +2321,7 @@ static bool try_fill_recv(struct virtnet_info *vi, struct receive_queue *rq,
		u64_stats_update_end_irqrestore(&rq->stats.syncp, flags);
	}

	return !oom;
	return err != -ENOMEM;
}

static void skb_recv_done(struct virtqueue *rvq)
@@ -5104,7 +5158,7 @@ static int virtnet_xsk_pool_enable(struct net_device *dev,
	struct receive_queue *rq;
	struct device *dma_dev;
	struct send_queue *sq;
	int err;
	int err, size;

	if (vi->hdr_len > xsk_pool_get_headroom(pool))
		return -EINVAL;
@@ -5135,6 +5189,12 @@ static int virtnet_xsk_pool_enable(struct net_device *dev,
	if (!dma_dev)
		return -EINVAL;

	size = virtqueue_get_vring_size(rq->vq);

	rq->xsk_buffs = kvcalloc(size, sizeof(*rq->xsk_buffs), GFP_KERNEL);
	if (!rq->xsk_buffs)
		return -ENOMEM;

	err = xsk_pool_dma_map(pool, dma_dev, 0);
	if (err)
		goto err_xsk_map;
@@ -5169,6 +5229,8 @@ static int virtnet_xsk_pool_disable(struct net_device *dev, u16 qid)

	xsk_pool_dma_unmap(pool, 0);

	kvfree(rq->xsk_buffs);

	return err;
}