]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2010-2014 Intel Corporation | |
7c673cae FG |
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); |