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