xref: /dpdk/app/test/test_red.c (revision f8dbaebbf1c9efcbb2e2354b341ed62175466a57)
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 shared 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);
1857