summaryrefslogtreecommitdiffstats
path: root/src/spdk/dpdk/lib/librte_graph
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/dpdk/lib/librte_graph
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/dpdk/lib/librte_graph')
-rw-r--r--src/spdk/dpdk/lib/librte_graph/Makefile28
-rw-r--r--src/spdk/dpdk/lib/librte_graph/graph.c587
-rw-r--r--src/spdk/dpdk/lib/librte_graph/graph_debug.c84
-rw-r--r--src/spdk/dpdk/lib/librte_graph/graph_ops.c169
-rw-r--r--src/spdk/dpdk/lib/librte_graph/graph_populate.c234
-rw-r--r--src/spdk/dpdk/lib/librte_graph/graph_private.h347
-rw-r--r--src/spdk/dpdk/lib/librte_graph/graph_stats.c406
-rw-r--r--src/spdk/dpdk/lib/librte_graph/meson.build11
-rw-r--r--src/spdk/dpdk/lib/librte_graph/node.c421
-rw-r--r--src/spdk/dpdk/lib/librte_graph/rte_graph.h668
-rw-r--r--src/spdk/dpdk/lib/librte_graph/rte_graph_version.map47
-rw-r--r--src/spdk/dpdk/lib/librte_graph/rte_graph_worker.h510
12 files changed, 3512 insertions, 0 deletions
diff --git a/src/spdk/dpdk/lib/librte_graph/Makefile b/src/spdk/dpdk/lib/librte_graph/Makefile
new file mode 100644
index 000000000..b66279c67
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_graph.a
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+LDLIBS += -lrte_eal
+
+EXPORT_MAP := rte_graph_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_stats.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph_worker.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/src/spdk/dpdk/lib/librte_graph/graph.c b/src/spdk/dpdk/lib/librte_graph/graph.c
new file mode 100644
index 000000000..0ea83df3d
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/graph.c
@@ -0,0 +1,587 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+#include <rte_spinlock.h>
+#include <rte_string_fns.h>
+
+#include "graph_private.h"
+
+static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list);
+static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+static rte_graph_t graph_id;
+int rte_graph_logtype;
+
+#define GRAPH_ID_CHECK(id) ID_CHECK(id, graph_id)
+
+/* Private functions */
+struct graph_head *
+graph_list_head_get(void)
+{
+ return &graph_list;
+}
+
+void
+graph_spinlock_lock(void)
+{
+ rte_spinlock_lock(&graph_lock);
+}
+
+void
+graph_spinlock_unlock(void)
+{
+ rte_spinlock_unlock(&graph_lock);
+}
+
+static int
+graph_node_add(struct graph *graph, struct node *node)
+{
+ struct graph_node *graph_node;
+ size_t sz;
+
+ /* Skip the duplicate nodes */
+ STAILQ_FOREACH(graph_node, &graph->node_list, next)
+ if (strncmp(node->name, graph_node->node->name,
+ RTE_NODE_NAMESIZE) == 0)
+ return 0;
+
+ /* Allocate new graph node object */
+ sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *);
+ graph_node = calloc(1, sz);
+
+ if (graph_node == NULL)
+ SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object",
+ node->name);
+
+ /* Initialize the graph node */
+ graph_node->node = node;
+
+ /* Add to graph node list */
+ STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next);
+ return 0;
+
+free:
+ free(graph_node);
+ return -rte_errno;
+}
+
+static struct graph_node *
+node_to_graph_node(struct graph *graph, struct node *node)
+{
+ struct graph_node *graph_node;
+
+ STAILQ_FOREACH(graph_node, &graph->node_list, next)
+ if (graph_node->node == node)
+ return graph_node;
+
+ SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name);
+fail:
+ return NULL;
+}
+
+static int
+graph_node_edges_add(struct graph *graph)
+{
+ struct graph_node *graph_node;
+ struct node *adjacency;
+ const char *next;
+ rte_edge_t i;
+
+ STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+ for (i = 0; i < graph_node->node->nb_edges; i++) {
+ next = graph_node->node->next_nodes[i];
+ adjacency = node_from_name(next);
+ if (adjacency == NULL)
+ SET_ERR_JMP(EINVAL, fail,
+ "Node %s not registered", next);
+ if (graph_node_add(graph, adjacency))
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ return -rte_errno;
+}
+
+static int
+graph_adjacency_list_update(struct graph *graph)
+{
+ struct graph_node *graph_node, *tmp;
+ struct node *adjacency;
+ const char *next;
+ rte_edge_t i;
+
+ STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+ for (i = 0; i < graph_node->node->nb_edges; i++) {
+ next = graph_node->node->next_nodes[i];
+ adjacency = node_from_name(next);
+ if (adjacency == NULL)
+ SET_ERR_JMP(EINVAL, fail,
+ "Node %s not registered", next);
+ tmp = node_to_graph_node(graph, adjacency);
+ if (tmp == NULL)
+ goto fail;
+ graph_node->adjacency_list[i] = tmp;
+ }
+ }
+
+ return 0;
+fail:
+ return -rte_errno;
+}
+
+static int
+expand_pattern_to_node(struct graph *graph, const char *pattern)
+{
+ struct node_head *node_head = node_list_head_get();
+ bool found = false;
+ struct node *node;
+
+ /* Check for pattern match */
+ STAILQ_FOREACH(node, node_head, next) {
+ if (fnmatch(pattern, node->name, 0) == 0) {
+ if (graph_node_add(graph, node))
+ goto fail;
+ found = true;
+ }
+ }
+ if (found == false)
+ SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern);
+
+ return 0;
+fail:
+ return -rte_errno;
+}
+
+static void
+graph_cleanup(struct graph *graph)
+{
+ struct graph_node *graph_node;
+
+ while (!STAILQ_EMPTY(&graph->node_list)) {
+ graph_node = STAILQ_FIRST(&graph->node_list);
+ STAILQ_REMOVE_HEAD(&graph->node_list, next);
+ free(graph_node);
+ }
+}
+
+static int
+graph_node_init(struct graph *graph)
+{
+ struct graph_node *graph_node;
+ const char *name;
+ int rc;
+
+ STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+ if (graph_node->node->init) {
+ name = graph_node->node->name;
+ rc = graph_node->node->init(
+ graph->graph,
+ graph_node_name_to_ptr(graph->graph, name));
+ if (rc)
+ SET_ERR_JMP(rc, err, "Node %s init() failed",
+ name);
+ }
+ }
+
+ return 0;
+err:
+ return -rte_errno;
+}
+
+static void
+graph_node_fini(struct graph *graph)
+{
+ struct graph_node *graph_node;
+
+ STAILQ_FOREACH(graph_node, &graph->node_list, next)
+ if (graph_node->node->fini)
+ graph_node->node->fini(
+ graph->graph,
+ graph_node_name_to_ptr(graph->graph,
+ graph_node->node->name));
+}
+
+static struct rte_graph *
+graph_mem_fixup_node_ctx(struct rte_graph *graph)
+{
+ struct rte_node *node;
+ struct node *node_db;
+ rte_graph_off_t off;
+ rte_node_t count;
+ const char *name;
+
+ rte_graph_foreach_node(count, off, graph, node) {
+ if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
+ name = node->name;
+ else /* Cloned node */
+ name = node->parent;
+
+ node_db = node_from_name(name);
+ if (node_db == NULL)
+ SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
+ node->process = node_db->process;
+ }
+
+ return graph;
+fail:
+ return NULL;
+}
+
+static struct rte_graph *
+graph_mem_fixup_secondary(struct rte_graph *graph)
+{
+ if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)
+ return graph;
+
+ return graph_mem_fixup_node_ctx(graph);
+}
+
+struct rte_graph *
+rte_graph_lookup(const char *name)
+{
+ const struct rte_memzone *mz;
+ struct rte_graph *rc = NULL;
+
+ mz = rte_memzone_lookup(name);
+ if (mz)
+ rc = mz->addr;
+
+ return graph_mem_fixup_secondary(rc);
+}
+
+rte_graph_t
+rte_graph_create(const char *name, struct rte_graph_param *prm)
+{
+ rte_node_t src_node_count;
+ struct graph *graph;
+ const char *pattern;
+ uint16_t i;
+
+ graph_spinlock_lock();
+
+ /* Check arguments sanity */
+ if (prm == NULL)
+ SET_ERR_JMP(EINVAL, fail, "Param should not be NULL");
+
+ if (name == NULL)
+ SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL");
+
+ /* Check for existence of duplicate graph */
+ STAILQ_FOREACH(graph, &graph_list, next)
+ if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)
+ SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s",
+ name);
+
+ /* Create graph object */
+ graph = calloc(1, sizeof(*graph));
+ if (graph == NULL)
+ SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object");
+
+ /* Initialize the graph object */
+ STAILQ_INIT(&graph->node_list);
+ if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)
+ SET_ERR_JMP(E2BIG, free, "Too big name=%s", name);
+
+ /* Expand node pattern and add the nodes to the graph */
+ for (i = 0; i < prm->nb_node_patterns; i++) {
+ pattern = prm->node_patterns[i];
+ if (expand_pattern_to_node(graph, pattern))
+ goto graph_cleanup;
+ }
+
+ /* Go over all the nodes edges and add them to the graph */
+ if (graph_node_edges_add(graph))
+ goto graph_cleanup;
+
+ /* Update adjacency list of all nodes in the graph */
+ if (graph_adjacency_list_update(graph))
+ goto graph_cleanup;
+
+ /* Make sure at least a source node present in the graph */
+ src_node_count = graph_src_nodes_count(graph);
+ if (src_node_count == 0)
+ goto graph_cleanup;
+
+ /* Make sure no node is pointing to source node */
+ if (graph_node_has_edge_to_src_node(graph))
+ goto graph_cleanup;
+
+ /* Don't allow node has loop to self */
+ if (graph_node_has_loop_edge(graph))
+ goto graph_cleanup;
+
+ /* Do BFS from src nodes on the graph to find isolated nodes */
+ if (graph_has_isolated_node(graph))
+ goto graph_cleanup;
+
+ /* Initialize graph object */
+ graph->socket = prm->socket_id;
+ graph->src_node_count = src_node_count;
+ graph->node_count = graph_nodes_count(graph);
+ graph->id = graph_id;
+
+ /* Allocate the Graph fast path memory and populate the data */
+ if (graph_fp_mem_create(graph))
+ goto graph_cleanup;
+
+ /* Call init() of the all the nodes in the graph */
+ if (graph_node_init(graph))
+ goto graph_mem_destroy;
+
+ /* All good, Lets add the graph to the list */
+ graph_id++;
+ STAILQ_INSERT_TAIL(&graph_list, graph, next);
+
+ graph_spinlock_unlock();
+ return graph->id;
+
+graph_mem_destroy:
+ graph_fp_mem_destroy(graph);
+graph_cleanup:
+ graph_cleanup(graph);
+free:
+ free(graph);
+fail:
+ graph_spinlock_unlock();
+ return RTE_GRAPH_ID_INVALID;
+}
+
+int
+rte_graph_destroy(rte_graph_t id)
+{
+ struct graph *graph, *tmp;
+ int rc = -ENOENT;
+
+ graph_spinlock_lock();
+
+ graph = STAILQ_FIRST(&graph_list);
+ while (graph != NULL) {
+ tmp = STAILQ_NEXT(graph, next);
+ if (graph->id == id) {
+ /* Call fini() of the all the nodes in the graph */
+ graph_node_fini(graph);
+ /* Destroy graph fast path memory */
+ rc = graph_fp_mem_destroy(graph);
+ if (rc)
+ SET_ERR_JMP(rc, done, "Graph %s destroy failed",
+ graph->name);
+
+ graph_cleanup(graph);
+ STAILQ_REMOVE(&graph_list, graph, graph, next);
+ free(graph);
+ graph_id--;
+ goto done;
+ }
+ graph = tmp;
+ }
+done:
+ graph_spinlock_unlock();
+ return rc;
+}
+
+rte_graph_t
+rte_graph_from_name(const char *name)
+{
+ struct graph *graph;
+
+ STAILQ_FOREACH(graph, &graph_list, next)
+ if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0)
+ return graph->id;
+
+ return RTE_GRAPH_ID_INVALID;
+}
+
+char *
+rte_graph_id_to_name(rte_graph_t id)
+{
+ struct graph *graph;
+
+ GRAPH_ID_CHECK(id);
+ STAILQ_FOREACH(graph, &graph_list, next)
+ if (graph->id == id)
+ return graph->name;
+
+fail:
+ return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get(rte_graph_t gid, uint32_t nid)
+{
+ struct rte_node *node;
+ struct graph *graph;
+ rte_graph_off_t off;
+ rte_node_t count;
+
+ GRAPH_ID_CHECK(gid);
+ STAILQ_FOREACH(graph, &graph_list, next)
+ if (graph->id == gid) {
+ rte_graph_foreach_node(count, off, graph->graph,
+ node) {
+ if (node->id == nid)
+ return node;
+ }
+ break;
+ }
+fail:
+ return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get_by_name(const char *graph_name, const char *node_name)
+{
+ struct rte_node *node;
+ struct graph *graph;
+ rte_graph_off_t off;
+ rte_node_t count;
+
+ STAILQ_FOREACH(graph, &graph_list, next)
+ if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) {
+ rte_graph_foreach_node(count, off, graph->graph,
+ node) {
+ if (!strncmp(node->name, node_name,
+ RTE_NODE_NAMESIZE))
+ return node;
+ }
+ break;
+ }
+
+ return NULL;
+}
+
+void __rte_noinline
+__rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
+{
+ uint16_t size = node->size;
+
+ RTE_VERIFY(size != UINT16_MAX);
+ /* Allocate double amount of size to avoid immediate realloc */
+ size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2));
+ node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+ RTE_CACHE_LINE_SIZE, graph->socket);
+ RTE_VERIFY(node->objs);
+ node->size = size;
+ node->realloc_count++;
+}
+
+void __rte_noinline
+__rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node,
+ uint16_t req_size)
+{
+ uint16_t size = node->size;
+
+ RTE_VERIFY(size != UINT16_MAX);
+ /* Allocate double amount of size to avoid immediate realloc */
+ size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2));
+ node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+ RTE_CACHE_LINE_SIZE, graph->socket);
+ RTE_VERIFY(node->objs);
+ node->size = size;
+ node->realloc_count++;
+}
+
+static int
+graph_to_dot(FILE *f, struct graph *graph)
+{
+ const char *src_edge_color = " [color=blue]\n";
+ const char *edge_color = "\n";
+ struct graph_node *graph_node;
+ char *node_name;
+ rte_edge_t i;
+ int rc;
+
+ rc = fprintf(f, "Digraph %s {\n\trankdir=LR;\n", graph->name);
+ if (rc < 0)
+ goto end;
+
+ STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+ node_name = graph_node->node->name;
+ for (i = 0; i < graph_node->node->nb_edges; i++) {
+ rc = fprintf(f, "\t\"%s\"->\"%s\"%s", node_name,
+ graph_node->adjacency_list[i]->node->name,
+ graph_node->node->flags & RTE_NODE_SOURCE_F
+ ? src_edge_color
+ : edge_color);
+ if (rc < 0)
+ goto end;
+ }
+ }
+ rc = fprintf(f, "}\n");
+ if (rc < 0)
+ goto end;
+
+ return 0;
+end:
+ rte_errno = EBADF;
+ return -rte_errno;
+}
+
+int
+rte_graph_export(const char *name, FILE *f)
+{
+ struct graph *graph;
+ int rc = ENOENT;
+
+ STAILQ_FOREACH(graph, &graph_list, next) {
+ if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) {
+ rc = graph_to_dot(f, graph);
+ goto end;
+ }
+ }
+end:
+ return -rc;
+}
+
+static void
+graph_scan_dump(FILE *f, rte_graph_t id, bool all)
+{
+ struct graph *graph;
+
+ RTE_VERIFY(f);
+ GRAPH_ID_CHECK(id);
+
+ STAILQ_FOREACH(graph, &graph_list, next) {
+ if (all == true) {
+ graph_dump(f, graph);
+ } else if (graph->id == id) {
+ graph_dump(f, graph);
+ return;
+ }
+ }
+fail:
+ return;
+}
+
+void
+rte_graph_dump(FILE *f, rte_graph_t id)
+{
+ graph_scan_dump(f, id, false);
+}
+
+void
+rte_graph_list_dump(FILE *f)
+{
+ graph_scan_dump(f, 0, true);
+}
+
+rte_graph_t
+rte_graph_max_count(void)
+{
+ return graph_id;
+}
+
+RTE_INIT(rte_graph_init_log)
+{
+ rte_graph_logtype = rte_log_register("lib.graph");
+ if (rte_graph_logtype >= 0)
+ rte_log_set_level(rte_graph_logtype, RTE_LOG_INFO);
+}
diff --git a/src/spdk/dpdk/lib/librte_graph/graph_debug.c b/src/spdk/dpdk/lib/librte_graph/graph_debug.c
new file mode 100644
index 000000000..f8aea16ac
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/graph_debug.c
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_debug.h>
+
+#include "graph_private.h"
+
+void
+graph_dump(FILE *f, struct graph *g)
+{
+ struct graph_node *graph_node;
+ rte_edge_t i = 0;
+
+ fprintf(f, "graph <%s>\n", g->name);
+ fprintf(f, " id=%" PRIu32 "\n", g->id);
+ fprintf(f, " cir_start=%" PRIu32 "\n", g->cir_start);
+ fprintf(f, " cir_mask=%" PRIu32 "\n", g->cir_mask);
+ fprintf(f, " addr=%p\n", g);
+ fprintf(f, " graph=%p\n", g->graph);
+ fprintf(f, " mem_sz=%zu\n", g->mem_sz);
+ fprintf(f, " node_count=%" PRIu32 "\n", g->node_count);
+ fprintf(f, " src_node_count=%" PRIu32 "\n", g->src_node_count);
+
+ STAILQ_FOREACH(graph_node, &g->node_list, next)
+ fprintf(f, " node[%d] <%s>\n", i++, graph_node->node->name);
+}
+
+void
+node_dump(FILE *f, struct node *n)
+{
+ rte_edge_t i;
+
+ fprintf(f, "node <%s>\n", n->name);
+ fprintf(f, " id=%" PRIu32 "\n", n->id);
+ fprintf(f, " flags=0x%" PRIx64 "\n", n->flags);
+ fprintf(f, " addr=%p\n", n);
+ fprintf(f, " process=%p\n", n->process);
+ fprintf(f, " nb_edges=%d\n", n->nb_edges);
+
+ for (i = 0; i < n->nb_edges; i++)
+ fprintf(f, " edge[%d] <%s>\n", i, n->next_nodes[i]);
+}
+
+void
+rte_graph_obj_dump(FILE *f, struct rte_graph *g, bool all)
+{
+ rte_node_t count;
+ rte_graph_off_t off;
+ struct rte_node *n;
+ rte_edge_t i;
+
+ fprintf(f, "graph <%s> @ %p\n", g->name, g);
+ fprintf(f, " id=%" PRIu32 "\n", g->id);
+ fprintf(f, " head=%" PRId32 "\n", (int32_t)g->head);
+ fprintf(f, " tail=%" PRId32 "\n", (int32_t)g->tail);
+ fprintf(f, " cir_mask=0x%" PRIx32 "\n", g->cir_mask);
+ fprintf(f, " nb_nodes=%" PRId32 "\n", g->nb_nodes);
+ fprintf(f, " socket=%d\n", g->socket);
+ fprintf(f, " fence=0x%" PRIx64 "\n", g->fence);
+ fprintf(f, " nodes_start=0x%" PRIx32 "\n", g->nodes_start);
+ fprintf(f, " cir_start=%p\n", g->cir_start);
+
+ rte_graph_foreach_node(count, off, g, n) {
+ if (!all && n->idx == 0)
+ continue;
+ fprintf(f, " node[%d] <%s>\n", count, n->name);
+ fprintf(f, " fence=0x%" PRIx64 "\n", n->fence);
+ fprintf(f, " objs=%p\n", n->objs);
+ fprintf(f, " process=%p\n", n->process);
+ fprintf(f, " id=0x%" PRIx32 "\n", n->id);
+ fprintf(f, " offset=0x%" PRIx32 "\n", n->off);
+ fprintf(f, " nb_edges=%" PRId32 "\n", n->nb_edges);
+ fprintf(f, " realloc_count=%d\n", n->realloc_count);
+ fprintf(f, " size=%d\n", n->size);
+ fprintf(f, " idx=%d\n", n->idx);
+ fprintf(f, " total_objs=%" PRId64 "\n", n->total_objs);
+ fprintf(f, " total_calls=%" PRId64 "\n", n->total_calls);
+ for (i = 0; i < n->nb_edges; i++)
+ fprintf(f, " edge[%d] <%s>\n", i,
+ n->nodes[i]->name);
+ }
+}
diff --git a/src/spdk/dpdk/lib/librte_graph/graph_ops.c b/src/spdk/dpdk/lib/librte_graph/graph_ops.c
new file mode 100644
index 000000000..335595311
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/graph_ops.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+
+#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;
+}
diff --git a/src/spdk/dpdk/lib/librte_graph/graph_populate.c b/src/spdk/dpdk/lib/librte_graph/graph_populate.c
new file mode 100644
index 000000000..093512efa
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/graph_populate.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+
+#include "graph_private.h"
+
+static size_t
+graph_fp_mem_calc_size(struct graph *graph)
+{
+ struct graph_node *graph_node;
+ rte_node_t val;
+ size_t sz;
+
+ /* Graph header */
+ sz = sizeof(struct rte_graph);
+ /* Source nodes list */
+ sz += sizeof(rte_graph_off_t) * graph->src_node_count;
+ /* Circular buffer for pending streams of size number of nodes */
+ val = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));
+ sz = RTE_ALIGN(sz, val);
+ graph->cir_start = sz;
+ graph->cir_mask = rte_align32pow2(graph->node_count) - 1;
+ sz += val;
+ /* Fence */
+ sz += sizeof(RTE_GRAPH_FENCE);
+ sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+ graph->nodes_start = sz;
+ /* For 0..N node objects with fence */
+ STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+ sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+ sz += sizeof(struct rte_node);
+ /* Pointer to next nodes(edges) */
+ sz += sizeof(struct rte_node *) * graph_node->node->nb_edges;
+ }
+
+ graph->mem_sz = sz;
+ return sz;
+}
+
+static void
+graph_header_popluate(struct graph *_graph)
+{
+ struct rte_graph *graph = _graph->graph;
+
+ graph->tail = 0;
+ graph->head = (int32_t)-_graph->src_node_count;
+ graph->cir_mask = _graph->cir_mask;
+ graph->nb_nodes = _graph->node_count;
+ graph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);
+ graph->nodes_start = _graph->nodes_start;
+ graph->socket = _graph->socket;
+ graph->id = _graph->id;
+ memcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);
+ graph->fence = RTE_GRAPH_FENCE;
+}
+
+static void
+graph_nodes_populate(struct graph *_graph)
+{
+ rte_graph_off_t off = _graph->nodes_start;
+ struct rte_graph *graph = _graph->graph;
+ struct graph_node *graph_node;
+ rte_edge_t count, nb_edges;
+ const char *parent;
+ rte_node_t pid;
+
+ STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+ struct rte_node *node = RTE_PTR_ADD(graph, off);
+ memset(node, 0, sizeof(*node));
+ node->fence = RTE_GRAPH_FENCE;
+ node->off = off;
+ node->process = graph_node->node->process;
+ memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
+ pid = graph_node->node->parent_id;
+ if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
+ parent = rte_node_id_to_name(pid);
+ memcpy(node->parent, parent, RTE_GRAPH_NAMESIZE);
+ }
+ node->id = graph_node->node->id;
+ node->parent_id = pid;
+ nb_edges = graph_node->node->nb_edges;
+ node->nb_edges = nb_edges;
+ off += sizeof(struct rte_node);
+ /* Copy the name in first pass to replace with rte_node* later*/
+ for (count = 0; count < nb_edges; count++)
+ node->nodes[count] = (struct rte_node *)&graph_node
+ ->adjacency_list[count]
+ ->node->name[0];
+
+ off += sizeof(struct rte_node *) * nb_edges;
+ off = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);
+ node->next = off;
+ __rte_node_stream_alloc(graph, node);
+ }
+}
+
+struct rte_node *
+graph_node_id_to_ptr(const struct rte_graph *graph, rte_node_t id)
+{
+ rte_node_t count;
+ rte_graph_off_t off;
+ struct rte_node *node;
+
+ rte_graph_foreach_node(count, off, graph, node)
+ if (unlikely(node->id == id))
+ return node;
+
+ return NULL;
+}
+
+struct rte_node *
+graph_node_name_to_ptr(const struct rte_graph *graph, const char *name)
+{
+ rte_node_t count;
+ rte_graph_off_t off;
+ struct rte_node *node;
+
+ rte_graph_foreach_node(count, off, graph, node)
+ if (strncmp(name, node->name, RTE_NODE_NAMESIZE) == 0)
+ return node;
+
+ return NULL;
+}
+
+static int
+graph_node_nexts_populate(struct graph *_graph)
+{
+ rte_node_t count, val;
+ rte_graph_off_t off;
+ struct rte_node *node;
+ const struct rte_graph *graph = _graph->graph;
+ const char *name;
+
+ rte_graph_foreach_node(count, off, graph, node) {
+ for (val = 0; val < node->nb_edges; val++) {
+ name = (const char *)node->nodes[val];
+ node->nodes[val] = graph_node_name_to_ptr(graph, name);
+ if (node->nodes[val] == NULL)
+ SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+ }
+ }
+
+ return 0;
+fail:
+ return -rte_errno;
+}
+
+static int
+graph_src_nodes_populate(struct graph *_graph)
+{
+ struct rte_graph *graph = _graph->graph;
+ struct graph_node *graph_node;
+ struct rte_node *node;
+ int32_t head = -1;
+ const char *name;
+
+ STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+ if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+ name = graph_node->node->name;
+ node = graph_node_name_to_ptr(graph, name);
+ if (node == NULL)
+ SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+
+ __rte_node_stream_alloc(graph, node);
+ graph->cir_start[head--] = node->off;
+ }
+ }
+
+ return 0;
+fail:
+ return -rte_errno;
+}
+
+static int
+graph_fp_mem_populate(struct graph *graph)
+{
+ int rc;
+
+ graph_header_popluate(graph);
+ graph_nodes_populate(graph);
+ rc = graph_node_nexts_populate(graph);
+ rc |= graph_src_nodes_populate(graph);
+
+ return rc;
+}
+
+int
+graph_fp_mem_create(struct graph *graph)
+{
+ const struct rte_memzone *mz;
+ size_t sz;
+
+ sz = graph_fp_mem_calc_size(graph);
+ mz = rte_memzone_reserve(graph->name, sz, graph->socket, 0);
+ if (mz == NULL)
+ SET_ERR_JMP(ENOMEM, fail, "Memzone %s reserve failed",
+ graph->name);
+
+ graph->graph = mz->addr;
+ graph->mz = mz;
+
+ return graph_fp_mem_populate(graph);
+fail:
+ return -rte_errno;
+}
+
+static void
+graph_nodes_mem_destroy(struct rte_graph *graph)
+{
+ rte_node_t count;
+ rte_graph_off_t off;
+ struct rte_node *node;
+
+ if (graph == NULL)
+ return;
+
+ rte_graph_foreach_node(count, off, graph, node)
+ rte_free(node->objs);
+}
+
+int
+graph_fp_mem_destroy(struct graph *graph)
+{
+ graph_nodes_mem_destroy(graph->graph);
+ return rte_memzone_free(graph->mz);
+}
diff --git a/src/spdk/dpdk/lib/librte_graph/graph_private.h b/src/spdk/dpdk/lib/librte_graph/graph_private.h
new file mode 100644
index 000000000..f9a85c892
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/graph_private.h
@@ -0,0 +1,347 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_PRIVATE_H_
+#define _RTE_GRAPH_PRIVATE_H_
+
+#include <inttypes.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_eal.h>
+
+#include "rte_graph.h"
+#include "rte_graph_worker.h"
+
+extern int rte_graph_logtype;
+
+#define GRAPH_LOG(level, ...) \
+ rte_log(RTE_LOG_##level, rte_graph_logtype, \
+ RTE_FMT("GRAPH: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n", \
+ __func__, __LINE__, RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define graph_err(...) GRAPH_LOG(ERR, __VA_ARGS__)
+#define graph_info(...) GRAPH_LOG(INFO, __VA_ARGS__)
+#define graph_dbg(...) GRAPH_LOG(DEBUG, __VA_ARGS__)
+
+#define ID_CHECK(id, id_max) \
+ do { \
+ if ((id) >= (id_max)) { \
+ rte_errno = EINVAL; \
+ goto fail; \
+ } \
+ } while (0)
+
+#define SET_ERR_JMP(err, where, fmt, ...) \
+ do { \
+ graph_err(fmt, ##__VA_ARGS__); \
+ rte_errno = err; \
+ goto where; \
+ } while (0)
+
+/**
+ * @internal
+ *
+ * Structure that holds node internal data.
+ */
+struct node {
+ STAILQ_ENTRY(node) next; /**< Next node in the list. */
+ char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+ uint64_t flags; /**< Node configuration flag. */
+ rte_node_process_t process; /**< Node process function. */
+ rte_node_init_t init; /**< Node init function. */
+ rte_node_fini_t fini; /**< Node fini function. */
+ rte_node_t id; /**< Allocated identifier for the node. */
+ rte_node_t parent_id; /**< Parent node identifier. */
+ rte_edge_t nb_edges; /**< Number of edges from this node. */
+ char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
+};
+
+/**
+ * @internal
+ *
+ * Structure that holds the graph node data.
+ */
+struct graph_node {
+ STAILQ_ENTRY(graph_node) next; /**< Next graph node in the list. */
+ struct node *node; /**< Pointer to internal node. */
+ bool visited; /**< Flag used in BFS to mark node visited. */
+ struct graph_node *adjacency_list[]; /**< Adjacency list of the node. */
+};
+
+/**
+ * @internal
+ *
+ * Structure that holds graph internal data.
+ */
+struct graph {
+ STAILQ_ENTRY(graph) next;
+ /**< List of graphs. */
+ char name[RTE_GRAPH_NAMESIZE];
+ /**< Name of the graph. */
+ const struct rte_memzone *mz;
+ /**< Memzone to store graph data. */
+ rte_graph_off_t nodes_start;
+ /**< Node memory start offset in graph reel. */
+ rte_node_t src_node_count;
+ /**< Number of source nodes in a graph. */
+ struct rte_graph *graph;
+ /**< Pointer to graph data. */
+ rte_node_t node_count;
+ /**< Total number of nodes. */
+ uint32_t cir_start;
+ /**< Circular buffer start offset in graph reel. */
+ uint32_t cir_mask;
+ /**< Circular buffer mask for wrap around. */
+ rte_graph_t id;
+ /**< Graph identifier. */
+ size_t mem_sz;
+ /**< Memory size of the graph. */
+ int socket;
+ /**< Socket identifier where memory is allocated. */
+ STAILQ_HEAD(gnode_list, graph_node) node_list;
+ /**< Nodes in a graph. */
+};
+
+/* Node functions */
+STAILQ_HEAD(node_head, node);
+
+/**
+ * @internal
+ *
+ * Get the head of the node list.
+ *
+ * @return
+ * Pointer to the node head.
+ */
+struct node_head *node_list_head_get(void);
+
+/**
+ * @internal
+ *
+ * Get node pointer from node name.
+ *
+ * @param name
+ * Pointer to character string containing the node name.
+ *
+ * @return
+ * Pointer to the node.
+ */
+struct node *node_from_name(const char *name);
+
+/* Graph list functions */
+STAILQ_HEAD(graph_head, graph);
+
+/**
+ * @internal
+ *
+ * Get the head of the graph list.
+ *
+ * @return
+ * Pointer to the graph head.
+ */
+struct graph_head *graph_list_head_get(void);
+
+/* Lock functions */
+/**
+ * @internal
+ *
+ * Take a lock on the graph internal spin lock.
+ */
+void graph_spinlock_lock(void);
+
+/**
+ * @internal
+ *
+ * Release a lock on the graph internal spin lock.
+ */
+void graph_spinlock_unlock(void);
+
+/* Graph operations */
+/**
+ * @internal
+ *
+ * Run a BFS(Breadth First Search) on the graph marking all
+ * the graph nodes as visited.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ * @param start
+ * Pointer to the starting graph node.
+ *
+ * @return
+ * - 0: Success.
+ * - -ENOMEM: Not enough memory for BFS.
+ */
+int graph_bfs(struct graph *graph, struct graph_node *start);
+
+/**
+ * @internal
+ *
+ * Check if there is an isolated node in the given graph.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ *
+ * @return
+ * - 0: No isolated node found.
+ * - 1: Isolated node found.
+ */
+int graph_has_isolated_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Check whether a node in the graph has next_node to a source node.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ *
+ * @return
+ * - 0: Node has an edge to source node.
+ * - 1: Node doesn't have an edge to source node.
+ */
+int graph_node_has_edge_to_src_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Checks whether node in the graph has a edge to itself i.e. forms a
+ * loop.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ *
+ * @return
+ * - 0: Node has an edge to itself.
+ * - 1: Node doesn't have an edge to itself.
+ */
+int graph_node_has_loop_edge(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of source nodes in the graph.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ *
+ * @return
+ * Number of source nodes.
+ */
+rte_node_t graph_src_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of total number of nodes in the graph.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ *
+ * @return
+ * Number of nodes.
+ */
+rte_node_t graph_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Clear the visited flag of all the nodes in the graph.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ */
+void graph_mark_nodes_as_not_visited(struct graph *graph);
+
+/* Fast path graph memory populate unctions */
+
+/**
+ * @internal
+ *
+ * Create fast-path memory for the graph and nodes.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ *
+ * @return
+ * - 0: Success.
+ * - -ENOMEM: Not enough for graph and nodes.
+ * - -EINVAL: Graph nodes not found.
+ */
+int graph_fp_mem_create(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Free fast-path memory used by graph and nodes.
+ *
+ * @param graph
+ * Pointer to the internal graph object.
+ *
+ * @return
+ * - 0: Success.
+ * - <0: Graph memzone related error.
+ */
+int graph_fp_mem_destroy(struct graph *graph);
+
+/* Lookup functions */
+/**
+ * @internal
+ *
+ * Get graph node object from node id.
+ *
+ * @param graph
+ * Pointer to rte_graph object.
+ * @param id
+ * Node Identifier.
+ *
+ * @return
+ * Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
+ rte_node_t id);
+
+/**
+ * @internal
+ *
+ * Get graph node object from node name.
+ *
+ * @param graph
+ * Pointer to rte_graph object.
+ * @param node_name
+ * Pointer to character string holding the node name.
+ *
+ * @return
+ * Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
+ const char *node_name);
+
+/* Debug functions */
+/**
+ * @internal
+ *
+ * Dump internal graph object data.
+ *
+ * @param f
+ * FILE pointer to dump the data.
+ * @param g
+ * Pointer to the internal graph object.
+ */
+void graph_dump(FILE *f, struct graph *g);
+
+/**
+ * @internal
+ *
+ * Dump internal node object data.
+ *
+ * @param f
+ * FILE pointer to dump the info.
+ * @param g
+ * Pointer to the internal node object.
+ */
+void node_dump(FILE *f, struct node *n);
+
+#endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/src/spdk/dpdk/lib/librte_graph/graph_stats.c b/src/spdk/dpdk/lib/librte_graph/graph_stats.c
new file mode 100644
index 000000000..125e08d73
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/graph_stats.c
@@ -0,0 +1,406 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+
+#include "graph_private.h"
+
+/* Capture all graphs of cluster */
+struct cluster {
+ rte_graph_t nb_graphs;
+ rte_graph_t size;
+
+ struct graph **graphs;
+};
+
+/* Capture same node ID across cluster */
+struct cluster_node {
+ struct rte_graph_cluster_node_stats stat;
+ rte_node_t nb_nodes;
+
+ struct rte_node *nodes[];
+};
+
+struct rte_graph_cluster_stats {
+ /* Header */
+ rte_graph_cluster_stats_cb_t fn;
+ uint32_t cluster_node_size; /* Size of struct cluster_node */
+ rte_node_t max_nodes;
+ int socket_id;
+ void *cookie;
+ size_t sz;
+
+ struct cluster_node clusters[];
+} __rte_cache_aligned;
+
+#define boarder() \
+ fprintf(f, "+-------------------------------+---------------+--------" \
+ "-------+---------------+---------------+---------------+-" \
+ "----------+\n")
+
+static inline void
+print_banner(FILE *f)
+{
+ boarder();
+ fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
+ "|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
+ "|cycles/call|");
+ boarder();
+}
+
+static inline void
+print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
+{
+ double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
+ const uint64_t prev_calls = stat->prev_calls;
+ const uint64_t prev_objs = stat->prev_objs;
+ const uint64_t cycles = stat->cycles;
+ const uint64_t calls = stat->calls;
+ const uint64_t objs = stat->objs;
+ uint64_t call_delta;
+
+ call_delta = calls - prev_calls;
+ objs_per_call =
+ call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
+ cycles_per_call =
+ call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
+ : 0;
+ ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
+ objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
+ objs_per_sec /= 1000000;
+
+ fprintf(f,
+ "|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
+ "|%-15.3f|%-15.6f|%-11.4f|\n",
+ stat->name, calls, objs, stat->realloc_count, objs_per_call,
+ objs_per_sec, cycles_per_call);
+}
+
+static int
+graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
+ const struct rte_graph_cluster_node_stats *stat)
+{
+ FILE *f = cookie;
+
+ if (unlikely(is_first))
+ print_banner(f);
+ if (stat->objs)
+ print_node(f, stat);
+ if (unlikely(is_last))
+ boarder();
+
+ return 0;
+};
+
+static struct rte_graph_cluster_stats *
+stats_mem_init(struct cluster *cluster,
+ const struct rte_graph_cluster_stats_param *prm)
+{
+ size_t sz = sizeof(struct rte_graph_cluster_stats);
+ struct rte_graph_cluster_stats *stats;
+ rte_graph_cluster_stats_cb_t fn;
+ int socket_id = prm->socket_id;
+ uint32_t cluster_node_size;
+
+ /* Fix up callback */
+ fn = prm->fn;
+ if (fn == NULL)
+ fn = graph_cluster_stats_cb;
+
+ cluster_node_size = sizeof(struct cluster_node);
+ /* For a given cluster, max nodes will be the max number of graphs */
+ cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
+ cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
+
+ stats = realloc(NULL, sz);
+ memset(stats, 0, sz);
+ if (stats) {
+ stats->fn = fn;
+ stats->cluster_node_size = cluster_node_size;
+ stats->max_nodes = 0;
+ stats->socket_id = socket_id;
+ stats->cookie = prm->cookie;
+ stats->sz = sz;
+ }
+
+ return stats;
+}
+
+static int
+stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
+ struct rte_graph *graph, struct graph_node *graph_node)
+{
+ struct rte_graph_cluster_stats *stats = *stats_in;
+ rte_node_t id = graph_node->node->id;
+ struct cluster_node *cluster;
+ struct rte_node *node;
+ rte_node_t count;
+
+ cluster = stats->clusters;
+
+ /* Iterate over cluster node array to find node ID match */
+ for (count = 0; count < stats->max_nodes; count++) {
+ /* Found an existing node in the reel */
+ if (cluster->stat.id == id) {
+ node = graph_node_id_to_ptr(graph, id);
+ if (node == NULL)
+ SET_ERR_JMP(
+ ENOENT, err,
+ "Failed to find node %s in graph %s",
+ graph_node->node->name, graph->name);
+
+ cluster->nodes[cluster->nb_nodes++] = node;
+ return 0;
+ }
+ cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
+ }
+
+ /* Hey, it is a new node, allocate space for it in the reel */
+ stats = realloc(stats, stats->sz + stats->cluster_node_size);
+ if (stats == NULL)
+ SET_ERR_JMP(ENOMEM, err, "Realloc failed");
+
+ /* Clear the new struct cluster_node area */
+ cluster = RTE_PTR_ADD(stats, stats->sz),
+ memset(cluster, 0, stats->cluster_node_size);
+ memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
+ cluster->stat.id = graph_node->node->id;
+ cluster->stat.hz = rte_get_timer_hz();
+ node = graph_node_id_to_ptr(graph, id);
+ if (node == NULL)
+ SET_ERR_JMP(ENOENT, err, "Failed to find node %s in graph %s",
+ graph_node->node->name, graph->name);
+ cluster->nodes[cluster->nb_nodes++] = node;
+
+ stats->sz += stats->cluster_node_size;
+ stats->max_nodes++;
+ *stats_in = stats;
+
+ return 0;
+err:
+ return -rte_errno;
+}
+
+static void
+stats_mem_fini(struct rte_graph_cluster_stats *stats)
+{
+ free(stats);
+}
+
+static void
+cluster_init(struct cluster *cluster)
+{
+ memset(cluster, 0, sizeof(*cluster));
+}
+
+static int
+cluster_add(struct cluster *cluster, struct graph *graph)
+{
+ rte_graph_t count;
+ size_t sz;
+
+ /* Skip the if graph is already added to cluster */
+ for (count = 0; count < cluster->nb_graphs; count++)
+ if (cluster->graphs[count] == graph)
+ return 0;
+
+ /* Expand the cluster if required to store graph objects */
+ if (cluster->nb_graphs + 1 > cluster->size) {
+ cluster->size = RTE_MAX(1, cluster->size * 2);
+ sz = sizeof(struct graph *) * cluster->size;
+ cluster->graphs = realloc(cluster->graphs, sz);
+ if (cluster->graphs == NULL)
+ SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
+ }
+
+ /* Add graph to cluster */
+ cluster->graphs[cluster->nb_graphs++] = graph;
+ return 0;
+
+free:
+ return -rte_errno;
+}
+
+static void
+cluster_fini(struct cluster *cluster)
+{
+ if (cluster->graphs)
+ free(cluster->graphs);
+}
+
+static int
+expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)
+{
+ struct graph_head *graph_head = graph_list_head_get();
+ struct graph *graph;
+ bool found = false;
+
+ /* Check for pattern match */
+ STAILQ_FOREACH(graph, graph_head, next) {
+ if (fnmatch(pattern, graph->name, 0) == 0) {
+ if (cluster_add(cluster, graph))
+ goto fail;
+ found = true;
+ }
+ }
+ if (found == false)
+ SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found",
+ pattern);
+
+ return 0;
+fail:
+ return -rte_errno;
+}
+
+struct rte_graph_cluster_stats *
+rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)
+{
+ struct rte_graph_cluster_stats *stats, *rc = NULL;
+ struct graph_node *graph_node;
+ struct cluster cluster;
+ struct graph *graph;
+ const char *pattern;
+ rte_graph_t i;
+
+ /* Sanity checks */
+ if (!rte_graph_has_stats_feature())
+ SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled");
+
+ if (prm == NULL)
+ SET_ERR_JMP(EINVAL, fail, "Invalid param");
+
+ if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)
+ SET_ERR_JMP(EINVAL, fail, "Invalid graph param");
+
+ cluster_init(&cluster);
+
+ graph_spinlock_lock();
+ /* Expand graph pattern and add the graph to the cluster */
+ for (i = 0; i < prm->nb_graph_patterns; i++) {
+ pattern = prm->graph_patterns[i];
+ if (expand_pattern_to_cluster(&cluster, pattern))
+ goto bad_pattern;
+ }
+
+ /* Alloc the stats memory */
+ stats = stats_mem_init(&cluster, prm);
+ if (stats == NULL)
+ SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory");
+
+ /* Iterate over M(Graph) x N (Nodes in graph) */
+ for (i = 0; i < cluster.nb_graphs; i++) {
+ graph = cluster.graphs[i];
+ STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+ struct rte_graph *graph_fp = graph->graph;
+ if (stats_mem_populate(&stats, graph_fp, graph_node))
+ goto realloc_fail;
+ }
+ }
+
+ /* Finally copy to hugepage memory to avoid pressure on rte_realloc */
+ rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);
+ if (rc)
+ rte_memcpy(rc, stats, stats->sz);
+ else
+ SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed");
+
+realloc_fail:
+ stats_mem_fini(stats);
+bad_pattern:
+ graph_spinlock_unlock();
+ cluster_fini(&cluster);
+fail:
+ return rc;
+}
+
+void
+rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)
+{
+ return rte_free(stat);
+}
+
+static inline void
+cluster_node_arregate_stats(struct cluster_node *cluster)
+{
+ uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;
+ struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+ struct rte_node *node;
+ rte_node_t count;
+
+ for (count = 0; count < cluster->nb_nodes; count++) {
+ node = cluster->nodes[count];
+
+ calls += node->total_calls;
+ objs += node->total_objs;
+ cycles += node->total_cycles;
+ realloc_count += node->realloc_count;
+ }
+
+ stat->calls = calls;
+ stat->objs = objs;
+ stat->cycles = cycles;
+ stat->ts = rte_get_timer_cycles();
+ stat->realloc_count = realloc_count;
+}
+
+static inline void
+cluster_node_store_prev_stats(struct cluster_node *cluster)
+{
+ struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+
+ stat->prev_ts = stat->ts;
+ stat->prev_calls = stat->calls;
+ stat->prev_objs = stat->objs;
+ stat->prev_cycles = stat->cycles;
+}
+
+void
+rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)
+{
+ struct cluster_node *cluster;
+ rte_node_t count;
+ int rc = 0;
+
+ cluster = stat->clusters;
+
+ for (count = 0; count < stat->max_nodes; count++) {
+ cluster_node_arregate_stats(cluster);
+ if (!skip_cb)
+ rc = stat->fn(!count, (count == stat->max_nodes - 1),
+ stat->cookie, &cluster->stat);
+ cluster_node_store_prev_stats(cluster);
+ if (rc)
+ break;
+ cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+ }
+}
+
+void
+rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)
+{
+ struct cluster_node *cluster;
+ rte_node_t count;
+
+ cluster = stat->clusters;
+
+ for (count = 0; count < stat->max_nodes; count++) {
+ struct rte_graph_cluster_node_stats *node = &cluster->stat;
+
+ node->ts = 0;
+ node->calls = 0;
+ node->objs = 0;
+ node->cycles = 0;
+ node->prev_ts = 0;
+ node->prev_calls = 0;
+ node->prev_objs = 0;
+ node->prev_cycles = 0;
+ node->realloc_count = 0;
+ cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+ }
+}
diff --git a/src/spdk/dpdk/lib/librte_graph/meson.build b/src/spdk/dpdk/lib/librte_graph/meson.build
new file mode 100644
index 000000000..8fa18b611
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+name = 'graph'
+
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_stats.c', 'graph_populate.c')
+headers = files('rte_graph.h', 'rte_graph_worker.h')
+
+deps += ['eal']
+build = false
+reason = 'not needed by SPDK'
diff --git a/src/spdk/dpdk/lib/librte_graph/node.c b/src/spdk/dpdk/lib/librte_graph/node.c
new file mode 100644
index 000000000..873c9ab16
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/node.c
@@ -0,0 +1,421 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_string_fns.h>
+
+#include "graph_private.h"
+
+static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
+static rte_node_t node_id;
+
+#define NODE_ID_CHECK(id) ID_CHECK(id, node_id)
+
+/* Private functions */
+struct node_head *
+node_list_head_get(void)
+{
+ return &node_list;
+}
+
+struct node *
+node_from_name(const char *name)
+{
+ struct node *node;
+
+ STAILQ_FOREACH(node, &node_list, next)
+ if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+ return node;
+
+ return NULL;
+}
+
+static bool
+node_has_duplicate_entry(const char *name)
+{
+ struct node *node;
+
+ /* Is duplicate name registered */
+ STAILQ_FOREACH(node, &node_list, next) {
+ if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0) {
+ rte_errno = EEXIST;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Public functions */
+rte_node_t
+__rte_node_register(const struct rte_node_register *reg)
+{
+ struct node *node;
+ rte_edge_t i;
+ size_t sz;
+
+ /* Limit Node specific metadata to one cacheline on 64B CL machine */
+ RTE_BUILD_BUG_ON((offsetof(struct rte_node, nodes) -
+ offsetof(struct rte_node, ctx)) !=
+ RTE_CACHE_LINE_MIN_SIZE);
+
+ graph_spinlock_lock();
+
+ /* Check sanity */
+ if (reg == NULL || reg->process == NULL) {
+ rte_errno = EINVAL;
+ goto fail;
+ }
+
+ /* Check for duplicate name */
+ if (node_has_duplicate_entry(reg->name))
+ goto fail;
+
+ sz = sizeof(struct node) + (reg->nb_edges * RTE_NODE_NAMESIZE);
+ node = calloc(1, sz);
+ if (node == NULL) {
+ rte_errno = ENOMEM;
+ goto fail;
+ }
+
+ /* Initialize the node */
+ if (rte_strscpy(node->name, reg->name, RTE_NODE_NAMESIZE) < 0) {
+ rte_errno = E2BIG;
+ goto free;
+ }
+ node->flags = reg->flags;
+ node->process = reg->process;
+ node->init = reg->init;
+ node->fini = reg->fini;
+ node->nb_edges = reg->nb_edges;
+ node->parent_id = reg->parent_id;
+ for (i = 0; i < reg->nb_edges; i++) {
+ if (rte_strscpy(node->next_nodes[i], reg->next_nodes[i],
+ RTE_NODE_NAMESIZE) < 0) {
+ rte_errno = E2BIG;
+ goto free;
+ }
+ }
+
+ node->id = node_id++;
+
+ /* Add the node at tail */
+ STAILQ_INSERT_TAIL(&node_list, node, next);
+ graph_spinlock_unlock();
+
+ return node->id;
+free:
+ free(node);
+fail:
+ graph_spinlock_unlock();
+ return RTE_NODE_ID_INVALID;
+}
+
+static int
+clone_name(struct rte_node_register *reg, struct node *node, const char *name)
+{
+ ssize_t sz, rc;
+
+#define SZ RTE_NODE_NAMESIZE
+ rc = rte_strscpy(reg->name, node->name, SZ);
+ if (rc < 0)
+ goto fail;
+ sz = rc;
+ rc = rte_strscpy(reg->name + sz, "-", RTE_MAX((int16_t)(SZ - sz), 0));
+ if (rc < 0)
+ goto fail;
+ sz += rc;
+ sz = rte_strscpy(reg->name + sz, name, RTE_MAX((int16_t)(SZ - sz), 0));
+ if (sz < 0)
+ goto fail;
+
+ return 0;
+fail:
+ rte_errno = E2BIG;
+ return -rte_errno;
+}
+
+static rte_node_t
+node_clone(struct node *node, const char *name)
+{
+ rte_node_t rc = RTE_NODE_ID_INVALID;
+ struct rte_node_register *reg;
+ rte_edge_t i;
+
+ /* Don't allow to clone a node from a cloned node */
+ if (node->parent_id != RTE_NODE_ID_INVALID) {
+ rte_errno = EEXIST;
+ goto fail;
+ }
+
+ /* Check for duplicate name */
+ if (node_has_duplicate_entry(name))
+ goto fail;
+
+ reg = calloc(1, sizeof(*reg) + (sizeof(char *) * node->nb_edges));
+ if (reg == NULL) {
+ rte_errno = ENOMEM;
+ goto fail;
+ }
+
+ /* Clone the source node */
+ reg->flags = node->flags;
+ reg->process = node->process;
+ reg->init = node->init;
+ reg->fini = node->fini;
+ reg->nb_edges = node->nb_edges;
+ reg->parent_id = node->id;
+
+ for (i = 0; i < node->nb_edges; i++)
+ reg->next_nodes[i] = node->next_nodes[i];
+
+ /* Naming ceremony of the new node. name is node->name + "-" + name */
+ if (clone_name(reg, node, name))
+ goto free;
+
+ rc = __rte_node_register(reg);
+free:
+ free(reg);
+fail:
+ return rc;
+}
+
+rte_node_t
+rte_node_clone(rte_node_t id, const char *name)
+{
+ struct node *node;
+
+ NODE_ID_CHECK(id);
+ STAILQ_FOREACH(node, &node_list, next)
+ if (node->id == id)
+ return node_clone(node, name);
+
+fail:
+ return RTE_NODE_ID_INVALID;
+}
+
+rte_node_t
+rte_node_from_name(const char *name)
+{
+ struct node *node;
+
+ STAILQ_FOREACH(node, &node_list, next)
+ if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+ return node->id;
+
+ return RTE_NODE_ID_INVALID;
+}
+
+char *
+rte_node_id_to_name(rte_node_t id)
+{
+ struct node *node;
+
+ NODE_ID_CHECK(id);
+ STAILQ_FOREACH(node, &node_list, next)
+ if (node->id == id)
+ return node->name;
+
+fail:
+ return NULL;
+}
+
+rte_edge_t
+rte_node_edge_count(rte_node_t id)
+{
+ struct node *node;
+
+ NODE_ID_CHECK(id);
+ STAILQ_FOREACH(node, &node_list, next)
+ if (node->id == id)
+ return node->nb_edges;
+fail:
+ return RTE_EDGE_ID_INVALID;
+}
+
+static rte_edge_t
+edge_update(struct node *node, struct node *prev, rte_edge_t from,
+ const char **next_nodes, rte_edge_t nb_edges)
+{
+ rte_edge_t i, max_edges, count = 0;
+ struct node *new_node;
+ bool need_realloc;
+ size_t sz;
+
+ if (from == RTE_EDGE_ID_INVALID)
+ from = node->nb_edges;
+
+ /* Don't create hole in next_nodes[] list */
+ if (from > node->nb_edges) {
+ rte_errno = ENOMEM;
+ goto fail;
+ }
+
+ /* Remove me from list */
+ STAILQ_REMOVE(&node_list, node, node, next);
+
+ /* Allocate the storage space for new node if required */
+ max_edges = from + nb_edges;
+ need_realloc = max_edges > node->nb_edges;
+ if (need_realloc) {
+ sz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);
+ new_node = realloc(node, sz);
+ if (new_node == NULL) {
+ rte_errno = ENOMEM;
+ goto restore;
+ } else {
+ node = new_node;
+ }
+ }
+
+ /* Update the new nodes name */
+ for (i = from; i < max_edges; i++, count++) {
+ if (rte_strscpy(node->next_nodes[i], next_nodes[count],
+ RTE_NODE_NAMESIZE) < 0) {
+ rte_errno = E2BIG;
+ goto restore;
+ }
+ }
+restore:
+ /* Update the linked list to point new node address in prev node */
+ if (prev)
+ STAILQ_INSERT_AFTER(&node_list, prev, node, next);
+ else
+ STAILQ_INSERT_HEAD(&node_list, node, next);
+
+ if (need_realloc)
+ node->nb_edges = max_edges;
+
+fail:
+ return count;
+}
+
+rte_edge_t
+rte_node_edge_shrink(rte_node_t id, rte_edge_t size)
+{
+ rte_edge_t rc = RTE_EDGE_ID_INVALID;
+ struct node *node;
+
+ NODE_ID_CHECK(id);
+ graph_spinlock_lock();
+
+ STAILQ_FOREACH(node, &node_list, next) {
+ if (node->id == id) {
+ if (node->nb_edges < size) {
+ rte_errno = E2BIG;
+ goto fail;
+ }
+ node->nb_edges = size;
+ rc = size;
+ break;
+ }
+ }
+
+fail:
+ graph_spinlock_unlock();
+ return rc;
+}
+
+rte_edge_t
+rte_node_edge_update(rte_node_t id, rte_edge_t from, const char **next_nodes,
+ uint16_t nb_edges)
+{
+ rte_edge_t rc = RTE_EDGE_ID_INVALID;
+ struct node *n, *prev;
+
+ NODE_ID_CHECK(id);
+ graph_spinlock_lock();
+
+ prev = NULL;
+ STAILQ_FOREACH(n, &node_list, next) {
+ if (n->id == id) {
+ rc = edge_update(n, prev, from, next_nodes, nb_edges);
+ break;
+ }
+ prev = n;
+ }
+
+ graph_spinlock_unlock();
+fail:
+ return rc;
+}
+
+static rte_node_t
+node_copy_edges(struct node *node, char *next_nodes[])
+{
+ rte_edge_t i;
+
+ for (i = 0; i < node->nb_edges; i++)
+ next_nodes[i] = node->next_nodes[i];
+
+ return i;
+}
+
+rte_node_t
+rte_node_edge_get(rte_node_t id, char *next_nodes[])
+{
+ rte_node_t rc = RTE_NODE_ID_INVALID;
+ struct node *node;
+
+ NODE_ID_CHECK(id);
+ graph_spinlock_lock();
+
+ STAILQ_FOREACH(node, &node_list, next) {
+ if (node->id == id) {
+ if (next_nodes == NULL)
+ rc = sizeof(char *) * node->nb_edges;
+ else
+ rc = node_copy_edges(node, next_nodes);
+ break;
+ }
+ }
+
+ graph_spinlock_unlock();
+fail:
+ return rc;
+}
+
+static void
+node_scan_dump(FILE *f, rte_node_t id, bool all)
+{
+ struct node *node;
+
+ RTE_ASSERT(f != NULL);
+ NODE_ID_CHECK(id);
+
+ STAILQ_FOREACH(node, &node_list, next) {
+ if (all == true) {
+ node_dump(f, node);
+ } else if (node->id == id) {
+ node_dump(f, node);
+ return;
+ }
+ }
+fail:
+ return;
+}
+
+void
+rte_node_dump(FILE *f, rte_node_t id)
+{
+ node_scan_dump(f, id, false);
+}
+
+void
+rte_node_list_dump(FILE *f)
+{
+ node_scan_dump(f, 0, true);
+}
+
+rte_node_t
+rte_node_max_count(void)
+{
+ return node_id;
+}
diff --git a/src/spdk/dpdk/lib/librte_graph/rte_graph.h b/src/spdk/dpdk/lib/librte_graph/rte_graph.h
new file mode 100644
index 000000000..9a26ffc18
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/rte_graph.h
@@ -0,0 +1,668 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_H_
+#define _RTE_GRAPH_H_
+
+/**
+ * @file rte_graph.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph architecture abstracts the data processing functions as
+ * "node" and "link" them together to create a complex "graph" to enable
+ * reusable/modular data processing functions.
+ *
+ * This API enables graph framework operations such as create, lookup,
+ * dump and destroy on graph and node operations such as clone,
+ * edge update, and edge shrink, etc. The API also allows to create the stats
+ * cluster to monitor per graph and per node stats.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_GRAPH_NAMESIZE 64 /**< Max length of graph name. */
+#define RTE_NODE_NAMESIZE 64 /**< Max length of node name. */
+#define RTE_GRAPH_OFF_INVALID UINT32_MAX /**< Invalid graph offset. */
+#define RTE_NODE_ID_INVALID UINT32_MAX /**< Invalid node id. */
+#define RTE_EDGE_ID_INVALID UINT16_MAX /**< Invalid edge id. */
+#define RTE_GRAPH_ID_INVALID UINT16_MAX /**< Invalid graph id. */
+#define RTE_GRAPH_FENCE 0xdeadbeef12345678ULL /**< Graph fence data. */
+
+typedef uint32_t rte_graph_off_t; /**< Graph offset type. */
+typedef uint32_t rte_node_t; /**< Node id type. */
+typedef uint16_t rte_edge_t; /**< Edge id type. */
+typedef uint16_t rte_graph_t; /**< Graph id type. */
+
+/** Burst size in terms of log2 */
+#if RTE_GRAPH_BURST_SIZE == 1
+#define RTE_GRAPH_BURST_SIZE_LOG2 0 /**< Object burst size of 1. */
+#elif RTE_GRAPH_BURST_SIZE == 2
+#define RTE_GRAPH_BURST_SIZE_LOG2 1 /**< Object burst size of 2. */
+#elif RTE_GRAPH_BURST_SIZE == 4
+#define RTE_GRAPH_BURST_SIZE_LOG2 2 /**< Object burst size of 4. */
+#elif RTE_GRAPH_BURST_SIZE == 8
+#define RTE_GRAPH_BURST_SIZE_LOG2 3 /**< Object burst size of 8. */
+#elif RTE_GRAPH_BURST_SIZE == 16
+#define RTE_GRAPH_BURST_SIZE_LOG2 4 /**< Object burst size of 16. */
+#elif RTE_GRAPH_BURST_SIZE == 32
+#define RTE_GRAPH_BURST_SIZE_LOG2 5 /**< Object burst size of 32. */
+#elif RTE_GRAPH_BURST_SIZE == 64
+#define RTE_GRAPH_BURST_SIZE_LOG2 6 /**< Object burst size of 64. */
+#elif RTE_GRAPH_BURST_SIZE == 128
+#define RTE_GRAPH_BURST_SIZE_LOG2 7 /**< Object burst size of 128. */
+#elif RTE_GRAPH_BURST_SIZE == 256
+#define RTE_GRAPH_BURST_SIZE_LOG2 8 /**< Object burst size of 256. */
+#else
+#error "Unsupported burst size"
+#endif
+
+/* Forward declaration */
+struct rte_node; /**< Node object */
+struct rte_graph; /**< Graph object */
+struct rte_graph_cluster_stats; /**< Stats for Cluster of graphs */
+struct rte_graph_cluster_node_stats; /**< Node stats within cluster of graphs */
+
+/**
+ * Node process function.
+ *
+ * The function invoked when the worker thread walks on nodes using
+ * rte_graph_walk().
+ *
+ * @param graph
+ * Pointer to the graph object.
+ * @param node
+ * Pointer to the node object.
+ * @param objs
+ * Pointer to an array of objects to be processed.
+ * @param nb_objs
+ * Number of objects in the array.
+ *
+ * @return
+ * Number of objects processed.
+ *
+ * @see rte_graph_walk()
+ *
+ */
+typedef uint16_t (*rte_node_process_t)(struct rte_graph *graph,
+ struct rte_node *node, void **objs,
+ uint16_t nb_objs);
+
+/**
+ * Node initialization function.
+ *
+ * The function invoked when the user creates the graph using rte_graph_create()
+ *
+ * @param graph
+ * Pointer to the graph object.
+ * @param node
+ * Pointer to the node object.
+ *
+ * @return
+ * - 0: Success.
+ * -<0: Failure.
+ *
+ * @see rte_graph_create()
+ */
+typedef int (*rte_node_init_t)(const struct rte_graph *graph,
+ struct rte_node *node);
+
+/**
+ * Node finalization function.
+ *
+ * The function invoked when the user destroys the graph using
+ * rte_graph_destroy().
+ *
+ * @param graph
+ * Pointer to the graph object.
+ * @param node
+ * Pointer to the node object.
+ *
+ * @see rte_graph_destroy()
+ */
+typedef void (*rte_node_fini_t)(const struct rte_graph *graph,
+ struct rte_node *node);
+
+/**
+ * Graph cluster stats callback.
+ *
+ * @param is_first
+ * Flag to denote that stats are of the first node.
+ * @param is_last
+ * Flag to denote that stats are of the last node.
+ * @param cookie
+ * Cookie supplied during stats creation.
+ * @param stats
+ * Node cluster stats data.
+ *
+ * @return
+ * - 0: Success.
+ * -<0: Failure.
+ */
+typedef int (*rte_graph_cluster_stats_cb_t)(bool is_first, bool is_last,
+ void *cookie, const struct rte_graph_cluster_node_stats *stats);
+
+/**
+ * Structure to hold configuration parameters for creating the graph.
+ *
+ * @see rte_graph_create()
+ */
+struct rte_graph_param {
+ int socket_id; /**< Socket id where memory is allocated. */
+ uint16_t nb_node_patterns; /**< Number of node patterns. */
+ const char **node_patterns;
+ /**< Array of node patterns based on shell pattern. */
+};
+
+/**
+ * Structure to hold configuration parameters for graph cluster stats create.
+ *
+ * @see rte_graph_cluster_stats_create()
+ */
+struct rte_graph_cluster_stats_param {
+ int socket_id;
+ /**< Socket id where memory is allocated */
+ rte_graph_cluster_stats_cb_t fn;
+ /**< Stats print callback function. NULL value allowed, in that case,
+ * default print stat function used.
+ */
+ RTE_STD_C11
+ union {
+ void *cookie;
+ FILE *f; /**< File pointer to dump the stats when fn == NULL. */
+ };
+ uint16_t nb_graph_patterns; /**< Number of graph patterns. */
+ const char **graph_patterns;
+ /**< Array of graph patterns based on shell pattern. */
+};
+
+/**
+ * Node cluster stats data structure.
+ *
+ * @see struct rte_graph_cluster_stats_param::fn
+ */
+struct rte_graph_cluster_node_stats {
+ uint64_t ts; /**< Current timestamp. */
+ uint64_t calls; /**< Current number of calls made. */
+ uint64_t objs; /**< Current number of objs processed. */
+ uint64_t cycles; /**< Current number of cycles. */
+
+ uint64_t prev_ts; /**< Previous call timestamp. */
+ uint64_t prev_calls; /**< Previous number of calls. */
+ uint64_t prev_objs; /**< Previous number of processed objs. */
+ uint64_t prev_cycles; /**< Previous number of cycles. */
+
+ uint64_t realloc_count; /**< Realloc count. */
+
+ rte_node_t id; /**< Node identifier of stats. */
+ uint64_t hz; /**< Cycles per seconds. */
+ char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+} __rte_cache_aligned;
+
+/**
+ * Create Graph.
+ *
+ * Create memory reel, detect loops and find isolated nodes.
+ *
+ * @param name
+ * Unique name for this graph.
+ * @param prm
+ * Graph parameter, includes node names and count to be included
+ * in this graph.
+ *
+ * @return
+ * Unique graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_create(const char *name, struct rte_graph_param *prm);
+
+/**
+ * Destroy Graph.
+ *
+ * Free Graph memory reel.
+ *
+ * @param id
+ * id of the graph to destroy.
+ *
+ * @return
+ * 0 on success, error otherwise.
+ */
+__rte_experimental
+int rte_graph_destroy(rte_graph_t id);
+
+/**
+ * Get graph id from graph name.
+ *
+ * @param name
+ * Name of the graph to get id.
+ *
+ * @return
+ * Graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_from_name(const char *name);
+
+/**
+ * Get graph name from graph id.
+ *
+ * @param id
+ * id of the graph to get name.
+ *
+ * @return
+ * Graph name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_graph_id_to_name(rte_graph_t id);
+
+/**
+ * Export the graph as graph viz dot file
+ *
+ * @param name
+ * Name of the graph to export.
+ * @param f
+ * File pointer to export the graph.
+ *
+ * @return
+ * 0 on success, error otherwise.
+ */
+__rte_experimental
+int rte_graph_export(const char *name, FILE *f);
+
+/**
+ * Get graph object from its name.
+ *
+ * Typical usage of this API to get graph objects in the worker thread and
+ * followed calling rte_graph_walk() in a loop.
+ *
+ * @param name
+ * Name of the graph.
+ *
+ * @return
+ * Graph pointer on success, NULL otherwise.
+ *
+ * @see rte_graph_walk()
+ */
+__rte_experimental
+struct rte_graph *rte_graph_lookup(const char *name);
+
+/**
+ * Get maximum number of graph available.
+ *
+ * @return
+ * Maximum graph count.
+ */
+__rte_experimental
+rte_graph_t rte_graph_max_count(void);
+
+/**
+ * Dump the graph information to file.
+ *
+ * @param f
+ * File pointer to dump graph info.
+ * @param id
+ * Graph id to get graph info.
+ */
+__rte_experimental
+void rte_graph_dump(FILE *f, rte_graph_t id);
+
+/**
+ * Dump all graphs information to file
+ *
+ * @param f
+ * File pointer to dump graph info.
+ */
+__rte_experimental
+void rte_graph_list_dump(FILE *f);
+
+/**
+ * Dump graph information along with node info to file
+ *
+ * @param f
+ * File pointer to dump graph info.
+ * @param graph
+ * Graph pointer to get graph info.
+ * @param all
+ * true to dump nodes in the graph.
+ */
+__rte_experimental
+void rte_graph_obj_dump(FILE *f, struct rte_graph *graph, bool all);
+
+/** Macro to browse rte_node object after the graph creation */
+#define rte_graph_foreach_node(count, off, graph, node) \
+ for (count = 0, off = graph->nodes_start, \
+ node = RTE_PTR_ADD(graph, off); \
+ count < graph->nb_nodes; \
+ off = node->next, node = RTE_PTR_ADD(graph, off), count++)
+
+/**
+ * Get node object with in graph from id.
+ *
+ * @param graph_id
+ * Graph id to get node pointer from.
+ * @param node_id
+ * Node id to get node pointer.
+ *
+ * @return
+ * Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get(rte_graph_t graph_id, rte_node_t node_id);
+
+/**
+ * Get node pointer with in graph from name.
+ *
+ * @param graph
+ * Graph name to get node pointer from.
+ * @param name
+ * Node name to get the node pointer.
+ *
+ * @return
+ * Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get_by_name(const char *graph,
+ const char *name);
+
+/**
+ * Create graph stats cluster to aggregate runtime node stats.
+ *
+ * @param prm
+ * Parameters including file pointer to dump stats,
+ * Graph pattern to create cluster and callback function.
+ *
+ * @return
+ * Valid pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_graph_cluster_stats *rte_graph_cluster_stats_create(
+ const struct rte_graph_cluster_stats_param *prm);
+
+/**
+ * Destroy cluster stats.
+ *
+ * @param stat
+ * Valid cluster pointer to destroy.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat);
+
+/**
+ * Get stats to application.
+ *
+ * @param[out] stat
+ * Cluster status.
+ * @param skip_cb
+ * true to skip callback function invocation.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat,
+ bool skip_cb);
+
+/**
+ * Reset cluster stats to zero.
+ *
+ * @param stat
+ * Valid cluster stats pointer.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat);
+
+/**
+ * Structure defines the node registration parameters.
+ *
+ * @see __rte_node_register(), RTE_NODE_REGISTER()
+ */
+struct rte_node_register {
+ char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+ uint64_t flags; /**< Node configuration flag. */
+#define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
+ rte_node_process_t process; /**< Node process function. */
+ rte_node_init_t init; /**< Node init function. */
+ rte_node_fini_t fini; /**< Node fini function. */
+ rte_node_t id; /**< Node Identifier. */
+ rte_node_t parent_id; /**< Identifier of parent node. */
+ rte_edge_t nb_edges; /**< Number of edges from this node. */
+ const char *next_nodes[]; /**< Names of next nodes. */
+};
+
+/**
+ * Register new packet processing node. Nodes can be registered
+ * dynamically via this call or statically via the RTE_NODE_REGISTER
+ * macro.
+ *
+ * @param node
+ * Valid node pointer with name, process function and next_nodes.
+ *
+ * @return
+ * Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ *
+ * @see RTE_NODE_REGISTER()
+ */
+__rte_experimental
+rte_node_t __rte_node_register(const struct rte_node_register *node);
+
+/**
+ * Register a static node.
+ *
+ * The static node is registered through the constructor scheme, thereby, it can
+ * be used in a multi-process scenario.
+ *
+ * @param node
+ * Valid node pointer with name, process function, and next_nodes.
+ */
+#define RTE_NODE_REGISTER(node) \
+ RTE_INIT(rte_node_register_##node) \
+ { \
+ node.parent_id = RTE_NODE_ID_INVALID; \
+ node.id = __rte_node_register(&node); \
+ }
+
+/**
+ * Clone a node from static node(node created from RTE_NODE_REGISTER).
+ *
+ * @param id
+ * Static node id to clone from.
+ * @param name
+ * Name of the new node. The library prepends the parent node name to the
+ * user-specified name. The final node name will be,
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ * Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_clone(rte_node_t id, const char *name);
+
+/**
+ * Get node id from node name.
+ *
+ * @param name
+ * Valid node name. In the case of the cloned node, the name will be
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ * Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_from_name(const char *name);
+
+/**
+ * Get node name from node id.
+ *
+ * @param id
+ * Valid node id.
+ *
+ * @return
+ * Valid node name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_node_id_to_name(rte_node_t id);
+
+/**
+ * Get the number of edges(next-nodes) for a node from node id.
+ *
+ * @param id
+ * Valid node id.
+ *
+ * @return
+ * Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_count(rte_node_t id);
+
+/**
+ * Update the edges for a node from node id.
+ *
+ * @param id
+ * Valid node id.
+ * @param from
+ * Index to update the edges from. RTE_EDGE_ID_INVALID is valid,
+ * in that case, it will be added to the end of the list.
+ * @param next_nodes
+ * Name of the edges to update.
+ * @param nb_edges
+ * Number of edges to update.
+ *
+ * @return
+ * Valid edge count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,
+ const char **next_nodes, uint16_t nb_edges);
+
+/**
+ * Shrink the edges to a given size.
+ *
+ * @param id
+ * Valid node id.
+ * @param size
+ * New size to shrink the edges.
+ *
+ * @return
+ * New size on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);
+
+/**
+ * Get the edge names from a given node.
+ *
+ * @param id
+ * Valid node id.
+ * @param[out] next_nodes
+ * Buffer to copy the edge names. The NULL value is allowed in that case,
+ * the function returns the size of the array that needs to be allocated.
+ *
+ * @return
+ * When next_nodes == NULL, it returns the size of the array else
+ * number of item copied.
+ */
+__rte_experimental
+rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);
+
+/**
+ * Get maximum nodes available.
+ *
+ * @return
+ * Maximum nodes count.
+ */
+__rte_experimental
+rte_node_t rte_node_max_count(void);
+
+/**
+ * Dump node info to file.
+ *
+ * @param f
+ * File pointer to dump the node info.
+ * @param id
+ * Node id to get the info.
+ */
+__rte_experimental
+void rte_node_dump(FILE *f, rte_node_t id);
+
+/**
+ * Dump all node info to file.
+ *
+ * @param f
+ * File pointer to dump the node info.
+ */
+__rte_experimental
+void rte_node_list_dump(FILE *f);
+
+/**
+ * Test the validity of node id.
+ *
+ * @param id
+ * Node id to check.
+ *
+ * @return
+ * 1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_node_is_invalid(rte_node_t id)
+{
+ return (id == RTE_NODE_ID_INVALID);
+}
+
+/**
+ * Test the validity of edge id.
+ *
+ * @param id
+ * Edge node id to check.
+ *
+ * @return
+ * 1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_edge_is_invalid(rte_edge_t id)
+{
+ return (id == RTE_EDGE_ID_INVALID);
+}
+
+/**
+ * Test the validity of graph id.
+ *
+ * @param id
+ * Graph id to check.
+ *
+ * @return
+ * 1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_is_invalid(rte_graph_t id)
+{
+ return (id == RTE_GRAPH_ID_INVALID);
+}
+
+/**
+ * Test stats feature support.
+ *
+ * @return
+ * 1 if stats enabled, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_has_stats_feature(void)
+{
+#ifdef RTE_LIBRTE_GRAPH_STATS
+ return RTE_LIBRTE_GRAPH_STATS;
+#else
+ return 0;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_H_ */
diff --git a/src/spdk/dpdk/lib/librte_graph/rte_graph_version.map b/src/spdk/dpdk/lib/librte_graph/rte_graph_version.map
new file mode 100644
index 000000000..13b838752
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/rte_graph_version.map
@@ -0,0 +1,47 @@
+EXPERIMENTAL {
+ global:
+
+ __rte_node_register;
+ __rte_node_stream_alloc;
+ __rte_node_stream_alloc_size;
+
+ rte_graph_create;
+ rte_graph_destroy;
+ rte_graph_dump;
+ rte_graph_export;
+ rte_graph_from_name;
+ rte_graph_id_to_name;
+ rte_graph_lookup;
+ rte_graph_list_dump;
+ rte_graph_max_count;
+ rte_graph_node_get;
+ rte_graph_node_get_by_name;
+ rte_graph_obj_dump;
+ rte_graph_walk;
+
+ rte_graph_cluster_stats_create;
+ rte_graph_cluster_stats_destroy;
+ rte_graph_cluster_stats_get;
+ rte_graph_cluster_stats_reset;
+
+ rte_node_clone;
+ rte_node_dump;
+ rte_node_edge_count;
+ rte_node_edge_get;
+ rte_node_edge_shrink;
+ rte_node_edge_update;
+ rte_node_enqueue;
+ rte_node_enqueue_x1;
+ rte_node_enqueue_x2;
+ rte_node_enqueue_x4;
+ rte_node_enqueue_next;
+ rte_node_from_name;
+ rte_node_id_to_name;
+ rte_node_list_dump;
+ rte_node_max_count;
+ rte_node_next_stream_get;
+ rte_node_next_stream_put;
+ rte_node_next_stream_move;
+
+ local: *;
+};
diff --git a/src/spdk/dpdk/lib/librte_graph/rte_graph_worker.h b/src/spdk/dpdk/lib/librte_graph/rte_graph_worker.h
new file mode 100644
index 000000000..4c3ddcbde
--- /dev/null
+++ b/src/spdk/dpdk/lib/librte_graph/rte_graph_worker.h
@@ -0,0 +1,510 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_WORKER_H_
+#define _RTE_GRAPH_WORKER_H_
+
+/**
+ * @file rte_graph_worker.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows a worker thread to walk over a graph and nodes to create,
+ * process, enqueue and move streams of objects to the next nodes.
+ */
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+
+#include "rte_graph.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ *
+ * Data structure to hold graph data.
+ */
+struct rte_graph {
+ uint32_t tail; /**< Tail of circular buffer. */
+ uint32_t head; /**< Head of circular buffer. */
+ uint32_t cir_mask; /**< Circular buffer wrap around mask. */
+ rte_node_t nb_nodes; /**< Number of nodes in the graph. */
+ rte_graph_off_t *cir_start; /**< Pointer to circular buffer. */
+ rte_graph_off_t nodes_start; /**< Offset at which node memory starts. */
+ rte_graph_t id; /**< Graph identifier. */
+ int socket; /**< Socket ID where memory is allocated. */
+ char name[RTE_GRAPH_NAMESIZE]; /**< Name of the graph. */
+ uint64_t fence; /**< Fence. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ *
+ * Data structure to hold node data.
+ */
+struct rte_node {
+ /* Slow path area */
+ uint64_t fence; /**< Fence. */
+ rte_graph_off_t next; /**< Index to next node. */
+ rte_node_t id; /**< Node identifier. */
+ rte_node_t parent_id; /**< Parent Node identifier. */
+ rte_edge_t nb_edges; /**< Number of edges from this node. */
+ uint32_t realloc_count; /**< Number of times realloced. */
+
+ char parent[RTE_NODE_NAMESIZE]; /**< Parent node name. */
+ char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+
+ /* Fast path area */
+#define RTE_NODE_CTX_SZ 16
+ uint8_t ctx[RTE_NODE_CTX_SZ] __rte_cache_aligned; /**< Node Context. */
+ uint16_t size; /**< Total number of objects available. */
+ uint16_t idx; /**< Number of objects used. */
+ rte_graph_off_t off; /**< Offset of node in the graph reel. */
+ uint64_t total_cycles; /**< Cycles spent in this node. */
+ uint64_t total_calls; /**< Calls done to this node. */
+ uint64_t total_objs; /**< Objects processed by this node. */
+ RTE_STD_C11
+ union {
+ void **objs; /**< Array of object pointers. */
+ uint64_t objs_u64;
+ };
+ RTE_STD_C11
+ union {
+ rte_node_process_t process; /**< Process function. */
+ uint64_t process_u64;
+ };
+ struct rte_node *nodes[] __rte_cache_min_aligned; /**< Next nodes. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ *
+ * Allocate a stream of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ * Pointer to the graph object.
+ * @param node
+ * Pointer to the node object.
+ */
+__rte_experimental
+void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
+
+/**
+ * @internal
+ *
+ * Allocate a stream with requested number of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ * Pointer to the graph object.
+ * @param node
+ * Pointer to the node object.
+ * @param req_size
+ * Number of objects to be allocated.
+ */
+__rte_experimental
+void __rte_node_stream_alloc_size(struct rte_graph *graph,
+ struct rte_node *node, uint16_t req_size);
+
+/**
+ * Perform graph walk on the circular buffer and invoke the process function
+ * of the nodes and collect the stats.
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup function.
+ *
+ * @see rte_graph_lookup()
+ */
+__rte_experimental
+static inline void
+rte_graph_walk(struct rte_graph *graph)
+{
+ const rte_graph_off_t *cir_start = graph->cir_start;
+ const rte_node_t mask = graph->cir_mask;
+ uint32_t head = graph->head;
+ struct rte_node *node;
+ uint64_t start;
+ uint16_t rc;
+ void **objs;
+
+ /*
+ * Walk on the source node(s) ((cir_start - head) -> cir_start) and then
+ * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)
+ * in a circular buffer fashion.
+ *
+ * +-----+ <= cir_start - head [number of source nodes]
+ * | |
+ * | ... | <= source nodes
+ * | |
+ * +-----+ <= cir_start [head = 0] [tail = 0]
+ * | |
+ * | ... | <= pending streams
+ * | |
+ * +-----+ <= cir_start + mask
+ */
+ while (likely(head != graph->tail)) {
+ node = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);
+ RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+ objs = node->objs;
+ rte_prefetch0(objs);
+
+ if (rte_graph_has_stats_feature()) {
+ start = rte_rdtsc();
+ rc = node->process(graph, node, objs, node->idx);
+ node->total_cycles += rte_rdtsc() - start;
+ node->total_calls++;
+ node->total_objs += rc;
+ } else {
+ node->process(graph, node, objs, node->idx);
+ }
+ node->idx = 0;
+ head = likely((int32_t)head > 0) ? head & mask : head;
+ }
+ graph->tail = 0;
+}
+
+/* Fast path helper functions */
+
+/**
+ * @internal
+ *
+ * Enqueue a given node to the tail of the graph reel.
+ *
+ * @param graph
+ * Pointer Graph object.
+ * @param node
+ * Pointer to node object to be enqueued.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_tail_update(struct rte_graph *graph, struct rte_node *node)
+{
+ uint32_t tail;
+
+ tail = graph->tail;
+ graph->cir_start[tail++] = node->off;
+ graph->tail = tail & graph->cir_mask;
+}
+
+/**
+ * @internal
+ *
+ * Enqueue sequence prologue function.
+ *
+ * Updates the node to tail of graph reel and resizes the number of objects
+ * available in the stream as needed.
+ *
+ * @param graph
+ * Pointer to the graph object.
+ * @param node
+ * Pointer to the node object.
+ * @param idx
+ * Index at which the object enqueue starts from.
+ * @param space
+ * Space required for the object enqueue.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_prologue(struct rte_graph *graph, struct rte_node *node,
+ const uint16_t idx, const uint16_t space)
+{
+
+ /* Add to the pending stream list if the node is new */
+ if (idx == 0)
+ __rte_node_enqueue_tail_update(graph, node);
+
+ if (unlikely(node->size < (idx + space)))
+ __rte_node_stream_alloc(graph, node);
+}
+
+/**
+ * @internal
+ *
+ * Get the node pointer from current node edge id.
+ *
+ * @param node
+ * Current node pointer.
+ * @param next
+ * Edge id of the required node.
+ *
+ * @return
+ * Pointer to the node denoted by the edge id.
+ */
+static __rte_always_inline struct rte_node *
+__rte_node_next_node_get(struct rte_node *node, rte_edge_t next)
+{
+ RTE_ASSERT(next < node->nb_edges);
+ RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+ node = node->nodes[next];
+ RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+
+ return node;
+}
+
+/**
+ * Enqueue the objs to next node for further processing and set
+ * the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup().
+ * @param node
+ * Current node pointer.
+ * @param next
+ * Relative next node index to enqueue objs.
+ * @param objs
+ * Objs to enqueue.
+ * @param nb_objs
+ * Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,
+ rte_edge_t next, void **objs, uint16_t nb_objs)
+{
+ node = __rte_node_next_node_get(node, next);
+ const uint16_t idx = node->idx;
+
+ __rte_node_enqueue_prologue(graph, node, idx, nb_objs);
+
+ rte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));
+ node->idx = idx + nb_objs;
+}
+
+/**
+ * Enqueue only one obj to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup().
+ * @param node
+ * Current node pointer.
+ * @param next
+ * Relative next node index to enqueue objs.
+ * @param obj
+ * Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x1(struct rte_graph *graph, struct rte_node *node,
+ rte_edge_t next, void *obj)
+{
+ node = __rte_node_next_node_get(node, next);
+ uint16_t idx = node->idx;
+
+ __rte_node_enqueue_prologue(graph, node, idx, 1);
+
+ node->objs[idx++] = obj;
+ node->idx = idx;
+}
+
+/**
+ * Enqueue only two objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue two objs.
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup().
+ * @param node
+ * Current node pointer.
+ * @param next
+ * Relative next node index to enqueue objs.
+ * @param obj0
+ * Obj to enqueue.
+ * @param obj1
+ * Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x2(struct rte_graph *graph, struct rte_node *node,
+ rte_edge_t next, void *obj0, void *obj1)
+{
+ node = __rte_node_next_node_get(node, next);
+ uint16_t idx = node->idx;
+
+ __rte_node_enqueue_prologue(graph, node, idx, 2);
+
+ node->objs[idx++] = obj0;
+ node->objs[idx++] = obj1;
+ node->idx = idx;
+}
+
+/**
+ * Enqueue only four objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue four objs.
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup().
+ * @param node
+ * Current node pointer.
+ * @param next
+ * Relative next node index to enqueue objs.
+ * @param obj0
+ * 1st obj to enqueue.
+ * @param obj1
+ * 2nd obj to enqueue.
+ * @param obj2
+ * 3rd obj to enqueue.
+ * @param obj3
+ * 4th obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x4(struct rte_graph *graph, struct rte_node *node,
+ rte_edge_t next, void *obj0, void *obj1, void *obj2,
+ void *obj3)
+{
+ node = __rte_node_next_node_get(node, next);
+ uint16_t idx = node->idx;
+
+ __rte_node_enqueue_prologue(graph, node, idx, 4);
+
+ node->objs[idx++] = obj0;
+ node->objs[idx++] = obj1;
+ node->objs[idx++] = obj2;
+ node->objs[idx++] = obj3;
+ node->idx = idx;
+}
+
+/**
+ * Enqueue objs to multiple next nodes for further processing and
+ * set the next nodes to pending state in the circular buffer.
+ * objs[i] will be enqueued to nexts[i].
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup().
+ * @param node
+ * Current node pointer.
+ * @param nexts
+ * List of relative next node indices to enqueue objs.
+ * @param objs
+ * List of objs to enqueue.
+ * @param nb_objs
+ * Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,
+ rte_edge_t *nexts, void **objs, uint16_t nb_objs)
+{
+ uint16_t i;
+
+ for (i = 0; i < nb_objs; i++)
+ rte_node_enqueue_x1(graph, node, nexts[i], objs[i]);
+}
+
+/**
+ * Get the stream of next node to enqueue the objs.
+ * Once done with the updating the objs, needs to call
+ * rte_node_next_stream_put to put the next node to pending state.
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup().
+ * @param node
+ * Current node pointer.
+ * @param next
+ * Relative next node index to get stream.
+ * @param nb_objs
+ * Requested free size of the next stream.
+ *
+ * @return
+ * Valid next stream on success.
+ *
+ * @see rte_node_next_stream_put().
+ */
+__rte_experimental
+static inline void **
+rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,
+ rte_edge_t next, uint16_t nb_objs)
+{
+ node = __rte_node_next_node_get(node, next);
+ const uint16_t idx = node->idx;
+ uint16_t free_space = node->size - idx;
+
+ if (unlikely(free_space < nb_objs))
+ __rte_node_stream_alloc_size(graph, node, nb_objs);
+
+ return &node->objs[idx];
+}
+
+/**
+ * Put the next stream to pending state in the circular buffer
+ * for further processing. Should be invoked after rte_node_next_stream_get().
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup().
+ * @param node
+ * Current node pointer.
+ * @param next
+ * Relative next node index..
+ * @param idx
+ * Number of objs updated in the stream after getting the stream using
+ * rte_node_next_stream_get.
+ *
+ * @see rte_node_next_stream_get().
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_put(struct rte_graph *graph, struct rte_node *node,
+ rte_edge_t next, uint16_t idx)
+{
+ if (unlikely(!idx))
+ return;
+
+ node = __rte_node_next_node_get(node, next);
+ if (node->idx == 0)
+ __rte_node_enqueue_tail_update(graph, node);
+
+ node->idx += idx;
+}
+
+/**
+ * Home run scenario, Enqueue all the objs of current node to next
+ * node in optimized way by swapping the streams of both nodes.
+ * Performs good when next node is already not in pending state.
+ * If next node is already in pending state then normal enqueue
+ * will be used.
+ *
+ * @param graph
+ * Graph pointer returned from rte_graph_lookup().
+ * @param src
+ * Current node pointer.
+ * @param next
+ * Relative next node index.
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_move(struct rte_graph *graph, struct rte_node *src,
+ rte_edge_t next)
+{
+ struct rte_node *dst = __rte_node_next_node_get(src, next);
+
+ /* Let swap the pointers if dst don't have valid objs */
+ if (likely(dst->idx == 0)) {
+ void **dobjs = dst->objs;
+ uint16_t dsz = dst->size;
+ dst->objs = src->objs;
+ dst->size = src->size;
+ src->objs = dobjs;
+ src->size = dsz;
+ dst->idx = src->idx;
+ __rte_node_enqueue_tail_update(graph, dst);
+ } else { /* Move the objects from src node to dst node */
+ rte_node_enqueue(graph, src, next, src->objs, src->idx);
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_WORKER_H_ */