本文基于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数据结构中。