b02150f819c923ddff264a4f1e8da12fe6f0c30e
[dpdk.git] / app / test / test_kni.c
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2013 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
35 #include <stdio.h>
36 #include <stdint.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <sys/wait.h>
40
41 #include <cmdline_parse.h>
42
43 #include "test.h"
44
45 #ifdef RTE_LIBRTE_KNI
46 #include <rte_mempool.h>
47 #include <rte_ethdev.h>
48 #include <rte_cycles.h>
49 #include <rte_kni.h>
50
51 #define NB_MBUF          (8192 * 16)
52 #define MAX_PACKET_SZ    2048
53 #define MBUF_SZ \
54         (MAX_PACKET_SZ + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
55 #define PKT_BURST_SZ     32
56 #define MEMPOOL_CACHE_SZ PKT_BURST_SZ
57 #define SOCKET           0
58 #define NB_RXD           128
59 #define NB_TXD           512
60 #define KNI_TIMEOUT_MS   5000 /* ms */
61
62 #define IFCONFIG "/sbin/ifconfig"
63
64 /* The threshold number of mbufs to be transmitted or received. */
65 #define KNI_NUM_MBUF_THRESHOLD 100
66 static int kni_pkt_mtu = 0;
67
68 struct test_kni_stats {
69         volatile uint64_t ingress;
70         volatile uint64_t egress;
71 };
72
73 static const struct rte_eth_rxconf rx_conf = {
74         .rx_thresh = {
75                 .pthresh = 8,
76                 .hthresh = 8,
77                 .wthresh = 4,
78         },
79         .rx_free_thresh = 0,
80 };
81
82 static const struct rte_eth_txconf tx_conf = {
83         .tx_thresh = {
84                 .pthresh = 36,
85                 .hthresh = 0,
86                 .wthresh = 0,
87         },
88         .tx_free_thresh = 0,
89         .tx_rs_thresh = 0,
90 };
91
92 static const struct rte_eth_conf port_conf = {
93         .rxmode = {
94                 .header_split = 0,
95                 .hw_ip_checksum = 0,
96                 .hw_vlan_filter = 0,
97                 .jumbo_frame = 0,
98                 .hw_strip_crc = 0,
99         },
100         .txmode = {
101                 .mq_mode = ETH_DCB_NONE,
102         },
103 };
104
105 static struct rte_kni_ops kni_ops = {
106         .change_mtu = NULL,
107         .config_network_if = NULL,
108 };
109
110 static unsigned lcore_master, lcore_ingress, lcore_egress;
111 static struct rte_kni *test_kni_ctx;
112 static struct test_kni_stats stats;
113
114 static volatile uint32_t test_kni_processing_flag;
115
116 static struct rte_mempool *
117 test_kni_create_mempool(void)
118 {
119         struct rte_mempool * mp;
120
121         mp = rte_mempool_lookup("kni_mempool");
122         if (!mp)
123                 mp = rte_mempool_create("kni_mempool",
124                                 NB_MBUF,
125                                 MBUF_SZ,
126                                 MEMPOOL_CACHE_SZ,
127                                 sizeof(struct rte_pktmbuf_pool_private),
128                                 rte_pktmbuf_pool_init,
129                                 NULL,
130                                 rte_pktmbuf_init,
131                                 NULL,
132                                 SOCKET,
133                                 0);
134
135         return mp;
136 }
137
138 static struct rte_mempool *
139 test_kni_lookup_mempool(void)
140 {
141         return rte_mempool_lookup("kni_mempool");
142 }
143 /* Callback for request of changing MTU */
144 static int
145 kni_change_mtu(uint8_t port_id, unsigned new_mtu)
146 {
147         printf("Change MTU of port %d to %u\n", port_id, new_mtu);
148         kni_pkt_mtu = new_mtu;  
149         printf("Change MTU of port %d to %i successfully.\n",
150                                          port_id, kni_pkt_mtu);
151         return 0;
152 }
153 /**
154  * This loop fully tests the basic functions of KNI. e.g. transmitting,
155  * receiving to, from kernel space, and kernel requests.
156  *
157  * This is the loop to transmit/receive mbufs to/from kernel interface with
158  * supported by KNI kernel module. The ingress lcore will allocate mbufs and
159  * transmit them to kernel space; while the egress lcore will receive the mbufs
160  * from kernel space and free them.
161  * On the master lcore, several commands will be run to check handling the
162  * kernel requests. And it will finally set the flag to exit the KNI
163  * transmitting/receiving to/from the kernel space.
164  *
165  * Note: To support this testing, the KNI kernel module needs to be insmodded
166  * in one of its loopback modes.
167  */
168 static int
169 test_kni_loop(__rte_unused void *arg)
170 {
171         int ret = 0;
172         unsigned nb_rx, nb_tx, num, i;
173         const unsigned lcore_id = rte_lcore_id();
174         struct rte_mbuf *pkts_burst[PKT_BURST_SZ];
175
176         if (lcore_id == lcore_master) {
177                 rte_delay_ms(KNI_TIMEOUT_MS);
178                 /* tests of handling kernel request */
179                 if (system(IFCONFIG " vEth0 up") == -1)
180                         ret = -1;
181                 if (system(IFCONFIG " vEth0 mtu 1400") == -1)
182                         ret = -1;
183                 if (system(IFCONFIG " vEth0 down") == -1)
184                         ret = -1;
185                 rte_delay_ms(KNI_TIMEOUT_MS);
186                 test_kni_processing_flag = 1;
187         } else if (lcore_id == lcore_ingress) {
188                 struct rte_mempool *mp = test_kni_lookup_mempool();
189
190                 if (mp == NULL)
191                         return -1;
192
193                 while (1) {
194                         if (test_kni_processing_flag)
195                                 break;
196
197                         for (nb_rx = 0; nb_rx < PKT_BURST_SZ; nb_rx++) {
198                                 pkts_burst[nb_rx] = rte_pktmbuf_alloc(mp);
199                                 if (!pkts_burst[nb_rx])
200                                         break;
201                         }
202
203                         num = rte_kni_tx_burst(test_kni_ctx, pkts_burst,
204                                                                 nb_rx);
205                         stats.ingress += num;
206                         rte_kni_handle_request(test_kni_ctx);
207                         if (num < nb_rx) {
208                                 for (i = num; i < nb_rx; i++) {
209                                         rte_pktmbuf_free(pkts_burst[i]);
210                                 }
211                         }
212                 }
213         } else if (lcore_id == lcore_egress) {
214                 while (1) {
215                         if (test_kni_processing_flag)
216                                 break;
217                         num = rte_kni_rx_burst(test_kni_ctx, pkts_burst,
218                                                         PKT_BURST_SZ);
219                         stats.egress += num;
220                         for (nb_tx = 0; nb_tx < num; nb_tx++)
221                                 rte_pktmbuf_free(pkts_burst[nb_tx]);
222                 }
223         }
224
225         return ret;
226 }
227
228 static int
229 test_kni_allocate_lcores(void)
230 {
231         unsigned i, count = 0;
232
233         lcore_master = rte_get_master_lcore();
234         printf("master lcore: %u\n", lcore_master);
235         for (i = 0; i < RTE_MAX_LCORE; i++) {
236                 if (count >=2 )
237                         break;
238                 if (rte_lcore_is_enabled(i) && i != lcore_master) {
239                         count ++;
240                         if (count == 1)
241                                 lcore_ingress = i;
242                         else if (count == 2)
243                                 lcore_egress = i;
244                 }
245         }
246         printf("count: %u\n", count);
247
248         return (count == 2 ? 0 : -1);
249 }
250
251 static int
252 test_kni_processing(uint8_t pid, struct rte_mempool *mp)
253 {
254         int ret = 0;
255         unsigned i;
256         struct rte_kni *kni;
257         int p_id,p_ret;
258         int status;
259
260         if (!mp)
261                 return -1;
262
263         /* basic test of kni processing */
264         kni = rte_kni_create(pid, MAX_PACKET_SZ, mp, &kni_ops);
265         if (!kni) {
266                 printf("fail to create kni\n");
267                 return -1;
268         }
269         if (rte_kni_get_port_id(kni) != pid) {
270                 printf("fail to get port id\n");
271                 ret = -1;
272                 goto fail_kni;
273         }
274
275         test_kni_ctx = kni;
276         test_kni_processing_flag = 0;
277         stats.ingress = 0;
278         stats.egress = 0;
279
280         /* create a subprocess to test the APIs of supporting multi-process */
281         p_id = fork();
282         if (p_id == 0) {
283                 struct rte_kni *kni_test;
284         #define TEST_MTU_SIZE  1450
285                 kni_test = rte_kni_info_get(RTE_MAX_ETHPORTS);
286                 if (kni_test) {
287                         printf("unexpectedly gets kni successfully with an invalid "
288                                                                         "port id\n");
289                         exit(-1);
290                 }
291                 kni_test = rte_kni_info_get(pid);
292                 if (NULL == kni_test) {
293                         printf("Failed to get KNI info of the port %d\n",pid);
294                         exit(-1);
295                 }
296                 struct rte_kni_ops kni_ops_test = {
297                         .change_mtu = kni_change_mtu,
298                         .config_network_if = NULL,
299                 };
300                 /* test of registering kni with NULL ops */     
301                 if (rte_kni_register_handlers(kni_test,NULL) == 0) {
302                         printf("unexpectedly register kni successfully"
303                                                 "with NULL ops\n");
304                         exit(-1);
305                 }
306                 if (rte_kni_register_handlers(kni_test,&kni_ops_test) < 0) {
307                         printf("Failed to register KNI request handler"
308                                                 "of the port %d\n",pid);
309                         exit(-1);
310                 }
311                 if (system(IFCONFIG " vEth0 mtu 1450") == -1) 
312                         exit(-1);
313                 
314                 rte_kni_handle_request(kni_test);
315                 if (kni_pkt_mtu != TEST_MTU_SIZE) {
316                         printf("Failed to change kni MTU\n");
317                         exit(-1) ;
318                 }
319
320                 /* test of unregistering kni request */
321                 kni_pkt_mtu = 0;
322                 if (rte_kni_unregister_handlers(kni_test) < 0) {
323                         printf("Failed to unregister kni request handlers\n");
324                         exit(-1);
325                 }
326                 if (system(IFCONFIG " vEth0 mtu 1450") == -1)
327                         exit(-1);
328
329                 rte_kni_handle_request(kni_test);
330                 if (kni_pkt_mtu != 0) {
331                         printf("Failed to test kni unregister handlers\n");
332                         exit(-1);               
333                 }
334                 exit(0);
335         }else if (p_id < 0) {
336                 printf("Failed to fork a process\n");
337                 return -1;
338         }else {
339                 p_ret = wait(&status);
340                 if (WIFEXITED(status)) 
341                         printf("test of multi-process api passed.\n");
342                 else {
343                         printf("KNI test:The child process %d exit abnormally./n",p_ret);
344                         return -1;
345                 }
346         }
347         rte_eal_mp_remote_launch(test_kni_loop, NULL, CALL_MASTER);
348         RTE_LCORE_FOREACH_SLAVE(i) {
349                 if (rte_eal_wait_lcore(i) < 0) {
350                         ret = -1;
351                         goto fail_kni;
352                 }
353         }
354         /**
355          * Check if the number of mbufs received from kernel space is equal
356          * to that of transmitted to kernel space
357          */
358         if (stats.ingress < KNI_NUM_MBUF_THRESHOLD ||
359                 stats.egress < KNI_NUM_MBUF_THRESHOLD) {
360                 printf("The ingress/egress number should not be "
361                         "less than %u\n", (unsigned)KNI_NUM_MBUF_THRESHOLD);
362                 ret = -1;
363                 goto fail_kni;
364         }
365
366         /* test of creating kni on a port which has been used for a kni */
367         if (rte_kni_create(pid, MAX_PACKET_SZ, mp, &kni_ops) != NULL) {
368                 printf("should not create a kni successfully for a port which"
369                                                 "has been used for a kni\n");
370                 ret = -1;
371                 goto fail_kni;
372         }
373
374         if (rte_kni_release(kni) < 0) {
375                 printf("fail to release kni\n");
376                 return -1;
377         }
378         test_kni_ctx = NULL;
379         
380         /* test of releasing a released kni device */
381         if (rte_kni_release(kni) == 0) {
382                 printf("should not release a released kni device\n");
383                 return -1;
384         }
385
386         /* test of reusing memzone */
387         kni = rte_kni_create(pid, MAX_PACKET_SZ, mp, &kni_ops);
388         if (!kni) {
389                 printf("fail to create kni\n");
390                 return -1;
391         }
392
393         /* Release the kni for following testing */
394         if (rte_kni_release(kni) < 0) {
395                 printf("fail to release kni\n");
396                 return -1;
397         }
398         
399         return ret;
400 fail_kni:
401         if (rte_kni_release(kni) < 0) {
402                 printf("fail to release kni\n");
403                 ret = -1;
404         }
405
406         return ret;
407 }
408
409 int
410 test_kni(void)
411 {
412         int ret = -1;
413         uint8_t nb_ports, pid;
414         struct rte_kni *kni;
415         struct rte_mempool * mp;
416
417         if (test_kni_allocate_lcores() < 0) {
418                 printf("No enough lcores for kni processing\n");
419                 return -1;
420         }
421
422         mp = test_kni_create_mempool();
423         if (!mp) {
424                 printf("fail to create mempool for kni\n");
425                 return -1;
426         }
427         ret = rte_pmd_init_all();
428         if (ret < 0) {
429                 printf("fail to initialize PMD\n");
430                 return -1;
431         }
432         ret = rte_eal_pci_probe();
433         if (ret < 0) {
434                 printf("fail to probe PCI devices\n");
435                 return -1;
436         }
437
438         nb_ports = rte_eth_dev_count();
439         if (nb_ports == 0) {
440                 printf("no supported nic port found\n");
441                 return -1;
442         }
443
444         /* configuring port 0 for the test is enough */
445         pid = 0;
446         ret = rte_eth_dev_configure(pid, 1, 1, &port_conf);
447         if (ret < 0) {
448                 printf("fail to configure port %d\n", pid);
449                 return -1;
450         }
451
452         ret = rte_eth_rx_queue_setup(pid, 0, NB_RXD, SOCKET, &rx_conf, mp);
453         if (ret < 0) {
454                 printf("fail to setup rx queue for port %d\n", pid);
455                 return -1;
456         }
457
458         ret = rte_eth_tx_queue_setup(pid, 0, NB_TXD, SOCKET, &tx_conf);
459         if (ret < 0) {
460                 printf("fail to setup tx queue for port %d\n", pid);
461                 return -1;
462         }
463
464         ret = rte_eth_dev_start(pid);
465         if (ret < 0) {
466                 printf("fail to start port %d\n", pid);
467                 return -1;
468         }
469
470         rte_eth_promiscuous_enable(pid);
471
472         /* basic test of kni processing */
473         ret = test_kni_processing(pid, mp);
474         if (ret < 0)
475                 goto fail;
476
477         /* test of creating kni with port id exceeds the maximum */
478         kni = rte_kni_create(RTE_MAX_ETHPORTS, MAX_PACKET_SZ, mp, &kni_ops);
479         if (kni) {
480                 printf("unexpectedly creates kni successfully with an invalid "
481                                                                 "port id\n");
482                 goto fail;
483         }
484
485         /* test of creating kni with NULL mempool pointer */
486         kni = rte_kni_create(pid, MAX_PACKET_SZ, NULL, &kni_ops);
487         if (kni) {
488                 printf("unexpectedly creates kni successfully with NULL "
489                                                         "mempool pointer\n");
490                 goto fail;
491         }
492
493         /* test of creating kni with NULL ops */
494         kni = rte_kni_create(pid, MAX_PACKET_SZ, mp, NULL);
495         if (!kni) { 
496                 printf("unexpectedly creates kni falied with NULL ops\n");
497                 goto fail;
498         }
499
500         /* test of releasing kni with NULL ops */
501         if (rte_kni_release(kni) < 0) {
502                 printf("fail to release kni\n");
503                 goto fail;
504         }       
505
506         /* test of getting port id according to NULL kni context */
507         if (rte_kni_get_port_id(NULL) < RTE_MAX_ETHPORTS) {
508                 printf("unexpectedly get port id successfully by NULL kni "
509                                                                 "pointer\n");
510                 goto fail;
511         }
512
513         /* test of releasing NULL kni context */
514         ret = rte_kni_release(NULL);
515         if (ret == 0) {
516                 printf("unexpectedly release kni successfully\n");
517                 goto fail;
518         }
519
520         ret = 0;
521
522 fail:
523         rte_eth_dev_stop(pid);
524
525         return ret;
526 }
527
528 #else /* RTE_LIBRTE_KNI */
529
530 int
531 test_kni(void)
532 {
533         printf("The KNI library is not included in this build\n");
534         return 0;
535 }
536
537 #endif /* RTE_LIBRTE_KNI */