xref: /dpdk/drivers/net/virtio/virtio_cvq.c (revision 1af8b0b2747fe6c6267fa7bedb602e569742362e)
135e7012eSMaxime Coquelin /* SPDX-License-Identifier: BSD-3-Clause
235e7012eSMaxime Coquelin  * Copyright(c) 2010-2016 Intel Corporation
335e7012eSMaxime Coquelin  * Copyright(c) 2022 Red Hat Inc,
435e7012eSMaxime Coquelin  */
535e7012eSMaxime Coquelin 
635e7012eSMaxime Coquelin #include <unistd.h>
735e7012eSMaxime Coquelin 
835e7012eSMaxime Coquelin #include <rte_common.h>
935e7012eSMaxime Coquelin #include <rte_eal.h>
1035e7012eSMaxime Coquelin #include <rte_errno.h>
1135e7012eSMaxime Coquelin 
1235e7012eSMaxime Coquelin #include "virtio_cvq.h"
1335e7012eSMaxime Coquelin #include "virtqueue.h"
1435e7012eSMaxime Coquelin 
1535e7012eSMaxime Coquelin static struct virtio_pmd_ctrl *
1635e7012eSMaxime Coquelin virtio_send_command_packed(struct virtnet_ctl *cvq,
1735e7012eSMaxime Coquelin 			   struct virtio_pmd_ctrl *ctrl,
1835e7012eSMaxime Coquelin 			   int *dlen, int pkt_num)
1935e7012eSMaxime Coquelin {
2035e7012eSMaxime Coquelin 	struct virtqueue *vq = virtnet_cq_to_vq(cvq);
2135e7012eSMaxime Coquelin 	int head;
2235e7012eSMaxime Coquelin 	struct vring_packed_desc *desc = vq->vq_packed.ring.desc;
2335e7012eSMaxime Coquelin 	struct virtio_pmd_ctrl *result;
2435e7012eSMaxime Coquelin 	uint16_t flags;
2535e7012eSMaxime Coquelin 	int sum = 0;
2635e7012eSMaxime Coquelin 	int nb_descs = 0;
2735e7012eSMaxime Coquelin 	int k;
2835e7012eSMaxime Coquelin 
2935e7012eSMaxime Coquelin 	/*
3035e7012eSMaxime Coquelin 	 * Format is enforced in qemu code:
3135e7012eSMaxime Coquelin 	 * One TX packet for header;
3235e7012eSMaxime Coquelin 	 * At least one TX packet per argument;
3335e7012eSMaxime Coquelin 	 * One RX packet for ACK.
3435e7012eSMaxime Coquelin 	 */
3535e7012eSMaxime Coquelin 	head = vq->vq_avail_idx;
3635e7012eSMaxime Coquelin 	flags = vq->vq_packed.cached_flags;
37a632f0f6SMaxime Coquelin 	desc[head].addr = cvq->hdr_mem;
3835e7012eSMaxime Coquelin 	desc[head].len = sizeof(struct virtio_net_ctrl_hdr);
3935e7012eSMaxime Coquelin 	vq->vq_free_cnt--;
4035e7012eSMaxime Coquelin 	nb_descs++;
4135e7012eSMaxime Coquelin 	if (++vq->vq_avail_idx >= vq->vq_nentries) {
4235e7012eSMaxime Coquelin 		vq->vq_avail_idx -= vq->vq_nentries;
4335e7012eSMaxime Coquelin 		vq->vq_packed.cached_flags ^= VRING_PACKED_DESC_F_AVAIL_USED;
4435e7012eSMaxime Coquelin 	}
4535e7012eSMaxime Coquelin 
4635e7012eSMaxime Coquelin 	for (k = 0; k < pkt_num; k++) {
47a632f0f6SMaxime Coquelin 		desc[vq->vq_avail_idx].addr = cvq->hdr_mem
4835e7012eSMaxime Coquelin 			+ sizeof(struct virtio_net_ctrl_hdr)
4935e7012eSMaxime Coquelin 			+ sizeof(ctrl->status) + sizeof(uint8_t) * sum;
5035e7012eSMaxime Coquelin 		desc[vq->vq_avail_idx].len = dlen[k];
5135e7012eSMaxime Coquelin 		desc[vq->vq_avail_idx].flags = VRING_DESC_F_NEXT |
5235e7012eSMaxime Coquelin 			vq->vq_packed.cached_flags;
5335e7012eSMaxime Coquelin 		sum += dlen[k];
5435e7012eSMaxime Coquelin 		vq->vq_free_cnt--;
5535e7012eSMaxime Coquelin 		nb_descs++;
5635e7012eSMaxime Coquelin 		if (++vq->vq_avail_idx >= vq->vq_nentries) {
5735e7012eSMaxime Coquelin 			vq->vq_avail_idx -= vq->vq_nentries;
5835e7012eSMaxime Coquelin 			vq->vq_packed.cached_flags ^=
5935e7012eSMaxime Coquelin 				VRING_PACKED_DESC_F_AVAIL_USED;
6035e7012eSMaxime Coquelin 		}
6135e7012eSMaxime Coquelin 	}
6235e7012eSMaxime Coquelin 
63a632f0f6SMaxime Coquelin 	desc[vq->vq_avail_idx].addr = cvq->hdr_mem
6435e7012eSMaxime Coquelin 		+ sizeof(struct virtio_net_ctrl_hdr);
6535e7012eSMaxime Coquelin 	desc[vq->vq_avail_idx].len = sizeof(ctrl->status);
6635e7012eSMaxime Coquelin 	desc[vq->vq_avail_idx].flags = VRING_DESC_F_WRITE |
6735e7012eSMaxime Coquelin 		vq->vq_packed.cached_flags;
6835e7012eSMaxime Coquelin 	vq->vq_free_cnt--;
6935e7012eSMaxime Coquelin 	nb_descs++;
7035e7012eSMaxime Coquelin 	if (++vq->vq_avail_idx >= vq->vq_nentries) {
7135e7012eSMaxime Coquelin 		vq->vq_avail_idx -= vq->vq_nentries;
7235e7012eSMaxime Coquelin 		vq->vq_packed.cached_flags ^= VRING_PACKED_DESC_F_AVAIL_USED;
7335e7012eSMaxime Coquelin 	}
7435e7012eSMaxime Coquelin 
7535e7012eSMaxime Coquelin 	virtqueue_store_flags_packed(&desc[head], VRING_DESC_F_NEXT | flags,
7635e7012eSMaxime Coquelin 			vq->hw->weak_barriers);
7735e7012eSMaxime Coquelin 
7835e7012eSMaxime Coquelin 	virtio_wmb(vq->hw->weak_barriers);
794dd3477cSMaxime Coquelin 	cvq->notify_queue(vq, cvq->notify_cookie);
8035e7012eSMaxime Coquelin 
8135e7012eSMaxime Coquelin 	/* wait for used desc in virtqueue
8235e7012eSMaxime Coquelin 	 * desc_is_used has a load-acquire or rte_io_rmb inside
8335e7012eSMaxime Coquelin 	 */
8435e7012eSMaxime Coquelin 	while (!desc_is_used(&desc[head], vq))
8535e7012eSMaxime Coquelin 		usleep(100);
8635e7012eSMaxime Coquelin 
8735e7012eSMaxime Coquelin 	/* now get used descriptors */
8835e7012eSMaxime Coquelin 	vq->vq_free_cnt += nb_descs;
8935e7012eSMaxime Coquelin 	vq->vq_used_cons_idx += nb_descs;
9035e7012eSMaxime Coquelin 	if (vq->vq_used_cons_idx >= vq->vq_nentries) {
9135e7012eSMaxime Coquelin 		vq->vq_used_cons_idx -= vq->vq_nentries;
9235e7012eSMaxime Coquelin 		vq->vq_packed.used_wrap_counter ^= 1;
9335e7012eSMaxime Coquelin 	}
9435e7012eSMaxime Coquelin 
95*1af8b0b2SDavid Marchand 	PMD_INIT_LOG(DEBUG, "vq->vq_free_cnt=%d", vq->vq_free_cnt);
96*1af8b0b2SDavid Marchand 	PMD_INIT_LOG(DEBUG, "vq->vq_avail_idx=%d", vq->vq_avail_idx);
97*1af8b0b2SDavid Marchand 	PMD_INIT_LOG(DEBUG, "vq->vq_used_cons_idx=%d", vq->vq_used_cons_idx);
98*1af8b0b2SDavid Marchand 	PMD_INIT_LOG(DEBUG, "vq->vq_packed.cached_flags=0x%x", vq->vq_packed.cached_flags);
99*1af8b0b2SDavid Marchand 	PMD_INIT_LOG(DEBUG, "vq->vq_packed.used_wrap_counter=%d", vq->vq_packed.used_wrap_counter);
10035e7012eSMaxime Coquelin 
101a632f0f6SMaxime Coquelin 	result = cvq->hdr_mz->addr;
10235e7012eSMaxime Coquelin 	return result;
10335e7012eSMaxime Coquelin }
10435e7012eSMaxime Coquelin 
10535e7012eSMaxime Coquelin static struct virtio_pmd_ctrl *
10635e7012eSMaxime Coquelin virtio_send_command_split(struct virtnet_ctl *cvq,
10735e7012eSMaxime Coquelin 			  struct virtio_pmd_ctrl *ctrl,
10835e7012eSMaxime Coquelin 			  int *dlen, int pkt_num)
10935e7012eSMaxime Coquelin {
11035e7012eSMaxime Coquelin 	struct virtio_pmd_ctrl *result;
11135e7012eSMaxime Coquelin 	struct virtqueue *vq = virtnet_cq_to_vq(cvq);
11235e7012eSMaxime Coquelin 	uint32_t head, i;
11335e7012eSMaxime Coquelin 	int k, sum = 0;
11435e7012eSMaxime Coquelin 
11535e7012eSMaxime Coquelin 	head = vq->vq_desc_head_idx;
11635e7012eSMaxime Coquelin 
11735e7012eSMaxime Coquelin 	/*
11835e7012eSMaxime Coquelin 	 * Format is enforced in qemu code:
11935e7012eSMaxime Coquelin 	 * One TX packet for header;
12035e7012eSMaxime Coquelin 	 * At least one TX packet per argument;
12135e7012eSMaxime Coquelin 	 * One RX packet for ACK.
12235e7012eSMaxime Coquelin 	 */
12335e7012eSMaxime Coquelin 	vq->vq_split.ring.desc[head].flags = VRING_DESC_F_NEXT;
124a632f0f6SMaxime Coquelin 	vq->vq_split.ring.desc[head].addr = cvq->hdr_mem;
12535e7012eSMaxime Coquelin 	vq->vq_split.ring.desc[head].len = sizeof(struct virtio_net_ctrl_hdr);
12635e7012eSMaxime Coquelin 	vq->vq_free_cnt--;
12735e7012eSMaxime Coquelin 	i = vq->vq_split.ring.desc[head].next;
12835e7012eSMaxime Coquelin 
12935e7012eSMaxime Coquelin 	for (k = 0; k < pkt_num; k++) {
13035e7012eSMaxime Coquelin 		vq->vq_split.ring.desc[i].flags = VRING_DESC_F_NEXT;
131a632f0f6SMaxime Coquelin 		vq->vq_split.ring.desc[i].addr = cvq->hdr_mem
13235e7012eSMaxime Coquelin 			+ sizeof(struct virtio_net_ctrl_hdr)
13335e7012eSMaxime Coquelin 			+ sizeof(ctrl->status) + sizeof(uint8_t) * sum;
13435e7012eSMaxime Coquelin 		vq->vq_split.ring.desc[i].len = dlen[k];
13535e7012eSMaxime Coquelin 		sum += dlen[k];
13635e7012eSMaxime Coquelin 		vq->vq_free_cnt--;
13735e7012eSMaxime Coquelin 		i = vq->vq_split.ring.desc[i].next;
13835e7012eSMaxime Coquelin 	}
13935e7012eSMaxime Coquelin 
14035e7012eSMaxime Coquelin 	vq->vq_split.ring.desc[i].flags = VRING_DESC_F_WRITE;
141a632f0f6SMaxime Coquelin 	vq->vq_split.ring.desc[i].addr = cvq->hdr_mem
14235e7012eSMaxime Coquelin 			+ sizeof(struct virtio_net_ctrl_hdr);
14335e7012eSMaxime Coquelin 	vq->vq_split.ring.desc[i].len = sizeof(ctrl->status);
14435e7012eSMaxime Coquelin 	vq->vq_free_cnt--;
14535e7012eSMaxime Coquelin 
14635e7012eSMaxime Coquelin 	vq->vq_desc_head_idx = vq->vq_split.ring.desc[i].next;
14735e7012eSMaxime Coquelin 
14835e7012eSMaxime Coquelin 	vq_update_avail_ring(vq, head);
14935e7012eSMaxime Coquelin 	vq_update_avail_idx(vq);
15035e7012eSMaxime Coquelin 
15135e7012eSMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "vq->vq_queue_index = %d", vq->vq_queue_index);
15235e7012eSMaxime Coquelin 
1534dd3477cSMaxime Coquelin 	cvq->notify_queue(vq, cvq->notify_cookie);
15435e7012eSMaxime Coquelin 
15535e7012eSMaxime Coquelin 	while (virtqueue_nused(vq) == 0)
15635e7012eSMaxime Coquelin 		usleep(100);
15735e7012eSMaxime Coquelin 
15835e7012eSMaxime Coquelin 	while (virtqueue_nused(vq)) {
15935e7012eSMaxime Coquelin 		uint32_t idx, desc_idx, used_idx;
16035e7012eSMaxime Coquelin 		struct vring_used_elem *uep;
16135e7012eSMaxime Coquelin 
16235e7012eSMaxime Coquelin 		used_idx = (uint32_t)(vq->vq_used_cons_idx
16335e7012eSMaxime Coquelin 				& (vq->vq_nentries - 1));
16435e7012eSMaxime Coquelin 		uep = &vq->vq_split.ring.used->ring[used_idx];
16535e7012eSMaxime Coquelin 		idx = (uint32_t)uep->id;
16635e7012eSMaxime Coquelin 		desc_idx = idx;
16735e7012eSMaxime Coquelin 
16835e7012eSMaxime Coquelin 		while (vq->vq_split.ring.desc[desc_idx].flags &
16935e7012eSMaxime Coquelin 				VRING_DESC_F_NEXT) {
17035e7012eSMaxime Coquelin 			desc_idx = vq->vq_split.ring.desc[desc_idx].next;
17135e7012eSMaxime Coquelin 			vq->vq_free_cnt++;
17235e7012eSMaxime Coquelin 		}
17335e7012eSMaxime Coquelin 
17435e7012eSMaxime Coquelin 		vq->vq_split.ring.desc[desc_idx].next = vq->vq_desc_head_idx;
17535e7012eSMaxime Coquelin 		vq->vq_desc_head_idx = idx;
17635e7012eSMaxime Coquelin 
17735e7012eSMaxime Coquelin 		vq->vq_used_cons_idx++;
17835e7012eSMaxime Coquelin 		vq->vq_free_cnt++;
17935e7012eSMaxime Coquelin 	}
18035e7012eSMaxime Coquelin 
181*1af8b0b2SDavid Marchand 	PMD_INIT_LOG(DEBUG, "vq->vq_free_cnt=%d", vq->vq_free_cnt);
182*1af8b0b2SDavid Marchand 	PMD_INIT_LOG(DEBUG, "vq->vq_desc_head_idx=%d", vq->vq_desc_head_idx);
18335e7012eSMaxime Coquelin 
184a632f0f6SMaxime Coquelin 	result = cvq->hdr_mz->addr;
18535e7012eSMaxime Coquelin 	return result;
18635e7012eSMaxime Coquelin }
18735e7012eSMaxime Coquelin 
18835e7012eSMaxime Coquelin int
18935e7012eSMaxime Coquelin virtio_send_command(struct virtnet_ctl *cvq, struct virtio_pmd_ctrl *ctrl, int *dlen, int pkt_num)
19035e7012eSMaxime Coquelin {
19135e7012eSMaxime Coquelin 	virtio_net_ctrl_ack status = ~0;
19235e7012eSMaxime Coquelin 	struct virtio_pmd_ctrl *result;
19335e7012eSMaxime Coquelin 	struct virtqueue *vq;
19435e7012eSMaxime Coquelin 
19535e7012eSMaxime Coquelin 	ctrl->status = status;
19635e7012eSMaxime Coquelin 
19735e7012eSMaxime Coquelin 	if (!cvq) {
19835e7012eSMaxime Coquelin 		PMD_INIT_LOG(ERR, "Control queue is not supported.");
19935e7012eSMaxime Coquelin 		return -1;
20035e7012eSMaxime Coquelin 	}
20135e7012eSMaxime Coquelin 
20235e7012eSMaxime Coquelin 	rte_spinlock_lock(&cvq->lock);
20335e7012eSMaxime Coquelin 	vq = virtnet_cq_to_vq(cvq);
20435e7012eSMaxime Coquelin 
20535e7012eSMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "vq->vq_desc_head_idx = %d, status = %d, "
20635e7012eSMaxime Coquelin 		"vq->hw->cvq = %p vq = %p",
20735e7012eSMaxime Coquelin 		vq->vq_desc_head_idx, status, vq->hw->cvq, vq);
20835e7012eSMaxime Coquelin 
20935e7012eSMaxime Coquelin 	if (vq->vq_free_cnt < pkt_num + 2 || pkt_num < 1) {
21035e7012eSMaxime Coquelin 		rte_spinlock_unlock(&cvq->lock);
21135e7012eSMaxime Coquelin 		return -1;
21235e7012eSMaxime Coquelin 	}
21335e7012eSMaxime Coquelin 
214a632f0f6SMaxime Coquelin 	memcpy(cvq->hdr_mz->addr, ctrl, sizeof(struct virtio_pmd_ctrl));
21535e7012eSMaxime Coquelin 
21635e7012eSMaxime Coquelin 	if (virtio_with_packed_queue(vq->hw))
21735e7012eSMaxime Coquelin 		result = virtio_send_command_packed(cvq, ctrl, dlen, pkt_num);
21835e7012eSMaxime Coquelin 	else
21935e7012eSMaxime Coquelin 		result = virtio_send_command_split(cvq, ctrl, dlen, pkt_num);
22035e7012eSMaxime Coquelin 
22135e7012eSMaxime Coquelin 	rte_spinlock_unlock(&cvq->lock);
22235e7012eSMaxime Coquelin 	return result->status;
22335e7012eSMaxime Coquelin }
22435e7012eSMaxime Coquelin 
225