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 : : vhost_user_iotlb_rd_lock(vq);
200 [ # # ]: 0 : if (vring_translate(dev, vq))
201 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to translate vring %d addresses",
202 : : index);
203 : :
204 [ # # ]: 0 : if (vhost_enable_guest_notification(dev, vq, 0))
205 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
206 : : "Failed to disable guest notifications on vring %d",
207 : : index);
208 : : vhost_user_iotlb_rd_unlock(vq);
209 : :
210 : 0 : vq_efd.index = index;
211 : 0 : vq_efd.fd = vq->kickfd;
212 : :
213 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
214 [ # # ]: 0 : if (ret) {
215 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to setup kickfd for VQ %u: %s",
216 : : index, strerror(errno));
217 : 0 : close(vq->kickfd);
218 : 0 : vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
219 : 0 : return;
220 : : }
221 : :
222 [ # # ]: 0 : if (vq == dev->cvq) {
223 : 0 : ret = fdset_add(&vduse.fdset, vq->kickfd, vduse_control_queue_event, NULL, dev);
224 [ # # ]: 0 : if (ret) {
225 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
226 : : "Failed to setup kickfd handler for VQ %u: %s",
227 : : index, strerror(errno));
228 : 0 : vq_efd.fd = VDUSE_EVENTFD_DEASSIGN;
229 : 0 : ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
230 : 0 : close(vq->kickfd);
231 : 0 : vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
232 : : }
233 : 0 : fdset_pipe_notify(&vduse.fdset);
234 : 0 : vhost_enable_guest_notification(dev, vq, 1);
235 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl queue event handler installed");
236 : : }
237 : : }
238 : :
239 : : static void
240 : 0 : vduse_vring_cleanup(struct virtio_net *dev, unsigned int index)
241 : : {
242 : 0 : struct vhost_virtqueue *vq = dev->virtqueue[index];
243 : : struct vduse_vq_eventfd vq_efd;
244 : : int ret;
245 : :
246 [ # # # # ]: 0 : if (vq == dev->cvq && vq->kickfd >= 0) {
247 : 0 : fdset_del(&vduse.fdset, vq->kickfd);
248 : 0 : fdset_pipe_notify(&vduse.fdset);
249 : : }
250 : :
251 : 0 : vq_efd.index = index;
252 : 0 : vq_efd.fd = VDUSE_EVENTFD_DEASSIGN;
253 : :
254 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
255 [ # # ]: 0 : if (ret)
256 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to cleanup kickfd for VQ %u: %s",
257 : : index, strerror(errno));
258 : :
259 : 0 : close(vq->kickfd);
260 : 0 : vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
261 : :
262 : 0 : vring_invalidate(dev, vq);
263 : :
264 : 0 : rte_free(vq->batch_copy_elems);
265 : 0 : vq->batch_copy_elems = NULL;
266 : :
267 : 0 : rte_free(vq->shadow_used_split);
268 : 0 : vq->shadow_used_split = NULL;
269 : :
270 : 0 : vq->enabled = false;
271 : 0 : vq->ready = false;
272 : 0 : vq->size = 0;
273 : 0 : vq->last_used_idx = 0;
274 : 0 : vq->last_avail_idx = 0;
275 : 0 : }
276 : :
277 : : static void
278 : 0 : vduse_device_start(struct virtio_net *dev)
279 : : {
280 : : unsigned int i, ret;
281 : :
282 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Starting device...");
283 : :
284 : 0 : dev->notify_ops = vhost_driver_callback_get(dev->ifname);
285 [ # # ]: 0 : if (!dev->notify_ops) {
286 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR,
287 : : "Failed to get callback ops for driver");
288 : 0 : return;
289 : : }
290 : :
291 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_DEV_GET_FEATURES, &dev->features);
292 [ # # ]: 0 : if (ret) {
293 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get features: %s",
294 : : strerror(errno));
295 : 0 : return;
296 : : }
297 : :
298 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Negotiated Virtio features: 0x%" PRIx64,
299 : : dev->features);
300 : :
301 [ # # ]: 0 : if (dev->features &
302 : : ((1ULL << VIRTIO_NET_F_MRG_RXBUF) |
303 : : (1ULL << VIRTIO_F_VERSION_1) |
304 : : (1ULL << VIRTIO_F_RING_PACKED))) {
305 : 0 : dev->vhost_hlen = sizeof(struct virtio_net_hdr_mrg_rxbuf);
306 : : } else {
307 : 0 : dev->vhost_hlen = sizeof(struct virtio_net_hdr);
308 : : }
309 : :
310 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++)
311 : 0 : vduse_vring_setup(dev, i);
312 : :
313 : 0 : dev->flags |= VIRTIO_DEV_READY;
314 : :
315 [ # # ]: 0 : if (dev->notify_ops->new_device(dev->vid) == 0)
316 : 0 : dev->flags |= VIRTIO_DEV_RUNNING;
317 : :
318 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++) {
319 : 0 : struct vhost_virtqueue *vq = dev->virtqueue[i];
320 : :
321 [ # # ]: 0 : if (vq == dev->cvq)
322 : 0 : continue;
323 : :
324 [ # # ]: 0 : if (dev->notify_ops->vring_state_changed)
325 : 0 : dev->notify_ops->vring_state_changed(dev->vid, i, vq->enabled);
326 : : }
327 : : }
328 : :
329 : : static void
330 : 0 : vduse_device_stop(struct virtio_net *dev)
331 : : {
332 : : unsigned int i;
333 : :
334 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Stopping device...");
335 : :
336 : 0 : vhost_destroy_device_notify(dev);
337 : :
338 : 0 : dev->flags &= ~VIRTIO_DEV_READY;
339 : :
340 [ # # ]: 0 : for (i = 0; i < dev->nr_vring; i++)
341 : 0 : vduse_vring_cleanup(dev, i);
342 : :
343 : 0 : vhost_user_iotlb_flush_all(dev);
344 : 0 : }
345 : :
346 : : static void
347 : 0 : vduse_events_handler(int fd, void *arg, int *remove __rte_unused)
348 : : {
349 : : struct virtio_net *dev = arg;
350 : : struct vduse_dev_request req;
351 : : struct vduse_dev_response resp;
352 : : struct vhost_virtqueue *vq;
353 : 0 : uint8_t old_status = dev->status;
354 : : int ret;
355 : :
356 : : memset(&resp, 0, sizeof(resp));
357 : :
358 : 0 : ret = read(fd, &req, sizeof(req));
359 [ # # ]: 0 : if (ret < 0) {
360 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to read request: %s",
361 : : strerror(errno));
362 : 0 : return;
363 [ # # ]: 0 : } else if (ret < (int)sizeof(req)) {
364 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Incomplete to read request %d", ret);
365 : 0 : return;
366 : : }
367 : :
368 [ # # ]: 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "New request: %s (%u)",
369 : : vduse_req_id_to_str(req.type), req.type);
370 : :
371 [ # # # # ]: 0 : switch (req.type) {
372 : 0 : case VDUSE_GET_VQ_STATE:
373 : 0 : vq = dev->virtqueue[req.vq_state.index];
374 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tvq index: %u, avail_index: %u",
375 : : req.vq_state.index, vq->last_avail_idx);
376 : 0 : resp.vq_state.split.avail_index = vq->last_avail_idx;
377 : 0 : resp.result = VDUSE_REQ_RESULT_OK;
378 : 0 : break;
379 : 0 : case VDUSE_SET_STATUS:
380 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tnew status: 0x%08x",
381 : : req.s.status);
382 : 0 : old_status = dev->status;
383 : 0 : dev->status = req.s.status;
384 : 0 : resp.result = VDUSE_REQ_RESULT_OK;
385 : 0 : break;
386 : 0 : case VDUSE_UPDATE_IOTLB:
387 : 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "\tIOVA range: %" PRIx64 " - %" PRIx64,
388 : : (uint64_t)req.iova.start, (uint64_t)req.iova.last);
389 : 0 : vhost_user_iotlb_cache_remove(dev, req.iova.start,
390 : 0 : req.iova.last - req.iova.start + 1);
391 : 0 : resp.result = VDUSE_REQ_RESULT_OK;
392 : 0 : break;
393 : 0 : default:
394 : 0 : resp.result = VDUSE_REQ_RESULT_FAILED;
395 : 0 : break;
396 : : }
397 : :
398 : 0 : resp.request_id = req.request_id;
399 : :
400 : 0 : ret = write(dev->vduse_dev_fd, &resp, sizeof(resp));
401 [ # # ]: 0 : if (ret != sizeof(resp)) {
402 : 0 : VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to write response %s",
403 : : strerror(errno));
404 : 0 : return;
405 : : }
406 : :
407 [ # # ]: 0 : if ((old_status ^ dev->status) & VIRTIO_DEVICE_STATUS_DRIVER_OK) {
408 [ # # ]: 0 : if (dev->status & VIRTIO_DEVICE_STATUS_DRIVER_OK)
409 : 0 : vduse_device_start(dev);
410 : : else
411 : 0 : vduse_device_stop(dev);
412 : : }
413 : :
414 [ # # ]: 0 : VHOST_CONFIG_LOG(dev->ifname, INFO, "Request %s (%u) handled successfully",
415 : : vduse_req_id_to_str(req.type), req.type);
416 : : }
417 : :
418 : : int
419 : 0 : vduse_device_create(const char *path, bool compliant_ol_flags)
420 : : {
421 : : int control_fd, dev_fd, vid, ret;
422 : : rte_thread_t fdset_tid;
423 : : uint32_t i, max_queue_pairs, total_queues;
424 : : struct virtio_net *dev;
425 : 0 : struct virtio_net_config vnet_config = {{ 0 }};
426 : 0 : uint64_t ver = VHOST_VDUSE_API_VERSION;
427 : : uint64_t features;
428 : : struct vduse_dev_config *dev_config = NULL;
429 : 0 : const char *name = path + strlen("/dev/vduse/");
430 : :
431 : : /* If first device, create events dispatcher thread */
432 [ # # ]: 0 : if (vduse_events_thread == false) {
433 : : /**
434 : : * create a pipe which will be waited by poll and notified to
435 : : * rebuild the wait list of poll.
436 : : */
437 [ # # ]: 0 : if (fdset_pipe_init(&vduse.fdset) < 0) {
438 : 0 : VHOST_CONFIG_LOG(path, ERR, "failed to create pipe for vduse fdset");
439 : 0 : return -1;
440 : : }
441 : :
442 : 0 : ret = rte_thread_create_internal_control(&fdset_tid, "vduse-evt",
443 : : fdset_event_dispatch, &vduse.fdset);
444 [ # # ]: 0 : if (ret != 0) {
445 : 0 : VHOST_CONFIG_LOG(path, ERR, "failed to create vduse fdset handling thread");
446 : 0 : fdset_pipe_uninit(&vduse.fdset);
447 : 0 : return -1;
448 : : }
449 : :
450 : 0 : vduse_events_thread = true;
451 : : }
452 : :
453 : : control_fd = open(VDUSE_CTRL_PATH, O_RDWR);
454 [ # # ]: 0 : if (control_fd < 0) {
455 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to open %s: %s",
456 : : VDUSE_CTRL_PATH, strerror(errno));
457 : 0 : return -1;
458 : : }
459 : :
460 [ # # ]: 0 : if (ioctl(control_fd, VDUSE_SET_API_VERSION, &ver)) {
461 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to set API version: %" PRIu64 ": %s",
462 : : ver, strerror(errno));
463 : : ret = -1;
464 : 0 : goto out_ctrl_close;
465 : : }
466 : :
467 : 0 : dev_config = malloc(offsetof(struct vduse_dev_config, config) +
468 : : sizeof(vnet_config));
469 [ # # ]: 0 : if (!dev_config) {
470 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to allocate VDUSE config");
471 : : ret = -1;
472 : 0 : goto out_ctrl_close;
473 : : }
474 : :
475 : 0 : ret = rte_vhost_driver_get_features(path, &features);
476 [ # # ]: 0 : if (ret < 0) {
477 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to get backend features");
478 : 0 : goto out_free;
479 : : }
480 : :
481 : 0 : ret = rte_vhost_driver_get_queue_num(path, &max_queue_pairs);
482 [ # # ]: 0 : if (ret < 0) {
483 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to get max queue pairs");
484 : 0 : goto out_free;
485 : : }
486 : :
487 : 0 : VHOST_CONFIG_LOG(path, INFO, "VDUSE max queue pairs: %u", max_queue_pairs);
488 : 0 : total_queues = max_queue_pairs * 2;
489 : :
490 [ # # ]: 0 : if (max_queue_pairs == 1)
491 : 0 : features &= ~(RTE_BIT64(VIRTIO_NET_F_CTRL_VQ) | RTE_BIT64(VIRTIO_NET_F_MQ));
492 : : else
493 : 0 : total_queues += 1; /* Includes ctrl queue */
494 : :
495 : 0 : vnet_config.max_virtqueue_pairs = max_queue_pairs;
496 : : memset(dev_config, 0, sizeof(struct vduse_dev_config));
497 : :
498 : 0 : strncpy(dev_config->name, name, VDUSE_NAME_MAX - 1);
499 : 0 : dev_config->device_id = VIRTIO_ID_NET;
500 : : dev_config->vendor_id = 0;
501 : 0 : dev_config->features = features;
502 : 0 : dev_config->vq_num = total_queues;
503 : 0 : dev_config->vq_align = sysconf(_SC_PAGE_SIZE);
504 : 0 : dev_config->config_size = sizeof(struct virtio_net_config);
505 : 0 : memcpy(dev_config->config, &vnet_config, sizeof(vnet_config));
506 : :
507 : 0 : ret = ioctl(control_fd, VDUSE_CREATE_DEV, dev_config);
508 [ # # ]: 0 : if (ret < 0) {
509 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to create VDUSE device: %s",
510 : : strerror(errno));
511 : 0 : goto out_free;
512 : : }
513 : :
514 : : dev_fd = open(path, O_RDWR);
515 [ # # ]: 0 : if (dev_fd < 0) {
516 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to open device %s: %s",
517 : : path, strerror(errno));
518 : : ret = -1;
519 : 0 : goto out_dev_close;
520 : : }
521 : :
522 : 0 : ret = fcntl(dev_fd, F_SETFL, O_NONBLOCK);
523 [ # # ]: 0 : if (ret < 0) {
524 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to set chardev as non-blocking: %s",
525 : : strerror(errno));
526 : 0 : goto out_dev_close;
527 : : }
528 : :
529 : 0 : vid = vhost_new_device(&vduse_backend_ops);
530 [ # # ]: 0 : if (vid < 0) {
531 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to create new Vhost device");
532 : : ret = -1;
533 : 0 : goto out_dev_close;
534 : : }
535 : :
536 : : dev = get_device(vid);
537 [ # # ]: 0 : if (!dev) {
538 : : ret = -1;
539 : 0 : goto out_dev_close;
540 : : }
541 : :
542 : 0 : strncpy(dev->ifname, path, IF_NAME_SZ - 1);
543 : 0 : dev->vduse_ctrl_fd = control_fd;
544 : 0 : dev->vduse_dev_fd = dev_fd;
545 : 0 : vhost_setup_virtio_net(dev->vid, true, compliant_ol_flags, true, true);
546 : :
547 [ # # ]: 0 : for (i = 0; i < total_queues; i++) {
548 : 0 : struct vduse_vq_config vq_cfg = { 0 };
549 : :
550 : 0 : ret = alloc_vring_queue(dev, i);
551 [ # # ]: 0 : if (ret) {
552 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to alloc vring %d metadata", i);
553 : 0 : goto out_dev_destroy;
554 : : }
555 : :
556 : 0 : vq_cfg.index = i;
557 : 0 : vq_cfg.max_size = 1024;
558 : :
559 : 0 : ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP, &vq_cfg);
560 [ # # ]: 0 : if (ret) {
561 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to set-up VQ %d", i);
562 : 0 : goto out_dev_destroy;
563 : : }
564 : : }
565 : :
566 : 0 : dev->cvq = dev->virtqueue[max_queue_pairs * 2];
567 : :
568 : 0 : ret = fdset_add(&vduse.fdset, dev->vduse_dev_fd, vduse_events_handler, NULL, dev);
569 [ # # ]: 0 : if (ret) {
570 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to add fd %d to vduse fdset",
571 : : dev->vduse_dev_fd);
572 : 0 : goto out_dev_destroy;
573 : : }
574 : 0 : fdset_pipe_notify(&vduse.fdset);
575 : :
576 : 0 : free(dev_config);
577 : :
578 : 0 : return 0;
579 : :
580 : 0 : out_dev_destroy:
581 : 0 : vhost_destroy_device(vid);
582 : 0 : out_dev_close:
583 [ # # ]: 0 : if (dev_fd >= 0)
584 : 0 : close(dev_fd);
585 : 0 : ioctl(control_fd, VDUSE_DESTROY_DEV, name);
586 : 0 : out_free:
587 : 0 : free(dev_config);
588 : 0 : out_ctrl_close:
589 : 0 : close(control_fd);
590 : :
591 : 0 : return ret;
592 : : }
593 : :
594 : : int
595 : 0 : vduse_device_destroy(const char *path)
596 : : {
597 : 0 : const char *name = path + strlen("/dev/vduse/");
598 : : struct virtio_net *dev;
599 : : int vid, ret;
600 : :
601 [ # # ]: 0 : for (vid = 0; vid < RTE_MAX_VHOST_DEVICE; vid++) {
602 : 0 : dev = vhost_devices[vid];
603 : :
604 [ # # ]: 0 : if (dev == NULL)
605 : 0 : continue;
606 : :
607 [ # # ]: 0 : if (!strcmp(path, dev->ifname))
608 : : break;
609 : : }
610 : :
611 [ # # ]: 0 : if (vid == RTE_MAX_VHOST_DEVICE)
612 : : return -1;
613 : :
614 : 0 : vduse_device_stop(dev);
615 : :
616 : 0 : fdset_del(&vduse.fdset, dev->vduse_dev_fd);
617 : 0 : fdset_pipe_notify(&vduse.fdset);
618 : :
619 [ # # ]: 0 : if (dev->vduse_dev_fd >= 0) {
620 : 0 : close(dev->vduse_dev_fd);
621 : 0 : dev->vduse_dev_fd = -1;
622 : : }
623 : :
624 [ # # ]: 0 : if (dev->vduse_ctrl_fd >= 0) {
625 : 0 : ret = ioctl(dev->vduse_ctrl_fd, VDUSE_DESTROY_DEV, name);
626 [ # # ]: 0 : if (ret)
627 : 0 : VHOST_CONFIG_LOG(name, ERR, "Failed to destroy VDUSE device: %s",
628 : : strerror(errno));
629 : 0 : close(dev->vduse_ctrl_fd);
630 : 0 : dev->vduse_ctrl_fd = -1;
631 : : }
632 : :
633 : 0 : vhost_destroy_device(vid);
634 : :
635 : 0 : return 0;
636 : : }
|