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