在main函数运行之前,通过gcc的构造函数,完成总线到链表中的注册,且完成各种pmd驱动到某类总线的注册,完成几类全局变量的数据关系。本文以pci总线和em pmd驱动为例。
1 gcc的构造函数 - constructor属性
gcc的constructor函数属性,可以让该函数在main函数之前运行,且可以指定函数运行的优先级,优先级>100,且值越小优先级越高。
ether dev初始化用到的是RTE_PRIORITY_BUS这个level的优先级。
另一个used的意思是,即使函数未被使用也不会被优化掉。
/**
* Run function before main() with high priority.
*
* @param func
* Constructor function.
* @param prio
* Priority number must be above 100.
* Lowest number is the first to run.
*/
#define RTE_INIT_PRIO(func, prio) \
static void __attribute__((constructor(RTE_PRIO(prio)), used)) func(void)
#define RTE_PRIORITY_LOG 101
#define RTE_PRIORITY_BUS 110
#define RTE_PRIORITY_CLASS 120
#define RTE_PRIORITY_LAST 65535
#define RTE_PRIO(prio) \
RTE_PRIORITY_ ## prio //##为连接符号
2 rte_bus_register
以PCI总线为例,把pci总线的全局变量rte_pci_bus,加入总线链表全局变量rte_bus_list的一个过程。
2.1 businitfn_pci 构造函数声明
#define RTE_REGISTER_BUS(nm, bus) \
RTE_INIT_PRIO(businitfn_ ##nm, BUS) \
{\
(bus).name = RTE_STR(nm);\
rte_bus_register(&bus); \
}
RTE_REGISTER_BUS(pci, rte_pci_bus.bus); ==> 等同于声明一个gcc的构造函数 businitfn_pci
static void __attribute__((constructor(110), used)) businitfn_pci(void)
{
rte_pci_bus.bus.name = RTE_STR(pci);
rte_bus_register(&rte_pci_bus.bus);
}
//全局变量 rte_pci_bus的结构和定义:
struct rte_pci_bus rte_pci_bus = {
.bus = {
.scan = rte_pci_scan,
.probe = rte_pci_probe,
.find_device = pci_find_device,
.plug = pci_plug,
.unplug = pci_unplug,
.parse = pci_parse,
.get_iommu_class = rte_pci_get_iommu_class,
.dev_iterate = rte_pci_dev_iterate,
.hot_unplug_handler = pci_hot_unplug_handler,
.sigbus_handler = pci_sigbus_handler,
},
.device_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.device_list),
.driver_list = TAILQ_HEAD_INITIALIZER(rte_pci_bus.driver_list),
};
2.2 rte_bus_register函数
2.2.1 struct rte_bus_list 链表结构体的定义
dpdk中对bus总线链表结构体的定义:TAILQ_HEAD(rte_bus_list, rte_bus); 其中rte_bus_list为新定义的链表结构体的名字,rte_bus为链表中元素的类型。
TAILQ的使用,需要包含linux系统头文件:#include <sys/queue.h>
在sys/queue.h中
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; \
struct type **tqh_last; \
}
2.2.2 定义全局变量rte_bus_list
用上面定义的链表结构体,定义一个同名的全局变量
static struct rte_bus_list rte_bus_list =
TAILQ_HEAD_INITIALIZER(rte_bus_list);
2.2.3 businitfn_pci 构造函数中完成链表insert
运行rte_bus_register(&rte_pci_bus.bus); 则是把rte_pci_bus.bus加入到链表全局变量rte_bus_list中。
void
rte_bus_register(struct rte_bus *bus)
{
RTE_VERIFY(bus);
RTE_VERIFY(bus->name && strlen(bus->name));
/* A bus should mandatorily have the scan implemented */
RTE_VERIFY(bus->scan);
RTE_VERIFY(bus->probe);
RTE_VERIFY(bus->find_device);
/* Buses supporting driver plug also require unplug. */
RTE_VERIFY(!bus->plug || bus->unplug);
TAILQ_INSERT_TAIL(&rte_bus_list, bus, next);
RTE_LOG(DEBUG, EAL, "Registered [%s] bus.\n", bus->name);
}
3. PMD driver注册到rte_pci_bus的driver list中
目标是要在rte_pci_bus.driver_list 链表中加入PMD driver,以net_e1000_em驱动为例。
3.1 声明gcc的构造函数 pciinitfn_net_e1000_em — 优先级最低
#define RTE_INIT(func) \
RTE_INIT_PRIO(func, LAST)
#define RTE_INIT_PRIO(func, prio) \
static void __attribute__((constructor(RTE_PRIO(prio)), used)) func(void)
#define RTE_PRIORITY_LAST 65535
#define RTE_PMD_REGISTER_PCI(nm, pci_drv) \
RTE_INIT(pciinitfn_ ##nm) \
{\
(pci_drv).driver.name = RTE_STR(nm);\
rte_pci_register(&pci_drv); \
} \
RTE_PMD_EXPORT_NAME(nm, __COUNTER__)
RTE_PMD_REGISTER_PCI(net_e1000_em, rte_em_pmd);
//=====> 等同于声明一个函数如下:
static void __attribute__((constructor(65535), used)) pciinitfn_net_e1000_em (void)
{
rte_em_pmd.driver.name = RTE_STR(net_e1000_em);
rte_pci_register(&rte_em_pmd);
}
3.2 rte_pci_register(&rte_em_pmd)
把pci pmd的全局变量rte_em_pmd挂到另一个全局变量rte_pci_bus.driver_list上。
//定义em pmd全局变量
static struct rte_pci_driver rte_em_pmd = {
.id_table = pci_id_em_map,
.drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC |
RTE_PCI_DRV_IOVA_AS_VA,
.probe = eth_em_pci_probe,
.remove = eth_em_pci_remove,
};
struct rte_pci_driver {
TAILQ_ENTRY(rte_pci_driver) next; /**< Next in list. */
struct rte_driver driver; /**< Inherit core driver. */ //从哪里继承?
struct rte_pci_bus *bus; /**< PCI bus reference. */ //反向指回总线变量
pci_probe_t *probe; /**< Device Probe function. */
pci_remove_t *remove; /**< Device Remove function. */
const struct rte_pci_id *id_table; /**< ID table, NULL terminated. */
uint32_t drv_flags; /**< Flags RTE_PCI_DRV_*. */
};
/* register driver */
void rte_pci_register(struct rte_pci_driver *rte_em_pmd)
{
TAILQ_INSERT_TAIL(&rte_pci_bus.driver_list, rte_em_pmd, next);
rte_em_pmd->bus = &rte_pci_bus;
}
4. gdb调试结果
4.1 run之前打了4个断点,分别如下。run之后businitfn_pci函数被断住
4.2
接着断在rte_bus_register,rte_bus_list变量在insert前后发生变化。
4.3
continue之后,除了pci总线,还是其他如dpaa/vmbus/ifpga等总线也相继断住且挂到rte_bus_list链上。
4.4
接着continue之后,断在rte_pci_register函数,处理各种pmd驱动挂到rte_pci_bus.driver_list链中的过程。
可以看到Breakpoint 3也断住了,正是注册em PMD驱动到rte_pci_bus.driver_list链中的过程。
5. 示意图
至此,在main函数运行之前,已经把rte_bus_list/ rte_pci_bus/ rte_pci_bus.driver_list / rte_em_pmd 等pmd,这些全局变量串联起来,同时这些全局变量上的函数指针也都挂上了。
5.1 rte_bus_list和rte_pci_bus的数据关系
5.2 rte_pci_bus.driver_list 和rte_em_pmd的数据关系