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