type
Post
status
Published
slug
2025/01/24/The learning of DPDK driver architecture ——pci bus
summary
tags
DPDK
category
DPDK
icon
password
new update day
Property
Jan 24, 2025 08:13 AM
created days
Last edited time
Jan 24, 2025 08:13 AM
最新有需要更新驱动架构,一开始不知道怎么编写对应的驱动接口与整体的驱动架构。于是就分析了一下 DPDK 的驱动架构。
DPDK 的驱动有3个层级,第一层为 bus ,总线层,第二层为 driver 层,第三层为 device 层设备层。
其中 bus 层里面包含了 driver 与 device 两个链表。
下面是 pci 总线的一个结构体定义。
  • dpdk-24.11.1\dpdk-stable-24.11.1\drivers\bus\pci\pci_common.c
struct rte_pci_bus rte_pci_bus = { .bus = { .scan = rte_pci_scan, .probe = pci_probe, .cleanup = pci_cleanup, .find_device = pci_find_device, .plug = pci_plug, .unplug = pci_unplug, .parse = pci_parse, .devargs_parse = rte_pci_devargs_parse, .dma_map = pci_dma_map, .dma_unmap = pci_dma_unmap, .get_iommu_class = rte_pci_get_iommu_class, .dev_iterate = rte_pci_dev_iterate, .hot_unplug_handler = pci_hot_unplug_handler, .sigbus_handler = pci_sigbus_handler, }, .device_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.device_list), .driver_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.driver_list), }; RTE_REGISTER_BUS(pci, rte_pci_bus.bus); RTE_LOG_REGISTER_DEFAULT(pci_bus_logtype, NOTICE);
可以使用下面这个文件中的接口,将驱动何设备注册进对应的 pci 总线上,以及对应的
  • dpdk-24.11.1\dpdk-stable-24.11.1\drivers\bus\pci\pci_common.c
/* register a driver */ void rte_pci_register(struct rte_pci_driver *driver) { TAILQ_INSERT_TAIL(&rte_pci_bus.driver_list, driver, next); } /* unregister a driver */ void rte_pci_unregister(struct rte_pci_driver *driver) { TAILQ_REMOVE(&rte_pci_bus.driver_list, driver, next); } /* Add a device to PCI bus */ void rte_pci_add_device(struct rte_pci_device *pci_dev) { TAILQ_INSERT_TAIL(&rte_pci_bus.device_list, pci_dev, next); } /* Insert a device into a predefined position in PCI bus */ void rte_pci_insert_device(struct rte_pci_device *exist_pci_dev, struct rte_pci_device *new_pci_dev) { TAILQ_INSERT_BEFORE(exist_pci_dev, new_pci_dev, next); } /* Remove a device from PCI bus */ static void rte_pci_remove_device(struct rte_pci_device *pci_dev) { TAILQ_REMOVE(&rte_pci_bus.device_list, pci_dev, next); }

具体的流程

