开启日志,日志级别是stderr
3.app_config_preproc(&app);
解析配置的一些预处理
比如检测配置文件是否存在,构造buffer,并执行system(buffer)
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, section_names[i], 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指定的文件中,方便我们日后排查问题时查看.
app_init(&app);应用程序的初始化工作
这块的初始化工作比较多
初始化eal层
初始化内存池
初始化link相关,发送队列,接收队列等
初始化软队列
初始化消息队列
注册pipeline类型
等等,下面一一解答
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是队列的个数*/
/检测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,msgq_id,f_handle(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);