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