rte_pci_scan

  1. rte_pci_scan()
    1. 扫描 sysfs path,对不同的 pcie addr 目录,使用 parse_pci_addr_format 函数进行设备 domain,bus,devid,function进行解析。
    2. 调用 pci_scan_one() 进行解析。然后填充设备列表。
      1. 分配对应得内存,填充对应的 vendor_id、device_id、等信息。
      2. 将 device 设备添加到 rte_pci_bus.device_list (经过排序的链表)
        1. 如果列表为空
          1. 直接插入
        2. 循环遍历 rte_pci_bus.device_list
          1. 使用 rte_pci_addr_cmp(&dev->addr, &dev2->addr); 判断应该插入的位置。从小到大进行排序。
          2. 如果设备已经注册了(addr一致的情况)。(加入对应的 bus 的设备列表中就代表注册过了。)
            1. 如果没有 probe 过。(driver 指针是否为空,如果为空的话就是没有 probe 过)
              1. 将对应的数据存储在
              if (!rte_dev_is_probed(&dev2->device)) { dev2->kdrv = dev->kdrv; dev2->max_vfs = dev->max_vfs; dev2->id = dev->id; pci_common_set(dev2);//设置对应的 name,解析devargs。设置 bus_info。 memmove(dev2->mem_resource, dev->mem_resource, sizeof(dev->mem_resource)); }
              如果设备已经插入,而且驱动已经 probe 。(这个情况会发生在 重复调用 rte_dev_probe 函数的时候,这个时候不需要做什么。除非驱动地址不一致,max_vfs 不一致,或者 dev_id 不一致。)
              /** * If device is plugged and driver is * probed already, (This happens when * we call rte_dev_probe which will * scan all device on the bus) we don't * need to do anything here unless... **/ if (dev2->kdrv != dev->kdrv || dev2->max_vfs != dev->max_vfs || memcmp(&dev2->id, &dev->id, sizeof(dev2->id))) /* * This should not happens. * But it is still possible if * we unbind a device from * vfio or uio before hotplug * remove and rebind it with * a different configure. * So we just print out the * error as an alarm. */ PCI_LOG(ERR, "Unexpected device scan at %s!", filename);
              如果没有 probe 过。(driver 指针是否为空,如果为空的话就是没有 probe 过)
              1. 将对应的数据存储在
              if (!rte_dev_is_probed(&dev2->device)) { dev2->kdrv = dev->kdrv; dev2->max_vfs = dev->max_vfs; dev2->id = dev->id; pci_common_set(dev2);//设置对应的 name,解析devargs。设置 bus_info。 memmove(dev2->mem_resource, dev->mem_resource, sizeof(dev->mem_resource)); }
              如果设备参数不一致,重新设置对应的参数
              if (dev2->device.devargs != dev->device.devargs) { rte_devargs_remove(dev2->device.devargs); pci_common_set(dev2); }
          3. 如果该设备在链表中已经存在,释放分配后的内存
        3. 将该设备添加到设备链表中。
    3. 遍历完目录后之后关闭对应的 dir

pci_probe

调用 probe ,循环遍历所有的设备,对于每个设备都调用 pci_probe_all_drivers 函数去匹配驱动。 对于每一个驱动调用 rte_pci_probe_one_driver 函数,循环使用每一个驱动匹配对应的设备。
/* * If vendor/device ID match, call the probe() function of all * registered driver for the given device. Return < 0 if initialization * failed, return 1 if no driver is found for this device. */ static int pci_probe_all_drivers(struct rte_pci_device *dev) { struct rte_pci_driver *dr = NULL; int rc = 0; if (dev == NULL) return -EINVAL; FOREACH_DRIVER_ON_PCIBUS(dr) { rc = rte_pci_probe_one_driver(dr, dev); if (rc < 0) /* negative value is an error */ return rc; if (rc > 0) /* positive value means driver doesn't support it */ continue; return 0; } return 1; } /* * Scan the content of the PCI bus, and call the probe() function for * all registered drivers that have a matching entry in its id_table * for discovered devices. */ static int pci_probe(void) { struct rte_pci_device *dev = NULL; size_t probed = 0, failed = 0; int ret = 0; FOREACH_DEVICE_ON_PCIBUS(dev) { probed++; ret = pci_probe_all_drivers(dev); if (ret < 0) { if (ret != -EEXIST) { PCI_LOG(ERR, "Requested device " PCI_PRI_FMT " cannot be used", dev->addr.domain, dev->addr.bus, dev->addr.devid, dev->addr.function); rte_errno = errno; failed++; } ret = 0; } } return (probed && probed == failed) ? -1 : 0; }

rte_pci_probe_one_driver

先使用 rte_pci_match 函数,进行匹配,如果 vendor/device ID match 匹配,调用 probe() 驱动的 probe() 函数。
/* * Match the PCI Driver and Device using the ID Table */ int rte_pci_match(const struct rte_pci_driver *pci_drv, const struct rte_pci_device *pci_dev) { const struct rte_pci_id *id_table; for (id_table = pci_drv->id_table; id_table->vendor_id != 0; id_table++) { /* check if device's identifiers match the driver's ones */ if (id_table->vendor_id != pci_dev->id.vendor_id && id_table->vendor_id != RTE_PCI_ANY_ID) continue; if (id_table->device_id != pci_dev->id.device_id && id_table->device_id != RTE_PCI_ANY_ID) continue; if (id_table->subsystem_vendor_id != pci_dev->id.subsystem_vendor_id && id_table->subsystem_vendor_id != RTE_PCI_ANY_ID) continue; if (id_table->subsystem_device_id != pci_dev->id.subsystem_device_id && id_table->subsystem_device_id != RTE_PCI_ANY_ID) continue; if (id_table->class_id != pci_dev->id.class_id && id_table->class_id != RTE_CLASS_ANY_ID) continue; return 1; } return 0; }
检查设备是否 probe 过。如果没有的话,获取设备的 iova_mode ,分配中断pci设备的中断实例。
引用驱动结构体。在 rte_pci_map_device 映射设备之前,配置驱动结构体,可以使用驱动标志配置配置。
如果驱动标志中 RTE_PCI_DRV_NEED_MAPPING ,则调用 rte_pci_map_device 进行 map。
调用 驱动的 probe 函数。如果已经 probed ,直接返回。如果出错,进行出错处理。设置对应设备的驱动。probe 之后,对应的 driver 不为 NULL。
/* * If vendor/device ID match, call the probe() function of the * driver. */ static int rte_pci_probe_one_driver(struct rte_pci_driver *dr, struct rte_pci_device *dev) { int ret; bool already_probed; struct rte_pci_addr *loc; if ((dr == NULL) || (dev == NULL)) return -EINVAL; loc = &dev->addr; /* The device is not blocked; Check if driver supports it */ if (!rte_pci_match(dr, dev)) /* Match of device and driver failed */ return 1; PCI_LOG(DEBUG, "PCI device "PCI_PRI_FMT" on NUMA socket %i", loc->domain, loc->bus, loc->devid, loc->function, dev->device.numa_node); /* no initialization when marked as blocked, return without error */ if (dev->device.devargs != NULL && dev->device.devargs->policy == RTE_DEV_BLOCKED) { PCI_LOG(INFO, " Device is blocked, not initializing"); return 1; } if (dev->device.numa_node < 0 && rte_socket_count() > 1) PCI_LOG(INFO, "Device %s is not NUMA-aware", dev->name); already_probed = rte_dev_is_probed(&dev->device); if (already_probed && !(dr->drv_flags & RTE_PCI_DRV_PROBE_AGAIN)) { PCI_LOG(DEBUG, "Device %s is already probed", dev->device.name); return -EEXIST; } PCI_LOG(DEBUG, " probe driver: %x:%x %s", dev->id.vendor_id, dev->id.device_id, dr->driver.name); if (!already_probed) { enum rte_iova_mode dev_iova_mode; enum rte_iova_mode iova_mode; dev_iova_mode = pci_device_iova_mode(dr, dev); iova_mode = rte_eal_iova_mode(); if (dev_iova_mode != RTE_IOVA_DC && dev_iova_mode != iova_mode) { PCI_LOG(ERR, " Expecting '%s' IOVA mode but current mode is '%s', not initializing", dev_iova_mode == RTE_IOVA_PA ? "PA" : "VA", iova_mode == RTE_IOVA_PA ? "PA" : "VA"); return -EINVAL; } /* Allocate interrupt instance for pci device */ dev->intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE); if (dev->intr_handle == NULL) { PCI_LOG(ERR, "Failed to create interrupt instance for %s", dev->device.name); return -ENOMEM; } dev->vfio_req_intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE); if (dev->vfio_req_intr_handle == NULL) { rte_intr_instance_free(dev->intr_handle); dev->intr_handle = NULL; PCI_LOG(ERR, "Failed to create vfio req interrupt instance for %s", dev->device.name); return -ENOMEM; } /* * Reference driver structure. * This needs to be before rte_pci_map_device(), as it enables * to use driver flags for adjusting configuration. */ dev->driver = dr; if (dev->driver->drv_flags & RTE_PCI_DRV_NEED_MAPPING) { ret = rte_pci_map_device(dev); if (ret != 0) { dev->driver = NULL; rte_intr_instance_free(dev->vfio_req_intr_handle); dev->vfio_req_intr_handle = NULL; rte_intr_instance_free(dev->intr_handle); dev->intr_handle = NULL; return ret; } } } PCI_LOG(INFO, "Probe PCI driver: %s (%x:%04x) device: "PCI_PRI_FMT" (socket %i)", dr->driver.name, dev->id.vendor_id, dev->id.device_id, loc->domain, loc->bus, loc->devid, loc->function, dev->device.numa_node); /* call the driver probe() function */ ret = dr->probe(dr, dev); if (already_probed) return ret; /* no rollback if already succeeded earlier */ if (ret) { dev->driver = NULL; if ((dr->drv_flags & RTE_PCI_DRV_NEED_MAPPING) && /* Don't unmap if device is unsupported and * driver needs mapped resources. */ !(ret > 0 && (dr->drv_flags & RTE_PCI_DRV_KEEP_MAPPED_RES))) rte_pci_unmap_device(dev); rte_intr_instance_free(dev->vfio_req_intr_handle); dev->vfio_req_intr_handle = NULL; rte_intr_instance_free(dev->intr_handle); dev->intr_handle = NULL; } else { dev->device.driver = &dr->driver; } return ret; }

