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