07.DPU网络开发SDK—DPDK(六)

上次内容继续对rte_eal_init()所做的工作进行分析。
 
  1. 大页内存配置
internal_conf中的no_hugetlbfs指明是否禁用大页内存,通过命令行参数”–no-huge”设置禁用,默认情况下大页内存是开启的。DPDK根据进程是primary还是secondary的调用不同的初始化过程来初始化大页内存。
  • 按照primary方式初始化
调用eal_hugepage_info_init()来初始化primary进程。
该func首先调用hugepage_info_init()来初始化大页信息,首先遍历/sys/kernel/mm/hugepages中每个名称以”hugepages-“开头的目录,这种目录每个都代表了一种大小的页面,从目录名称中即可获得大页的大小,如hugepages-1048576kB代表1GB大小的页面。遍历过程中,根据目录名称获取到大页内存页面大小之后,接下来检查大页是否被正确挂载。打开/proc/mounts文件,查找到fstype为hugetlbfs类型的挂载项,获取到挂载点;如果没有挂载点,那么会寻找下一个大小类型的大页。找到挂载点之后,会调用calc_num_pages()计算页面数量。
需要额外注意的是,对于arm架构,可以支持4种页面大小类型,其他架构则只支持3种页面类型,多出的页面大小类型不做处理。多种符合要求的大页类型经过分析之后,相关信息会存放在internal_config的hugepage_info数组中。
hugepage_info_init()之后,如果是不共享文件模式(internal_config中的no_shconf置位),那么初始化过程就到此为止了,如果是共享模式,需要额外完成如下工作:通过调用create_shared_memory()在/var/run/dpdk/rte目录下创建hugepage_info文件,并通过mmap()将文件映射到DPDK进程的虚拟地址空间,之后将internal_config的hugepage_info数组拷贝到该文件中。
  • 按照secondary方式初始化
调用eal_hugepage_info_read()来初始化secondary进程,该func打开primary进程创建的/var/run/dpdk/rte目录下的hugepage_info文件,将文件中的信息拷贝到internal_config结构中的hugepage_info数组当中。
  1. 日志初始化
调用rte_eal_log_init()来初始化文件,该func中调用系统调用fopencookie和openlog来初始化日志文件。
  1. VFIO初始化(可选)
VFIO是否进行初始化由DPDK编译时是否开启了VFIO_PRESENT编译选项决定,开启了该编译选项时,调用rte_eal_vfio_setup()来初始化VFIO。初始化过程主要是判断当前系统中是否存在vifo模块。
  1. 内存域初始化
rte_eal_memzone_init()初始化的是rte_config中的mem_config下的memzone成员。同样根据DPDK进程是primary还是secondary分为不同的初始化过程。
  • 按照primary方式初始化
调用rte_fbarray_init()来初始化primary进程的memzone。DPDK中rte_memzone结构体是表示memzone的结构,需要为其分配内存空间;分配内存空间之前需要确定空间大小;DPDK允许RTE_MAX_MEMZONE(默认值2560)个内存域,那么就需要sizeof(rte_memzone) * RTE_MAX_MEMZONE大小的内存空间;同时还需要一个位图结构来表示这些内存域的使用情况,cal_data_size()完成了上述的计算工作。
确定完需要的内存大小之后,通过eal_get_virtual_area()确定一个虚拟地址data作为rte_memzone结构体的存储地址。确定好地址之后,接下来会根据是否设置了no_shconf对data进行不同的处理:如果是非共享模式,那么需要在虚拟地址data上创建一个匿名的内存映射;如果是共享的,会在/var/run/dpdk/rte目录下创建一个名称为fbarray_memzone的文件,并将文件通过mmap映射到虚拟地址data上。上面提到的一些信息,会存放在一个mem_area类型的结构体当中,并插入到模块全局列表mem_area_tailq中;另外还会存放在mem_config下的memzone成员当中。
  • 按照secondary方式初始化
调用rte_fbarray_attach()来初始化secondary进程的memzone,部分过程和rte_fbarry_init()相同,但是会直接打开fbarray_memzone的文件将该文件mmap()到虚拟地址空间data上。
  1. 内存初始化
调用rte_eal_memory_init()来初始化内存配置,分为多个步骤进行。
24.1. 内存段初始化
首先调用rte_eal_memseg_init()来初始化memseg。初始化memseg之前,需要将当前进程的资源限制进行下修改,更改可打开文件的最大数量。初始化memseg同样按照进程是primary还是secondary分别执行不同的初始化过程。
  • 按照primary方式初始化
编译DPDK时,会通过宏定义指定编译的系统架构为32位还是64位,这也决定了memseg的初始化方式的不同。
以64位系统初始化memseg_primary_init()为例,首先会初始化一个本地的结构体memtype的数组,结构体内包含hugepagesize和socket_id两个值,也就是说数组大小n_memtypes为前面统计出的大页内存种类数量*主机socket数量。memtype数组信息填充之后,计算出一些内存大小限制条件:通过宏定义RTE_MAX_MEM_MB(值为512GB)、RTE_MAX_MEM_MB_PER_TYPE(值为64GB)和n_memtypes确定单种类内存的大小限制max_mem_per_type和总的内存大小限制max_mem;通过RTE_MAX_MEMSEG_LISTS和n_memtypes确定每种memtype的segment数量。
接下来需要为每种memtype创建segment list,针对每种类型,需要将下列内容列入考虑范围后,得出一个精确的segment大小。
  • 该memtype可用内存总量
  • 每个segment允许的内存总量
  • 适配内存总量需要的segment数量
  • memtype允许的segment数量
  • 每个segment list允许的segment数量
  • 允许创建的segment list数量
确定好这些限制之后,调用eal_memseg_list_init()初始化每一个memseg list,该func最终是调用rte_fbarray_init()来实现初始化;然后是调用eal_memseg_list_alloc()来分配空间,该func最终是调用eal_get_virtual_area()来实现虚拟地址的分配。
  • 按照secondary方式初始化
调用memseg_secondary_init()来初始化memseg,该func中首先调用rte_fbarray_attach()来实现从文件获取primary进程memseg分配情况,然后通过调用eal_memseg_list_alloc()并最终调用eal_get_virtual_area()来分配虚拟内存空间。出现的几个func在前面memzone的初始化过程中已经出现过,这里不再具体说明。
 
内存初始化过程很长,未完待续……
 
往期回顾:
06.DPU网络开发SDK—DPDK(五)
05.DPU网络开发SDK—DPDK(四)
04.DPU网络开发SDK—DPDK(三)

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

您正在使用您的 WordPress.com 账号评论。 注销 /  更改 )

Twitter picture

您正在使用您的 Twitter 账号评论。 注销 /  更改 )

Facebook photo

您正在使用您的 Facebook 账号评论。 注销 /  更改 )

Connecting to %s