.cleanup = pci_cleanup

进行分配的内存的清理。
static int pci_cleanup(void) { struct rte_pci_device *dev, *tmp_dev; int error = 0; RTE_TAILQ_FOREACH_SAFE(dev, &rte_pci_bus.device_list, next, tmp_dev) { struct rte_pci_driver *drv = dev->driver; int ret = 0; if (drv == NULL || drv->remove == NULL) goto free; ret = drv->remove(dev); if (ret < 0) { rte_errno = errno; error = -1; } dev->driver = NULL; dev->device.driver = NULL; free: /* free interrupt handles */ rte_intr_instance_free(dev->intr_handle); dev->intr_handle = NULL; rte_intr_instance_free(dev->vfio_req_intr_handle); dev->vfio_req_intr_handle = NULL; pci_free(RTE_PCI_DEVICE_INTERNAL(dev)); } return error; }

.find_device = pci_find_device

循环遍历 bus 的 device 列表,使用 cmp 函数,进行查找。如果 cmp 函数返回 0 ,代表找到了。
static struct rte_device * pci_find_device(const struct rte_device *start, rte_dev_cmp_t cmp, const void *data) { const struct rte_pci_device *pstart; struct rte_pci_device *pdev; if (start != NULL) { pstart = RTE_DEV_TO_PCI_CONST(start); pdev = TAILQ_NEXT(pstart, next); } else { pdev = TAILQ_FIRST(&rte_pci_bus.device_list); } while (pdev != NULL) { if (cmp(&pdev->device, data) == 0) return &pdev->device; pdev = TAILQ_NEXT(pdev, next); } return NULL; }

