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上。

int
rte_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 uevent
lrwxrwxrwx 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 resource
drwxr-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_status
drwxr-xr-x 3 root root      0 Nov 19 15:31 uio
lrwxrwxrwx 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 int
rte_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 0
drwxr-xr-x 5 root root 0 Nov 19 15:31 uio0
root@Ubuntu:~# ls -ltr /sys/bus/pci/devices/0000\:00\:0a.0/uio/
total 0
drwxr-xr-x 5 root root 0 Nov 19 15:31 uio1
root@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 0
drwxr-xr-x 2 root root 0 Nov 19 15:32 map0
root@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 name
root@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 
0xf0440000
root@Ubuntu:~#
root@Ubuntu:~# cat /sys/bus/pci/devices/0000\:00\:09.0/resource
0x00000000f0440000 0x00000000f045ffff 0x0000000000040200
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x000000000000d248 0x000000000000d24f 0x0000000000040101
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
root@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 = &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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM