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