.plug = pci_plug

plug 的时候,调用 pci_probe_all_drivers,
static int pci_plug(struct rte_device *dev) { return pci_probe_all_drivers(RTE_DEV_TO_PCI(dev)); }

.unplug = pci_unplug

当设备 unplug 的时候,先调用 detach 函数,把设备 detach 掉。
 
/* * If vendor/device ID match, call the remove() function of the * driver. * 该注释说明函数的主要功能:当设备的厂商ID和设备ID匹配时,调用驱动程序的remove()函数,用于分离PCI设备。 */ // 定义一个静态函数rte_pci_detach_dev,用于分离PCI设备 // 函数接收一个指向rte_pci_device结构体的指针作为参数,返回一个整数值表示操作结果 static int rte_pci_detach_dev(struct rte_pci_device *dev) { // 定义一个指向rte_pci_addr结构体的指针loc,用于存储PCI设备的地址信息 struct rte_pci_addr *loc; // 定义一个指向rte_pci_driver结构体的指针dr,用于存储PCI设备的驱动程序信息 struct rte_pci_driver *dr; // 定义一个整数变量ret,用于存储函数的返回值,初始化为0 int ret = 0; // 检查传入的dev指针是否为NULL // 如果为NULL,说明传入的参数无效,返回错误码-EINVAL if (dev == NULL) return -EINVAL; // 将dev结构体中的driver成员赋值给dr指针,获取该PCI设备的驱动程序 dr = dev->driver; // 将dev结构体中的addr成员的地址赋值给loc指针,获取该PCI设备的地址信息 loc = &dev->addr; // 打印调试信息,输出PCI设备的详细信息,包括设备的域、总线、设备ID、功能号以及所在的NUMA节点 PCI_LOG(DEBUG, "PCI device "PCI_PRI_FMT" on NUMA socket %i", loc->domain, loc->bus, loc->devid, loc->function, dev->device.numa_node); // 打印调试信息,输出要移除的驱动程序的信息,包括设备的厂商ID、设备ID以及驱动程序的名称 PCI_LOG(DEBUG, " remove driver: %x:%x %s", dev->id.vendor_id, dev->id.device_id, dr->driver.name); // 检查驱动程序的remove函数指针是否不为NULL // 如果不为NULL,说明驱动程序提供了remove函数,可以调用该函数来移除设备 if (dr->remove) { // 调用驱动程序的remove函数,传入当前PCI设备的指针,并将返回值赋给ret ret = dr->remove(dev); // 检查remove函数的返回值是否小于0 // 如果小于0,说明移除设备时发生了错误,直接返回该错误码 if (ret < 0) return ret; } /* clear driver structure */ // 清空设备的驱动程序信息,将dev结构体中的driver成员和device结构体中的driver成员都置为NULL dev->driver = NULL; dev->device.driver = NULL; // 检查驱动程序的标志位drv_flags是否包含RTE_PCI_DRV_NEED_MAPPING标志 // 如果包含该标志,说明设备使用了igb_uio,需要对其资源进行解映射操作 if (dr->drv_flags & RTE_PCI_DRV_NEED_MAPPING) /* unmap resources for devices that use igb_uio */ // 调用rte_pci_unmap_device函数对设备的资源进行解映射操作 rte_pci_unmap_device(dev); // 释放设备的中断处理实例,将其占用的资源归还给系统 rte_intr_instance_free(dev->intr_handle); // 将设备的中断处理句柄置为NULL,避免悬空指针 dev->intr_handle = NULL; // 释放设备的VFIO请求中断处理实例,将其占用的资源归还给系统 rte_intr_instance_free(dev->vfio_req_intr_handle); // 将设备的VFIO请求中断处理句柄置为NULL,避免悬空指针 dev->vfio_req_intr_handle = NULL; // 操作成功,返回0表示正常结束 return 0; } static int pci_unplug(struct rte_device *dev) { struct rte_pci_device *pdev; int ret; pdev = RTE_DEV_TO_PCI(dev); ret = rte_pci_detach_dev(pdev); if (ret == 0) { rte_pci_remove_device(pdev); rte_devargs_remove(dev->devargs); pci_free(RTE_PCI_DEVICE_INTERNAL(pdev)); } return ret; }

