DPDK rte_eal_init 初始化分析(根据最新的更新)

端口初始化流程

  • 1. 注册设备驱动到“dev_driver_list”链表中
  • 2.扫描系统中的pci设备,并注册到“pci_device_list”中
  • 3、初始化注册的驱动
  • 4、网卡设备初始化

端口初始化流程
 
如上所示给出了端口打开的简单流程图,下面以ixgbe驱动为例详细说明:

端å£çåå§åæµç¨

1. 注册设备驱动到“dev_driver_list”链表中

这个链表节点为:
 

/**
 * A structure describing a device driver.
 */
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. */
};


将ixgbe的这些信息注册到该链表中:

static struct rte_driver rte_ixgbe_driver = {
    .type = PMD_PDEV,
    .init = rte_ixgbe_pmd_init,
};

PMD_REGISTER_DRIVER(rte_ixgbe_driver);


PMD_REGISTER_DRIVER为dpdk定义的宏,使用了GNU C提供的“__attribute__(constructor)”机制,使得注册设备驱动的过程在main函数执行之前完成。 
这样就有了设备驱动类型、设备驱动的初始化函数

2.扫描系统中的pci设备,并注册到“pci_device_list”中

链表节点为:

/**
 * A structure describing a PCI device.
 */
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 */
    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设备的相关信息后,记录到这样的一个结构体中。如何获取到这些信息: 
在main函数的一开始,调用rte_eal_init()获取用户、系统的相关配置信息以及设置基础运行环境,其中包括调用rte_eal_pci_init()来扫描、获取系统中的CPI网卡信息; 
首先,初始化pci_device_list链表,后面扫描的到的pci网卡设备信息会记录到这个链表中; 
然后,调用rte_eal_pci_scan()扫描系统中的PCI网卡:遍历”/sys/bus/pci/devices”目录下的所有pci地址,逐个获取对应的pci地址、pci id、sriov使能时的vf个数、亲和的numa、设备地址空间、驱动类型等;

/*
 * Scan the content of the PCI bus, and the devices in the devices list
 */
int rte_eal_pci_scan(void)
{
    struct dirent *e;
    DIR *dir;
    char dirname[PATH_MAX];
    uint16_t domain;
    uint8_t bus, devid, function;

    dir = opendir(SYSFS_PCI_DEVICES);
    if (dir == NULL) {
        RTE_LOG(ERR, EAL, "%s(): opendir failed: %s\n",
            __func__, strerror(errno));
        return -1;
    }

    while ((e = readdir(dir)) != NULL) {
        if (e->d_name[0] == '.')
            continue;

        if (parse_pci_addr_format(e->d_name, sizeof(e->d_name), &domain,
                &bus, &devid, &function) != 0)
            continue;

        snprintf(dirname, sizeof(dirname), "%s/%s", SYSFS_PCI_DEVICES,
             e->d_name);
        if (pci_scan_one(dirname, domain, bus, devid, function) < 0)
            goto error;
    }
    closedir(dir);
    return 0;

error:
    closedir(dir);
    return -1;
}


这样,扫描并记录了系统中所有的pci设备的相关信息,后面根据上面获取的这些设备信息以及前面注册的驱动信息,就可以完成具体网卡设备的初始化;

3、初始化注册的驱动

在rte_eal_init()函数中,后面会调用rte_eal_dev_init()来初始化前面注册的驱动“dev_driver_list”:分别调用注册的每款驱动的初始化函数,把每款驱动的一些信息记录到“pci_driver_list”链表中,链表节点为:

/**
 * @internal
 * The structure associated with a PMD Ethernet driver.
 *
 * Each Ethernet driver acts as a PCI driver and is represented by a generic
 * *eth_driver* structure that holds:
 *
 * - An *rte_pci_driver* structure (which must be the first field).
 *
 * - The *eth_dev_init* function invoked for each matching PCI device.
 *
 * - The *eth_dev_uninit* function invoked for each matching PCI device.
 *
 * - The size of the private data to allocate for each matching device.
 */
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. */
};


