Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3569456
  • 博文数量: 205
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7385
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-23 18:56
个人简介

将晦涩难懂的技术讲的通俗易懂

文章分类

全部博文(205)

文章存档

2024年(8)

2023年(9)

2022年(4)

2021年(12)

2020年(8)

2019年(18)

2018年(19)

2017年(9)

2016年(26)

2015年(18)

2014年(54)

2013年(20)

分类: LINUX

2018-07-01 12:01:11

dpdk 17.11 支持iova

——lvyilong316

bus概念的支持

dpdk 16.11是没有bud这一层抽象的,直接通过rte_eal_initàrte_eal_pci_init调用pci设备的初始化。也就是dpdk 16.11只支持pci这一种总线设备。但是到了dpdk17.11引入了bus的概念。

rte_eal_inità rte_bus_scan 进行bus的初始化。

l  rte_bus_scan

点击(此处)折叠或打开

  1. int
  2. rte_bus_scan(void)
  3. {
  4.          int ret;
  5.          struct rte_bus *bus = NULL;
  6.  
  7.          TAILQ_FOREACH(bus, &rte_bus_list, next) {
  8.                    ret = bus->scan();
  9.                    if (ret)
  10.                             RTE_LOG(ERR, EAL, "Scan for (%s) bus failed.\n",
  11.                                      bus->name);
  12.          }
  13.  
  14.          return 0;
  15. }

这个函数会调用rte_bus_list上注册的所有busscan函数,这些bus是通过rte_bus_register函数注册上去的,而宏RTE_REGISTER_BUS又是rte_bus_register的封装。例如pci bus的注册过程如下所示。

点击(此处)折叠或打开

  1. struct rte_pci_bus rte_pci_bus = {
  2.          .bus = {
  3.                    .scan = rte_pci_scan,
  4.                    .probe = rte_pci_probe,
  5.                    .find_device = pci_find_device,
  6.                    .plug = pci_plug,
  7.                    .unplug = pci_unplug,
  8.                    .parse = pci_parse,
  9.                    .get_iommu_class = rte_pci_get_iommu_class,
  10.          },
  11.          .device_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.device_list),
  12.          .driver_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.driver_list),
  13. };

RTE_REGISTER_BUS(pci, rte_pci_bus.bus);

所以pci设备的初始化是通过以下路径完成的:rte_eal_inità rte_bus_scanà rte_pci_scan。而对应驱动的加载则是通过如下调用完成的:rte_eal_inità rte_bus_probeà rte_pci_probe完成的

iova支持

所谓iova可以理解为io的地址,或者说是DMA的地址。在17.11中很多之前的phys_addr关键字都被替换为了iova关键字了。因为在之前dpdk不感知iommu,设置DMA都是用物理地址,但是在借助iommu时就可以使用虚拟地址进行DMA了。在rte_eal_init中还有如下调用:

         /* autodetect the iova mapping mode (default is iova_pa) */

         rte_eal_get_configuration()->iova_mode = rte_bus_get_iommu_class();

通过调用对应busget_iommu_class函数,来获取iova的模式。

l  rte_bus_get_iommu_class

点击(此处)折叠或打开

  1. enum rte_iova_mode
  2. rte_bus_get_iommu_class(void)
  3. {
  4.          int mode = RTE_IOVA_DC;
  5.          struct rte_bus *bus;
  6.  
  7.          TAILQ_FOREACH(bus, &rte_bus_list, next) {
  8.  
  9.                    if (bus->get_iommu_class)
  10.                             mode |= bus->get_iommu_class();
  11.          }
  12.  
  13.          if (mode != RTE_IOVA_VA) {
  14.                    /* Use default IOVA mode */
  15.                    mode = RTE_IOVA_PA;
  16.          }
  17.          return mode;
  18. }

    iova有两种模式,一种是RTE_IOVA_VA,一种是RTE_IOVA_PARTE_IOVA_VA表示DMA操作可以使用虚拟地址表示目的地址,而RTE_IOVA_PA则表示DMA必须要用物理地址作为目的地址。

我们以pciget_iommu_class为例,其对应实现为

l  rte_pci_get_iommu_class

