xref: /dpdk/lib/vhost/virtio_net_ctrl.c (revision 15677ca2c751b3be2f02429bb006d859dccae0c0)
1474f4d78SMaxime Coquelin /* SPDX-License-Identifier: BSD-3-Clause
2474f4d78SMaxime Coquelin  * Copyright (c) 2023 Red Hat, Inc.
3474f4d78SMaxime Coquelin  */
4474f4d78SMaxime Coquelin 
5474f4d78SMaxime Coquelin #include <stdint.h>
6474f4d78SMaxime Coquelin #include <stdio.h>
7474f4d78SMaxime Coquelin #include <unistd.h>
8474f4d78SMaxime Coquelin 
9474f4d78SMaxime Coquelin #include "iotlb.h"
10474f4d78SMaxime Coquelin #include "vhost.h"
11474f4d78SMaxime Coquelin #include "virtio_net_ctrl.h"
12474f4d78SMaxime Coquelin 
13474f4d78SMaxime Coquelin struct virtio_net_ctrl {
14474f4d78SMaxime Coquelin 	uint8_t class;
15474f4d78SMaxime Coquelin 	uint8_t command;
16474f4d78SMaxime Coquelin 	uint8_t command_data[];
17474f4d78SMaxime Coquelin };
18474f4d78SMaxime Coquelin 
19474f4d78SMaxime Coquelin struct virtio_net_ctrl_elem {
20474f4d78SMaxime Coquelin 	struct virtio_net_ctrl *ctrl_req;
21474f4d78SMaxime Coquelin 	uint16_t head_idx;
22474f4d78SMaxime Coquelin 	uint16_t n_descs;
23474f4d78SMaxime Coquelin 	uint8_t *desc_ack;
24474f4d78SMaxime Coquelin };
25474f4d78SMaxime Coquelin 
26474f4d78SMaxime Coquelin static int
27474f4d78SMaxime Coquelin virtio_net_ctrl_pop(struct virtio_net *dev, struct vhost_virtqueue *cvq,
28474f4d78SMaxime Coquelin 		struct virtio_net_ctrl_elem *ctrl_elem)
29474f4d78SMaxime Coquelin 	__rte_shared_locks_required(&cvq->iotlb_lock)
30474f4d78SMaxime Coquelin {
31474f4d78SMaxime Coquelin 	uint16_t avail_idx, desc_idx, n_descs = 0;
32474f4d78SMaxime Coquelin 	uint64_t desc_len, desc_addr, desc_iova, data_len = 0;
33474f4d78SMaxime Coquelin 	uint8_t *ctrl_req;
34474f4d78SMaxime Coquelin 	struct vring_desc *descs;
35474f4d78SMaxime Coquelin 
365147b641STyler Retzlaff 	avail_idx = rte_atomic_load_explicit((unsigned short __rte_atomic *)&cvq->avail->idx,
375147b641STyler Retzlaff 		rte_memory_order_acquire);
38474f4d78SMaxime Coquelin 	if (avail_idx == cvq->last_avail_idx) {
390e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, DEBUG, "Control queue empty");
40474f4d78SMaxime Coquelin 		return 0;
41474f4d78SMaxime Coquelin 	}
42474f4d78SMaxime Coquelin 
43474f4d78SMaxime Coquelin 	desc_idx = cvq->avail->ring[cvq->last_avail_idx];
44474f4d78SMaxime Coquelin 	if (desc_idx >= cvq->size) {
450e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Out of range desc index, dropping");
46474f4d78SMaxime Coquelin 		goto err;
47474f4d78SMaxime Coquelin 	}
48474f4d78SMaxime Coquelin 
49474f4d78SMaxime Coquelin 	ctrl_elem->head_idx = desc_idx;
50474f4d78SMaxime Coquelin 
51474f4d78SMaxime Coquelin 	if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) {
52474f4d78SMaxime Coquelin 		desc_len = cvq->desc[desc_idx].len;
53474f4d78SMaxime Coquelin 		desc_iova = cvq->desc[desc_idx].addr;
54474f4d78SMaxime Coquelin 
55474f4d78SMaxime Coquelin 		descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
56474f4d78SMaxime Coquelin 					desc_iova, &desc_len, VHOST_ACCESS_RO);
57474f4d78SMaxime Coquelin 		if (!descs || desc_len != cvq->desc[desc_idx].len) {
580e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs");
59474f4d78SMaxime Coquelin 			goto err;
60474f4d78SMaxime Coquelin 		}
61474f4d78SMaxime Coquelin 
62474f4d78SMaxime Coquelin 		desc_idx = 0;
63474f4d78SMaxime Coquelin 	} else {
64474f4d78SMaxime Coquelin 		descs = cvq->desc;
65474f4d78SMaxime Coquelin 	}
66474f4d78SMaxime Coquelin 
67474f4d78SMaxime Coquelin 	while (1) {
68474f4d78SMaxime Coquelin 		desc_len = descs[desc_idx].len;
69474f4d78SMaxime Coquelin 		desc_iova = descs[desc_idx].addr;
70474f4d78SMaxime Coquelin 
71474f4d78SMaxime Coquelin 		n_descs++;
72474f4d78SMaxime Coquelin 
73474f4d78SMaxime Coquelin 		if (descs[desc_idx].flags & VRING_DESC_F_WRITE) {
74474f4d78SMaxime Coquelin 			if (ctrl_elem->desc_ack) {
750e21c7c0SDavid Marchand 				VHOST_CONFIG_LOG(dev->ifname, ERR,
760e21c7c0SDavid Marchand 						"Unexpected ctrl chain layout");
77474f4d78SMaxime Coquelin 				goto err;
78474f4d78SMaxime Coquelin 			}
79474f4d78SMaxime Coquelin 
80474f4d78SMaxime Coquelin 			if (desc_len != sizeof(uint8_t)) {
810e21c7c0SDavid Marchand 				VHOST_CONFIG_LOG(dev->ifname, ERR,
820e21c7c0SDavid Marchand 						"Invalid ack size for ctrl req, dropping");
83474f4d78SMaxime Coquelin 				goto err;
84474f4d78SMaxime Coquelin 			}
85474f4d78SMaxime Coquelin 
86474f4d78SMaxime Coquelin 			ctrl_elem->desc_ack = (uint8_t *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
87474f4d78SMaxime Coquelin 					desc_iova, &desc_len, VHOST_ACCESS_WO);
88474f4d78SMaxime Coquelin 			if (!ctrl_elem->desc_ack || desc_len != sizeof(uint8_t)) {
890e21c7c0SDavid Marchand 				VHOST_CONFIG_LOG(dev->ifname, ERR,
900e21c7c0SDavid Marchand 						"Failed to map ctrl ack descriptor");
91474f4d78SMaxime Coquelin 				goto err;
92474f4d78SMaxime Coquelin 			}
93474f4d78SMaxime Coquelin 		} else {
94474f4d78SMaxime Coquelin 			if (ctrl_elem->desc_ack) {
950e21c7c0SDavid Marchand 				VHOST_CONFIG_LOG(dev->ifname, ERR,
960e21c7c0SDavid Marchand 						"Unexpected ctrl chain layout");
97474f4d78SMaxime Coquelin 				goto err;
98474f4d78SMaxime Coquelin 			}
99474f4d78SMaxime Coquelin 
100474f4d78SMaxime Coquelin 			data_len += desc_len;
101474f4d78SMaxime Coquelin 		}
102474f4d78SMaxime Coquelin 
103474f4d78SMaxime Coquelin 		if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT))
104474f4d78SMaxime Coquelin 			break;
105474f4d78SMaxime Coquelin 
106474f4d78SMaxime Coquelin 		desc_idx = descs[desc_idx].next;
107474f4d78SMaxime Coquelin 	}
108474f4d78SMaxime Coquelin 
109474f4d78SMaxime Coquelin 	desc_idx = ctrl_elem->head_idx;
110474f4d78SMaxime Coquelin 
111474f4d78SMaxime Coquelin 	if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT)
112474f4d78SMaxime Coquelin 		ctrl_elem->n_descs = 1;
113474f4d78SMaxime Coquelin 	else
114474f4d78SMaxime Coquelin 		ctrl_elem->n_descs = n_descs;
115474f4d78SMaxime Coquelin 
116474f4d78SMaxime Coquelin 	if (!ctrl_elem->desc_ack) {
1170e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Missing ctrl ack descriptor");
118474f4d78SMaxime Coquelin 		goto err;
119474f4d78SMaxime Coquelin 	}
120474f4d78SMaxime Coquelin 
121474f4d78SMaxime Coquelin 	if (data_len < sizeof(ctrl_elem->ctrl_req->class) + sizeof(ctrl_elem->ctrl_req->command)) {
1220e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid control header size");
123474f4d78SMaxime Coquelin 		goto err;
124474f4d78SMaxime Coquelin 	}
125474f4d78SMaxime Coquelin 
126474f4d78SMaxime Coquelin 	ctrl_elem->ctrl_req = malloc(data_len);
127474f4d78SMaxime Coquelin 	if (!ctrl_elem->ctrl_req) {
1280e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to alloc ctrl request");
129474f4d78SMaxime Coquelin 		goto err;
130474f4d78SMaxime Coquelin 	}
131474f4d78SMaxime Coquelin 
132474f4d78SMaxime Coquelin 	ctrl_req = (uint8_t *)ctrl_elem->ctrl_req;
133474f4d78SMaxime Coquelin 
134474f4d78SMaxime Coquelin 	if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) {
135474f4d78SMaxime Coquelin 		desc_len = cvq->desc[desc_idx].len;
136474f4d78SMaxime Coquelin 		desc_iova = cvq->desc[desc_idx].addr;
137474f4d78SMaxime Coquelin 
138474f4d78SMaxime Coquelin 		descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
139474f4d78SMaxime Coquelin 					desc_iova, &desc_len, VHOST_ACCESS_RO);
140474f4d78SMaxime Coquelin 		if (!descs || desc_len != cvq->desc[desc_idx].len) {
1410e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs");
142474f4d78SMaxime Coquelin 			goto free_err;
143474f4d78SMaxime Coquelin 		}
144474f4d78SMaxime Coquelin 
145474f4d78SMaxime Coquelin 		desc_idx = 0;
146474f4d78SMaxime Coquelin 	} else {
147474f4d78SMaxime Coquelin 		descs = cvq->desc;
148474f4d78SMaxime Coquelin 	}
149474f4d78SMaxime Coquelin 
150474f4d78SMaxime Coquelin 	while (!(descs[desc_idx].flags & VRING_DESC_F_WRITE)) {
151474f4d78SMaxime Coquelin 		desc_len = descs[desc_idx].len;
152474f4d78SMaxime Coquelin 		desc_iova = descs[desc_idx].addr;
153474f4d78SMaxime Coquelin 
154474f4d78SMaxime Coquelin 		desc_addr = vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_RO);
155474f4d78SMaxime Coquelin 		if (!desc_addr || desc_len < descs[desc_idx].len) {
1560e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl descriptor");
157474f4d78SMaxime Coquelin 			goto free_err;
158474f4d78SMaxime Coquelin 		}
159474f4d78SMaxime Coquelin 
160474f4d78SMaxime Coquelin 		memcpy(ctrl_req, (void *)(uintptr_t)desc_addr, desc_len);
161474f4d78SMaxime Coquelin 		ctrl_req += desc_len;
162474f4d78SMaxime Coquelin 
163474f4d78SMaxime Coquelin 		if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT))
164474f4d78SMaxime Coquelin 			break;
165474f4d78SMaxime Coquelin 
166474f4d78SMaxime Coquelin 		desc_idx = descs[desc_idx].next;
167474f4d78SMaxime Coquelin 	}
168474f4d78SMaxime Coquelin 
169474f4d78SMaxime Coquelin 	cvq->last_avail_idx++;
170474f4d78SMaxime Coquelin 	if (cvq->last_avail_idx >= cvq->size)
171474f4d78SMaxime Coquelin 		cvq->last_avail_idx -= cvq->size;
172*15677ca2SMaxime Coquelin 	vhost_virtqueue_reconnect_log_split(cvq);
173474f4d78SMaxime Coquelin 
174474f4d78SMaxime Coquelin 	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
175474f4d78SMaxime Coquelin 		vhost_avail_event(cvq) = cvq->last_avail_idx;
176474f4d78SMaxime Coquelin 
177474f4d78SMaxime Coquelin 	return 1;
178474f4d78SMaxime Coquelin 
179474f4d78SMaxime Coquelin free_err:
180474f4d78SMaxime Coquelin 	free(ctrl_elem->ctrl_req);
181474f4d78SMaxime Coquelin err:
182474f4d78SMaxime Coquelin 	cvq->last_avail_idx++;
183474f4d78SMaxime Coquelin 	if (cvq->last_avail_idx >= cvq->size)
184474f4d78SMaxime Coquelin 		cvq->last_avail_idx -= cvq->size;
185*15677ca2SMaxime Coquelin 	vhost_virtqueue_reconnect_log_split(cvq);
186474f4d78SMaxime Coquelin 
187474f4d78SMaxime Coquelin 	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
188474f4d78SMaxime Coquelin 		vhost_avail_event(cvq) = cvq->last_avail_idx;
189474f4d78SMaxime Coquelin 
190474f4d78SMaxime Coquelin 	return -1;
191474f4d78SMaxime Coquelin }
192474f4d78SMaxime Coquelin 
193474f4d78SMaxime Coquelin static uint8_t
194474f4d78SMaxime Coquelin virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl *ctrl_req)
195474f4d78SMaxime Coquelin {
196474f4d78SMaxime Coquelin 	uint8_t ret = VIRTIO_NET_ERR;
197474f4d78SMaxime Coquelin 
198474f4d78SMaxime Coquelin 	if (ctrl_req->class == VIRTIO_NET_CTRL_MQ &&
199474f4d78SMaxime Coquelin 			ctrl_req->command == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
200474f4d78SMaxime Coquelin 		uint16_t queue_pairs;
201474f4d78SMaxime Coquelin 		uint32_t i;
202474f4d78SMaxime Coquelin 
203474f4d78SMaxime Coquelin 		queue_pairs = *(uint16_t *)(uintptr_t)ctrl_req->command_data;
2040e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl req: MQ %u queue pairs", queue_pairs);
205474f4d78SMaxime Coquelin 		ret = VIRTIO_NET_OK;
206474f4d78SMaxime Coquelin 
207474f4d78SMaxime Coquelin 		for (i = 0; i < dev->nr_vring; i++) {
208474f4d78SMaxime Coquelin 			struct vhost_virtqueue *vq = dev->virtqueue[i];
209474f4d78SMaxime Coquelin 			bool enable;
210474f4d78SMaxime Coquelin 
211474f4d78SMaxime Coquelin 			if (vq == dev->cvq)
212474f4d78SMaxime Coquelin 				continue;
213474f4d78SMaxime Coquelin 
214474f4d78SMaxime Coquelin 			if (i < queue_pairs * 2)
215474f4d78SMaxime Coquelin 				enable = true;
216474f4d78SMaxime Coquelin 			else
217474f4d78SMaxime Coquelin 				enable = false;
218474f4d78SMaxime Coquelin 
219474f4d78SMaxime Coquelin 			vq->enabled = enable;
220474f4d78SMaxime Coquelin 			if (dev->notify_ops->vring_state_changed)
221474f4d78SMaxime Coquelin 				dev->notify_ops->vring_state_changed(dev->vid, i, enable);
222474f4d78SMaxime Coquelin 		}
223474f4d78SMaxime Coquelin 	}
224474f4d78SMaxime Coquelin 
225474f4d78SMaxime Coquelin 	return ret;
226474f4d78SMaxime Coquelin }
227474f4d78SMaxime Coquelin 
228474f4d78SMaxime Coquelin static int
229474f4d78SMaxime Coquelin virtio_net_ctrl_push(struct virtio_net *dev, struct virtio_net_ctrl_elem *ctrl_elem)
230474f4d78SMaxime Coquelin {
231474f4d78SMaxime Coquelin 	struct vhost_virtqueue *cvq = dev->cvq;
232474f4d78SMaxime Coquelin 	struct vring_used_elem *used_elem;
233474f4d78SMaxime Coquelin 
234474f4d78SMaxime Coquelin 	used_elem = &cvq->used->ring[cvq->last_used_idx];
235474f4d78SMaxime Coquelin 	used_elem->id = ctrl_elem->head_idx;
236474f4d78SMaxime Coquelin 	used_elem->len = ctrl_elem->n_descs;
237474f4d78SMaxime Coquelin 
238474f4d78SMaxime Coquelin 	cvq->last_used_idx++;
239474f4d78SMaxime Coquelin 	if (cvq->last_used_idx >= cvq->size)
240474f4d78SMaxime Coquelin 		cvq->last_used_idx -= cvq->size;
241474f4d78SMaxime Coquelin 
2425147b641STyler Retzlaff 	rte_atomic_store_explicit((unsigned short __rte_atomic *)&cvq->used->idx,
2435147b641STyler Retzlaff 		cvq->last_used_idx, rte_memory_order_release);
244474f4d78SMaxime Coquelin 
245474f4d78SMaxime Coquelin 	vhost_vring_call_split(dev, dev->cvq);
246474f4d78SMaxime Coquelin 
247474f4d78SMaxime Coquelin 	free(ctrl_elem->ctrl_req);
248474f4d78SMaxime Coquelin 
249474f4d78SMaxime Coquelin 	return 0;
250474f4d78SMaxime Coquelin }
251474f4d78SMaxime Coquelin 
252474f4d78SMaxime Coquelin int
253474f4d78SMaxime Coquelin virtio_net_ctrl_handle(struct virtio_net *dev)
254474f4d78SMaxime Coquelin {
255474f4d78SMaxime Coquelin 	int ret = 0;
256474f4d78SMaxime Coquelin 
257474f4d78SMaxime Coquelin 	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
2580e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Packed ring not supported yet");
259474f4d78SMaxime Coquelin 		return -1;
260474f4d78SMaxime Coquelin 	}
261474f4d78SMaxime Coquelin 
262474f4d78SMaxime Coquelin 	if (!dev->cvq) {
2630e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "missing control queue");
264474f4d78SMaxime Coquelin 		return -1;
265474f4d78SMaxime Coquelin 	}
266474f4d78SMaxime Coquelin 
267474f4d78SMaxime Coquelin 	rte_rwlock_read_lock(&dev->cvq->access_lock);
268474f4d78SMaxime Coquelin 	vhost_user_iotlb_rd_lock(dev->cvq);
269474f4d78SMaxime Coquelin 
270474f4d78SMaxime Coquelin 	while (1) {
271474f4d78SMaxime Coquelin 		struct virtio_net_ctrl_elem ctrl_elem;
272474f4d78SMaxime Coquelin 
273474f4d78SMaxime Coquelin 		memset(&ctrl_elem, 0, sizeof(struct virtio_net_ctrl_elem));
274474f4d78SMaxime Coquelin 
275474f4d78SMaxime Coquelin 		ret = virtio_net_ctrl_pop(dev, dev->cvq, &ctrl_elem);
276474f4d78SMaxime Coquelin 		if (ret <= 0)
277474f4d78SMaxime Coquelin 			break;
278474f4d78SMaxime Coquelin 
279474f4d78SMaxime Coquelin 		*ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, ctrl_elem.ctrl_req);
280474f4d78SMaxime Coquelin 
281474f4d78SMaxime Coquelin 		ret = virtio_net_ctrl_push(dev, &ctrl_elem);
282474f4d78SMaxime Coquelin 		if (ret < 0)
283474f4d78SMaxime Coquelin 			break;
284474f4d78SMaxime Coquelin 	}
285474f4d78SMaxime Coquelin 
286474f4d78SMaxime Coquelin 	vhost_user_iotlb_rd_unlock(dev->cvq);
287474f4d78SMaxime Coquelin 	rte_rwlock_read_unlock(&dev->cvq->access_lock);
288474f4d78SMaxime Coquelin 
289474f4d78SMaxime Coquelin 	return ret;
290474f4d78SMaxime Coquelin }
291