结构中记录设备的init、uinit、私有数据大小以及pci driver信息,而struct rte_pci_driver中的记录了驱动支持的网卡设备的verder id、device id信息,这个在后面具体的PCI网卡设备初始化时,会根据这些信息来匹配驱动:

/**
 * A structure describing a PCI driver.
 */
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. */
    const struct rte_pci_id *id_table;      /**< ID table, NULL terminated. */
    uint32_t drv_flags;                     /**< Flags contolling handling of device. */
};

/**
 * A structure describing an ID for a PCI driver. Each driver provides a
 * table of these IDs for each device that it supports.
 */
struct rte_pci_id {
    uint16_t vendor_id;           /**< Vendor ID or PCI_ANY_ID. */
    uint16_t device_id;           /**< Device ID or PCI_ANY_ID. */
    uint16_t subsystem_vendor_id; /**< Subsystem vendor ID or PCI_ANY_ID. */
    uint16_t subsystem_device_id; /**< Subsystem device ID or PCI_ANY_ID. */
};


已ixgbe类型的网卡为例,注册的信息为rte_ixgbe_pmd:

static struct eth_driver rte_ixgbe_pmd = {
    .pci_drv = {
        .name = "rte_ixgbe_pmd",
        .id_table = pci_id_ixgbe_map,
        .drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC |
            RTE_PCI_DRV_DETACHABLE,
    },
    .eth_dev_init = eth_ixgbe_dev_init,
    .eth_dev_uninit = eth_ixgbe_dev_uninit,
    .dev_private_size = sizeof(struct ixgbe_adapter),
};

至此,注册的每款驱动的设备初始化,支持的设备等信息以及系统中所有的pci设备信息就已经都有了,分别记录在”pci_driver_list”和”pci_device_list”这两个全局的链表中,接下来就可以完成设备匹配驱动,别初始化设备了。

4、网卡设备初始化

rte_eal_init()函数接下来调用rte_eal_pci_probe()函数完成具体的设备的初始化

/*
 * Scan the content of the PCI bus, and call the devinit() function for
 * all registered drivers that have a matching entry in its id_table
 * for discovered devices.
 */
int rte_eal_pci_probe(void)
{
    struct rte_pci_device *dev = NULL;
    struct rte_devargs *devargs;
    int probe_all = 0;
    int ret = 0;

    /* 如果配置了白名单,只初始化白名单中的设备,否则所有支持的设备都初始化 */
    if (rte_eal_devargs_type_count(RTE_DEVTYPE_WHITELISTED_PCI) == 0)
        probe_all = 1;

    TAILQ_FOREACH(dev, &pci_device_list, next) {

        /* set devargs in PCI structure */
        devargs = pci_devargs_lookup(dev);
        if (devargs != NULL)
            dev->devargs = devargs;

        /* probe all or only whitelisted devices */
        if (probe_all)
            ret = pci_probe_all_drivers(dev);
        else if (devargs != NULL &&
            devargs->type == RTE_DEVTYPE_WHITELISTED_PCI)
            ret = pci_probe_all_drivers(dev);
        if (ret < 0)
            rte_exit(EXIT_FAILURE, "Requested device " PCI_PRI_FMT
                 " cannot be used\n", dev->addr.domain, dev->addr.bus,
                 dev->addr.devid, dev->addr.function);
    }

    return 0;
}

rte_eal_pci_probe_one_driver()函数中,在probe一个具体的设备时,比较vendor id、device id,然后映射设备资源、调用驱动的设备初始化函数:

/*
 * If vendor/device ID match, call the devinit() function of the
 * driver.
 */
