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