kni

当dpdk不想在用户态实现协议栈的还,可以采用KNI 调用内核实现的协议栈
kni分为用户态和内核态
其内核态的入口为lib/librte_eal/linuxapp/kni/kni_misc.c中
#可以看这是个标准的ko的实现,其入口函数是kni_init

module_init(kni_init);
static int __init
kni_init(void)
{
	int rc;
	#检查kthread_mode 这个参数是否等于single 或者 multiple
	if (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");
	else
		pr_debug("Multiple kernel thread mode enabled\n");
	#注册一个网络子系统
#ifdef HAVE_SIMPLIFIED_PERNET_OPERATIONS
	rc = register_pernet_subsys(&kni_net_ops);
#else
	rc = register_pernet_gen_subsys(&kni_net_id, &kni_net_ops);
#endif
	if (rc)
		return -EPERM;
	#注册一个misc设备,这个misc 设备包含ioctl操作
	rc = misc_register(&kni_misc);
	if (rc != 0) {
		pr_err("Misc registration failed\n");
		goto out;
	}
	#根据输入参数配置lo mode
	kni_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_handler
	signal(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]);
		}
	} else
		RTE_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_burst
		nb_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;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/tiantao2012/article/details/80678603
kni
kNN
JNI
INI
SNI
cni