static int
rte_eal_pci_probe_one_driver(struct rte_pci_driver *dr, struct rte_pci_device *dev)
{
    int ret;
    const struct rte_pci_id *id_table;

    for (id_table = dr->id_table; id_table->vendor_id != 0; id_table++) {

        /* check if device's identifiers match the driver's ones */
        ... ...

        if (dr->drv_flags & RTE_PCI_DRV_NEED_MAPPING) {
            /* map resources for devices that use igb_uio */
            ret = rte_eal_pci_map_device(dev);
            if (ret != 0)
                return ret;
        } else if (dr->drv_flags & RTE_PCI_DRV_FORCE_UNBIND &&
                rte_eal_process_type() == RTE_PROC_PRIMARY) {
            /* unbind current driver */
            if (pci_unbind_kernel_driver(dev) < 0)
                return -1;
        }
        /* call the driver devinit() function */
        return dr->devinit(dr, dev);
    }
    /* return positive value if driver doesn't support this device */
    return 1;
}

pci_uio_map_resource()函数为pci设备在虚拟地址空间映射pci资源,后续直接通过操作内存来操作pci设备; 
驱动的设备初始化函数rte_eth_dev_init()主要是初始化dpdk驱动框架中,为每个设备分配资源以及资源的初始化:

/* 每个设备对应数组的一个成员,记录了设备相关的所有信息 */
struct rte_eth_dev rte_eth_devices[RTE_MAX_ETHPORTS];

/* 端口相关的配置 */
struct rte_eth_dev_data *dat;


dpdk框架中,对端口的初始化操作已经基本完成,后面则是根据用户的设置,配置端口的收发包队列以及最终start端口,开始收发包: 
a、rte_eth_dev_configure()函数完成端口配置:队列数配置、RSS、offload等等设置; 
b、rte_eth_rx_queue_setup()、rte_eth_tx_queue_setup()函数分别设置端口的每个收发队列:ring空间申请、初始化等; 
c、rte_eth_dev_start()函数:发送队列初始化buf填充,端口使能(具体可以参考代码或网卡芯片手册,均是相关寄存器设置);

rte_eal_init

├──rte_atomic32_test_and_set:操作静态局部变量run_once确保函数只执行一次

├──获取主线程的线程ID

├──rte_eal_log_early_init:将stderr作为日志输出的文件

│   │

│   └──rte_openlog_stream

├──eal_log_level_parse

│   │

│   ├──eal_reset_internal_config:初始化结构体struct internal_config

│   │

│   └──解析命令行参数,只处理“--log-level”,保存在internal_config.log_level

├──rte_set_log_level:设置log level

├──rte_eal_cpu_init:赋值全局结构struct lcore_config

│   │

│   ├──rte_eal_get_configuration:获取全局配置结构struct rte_config,初始指向全局变量early_mem_config

│   │

│   ├──eal_cpu_detected

│   │   │

│   │   └──如果文件“/sys/devices/system/cpu/cpu%u/topology/core_id”存在,则存在此编号的cpu

│   │

│   ├──eal_cpu_core_id

│   │   │

│   │   └──eal_parse_sysfs_value:读取文件“/sys/devices/system/cpu/cpu%u/topology/core_id”,

│   │         获取core number onsocket for this lcore

│   │

│   ├──eal_cpu_socket_id

│   │   │

│   │   └──如果目录“/sys/devices/system/node/node%u/cpu%u”存在,得到physical socket id for this lcore

│   │

│   └──计数得到number of available logical cores,保存在structrte_config.lcore_count中

├──eal_parse_args:解析处理EAL的命令行参数,赋值struct internal_config结构的相关字段

├──eal_hugepage_info_init:赋值struct hugepage_info数组(internal_config.hugepage_info)

│   │

│   ├──打开目录“/sys/kernel/mm/hugepages”

│   │

│   ├──遍历子目录,如“hugepages-2048kB”,即不同大小的hugepage

│   │   │

│   │   ├──获取当前大小hugepage对应的structhugepage_info结构体

│   │   │

│   │   ├──get_hugepage_dir

│   │   │   │

│   │   │   └──读取文件“/proc/mounts”,查找所有“hugetlbfs”,找到当前大小hugepage对应的挂载路径,

│   │   │         如“/mnt/huge”

│   │   │

│   │   ├──打开hugepage目录文件,上文件锁

│   │   │

│   │   ├──删除hugepage files,如“rtemap_xxx”

