Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2023 Red Hat, Inc.
3 : : */
4 : :
5 : : #include <stdint.h>
6 : : #include <stdio.h>
7 : : #include <unistd.h>
8 : :
9 : : #include "iotlb.h"
10 : : #include "vhost.h"
11 : : #include "virtio_net_ctrl.h"
12 : :
13 : : struct virtio_net_ctrl {
14 : : uint8_t class;
15 : : uint8_t command;
16 : : uint8_t command_data[];
17 : : };
18 : :
19 : : struct virtio_net_ctrl_elem {
20 : : struct virtio_net_ctrl *ctrl_req;
21 : : uint16_t head_idx;
22 : : uint16_t n_descs;
23 : : uint8_t *desc_ack;
24 : : };
25 : :
26 : : static int
27 : 0 : virtio_net_ctrl_pop(struct virtio_net *dev, struct vhost_virtqueue *cvq,
28 : : struct virtio_net_ctrl_elem *ctrl_elem)
29 : : __rte_requires_shared_capability(&cvq->iotlb_lock)
30 : : {
31 : : uint16_t avail_idx, desc_idx, n_descs = 0, nr_descs, cnt = 0;
32 : : uint64_t desc_len, desc_addr, desc_iova, data_len = 0;
33 : : uint8_t *ctrl_req;
34 : : struct vring_desc *descs;
35 : :
36 : 0 : avail_idx = rte_atomic_load_explicit((unsigned short __rte_atomic *)&cvq->avail->idx,
37 : : rte_memory_order_acquire);
38 [ # # ]: 0 : if (avail_idx == cvq->last_avail_idx) {
39 : 0 : VHOST_CONFIG_LOG(dev->ifname, DEBUG, "Control queue empty");
40 : 0 : return 0;
41 : : }
42 : :
43 : 0 : desc_idx = cvq->avail->ring[cvq->last_avail_idx & (cvq->size - 1)];
44 [ # # ]: 0 : if (desc_idx >= cvq->size) {
45 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Out of range desc index, dropping");
46 : 0 : goto err;
47 : : }
48 : :
49 : 0 : ctrl_elem->head_idx = desc_idx;
50 : :
51 [ # # ]: 0 : if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) {
52 : 0 : desc_len = cvq->desc[desc_idx].len;
53 [ # # ]: 0 : desc_iova = cvq->desc[desc_idx].addr;
54 : :
55 : 0 : descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
56 : : desc_iova, &desc_len, VHOST_ACCESS_RO);
57 [ # # # # ]: 0 : if (!descs || desc_len != cvq->desc[desc_idx].len) {
58 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs");
59 : 0 : goto err;
60 : : }
61 : :
62 : 0 : nr_descs = desc_len / sizeof(struct vring_desc);
63 : : desc_idx = 0;
64 : : } else {
65 : : descs = cvq->desc;
66 : 0 : nr_descs = cvq->size;
67 : : }
68 : :
69 : : while (1) {
70 [ # # # # ]: 0 : if (unlikely(desc_idx >= nr_descs || cnt++ >= nr_descs)) {
71 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid ctrl descriptor chain");
72 : 0 : goto err;
73 : : }
74 : :
75 : 0 : desc_len = descs[desc_idx].len;
76 : 0 : desc_iova = descs[desc_idx].addr;
77 : :
78 : : n_descs++;
79 : :
80 [ # # ]: 0 : if (descs[desc_idx].flags & VRING_DESC_F_WRITE) {
81 [ # # ]: 0 : if (ctrl_elem->desc_ack) {
82 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
83 : : "Unexpected ctrl chain layout");
84 : 0 : goto err;
85 : : }
86 : :
87 [ # # ]: 0 : if (desc_len != sizeof(uint8_t)) {
88 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
89 : : "Invalid ack size for ctrl req, dropping");
90 : 0 : goto err;
91 : : }
92 : :
93 : 0 : ctrl_elem->desc_ack = (uint8_t *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
94 : : desc_iova, &desc_len, VHOST_ACCESS_WO);
95 [ # # # # ]: 0 : if (!ctrl_elem->desc_ack || desc_len != sizeof(uint8_t)) {
96 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
97 : : "Failed to map ctrl ack descriptor");
98 : 0 : goto err;
99 : : }
100 : : } else {
101 [ # # ]: 0 : if (ctrl_elem->desc_ack) {
102 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
103 : : "Unexpected ctrl chain layout");
104 : 0 : goto err;
105 : : }
106 : :
107 : 0 : data_len += desc_len;
108 : : }
109 : :
110 [ # # ]: 0 : if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT))
111 : : break;
112 : :
113 : 0 : desc_idx = descs[desc_idx].next;
114 : : }
115 : :
116 : 0 : desc_idx = ctrl_elem->head_idx;
117 : :
118 [ # # ]: 0 : if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT)
119 : 0 : ctrl_elem->n_descs = 1;
120 : : else
121 : 0 : ctrl_elem->n_descs = n_descs;
122 : :
123 [ # # ]: 0 : if (!ctrl_elem->desc_ack) {
124 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Missing ctrl ack descriptor");
125 : 0 : goto err;
126 : : }
127 : :
128 [ # # ]: 0 : if (data_len < sizeof(ctrl_elem->ctrl_req->class) + sizeof(ctrl_elem->ctrl_req->command)) {
129 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid control header size");
130 : 0 : goto err;
131 : : }
132 : :
133 : 0 : ctrl_elem->ctrl_req = malloc(data_len);
134 [ # # ]: 0 : if (!ctrl_elem->ctrl_req) {
135 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to alloc ctrl request");
136 : 0 : goto err;
137 : : }
138 : :
139 : : ctrl_req = (uint8_t *)ctrl_elem->ctrl_req;
140 : :
141 [ # # ]: 0 : if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) {
142 : 0 : desc_len = cvq->desc[desc_idx].len;
143 [ # # ]: 0 : desc_iova = cvq->desc[desc_idx].addr;
144 : :
145 : 0 : descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
146 : : desc_iova, &desc_len, VHOST_ACCESS_RO);
147 [ # # # # ]: 0 : if (!descs || desc_len != cvq->desc[desc_idx].len) {
148 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs");
149 : 0 : goto free_err;
150 : : }
151 : :
152 : 0 : nr_descs = desc_len / sizeof(struct vring_desc);
153 : : desc_idx = 0;
154 : : } else {
155 : : descs = cvq->desc;
156 : 0 : nr_descs = cvq->size;
157 : : }
158 : :
159 : : cnt = 0;
160 : : while (1) {
161 [ # # # # ]: 0 : if (unlikely(desc_idx >= nr_descs || cnt++ >= nr_descs)) {
162 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid ctrl descriptor chain");
163 : 0 : goto free_err;
164 : : }
165 : :
166 [ # # ]: 0 : if (descs[desc_idx].flags & VRING_DESC_F_WRITE)
167 : : break;
168 : :
169 : 0 : desc_len = descs[desc_idx].len;
170 [ # # ]: 0 : desc_iova = descs[desc_idx].addr;
171 : :
172 : : desc_addr = vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_RO);
173 [ # # # # ]: 0 : if (!desc_addr || desc_len < descs[desc_idx].len) {
174 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl descriptor");
175 : 0 : goto free_err;
176 : : }
177 : :
178 [ # # ]: 0 : memcpy(ctrl_req, (void *)(uintptr_t)desc_addr, desc_len);
179 : 0 : ctrl_req += desc_len;
180 : :
181 [ # # ]: 0 : if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT))
182 : : break;
183 : :
184 : 0 : desc_idx = descs[desc_idx].next;
185 : : }
186 : :
187 [ # # ]: 0 : cvq->last_avail_idx++;
188 : : vhost_virtqueue_reconnect_log_split(cvq);
189 : :
190 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
191 : 0 : vhost_avail_event(cvq) = cvq->last_avail_idx;
192 : :
193 : : return 1;
194 : :
195 : 0 : free_err:
196 : 0 : free(ctrl_elem->ctrl_req);
197 : 0 : err:
198 [ # # ]: 0 : cvq->last_avail_idx++;
199 : : vhost_virtqueue_reconnect_log_split(cvq);
200 : :
201 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
202 : 0 : vhost_avail_event(cvq) = cvq->last_avail_idx;
203 : :
204 : : return -1;
205 : : }
206 : :
207 : : static uint8_t
208 : 0 : virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl *ctrl_req)
209 : : {
210 : : uint8_t ret = VIRTIO_NET_ERR;
211 : :
212 [ # # ]: 0 : if (ctrl_req->class == VIRTIO_NET_CTRL_MQ &&
213 [ # # ]: 0 : ctrl_req->command == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
214 : : uint16_t queue_pairs;
215 : : uint32_t i;
216 : :
217 : 0 : queue_pairs = *(uint16_t *)(uintptr_t)ctrl_req->command_data;
218 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl req: MQ %u queue pairs", queue_pairs);
219 : : ret = VIRTIO_NET_OK;
220 : :
221 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++) {
222 : 0 : struct vhost_virtqueue *vq = dev->virtqueue[i];
223 : : bool enable;
224 : :
225 [ # # ]: 0 : if (vq == dev->cvq)
226 : 0 : continue;
227 : :
228 [ # # ]: 0 : if (i < queue_pairs * 2)
229 : : enable = true;
230 : : else
231 : : enable = false;
232 : :
233 : 0 : vq->enabled = enable;
234 [ # # ]: 0 : if (dev->notify_ops->vring_state_changed)
235 : 0 : dev->notify_ops->vring_state_changed(dev->vid, i, enable);
236 : : }
237 : : }
238 : :
239 : 0 : return ret;
240 : : }
241 : :
242 : : static int
243 : 0 : virtio_net_ctrl_push(struct virtio_net *dev, struct virtio_net_ctrl_elem *ctrl_elem)
244 : : {
245 : 0 : struct vhost_virtqueue *cvq = dev->cvq;
246 : : struct vring_used_elem *used_elem;
247 : :
248 : 0 : used_elem = &cvq->used->ring[cvq->last_used_idx & (cvq->size - 1)];
249 : 0 : used_elem->id = ctrl_elem->head_idx;
250 : 0 : used_elem->len = sizeof(*ctrl_elem->desc_ack);
251 : :
252 : 0 : cvq->last_used_idx++;
253 : :
254 : 0 : rte_atomic_store_explicit((unsigned short __rte_atomic *)&cvq->used->idx,
255 : : cvq->last_used_idx, rte_memory_order_release);
256 : :
257 : 0 : vhost_vring_call_split(dev, dev->cvq);
258 : :
259 : 0 : free(ctrl_elem->ctrl_req);
260 : :
261 : 0 : return 0;
262 : : }
263 : :
264 : : int
265 : 0 : virtio_net_ctrl_handle(struct virtio_net *dev)
266 : : {
267 : : int ret = 0;
268 : :
269 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
270 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Packed ring not supported yet");
271 : 0 : return -1;
272 : : }
273 : :
274 [ # # ]: 0 : if (!dev->cvq) {
275 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "missing control queue");
276 : 0 : return -1;
277 : : }
278 : :
279 : 0 : rte_rwlock_read_lock(&dev->cvq->access_lock);
280 : 0 : vhost_user_iotlb_rd_lock(dev->cvq);
281 : :
282 : 0 : while (1) {
283 : : struct virtio_net_ctrl_elem ctrl_elem;
284 : :
285 : : memset(&ctrl_elem, 0, sizeof(struct virtio_net_ctrl_elem));
286 : :
287 : 0 : ret = virtio_net_ctrl_pop(dev, dev->cvq, &ctrl_elem);
288 [ # # ]: 0 : if (ret <= 0)
289 : : break;
290 : :
291 : 0 : *ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, ctrl_elem.ctrl_req);
292 : :
293 : 0 : ret = virtio_net_ctrl_push(dev, &ctrl_elem);
294 [ # # ]: 0 : if (ret < 0)
295 : : break;
296 : : }
297 : :
298 : 0 : vhost_user_iotlb_rd_unlock(dev->cvq);
299 : 0 : rte_rwlock_read_unlock(&dev->cvq->access_lock);
300 : :
301 : 0 : return ret;
302 : : }
|