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 : : #include <fcntl.h>
9 : :
10 : :
11 : : #include <linux/vduse.h>
12 : : #include <linux/virtio_net.h>
13 : :
14 : : #include <sys/ioctl.h>
15 : : #include <sys/mman.h>
16 : : #include <sys/stat.h>
17 : :
18 : : #include <rte_common.h>
19 : : #include <rte_thread.h>
20 : :
21 : : #include "fd_man.h"
22 : : #include "iotlb.h"
23 : : #include "vduse.h"
24 : : #include "vhost.h"
25 : : #include "virtio_net_ctrl.h"
26 : :
27 : : #define VHOST_VDUSE_API_VERSION 0
28 : : #define VDUSE_CTRL_PATH "/dev/vduse/control"
29 : :
30 : : struct vduse {
31 : : struct fdset fdset;
32 : : };
33 : :
34 : : static struct vduse vduse = {
35 : : .fdset = {
36 : : .fd = { [0 ... MAX_FDS - 1] = {-1, NULL, NULL, NULL, 0} },
37 : : .fd_mutex = PTHREAD_MUTEX_INITIALIZER,
38 : : .fd_pooling_mutex = PTHREAD_MUTEX_INITIALIZER,
39 : : .num = 0
40 : : },
41 : : };
42 : :
43 : : static bool vduse_events_thread;
44 : :
45 : : static const char * const vduse_reqs_str[] = {
46 : : "VDUSE_GET_VQ_STATE",
47 : : "VDUSE_SET_STATUS",
48 : : "VDUSE_UPDATE_IOTLB",
49 : : };
50 : :
51 : : #define vduse_req_id_to_str(id) \
52 : : (id < RTE_DIM(vduse_reqs_str) ? \
53 : : vduse_reqs_str[id] : "Unknown")
54 : :
55 : : static int
56 : 0 : vduse_inject_irq(struct virtio_net *dev, struct vhost_virtqueue *vq)
57 : : {
58 : 0 : return ioctl(dev->vduse_dev_fd, VDUSE_VQ_INJECT_IRQ, &vq->index);
59 : : }
60 : :
61 : : static void
62 : 0 : vduse_iotlb_remove_notify(uint64_t addr, uint64_t offset, uint64_t size)
63 : : {
64 : 0 : munmap((void *)(uintptr_t)addr, offset + size);
65 : 0 : }
66 : :
67 : : static int
68 : 0 : vduse_iotlb_miss(struct virtio_net *dev, uint64_t iova, uint8_t perm __rte_unused)
69 : : {
70 : : struct vduse_iotlb_entry entry;
71 : : uint64_t size, page_size;
72 : : struct stat stat;
73 : : void *mmap_addr;
74 : : int fd, ret;
75 : :
76 : 0 : entry.start = iova;
77 : 0 : entry.last = iova + 1;
78 : :
79 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_IOTLB_GET_FD, &entry);
80 [ # # ]: 0 : if (ret < 0) {
81 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get IOTLB entry for 0x%" PRIx64,
82 : : iova);
83 : 0 : return -1;
84 : : }
85 : :
86 : : fd = ret;
87 : :
88 : 0 : VHOST_CONFIG_LOG(dev->ifname, DEBUG, "New IOTLB entry:");
89 : 0 : VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tIOVA: %" PRIx64 " - %" PRIx64,
90 : : (uint64_t)entry.start, (uint64_t)entry.last);
91 : 0 : VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\toffset: %" PRIx64, (uint64_t)entry.offset);
92 : 0 : VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tfd: %d", fd);
93 : 0 : VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tperm: %x", entry.perm);
94 : :
95 : 0 : size = entry.last - entry.start + 1;
96 : 0 : mmap_addr = mmap(0, size + entry.offset, entry.perm, MAP_SHARED, fd, 0);
97 [ # # ]: 0 : if (!mmap_addr) {
98 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
99 : : "Failed to mmap IOTLB entry for 0x%" PRIx64, iova);
100 : : ret = -1;
101 : 0 : goto close_fd;
102 : : }
103 : :
104 : 0 : ret = fstat(fd, &stat);
105 [ # # ]: 0 : if (ret < 0) {
106 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get page size.");
107 : 0 : munmap(mmap_addr, entry.offset + size);
108 : 0 : goto close_fd;
109 : : }
110 : 0 : page_size = (uint64_t)stat.st_blksize;
111 : :
112 : 0 : vhost_user_iotlb_cache_insert(dev, entry.start, (uint64_t)(uintptr_t)mmap_addr,
113 : 0 : entry.offset, size, page_size, entry.perm);
114 : :
115 : : ret = 0;
116 : 0 : close_fd:
117 : 0 : close(fd);
118 : :
119 : 0 : return ret;
120 : : }
121 : :
122 : : static struct vhost_backend_ops vduse_backend_ops = {
123 : : .iotlb_miss = vduse_iotlb_miss,
124 : : .iotlb_remove_notify = vduse_iotlb_remove_notify,
125 : : .inject_irq = vduse_inject_irq,
126 : : };
127 : :
128 : : static void
129 : 0 : vduse_control_queue_event(int fd, void *arg, int *remove __rte_unused)
130 : : {
131 : : struct virtio_net *dev = arg;
132 : : uint64_t buf;
133 : : int ret;
134 : :
135 : 0 : ret = read(fd, &buf, sizeof(buf));
136 [ # # ]: 0 : if (ret < 0) {
137 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to read control queue event: %s",
138 : : strerror(errno));
139 : 0 : return;
140 : : }
141 : :
142 : 0 : VHOST_CONFIG_LOG(dev->ifname, DEBUG, "Control queue kicked");
143 [ # # ]: 0 : if (virtio_net_ctrl_handle(dev))
144 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to handle ctrl request");
145 : : }
146 : :
147 : : static void
148 : 0 : vduse_vring_setup(struct virtio_net *dev, unsigned int index)
149 : : {
150 : 0 : struct vhost_virtqueue *vq = dev->virtqueue[index];
151 : : struct vhost_vring_addr *ra = &vq->ring_addrs;
152 : : struct vduse_vq_info vq_info;
153 : : struct vduse_vq_eventfd vq_efd;
154 : : int ret;
155 : :
156 : 0 : vq_info.index = index;
157 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_GET_INFO, &vq_info);
158 [ # # ]: 0 : if (ret) {
159 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get VQ %u info: %s",
160 : : index, strerror(errno));
161 : 0 : return;
162 : : }
163 : :
164 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "VQ %u info:", index);
165 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tnum: %u", vq_info.num);
166 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdesc_addr: %llx",
167 : : (unsigned long long)vq_info.desc_addr);
168 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdriver_addr: %llx",
169 : : (unsigned long long)vq_info.driver_addr);
170 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdevice_addr: %llx",
171 : : (unsigned long long)vq_info.device_addr);
172 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tavail_idx: %u", vq_info.split.avail_index);
173 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tready: %u", vq_info.ready);
174 : :
175 : 0 : vq->last_avail_idx = vq_info.split.avail_index;
176 : 0 : vq->size = vq_info.num;
177 : 0 : vq->ready = true;
178 : 0 : vq->enabled = vq_info.ready;
179 : 0 : ra->desc_user_addr = vq_info.desc_addr;
180 : 0 : ra->avail_user_addr = vq_info.driver_addr;
181 : 0 : ra->used_user_addr = vq_info.device_addr;
182 : :
183 : 0 : vq->kickfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
184 [ # # ]: 0 : if (vq->kickfd < 0) {
185 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to init kickfd for VQ %u: %s",
186 : : index, strerror(errno));
187 : 0 : vq->kickfd = VIRTIO_INVALID_EVENTFD;
188 : 0 : return;
189 : : }
190 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tkick fd: %d", vq->kickfd);
191 : :
192 : 0 : vq->shadow_used_split = rte_malloc_socket(NULL,
193 : 0 : vq->size * sizeof(struct vring_used_elem),
194 : : RTE_CACHE_LINE_SIZE, 0);
195 : 0 : vq->batch_copy_elems = rte_malloc_socket(NULL,
196 : 0 : vq->size * sizeof(struct batch_copy_elem),
197 : : RTE_CACHE_LINE_SIZE, 0);
198 : :
199 : 0 : rte_rwlock_write_lock(&vq->access_lock);
200 : : vhost_user_iotlb_rd_lock(vq);
201 [ # # ]: 0 : if (vring_translate(dev, vq))
202 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to translate vring %d addresses",
203 : : index);
204 : :
205 [ # # ]: 0 : if (vhost_enable_guest_notification(dev, vq, 0))
206 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
207 : : "Failed to disable guest notifications on vring %d",
208 : : index);
209 : : vhost_user_iotlb_rd_unlock(vq);
210 : : rte_rwlock_write_unlock(&vq->access_lock);
211 : :
212 : 0 : vq_efd.index = index;
213 : 0 : vq_efd.fd = vq->kickfd;
214 : :
215 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
216 [ # # ]: 0 : if (ret) {
217 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to setup kickfd for VQ %u: %s",
218 : : index, strerror(errno));
219 : 0 : close(vq->kickfd);
220 : 0 : vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
221 : 0 : return;
222 : : }
223 : :
224 [ # # ]: 0 : if (vq == dev->cvq) {
225 : 0 : ret = fdset_add(&vduse.fdset, vq->kickfd, vduse_control_queue_event, NULL, dev);
226 [ # # ]: 0 : if (ret) {
227 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
228 : : "Failed to setup kickfd handler for VQ %u: %s",
229 : : index, strerror(errno));
230 : 0 : vq_efd.fd = VDUSE_EVENTFD_DEASSIGN;
231 : 0 : ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
232 : 0 : close(vq->kickfd);
233 : 0 : vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
234 : : }
235 : 0 : fdset_pipe_notify(&vduse.fdset);
236 : 0 : vhost_enable_guest_notification(dev, vq, 1);
237 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl queue event handler installed");
238 : : }
239 : : }
240 : :
241 : : static void
242 : 0 : vduse_vring_cleanup(struct virtio_net *dev, unsigned int index)
243 : : {
244 : 0 : struct vhost_virtqueue *vq = dev->virtqueue[index];
245 : : struct vduse_vq_eventfd vq_efd;
246 : : int ret;
247 : :
248 [ # # # # ]: 0 : if (vq == dev->cvq && vq->kickfd >= 0) {
249 : 0 : fdset_del(&vduse.fdset, vq->kickfd);
250 : 0 : fdset_pipe_notify(&vduse.fdset);
251 : : }
252 : :
253 : 0 : vq_efd.index = index;
254 : 0 : vq_efd.fd = VDUSE_EVENTFD_DEASSIGN;
255 : :
256 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
257 [ # # ]: 0 : if (ret)
258 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to cleanup kickfd for VQ %u: %s",
259 : : index, strerror(errno));
260 : :
261 : 0 : close(vq->kickfd);
262 : 0 : vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
263 : :
264 : 0 : rte_rwlock_write_lock(&vq->access_lock);
265 : 0 : vring_invalidate(dev, vq);
266 : : rte_rwlock_write_unlock(&vq->access_lock);
267 : :
268 : 0 : rte_free(vq->batch_copy_elems);
269 : 0 : vq->batch_copy_elems = NULL;
270 : :
271 : 0 : rte_free(vq->shadow_used_split);
272 : 0 : vq->shadow_used_split = NULL;
273 : :
274 : 0 : vq->enabled = false;
275 : 0 : vq->ready = false;
276 : 0 : vq->size = 0;
277 : 0 : vq->last_used_idx = 0;
278 : 0 : vq->last_avail_idx = 0;
279 : 0 : }
280 : :
281 : : static void
282 : 0 : vduse_device_start(struct virtio_net *dev)
283 : : {
284 : : unsigned int i, ret;
285 : :
286 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Starting device...");
287 : :
288 : 0 : dev->notify_ops = vhost_driver_callback_get(dev->ifname);
289 [ # # ]: 0 : if (!dev->notify_ops) {
290 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
291 : : "Failed to get callback ops for driver");
292 : 0 : return;
293 : : }
294 : :
295 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_DEV_GET_FEATURES, &dev->features);
296 [ # # ]: 0 : if (ret) {
297 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get features: %s",
298 : : strerror(errno));
299 : 0 : return;
300 : : }
301 : :
302 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Negotiated Virtio features: 0x%" PRIx64,
303 : : dev->features);
304 : :
305 [ # # ]: 0 : if (dev->features &
306 : : ((1ULL << VIRTIO_NET_F_MRG_RXBUF) |
307 : : (1ULL << VIRTIO_F_VERSION_1) |
308 : : (1ULL << VIRTIO_F_RING_PACKED))) {
309 : 0 : dev->vhost_hlen = sizeof(struct virtio_net_hdr_mrg_rxbuf);
310 : : } else {
311 : 0 : dev->vhost_hlen = sizeof(struct virtio_net_hdr);
312 : : }
313 : :
314 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++)
315 : 0 : vduse_vring_setup(dev, i);
316 : :
317 : 0 : dev->flags |= VIRTIO_DEV_READY;
318 : :
319 [ # # ]: 0 : if (dev->notify_ops->new_device(dev->vid) == 0)
320 : 0 : dev->flags |= VIRTIO_DEV_RUNNING;
321 : :
322 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++) {
323 : 0 : struct vhost_virtqueue *vq = dev->virtqueue[i];
324 : :
325 [ # # ]: 0 : if (vq == dev->cvq)
326 : 0 : continue;
327 : :
328 [ # # ]: 0 : if (dev->notify_ops->vring_state_changed)
329 : 0 : dev->notify_ops->vring_state_changed(dev->vid, i, vq->enabled);
330 : : }
331 : : }
332 : :
333 : : static void
334 : 0 : vduse_device_stop(struct virtio_net *dev)
335 : : {
336 : : unsigned int i;
337 : :
338 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Stopping device...");
339 : :
340 : 0 : vhost_destroy_device_notify(dev);
341 : :
342 : 0 : dev->flags &= ~VIRTIO_DEV_READY;
343 : :
344 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++)
345 : 0 : vduse_vring_cleanup(dev, i);
346 : :
347 : 0 : vhost_user_iotlb_flush_all(dev);
348 : 0 : }
349 : :
350 : : static void
351 : 0 : vduse_events_handler(int fd, void *arg, int *remove __rte_unused)
352 : : {
353 : : struct virtio_net *dev = arg;
354 : : struct vduse_dev_request req;
355 : : struct vduse_dev_response resp;
356 : : struct vhost_virtqueue *vq;
357 : 0 : uint8_t old_status = dev->status;
358 : : int ret;
359 : :
360 : : memset(&resp, 0, sizeof(resp));
361 : :
362 : 0 : ret = read(fd, &req, sizeof(req));
363 [ # # ]: 0 : if (ret < 0) {
364 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to read request: %s",
365 : : strerror(errno));
366 : 0 : return;
367 [ # # ]: 0 : } else if (ret < (int)sizeof(req)) {
368 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Incomplete to read request %d", ret);
369 : 0 : return;
370 : : }
371 : :
372 [ # # ]: 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "New request: %s (%u)",
373 : : vduse_req_id_to_str(req.type), req.type);
374 : :
375 [ # # # # ]: 0 : switch (req.type) {
376 : 0 : case VDUSE_GET_VQ_STATE:
377 : 0 : vq = dev->virtqueue[req.vq_state.index];
378 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tvq index: %u, avail_index: %u",
379 : : req.vq_state.index, vq->last_avail_idx);
380 : 0 : resp.vq_state.split.avail_index = vq->last_avail_idx;
381 : 0 : resp.result = VDUSE_REQ_RESULT_OK;
382 : 0 : break;
383 : 0 : case VDUSE_SET_STATUS:
384 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tnew status: 0x%08x",
385 : : req.s.status);
386 : 0 : old_status = dev->status;
387 : 0 : dev->status = req.s.status;
388 : 0 : resp.result = VDUSE_REQ_RESULT_OK;
389 : 0 : break;
390 : 0 : case VDUSE_UPDATE_IOTLB:
391 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tIOVA range: %" PRIx64 " - %" PRIx64,
392 : : (uint64_t)req.iova.start, (uint64_t)req.iova.last);
393 : 0 : vhost_user_iotlb_cache_remove(dev, req.iova.start,
394 : 0 : req.iova.last - req.iova.start + 1);
395 : 0 : resp.result = VDUSE_REQ_RESULT_OK;
396 : 0 : break;
397 : 0 : default:
398 : 0 : resp.result = VDUSE_REQ_RESULT_FAILED;
399 : 0 : break;
400 : : }
401 : :
402 : 0 : resp.request_id = req.request_id;
403 : :
404 : 0 : ret = write(dev->vduse_dev_fd, &resp, sizeof(resp));
405 [ # # ]: 0 : if (ret != sizeof(resp)) {
406 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to write response %s",
407 : : strerror(errno));
408 : 0 : return;
409 : : }
410 : :
411 [ # # ]: 0 : if ((old_status ^ dev->status) & VIRTIO_DEVICE_STATUS_DRIVER_OK) {
412 [ # # ]: 0 : if (dev->status & VIRTIO_DEVICE_STATUS_DRIVER_OK)
413 : 0 : vduse_device_start(dev);
414 : : else
415 : 0 : vduse_device_stop(dev);
416 : : }
417 : :
418 [ # # ]: 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Request %s (%u) handled successfully",
419 : : vduse_req_id_to_str(req.type), req.type);
420 : : }
421 : :
422 : : int
423 : 0 : vduse_device_create(const char *path, bool compliant_ol_flags)
424 : : {
425 : : int control_fd, dev_fd, vid, ret;
426 : : rte_thread_t fdset_tid;
427 : : uint32_t i, max_queue_pairs, total_queues;
428 : : struct virtio_net *dev;
429 : 0 : struct virtio_net_config vnet_config = {{ 0 }};
430 : 0 : uint64_t ver = VHOST_VDUSE_API_VERSION;
431 : : uint64_t features;
432 : : struct vduse_dev_config *dev_config = NULL;
433 : 0 : const char *name = path + strlen("/dev/vduse/");
434 : :
435 : : /* If first device, create events dispatcher thread */
436 [ # # ]: 0 : if (vduse_events_thread == false) {
437 : : /**
438 : : * create a pipe which will be waited by poll and notified to
439 : : * rebuild the wait list of poll.
440 : : */
441 [ # # ]: 0 : if (fdset_pipe_init(&vduse.fdset) < 0) {
442 : 0 : VHOST_CONFIG_LOG(path, ERR, "failed to create pipe for vduse fdset");
443 : 0 : return -1;
444 : : }
445 : :
446 : 0 : ret = rte_thread_create_internal_control(&fdset_tid, "vduse-evt",
447 : : fdset_event_dispatch, &vduse.fdset);
448 [ # # ]: 0 : if (ret != 0) {
449 : 0 : VHOST_CONFIG_LOG(path, ERR, "failed to create vduse fdset handling thread");
450 : 0 : fdset_pipe_uninit(&vduse.fdset);
451 : 0 : return -1;
452 : : }
453 : :
454 : 0 : vduse_events_thread = true;
455 : : }
456 : :
457 : : control_fd = open(VDUSE_CTRL_PATH, O_RDWR);
458 [ # # ]: 0 : if (control_fd < 0) {
459 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to open %s: %s",
460 : : VDUSE_CTRL_PATH, strerror(errno));
461 : 0 : return -1;
462 : : }
463 : :
464 [ # # ]: 0 : if (ioctl(control_fd, VDUSE_SET_API_VERSION, &ver)) {
465 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to set API version: %" PRIu64 ": %s",
466 : : ver, strerror(errno));
467 : : ret = -1;
468 : 0 : goto out_ctrl_close;
469 : : }
470 : :
471 : 0 : dev_config = malloc(offsetof(struct vduse_dev_config, config) +
472 : : sizeof(vnet_config));
473 [ # # ]: 0 : if (!dev_config) {
474 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to allocate VDUSE config");
475 : : ret = -1;
476 : 0 : goto out_ctrl_close;
477 : : }
478 : :
479 : 0 : ret = rte_vhost_driver_get_features(path, &features);
480 [ # # ]: 0 : if (ret < 0) {
481 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to get backend features");
482 : 0 : goto out_free;
483 : : }
484 : :
485 : 0 : ret = rte_vhost_driver_get_queue_num(path, &max_queue_pairs);
486 [ # # ]: 0 : if (ret < 0) {
487 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to get max queue pairs");
488 : 0 : goto out_free;
489 : : }
490 : :
491 : 0 : VHOST_CONFIG_LOG(path, INFO, "VDUSE max queue pairs: %u", max_queue_pairs);
492 : 0 : total_queues = max_queue_pairs * 2;
493 : :
494 [ # # ]: 0 : if (max_queue_pairs == 1)
495 : 0 : features &= ~(RTE_BIT64(VIRTIO_NET_F_CTRL_VQ) | RTE_BIT64(VIRTIO_NET_F_MQ));
496 : : else
497 : 0 : total_queues += 1; /* Includes ctrl queue */
498 : :
499 : 0 : vnet_config.max_virtqueue_pairs = max_queue_pairs;
500 : : memset(dev_config, 0, sizeof(struct vduse_dev_config));
501 : :
502 : 0 : strncpy(dev_config->name, name, VDUSE_NAME_MAX - 1);
503 : 0 : dev_config->device_id = VIRTIO_ID_NET;
504 : : dev_config->vendor_id = 0;
505 : 0 : dev_config->features = features;
506 : 0 : dev_config->vq_num = total_queues;
507 : 0 : dev_config->vq_align = sysconf(_SC_PAGE_SIZE);
508 : 0 : dev_config->config_size = sizeof(struct virtio_net_config);
509 : 0 : memcpy(dev_config->config, &vnet_config, sizeof(vnet_config));
510 : :
511 : 0 : ret = ioctl(control_fd, VDUSE_CREATE_DEV, dev_config);
512 [ # # ]: 0 : if (ret < 0) {
513 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to create VDUSE device: %s",
514 : : strerror(errno));
515 : 0 : goto out_free;
516 : : }
517 : :
518 : : dev_fd = open(path, O_RDWR);
519 [ # # ]: 0 : if (dev_fd < 0) {
520 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to open device %s: %s",
521 : : path, strerror(errno));
522 : : ret = -1;
523 : 0 : goto out_dev_close;
524 : : }
525 : :
526 : 0 : ret = fcntl(dev_fd, F_SETFL, O_NONBLOCK);
527 [ # # ]: 0 : if (ret < 0) {
528 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to set chardev as non-blocking: %s",
529 : : strerror(errno));
530 : 0 : goto out_dev_close;
531 : : }
532 : :
533 : 0 : vid = vhost_new_device(&vduse_backend_ops);
534 [ # # ]: 0 : if (vid < 0) {
535 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to create new Vhost device");
536 : : ret = -1;
537 : 0 : goto out_dev_close;
538 : : }
539 : :
540 : : dev = get_device(vid);
541 [ # # ]: 0 : if (!dev) {
542 : : ret = -1;
543 : 0 : goto out_dev_close;
544 : : }
545 : :
546 : 0 : strncpy(dev->ifname, path, IF_NAME_SZ - 1);
547 : 0 : dev->vduse_ctrl_fd = control_fd;
548 : 0 : dev->vduse_dev_fd = dev_fd;
549 : 0 : vhost_setup_virtio_net(dev->vid, true, compliant_ol_flags, true, true);
550 : :
551 [ # # ]: 0 : for (i = 0; i < total_queues; i++) {
552 : 0 : struct vduse_vq_config vq_cfg = { 0 };
553 : :
554 : 0 : ret = alloc_vring_queue(dev, i);
555 [ # # ]: 0 : if (ret) {
556 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to alloc vring %d metadata", i);
557 : 0 : goto out_dev_destroy;
558 : : }
559 : :
560 : 0 : vq_cfg.index = i;
561 : 0 : vq_cfg.max_size = 1024;
562 : :
563 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP, &vq_cfg);
564 [ # # ]: 0 : if (ret) {
565 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to set-up VQ %d", i);
566 : 0 : goto out_dev_destroy;
567 : : }
568 : : }
569 : :
570 : 0 : dev->cvq = dev->virtqueue[max_queue_pairs * 2];
571 : :
572 : 0 : ret = fdset_add(&vduse.fdset, dev->vduse_dev_fd, vduse_events_handler, NULL, dev);
573 [ # # ]: 0 : if (ret) {
574 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to add fd %d to vduse fdset",
575 : : dev->vduse_dev_fd);
576 : 0 : goto out_dev_destroy;
577 : : }
578 : 0 : fdset_pipe_notify(&vduse.fdset);
579 : :
580 : 0 : free(dev_config);
581 : :
582 : 0 : return 0;
583 : :
584 : 0 : out_dev_destroy:
585 : 0 : vhost_destroy_device(vid);
586 : 0 : out_dev_close:
587 [ # # ]: 0 : if (dev_fd >= 0)
588 : 0 : close(dev_fd);
589 : 0 : ioctl(control_fd, VDUSE_DESTROY_DEV, name);
590 : 0 : out_free:
591 : 0 : free(dev_config);
592 : 0 : out_ctrl_close:
593 : 0 : close(control_fd);
594 : :
595 : 0 : return ret;
596 : : }
597 : :
598 : : int
599 : 0 : vduse_device_destroy(const char *path)
600 : : {
601 : 0 : const char *name = path + strlen("/dev/vduse/");
602 : : struct virtio_net *dev;
603 : : int vid, ret;
604 : :
605 [ # # ]: 0 : for (vid = 0; vid < RTE_MAX_VHOST_DEVICE; vid++) {
606 : 0 : dev = vhost_devices[vid];
607 : :
608 [ # # ]: 0 : if (dev == NULL)
609 : 0 : continue;
610 : :
611 [ # # ]: 0 : if (!strcmp(path, dev->ifname))
612 : : break;
613 : : }
614 : :
615 [ # # ]: 0 : if (vid == RTE_MAX_VHOST_DEVICE)
616 : : return -1;
617 : :
618 : 0 : vduse_device_stop(dev);
619 : :
620 : 0 : fdset_del(&vduse.fdset, dev->vduse_dev_fd);
621 : 0 : fdset_pipe_notify(&vduse.fdset);
622 : :
623 [ # # ]: 0 : if (dev->vduse_dev_fd >= 0) {
624 : 0 : close(dev->vduse_dev_fd);
625 : 0 : dev->vduse_dev_fd = -1;
626 : : }
627 : :
628 [ # # ]: 0 : if (dev->vduse_ctrl_fd >= 0) {
629 : 0 : ret = ioctl(dev->vduse_ctrl_fd, VDUSE_DESTROY_DEV, name);
630 [ # # ]: 0 : if (ret)
631 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to destroy VDUSE device: %s",
632 : : strerror(errno));
633 : 0 : close(dev->vduse_ctrl_fd);
634 : 0 : dev->vduse_ctrl_fd = -1;
635 : : }
636 : :
637 : 0 : vhost_destroy_device(vid);
638 : :
639 : 0 : return 0;
640 : : }
|