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_shared_locks_required(&cvq->iotlb_lock)
30 : : {
31 : : uint16_t avail_idx, desc_idx, n_descs = 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];
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 : : desc_idx = 0;
63 : : } else {
64 : : descs = cvq->desc;
65 : : }
66 : :
67 : : while (1) {
68 : 0 : desc_len = descs[desc_idx].len;
69 : 0 : desc_iova = descs[desc_idx].addr;
70 : :
71 : 0 : n_descs++;
72 : :
73 [ # # ]: 0 : if (descs[desc_idx].flags & VRING_DESC_F_WRITE) {
74 [ # # ]: 0 : if (ctrl_elem->desc_ack) {
75 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
76 : : "Unexpected ctrl chain layout");
77 : 0 : goto err;
78 : : }
79 : :
80 [ # # ]: 0 : if (desc_len != sizeof(uint8_t)) {
81 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
82 : : "Invalid ack size for ctrl req, dropping");
83 : 0 : goto err;
84 : : }
85 : :
86 : 0 : ctrl_elem->desc_ack = (uint8_t *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
87 : : desc_iova, &desc_len, VHOST_ACCESS_WO);
88 [ # # # # ]: 0 : if (!ctrl_elem->desc_ack || desc_len != sizeof(uint8_t)) {
89 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
90 : : "Failed to map ctrl ack descriptor");
91 : 0 : goto err;
92 : : }
93 : : } else {
94 [ # # ]: 0 : if (ctrl_elem->desc_ack) {
95 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
96 : : "Unexpected ctrl chain layout");
97 : 0 : goto err;
98 : : }
99 : :
100 : 0 : data_len += desc_len;
101 : : }
102 : :
103 [ # # ]: 0 : if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT))
104 : : break;
105 : :
106 : 0 : desc_idx = descs[desc_idx].next;
107 : : }
108 : :
109 : 0 : desc_idx = ctrl_elem->head_idx;
110 : :
111 [ # # ]: 0 : if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT)
112 : 0 : ctrl_elem->n_descs = 1;
113 : : else
114 : 0 : ctrl_elem->n_descs = n_descs;
115 : :
116 [ # # ]: 0 : if (!ctrl_elem->desc_ack) {
117 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Missing ctrl ack descriptor");
118 : 0 : goto err;
119 : : }
120 : :
121 [ # # ]: 0 : if (data_len < sizeof(ctrl_elem->ctrl_req->class) + sizeof(ctrl_elem->ctrl_req->command)) {
122 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Invalid control header size");
123 : 0 : goto err;
124 : : }
125 : :
126 : 0 : ctrl_elem->ctrl_req = malloc(data_len);
127 [ # # ]: 0 : if (!ctrl_elem->ctrl_req) {
128 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to alloc ctrl request");
129 : 0 : goto err;
130 : : }
131 : :
132 : : ctrl_req = (uint8_t *)ctrl_elem->ctrl_req;
133 : :
134 [ # # ]: 0 : if (cvq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) {
135 : 0 : desc_len = cvq->desc[desc_idx].len;
136 [ # # ]: 0 : desc_iova = cvq->desc[desc_idx].addr;
137 : :
138 : 0 : descs = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, cvq,
139 : : desc_iova, &desc_len, VHOST_ACCESS_RO);
140 [ # # # # ]: 0 : if (!descs || desc_len != cvq->desc[desc_idx].len) {
141 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl indirect descs");
142 : 0 : goto free_err;
143 : : }
144 : :
145 : : desc_idx = 0;
146 : : } else {
147 : : descs = cvq->desc;
148 : : }
149 : :
150 [ # # ]: 0 : while (!(descs[desc_idx].flags & VRING_DESC_F_WRITE)) {
151 : 0 : desc_len = descs[desc_idx].len;
152 [ # # ]: 0 : desc_iova = descs[desc_idx].addr;
153 : :
154 : : desc_addr = vhost_iova_to_vva(dev, cvq, desc_iova, &desc_len, VHOST_ACCESS_RO);
155 [ # # # # ]: 0 : if (!desc_addr || desc_len < descs[desc_idx].len) {
156 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to map ctrl descriptor");
157 : 0 : goto free_err;
158 : : }
159 : :
160 [ # # ]: 0 : memcpy(ctrl_req, (void *)(uintptr_t)desc_addr, desc_len);
161 : 0 : ctrl_req += desc_len;
162 : :
163 [ # # ]: 0 : if (!(descs[desc_idx].flags & VRING_DESC_F_NEXT))
164 : : break;
165 : :
166 : 0 : desc_idx = descs[desc_idx].next;
167 : : }
168 : :
169 : 0 : cvq->last_avail_idx++;
170 [ # # ]: 0 : if (cvq->last_avail_idx >= cvq->size)
171 : 0 : cvq->last_avail_idx -= cvq->size;
172 : :
173 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
174 : 0 : vhost_avail_event(cvq) = cvq->last_avail_idx;
175 : :
176 : : return 1;
177 : :
178 : 0 : free_err:
179 : 0 : free(ctrl_elem->ctrl_req);
180 : 0 : err:
181 : 0 : cvq->last_avail_idx++;
182 [ # # ]: 0 : if (cvq->last_avail_idx >= cvq->size)
183 : 0 : cvq->last_avail_idx -= cvq->size;
184 : :
185 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
186 : 0 : vhost_avail_event(cvq) = cvq->last_avail_idx;
187 : :
188 : : return -1;
189 : : }
190 : :
191 : : static uint8_t
192 : 0 : virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl *ctrl_req)
193 : : {
194 : : uint8_t ret = VIRTIO_NET_ERR;
195 : :
196 [ # # ]: 0 : if (ctrl_req->class == VIRTIO_NET_CTRL_MQ &&
197 [ # # ]: 0 : ctrl_req->command == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
198 : : uint16_t queue_pairs;
199 : : uint32_t i;
200 : :
201 : 0 : queue_pairs = *(uint16_t *)(uintptr_t)ctrl_req->command_data;
202 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl req: MQ %u queue pairs", queue_pairs);
203 : : ret = VIRTIO_NET_OK;
204 : :
205 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++) {
206 : 0 : struct vhost_virtqueue *vq = dev->virtqueue[i];
207 : : bool enable;
208 : :
209 [ # # ]: 0 : if (vq == dev->cvq)
210 : 0 : continue;
211 : :
212 [ # # ]: 0 : if (i < queue_pairs * 2)
213 : : enable = true;
214 : : else
215 : : enable = false;
216 : :
217 : 0 : vq->enabled = enable;
218 [ # # ]: 0 : if (dev->notify_ops->vring_state_changed)
219 : 0 : dev->notify_ops->vring_state_changed(dev->vid, i, enable);
220 : : }
221 : : }
222 : :
223 : 0 : return ret;
224 : : }
225 : :
226 : : static int
227 : 0 : virtio_net_ctrl_push(struct virtio_net *dev, struct virtio_net_ctrl_elem *ctrl_elem)
228 : : {
229 : 0 : struct vhost_virtqueue *cvq = dev->cvq;
230 : : struct vring_used_elem *used_elem;
231 : :
232 : 0 : used_elem = &cvq->used->ring[cvq->last_used_idx];
233 : 0 : used_elem->id = ctrl_elem->head_idx;
234 : 0 : used_elem->len = ctrl_elem->n_descs;
235 : :
236 : 0 : cvq->last_used_idx++;
237 [ # # ]: 0 : if (cvq->last_used_idx >= cvq->size)
238 : 0 : cvq->last_used_idx -= cvq->size;
239 : :
240 : 0 : rte_atomic_store_explicit((unsigned short __rte_atomic *)&cvq->used->idx,
241 : : cvq->last_used_idx, rte_memory_order_release);
242 : :
243 : 0 : vhost_vring_call_split(dev, dev->cvq);
244 : :
245 : 0 : free(ctrl_elem->ctrl_req);
246 : :
247 : 0 : return 0;
248 : : }
249 : :
250 : : int
251 : 0 : virtio_net_ctrl_handle(struct virtio_net *dev)
252 : : {
253 : : int ret = 0;
254 : :
255 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
256 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Packed ring not supported yet");
257 : 0 : return -1;
258 : : }
259 : :
260 [ # # ]: 0 : if (!dev->cvq) {
261 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "missing control queue");
262 : 0 : return -1;
263 : : }
264 : :
265 : 0 : rte_rwlock_read_lock(&dev->cvq->access_lock);
266 : 0 : vhost_user_iotlb_rd_lock(dev->cvq);
267 : :
268 : 0 : while (1) {
269 : : struct virtio_net_ctrl_elem ctrl_elem;
270 : :
271 : : memset(&ctrl_elem, 0, sizeof(struct virtio_net_ctrl_elem));
272 : :
273 : 0 : ret = virtio_net_ctrl_pop(dev, dev->cvq, &ctrl_elem);
274 [ # # ]: 0 : if (ret <= 0)
275 : : break;
276 : :
277 : 0 : *ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, ctrl_elem.ctrl_req);
278 : :
279 : 0 : ret = virtio_net_ctrl_push(dev, &ctrl_elem);
280 [ # # ]: 0 : if (ret < 0)
281 : : break;
282 : : }
283 : :
284 : 0 : vhost_user_iotlb_rd_unlock(dev->cvq);
285 : 0 : rte_rwlock_read_unlock(&dev->cvq->access_lock);
286 : :
287 : 0 : return ret;
288 : : }
|