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 : : vhost_virtqueue_reconnect_log_split(cvq);
173 : :
174 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
175 : 0 : vhost_avail_event(cvq) = cvq->last_avail_idx;
176 : :
177 : : return 1;
178 : :
179 : 0 : free_err:
180 : 0 : free(ctrl_elem->ctrl_req);
181 : 0 : err:
182 : 0 : cvq->last_avail_idx++;
183 [ # # ]: 0 : if (cvq->last_avail_idx >= cvq->size)
184 : 0 : cvq->last_avail_idx -= cvq->size;
185 : : vhost_virtqueue_reconnect_log_split(cvq);
186 : :
187 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
188 : 0 : vhost_avail_event(cvq) = cvq->last_avail_idx;
189 : :
190 : : return -1;
191 : : }
192 : :
193 : : static uint8_t
194 : 0 : virtio_net_ctrl_handle_req(struct virtio_net *dev, struct virtio_net_ctrl *ctrl_req)
195 : : {
196 : : uint8_t ret = VIRTIO_NET_ERR;
197 : :
198 [ # # ]: 0 : if (ctrl_req->class == VIRTIO_NET_CTRL_MQ &&
199 [ # # ]: 0 : ctrl_req->command == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
200 : : uint16_t queue_pairs;
201 : : uint32_t i;
202 : :
203 : 0 : queue_pairs = *(uint16_t *)(uintptr_t)ctrl_req->command_data;
204 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl req: MQ %u queue pairs", queue_pairs);
205 : : ret = VIRTIO_NET_OK;
206 : :
207 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++) {
208 : 0 : struct vhost_virtqueue *vq = dev->virtqueue[i];
209 : : bool enable;
210 : :
211 [ # # ]: 0 : if (vq == dev->cvq)
212 : 0 : continue;
213 : :
214 [ # # ]: 0 : if (i < queue_pairs * 2)
215 : : enable = true;
216 : : else
217 : : enable = false;
218 : :
219 : 0 : vq->enabled = enable;
220 [ # # ]: 0 : if (dev->notify_ops->vring_state_changed)
221 : 0 : dev->notify_ops->vring_state_changed(dev->vid, i, enable);
222 : : }
223 : : }
224 : :
225 : 0 : return ret;
226 : : }
227 : :
228 : : static int
229 : 0 : virtio_net_ctrl_push(struct virtio_net *dev, struct virtio_net_ctrl_elem *ctrl_elem)
230 : : {
231 : 0 : struct vhost_virtqueue *cvq = dev->cvq;
232 : : struct vring_used_elem *used_elem;
233 : :
234 : 0 : used_elem = &cvq->used->ring[cvq->last_used_idx];
235 : 0 : used_elem->id = ctrl_elem->head_idx;
236 : 0 : used_elem->len = ctrl_elem->n_descs;
237 : :
238 : 0 : cvq->last_used_idx++;
239 [ # # ]: 0 : if (cvq->last_used_idx >= cvq->size)
240 : 0 : cvq->last_used_idx -= cvq->size;
241 : :
242 : 0 : rte_atomic_store_explicit((unsigned short __rte_atomic *)&cvq->used->idx,
243 : : cvq->last_used_idx, rte_memory_order_release);
244 : :
245 : 0 : vhost_vring_call_split(dev, dev->cvq);
246 : :
247 : 0 : free(ctrl_elem->ctrl_req);
248 : :
249 : 0 : return 0;
250 : : }
251 : :
252 : : int
253 : 0 : virtio_net_ctrl_handle(struct virtio_net *dev)
254 : : {
255 : : int ret = 0;
256 : :
257 [ # # ]: 0 : if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
258 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Packed ring not supported yet");
259 : 0 : return -1;
260 : : }
261 : :
262 [ # # ]: 0 : if (!dev->cvq) {
263 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "missing control queue");
264 : 0 : return -1;
265 : : }
266 : :
267 : 0 : rte_rwlock_read_lock(&dev->cvq->access_lock);
268 : 0 : vhost_user_iotlb_rd_lock(dev->cvq);
269 : :
270 : 0 : while (1) {
271 : : struct virtio_net_ctrl_elem ctrl_elem;
272 : :
273 : : memset(&ctrl_elem, 0, sizeof(struct virtio_net_ctrl_elem));
274 : :
275 : 0 : ret = virtio_net_ctrl_pop(dev, dev->cvq, &ctrl_elem);
276 [ # # ]: 0 : if (ret <= 0)
277 : : break;
278 : :
279 : 0 : *ctrl_elem.desc_ack = virtio_net_ctrl_handle_req(dev, ctrl_elem.ctrl_req);
280 : :
281 : 0 : ret = virtio_net_ctrl_push(dev, &ctrl_elem);
282 [ # # ]: 0 : if (ret < 0)
283 : : break;
284 : : }
285 : :
286 : 0 : vhost_user_iotlb_rd_unlock(dev->cvq);
287 : 0 : rte_rwlock_read_unlock(&dev->cvq->access_lock);
288 : :
289 : 0 : return ret;
290 : : }
|