DPU网络开发SDK—DPDK(四)

13 篇文章 8 订阅

rte_eal_init()

接上次内容继续对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()等。

struct rte_bus {
              TAILQ_ENTRY(rte_bus) next;   //在rte_bus_list中的入口
              const char *name;            //总线名称
              rte_bus_scan_t scan;         //遍历总线上挂载的设备
              rte_bus_probe_t probe;       //探测总线上挂载的设备
              rte_bus_find_device_t find_device; //查找总线上挂载的设备
              rte_bus_plug_t plug;         //建立某个设备和驱动的联系
              rte_bus_unplug_t unplug;     //接触某个设备和驱动的联系
              rte_bus_parse_t parse;       //解析设备名称
              rte_dev_dma_map_t dma_map;   //设备映射到DMP
              rte_dev_dma_unmap_t dma_unmap; //解除设备到DMA的映射
              struct rte_bus_conf conf;    //总线配置
              rte_bus_get_iommu_class_t get_iommu_class; //iommu类别
              rte_dev_iterate_t dev_iterate; //设备迭代
              rte_bus_hot_unplug_handler_t hot_unplug_handler;  //处理热拔出设备失败
              rte_bus_sigbus_handler_t sigbus_handler;  //处理sigbus错误
};

rte_bus的实现是仿照了Linux内核中总线的角色,主要是为了建立设备和驱动之间的联系,后期我们会以某个总线设备为例查看这些接口方法的具体实现。

未完待续…

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值