当dpdk不想在用户态实现协议栈的还,可以采用KNI 调用内核实现的协议栈
kni分为用户态和内核态
其内核态的入口为lib/librte_eal/linuxapp/kni/kni_misc.c中
#可以看这是个标准的ko的实现,其入口函数是kni_initmodule_init(kni_init);
static int __init
kni_init(void)
{int rc;#检查kthread_mode 这个参数是否等于single 或者 multipleif (kni_parse_kthread_mode() < 0) {pr_err("Invalid parameter for kthread_mode\n");return -EINVAL;}if (multiple_kthread_on == 0)pr_debug("Single kernel thread for all KNI devices\n");elsepr_debug("Multiple kernel thread mode enabled\n");#注册一个网络子系统
#ifdef HAVE_SIMPLIFIED_PERNET_OPERATIONSrc = register_pernet_subsys(&kni_net_ops);
#elserc = register_pernet_gen_subsys(&kni_net_id, &kni_net_ops);
#endifif (rc)return -EPERM;#注册一个misc设备,这个misc 设备包含ioctl操作rc = misc_register(&kni_misc);if (rc != 0) {pr_err("Misc registration failed\n");goto out;}#根据输入参数配置lo modekni_net_config_lo_mode(lo_mode);return 0;
}
这个模块可以接受的参数如下:
MODULE_PARM_DESC(lo_mode,
"KNI loopback mode (default=lo_mode_none):\n"
" lo_mode_none Kernel loopback disabled\n"
" lo_mode_fifo Enable kernel loopback with fifo\n"
" lo_mode_fifo_skb Enable kernel loopback with fifo and skb buffer\n"
"\n"
);module_param(kthread_mode, charp, S_IRUGO);
MODULE_PARM_DESC(kthread_mode,
"Kernel thread mode (default=single):\n"
" single Single kernel thread mode enabled.\n"
" multiple Multiple kernel thread mode enabled.\n"
"\n"
);可见可以配置lo mode和kthread_mode
用户态可以参考example/kni/main.c中的写法
int
main(int argc, char** argv)
{int ret;uint16_t nb_sys_ports, port;unsigned i;/* Associate signal_hanlder function with USR signals */#注册下面四个信号量的处理函数为signal_handlersignal(SIGUSR1, signal_handler);signal(SIGUSR2, signal_handler);signal(SIGRTMIN, signal_handler);signal(SIGINT, signal_handler);/* Initialise EAL */#初始化eal环境。ret = rte_eal_init(argc, argv);if (ret < 0)rte_exit(EXIT_FAILURE, "Could not initialise EAL (%d)\n", ret);argc -= ret;argv += ret;/* Parse application arguments (after the EAL ones) */ret = parse_args(argc, argv);if (ret < 0)rte_exit(EXIT_FAILURE, "Could not parse input parameters\n");/* Create the mbuf pool */#创建pktmbuf_pool pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF,MEMPOOL_CACHE_SZ, 0, MBUF_DATA_SZ, rte_socket_id());if (pktmbuf_pool == NULL) {rte_exit(EXIT_FAILURE, "Could not initialise mbuf pool\n");return -1;}/* Get number of ports found in scan */#得到网卡的port的个数nb_sys_ports = rte_eth_dev_count();if (nb_sys_ports == 0)rte_exit(EXIT_FAILURE, "No supported Ethernet device found\n");/* Check if the configured port ID is valid */#检查这些网口id 是否有效for (i = 0; i < RTE_MAX_ETHPORTS; i++)if (kni_port_params_array[i] && i >= nb_sys_ports)rte_exit(EXIT_FAILURE, "Configured invalid ""port ID %u\n", i);/* Initialize KNI subsystem */#初始化KNI 子系统init_kni();#让每个核都运行main_loop/* Launch per-lcore function on every lcore */rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);RTE_LCORE_FOREACH_SLAVE(i) {if (rte_eal_wait_lcore(i) < 0)return -1;}}
具体的mail_loop实现如下:
static int
main_loop(__rte_unused void *arg)
{uint8_t i, nb_ports = rte_eth_dev_count();int32_t f_stop;const unsigned lcore_id = rte_lcore_id();enum lcore_rxtx {LCORE_NONE,LCORE_RX,LCORE_TX,LCORE_MAX};enum lcore_rxtx flag = LCORE_NONE;#具体是发送还是接受for (i = 0; i < nb_ports; i++) {if (!kni_port_params_array[i])continue;if (kni_port_params_array[i]->lcore_rx == (uint8_t)lcore_id) {flag = LCORE_RX;break;} else if (kni_port_params_array[i]->lcore_tx ==(uint8_t)lcore_id) {flag = LCORE_TX;break;}}#给你讲flag决定是发送数据还是接受手机if (flag == LCORE_RX) {RTE_LOG(INFO, APP, "Lcore %u is reading from port %d\n",kni_port_params_array[i]->lcore_rx,kni_port_params_array[i]->port_id);while (1) {f_stop = rte_atomic32_read(&kni_stop);if (f_stop)break;#循环接受数据kni_ingress(kni_port_params_array[i]);}} else if (flag == LCORE_TX) {RTE_LOG(INFO, APP, "Lcore %u is writing to port %d\n",kni_port_params_array[i]->lcore_tx,kni_port_params_array[i]->port_id);while (1) {f_stop = rte_atomic32_read(&kni_stop);if (f_stop)break;#循环发送数据kni_egress(kni_port_params_array[i]);}} elseRTE_LOG(INFO, APP, "Lcore %u has nothing to do\n", lcore_id);return 0;
}
我们看看接受数据的情况
static void
kni_ingress(struct kni_port_params *p)
{uint8_t i;uint16_t port_id;unsigned nb_rx, num;uint32_t nb_kni;struct rte_mbuf *pkts_burst[PKT_BURST_SZ];if (p == NULL)return;nb_kni = p->nb_kni;port_id = p->port_id;for (i = 0; i < nb_kni; i++) {/* Burst rx from eth */#首先从网口读数据到pkts_burstnb_rx = rte_eth_rx_burst(port_id, 0, pkts_burst, PKT_BURST_SZ);if (unlikely(nb_rx > PKT_BURST_SZ)) {RTE_LOG(ERR, APP, "Error receiving from eth\n");return;}/* Burst tx to kni */#将pkts_burst中的数据督导kni中num = rte_kni_tx_burst(p->kni[i], pkts_burst, nb_rx);kni_stats[port_id].rx_packets += num;rte_kni_handle_request(p->kni[i]);if (unlikely(num < nb_rx)) {/* Free mbufs not tx to kni interface */kni_burst_free_mbufs(&pkts_burst[num], nb_rx - num);kni_stats[port_id].rx_dropped += nb_rx - num;}}
}