Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
3 : : */
4 : :
5 : : /*
6 : : * Architecture Overview
7 : : * =====================
8 : : * CDX is a Hardware Architecture designed for AMD FPGA devices. It
9 : : * consists of sophisticated mechanism for interaction between FPGA,
10 : : * Firmware and the APUs (Application CPUs).
11 : : *
12 : : * Firmware resides on RPU (Realtime CPUs) which interacts with
13 : : * the FPGA program manager and the APUs. The RPU provides memory-mapped
14 : : * interface (RPU if) which is used to communicate with APUs.
15 : : *
16 : : * The diagram below shows an overview of the AMD CDX architecture:
17 : : *
18 : : * +--------------------------------------+
19 : : * | DPDK |
20 : : * | DPDK CDX drivers |
21 : : * | | |
22 : : * | DPDK AMD CDX bus |
23 : : * | | |
24 : : * +-----------------------------|--------+
25 : : * |
26 : : * +-----------------------------|--------+
27 : : * | Application CPUs (APU) | |
28 : : * | | |
29 : : * | VFIO CDX driver |
30 : : * | Linux OS | |
31 : : * | Linux AMD CDX bus |
32 : : * | | |
33 : : * +-----------------------------|--------+
34 : : * |
35 : : * |
36 : : * +------------------------| RPU if |----+
37 : : * | | |
38 : : * | V |
39 : : * | Realtime CPUs (RPU) |
40 : : * | |
41 : : * +--------------------------------------+
42 : : * |
43 : : * +---------------------|----------------+
44 : : * | FPGA | |
45 : : * | +-----------------------+ |
46 : : * | | | | |
47 : : * | +-------+ +-------+ +-------+ |
48 : : * | | dev 1 | | dev 2 | | dev 3 | |
49 : : * | +-------+ +-------+ +-------+ |
50 : : * +--------------------------------------+
51 : : *
52 : : * The RPU firmware extracts the device information from the loaded FPGA
53 : : * image and implements a mechanism that allows the APU drivers to
54 : : * enumerate such devices (device personality and resource details) via
55 : : * a dedicated communication channel.
56 : : *
57 : : * VFIO CDX driver provides the CDX device resources like MMIO and interrupts
58 : : * to map to user-space. DPDK CDX bus uses sysfs interface and the vfio-cdx
59 : : * driver to discover and initialize the CDX devices for user-space
60 : : * applications.
61 : : */
62 : :
63 : : /**
64 : : * @file
65 : : * CDX probing using Linux sysfs.
66 : : */
67 : :
68 : : #include <string.h>
69 : : #include <dirent.h>
70 : :
71 : : #include <rte_eal_paging.h>
72 : : #include <rte_errno.h>
73 : : #include <rte_devargs.h>
74 : : #include <rte_kvargs.h>
75 : : #include <rte_malloc.h>
76 : : #include <rte_vfio.h>
77 : :
78 : : #include <eal_export.h>
79 : : #include <eal_filesystem.h>
80 : :
81 : : #include "bus_cdx_driver.h"
82 : : #include "cdx_logs.h"
83 : : #include "private.h"
84 : :
85 : : #define CDX_DEV_PREFIX "cdx-"
86 : :
87 : : /* CDX Bus iterators */
88 : : #define FOREACH_DEVICE_ON_CDXBUS(p) \
89 : : RTE_TAILQ_FOREACH(p, &rte_cdx_bus.device_list, next)
90 : :
91 : : #define FOREACH_DRIVER_ON_CDXBUS(p) \
92 : : RTE_TAILQ_FOREACH(p, &rte_cdx_bus.driver_list, next)
93 : :
94 : : struct rte_cdx_bus rte_cdx_bus;
95 : :
96 : : enum cdx_params {
97 : : RTE_CDX_PARAM_NAME,
98 : : };
99 : :
100 : : static const char * const cdx_params_keys[] = {
101 : : [RTE_CDX_PARAM_NAME] = "name",
102 : : NULL,
103 : : };
104 : :
105 : : /* Add a device to CDX bus */
106 : : static void
107 : : cdx_add_device(struct rte_cdx_device *cdx_dev)
108 : : {
109 : 0 : TAILQ_INSERT_TAIL(&rte_cdx_bus.device_list, cdx_dev, next);
110 : : }
111 : :
112 : : static int
113 : 0 : cdx_get_kernel_driver_by_path(const char *filename, char *driver_name,
114 : : size_t len)
115 : : {
116 : : int count;
117 : : char path[PATH_MAX];
118 : : char *name;
119 : :
120 [ # # ]: 0 : if (!filename || !driver_name)
121 : : return -1;
122 : :
123 : 0 : count = readlink(filename, path, PATH_MAX);
124 [ # # ]: 0 : if (count >= PATH_MAX)
125 : : return -1;
126 : :
127 : : /* For device does not have a driver */
128 [ # # ]: 0 : if (count < 0)
129 : : return 1;
130 : :
131 : 0 : path[count] = '\0';
132 : :
133 : 0 : name = strrchr(path, '/');
134 [ # # ]: 0 : if (name) {
135 : 0 : strlcpy(driver_name, name + 1, len);
136 : 0 : return 0;
137 : : }
138 : :
139 : : return -1;
140 : : }
141 : :
142 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_cdx_map_device)
143 : 0 : int rte_cdx_map_device(struct rte_cdx_device *dev)
144 : : {
145 : 0 : return cdx_vfio_map_resource(dev);
146 : : }
147 : :
148 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_cdx_unmap_device)
149 : 0 : void rte_cdx_unmap_device(struct rte_cdx_device *dev)
150 : : {
151 : 0 : cdx_vfio_unmap_resource(dev);
152 : 0 : }
153 : :
154 : : /*
155 : : * Scan one cdx sysfs entry, and fill the devices list from it.
156 : : * It checks if the CDX device is bound to vfio-cdx driver. In case
157 : : * the device is vfio bound, it reads the vendor and device id and
158 : : * stores it for device-driver matching.
159 : : */
160 : : static int
161 : 0 : cdx_scan_one(const char *dirname, const char *dev_name)
162 : : {
163 : : char filename[PATH_MAX];
164 : : struct rte_cdx_device *dev = NULL;
165 : : char driver[PATH_MAX];
166 : : unsigned long tmp;
167 : : int ret;
168 : :
169 : 0 : dev = calloc(1, sizeof(*dev));
170 [ # # ]: 0 : if (!dev)
171 : : return -ENOMEM;
172 : :
173 : 0 : dev->device.bus = &rte_cdx_bus.bus;
174 : 0 : memcpy(dev->name, dev_name, RTE_DEV_NAME_MAX_LEN);
175 : 0 : dev->device.name = dev->name;
176 : :
177 : : /* parse driver */
178 : : snprintf(filename, sizeof(filename), "%s/driver", dirname);
179 : 0 : ret = cdx_get_kernel_driver_by_path(filename, driver, sizeof(driver));
180 [ # # ]: 0 : if (ret < 0) {
181 : 0 : CDX_BUS_ERR("Fail to get kernel driver");
182 : 0 : free(dev);
183 : 0 : return -1;
184 : : }
185 : :
186 : : /* Allocate interrupt instance for cdx device */
187 : 0 : dev->intr_handle =
188 : 0 : rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
189 [ # # ]: 0 : if (dev->intr_handle == NULL) {
190 : 0 : CDX_BUS_ERR("Failed to create interrupt instance for %s",
191 : : dev->device.name);
192 : 0 : free(dev);
193 : 0 : return -ENOMEM;
194 : : }
195 : :
196 : : /*
197 : : * Check if device is bound to 'vfio-cdx' driver, so that user-space
198 : : * can gracefully access the device.
199 : : */
200 [ # # # # ]: 0 : if (ret || strcmp(driver, "vfio-cdx")) {
201 : : ret = 0;
202 : 0 : goto err;
203 : : }
204 : :
205 : : /* get vendor id */
206 : : snprintf(filename, sizeof(filename), "%s/vendor", dirname);
207 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0) {
208 : : ret = -1;
209 : 0 : goto err;
210 : : }
211 : 0 : dev->id.vendor_id = (uint16_t)tmp;
212 : :
213 : : /* get device id */
214 : : snprintf(filename, sizeof(filename), "%s/device", dirname);
215 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0) {
216 : : ret = -1;
217 : 0 : goto err;
218 : : }
219 : 0 : dev->id.device_id = (uint16_t)tmp;
220 : :
221 : : cdx_add_device(dev);
222 : :
223 : 0 : return 0;
224 : :
225 : 0 : err:
226 : 0 : rte_intr_instance_free(dev->intr_handle);
227 : 0 : free(dev);
228 : 0 : return ret;
229 : : }
230 : :
231 : : /*
232 : : * Scan the content of the CDX bus, and the devices in the devices
233 : : * list.
234 : : */
235 : : static int
236 : 216 : cdx_scan(void)
237 : : {
238 : : struct dirent *e;
239 : : DIR *dir;
240 : : char dirname[PATH_MAX];
241 : :
242 : 216 : dir = opendir(RTE_CDX_BUS_DEVICES_PATH);
243 [ + - ]: 216 : if (dir == NULL) {
244 : 216 : CDX_BUS_INFO("%s(): opendir failed: %s", __func__,
245 : : strerror(errno));
246 : 216 : return 0;
247 : : }
248 : :
249 [ # # ]: 0 : while ((e = readdir(dir)) != NULL) {
250 [ # # ]: 0 : if (e->d_name[0] == '.')
251 : 0 : continue;
252 : :
253 [ # # ]: 0 : if (rte_bus_device_is_ignored(&rte_cdx_bus.bus, e->d_name))
254 : 0 : continue;
255 : :
256 : : snprintf(dirname, sizeof(dirname), "%s/%s",
257 : : RTE_CDX_BUS_DEVICES_PATH, e->d_name);
258 : :
259 [ # # ]: 0 : if (cdx_scan_one(dirname, e->d_name) < 0)
260 : 0 : goto error;
261 : : }
262 : 0 : closedir(dir);
263 : 0 : return 0;
264 : :
265 : : error:
266 : 0 : closedir(dir);
267 : 0 : return -1;
268 : : }
269 : :
270 : : /* map a particular resource from a file */
271 : : void *
272 : 0 : cdx_map_resource(void *requested_addr, int fd, uint64_t offset, size_t size,
273 : : int additional_flags)
274 : : {
275 : : void *mapaddr;
276 : :
277 : : /* Map the cdx MMIO memory resource of device */
278 : 0 : mapaddr = rte_mem_map(requested_addr, size,
279 : : RTE_PROT_READ | RTE_PROT_WRITE,
280 : : RTE_MAP_SHARED | additional_flags, fd, offset);
281 [ # # ]: 0 : if (mapaddr == NULL) {
282 : 0 : CDX_BUS_ERR("%s(): cannot map resource(%d, %p, 0x%zx, 0x%"PRIx64"): %s (%p)",
283 : : __func__, fd, requested_addr, size, offset,
284 : : rte_strerror(rte_errno), mapaddr);
285 : : }
286 : 0 : CDX_BUS_DEBUG("CDX MMIO memory mapped at %p", mapaddr);
287 : :
288 : 0 : return mapaddr;
289 : : }
290 : :
291 : : /* unmap a particular resource */
292 : : void
293 : 0 : cdx_unmap_resource(void *requested_addr, size_t size)
294 : : {
295 [ # # ]: 0 : if (requested_addr == NULL)
296 : : return;
297 : :
298 : 0 : CDX_BUS_DEBUG("Unmapping CDX memory at %p", requested_addr);
299 : :
300 : : /* Unmap the CDX memory resource of device */
301 [ # # ]: 0 : if (rte_mem_unmap(requested_addr, size)) {
302 : 0 : CDX_BUS_ERR("%s(): cannot mem unmap(%p, %#zx): %s", __func__,
303 : : requested_addr, size, rte_strerror(rte_errno));
304 : : }
305 : : }
306 : : /*
307 : : * Match the CDX Driver and Device using device id and vendor id.
308 : : */
309 : : static bool
310 : : cdx_match(const struct rte_cdx_driver *cdx_drv,
311 : : const struct rte_cdx_device *cdx_dev)
312 : : {
313 : : const struct rte_cdx_id *id_table;
314 : :
315 [ # # ]: 0 : for (id_table = cdx_drv->id_table; id_table->vendor_id != 0;
316 : 0 : id_table++) {
317 : : /* check if device's identifiers match the driver's ones */
318 [ # # # # ]: 0 : if (id_table->vendor_id != cdx_dev->id.vendor_id &&
319 : : id_table->vendor_id != RTE_CDX_ANY_ID)
320 : 0 : continue;
321 [ # # # # ]: 0 : if (id_table->device_id != cdx_dev->id.device_id &&
322 : : id_table->device_id != RTE_CDX_ANY_ID)
323 : 0 : continue;
324 : :
325 : : return 1;
326 : : }
327 : :
328 : : return 0;
329 : : }
330 : :
331 : : /*
332 : : * If vendor id and device id match, call the probe() function of the
333 : : * driver.
334 : : */
335 : : static int
336 : 0 : cdx_probe_one_driver(struct rte_cdx_driver *dr,
337 : : struct rte_cdx_device *dev)
338 : : {
339 : 0 : const char *dev_name = dev->name;
340 : : bool already_probed;
341 : : int ret;
342 : :
343 : : /* The device is not blocked; Check if driver supports it */
344 [ # # ]: 0 : if (!cdx_match(dr, dev))
345 : : /* Match of device and driver failed */
346 : : return 1;
347 : :
348 : 0 : already_probed = rte_dev_is_probed(&dev->device);
349 [ # # ]: 0 : if (already_probed) {
350 : 0 : CDX_BUS_INFO("Device %s is already probed", dev_name);
351 : 0 : return -EEXIST;
352 : : }
353 : :
354 : 0 : CDX_BUS_DEBUG(" probe device %s using driver: %s", dev_name,
355 : : dr->driver.name);
356 : :
357 [ # # ]: 0 : if (dr->drv_flags & RTE_CDX_DRV_NEED_MAPPING) {
358 : 0 : ret = cdx_vfio_map_resource(dev);
359 [ # # ]: 0 : if (ret != 0) {
360 : 0 : CDX_BUS_ERR("CDX map device failed: %d", ret);
361 : 0 : goto error_map_device;
362 : : }
363 : : }
364 : :
365 : : /* call the driver probe() function */
366 : 0 : ret = dr->probe(dr, dev);
367 [ # # ]: 0 : if (ret) {
368 : 0 : CDX_BUS_ERR("Probe CDX driver: %s device: %s failed: %d",
369 : : dr->driver.name, dev_name, ret);
370 : 0 : goto error_probe;
371 : : } else {
372 : 0 : dev->device.driver = &dr->driver;
373 : : }
374 : 0 : dev->driver = dr;
375 : :
376 : 0 : return ret;
377 : :
378 : : error_probe:
379 : 0 : cdx_vfio_unmap_resource(dev);
380 : 0 : rte_intr_instance_free(dev->intr_handle);
381 : 0 : dev->intr_handle = NULL;
382 : : error_map_device:
383 : : return ret;
384 : : }
385 : :
386 : : /*
387 : : * If vendor/device ID match, call the probe() function of all
388 : : * registered driver for the given device. Return < 0 if initialization
389 : : * failed, return 1 if no driver is found for this device.
390 : : */
391 : : static int
392 : 0 : cdx_probe_all_drivers(struct rte_cdx_device *dev)
393 : : {
394 : : struct rte_cdx_driver *dr = NULL;
395 : : int rc = 0;
396 : :
397 [ # # ]: 0 : FOREACH_DRIVER_ON_CDXBUS(dr) {
398 : 0 : rc = cdx_probe_one_driver(dr, dev);
399 [ # # ]: 0 : if (rc < 0)
400 : : /* negative value is an error */
401 : 0 : return rc;
402 [ # # ]: 0 : if (rc > 0)
403 : : /* positive value means driver doesn't support it */
404 : : continue;
405 : : return 0;
406 : : }
407 : : return 1;
408 : : }
409 : :
410 : : /*
411 : : * Scan the content of the CDX bus, and call the probe() function for
412 : : * all registered drivers that have a matching entry in its id_table
413 : : * for discovered devices.
414 : : */
415 : : static int
416 : 211 : cdx_probe(void)
417 : : {
418 : : struct rte_cdx_device *dev = NULL;
419 : : size_t probed = 0, failed = 0;
420 : : int ret = 0;
421 : :
422 [ - + ]: 211 : FOREACH_DEVICE_ON_CDXBUS(dev) {
423 : 0 : probed++;
424 : :
425 : 0 : ret = cdx_probe_all_drivers(dev);
426 [ # # ]: 0 : if (ret < 0) {
427 : 0 : CDX_BUS_ERR("Requested device %s cannot be used",
428 : : dev->name);
429 : 0 : rte_errno = errno;
430 : 0 : failed++;
431 : : }
432 : : }
433 : :
434 [ + - ]: 211 : return (probed && probed == failed) ? -1 : 0;
435 : : }
436 : :
437 : : static int
438 : 42 : cdx_parse(const char *name, void *addr)
439 : : {
440 : : const char **out = addr;
441 : : int ret;
442 : :
443 : 42 : ret = strncmp(name, CDX_DEV_PREFIX, strlen(CDX_DEV_PREFIX));
444 : :
445 [ - + ]: 42 : if (ret == 0 && addr)
446 : 0 : *out = name;
447 : :
448 : 42 : return ret;
449 : : }
450 : :
451 : : /* register a driver */
452 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_cdx_register)
453 : : void
454 : 0 : rte_cdx_register(struct rte_cdx_driver *driver)
455 : : {
456 : 0 : TAILQ_INSERT_TAIL(&rte_cdx_bus.driver_list, driver, next);
457 : 0 : driver->bus = &rte_cdx_bus;
458 : 0 : }
459 : :
460 : : /* unregister a driver */
461 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_cdx_unregister)
462 : : void
463 : 0 : rte_cdx_unregister(struct rte_cdx_driver *driver)
464 : : {
465 [ # # ]: 0 : TAILQ_REMOVE(&rte_cdx_bus.driver_list, driver, next);
466 : 0 : driver->bus = NULL;
467 : 0 : }
468 : :
469 : : static struct rte_device *
470 : 0 : cdx_find_device(const struct rte_device *start, rte_dev_cmp_t cmp,
471 : : const void *data)
472 : : {
473 : : const struct rte_cdx_device *cdx_start;
474 : : struct rte_cdx_device *cdx_dev;
475 : :
476 [ # # ]: 0 : if (start != NULL) {
477 : 0 : cdx_start = RTE_DEV_TO_CDX_DEV_CONST(start);
478 : 0 : cdx_dev = TAILQ_NEXT(cdx_start, next);
479 : : } else {
480 : 0 : cdx_dev = TAILQ_FIRST(&rte_cdx_bus.device_list);
481 : : }
482 [ # # ]: 0 : while (cdx_dev != NULL) {
483 [ # # ]: 0 : if (cmp(&cdx_dev->device, data) == 0)
484 : 0 : return &cdx_dev->device;
485 : 0 : cdx_dev = TAILQ_NEXT(cdx_dev, next);
486 : : }
487 : : return NULL;
488 : : }
489 : :
490 : : /* Remove a device from CDX bus */
491 : : static void
492 : : cdx_remove_device(struct rte_cdx_device *cdx_dev)
493 : : {
494 [ # # ]: 0 : TAILQ_REMOVE(&rte_cdx_bus.device_list, cdx_dev, next);
495 : : }
496 : :
497 : : /*
498 : : * If vendor/device ID match, call the remove() function of the
499 : : * driver.
500 : : */
501 : : static int
502 : 0 : cdx_detach_dev(struct rte_cdx_device *dev)
503 : : {
504 : : struct rte_cdx_driver *dr;
505 : : int ret = 0;
506 : :
507 [ # # ]: 0 : if (dev == NULL)
508 : : return -EINVAL;
509 : :
510 : 0 : dr = dev->driver;
511 : :
512 : 0 : CDX_BUS_DEBUG("detach device %s using driver: %s",
513 : : dev->device.name, dr->driver.name);
514 : :
515 [ # # ]: 0 : if (dr->remove) {
516 : 0 : ret = dr->remove(dev);
517 [ # # ]: 0 : if (ret < 0)
518 : : return ret;
519 : : }
520 : :
521 : : /* clear driver structure */
522 : 0 : dev->driver = NULL;
523 : 0 : dev->device.driver = NULL;
524 : :
525 : 0 : rte_cdx_unmap_device(dev);
526 : :
527 : 0 : rte_intr_instance_free(dev->intr_handle);
528 : 0 : dev->intr_handle = NULL;
529 : :
530 : 0 : return 0;
531 : : }
532 : :
533 : : static int
534 : 0 : cdx_plug(struct rte_device *dev)
535 : : {
536 : 0 : return cdx_probe_all_drivers(RTE_DEV_TO_CDX_DEV(dev));
537 : : }
538 : :
539 : : static int
540 : 0 : cdx_unplug(struct rte_device *dev)
541 : : {
542 : : struct rte_cdx_device *cdx_dev;
543 : : int ret;
544 : :
545 : 0 : cdx_dev = RTE_DEV_TO_CDX_DEV(dev);
546 : 0 : ret = cdx_detach_dev(cdx_dev);
547 [ # # ]: 0 : if (ret == 0) {
548 : : cdx_remove_device(cdx_dev);
549 : 0 : rte_devargs_remove(dev->devargs);
550 : 0 : free(cdx_dev);
551 : : }
552 : 0 : return ret;
553 : : }
554 : :
555 : : static int
556 : 0 : cdx_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
557 : : {
558 : : RTE_SET_USED(dev);
559 : :
560 : 0 : return rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD,
561 : : (uintptr_t)addr, iova, len);
562 : : }
563 : :
564 : : static int
565 : 0 : cdx_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
566 : : {
567 : : RTE_SET_USED(dev);
568 : :
569 : 0 : return rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD,
570 : : (uintptr_t)addr, iova, len);
571 : : }
572 : :
573 : : static enum rte_iova_mode
574 : 216 : cdx_get_iommu_class(void)
575 : : {
576 [ + - ]: 216 : if (TAILQ_EMPTY(&rte_cdx_bus.device_list))
577 : 216 : return RTE_IOVA_DC;
578 : :
579 : : return RTE_IOVA_VA;
580 : : }
581 : :
582 : : static int
583 : 0 : cdx_dev_match(const struct rte_device *dev,
584 : : const void *_kvlist)
585 : : {
586 : : const struct rte_kvargs *kvlist = _kvlist;
587 : : const char *key = cdx_params_keys[RTE_CDX_PARAM_NAME];
588 : : const char *name;
589 : :
590 : : /* no kvlist arg, all devices match */
591 [ # # ]: 0 : if (kvlist == NULL)
592 : : return 0;
593 : :
594 : : /* if key is present in kvlist and does not match, filter device */
595 : 0 : name = rte_kvargs_get(kvlist, key);
596 [ # # # # ]: 0 : if (name != NULL && strcmp(name, dev->name))
597 : 0 : return -1;
598 : :
599 : : return 0;
600 : : }
601 : :
602 : : static void *
603 : 0 : cdx_dev_iterate(const void *start,
604 : : const char *str,
605 : : const struct rte_dev_iterator *it __rte_unused)
606 : : {
607 : : rte_bus_find_device_t find_device;
608 : : struct rte_kvargs *kvargs = NULL;
609 : : struct rte_device *dev;
610 : :
611 [ # # ]: 0 : if (str != NULL) {
612 : 0 : kvargs = rte_kvargs_parse(str, cdx_params_keys);
613 [ # # ]: 0 : if (kvargs == NULL) {
614 : 0 : CDX_BUS_ERR("cannot parse argument list %s", str);
615 : 0 : rte_errno = EINVAL;
616 : 0 : return NULL;
617 : : }
618 : : }
619 : 0 : find_device = rte_cdx_bus.bus.find_device;
620 : 0 : dev = find_device(start, cdx_dev_match, kvargs);
621 : 0 : rte_kvargs_free(kvargs);
622 : 0 : return dev;
623 : : }
624 : :
625 : : struct rte_cdx_bus rte_cdx_bus = {
626 : : .bus = {
627 : : .scan = cdx_scan,
628 : : .probe = cdx_probe,
629 : : .find_device = cdx_find_device,
630 : : .plug = cdx_plug,
631 : : .unplug = cdx_unplug,
632 : : .parse = cdx_parse,
633 : : .dma_map = cdx_dma_map,
634 : : .dma_unmap = cdx_dma_unmap,
635 : : .get_iommu_class = cdx_get_iommu_class,
636 : : .dev_iterate = cdx_dev_iterate,
637 : : },
638 : : .device_list = TAILQ_HEAD_INITIALIZER(rte_cdx_bus.device_list),
639 : : .driver_list = TAILQ_HEAD_INITIALIZER(rte_cdx_bus.driver_list),
640 : : };
641 : :
642 : 284 : RTE_REGISTER_BUS(cdx, rte_cdx_bus.bus);
643 [ - + ]: 284 : RTE_LOG_REGISTER_DEFAULT(cdx_logtype_bus, NOTICE);
|