Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : *
3 : : * Copyright (c) 2015-2016 Freescale Semiconductor, Inc. All rights reserved.
4 : : * Copyright 2016-2021 NXP
5 : : *
6 : : */
7 : :
8 : : #include <unistd.h>
9 : : #include <stdio.h>
10 : : #include <sys/types.h>
11 : : #include <string.h>
12 : : #include <stdlib.h>
13 : : #include <fcntl.h>
14 : : #include <errno.h>
15 : : #include <sys/ioctl.h>
16 : : #include <sys/stat.h>
17 : : #include <sys/mman.h>
18 : : #include <sys/vfs.h>
19 : : #include <libgen.h>
20 : : #include <dirent.h>
21 : : #include <sys/eventfd.h>
22 : :
23 : : #include <eal_filesystem.h>
24 : : #include <rte_mbuf.h>
25 : : #include <ethdev_driver.h>
26 : : #include <rte_malloc.h>
27 : : #include <rte_memcpy.h>
28 : : #include <rte_string_fns.h>
29 : : #include <rte_cycles.h>
30 : : #include <rte_kvargs.h>
31 : : #include <dev_driver.h>
32 : : #include <rte_eal_memconfig.h>
33 : :
34 : : #include "private.h"
35 : : #include "fslmc_vfio.h"
36 : : #include "fslmc_logs.h"
37 : : #include <mc/fsl_dpmng.h>
38 : :
39 : : #include "portal/dpaa2_hw_pvt.h"
40 : : #include "portal/dpaa2_hw_dpio.h"
41 : :
42 : : #define FSLMC_CONTAINER_MAX_LEN 8 /**< Of the format dprc.XX */
43 : :
44 : : /* Number of VFIO containers & groups with in */
45 : : static struct fslmc_vfio_group vfio_group;
46 : : static struct fslmc_vfio_container vfio_container;
47 : : static int container_device_fd;
48 : : char *fslmc_container;
49 : : static int fslmc_iommu_type;
50 : : static uint32_t *msi_intr_vaddr;
51 : : void *(*rte_mcp_ptr_list);
52 : :
53 : : void *
54 : 0 : dpaa2_get_mcp_ptr(int portal_idx)
55 : : {
56 [ # # ]: 0 : if (rte_mcp_ptr_list)
57 : 0 : return rte_mcp_ptr_list[portal_idx];
58 : : else
59 : : return NULL;
60 : : }
61 : :
62 : : static struct rte_dpaa2_object_list dpaa2_obj_list =
63 : : TAILQ_HEAD_INITIALIZER(dpaa2_obj_list);
64 : :
65 : : /*register a fslmc bus based dpaa2 driver */
66 : : void
67 : 1410 : rte_fslmc_object_register(struct rte_dpaa2_object *object)
68 : : {
69 [ - + ]: 1410 : RTE_VERIFY(object);
70 : :
71 : 1410 : TAILQ_INSERT_TAIL(&dpaa2_obj_list, object, next);
72 : 1410 : }
73 : :
74 : : int
75 : 169 : fslmc_get_container_group(int *groupid)
76 : : {
77 : : int ret;
78 : : char *container;
79 : :
80 [ + - ]: 169 : if (!fslmc_container) {
81 : 169 : container = getenv("DPRC");
82 [ + - ]: 169 : if (container == NULL) {
83 : 169 : DPAA2_BUS_DEBUG("DPAA2: DPRC not available");
84 : 169 : return -EINVAL;
85 : : }
86 : :
87 [ # # ]: 0 : if (strlen(container) >= FSLMC_CONTAINER_MAX_LEN) {
88 : 0 : DPAA2_BUS_ERR("Invalid container name: %s", container);
89 : 0 : return -1;
90 : : }
91 : :
92 : 0 : fslmc_container = strdup(container);
93 [ # # ]: 0 : if (!fslmc_container) {
94 : 0 : DPAA2_BUS_ERR("Mem alloc failure; Container name");
95 : 0 : return -ENOMEM;
96 : : }
97 : : }
98 : :
99 : 0 : fslmc_iommu_type = (rte_vfio_noiommu_is_enabled() == 1) ?
100 [ # # ]: 0 : RTE_VFIO_NOIOMMU : VFIO_TYPE1_IOMMU;
101 : :
102 : : /* get group number */
103 : 0 : ret = rte_vfio_get_group_num(SYSFS_FSL_MC_DEVICES,
104 : : fslmc_container, groupid);
105 [ # # ]: 0 : if (ret <= 0) {
106 : 0 : DPAA2_BUS_ERR("Unable to find %s IOMMU group", fslmc_container);
107 : 0 : return -1;
108 : : }
109 : :
110 : 0 : DPAA2_BUS_DEBUG("Container: %s has VFIO iommu group id = %d",
111 : : fslmc_container, *groupid);
112 : :
113 : 0 : return 0;
114 : : }
115 : :
116 : : static int
117 : 0 : vfio_connect_container(void)
118 : : {
119 : : int fd, ret;
120 : :
121 [ # # ]: 0 : if (vfio_container.used) {
122 : 0 : DPAA2_BUS_DEBUG("No container available");
123 : 0 : return -1;
124 : : }
125 : :
126 : : /* Try connecting to vfio container if already created */
127 [ # # ]: 0 : if (!ioctl(vfio_group.fd, VFIO_GROUP_SET_CONTAINER,
128 : : &vfio_container.fd)) {
129 : 0 : DPAA2_BUS_DEBUG(
130 : : "Container pre-exists with FD[0x%x] for this group",
131 : : vfio_container.fd);
132 : 0 : vfio_group.container = &vfio_container;
133 : 0 : return 0;
134 : : }
135 : :
136 : : /* Opens main vfio file descriptor which represents the "container" */
137 : 0 : fd = rte_vfio_get_container_fd();
138 [ # # ]: 0 : if (fd < 0) {
139 : 0 : DPAA2_BUS_ERR("Failed to open VFIO container");
140 : 0 : return -errno;
141 : : }
142 : :
143 : : /* Check whether support for SMMU type IOMMU present or not */
144 [ # # ]: 0 : if (ioctl(fd, VFIO_CHECK_EXTENSION, fslmc_iommu_type)) {
145 : : /* Connect group to container */
146 : 0 : ret = ioctl(vfio_group.fd, VFIO_GROUP_SET_CONTAINER, &fd);
147 [ # # ]: 0 : if (ret) {
148 : 0 : DPAA2_BUS_ERR("Failed to setup group container");
149 : 0 : close(fd);
150 : 0 : return -errno;
151 : : }
152 : :
153 : 0 : ret = ioctl(fd, VFIO_SET_IOMMU, fslmc_iommu_type);
154 [ # # ]: 0 : if (ret) {
155 : 0 : DPAA2_BUS_ERR("Failed to setup VFIO iommu");
156 : 0 : close(fd);
157 : 0 : return -errno;
158 : : }
159 : : } else {
160 : 0 : DPAA2_BUS_ERR("No supported IOMMU available");
161 : 0 : close(fd);
162 : 0 : return -EINVAL;
163 : : }
164 : :
165 : 0 : vfio_container.used = 1;
166 : 0 : vfio_container.fd = fd;
167 : 0 : vfio_container.group = &vfio_group;
168 : 0 : vfio_group.container = &vfio_container;
169 : :
170 : 0 : return 0;
171 : : }
172 : :
173 : 0 : static int vfio_map_irq_region(struct fslmc_vfio_group *group)
174 : : {
175 : : int ret;
176 : : unsigned long *vaddr = NULL;
177 : 0 : struct vfio_iommu_type1_dma_map map = {
178 : : .argsz = sizeof(map),
179 : : .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
180 : : .vaddr = 0x6030000,
181 : : .iova = 0x6030000,
182 : : .size = 0x1000,
183 : : };
184 : :
185 : 0 : vaddr = (unsigned long *)mmap(NULL, 0x1000, PROT_WRITE |
186 : : PROT_READ, MAP_SHARED, container_device_fd, 0x6030000);
187 [ # # ]: 0 : if (vaddr == MAP_FAILED) {
188 : 0 : DPAA2_BUS_INFO("Unable to map region (errno = %d)", errno);
189 : 0 : return -errno;
190 : : }
191 : :
192 : 0 : msi_intr_vaddr = (uint32_t *)((char *)(vaddr) + 64);
193 : 0 : map.vaddr = (unsigned long)vaddr;
194 : 0 : ret = ioctl(group->container->fd, VFIO_IOMMU_MAP_DMA, &map);
195 [ # # ]: 0 : if (ret == 0)
196 : : return 0;
197 : :
198 : 0 : DPAA2_BUS_ERR("Unable to map DMA address (errno = %d)", errno);
199 : 0 : return -errno;
200 : : }
201 : :
202 : : static int fslmc_map_dma(uint64_t vaddr, rte_iova_t iovaddr, size_t len);
203 : : static int fslmc_unmap_dma(uint64_t vaddr, rte_iova_t iovaddr, size_t len);
204 : :
205 : : static void
206 : 0 : fslmc_memevent_cb(enum rte_mem_event type, const void *addr, size_t len,
207 : : void *arg __rte_unused)
208 : : {
209 : : struct rte_memseg_list *msl;
210 : : struct rte_memseg *ms;
211 : : size_t cur_len = 0, map_len = 0;
212 : : uint64_t virt_addr;
213 : : rte_iova_t iova_addr;
214 : : int ret;
215 : :
216 : 0 : msl = rte_mem_virt2memseg_list(addr);
217 : :
218 [ # # ]: 0 : while (cur_len < len) {
219 : 0 : const void *va = RTE_PTR_ADD(addr, cur_len);
220 : :
221 : 0 : ms = rte_mem_virt2memseg(va, msl);
222 : 0 : iova_addr = ms->iova;
223 : 0 : virt_addr = ms->addr_64;
224 : 0 : map_len = ms->len;
225 : :
226 [ # # ]: 0 : DPAA2_BUS_DEBUG("Request for %s, va=%p, "
227 : : "virt_addr=0x%" PRIx64 ", "
228 : : "iova=0x%" PRIx64 ", map_len=%zu",
229 : : type == RTE_MEM_EVENT_ALLOC ?
230 : : "alloc" : "dealloc",
231 : : va, virt_addr, iova_addr, map_len);
232 : :
233 : : /* iova_addr may be set to RTE_BAD_IOVA */
234 [ # # ]: 0 : if (iova_addr == RTE_BAD_IOVA) {
235 : 0 : DPAA2_BUS_DEBUG("Segment has invalid iova, skipping\n");
236 : 0 : cur_len += map_len;
237 : 0 : continue;
238 : : }
239 : :
240 [ # # ]: 0 : if (type == RTE_MEM_EVENT_ALLOC)
241 : 0 : ret = fslmc_map_dma(virt_addr, iova_addr, map_len);
242 : : else
243 : 0 : ret = fslmc_unmap_dma(virt_addr, iova_addr, map_len);
244 : :
245 [ # # ]: 0 : if (ret != 0) {
246 : 0 : DPAA2_BUS_ERR("DMA Mapping/Unmapping failed. "
247 : : "Map=%d, addr=%p, len=%zu, err:(%d)",
248 : : type, va, map_len, ret);
249 : 0 : return;
250 : : }
251 : :
252 : 0 : cur_len += map_len;
253 : : }
254 : :
255 [ # # ]: 0 : if (type == RTE_MEM_EVENT_ALLOC)
256 : 0 : DPAA2_BUS_DEBUG("Total Mapped: addr=%p, len=%zu",
257 : : addr, len);
258 : : else
259 : 0 : DPAA2_BUS_DEBUG("Total Unmapped: addr=%p, len=%zu",
260 : : addr, len);
261 : : }
262 : :
263 : : static int
264 : 0 : fslmc_map_dma(uint64_t vaddr, rte_iova_t iovaddr __rte_unused, size_t len)
265 : : {
266 : : struct fslmc_vfio_group *group;
267 : 0 : struct vfio_iommu_type1_dma_map dma_map = {
268 : : .argsz = sizeof(struct vfio_iommu_type1_dma_map),
269 : : .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
270 : : };
271 : : int ret;
272 : :
273 [ # # ]: 0 : if (fslmc_iommu_type == RTE_VFIO_NOIOMMU) {
274 : 0 : DPAA2_BUS_DEBUG("Running in NOIOMMU mode");
275 : 0 : return 0;
276 : : }
277 : :
278 : 0 : dma_map.size = len;
279 : 0 : dma_map.vaddr = vaddr;
280 : :
281 : : #ifdef RTE_LIBRTE_DPAA2_USE_PHYS_IOVA
282 : 0 : dma_map.iova = iovaddr;
283 : : #else
284 : : dma_map.iova = dma_map.vaddr;
285 : : #endif
286 : :
287 : : /* SET DMA MAP for IOMMU */
288 : : group = &vfio_group;
289 : :
290 [ # # ]: 0 : if (!group->container) {
291 : 0 : DPAA2_BUS_ERR("Container is not connected ");
292 : 0 : return -1;
293 : : }
294 : :
295 : 0 : DPAA2_BUS_DEBUG("--> Map address: 0x%"PRIx64", size: %"PRIu64"",
296 : : (uint64_t)dma_map.vaddr, (uint64_t)dma_map.size);
297 : 0 : ret = ioctl(group->container->fd, VFIO_IOMMU_MAP_DMA, &dma_map);
298 [ # # ]: 0 : if (ret) {
299 : 0 : DPAA2_BUS_ERR("VFIO_IOMMU_MAP_DMA API(errno = %d)",
300 : : errno);
301 : 0 : return -1;
302 : : }
303 : :
304 : : return 0;
305 : : }
306 : :
307 : : static int
308 : 0 : fslmc_unmap_dma(uint64_t vaddr, uint64_t iovaddr __rte_unused, size_t len)
309 : : {
310 : : struct fslmc_vfio_group *group;
311 : 0 : struct vfio_iommu_type1_dma_unmap dma_unmap = {
312 : : .argsz = sizeof(struct vfio_iommu_type1_dma_unmap),
313 : : .flags = 0,
314 : : };
315 : : int ret;
316 : :
317 [ # # ]: 0 : if (fslmc_iommu_type == RTE_VFIO_NOIOMMU) {
318 : 0 : DPAA2_BUS_DEBUG("Running in NOIOMMU mode");
319 : 0 : return 0;
320 : : }
321 : :
322 : 0 : dma_unmap.size = len;
323 : 0 : dma_unmap.iova = vaddr;
324 : :
325 : : /* SET DMA MAP for IOMMU */
326 : : group = &vfio_group;
327 : :
328 [ # # ]: 0 : if (!group->container) {
329 : 0 : DPAA2_BUS_ERR("Container is not connected ");
330 : 0 : return -1;
331 : : }
332 : :
333 : 0 : DPAA2_BUS_DEBUG("--> Unmap address: 0x%"PRIx64", size: %"PRIu64"",
334 : : (uint64_t)dma_unmap.iova, (uint64_t)dma_unmap.size);
335 : 0 : ret = ioctl(group->container->fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap);
336 [ # # ]: 0 : if (ret) {
337 : 0 : DPAA2_BUS_ERR("VFIO_IOMMU_UNMAP_DMA API(errno = %d)",
338 : : errno);
339 : 0 : return -1;
340 : : }
341 : :
342 : : return 0;
343 : : }
344 : :
345 : : static int
346 : 0 : fslmc_dmamap_seg(const struct rte_memseg_list *msl __rte_unused,
347 : : const struct rte_memseg *ms, void *arg)
348 : : {
349 : : int *n_segs = arg;
350 : : int ret;
351 : :
352 : : /* if IOVA address is invalid, skip */
353 [ # # ]: 0 : if (ms->iova == RTE_BAD_IOVA)
354 : : return 0;
355 : :
356 : 0 : ret = fslmc_map_dma(ms->addr_64, ms->iova, ms->len);
357 [ # # ]: 0 : if (ret)
358 : 0 : DPAA2_BUS_ERR("Unable to VFIO map (addr=%p, len=%zu)",
359 : : ms->addr, ms->len);
360 : : else
361 : 0 : (*n_segs)++;
362 : :
363 : : return ret;
364 : : }
365 : :
366 : : int
367 : 0 : rte_fslmc_vfio_mem_dmamap(uint64_t vaddr, uint64_t iova, uint64_t size)
368 : : {
369 : : int ret;
370 : : struct fslmc_vfio_group *group;
371 : 0 : struct vfio_iommu_type1_dma_map dma_map = {
372 : : .argsz = sizeof(struct vfio_iommu_type1_dma_map),
373 : : .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
374 : : };
375 : :
376 [ # # ]: 0 : if (fslmc_iommu_type == RTE_VFIO_NOIOMMU) {
377 : 0 : DPAA2_BUS_DEBUG("Running in NOIOMMU mode");
378 : 0 : return 0;
379 : : }
380 : :
381 : : /* SET DMA MAP for IOMMU */
382 : : group = &vfio_group;
383 [ # # ]: 0 : if (!group->container) {
384 : 0 : DPAA2_BUS_ERR("Container is not connected");
385 : 0 : return -1;
386 : : }
387 : :
388 : 0 : dma_map.size = size;
389 : 0 : dma_map.vaddr = vaddr;
390 : 0 : dma_map.iova = iova;
391 : :
392 : 0 : DPAA2_BUS_DEBUG("VFIOdmamap 0x%"PRIx64":0x%"PRIx64",size 0x%"PRIx64"\n",
393 : : (uint64_t)dma_map.vaddr, (uint64_t)dma_map.iova,
394 : : (uint64_t)dma_map.size);
395 : 0 : ret = ioctl(group->container->fd, VFIO_IOMMU_MAP_DMA,
396 : : &dma_map);
397 [ # # ]: 0 : if (ret) {
398 : 0 : printf("Unable to map DMA address (errno = %d)\n",
399 : 0 : errno);
400 : 0 : return ret;
401 : : }
402 : :
403 : : return 0;
404 : : }
405 : :
406 : 0 : int rte_fslmc_vfio_dmamap(void)
407 : : {
408 : 0 : int i = 0, ret;
409 : :
410 : : /* Lock before parsing and registering callback to memory subsystem */
411 : 0 : rte_mcfg_mem_read_lock();
412 : :
413 [ # # ]: 0 : if (rte_memseg_walk(fslmc_dmamap_seg, &i) < 0) {
414 : 0 : rte_mcfg_mem_read_unlock();
415 : 0 : return -1;
416 : : }
417 : :
418 : 0 : ret = rte_mem_event_callback_register("fslmc_memevent_clb",
419 : : fslmc_memevent_cb, NULL);
420 [ # # # # ]: 0 : if (ret && rte_errno == ENOTSUP)
421 : 0 : DPAA2_BUS_DEBUG("Memory event callbacks not supported");
422 [ # # ]: 0 : else if (ret)
423 : 0 : DPAA2_BUS_DEBUG("Unable to install memory handler");
424 : : else
425 : 0 : DPAA2_BUS_DEBUG("Installed memory callback handler");
426 : :
427 : 0 : DPAA2_BUS_DEBUG("Total %d segments found.", i);
428 : :
429 : : /* TODO - This is a W.A. as VFIO currently does not add the mapping of
430 : : * the interrupt region to SMMU. This should be removed once the
431 : : * support is added in the Kernel.
432 : : */
433 : 0 : vfio_map_irq_region(&vfio_group);
434 : :
435 : : /* Existing segments have been mapped and memory callback for hotplug
436 : : * has been installed.
437 : : */
438 : 0 : rte_mcfg_mem_read_unlock();
439 : :
440 : 0 : return 0;
441 : : }
442 : :
443 : : static int
444 : 0 : fslmc_vfio_setup_device(const char *sysfs_base, const char *dev_addr,
445 : : int *vfio_dev_fd, struct vfio_device_info *device_info)
446 : : {
447 : 0 : struct vfio_group_status group_status = {
448 : : .argsz = sizeof(group_status)
449 : : };
450 : : int vfio_group_fd, vfio_container_fd, iommu_group_no, ret;
451 : :
452 : : /* get group number */
453 : 0 : ret = rte_vfio_get_group_num(sysfs_base, dev_addr, &iommu_group_no);
454 [ # # ]: 0 : if (ret < 0)
455 : : return -1;
456 : :
457 : : /* get the actual group fd */
458 : 0 : vfio_group_fd = rte_vfio_get_group_fd(iommu_group_no);
459 [ # # ]: 0 : if (vfio_group_fd < 0 && vfio_group_fd != -ENOENT)
460 : : return -1;
461 : :
462 : : /*
463 : : * if vfio_group_fd == -ENOENT, that means the device
464 : : * isn't managed by VFIO
465 : : */
466 [ # # ]: 0 : if (vfio_group_fd == -ENOENT) {
467 : 0 : RTE_LOG(WARNING, EAL, " %s not managed by VFIO driver, skipping\n",
468 : : dev_addr);
469 : 0 : return 1;
470 : : }
471 : :
472 : : /* Opens main vfio file descriptor which represents the "container" */
473 : 0 : vfio_container_fd = rte_vfio_get_container_fd();
474 [ # # ]: 0 : if (vfio_container_fd < 0) {
475 : 0 : DPAA2_BUS_ERR("Failed to open VFIO container");
476 : 0 : return -errno;
477 : : }
478 : :
479 : : /* check if the group is viable */
480 : 0 : ret = ioctl(vfio_group_fd, VFIO_GROUP_GET_STATUS, &group_status);
481 [ # # ]: 0 : if (ret) {
482 : 0 : DPAA2_BUS_ERR(" %s cannot get group status, "
483 : : "error %i (%s)\n", dev_addr,
484 : : errno, strerror(errno));
485 : 0 : close(vfio_group_fd);
486 : 0 : rte_vfio_clear_group(vfio_group_fd);
487 : 0 : return -1;
488 [ # # ]: 0 : } else if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
489 : 0 : DPAA2_BUS_ERR(" %s VFIO group is not viable!\n", dev_addr);
490 : 0 : close(vfio_group_fd);
491 : 0 : rte_vfio_clear_group(vfio_group_fd);
492 : 0 : return -1;
493 : : }
494 : : /* At this point, we know that this group is viable (meaning,
495 : : * all devices are either bound to VFIO or not bound to anything)
496 : : */
497 : :
498 : : /* check if group does not have a container yet */
499 [ # # ]: 0 : if (!(group_status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) {
500 : :
501 : : /* add group to a container */
502 : 0 : ret = ioctl(vfio_group_fd, VFIO_GROUP_SET_CONTAINER,
503 : : &vfio_container_fd);
504 [ # # ]: 0 : if (ret) {
505 : 0 : DPAA2_BUS_ERR(" %s cannot add VFIO group to container, "
506 : : "error %i (%s)\n", dev_addr,
507 : : errno, strerror(errno));
508 : 0 : close(vfio_group_fd);
509 : 0 : close(vfio_container_fd);
510 : 0 : rte_vfio_clear_group(vfio_group_fd);
511 : 0 : return -1;
512 : : }
513 : :
514 : : /*
515 : : * set an IOMMU type for container
516 : : *
517 : : */
518 [ # # ]: 0 : if (ioctl(vfio_container_fd, VFIO_CHECK_EXTENSION,
519 : : fslmc_iommu_type)) {
520 : 0 : ret = ioctl(vfio_container_fd, VFIO_SET_IOMMU,
521 : : fslmc_iommu_type);
522 [ # # ]: 0 : if (ret) {
523 : 0 : DPAA2_BUS_ERR("Failed to setup VFIO iommu");
524 : 0 : close(vfio_group_fd);
525 : 0 : close(vfio_container_fd);
526 : 0 : return -errno;
527 : : }
528 : : } else {
529 : 0 : DPAA2_BUS_ERR("No supported IOMMU available");
530 : 0 : close(vfio_group_fd);
531 : 0 : close(vfio_container_fd);
532 : 0 : return -EINVAL;
533 : : }
534 : : }
535 : :
536 : : /* get a file descriptor for the device */
537 : 0 : *vfio_dev_fd = ioctl(vfio_group_fd, VFIO_GROUP_GET_DEVICE_FD, dev_addr);
538 [ # # ]: 0 : if (*vfio_dev_fd < 0) {
539 : : /* if we cannot get a device fd, this implies a problem with
540 : : * the VFIO group or the container not having IOMMU configured.
541 : : */
542 : :
543 : 0 : DPAA2_BUS_WARN("Getting a vfio_dev_fd for %s failed", dev_addr);
544 : 0 : close(vfio_group_fd);
545 : 0 : close(vfio_container_fd);
546 : 0 : rte_vfio_clear_group(vfio_group_fd);
547 : 0 : return -1;
548 : : }
549 : :
550 : : /* test and setup the device */
551 : 0 : ret = ioctl(*vfio_dev_fd, VFIO_DEVICE_GET_INFO, device_info);
552 [ # # ]: 0 : if (ret) {
553 : 0 : DPAA2_BUS_ERR(" %s cannot get device info, error %i (%s)",
554 : : dev_addr, errno, strerror(errno));
555 : 0 : close(*vfio_dev_fd);
556 : 0 : close(vfio_group_fd);
557 : 0 : close(vfio_container_fd);
558 : 0 : rte_vfio_clear_group(vfio_group_fd);
559 : 0 : return -1;
560 : : }
561 : :
562 : : return 0;
563 : : }
564 : :
565 : 0 : static intptr_t vfio_map_mcp_obj(const char *mcp_obj)
566 : : {
567 : : intptr_t v_addr = (intptr_t)MAP_FAILED;
568 : : int32_t ret, mc_fd;
569 : : struct vfio_group_status status = { .argsz = sizeof(status) };
570 : :
571 : 0 : struct vfio_device_info d_info = { .argsz = sizeof(d_info) };
572 : 0 : struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
573 : :
574 : 0 : fslmc_vfio_setup_device(SYSFS_FSL_MC_DEVICES, mcp_obj,
575 : : &mc_fd, &d_info);
576 : :
577 : : /* getting device region info*/
578 : 0 : ret = ioctl(mc_fd, VFIO_DEVICE_GET_REGION_INFO, ®_info);
579 [ # # ]: 0 : if (ret < 0) {
580 : 0 : DPAA2_BUS_ERR("Error in VFIO getting REGION_INFO");
581 : 0 : goto MC_FAILURE;
582 : : }
583 : :
584 : 0 : v_addr = (size_t)mmap(NULL, reg_info.size,
585 : : PROT_WRITE | PROT_READ, MAP_SHARED,
586 : 0 : mc_fd, reg_info.offset);
587 : :
588 : 0 : MC_FAILURE:
589 : 0 : close(mc_fd);
590 : :
591 : 0 : return v_addr;
592 : : }
593 : :
594 : : #define IRQ_SET_BUF_LEN (sizeof(struct vfio_irq_set) + sizeof(int))
595 : :
596 : 0 : int rte_dpaa2_intr_enable(struct rte_intr_handle *intr_handle, int index)
597 : : {
598 : : int len, ret;
599 : : char irq_set_buf[IRQ_SET_BUF_LEN];
600 : : struct vfio_irq_set *irq_set;
601 : : int *fd_ptr, vfio_dev_fd;
602 : :
603 : : len = sizeof(irq_set_buf);
604 : :
605 : : irq_set = (struct vfio_irq_set *)irq_set_buf;
606 : 0 : irq_set->argsz = len;
607 : 0 : irq_set->count = 1;
608 : 0 : irq_set->flags =
609 : : VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
610 : 0 : irq_set->index = index;
611 : 0 : irq_set->start = 0;
612 : : fd_ptr = (int *)&irq_set->data;
613 : 0 : *fd_ptr = rte_intr_fd_get(intr_handle);
614 : :
615 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
616 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
617 [ # # ]: 0 : if (ret) {
618 : 0 : DPAA2_BUS_ERR("Error:dpaa2 SET IRQs fd=%d, err = %d(%s)",
619 : : rte_intr_fd_get(intr_handle), errno,
620 : : strerror(errno));
621 : 0 : return ret;
622 : : }
623 : :
624 : : return ret;
625 : : }
626 : :
627 : 0 : int rte_dpaa2_intr_disable(struct rte_intr_handle *intr_handle, int index)
628 : : {
629 : : struct vfio_irq_set *irq_set;
630 : : char irq_set_buf[IRQ_SET_BUF_LEN];
631 : : int len, ret, vfio_dev_fd;
632 : :
633 : : len = sizeof(struct vfio_irq_set);
634 : :
635 : : irq_set = (struct vfio_irq_set *)irq_set_buf;
636 : 0 : irq_set->argsz = len;
637 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;
638 : 0 : irq_set->index = index;
639 : 0 : irq_set->start = 0;
640 : 0 : irq_set->count = 0;
641 : :
642 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
643 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
644 [ # # ]: 0 : if (ret)
645 : 0 : DPAA2_BUS_ERR(
646 : : "Error disabling dpaa2 interrupts for fd %d",
647 : : rte_intr_fd_get(intr_handle));
648 : :
649 : 0 : return ret;
650 : : }
651 : :
652 : : /* set up interrupt support (but not enable interrupts) */
653 : : int
654 : 0 : rte_dpaa2_vfio_setup_intr(struct rte_intr_handle *intr_handle,
655 : : int vfio_dev_fd,
656 : : int num_irqs)
657 : : {
658 : : int i, ret;
659 : :
660 : : /* start from MSI-X interrupt type */
661 [ # # ]: 0 : for (i = 0; i < num_irqs; i++) {
662 : 0 : struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) };
663 : : int fd = -1;
664 : :
665 : 0 : irq_info.index = i;
666 : :
667 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info);
668 [ # # ]: 0 : if (ret < 0) {
669 : 0 : DPAA2_BUS_ERR("Cannot get IRQ(%d) info, error %i (%s)",
670 : : i, errno, strerror(errno));
671 : 0 : return -1;
672 : : }
673 : :
674 : : /* if this vector cannot be used with eventfd,
675 : : * fail if we explicitly
676 : : * specified interrupt type, otherwise continue
677 : : */
678 [ # # ]: 0 : if ((irq_info.flags & VFIO_IRQ_INFO_EVENTFD) == 0)
679 : 0 : continue;
680 : :
681 : : /* set up an eventfd for interrupts */
682 : 0 : fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
683 [ # # ]: 0 : if (fd < 0) {
684 : 0 : DPAA2_BUS_ERR("Cannot set up eventfd, error %i (%s)",
685 : : errno, strerror(errno));
686 : 0 : return -1;
687 : : }
688 : :
689 [ # # ]: 0 : if (rte_intr_fd_set(intr_handle, fd))
690 : 0 : return -rte_errno;
691 : :
692 [ # # ]: 0 : if (rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_VFIO_MSI))
693 : 0 : return -rte_errno;
694 : :
695 [ # # ]: 0 : if (rte_intr_dev_fd_set(intr_handle, vfio_dev_fd))
696 : 0 : return -rte_errno;
697 : :
698 : : return 0;
699 : : }
700 : :
701 : : /* if we're here, we haven't found a suitable interrupt vector */
702 : : return -1;
703 : : }
704 : :
705 : : /*
706 : : * fslmc_process_iodevices for processing only IO (ETH, CRYPTO, and possibly
707 : : * EVENT) devices.
708 : : */
709 : : static int
710 : 0 : fslmc_process_iodevices(struct rte_dpaa2_device *dev)
711 : : {
712 : : int dev_fd;
713 : 0 : struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
714 : : struct rte_dpaa2_object *object = NULL;
715 : :
716 : 0 : fslmc_vfio_setup_device(SYSFS_FSL_MC_DEVICES, dev->device.name,
717 : : &dev_fd, &device_info);
718 : :
719 [ # # # ]: 0 : switch (dev->dev_type) {
720 : 0 : case DPAA2_ETH:
721 : 0 : rte_dpaa2_vfio_setup_intr(dev->intr_handle, dev_fd,
722 : 0 : device_info.num_irqs);
723 : 0 : break;
724 : 0 : case DPAA2_CON:
725 : : case DPAA2_IO:
726 : : case DPAA2_CI:
727 : : case DPAA2_BPOOL:
728 : : case DPAA2_DPRTC:
729 : : case DPAA2_MUX:
730 : : case DPAA2_DPRC:
731 [ # # ]: 0 : TAILQ_FOREACH(object, &dpaa2_obj_list, next) {
732 [ # # ]: 0 : if (dev->dev_type == object->dev_type)
733 : 0 : object->create(dev_fd, &device_info,
734 : 0 : dev->object_id);
735 : : else
736 : 0 : continue;
737 : : }
738 : : break;
739 : : default:
740 : : break;
741 : : }
742 : :
743 : 0 : DPAA2_BUS_LOG(DEBUG, "Device (%s) abstracted from VFIO",
744 : : dev->device.name);
745 : 0 : return 0;
746 : : }
747 : :
748 : : static int
749 : 0 : fslmc_process_mcp(struct rte_dpaa2_device *dev)
750 : : {
751 : : int ret;
752 : : intptr_t v_addr;
753 : 0 : struct fsl_mc_io dpmng = {0};
754 : 0 : struct mc_version mc_ver_info = {0};
755 : :
756 : 0 : rte_mcp_ptr_list = malloc(sizeof(void *) * (MC_PORTAL_INDEX + 1));
757 [ # # ]: 0 : if (!rte_mcp_ptr_list) {
758 : 0 : DPAA2_BUS_ERR("Unable to allocate MC portal memory");
759 : : ret = -ENOMEM;
760 : 0 : goto cleanup;
761 : : }
762 : :
763 : 0 : v_addr = vfio_map_mcp_obj(dev->device.name);
764 [ # # ]: 0 : if (v_addr == (intptr_t)MAP_FAILED) {
765 : 0 : DPAA2_BUS_ERR("Error mapping region (errno = %d)", errno);
766 : : ret = -1;
767 : 0 : goto cleanup;
768 : : }
769 : :
770 : : /* check the MC version compatibility */
771 : 0 : dpmng.regs = (void *)v_addr;
772 : :
773 : : /* In case of secondary processes, MC version check is no longer
774 : : * required.
775 : : */
776 [ # # ]: 0 : if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
777 : 0 : rte_mcp_ptr_list[MC_PORTAL_INDEX] = (void *)v_addr;
778 : 0 : return 0;
779 : : }
780 : :
781 [ # # ]: 0 : if (mc_get_version(&dpmng, CMD_PRI_LOW, &mc_ver_info)) {
782 : 0 : DPAA2_BUS_ERR("Unable to obtain MC version");
783 : : ret = -1;
784 : 0 : goto cleanup;
785 : : }
786 : :
787 [ # # ]: 0 : if ((mc_ver_info.major != MC_VER_MAJOR) ||
788 [ # # ]: 0 : (mc_ver_info.minor < MC_VER_MINOR)) {
789 : 0 : DPAA2_BUS_ERR("DPAA2 MC version not compatible!"
790 : : " Expected %d.%d.x, Detected %d.%d.%d",
791 : : MC_VER_MAJOR, MC_VER_MINOR,
792 : : mc_ver_info.major, mc_ver_info.minor,
793 : : mc_ver_info.revision);
794 : : ret = -1;
795 : 0 : goto cleanup;
796 : : }
797 : 0 : rte_mcp_ptr_list[MC_PORTAL_INDEX] = (void *)v_addr;
798 : :
799 : 0 : return 0;
800 : :
801 : 0 : cleanup:
802 [ # # ]: 0 : if (rte_mcp_ptr_list) {
803 : 0 : free(rte_mcp_ptr_list);
804 : 0 : rte_mcp_ptr_list = NULL;
805 : : }
806 : :
807 : : return ret;
808 : : }
809 : :
810 : : int
811 : 0 : fslmc_vfio_process_group(void)
812 : : {
813 : : int ret;
814 : : int found_mportal = 0;
815 : : struct rte_dpaa2_device *dev, *dev_temp;
816 : : bool is_dpmcp_in_blocklist = false, is_dpio_in_blocklist = false;
817 : : int dpmcp_count = 0, dpio_count = 0, current_device;
818 : :
819 [ # # ]: 0 : RTE_TAILQ_FOREACH_SAFE(dev, &rte_fslmc_bus.device_list, next,
820 : : dev_temp) {
821 [ # # ]: 0 : if (dev->dev_type == DPAA2_MPORTAL) {
822 : 0 : dpmcp_count++;
823 [ # # ]: 0 : if (dev->device.devargs &&
824 [ # # ]: 0 : dev->device.devargs->policy == RTE_DEV_BLOCKED)
825 : : is_dpmcp_in_blocklist = true;
826 : : }
827 [ # # ]: 0 : if (dev->dev_type == DPAA2_IO) {
828 : 0 : dpio_count++;
829 [ # # ]: 0 : if (dev->device.devargs &&
830 [ # # ]: 0 : dev->device.devargs->policy == RTE_DEV_BLOCKED)
831 : : is_dpio_in_blocklist = true;
832 : : }
833 : : }
834 : :
835 : : /* Search the MCP as that should be initialized first. */
836 : : current_device = 0;
837 [ # # ]: 0 : RTE_TAILQ_FOREACH_SAFE(dev, &rte_fslmc_bus.device_list, next,
838 : : dev_temp) {
839 [ # # ]: 0 : if (dev->dev_type == DPAA2_MPORTAL) {
840 : 0 : current_device++;
841 [ # # ]: 0 : if (dev->device.devargs &&
842 [ # # ]: 0 : dev->device.devargs->policy == RTE_DEV_BLOCKED) {
843 : 0 : DPAA2_BUS_LOG(DEBUG, "%s Blocked, skipping",
844 : : dev->device.name);
845 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list,
846 : : dev, next);
847 : 0 : continue;
848 : : }
849 : :
850 [ # # # # ]: 0 : if (rte_eal_process_type() == RTE_PROC_SECONDARY &&
851 : : !is_dpmcp_in_blocklist) {
852 : 0 : if (dpmcp_count == 1 ||
853 [ # # ]: 0 : current_device != dpmcp_count) {
854 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list,
855 : : dev, next);
856 : 0 : continue;
857 : : }
858 : : }
859 : :
860 [ # # ]: 0 : if (!found_mportal) {
861 : 0 : ret = fslmc_process_mcp(dev);
862 [ # # ]: 0 : if (ret) {
863 : 0 : DPAA2_BUS_ERR("Unable to map MC Portal");
864 : 0 : return -1;
865 : : }
866 : : found_mportal = 1;
867 : : }
868 : :
869 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list, dev, next);
870 : 0 : free(dev);
871 : : dev = NULL;
872 : : /* Ideally there is only a single dpmcp, but in case
873 : : * multiple exists, looping on remaining devices.
874 : : */
875 : : }
876 : : }
877 : :
878 : : /* Cannot continue if there is not even a single mportal */
879 [ # # ]: 0 : if (!found_mportal) {
880 : 0 : DPAA2_BUS_ERR("No MC Portal device found. Not continuing");
881 : 0 : return -1;
882 : : }
883 : :
884 : : /* Search for DPRC device next as it updates endpoint of
885 : : * other devices.
886 : : */
887 : : current_device = 0;
888 [ # # ]: 0 : RTE_TAILQ_FOREACH_SAFE(dev, &rte_fslmc_bus.device_list, next, dev_temp) {
889 [ # # ]: 0 : if (dev->dev_type == DPAA2_DPRC) {
890 : 0 : ret = fslmc_process_iodevices(dev);
891 [ # # ]: 0 : if (ret) {
892 : 0 : DPAA2_BUS_ERR("Unable to process dprc");
893 : 0 : return -1;
894 : : }
895 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list, dev, next);
896 : : }
897 : : }
898 : :
899 : : current_device = 0;
900 [ # # ]: 0 : RTE_TAILQ_FOREACH_SAFE(dev, &rte_fslmc_bus.device_list, next,
901 : : dev_temp) {
902 [ # # ]: 0 : if (dev->dev_type == DPAA2_IO)
903 : 0 : current_device++;
904 [ # # ]: 0 : if (dev->device.devargs &&
905 [ # # ]: 0 : dev->device.devargs->policy == RTE_DEV_BLOCKED) {
906 : 0 : DPAA2_BUS_LOG(DEBUG, "%s Blocked, skipping",
907 : : dev->device.name);
908 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list, dev, next);
909 : 0 : continue;
910 : : }
911 [ # # ]: 0 : if (rte_eal_process_type() == RTE_PROC_SECONDARY &&
912 [ # # ]: 0 : dev->dev_type != DPAA2_ETH &&
913 : : dev->dev_type != DPAA2_CRYPTO &&
914 : 0 : dev->dev_type != DPAA2_QDMA &&
915 : : dev->dev_type != DPAA2_IO) {
916 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list, dev, next);
917 : 0 : continue;
918 : : }
919 [ # # # # ]: 0 : switch (dev->dev_type) {
920 : 0 : case DPAA2_ETH:
921 : : case DPAA2_CRYPTO:
922 : : case DPAA2_QDMA:
923 : 0 : ret = fslmc_process_iodevices(dev);
924 [ # # ]: 0 : if (ret) {
925 : 0 : DPAA2_BUS_DEBUG("Dev (%s) init failed",
926 : : dev->device.name);
927 : 0 : return ret;
928 : : }
929 : : break;
930 : 0 : case DPAA2_CON:
931 : : case DPAA2_CI:
932 : : case DPAA2_BPOOL:
933 : : case DPAA2_DPRTC:
934 : : case DPAA2_MUX:
935 : : /* IN case of secondary processes, all control objects
936 : : * like dpbp, dpcon, dpci are not initialized/required
937 : : * - all of these are assumed to be initialized and made
938 : : * available by primary.
939 : : */
940 [ # # ]: 0 : if (rte_eal_process_type() == RTE_PROC_SECONDARY)
941 : 0 : continue;
942 : :
943 : : /* Call the object creation routine and remove the
944 : : * device entry from device list
945 : : */
946 : 0 : ret = fslmc_process_iodevices(dev);
947 [ # # ]: 0 : if (ret) {
948 : 0 : DPAA2_BUS_DEBUG("Dev (%s) init failed",
949 : : dev->device.name);
950 : 0 : return -1;
951 : : }
952 : :
953 : : break;
954 : 0 : case DPAA2_IO:
955 [ # # ]: 0 : if (!is_dpio_in_blocklist && dpio_count > 1) {
956 [ # # ]: 0 : if (rte_eal_process_type() == RTE_PROC_SECONDARY
957 [ # # ]: 0 : && current_device != dpio_count) {
958 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list,
959 : : dev, next);
960 : 0 : break;
961 : : }
962 [ # # ]: 0 : if (rte_eal_process_type() == RTE_PROC_PRIMARY
963 [ # # ]: 0 : && current_device == dpio_count) {
964 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list,
965 : : dev, next);
966 : 0 : break;
967 : : }
968 : : }
969 : :
970 : 0 : ret = fslmc_process_iodevices(dev);
971 [ # # ]: 0 : if (ret) {
972 : 0 : DPAA2_BUS_DEBUG("Dev (%s) init failed",
973 : : dev->device.name);
974 : 0 : return -1;
975 : : }
976 : :
977 : : break;
978 : 0 : case DPAA2_UNKNOWN:
979 : : default:
980 : : /* Unknown - ignore */
981 : 0 : DPAA2_BUS_DEBUG("Found unknown device (%s)",
982 : : dev->device.name);
983 [ # # ]: 0 : TAILQ_REMOVE(&rte_fslmc_bus.device_list, dev, next);
984 : 0 : free(dev);
985 : : dev = NULL;
986 : : }
987 : : }
988 : :
989 : : return 0;
990 : : }
991 : :
992 : : int
993 : 0 : fslmc_vfio_setup_group(void)
994 : : {
995 : : int groupid;
996 : : int ret;
997 : : int vfio_container_fd;
998 : 0 : struct vfio_group_status status = { .argsz = sizeof(status) };
999 : :
1000 : : /* if already done once */
1001 [ # # ]: 0 : if (container_device_fd)
1002 : : return 0;
1003 : :
1004 : 0 : ret = fslmc_get_container_group(&groupid);
1005 [ # # ]: 0 : if (ret)
1006 : : return ret;
1007 : :
1008 : : /* In case this group was already opened, continue without any
1009 : : * processing.
1010 : : */
1011 [ # # ]: 0 : if (vfio_group.groupid == groupid) {
1012 : 0 : DPAA2_BUS_ERR("groupid already exists %d", groupid);
1013 : 0 : return 0;
1014 : : }
1015 : :
1016 : 0 : ret = rte_vfio_container_create();
1017 [ # # ]: 0 : if (ret < 0) {
1018 : 0 : DPAA2_BUS_ERR("Failed to open VFIO container");
1019 : 0 : return ret;
1020 : : }
1021 : : vfio_container_fd = ret;
1022 : :
1023 : : /* Get the actual group fd */
1024 : 0 : ret = rte_vfio_container_group_bind(vfio_container_fd, groupid);
1025 [ # # ]: 0 : if (ret < 0)
1026 : : return ret;
1027 : 0 : vfio_group.fd = ret;
1028 : :
1029 : : /* Check group viability */
1030 : 0 : ret = ioctl(vfio_group.fd, VFIO_GROUP_GET_STATUS, &status);
1031 [ # # ]: 0 : if (ret) {
1032 : 0 : DPAA2_BUS_ERR("VFIO error getting group status");
1033 : 0 : close(vfio_group.fd);
1034 : 0 : rte_vfio_clear_group(vfio_group.fd);
1035 : 0 : return ret;
1036 : : }
1037 : :
1038 [ # # ]: 0 : if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
1039 : 0 : DPAA2_BUS_ERR("VFIO group not viable");
1040 : 0 : close(vfio_group.fd);
1041 : 0 : rte_vfio_clear_group(vfio_group.fd);
1042 : 0 : return -EPERM;
1043 : : }
1044 : : /* Since Group is VIABLE, Store the groupid */
1045 : 0 : vfio_group.groupid = groupid;
1046 : :
1047 : : /* check if group does not have a container yet */
1048 [ # # ]: 0 : if (!(status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) {
1049 : : /* Now connect this IOMMU group to given container */
1050 : 0 : ret = vfio_connect_container();
1051 [ # # ]: 0 : if (ret) {
1052 : 0 : DPAA2_BUS_ERR(
1053 : : "Error connecting container with groupid %d",
1054 : : groupid);
1055 : 0 : close(vfio_group.fd);
1056 : 0 : rte_vfio_clear_group(vfio_group.fd);
1057 : 0 : return ret;
1058 : : }
1059 : : }
1060 : :
1061 : : /* Get Device information */
1062 : 0 : ret = ioctl(vfio_group.fd, VFIO_GROUP_GET_DEVICE_FD, fslmc_container);
1063 [ # # ]: 0 : if (ret < 0) {
1064 : 0 : DPAA2_BUS_ERR("Error getting device %s fd from group %d",
1065 : : fslmc_container, vfio_group.groupid);
1066 : 0 : close(vfio_group.fd);
1067 : 0 : rte_vfio_clear_group(vfio_group.fd);
1068 : 0 : return ret;
1069 : : }
1070 : 0 : container_device_fd = ret;
1071 : 0 : DPAA2_BUS_DEBUG("VFIO Container FD is [0x%X]",
1072 : : container_device_fd);
1073 : :
1074 : 0 : return 0;
1075 : : }
|