│   │   │

│   │   └──get_num_hugepages

│   │         │

│   │         └──读取文件“/sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages”和

│   │               “/sys/kernel/mm/hugepages/hugepages-2048kB/resv_hugepages”,获取hugepage总数

│   │

│   ├──对struct internal_config结构中的各个大小的struct hugepage_info进行排序,从大到小

│   │

│   └──检查是否至少有一个有效的struct hugepage_info

├──eal_get_hugepage_mem_size:

│   │

│   └──遍历struct hugepage_info,获取所有hugepage占用内存的总数,结果存放在internal_config.memory

├──将当前时间作为种子,产生伪随机数序列

├──rte_config_init

│   │

│   ├──主应用的情况(RTE_PROC_PRIMARY)

│   │   │

│   │   └──rte_eal_config_create

│   │         │

│   │         ├──eal_runtime_config_path:获取runtime配置文件路径,如“/var/run/.rte_config”

│   │         │

│   │         ├──打开文件,上锁,mmap映射文件到内存

│   │         │

│   │         ├──将early configuration structure(全局变量early_mem_config)拷贝到此内存中,

│   │         │   rte_config.mem_config指向这块内存

│   │         │

│   │         └──映射地址保存在rte_config.mem_config->mem_cfg_addr中,用于从应用将来映射到相同的地址

│   │

│   └──从应用的情况(RTE_PROC_SECONDARY)

│         │

│         ├──rte_eal_config_attach

│         │   │

│         │   ├──eal_runtime_config_path

│         │   │

│         │   ├──打开文件,mmap映射文件到内存

│         │   │

│         │   └──rte_config.mem_config指向映射的内存

│         │

│         ├──rte_eal_mcfg_wait_complete

│         │   │

│         │   └──如果struct rte_mem_config结构的magic成员没有被写成RTE_MAGIC,就继续等待

│         │          (主应用ready后会将struct rte_mem_config结构的magic成员写成RTE_MAGIC)

│         │

│         └──rte_eal_config_reattach

│               │

│               ├──从前面mmap映射文件中获取主应用mmap的映射地址(即rte_config.mem_config->mem_cfg_addr)

│               │

│               ├──munmap解除先前的映射

│               │

│               ├──指定主应用映射地址重新执行mmap映射,如果最终映射地址和指定映射地址不一致,则出错退出

│               │

│               └──将rte_config.mem_config指向重新映射的内存

├──rte_eal_pci_init

│   │

│   ├──初始化以全局变量pci_driver_list和pci_device_list为头的tail queue

│   │

│   └──rte_eal_pci_scan

│         │

│         └──遍历目录“/sys/bus/pci/devices”下的所有子目录

│               │

│               ├──parse_pci_addr_format:从目录名称中获取PCI设备的domain、bus、devid、function信息

│               │

│               └──pci_scan_one

│                     │

│                     ├──mallocstruct rte_pci_device结构

│                     │

│                     ├──读取目录“/sys/bus/pci/devices/0000\:03\:00.0/”下相关文件,填充struct rte_pci_device结构字段

│                     │

│                     ├──pci_get_kernel_driver_by_path:获取驱动名称

│                     │

│                     ├──赋值所支持的驱动(structrte_pci_device结构的kdrv字段)

│                     │

│                     └──将此struct rte_pci_device结构按序插入到全局队列pci_device_list

├──rte_eal_memory_init

├──eal_hugedirs_unlock:解锁hugepage目录(由前面的eal_hugepage_info_init函数加锁)

├──rte_eal_memzone_init

├──rte_eal_tailqs_init

│   │

│   └──遍历以全局变量rte_tailq_elem_head为头部的struct rte_tailq_elem结构tailq链表

│         │

│         └──rte_eal_tailq_update

│               │

│               ├──rte_eal_tailq_create:主应用的情况(RTE_PROC_PRIMARY)

│               │   │

│               │   ├──rte_eal_tailq_lookup

│               │   │   │

│               │   │   └──在struct rte_mem_config结构的structrte_tailq_head结构数组中查找

