1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2017 Intel Corporation 3 */ 4 5 #include "test.h" 6 7 #include <unistd.h> 8 #include <string.h> 9 #include <rte_cycles.h> 10 #include <rte_errno.h> 11 #include <rte_mempool.h> 12 #include <rte_mbuf.h> 13 #include <rte_distributor.h> 14 #include <rte_string_fns.h> 15 16 #define ITER_POWER 20 /* log 2 of how many iterations we do when timing. */ 17 #define BURST 32 18 #define BIG_BATCH 1024 19 20 struct worker_params { 21 char name[64]; 22 struct rte_distributor *dist; 23 }; 24 25 struct worker_params worker_params; 26 27 /* statics - all zero-initialized by default */ 28 static volatile int quit; /**< general quit variable for all threads */ 29 static volatile int zero_quit; /**< var for when we just want thr0 to quit*/ 30 static volatile unsigned worker_idx; 31 32 struct worker_stats { 33 volatile unsigned handled_packets; 34 } __rte_cache_aligned; 35 struct worker_stats worker_stats[RTE_MAX_LCORE]; 36 37 /* returns the total count of the number of packets handled by the worker 38 * functions given below. 39 */ 40 static inline unsigned 41 total_packet_count(void) 42 { 43 unsigned i, count = 0; 44 for (i = 0; i < worker_idx; i++) 45 count += worker_stats[i].handled_packets; 46 return count; 47 } 48 49 /* resets the packet counts for a new test */ 50 static inline void 51 clear_packet_count(void) 52 { 53 memset(&worker_stats, 0, sizeof(worker_stats)); 54 } 55 56 /* this is the basic worker function for sanity test 57 * it does nothing but return packets and count them. 58 */ 59 static int 60 handle_work(void *arg) 61 { 62 struct rte_mbuf *buf[8] __rte_cache_aligned; 63 struct worker_params *wp = arg; 64 struct rte_distributor *db = wp->dist; 65 unsigned int count = 0, num = 0; 66 unsigned int id = __sync_fetch_and_add(&worker_idx, 1); 67 int i; 68 69 for (i = 0; i < 8; i++) 70 buf[i] = NULL; 71 num = rte_distributor_get_pkt(db, id, buf, buf, num); 72 while (!quit) { 73 worker_stats[id].handled_packets += num; 74 count += num; 75 num = rte_distributor_get_pkt(db, id, 76 buf, buf, num); 77 } 78 worker_stats[id].handled_packets += num; 79 count += num; 80 rte_distributor_return_pkt(db, id, buf, num); 81 return 0; 82 } 83 84 /* do basic sanity testing of the distributor. This test tests the following: 85 * - send 32 packets through distributor with the same tag and ensure they 86 * all go to the one worker 87 * - send 32 packets through the distributor with two different tags and 88 * verify that they go equally to two different workers. 89 * - send 32 packets with different tags through the distributors and 90 * just verify we get all packets back. 91 * - send 1024 packets through the distributor, gathering the returned packets 92 * as we go. Then verify that we correctly got all 1024 pointers back again, 93 * not necessarily in the same order (as different flows). 94 */ 95 static int 96 sanity_test(struct worker_params *wp, struct rte_mempool *p) 97 { 98 struct rte_distributor *db = wp->dist; 99 struct rte_mbuf *bufs[BURST]; 100 struct rte_mbuf *returns[BURST*2]; 101 unsigned int i, count; 102 unsigned int retries; 103 104 printf("=== Basic distributor sanity tests ===\n"); 105 clear_packet_count(); 106 if (rte_mempool_get_bulk(p, (void *)bufs, BURST) != 0) { 107 printf("line %d: Error getting mbufs from pool\n", __LINE__); 108 return -1; 109 } 110 111 /* now set all hash values in all buffers to zero, so all pkts go to the 112 * one worker thread */ 113 for (i = 0; i < BURST; i++) 114 bufs[i]->hash.usr = 0; 115 116 rte_distributor_process(db, bufs, BURST); 117 count = 0; 118 do { 119 120 rte_distributor_flush(db); 121 count += rte_distributor_returned_pkts(db, 122 returns, BURST*2); 123 } while (count < BURST); 124 125 if (total_packet_count() != BURST) { 126 printf("Line %d: Error, not all packets flushed. " 127 "Expected %u, got %u\n", 128 __LINE__, BURST, total_packet_count()); 129 return -1; 130 } 131 132 for (i = 0; i < rte_lcore_count() - 1; i++) 133 printf("Worker %u handled %u packets\n", i, 134 worker_stats[i].handled_packets); 135 printf("Sanity test with all zero hashes done.\n"); 136 137 /* pick two flows and check they go correctly */ 138 if (rte_lcore_count() >= 3) { 139 clear_packet_count(); 140 for (i = 0; i < BURST; i++) 141 bufs[i]->hash.usr = (i & 1) << 8; 142 143 rte_distributor_process(db, bufs, BURST); 144 count = 0; 145 do { 146 rte_distributor_flush(db); 147 count += rte_distributor_returned_pkts(db, 148 returns, BURST*2); 149 } while (count < BURST); 150 if (total_packet_count() != BURST) { 151 printf("Line %d: Error, not all packets flushed. " 152 "Expected %u, got %u\n", 153 __LINE__, BURST, total_packet_count()); 154 return -1; 155 } 156 157 for (i = 0; i < rte_lcore_count() - 1; i++) 158 printf("Worker %u handled %u packets\n", i, 159 worker_stats[i].handled_packets); 160 printf("Sanity test with two hash values done\n"); 161 } 162 163 /* give a different hash value to each packet, 164 * so load gets distributed */ 165 clear_packet_count(); 166 for (i = 0; i < BURST; i++) 167 bufs[i]->hash.usr = i+1; 168 169 rte_distributor_process(db, bufs, BURST); 170 count = 0; 171 do { 172 rte_distributor_flush(db); 173 count += rte_distributor_returned_pkts(db, 174 returns, BURST*2); 175 } while (count < BURST); 176 if (total_packet_count() != BURST) { 177 printf("Line %d: Error, not all packets flushed. " 178 "Expected %u, got %u\n", 179 __LINE__, BURST, total_packet_count()); 180 return -1; 181 } 182 183 for (i = 0; i < rte_lcore_count() - 1; i++) 184 printf("Worker %u handled %u packets\n", i, 185 worker_stats[i].handled_packets); 186 printf("Sanity test with non-zero hashes done\n"); 187 188 rte_mempool_put_bulk(p, (void *)bufs, BURST); 189 190 /* sanity test with BIG_BATCH packets to ensure they all arrived back 191 * from the returned packets function */ 192 clear_packet_count(); 193 struct rte_mbuf *many_bufs[BIG_BATCH], *return_bufs[BIG_BATCH]; 194 unsigned num_returned = 0; 195 196 /* flush out any remaining packets */ 197 rte_distributor_flush(db); 198 rte_distributor_clear_returns(db); 199 200 if (rte_mempool_get_bulk(p, (void *)many_bufs, BIG_BATCH) != 0) { 201 printf("line %d: Error getting mbufs from pool\n", __LINE__); 202 return -1; 203 } 204 for (i = 0; i < BIG_BATCH; i++) 205 many_bufs[i]->hash.usr = i << 2; 206 207 printf("=== testing big burst (%s) ===\n", wp->name); 208 for (i = 0; i < BIG_BATCH/BURST; i++) { 209 rte_distributor_process(db, 210 &many_bufs[i*BURST], BURST); 211 count = rte_distributor_returned_pkts(db, 212 &return_bufs[num_returned], 213 BIG_BATCH - num_returned); 214 num_returned += count; 215 } 216 rte_distributor_flush(db); 217 count = rte_distributor_returned_pkts(db, 218 &return_bufs[num_returned], 219 BIG_BATCH - num_returned); 220 num_returned += count; 221 retries = 0; 222 do { 223 rte_distributor_flush(db); 224 count = rte_distributor_returned_pkts(db, 225 &return_bufs[num_returned], 226 BIG_BATCH - num_returned); 227 num_returned += count; 228 retries++; 229 } while ((num_returned < BIG_BATCH) && (retries < 100)); 230 231 if (num_returned != BIG_BATCH) { 232 printf("line %d: Missing packets, expected %d\n", 233 __LINE__, num_returned); 234 return -1; 235 } 236 237 /* big check - make sure all packets made it back!! */ 238 for (i = 0; i < BIG_BATCH; i++) { 239 unsigned j; 240 struct rte_mbuf *src = many_bufs[i]; 241 for (j = 0; j < BIG_BATCH; j++) { 242 if (return_bufs[j] == src) 243 break; 244 } 245 246 if (j == BIG_BATCH) { 247 printf("Error: could not find source packet #%u\n", i); 248 return -1; 249 } 250 } 251 printf("Sanity test of returned packets done\n"); 252 253 rte_mempool_put_bulk(p, (void *)many_bufs, BIG_BATCH); 254 255 printf("\n"); 256 return 0; 257 } 258 259 260 /* to test that the distributor does not lose packets, we use this worker 261 * function which frees mbufs when it gets them. The distributor thread does 262 * the mbuf allocation. If distributor drops packets we'll eventually run out 263 * of mbufs. 264 */ 265 static int 266 handle_work_with_free_mbufs(void *arg) 267 { 268 struct rte_mbuf *buf[8] __rte_cache_aligned; 269 struct worker_params *wp = arg; 270 struct rte_distributor *d = wp->dist; 271 unsigned int count = 0; 272 unsigned int i; 273 unsigned int num = 0; 274 unsigned int id = __sync_fetch_and_add(&worker_idx, 1); 275 276 for (i = 0; i < 8; i++) 277 buf[i] = NULL; 278 num = rte_distributor_get_pkt(d, id, buf, buf, num); 279 while (!quit) { 280 worker_stats[id].handled_packets += num; 281 count += num; 282 for (i = 0; i < num; i++) 283 rte_pktmbuf_free(buf[i]); 284 num = rte_distributor_get_pkt(d, 285 id, buf, buf, num); 286 } 287 worker_stats[id].handled_packets += num; 288 count += num; 289 rte_distributor_return_pkt(d, id, buf, num); 290 return 0; 291 } 292 293 /* Perform a sanity test of the distributor with a large number of packets, 294 * where we allocate a new set of mbufs for each burst. The workers then 295 * free the mbufs. This ensures that we don't have any packet leaks in the 296 * library. 297 */ 298 static int 299 sanity_test_with_mbuf_alloc(struct worker_params *wp, struct rte_mempool *p) 300 { 301 struct rte_distributor *d = wp->dist; 302 unsigned i; 303 struct rte_mbuf *bufs[BURST]; 304 305 printf("=== Sanity test with mbuf alloc/free (%s) ===\n", wp->name); 306 307 clear_packet_count(); 308 for (i = 0; i < ((1<<ITER_POWER)); i += BURST) { 309 unsigned j; 310 while (rte_mempool_get_bulk(p, (void *)bufs, BURST) < 0) 311 rte_distributor_process(d, NULL, 0); 312 for (j = 0; j < BURST; j++) { 313 bufs[j]->hash.usr = (i+j) << 1; 314 rte_mbuf_refcnt_set(bufs[j], 1); 315 } 316 317 rte_distributor_process(d, bufs, BURST); 318 } 319 320 rte_distributor_flush(d); 321 322 rte_delay_us(10000); 323 324 if (total_packet_count() < (1<<ITER_POWER)) { 325 printf("Line %u: Packet count is incorrect, %u, expected %u\n", 326 __LINE__, total_packet_count(), 327 (1<<ITER_POWER)); 328 return -1; 329 } 330 331 printf("Sanity test with mbuf alloc/free passed\n\n"); 332 return 0; 333 } 334 335 static int 336 handle_work_for_shutdown_test(void *arg) 337 { 338 struct rte_mbuf *pkt = NULL; 339 struct rte_mbuf *buf[8] __rte_cache_aligned; 340 struct worker_params *wp = arg; 341 struct rte_distributor *d = wp->dist; 342 unsigned int count = 0; 343 unsigned int num = 0; 344 unsigned int total = 0; 345 unsigned int i; 346 unsigned int returned = 0; 347 const unsigned int id = __sync_fetch_and_add(&worker_idx, 1); 348 349 num = rte_distributor_get_pkt(d, id, buf, buf, num); 350 351 /* wait for quit single globally, or for worker zero, wait 352 * for zero_quit */ 353 while (!quit && !(id == 0 && zero_quit)) { 354 worker_stats[id].handled_packets += num; 355 count += num; 356 for (i = 0; i < num; i++) 357 rte_pktmbuf_free(buf[i]); 358 num = rte_distributor_get_pkt(d, 359 id, buf, buf, num); 360 total += num; 361 } 362 worker_stats[id].handled_packets += num; 363 count += num; 364 returned = rte_distributor_return_pkt(d, id, buf, num); 365 366 if (id == 0) { 367 /* for worker zero, allow it to restart to pick up last packet 368 * when all workers are shutting down. 369 */ 370 while (zero_quit) 371 usleep(100); 372 373 num = rte_distributor_get_pkt(d, 374 id, buf, buf, num); 375 376 while (!quit) { 377 worker_stats[id].handled_packets++, count++; 378 rte_pktmbuf_free(pkt); 379 num = rte_distributor_get_pkt(d, id, buf, buf, num); 380 } 381 returned = rte_distributor_return_pkt(d, 382 id, buf, num); 383 printf("Num returned = %d\n", returned); 384 } 385 return 0; 386 } 387 388 389 /* Perform a sanity test of the distributor with a large number of packets, 390 * where we allocate a new set of mbufs for each burst. The workers then 391 * free the mbufs. This ensures that we don't have any packet leaks in the 392 * library. 393 */ 394 static int 395 sanity_test_with_worker_shutdown(struct worker_params *wp, 396 struct rte_mempool *p) 397 { 398 struct rte_distributor *d = wp->dist; 399 struct rte_mbuf *bufs[BURST]; 400 unsigned i; 401 402 printf("=== Sanity test of worker shutdown ===\n"); 403 404 clear_packet_count(); 405 406 if (rte_mempool_get_bulk(p, (void *)bufs, BURST) != 0) { 407 printf("line %d: Error getting mbufs from pool\n", __LINE__); 408 return -1; 409 } 410 411 /* 412 * Now set all hash values in all buffers to same value so all 413 * pkts go to the one worker thread 414 */ 415 for (i = 0; i < BURST; i++) 416 bufs[i]->hash.usr = 1; 417 418 rte_distributor_process(d, bufs, BURST); 419 rte_distributor_flush(d); 420 421 /* at this point, we will have processed some packets and have a full 422 * backlog for the other ones at worker 0. 423 */ 424 425 /* get more buffers to queue up, again setting them to the same flow */ 426 if (rte_mempool_get_bulk(p, (void *)bufs, BURST) != 0) { 427 printf("line %d: Error getting mbufs from pool\n", __LINE__); 428 return -1; 429 } 430 for (i = 0; i < BURST; i++) 431 bufs[i]->hash.usr = 1; 432 433 /* get worker zero to quit */ 434 zero_quit = 1; 435 rte_distributor_process(d, bufs, BURST); 436 437 /* flush the distributor */ 438 rte_distributor_flush(d); 439 rte_delay_us(10000); 440 441 for (i = 0; i < rte_lcore_count() - 1; i++) 442 printf("Worker %u handled %u packets\n", i, 443 worker_stats[i].handled_packets); 444 445 if (total_packet_count() != BURST * 2) { 446 printf("Line %d: Error, not all packets flushed. " 447 "Expected %u, got %u\n", 448 __LINE__, BURST * 2, total_packet_count()); 449 return -1; 450 } 451 452 printf("Sanity test with worker shutdown passed\n\n"); 453 return 0; 454 } 455 456 /* Test that the flush function is able to move packets between workers when 457 * one worker shuts down.. 458 */ 459 static int 460 test_flush_with_worker_shutdown(struct worker_params *wp, 461 struct rte_mempool *p) 462 { 463 struct rte_distributor *d = wp->dist; 464 struct rte_mbuf *bufs[BURST]; 465 unsigned i; 466 467 printf("=== Test flush fn with worker shutdown (%s) ===\n", wp->name); 468 469 clear_packet_count(); 470 if (rte_mempool_get_bulk(p, (void *)bufs, BURST) != 0) { 471 printf("line %d: Error getting mbufs from pool\n", __LINE__); 472 return -1; 473 } 474 475 /* now set all hash values in all buffers to zero, so all pkts go to the 476 * one worker thread */ 477 for (i = 0; i < BURST; i++) 478 bufs[i]->hash.usr = 0; 479 480 rte_distributor_process(d, bufs, BURST); 481 /* at this point, we will have processed some packets and have a full 482 * backlog for the other ones at worker 0. 483 */ 484 485 /* get worker zero to quit */ 486 zero_quit = 1; 487 488 /* flush the distributor */ 489 rte_distributor_flush(d); 490 491 rte_delay_us(10000); 492 493 zero_quit = 0; 494 for (i = 0; i < rte_lcore_count() - 1; i++) 495 printf("Worker %u handled %u packets\n", i, 496 worker_stats[i].handled_packets); 497 498 if (total_packet_count() != BURST) { 499 printf("Line %d: Error, not all packets flushed. " 500 "Expected %u, got %u\n", 501 __LINE__, BURST, total_packet_count()); 502 return -1; 503 } 504 505 printf("Flush test with worker shutdown passed\n\n"); 506 return 0; 507 } 508 509 static 510 int test_error_distributor_create_name(void) 511 { 512 struct rte_distributor *d = NULL; 513 struct rte_distributor *db = NULL; 514 char *name = NULL; 515 516 d = rte_distributor_create(name, rte_socket_id(), 517 rte_lcore_count() - 1, 518 RTE_DIST_ALG_SINGLE); 519 if (d != NULL || rte_errno != EINVAL) { 520 printf("ERROR: No error on create() with NULL name param\n"); 521 return -1; 522 } 523 524 db = rte_distributor_create(name, rte_socket_id(), 525 rte_lcore_count() - 1, 526 RTE_DIST_ALG_BURST); 527 if (db != NULL || rte_errno != EINVAL) { 528 printf("ERROR: No error on create() with NULL param\n"); 529 return -1; 530 } 531 532 return 0; 533 } 534 535 536 static 537 int test_error_distributor_create_numworkers(void) 538 { 539 struct rte_distributor *ds = NULL; 540 struct rte_distributor *db = NULL; 541 542 ds = rte_distributor_create("test_numworkers", rte_socket_id(), 543 RTE_MAX_LCORE + 10, 544 RTE_DIST_ALG_SINGLE); 545 if (ds != NULL || rte_errno != EINVAL) { 546 printf("ERROR: No error on create() with num_workers > MAX\n"); 547 return -1; 548 } 549 550 db = rte_distributor_create("test_numworkers", rte_socket_id(), 551 RTE_MAX_LCORE + 10, 552 RTE_DIST_ALG_BURST); 553 if (db != NULL || rte_errno != EINVAL) { 554 printf("ERROR: No error on create() num_workers > MAX\n"); 555 return -1; 556 } 557 558 return 0; 559 } 560 561 562 /* Useful function which ensures that all worker functions terminate */ 563 static void 564 quit_workers(struct worker_params *wp, struct rte_mempool *p) 565 { 566 struct rte_distributor *d = wp->dist; 567 const unsigned num_workers = rte_lcore_count() - 1; 568 unsigned i; 569 struct rte_mbuf *bufs[RTE_MAX_LCORE]; 570 rte_mempool_get_bulk(p, (void *)bufs, num_workers); 571 572 zero_quit = 0; 573 quit = 1; 574 for (i = 0; i < num_workers; i++) 575 bufs[i]->hash.usr = i << 1; 576 rte_distributor_process(d, bufs, num_workers); 577 578 rte_mempool_put_bulk(p, (void *)bufs, num_workers); 579 580 rte_distributor_process(d, NULL, 0); 581 rte_distributor_flush(d); 582 rte_eal_mp_wait_lcore(); 583 quit = 0; 584 worker_idx = 0; 585 } 586 587 static int 588 test_distributor(void) 589 { 590 static struct rte_distributor *ds; 591 static struct rte_distributor *db; 592 static struct rte_distributor *dist[2]; 593 static struct rte_mempool *p; 594 int i; 595 596 if (rte_lcore_count() < 2) { 597 printf("ERROR: not enough cores to test distributor\n"); 598 return -1; 599 } 600 601 if (db == NULL) { 602 db = rte_distributor_create("Test_dist_burst", rte_socket_id(), 603 rte_lcore_count() - 1, 604 RTE_DIST_ALG_BURST); 605 if (db == NULL) { 606 printf("Error creating burst distributor\n"); 607 return -1; 608 } 609 } else { 610 rte_distributor_flush(db); 611 rte_distributor_clear_returns(db); 612 } 613 614 if (ds == NULL) { 615 ds = rte_distributor_create("Test_dist_single", 616 rte_socket_id(), 617 rte_lcore_count() - 1, 618 RTE_DIST_ALG_SINGLE); 619 if (ds == NULL) { 620 printf("Error creating single distributor\n"); 621 return -1; 622 } 623 } else { 624 rte_distributor_flush(ds); 625 rte_distributor_clear_returns(ds); 626 } 627 628 const unsigned nb_bufs = (511 * rte_lcore_count()) < BIG_BATCH ? 629 (BIG_BATCH * 2) - 1 : (511 * rte_lcore_count()); 630 if (p == NULL) { 631 p = rte_pktmbuf_pool_create("DT_MBUF_POOL", nb_bufs, BURST, 632 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); 633 if (p == NULL) { 634 printf("Error creating mempool\n"); 635 return -1; 636 } 637 } 638 639 dist[0] = ds; 640 dist[1] = db; 641 642 for (i = 0; i < 2; i++) { 643 644 worker_params.dist = dist[i]; 645 if (i) 646 strlcpy(worker_params.name, "burst", 647 sizeof(worker_params.name)); 648 else 649 strlcpy(worker_params.name, "single", 650 sizeof(worker_params.name)); 651 652 rte_eal_mp_remote_launch(handle_work, 653 &worker_params, SKIP_MASTER); 654 if (sanity_test(&worker_params, p) < 0) 655 goto err; 656 quit_workers(&worker_params, p); 657 658 rte_eal_mp_remote_launch(handle_work_with_free_mbufs, 659 &worker_params, SKIP_MASTER); 660 if (sanity_test_with_mbuf_alloc(&worker_params, p) < 0) 661 goto err; 662 quit_workers(&worker_params, p); 663 664 if (rte_lcore_count() > 2) { 665 rte_eal_mp_remote_launch(handle_work_for_shutdown_test, 666 &worker_params, 667 SKIP_MASTER); 668 if (sanity_test_with_worker_shutdown(&worker_params, 669 p) < 0) 670 goto err; 671 quit_workers(&worker_params, p); 672 673 rte_eal_mp_remote_launch(handle_work_for_shutdown_test, 674 &worker_params, 675 SKIP_MASTER); 676 if (test_flush_with_worker_shutdown(&worker_params, 677 p) < 0) 678 goto err; 679 quit_workers(&worker_params, p); 680 681 } else { 682 printf("Too few cores to run worker shutdown test\n"); 683 } 684 685 } 686 687 if (test_error_distributor_create_numworkers() == -1 || 688 test_error_distributor_create_name() == -1) { 689 printf("rte_distributor_create parameter check tests failed"); 690 return -1; 691 } 692 693 return 0; 694 695 err: 696 quit_workers(&worker_params, p); 697 return -1; 698 } 699 700 REGISTER_TEST_COMMAND(distributor_autotest, test_distributor); 701