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