本文基于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设备。
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; }
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
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。
三、PCI驱动注册
调用rte_eal_init()--->rte_eal_dev_init()函数,遍历dev_driver_list链表,执行网卡驱动对应的init的回调函数,注册PCI驱动。
/* Once the vdevs are initalized, start calling all the pdev drivers */ TAILQ_FOREACH(driver, &dev_driver_list, next) { if (driver->type != PMD_PDEV) continue; /* PDEV drivers don't get passed any parameters */ driver->init(NULL, NULL); }
以e1000网卡为例,执行的init回调函数就是rte_igb_pmd_init()函数。
static int rte_igb_pmd_init(const char *name __rte_unused, const char *params __rte_unused) { rte_eth_driver_register(&rte_igb_pmd); return 0; }
rte_eth_driver_register()主要是指定PCI设备的初始化函数为rte_eth_dev_init(),以及注册PCI驱动,将PCI驱动挂到pci_driver_list全局链表上。
void rte_eth_driver_register(struct eth_driver *eth_drv) { eth_drv->pci_drv.devinit = rte_eth_dev_init; rte_eal_pci_register(ð_drv->pci_drv); }
其中,rte_igb_pmd数据结构如下,指定e1000网卡的初始化函数是eth_igb_dev_init()。
static struct eth_driver rte_igb_pmd = { { .name = "rte_igb_pmd", .id_table = pci_id_igb_map, .drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC, }, .eth_dev_init = eth_igb_dev_init, .dev_private_size = sizeof(struct e1000_adapter), };
四、网卡初始化
调用rte_eal_init()--->rte_eal_pci_probe()函数,遍历pci_device_list和pci_driver_list链表,根据PCI_ID,将pci_device与pci_driver绑定,并调用pci_driver的init回调函数rte_eth_dev_init(),初始化PCI设备。
在rte_eal_pci_probe_one_driver()函数中,
1、首先通过比对PCI_ID的vendor_id、device_id、subsystem_vendor_id、subsystem_device_id四个字段判断pci设备和pci驱动是否匹配。
2、PCI设备和PCI驱动匹配后,调用pci_map_device()函数为该PCI设备创建map resource。具体如下:
b、初始化PCI设备的中断句柄。
rte_pci_device->intr_handler.fd = open(“/dev/uioID”, O_RDWR); /* ID为0或1,即uio0或uio1*/ rte_pci_device->intr_handler.type = RTE_INTR_HANDLER_UIO;
struct pci_map { void *addr; uint_t offset; uint_t size; uint_t phaddr; };root@Ubuntu:~# ls -ltr /sys/bus/pci/devices/0000\:00\:09.0/uio/uio0/maps/ total 0 drwxr-xr-x 2 root root 0 Nov 19 15:32 map0 root@Ubuntu:~# ls -ltr /sys/bus/pci/devices/0000\:00\:09.0/uio/uio0/maps/map0/ total 0 -r--r--r-- 1 root root 4096 Nov 19 15:32 size -r--r--r-- 1 root root 4096 Nov 19 15:32 offset -r--r--r-- 1 root root 4096 Nov 19 15:32 addr -r--r--r-- 1 root root 4096 Nov 19 15:34 name root@Ubuntu:~#
root@Ubuntu:~# cat /sys/bus/pci/devices/0000\:00\:09.0/uio/uio0/maps/map0/addr 0xf0440000 root@Ubuntu:~#root@Ubuntu:~# cat /sys/bus/pci/devices/0000\:00\:09.0/resource 0x00000000f0440000 0x00000000f045ffff 0x0000000000040200 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x000000000000d248 0x000000000000d24f 0x0000000000040101 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 root@Ubuntu:~#
e、将所有UIO设备的resource信息都记录在struct mapped_pci_resource数据结构中,并挂到全局链表pci_res_list上。
struct mapped_pci_resource { TAILQ_ENTRY(uio_resource) next; struct rte_pci_addr pci_addr; char path[PATH_MAX]; size_t nb_maps; struct uio_map maps[PCI_MAX_RESOURCE]; };
3、调用rte_eth_dev_init()初始化PCI设备。
a、首先,调用rte_eth_dev_allocate()在全局数组rte_eth_devices[]中分配一个网卡设备。并在全局数组rte_eth_dev_data[]中为网卡设备的数据域分配空间。
eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports];并调用rte_zmalloc()为网卡设备的私有数据结构分配空间。
rte_eth_dev->rte_eth_dev_data->dev_private = rte_zmalloc(sizeof(struct e1000_adapter));b、调用eth_igb_dev_init()初始化网卡设备。首先设置网卡设备的操作函数集,以及收包、发包函数。
eth_dev->dev_ops = ð_igb_ops; eth_dev->rx_pkt_burst = ð_igb_recv_pkts; eth_dev->tx_pkt_burst = ð_igb_xmit_pkts;c、注册中断处理函数。
rte_intr_callback_register(&(pci_dev->intr_handle), eth_igb_interrupt_handler, (void *)eth_dev);
五、设备与驱动相互映射关系图
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 7swz.com 版权所有 赣ICP备2024042798号-8
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务