dpdk学习之ip_pipeline源代码分析

前端之家收集整理的这篇文章主要介绍了dpdk学习之ip_pipeline源代码分析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

首先从main函数开始,我们一个函数一个函数的逐层进行分析

  1. rte_openlog_stream(stderr)

开启日志,日志级别是stderr

  1. app_config_args函数
    调用getopt_long函数循环解析运行参数,如解析-f -s -p -l 参数

3.app_config_preproc(&app);
解析配置的一些预处理
比如检测配置文件是否存在,构造buffer,并执行system(buffer)

  1. app_config_parse(&app,app.parser_file);
    解析配置文件中的section节点,以及各个节点的参数
int
app_config_parse(struct app_params *app,const char *file_name)
{
    struct rte_cfgfile *cfg;
    char **section_names;
    int i,j,sect_count;

    /* Implicit mempools */
    create_implicit_mempools(app);

    /* Port mask 根据port mask生成link_params参数,并记录下portid */
    if (app->port_mask)
        create_implicit_links_from_port_mask(app,app->port_mask);

    /* Load application configuration file * 读取配置文件 */
    cfg = rte_cfgfile_load(file_name,0);
    APP_CHECK((cfg != NULL),"Parse error: Unable to load config "
        "file %s",file_name);

    //获取section数
    sect_count = rte_cfgfile_num_sections(cfg,NULL,0);
    APP_CHECK((sect_count > 0),"Parse error: number of sections "
        "in file \"%s\" return %d",file_name,sect_count);

    //为section申请资源
    section_names = malloc(sect_count * sizeof(char *));
    PARSE_ERROR_MALLOC(section_names != NULL);

    for (i = 0; i < sect_count; i++)
        section_names[i] = malloc(CFG_NAME_LEN);

    //sect_count为section个数
    rte_cfgfile_sections(cfg,section_names,sect_count);

    for (i = 0; i < sect_count; i++) {
        const struct config_section *sch_s;
        int len,cfg_name_len;

        cfg_name_len = strlen(section_names[i]);

        /* Find section type */
        for (j = 0; j < (int)RTE_DIM(cfg_file_scheme); j++) {
            sch_s = &cfg_file_scheme[j];
            len = strlen(sch_s->prefix);

            if (cfg_name_len < len)
                continue;

            /* After section name we expect only '\0' or digit or * digit dot digit,so protect against false matching,* for example: "ABC" should match section name * "ABC0.0",but it should not match section_name * "ABCDEF". */
            if ((section_names[i][len] != '\0') &&
                !isdigit(section_names[i][len]))
                continue;

            if (strncmp(sch_s->prefix,section_names[i],len) == 0)
                break;
        }

        APP_CHECK(j < (int)RTE_DIM(cfg_file_scheme),"Parse error: unknown section %s",section_names[i]);

        APP_CHECK(validate_name(section_names[i],sch_s->prefix,sch_s->numbers) == 0,"Parse error: invalid section name \"%s\"",section_names[i]);

        sch_s->load(app,cfg);
    }

    for (i = 0; i < sect_count; i++)
        free(section_names[i]);

    free(section_names);

    rte_cfgfile_close(cfg);

    //统计app中每一种section元素的配置数量
    APP_PARAM_COUNT(app->mempool_params,app->n_mempools);
    APP_PARAM_COUNT(app->link_params,app->n_links);//这里获取到的n_links容易出错啊
    APP_PARAM_COUNT(app->hwq_in_params,app->n_pktq_hwq_in);
    APP_PARAM_COUNT(app->hwq_out_params,app->n_pktq_hwq_out);
    APP_PARAM_COUNT(app->swq_params,app->n_pktq_swq);
    APP_PARAM_COUNT(app->tm_params,app->n_pktq_tm);
    APP_PARAM_COUNT(app->tap_params,app->n_pktq_tap);
    APP_PARAM_COUNT(app->kni_params,app->n_pktq_kni);
    APP_PARAM_COUNT(app->source_params,app->n_pktq_source);
    APP_PARAM_COUNT(app->sink_params,app->n_pktq_sink);
    APP_PARAM_COUNT(app->msgq_params,app->n_msgq);
    APP_PARAM_COUNT(app->pipeline_params,app->n_pipelines);

    /*app->port_mask为0的情况*/
    if (app->port_mask == 0)
        assign_link_pmd_id_from_pci_bdf(app);

    /* Save configuration to output file 将配置文件保存到app->output_file指定的文件中 */
    app_config_save(app,app->output_file);

    /* Load TM configuration files */
    app_config_parse_tm(app);

    return 0;
}

create_implicit_mempools(app);函数
APP_PARAM_ADD(app->mempool_params,“MEMPOOL0”);
将MEMPOOL0加入到内存池中,也就是说MEMPOOL0是默认的内存池

读取配置文件中的section数,然后为section申请内存资源.

调用APP_PARAM_COUNT去统计pipeline中每一种section元素的配置数量

APP_PARAM_COUNT宏的作用就是统计数组中有效元素的个数