│               │   │

│               │   └──如果没找到,占用struct rte_tailq_head结构数组的一个空位

│               │

│               └──rte_eal_tailq_lookup:从应用的情况(RTE_PROC_SECONDARY)

├──rte_eal_log_init

│   │

│   ├──调用fopencookie,定义一个定制的写日志接口

│   │

│   ├──调用openlog打开日志

│   │

│   └──rte_eal_common_log_init

│         │

│         ├──STAILQ_INIT:初始化Singly-linked Tail queue,队头为log_history

│         │

│         ├──rte_mempool_create

│         │

│         ├──如果创建mempool失败,调用rte_mempool_lookup

│         │   │

│         │   ├──获取链接所有mempool结构链表的头结构structrte_mempool_list

│         │   │

│         │   ├──遍历链接所有mempool结构链表的所有结点

│         │   │   │

│         │   │   └──比较struct rte_tailq_entry结构的data域指向的struct rte_mempool结构的名称,

│         │   │         是否与指定名称相同

│         │   │

│         │   └──返回找到的指向struct rte_mempool结构的指针,或NULL

│         │

│         └──rte_openlog_stream:按照参数,修改struct rte_logs日志结构的相关参数

├──rte_eal_alarm_init

│   │

│   └──赋值全局的struct rte_intr_handle结构,调用timerfd_create函数创建定时器timer对象

├──rte_eal_timer_init

│   │

│   ├──设定全局变量eal_timer_source为EAL_TIMER_TSC(TSC/HPET)

│   │

│   ├──set_tsc_freq:设置TSC frequency(每秒钟时钟中断的次数)

│   │

│   └──check_tsc_flags

│         │

│         └──解析文件“/proc/cpuinfo”,检查“flags”属性中“constant_tsc”和“nonstop_tsc”是否存在

│                (“constant_tsc”和“nonstop_tsc”都存在,说明TSC计时是可靠的)

├──eal_check_mem_on_local_socket

│   │

│   ├──获取masterlcore对应的numa socket

│   │

│   ├──rte_eal_get_physmem_layout:获取struct rte_memseg结构数组地址

│   │

│   └──遍历struct rte_memseg结构数组,检查特定struct rte_memseg结构是否存在(对应此numa socket,并且长度大于0)

├──eal_plugins_init

│    (EAL的“-d”选项可以指定需要载入的动态链接库)

│   │

│   ├──如果全局变量default_solib_dir所指的Default path of external loadable drivers有效

│   │   │

│   │   └──eal_plugin_add

│   │         │

│   │         ├──malloc一个struct shared_driver结构,拷贝路径名称

│   │         │

│   │         └──将此struct shared_driver结构挂载到List of external loadable drivers中

│   │

│   └──遍历List of external loadable drivers上挂载的所有struct shared_driver结构

│         │

│         ├──如果当前struct shared_driver结构所保存的路径是目录

│         │   │

│         │   └──eal_plugindir_init

│         │         │

│         │         └──对目录中的每个普通文件,执行eal_plugin_add

│         │                (将文件挂载到Listof external loadable drivers的尾部,待接下来的遍历循环进行处理)

│         │

│         └──否则,是共享库的情况

│               │

│               └──调用dlopen打开指定的动态链接库

├──eal_thread_init_master

│   │

│   ├──设置主线程的lcore_id

│   │

│   └──eal_thread_set_affinity

│         │

│         ├──rte_gettid

│         │   │

│         │   └──rte_sys_gettid:获取线程的tid

│         │

│         └──rte_thread_set_affinity

│               │

│               └──设置线程的CPU亲和性,记录numasocket等信息

├──eal_thread_dump_affinity

│   │

│   ├──rte_thread_get_affinity

│   │

│   └──dump当前线程的CPU affinity

├──rte_eal_dev_init

│   │

│   ├──遍历以全局变量devargs_list为头的struct rte_devargs结构链表

│   │    (struct rte_devargs——Structure that stores a device given by the user with its arguments)

│   │   │

