本文基于DPDK-1.8.0分析。
网卡驱动模型一般包含三层,即,PCI总线设备、网卡设备以及网卡设备的私有数据结构,即将设备的共性一层层的抽象,PCI总线设备包含网卡设备,网卡设备又包含其私有数据结构。在DPDK中,首先会注册设备驱动,然后查找当前系统有哪些PCI设备,并通过PCI_ID为PCI设备找到对应的驱动,最后调用驱动初始化设备。
一、网卡驱动注册
以e1000网卡驱动为例说明。
在1.8.0版本中,网卡驱动的注册使用了一种奇技淫巧的方法,使用GCC attribute扩展属性的constructor属性,使得网卡驱动的注册在程序MAIN函数之前就执行了。
static struct rte_driver pmd_igb_drv = {
.type = PMD_PDEV,
.init = rte_igb_pmd_init,
};
static struct rte_driver pmd_igbvf_drv = {
.type = PMD_PDEV,
.init = rte_igbvf_pmd_init,
};
PMD_REGISTER_DRIVER(pmd_igb_drv);
PMD_REGISTER_DRIVER(pmd_igbvf_drv);
其中PMD_REGISTER_DRIVER()宏的定义如下:
#define PMD_REGISTER_DRIVER(d)\
void devinitfn_ ##d(void);\
void __attribute__((constructor, used)) devinitfn_ ##d(void)\
{\
rte_eal_driver_register(&d);\
}
使用attribute的constructor属性,在MAIN函数执行前,就执行rte_eal_driver_register()函数,将pmd_igb_drv驱动挂到全局dev_driver_list链表上。
二、扫描当前系统有哪些PCI设备
调用rte_eal_init()--->rte_eal_pci_init()函数,查找当前系统中有哪些网卡,分别是什么类型,并将它们挂到全局链表pci_device_list上。
1、首先初始化全局链表pci_driver_list、pci_device_list。用于挂载PCI驱动及PCI设备。
2、pci_scan()通过读取/sys/bus/pci/devices/目录下的信息,扫描当前系统的PCI设备,并初始化,并按照PCI地址从大到小的顺序挂在到pci_debice_list上。
int
rte_eal_pci_init(void)
{
TAILQ_INIT(&pci_driver_list);
TAILQ_INIT(&pci_device_list);
pci_res_list = RTE_TAILQ_RESERVE_BY_IDX(RTE_TAILQ_PCI,
mapped_pci_res_list);
/* for debug purposes, PCI can be disabled */
if (internal_config.no_pci)
return 0;
if (pci_scan() < 0) {
RTE_LOG(ERR, EAL, "%s(): Cannot scan PCI bus\n", __func__);
return -1;
}
#ifdef VFIO_PRESENT
pci_vfio_enable();
if (pci_vfio_is_enabled()) {
/* if we are primary process, create a thread to communicate with
* secondary processes. the thread will use a socket to wait for
* requests from secondary process to send open file descriptors,
* because VFIO does not allow multiple open descriptors on a group or
* VFIO container.
*/
if (internal_config.process_type == RTE_PROC_PRIMARY &&
pci_vfio_mp_sync_setup() < 0)
return -1;
}
#endif
return 0;
}
pcai_scan()通过读取/sys/bus/pci/devices/目录下相关PCI设备的如下文件,获取对应的信息,初始化struct rte_pci_device数据结构,并将其按照PCI地址从大到小的顺序挂到pci_device_list链表上。
struct rte_pci_device {
TAILQ_ENTRY(rte_pci_device) next; /**< Next probed PCI device. */
struct rte_pci_addr addr; /**< PCI location. */
struct rte_pci_id id; /**< PCI ID. */
struct rte_pci_resource mem_resource[PCI_MAX_RESOURCE]; /**< PCI Memory Resource */
struct rte_intr_handle intr_handle; /**< Interrupt handle */
const struct rte_pci_driver *driver; /**< Associated driver */
uint16_t max_vfs; /**< sriov enable if not zero */
int numa_node; /**< NUMA node connection */
struct rte_devargs *devargs; /**< Device user arguments */
};
root@Ubuntu:~# ls -ltr /sys/bus/pci/devices/0000\:00\:09.0/
total 0
-rw-r--r-- 1 root root 4096 Nov 15 12:18 uevent
lrwxrwxrwx 1 root root 0 Nov 15 12:18 subsystem -> ../../../bus/pci
-r--r--r-- 1 root root 4096 Nov 15 12:19 class
-r--r--r-- 1 root root 4096 Nov 15 12:19 vendor
-r--r--r-- 1 root root 4096 Nov 15 12:19 device
-rw-r--r-- 1 root root 256 Nov 15 12:19 config
-r--r--r-- 1 root root 4096 Nov 15 12:19 local_cpus
-r--r--r-- 1 root root 4096 Nov 15 12:19 irq
-r--r--r-- 1 root root 4096 Nov 15 12:20 resource
drwxr-xr-x 2 root root 0 Nov 15 12:20 power
-r--r--r-- 1 root root 4096 Nov 19 14:33 subsystem_vendor
-r--r--r-- 1 root root 4096 Nov 19 14:33 subsystem_device
-r--r--r-- 1 root root 4096 Nov 19 14:33 numa_node
-rw------- 1 root root 8 Nov 19 14:58 resource2
-rw------- 1 root root 131072 Nov 19 14:58 resource0
--w------- 1 root root 4096 Nov 19 14:58 reset
--w--w---- 1 root root 4096 Nov 19 14:58 rescan
--w--w---- 1 root root 4096 Nov 19 14:58 remove
-rw-r--r-- 1 root root 4096 Nov 19 14:58 msi_bus
-r--r--r-- 1 root root 4096 Nov 19 14:58 modalias
-r--r--r-- 1 root root 4096 Nov 19 14:58 local_cpulist
-rw------- 1 root root 4096 Nov 19 14:58 enable
-r--r--r-- 1 root root 4096 Nov 19 14:58 dma_mask_bits
-r--r--r-- 1 root root 4096 Nov 19 14:58 consistent_dma_mask_bits
-rw-r--r-- 1 root root 4096 Nov 19 14:58 broken_parity_status
drwxr-xr-x 3 root root 0 Nov 19 15:31 uio
lrwxrwxrwx 1 root root 0 Nov 19 15:31 driver -> ../../../bus/pci/drivers/igb_uio
-rw-r--r-- 1 root root 4096 Nov 19 15:32 max_vfs
目录名:就是PCI设备的地址,记录在struct rte_pci_addr数据结构中。
vendor文件:获取PCI_ID.vendor_id。
device文件:获取PCI_ID.device_id。
subsystem_vendor文件:获取PCI_ID.subsystem_vendor_id。
subsystem_device文件:获取PCI_ID.subsystem_device_id。
numa_node文件:获取PCI设备属于哪个CPU socket。
resource文件:获取PCI设备的在地址总线上的物理地址,以及物理地址空间的大小,记录在struct rte_pci_resouce数据结构中。