点击(此处)折叠或打开

  1. /*
  2.  * Get iommu class of PCI devices on the bus.
  3.  */
  4. enum rte_iova_mode
  5. rte_pci_get_iommu_class(void)
  6. {
  7.          bool is_bound;
  8.          bool is_vfio_noiommu_enabled = true;
  9.          bool has_iova_va;
  10.          bool is_bound_uio;
  11.          bool iommu_no_va;
  12.     /* 设备是否已经bonding相应驱动 */
  13.          is_bound = pci_one_device_is_bound();
  14.          if (!is_bound)
  15.                    return RTE_IOVA_DC;
  16.  
  17.          has_iova_va = pci_one_device_has_iova_va(); /*是否有设备是否支持iova_va */
  18.          is_bound_uio = pci_one_device_bound_uio(); /*是否有设备绑定了uio*/
  19.          iommu_no_va = !pci_devices_iommu_support_va(); /*iommu是否支持使用虚拟地址作为iova*/
  20.          if (has_iova_va && !is_bound_uio && !is_vfio_noiommu_enabled &&
  21.                             !iommu_no_va)
  22.                    return RTE_IOVA_VA;
  23.  
  24.          if (has_iova_va) {
  25.                    RTE_LOG(WARNING, EAL, "Some devices want iova as va but pa will be used because.. ");
  26.                    if (is_vfio_noiommu_enabled)
  27.                             RTE_LOG(WARNING, EAL, "vfio-noiommu mode configured\n");
  28.                    if (is_bound_uio)
  29.                             RTE_LOG(WARNING, EAL, "few device bound to UIO\n");
  30.                    if (iommu_no_va)
  31.                             RTE_LOG(WARNING, EAL, "IOMMU does not support IOVA as VA\n");
  32.          }
  33.  
  34.          return RTE_IOVA_PA;
  35. }

决定pci设备最终的iova mode的条件有三个。首先是has_iova_va,这个通过pci_one_device_has_iova_va来判断。

l  pci_one_device_has_iova_va

点击(此处)折叠或打开

  1. static inline int
  2. pci_one_device_has_iova_va(void)
  3. {
  4.          struct rte_pci_device *dev = NULL;
  5.          struct rte_pci_driver *drv = NULL;
  6.  
  7.          FOREACH_DRIVER_ON_PCIBUS(drv) {
  8.                    if (drv && drv->drv_flags & RTE_PCI_DRV_IOVA_AS_VA) {
  9.                             FOREACH_DEVICE_ON_PCIBUS(dev) {
  10.                                      if (dev->kdrv == RTE_KDRV_VFIO &&
  11.                                          rte_pci_match(drv, dev))
  12.                                                return 1;
  13.                             }
  14.                    }
  15.          }
  16.          return 0;
  17. }

从这个函数的逻辑可以看出当设备对应的pmd驱动支持RTE_PCI_DRV_IOVA_AS_VA,且设备当前绑定了vfio驱动,则认为这个设备支持RTE_IOVA_VA

第二个条件为is_bound_uio,通过pci_one_device_bound_uio来获取,其实就是判断是否有pci设备绑定了uio驱动,我们指定uio驱动是不支持iommu的,一旦有绑定uio的设备就不能使用RTE_IOVA_VA

第三个条件为iommu_no_va,这个条件通过pci_devices_iommu_support_va来获取,也就是多去pci设备目录下的iommu特性,判断iommu是否可以支持用虚拟地址作为DMA地址。如下所示:

$cat /sys/bus/pci/devices/0000\:01\:00.0/iommu/intel-iommu/cap

8d2078c106f0466

    所以判断dpdk最终是否能够使用虚拟地址作为DMA地址除了设备绑定vfiopmd支持之外,还需要有iommu对应特性的支持以及不能有设备绑定uio

另外注意在rte_eal_init中还有如下判断逻辑,即如果当前主机加载了kni模块就不能在使用RTE_IOVA_VA模式了,因为kni需要依赖RTE_IOVA_PA

点击(此处)折叠或打开

  1. /* Workaround for KNI which requires physical address to work */
  2.          if (rte_eal_get_configuration()->iova_mode == RTE_IOVA_VA &&
  3.                             rte_eal_check_module("rte_kni") == 1) {
  4.                    rte_eal_get_configuration()->iova_mode = RTE_IOVA_PA;
  5.                    RTE_LOG(WARNING, EAL,
  6.                             "Some devices want IOVA as VA but PA will be used because.. "
  7.                             "KNI module inserted\n");
  8.          }

阅读(5809) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~