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