β

DPDK设备管理

Hoolev 727 阅读
 

当我们指定网卡给DPDK使用后,Linux系统就失去了对网卡的管理,由DPDK完全接管。 因此这里所说的设备管理,主要是指对网卡的管理。 网卡驱动模型一般包含三层,即,PCI总线设备、网卡设备以及网卡设备的私有数据结构, 即将设备的共性一层层的抽象,PCI总线设备包含网卡设备,网卡设备又包含其私有数据结构。

DPDK-2.0

概述

在DPDK中,首先会注册设备驱动,然后查找当前系统有哪些PCI设备,并通过PCI_ID为PCI设备找到对应的驱动,最后调用驱动初始化设备。

DPDK完成这些工作需要用到三个链表:

这里需要说明下所谓的PCI设备驱动实际包括PCI设备驱动和设备本身驱动两部分,dev_driver_list是设备本身驱动,pci_driver_list是PCI设备驱动。

网卡驱动注册

DPDK支持的所有网卡驱动都通过PMD_REGISTER_DRIVER宏进行注册,PMD_REGISTER_DRIVER宏定义如下:

#define PMD_REGISTER_DRIVER(d)\
void devinitfn_ ##d(void);\
void __attribute__((constructor, used)) devinitfn_ ##d(void)\
{\
	rte_eal_driver_register(&d);\
}

DPDK通过GCC attribute的constructor属性,在MAIN函数执行前,就执行 rte_eal_driver_register() 函数,将驱动挂到全局链表dev_driver_list上。

设备驱动结构定义如下,其中rte_driver.init是设备的初始化函数,在该函数中会注册网卡对应的PCI驱动到全局链表pci_driver_list上。

struct rte_driver {
	TAILQ_ENTRY(rte_driver) next;  /**< Next in list. */
	enum pmd_type type;		       /**< PMD Driver type */
	const char *name;              /**< Driver name. */
	rte_dev_init_t *init;          /**< Device init. function. */
	rte_dev_uninit_t *uninit;      /**< Device uninit. function. */
};

PCI设备扫描

调用 rte_eal_pci_init() 函数,通过遍历/sys/bus/pci/devices/目录查找当前系统中有哪些PCI设备,分别是什么类型,并将它们挂到全局链表pci_device_list上。

遍历工作由 pci_scan() 完成, pcai_scan() 通过读取/sys/bus/pci/devices/目录下相关PCI设备文件,获取对应的信息, 初始化struct rte_pci_device数据结构,并按照PCI地址从大到小的顺序挂到pci_device_list链表上。

PCI设备结构定义如下:

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. */
	/**< PCI Memory Resource */
	struct rte_pci_resource mem_resource[PCI_MAX_RESOURCE];  
	struct rte_intr_handle intr_handle;  /**< Interrupt handle */
	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 */
	enum rte_kernel_driver kdrv;   /**< Kernel driver passthrough */
};

每个PCI外设由一个PCI域(PCI domain)、一个总线编号(bus number)、 一个设备编号(device number)及一个功能编号(function number)来标识。 每个PCI域可以拥有最多256个总线,每个总线上可支持32个设备,每个设备最多可有8种功能。

PCI驱动注册

PCI驱动注册由 rte_eal_dev_init() 完成,主要功能就是遍历dev_driver_list链表, 执行网卡驱动对应的init的回调函数,注册PCI驱动,并挂到全局链表pci_driver_list上。

PCI驱动结构定义如下:

struct rte_pci_driver {
	TAILQ_ENTRY(rte_pci_driver) next;  /**< Next in list. */
	const char *name;             /**< Driver name. */
	pci_devinit_t *devinit;       /**< Device init. function. */
	pci_devuninit_t *devuninit;   /**< Device uninit function. */
	struct rte_pci_id *id_table;  /**< ID table, NULL terminated. */
	uint32_t drv_flags;  /**< Flags contolling handling of device. */
};

网卡驱动对应的init回调函数会调用 rte_eth_driver_register(struct eth_driver *eth_drv) 注册devinit和devuninit两个回调函数, 所有以太网网卡设备的devinit都会注册为rte_eth_dev_init,devuninit注册为rte_eth_dev_uninit。

struct eth_driver定义如下:

struct eth_driver {
	struct rte_pci_driver pci_drv; /**< The PMD is also a PCI driver. */
	eth_dev_init_t eth_dev_init;   /**< Device init function. */
	eth_dev_uninit_t eth_dev_uninit; /**< Device uninit function. */
	unsigned int dev_private_size; /**< Size of device private data. */
};

eth_driver定义了PCI网卡设备驱动,既包含了PCI设备驱动(pci_drv)又包含了设备本身驱动(eth_dev_init)。 每个类型的以太网卡都会定义一个静态的eth_driver数据结构,eth_dev_init和eth_dev_uninit在定义结构时初始化, pci_drv在PCI驱动注册时赋值。

网卡初始化

调用 rte_eal_pci_probe() 函数,遍历pci_device_list和pci_driver_list链表, 根据rte_pci_id,将rte_pci_device与rte_pci_driver绑定, 并调用rte_pci_driver的devinit回调函数初始化PCI设备。

rte_eal_pci_probe_one_driver() 函数中,

参考资料

 
作者:Hoolev
原文地址:DPDK设备管理, 感谢原作者分享。

发表评论