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