│   │   ├──跳过physical device

│   │   │

│   │   └──rte_eal_vdev_init

│   │         │

│   │         └──遍历以全局变量dev_driver_list为头的struct rte_driver结构链表

│   │               │

│   │               ├──跳过physicaldevicedriver

│   │               │

│   │               └──search a driver prefix in virtual device name,如果匹配执行struct rte_driver结构中的init函数

│   │

│   └──遍历以全局变量dev_driver_list为头的struct rte_driver结构链表

│          (进程main函数运行前,通过PMD_REGISTER_DRIVER(xxx)宏定义,内在调用rte_eal_driver_register

│         将各种structrte_driver结构(e.g.rte_ixgbe_driver),插入到此struct rte_driver结构链表中)

│         │

│         ├──跳过virtual devicedriver

│         │

│         └──执行struct rte_driver结构中的init函数

│                (e.g.rte_ixgbe_pmd_init)

│               │

│               └──rte_eth_driver_register:Register an Ethernet [Poll Mode] driver

│                     │

│                     ├──赋值struct eth_driver结构中的PCIdriver字段包含的Device init/uninit function pointer

│                     │

│                     └──rte_eal_pci_register

│                           │

│                           └──将刚赋值的PCIdriver数据结构插入到以全局变量pci_driver_list为头的struct rte_pci_driver结构链表中

├──rte_eal_intr_init

│   │

│   ├──初始化global interrupt source head

│   │

│   ├──创建pipe

│   │

│   ├──创建线程来等待处理中断,线程执行函数为eal_intr_thread_main

│   │   │

│   │   └──线程运行循环

│   │         │

│   │         ├──epoll_create:创建epoll文件描述符

│   │         │

│   │         ├──epoll_ctl:把前面创建的the read end of the pipe,添加到epoll wait list中

│   │         │

│   │         ├──遍历以global interrupt source head为头部的struct rte_intr_source结构链表

│   │         │   │

│   │         │   ├──如果当前struct rte_intr_source结构没有挂载的callback函数,跳过

│   │         │   │

│   │         │   └──把所有的uio device file descriptor,添加到epoll wait list中

│   │         │

│   │         ├──eal_intr_handle_interrupts

│   │         │   │

│   │         │   └──循环

│   │         │         │

│   │         │         ├──epoll_wait:wait for an I/O event on an epoll file descriptor

│   │         │         │

│   │         │         ├──eal_intr_process_interrupts

│   │         │         │   │

│   │         │         │   └──遍历所有发生的I/O event

│   │         │         │         │

│   │         │         │         ├──如果the read end of the pipe可用,执行read操作,函数返回

│   │         │         │         │    (此时会rebuild thewait list)

│   │         │         │         │

│   │         │         │         ├──遍历struct rte_intr_source结构链表,查找当前I/O event对应的structrte_intr_source结构

│   │         │         │         │

│   │         │         │         ├──根据interrupt handle type(uio/alarm/…),确定需要读取的字节长度

│   │         │         │         │

│   │         │         │         ├──执行文件read操作

│   │         │         │         │

│   │         │         │         └──如果read数据成功,执行当前struct rte_intr_source结构挂载的所有callback函数

│   │         │         │

│   │         │         └──调用eal_intr_process_interrupts返回负数,本次中断处理结束返回

│   │         │

│   │         └──关闭epoll文件描述符

│   │

│   └──如果创建线程成功,调用rte_thread_setname给线程设置名称“eal-intr-thread”

│         │

│         └──pthread_setname_np

├──循环(browse all running lcores except the master lcore)

│   │

│   ├──创建主线程与子线程通信使用的pipe

│   │

│   ├──设置子线程状态为WAIT

│   │

│   ├──创建子线程,线程执行函数为eal_thread_loop

│   │   │

│   │   ├──根据线程ID,获取当前线程的lcore_id

│   │   │

│   │   ├──获取主线程向子线程通信所用管道,子线程读取数据的file descriptor(m2s)

