]> git.proxmox.com Git - ceph.git/blame - ceph/src/spdk/dpdk/test/test/test_red.c
update download target update for octopus release
[ceph.git] / ceph / src / spdk / dpdk / test / test / test_red.c
CommitLineData
11fdf7f2
TL
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation
7c673cae
FG
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 */
34struct 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
43struct 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
54struct 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
64struct 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
74enum test_result {
75 FAIL = 0,
76 PASS
77};
78
79/**< Test structure to define tests to run */
80struct tests {
81 struct test_config *testcfg;
82 enum test_result (*testfn)(struct test_config *);
83};
84
85struct 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
94static const uint64_t port_speed_bytes = (10ULL*1000ULL*1000ULL*1000ULL)/8ULL;
95static double inv_cycles_per_byte = 0;
96static double pkt_time_usec = 0;
97
98static 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
105static uint64_t get_port_ts(void)
106{
107 return (uint64_t)((double)rte_rdtsc() * inv_cycles_per_byte);
108}
109
110static 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
119static inline void rdtsc_prof_start(struct rdtsc_prof *p)
120{
121 p->clk_start = rte_rdtsc_precise();
122}
123
124static 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
137static 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
149static 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
158static 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
167static 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
177static 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
182static 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 */
190static 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 */
210static 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 */
230static 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 */
250static enum test_result
251test_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 */
277static int
278increase_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 */
312static int
313increase_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 */
343static struct rte_red_config ft_wrconfig[1];
344static struct rte_red ft_rtdata[1];
345static uint8_t ft_wq_log2[] = {9};
346static uint8_t ft_maxp_inv[] = {10};
347static uint32_t ft_qconfig[] = {0, 0, 1, 1};
348static uint32_t ft_q[] ={0};
349static uint32_t ft_dropped[] ={0};
350static uint32_t ft_enqueued[] ={0};
351
352static 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
361static 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
372static 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 */
385static 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 */
411static 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
413static 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
433static 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 }
509out:
510 return result;
511}
512
513/**
514 * Test F2: functional test 2
515 */
516static uint32_t ft2_tlevel[] = {127};
517static uint8_t ft2_wq_log2[] = {9, 9, 9, 9, 9, 9, 9, 9, 9, 9};
518static uint8_t ft2_maxp_inv[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
519static struct rte_red_config ft2_rconfig[10];
520
521static 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
530static 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
550static 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 }
620out:
621 return result;
622}
623
624/**
625 * Test F3: functional test 3
626 */
627static uint32_t ft3_tlevel[] = {1022};
628
629static 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
638static 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
657static 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 }
733out:
734 return result;
735}
736
737/**
738 * Test F4: functional test 4
739 */
740static uint32_t ft4_tlevel[] = {1022};
741static uint8_t ft4_wq_log2[] = {11};
742
743static 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
752static 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
763static 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
784static 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");
875out:
876 return result;
877}
878
879/**
880 * Test F5: functional test 5
881 */
882static uint32_t ft5_tlevel[] = {127};
883static uint8_t ft5_wq_log2[] = {9, 8};
884static uint8_t ft5_maxp_inv[] = {10, 20};
885static struct rte_red_config ft5_config[2];
886static struct rte_red ft5_data[4];
887static uint32_t ft5_q[4];
888static uint32_t ft5_dropped[] = {0, 0, 0, 0};
889static uint32_t ft5_enqueued[] = {0, 0, 0, 0};
890
891static 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
900static 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
911struct 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
921static 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
944static 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 }
1015out:
1016 return result;
1017}
1018
1019/**
1020 * Test F6: functional test 6
1021 */
1022static uint32_t ft6_tlevel[] = {1022};
1023static uint8_t ft6_wq_log2[] = {9, 8};
1024static uint8_t ft6_maxp_inv[] = {10, 20};
1025static struct rte_red_config ft6_config[2];
1026static struct rte_red ft6_data[4];
1027static uint32_t ft6_q[4];
1028
1029static 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
1038static 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
1049static 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 sharte_red 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
1071static 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 }
1146out:
1147 return result;
1148}
1149
1150/**
1151 * setup default values for the performance test structures
1152 */
1153static struct rte_red_config pt_wrconfig[1];
1154static struct rte_red pt_rtdata[1];
1155static uint8_t pt_wq_log2[] = {9};
1156static uint8_t pt_maxp_inv[] = {10};
1157static uint32_t pt_qconfig[] = {0};
1158static uint32_t pt_q[] = {0};
1159static uint32_t pt_dropped[] = {0};
1160static uint32_t pt_enqueued[] = {0};
1161
1162static 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
1171static 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 */
1185static 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 */
1216static uint32_t pt1_tlevel[] = {16};
1217static uint32_t pt2_tlevel[] = {80};
1218static uint32_t pt3_tlevel[] = {144};
1219
1220static 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
1230static 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
1241static 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
1252static 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 */
1267static 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);
1317out:
1318 return result;
1319}
1320
1321/**
1322 * Setup test structures for tests P4, P5, P6
1323 * performance tests 4, 5 and 6
1324 */
1325static uint32_t pt4_tlevel[] = {16};
1326static uint32_t pt5_tlevel[] = {80};
1327static uint32_t pt6_tlevel[] = {144};
1328
1329static 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
1338static 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
1357static 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
1376static 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 */
1399static 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);
1504out:
1505 return result;
1506}
1507
1508/**
1509 * setup default values for overflow test structures
1510 */
1511static uint32_t avg_max = 0;
1512static uint32_t avg_max_bits = 0;
1513
1514static struct rte_red_config ovfl_wrconfig[1];
1515static struct rte_red ovfl_rtdata[1];
1516static uint8_t ovfl_maxp_inv[] = {10};
1517static uint32_t ovfl_qconfig[] = {0, 0, 1, 1};
1518static uint32_t ovfl_q[] ={0};
1519static uint32_t ovfl_dropped[] ={0};
1520static uint32_t ovfl_enqueued[] ={0};
1521static uint32_t ovfl_tlevel[] = {1023};
1522static uint8_t ovfl_wq_log2[] = {12};
1523
1524static 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
1533static 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
1544static 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
1554static 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
1568static 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
1589static 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);
1668out:
1669 return result;
1670}
1671
1672/**
1673 * define the functional and performance tests to be executed
1674 */
1675struct 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
1685struct 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
1695struct 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 */
1707static 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 */
1735static int
1736test_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
1798static void
1799show_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
1808static int
1809tell_the_result(const uint32_t num_tests, const uint32_t num_pass)
1810{
1811 return (num_pass == num_tests) ? 0 : 1;
1812}
1813
1814static int
1815test_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
1828static int
1829test_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
1839static int
1840test_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
1854REGISTER_TEST_COMMAND(red_autotest, test_red);
1855REGISTER_TEST_COMMAND(red_perf, test_red_perf);
1856REGISTER_TEST_COMMAND(red_all, test_red_all);