xref: /dpdk/app/test/test_graph_perf.c (revision 8b8036a66e3d59ffa58afb8d96fa2c73262155a7)
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 	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
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
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
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
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
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
250 graph_perf_teardown(void)
251 {
252 }
253 
254 static inline rte_node_t
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
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
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
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
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(&param, 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(&param);
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
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
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
692 graph_hr_4s_1n_1src_1snk(void)
693 {
694 	return measure_perf();
695 }
696 
697 static inline int
698 graph_hr_4s_1n_1src_1snk_brst_one(void)
699 {
700 	return measure_perf();
701 }
702 
703 static inline int
704 graph_hr_4s_1n_2src_1snk(void)
705 {
706 	return measure_perf();
707 }
708 
709 static inline int
710 graph_hr_4s_1n_1src_2snk(void)
711 {
712 	return measure_perf();
713 }
714 
715 static inline int
716 graph_tree_4s_4n_1src_4snk(void)
717 {
718 	return measure_perf();
719 }
720 
721 static inline int
722 graph_reverse_tree_3s_4n_1src_1snk(void)
723 {
724 	return measure_perf();
725 }
726 
727 static inline int
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
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
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
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
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
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
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
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
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