首页 > 代码库 > DPDK收发包处理流程-----(一)网卡初始化

DPDK收发包处理流程-----(一)网卡初始化

本文基于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设备。

2、pci_scan()通过读取/sys/bus/pci/devices/目录下的信息,扫描当前系统的PCI设备,并初始化,并按照PCI地址从大到小的顺序挂在到pci_debice_list上。

intrte_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;}

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

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 ueventlrwxrwxrwx 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 resourcedrwxr-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_statusdrwxr-xr-x 3 root root      0 Nov 19 15:31 uiolrwxrwxrwx 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

目录名:就是PCI设备的地址,记录在struct rte_pci_addr数据结构中。

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。

resource文件:获取PCI设备的在地址总线上的物理地址,以及物理地址空间的大小,记录在struct rte_pci_resouce数据结构中。

三、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 intrte_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(&eth_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设备。

(YK14$%`JT1VRV4HGIA8~5S

在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。具体如下:

a、首先读取/sys/bus/pci/devices/PCI设备目录下的uio目录,获取uio设备的ID,该ID就是uio目录名最后几位的数字。当igb_uio模块与网卡设备绑定的时候,会在/sys/bus/pci/devices/对应的PCI设备目录下创建uio目录。

如果启动参数中指定了OPT_CREATE_UIO_DEV_NUM,会在/dev目录下创建对应uio设备的设备文件。

root@Ubuntu:~# ls -ltr /sys/bus/pci/devices/0000\:00\:09.0/uio/total 0drwxr-xr-x 5 root root 0 Nov 19 15:31 uio0root@Ubuntu:~# ls -ltr /sys/bus/pci/devices/0000\:00\:0a.0/uio/total 0drwxr-xr-x 5 root root 0 Nov 19 15:31 uio1root@Ubuntu:~#

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;

c、读取/sys/bus/pci/devices/0000\:00\:09.0/uio/uio0/maps/map0/目录下的文件,获取UIO设备的map resource。并将其记录在struct pci_map数据结构中。

struct pci_map {    void *addr;    uint64_t offset;    uint64_t size;    uint64_t phaddr;};
root@Ubuntu:~# ls -ltr /sys/bus/pci/devices/0000\:00\:09.0/uio/uio0/maps/total 0drwxr-xr-x 2 root root 0 Nov 19 15:32 map0root@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 nameroot@Ubuntu:~#

d、检查PCI设备和UIO设备在内存总线上的物理地址是否一致。如果一致,对/dev/uioID文件mmap一段内存空间,并将其记录在pci_map->addr和rte_pci_device->mem_resource[].addr中。

root@Ubuntu:~# cat /sys/bus/pci/devices/0000\:00\:09.0/uio/uio0/maps/map0/addr 0xf0440000root@Ubuntu:~#
root@Ubuntu:~# cat /sys/bus/pci/devices/0000\:00\:09.0/resource0x00000000f0440000 0x00000000f045ffff 0x00000000000402000x0000000000000000 0x0000000000000000 0x00000000000000000x000000000000d248 0x000000000000d24f 0x00000000000401010x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x0000000000000000root@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 = http://www.mamicode.com/&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 = &eth_igb_ops;eth_dev->rx_pkt_burst = &eth_igb_recv_pkts;eth_dev->tx_pkt_burst = &eth_igb_xmit_pkts;

初始化网卡设备的硬件相关数据结构struct e1000_hw,包括设备ID、硬件操作函数集、在内存地址总线上映射的地址、MAC地址等等。

c、注册中断处理函数。

rte_intr_callback_register(&(pci_dev->intr_handle),                eth_igb_interrupt_handler, (void *)eth_dev);

五、设备与驱动相互映射关系图

rte_device-1

 

 

 

错误之处,欢迎指出。

转载请标明转自http://www.cnblogs.com/MerlinJ/p/4108021.html 

DPDK收发包处理流程-----(一)网卡初始化