.parse = pci_parse

/** * A structure describing the location of a PCI device. * 该结构体用于描述PCI设备的位置信息。 */ // 定义rte_pci_addr结构体,用于存储PCI设备的位置信息 struct rte_pci_addr { uint32_t domain; /**< Device domain */ // 表示PCI设备的域,使用32位无符号整数存储 uint8_t bus; /**< Device bus */ // 表示PCI设备所在的总线,使用8位无符号整数存储 uint8_t devid; /**< Device ID */ // 表示PCI设备的ID,使用8位无符号整数存储 uint8_t function; /**< Device function. */ // 表示PCI设备的功能号,使用8位无符号整数存储 }; // 定义一个静态内联函数get_u8_pciaddr_field,用于从输入字符串中解析出一个8位无符号整数 // in:输入的字符串 // _u8:用于存储解析结果的指针 // dlm:分隔符 static inline const char * get_u8_pciaddr_field(const char *in, void *_u8, char dlm) { unsigned long val; // 用于临时存储解析出的无符号长整型值 uint8_t *u8 = _u8; // 将传入的指针转换为指向8位无符号整数的指针 char *end; // 用于存储strtoul函数解析结束后的位置 /* empty string is an error though strtoul() returns 0 */ // 如果输入字符串为空,直接返回NULL表示解析失败 if (*in == '\0') return NULL; /* PCI field starting with spaces is forbidden. * Negative wrap-around is not reported as an error by strtoul. */ // 如果输入字符串以空格或负号开头,返回NULL表示解析失败 if (*in == ' ' || *in == '-') return NULL; errno = 0; // 重置errno为0,用于检查strtoul函数是否出错 val = strtoul(in, &end, 16); // 使用strtoul函数将输入字符串从16进制解析为无符号长整型值 // 如果解析过程中出现错误,或者解析结束后的字符不是分隔符,或者解析的值超过了8位无符号整数的最大值 if (errno != 0 || end[0] != dlm || val > UINT8_MAX) { errno = errno ? errno : EINVAL; // 如果errno为0,将其设置为EINVAL表示无效参数 return NULL; } *u8 = (uint8_t)val; // 将解析出的值转换为8位无符号整数并存储到传入的指针所指向的位置 return end + 1; // 返回分隔符之后的字符串指针 } // 定义一个静态函数pci_bdf_parse,用于解析格式为 "bus:devid.function" 的PCI设备地址字符串 // input:输入的PCI设备地址字符串 // dev_addr:用于存储解析结果的rte_pci_addr结构体指针 static int pci_bdf_parse(const char *input, struct rte_pci_addr *dev_addr) { const char *in = input; // 保存输入字符串的指针 dev_addr->domain = 0; // 将PCI设备的域初始化为0 in = get_u8_pciaddr_field(in, &dev_addr->bus, ':'); // 解析总线号,如果解析失败,返回-EINVAL if (in == NULL) return -EINVAL; in = get_u8_pciaddr_field(in, &dev_addr->devid, '.'); // 解析设备ID,如果解析失败,返回-EINVAL if (in == NULL) return -EINVAL; in = get_u8_pciaddr_field(in, &dev_addr->function, '\0'); // 解析功能号,如果解析失败,返回-EINVAL if (in == NULL) return -EINVAL; return 0; // 解析成功,返回0 } // 定义一个静态函数pci_dbdf_parse,用于解析格式为 "domain:bus:devid.function" 的PCI设备地址字符串 // input:输入的PCI设备地址字符串 // dev_addr:用于存储解析结果的rte_pci_addr结构体指针 static int pci_dbdf_parse(const char *input, struct rte_pci_addr *dev_addr) { const char *in = input; // 保存输入字符串的指针 unsigned long val; // 用于临时存储解析出的无符号长整型值 char *end; // 用于存储strtoul函数解析结束后的位置 /* PCI id starting with spaces is forbidden. * Negative wrap-around is not reported as an error by strtoul. */ // 如果输入字符串以空格或负号开头,返回-EINVAL表示解析失败 if (*in == ' ' || *in == '-') return -EINVAL; errno = 0; // 重置errno为0,用于检查strtoul函数是否出错 val = strtoul(in, &end, 16); // 使用strtoul函数将输入字符串从16进制解析为无符号长整型值 /* Empty string is not an error for strtoul, but the check * end[0] != ':' * will detect the issue. */ // 如果解析过程中出现错误,或者解析结束后的字符不是冒号,或者解析的值超过了32位无符号整数的最大值 if (errno != 0 || end[0] != ':' || val > UINT32_MAX) return -EINVAL; dev_addr->domain = (uint32_t)val; // 将解析出的域值转换为32位无符号整数并存储到dev_addr结构体中 in = end + 1; // 更新输入字符串指针到冒号之后 in = get_u8_pciaddr_field(in, &dev_addr->bus, ':'); // 解析总线号,如果解析失败,返回-EINVAL if (in == NULL) return -EINVAL; in = get_u8_pciaddr_field(in, &dev_addr->devid, '.'); // 解析设备ID,如果解析失败,返回-EINVAL if (in == NULL) return -EINVAL; in = get_u8_pciaddr_field(in, &dev_addr->function, '\0'); // 解析功能号,如果解析失败,返回-EINVAL if (in == NULL) return -EINVAL; return 0; // 解析成功,返回0 } // 定义一个函数rte_pci_addr_parse,用于解析PCI设备地址字符串 // str:输入的PCI设备地址字符串 // addr:用于存储解析结果的rte_pci_addr结构体指针 int rte_pci_addr_parse(const char *str, struct rte_pci_addr *addr) { // 先尝试使用pci_bdf_parse函数解析,如果解析成功或者使用pci_dbdf_parse函数解析成功 if (pci_bdf_parse(str, addr) == 0 || pci_dbdf_parse(str, addr) == 0) return 0; return -1; // 两种解析方式都失败,返回-1 } // 定义一个静态函数pci_parse,用于解析PCI设备地址字符串并将结果存储到指定位置 // name:输入的PCI设备地址字符串 // addr:用于存储解析结果的指针 static int pci_parse(const char *name, void *addr) { struct rte_pci_addr *out = addr; // 将传入的指针转换为指向rte_pci_addr结构体的指针 struct rte_pci_addr pci_addr; // 临时存储解析结果的rte_pci_addr结构体 bool parse; // 用于存储解析是否成功的标志 parse = (rte_pci_addr_parse(name, &pci_addr) == 0); // 调用rte_pci_addr_parse函数解析输入字符串,并将结果存储到pci_addr结构体中 // 如果解析成功,parse为true,否则为false if (parse && addr != NULL) *out = pci_addr; // 如果解析成功且传入的指针不为NULL,将解析结果复制到传入的指针所指向的位置 return parse == false; // 如果解析失败,返回1,否则返回0 }
 
 
欢迎加入喵星计算机技术研究院,原创技术文章第一时间推送。
notion image
 
My first postDPDK 17,18,22 版本的编译方法