│   │   │   获取子线程向主线程通信所用管道,子线程发送数据的file descriptor(s2m)

│   │   │

│   │   ├──eal_thread_set_affinity:设置子线程cpu affinity

│   │   │

│   │   ├──eal_thread_dump_affinity

│   │   │

│   │   └──线程主循环

│   │         │

│   │         ├──等待读取主线程发送的命令

│   │         │

│   │         ├──设置线程状态为RUNNING

│   │         │

│   │         ├──向主线程发送ack

│   │         │

│   │         ├──读取当前lcore对应的structlcore_config结构中的lcore_function_t类型函数指针,及调用参数

│   │         │

│   │         ├──执行所指函数,并存储返回值

│   │         │

│   │         └──设置线程状态为FINISHED

│   │

│   └──如果创建线程成功,调用rte_thread_setname给线程设置名称“lcore-slave-xx”

├──rte_eal_mp_remote_launch:指示所有子线程启动一个dummyfunction

│   │

│   ├──检查各个子线程/lcore的状态是否处于WAIT

│   │

│   ├──rte_eal_remote_launch:向各个子线程/lcore发送执行命令

│   │   │

│   │   ├──获取主线程向子线程通信所用管道,主线程发送数据的file descriptor(m2s)

│   │   │   获取子线程向主线程通信所用管道,主线程读取数据的file descriptor(s2m)

│   │   │

│   │   ├──将lcore_function_t类型函数指针,及调用参数填入当前lcore对应的structlcore_config结构

│   │   │

│   │   ├──向子线程发送命令

│   │   │

│   │   └──等待读取子线程发送的ack

│   │

│   └──如果最后一个参数值为CALL_MASTER(lcore handler executed by master core),主线程也执行所指函数

├──rte_eal_mp_wait_lcore

│   │

│   └──rte_eal_wait_lcore:等待所有子线程完成工作

│         │

│         ├──如果子线程处于WAIT状态,直接返回

│         │

│         ├──如果子线程处于RUNNING状态,循环等待

│         │

│         └──将子线程状态从FINISHED改为WAIT

├──rte_eal_pci_probe

│   │

│   ├──rte_eal_devargs_type_count

│   │   │

│   │   └──遍历以全局变量devargs_list为头的struct rte_devargs结构链表,

│   │         计算指定类型(whitelist/blacklist/virtual)的设备数目

│   │

│   └──遍历以全局变量pci_device_list为头的struct rte_pci_device结构链表

│         │

│         ├──pci_devargs_lookup:当前PCI device是否由user在命令行参数中被指定

│         │   │

│         │   └──遍历以全局变量devargs_list为头的struct rte_devargs结构链表

│         │         │

│         │         ├──跳过virtual device

│         │         │

│         │         ├──rte_eal_compare_pci_addr

│         │         │   │

│         │         │   └──比较两个PCI devices的Domain-Bus-Device-Function

│         │         │

│         │         └──如果比较成功,返回此struct rte_devargs结构

│         │

│         ├──如果user在命令行中没有指定任何whitelist设备,执行pci_probe_all_drivers

│         │   │

│         │   └──遍历以全局变量pci_driver_list为头的struct rte_pci_driver结构链表

│         │         │

│         │         ├──rte_eal_pci_probe_one_driver

│         │         │   │

│         │         │   └──遍历当前PCI driver所支持的设备ID列表

│         │         │         │

│         │         │         ├──确认PCI device ID是否在列表中

│         │         │         │    (比较Vendor ID、Device ID、Subsystem vendor ID、Subsystem device ID)

│         │         │         │

│         │         │         ├──如果user在命令行中指定当前设备为blacklist设备,则直接返回1

│         │         │         │

│         │         │         ├──如果PCI driver指定RTE_PCI_DRV_NEED_MAPPING标志(Device needsPCI BAR mapping)

│         │         │         │   │

│         │         │         │   ├──rte_eal_pci_map_device

│         │         │         │   │

│         │         │         │   └──如果map resources不成功,直接返回

│         │         │         │

