05.DPU网络开发SDK—DPDK(四)

接上次内容继续对rte_eal_init()所做的工作进行分析。
12.初始化配置
rte_config_init()中,会根据process_type进行不同的初始化任务。process_type是由eal的启动参数指定的,目前支持两种模式:primary和secondary。process_type在解析参数时存放在internal_config中,然后此处赋值到rte_config中。
 
  • 按照primary方式启动
DPDK在解析参数的时候,会初始化一个runtime_dir全局变量。如果没有特殊配置,这个目录为/var/run/dpdk/rte,DPDK会基于此目录中的文件实现一些进程间通信。
 
primary方式启动时,会调用rte_eal_config_create(),该过程在runtime_dir中创建一个名称为config的文件,将文件大小截为rte_mem_config的大小,并添加一个文件锁。文件锁类型为struct flock,这种锁可以指定文件的哪些数据片段需要被保护。在此处保护的是整个文件,也就是整个rte_mem_config。
 
接下来需要将该文件映射到DPDK进程的虚拟空间当中,调用mmap()进行此操作,映射之后可读可写,且其他进程同样可以映射该文件到其虚拟空间当中。在映射之前,会确定映射到进程的具体哪个虚拟地址上,该过程由eal_get_virtual_area()实现。
 
eal_get_virtual_area()会基于internal_config中的base_virtaddr确定目标映射地址(该值默认为0,可通过启动参数修改之)。在base_virtaddr的基础上,以系统的page_size为递增单位,依次检查某个映射地址是否满足映射要求,直到找第一个满足要求的映射地址为止。
 
完成对config文件的映射之后,将rte_config中的rte_mem_config拷贝到映射好的虚拟地址空间,并将rte_config中rte_mem_config的指针指向此空间,后续对rte_mem_config的修改就可以直接反馈到config文件当中了。
 
  • 按照secondary方式启动
如果是secondary启动DPDK进程,则直接打开primary进程创建的config文件并mmap()到secondary进程中,后将rte_config中rte_mem_config的指针指向此空间。如此secondary进程看到的rte_mem_config看到的内容和primary进程看到的是一样的,两者在mem配置方面实现了进程间的通信,且primary进程在创建config文件的过程中为其设置了锁,使得对其的写操作是互斥的,避免了两个进程之间的竞争问题。
13.初始化信号处理
此处启动一个线程eal_intr_thread_main(),该线程中无限循环执行如下的过程:
  • 创建一个epoll,并将中断信号传递管道中读信号的一侧的文件描述符intr_pipe.readfp添加到epoll中进行监听。
  • 将intr模块的全局变量intr_sources列表中的所有文件描述符添加到epoll中进行监听。
  • 无限循环等待epoll中的文件描述符的事件的发生,并依次处理发生的事件。
  • 关闭epoll。
14.创建一个定时器
此处调用Linux的系统函数timerfd_create()创建一个定时器的文件描述符,并将其存在alarm模块的全局变量intr_handle中。在后续的操作中,intr_handle会注册到前面提到的intr_sources列表中。
15.初始化进程间通信文件
rte_mp_channel_init()在/var/run/dpdk/rte目录下生成一个文件,名称为mp_socket_*。调用open_socket_id创建UDP类型的socket,保存于全局变量mp_fd中,并调用bind将mp_fd与通信文件进行绑定。
如果进程是primary,那么通信文件为/var/run/dpdk/rte/mp_socket,如果是secondary,通信文件为/var/run/dpdk/rte/mp_socket_${pid}_${timestamp}。然后重启一个线程,线程处理函数为mp_handle(),该处理函数无限循环接收mp_fd代表的socket发来的信息并处理之。该进程间通信文件的是primary和secondary进程之间的通信,secondary进程之间是没有通信需求的。
 
16.设备热插拔初始化
eal_mp_dev_hotplug_init()中,如果当前进程是primary,则会注册一个回调函数handle_secondary_request()用于处理处理来自secondary进程的设备热插拔请求。反之secondary进程中会注册handle_primary_request()函数。
 
17.各总线遍历设备
bus模块全局变量rte_bus_list存储了所有的总线的列表,此处依次遍历每个总线并调用总线的scan()方法。
 
在DPDK中存在一个描述总线的结构struct rte_bus,该结构中定义了一些总线的接口函数,scan()就是其中之一。对于总线来说,scan()方法的主要作用是用来遍历所有的设备,并将设备与注册在该总线上的驱动进行匹配,当匹配成功时,建立驱动和设备之间的对应关系。DPDK只提供了接口要求各总线实现该接口,具体如何实现则由各总线设备自行定义。
 
总线通过注册的方式注册到rte_bus_list当中,目前DPDK支持的总线有:
  • vmbus
  • vdev
  • pci
  • fpga
  • fslmc
  • dpaa
struct rte_bus的结构如下,除了scan()方法,还有probe(),find_device()等。

 

WechatIMG318

rte_bus的实现是仿照了Linux内核中总线的角色,主要是为了建立设备和驱动之间的联系,后期我们会以某个总线设备为例查看这些接口方法的具体实现。
未完待续… 
 
往期回顾:
04.DPU网络开发SDK—DPDK(三)
03.DPU网络开发SDK——DPDK(二)
02.DPU网络开发SDK——DPDK(一)
01.DPU简介