/* SPDX-License-Identifier: BSD-3-Clause * Copyright(C) 2020 Marvell International Ltd. */ #include #include #include #include #include "graph_private.h" /* Check whether a node has next_node to itself */ static inline int node_has_loop_edge(struct node *node) { rte_edge_t i; char *name; int rc = 0; for (i = 0; i < node->nb_edges; i++) { if (strncmp(node->name, node->next_nodes[i], RTE_NODE_NAMESIZE) == 0) { name = node->name; rc = 1; SET_ERR_JMP(EINVAL, fail, "Node %s has loop to self", name); } } fail: return rc; } int graph_node_has_loop_edge(struct graph *graph) { struct graph_node *graph_node; STAILQ_FOREACH(graph_node, &graph->node_list, next) if (node_has_loop_edge(graph_node->node)) return 1; return 0; } rte_node_t graph_src_nodes_count(struct graph *graph) { struct graph_node *graph_node; rte_node_t rc = 0; STAILQ_FOREACH(graph_node, &graph->node_list, next) if (graph_node->node->flags & RTE_NODE_SOURCE_F) rc++; if (rc == 0) SET_ERR_JMP(EINVAL, fail, "Graph needs at least a source node"); fail: return rc; } /* Check whether a node has next_node to a source node */ int graph_node_has_edge_to_src_node(struct graph *graph) { struct graph_node *graph_node; struct node *node; rte_edge_t i; STAILQ_FOREACH(graph_node, &graph->node_list, next) { for (i = 0; i < graph_node->node->nb_edges; i++) { node = graph_node->adjacency_list[i]->node; if (node->flags & RTE_NODE_SOURCE_F) SET_ERR_JMP( EEXIST, fail, "Node %s points to the source node %s", graph_node->node->name, node->name); } } return 0; fail: return 1; } rte_node_t graph_nodes_count(struct graph *graph) { struct graph_node *graph_node; rte_node_t count = 0; STAILQ_FOREACH(graph_node, &graph->node_list, next) count++; return count; } void graph_mark_nodes_as_not_visited(struct graph *graph) { struct graph_node *graph_node; STAILQ_FOREACH(graph_node, &graph->node_list, next) graph_node->visited = false; } int graph_bfs(struct graph *graph, struct graph_node *start) { struct graph_node **queue, *v, *tmp; uint16_t head = 0, tail = 0; rte_edge_t i; size_t sz; sz = sizeof(struct graph_node *) * graph_nodes_count(graph); queue = malloc(sz); if (queue == NULL) SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu", sz); /* BFS algorithm */ queue[tail++] = start; start->visited = true; while (head != tail) { v = queue[head++]; for (i = 0; i < v->node->nb_edges; i++) { tmp = v->adjacency_list[i]; if (tmp->visited == false) { queue[tail++] = tmp; tmp->visited = true; } } } free(queue); return 0; fail: return -rte_errno; } /* Check whether a node has connected path or parent node */ int graph_has_isolated_node(struct graph *graph) { struct graph_node *graph_node; graph_mark_nodes_as_not_visited(graph); STAILQ_FOREACH(graph_node, &graph->node_list, next) { if (graph_node->node->flags & RTE_NODE_SOURCE_F) { if (graph_node->node->nb_edges == 0) SET_ERR_JMP(EINVAL, fail, "%s node needs minimum one edge", graph_node->node->name); if (graph_bfs(graph, graph_node)) goto fail; } } STAILQ_FOREACH(graph_node, &graph->node_list, next) if (graph_node->visited == false) SET_ERR_JMP(EINVAL, fail, "Found isolated node %s", graph_node->node->name); return 0; fail: return 1; }