│         │         │         ├──如果PCI driver指定RTE_PCI_DRV_FORCE_UNBIND标志

│         │         │         │   │

│         │         │         │   ├──pci_unbind_kernel_driver

│         │         │         │   │

│         │         │         │   └──如果unbind current driver不成功,直接返回

│         │         │         │

│         │         │         └──调用PCI driver devinit() function

│         │         │                (e.g.rte_eth_dev_init)

│         │         │               │

│         │         │               ├──rte_eth_dev_create_unique_device_name

│         │         │               │    (Create unique Ethernet device name usingPCI address)

│         │         │               │

│         │         │               ├──rte_eth_dev_allocate

│         │         │               │   │

│         │         │               │   ├──rte_eth_dev_find_free_port

│         │         │               │   │   │

│         │         │               │   │   └──在全局struct rte_eth_dev结构数组rte_eth_devices中查找未使用的空位,返回空位索引

│         │         │               │   │

│         │         │               │   ├──如果struct rte_eth_dev_data结构类型全局指针rte_eth_dev_data为NULL,

│         │         │               │   │   执行rte_eth_dev_data_alloc

│         │         │               │   │   │

│         │         │               │   │   ├──主应用的情况(RTE_PROC_PRIMARY)

│         │         │               │   │   │   │

│         │         │               │   │   │   └──rte_memzone_reserve:reserve名称为“rte_eth_dev_data”的memzone,

│         │         │               │   │   │         存放各个ethernet device对应的struct rte_eth_dev_data结构数据

│         │         │               │   │   │

│         │         │               │   │   ├──从应用的情况(RTE_PROC_SECONDARY)

│         │         │               │   │   │   │

│         │         │               │   │   │   └──rte_memzone_lookup:lookup名称为“rte_eth_dev_data”的memzone

│         │         │               │   │   │

│         │         │               │   │   ├──全局指针rte_eth_dev_data指向memzone所引用的内存

│         │         │               │   │   │

│         │         │               │   │   └──如果是主应用,清空初始化这片struct rte_eth_dev_data结构内存

│         │         │               │   │

│         │         │               │   ├──rte_eth_dev_allocated

│         │         │               │   │   │

│         │         │               │   │   └──查找是否已存在相同名称的ethernet device

│         │         │               │   │

│         │         │               │   └──填写struct rte_eth_dev结构和structrte_eth_dev_data结构的相关字段

│         │         │               │

│         │         │               ├──如果是主应用,按照structrte_eth_dev结构dev_private_size域所指定的

│         │         │               │    Size of device private data(对应PMD Ethernetdriver定义时就会被指定),

│         │         │               │    rte_zmalloc对应大小内存,地址保存在struct rte_eth_dev_data结构的dev_private域

│         │         │               │

│         │         │               ├──TAILQ_INIT:初始化以struct rte_eth_dev结构link_intr_cbs域为链表头的,

│         │         │               │    struct rte_eth_dev_callback结构链表

│         │         │               │

│         │         │               ├──设置设备默认MTU

│         │         │               │

│         │         │               ├──Invoke PMD device initialization function,即struct eth_driver结构的eth_dev_init域所指函数

│         │         │               │     (e.g.eth_ixgbe_dev_init)

│         │         │               │

│         │         │               ├──如果是主应用,free地址保存在struct rte_eth_dev_data结构的dev_private域的内存区

│         │         │               │

│         │         │               └──rte_eth_dev_release_port

│         │         │                     │

│         │         │                     └──释放此struct rte_eth_dev结构在全局数组rte_eth_devices中所占用的位置

│         │         │

│         │         └──如果当前driver与设备不匹配,继续尝试下个driver

│         │

│         └──否则如果user指定当前PCIdevice为whitelist设备,也执行pci_probe_all_drivers

└──rte_eal_mcfg_complete

     │

     └──如果是主应用,将全局内存配置struct rte_mem_config结构的magic成员写成RTE_MAGIC,

           表明主应用EAL初始化完成
 

猜你喜欢

转载自blog.csdn.net/armlinuxww/article/details/90288725