最后将配置文件保存到app->output_file指定的文件中,方便我们日后排查问题时查看.

  1. app_config_check 配置文件的参数检测

    包括了各个section节点的检测,具体的可以看下源代码

  2. app_init(&app);应用程序的初始化工作
    这块的初始化工作比较多
    初始化eal层
    初始化内存池
    初始化link相关,发送队列,接收队列等
    初始化软队列
    初始化消息队列
    注册pipeline类型
    等等,下面一一解答

6.1 初始化内存池

6.2 初始化link链接
记住,link实例是通过运行命令行参数port_mash生成
/获取rx queue数量/
n_hwq_in = app_link_get_n_rxq(app,p_link);

/获取rx queue数量/
n_hwq_out = app_link_get_n_txq(app,p_link);

/设置rte_eth_conf结构体/
app_init_link_set_config(p_link);

调用关键函数rte_eth_dev_configure,这个函数不多说

/获取端口的mac地址/
rte_eth_macaddr_get(p_link->pmd_id,
(struct ether_addr *) &p_link->mac_addr);

/设置混杂模式/
if (p_link->promisc)
rte_eth_promiscuous_enable(p_link->pmd_id);

下面分别是创建接收队列
/* RXQ 创建接收队列,n_pktq_hwq_in是队列的个数*/

/* TXQ 创建发送队列*/

/检测link状态/
app_check_link(app);

6.3 创建软队列
app_init_swq(app);

其实是通过rte_ring_create函数来创建无锁队列
刚开始看官网文档时,我一直在想,software queue 软队列,到底是什么意思,其实就是ring无锁队列而已.

6.4 注册pipeline节点类型
只分析下面两个有代表性的
app_pipeline_type_register(app,&pipeline_master);
app_pipeline_type_register(app,&pipeline_flow_actions);

struct pipeline_type pipeline_master = {
.name = “MASTER”,
.be_ops = &pipeline_master_be_ops,
.fe_ops = &pipeline_master_fe_ops,
};
pipeline_master_be_ops是什么呢?转定义
struct pipeline_be_ops pipeline_master_be_ops = {
.f_init = pipeline_init,
.f_free = pipeline_free,
.f_run = pipeline_run,
.f_timer = pipeline_timer,
};
分别为f_init等函数指针变量进行赋值操作.

pipeline_flow_actions的定义
struct pipeline_type pipeline_flow_actions = {
.name = “FLOW_ACTIONS”,
.be_ops = &pipeline_flow_actions_be_ops,
.fe_ops = &pipeline_flow_actions_fe_ops,
};

pipeline_flow_actions_be_ops定义如下:
struct pipeline_be_ops pipeline_flow_actions_be_ops = {
.f_init = pipeline_fa_init,
.f_free = pipeline_fa_free,
.f_run = NULL,
.f_timer = pipeline_fa_timer,
};

static int
pipeline_fa_timer(void *pipeline)
{
struct pipeline p = (struct pipeline ) pipeline;

pipeline_msg_req_handle(p);
rte_pipeline_flush(p->p);

return 0;

}
pipeline_msg_req_handle(p);是处理请求消息
int
pipeline_msg_req_handle(struct pipeline *p)
{
uint32_t msgq_id;

for (msgq_id = 0; msgq_id < p->n_msgq; msgq_id++) {
    for ( ; ; ) {
        struct pipeline_msg_req *req;
        pipeline_msg_req_handler f_handle;

        req = pipeline_msg_recv(p,msgq_id);
        if (req == NULL)
            break;

        f_handle = (req->type < PIPELINE_MSG_REQS) ?
            p->handlers[req->type] :
            pipeline_msg_req_invalid_handler;

        if (f_handle == NULL)
            f_handle = pipeline_msg_req_invalid_handler;

        pipeline_msg_send(p,msgq_id,f_handle(p,(void *) req));
    }
}

return 0;

}
req = pipeline_msg_recv(p,msgq_id);不断接收消息
pipeline_msg_send(p,(void *) req));
将消息发送出去,请求消息的处理方式填充到了f_handle函数指针中

6.5 app_init_pipelines 初始化pipeline模块
主体代码

back-end 和 front-end 这个后续研究透了,再补充下
这里引入pipeline_type结构体

struct pipeline_type {
    const char *name;

    /* pipeline back-end */
    struct pipeline_be_ops *be_ops;

    /* pipeline front-end */
    struct pipeline_fe_ops *fe_ops;
};
struct pipeline_fe_ops {
    pipeline_fe_op_init f_init;
    pipeline_fe_op_post_init f_post_init;
    pipeline_fe_op_free f_free;
    pipeline_fe_op_track f_track;
    cmdline_parse_ctx_t *cmds;
};
pipeline_fe_ops结构体中的成员都是函数指针类型的,函数指针类型的定义如下
typedef void* (*pipeline_fe_op_init)(struct pipeline_params *params,void *arg);

typedef int (*pipeline_fe_op_post_init)(void *pipeline);

typedef int (*pipeline_fe_op_free)(void *pipeline);

typedef int (*pipeline_fe_op_track)(struct pipeline_params *params,uint32_t port_in,uint32_t *port_out);

猜你在找的设计模式相关文章