1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(C) 2020 Marvell International Ltd.
3 */
4 #include <inttypes.h>
5 #include <signal.h>
6 #include <stdio.h>
7 #include <unistd.h>
8
9 #include <rte_common.h>
10 #include <rte_cycles.h>
11 #include <rte_errno.h>
12 #include <rte_graph.h>
13 #include <rte_graph_worker.h>
14 #include <rte_lcore.h>
15 #include <rte_malloc.h>
16 #include <rte_mbuf.h>
17
18 #include "test.h"
19
20 #define TEST_GRAPH_PERF_MZ "graph_perf_data"
21 #define TEST_GRAPH_SRC_NAME "test_graph_perf_source"
22 #define TEST_GRAPH_SRC_BRST_ONE_NAME "test_graph_perf_source_one"
23 #define TEST_GRAPH_WRK_NAME "test_graph_perf_worker"
24 #define TEST_GRAPH_SNK_NAME "test_graph_perf_sink"
25
26 #define SOURCES(map) RTE_DIM(map)
27 #define STAGES(map) RTE_DIM(map)
28 #define NODES_PER_STAGE(map) RTE_DIM(map[0])
29 #define SINKS(map) RTE_DIM(map[0])
30
31 #define MAX_EDGES_PER_NODE 7
32
33 struct test_node_data {
34 uint8_t node_id;
35 uint8_t is_sink;
36 uint8_t next_nodes[MAX_EDGES_PER_NODE];
37 uint8_t next_percentage[MAX_EDGES_PER_NODE];
38 };
39
40 struct test_graph_perf {
41 uint16_t nb_nodes;
42 rte_graph_t graph_id;
43 struct test_node_data *node_data;
44 };
45
46 struct graph_lcore_data {
47 uint8_t done;
48 rte_graph_t graph_id;
49 };
50
51 static struct test_node_data *
graph_get_node_data(struct test_graph_perf * graph_data,rte_node_t id)52 graph_get_node_data(struct test_graph_perf *graph_data, rte_node_t id)
53 {
54 struct test_node_data *node_data = NULL;
55 int i;
56
57 for (i = 0; i < graph_data->nb_nodes; i++)
58 if (graph_data->node_data[i].node_id == id) {
59 node_data = &graph_data->node_data[i];
60 break;
61 }
62
63 return node_data;
64 }
65
66 static int
test_node_ctx_init(const struct rte_graph * graph,struct rte_node * node)67 test_node_ctx_init(const struct rte_graph *graph, struct rte_node *node)
68 {
69 struct test_graph_perf *graph_data;
70 struct test_node_data *node_data;
71 const struct rte_memzone *mz;
72 rte_node_t nid = node->id;
73 rte_edge_t edge = 0;
74 int i;
75
76 RTE_SET_USED(graph);
77
78 mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
79 if (mz == NULL)
80 return -ENOMEM;
81 graph_data = mz->addr;
82 node_data = graph_get_node_data(graph_data, nid);
83 node->ctx[0] = node->nb_edges;
84 for (i = 0; i < node->nb_edges && !node_data->is_sink; i++, edge++) {
85 node->ctx[i + 1] = edge;
86 node->ctx[i + 9] = node_data->next_percentage[i];
87 }
88
89 return 0;
90 }
91
92 /* Source node function */
93 static uint16_t
test_perf_node_worker_source(struct rte_graph * graph,struct rte_node * node,void ** objs,uint16_t nb_objs)94 test_perf_node_worker_source(struct rte_graph *graph, struct rte_node *node,
95 void **objs, uint16_t nb_objs)
96 {
97 uint16_t count;
98 int i;
99
100 RTE_SET_USED(objs);
101 RTE_SET_USED(nb_objs);
102
103 /* Create a proportional stream for every next */
104 for (i = 0; i < node->ctx[0]; i++) {
105 count = (node->ctx[i + 9] * RTE_GRAPH_BURST_SIZE) / 100;
106 rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
107 rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
108 }
109
110 return RTE_GRAPH_BURST_SIZE;
111 }
112
113 static struct rte_node_register test_graph_perf_source = {
114 .name = TEST_GRAPH_SRC_NAME,
115 .process = test_perf_node_worker_source,
116 .flags = RTE_NODE_SOURCE_F,
117 .init = test_node_ctx_init,
118 };
119
120 RTE_NODE_REGISTER(test_graph_perf_source);
121
122 static uint16_t
test_perf_node_worker_source_burst_one(struct rte_graph * graph,struct rte_node * node,void ** objs,uint16_t nb_objs)123 test_perf_node_worker_source_burst_one(struct rte_graph *graph,
124 struct rte_node *node, void **objs,
125 uint16_t nb_objs)
126 {
127 uint16_t count;
128 int i;
129
130 RTE_SET_USED(objs);
131 RTE_SET_USED(nb_objs);
132
133 /* Create a proportional stream for every next */
134 for (i = 0; i < node->ctx[0]; i++) {
135 count = (node->ctx[i + 9]) / 100;
136 rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
137 rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
138 }
139
140 return 1;
141 }
142
143 static struct rte_node_register test_graph_perf_source_burst_one = {
144 .name = TEST_GRAPH_SRC_BRST_ONE_NAME,
145 .process = test_perf_node_worker_source_burst_one,
146 .flags = RTE_NODE_SOURCE_F,
147 .init = test_node_ctx_init,
148 };
149
150 RTE_NODE_REGISTER(test_graph_perf_source_burst_one);
151
152 /* Worker node function */
153 static uint16_t
test_perf_node_worker(struct rte_graph * graph,struct rte_node * node,void ** objs,uint16_t nb_objs)154 test_perf_node_worker(struct rte_graph *graph, struct rte_node *node,
155 void **objs, uint16_t nb_objs)
156 {
157 uint16_t next = 0;
158 uint16_t enq = 0;
159 uint16_t count;
160 int i;
161
162 /* Move stream for single next node */
163 if (node->ctx[0] == 1) {
164 rte_node_next_stream_move(graph, node, node->ctx[1]);
165 return nb_objs;
166 }
167
168 /* Enqueue objects to next nodes proportionally */
169 for (i = 0; i < node->ctx[0]; i++) {
170 next = node->ctx[i + 1];
171 count = (node->ctx[i + 9] * nb_objs) / 100;
172 enq += count;
173 while (count) {
174 switch (count & (4 - 1)) {
175 case 0:
176 rte_node_enqueue_x4(graph, node, next, objs[0],
177 objs[1], objs[2], objs[3]);
178 objs += 4;
179 count -= 4;
180 break;
181 case 1:
182 rte_node_enqueue_x1(graph, node, next, objs[0]);
183 objs += 1;
184 count -= 1;
185 break;
186 case 2:
187 rte_node_enqueue_x2(graph, node, next, objs[0],
188 objs[1]);
189 objs += 2;
190 count -= 2;
191 break;
192 case 3:
193 rte_node_enqueue_x2(graph, node, next, objs[0],
194 objs[1]);
195 rte_node_enqueue_x1(graph, node, next, objs[0]);
196 objs += 3;
197 count -= 3;
198 break;
199 }
200 }
201 }
202
203 if (enq != nb_objs)
204 rte_node_enqueue(graph, node, next, objs, nb_objs - enq);
205
206 return nb_objs;
207 }
208
209 static struct rte_node_register test_graph_perf_worker = {
210 .name = TEST_GRAPH_WRK_NAME,
211 .process = test_perf_node_worker,
212 .init = test_node_ctx_init,
213 };
214
215 RTE_NODE_REGISTER(test_graph_perf_worker);
216
217 /* Last node in graph a.k.a sink node */
218 static uint16_t
test_perf_node_sink(struct rte_graph * graph,struct rte_node * node,void ** objs,uint16_t nb_objs)219 test_perf_node_sink(struct rte_graph *graph, struct rte_node *node, void **objs,
220 uint16_t nb_objs)
221 {
222 RTE_SET_USED(graph);
223 RTE_SET_USED(node);
224 RTE_SET_USED(objs);
225 RTE_SET_USED(nb_objs);
226
227 return nb_objs;
228 }
229
230 static struct rte_node_register test_graph_perf_sink = {
231 .name = TEST_GRAPH_SNK_NAME,
232 .process = test_perf_node_sink,
233 .init = test_node_ctx_init,
234 };
235
236 RTE_NODE_REGISTER(test_graph_perf_sink);
237
238 static int
graph_perf_setup(void)239 graph_perf_setup(void)
240 {
241 if (rte_lcore_count() < 2) {
242 printf("Test requires at least 2 lcores\n");
243 return TEST_SKIPPED;
244 }
245
246 return 0;
247 }
248
249 static void
graph_perf_teardown(void)250 graph_perf_teardown(void)
251 {
252 }
253
254 static inline rte_node_t
graph_node_get(const char * pname,char * nname)255 graph_node_get(const char *pname, char *nname)
256 {
257 rte_node_t pnode_id = rte_node_from_name(pname);
258 char lookup_name[RTE_NODE_NAMESIZE];
259 rte_node_t node_id;
260
261 snprintf(lookup_name, RTE_NODE_NAMESIZE, "%s-%s", pname, nname);
262 node_id = rte_node_from_name(lookup_name);
263
264 if (node_id != RTE_NODE_ID_INVALID) {
265 if (rte_node_edge_count(node_id))
266 rte_node_edge_shrink(node_id, 0);
267 return node_id;
268 }
269
270 return rte_node_clone(pnode_id, nname);
271 }
272
273 static uint16_t
graph_node_count_edges(uint32_t stage,uint16_t node,uint16_t nodes_per_stage,uint8_t edge_map[][nodes_per_stage][nodes_per_stage],char * ename[],struct test_node_data * node_data,rte_node_t ** node_map)274 graph_node_count_edges(uint32_t stage, uint16_t node, uint16_t nodes_per_stage,
275 uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
276 char *ename[], struct test_node_data *node_data,
277 rte_node_t **node_map)
278 {
279 uint8_t total_percent = 0;
280 uint16_t edges = 0;
281 int i;
282
283 for (i = 0; i < nodes_per_stage && edges < MAX_EDGES_PER_NODE; i++) {
284 if (edge_map[stage + 1][i][node]) {
285 ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
286 snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
287 rte_node_id_to_name(node_map[stage + 1][i]));
288 node_data->next_nodes[edges] = node_map[stage + 1][i];
289 node_data->next_percentage[edges] =
290 edge_map[stage + 1][i][node];
291 edges++;
292 total_percent += edge_map[stage + 1][i][node];
293 }
294 }
295
296 if (edges >= MAX_EDGES_PER_NODE || (edges && total_percent != 100)) {
297 for (i = 0; i < edges; i++)
298 free(ename[i]);
299 return RTE_EDGE_ID_INVALID;
300 }
301
302 return edges;
303 }
304
305 static int
graph_init(const char * gname,uint8_t nb_srcs,uint8_t nb_sinks,uint32_t stages,uint16_t nodes_per_stage,uint8_t src_map[][nodes_per_stage],uint8_t snk_map[][nb_sinks],uint8_t edge_map[][nodes_per_stage][nodes_per_stage],uint8_t burst_one)306 graph_init(const char *gname, uint8_t nb_srcs, uint8_t nb_sinks,
307 uint32_t stages, uint16_t nodes_per_stage,
308 uint8_t src_map[][nodes_per_stage], uint8_t snk_map[][nb_sinks],
309 uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
310 uint8_t burst_one)
311 {
312 struct test_graph_perf *graph_data;
313 char nname[RTE_NODE_NAMESIZE / 2];
314 struct test_node_data *node_data;
315 char *ename[nodes_per_stage];
316 struct rte_graph_param gconf;
317 const struct rte_memzone *mz;
318 uint8_t total_percent = 0;
319 rte_node_t *src_nodes;
320 rte_node_t *snk_nodes;
321 rte_node_t **node_map;
322 char **node_patterns;
323 rte_graph_t graph_id;
324 rte_edge_t edges;
325 rte_edge_t count;
326 uint32_t i, j, k;
327
328 mz = rte_memzone_reserve(TEST_GRAPH_PERF_MZ,
329 sizeof(struct test_graph_perf), 0, 0);
330 if (mz == NULL) {
331 printf("Failed to allocate graph common memory\n");
332 return -ENOMEM;
333 }
334
335 graph_data = mz->addr;
336 graph_data->nb_nodes = 0;
337 graph_data->node_data =
338 malloc(sizeof(struct test_node_data) *
339 (nb_srcs + nb_sinks + stages * nodes_per_stage));
340 if (graph_data->node_data == NULL) {
341 printf("Failed to reserve memzone for graph data\n");
342 goto memzone_free;
343 }
344
345 node_patterns = malloc(sizeof(char *) *
346 (nb_srcs + nb_sinks + stages * nodes_per_stage));
347 if (node_patterns == NULL) {
348 printf("Failed to reserve memory for node patterns\n");
349 goto data_free;
350 }
351
352 src_nodes = malloc(sizeof(rte_node_t) * nb_srcs);
353 if (src_nodes == NULL) {
354 printf("Failed to reserve memory for src nodes\n");
355 goto pattern_free;
356 }
357
358 snk_nodes = malloc(sizeof(rte_node_t) * nb_sinks);
359 if (snk_nodes == NULL) {
360 printf("Failed to reserve memory for snk nodes\n");
361 goto src_free;
362 }
363
364 node_map = malloc(sizeof(rte_node_t *) * stages +
365 sizeof(rte_node_t) * nodes_per_stage * stages);
366 if (node_map == NULL) {
367 printf("Failed to reserve memory for node map\n");
368 goto snk_free;
369 }
370
371 /* Setup the Graph */
372 for (i = 0; i < stages; i++) {
373 node_map[i] =
374 (rte_node_t *)(node_map + stages) + nodes_per_stage * i;
375 for (j = 0; j < nodes_per_stage; j++) {
376 total_percent = 0;
377 for (k = 0; k < nodes_per_stage; k++)
378 total_percent += edge_map[i][j][k];
379 if (!total_percent)
380 continue;
381 node_patterns[graph_data->nb_nodes] =
382 malloc(RTE_NODE_NAMESIZE);
383 if (node_patterns[graph_data->nb_nodes] == NULL) {
384 printf("Failed to create memory for pattern\n");
385 goto pattern_name_free;
386 }
387
388 /* Clone a worker node */
389 snprintf(nname, sizeof(nname), "%d-%d", i, j);
390 node_map[i][j] =
391 graph_node_get(TEST_GRAPH_WRK_NAME, nname);
392 if (node_map[i][j] == RTE_NODE_ID_INVALID) {
393 printf("Failed to create node[%s]\n", nname);
394 graph_data->nb_nodes++;
395 goto pattern_name_free;
396 }
397 snprintf(node_patterns[graph_data->nb_nodes],
398 RTE_NODE_NAMESIZE, "%s",
399 rte_node_id_to_name(node_map[i][j]));
400 node_data =
401 &graph_data->node_data[graph_data->nb_nodes];
402 node_data->node_id = node_map[i][j];
403 node_data->is_sink = false;
404 graph_data->nb_nodes++;
405 }
406 }
407
408 for (i = 0; i < stages - 1; i++) {
409 for (j = 0; j < nodes_per_stage; j++) {
410 /* Count edges i.e connections of worker node to next */
411 node_data =
412 graph_get_node_data(graph_data, node_map[i][j]);
413 edges = graph_node_count_edges(i, j, nodes_per_stage,
414 edge_map, ename,
415 node_data, node_map);
416 if (edges == RTE_EDGE_ID_INVALID) {
417 printf("Invalid edge configuration\n");
418 goto pattern_name_free;
419 }
420 if (!edges)
421 continue;
422
423 /* Connect a node in stage 'i' to nodes
424 * in stage 'i + 1' with edges.
425 */
426 count = rte_node_edge_update(
427 node_map[i][j], 0,
428 (const char **)(uintptr_t)ename, edges);
429 for (k = 0; k < edges; k++)
430 free(ename[k]);
431 if (count != edges) {
432 printf("Couldn't add edges %d %d\n", edges,
433 count);
434 goto pattern_name_free;
435 }
436 }
437 }
438
439 /* Setup Source nodes */
440 for (i = 0; i < nb_srcs; i++) {
441 edges = 0;
442 total_percent = 0;
443 node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
444 if (node_patterns[graph_data->nb_nodes] == NULL) {
445 printf("Failed to create memory for pattern\n");
446 goto pattern_name_free;
447 }
448 /* Clone a source node */
449 snprintf(nname, sizeof(nname), "%d", i);
450 src_nodes[i] =
451 graph_node_get(burst_one ? TEST_GRAPH_SRC_BRST_ONE_NAME
452 : TEST_GRAPH_SRC_NAME,
453 nname);
454 if (src_nodes[i] == RTE_NODE_ID_INVALID) {
455 printf("Failed to create node[%s]\n", nname);
456 graph_data->nb_nodes++;
457 goto pattern_name_free;
458 }
459 snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
460 "%s", rte_node_id_to_name(src_nodes[i]));
461 node_data = &graph_data->node_data[graph_data->nb_nodes];
462 node_data->node_id = src_nodes[i];
463 node_data->is_sink = false;
464 graph_data->nb_nodes++;
465
466 /* Prepare next node list to connect to */
467 for (j = 0; j < nodes_per_stage; j++) {
468 if (!src_map[i][j])
469 continue;
470 ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
471 snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
472 rte_node_id_to_name(node_map[0][j]));
473 node_data->next_nodes[edges] = node_map[0][j];
474 node_data->next_percentage[edges] = src_map[i][j];
475 edges++;
476 total_percent += src_map[i][j];
477 }
478
479 if (!edges)
480 continue;
481 if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
482 printf("Invalid edge configuration\n");
483 for (j = 0; j < edges; j++)
484 free(ename[j]);
485 goto pattern_name_free;
486 }
487
488 /* Connect to list of next nodes using edges */
489 count = rte_node_edge_update(src_nodes[i], 0,
490 (const char **)(uintptr_t)ename,
491 edges);
492 for (k = 0; k < edges; k++)
493 free(ename[k]);
494 if (count != edges) {
495 printf("Couldn't add edges %d %d\n", edges, count);
496 goto pattern_name_free;
497 }
498 }
499
500 /* Setup Sink nodes */
501 for (i = 0; i < nb_sinks; i++) {
502 node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
503 if (node_patterns[graph_data->nb_nodes] == NULL) {
504 printf("Failed to create memory for pattern\n");
505 goto pattern_name_free;
506 }
507
508 /* Clone a sink node */
509 snprintf(nname, sizeof(nname), "%d", i);
510 snk_nodes[i] = graph_node_get(TEST_GRAPH_SNK_NAME, nname);
511 if (snk_nodes[i] == RTE_NODE_ID_INVALID) {
512 printf("Failed to create node[%s]\n", nname);
513 graph_data->nb_nodes++;
514 goto pattern_name_free;
515 }
516 snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
517 "%s", rte_node_id_to_name(snk_nodes[i]));
518 node_data = &graph_data->node_data[graph_data->nb_nodes];
519 node_data->node_id = snk_nodes[i];
520 node_data->is_sink = true;
521 graph_data->nb_nodes++;
522 }
523
524 /* Connect last stage worker nodes to sink nodes */
525 for (i = 0; i < nodes_per_stage; i++) {
526 edges = 0;
527 total_percent = 0;
528 node_data = graph_get_node_data(graph_data,
529 node_map[stages - 1][i]);
530 /* Prepare list of sink nodes to connect to */
531 for (j = 0; j < nb_sinks; j++) {
532 if (!snk_map[i][j])
533 continue;
534 ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
535 snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
536 rte_node_id_to_name(snk_nodes[j]));
537 node_data->next_nodes[edges] = snk_nodes[j];
538 node_data->next_percentage[edges] = snk_map[i][j];
539 edges++;
540 total_percent += snk_map[i][j];
541 }
542 if (!edges)
543 continue;
544 if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
545 printf("Invalid edge configuration\n");
546 for (j = 0; j < edges; j++)
547 free(ename[i]);
548 goto pattern_name_free;
549 }
550
551 /* Connect a worker node to a list of sink nodes */
552 count = rte_node_edge_update(node_map[stages - 1][i], 0,
553 (const char **)(uintptr_t)ename,
554 edges);
555 for (k = 0; k < edges; k++)
556 free(ename[k]);
557 if (count != edges) {
558 printf("Couldn't add edges %d %d\n", edges, count);
559 goto pattern_name_free;
560 }
561 }
562
563 /* Create a Graph */
564 gconf.socket_id = SOCKET_ID_ANY;
565 gconf.nb_node_patterns = graph_data->nb_nodes;
566 gconf.node_patterns = (const char **)(uintptr_t)node_patterns;
567
568 graph_id = rte_graph_create(gname, &gconf);
569 if (graph_id == RTE_GRAPH_ID_INVALID) {
570 printf("Graph creation failed with error = %d\n", rte_errno);
571 goto pattern_name_free;
572 }
573 graph_data->graph_id = graph_id;
574
575 free(node_map);
576 for (i = 0; i < graph_data->nb_nodes; i++)
577 free(node_patterns[i]);
578 free(snk_nodes);
579 free(src_nodes);
580 free(node_patterns);
581 return 0;
582
583 pattern_name_free:
584 free(node_map);
585 for (i = 0; i < graph_data->nb_nodes; i++)
586 free(node_patterns[i]);
587 snk_free:
588 free(snk_nodes);
589 src_free:
590 free(src_nodes);
591 pattern_free:
592 free(node_patterns);
593 data_free:
594 free(graph_data->node_data);
595 memzone_free:
596 rte_memzone_free(mz);
597 return -ENOMEM;
598 }
599
600 /* Worker thread function */
601 static int
_graph_perf_wrapper(void * args)602 _graph_perf_wrapper(void *args)
603 {
604 struct graph_lcore_data *data = args;
605 struct rte_graph *graph;
606
607 /* Lookup graph */
608 graph = rte_graph_lookup(rte_graph_id_to_name(data->graph_id));
609
610 /* Graph walk until done */
611 while (!data->done)
612 rte_graph_walk(graph);
613
614 return 0;
615 }
616
617 static int
measure_perf_get(rte_graph_t graph_id)618 measure_perf_get(rte_graph_t graph_id)
619 {
620 const char *pattern = rte_graph_id_to_name(graph_id);
621 uint32_t lcore_id = rte_get_next_lcore(-1, 1, 0);
622 struct rte_graph_cluster_stats_param param;
623 struct rte_graph_cluster_stats *stats;
624 struct graph_lcore_data *data;
625
626 data = rte_zmalloc("Graph_perf", sizeof(struct graph_lcore_data),
627 RTE_CACHE_LINE_SIZE);
628 data->graph_id = graph_id;
629 data->done = 0;
630
631 /* Run graph worker thread function */
632 rte_eal_remote_launch(_graph_perf_wrapper, data, lcore_id);
633
634 /* Collect stats for few msecs */
635 if (rte_graph_has_stats_feature()) {
636 memset(¶m, 0, sizeof(param));
637 param.f = stdout;
638 param.socket_id = SOCKET_ID_ANY;
639 param.graph_patterns = &pattern;
640 param.nb_graph_patterns = 1;
641
642 stats = rte_graph_cluster_stats_create(¶m);
643 if (stats == NULL) {
644 printf("Failed to create stats\n");
645 return -ENOMEM;
646 }
647
648 rte_delay_ms(3E2);
649 rte_graph_cluster_stats_get(stats, true);
650 rte_delay_ms(1E3);
651 rte_graph_cluster_stats_get(stats, false);
652 rte_graph_cluster_stats_destroy(stats);
653 } else
654 rte_delay_ms(1E3);
655
656 data->done = 1;
657 rte_eal_wait_lcore(lcore_id);
658
659 return 0;
660 }
661
662 static inline void
graph_fini(void)663 graph_fini(void)
664 {
665 const struct rte_memzone *mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
666 struct test_graph_perf *graph_data;
667
668 if (mz == NULL)
669 return;
670 graph_data = mz->addr;
671
672 rte_graph_destroy(graph_data->graph_id);
673 free(graph_data->node_data);
674 rte_memzone_free(rte_memzone_lookup(TEST_GRAPH_PERF_MZ));
675 }
676
677 static int
measure_perf(void)678 measure_perf(void)
679 {
680 const struct rte_memzone *mz;
681 struct test_graph_perf *graph_data;
682
683 mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
684 if (mz == NULL)
685 return -ENOMEM;
686 graph_data = mz->addr;
687
688 return measure_perf_get(graph_data->graph_id);
689 }
690
691 static inline int
graph_hr_4s_1n_1src_1snk(void)692 graph_hr_4s_1n_1src_1snk(void)
693 {
694 return measure_perf();
695 }
696
697 static inline int
graph_hr_4s_1n_1src_1snk_brst_one(void)698 graph_hr_4s_1n_1src_1snk_brst_one(void)
699 {
700 return measure_perf();
701 }
702
703 static inline int
graph_hr_4s_1n_2src_1snk(void)704 graph_hr_4s_1n_2src_1snk(void)
705 {
706 return measure_perf();
707 }
708
709 static inline int
graph_hr_4s_1n_1src_2snk(void)710 graph_hr_4s_1n_1src_2snk(void)
711 {
712 return measure_perf();
713 }
714
715 static inline int
graph_tree_4s_4n_1src_4snk(void)716 graph_tree_4s_4n_1src_4snk(void)
717 {
718 return measure_perf();
719 }
720
721 static inline int
graph_reverse_tree_3s_4n_1src_1snk(void)722 graph_reverse_tree_3s_4n_1src_1snk(void)
723 {
724 return measure_perf();
725 }
726
727 static inline int
graph_parallel_tree_5s_4n_4src_4snk(void)728 graph_parallel_tree_5s_4n_4src_4snk(void)
729 {
730 return measure_perf();
731 }
732
733 /* Graph Topology
734 * nodes per stage: 1
735 * stages: 4
736 * src: 1
737 * sink: 1
738 */
739 static inline int
graph_init_hr(void)740 graph_init_hr(void)
741 {
742 uint8_t edge_map[][1][1] = {
743 { {100} },
744 { {100} },
745 { {100} },
746 { {100} },
747 };
748 uint8_t src_map[][1] = { {100} };
749 uint8_t snk_map[][1] = { {100} };
750
751 return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
752 STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
753 snk_map, edge_map, 0);
754 }
755
756 /* Graph Topology
757 * nodes per stage: 1
758 * stages: 4
759 * src: 1
760 * sink: 1
761 */
762 static inline int
graph_init_hr_brst_one(void)763 graph_init_hr_brst_one(void)
764 {
765 uint8_t edge_map[][1][1] = {
766 { {100} },
767 { {100} },
768 { {100} },
769 { {100} },
770 };
771 uint8_t src_map[][1] = { {100} };
772 uint8_t snk_map[][1] = { {100} };
773
774 return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
775 STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
776 snk_map, edge_map, 1);
777 }
778
779 /* Graph Topology
780 * nodes per stage: 1
781 * stages: 4
782 * src: 2
783 * sink: 1
784 */
785 static inline int
graph_init_hr_multi_src(void)786 graph_init_hr_multi_src(void)
787 {
788 uint8_t edge_map[][1][1] = {
789 { {100} },
790 { {100} },
791 { {100} },
792 { {100} },
793 };
794 uint8_t src_map[][1] = {
795 {100}, {100}
796 };
797 uint8_t snk_map[][1] = { {100} };
798
799 return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
800 STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
801 snk_map, edge_map, 0);
802 }
803
804 /* Graph Topology
805 * nodes per stage: 1
806 * stages: 4
807 * src: 1
808 * sink: 2
809 */
810 static inline int
graph_init_hr_multi_snk(void)811 graph_init_hr_multi_snk(void)
812 {
813 uint8_t edge_map[][1][1] = {
814 { {100} },
815 { {100} },
816 { {100} },
817 { {100} },
818 };
819 uint8_t src_map[][1] = { {100} };
820 uint8_t snk_map[][2] = { {50, 50} };
821
822 return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
823 STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
824 snk_map, edge_map, 0);
825 }
826
827 /* Graph Topology
828 * nodes per stage: 4
829 * stages: 4
830 * src: 1
831 * sink: 4
832 */
833 static inline int
graph_init_tree(void)834 graph_init_tree(void)
835 {
836 uint8_t edge_map[][4][4] = {
837 {
838 {100, 0, 0, 0},
839 {0, 0, 0, 0},
840 {0, 0, 0, 0},
841 {0, 0, 0, 0}
842 },
843 {
844 {50, 0, 0, 0},
845 {50, 0, 0, 0},
846 {0, 0, 0, 0},
847 {0, 0, 0, 0}
848 },
849 {
850 {33, 33, 0, 0},
851 {34, 34, 0, 0},
852 {33, 33, 0, 0},
853 {0, 0, 0, 0}
854 },
855 {
856 {25, 25, 25, 0},
857 {25, 25, 25, 0},
858 {25, 25, 25, 0},
859 {25, 25, 25, 0}
860 }
861 };
862 uint8_t src_map[][4] = { {100, 0, 0, 0} };
863 uint8_t snk_map[][4] = {
864 {100, 0, 0, 0},
865 {0, 100, 0, 0},
866 {0, 0, 100, 0},
867 {0, 0, 0, 100}
868 };
869
870 return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
871 STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
872 snk_map, edge_map, 0);
873 }
874
875 /* Graph Topology
876 * nodes per stage: 4
877 * stages: 3
878 * src: 1
879 * sink: 1
880 */
881 static inline int
graph_init_reverse_tree(void)882 graph_init_reverse_tree(void)
883 {
884 uint8_t edge_map[][4][4] = {
885 {
886 {25, 25, 25, 25},
887 {25, 25, 25, 25},
888 {25, 25, 25, 25},
889 {25, 25, 25, 25}
890 },
891 {
892 {33, 33, 33, 33},
893 {33, 33, 33, 33},
894 {34, 34, 34, 34},
895 {0, 0, 0, 0}
896 },
897 {
898 {50, 50, 50, 0},
899 {50, 50, 50, 0},
900 {0, 0, 0, 0},
901 {0, 0, 0, 0}
902 },
903 };
904 uint8_t src_map[][4] = { {25, 25, 25, 25} };
905 uint8_t snk_map[][1] = { {100}, {100}, {0}, {0} };
906
907 return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
908 STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
909 snk_map, edge_map, 0);
910 }
911
912 /* Graph Topology
913 * nodes per stage: 4
914 * stages: 5
915 * src: 4
916 * sink: 4
917 */
918 static inline int
graph_init_parallel_tree(void)919 graph_init_parallel_tree(void)
920 {
921 uint8_t edge_map[][4][4] = {
922 {
923 {100, 0, 0, 0},
924 {0, 100, 0, 0},
925 {0, 0, 100, 0},
926 {0, 0, 0, 100}
927 },
928 {
929 {100, 0, 0, 0},
930 {0, 100, 0, 0},
931 {0, 0, 100, 0},
932 {0, 0, 0, 100}
933 },
934 {
935 {100, 0, 0, 0},
936 {0, 100, 0, 0},
937 {0, 0, 100, 0},
938 {0, 0, 0, 100}
939 },
940 {
941 {100, 0, 0, 0},
942 {0, 100, 0, 0},
943 {0, 0, 100, 0},
944 {0, 0, 0, 100}
945 },
946 {
947 {100, 0, 0, 0},
948 {0, 100, 0, 0},
949 {0, 0, 100, 0},
950 {0, 0, 0, 100}
951 },
952 };
953 uint8_t src_map[][4] = {
954 {100, 0, 0, 0},
955 {0, 100, 0, 0},
956 {0, 0, 100, 0},
957 {0, 0, 0, 100}
958 };
959 uint8_t snk_map[][4] = {
960 {100, 0, 0, 0},
961 {0, 100, 0, 0},
962 {0, 0, 100, 0},
963 {0, 0, 0, 100}
964 };
965
966 return graph_init("graph_parallel", SOURCES(src_map), SINKS(snk_map),
967 STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
968 snk_map, edge_map, 0);
969 }
970
971 /** Graph Creation cheat sheet
972 * edge_map -> dictates graph flow from worker stage 0 to worker stage n-1.
973 * src_map -> dictates source nodes enqueue percentage to worker stage 0.
974 * snk_map -> dictates stage n-1 enqueue percentage to sink.
975 *
976 * Layout:
977 * edge_map[<nb_stages>][<nodes_per_stg>][<nodes_in_nxt_stg = nodes_per_stg>]
978 * src_map[<nb_sources>][<nodes_in_stage0 = nodes_per_stage>]
979 * snk_map[<nodes_in_stage(n-1) = nodes_per_stage>][<nb_sinks>]
980 *
981 * The last array dictates the percentage of received objs to enqueue to next
982 * stage.
983 *
984 * Note: edge_map[][0][] will always be unused as it will receive from source
985 *
986 * Example:
987 * Graph:
988 * http://bit.ly/2PqbqOy
989 * Each stage(n) connects to all nodes in the next stage in decreasing
990 * order.
991 * Since we can't resize the edge_map dynamically we get away by creating
992 * dummy nodes and assigning 0 percentages.
993 * Max nodes across all stages = 4
994 * stages = 3
995 * nb_src = 1
996 * nb_snk = 1
997 * // Stages
998 * edge_map[][4][4] = {
999 * // Nodes per stage
1000 * {
1001 * {25, 25, 25, 25},
1002 * {25, 25, 25, 25},
1003 * {25, 25, 25, 25},
1004 * {25, 25, 25, 25}
1005 * }, // This will be unused.
1006 * {
1007 * // Nodes enabled in current stage + prev stage enq %
1008 * {33, 33, 33, 33},
1009 * {33, 33, 33, 33},
1010 * {34, 34, 34, 34},
1011 * {0, 0, 0, 0}
1012 * },
1013 * {
1014 * {50, 50, 50, 0},
1015 * {50, 50, 50, 0},
1016 * {0, 0, 0, 0},
1017 * {0, 0, 0, 0}
1018 * },
1019 * };
1020 * Above, each stage tells how much it should receive from previous except
1021 * from stage_0.
1022 *
1023 * src_map[][4] = { {25, 25, 25, 25} };
1024 * Here, we tell each source the % it has to send to stage_0 nodes. In
1025 * case we want 2 source node we can declare as
1026 * src_map[][4] = { {25, 25, 25, 25}, {25, 25, 25, 25} };
1027 *
1028 * snk_map[][1] = { {100}, {100}, {0}, {0} }
1029 * Here, we tell stage - 1 nodes how much to enqueue to sink_0.
1030 * If we have 2 sinks we can do as follows
1031 * snk_map[][2] = { {50, 50}, {50, 50}, {0, 0}, {0, 0} }
1032 */
1033
1034 static struct unit_test_suite graph_perf_testsuite = {
1035 .suite_name = "Graph library performance test suite",
1036 .setup = graph_perf_setup,
1037 .teardown = graph_perf_teardown,
1038 .unit_test_cases = {
1039 TEST_CASE_ST(graph_init_hr, graph_fini,
1040 graph_hr_4s_1n_1src_1snk),
1041 TEST_CASE_ST(graph_init_hr_brst_one, graph_fini,
1042 graph_hr_4s_1n_1src_1snk_brst_one),
1043 TEST_CASE_ST(graph_init_hr_multi_src, graph_fini,
1044 graph_hr_4s_1n_2src_1snk),
1045 TEST_CASE_ST(graph_init_hr_multi_snk, graph_fini,
1046 graph_hr_4s_1n_1src_2snk),
1047 TEST_CASE_ST(graph_init_tree, graph_fini,
1048 graph_tree_4s_4n_1src_4snk),
1049 TEST_CASE_ST(graph_init_reverse_tree, graph_fini,
1050 graph_reverse_tree_3s_4n_1src_1snk),
1051 TEST_CASE_ST(graph_init_parallel_tree, graph_fini,
1052 graph_parallel_tree_5s_4n_4src_4snk),
1053 TEST_CASES_END(), /**< NULL terminate unit test array */
1054 },
1055 };
1056
1057 static int
test_graph_perf_func(void)1058 test_graph_perf_func(void)
1059 {
1060 return unit_test_suite_runner(&graph_perf_testsuite);
1061 }
1062
1063 REGISTER_TEST_COMMAND(graph_perf_autotest, test_graph_perf_func);
1064