DPDK rte_eal_init 初始化分析(根据最新的更新)
端口初始化流程
- 1. 注册设备驱动到“dev_driver_list”链表中
- 2.扫描系统中的pci设备,并注册到“pci_device_list”中
- 3、初始化注册的驱动
- 4、网卡设备初始化
端口初始化流程
如上所示给出了端口打开的简单流程图,下面以ixgbe驱动为例详细说明:
1. 注册设备驱动到“dev_driver_list”链表中
这个链表节点为:
/*** 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函数执行之前完成。
这样就有了设备驱动类型、设备驱动的初始化函数
2.扫描系统中的pci设备,并注册到“pci_device_list”中
链表节点为:
/*** 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设备的相关信息,后面根据上面获取的这些设备信息以及前面注册的驱动信息,就可以完成具体网卡设备的初始化;
3、初始化注册的驱动
在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”这两个全局的链表中,接下来就可以完成设备匹配驱动,别初始化设备了。
4、网卡设备初始化
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初始化完成
DPDK rte_eal_init 初始化分析(根据最新的更新)相关推荐
- dpdk内存管理分析
dpdk内存管理分析 文章目录 dpdk内存管理分析 1.1 简述 1.2 `rte_config_init`分析 1.3 `eal_hugepage_info_init`的分析 1.4 `rte_e ...
- DPDK igb_uio驱动分析
本文整理下之前的学习笔记,基于DPDK17.11版本源码分析.主要分析一下igb_uio驱动源码. 总线-设备-驱动 首先简单介绍一下kernel中的总线-设备-驱动模型,以pci总线为例,pci总线 ...
- djc加密数字货币_2019年最新加密数字货币排行榜 依据最新GitHub更新量排名
原标题:2019年最新加密数字货币排行榜 依据最新GitHub更新量排名 在过去12个月里,数字资产大规模贬值,然而在这一现象背后,加密货币开发却在发展.从数据收集网站的研究来看,GitHub上的加密 ...
- SECTIONS bss段初始化分析
SECTIONS bss段初始化分析 都知道bss段需要初始化,但是这是为什么呢? 通过浏览资料,都会发现,bss段是不会出现在程序下载文件(*.bin .hex)中的,因为全都是0.如果把出现在程序 ...
- sparkContext之一:sparkContext的初始化分析
Spark源码学习:sparkContext的初始化分析 spark可以运行在本地模式local下,可以运行在yarn和standalone模式下,但是本地程序是通过什么渠道和这些集群交互的呢?那就是 ...
- 简单的DPDK介绍与分析
DPDK介绍及分析 什么是DPDK Intel® DPDK 全称 __Intel Data Plane Development Kit__,是intel提供的数据平面开发工具集,为Intel arch ...
- Xshell 6 提示 要继续使用此程序,您必须应用最新的更新
楼主使用软件版本:Xshell 6 使用大约一年后无法正常打开软件,并提示"要继续使用此程序,您必须应用最新的更新" 解决方法一:按照提示更新软件 结果:提示无法下载而失败 解决方 ...
- Xshell无法启动:要继续使用此程序,您必须应用最新的更新或使用新版本
我是小马甲~ 2018年12月27日 1 1491 次浏览 教程资源 | 网络资源 XShell | Xshell5强制升 今天博主开启电脑后启动Xshell 突然出现上图的提示!直接无法启动了 ...
- Xftp5解决“要继续使用此程序,您必须应用最新的更新或使用新版本”
一直用的Xftp5,之前一直好好的,今天打开突然提示"要继续使用此程序,您必须应用最新的更新或使用新版本". 研究了一下,发现Xshell5配置文件中写入了强制升级时间,这个版本是 ...
最新文章
- 超级计算机预测降雪,南方九省即将大雪纷飞?超级计算机:可能性增加,但还没有确定...
- [转载]模糊系统:挑战与机遇并存——十年研究之感悟 王立新
- 本周 Github 精选:13 款炼丹利器,有开源工具包也有超大数据集
- PHP7 serialize_precision 配置不当导致 json_encode() 浮点小数溢出错误
- 一个风格诡异的ABAP学习网站
- mysql流程控制_Mysql之流程控制结构
- 嵌入式linux学习
- ASP输出生成Word 、Excel、Txt文件的方法
- zuul网关_Spring Cloud第五章:服务网关Zuul
- 欧盟《一般数据保护法案》(GDPR)核心要点 本文更多的是站在企业角度来思考法案对物联网行业的影响以及应对措施,一来希望与同行企业可以就GDPR进行更多的互动讨论;二来也是希望传播国际法案对于安全和
- TextWatcher使用
- 电脑主机前面的耳机插孔没声音怎么办?旧时光 oldtimeblog
- 在C#中用一个按钮控制斑马zebra条码打印机进行打印
- EBS开发_创建销售人员API
- Sqoop--全量/增量、导入/导出
- 常用adb命令学习:查看和修改设备的输入法
- 从高考到程序员——我一直在寻找答案
- 儿童近视率攀升,什么原因容易导致近视呢?
- 与师生谈人工智能3:精确定义之病
- 纵向表格转为横向表格
热门文章
- 今天在Palm上读完钱穆的《中国历代政治得失》
- eclipse工程搭建步骤
- php rewrite模块安装,Linux_Linux下Apache安装/增加mod_rewrite模块,如果你的服务器apache还没有安 - phpStudy...
- KUKA机器人信息编程—信息类型介绍
- Android平台签名证书(.keystore)及IOS平台(.p12证书)
- 利用Word模板自动生成文档
- 你好,放大器——失调电压漂移(Offset Voltage Drift)
- mongoDB数据库命令行基本操作
- UiAutomator自定义一个一键清理后台的方法
- 基于Android studio的校园小助手app设计