参考文献:
DPDK官网
........................................................................................................................................
一. DPDK KNI 作用
DPDK Kernel NIC Interface(KNI)允许用户空间应用程序访问Linux *控制面。
使用DPDK KNI的好处是:
- 比现有的Linux TUN / TAP接口更快(通过消除系统调用和copy_to_user()/copy_from_user()操作)。
- 允许使用标准Linux网络工具(如ethtool,ifconfig和tcpdump)管理DPDK端口。
- 允许与内核网络堆栈的接口。
使用DPDK内核NIC接口的应用程序的组件如图所示。
KNI内核可加载模块支持两种类型的设备:
- 其他设备:
- 创建网络设备(通过ioctl调用)。
- 维护所有KNI实例共享的内核线程上下文(模拟网络驱动程序的RX端)。
- 对于单内核线程模式,维护所有KNI实例共享的内核线程上下文(模拟网络驱动程序的RX端)。
- 对于多个内核线程模式,为每个KNI实例(模拟新驱动程序的RX侧)维护一个内核线程上下文。
网络设备:
- 通过实现由struct net_device定义的诸如netdev_ops,header_ops,ethtool_ops之类的几个操作提供的Net功能,包括支持DPDK mbufs和FIFO。
- 接口名称由用户空间提供。 MAC地址可以是真正的NIC MAC地址或随机
DPDK缓冲区流
为了最小化在内核空间中运行的DPDK代码的数量,mbuf mempool仅在用户空间中进行管理。内核模块可以感知mbufs,但是所有mbuf分配和释放操作将仅由DPDK应用程序处理。
kni 源码讲解
1 /* Initialise EAL */ 2 ret = rte_eal_init(argc, argv); 3 if (ret < 0) 4 rte_exit(EXIT_FAILURE, "Could not initialise EAL (%d) ", ret); 5 argc -= ret; 6 argv += ret; 7 8 /* Parse application arguments (after the EAL ones) */ 9 ret = parse_args(argc, argv); 10 if (ret < 0) 11 rte_exit(EXIT_FAILURE, "Could not parse input parameters "); 12 13 /* Create the mbuf pool */ 14 pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF, 15 MEMPOOL_CACHE_SZ, 0, MBUF_DATA_SZ, rte_socket_id()); 16 if (pktmbuf_pool == NULL) { 17 rte_exit(EXIT_FAILURE, "Could not initialise mbuf pool "); 18 return -1; 19 } 20 21 /* Get number of ports found in scan */ 22 nb_sys_ports = rte_eth_dev_count_avail(); 23 if (nb_sys_ports == 0) 24 rte_exit(EXIT_FAILURE, "No supported Ethernet device found "); 25 26 /* Check if the configured port ID is valid */ 27 for (i = 0; i < RTE_MAX_ETHPORTS; i++) 28 if (kni_port_params_array[i] && !rte_eth_dev_is_valid_port(i)) 29 rte_exit(EXIT_FAILURE, "Configured invalid " 30 "port ID %u ", i); 31 32 /* Initialize KNI subsystem */ 33 init_kni(); 34 35 /* Initialise each port */ 36 RTE_ETH_FOREACH_DEV(port) { 37 /* Skip ports that are not enabled */ 38 if (!(ports_mask & (1 << port))) 39 continue; 40 init_port(port); 41 42 if (port >= RTE_MAX_ETHPORTS) 43 rte_exit(EXIT_FAILURE, "Can not use more than " 44 "%d ports for kni ", RTE_MAX_ETHPORTS); 45 46 kni_alloc(port); 47 } 48 check_all_ports_link_status(ports_mask); 49 50 pid = getpid(); 51 RTE_LOG(INFO, APP, "======================== "); 52 RTE_LOG(INFO, APP, "KNI Running "); 53 RTE_LOG(INFO, APP, "kill -SIGUSR1 %d ", pid); 54 RTE_LOG(INFO, APP, " Show KNI Statistics. "); 55 RTE_LOG(INFO, APP, "kill -SIGUSR2 %d ", pid); 56 RTE_LOG(INFO, APP, " Zero KNI Statistics. "); 57 RTE_LOG(INFO, APP, "======================== "); 58 fflush(stdout); 59 60 ret = rte_ctrl_thread_create(&kni_link_tid, 61 "KNI link status check", NULL, 62 monitor_all_ports_link_status, NULL); 63 if (ret < 0) 64 rte_exit(EXIT_FAILURE, 65 "Could not create link status thread! "); 66 67 /* Launch per-lcore function on every lcore */ 68 rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER); 69 RTE_LCORE_FOREACH_SLAVE(i) { 70 if (rte_eal_wait_lcore(i) < 0) 71 return -1; 72 } 73 monitor_links = 0; 74 pthread_join(kni_link_tid, &retval); 75 76 /* Release resources */ 77 RTE_ETH_FOREACH_DEV(port) { 78 if (!(ports_mask & (1 << port))) 79 continue; 80 kni_free_kni(port); 81 } 82 for (i = 0; i < RTE_MAX_ETHPORTS; i++) 83 if (kni_port_params_array[i]) { 84 rte_free(kni_port_params_array[i]); 85 kni_port_params_array[i] = NULL; 86 } 87 88 return 0;
main 主要完成步骤:
1.进行EAL层的初始化
2.解析DPDK和应用层的参数
3.分配报文的mempool,为接收报文做准备
4.初始化以及启动接口
5.启动各个核上的线程