从源码说明下DPDK网卡驱动初始化框架
最近有些个人时间了,那就写写东西吧。
DPDK网卡驱动初始化框架
先找下DPDK环境初始化中,与网卡驱动初始化相关的几个函数,以这几个函数为入口,再进行深入分析
● rte_bus_scan
● rte_bus_probe
rte_bus_scan
先来看下rte_bus_scan这个函数:
int
rte_bus_scan(void)
{
int ret;
struct rte_bus *bus = NULL;
TAILQ_FOREACH(bus, &rte_bus_list, next) {
ret = bus->scan();
if (ret)
RTE_LOG(ERR, EAL, "Scan for (%s) bus failed.\n",
bus->name);
}
return 0;
}
可以看到它是再遍历rte_bus_list这个链表,再分别各个bus的scan函数,这样的话就有三个疑问了
● rte_bus_list这里面放了什么东西?
● 元素是如何被放到rte_bus_list中的?
● scan做了什么?
先来看下rte_bus_list放了什么东西
分析代码可以看到,注册了各种不同总线上的ops函数,包含:
dpaa、fslmc、ifpga、pci、vdev以及vmbus,这里我们就重点关注pci吧
那么这一个个元素是如何被注册进rte_bus_list的呢,可以看到每个bus,都会调用RTE_REGISTER_BUS宏,而这个宏的定义就是调用了rte_bus_register函数,将不同bus注册到list上的。并且我们还可以看到RTE_REGISTER_BUS宏中使用的RTE_INIT_PRIO,其定义是如下:
#define RTE_INIT_PRIO(func, prio) \
static void __attribute__((constructor(RTE_PRIO(prio)), used)) func(void)
所以在main函数执行前,便会将rte_bus_list初始化完成
最后我们以pci scan为例,看下都做了些什么:
->rte_pci_scan//遍历/sys/bus/pci/devices路径下的内容,并格式化路径名称
->pci_scan_one //获得该pci总线下,设备的vendor id、device_id以及当前所使用的驱动,并放入rte_pci_bus.device_list链表中,rte_pci_bus.device_list链表在RTE_REGISTER_BUS宏中就已经被初始化了,直接插入即可
rte_bus_probe
rte_bus_scan看完了,再看下rte_bus_probe,大概也就知道是调用不同bus的probe函数了,我们看下pci的probe做了些什么?
->rte_pci_probe
->FOREACH_DEVICE_ON_PCIBUS(dev)//遍历rte_pci_bus.device_list链表中的dev
->pci_probe_all_drivers
->FOREACH_DRIVER_ON_PCIBUS(dr)//遍历rte_pci_bus.driver_list(我们好像还不知道这个list是做什么的,在哪被初始化的,跟一下看看)
可以看到这个list里放了各种dpdk pmd驱动的信息,并且用RTE_PMD_REGISTER_PCI宏,在main函数执行前,将dpdk的pmd driver信息,放入rte_pci_bus.driver_list中
->rte_pci_probe_one_driver
->rte_pci_match//判断当前dev是否支持dpdk的pmd驱动,若不支持,则直接返回尝试下一个dpdk pmd
->dr->probe//调用匹配到的dpdk pmd driver的probe函数
->rte_eth_dev_create//创建一个dpdk pmd driver
->rte_eth_dev_allocate//从rte_eth_devices[RTE_MAX_ETHPORTS]链表里申请一个坑位,并填充一些信息
->ethdev_init//调用当前驱动的ini函数进行初始化
以上大概就是DPDK网卡驱动初始化框架的内容了,总的来说就是3个链表、1个数组,需要掌握:
1、rte_bus_list
2、device_list
3、driver_list
4、rte_eth_devices