本文以X710网卡设备为例,介绍网卡的scan和probe流程的;通过本篇文章的介绍可以大致了解UIO驱动、PMD驱动之间的关联关系以及如何确认网卡对应的PMD驱动的。针对probe流程处理了解的比较片面,有了解比较深的同学,希望能找您学习一下。
1、BUS总线设备扫描
ret_bus_scan函数在目录dpdk/lib/librte_eal/common/eal_common_bus.c
初始化流程在EAL环境初始化调用 ret_bus_scan函数完成的。内部会调用各自类型的bus->scan接口;目的是扫描所有该类型bus下注册的设备。
下面有bus设备注册、以PCI设备的注册来详细说明注册流程。
1.1 Bus设备的注册
是由宏RTE_REGISTER_BUS,是一个构造函数,在程序main启动前完成的注册;将bus类型注册到全局结构rte_bus_list 链表上;目前有6中设备类型,本文主要关心网卡设备的。目前查询宏使用的地方如下。(对应宏所在文件:dpdk/lib/librte_eal/include/rte_bus.h)
1/**
2 * Helper for Bus registration.
3 * The constructor has higher priority than PMD constructors.
4 */
5#define RTE_REGISTER_BUS(nm, bus) \
6RTE_INIT_PRIO(businitfn_ ##nm, BUS) \
7{\
8 (bus).name = RTE_STR(nm);\
9 rte_bus_register(&bus); \
10}
11RTE_REGISTER_BUS(FSL_DPAA_BUS_NAME, rte_dpaa_bus.bus);/*drivers/bus/dpaa/dpaa_bus.c*/
12RTE_REGISTER_BUS(FSLMC_BUS_NAME, rte_fslmc_bus.bus)/*drivers/bus/fslmc/fslmc_bus.c*/
13RTE_REGISTER_BUS(IFPGA_BUS_NAME, rte_ifpga_bus);/*drivers/bus/ifpga/ifpga_bus.c*/
14RTE_REGISTER_BUS(pci, rte_pci_bus.bus);/*drivers/bus/pci/pci_common.c*/
15RTE_REGISTER_BUS(vdev, rte_vdev_bus);/*drivers/bus/vdev/vdev.c*/
16RTE_REGISTER_BUS(vmbus, rte_vmbus_bus.bus);/*drivers/bus/vmbus/vmbus_common.c*/
网卡设备BUS的注册
我们重点学习网卡设备的注册,网卡设备bus已经注册到全局变量rte_bus_list链表上。
1/*网卡bus的全局变量*/
2struct rte_pci_bus rte_pci_bus = {
3 .bus = { /*网卡的struct rte_bus设备内容*/
4 .scan = rte_pci_scan,
5 .probe = pci_probe,
6 .find_device = pci_find_device,
7 .plug = pci_plug,
8 .unplug = pci_unplug,
9 .parse = pci_parse,
10 .dma_map = pci_dma_map,
11 .dma_unmap = pci_dma_unmap,
12 .get_iommu_class = rte_pci_get_iommu_class,
13 .dev_iterate = rte_pci_dev_iterate,
14 .hot_unplug_handler = pci_hot_unplug_handler,
15 .sigbus_handler = pci_sigbus_handler,
16 },
17 /*实例化网卡设备后下挂到尾队列链表中*/
18 .device_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.device_list),
19 /*对应所有网卡的PMD驱动链表,下面重点说一下*/
20 .driver_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.driver_list),
21};
22/*网卡设备注册*/
23RTE_REGISTER_BUS(pci, rte_pci_bus.bus);
下面是通过gdb在全局bus链表上查找到pci设备全局变量注册信息。来打印看一下网卡设备注册后struct rte_bus 内容如下:
1/*打印对应pci 内容*/
2(gdb) p rte_bus_list.tqh_first[0].next.tqe_next[0].next.tqe_next[0].next.tqe_next[0]
3$7 = {
4 next = {/*双向链表情况*/
5 tqe_next = 0x399cd40 <rte_vdev_bus>,
6 tqe_prev = 0x399cb40 <rte_ifpga_bus>
7 },
8 name = 0x33f0f9f "pci", /*注册时的name*/
9 scan = 0x485183 <rte_pci_scan>,
10 probe = 0x489590 <rte_pci_probe>,/*最新的代码是pci_probe*/
11 find_device = 0x489a35 <pci_find_device>,
12 plug = 0x489cf8 <pci_plug>,
13 unplug = 0x489d26 <pci_unplug>,
14 parse = 0x489835 <pci_parse>,
15 dma_map = 0x489d90 <pci_dma_map>,
16 dma_unmap = 0x489e76 <pci_dma_unmap>,
17 conf = {
18 scan_mode = RTE_BUS_SCAN_BLACKLIST
19 },
20 get_iommu_class = 0x489fc8 <rte_pci_get_iommu_class>,
21 dev_iterate = 0x488dc0 <rte_pci_dev_iterate>,
22 hot_unplug_handler = 0x489bd6 <pci_hot_unplug_handler>,
23 sigbus_handler = 0x489c70 <pci_sigbus_handler>
24}
网卡设备scan操作
在EAL环境初始化函数rte_eal_init()->rte_bus_scan()遍历全局变量rte_bus_list,调用对应bus回调函数scan。
1int rte_bus_scan(void)
2{
3 int ret;
4 struct rte_bus *bus = NULL;
5 /*遍历bus链表,执行相应bus的scan函数*/
6 TAILQ_FOREACH(bus, &rte_bus_list, next) {
7 ret = bus->scan();
8 if (ret)
9 RTE_LOG(ERR, EAL, "Scan for (%s) bus failed.\n",
10 bus->name);
11 }
12 return 0;
13}
网卡设备调用rte_pci_scan(),函数的大致意思是打开并扫描目录“/sys/bus/pci/devices”下的PCI设备;一个目录名称表示一个pci设备;根据我们设置的网卡黑名单或白名单进行过滤,符合条件的设备,调用pci_scan_one实例化网卡设备rte_pci_device,并进行一些数据的填充(rte_pci_addr(网卡pci编号),struct rte_pci_id (网卡身份信息vendor_id、device_id来确定使用那个网卡驱动)max_vfs(sriov支持最大数量)、numa_node)。
pci_scan_one 函数只是实例化网卡设备并填充网卡的一些基本信息,并没有申请相应的资源,资源是由函数rte_bus_probe完成的。下面大概说一下具体操作流程。
1、打开目录并扫描目录“/sys/bus/pci/devices,”的网卡设备。
1root@domain 0000:00:07.0]# cd /sys/bus/pci/devices/
2[root@domain devices]# ls /*当前目录的pci设备编码如下*/
30000:00:00.0 0000:00:01.0 0000:00:01.1 0000:00:01.2 0000:00:01.3 0000:00:02.0 0000:00:03.0 0000:00:04.0 0000:00:05.0 0000:00:06.0 0000:00:07.0 0000:00:08.0
2、通过lspci | grep Eth ,我们可以知道哪些是Eth网卡设备
1[root@domain devices]# lspci | grep Eth /*但是只有四个数据以太网卡设备*/
200:03.0 Ethernet controlle