xref: /dpdk/lib/graph/graph.c (revision ce7a737c8f02cd001f3f66f5c4e73ab7060ed588)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2020 Marvell International Ltd.
3  */
4 
5 #include <fnmatch.h>
6 #include <stdbool.h>
7 #include <stdlib.h>
8 
9 #include <rte_common.h>
10 #include <rte_debug.h>
11 #include <rte_errno.h>
12 #include <rte_malloc.h>
13 #include <rte_memzone.h>
14 #include <rte_spinlock.h>
15 #include <rte_string_fns.h>
16 
17 #include "graph_private.h"
18 #include "graph_pcap_private.h"
19 
20 static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list);
21 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
22 static rte_graph_t graph_id;
23 
24 #define GRAPH_ID_CHECK(id) ID_CHECK(id, graph_id)
25 
26 /* Private functions */
27 struct graph_head *
28 graph_list_head_get(void)
29 {
30 	return &graph_list;
31 }
32 
33 rte_spinlock_t *
34 graph_spinlock_get(void)
35 {
36 	return &graph_lock;
37 }
38 
39 void
40 graph_spinlock_lock(void)
41 {
42 	rte_spinlock_lock(graph_spinlock_get());
43 }
44 
45 void
46 graph_spinlock_unlock(void)
47 {
48 	rte_spinlock_unlock(graph_spinlock_get());
49 }
50 
51 static int
52 graph_node_add(struct graph *graph, struct node *node)
53 {
54 	struct graph_node *graph_node;
55 	size_t sz;
56 
57 	/* Skip the duplicate nodes */
58 	STAILQ_FOREACH(graph_node, &graph->node_list, next)
59 		if (strncmp(node->name, graph_node->node->name,
60 			    RTE_NODE_NAMESIZE) == 0)
61 			return 0;
62 
63 	/* Allocate new graph node object */
64 	sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *);
65 	graph_node = calloc(1, sz);
66 
67 	if (graph_node == NULL)
68 		SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object",
69 			    node->name);
70 
71 	/* Initialize the graph node */
72 	graph_node->node = node;
73 
74 	/* Add to graph node list */
75 	STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next);
76 	return 0;
77 
78 free:
79 	free(graph_node);
80 	return -rte_errno;
81 }
82 
83 static struct graph_node *
84 node_to_graph_node(struct graph *graph, struct node *node)
85 {
86 	struct graph_node *graph_node;
87 
88 	STAILQ_FOREACH(graph_node, &graph->node_list, next)
89 		if (graph_node->node == node)
90 			return graph_node;
91 
92 	SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name);
93 fail:
94 	return NULL;
95 }
96 
97 static int
98 graph_node_edges_add(struct graph *graph)
99 {
100 	struct graph_node *graph_node;
101 	struct node *adjacency;
102 	const char *next;
103 	rte_edge_t i;
104 
105 	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
106 		for (i = 0; i < graph_node->node->nb_edges; i++) {
107 			next = graph_node->node->next_nodes[i];
108 			adjacency = node_from_name(next);
109 			if (adjacency == NULL)
110 				SET_ERR_JMP(EINVAL, fail,
111 					    "Node %s not registered", next);
112 			if (graph_node_add(graph, adjacency))
113 				goto fail;
114 		}
115 	}
116 	return 0;
117 fail:
118 	return -rte_errno;
119 }
120 
121 static int
122 graph_adjacency_list_update(struct graph *graph)
123 {
124 	struct graph_node *graph_node, *tmp;
125 	struct node *adjacency;
126 	const char *next;
127 	rte_edge_t i;
128 
129 	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
130 		for (i = 0; i < graph_node->node->nb_edges; i++) {
131 			next = graph_node->node->next_nodes[i];
132 			adjacency = node_from_name(next);
133 			if (adjacency == NULL)
134 				SET_ERR_JMP(EINVAL, fail,
135 					    "Node %s not registered", next);
136 			tmp = node_to_graph_node(graph, adjacency);
137 			if (tmp == NULL)
138 				goto fail;
139 			graph_node->adjacency_list[i] = tmp;
140 		}
141 	}
142 
143 	return 0;
144 fail:
145 	return -rte_errno;
146 }
147 
148 static int
149 expand_pattern_to_node(struct graph *graph, const char *pattern)
150 {
151 	struct node_head *node_head = node_list_head_get();
152 	bool found = false;
153 	struct node *node;
154 
155 	/* Check for pattern match */
156 	STAILQ_FOREACH(node, node_head, next) {
157 		if (fnmatch(pattern, node->name, 0) == 0) {
158 			if (graph_node_add(graph, node))
159 				goto fail;
160 			found = true;
161 		}
162 	}
163 	if (found == false)
164 		SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern);
165 
166 	return 0;
167 fail:
168 	return -rte_errno;
169 }
170 
171 static void
172 graph_cleanup(struct graph *graph)
173 {
174 	struct graph_node *graph_node;
175 
176 	while (!STAILQ_EMPTY(&graph->node_list)) {
177 		graph_node = STAILQ_FIRST(&graph->node_list);
178 		STAILQ_REMOVE_HEAD(&graph->node_list, next);
179 		free(graph_node);
180 	}
181 }
182 
183 static int
184 graph_node_init(struct graph *graph)
185 {
186 	struct graph_node *graph_node;
187 	const char *name;
188 	int rc;
189 
190 	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
191 		if (graph_node->node->init) {
192 			name = graph_node->node->name;
193 			rc = graph_node->node->init(
194 				graph->graph,
195 				graph_node_name_to_ptr(graph->graph, name));
196 			if (rc)
197 				SET_ERR_JMP(rc, err, "Node %s init() failed",
198 					    name);
199 		}
200 	}
201 
202 	return 0;
203 err:
204 	return -rte_errno;
205 }
206 
207 static void
208 graph_node_fini(struct graph *graph)
209 {
210 	struct graph_node *graph_node;
211 
212 	STAILQ_FOREACH(graph_node, &graph->node_list, next)
213 		if (graph_node->node->fini)
214 			graph_node->node->fini(
215 				graph->graph,
216 				graph_node_name_to_ptr(graph->graph,
217 						       graph_node->node->name));
218 }
219 
220 static struct rte_graph *
221 graph_mem_fixup_node_ctx(struct rte_graph *graph)
222 {
223 	struct rte_node *node;
224 	struct node *node_db;
225 	rte_graph_off_t off;
226 	rte_node_t count;
227 	const char *name;
228 
229 	rte_graph_foreach_node(count, off, graph, node) {
230 		if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
231 			name = node->name;
232 		else /* Cloned node */
233 			name = node->parent;
234 
235 		node_db = node_from_name(name);
236 		if (node_db == NULL)
237 			SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
238 
239 		if (graph->pcap_enable) {
240 			node->process = graph_pcap_dispatch;
241 			node->original_process = node_db->process;
242 		} else
243 			node->process = node_db->process;
244 	}
245 
246 	return graph;
247 fail:
248 	return NULL;
249 }
250 
251 static struct rte_graph *
252 graph_mem_fixup_secondary(struct rte_graph *graph)
253 {
254 	if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)
255 		return graph;
256 
257 	if (graph_pcap_file_open(graph->pcap_filename) || graph_pcap_mp_init())
258 		graph_pcap_exit(graph);
259 
260 	return graph_mem_fixup_node_ctx(graph);
261 }
262 
263 struct rte_graph *
264 rte_graph_lookup(const char *name)
265 {
266 	const struct rte_memzone *mz;
267 	struct rte_graph *rc = NULL;
268 
269 	mz = rte_memzone_lookup(name);
270 	if (mz)
271 		rc = mz->addr;
272 
273 	return graph_mem_fixup_secondary(rc);
274 }
275 
276 rte_graph_t
277 rte_graph_create(const char *name, struct rte_graph_param *prm)
278 {
279 	rte_node_t src_node_count;
280 	struct graph *graph;
281 	const char *pattern;
282 	uint16_t i;
283 
284 	graph_spinlock_lock();
285 
286 	/* Check arguments sanity */
287 	if (prm == NULL)
288 		SET_ERR_JMP(EINVAL, fail, "Param should not be NULL");
289 
290 	if (name == NULL)
291 		SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL");
292 
293 	/* Check for existence of duplicate graph */
294 	STAILQ_FOREACH(graph, &graph_list, next)
295 		if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)
296 			SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s",
297 				    name);
298 
299 	/* Create graph object */
300 	graph = calloc(1, sizeof(*graph));
301 	if (graph == NULL)
302 		SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object");
303 
304 	/* Initialize the graph object */
305 	STAILQ_INIT(&graph->node_list);
306 	if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)
307 		SET_ERR_JMP(E2BIG, free, "Too big name=%s", name);
308 
309 	/* Expand node pattern and add the nodes to the graph */
310 	for (i = 0; i < prm->nb_node_patterns; i++) {
311 		pattern = prm->node_patterns[i];
312 		if (expand_pattern_to_node(graph, pattern))
313 			goto graph_cleanup;
314 	}
315 
316 	/* Go over all the nodes edges and add them to the graph */
317 	if (graph_node_edges_add(graph))
318 		goto graph_cleanup;
319 
320 	/* Update adjacency list of all nodes in the graph */
321 	if (graph_adjacency_list_update(graph))
322 		goto graph_cleanup;
323 
324 	/* Make sure at least a source node present in the graph */
325 	src_node_count = graph_src_nodes_count(graph);
326 	if (src_node_count == 0)
327 		goto graph_cleanup;
328 
329 	/* Make sure no node is pointing to source node */
330 	if (graph_node_has_edge_to_src_node(graph))
331 		goto graph_cleanup;
332 
333 	/* Don't allow node has loop to self */
334 	if (graph_node_has_loop_edge(graph))
335 		goto graph_cleanup;
336 
337 	/* Do BFS from src nodes on the graph to find isolated nodes */
338 	if (graph_has_isolated_node(graph))
339 		goto graph_cleanup;
340 
341 	/* Initialize pcap config. */
342 	graph_pcap_enable(prm->pcap_enable);
343 
344 	/* Initialize graph object */
345 	graph->socket = prm->socket_id;
346 	graph->src_node_count = src_node_count;
347 	graph->node_count = graph_nodes_count(graph);
348 	graph->id = graph_id;
349 	graph->num_pkt_to_capture = prm->num_pkt_to_capture;
350 	if (prm->pcap_filename)
351 		rte_strscpy(graph->pcap_filename, prm->pcap_filename, RTE_GRAPH_PCAP_FILE_SZ);
352 
353 	/* Allocate the Graph fast path memory and populate the data */
354 	if (graph_fp_mem_create(graph))
355 		goto graph_cleanup;
356 
357 	/* Call init() of the all the nodes in the graph */
358 	if (graph_node_init(graph))
359 		goto graph_mem_destroy;
360 
361 	/* All good, Lets add the graph to the list */
362 	graph_id++;
363 	STAILQ_INSERT_TAIL(&graph_list, graph, next);
364 
365 	graph_spinlock_unlock();
366 	return graph->id;
367 
368 graph_mem_destroy:
369 	graph_fp_mem_destroy(graph);
370 graph_cleanup:
371 	graph_cleanup(graph);
372 free:
373 	free(graph);
374 fail:
375 	graph_spinlock_unlock();
376 	return RTE_GRAPH_ID_INVALID;
377 }
378 
379 int
380 rte_graph_destroy(rte_graph_t id)
381 {
382 	struct graph *graph, *tmp;
383 	int rc = -ENOENT;
384 
385 	graph_spinlock_lock();
386 
387 	graph = STAILQ_FIRST(&graph_list);
388 	while (graph != NULL) {
389 		tmp = STAILQ_NEXT(graph, next);
390 		if (graph->id == id) {
391 			/* Call fini() of the all the nodes in the graph */
392 			graph_node_fini(graph);
393 			/* Destroy graph fast path memory */
394 			rc = graph_fp_mem_destroy(graph);
395 			if (rc)
396 				SET_ERR_JMP(rc, done, "Graph %s destroy failed",
397 					    graph->name);
398 
399 			graph_cleanup(graph);
400 			STAILQ_REMOVE(&graph_list, graph, graph, next);
401 			free(graph);
402 			graph_id--;
403 			goto done;
404 		}
405 		graph = tmp;
406 	}
407 done:
408 	graph_spinlock_unlock();
409 	return rc;
410 }
411 
412 rte_graph_t
413 rte_graph_from_name(const char *name)
414 {
415 	struct graph *graph;
416 
417 	STAILQ_FOREACH(graph, &graph_list, next)
418 		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0)
419 			return graph->id;
420 
421 	return RTE_GRAPH_ID_INVALID;
422 }
423 
424 char *
425 rte_graph_id_to_name(rte_graph_t id)
426 {
427 	struct graph *graph;
428 
429 	GRAPH_ID_CHECK(id);
430 	STAILQ_FOREACH(graph, &graph_list, next)
431 		if (graph->id == id)
432 			return graph->name;
433 
434 fail:
435 	return NULL;
436 }
437 
438 struct rte_node *
439 rte_graph_node_get(rte_graph_t gid, uint32_t nid)
440 {
441 	struct rte_node *node;
442 	struct graph *graph;
443 	rte_graph_off_t off;
444 	rte_node_t count;
445 
446 	GRAPH_ID_CHECK(gid);
447 	STAILQ_FOREACH(graph, &graph_list, next)
448 		if (graph->id == gid) {
449 			rte_graph_foreach_node(count, off, graph->graph,
450 						node) {
451 				if (node->id == nid)
452 					return node;
453 			}
454 			break;
455 		}
456 fail:
457 	return NULL;
458 }
459 
460 struct rte_node *
461 rte_graph_node_get_by_name(const char *graph_name, const char *node_name)
462 {
463 	struct rte_node *node;
464 	struct graph *graph;
465 	rte_graph_off_t off;
466 	rte_node_t count;
467 
468 	STAILQ_FOREACH(graph, &graph_list, next)
469 		if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) {
470 			rte_graph_foreach_node(count, off, graph->graph,
471 						node) {
472 				if (!strncmp(node->name, node_name,
473 					     RTE_NODE_NAMESIZE))
474 					return node;
475 			}
476 			break;
477 		}
478 
479 	return NULL;
480 }
481 
482 void __rte_noinline
483 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
484 {
485 	uint16_t size = node->size;
486 
487 	RTE_VERIFY(size != UINT16_MAX);
488 	/* Allocate double amount of size to avoid immediate realloc */
489 	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2));
490 	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
491 					RTE_CACHE_LINE_SIZE, graph->socket);
492 	RTE_VERIFY(node->objs);
493 	node->size = size;
494 	node->realloc_count++;
495 }
496 
497 void __rte_noinline
498 __rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node,
499 			     uint16_t req_size)
500 {
501 	uint16_t size = node->size;
502 
503 	RTE_VERIFY(size != UINT16_MAX);
504 	/* Allocate double amount of size to avoid immediate realloc */
505 	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2));
506 	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
507 					RTE_CACHE_LINE_SIZE, graph->socket);
508 	RTE_VERIFY(node->objs);
509 	node->size = size;
510 	node->realloc_count++;
511 }
512 
513 static int
514 graph_to_dot(FILE *f, struct graph *graph)
515 {
516 	const char *src_edge_color = " [color=blue]\n";
517 	const char *edge_color = "\n";
518 	struct graph_node *graph_node;
519 	char *node_name;
520 	rte_edge_t i;
521 	int rc;
522 
523 	rc = fprintf(f, "Digraph %s {\n\trankdir=LR;\n", graph->name);
524 	if (rc < 0)
525 		goto end;
526 
527 	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
528 		node_name = graph_node->node->name;
529 		for (i = 0; i < graph_node->node->nb_edges; i++) {
530 			rc = fprintf(f, "\t\"%s\"->\"%s\"%s", node_name,
531 				     graph_node->adjacency_list[i]->node->name,
532 				     graph_node->node->flags & RTE_NODE_SOURCE_F
533 					     ? src_edge_color
534 					     : edge_color);
535 			if (rc < 0)
536 				goto end;
537 		}
538 	}
539 	rc = fprintf(f, "}\n");
540 	if (rc < 0)
541 		goto end;
542 
543 	return 0;
544 end:
545 	rte_errno = EBADF;
546 	return -rte_errno;
547 }
548 
549 int
550 rte_graph_export(const char *name, FILE *f)
551 {
552 	struct graph *graph;
553 	int rc = ENOENT;
554 
555 	STAILQ_FOREACH(graph, &graph_list, next) {
556 		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) {
557 			rc = graph_to_dot(f, graph);
558 			goto end;
559 		}
560 	}
561 end:
562 	return -rc;
563 }
564 
565 static void
566 graph_scan_dump(FILE *f, rte_graph_t id, bool all)
567 {
568 	struct graph *graph;
569 
570 	RTE_VERIFY(f);
571 	GRAPH_ID_CHECK(id);
572 
573 	STAILQ_FOREACH(graph, &graph_list, next) {
574 		if (all == true) {
575 			graph_dump(f, graph);
576 		} else if (graph->id == id) {
577 			graph_dump(f, graph);
578 			return;
579 		}
580 	}
581 fail:
582 	return;
583 }
584 
585 void
586 rte_graph_dump(FILE *f, rte_graph_t id)
587 {
588 	graph_scan_dump(f, id, false);
589 }
590 
591 void
592 rte_graph_list_dump(FILE *f)
593 {
594 	graph_scan_dump(f, 0, true);
595 }
596 
597 rte_graph_t
598 rte_graph_max_count(void)
599 {
600 	return graph_id;
601 }
602 
603 RTE_LOG_REGISTER_DEFAULT(rte_graph_logtype, INFO);
604