# ff_init函数详解
int ff_init(int argc, char * const argv[])
{
int ret;
/**
* 1. 解析命令行参数
* 2. 加载ini格式配置文件
*/
ret = ff_load_config(argc, argv);
if (ret < 0)
exit(1);
/**
* 多进程框架下dpdk相关初始化
*/
ret = ff_dpdk_init(dpdk_argc, (char **)&dpdk_argv);
if (ret < 0)
exit(1);
/**
* 初始化freebsd子系统,启动freebsd系统
* 1. 设置kern.hz环境变量,更改freebsd系统的系统时钟
* 2. 设置freebsd boot相关环境变量
* 3. 设置freebsd系统物理内存大小,默认为256M
* 4. 分配和初始化numa结构中per-cpu结构
* 5. 初始化freebsd系统中0号进程
* 6. 启动freebsd内存管理子系统
* 7. freebsd同步机制初始化
* 8. 启动freebsd系统
* 9. 预留文件描述符
* 10. 设置freebsd sysctl相关参数
*/
ret = ff_freebsd_init();
if (ret < 0)
exit(1);
/**
*
* 1.新建freebsd网络设备并加入freebsd内核中
* 2.设置网络设备驱动相关函数指针
* 3.将dpdk soft context与freebsd协议栈上下文关联
*/
ret = ff_dpdk_if_up();
if (ret < 0)
exit(1);
return 0;
}
int ff_load_config(int argc, char *const argv[])
{
/**
* ff_global_cfg全局配置变量
* 初始化配置为默认值
*/
ff_default_config(&ff_global_cfg);
/**
* 解析命令行参数相关配置
* 1. --conf|-c 指定配置文件
* 2. --proc-type|-t 指定当前进程类型,primary|secondary|auto
* 3. --proc-id|-p 指定当前进程使用lcore索引
*/
int ret = ff_parse_args(&ff_global_cfg, argc, argv);
if (ret < 0) {
return ret;
}
/**
* 解析ini文件,初始化其余配置项
*/
ret = ini_parse(ff_global_cfg.filename, ini_parse_handler,
&ff_global_cfg);
if (ret != 0) {
printf("parse %s failed on line %d\n", ff_global_cfg.filename, ret);
return -1;
}
/**
* 配置检查
* 1. kni选项检查,如果启用了kni选项,必须指定method参数
* 2. 检查port相关配置,必须包含addr、netmask、broadcast、gateway
* 3. 指定使用lcore有效性
* 4. 启用kni功能时,primary进程使用的lcore必须在启用port端口lcore_list中
*/
if (ff_check_config(&ff_global_cfg)) {
return -1;
}
/**
* 根据配置项设置dpdk eal层(Environment Abstraction Layer)初始化参数
*/
if (dpdk_args_setup(&ff_global_cfg) <= 0) {
return -1;
}
return 0;
}
int ff_dpdk_init(int argc, char **argv)
{
/**
*参数校验
*/
if (ff_global_cfg.dpdk.nb_procs < 1 ||
ff_global_cfg.dpdk.nb_procs > RTE_MAX_LCORE ||
ff_global_cfg.dpdk.proc_id >= ff_global_cfg.dpdk.nb_procs ||
ff_global_cfg.dpdk.proc_id < 0) {
printf("param num_procs[%d] or proc_id[%d] error!\n",
ff_global_cfg.dpdk.nb_procs,
ff_global_cfg.dpdk.proc_id);
exit(1);
}
/* dpdk eal初始化 */
int ret = rte_eal_init(argc, argv);
if (ret < 0) {
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
}
numa_on = ff_global_cfg.dpdk.numa_on;
idle_sleep = ff_global_cfg.dpdk.idle_sleep;
/**
*初始化lcore相应配置
*1. 检查port有效性
*2. 检查enable lcore有效性
*3. numa启用时,设置lcore对应cpu所在socketid
*4. 设置当前lcore与需要处理网卡队列关系(port设置的lcore_list中每个lcore处理网卡的一个rx队列核一个tx队列)
*/
init_lcore_conf();
/**
*初始化网卡收发包mbuf内存缓冲池
*1. 由primary进程分配
*2. 如果支持numa,则为numa中每个socket分配一个缓冲(局部性原理)
*3. 缓冲池名称为mbuf_pool_socketid
*/
init_mem_pool();
/**
*初始化网卡队列通信dispatch_ring数组,,该数组主要用于当用户通过ff_regist_packet_dispatcher注册了自定义分发器后,在不同lcore之间分发接收到的数据包
*1. dispatch_ring为一二位数组,行数为启用port数目,列数为对应网卡启用队列数目
*2. 指针指向rte_ring结构
*3. ring名称为dispatch_ring_p#portid_q#queueid
*4. ring由primary进程分配
*5. ring为单消费者,多生产者
*/
init_dispatch_ring();
/**
*初始化message_pool和msg_ring
*1. message_pool名称为ff_msg_pool
*2. 该ring主要用于ioctl等控制,供ifconfig等工具程序使用
*3. 由primary进程分配
*4. 每个lcore分配一对ring,一个用于IN,一个用于OUT
*5. ring名称为ff_msg_ring_in_#lcoreid、ff_msg_ring_out_#lcoreid
*6. ring为单生产者、单消费者
*/
init_msg_ring();
#ifdef FF_KNI
enable_kni = ff_global_cfg.kni.enable;
if (enable_kni) {
init_kni();
}
#endif
/**
*配置网卡并启动网卡,该函数主要在primary进程中执行
*1. 获取网卡配置
*2. 设置网卡RX、TX队列配置
*3. 设置RSS(receive side scaling)参数,如果开启多队列,则设置为对称RSS
*4. 开启网卡混杂模式
*5. 开启pcap模式
*6. 检查所有启用网卡连接状态
*/
ret = init_port_start();
if (ret < 0) {
rte_exit(EXIT_FAILURE, "init_port_start failed\n");
}
/**
*初始化定时器相关
*1. 初始化dpdk定时器子系统
*2. 根据配置文件提供hz初始化定时器时钟频率
*3. 初始化freebsd协议栈使用定时器,设置回调函数
*/
init_clock();
return 0;
}
static int init_kni(void)
{
int nb_ports = rte_eth_dev_count();
kni_accept = 0;
if(strcasecmp(ff_global_cfg.kni.method, "accept") == 0)
kni_accept = 1;
/**
*kni模块初始化
*1. primary完成用于存储kni子系统统计信息数组指针kni_stat,每个port对应一个kni_interface_stats结构
*2. primary完成kni子系统初始化
*3. 分配用于存储kni模块通信使用ring指针数组kni_rp,缓冲池名称为kni::ring_%d,每个port对应一个ring
*4. 根据配置文件初始化kni模块相关tcp/udp层端口位图,位图大小为8192*8
*/
ff_kni_init(nb_ports, ff_global_cfg.kni.tcp_port, ff_global_cfg.kni.udp_port);
unsigned socket_id = lcore_conf.socket_id;
/**
*关联kni模块使用mbuf缓冲池与当前进程proc lcore网卡收发包mbuf内存缓冲池在同一numa socket上
*/
struct rte_mempool *mbuf_pool = pktmbuf_pool[socket_id];
nb_ports = ff_global_cfg.dpdk.nb_ports;
int i, ret;
for (i = 0; i < nb_ports; i++) {
uint16_t port_id = ff_global_cfg.dpdk.portid_list[i];
/**
*分配kni模块使用的数据结构,由primary进程分配,secondary进程获取即可
*1. 分配统计信息结构
*2. 为每个port分配对应的rte_kni结构
*3. 为kni模块通信使用ring分配空间
*4. ring为单消费者、多生产者
*/
ff_kni_alloc(port_id, socket_id, mbuf_pool, KNI_QUEUE_SIZE);
}
return 0;
}
int ff_dpdk_if_up(void)
{
int i;
struct lcore_conf *qconf = &lcore_conf;
/**
* 遍历所有的网卡port
*/
for (i = 0; i < qconf->nb_tx_port; i++) {
uint16_t port_id = qconf->tx_port_id[i];
/**
* 获取port对应配置
*/
struct ff_port_cfg *pconf = &qconf->port_cfgs[port_id];
/**
* 1.新建freebsd网络设备并加入freebsd内核中
* 2.设置网络设备驱动相关函数指针
* 3.将dpdk soft context与freebsd协议栈上下文关联
*/
veth_ctx[port_id] = ff_veth_attach(pconf);
if (veth_ctx[port_id] == NULL) {
rte_exit(EXIT_FAILURE, "ff_veth_attach failed");
}
}
return 0;
}