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