端口初始化流程
这个链表节点为:
/**
* A structure describing a device driver.
*/
struct rte_driver {
TAILQ_ENTRY(rte_driver) next; /**< Next in list. */
enum pmd_type type; /**< PMD Driver type */
const char *name; /**< Driver name. */
rte_dev_init_t *init; /**< Device init. function. */
rte_dev_uninit_t *uninit; /**< Device uninit. function. */
};
将ixgbe的这些信息注册到该链表中:
static struct rte_driver rte_ixgbe_driver = {
.type = PMD_PDEV,
.init = rte_ixgbe_pmd_init,
};
PMD_REGISTER_DRIVER(rte_ixgbe_driver);
PMD_REGISTER_DRIVER为dpdk定义的宏,使用了GNU C提供的“__attribute__(constructor)”机制,使得注册设备驱动的过程在main函数执行之前完成。
这样就有了设备驱动类型、设备驱动的初始化函数
链表节点为:
/**
* A structure describing a PCI device.
*/
struct rte_pci_device {
TAILQ_ENTRY(rte_pci_device) next; /**< Next probed PCI device. */
struct rte_pci_addr addr; /**< PCI location. */
struct rte_pci_id id; /**< PCI ID. */
struct rte_pci_resource mem_resource[PCI_MAX_RESOURCE]; /**< PCI Memory Resource */
struct rte_intr_handle intr_handle; /**< Interrupt handle */
struct rte_pci_driver *driver; /**< Associated driver */
uint16_t max_vfs; /**< sriov enable if not zero */
int numa_node; /**< NUMA node connection */
struct rte_devargs *devargs; /**< Device user arguments */
enum rte_kernel_driver kdrv; /**< Kernel driver passthrough */
};
从系统中获取到PCI设备的相关信息后,记录到这样的一个结构体中。如何获取到这些信息:
在main函数的一开始,调用rte_eal_init()获取用户、系统的相关配置信息以及设置基础运行环境,其中包括调用rte_eal_pci_init()来扫描、获取系统中的CPI网卡信息;
首先,初始化pci_device_list链表,后面扫描的到的pci网卡设备信息会记录到这个链表中;
然后,调用rte_eal_pci_scan()扫描系统中的PCI网卡:遍历”/sys/bus/pci/devices”目录下的所有pci地址,逐个获取对应的pci地址、pci id、sriov使能时的vf个数、亲和的numa、设备地址空间、驱动类型等;
/*
* Scan the content of the PCI bus, and the devices in the devices list
*/
int rte_eal_pci_scan(void)
{
struct dirent *e;
DIR *dir;
char dirname[PATH_MAX];
uint16_t domain;
uint8_t bus, devid, function;
dir = opendir(SYSFS_PCI_DEVICES);
if (dir == NULL) {
RTE_LOG(ERR, EAL, "%s(): opendir failed: %s\n",
__func__, strerror(errno));
return -1;
}
while ((e = readdir(dir)) != NULL) {
if (e->d_name[0] == '.')
continue;
if (parse_pci_addr_format(e->d_name, sizeof(e->d_name), &domain,
&bus, &devid, &function) != 0)
continue;
snprintf(dirname, sizeof(dirname), "%s/%s", SYSFS_PCI_DEVICES,
e->d_name);
if (pci_scan_one(dirname, domain, bus, devid, function) < 0)
goto error;
}
closedir(dir);
return 0;
error:
closedir(dir);
return -1;
}
这样,扫描并记录了系统中所有的pci设备的相关信息,后面根据上面获取的这些设备信息以及前面注册的驱动信息,就可以完成具体网卡设备的初始化;
在rte_eal_init()函数中,后面会调用rte_eal_dev_init()来初始化前面注册的驱动“dev_driver_list”:分别调用注册的每款驱动的初始化函数,把每款驱动的一些信息记录到“pci_driver_list”链表中,链表节点为:
/**
* @internal
* The structure associated with a PMD Ethernet driver.
*
* Each Ethernet driver acts as a PCI driver and is represented by a generic
* *eth_driver* structure that holds:
*
* - An *rte_pci_driver* structure (which must be the first field).
*
* - The *eth_dev_init* function invoked for each matching PCI device.
*
* - The *eth_dev_uninit* function invoked for each matching PCI device.
*
* - The size of the private data to allocate for each matching device.
*/
struct eth_driver {
struct rte_pci_driver pci_drv; /**< The PMD is also a PCI driver. */
eth_dev_init_t eth_dev_init; /**< Device init function. */
eth_dev_uninit_t eth_dev_uninit; /**< Device uninit function. */
unsigned int dev_private_size; /**< Size of device private data. */
};
结构中记录设备的init、uinit、私有数据大小以及pci driver信息,而struct rte_pci_driver中的记录了驱动支持的网卡设备的verder id、device id信息,这个在后面具体的PCI网卡设备初始化时,会根据这些信息来匹配驱动:
/**
* A structure describing a PCI driver.
*/
struct rte_pci_driver {
TAILQ_ENTRY(rte_pci_driver) next; /**< Next in list. */
const char *name; /**< Driver name. */
pci_devinit_t *devinit; /**< Device init. function. */
pci_devuninit_t *devuninit; /**< Device uninit function. */
const struct rte_pci_id *id_table; /**< ID table, NULL terminated. */
uint32_t drv_flags; /**< Flags contolling handling of device. */
};
/**
* A structure describing an ID for a PCI driver. Each driver provides a
* table of these IDs for each device that it supports.
*/
struct rte_pci_id {
uint16_t vendor_id; /**< Vendor ID or PCI_ANY_ID. */
uint16_t device_id; /**< Device ID or PCI_ANY_ID. */
uint16_t subsystem_vendor_id; /**< Subsystem vendor ID or PCI_ANY_ID. */
uint16_t subsystem_device_id; /**< Subsystem device ID or PCI_ANY_ID. */
};
已ixgbe类型的网卡为例,注册的信息为rte_ixgbe_pmd:
static struct eth_driver rte_ixgbe_pmd = {
.pci_drv = {
.name = "rte_ixgbe_pmd",
.id_table = pci_id_ixgbe_map,
.drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC |
RTE_PCI_DRV_DETACHABLE,
},
.eth_dev_init = eth_ixgbe_dev_init,
.eth_dev_uninit = eth_ixgbe_dev_uninit,
.dev_private_size = sizeof(struct ixgbe_adapter),
};
至此,注册的每款驱动的设备初始化,支持的设备等信息以及系统中所有的pci设备信息就已经都有了,分别记录在”pci_driver_list”和”pci_device_list”这两个全局的链表中,接下来就可以完成设备匹配驱动,别初始化设备了。
rte_eal_init()函数接下来调用rte_eal_pci_probe()函数完成具体的设备的初始化
/*
* Scan the content of the PCI bus, and call the devinit() function for
* all registered drivers that have a matching entry in its id_table
* for discovered devices.
*/
int rte_eal_pci_probe(void)
{
struct rte_pci_device *dev = NULL;
struct rte_devargs *devargs;
int probe_all = 0;
int ret = 0;
/* 如果配置了白名单,只初始化白名单中的设备,否则所有支持的设备都初始化 */
if (rte_eal_devargs_type_count(RTE_DEVTYPE_WHITELISTED_PCI) == 0)
probe_all = 1;
TAILQ_FOREACH(dev, &pci_device_list, next) {
/* set devargs in PCI structure */
devargs = pci_devargs_lookup(dev);
if (devargs != NULL)
dev->devargs = devargs;
/* probe all or only whitelisted devices */
if (probe_all)
ret = pci_probe_all_drivers(dev);
else if (devargs != NULL &&
devargs->type == RTE_DEVTYPE_WHITELISTED_PCI)
ret = pci_probe_all_drivers(dev);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Requested device " PCI_PRI_FMT
" cannot be used\n", dev->addr.domain, dev->addr.bus,
dev->addr.devid, dev->addr.function);
}
return 0;
}
rte_eal_pci_probe_one_driver()函数中,在probe一个具体的设备时,比较vendor id、device id,然后映射设备资源、调用驱动的设备初始化函数:
/*
* If vendor/device ID match, call the devinit() function of the
* driver.
*/
static int
rte_eal_pci_probe_one_driver(struct rte_pci_driver *dr, struct rte_pci_device *dev)
{
int ret;
const struct rte_pci_id *id_table;
for (id_table = dr->id_table; id_table->vendor_id != 0; id_table++) {
/* check if device's identifiers match the driver's ones */
... ...
if (dr->drv_flags & RTE_PCI_DRV_NEED_MAPPING) {
/* map resources for devices that use igb_uio */
ret = rte_eal_pci_map_device(dev);
if (ret != 0)
return ret;
} else if (dr->drv_flags & RTE_PCI_DRV_FORCE_UNBIND &&
rte_eal_process_type() == RTE_PROC_PRIMARY) {
/* unbind current driver */
if (pci_unbind_kernel_driver(dev) < 0)
return -1;
}
/* call the driver devinit() function */
return dr->devinit(dr, dev);
}
/* return positive value if driver doesn't support this device */
return 1;
}
pci_uio_map_resource()函数为pci设备在虚拟地址空间映射pci资源,后续直接通过操作内存来操作pci设备;
驱动的设备初始化函数rte_eth_dev_init()主要是初始化dpdk驱动框架中,为每个设备分配资源以及资源的初始化:
/* 每个设备对应数组的一个成员,记录了设备相关的所有信息 */
struct rte_eth_dev rte_eth_devices[RTE_MAX_ETHPORTS];
/* 端口相关的配置 */
struct rte_eth_dev_data *dat;
dpdk框架中,对端口的初始化操作已经基本完成,后面则是根据用户的设置,配置端口的收发包队列以及最终start端口,开始收发包:
a、rte_eth_dev_configure()函数完成端口配置:队列数配置、RSS、offload等等设置;
b、rte_eth_rx_queue_setup()、rte_eth_tx_queue_setup()函数分别设置端口的每个收发队列:ring空间申请、初始化等;
c、rte_eth_dev_start()函数:发送队列初始化buf填充,端口使能(具体可以参考代码或网卡芯片手册,均是相关寄存器设置);
rte_eal_init
│
├──rte_atomic32_test_and_set:操作静态局部变量run_once确保函数只执行一次
│
├──获取主线程的线程ID
│
├──rte_eal_log_early_init:将stderr作为日志输出的文件
│ │
│ └──rte_openlog_stream
│
├──eal_log_level_parse
│ │
│ ├──eal_reset_internal_config:初始化结构体struct internal_config
│ │
│ └──解析命令行参数,只处理“--log-level”,保存在internal_config.log_level
│
├──rte_set_log_level:设置log level
│
├──rte_eal_cpu_init:赋值全局结构struct lcore_config
│ │
│ ├──rte_eal_get_configuration:获取全局配置结构struct rte_config,初始指向全局变量early_mem_config
│ │
│ ├──eal_cpu_detected
│ │ │
│ │ └──如果文件“/sys/devices/system/cpu/cpu%u/topology/core_id”存在,则存在此编号的cpu
│ │
│ ├──eal_cpu_core_id
│ │ │
│ │ └──eal_parse_sysfs_value:读取文件“/sys/devices/system/cpu/cpu%u/topology/core_id”,
│ │ 获取core number onsocket for this lcore
│ │
│ ├──eal_cpu_socket_id
│ │ │
│ │ └──如果目录“/sys/devices/system/node/node%u/cpu%u”存在,得到physical socket id for this lcore
│ │
│ └──计数得到number of available logical cores,保存在structrte_config.lcore_count中
│
├──eal_parse_args:解析处理EAL的命令行参数,赋值struct internal_config结构的相关字段
│
├──eal_hugepage_info_init:赋值struct hugepage_info数组(internal_config.hugepage_info)
│ │
│ ├──打开目录“/sys/kernel/mm/hugepages”
│ │
│ ├──遍历子目录,如“hugepages-2048kB”,即不同大小的hugepage
│ │ │
│ │ ├──获取当前大小hugepage对应的structhugepage_info结构体
│ │ │
│ │ ├──get_hugepage_dir
│ │ │ │
│ │ │ └──读取文件“/proc/mounts”,查找所有“hugetlbfs”,找到当前大小hugepage对应的挂载路径,
│ │ │ 如“/mnt/huge”
│ │ │
│ │ ├──打开hugepage目录文件,上文件锁
│ │ │
│ │ ├──删除hugepage files,如“rtemap_xxx”
│ │ │
│ │ └──get_num_hugepages
│ │ │
│ │ └──读取文件“/sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages”和
│ │ “/sys/kernel/mm/hugepages/hugepages-2048kB/resv_hugepages”,获取hugepage总数
│ │
│ ├──对struct internal_config结构中的各个大小的struct hugepage_info进行排序,从大到小
│ │
│ └──检查是否至少有一个有效的struct hugepage_info
│
├──eal_get_hugepage_mem_size:
│ │
│ └──遍历struct hugepage_info,获取所有hugepage占用内存的总数,结果存放在internal_config.memory
│
├──将当前时间作为种子,产生伪随机数序列
│
├──rte_config_init
│ │
│ ├──主应用的情况(RTE_PROC_PRIMARY)
│ │ │
│ │ └──rte_eal_config_create
│ │ │
│ │ ├──eal_runtime_config_path:获取runtime配置文件路径,如“/var/run/.rte_config”
│ │ │
│ │ ├──打开文件,上锁,mmap映射文件到内存
│ │ │
│ │ ├──将early configuration structure(全局变量early_mem_config)拷贝到此内存中,
│ │ │ rte_config.mem_config指向这块内存
│ │ │
│ │ └──映射地址保存在rte_config.mem_config->mem_cfg_addr中,用于从应用将来映射到相同的地址
│ │
│ └──从应用的情况(RTE_PROC_SECONDARY)
│ │
│ ├──rte_eal_config_attach
│ │ │
│ │ ├──eal_runtime_config_path
│ │ │
│ │ ├──打开文件,mmap映射文件到内存
│ │ │
│ │ └──rte_config.mem_config指向映射的内存
│ │
│ ├──rte_eal_mcfg_wait_complete
│ │ │
│ │ └──如果struct rte_mem_config结构的magic成员没有被写成RTE_MAGIC,就继续等待
│ │ (主应用ready后会将struct rte_mem_config结构的magic成员写成RTE_MAGIC)
│ │
│ └──rte_eal_config_reattach
│ │
│ ├──从前面mmap映射文件中获取主应用mmap的映射地址(即rte_config.mem_config->mem_cfg_addr)
│ │
│ ├──munmap解除先前的映射
│ │
│ ├──指定主应用映射地址重新执行mmap映射,如果最终映射地址和指定映射地址不一致,则出错退出
│ │
│ └──将rte_config.mem_config指向重新映射的内存
│
├──rte_eal_pci_init
│ │
│ ├──初始化以全局变量pci_driver_list和pci_device_list为头的tail queue
│ │
│ └──rte_eal_pci_scan
│ │
│ └──遍历目录“/sys/bus/pci/devices”下的所有子目录
│ │
│ ├──parse_pci_addr_format:从目录名称中获取PCI设备的domain、bus、devid、function信息
│ │
│ └──pci_scan_one
│ │
│ ├──mallocstruct rte_pci_device结构
│ │
│ ├──读取目录“/sys/bus/pci/devices/0000\:03\:00.0/”下相关文件,填充struct rte_pci_device结构字段
│ │
│ ├──pci_get_kernel_driver_by_path:获取驱动名称
│ │
│ ├──赋值所支持的驱动(structrte_pci_device结构的kdrv字段)
│ │
│ └──将此struct rte_pci_device结构按序插入到全局队列pci_device_list
│
├──rte_eal_memory_init
│
├──eal_hugedirs_unlock:解锁hugepage目录(由前面的eal_hugepage_info_init函数加锁)
│
├──rte_eal_memzone_init
│
├──rte_eal_tailqs_init
│ │
│ └──遍历以全局变量rte_tailq_elem_head为头部的struct rte_tailq_elem结构tailq链表
│ │
│ └──rte_eal_tailq_update
│ │
│ ├──rte_eal_tailq_create:主应用的情况(RTE_PROC_PRIMARY)
│ │ │
│ │ ├──rte_eal_tailq_lookup
│ │ │ │
│ │ │ └──在struct rte_mem_config结构的structrte_tailq_head结构数组中查找
│ │ │
│ │ └──如果没找到,占用struct rte_tailq_head结构数组的一个空位
│ │
│ └──rte_eal_tailq_lookup:从应用的情况(RTE_PROC_SECONDARY)
│
├──rte_eal_log_init
│ │
│ ├──调用fopencookie,定义一个定制的写日志接口
│ │
│ ├──调用openlog打开日志
│ │
│ └──rte_eal_common_log_init
│ │
│ ├──STAILQ_INIT:初始化Singly-linked Tail queue,队头为log_history
│ │
│ ├──rte_mempool_create
│ │
│ ├──如果创建mempool失败,调用rte_mempool_lookup
│ │ │
│ │ ├──获取链接所有mempool结构链表的头结构structrte_mempool_list
│ │ │
│ │ ├──遍历链接所有mempool结构链表的所有结点
│ │ │ │
│ │ │ └──比较struct rte_tailq_entry结构的data域指向的struct rte_mempool结构的名称,
│ │ │ 是否与指定名称相同
│ │ │
│ │ └──返回找到的指向struct rte_mempool结构的指针,或NULL
│ │
│ └──rte_openlog_stream:按照参数,修改struct rte_logs日志结构的相关参数
│
├──rte_eal_alarm_init
│ │
│ └──赋值全局的struct rte_intr_handle结构,调用timerfd_create函数创建定时器timer对象
│
├──rte_eal_timer_init
│ │
│ ├──设定全局变量eal_timer_source为EAL_TIMER_TSC(TSC/HPET)
│ │
│ ├──set_tsc_freq:设置TSC frequency(每秒钟时钟中断的次数)
│ │
│ └──check_tsc_flags
│ │
│ └──解析文件“/proc/cpuinfo”,检查“flags”属性中“constant_tsc”和“nonstop_tsc”是否存在
│ (“constant_tsc”和“nonstop_tsc”都存在,说明TSC计时是可靠的)
│
├──eal_check_mem_on_local_socket
│ │
│ ├──获取masterlcore对应的numa socket
│ │
│ ├──rte_eal_get_physmem_layout:获取struct rte_memseg结构数组地址
│ │
│ └──遍历struct rte_memseg结构数组,检查特定struct rte_memseg结构是否存在(对应此numa socket,并且长度大于0)
│
├──eal_plugins_init
│ (EAL的“-d”选项可以指定需要载入的动态链接库)
│ │
│ ├──如果全局变量default_solib_dir所指的Default path of external loadable drivers有效
│ │ │
│ │ └──eal_plugin_add
│ │ │
│ │ ├──malloc一个struct shared_driver结构,拷贝路径名称
│ │ │
│ │ └──将此struct shared_driver结构挂载到List of external loadable drivers中
│ │
│ └──遍历List of external loadable drivers上挂载的所有struct shared_driver结构
│ │
│ ├──如果当前struct shared_driver结构所保存的路径是目录
│ │ │
│ │ └──eal_plugindir_init
│ │ │
│ │ └──对目录中的每个普通文件,执行eal_plugin_add
│ │ (将文件挂载到Listof external loadable drivers的尾部,待接下来的遍历循环进行处理)
│ │
│ └──否则,是共享库的情况
│ │
│ └──调用dlopen打开指定的动态链接库
│
├──eal_thread_init_master
│ │
│ ├──设置主线程的lcore_id
│ │
│ └──eal_thread_set_affinity
│ │
│ ├──rte_gettid
│ │ │
│ │ └──rte_sys_gettid:获取线程的tid
│ │
│ └──rte_thread_set_affinity
│ │
│ └──设置线程的CPU亲和性,记录numasocket等信息
│
├──eal_thread_dump_affinity
│ │
│ ├──rte_thread_get_affinity
│ │
│ └──dump当前线程的CPU affinity
│
├──rte_eal_dev_init
│ │
│ ├──遍历以全局变量devargs_list为头的struct rte_devargs结构链表
│ │ (struct rte_devargs——Structure that stores a device given by the user with its arguments)
│ │ │
│ │ ├──跳过physical device
│ │ │
│ │ └──rte_eal_vdev_init
│ │ │
│ │ └──遍历以全局变量dev_driver_list为头的struct rte_driver结构链表
│ │ │
│ │ ├──跳过physicaldevicedriver
│ │ │
│ │ └──search a driver prefix in virtual device name,如果匹配执行struct rte_driver结构中的init函数
│ │
│ └──遍历以全局变量dev_driver_list为头的struct rte_driver结构链表
│ (进程main函数运行前,通过PMD_REGISTER_DRIVER(xxx)宏定义,内在调用rte_eal_driver_register
│ 将各种structrte_driver结构(e.g.rte_ixgbe_driver),插入到此struct rte_driver结构链表中)
│ │
│ ├──跳过virtual devicedriver
│ │
│ └──执行struct rte_driver结构中的init函数
│ (e.g.rte_ixgbe_pmd_init)
│ │
│ └──rte_eth_driver_register:Register an Ethernet [Poll Mode] driver
│ │
│ ├──赋值struct eth_driver结构中的PCIdriver字段包含的Device init/uninit function pointer
│ │
│ └──rte_eal_pci_register
│ │
│ └──将刚赋值的PCIdriver数据结构插入到以全局变量pci_driver_list为头的struct rte_pci_driver结构链表中
│
├──rte_eal_intr_init
│ │
│ ├──初始化global interrupt source head
│ │
│ ├──创建pipe
│ │
│ ├──创建线程来等待处理中断,线程执行函数为eal_intr_thread_main
│ │ │
│ │ └──线程运行循环
│ │ │
│ │ ├──epoll_create:创建epoll文件描述符
│ │ │
│ │ ├──epoll_ctl:把前面创建的the read end of the pipe,添加到epoll wait list中
│ │ │
│ │ ├──遍历以global interrupt source head为头部的struct rte_intr_source结构链表
│ │ │ │
│ │ │ ├──如果当前struct rte_intr_source结构没有挂载的callback函数,跳过
│ │ │ │
│ │ │ └──把所有的uio device file descriptor,添加到epoll wait list中
│ │ │
│ │ ├──eal_intr_handle_interrupts
│ │ │ │
│ │ │ └──循环
│ │ │ │
│ │ │ ├──epoll_wait:wait for an I/O event on an epoll file descriptor
│ │ │ │
│ │ │ ├──eal_intr_process_interrupts
│ │ │ │ │
│ │ │ │ └──遍历所有发生的I/O event
│ │ │ │ │
│ │ │ │ ├──如果the read end of the pipe可用,执行read操作,函数返回
│ │ │ │ │ (此时会rebuild thewait list)
│ │ │ │ │
│ │ │ │ ├──遍历struct rte_intr_source结构链表,查找当前I/O event对应的structrte_intr_source结构
│ │ │ │ │
│ │ │ │ ├──根据interrupt handle type(uio/alarm/…),确定需要读取的字节长度
│ │ │ │ │
│ │ │ │ ├──执行文件read操作
│ │ │ │ │
│ │ │ │ └──如果read数据成功,执行当前struct rte_intr_source结构挂载的所有callback函数
│ │ │ │
│ │ │ └──调用eal_intr_process_interrupts返回负数,本次中断处理结束返回
│ │ │
│ │ └──关闭epoll文件描述符
│ │
│ └──如果创建线程成功,调用rte_thread_setname给线程设置名称“eal-intr-thread”
│ │
│ └──pthread_setname_np
│
├──循环(browse all running lcores except the master lcore)
│ │
│ ├──创建主线程与子线程通信使用的pipe
│ │
│ ├──设置子线程状态为WAIT
│ │
│ ├──创建子线程,线程执行函数为eal_thread_loop
│ │ │
│ │ ├──根据线程ID,获取当前线程的lcore_id
│ │ │
│ │ ├──获取主线程向子线程通信所用管道,子线程读取数据的file descriptor(m2s)
│ │ │ 获取子线程向主线程通信所用管道,子线程发送数据的file descriptor(s2m)
│ │ │
│ │ ├──eal_thread_set_affinity:设置子线程cpu affinity
│ │ │
│ │ ├──eal_thread_dump_affinity
│ │ │
│ │ └──线程主循环
│ │ │
│ │ ├──等待读取主线程发送的命令
│ │ │
│ │ ├──设置线程状态为RUNNING
│ │ │
│ │ ├──向主线程发送ack
│ │ │
│ │ ├──读取当前lcore对应的structlcore_config结构中的lcore_function_t类型函数指针,及调用参数
│ │ │
│ │ ├──执行所指函数,并存储返回值
│ │ │
│ │ └──设置线程状态为FINISHED
│ │
│ └──如果创建线程成功,调用rte_thread_setname给线程设置名称“lcore-slave-xx”
│
├──rte_eal_mp_remote_launch:指示所有子线程启动一个dummyfunction
│ │
│ ├──检查各个子线程/lcore的状态是否处于WAIT
│ │
│ ├──rte_eal_remote_launch:向各个子线程/lcore发送执行命令
│ │ │
│ │ ├──获取主线程向子线程通信所用管道,主线程发送数据的file descriptor(m2s)
│ │ │ 获取子线程向主线程通信所用管道,主线程读取数据的file descriptor(s2m)
│ │ │
│ │ ├──将lcore_function_t类型函数指针,及调用参数填入当前lcore对应的structlcore_config结构
│ │ │
│ │ ├──向子线程发送命令
│ │ │
│ │ └──等待读取子线程发送的ack
│ │
│ └──如果最后一个参数值为CALL_MASTER(lcore handler executed by master core),主线程也执行所指函数
│
├──rte_eal_mp_wait_lcore
│ │
│ └──rte_eal_wait_lcore:等待所有子线程完成工作
│ │
│ ├──如果子线程处于WAIT状态,直接返回
│ │
│ ├──如果子线程处于RUNNING状态,循环等待
│ │
│ └──将子线程状态从FINISHED改为WAIT
│
├──rte_eal_pci_probe
│ │
│ ├──rte_eal_devargs_type_count
│ │ │
│ │ └──遍历以全局变量devargs_list为头的struct rte_devargs结构链表,
│ │ 计算指定类型(whitelist/blacklist/virtual)的设备数目
│ │
│ └──遍历以全局变量pci_device_list为头的struct rte_pci_device结构链表
│ │
│ ├──pci_devargs_lookup:当前PCI device是否由user在命令行参数中被指定
│ │ │
│ │ └──遍历以全局变量devargs_list为头的struct rte_devargs结构链表
│ │ │
│ │ ├──跳过virtual device
│ │ │
│ │ ├──rte_eal_compare_pci_addr
│ │ │ │
│ │ │ └──比较两个PCI devices的Domain-Bus-Device-Function
│ │ │
│ │ └──如果比较成功,返回此struct rte_devargs结构
│ │
│ ├──如果user在命令行中没有指定任何whitelist设备,执行pci_probe_all_drivers
│ │ │
│ │ └──遍历以全局变量pci_driver_list为头的struct rte_pci_driver结构链表
│ │ │
│ │ ├──rte_eal_pci_probe_one_driver
│ │ │ │
│ │ │ └──遍历当前PCI driver所支持的设备ID列表
│ │ │ │
│ │ │ ├──确认PCI device ID是否在列表中
│ │ │ │ (比较Vendor ID、Device ID、Subsystem vendor ID、Subsystem device ID)
│ │ │ │
│ │ │ ├──如果user在命令行中指定当前设备为blacklist设备,则直接返回1
│ │ │ │
│ │ │ ├──如果PCI driver指定RTE_PCI_DRV_NEED_MAPPING标志(Device needsPCI BAR mapping)
│ │ │ │ │
│ │ │ │ ├──rte_eal_pci_map_device
│ │ │ │ │
│ │ │ │ └──如果map resources不成功,直接返回
│ │ │ │
│ │ │ ├──如果PCI driver指定RTE_PCI_DRV_FORCE_UNBIND标志
│ │ │ │ │
│ │ │ │ ├──pci_unbind_kernel_driver
│ │ │ │ │
│ │ │ │ └──如果unbind current driver不成功,直接返回
│ │ │ │
│ │ │ └──调用PCI driver devinit() function
│ │ │ (e.g.rte_eth_dev_init)
│ │ │ │
│ │ │ ├──rte_eth_dev_create_unique_device_name
│ │ │ │ (Create unique Ethernet device name usingPCI address)
│ │ │ │
│ │ │ ├──rte_eth_dev_allocate
│ │ │ │ │
│ │ │ │ ├──rte_eth_dev_find_free_port
│ │ │ │ │ │
│ │ │ │ │ └──在全局struct rte_eth_dev结构数组rte_eth_devices中查找未使用的空位,返回空位索引
│ │ │ │ │
│ │ │ │ ├──如果struct rte_eth_dev_data结构类型全局指针rte_eth_dev_data为NULL,
│ │ │ │ │ 执行rte_eth_dev_data_alloc
│ │ │ │ │ │
│ │ │ │ │ ├──主应用的情况(RTE_PROC_PRIMARY)
│ │ │ │ │ │ │
│ │ │ │ │ │ └──rte_memzone_reserve:reserve名称为“rte_eth_dev_data”的memzone,
│ │ │ │ │ │ 存放各个ethernet device对应的struct rte_eth_dev_data结构数据
│ │ │ │ │ │
│ │ │ │ │ ├──从应用的情况(RTE_PROC_SECONDARY)
│ │ │ │ │ │ │
│ │ │ │ │ │ └──rte_memzone_lookup:lookup名称为“rte_eth_dev_data”的memzone
│ │ │ │ │ │
│ │ │ │ │ ├──全局指针rte_eth_dev_data指向memzone所引用的内存
│ │ │ │ │ │
│ │ │ │ │ └──如果是主应用,清空初始化这片struct rte_eth_dev_data结构内存
│ │ │ │ │
│ │ │ │ ├──rte_eth_dev_allocated
│ │ │ │ │ │
│ │ │ │ │ └──查找是否已存在相同名称的ethernet device
│ │ │ │ │
│ │ │ │ └──填写struct rte_eth_dev结构和structrte_eth_dev_data结构的相关字段
│ │ │ │
│ │ │ ├──如果是主应用,按照structrte_eth_dev结构dev_private_size域所指定的
│ │ │ │ Size of device private data(对应PMD Ethernetdriver定义时就会被指定),
│ │ │ │ rte_zmalloc对应大小内存,地址保存在struct rte_eth_dev_data结构的dev_private域
│ │ │ │
│ │ │ ├──TAILQ_INIT:初始化以struct rte_eth_dev结构link_intr_cbs域为链表头的,
│ │ │ │ struct rte_eth_dev_callback结构链表
│ │ │ │
│ │ │ ├──设置设备默认MTU
│ │ │ │
│ │ │ ├──Invoke PMD device initialization function,即struct eth_driver结构的eth_dev_init域所指函数
│ │ │ │ (e.g.eth_ixgbe_dev_init)
│ │ │ │
│ │ │ ├──如果是主应用,free地址保存在struct rte_eth_dev_data结构的dev_private域的内存区
│ │ │ │
│ │ │ └──rte_eth_dev_release_port
│ │ │ │
│ │ │ └──释放此struct rte_eth_dev结构在全局数组rte_eth_devices中所占用的位置
│ │ │
│ │ └──如果当前driver与设备不匹配,继续尝试下个driver
│ │
│ └──否则如果user指定当前PCIdevice为whitelist设备,也执行pci_probe_all_drivers
│
└──rte_eal_mcfg_complete
│
└──如果是主应用,将全局内存配置struct rte_mem_config结构的magic成员写成RTE_MAGIC,
表明主应用EAL初始化完成