1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include "test.h" 6 7 #include <stdlib.h> 8 #include <stdio.h> 9 #include <string.h> 10 #include <stdint.h> 11 #include <unistd.h> 12 #include <inttypes.h> 13 14 #ifdef RTE_EXEC_ENV_WINDOWS 15 static int 16 test_red(void) 17 { 18 printf("red not supported on Windows, skipping test\n"); 19 return TEST_SKIPPED; 20 } 21 22 static int 23 test_red_perf(void) 24 { 25 printf("red_perf not supported on Windows, skipping test\n"); 26 return TEST_SKIPPED; 27 } 28 29 static int 30 test_red_all(void) 31 { 32 printf("red_all not supported on Windows, skipping test\n"); 33 return TEST_SKIPPED; 34 } 35 #else 36 37 #include <sys/time.h> 38 #include <time.h> 39 #include <math.h> 40 41 #include <rte_red.h> 42 43 #ifdef __INTEL_COMPILER 44 #pragma warning(disable:2259) /* conversion may lose significant bits */ 45 #pragma warning(disable:181) /* Arg incompatible with format string */ 46 #endif 47 48 #define TEST_HZ_PER_KHZ 1000 49 #define TEST_NSEC_MARGIN 500 /**< nanosecond margin when calculating clk freq */ 50 51 #define MAX_QEMPTY_TIME_MSEC 50000 52 #define MSEC_PER_SEC 1000 /**< Milli-seconds per second */ 53 #define USEC_PER_MSEC 1000 /**< Micro-seconds per milli-second */ 54 #define USEC_PER_SEC 1000000 /**< Micro-seconds per second */ 55 #define NSEC_PER_SEC (USEC_PER_SEC * 1000) /**< Nano-seconds per second */ 56 57 /**< structures for testing rte_red performance and function */ 58 struct test_rte_red_config { /**< Test structure for RTE_RED config */ 59 struct rte_red_config *rconfig; /**< RTE_RED configuration parameters */ 60 uint8_t num_cfg; /**< Number of RTE_RED configs to test */ 61 uint8_t *wq_log2; /**< Test wq_log2 value to use */ 62 uint32_t min_th; /**< Queue minimum threshold */ 63 uint32_t max_th; /**< Queue maximum threshold */ 64 uint8_t *maxp_inv; /**< Inverse mark probability */ 65 }; 66 67 struct test_queue { /**< Test structure for RTE_RED Queues */ 68 struct rte_red *rdata; /**< RTE_RED runtime data */ 69 uint32_t num_queues; /**< Number of RTE_RED queues to test */ 70 uint32_t *qconfig; /**< Configuration of RTE_RED queues for test */ 71 uint32_t *q; /**< Queue size */ 72 uint32_t q_ramp_up; /**< Num of enqueues to ramp up the queue */ 73 uint32_t avg_ramp_up; /**< Average num of enqueues to ramp up the queue */ 74 uint32_t avg_tolerance; /**< Tolerance in queue average */ 75 double drop_tolerance; /**< Drop tolerance of packets not enqueued */ 76 }; 77 78 struct test_var { /**< Test variables used for testing RTE_RED */ 79 uint32_t wait_usec; /**< Micro second wait interval */ 80 uint32_t num_iterations; /**< Number of test iterations */ 81 uint32_t num_ops; /**< Number of test operations */ 82 uint64_t clk_freq; /**< CPU clock frequency */ 83 uint32_t sleep_sec; /**< Seconds to sleep */ 84 uint32_t *dropped; /**< Test operations dropped */ 85 uint32_t *enqueued; /**< Test operations enqueued */ 86 }; 87 88 struct test_config { /**< Test structure for RTE_RED */ 89 const char *ifname; /**< Interface name */ 90 const char *msg; /**< Test message for display */ 91 const char *htxt; /**< Header txt display for result output */ 92 struct test_rte_red_config *tconfig; /**< Test structure for RTE_RED config */ 93 struct test_queue *tqueue; /**< Test structure for RTE_RED Queues */ 94 struct test_var *tvar; /**< Test variables used for testing RTE_RED */ 95 uint32_t *tlevel; /**< Queue levels */ 96 }; 97 98 enum test_result { 99 FAIL = 0, 100 PASS 101 }; 102 103 /**< Test structure to define tests to run */ 104 struct tests { 105 struct test_config *testcfg; 106 enum test_result (*testfn)(struct test_config *); 107 }; 108 109 struct rdtsc_prof { 110 uint64_t clk_start; 111 uint64_t clk_min; /**< min clocks */ 112 uint64_t clk_max; /**< max clocks */ 113 uint64_t clk_avgc; /**< count to calc average */ 114 double clk_avg; /**< cumulative sum to calc average */ 115 const char *name; 116 }; 117 118 static const uint64_t port_speed_bytes = (10ULL*1000ULL*1000ULL*1000ULL)/8ULL; 119 static double inv_cycles_per_byte = 0; 120 static double pkt_time_usec = 0; 121 122 static void init_port_ts(uint64_t cpu_clock) 123 { 124 double cycles_per_byte = (double)(cpu_clock) / (double)(port_speed_bytes); 125 inv_cycles_per_byte = 1.0 / cycles_per_byte; 126 pkt_time_usec = 1000000.0 / ((double)port_speed_bytes / (double)RTE_RED_S); 127 } 128 129 static uint64_t get_port_ts(void) 130 { 131 return (uint64_t)((double)rte_rdtsc() * inv_cycles_per_byte); 132 } 133 134 static void rdtsc_prof_init(struct rdtsc_prof *p, const char *name) 135 { 136 p->clk_min = (uint64_t)(-1LL); 137 p->clk_max = 0; 138 p->clk_avg = 0; 139 p->clk_avgc = 0; 140 p->name = name; 141 } 142 143 static inline void rdtsc_prof_start(struct rdtsc_prof *p) 144 { 145 p->clk_start = rte_rdtsc_precise(); 146 } 147 148 static inline void rdtsc_prof_end(struct rdtsc_prof *p) 149 { 150 uint64_t clk_start = rte_rdtsc() - p->clk_start; 151 152 p->clk_avgc++; 153 p->clk_avg += (double) clk_start; 154 155 if (clk_start > p->clk_max) 156 p->clk_max = clk_start; 157 if (clk_start < p->clk_min) 158 p->clk_min = clk_start; 159 } 160 161 static void rdtsc_prof_print(struct rdtsc_prof *p) 162 { 163 if (p->clk_avgc>0) { 164 printf("RDTSC stats for %s: n=%" PRIu64 ", min=%" PRIu64 ", max=%" PRIu64 ", avg=%.1f\n", 165 p->name, 166 p->clk_avgc, 167 p->clk_min, 168 p->clk_max, 169 (p->clk_avg / ((double) p->clk_avgc))); 170 } 171 } 172 173 static uint32_t rte_red_get_avg_int(const struct rte_red_config *red_cfg, 174 struct rte_red *red) 175 { 176 /** 177 * scale by 1/n and convert from fixed-point to integer 178 */ 179 return red->avg >> (RTE_RED_SCALING + red_cfg->wq_log2); 180 } 181 182 static double rte_red_get_avg_float(const struct rte_red_config *red_cfg, 183 struct rte_red *red) 184 { 185 /** 186 * scale by 1/n and convert from fixed-point to floating-point 187 */ 188 return ldexp((double)red->avg, -(RTE_RED_SCALING + red_cfg->wq_log2)); 189 } 190 191 static void rte_red_set_avg_int(const struct rte_red_config *red_cfg, 192 struct rte_red *red, 193 uint32_t avg) 194 { 195 /** 196 * scale by n and convert from integer to fixed-point 197 */ 198 red->avg = avg << (RTE_RED_SCALING + red_cfg->wq_log2); 199 } 200 201 static double calc_exp_avg_on_empty(double avg, uint32_t n, uint32_t time_diff) 202 { 203 return avg * pow((1.0 - 1.0 / (double)n), (double)time_diff / pkt_time_usec); 204 } 205 206 static double calc_drop_rate(uint32_t enqueued, uint32_t dropped) 207 { 208 return (double)dropped / ((double)enqueued + (double)dropped); 209 } 210 211 /** 212 * calculate the drop probability 213 */ 214 static double calc_drop_prob(uint32_t min_th, uint32_t max_th, 215 uint32_t maxp_inv, uint32_t avg) 216 { 217 double drop_prob = 0.0; 218 219 if (avg < min_th) { 220 drop_prob = 0.0; 221 } else if (avg < max_th) { 222 drop_prob = (1.0 / (double)maxp_inv) 223 * ((double)(avg - min_th) 224 / (double)(max_th - min_th)); 225 } else { 226 drop_prob = 1.0; 227 } 228 return drop_prob; 229 } 230 231 /** 232 * check if drop rate matches drop probability within tolerance 233 */ 234 static int check_drop_rate(double *diff, double drop_rate, double drop_prob, double tolerance) 235 { 236 double abs_diff = 0.0; 237 int ret = 1; 238 239 abs_diff = fabs(drop_rate - drop_prob); 240 if ((int)abs_diff == 0) { 241 *diff = 0.0; 242 } else { 243 *diff = (abs_diff / drop_prob) * 100.0; 244 if (*diff > tolerance) { 245 ret = 0; 246 } 247 } 248 return ret; 249 } 250 251 /** 252 * check if average queue size is within tolerance 253 */ 254 static int check_avg(double *diff, double avg, double exp_avg, double tolerance) 255 { 256 double abs_diff = 0.0; 257 int ret = 1; 258 259 abs_diff = fabs(avg - exp_avg); 260 if ((int)abs_diff == 0) { 261 *diff = 0.0; 262 } else { 263 *diff = (abs_diff / exp_avg) * 100.0; 264 if (*diff > tolerance) { 265 ret = 0; 266 } 267 } 268 return ret; 269 } 270 271 /** 272 * initialize the test rte_red config 273 */ 274 static enum test_result 275 test_rte_red_init(struct test_config *tcfg) 276 { 277 unsigned i = 0; 278 279 tcfg->tvar->clk_freq = rte_get_timer_hz(); 280 init_port_ts( tcfg->tvar->clk_freq ); 281 282 for (i = 0; i < tcfg->tconfig->num_cfg; i++) { 283 if (rte_red_config_init(&tcfg->tconfig->rconfig[i], 284 (uint16_t)tcfg->tconfig->wq_log2[i], 285 (uint16_t)tcfg->tconfig->min_th, 286 (uint16_t)tcfg->tconfig->max_th, 287 (uint16_t)tcfg->tconfig->maxp_inv[i]) != 0) { 288 return FAIL; 289 } 290 } 291 292 *tcfg->tqueue->q = 0; 293 *tcfg->tvar->dropped = 0; 294 *tcfg->tvar->enqueued = 0; 295 return PASS; 296 } 297 298 /** 299 * enqueue until actual queue size reaches target level 300 */ 301 static int 302 increase_actual_qsize(struct rte_red_config *red_cfg, 303 struct rte_red *red, 304 uint32_t *q, 305 uint32_t level, 306 uint32_t attempts) 307 { 308 uint32_t i = 0; 309 310 for (i = 0; i < attempts; i++) { 311 int ret = 0; 312 313 /** 314 * enqueue 315 */ 316 ret = rte_red_enqueue(red_cfg, red, *q, get_port_ts() ); 317 if (ret == 0) { 318 if (++(*q) >= level) 319 break; 320 } 321 } 322 /** 323 * check if target actual queue size has been reached 324 */ 325 if (*q != level) 326 return -1; 327 /** 328 * success 329 */ 330 return 0; 331 } 332 333 /** 334 * enqueue until average queue size reaches target level 335 */ 336 static int 337 increase_average_qsize(struct rte_red_config *red_cfg, 338 struct rte_red *red, 339 uint32_t *q, 340 uint32_t level, 341 uint32_t num_ops) 342 { 343 uint32_t avg = 0; 344 uint32_t i = 0; 345 346 for (i = 0; i < num_ops; i++) { 347 /** 348 * enqueue 349 */ 350 rte_red_enqueue(red_cfg, red, *q, get_port_ts()); 351 } 352 /** 353 * check if target average queue size has been reached 354 */ 355 avg = rte_red_get_avg_int(red_cfg, red); 356 if (avg != level) 357 return -1; 358 /** 359 * success 360 */ 361 return 0; 362 } 363 364 /** 365 * setup default values for the functional test structures 366 */ 367 static struct rte_red_config ft_wrconfig[1]; 368 static struct rte_red ft_rtdata[1]; 369 static uint8_t ft_wq_log2[] = {9}; 370 static uint8_t ft_maxp_inv[] = {10}; 371 static uint32_t ft_qconfig[] = {0, 0, 1, 1}; 372 static uint32_t ft_q[] ={0}; 373 static uint32_t ft_dropped[] ={0}; 374 static uint32_t ft_enqueued[] ={0}; 375 376 static struct test_rte_red_config ft_tconfig = { 377 .rconfig = ft_wrconfig, 378 .num_cfg = RTE_DIM(ft_wrconfig), 379 .wq_log2 = ft_wq_log2, 380 .min_th = 32, 381 .max_th = 128, 382 .maxp_inv = ft_maxp_inv, 383 }; 384 385 static struct test_queue ft_tqueue = { 386 .rdata = ft_rtdata, 387 .num_queues = RTE_DIM(ft_rtdata), 388 .qconfig = ft_qconfig, 389 .q = ft_q, 390 .q_ramp_up = 1000000, 391 .avg_ramp_up = 1000000, 392 .avg_tolerance = 5, /* 5 percent */ 393 .drop_tolerance = 50, /* 50 percent */ 394 }; 395 396 static struct test_var ft_tvar = { 397 .wait_usec = 10000, 398 .num_iterations = 5, 399 .num_ops = 10000, 400 .clk_freq = 0, 401 .dropped = ft_dropped, 402 .enqueued = ft_enqueued, 403 .sleep_sec = (MAX_QEMPTY_TIME_MSEC / MSEC_PER_SEC) + 2, 404 }; 405 406 /** 407 * functional test enqueue/dequeue packets 408 */ 409 static void enqueue_dequeue_func(struct rte_red_config *red_cfg, 410 struct rte_red *red, 411 uint32_t *q, 412 uint32_t num_ops, 413 uint32_t *enqueued, 414 uint32_t *dropped) 415 { 416 uint32_t i = 0; 417 418 for (i = 0; i < num_ops; i++) { 419 int ret = 0; 420 421 /** 422 * enqueue 423 */ 424 ret = rte_red_enqueue(red_cfg, red, *q, get_port_ts()); 425 if (ret == 0) 426 (*enqueued)++; 427 else 428 (*dropped)++; 429 } 430 } 431 432 /** 433 * Test F1: functional test 1 434 */ 435 static uint32_t ft1_tlevels[] = {6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108, 114, 120, 126, 132, 138, 144}; 436 437 static struct test_config func_test1_config = { 438 .ifname = "functional test 1 interface", 439 .msg = "functional test 1 : use one rte_red configuration,\n" 440 " increase average queue size to various levels,\n" 441 " compare drop rate to drop probability\n\n", 442 .htxt = " " 443 "avg queue size " 444 "enqueued " 445 "dropped " 446 "drop prob % " 447 "drop rate % " 448 "diff % " 449 "tolerance % " 450 "\n", 451 .tconfig = &ft_tconfig, 452 .tqueue = &ft_tqueue, 453 .tvar = &ft_tvar, 454 .tlevel = ft1_tlevels, 455 }; 456 457 static enum test_result func_test1(struct test_config *tcfg) 458 { 459 enum test_result result = PASS; 460 uint32_t i = 0; 461 462 printf("%s", tcfg->msg); 463 464 if (test_rte_red_init(tcfg) != PASS) { 465 result = FAIL; 466 goto out; 467 } 468 469 printf("%s", tcfg->htxt); 470 471 for (i = 0; i < RTE_DIM(ft1_tlevels); i++) { 472 const char *label = NULL; 473 uint32_t avg = 0; 474 double drop_rate = 0.0; 475 double drop_prob = 0.0; 476 double diff = 0.0; 477 478 /** 479 * reset rte_red run-time data 480 */ 481 rte_red_rt_data_init(tcfg->tqueue->rdata); 482 *tcfg->tvar->enqueued = 0; 483 *tcfg->tvar->dropped = 0; 484 485 if (increase_actual_qsize(tcfg->tconfig->rconfig, 486 tcfg->tqueue->rdata, 487 tcfg->tqueue->q, 488 tcfg->tlevel[i], 489 tcfg->tqueue->q_ramp_up) != 0) { 490 result = FAIL; 491 goto out; 492 } 493 494 if (increase_average_qsize(tcfg->tconfig->rconfig, 495 tcfg->tqueue->rdata, 496 tcfg->tqueue->q, 497 tcfg->tlevel[i], 498 tcfg->tqueue->avg_ramp_up) != 0) { 499 result = FAIL; 500 goto out; 501 } 502 503 enqueue_dequeue_func(tcfg->tconfig->rconfig, 504 tcfg->tqueue->rdata, 505 tcfg->tqueue->q, 506 tcfg->tvar->num_ops, 507 tcfg->tvar->enqueued, 508 tcfg->tvar->dropped); 509 510 avg = rte_red_get_avg_int(tcfg->tconfig->rconfig, tcfg->tqueue->rdata); 511 if (avg != tcfg->tlevel[i]) { 512 fprintf(stderr, "Fail: avg != level\n"); 513 result = FAIL; 514 } 515 516 drop_rate = calc_drop_rate(*tcfg->tvar->enqueued, *tcfg->tvar->dropped); 517 drop_prob = calc_drop_prob(tcfg->tconfig->min_th, tcfg->tconfig->max_th, 518 *tcfg->tconfig->maxp_inv, tcfg->tlevel[i]); 519 if (!check_drop_rate(&diff, drop_rate, drop_prob, (double)tcfg->tqueue->drop_tolerance)) 520 result = FAIL; 521 522 if (tcfg->tlevel[i] == tcfg->tconfig->min_th) 523 label = "min thresh: "; 524 else if (tcfg->tlevel[i] == tcfg->tconfig->max_th) 525 label = "max thresh: "; 526 else 527 label = " "; 528 printf("%s%-15u%-15u%-15u%-15.4lf%-15.4lf%-15.4lf%-15.4lf\n", 529 label, avg, *tcfg->tvar->enqueued, *tcfg->tvar->dropped, 530 drop_prob * 100.0, drop_rate * 100.0, diff, 531 (double)tcfg->tqueue->drop_tolerance); 532 } 533 out: 534 return result; 535 } 536 537 /** 538 * Test F2: functional test 2 539 */ 540 static uint32_t ft2_tlevel[] = {127}; 541 static uint8_t ft2_wq_log2[] = {9, 9, 9, 9, 9, 9, 9, 9, 9, 9}; 542 static uint8_t ft2_maxp_inv[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; 543 static struct rte_red_config ft2_rconfig[10]; 544 545 static struct test_rte_red_config ft2_tconfig = { 546 .rconfig = ft2_rconfig, 547 .num_cfg = RTE_DIM(ft2_rconfig), 548 .wq_log2 = ft2_wq_log2, 549 .min_th = 32, 550 .max_th = 128, 551 .maxp_inv = ft2_maxp_inv, 552 }; 553 554 static struct test_config func_test2_config = { 555 .ifname = "functional test 2 interface", 556 .msg = "functional test 2 : use several RED configurations,\n" 557 " increase average queue size to just below maximum threshold,\n" 558 " compare drop rate to drop probability\n\n", 559 .htxt = "RED config " 560 "avg queue size " 561 "min threshold " 562 "max threshold " 563 "drop prob % " 564 "drop rate % " 565 "diff % " 566 "tolerance % " 567 "\n", 568 .tconfig = &ft2_tconfig, 569 .tqueue = &ft_tqueue, 570 .tvar = &ft_tvar, 571 .tlevel = ft2_tlevel, 572 }; 573 574 static enum test_result func_test2(struct test_config *tcfg) 575 { 576 enum test_result result = PASS; 577 double prev_drop_rate = 1.0; 578 uint32_t i = 0; 579 580 printf("%s", tcfg->msg); 581 582 if (test_rte_red_init(tcfg) != PASS) { 583 result = FAIL; 584 goto out; 585 } 586 rte_red_rt_data_init(tcfg->tqueue->rdata); 587 588 if (increase_actual_qsize(tcfg->tconfig->rconfig, 589 tcfg->tqueue->rdata, 590 tcfg->tqueue->q, 591 *tcfg->tlevel, 592 tcfg->tqueue->q_ramp_up) != 0) { 593 result = FAIL; 594 goto out; 595 } 596 597 if (increase_average_qsize(tcfg->tconfig->rconfig, 598 tcfg->tqueue->rdata, 599 tcfg->tqueue->q, 600 *tcfg->tlevel, 601 tcfg->tqueue->avg_ramp_up) != 0) { 602 result = FAIL; 603 goto out; 604 } 605 printf("%s", tcfg->htxt); 606 607 for (i = 0; i < tcfg->tconfig->num_cfg; i++) { 608 uint32_t avg = 0; 609 double drop_rate = 0.0; 610 double drop_prob = 0.0; 611 double diff = 0.0; 612 613 *tcfg->tvar->dropped = 0; 614 *tcfg->tvar->enqueued = 0; 615 616 enqueue_dequeue_func(&tcfg->tconfig->rconfig[i], 617 tcfg->tqueue->rdata, 618 tcfg->tqueue->q, 619 tcfg->tvar->num_ops, 620 tcfg->tvar->enqueued, 621 tcfg->tvar->dropped); 622 623 avg = rte_red_get_avg_int(&tcfg->tconfig->rconfig[i], tcfg->tqueue->rdata); 624 if (avg != *tcfg->tlevel) 625 result = FAIL; 626 627 drop_rate = calc_drop_rate(*tcfg->tvar->enqueued, *tcfg->tvar->dropped); 628 drop_prob = calc_drop_prob(tcfg->tconfig->min_th, tcfg->tconfig->max_th, 629 tcfg->tconfig->maxp_inv[i], *tcfg->tlevel); 630 if (!check_drop_rate(&diff, drop_rate, drop_prob, (double)tcfg->tqueue->drop_tolerance)) 631 result = FAIL; 632 /** 633 * drop rate should decrease as maxp_inv increases 634 */ 635 if (drop_rate > prev_drop_rate) 636 result = FAIL; 637 prev_drop_rate = drop_rate; 638 639 printf("%-15u%-15u%-15u%-15u%-15.4lf%-15.4lf%-15.4lf%-15.4lf\n", 640 i, avg, tcfg->tconfig->min_th, tcfg->tconfig->max_th, 641 drop_prob * 100.0, drop_rate * 100.0, diff, 642 (double)tcfg->tqueue->drop_tolerance); 643 } 644 out: 645 return result; 646 } 647 648 /** 649 * Test F3: functional test 3 650 */ 651 static uint32_t ft3_tlevel[] = {1022}; 652 653 static struct test_rte_red_config ft3_tconfig = { 654 .rconfig = ft_wrconfig, 655 .num_cfg = RTE_DIM(ft_wrconfig), 656 .wq_log2 = ft_wq_log2, 657 .min_th = 32, 658 .max_th = 1023, 659 .maxp_inv = ft_maxp_inv, 660 }; 661 662 static struct test_config func_test3_config = { 663 .ifname = "functional test 3 interface", 664 .msg = "functional test 3 : use one RED configuration,\n" 665 " increase average queue size to target level,\n" 666 " dequeue all packets until queue is empty,\n" 667 " confirm that average queue size is computed correctly while queue is empty\n\n", 668 .htxt = "q avg before " 669 "q avg after " 670 "expected " 671 "difference % " 672 "tolerance % " 673 "result " 674 "\n", 675 .tconfig = &ft3_tconfig, 676 .tqueue = &ft_tqueue, 677 .tvar = &ft_tvar, 678 .tlevel = ft3_tlevel, 679 }; 680 681 static enum test_result func_test3(struct test_config *tcfg) 682 { 683 enum test_result result = PASS; 684 uint32_t i = 0; 685 686 printf("%s", tcfg->msg); 687 688 if (test_rte_red_init(tcfg) != PASS) { 689 result = FAIL; 690 goto out; 691 } 692 693 rte_red_rt_data_init(tcfg->tqueue->rdata); 694 695 if (increase_actual_qsize(tcfg->tconfig->rconfig, 696 tcfg->tqueue->rdata, 697 tcfg->tqueue->q, 698 *tcfg->tlevel, 699 tcfg->tqueue->q_ramp_up) != 0) { 700 result = FAIL; 701 goto out; 702 } 703 704 if (increase_average_qsize(tcfg->tconfig->rconfig, 705 tcfg->tqueue->rdata, 706 tcfg->tqueue->q, 707 *tcfg->tlevel, 708 tcfg->tqueue->avg_ramp_up) != 0) { 709 result = FAIL; 710 goto out; 711 } 712 713 printf("%s", tcfg->htxt); 714 715 for (i = 0; i < tcfg->tvar->num_iterations; i++) { 716 double avg_before = 0; 717 double avg_after = 0; 718 double exp_avg = 0; 719 double diff = 0.0; 720 721 avg_before = rte_red_get_avg_float(tcfg->tconfig->rconfig, tcfg->tqueue->rdata); 722 723 /** 724 * empty the queue 725 */ 726 *tcfg->tqueue->q = 0; 727 rte_red_mark_queue_empty(tcfg->tqueue->rdata, get_port_ts()); 728 729 rte_delay_us(tcfg->tvar->wait_usec); 730 731 /** 732 * enqueue one packet to recalculate average queue size 733 */ 734 if (rte_red_enqueue(tcfg->tconfig->rconfig, 735 tcfg->tqueue->rdata, 736 *tcfg->tqueue->q, 737 get_port_ts()) == 0) { 738 (*tcfg->tqueue->q)++; 739 } else { 740 printf("%s:%d: packet enqueued on empty queue was dropped\n", __func__, __LINE__); 741 result = FAIL; 742 } 743 744 exp_avg = calc_exp_avg_on_empty(avg_before, 745 (1 << *tcfg->tconfig->wq_log2), 746 tcfg->tvar->wait_usec); 747 avg_after = rte_red_get_avg_float(tcfg->tconfig->rconfig, 748 tcfg->tqueue->rdata); 749 if (!check_avg(&diff, avg_after, exp_avg, (double)tcfg->tqueue->avg_tolerance)) 750 result = FAIL; 751 752 printf("%-15.4lf%-15.4lf%-15.4lf%-15.4lf%-15.4lf%-15s\n", 753 avg_before, avg_after, exp_avg, diff, 754 (double)tcfg->tqueue->avg_tolerance, 755 diff <= (double)tcfg->tqueue->avg_tolerance ? "pass" : "fail"); 756 } 757 out: 758 return result; 759 } 760 761 /** 762 * Test F4: functional test 4 763 */ 764 static uint32_t ft4_tlevel[] = {1022}; 765 static uint8_t ft4_wq_log2[] = {11}; 766 767 static struct test_rte_red_config ft4_tconfig = { 768 .rconfig = ft_wrconfig, 769 .num_cfg = RTE_DIM(ft_wrconfig), 770 .min_th = 32, 771 .max_th = 1023, 772 .wq_log2 = ft4_wq_log2, 773 .maxp_inv = ft_maxp_inv, 774 }; 775 776 static struct test_queue ft4_tqueue = { 777 .rdata = ft_rtdata, 778 .num_queues = RTE_DIM(ft_rtdata), 779 .qconfig = ft_qconfig, 780 .q = ft_q, 781 .q_ramp_up = 1000000, 782 .avg_ramp_up = 1000000, 783 .avg_tolerance = 0, /* 0 percent */ 784 .drop_tolerance = 50, /* 50 percent */ 785 }; 786 787 static struct test_config func_test4_config = { 788 .ifname = "functional test 4 interface", 789 .msg = "functional test 4 : use one RED configuration,\n" 790 " increase average queue size to target level,\n" 791 " dequeue all packets until queue is empty,\n" 792 " confirm that average queue size is computed correctly while\n" 793 " queue is empty for more than 50 sec,\n" 794 " (this test takes 52 sec to run)\n\n", 795 .htxt = "q avg before " 796 "q avg after " 797 "expected " 798 "difference % " 799 "tolerance % " 800 "result " 801 "\n", 802 .tconfig = &ft4_tconfig, 803 .tqueue = &ft4_tqueue, 804 .tvar = &ft_tvar, 805 .tlevel = ft4_tlevel, 806 }; 807 808 static enum test_result func_test4(struct test_config *tcfg) 809 { 810 enum test_result result = PASS; 811 uint64_t time_diff = 0; 812 uint64_t start = 0; 813 double avg_before = 0.0; 814 double avg_after = 0.0; 815 double exp_avg = 0.0; 816 double diff = 0.0; 817 818 printf("%s", tcfg->msg); 819 820 if (test_rte_red_init(tcfg) != PASS) { 821 result = FAIL; 822 goto out; 823 } 824 825 rte_red_rt_data_init(tcfg->tqueue->rdata); 826 827 if (increase_actual_qsize(tcfg->tconfig->rconfig, 828 tcfg->tqueue->rdata, 829 tcfg->tqueue->q, 830 *tcfg->tlevel, 831 tcfg->tqueue->q_ramp_up) != 0) { 832 result = FAIL; 833 goto out; 834 } 835 836 if (increase_average_qsize(tcfg->tconfig->rconfig, 837 tcfg->tqueue->rdata, 838 tcfg->tqueue->q, 839 *tcfg->tlevel, 840 tcfg->tqueue->avg_ramp_up) != 0) { 841 result = FAIL; 842 goto out; 843 } 844 845 printf("%s", tcfg->htxt); 846 847 avg_before = rte_red_get_avg_float(tcfg->tconfig->rconfig, tcfg->tqueue->rdata); 848 849 /** 850 * empty the queue 851 */ 852 *tcfg->tqueue->q = 0; 853 rte_red_mark_queue_empty(tcfg->tqueue->rdata, get_port_ts()); 854 855 /** 856 * record empty time locally 857 */ 858 start = rte_rdtsc(); 859 860 sleep(tcfg->tvar->sleep_sec); 861 862 /** 863 * enqueue one packet to recalculate average queue size 864 */ 865 if (rte_red_enqueue(tcfg->tconfig->rconfig, 866 tcfg->tqueue->rdata, 867 *tcfg->tqueue->q, 868 get_port_ts()) != 0) { 869 result = FAIL; 870 goto out; 871 } 872 (*tcfg->tqueue->q)++; 873 874 /** 875 * calculate how long queue has been empty 876 */ 877 time_diff = ((rte_rdtsc() - start) / tcfg->tvar->clk_freq) 878 * MSEC_PER_SEC; 879 if (time_diff < MAX_QEMPTY_TIME_MSEC) { 880 /** 881 * this could happen if sleep was interrupted for some reason 882 */ 883 result = FAIL; 884 goto out; 885 } 886 887 /** 888 * confirm that average queue size is now at expected level 889 */ 890 exp_avg = 0.0; 891 avg_after = rte_red_get_avg_float(tcfg->tconfig->rconfig, tcfg->tqueue->rdata); 892 if (!check_avg(&diff, avg_after, exp_avg, (double)tcfg->tqueue->avg_tolerance)) 893 result = FAIL; 894 895 printf("%-15.4lf%-15.4lf%-15.4lf%-15.4lf%-15.4lf%-15s\n", 896 avg_before, avg_after, exp_avg, 897 diff, (double)tcfg->tqueue->avg_tolerance, 898 diff <= (double)tcfg->tqueue->avg_tolerance ? "pass" : "fail"); 899 out: 900 return result; 901 } 902 903 /** 904 * Test F5: functional test 5 905 */ 906 static uint32_t ft5_tlevel[] = {127}; 907 static uint8_t ft5_wq_log2[] = {9, 8}; 908 static uint8_t ft5_maxp_inv[] = {10, 20}; 909 static struct rte_red_config ft5_config[2]; 910 static struct rte_red ft5_data[4]; 911 static uint32_t ft5_q[4]; 912 static uint32_t ft5_dropped[] = {0, 0, 0, 0}; 913 static uint32_t ft5_enqueued[] = {0, 0, 0, 0}; 914 915 static struct test_rte_red_config ft5_tconfig = { 916 .rconfig = ft5_config, 917 .num_cfg = RTE_DIM(ft5_config), 918 .min_th = 32, 919 .max_th = 128, 920 .wq_log2 = ft5_wq_log2, 921 .maxp_inv = ft5_maxp_inv, 922 }; 923 924 static struct test_queue ft5_tqueue = { 925 .rdata = ft5_data, 926 .num_queues = RTE_DIM(ft5_data), 927 .qconfig = ft_qconfig, 928 .q = ft5_q, 929 .q_ramp_up = 1000000, 930 .avg_ramp_up = 1000000, 931 .avg_tolerance = 5, /* 10 percent */ 932 .drop_tolerance = 50, /* 50 percent */ 933 }; 934 935 struct test_var ft5_tvar = { 936 .wait_usec = 0, 937 .num_iterations = 15, 938 .num_ops = 10000, 939 .clk_freq = 0, 940 .dropped = ft5_dropped, 941 .enqueued = ft5_enqueued, 942 .sleep_sec = 0, 943 }; 944 945 static struct test_config func_test5_config = { 946 .ifname = "functional test 5 interface", 947 .msg = "functional test 5 : use several queues (each with its own run-time data),\n" 948 " use several RED configurations (such that each configuration is shared by multiple queues),\n" 949 " increase average queue size to just below maximum threshold,\n" 950 " compare drop rate to drop probability,\n" 951 " (this is a larger scale version of functional test 2)\n\n", 952 .htxt = "queue " 953 "config " 954 "avg queue size " 955 "min threshold " 956 "max threshold " 957 "drop prob % " 958 "drop rate % " 959 "diff % " 960 "tolerance % " 961 "\n", 962 .tconfig = &ft5_tconfig, 963 .tqueue = &ft5_tqueue, 964 .tvar = &ft5_tvar, 965 .tlevel = ft5_tlevel, 966 }; 967 968 static enum test_result func_test5(struct test_config *tcfg) 969 { 970 enum test_result result = PASS; 971 uint32_t j = 0; 972 973 printf("%s", tcfg->msg); 974 975 if (test_rte_red_init(tcfg) != PASS) { 976 result = FAIL; 977 goto out; 978 } 979 980 printf("%s", tcfg->htxt); 981 982 for (j = 0; j < tcfg->tqueue->num_queues; j++) { 983 rte_red_rt_data_init(&tcfg->tqueue->rdata[j]); 984 tcfg->tqueue->q[j] = 0; 985 986 if (increase_actual_qsize(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 987 &tcfg->tqueue->rdata[j], 988 &tcfg->tqueue->q[j], 989 *tcfg->tlevel, 990 tcfg->tqueue->q_ramp_up) != 0) { 991 result = FAIL; 992 goto out; 993 } 994 995 if (increase_average_qsize(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 996 &tcfg->tqueue->rdata[j], 997 &tcfg->tqueue->q[j], 998 *tcfg->tlevel, 999 tcfg->tqueue->avg_ramp_up) != 0) { 1000 result = FAIL; 1001 goto out; 1002 } 1003 } 1004 1005 for (j = 0; j < tcfg->tqueue->num_queues; j++) { 1006 uint32_t avg = 0; 1007 double drop_rate = 0.0; 1008 double drop_prob = 0.0; 1009 double diff = 0.0; 1010 1011 tcfg->tvar->dropped[j] = 0; 1012 tcfg->tvar->enqueued[j] = 0; 1013 1014 enqueue_dequeue_func(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 1015 &tcfg->tqueue->rdata[j], 1016 &tcfg->tqueue->q[j], 1017 tcfg->tvar->num_ops, 1018 &tcfg->tvar->enqueued[j], 1019 &tcfg->tvar->dropped[j]); 1020 1021 avg = rte_red_get_avg_int(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 1022 &tcfg->tqueue->rdata[j]); 1023 if (avg != *tcfg->tlevel) 1024 result = FAIL; 1025 1026 drop_rate = calc_drop_rate(tcfg->tvar->enqueued[j],tcfg->tvar->dropped[j]); 1027 drop_prob = calc_drop_prob(tcfg->tconfig->min_th, tcfg->tconfig->max_th, 1028 tcfg->tconfig->maxp_inv[tcfg->tqueue->qconfig[j]], 1029 *tcfg->tlevel); 1030 if (!check_drop_rate(&diff, drop_rate, drop_prob, (double)tcfg->tqueue->drop_tolerance)) 1031 result = FAIL; 1032 1033 printf("%-15u%-15u%-15u%-15u%-15u%-15.4lf%-15.4lf%-15.4lf%-15.4lf\n", 1034 j, tcfg->tqueue->qconfig[j], avg, 1035 tcfg->tconfig->min_th, tcfg->tconfig->max_th, 1036 drop_prob * 100.0, drop_rate * 100.0, 1037 diff, (double)tcfg->tqueue->drop_tolerance); 1038 } 1039 out: 1040 return result; 1041 } 1042 1043 /** 1044 * Test F6: functional test 6 1045 */ 1046 static uint32_t ft6_tlevel[] = {1022}; 1047 static uint8_t ft6_wq_log2[] = {9, 8}; 1048 static uint8_t ft6_maxp_inv[] = {10, 20}; 1049 static struct rte_red_config ft6_config[2]; 1050 static struct rte_red ft6_data[4]; 1051 static uint32_t ft6_q[4]; 1052 1053 static struct test_rte_red_config ft6_tconfig = { 1054 .rconfig = ft6_config, 1055 .num_cfg = RTE_DIM(ft6_config), 1056 .min_th = 32, 1057 .max_th = 1023, 1058 .wq_log2 = ft6_wq_log2, 1059 .maxp_inv = ft6_maxp_inv, 1060 }; 1061 1062 static struct test_queue ft6_tqueue = { 1063 .rdata = ft6_data, 1064 .num_queues = RTE_DIM(ft6_data), 1065 .qconfig = ft_qconfig, 1066 .q = ft6_q, 1067 .q_ramp_up = 1000000, 1068 .avg_ramp_up = 1000000, 1069 .avg_tolerance = 5, /* 10 percent */ 1070 .drop_tolerance = 50, /* 50 percent */ 1071 }; 1072 1073 static struct test_config func_test6_config = { 1074 .ifname = "functional test 6 interface", 1075 .msg = "functional test 6 : use several queues (each with its own run-time data),\n" 1076 " use several RED configurations (such that each configuration is shared by multiple queues),\n" 1077 " increase average queue size to target level,\n" 1078 " dequeue all packets until queue is empty,\n" 1079 " confirm that average queue size is computed correctly while queue is empty\n" 1080 " (this is a larger scale version of functional test 3)\n\n", 1081 .htxt = "queue " 1082 "config " 1083 "q avg before " 1084 "q avg after " 1085 "expected " 1086 "difference % " 1087 "tolerance % " 1088 "result ""\n", 1089 .tconfig = &ft6_tconfig, 1090 .tqueue = &ft6_tqueue, 1091 .tvar = &ft_tvar, 1092 .tlevel = ft6_tlevel, 1093 }; 1094 1095 static enum test_result func_test6(struct test_config *tcfg) 1096 { 1097 enum test_result result = PASS; 1098 uint32_t j = 0; 1099 1100 printf("%s", tcfg->msg); 1101 if (test_rte_red_init(tcfg) != PASS) { 1102 result = FAIL; 1103 goto out; 1104 } 1105 printf("%s", tcfg->htxt); 1106 1107 for (j = 0; j < tcfg->tqueue->num_queues; j++) { 1108 rte_red_rt_data_init(&tcfg->tqueue->rdata[j]); 1109 tcfg->tqueue->q[j] = 0; 1110 1111 if (increase_actual_qsize(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 1112 &tcfg->tqueue->rdata[j], 1113 &tcfg->tqueue->q[j], 1114 *tcfg->tlevel, 1115 tcfg->tqueue->q_ramp_up) != 0) { 1116 result = FAIL; 1117 goto out; 1118 } 1119 if (increase_average_qsize(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 1120 &tcfg->tqueue->rdata[j], 1121 &tcfg->tqueue->q[j], 1122 *tcfg->tlevel, 1123 tcfg->tqueue->avg_ramp_up) != 0) { 1124 result = FAIL; 1125 goto out; 1126 } 1127 } 1128 for (j = 0; j < tcfg->tqueue->num_queues; j++) { 1129 double avg_before = 0; 1130 double avg_after = 0; 1131 double exp_avg = 0; 1132 double diff = 0.0; 1133 1134 avg_before = rte_red_get_avg_float(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 1135 &tcfg->tqueue->rdata[j]); 1136 1137 /** 1138 * empty the queue 1139 */ 1140 tcfg->tqueue->q[j] = 0; 1141 rte_red_mark_queue_empty(&tcfg->tqueue->rdata[j], get_port_ts()); 1142 rte_delay_us(tcfg->tvar->wait_usec); 1143 1144 /** 1145 * enqueue one packet to recalculate average queue size 1146 */ 1147 if (rte_red_enqueue(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 1148 &tcfg->tqueue->rdata[j], 1149 tcfg->tqueue->q[j], 1150 get_port_ts()) == 0) { 1151 tcfg->tqueue->q[j]++; 1152 } else { 1153 printf("%s:%d: packet enqueued on empty queue was dropped\n", __func__, __LINE__); 1154 result = FAIL; 1155 } 1156 1157 exp_avg = calc_exp_avg_on_empty(avg_before, 1158 (1 << tcfg->tconfig->wq_log2[tcfg->tqueue->qconfig[j]]), 1159 tcfg->tvar->wait_usec); 1160 avg_after = rte_red_get_avg_float(&tcfg->tconfig->rconfig[tcfg->tqueue->qconfig[j]], 1161 &tcfg->tqueue->rdata[j]); 1162 if (!check_avg(&diff, avg_after, exp_avg, (double)tcfg->tqueue->avg_tolerance)) 1163 result = FAIL; 1164 1165 printf("%-15u%-15u%-15.4lf%-15.4lf%-15.4lf%-15.4lf%-15.4lf%-15s\n", 1166 j, tcfg->tqueue->qconfig[j], avg_before, avg_after, 1167 exp_avg, diff, (double)tcfg->tqueue->avg_tolerance, 1168 diff <= tcfg->tqueue->avg_tolerance ? "pass" : "fail"); 1169 } 1170 out: 1171 return result; 1172 } 1173 1174 /** 1175 * setup default values for the performance test structures 1176 */ 1177 static struct rte_red_config pt_wrconfig[1]; 1178 static struct rte_red pt_rtdata[1]; 1179 static uint8_t pt_wq_log2[] = {9}; 1180 static uint8_t pt_maxp_inv[] = {10}; 1181 static uint32_t pt_qconfig[] = {0}; 1182 static uint32_t pt_q[] = {0}; 1183 static uint32_t pt_dropped[] = {0}; 1184 static uint32_t pt_enqueued[] = {0}; 1185 1186 static struct test_rte_red_config pt_tconfig = { 1187 .rconfig = pt_wrconfig, 1188 .num_cfg = RTE_DIM(pt_wrconfig), 1189 .wq_log2 = pt_wq_log2, 1190 .min_th = 32, 1191 .max_th = 128, 1192 .maxp_inv = pt_maxp_inv, 1193 }; 1194 1195 static struct test_queue pt_tqueue = { 1196 .rdata = pt_rtdata, 1197 .num_queues = RTE_DIM(pt_rtdata), 1198 .qconfig = pt_qconfig, 1199 .q = pt_q, 1200 .q_ramp_up = 1000000, 1201 .avg_ramp_up = 1000000, 1202 .avg_tolerance = 5, /* 10 percent */ 1203 .drop_tolerance = 50, /* 50 percent */ 1204 }; 1205 1206 /** 1207 * enqueue/dequeue packets 1208 */ 1209 static void enqueue_dequeue_perf(struct rte_red_config *red_cfg, 1210 struct rte_red *red, 1211 uint32_t *q, 1212 uint32_t num_ops, 1213 uint32_t *enqueued, 1214 uint32_t *dropped, 1215 struct rdtsc_prof *prof) 1216 { 1217 uint32_t i = 0; 1218 1219 for (i = 0; i < num_ops; i++) { 1220 uint64_t ts = 0; 1221 int ret = 0; 1222 /** 1223 * enqueue 1224 */ 1225 ts = get_port_ts(); 1226 rdtsc_prof_start(prof); 1227 ret = rte_red_enqueue(red_cfg, red, *q, ts ); 1228 rdtsc_prof_end(prof); 1229 if (ret == 0) 1230 (*enqueued)++; 1231 else 1232 (*dropped)++; 1233 } 1234 } 1235 1236 /** 1237 * Setup test structures for tests P1, P2, P3 1238 * performance tests 1, 2 and 3 1239 */ 1240 static uint32_t pt1_tlevel[] = {16}; 1241 static uint32_t pt2_tlevel[] = {80}; 1242 static uint32_t pt3_tlevel[] = {144}; 1243 1244 static struct test_var perf1_tvar = { 1245 .wait_usec = 0, 1246 .num_iterations = 15, 1247 .num_ops = 50000000, 1248 .clk_freq = 0, 1249 .dropped = pt_dropped, 1250 .enqueued = pt_enqueued, 1251 .sleep_sec = 0 1252 }; 1253 1254 static struct test_config perf1_test1_config = { 1255 .ifname = "performance test 1 interface", 1256 .msg = "performance test 1 : use one RED configuration,\n" 1257 " set actual and average queue sizes to level below min threshold,\n" 1258 " measure enqueue performance\n\n", 1259 .tconfig = &pt_tconfig, 1260 .tqueue = &pt_tqueue, 1261 .tvar = &perf1_tvar, 1262 .tlevel = pt1_tlevel, 1263 }; 1264 1265 static struct test_config perf1_test2_config = { 1266 .ifname = "performance test 2 interface", 1267 .msg = "performance test 2 : use one RED configuration,\n" 1268 " set actual and average queue sizes to level in between min and max thresholds,\n" 1269 " measure enqueue performance\n\n", 1270 .tconfig = &pt_tconfig, 1271 .tqueue = &pt_tqueue, 1272 .tvar = &perf1_tvar, 1273 .tlevel = pt2_tlevel, 1274 }; 1275 1276 static struct test_config perf1_test3_config = { 1277 .ifname = "performance test 3 interface", 1278 .msg = "performance test 3 : use one RED configuration,\n" 1279 " set actual and average queue sizes to level above max threshold,\n" 1280 " measure enqueue performance\n\n", 1281 .tconfig = &pt_tconfig, 1282 .tqueue = &pt_tqueue, 1283 .tvar = &perf1_tvar, 1284 .tlevel = pt3_tlevel, 1285 }; 1286 1287 /** 1288 * Performance test function to measure enqueue performance. 1289 * This runs performance tests 1, 2 and 3 1290 */ 1291 static enum test_result perf1_test(struct test_config *tcfg) 1292 { 1293 enum test_result result = PASS; 1294 struct rdtsc_prof prof = {0, 0, 0, 0, 0.0, NULL}; 1295 uint32_t total = 0; 1296 1297 printf("%s", tcfg->msg); 1298 1299 rdtsc_prof_init(&prof, "enqueue"); 1300 1301 if (test_rte_red_init(tcfg) != PASS) { 1302 result = FAIL; 1303 goto out; 1304 } 1305 1306 /** 1307 * set average queue size to target level 1308 */ 1309 *tcfg->tqueue->q = *tcfg->tlevel; 1310 1311 /** 1312 * initialize the rte_red run time data structure 1313 */ 1314 rte_red_rt_data_init(tcfg->tqueue->rdata); 1315 1316 /** 1317 * set the queue average 1318 */ 1319 rte_red_set_avg_int(tcfg->tconfig->rconfig, tcfg->tqueue->rdata, *tcfg->tlevel); 1320 if (rte_red_get_avg_int(tcfg->tconfig->rconfig, tcfg->tqueue->rdata) 1321 != *tcfg->tlevel) { 1322 result = FAIL; 1323 goto out; 1324 } 1325 1326 enqueue_dequeue_perf(tcfg->tconfig->rconfig, 1327 tcfg->tqueue->rdata, 1328 tcfg->tqueue->q, 1329 tcfg->tvar->num_ops, 1330 tcfg->tvar->enqueued, 1331 tcfg->tvar->dropped, 1332 &prof); 1333 1334 total = *tcfg->tvar->enqueued + *tcfg->tvar->dropped; 1335 1336 printf("\ntotal: %u, enqueued: %u (%.2lf%%), dropped: %u (%.2lf%%)\n", total, 1337 *tcfg->tvar->enqueued, ((double)(*tcfg->tvar->enqueued) / (double)total) * 100.0, 1338 *tcfg->tvar->dropped, ((double)(*tcfg->tvar->dropped) / (double)total) * 100.0); 1339 1340 rdtsc_prof_print(&prof); 1341 out: 1342 return result; 1343 } 1344 1345 /** 1346 * Setup test structures for tests P4, P5, P6 1347 * performance tests 4, 5 and 6 1348 */ 1349 static uint32_t pt4_tlevel[] = {16}; 1350 static uint32_t pt5_tlevel[] = {80}; 1351 static uint32_t pt6_tlevel[] = {144}; 1352 1353 static struct test_var perf2_tvar = { 1354 .wait_usec = 500, 1355 .num_iterations = 10000, 1356 .num_ops = 10000, 1357 .dropped = pt_dropped, 1358 .enqueued = pt_enqueued, 1359 .sleep_sec = 0 1360 }; 1361 1362 static struct test_config perf2_test4_config = { 1363 .ifname = "performance test 4 interface", 1364 .msg = "performance test 4 : use one RED configuration,\n" 1365 " set actual and average queue sizes to level below min threshold,\n" 1366 " dequeue all packets until queue is empty,\n" 1367 " measure enqueue performance when queue is empty\n\n", 1368 .htxt = "iteration " 1369 "q avg before " 1370 "q avg after " 1371 "expected " 1372 "difference % " 1373 "tolerance % " 1374 "result ""\n", 1375 .tconfig = &pt_tconfig, 1376 .tqueue = &pt_tqueue, 1377 .tvar = &perf2_tvar, 1378 .tlevel = pt4_tlevel, 1379 }; 1380 1381 static struct test_config perf2_test5_config = { 1382 .ifname = "performance test 5 interface", 1383 .msg = "performance test 5 : use one RED configuration,\n" 1384 " set actual and average queue sizes to level in between min and max thresholds,\n" 1385 " dequeue all packets until queue is empty,\n" 1386 " measure enqueue performance when queue is empty\n\n", 1387 .htxt = "iteration " 1388 "q avg before " 1389 "q avg after " 1390 "expected " 1391 "difference " 1392 "tolerance " 1393 "result ""\n", 1394 .tconfig = &pt_tconfig, 1395 .tqueue = &pt_tqueue, 1396 .tvar = &perf2_tvar, 1397 .tlevel = pt5_tlevel, 1398 }; 1399 1400 static struct test_config perf2_test6_config = { 1401 .ifname = "performance test 6 interface", 1402 .msg = "performance test 6 : use one RED configuration,\n" 1403 " set actual and average queue sizes to level above max threshold,\n" 1404 " dequeue all packets until queue is empty,\n" 1405 " measure enqueue performance when queue is empty\n\n", 1406 .htxt = "iteration " 1407 "q avg before " 1408 "q avg after " 1409 "expected " 1410 "difference % " 1411 "tolerance % " 1412 "result ""\n", 1413 .tconfig = &pt_tconfig, 1414 .tqueue = &pt_tqueue, 1415 .tvar = &perf2_tvar, 1416 .tlevel = pt6_tlevel, 1417 }; 1418 1419 /** 1420 * Performance test function to measure enqueue performance when the 1421 * queue is empty. This runs performance tests 4, 5 and 6 1422 */ 1423 static enum test_result perf2_test(struct test_config *tcfg) 1424 { 1425 enum test_result result = PASS; 1426 struct rdtsc_prof prof = {0, 0, 0, 0, 0.0, NULL}; 1427 uint32_t total = 0; 1428 uint32_t i = 0; 1429 1430 printf("%s", tcfg->msg); 1431 1432 rdtsc_prof_init(&prof, "enqueue"); 1433 1434 if (test_rte_red_init(tcfg) != PASS) { 1435 result = FAIL; 1436 goto out; 1437 } 1438 1439 printf("%s", tcfg->htxt); 1440 1441 for (i = 0; i < tcfg->tvar->num_iterations; i++) { 1442 uint32_t count = 0; 1443 uint64_t ts = 0; 1444 double avg_before = 0; 1445 int ret = 0; 1446 1447 /** 1448 * set average queue size to target level 1449 */ 1450 *tcfg->tqueue->q = *tcfg->tlevel; 1451 count = (*tcfg->tqueue->rdata).count; 1452 1453 /** 1454 * initialize the rte_red run time data structure 1455 */ 1456 rte_red_rt_data_init(tcfg->tqueue->rdata); 1457 (*tcfg->tqueue->rdata).count = count; 1458 1459 /** 1460 * set the queue average 1461 */ 1462 rte_red_set_avg_int(tcfg->tconfig->rconfig, tcfg->tqueue->rdata, *tcfg->tlevel); 1463 avg_before = rte_red_get_avg_float(tcfg->tconfig->rconfig, tcfg->tqueue->rdata); 1464 if ((avg_before < *tcfg->tlevel) || (avg_before > *tcfg->tlevel)) { 1465 result = FAIL; 1466 goto out; 1467 } 1468 1469 /** 1470 * empty the queue 1471 */ 1472 *tcfg->tqueue->q = 0; 1473 rte_red_mark_queue_empty(tcfg->tqueue->rdata, get_port_ts()); 1474 1475 /** 1476 * wait for specified period of time 1477 */ 1478 rte_delay_us(tcfg->tvar->wait_usec); 1479 1480 /** 1481 * measure performance of enqueue operation while queue is empty 1482 */ 1483 ts = get_port_ts(); 1484 rdtsc_prof_start(&prof); 1485 ret = rte_red_enqueue(tcfg->tconfig->rconfig, tcfg->tqueue->rdata, 1486 *tcfg->tqueue->q, ts ); 1487 rdtsc_prof_end(&prof); 1488 1489 /** 1490 * gather enqueued/dropped statistics 1491 */ 1492 if (ret == 0) 1493 (*tcfg->tvar->enqueued)++; 1494 else 1495 (*tcfg->tvar->dropped)++; 1496 1497 /** 1498 * on first and last iteration, confirm that 1499 * average queue size was computed correctly 1500 */ 1501 if ((i == 0) || (i == tcfg->tvar->num_iterations - 1)) { 1502 double avg_after = 0; 1503 double exp_avg = 0; 1504 double diff = 0.0; 1505 int ok = 0; 1506 1507 avg_after = rte_red_get_avg_float(tcfg->tconfig->rconfig, tcfg->tqueue->rdata); 1508 exp_avg = calc_exp_avg_on_empty(avg_before, 1509 (1 << *tcfg->tconfig->wq_log2), 1510 tcfg->tvar->wait_usec); 1511 if (check_avg(&diff, avg_after, exp_avg, (double)tcfg->tqueue->avg_tolerance)) 1512 ok = 1; 1513 printf("%-15u%-15.4lf%-15.4lf%-15.4lf%-15.4lf%-15.4lf%-15s\n", 1514 i, avg_before, avg_after, exp_avg, diff, 1515 (double)tcfg->tqueue->avg_tolerance, ok ? "pass" : "fail"); 1516 if (!ok) { 1517 result = FAIL; 1518 goto out; 1519 } 1520 } 1521 } 1522 total = *tcfg->tvar->enqueued + *tcfg->tvar->dropped; 1523 printf("\ntotal: %u, enqueued: %u (%.2lf%%), dropped: %u (%.2lf%%)\n", total, 1524 *tcfg->tvar->enqueued, ((double)(*tcfg->tvar->enqueued) / (double)total) * 100.0, 1525 *tcfg->tvar->dropped, ((double)(*tcfg->tvar->dropped) / (double)total) * 100.0); 1526 1527 rdtsc_prof_print(&prof); 1528 out: 1529 return result; 1530 } 1531 1532 /** 1533 * setup default values for overflow test structures 1534 */ 1535 static uint32_t avg_max = 0; 1536 static uint32_t avg_max_bits = 0; 1537 1538 static struct rte_red_config ovfl_wrconfig[1]; 1539 static struct rte_red ovfl_rtdata[1]; 1540 static uint8_t ovfl_maxp_inv[] = {10}; 1541 static uint32_t ovfl_qconfig[] = {0, 0, 1, 1}; 1542 static uint32_t ovfl_q[] ={0}; 1543 static uint32_t ovfl_dropped[] ={0}; 1544 static uint32_t ovfl_enqueued[] ={0}; 1545 static uint32_t ovfl_tlevel[] = {1023}; 1546 static uint8_t ovfl_wq_log2[] = {12}; 1547 1548 static struct test_rte_red_config ovfl_tconfig = { 1549 .rconfig = ovfl_wrconfig, 1550 .num_cfg = RTE_DIM(ovfl_wrconfig), 1551 .wq_log2 = ovfl_wq_log2, 1552 .min_th = 32, 1553 .max_th = 1023, 1554 .maxp_inv = ovfl_maxp_inv, 1555 }; 1556 1557 static struct test_queue ovfl_tqueue = { 1558 .rdata = ovfl_rtdata, 1559 .num_queues = RTE_DIM(ovfl_rtdata), 1560 .qconfig = ovfl_qconfig, 1561 .q = ovfl_q, 1562 .q_ramp_up = 1000000, 1563 .avg_ramp_up = 1000000, 1564 .avg_tolerance = 5, /* 10 percent */ 1565 .drop_tolerance = 50, /* 50 percent */ 1566 }; 1567 1568 static struct test_var ovfl_tvar = { 1569 .wait_usec = 10000, 1570 .num_iterations = 1, 1571 .num_ops = 10000, 1572 .clk_freq = 0, 1573 .dropped = ovfl_dropped, 1574 .enqueued = ovfl_enqueued, 1575 .sleep_sec = 0 1576 }; 1577 1578 static void ovfl_check_avg(uint32_t avg) 1579 { 1580 if (avg > avg_max) { 1581 double avg_log = 0; 1582 uint32_t bits = 0; 1583 avg_max = avg; 1584 avg_log = log(((double)avg_max)); 1585 avg_log = avg_log / log(2.0); 1586 bits = (uint32_t)ceil(avg_log); 1587 if (bits > avg_max_bits) 1588 avg_max_bits = bits; 1589 } 1590 } 1591 1592 static struct test_config ovfl_test1_config = { 1593 .ifname = "queue average overflow test interface", 1594 .msg = "overflow test 1 : use one RED configuration,\n" 1595 " increase average queue size to target level,\n" 1596 " check maximum number of bits required to represent avg_s\n\n", 1597 .htxt = "avg queue size " 1598 "wq_log2 " 1599 "fraction bits " 1600 "max queue avg " 1601 "num bits " 1602 "enqueued " 1603 "dropped " 1604 "drop prob % " 1605 "drop rate % " 1606 "\n", 1607 .tconfig = &ovfl_tconfig, 1608 .tqueue = &ovfl_tqueue, 1609 .tvar = &ovfl_tvar, 1610 .tlevel = ovfl_tlevel, 1611 }; 1612 1613 static enum test_result ovfl_test1(struct test_config *tcfg) 1614 { 1615 enum test_result result = PASS; 1616 uint32_t avg = 0; 1617 uint32_t i = 0; 1618 double drop_rate = 0.0; 1619 double drop_prob = 0.0; 1620 double diff = 0.0; 1621 int ret = 0; 1622 1623 printf("%s", tcfg->msg); 1624 1625 if (test_rte_red_init(tcfg) != PASS) { 1626 1627 result = FAIL; 1628 goto out; 1629 } 1630 1631 /** 1632 * reset rte_red run-time data 1633 */ 1634 rte_red_rt_data_init(tcfg->tqueue->rdata); 1635 1636 /** 1637 * increase actual queue size 1638 */ 1639 for (i = 0; i < tcfg->tqueue->q_ramp_up; i++) { 1640 ret = rte_red_enqueue(tcfg->tconfig->rconfig, tcfg->tqueue->rdata, 1641 *tcfg->tqueue->q, get_port_ts()); 1642 1643 if (ret == 0) { 1644 if (++(*tcfg->tqueue->q) >= *tcfg->tlevel) 1645 break; 1646 } 1647 } 1648 1649 /** 1650 * enqueue 1651 */ 1652 for (i = 0; i < tcfg->tqueue->avg_ramp_up; i++) { 1653 ret = rte_red_enqueue(tcfg->tconfig->rconfig, tcfg->tqueue->rdata, 1654 *tcfg->tqueue->q, get_port_ts()); 1655 ovfl_check_avg((*tcfg->tqueue->rdata).avg); 1656 avg = rte_red_get_avg_int(tcfg->tconfig->rconfig, tcfg->tqueue->rdata); 1657 if (avg == *tcfg->tlevel) { 1658 if (ret == 0) 1659 (*tcfg->tvar->enqueued)++; 1660 else 1661 (*tcfg->tvar->dropped)++; 1662 } 1663 } 1664 1665 /** 1666 * check if target average queue size has been reached 1667 */ 1668 avg = rte_red_get_avg_int(tcfg->tconfig->rconfig, tcfg->tqueue->rdata); 1669 if (avg != *tcfg->tlevel) { 1670 result = FAIL; 1671 goto out; 1672 } 1673 1674 /** 1675 * check drop rate against drop probability 1676 */ 1677 drop_rate = calc_drop_rate(*tcfg->tvar->enqueued, *tcfg->tvar->dropped); 1678 drop_prob = calc_drop_prob(tcfg->tconfig->min_th, 1679 tcfg->tconfig->max_th, 1680 *tcfg->tconfig->maxp_inv, 1681 *tcfg->tlevel); 1682 if (!check_drop_rate(&diff, drop_rate, drop_prob, (double)tcfg->tqueue->drop_tolerance)) 1683 result = FAIL; 1684 1685 printf("%s", tcfg->htxt); 1686 1687 printf("%-16u%-9u%-15u0x%08x %-10u%-10u%-10u%-13.2lf%-13.2lf\n", 1688 avg, *tcfg->tconfig->wq_log2, RTE_RED_SCALING, 1689 avg_max, avg_max_bits, 1690 *tcfg->tvar->enqueued, *tcfg->tvar->dropped, 1691 drop_prob * 100.0, drop_rate * 100.0); 1692 out: 1693 return result; 1694 } 1695 1696 /** 1697 * define the functional and performance tests to be executed 1698 */ 1699 struct tests func_tests[] = { 1700 { &func_test1_config, func_test1 }, 1701 { &func_test2_config, func_test2 }, 1702 { &func_test3_config, func_test3 }, 1703 { &func_test4_config, func_test4 }, 1704 { &func_test5_config, func_test5 }, 1705 { &func_test6_config, func_test6 }, 1706 { &ovfl_test1_config, ovfl_test1 }, 1707 }; 1708 1709 struct tests func_tests_quick[] = { 1710 { &func_test1_config, func_test1 }, 1711 { &func_test2_config, func_test2 }, 1712 { &func_test3_config, func_test3 }, 1713 /* no test 4 as it takes a lot of time */ 1714 { &func_test5_config, func_test5 }, 1715 { &func_test6_config, func_test6 }, 1716 { &ovfl_test1_config, ovfl_test1 }, 1717 }; 1718 1719 struct tests perf_tests[] = { 1720 { &perf1_test1_config, perf1_test }, 1721 { &perf1_test2_config, perf1_test }, 1722 { &perf1_test3_config, perf1_test }, 1723 { &perf2_test4_config, perf2_test }, 1724 { &perf2_test5_config, perf2_test }, 1725 { &perf2_test6_config, perf2_test }, 1726 }; 1727 1728 /** 1729 * function to execute the required_red tests 1730 */ 1731 static void run_tests(struct tests *test_type, uint32_t test_count, uint32_t *num_tests, uint32_t *num_pass) 1732 { 1733 enum test_result result = PASS; 1734 uint32_t i = 0; 1735 1736 for (i = 0; i < test_count; i++) { 1737 printf("\n--------------------------------------------------------------------------------\n"); 1738 result = test_type[i].testfn(test_type[i].testcfg); 1739 (*num_tests)++; 1740 if (result == PASS) { 1741 (*num_pass)++; 1742 printf("-------------------------------------<pass>-------------------------------------\n"); 1743 } else { 1744 printf("-------------------------------------<fail>-------------------------------------\n"); 1745 } 1746 } 1747 return; 1748 } 1749 1750 /** 1751 * check if functions accept invalid parameters 1752 * 1753 * First, all functions will be called without initialized RED 1754 * Then, all of them will be called with NULL/invalid parameters 1755 * 1756 * Some functions are not tested as they are performance-critical and thus 1757 * don't do any parameter checking. 1758 */ 1759 static int 1760 test_invalid_parameters(void) 1761 { 1762 struct rte_red_config config; 1763 1764 if (rte_red_rt_data_init(NULL) == 0) { 1765 printf("rte_red_rt_data_init should have failed!\n"); 1766 return -1; 1767 } 1768 1769 if (rte_red_config_init(NULL, 0, 0, 0, 0) == 0) { 1770 printf("rte_red_config_init should have failed!\n"); 1771 return -1; 1772 } 1773 1774 if (rte_red_rt_data_init(NULL) == 0) { 1775 printf("rte_red_rt_data_init should have failed!\n"); 1776 return -1; 1777 } 1778 1779 /* NULL config */ 1780 if (rte_red_config_init(NULL, 0, 0, 0, 0) == 0) { 1781 printf("%i: rte_red_config_init should have failed!\n", __LINE__); 1782 return -1; 1783 } 1784 /* min_threshold == max_threshold */ 1785 if (rte_red_config_init(&config, 0, 1, 1, 0) == 0) { 1786 printf("%i: rte_red_config_init should have failed!\n", __LINE__); 1787 return -1; 1788 } 1789 /* min_threshold > max_threshold */ 1790 if (rte_red_config_init(&config, 0, 2, 1, 0) == 0) { 1791 printf("%i: rte_red_config_init should have failed!\n", __LINE__); 1792 return -1; 1793 } 1794 /* wq_log2 > RTE_RED_WQ_LOG2_MAX */ 1795 if (rte_red_config_init(&config, 1796 RTE_RED_WQ_LOG2_MAX + 1, 1, 2, 0) == 0) { 1797 printf("%i: rte_red_config_init should have failed!\n", __LINE__); 1798 return -1; 1799 } 1800 /* wq_log2 < RTE_RED_WQ_LOG2_MIN */ 1801 if (rte_red_config_init(&config, 1802 RTE_RED_WQ_LOG2_MIN - 1, 1, 2, 0) == 0) { 1803 printf("%i: rte_red_config_init should have failed!\n", __LINE__); 1804 return -1; 1805 } 1806 /* maxp_inv > RTE_RED_MAXP_INV_MAX */ 1807 if (rte_red_config_init(&config, 1808 RTE_RED_WQ_LOG2_MIN, 1, 2, RTE_RED_MAXP_INV_MAX + 1) == 0) { 1809 printf("%i: rte_red_config_init should have failed!\n", __LINE__); 1810 return -1; 1811 } 1812 /* maxp_inv < RTE_RED_MAXP_INV_MIN */ 1813 if (rte_red_config_init(&config, 1814 RTE_RED_WQ_LOG2_MIN, 1, 2, RTE_RED_MAXP_INV_MIN - 1) == 0) { 1815 printf("%i: rte_red_config_init should have failed!\n", __LINE__); 1816 return -1; 1817 } 1818 1819 return 0; 1820 } 1821 1822 static void 1823 show_stats(const uint32_t num_tests, const uint32_t num_pass) 1824 { 1825 if (num_pass == num_tests) 1826 printf("[total: %u, pass: %u]\n", num_tests, num_pass); 1827 else 1828 printf("[total: %u, pass: %u, fail: %u]\n", num_tests, num_pass, 1829 num_tests - num_pass); 1830 } 1831 1832 static int 1833 tell_the_result(const uint32_t num_tests, const uint32_t num_pass) 1834 { 1835 return (num_pass == num_tests) ? 0 : 1; 1836 } 1837 1838 static int 1839 test_red(void) 1840 { 1841 uint32_t num_tests = 0; 1842 uint32_t num_pass = 0; 1843 1844 if (test_invalid_parameters() < 0) 1845 return -1; 1846 run_tests(func_tests_quick, RTE_DIM(func_tests_quick), 1847 &num_tests, &num_pass); 1848 show_stats(num_tests, num_pass); 1849 return tell_the_result(num_tests, num_pass); 1850 } 1851 1852 static int 1853 test_red_perf(void) 1854 { 1855 uint32_t num_tests = 0; 1856 uint32_t num_pass = 0; 1857 1858 run_tests(perf_tests, RTE_DIM(perf_tests), &num_tests, &num_pass); 1859 show_stats(num_tests, num_pass); 1860 return tell_the_result(num_tests, num_pass); 1861 } 1862 1863 static int 1864 test_red_all(void) 1865 { 1866 uint32_t num_tests = 0; 1867 uint32_t num_pass = 0; 1868 1869 if (test_invalid_parameters() < 0) 1870 return -1; 1871 1872 run_tests(func_tests, RTE_DIM(func_tests), &num_tests, &num_pass); 1873 run_tests(perf_tests, RTE_DIM(perf_tests), &num_tests, &num_pass); 1874 show_stats(num_tests, num_pass); 1875 return tell_the_result(num_tests, num_pass); 1876 } 1877 1878 #endif /* !RTE_EXEC_ENV_WINDOWS */ 1879 1880 REGISTER_TEST_COMMAND(red_autotest, test_red); 1881 REGISTER_PERF_TEST(red_perf, test_red_perf); 1882 REGISTER_PERF_TEST(red_all, test_red_all); 1883