内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

dpdk pci设备初始化

2019-01-22 11:16 出处:清屏网 人气: 评论(0

dpdk pci 设备初始化

(代码来自dpdk16.11)

——lvyilong316

dpdk pci 设备的初始化主要由 rte_eal_pci_init 函数完成,它是在 rte_eal_init 中被调用的。 rte_eal_init à rte_eal_pci_init

l   rte_eal_pci_init

点击( 此处 )折叠或打开

  1. int
  2. rte_eal_pci_init ( void )
  3. {
  4.       / * for debug purposes , PCI can be disabled * /
  5.      if ( internal_config . no_pci )
  6.         return 0 ;
  7.  
  8.       if ( rte_eal_pci_scan ( ) < 0 ) {
  9.            RTE_LOG ( ERR , EAL , "%s(): Cannot scan PCI bus\n" , __func__ ) ;
  10.            return - 1 ;
  11.  
  12.       }
  13.      return 0 ;
  14. }

如果配置了 no_pci 则直接返回,否则调用 rte_eal_pci_scan

l   rte_eal_pci_scan

这个函数主要是遍历系统的 /sys/bus/pci/devices 目录中的每个子目录, /sys/bus/pci/devices 目录中每个子目录都对应一个 pci

设备,如下图所示。

点击( 此处 )折叠或打开

  1. int
  2. rte_eal_pci_scan ( void )
  3. {
  4.     struct dirent * e ;
  5.     DIR * dir ;
  6.     char dirname [ PATH_MAX ] ;
  7.     struct rte_pci_addr addr ;
  8.      / * 打开 / sys / bus / pci / devices 目录 * /
  9.     dir = opendir ( pci_get_sysfs_path ( ) ) ;
  10.      if ( dir = = NULL ) {
  11.         RTE_LOG ( ERR , EAL , "%s(): opendir failed: %s\n" ,
  12.             __func__ , strerror ( errno ) ) ;
  13.         return - 1 ;
  14.      }
  15.  
  16.      while ( ( e = readdir ( dir ) ) ! = NULL ) {
  17.          if ( e - > d_name [ 0 ] = = ' . ' )
  18.             continue ;
  19.          / * 根据pci设备标识(地址)如:0000 : 01 : 11 . 4初始化addr中的domain,bus,devid,function * /
  20.          if ( parse_pci_addr_format ( e - > d_name , sizeof ( e - > d_name ) , & addr ) ! = 0 )
  21.             continue ;
  22.  
  23.         snprintf ( dirname , sizeof ( dirname ) , "%s/%s" ,
  24.                 pci_get_sysfs_path ( ) , e - > d_name ) ;
  25.          / * 读取每个pci设备的目录,创建并初始化rte_pci_device结构,加入全局链表pci_device_list * /
  26.          if ( pci_scan_one ( dirname , & addr ) < 0 )
  27.             goto error ;
  28.      }
  29.     closedir ( dir ) ;
  30.     return 0 ;
  31.  
  32. error :
  33.     closedir ( dir ) ;
  34.     return - 1 ;
  35. }

pci_scan_one 函数又会去读取每个子目录,每个子目录中包含这个 pci 设备的属性信息。如下图所示。

根据这些属性信息初始化一个 pci 设备,这个 pci 设备用 rte_pci_device 结构表示。

l   pci 地址和 pci id

这里说明一点,就是 pci 地址和 pci id 的区别,在 /sys/bus/pci/devices 目录下中看到的数字是 pci 地址,在 dpdk 中使用 rte_pci_addr 结构表示。

点击( 此处 )折叠或打开

  1. struct rte_pci_addr {
  2.     uint16_t domain ; / * * < Device domain * /
  3.     uint8_t bus ; / * * < Device bus * /
  4.     uint8_t devid ; / * * < Device ID * /
  5.     uint8_t function ; / * * < Device function . * /
  6. } ;

pci id class_id vendor_id device_id 等组成。 dpdk 中用 rte_pci_id 结构表示。

点击( 此处 )折叠或打开

  1. struct rte_pci_id {
  2.     uint32_t class_id ; / * * < Class ID ( class , subclass , pi ) or RTE_CLASS_ANY_ID . * /
  3.     uint16_t vendor_id ; / * * < Vendor ID or PCI_ANY_ID . * /
  4.     uint16_t device_id ; / * * < Device ID or PCI_ANY_ID . * /
  5.     uint16_t subsystem_vendor_id ; / * * < Subsystem vendor ID or PCI_ANY_ID . * /
  6.     uint16_t subsystem_device_id ; / * * < Subsystem device ID or PCI_ANY_ID . * /
  7. } ;

l   pci_scan_one

点击( 此处 )折叠或打开

  1. static int pci_scan_one ( const char * dirname , const struct rte_pci_addr * addr )
  2. {
  3.     char filename [ PATH_MAX ] ;
  4.     unsigned long tmp ;
  5.     struct rte_pci_device * dev ;
  6.     char driver [ PATH_MAX ] ;
  7.      int ret ;
  8.  
  9.     dev = malloc ( sizeof ( * dev ) ) ;
  10.      if ( dev = = NULL )
  11.         return - 1 ;
  12.  
  13.     memset ( dev , 0 , sizeof ( * dev ) ) ;
  14.     dev - > addr = * addr ;
  15.      / * 根据设备属性,初始化设备的pci id * /
  16.      / * get vendor id * /
  17.     snprintf ( filename , sizeof ( filename ) , "%s/vendor" , dirname ) ;
  18.      if ( eal_parse_sysfs_value ( filename , & tmp ) < 0 ) {
  19.         free ( dev ) ;
  20.         return - 1 ;
  21.      }
  22.     dev - > id . vendor_id = ( uint16_t ) tmp ;
  23.  
  24.      / * get device id * /
  25.     snprintf ( filename , sizeof ( filename ) , "%s/device" , dirname ) ;
  26.      if ( eal_parse_sysfs_value ( filename , & tmp ) < 0 ) {
  27.         free ( dev ) ;
  28.         return - 1 ;
  29.      }
  30.     dev - > id . device_id = ( uint16_t ) tmp ;
  31.  
  32.      / * get subsystem_vendor id * /
  33.     snprintf ( filename , sizeof ( filename ) , "%s/subsystem_vendor" ,
  34.          dirname ) ;
  35.      if ( eal_parse_sysfs_value ( filename , & tmp ) < 0 ) {
  36.         free ( dev ) ;
  37.         return - 1 ;
  38.      }
  39.     dev - > id . subsystem_vendor_id = ( uint16_t ) tmp ;
  40.  
  41.      / * get subsystem_device id * /
  42.     snprintf ( filename , sizeof ( filename ) , "%s/subsystem_device" ,
  43.          dirname ) ;
  44.      if ( eal_parse_sysfs_value ( filename , & tmp ) < 0 ) {
  45.         free ( dev ) ;
  46.         return - 1 ;
  47.      }
  48.     dev - > id . subsystem_device_id = ( uint16_t ) tmp ;
  49.  
  50.      / * get class_id * /
  51.     snprintf ( filename , sizeof ( filename ) , "%s/class" ,
  52.          dirname ) ;
  53.      if ( eal_parse_sysfs_value ( filename , & tmp ) < 0 ) {
  54.         free ( dev ) ;
  55.         return - 1 ;
  56.      }
  57.      / * the least 24 bits are valid : class , subclass , program interface * /
  58.     dev - > id . class_id = ( uint32_t ) tmp & RTE_CLASS_ANY_ID ;
  59.  
  60.      / * get max_vfs * /
  61.      / * 获取设备的vf个数 * /
  62.     dev - > max_vfs = 0 ;
  63.     snprintf ( filename , sizeof ( filename ) , "%s/max_vfs" , dirname ) ;
  64.      if ( ! access ( filename , F_OK ) & &
  65.      eal_parse_sysfs_value ( filename , & tmp ) = = 0 )
  66.         dev - > max_vfs = ( uint16_t ) tmp ;
  67.      else {
  68.          / * for non igb_uio driver , need kernel version > = 3 . 8 * /
  69.         snprintf ( filename , sizeof ( filename ) ,
  70.              "%s/sriov_numvfs" , dirname ) ;
  71.          if ( ! access ( filename , F_OK ) & &
  72.          eal_parse_sysfs_value ( filename , & tmp ) = = 0 )
  73.             dev - > max_vfs = ( uint16_t ) tmp ;
  74.      }
  75.  
  76.      / * get numa node * /
  77.      / * 如果设备开启numa,则在设备属性中会有numa_node的属性 * /
  78.     snprintf ( filename , sizeof ( filename ) , "%s/numa_node" ,
  79.          dirname ) ;
  80.      if ( access ( filename , R_OK ) ! = 0 ) {
  81.          / * if no NUMA support , set default to 0 * /
  82.         dev - > device . numa_node = 0 ;
  83.      } else {
  84.          if ( eal_parse_sysfs_value ( filename , & tmp ) < 0 ) {
  85.             free ( dev ) ;
  86.             return - 1 ;
  87.          }
  88.         dev - > device . numa_node = tmp ;
  89.      }
  90.  
  91.      / * parse resources * /
  92.      / * 获取pci设备映射的地址空间 * /
  93.     snprintf ( filename , sizeof ( filename ) , "%s/resource" , dirname ) ;
  94.      if ( pci_parse_sysfs_resource ( filename , dev ) < 0 ) {
  95.         RTE_LOG ( ERR , EAL , "%s(): cannot parse resource\n" , __func__ ) ;
  96.         free ( dev ) ;
  97.         return - 1 ;
  98.      }
  99.  
  100.      / * parse driver * /
  101.      / * 获取pci设备关联的驱动,如 / sys / bus / pci / devices / 0000 \ : 01 \ : 10 . 2 / driver * /
  102.     snprintf ( filename , sizeof ( filename ) , "%s/driver" , dirname ) ;
  103.      / * 使用readlink获取驱动的名称 * /
  104.     ret = pci_get_kernel_driver_by_path ( filename , driver ) ;
  105.      if ( ret < 0 ) {
  106.         RTE_LOG ( ERR , EAL , "Fail to get kernel driver\n" ) ;
  107.         free ( dev ) ;
  108.         return - 1 ;
  109.      }
  110.  
  111.      if ( ! ret ) { / * 如果设备有关联驱动 * /
  112.          if ( ! strcmp ( driver , "vfio-pci" ) )
  113.             dev - > kdrv = RTE_KDRV_VFIO ; / * 目前dpdk只支持三种驱动 : vfio - pci , igb_uio , uio_pci_generic * /
  114.          else if ( ! strcmp ( driver , "igb_uio" ) )
  115.             dev - > kdrv = RTE_KDRV_IGB_UIO ;
  116.          else if ( ! strcmp ( driver , "uio_pci_generic" ) )
  117.             dev - > kdrv = RTE_KDRV_UIO_GENERIC ;
  118.          else
  119.             dev - > kdrv = RTE_KDRV_UNKNOWN ;
  120.      } else
  121.         dev - > kdrv = RTE_KDRV_NONE ;
  122.  
  123.      / * device is valid , add in list ( sorted ) * /
  124.      if ( TAILQ_EMPTY ( & pci_device_list ) ) { / * 如果pci_device_list为空,直接将当前设备插入链表 * /
  125.         rte_eal_device_insert ( & dev - > device ) ;
  126.         TAILQ_INSERT_TAIL ( & pci_device_list , dev , next ) ;
  127.      } else {
  128.         struct rte_pci_device * dev2 ;
  129.          int ret ;
  130.          / * 将pci设备按照pci地址由大到小插入pci_device_list * /
  131.         TAILQ_FOREACH ( dev2 , & pci_device_list , next ) {
  132.             ret = rte_eal_compare_pci_addr ( & dev - > addr , & dev2 - > addr ) ;
  133.              if ( ret > 0 )
  134.                 continue ;
  135.  
  136.              if ( ret < 0 ) {
  137.                 TAILQ_INSERT_BEFORE ( dev2 , dev , next ) ;
  138.                 rte_eal_device_insert ( & dev - > device ) ;
  139.              } else { / * already registered * /
  140.                 dev2 - > kdrv = dev - > kdrv ;
  141.                 dev2 - > max_vfs = dev - > max_vfs ;
  142.                 memmove ( dev2 - > mem_resource , dev - > mem_resource ,
  143.                     sizeof ( dev - > mem_resource ) ) ;
  144.                 free ( dev ) ;
  145.              }
  146.             return 0 ;
  147.          }
  148.         rte_eal_device_insert ( & dev - > device ) ;
  149.         TAILQ_INSERT_TAIL ( & pci_device_list , dev , next ) ;
  150.      }
  151.  
  152.     return 0 ;
  153. }

其中一步需要提一下,就是打开 pci 设备目录下的 resource 文件,获取 pci 设备的地址空间(也就是 pci BAR ),并将其保存在 dev->mem_resource 中。这个 pci 地址在后面 pci 资源映射中会用到。

通过 readlink pci 设备目录下的 driver 属性获取 pci 设备的驱动,目前 dpdk 只支持三种 pci 驱动: vfio-pci,igb_uio,uio_pci_generic

最后将创建初始化好的 rte_pci_device 结构按照 pci 地址由大到小插入全局链表 pci_device_list 中。相关数据结构关系如下图所示。


分享给小伙伴们:
本文标签: dpdkpci

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号