rte_eal_init
接上次内容继续对rte_eal_init()所做的工作进行分析。
18. 检查是否允许直接物理地址访问
rte_eal_using_phys_addrs()会去检查当前系统是否允许在进程虚拟地址空间直接访问物理地址。需要有两个支持条件:存在大页内存和能够进行虚拟地址到物理地址的转换。
-
rte_eal_has_hugepages()
该func就是检查internal_config中的no_hugetlbfs是否置位,置位表示不使用大页内存,默认情况下是不置位的,如果想置位,需要通过DPDK启动参数--no-huge来指定。
-
rte_mem_virt2phy()
该func的作用是随机传入一个进程中的虚拟内存地址,返回该虚拟内存地址映射到的物理地址。在这里,通过传入地址为0的虚拟地址,如果返回的结果不是无效的物理地址的话,则表示当前系统是允许的。
要实现从虚拟地址获得物理地址,需要借助/proc文件系统的支持。假设被寻找物理地址的虚拟地址值为virtaddr,首先要确保能够打开/proc/self/pagemap文件,获取到系统默认的页面大小pagesize。接下来获取virtaddr在虚拟地址空间的页面索引值vrit_pfn,即virt_pfn = virtaddr/pagesize。因为页面是虚拟内存映射到物理内存的最小单位,故需要获取到页面索引。
获取到virt_pfn之后,即可根据此值访问pagemap文件了。在Linux 64位操作系统中,需要54比特位空间存放物理页面的索引(参考Linux文档pagemap.txt得知),向上对齐为8字节空间。那么在pagemap中,offset为virt_pfn * 8的位置,即为索引为virt_pfn的虚拟内存页面映射到的物理内存页面的索引phy_page。
获取到phy_page之后,即可根据此值,还原出virtaddr映射到的物理地址phyaddr。特殊情况下,通过pagemap文件无法获取到phy_page的值,那么表示该系统不支持虚拟地址到物理地址的转换。
19. iova模式的处理
Internal_config中iova_mode指明设备访问物理内存的方式,一共有三种: RTE_IOVA_PA、RTE_IOVA_VA和RTE_IOVA_DC,分别表示直接访问内存物理地址,通过进程虚拟地址访问,不关心访问方式。默认情况下不关心具体访问方式,其他两种情况,需要通过DPDK启动参数--iova-mode来指定。
接下来详细介绍一下DPDK的iova模式。
DPDK是用户态的SDK,使用该SDK访问的是进程的虚拟地址,常规情况下,用户态进行IO操作需要通过系统调用进入内核态,在内核态完成设备与内存之间的交互,此种操作效率低下。为解决此问题,DPDK提供了一些API,能够实现在用户态模式下实现IO操作。然而硬件设备是不能直接操作进程的虚拟地址空间的,他能够操作的是物理地址,为此DPDK提供了IO虚拟地址IOVA来支持硬件设备可以直接操作进程的虚拟地址。
DPDK统一通过IOVA实现与硬件设备的数据交换,但需要区分IOVA是寻址的是内存物理地址还是进程的虚拟地址的情况,这就分区出了iova_pa和iova_va两种情况。
-
PA模式
该模式下,分配到DPDK存储区的iova都是实际的物理地址,当进程初始化iova时,虚拟地址空间会完成到物理内存页面的映射,这样当分配内存时,不会引发缺页异常的情况。但这种模式也存在一定的缺点,首先是需要在进程启动时需要root用户的特权,否则无法访问系统的页面映射,也就无法获取到内存区域的真实物理地址。其次是虚拟内存的分配受到物理内存的限制,当物理内存不是连续的内存片段时,iova的虚拟内存也只能是多个片段,不能在逻辑上将物理内存合并。如下图所示:
极端情况下,如果物理内存过于破碎,片段太多,会耗尽表示iova虚拟内存的数据结构,导致初始化失败。解决该问题的方法一般是使用1GB或者2MB的大页内存,并在机器启动时显式指定保留一定数量的大页内存。
-
VA模式
相比PA模式,VA模式下虚拟内存映射物理内存不会受到物理内存片段过多的影响,DPDK的eal层会依靠内核的基础设施将多个物理内存片段映射为一个连续的虚拟内存片段,该过程不需要root权限也可以执行。如下图所示:
internal_config中的iova_mode默认为RTE_IOVA_DC,即不关心是哪种。在这种情况下,DPDK初始化过程中需要自行判断该采取哪种模式。首先调用rte_bus_get_iommu_class()来获取iova的模式,该func中遍历rte_bus_list中每个bus,如果bus定义了get_iommu_class这个方法,那么就调用该方法获取该bus需要PA还是VA。如果所有的bus都是PA或者都是VA,那么get_iommu_class就返回具体的模式,如果有的bus需要PA模式而有的需要VA模式,那么仍然是返回DC模式。
调用了get_iommu_class之后如果仍然是DC模式,那么就需要最终确定选择PA还是VA了。如果不支持直接物理地址访问,选择VA;如果内核模块rte_kni有加载,选择PA;如果iommu是使能的,选择VA;除此之外的情况,选择PA。
确定iommu是不是使能的是通过调用is_iommu_enabled()实现,该func通过打开/sys/kernel/iommu_groups目录,统计其中的文件数量在确定。
未完待续……