test_kni.c revision aab0c291
1/*-
2 *   BSD LICENSE
3 *
4 *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5 *   All rights reserved.
6 *
7 *   Redistribution and use in source and binary forms, with or without
8 *   modification, are permitted provided that the following conditions
9 *   are met:
10 *
11 *     * Redistributions of source code must retain the above copyright
12 *       notice, this list of conditions and the following disclaimer.
13 *     * Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in
15 *       the documentation and/or other materials provided with the
16 *       distribution.
17 *     * Neither the name of Intel Corporation nor the names of its
18 *       contributors may be used to endorse or promote products derived
19 *       from this software without specific prior written permission.
20 *
21 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <stdio.h>
35#include <stdint.h>
36#include <unistd.h>
37#include <string.h>
38#include <sys/wait.h>
39
40#include "test.h"
41
42#include <rte_string_fns.h>
43#include <rte_mempool.h>
44#include <rte_ethdev.h>
45#include <rte_cycles.h>
46#include <rte_kni.h>
47
48#define NB_MBUF          8192
49#define MAX_PACKET_SZ    2048
50#define MBUF_DATA_SZ     (MAX_PACKET_SZ + RTE_PKTMBUF_HEADROOM)
51#define PKT_BURST_SZ     32
52#define MEMPOOL_CACHE_SZ PKT_BURST_SZ
53#define SOCKET           0
54#define NB_RXD           128
55#define NB_TXD           512
56#define KNI_TIMEOUT_MS   5000 /* ms */
57
58#define IFCONFIG      "/sbin/ifconfig "
59#define TEST_KNI_PORT "test_kni_port"
60#define KNI_TEST_MAX_PORTS 4
61/* The threshold number of mbufs to be transmitted or received. */
62#define KNI_NUM_MBUF_THRESHOLD 100
63static int kni_pkt_mtu = 0;
64
65struct test_kni_stats {
66	volatile uint64_t ingress;
67	volatile uint64_t egress;
68};
69
70static const struct rte_eth_rxconf rx_conf = {
71	.rx_thresh = {
72		.pthresh = 8,
73		.hthresh = 8,
74		.wthresh = 4,
75	},
76	.rx_free_thresh = 0,
77};
78
79static const struct rte_eth_txconf tx_conf = {
80	.tx_thresh = {
81		.pthresh = 36,
82		.hthresh = 0,
83		.wthresh = 0,
84	},
85	.tx_free_thresh = 0,
86	.tx_rs_thresh = 0,
87};
88
89static const struct rte_eth_conf port_conf = {
90	.rxmode = {
91		.header_split = 0,
92		.hw_ip_checksum = 0,
93		.hw_vlan_filter = 0,
94		.jumbo_frame = 0,
95		.hw_strip_crc = 1,
96	},
97	.txmode = {
98		.mq_mode = ETH_DCB_NONE,
99	},
100};
101
102static struct rte_kni_ops kni_ops = {
103	.change_mtu = NULL,
104	.config_network_if = NULL,
105};
106
107static unsigned lcore_master, lcore_ingress, lcore_egress;
108static struct rte_kni *test_kni_ctx;
109static struct test_kni_stats stats;
110
111static volatile uint32_t test_kni_processing_flag;
112
113static struct rte_mempool *
114test_kni_create_mempool(void)
115{
116	struct rte_mempool * mp;
117
118	mp = rte_mempool_lookup("kni_mempool");
119	if (!mp)
120		mp = rte_pktmbuf_pool_create("kni_mempool",
121				NB_MBUF,
122				MEMPOOL_CACHE_SZ, 0, MBUF_DATA_SZ,
123				SOCKET);
124
125	return mp;
126}
127
128static struct rte_mempool *
129test_kni_lookup_mempool(void)
130{
131	return rte_mempool_lookup("kni_mempool");
132}
133/* Callback for request of changing MTU */
134static int
135kni_change_mtu(uint8_t port_id, unsigned new_mtu)
136{
137	printf("Change MTU of port %d to %u\n", port_id, new_mtu);
138	kni_pkt_mtu = new_mtu;
139	printf("Change MTU of port %d to %i successfully.\n",
140					 port_id, kni_pkt_mtu);
141	return 0;
142}
143/**
144 * This loop fully tests the basic functions of KNI. e.g. transmitting,
145 * receiving to, from kernel space, and kernel requests.
146 *
147 * This is the loop to transmit/receive mbufs to/from kernel interface with
148 * supported by KNI kernel module. The ingress lcore will allocate mbufs and
149 * transmit them to kernel space; while the egress lcore will receive the mbufs
150 * from kernel space and free them.
151 * On the master lcore, several commands will be run to check handling the
152 * kernel requests. And it will finally set the flag to exit the KNI
153 * transmitting/receiving to/from the kernel space.
154 *
155 * Note: To support this testing, the KNI kernel module needs to be insmodded
156 * in one of its loopback modes.
157 */
158static int
159test_kni_loop(__rte_unused void *arg)
160{
161	int ret = 0;
162	unsigned nb_rx, nb_tx, num, i;
163	const unsigned lcore_id = rte_lcore_id();
164	struct rte_mbuf *pkts_burst[PKT_BURST_SZ];
165
166	if (lcore_id == lcore_master) {
167		rte_delay_ms(KNI_TIMEOUT_MS);
168		/* tests of handling kernel request */
169		if (system(IFCONFIG TEST_KNI_PORT" up") == -1)
170			ret = -1;
171		if (system(IFCONFIG TEST_KNI_PORT" mtu 1400") == -1)
172			ret = -1;
173		if (system(IFCONFIG TEST_KNI_PORT" down") == -1)
174			ret = -1;
175		rte_delay_ms(KNI_TIMEOUT_MS);
176		test_kni_processing_flag = 1;
177	} else if (lcore_id == lcore_ingress) {
178		struct rte_mempool *mp = test_kni_lookup_mempool();
179
180		if (mp == NULL)
181			return -1;
182
183		while (1) {
184			if (test_kni_processing_flag)
185				break;
186
187			for (nb_rx = 0; nb_rx < PKT_BURST_SZ; nb_rx++) {
188				pkts_burst[nb_rx] = rte_pktmbuf_alloc(mp);
189				if (!pkts_burst[nb_rx])
190					break;
191			}
192
193			num = rte_kni_tx_burst(test_kni_ctx, pkts_burst,
194								nb_rx);
195			stats.ingress += num;
196			rte_kni_handle_request(test_kni_ctx);
197			if (num < nb_rx) {
198				for (i = num; i < nb_rx; i++) {
199					rte_pktmbuf_free(pkts_burst[i]);
200				}
201			}
202			rte_delay_ms(10);
203		}
204	} else if (lcore_id == lcore_egress) {
205		while (1) {
206			if (test_kni_processing_flag)
207				break;
208			num = rte_kni_rx_burst(test_kni_ctx, pkts_burst,
209							PKT_BURST_SZ);
210			stats.egress += num;
211			for (nb_tx = 0; nb_tx < num; nb_tx++)
212				rte_pktmbuf_free(pkts_burst[nb_tx]);
213			rte_delay_ms(10);
214		}
215	}
216
217	return ret;
218}
219
220static int
221test_kni_allocate_lcores(void)
222{
223	unsigned i, count = 0;
224
225	lcore_master = rte_get_master_lcore();
226	printf("master lcore: %u\n", lcore_master);
227	for (i = 0; i < RTE_MAX_LCORE; i++) {
228		if (count >=2 )
229			break;
230		if (rte_lcore_is_enabled(i) && i != lcore_master) {
231			count ++;
232			if (count == 1)
233				lcore_ingress = i;
234			else if (count == 2)
235				lcore_egress = i;
236		}
237	}
238	printf("count: %u\n", count);
239
240	return count == 2 ? 0 : -1;
241}
242
243static int
244test_kni_register_handler_mp(void)
245{
246#define TEST_KNI_HANDLE_REQ_COUNT    10  /* 5s */
247#define TEST_KNI_HANDLE_REQ_INTERVAL 500 /* ms */
248#define TEST_KNI_MTU                 1450
249#define TEST_KNI_MTU_STR             " 1450"
250	int pid;
251
252	pid = fork();
253	if (pid < 0) {
254		printf("Failed to fork a process\n");
255		return -1;
256	} else if (pid == 0) {
257		int i;
258		struct rte_kni *kni = rte_kni_get(TEST_KNI_PORT);
259		struct rte_kni_ops ops = {
260			.change_mtu = kni_change_mtu,
261			.config_network_if = NULL,
262		};
263
264		if (!kni) {
265			printf("Failed to get KNI named %s\n", TEST_KNI_PORT);
266			exit(-1);
267		}
268
269		kni_pkt_mtu = 0;
270
271		/* Check with the invalid parameters */
272		if (rte_kni_register_handlers(kni, NULL) == 0) {
273			printf("Unexpectedly register successuflly "
274					"with NULL ops pointer\n");
275			exit(-1);
276		}
277		if (rte_kni_register_handlers(NULL, &ops) == 0) {
278			printf("Unexpectedly register successfully "
279					"to NULL KNI device pointer\n");
280			exit(-1);
281		}
282
283		if (rte_kni_register_handlers(kni, &ops)) {
284			printf("Fail to register ops\n");
285			exit(-1);
286		}
287
288		/* Check registering again after it has been registered */
289		if (rte_kni_register_handlers(kni, &ops) == 0) {
290			printf("Unexpectedly register successfully after "
291					"it has already been registered\n");
292			exit(-1);
293		}
294
295		/**
296		 * Handle the request of setting MTU,
297		 * with registered handlers.
298		 */
299		for (i = 0; i < TEST_KNI_HANDLE_REQ_COUNT; i++) {
300			rte_kni_handle_request(kni);
301			if (kni_pkt_mtu == TEST_KNI_MTU)
302				break;
303			rte_delay_ms(TEST_KNI_HANDLE_REQ_INTERVAL);
304		}
305		if (i >= TEST_KNI_HANDLE_REQ_COUNT) {
306			printf("MTU has not been set\n");
307			exit(-1);
308		}
309
310		kni_pkt_mtu = 0;
311		if (rte_kni_unregister_handlers(kni) < 0) {
312			printf("Fail to unregister ops\n");
313			exit(-1);
314		}
315
316		/* Check with invalid parameter */
317		if (rte_kni_unregister_handlers(NULL) == 0) {
318			exit(-1);
319		}
320
321		/**
322		 * Handle the request of setting MTU,
323		 * without registered handlers.
324		 */
325		for (i = 0; i < TEST_KNI_HANDLE_REQ_COUNT; i++) {
326			rte_kni_handle_request(kni);
327			if (kni_pkt_mtu != 0)
328				break;
329			rte_delay_ms(TEST_KNI_HANDLE_REQ_INTERVAL);
330		}
331		if (kni_pkt_mtu != 0) {
332			printf("MTU shouldn't be set\n");
333			exit(-1);
334		}
335
336		exit(0);
337	} else {
338		int p_ret, status;
339
340		rte_delay_ms(1000);
341		if (system(IFCONFIG TEST_KNI_PORT " mtu" TEST_KNI_MTU_STR)
342								== -1)
343			return -1;
344
345		rte_delay_ms(1000);
346		if (system(IFCONFIG TEST_KNI_PORT " mtu" TEST_KNI_MTU_STR)
347								== -1)
348			return -1;
349
350		p_ret = wait(&status);
351		if (!WIFEXITED(status)) {
352			printf("Child process (%d) exit abnormally\n", p_ret);
353			return -1;
354		}
355		if (WEXITSTATUS(status) != 0) {
356			printf("Child process exit with failure\n");
357			return -1;
358		}
359	}
360
361	return 0;
362}
363
364static int
365test_kni_processing(uint8_t port_id, struct rte_mempool *mp)
366{
367	int ret = 0;
368	unsigned i;
369	struct rte_kni *kni;
370	struct rte_kni_conf conf;
371	struct rte_eth_dev_info info;
372	struct rte_kni_ops ops;
373
374	if (!mp)
375		return -1;
376
377	memset(&conf, 0, sizeof(conf));
378	memset(&info, 0, sizeof(info));
379	memset(&ops, 0, sizeof(ops));
380
381	rte_eth_dev_info_get(port_id, &info);
382	conf.addr = info.pci_dev->addr;
383	conf.id = info.pci_dev->id;
384	snprintf(conf.name, sizeof(conf.name), TEST_KNI_PORT);
385
386	/* core id 1 configured for kernel thread */
387	conf.core_id = 1;
388	conf.force_bind = 1;
389	conf.mbuf_size = MAX_PACKET_SZ;
390	conf.group_id = (uint16_t)port_id;
391
392	ops = kni_ops;
393	ops.port_id = port_id;
394
395	/* basic test of kni processing */
396	kni = rte_kni_alloc(mp, &conf, &ops);
397	if (!kni) {
398		printf("fail to create kni\n");
399		return -1;
400	}
401
402	test_kni_ctx = kni;
403	test_kni_processing_flag = 0;
404	stats.ingress = 0;
405	stats.egress = 0;
406
407	/**
408	 * Check multiple processes support on
409	 * registerring/unregisterring handlers.
410	 */
411	if (test_kni_register_handler_mp() < 0) {
412		printf("fail to check multiple process support\n");
413		ret = -1;
414		goto fail_kni;
415	}
416
417	rte_eal_mp_remote_launch(test_kni_loop, NULL, CALL_MASTER);
418	RTE_LCORE_FOREACH_SLAVE(i) {
419		if (rte_eal_wait_lcore(i) < 0) {
420			ret = -1;
421			goto fail_kni;
422		}
423	}
424	/**
425	 * Check if the number of mbufs received from kernel space is equal
426	 * to that of transmitted to kernel space
427	 */
428	if (stats.ingress < KNI_NUM_MBUF_THRESHOLD ||
429		stats.egress < KNI_NUM_MBUF_THRESHOLD) {
430		printf("The ingress/egress number should not be "
431			"less than %u\n", (unsigned)KNI_NUM_MBUF_THRESHOLD);
432		ret = -1;
433		goto fail_kni;
434	}
435
436	if (rte_kni_release(kni) < 0) {
437		printf("fail to release kni\n");
438		return -1;
439	}
440	test_kni_ctx = NULL;
441
442	/* test of releasing a released kni device */
443	if (rte_kni_release(kni) == 0) {
444		printf("should not release a released kni device\n");
445		return -1;
446	}
447
448	/* test of reusing memzone */
449	kni = rte_kni_alloc(mp, &conf, &ops);
450	if (!kni) {
451		printf("fail to create kni\n");
452		return -1;
453	}
454
455	/* Release the kni for following testing */
456	if (rte_kni_release(kni) < 0) {
457		printf("fail to release kni\n");
458		return -1;
459	}
460
461	return ret;
462fail_kni:
463	if (rte_kni_release(kni) < 0) {
464		printf("fail to release kni\n");
465		ret = -1;
466	}
467
468	return ret;
469}
470
471static int
472test_kni(void)
473{
474	int ret = -1;
475	uint8_t nb_ports, port_id;
476	struct rte_kni *kni;
477	struct rte_mempool *mp;
478	struct rte_kni_conf conf;
479	struct rte_eth_dev_info info;
480	struct rte_kni_ops ops;
481
482	/* Initialize KNI subsytem */
483	rte_kni_init(KNI_TEST_MAX_PORTS);
484
485	if (test_kni_allocate_lcores() < 0) {
486		printf("No enough lcores for kni processing\n");
487		return -1;
488	}
489
490	mp = test_kni_create_mempool();
491	if (!mp) {
492		printf("fail to create mempool for kni\n");
493		return -1;
494	}
495
496	nb_ports = rte_eth_dev_count();
497	if (nb_ports == 0) {
498		printf("no supported nic port found\n");
499		return -1;
500	}
501
502	/* configuring port 0 for the test is enough */
503	port_id = 0;
504	ret = rte_eth_dev_configure(port_id, 1, 1, &port_conf);
505	if (ret < 0) {
506		printf("fail to configure port %d\n", port_id);
507		return -1;
508	}
509
510	ret = rte_eth_rx_queue_setup(port_id, 0, NB_RXD, SOCKET, &rx_conf, mp);
511	if (ret < 0) {
512		printf("fail to setup rx queue for port %d\n", port_id);
513		return -1;
514	}
515
516	ret = rte_eth_tx_queue_setup(port_id, 0, NB_TXD, SOCKET, &tx_conf);
517	if (ret < 0) {
518		printf("fail to setup tx queue for port %d\n", port_id);
519		return -1;
520	}
521
522	ret = rte_eth_dev_start(port_id);
523	if (ret < 0) {
524		printf("fail to start port %d\n", port_id);
525		return -1;
526	}
527	rte_eth_promiscuous_enable(port_id);
528
529	/* basic test of kni processing */
530	ret = test_kni_processing(port_id, mp);
531	if (ret < 0)
532		goto fail;
533
534	/* test of allocating KNI with NULL mempool pointer */
535	memset(&info, 0, sizeof(info));
536	memset(&conf, 0, sizeof(conf));
537	memset(&ops, 0, sizeof(ops));
538	rte_eth_dev_info_get(port_id, &info);
539	conf.addr = info.pci_dev->addr;
540	conf.id = info.pci_dev->id;
541	conf.group_id = (uint16_t)port_id;
542	conf.mbuf_size = MAX_PACKET_SZ;
543
544	ops = kni_ops;
545	ops.port_id = port_id;
546	kni = rte_kni_alloc(NULL, &conf, &ops);
547	if (kni) {
548		ret = -1;
549		printf("unexpectedly creates kni successfully with NULL "
550							"mempool pointer\n");
551		goto fail;
552	}
553
554	/* test of allocating KNI without configurations */
555	kni = rte_kni_alloc(mp, NULL, NULL);
556	if (kni) {
557		ret = -1;
558		printf("Unexpectedly allocate KNI device successfully "
559					"without configurations\n");
560		goto fail;
561	}
562
563	/* test of allocating KNI without a name */
564	memset(&conf, 0, sizeof(conf));
565	memset(&info, 0, sizeof(info));
566	memset(&ops, 0, sizeof(ops));
567	rte_eth_dev_info_get(port_id, &info);
568	conf.addr = info.pci_dev->addr;
569	conf.id = info.pci_dev->id;
570	conf.group_id = (uint16_t)port_id;
571	conf.mbuf_size = MAX_PACKET_SZ;
572
573	ops = kni_ops;
574	ops.port_id = port_id;
575	kni = rte_kni_alloc(mp, &conf, &ops);
576	if (kni) {
577		ret = -1;
578		printf("Unexpectedly allocate a KNI device successfully "
579						"without a name\n");
580		goto fail;
581	}
582
583	/* test of releasing NULL kni context */
584	ret = rte_kni_release(NULL);
585	if (ret == 0) {
586		ret = -1;
587		printf("unexpectedly release kni successfully\n");
588		goto fail;
589	}
590
591	/* test of handling request on NULL device pointer */
592	ret = rte_kni_handle_request(NULL);
593	if (ret == 0) {
594		ret = -1;
595		printf("Unexpectedly handle request on NULL device pointer\n");
596		goto fail;
597	}
598
599	/* test of getting KNI device with pointer to NULL */
600	kni = rte_kni_get(NULL);
601	if (kni) {
602		ret = -1;
603		printf("Unexpectedly get a KNI device with "
604					"NULL name pointer\n");
605		goto fail;
606	}
607
608	/* test of getting KNI device with an zero length name string */
609	memset(&conf, 0, sizeof(conf));
610	kni = rte_kni_get(conf.name);
611	if (kni) {
612		ret = -1;
613		printf("Unexpectedly get a KNI device with "
614				"zero length name string\n");
615		goto fail;
616	}
617
618	/* test of getting KNI device with an invalid string name */
619	memset(&conf, 0, sizeof(conf));
620	snprintf(conf.name, sizeof(conf.name), "testing");
621	kni = rte_kni_get(conf.name);
622	if (kni) {
623		ret = -1;
624		printf("Unexpectedly get a KNI device with "
625				"a never used name string\n");
626		goto fail;
627	}
628	ret = 0;
629
630fail:
631	rte_eth_dev_stop(port_id);
632
633	return ret;
634}
635
636REGISTER_TEST_COMMAND(kni_autotest, test_kni);
637