get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/65433/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 65433,
    "url": "http://patches.dpdk.org/api/patches/65433/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200131170201.3236153-2-jerinj@marvell.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20200131170201.3236153-2-jerinj@marvell.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200131170201.3236153-2-jerinj@marvell.com",
    "date": "2020-01-31T17:01:57",
    "name": "[RFC,1/5] graph: introduce graph subsystem",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "d437fd8f2832f9ca9e15500e03555eee6c2d6846",
    "submitter": {
        "id": 1188,
        "url": "http://patches.dpdk.org/api/people/1188/?format=api",
        "name": "Jerin Jacob Kollanukkaran",
        "email": "jerinj@marvell.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20200131170201.3236153-2-jerinj@marvell.com/mbox/",
    "series": [
        {
            "id": 8382,
            "url": "http://patches.dpdk.org/api/series/8382/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=8382",
            "date": "2020-01-31T17:01:56",
            "name": "graph: introduce graph subsystem",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/8382/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/65433/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/65433/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 81CBFA0524;\n\tFri, 31 Jan 2020 18:02:58 +0100 (CET)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 53D861C0DA;\n\tFri, 31 Jan 2020 18:02:58 +0100 (CET)",
            "from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com\n [67.231.156.173]) by dpdk.org (Postfix) with ESMTP id E93C21C029;\n Fri, 31 Jan 2020 18:02:56 +0100 (CET)",
            "from pps.filterd (m0045851.ppops.net [127.0.0.1])\n by mx0b-0016f401.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id\n 00VGvZKo000858; Fri, 31 Jan 2020 09:02:40 -0800",
            "from sc-exch02.marvell.com ([199.233.58.182])\n by mx0b-0016f401.pphosted.com with ESMTP id 2xrp2tmg9j-1\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);\n Fri, 31 Jan 2020 09:02:38 -0800",
            "from SC-EXCH03.marvell.com (10.93.176.83) by SC-EXCH02.marvell.com\n (10.93.176.82) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Fri, 31 Jan\n 2020 09:02:35 -0800",
            "from maili.marvell.com (10.93.176.43) by SC-EXCH03.marvell.com\n (10.93.176.83) with Microsoft SMTP Server id 15.0.1497.2 via Frontend\n Transport; Fri, 31 Jan 2020 09:02:34 -0800",
            "from jerin-lab.marvell.com (jerin-lab.marvell.com [10.28.34.14])\n by maili.marvell.com (Postfix) with ESMTP id 9C8DE3F7041;\n Fri, 31 Jan 2020 09:02:24 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com;\n h=from : to : cc :\n subject : date : message-id : in-reply-to : references : mime-version :\n content-type : content-transfer-encoding; s=pfpt0818;\n bh=YB7h0Q8XfczG0VKHOLzZib9BUlhNqrmsYeyZvy6a6pg=;\n b=c0qNfUaZv5qbkGLBO5TdDoRoYuZJ7vhwUvte84+smUlXJiLbjbmZh4EigJShEUhSF0P8\n QHkbmMp+kChqNLMGc0w/UHil6ryQ6maH79UargoF7ftr8qDFdRx0LE4Po3SjHEidp+X2\n NkGJT9DMczAUk3DKJ5FgRc0O9xRSbyb4JWvEGcmlghDCEXNmGhlOWOURtNGq86E1T+Vy\n Ha7jnHFQsLcBDkKD2gqpWzSilWAnNViFTl+HXCUJXnSYvrLX3cLP8CzLATGAqX6Nnv+F\n wD/i27TXz/9I2mU+wosXt2Pwkr9jGlujl01IqUxAVptvGZ55ZAZnu+wmOP87Gnn0cX28 MA==",
        "From": "<jerinj@marvell.com>",
        "To": "<dev@dpdk.org>",
        "CC": "<pkapoor@marvell.com>, <ndabilpuram@marvell.com>,\n <kirankumark@marvell.com>, <pbhagavatula@marvell.com>,\n <pathreya@marvell.com>, <nsaxena@marvell.com>,\n <sshankarnara@marvell.com>, <honnappa.nagarahalli@arm.com>,\n <thomas@monjalon.net>, <david.marchand@redhat.com>,\n <ferruh.yigit@intel.com>, <arybchenko@solarflare.com>,\n <ajit.khaparde@broadcom.com>, <xiaolong.ye@intel.com>,\n <rasland@mellanox.com>, <maxime.coquelin@redhat.com>,\n <akhil.goyal@nxp.com>, <cristian.dumitrescu@intel.com>,\n <john.mcnamara@intel.com>, <bruce.richardson@intel.com>,\n <anatoly.burakov@intel.com>, <gavin.hu@arm.com>,\n <drc@linux.vnet.ibm.com>, <konstantin.ananyev@intel.com>,\n <pallavi.kadam@intel.com>, <olivier.matz@6wind.com>,\n <gage.eads@intel.com>, <nikhil.rao@intel.com>,\n <erik.g.carrillo@intel.com>, <hemant.agrawal@nxp.com>,\n <artem.andreev@oktetlabs.ru>, <sthemmin@microsoft.com>,\n <shahafs@mellanox.com>, <keith.wiles@intel.com>,\n <mattias.ronnblom@ericsson.com>, <jasvinder.singh@intel.com>,\n <vladimir.medvedkin@intel.com>, <mdr@ashroe.eu>, <techboard@dpdk.org>,\n \"Jerin Jacob\" <jerinj@marvell.com>",
        "Date": "Fri, 31 Jan 2020 22:31:57 +0530",
        "Message-ID": "<20200131170201.3236153-2-jerinj@marvell.com>",
        "X-Mailer": "git-send-email 2.24.1",
        "In-Reply-To": "<20200131170201.3236153-1-jerinj@marvell.com>",
        "References": "<20200131170201.3236153-1-jerinj@marvell.com>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"UTF-8\"",
        "Content-Transfer-Encoding": "8bit",
        "X-Proofpoint-Virus-Version": "vendor=fsecure engine=2.50.10434:6.0.138, 18.0.572\n definitions=2020-01-31_04:2020-01-31,\n 2020-01-31 signatures=0",
        "Subject": "[dpdk-dev]  [RFC PATCH 1/5] graph: introduce graph subsystem",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "From: Jerin Jacob <jerinj@marvell.com>\n\nAbstracting the data processing functions as “nodes” and “link” them\ntogether to create complex “graph” to enable reusable data processing functions\nhas been identified as proven architecture.\n\nIntroducing the graph infrastructure for packet processing.\n\nSigned-off-by: Jerin Jacob <jerinj@marvell.com>\n---\n app/test/meson.build                   |   1 +\n config/common_base                     |   7 +\n config/rte_config.h                    |   4 +\n lib/Makefile                           |   2 +\n lib/librte_graph/Makefile              |  28 ++\n lib/librte_graph/graph.c               | 578 +++++++++++++++++++++++++\n lib/librte_graph/graph_debug.c         |  81 ++++\n lib/librte_graph/graph_ops.c           | 163 +++++++\n lib/librte_graph/graph_populate.c      | 224 ++++++++++\n lib/librte_graph/graph_private.h       | 113 +++++\n lib/librte_graph/graph_stats.c         | 396 +++++++++++++++++\n lib/librte_graph/meson.build           |  11 +\n lib/librte_graph/node.c                | 419 ++++++++++++++++++\n lib/librte_graph/rte_graph.h           | 277 ++++++++++++\n lib/librte_graph/rte_graph_version.map |  46 ++\n lib/librte_graph/rte_graph_worker.h    | 280 ++++++++++++\n lib/meson.build                        |   2 +-\n mk/rte.app.mk                          |   1 +\n 18 files changed, 2632 insertions(+), 1 deletion(-)\n create mode 100644 lib/librte_graph/Makefile\n create mode 100644 lib/librte_graph/graph.c\n create mode 100644 lib/librte_graph/graph_debug.c\n create mode 100644 lib/librte_graph/graph_ops.c\n create mode 100644 lib/librte_graph/graph_populate.c\n create mode 100644 lib/librte_graph/graph_private.h\n create mode 100644 lib/librte_graph/graph_stats.c\n create mode 100644 lib/librte_graph/meson.build\n create mode 100644 lib/librte_graph/node.c\n create mode 100644 lib/librte_graph/rte_graph.h\n create mode 100644 lib/librte_graph/rte_graph_version.map\n create mode 100644 lib/librte_graph/rte_graph_worker.h",
    "diff": "diff --git a/app/test/meson.build b/app/test/meson.build\nindex 22b0cefaa..e1cdae3cb 100644\n--- a/app/test/meson.build\n+++ b/app/test/meson.build\n@@ -160,6 +160,7 @@ test_deps = ['acl',\n \t'ring',\n \t'stack',\n \t'timer'\n+\t'graph',\n ]\n \n fast_test_names = [\ndiff --git a/config/common_base b/config/common_base\nindex c897dd0ae..badcc0be5 100644\n--- a/config/common_base\n+++ b/config/common_base\n@@ -1070,6 +1070,13 @@ CONFIG_RTE_LIBRTE_BPF_ELF=n\n #\n CONFIG_RTE_LIBRTE_IPSEC=y\n \n+#\n+# Compile librte_graph\n+#\n+CONFIG_RTE_LIBRTE_GRAPH=y\n+CONFIG_RTE_GRAPH_BURST_SIZE=256\n+CONFIG_RTE_LIBRTE_GRAPH_STATS=y\n+CONFIG_RTE_LIBRTE_GRAPH_DEBUG=n\n #\n # Compile the test application\n #\ndiff --git a/config/rte_config.h b/config/rte_config.h\nindex d30786bc0..e9201fd46 100644\n--- a/config/rte_config.h\n+++ b/config/rte_config.h\n@@ -98,6 +98,10 @@\n /* KNI defines */\n #define RTE_KNI_PREEMPT_DEFAULT 1\n \n+/* rte_graph defines */\n+#define RTE_GRAPH_BURST_SIZE 256\n+#define RTE_LIBRTE_GRAPH_STATS 1\n+\n /****** driver defines ********/\n \n /* QuickAssist device */\ndiff --git a/lib/Makefile b/lib/Makefile\nindex 46b91ae1a..495f572bf 100644\n--- a/lib/Makefile\n+++ b/lib/Makefile\n@@ -119,6 +119,8 @@ DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev\n DIRS-$(CONFIG_RTE_LIBRTE_RCU) += librte_rcu\n DEPDIRS-librte_rcu := librte_eal\n \n+DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph\n+DEPDIRS-librte_graph := librte_eal librte_mbuf\n ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)\n DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni\n endif\ndiff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile\nnew file mode 100644\nindex 000000000..967c8d9bc\n--- /dev/null\n+++ b/lib/librte_graph/Makefile\n@@ -0,0 +1,28 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2020 Marvell International Ltd.\n+#\n+\n+include $(RTE_SDK)/mk/rte.vars.mk\n+\n+# library name\n+LIB = librte_graph.a\n+\n+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API\n+CFLAGS += $(WERROR_FLAGS)\n+LDLIBS += -lrte_eal\n+\n+EXPORT_MAP := rte_graph_version.map\n+\n+# all source are stored in SRCS-y\n+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c\n+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c\n+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c\n+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c\n+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_stats.c\n+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c\n+\n+# install header files\n+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h\n+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph_worker.h\n+\n+include $(RTE_SDK)/mk/rte.lib.mk\ndiff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c\nnew file mode 100644\nindex 000000000..0bdd6e1c0\n--- /dev/null\n+++ b/lib/librte_graph/graph.c\n@@ -0,0 +1,578 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <stdbool.h>\n+#include <fnmatch.h>\n+\n+#include <rte_common.h>\n+#include <rte_debug.h>\n+#include <rte_errno.h>\n+#include <rte_graph.h>\n+#include <rte_spinlock.h>\n+#include <rte_string_fns.h>\n+#include <rte_malloc.h>\n+#include <rte_memzone.h>\n+\n+#include \"graph_private.h\"\n+\n+static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list);\n+static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;\n+static rte_graph_t graph_id;\n+int rte_graph_logtype;\n+\n+#define graph_id_check(id) id_check(id, graph_id)\n+\n+/* Private functions */\n+struct graph_head *\n+graph_list_head_get(void)\n+{\n+\treturn &graph_list;\n+}\n+\n+void\n+graph_spinlock_lock(void)\n+{\n+\trte_spinlock_lock(&graph_lock);\n+}\n+\n+void\n+graph_spinlock_unlock(void)\n+{\n+\trte_spinlock_unlock(&graph_lock);\n+}\n+\n+static int\n+graph_node_add(struct graph *graph, struct node *node)\n+{\n+\tstruct graph_node *graph_node;\n+\tsize_t sz;\n+\n+\t/* Skip the duplicate nodes */\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next)\n+\t\tif (strncmp(node->name, graph_node->node->name,\n+\t\t\t    RTE_NODE_NAMESIZE) == 0)\n+\t\t\treturn 0;\n+\n+\t/* Allocate new graph node object */\n+\tsz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *);\n+\tgraph_node = calloc(1, sz);\n+\n+\tif (graph_node == NULL)\n+\t\tset_err(ENOMEM, free, \"failed to calloc %s object\", node->name);\n+\n+\t/* Initialize the graph node */\n+\tgraph_node->node = node;\n+\n+\t/* Add to graph node list */\n+\tSTAILQ_INSERT_TAIL(&graph->node_list, graph_node, next);\n+\treturn 0;\n+\n+free:\n+\tfree(graph_node);\n+\treturn -rte_errno;\n+}\n+\n+static struct graph_node *\n+node_to_graph_node(struct graph *graph, struct node *node)\n+{\n+\tstruct graph_node *graph_node;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next)\n+\t\tif (graph_node->node == node)\n+\t\t\treturn graph_node;\n+\n+\tset_err(ENODEV, fail, \"found isolated node %s\", node->name);\n+fail:\n+\treturn NULL;\n+}\n+\n+static int\n+graph_node_edges_add(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\tstruct node *adjacency;\n+\tconst char *next;\n+\trte_edge_t i;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next) {\n+\t\tfor (i = 0; i < graph_node->node->nb_edges; i++) {\n+\t\t\tnext = graph_node->node->next_nodes[i];\n+\t\t\tadjacency = node_from_name(next);\n+\t\t\tif (adjacency == NULL)\n+\t\t\t\tset_err(EINVAL, fail, \"node %s not registered\",\n+\t\t\t\t\tnext);\n+\t\t\tif (graph_node_add(graph, adjacency))\n+\t\t\t\tgoto fail;\n+\t\t}\n+\t}\n+\treturn 0;\n+fail:\n+\treturn -rte_errno;\n+}\n+\n+static int\n+graph_adjacency_list_update(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node, *tmp;\n+\tstruct node *adjacency;\n+\tconst char *next;\n+\trte_edge_t i;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next) {\n+\t\tfor (i = 0; i < graph_node->node->nb_edges; i++) {\n+\t\t\tnext = graph_node->node->next_nodes[i];\n+\t\t\tadjacency = node_from_name(next);\n+\t\t\tif (adjacency == NULL)\n+\t\t\t\tset_err(EINVAL, fail, \"node %s not registered\",\n+\t\t\t\t\tnext);\n+\t\t\ttmp = node_to_graph_node(graph, adjacency);\n+\t\t\tif (tmp == NULL)\n+\t\t\t\tgoto fail;\n+\t\t\tgraph_node->adjacency_list[i] = tmp;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+fail:\n+\treturn -rte_errno;\n+}\n+\n+static int\n+expand_pattern_to_node(struct graph *graph, const char *pattern)\n+{\n+\tstruct node_head *node_head = node_list_head_get();\n+\tbool found = false;\n+\tstruct node *node;\n+\n+\t/* Check for pattern match */\n+\tSTAILQ_FOREACH(node, node_head, next) {\n+\t\tif (fnmatch(pattern, node->name, 0) == 0) {\n+\t\t\tif (graph_node_add(graph, node))\n+\t\t\t\tgoto fail;\n+\t\t\tfound = true;\n+\t\t}\n+\t}\n+\tif (found == false)\n+\t\tset_err(EFAULT, fail, \"pattern %s node not found\", pattern);\n+\n+\treturn 0;\n+fail:\n+\treturn -rte_errno;\n+}\n+\n+static void\n+graph_cleanup(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\n+\twhile (!STAILQ_EMPTY(&graph->node_list)) {\n+\t\tgraph_node = STAILQ_FIRST(&graph->node_list);\n+\t\tSTAILQ_REMOVE_HEAD(&graph->node_list, next);\n+\t\tfree(graph_node);\n+\t}\n+}\n+\n+static int\n+graph_node_init(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\tconst char *name;\n+\tint rc;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next) {\n+\t\tif (graph_node->node->init) {\n+\t\t\tname = graph_node->node->name;\n+\t\t\trc = graph_node->node->init(graph->graph,\n+\t\t\t\tgraph_node_name_to_ptr(graph->graph, name));\n+\t\t\tif (rc)\n+\t\t\t\tset_err(rc, err, \"node %s init() failed\", name);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+err:\n+\treturn -rte_errno;\n+}\n+\n+static void\n+graph_node_fini(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next)\n+\t\tif (graph_node->node->fini)\n+\t\t\tgraph_node->node->fini(graph->graph,\n+\t\t\tgraph_node_name_to_ptr(graph->graph,\n+\t\t\tgraph_node->node->name));\n+}\n+\n+static struct rte_graph *\n+graph_mem_fixup_node_ctx(struct rte_graph *graph)\n+{\n+\trte_node_t count; rte_graph_off_t off; struct rte_node *node;\n+\tstruct node *node_db;\n+\tconst char *name;\n+\n+\trte_graph_foreach_node(count, off, graph, node) {\n+\t\tif (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */\n+\t\t\tname = node->name;\n+\t\telse /* Cloned node */\n+\t\t\tname = node->parent;\n+\n+\t\tnode_db = node_from_name(name);\n+\t\tif (node_db == NULL)\n+\t\t\tset_err(ENOLINK, fail, \"node %s not found\", name);\n+\t\tnode->process = node_db->process;\n+\t}\n+\n+\treturn graph;\n+fail:\n+\treturn NULL;\n+}\n+\n+static struct rte_graph *\n+graph_mem_fixup_secondray(struct rte_graph *graph)\n+{\n+\tif (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)\n+\t\treturn graph;\n+\n+\treturn graph_mem_fixup_node_ctx(graph);\n+}\n+\n+struct rte_graph *\n+rte_graph_lookup(const char *name)\n+{\n+\tconst struct rte_memzone *mz;\n+\tstruct rte_graph *rc = NULL;\n+\n+\tmz = rte_memzone_lookup(name);\n+\tif (mz)\n+\t\trc = mz->addr;\n+\n+\treturn graph_mem_fixup_secondray(rc);\n+}\n+\n+rte_graph_t\n+rte_graph_create(const char *name, struct rte_graph_param *prm)\n+{\n+\tstruct graph *graph;\n+\tconst char *pattern;\n+\tuint16_t i;\n+\n+\tgraph_spinlock_lock();\n+\n+\t/* Check arguments sanity */\n+\tif (prm == NULL)\n+\t\tset_err(EINVAL, fail, \"param should not be NULL\");\n+\n+\tif (name == NULL)\n+\t\tset_err(EINVAL, fail, \"graph name should not be NULL\");\n+\n+\t/* Check for existence of duplicate graph */\n+\tSTAILQ_FOREACH(graph, &graph_list, next)\n+\t\tif (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)\n+\t\t\tset_err(EEXIST, fail, \"found duplicate graph %s\", name);\n+\n+\t/* Create graph object */\n+\tgraph = calloc(1, sizeof(*graph));\n+\tif (graph == NULL)\n+\t\tset_err(ENOMEM, fail, \"failed to calloc graph object\");\n+\n+\t/* Initilze the graph object */\n+\tSTAILQ_INIT(&graph->node_list);\n+\tif (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)\n+\t\tset_err(E2BIG, free, \"too big name=%s\", name);\n+\n+\t/* Expand node pattern and add the nodes to the graph */\n+\tfor (i = 0; i < prm->nb_node_patterns; i++) {\n+\t\tpattern = prm->node_patterns[i];\n+\t\tif (expand_pattern_to_node(graph, pattern))\n+\t\t\tgoto graph_cleanup;\n+\t}\n+\n+\t/* Go over all the nodes edges and add them to the graph */\n+\tif (graph_node_edges_add(graph))\n+\t\tgoto graph_cleanup;\n+\n+\t/* Update adjacency list of all nodes in the graph */\n+\tif (graph_adjacency_list_update(graph))\n+\t\tgoto graph_cleanup;\n+\n+\t/* Make sure atleast a source node present in the graph */\n+\tif (!graph_src_nodes_count(graph))\n+\t\tgoto graph_cleanup;\n+\n+\t/* Make sure no node is pointing to source node */\n+\tif (graph_node_has_edge_to_src_node(graph))\n+\t\tgoto graph_cleanup;\n+\n+\t/* Don't allow node has loop to self */\n+\tif (graph_node_has_loop_edge(graph))\n+\t\tgoto graph_cleanup;\n+\n+\t/* Do BFS from src nodes on the graph to find isolated nodes */\n+\tif (graph_has_isolated_node(graph))\n+\t\tgoto graph_cleanup;\n+\n+\t/* Initlize graph object */\n+\tgraph->socket = prm->socket_id;\n+\tgraph->src_node_count = graph_src_nodes_count(graph);\n+\tgraph->node_count = graph_nodes_count(graph);\n+\tgraph->id = graph_id;\n+\n+\t/* Allocate the Graph fast path memory and populate the data */\n+\tif (graph_fp_mem_create(graph))\n+\t\tgoto graph_cleanup;\n+\n+\t/* Call init() of the all the nodes in the graph */\n+\tif (graph_node_init(graph))\n+\t\tgoto graph_mem_destroy;\n+\n+\t/* All good, Lets add the graph to the list */\n+\tgraph_id++;\n+\tSTAILQ_INSERT_TAIL(&graph_list, graph, next);\n+\n+\tgraph_spinlock_unlock();\n+\treturn graph->id;\n+\n+graph_mem_destroy:\n+\tgraph_fp_mem_destroy(graph);\n+graph_cleanup:\n+\tgraph_cleanup(graph);\n+free:\n+\tfree(graph);\n+fail:\n+\tgraph_spinlock_unlock();\n+\treturn RTE_GRAPH_ID_INVALID;\n+}\n+\n+rte_graph_t\n+rte_graph_destroy(const char *graph_name)\n+{\n+\trte_graph_t rc = RTE_GRAPH_ID_INVALID;\n+\tstruct graph *graph, *tmp;\n+\tconst char *name;\n+\n+\tgraph_spinlock_lock();\n+\n+\tgraph = STAILQ_FIRST(&graph_list);\n+\twhile (graph != NULL) {\n+\t\ttmp = STAILQ_NEXT(graph, next);\n+\t\tname = graph->name;\n+\t\tif (strncmp(name, graph_name, RTE_GRAPH_NAMESIZE) == 0) {\n+\t\t\t/* Call fini() of the all the nodes in the graph */\n+\t\t\tgraph_node_fini(graph);\n+\t\t\t/* Destroy graph fast path memory */\n+\t\t\trc = graph_fp_mem_destroy(graph);\n+\t\t\tif (rc)\n+\t\t\t\tset_err(rc, done, \"mz %s free failed\", name);\n+\n+\t\t\tgraph_cleanup(graph);\n+\t\t\tSTAILQ_REMOVE(&graph_list, graph, graph, next);\n+\t\t\trc = graph->id;\n+\t\t\tfree(graph);\n+\t\t\tgraph_id--;\n+\t\t\tgoto done;\n+\t\t}\n+\t\tgraph = tmp;\n+\t}\n+done:\n+\tgraph_spinlock_unlock();\n+\treturn rc;\n+}\n+\n+rte_graph_t\n+rte_graph_from_name(const char *name)\n+{\n+\tstruct graph *graph;\n+\n+\tSTAILQ_FOREACH(graph, &graph_list, next)\n+\t\tif (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0)\n+\t\t\treturn graph->id;\n+\n+\treturn RTE_GRAPH_ID_INVALID;\n+}\n+\n+char *\n+rte_graph_id_to_name(rte_graph_t id)\n+{\n+\tstruct graph *graph;\n+\n+\tgraph_id_check(id);\n+\tSTAILQ_FOREACH(graph, &graph_list, next)\n+\t\tif (graph->id == id)\n+\t\t\treturn graph->name;\n+\n+fail:\n+\treturn NULL;\n+}\n+\n+struct rte_node *rte_graph_node_get(rte_graph_t gid, uint32_t nid)\n+{\n+\tstruct rte_node *node;\n+\tstruct graph *graph;\n+\trte_graph_off_t off;\n+\trte_node_t count;\n+\n+\tgraph_id_check(gid);\n+\tSTAILQ_FOREACH(graph, &graph_list, next)\n+\t\tif (graph->id == gid) {\n+\t\t\trte_graph_foreach_node(count, off, graph->graph, node) {\n+\t\t\t\tif (node->id == nid)\n+\t\t\t\t\treturn node;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+fail:\n+\treturn NULL;\n+}\n+\n+struct rte_node *rte_graph_node_get_by_name(const char *graph_name,\n+\t\t\t\t\t    const char *node_name)\n+{\n+\tstruct rte_node *node;\n+\tstruct graph *graph;\n+\trte_graph_off_t off;\n+\trte_node_t count;\n+\n+\tSTAILQ_FOREACH(graph, &graph_list, next)\n+\t\tif (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) {\n+\t\t\trte_graph_foreach_node(count, off, graph->graph, node) {\n+\t\t\t\tif (!strncmp(node->name, node_name,\n+\t\t\t\t\t     RTE_NODE_NAMESIZE))\n+\t\t\t\t\treturn node;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\n+\treturn NULL;\n+}\n+\n+__rte_experimental void __rte_noinline\n+__rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)\n+{\n+\tuint16_t size = node->size;\n+\n+\tRTE_VERIFY(size != UINT16_MAX);\n+\t/* Allocate double amount of size to avoid immediate realloc */\n+\tsize = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2));\n+\tnode->objs = rte_realloc_socket(node->objs, size * sizeof(void *),\n+\t\t\t\t\tRTE_CACHE_LINE_SIZE, graph->socket);\n+\tRTE_VERIFY(node->objs);\n+\tnode->size = size;\n+\tnode->realloc_count++;\n+}\n+\n+__rte_experimental void __rte_noinline\n+__rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node,\n+\t\t\t     uint16_t req_size)\n+{\n+\tuint16_t size = node->size;\n+\n+\tRTE_VERIFY(size != UINT16_MAX);\n+\t/* Allocate double amount of size to avoid immediate realloc */\n+\tsize = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2));\n+\tnode->objs = rte_realloc_socket(node->objs, size * sizeof(void *),\n+\t\t\t\t\tRTE_CACHE_LINE_SIZE, graph->socket);\n+\tRTE_VERIFY(node->objs);\n+\tnode->size = size;\n+\tnode->realloc_count++;\n+}\n+\n+static int\n+graph_to_dot(FILE *f, struct graph *graph)\n+{\n+\tconst char *src_edge_color = \" [color=blue]\\n\";\n+\tconst char *edge_color = \"\\n\";\n+\tstruct graph_node *graph_node;\n+\tchar *node_name;\n+\trte_edge_t i;\n+\tint rc;\n+\n+\trc = fprintf(f, \"digraph %s {\\n\\trankdir=LR;\\n\", graph->name);\n+\tif (rc < 0)\n+\t\tgoto end;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next) {\n+\t\tnode_name = graph_node->node->name;\n+\t\tfor (i = 0; i < graph_node->node->nb_edges; i++) {\n+\t\t\trc = fprintf(f, \"\\t\\\"%s\\\"->\\\"%s\\\"%s\", node_name,\n+\t\t\t\tgraph_node->adjacency_list[i]->node->name,\n+\t\t\t\tgraph_node->node->flags & RTE_NODE_SOURCE_F ?\n+\t\t\t\tsrc_edge_color : edge_color);\n+\t\t\tif (rc < 0)\n+\t\t\t\tgoto end;\n+\t\t}\n+\t}\n+\trc = fprintf(f, \"}\\n\");\n+\tif (rc < 0)\n+\t\tgoto end;\n+\n+\treturn 0;\n+end:\n+\trte_errno = EBADF;\n+\treturn -rte_errno;\n+}\n+\n+rte_graph_t\n+rte_graph_export(const char *name, FILE *f)\n+{\n+\trte_graph_t rc = RTE_GRAPH_ID_INVALID;\n+\tstruct graph *graph;\n+\n+\tSTAILQ_FOREACH(graph, &graph_list, next) {\n+\t\tif (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) {\n+\t\t\trc = graph_to_dot(f, graph);\n+\t\t\tgoto end;\n+\t\t}\n+\t}\n+\trte_errno = ENOENT;\n+end:\n+\treturn rc;\n+}\n+\n+static void\n+graph_scan_dump(FILE *f, rte_graph_t id, bool all)\n+{\n+\tstruct graph *graph;\n+\n+\tRTE_VERIFY(f);\n+\tgraph_id_check(id);\n+\n+\tSTAILQ_FOREACH(graph, &graph_list, next) {\n+\t\tif (all == true) {\n+\t\t\tgraph_dump(f, graph);\n+\t\t} else if (graph->id == id) {\n+\t\t\tgraph_dump(f, graph);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+fail:\n+\treturn;\n+}\n+\n+void\n+rte_graph_dump(FILE *f, rte_graph_t id)\n+{\n+\tgraph_scan_dump(f, id, false);\n+}\n+\n+void\n+rte_graph_list_dump(FILE *f)\n+{\n+\tgraph_scan_dump(f, 0, true);\n+}\n+\n+RTE_INIT(rte_graph_init_log)\n+{\n+\trte_graph_logtype = rte_log_register(\"lib.graph\");\n+\tif (rte_graph_logtype >= 0)\n+\t\trte_log_set_level(rte_graph_logtype, RTE_LOG_INFO);\n+}\n+\n+rte_graph_t\n+rte_graph_max_count(void)\n+{\n+\treturn graph_id;\n+}\ndiff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c\nnew file mode 100644\nindex 000000000..7d42889fb\n--- /dev/null\n+++ b/lib/librte_graph/graph_debug.c\n@@ -0,0 +1,81 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <rte_common.h>\n+#include <rte_debug.h>\n+\n+#include \"graph_private.h\"\n+\n+void\n+graph_dump(FILE *f, struct graph *g)\n+{\n+\tstruct graph_node *graph_node;\n+\trte_edge_t i = 0;\n+\n+\tfprintf(f, \"graph <%s>\\n\", g->name);\n+\tfprintf(f, \"  id=%\"PRIu32\"\\n\", g->id);\n+\tfprintf(f, \"  cir_start=%\"PRIu32\"\\n\", g->cir_start);\n+\tfprintf(f, \"  cir_mask=%\"PRIu32\"\\n\", g->cir_mask);\n+\tfprintf(f, \"  addr=%p\\n\", g);\n+\tfprintf(f, \"  graph=%p\\n\", g->graph);\n+\tfprintf(f, \"  mem_sz=%zu\\n\", g->mem_sz);\n+\tfprintf(f, \"  node_count=%\"PRIu32\"\\n\", g->node_count);\n+\tfprintf(f, \"  src_node_count=%\"PRIu32\"\\n\", g->src_node_count);\n+\n+\tSTAILQ_FOREACH(graph_node, &g->node_list, next)\n+\t\tfprintf(f, \"     node[%d] <%s>\\n\", i++, graph_node->node->name);\n+}\n+\n+void\n+node_dump(FILE *f, struct node *n)\n+{\n+\trte_edge_t i;\n+\n+\tfprintf(f, \"node <%s>\\n\", n->name);\n+\tfprintf(f, \"  id=%\"PRIu32\"\\n\", n->id);\n+\tfprintf(f, \"  flags=0x%\" PRIx64 \"\\n\", n->flags);\n+\tfprintf(f, \"  addr=%p\\n\", n);\n+\tfprintf(f, \"  process=%p\\n\", n->process);\n+\tfprintf(f, \"  nb_edges=%d\\n\", n->nb_edges);\n+\n+\tfor (i = 0; i < n->nb_edges; i++)\n+\t\tfprintf(f, \"     edge[%d] <%s>\\n\", i, n->next_nodes[i]);\n+}\n+\n+void\n+rte_graph_obj_dump(FILE *f, struct  rte_graph *g, bool all)\n+{\n+\trte_node_t count; rte_graph_off_t off; struct rte_node *n; rte_edge_t i;\n+\n+\tfprintf(f, \"graph <%s> @ %p\\n\", g->name, g);\n+\tfprintf(f, \"  id=%\"PRIu32\"\\n\", g->id);\n+\tfprintf(f, \"  head=%\"PRId32\"\\n\", (int32_t)g->head);\n+\tfprintf(f, \"  tail=%\"PRId32\"\\n\", (int32_t)g->tail);\n+\tfprintf(f, \"  cir_mask=0x%\"PRIx32\"\\n\", g->cir_mask);\n+\tfprintf(f, \"  nb_nodes=%\"PRId32\"\\n\", g->nb_nodes);\n+\tfprintf(f, \"  socket=%d\\n\", g->socket);\n+\tfprintf(f, \"  fence=0x%\" PRIx64 \"\\n\", g->fence);\n+\tfprintf(f, \"  nodes_start=0x%\"PRIx32\"\\n\", g->nodes_start);\n+\tfprintf(f, \"  cir_start=%p\\n\", g->cir_start);\n+\n+\trte_graph_foreach_node(count, off, g, n) {\n+\t\tif (!all && n->idx == 0)\n+\t\t\tcontinue;\n+\t\tfprintf(f, \"     node[%d] <%s>\\n\", count, n->name);\n+\t\tfprintf(f, \"       fence=0x%\" PRIx64 \"\\n\", n->fence);\n+\t\tfprintf(f, \"       objs=%p\\n\", n->objs);\n+\t\tfprintf(f, \"       process=%p\\n\", n->process);\n+\t\tfprintf(f, \"       id=0x%\" PRIx32 \"\\n\", n->id);\n+\t\tfprintf(f, \"       offset=0x%\" PRIx32 \"\\n\", n->off);\n+\t\tfprintf(f, \"       nb_edges=%\" PRId32 \"\\n\", n->nb_edges);\n+\t\tfprintf(f, \"       realloc_count=%d\\n\", n->realloc_count);\n+\t\tfprintf(f, \"       size=%d\\n\", n->size);\n+\t\tfprintf(f, \"       idx=%d\\n\", n->idx);\n+\t\tfprintf(f, \"       total_objs=%\" PRId64 \"\\n\", n->total_objs);\n+\t\tfprintf(f, \"       total_calls=%\" PRId64 \"\\n\", n->total_calls);\n+\t\tfor (i = 0; i < n->nb_edges; i++)\n+\t\t\tfprintf(f, \"          edge[%d] <%s>\\n\",\n+\t\t\t\ti, n->nodes[i]->name);\n+\t}\n+}\ndiff --git a/lib/librte_graph/graph_ops.c b/lib/librte_graph/graph_ops.c\nnew file mode 100644\nindex 000000000..6f5c69599\n--- /dev/null\n+++ b/lib/librte_graph/graph_ops.c\n@@ -0,0 +1,163 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <stdbool.h>\n+#include <string.h>\n+\n+#include <rte_common.h>\n+#include <rte_errno.h>\n+\n+#include \"graph_private.h\"\n+\n+static inline int\n+node_has_loop_edge(struct node *node)\n+{\n+\trte_edge_t i;\n+\tchar *name;\n+\tint rc = 0;\n+\n+\tfor (i = 0; i < node->nb_edges; i++) {\n+\t\tif (strncmp(node->name, node->next_nodes[i],\n+\t\t\t    RTE_NODE_NAMESIZE) == 0) {\n+\t\t\tname =  node->name;\n+\t\t\trc = 1;\n+\t\t\tset_err(EINVAL, fail, \"node %s has loop to self\", name);\n+\t\t}\n+\t}\n+fail:\n+\treturn rc;\n+}\n+\n+int\n+graph_node_has_loop_edge(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next)\n+\t\tif (node_has_loop_edge(graph_node->node))\n+\t\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n+rte_node_t\n+graph_src_nodes_count(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\trte_node_t rc = 0;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next)\n+\t\tif (graph_node->node->flags & RTE_NODE_SOURCE_F)\n+\t\t\trc++;\n+\n+\tif (rc == 0)\n+\t\tset_err(EINVAL, fail, \"graph needs atleast a source node\");\n+fail:\n+\treturn rc;\n+}\n+\n+int\n+graph_node_has_edge_to_src_node(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\tstruct node *node;\n+\trte_edge_t i;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next) {\n+\t\tfor (i = 0; i < graph_node->node->nb_edges; i++) {\n+\t\t\tnode = graph_node->adjacency_list[i]->node;\n+\t\t\tif (node->flags & RTE_NODE_SOURCE_F)\n+\t\t\t\tset_err(EEXIST, fail,\n+\t\t\t\t\"node %s points to the source node %s\",\n+\t\t\t\tgraph_node->node->name, node->name);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+fail:\n+\treturn 1;\n+}\n+\n+rte_node_t\n+graph_nodes_count(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\trte_node_t count = 0;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next)\n+\t\tcount++;\n+\n+\treturn count;\n+}\n+\n+void\n+graph_mark_nodes_as_not_visited(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next)\n+\t\tgraph_node->visited = false;\n+}\n+\n+int\n+graph_bfs(struct graph *graph, struct graph_node *start)\n+{\n+\tstruct graph_node **queue, *v, *tmp;\n+\tuint16_t head = 0, tail = 0;\n+\trte_edge_t i;\n+\tsize_t sz;\n+\n+\tsz = sizeof(struct graph_node *) * graph_nodes_count(graph);\n+\tqueue = malloc(sz);\n+\tif (queue == NULL)\n+\t\tset_err(ENOMEM, fail, \"failed to alloc bfs queue of %zu\", sz);\n+\n+\t/* BFS algorithm */\n+\tqueue[tail++] = start;\n+\tstart->visited = true;\n+\twhile (head != tail) {\n+\t\tv = queue[head++];\n+\t\tfor (i = 0; i < v->node->nb_edges; i++) {\n+\t\t\ttmp = v->adjacency_list[i];\n+\t\t\tif (tmp->visited == false) {\n+\t\t\t\tqueue[tail++] = tmp;\n+\t\t\t\ttmp->visited = true;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfree(queue);\n+\treturn 0;\n+\n+fail:\n+\treturn -rte_errno;\n+}\n+\n+int\n+graph_has_isolated_node(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\n+\tgraph_mark_nodes_as_not_visited(graph);\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next) {\n+\t\tif (graph_node->node->flags & RTE_NODE_SOURCE_F) {\n+\t\t\tif (graph_node->node->nb_edges == 0)\n+\t\t\t\tset_err(EINVAL, fail,\n+\t\t\t\t\"%s node needs minimum one edge\",\n+\t\t\t\tgraph_node->node->name);\n+\t\t\tif (graph_bfs(graph, graph_node))\n+\t\t\t\tgoto fail;\n+\t\t}\n+\t}\n+\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next)\n+\t\tif (graph_node->visited == false)\n+\t\t\tset_err(EINVAL, fail, \"found isolated node %s\",\n+\t\t\t\tgraph_node->node->name);\n+\n+\treturn 0;\n+fail:\n+\treturn 1;\n+}\ndiff --git a/lib/librte_graph/graph_populate.c b/lib/librte_graph/graph_populate.c\nnew file mode 100644\nindex 000000000..72476db11\n--- /dev/null\n+++ b/lib/librte_graph/graph_populate.c\n@@ -0,0 +1,224 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <stdbool.h>\n+#include <fnmatch.h>\n+\n+#include <rte_common.h>\n+#include <rte_errno.h>\n+#include <rte_memzone.h>\n+#include <rte_malloc.h>\n+\n+#include \"graph_private.h\"\n+\n+static size_t\n+graph_fp_mem_calc_size(struct graph *graph)\n+{\n+\tstruct graph_node *graph_node;\n+\trte_node_t val;\n+\tsize_t sz;\n+\n+\t/* Graph header */\n+\tsz = sizeof(struct rte_graph);\n+\t/* Source nodes list */\n+\tsz += sizeof(rte_graph_off_t) * graph->src_node_count;\n+\t/* Circular buffer for pending streams of size number of nodes */\n+\tval = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));\n+\tsz = RTE_ALIGN(sz, val);\n+\tgraph->cir_start = sz;\n+\tgraph->cir_mask = rte_align32pow2(graph->node_count) -  1;\n+\tsz += val;\n+\t/* Fence */\n+\tsz += sizeof(RTE_GRAPH_FENCE);\n+\tsz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);\n+\tgraph->nodes_start = sz;\n+\t/* For 0..N node objects with fence */\n+\tSTAILQ_FOREACH(graph_node, &graph->node_list, next) {\n+\t\tsz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);\n+\t\tsz += sizeof(struct rte_node);\n+\t\t/* Pointer to next nodes(edges) */\n+\t\tsz += sizeof(struct rte_node *) * graph_node->node->nb_edges;\n+\t}\n+\n+\tgraph->mem_sz = sz;\n+\treturn sz;\n+}\n+\n+static void\n+graph_header_popluate(struct graph *_graph)\n+{\n+\tstruct rte_graph *graph = _graph->graph;\n+\n+\tgraph->tail = 0;\n+\tgraph->head = (int32_t)-_graph->src_node_count;\n+\tgraph->cir_mask = _graph->cir_mask;\n+\tgraph->nb_nodes = _graph->node_count;\n+\tgraph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);\n+\tgraph->nodes_start = _graph->nodes_start;\n+\tgraph->socket = _graph->socket;\n+\tgraph->id = _graph->id;\n+\tmemcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);\n+\tgraph->fence = RTE_GRAPH_FENCE;\n+}\n+\n+static void\n+graph_nodes_populate(struct graph *_graph)\n+{\n+\trte_graph_off_t off = _graph->nodes_start;\n+\tstruct rte_graph *graph = _graph->graph;\n+\tstruct graph_node *graph_node;\n+\trte_edge_t count, nb_edges;\n+\tconst char *parent;\n+\trte_node_t pid;\n+\n+\tSTAILQ_FOREACH(graph_node, &_graph->node_list, next) {\n+\t\tstruct rte_node *node = RTE_PTR_ADD(graph, off);\n+\t\tmemset(node, 0, sizeof(*node));\n+\t\tnode->fence = RTE_GRAPH_FENCE;\n+\t\tnode->off = off;\n+\t\tnode->process = graph_node->node->process;\n+\t\tmemcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);\n+\t\tpid = graph_node->node->parent_id;\n+\t\tif (pid != RTE_NODE_ID_INVALID) { /* Cloned node */\n+\t\t\tparent = rte_node_id_to_name(pid);\n+\t\t\tmemcpy(node->parent, parent, RTE_GRAPH_NAMESIZE);\n+\t\t}\n+\t\tnode->id = graph_node->node->id;\n+\t\tnode->parent_id = pid;\n+\t\tnb_edges = graph_node->node->nb_edges;\n+\t\tnode->nb_edges = nb_edges;\n+\t\toff += sizeof(struct rte_node);\n+\t\t/* Copy the name in first pass to replace with rte_node* later*/\n+\t\tfor (count = 0; count < nb_edges; count++)\n+\t\t\tnode->nodes[count] = (struct rte_node *)\n+\t\t\t&graph_node->adjacency_list[count]->node->name[0];\n+\n+\t\toff += sizeof(struct rte_node *) * nb_edges;\n+\t\toff = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);\n+\t\tnode->next = off;\n+\t\t__rte_node_stream_alloc(graph, node);\n+\t}\n+}\n+\n+struct rte_node *\n+graph_node_id_to_ptr(const struct rte_graph *graph, rte_node_t id)\n+{\n+\trte_node_t count; rte_graph_off_t off; struct rte_node *node;\n+\n+\trte_graph_foreach_node(count, off, graph, node)\n+\t\tif (unlikely(node->id == id))\n+\t\t\treturn node;\n+\n+\treturn NULL;\n+}\n+\n+struct rte_node *\n+graph_node_name_to_ptr(const struct rte_graph *graph, const char *name)\n+{\n+\trte_node_t count; rte_graph_off_t off; struct rte_node *node;\n+\n+\trte_graph_foreach_node(count, off, graph, node)\n+\t\tif (strncmp(name, node->name, RTE_NODE_NAMESIZE) == 0)\n+\t\t\treturn node;\n+\n+\treturn NULL;\n+}\n+\n+static int\n+graph_node_nexts_populate(struct graph *_graph)\n+{\n+\trte_node_t count, val; rte_graph_off_t off; struct rte_node *node;\n+\tconst struct rte_graph *graph = _graph->graph;\n+\tconst char *name;\n+\n+\trte_graph_foreach_node(count, off, graph, node) {\n+\t\tfor (val = 0; val < node->nb_edges; val++) {\n+\t\t\tname = (const char *)node->nodes[val];\n+\t\t\tnode->nodes[val] = graph_node_name_to_ptr(graph, name);\n+\t\t\tif (node->nodes[val] == NULL)\n+\t\t\t\tset_err(EINVAL, fail, \"%s not found\", name);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+fail:\n+\treturn -rte_errno;\n+}\n+\n+static int\n+graph_src_nodes_populate(struct graph *_graph)\n+{\n+\tstruct rte_graph *graph = _graph->graph;\n+\tstruct graph_node *graph_node;\n+\tstruct rte_node *node;\n+\tint32_t head = -1;\n+\tconst char *name;\n+\n+\tSTAILQ_FOREACH(graph_node, &_graph->node_list, next) {\n+\t\tif (graph_node->node->flags & RTE_NODE_SOURCE_F) {\n+\t\t\tname = graph_node->node->name;\n+\t\t\tnode = graph_node_name_to_ptr(graph, name);\n+\t\t\tif (node == NULL)\n+\t\t\t\tset_err(EINVAL, fail, \"%s not found\", name);\n+\n+\t\t\t__rte_node_stream_alloc(graph, node);\n+\t\t\tgraph->cir_start[head--] = node->off;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+fail:\n+\treturn -rte_errno;\n+}\n+\n+static int\n+graph_fp_mem_populate(struct graph *graph)\n+{\n+\tint rc;\n+\n+\tgraph_header_popluate(graph);\n+\tgraph_nodes_populate(graph);\n+\trc = graph_node_nexts_populate(graph);\n+\trc |= graph_src_nodes_populate(graph);\n+\n+\treturn rc;\n+}\n+\n+int\n+graph_fp_mem_create(struct graph *graph)\n+{\n+\tconst struct rte_memzone *mz;\n+\tsize_t sz;\n+\n+\tsz = graph_fp_mem_calc_size(graph);\n+\tmz = rte_memzone_reserve(graph->name, sz, graph->socket, 0);\n+\tif (mz == NULL)\n+\t\tset_err(ENOMEM, fail, \"memzone %s reserve failed\", graph->name);\n+\n+\tgraph->graph = mz->addr;\n+\tgraph->mz = mz;\n+\n+\treturn graph_fp_mem_populate(graph);\n+fail:\n+\treturn -rte_errno;\n+}\n+\n+static void\n+graph_nodes_mem_destroy(struct rte_graph *graph)\n+{\n+\trte_node_t count; rte_graph_off_t off; struct rte_node *node;\n+\n+\tif (graph == NULL)\n+\t\treturn;\n+\n+\trte_graph_foreach_node(count, off, graph, node)\n+\t\trte_free(node->objs);\n+}\n+\n+int\n+graph_fp_mem_destroy(struct graph *graph)\n+{\n+\tgraph_nodes_mem_destroy(graph->graph);\n+\treturn rte_memzone_free(graph->mz);\n+}\ndiff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h\nnew file mode 100644\nindex 000000000..47c84bb15\n--- /dev/null\n+++ b/lib/librte_graph/graph_private.h\n@@ -0,0 +1,113 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#ifndef _RTE_GRAPH_PRIVATE_H_\n+#define _RTE_GRAPH_PRIVATE_H_\n+\n+#include <inttypes.h>\n+#include <sys/queue.h>\n+\n+#include <rte_common.h>\n+#include <rte_eal.h>\n+#include <rte_graph.h>\n+#include <rte_graph_worker.h>\n+\n+extern int rte_graph_logtype;\n+\n+#define GRAPH_LOG(level, ...)\t\t\t\t\t\t       \\\n+\trte_log(RTE_LOG_ ## level, rte_graph_logtype,\t\t\t       \\\n+\t\tRTE_FMT(\"GRAPH: %s():%u \" RTE_FMT_HEAD(__VA_ARGS__,)           \\\n+\t\t\t\"\\n\",  __func__, __LINE__,\t\t               \\\n+\t\t\tRTE_FMT_TAIL(__VA_ARGS__,)))\n+\n+#define graph_err(...)  GRAPH_LOG(ERR, __VA_ARGS__)\n+#define graph_info(...)\tGRAPH_LOG(INFO, __VA_ARGS__)\n+#define graph_dbg(...)\tGRAPH_LOG(DEBUG, __VA_ARGS__)\n+\n+#define id_check(id, id_max) do {\t\t\t\t\t       \\\n+\tif ((id) >= (id_max)) {\t\t\t\t\t\t       \\\n+\t\trte_errno = EINVAL;\t\t\t\t\t       \\\n+\t\tgoto fail;\t\t\t\t\t\t       \\\n+\t}\t\t\t\t\t\t\t\t       \\\n+} while (0)\n+\n+#define set_err(err, where, fmt, ...) do {\t\t\t\t       \\\n+\tgraph_err(fmt, ##__VA_ARGS__);\t\t\t\t\t       \\\n+\trte_errno = err;\t\t\t\t\t\t       \\\n+\tgoto where;\t\t\t\t\t\t\t       \\\n+} while (0)\n+\n+struct node {\n+\tSTAILQ_ENTRY(node) next;\n+\tchar name[RTE_NODE_NAMESIZE];\t/* Name of the node. */\n+\tuint64_t flags; /* Node configuration flag */\n+\trte_node_process_t process;  /* Node process function */\n+\trte_node_init_t init; /* Node init function */\n+\trte_node_fini_t fini; /* Node fini function */\n+\trte_node_t id; /* Allocated ID for this node */\n+\trte_node_t parent_id; /* Id of parent node */\n+\trte_edge_t nb_edges; /* Number of edges from this node */\n+\tchar next_nodes[][RTE_NODE_NAMESIZE]; /* Names of next nodes. */\n+};\n+\n+struct graph_node {\n+\tSTAILQ_ENTRY(graph_node) next;\n+\tstruct node *node;\n+\tbool visited;\n+\tstruct graph_node *adjacency_list[];\n+};\n+\n+struct graph {\n+\tSTAILQ_ENTRY(graph) next; /* List of graphs */\n+\tchar name[RTE_GRAPH_NAMESIZE];\t/* Name of the graph. */\n+\tconst struct rte_memzone *mz;\n+\trte_graph_off_t nodes_start;\n+\trte_node_t src_node_count;\n+\tstruct rte_graph *graph;\n+\trte_node_t node_count;\n+\tuint32_t cir_start;\n+\tuint32_t cir_mask;\n+\trte_graph_t id;\n+\tsize_t mem_sz;\n+\tint socket;\n+\tSTAILQ_HEAD(gnode_list, graph_node) node_list;/* Nodes in a graph */\n+};\n+\n+/* Node functions */\n+STAILQ_HEAD(node_head, node);\n+struct node_head *node_list_head_get(void);\n+struct node *node_from_name(const char *name);\n+\n+/* Graph list functions */\n+STAILQ_HEAD(graph_head, graph);\n+struct graph_head *graph_list_head_get(void);\n+\n+/* Lock functions */\n+void graph_spinlock_lock(void);\n+void graph_spinlock_unlock(void);\n+\n+/* Graph operations */\n+int graph_bfs(struct graph *graph, struct graph_node *start);\n+int graph_has_isolated_node(struct graph *graph);\n+int graph_node_has_edge_to_src_node(struct graph *graph);\n+int graph_node_has_loop_edge(struct graph *graph);\n+rte_node_t graph_src_nodes_count(struct graph *graph);\n+rte_node_t graph_nodes_count(struct graph *graph);\n+void graph_mark_nodes_as_not_visited(struct graph *graph);\n+\n+/* Fast path graph memory populate functions */\n+int graph_fp_mem_create(struct graph *graph);\n+int graph_fp_mem_destroy(struct graph *graph);\n+\n+/* Lookup functions */\n+struct rte_node *\n+graph_node_id_to_ptr(const struct rte_graph *graph, rte_node_t id);\n+struct rte_node *\n+graph_node_name_to_ptr(const struct rte_graph *graph, const char *node_name);\n+\n+/* Debug functions */\n+void graph_dump(FILE *f, struct graph *g);\n+void node_dump(FILE *f, struct node *n);\n+\n+#endif /* _RTE_GRAPH_PRIVATE_H_ */\ndiff --git a/lib/librte_graph/graph_stats.c b/lib/librte_graph/graph_stats.c\nnew file mode 100644\nindex 000000000..0dcbee8a8\n--- /dev/null\n+++ b/lib/librte_graph/graph_stats.c\n@@ -0,0 +1,396 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <stdbool.h>\n+#include <fnmatch.h>\n+\n+#include <rte_common.h>\n+#include <rte_errno.h>\n+#include <rte_malloc.h>\n+\n+#include \"graph_private.h\"\n+\n+/* Capture all graphs of cluster */\n+struct cluster {\n+\trte_graph_t nb_graphs;\n+\trte_graph_t size;\n+\n+\tstruct graph **graphs;\n+};\n+\n+/* Capture same node ID across cluster  */\n+struct cluster_node {\n+\tstruct rte_graph_cluster_node_stats stat;\n+\trte_node_t nb_nodes;\n+\n+\tstruct rte_node *nodes[];\n+};\n+\n+struct rte_graph_cluster_stats {\n+\t/* Header */\n+\trte_graph_cluster_stats_cb_t fn;\n+\tuint32_t cluster_node_size; /* Size of struct cluster_node */\n+\trte_node_t max_nodes;\n+\tint socket_id;\n+\tvoid *cookie;\n+\tsize_t sz;\n+\n+\tstruct cluster_node clusters[];\n+} __rte_cache_aligned;\n+\n+#define boarder() fprintf(f, \"+-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+\\n\")\n+\n+static inline void\n+print_banner(FILE *f)\n+{\n+\tboarder();\n+\tfprintf(f, \"%-32s%-16s%-16s%-16s%-16s%-16s%-16s\\n\", \"|Node\", \"|calls\", \"|objs\", \"|realloc_count\", \"|objs/call\", \"|objs/sec(10E6)\", \"|cycles/call|\");\n+\tboarder();\n+}\n+\n+static inline void\n+print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)\n+{\n+\tdouble objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;\n+\tconst uint64_t prev_calls = stat->prev_calls;\n+\tconst uint64_t prev_objs = stat->prev_objs;\n+\tconst uint64_t cycles = stat->cycles;\n+\tconst uint64_t calls = stat->calls;\n+\tconst uint64_t objs = stat->objs;\n+\tuint64_t call_delta;\n+\n+\tcall_delta = calls - prev_calls;\n+\tobjs_per_call = call_delta ?\n+\t\t\t(double)((objs - prev_objs)/call_delta) : 0;\n+\tcycles_per_call = call_delta ?\n+\t\t\t(double)((cycles - stat->prev_cycles)/call_delta) : 0;\n+\tts_per_hz = (double)((stat->ts - stat->prev_ts)/stat->hz);\n+\tobjs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;\n+\tobjs_per_sec /= 1000000;\n+\n+\tfprintf(f, \"|%-31s|%-15\"PRIu64\"|%-15\"PRIu64\"|%-15\"PRIu64\"|%-15.3f|%-15.6f|%-11.4f|\\n\",\n+\t\tstat->name, calls, objs, stat->realloc_count, objs_per_call,\n+\t\tobjs_per_sec, cycles_per_call);\n+}\n+\n+static int\n+graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,\n+\t\t       const struct rte_graph_cluster_node_stats *stat)\n+{\n+\tFILE *f = cookie;\n+\n+\tif (unlikely(is_first))\n+\t\tprint_banner(f);\n+\tif (stat->objs)\n+\t\tprint_node(f, stat);\n+\tif (unlikely(is_last))\n+\t\tboarder();\n+\n+\treturn 0;\n+};\n+\n+static struct rte_graph_cluster_stats *\n+stats_mem_init(struct cluster *cluster,\n+\t       const struct rte_graph_cluster_stats_param *prm)\n+{\n+\tsize_t sz = sizeof(struct rte_graph_cluster_stats);\n+\tstruct rte_graph_cluster_stats *stats;\n+\trte_graph_cluster_stats_cb_t fn;\n+\tint socket_id = prm->socket_id;\n+\tuint32_t cluster_node_size;\n+\n+\t/* Fixup callback */\n+\tfn = prm->fn;\n+\tif (fn == NULL)\n+\t\tfn = graph_cluster_stats_cb;\n+\n+\tcluster_node_size = sizeof(struct cluster_node);\n+\t/* For a given cluster, max nodes will be the max number of graphs */\n+\tcluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);\n+\tcluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);\n+\n+\tstats = realloc(NULL, sz);\n+\tmemset(stats, 0, sz);\n+\tif (stats) {\n+\t\tstats->fn = fn;\n+\t\tstats->cluster_node_size = cluster_node_size;\n+\t\tstats->max_nodes = 0;\n+\t\tstats->socket_id = socket_id;\n+\t\tstats->cookie = prm->cookie;\n+\t\tstats->sz = sz;\n+\t}\n+\n+\treturn stats;\n+}\n+\n+static int\n+stats_mem_populate(struct rte_graph_cluster_stats **stats_in,\n+\t\t       struct rte_graph *graph, struct graph_node *graph_node)\n+{\n+\tstruct rte_graph_cluster_stats *stats = *stats_in;\n+\trte_node_t id = graph_node->node->id;\n+\tstruct cluster_node *cluster;\n+\tstruct rte_node *node;\n+\trte_node_t count;\n+\n+\tcluster = stats->clusters;\n+\n+\t/* Iterate over cluster node array to find node ID match */\n+\tfor (count = 0; count < stats->max_nodes; count++) {\n+\t\t/* Found an existing node in the reel */\n+\t\tif (cluster->stat.id == id) {\n+\t\t\tnode = graph_node_id_to_ptr(graph, id);\n+\t\t\tif (node == NULL)\n+\t\t\t\tset_err(ENOKEY, err,\n+\t\t\t\t\"failed to find node %s in graph %s\",\n+\t\t\t\tgraph_node->node->name, graph->name);\n+\n+\t\t\tcluster->nodes[cluster->nb_nodes++] = node;\n+\t\t\treturn 0;\n+\t\t}\n+\t\tcluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);\n+\t}\n+\n+\t/* Hey, it is a new node, allocate space for it in the reel */\n+\tstats = realloc(stats, stats->sz + stats->cluster_node_size);\n+\tif (stats == NULL)\n+\t\tset_err(ENOMEM, err, \"realloc failed\");\n+\n+\t/* Clear the new struct cluster_node area */\n+\tcluster = RTE_PTR_ADD(stats, stats->sz),\n+\tmemset(cluster,  0, stats->cluster_node_size);\n+\tmemcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);\n+\tcluster->stat.id = graph_node->node->id;\n+\tcluster->stat.hz = rte_get_timer_hz();\n+\tnode = graph_node_id_to_ptr(graph, id);\n+\tif (node == NULL)\n+\t\tset_err(ENOKEY, err, \"failed to find node %s in graph %s\",\n+\t\t\tgraph_node->node->name, graph->name);\n+\tcluster->nodes[cluster->nb_nodes++] = node;\n+\n+\tstats->sz += stats->cluster_node_size;\n+\tstats->max_nodes++;\n+\t*stats_in = stats;\n+\n+\treturn 0;\n+err:\n+\treturn -rte_errno;\n+}\n+\n+static void\n+stats_mem_fini(struct rte_graph_cluster_stats *stats)\n+{\n+\tfree(stats);\n+}\n+\n+static void\n+cluster_init(struct cluster *cluster)\n+{\n+\tmemset(cluster, 0, sizeof(*cluster));\n+}\n+\n+static int\n+cluster_add(struct cluster *cluster, struct graph *graph)\n+{\n+\trte_graph_t count;\n+\tsize_t sz;\n+\n+\t/* Skip the if graph is already added to cluster */\n+\tfor (count = 0; count < cluster->nb_graphs; count++)\n+\t\tif (cluster->graphs[count] == graph)\n+\t\t\treturn 0;\n+\n+\t/* Exapand the cluster if required to store graph objects */\n+\tif (cluster->nb_graphs + 1 > cluster->size) {\n+\t\tcluster->size = RTE_MAX(1, cluster->size * 2);\n+\t\tsz = sizeof(struct graph *) * cluster->size;\n+\t\tcluster->graphs = realloc(cluster->graphs, sz);\n+\t\tif (cluster->graphs == NULL)\n+\t\t\tset_err(ENOMEM, free, \"failed to realloc\");\n+\t}\n+\n+\t/* Add graph to cluster */\n+\tcluster->graphs[cluster->nb_graphs++] = graph;\n+\treturn 0;\n+\n+free:\n+\treturn -rte_errno;\n+}\n+\n+static void\n+cluster_fini(struct cluster *cluster)\n+{\n+\tif (cluster->graphs)\n+\t\tfree(cluster->graphs);\n+}\n+\n+static int\n+expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)\n+{\n+\tstruct graph_head *graph_head = graph_list_head_get();\n+\tstruct graph *graph;\n+\tbool found = false;\n+\n+\t/* Check for pattern match */\n+\tSTAILQ_FOREACH(graph, graph_head, next) {\n+\t\tif (fnmatch(pattern, graph->name, 0) == 0) {\n+\t\t\tif (cluster_add(cluster, graph))\n+\t\t\t\tgoto fail;\n+\t\t\tfound = true;\n+\t\t}\n+\t}\n+\tif (found == false)\n+\t\tset_err(EFAULT, fail, \"pattern %s graph not found\", pattern);\n+\n+\treturn 0;\n+fail:\n+\treturn -rte_errno;\n+}\n+\n+struct rte_graph_cluster_stats *\n+rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)\n+{\n+\tstruct rte_graph_cluster_stats *stats, *rc = NULL;\n+\tstruct graph_node *graph_node;\n+\tstruct cluster cluster;\n+\tstruct graph *graph;\n+\tconst char *pattern;\n+\trte_graph_t i;\n+\n+\t/* Sanity checks */\n+\tif (!rte_graph_has_stats_feature())\n+\t\tset_err(EINVAL, fail, \"stats feature is not enabled\");\n+\n+\tif (prm == NULL)\n+\t\tset_err(EINVAL, fail, \"invalid param\");\n+\n+\tif (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)\n+\t\tset_err(EINVAL, fail, \"invalid graph param\");\n+\n+\tcluster_init(&cluster);\n+\n+\tgraph_spinlock_lock();\n+\t/* Expand graph pattern and add the graph to the cluster */\n+\tfor (i = 0; i < prm->nb_graph_patterns; i++) {\n+\t\tpattern = prm->graph_patterns[i];\n+\t\tif (expand_pattern_to_cluster(&cluster, pattern))\n+\t\t\tgoto bad_pattern;\n+\t}\n+\n+\t/* Alloc the stats memory */\n+\tstats = stats_mem_init(&cluster, prm);\n+\tif (stats == NULL)\n+\t\tset_err(ENOMEM, bad_pattern, \"failed alloc stats memory\");\n+\n+\t/* Iterate over M(Graph) x N (Nodes in graph) */\n+\tfor (i = 0; i < cluster.nb_graphs; i++) {\n+\t\tgraph = cluster.graphs[i];\n+\t\tSTAILQ_FOREACH(graph_node, &graph->node_list, next) {\n+\t\t\tstruct rte_graph *graph_fp = graph->graph;\n+\t\t\tif (stats_mem_populate(&stats, graph_fp, graph_node))\n+\t\t\t\tgoto realloc_fail;\n+\t\t}\n+\t}\n+\n+\t/* Finally copy to hugepage memory to avoid pressure on rte_realloc */\n+\trc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);\n+\tif (rc)\n+\t\trte_memcpy(rc, stats, stats->sz);\n+\telse\n+\t\tset_err(ENOMEM, realloc_fail, \"rte_malloc failed\");\n+\n+realloc_fail:\n+\tstats_mem_fini(stats);\n+bad_pattern:\n+\tgraph_spinlock_unlock();\n+\tcluster_fini(&cluster);\n+fail:\n+\treturn rc;\n+}\n+\n+void\n+rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)\n+{\n+\treturn rte_free(stat);\n+}\n+\n+static inline void\n+cluster_node_arregate_stats(struct cluster_node *cluster)\n+{\n+\tuint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;\n+\tstruct rte_graph_cluster_node_stats *stat = &cluster->stat;\n+\tstruct rte_node *node;\n+\trte_node_t count;\n+\n+\tfor (count = 0; count < cluster->nb_nodes; count++) {\n+\t\tnode = cluster->nodes[count];\n+\n+\t\tcalls += node->total_calls;\n+\t\tobjs += node->total_objs;\n+\t\tcycles += node->total_cycles;\n+\t\trealloc_count += node->realloc_count;\n+\t}\n+\n+\tstat->calls = calls;\n+\tstat->objs = objs;\n+\tstat->cycles = cycles;\n+\tstat->ts = rte_get_timer_cycles();\n+\tstat->realloc_count = realloc_count;\n+}\n+\n+static inline void\n+cluster_node_store_prev_stats(struct cluster_node *cluster)\n+{\n+\tstruct rte_graph_cluster_node_stats *stat = &cluster->stat;\n+\n+\tstat->prev_ts = stat->ts;\n+\tstat->prev_calls = stat->calls;\n+\tstat->prev_objs = stat->objs;\n+\tstat->prev_cycles = stat->cycles;\n+}\n+\n+void\n+rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)\n+{\n+\tstruct cluster_node *cluster;\n+\trte_node_t count;\n+\tint rc = 0;\n+\n+\tcluster = stat->clusters;\n+\n+\tfor (count = 0; count < stat->max_nodes; count++) {\n+\t\tcluster_node_arregate_stats(cluster);\n+\t\tif (!skip_cb)\n+\t\t\trc = stat->fn(!count, (count == stat->max_nodes - 1),\n+\t\t\t\t      stat->cookie, &cluster->stat);\n+\t\tcluster_node_store_prev_stats(cluster);\n+\t\tif (rc)\n+\t\t\tbreak;\n+\t\tcluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);\n+\t}\n+}\n+\n+void\n+rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)\n+{\n+\tstruct cluster_node *cluster;\n+\trte_node_t count;\n+\n+\tcluster = stat->clusters;\n+\n+\tfor (count = 0; count < stat->max_nodes; count++) {\n+\t\tstruct rte_graph_cluster_node_stats *node = &cluster->stat;\n+\n+\t\tnode->ts = 0;\n+\t\tnode->calls = 0;\n+\t\tnode->objs = 0;\n+\t\tnode->cycles = 0;\n+\t\tnode->prev_ts = 0;\n+\t\tnode->prev_calls = 0;\n+\t\tnode->prev_objs = 0;\n+\t\tnode->prev_cycles = 0;\n+\t\tnode->realloc_count = 0;\n+\t\tcluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);\n+\t}\n+}\ndiff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build\nnew file mode 100644\nindex 000000000..929a17f84\n--- /dev/null\n+++ b/lib/librte_graph/meson.build\n@@ -0,0 +1,11 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2020 Marvell International Ltd.\n+#\n+\n+name = 'graph'\n+\n+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_stats.c', 'graph_populate.c')\n+headers = files('rte_graph.h', 'rte_graph_worker.h')\n+allow_experimental_apis = true\n+\n+deps += ['eal']\ndiff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c\nnew file mode 100644\nindex 000000000..503721943\n--- /dev/null\n+++ b/lib/librte_graph/node.c\n@@ -0,0 +1,419 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <stdbool.h>\n+#include <stdio.h>\n+#include <string.h>\n+\n+#include <rte_common.h>\n+#include <rte_debug.h>\n+#include <rte_eal.h>\n+#include <rte_errno.h>\n+#include <rte_string_fns.h>\n+\n+#include \"graph_private.h\"\n+\n+static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);\n+static rte_node_t node_id;\n+\n+#define node_id_check(id) id_check(id, node_id)\n+\n+/* Private functions */\n+struct node_head *node_list_head_get(void)\n+{\n+\treturn &node_list;\n+}\n+\n+struct node *\n+node_from_name(const char *name)\n+{\n+\tstruct node *node;\n+\n+\tSTAILQ_FOREACH(node, &node_list, next)\n+\t\tif (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)\n+\t\t\treturn node;\n+\n+\treturn NULL;\n+}\n+\n+static bool\n+node_has_duplicate_entry(const char *name)\n+{\n+\tstruct node *node;\n+\n+\t/* Is duplicate name registred */\n+\tSTAILQ_FOREACH(node, &node_list, next) {\n+\t\tif (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0) {\n+\t\t\trte_errno = EEXIST;\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+/* Public functions */\n+rte_node_t\n+__rte_node_register(const struct rte_node_register *reg)\n+{\n+\tstruct node *node;\n+\trte_edge_t i;\n+\tsize_t sz;\n+\n+\tRTE_BUILD_BUG_ON((offsetof(struct rte_node, nodes) -\n+\t\t\t  offsetof(struct rte_node, ctx))\n+\t\t\t  != RTE_CACHE_LINE_MIN_SIZE);\n+\n+\tgraph_spinlock_lock();\n+\n+\t/* Check sanity */\n+\tif (reg == NULL || reg->process == NULL) {\n+\t\trte_errno = EINVAL;\n+\t\tgoto fail;\n+\t}\n+\n+\t/* Check for duplicate name */\n+\tif (node_has_duplicate_entry(reg->name))\n+\t\tgoto fail;\n+\n+\tsz = sizeof(struct node) + (reg->nb_edges * RTE_NODE_NAMESIZE);\n+\tnode = calloc(1, sz);\n+\tif (node == NULL) {\n+\t\trte_errno = ENOMEM;\n+\t\tgoto fail;\n+\t}\n+\n+\t/* Initialize the node */\n+\tif (rte_strscpy(node->name, reg->name, RTE_NODE_NAMESIZE) < 0) {\n+\t\trte_errno = E2BIG;\n+\t\tgoto free;\n+\t}\n+\tnode->flags = reg->flags;\n+\tnode->process = reg->process;\n+\tnode->init = reg->init;\n+\tnode->fini = reg->fini;\n+\tnode->nb_edges = reg->nb_edges;\n+\tnode->parent_id = reg->parent_id;\n+\tfor (i = 0; i < reg->nb_edges; i++) {\n+\t\tif (rte_strscpy(node->next_nodes[i], reg->next_nodes[i],\n+\t\t\tRTE_NODE_NAMESIZE) < 0) {\n+\t\t\trte_errno = E2BIG;\n+\t\t\tgoto free;\n+\t\t}\n+\t}\n+\n+\tnode->id = node_id++;\n+\n+\t/* Add the node at tail */\n+\tSTAILQ_INSERT_TAIL(&node_list, node, next);\n+\tgraph_spinlock_unlock();\n+\n+\treturn node->id;\n+free:\n+\tfree(node);\n+fail:\n+\tgraph_spinlock_unlock();\n+\treturn RTE_NODE_ID_INVALID;\n+}\n+\n+static int\n+clone_name(struct rte_node_register *reg, struct node *node, const char *name)\n+{\n+\tssize_t sz, rc;\n+\n+#define SZ RTE_NODE_NAMESIZE\n+\trc = rte_strscpy(reg->name, node->name, SZ);\n+\tif (rc < 0)\n+\t\tgoto fail;\n+\tsz = rc;\n+\trc = rte_strscpy(reg->name + sz, \"-\", RTE_MAX((int16_t)(SZ - sz), 0));\n+\tif (rc < 0)\n+\t\tgoto fail;\n+\tsz += rc;\n+\tsz = rte_strscpy(reg->name + sz, name, RTE_MAX((int16_t)(SZ - sz), 0));\n+\tif (sz < 0)\n+\t\tgoto fail;\n+\n+\treturn 0;\n+fail:\n+\trte_errno = E2BIG;\n+\treturn -rte_errno;\n+}\n+\n+static rte_node_t\n+node_clone(struct node *node, const char *name)\n+{\n+\trte_node_t rc = RTE_NODE_ID_INVALID;\n+\tstruct rte_node_register *reg;\n+\trte_edge_t i;\n+\n+\t/* Don't allow to clone a node from a cloned node */\n+\tif (node->parent_id != RTE_NODE_ID_INVALID) {\n+\t\trte_errno = EEXIST;\n+\t\tgoto fail;\n+\t}\n+\n+\t/* Check for duplicate name */\n+\tif (node_has_duplicate_entry(name))\n+\t\tgoto fail;\n+\n+\treg = calloc(1, sizeof(*reg) + (sizeof(char *) * node->nb_edges));\n+\tif (reg == NULL) {\n+\t\trte_errno = ENOMEM;\n+\t\tgoto fail;\n+\t}\n+\n+\t/* Clone the source node */\n+\treg->flags = node->flags;\n+\treg->process = node->process;\n+\treg->init = node->init;\n+\treg->fini = node->fini;\n+\treg->nb_edges = node->nb_edges;\n+\treg->parent_id = node->id;\n+\n+\tfor (i = 0; i < node->nb_edges; i++)\n+\t\treg->next_nodes[i] = node->next_nodes[i];\n+\n+\t/* Naming ceremony of the new node. name is node->name + \"-\" + name */\n+\tif (clone_name(reg, node, name))\n+\t\tgoto free;\n+\n+\trc = __rte_node_register(reg);\n+free:\n+\tfree(reg);\n+fail:\n+\treturn rc;\n+}\n+\n+rte_node_t\n+rte_node_clone(rte_node_t id, const char *name)\n+{\n+\tstruct node *node;\n+\n+\tnode_id_check(id);\n+\tSTAILQ_FOREACH(node, &node_list, next)\n+\t\tif (node->id == id)\n+\t\t\treturn node_clone(node, name);\n+\n+fail:\n+\treturn RTE_NODE_ID_INVALID;\n+}\n+\n+rte_node_t\n+rte_node_from_name(const char *name)\n+{\n+\tstruct node *node;\n+\n+\tSTAILQ_FOREACH(node, &node_list, next)\n+\t\tif (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)\n+\t\t\treturn node->id;\n+\n+\treturn RTE_NODE_ID_INVALID;\n+}\n+\n+char *\n+rte_node_id_to_name(rte_node_t id)\n+{\n+\tstruct node *node;\n+\n+\tnode_id_check(id);\n+\tSTAILQ_FOREACH(node, &node_list, next)\n+\t\tif (node->id == id)\n+\t\t\treturn node->name;\n+\n+fail:\n+\treturn NULL;\n+}\n+\n+rte_edge_t\n+rte_node_edge_count(rte_node_t id)\n+{\n+\tstruct node *node;\n+\n+\tnode_id_check(id);\n+\tSTAILQ_FOREACH(node, &node_list, next)\n+\t\tif (node->id == id)\n+\t\t\treturn node->nb_edges;\n+fail:\n+\treturn RTE_EDGE_ID_INVALID;\n+}\n+\n+static rte_edge_t\n+edge_update(struct node *node, struct node *prev, rte_edge_t from,\n+\t    const char **next_nodes, rte_edge_t nb_edges)\n+{\n+\trte_edge_t i, max_edges, count = 0;\n+\tstruct node *new_node;\n+\tbool need_realloc;\n+\tsize_t sz;\n+\n+\tif (from == RTE_EDGE_ID_INVALID)\n+\t\tfrom = node->nb_edges;\n+\n+\t/* Don't create hole in next_nodes[] list */\n+\tif (from > node->nb_edges) {\n+\t\trte_errno = ENOMEM;\n+\t\tgoto fail;\n+\t}\n+\n+\t/* Remove me from list */\n+\tSTAILQ_REMOVE(&node_list, node, node, next);\n+\n+\t/* Allocate the storage space for new node if required */\n+\tmax_edges = from + nb_edges;\n+\tneed_realloc = max_edges > node->nb_edges;\n+\tif (need_realloc) {\n+\t\tsz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);\n+\t\tnew_node = realloc(node, sz);\n+\t\tif (new_node == NULL) {\n+\t\t\trte_errno = ENOMEM;\n+\t\t\tgoto restore;\n+\t\t} else {\n+\t\t\tnode = new_node;\n+\t\t}\n+\t}\n+\n+\t/* Update the new nodes name */\n+\tfor (i = from; i < max_edges; i++, count++) {\n+\t\tif (rte_strscpy(node->next_nodes[i], next_nodes[count],\n+\t\t\tRTE_NODE_NAMESIZE) < 0) {\n+\t\t\trte_errno = E2BIG;\n+\t\t\tgoto restore;\n+\t\t}\n+\t}\n+restore:\n+\t/* Update the linked list to point new node address in prev node */\n+\tif (prev)\n+\t\tSTAILQ_INSERT_AFTER(&node_list, prev, node, next);\n+\telse\n+\t\tSTAILQ_INSERT_HEAD(&node_list, node, next);\n+\n+\tif (need_realloc)\n+\t\tnode->nb_edges += count;\n+\n+fail:\n+\treturn count;\n+}\n+\n+rte_edge_t\n+rte_node_edge_shrink(rte_node_t id, rte_edge_t size)\n+{\n+\trte_edge_t rc = RTE_EDGE_ID_INVALID;\n+\tstruct node *node;\n+\n+\tnode_id_check(id);\n+\tgraph_spinlock_lock();\n+\n+\tSTAILQ_FOREACH(node, &node_list, next) {\n+\t\tif (node->id == id) {\n+\t\t\tif (node->nb_edges < size) {\n+\t\t\t\trte_errno = E2BIG;\n+\t\t\t\tgoto fail;\n+\t\t\t}\n+\t\t\tnode->nb_edges = size;\n+\t\t\trc = size;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+fail:\n+\tgraph_spinlock_unlock();\n+\treturn rc;\n+}\n+\n+rte_edge_t\n+rte_node_edge_update(rte_node_t id, rte_edge_t from,\n+\t\t     const char **next_nodes, uint16_t nb_edges)\n+{\n+\trte_edge_t rc = RTE_EDGE_ID_INVALID;\n+\tstruct node *n, *prev;\n+\n+\tnode_id_check(id);\n+\tgraph_spinlock_lock();\n+\n+\tprev = NULL;\n+\tSTAILQ_FOREACH(n, &node_list, next) {\n+\t\tif (n->id == id) {\n+\t\t\trc = edge_update(n, prev, from, next_nodes, nb_edges);\n+\t\t\tbreak;\n+\t\t}\n+\t\tprev = n;\n+\t}\n+\n+\tgraph_spinlock_unlock();\n+fail:\n+\treturn rc;\n+}\n+\n+static rte_node_t\n+node_copy_edges(struct node *node, char *next_nodes[])\n+{\n+\trte_edge_t i;\n+\n+\tfor (i = 0; i < node->nb_edges; i++)\n+\t\tnext_nodes[i] = node->next_nodes[i];\n+\n+\treturn i;\n+}\n+\n+rte_node_t\n+rte_node_edge_get(rte_node_t id, char *next_nodes[])\n+{\n+\trte_node_t rc = RTE_NODE_ID_INVALID;\n+\tstruct node *node;\n+\n+\tnode_id_check(id);\n+\tgraph_spinlock_lock();\n+\n+\tSTAILQ_FOREACH(node, &node_list, next) {\n+\t\tif (node->id == id) {\n+\t\t\tif (next_nodes == NULL)\n+\t\t\t\trc = sizeof(char *) * node->nb_edges;\n+\t\t\telse\n+\t\t\t\trc = node_copy_edges(node, next_nodes);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tgraph_spinlock_unlock();\n+fail:\n+\treturn rc;\n+}\n+\n+static void\n+node_scan_dump(FILE *f, rte_node_t id, bool all)\n+{\n+\tstruct node *node;\n+\n+\tRTE_ASSERT(f != NULL);\n+\tnode_id_check(id);\n+\n+\tSTAILQ_FOREACH(node, &node_list, next) {\n+\t\tif (all == true) {\n+\t\t\tnode_dump(f, node);\n+\t\t} else if (node->id == id) {\n+\t\t\tnode_dump(f, node);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+fail:\n+\treturn;\n+}\n+\n+void\n+rte_node_dump(FILE *f, rte_node_t id)\n+{\n+\tnode_scan_dump(f, id, false);\n+}\n+\n+void\n+rte_node_list_dump(FILE *f)\n+{\n+\tnode_scan_dump(f, 0, true);\n+}\n+\n+rte_node_t\n+rte_node_max_count(void)\n+{\n+\treturn node_id;\n+}\ndiff --git a/lib/librte_graph/rte_graph.h b/lib/librte_graph/rte_graph.h\nnew file mode 100644\nindex 000000000..aa6df76dd\n--- /dev/null\n+++ b/lib/librte_graph/rte_graph.h\n@@ -0,0 +1,277 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#ifndef _RTE_GRAPH_H_\n+#define _RTE_GRAPH_H_\n+\n+/**\n+ * @file rte_graph.h\n+ * @b EXPERIMENTAL: this API may change without prior notice\n+ *\n+ * RTE GRAPH support.\n+ * librte_graph provides a framework to <fill the remaning>\n+ * and need to add rte_experimental\n+ */\n+\n+#include <stdio.h>\n+#include <stdbool.h>\n+\n+#include <rte_common.h>\n+#include <rte_compat.h>\n+#include <rte_memcpy.h>\n+#include <rte_memory.h>\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+#define RTE_GRAPH_NAMESIZE\t64 /**< Max length of graph name. */\n+#define RTE_NODE_NAMESIZE\t64 /**< Max length of node name. */\n+#define RTE_GRAPH_OFF_INVALID UINT32_MAX\n+#define RTE_NODE_ID_INVALID UINT32_MAX\n+#define RTE_EDGE_ID_INVALID UINT16_MAX\n+#define RTE_GRAPH_ID_INVALID UINT16_MAX\n+#define RTE_GRAPH_FENCE 0xdeadbeef12345678ULL\n+\n+typedef uint32_t rte_graph_off_t;\n+typedef uint32_t rte_node_t;\n+typedef uint16_t rte_edge_t;\n+typedef uint16_t rte_graph_t;\n+\n+/**< Burst size in terms of log2 */\n+#if RTE_GRAPH_BURST_SIZE == 1\n+#define RTE_GRAPH_BURST_SIZE_LOG2 0\n+#elif RTE_GRAPH_BURST_SIZE == 2\n+#define RTE_GRAPH_BURST_SIZE_LOG2 1\n+#elif RTE_GRAPH_BURST_SIZE == 4\n+#define RTE_GRAPH_BURST_SIZE_LOG2 2\n+#elif RTE_GRAPH_BURST_SIZE == 8\n+#define RTE_GRAPH_BURST_SIZE_LOG2 3\n+#elif RTE_GRAPH_BURST_SIZE == 16\n+#define RTE_GRAPH_BURST_SIZE_LOG2 4\n+#elif RTE_GRAPH_BURST_SIZE == 32\n+#define RTE_GRAPH_BURST_SIZE_LOG2 5\n+#elif RTE_GRAPH_BURST_SIZE == 64\n+#define RTE_GRAPH_BURST_SIZE_LOG2 6\n+#elif RTE_GRAPH_BURST_SIZE == 128\n+#define RTE_GRAPH_BURST_SIZE_LOG2 7\n+#elif RTE_GRAPH_BURST_SIZE == 256\n+#define RTE_GRAPH_BURST_SIZE_LOG2 8\n+#else\n+#error \"Unsupported burst size\"\n+#endif\n+\n+/* Forward declaration */\n+struct rte_node;\n+struct rte_graph;\n+struct rte_graph_cluster_stats;\n+struct rte_graph_cluster_node_stats;\n+\n+typedef uint16_t (*rte_node_process_t)\n+\t(struct rte_graph *graph, struct rte_node *node, void **o, uint16_t nb);\n+typedef int (*rte_node_init_t)\n+\t(const struct rte_graph *graph, struct rte_node *node);\n+typedef void (*rte_node_fini_t)\n+\t(const struct rte_graph *graph, struct rte_node *node);\n+typedef int (*rte_graph_cluster_stats_cb_t)(bool is_first, bool is_last,\n+\t\tvoid *cookie, const struct rte_graph_cluster_node_stats *);\n+\n+/**\n+ * Configuration parameters for creating the graph.\n+ */\n+struct rte_graph_param {\n+\tint socket_id;\n+\tuint16_t nb_node_patterns;\n+\tconst char **node_patterns;\n+};\n+\n+struct rte_graph_cluster_stats_param {\n+\tint socket_id;\n+\t/* NULL value allowed, in that case, default print stat function used */\n+\trte_graph_cluster_stats_cb_t fn;\n+\tRTE_STD_C11\n+\tunion {\n+\t\tvoid *cookie;\n+\t\tFILE *f; /* Where to dump the stats when fn == NULL */\n+\t};\n+\tuint16_t nb_graph_patterns;\n+\tconst char **graph_patterns;\n+};\n+\n+/* Stats functions */\n+struct rte_graph_cluster_node_stats {\n+\tuint64_t ts;\n+\tuint64_t calls;\n+\tuint64_t objs;\n+\tuint64_t cycles;\n+\n+\tuint64_t prev_ts;\n+\tuint64_t prev_calls;\n+\tuint64_t prev_objs;\n+\tuint64_t prev_cycles;\n+\n+\tuint64_t realloc_count;\n+\n+\trte_node_t id;\n+\tuint64_t hz;\n+\tchar name[RTE_NODE_NAMESIZE];\n+} __rte_cache_aligned;\n+\n+/** Structure defines the node registration parameters */\n+struct rte_node_register {\n+\tchar name[RTE_NODE_NAMESIZE];\t/**< Name of the node. */\n+\tuint64_t flags; /**< Node configuration flag */\n+#define RTE_NODE_SOURCE_F\t(1ULL << 0)\n+\tRTE_STD_C11\n+\trte_node_process_t process;  /**< Node process function */\n+\trte_node_init_t init;\n+\trte_node_fini_t fini;\n+\trte_node_t id; /* out */\n+\trte_node_t parent_id; /* Id of parent node */\n+\trte_edge_t nb_edges; /**< Number of edges from this node */\n+\tconst char *next_nodes[]; /* Names of next nodes. */\n+};\n+\n+/* Graph create functions */\n+__rte_experimental\n+rte_graph_t rte_graph_create(const char *name, struct rte_graph_param *prm);\n+__rte_experimental\n+rte_graph_t rte_graph_destroy(const char *name);\n+\n+/* Lookup functions */\n+__rte_experimental\n+rte_graph_t rte_graph_from_name(const char *name);\n+__rte_experimental\n+char *rte_graph_id_to_name(rte_graph_t id);\n+\n+/* Export the graph as graphviz dot file */\n+__rte_experimental\n+rte_graph_t rte_graph_export(const char *name, FILE *f);\n+\n+/* Get graph object for fast path in worker for walk */\n+__rte_experimental\n+struct rte_graph *rte_graph_lookup(const char *name);\n+\n+/* Get graph max count */\n+__rte_experimental\n+rte_graph_t rte_graph_max_count(void);\n+\n+/* Debug functions */\n+__rte_experimental\n+void rte_graph_dump(FILE *f, rte_graph_t id);\n+__rte_experimental\n+void rte_graph_list_dump(FILE *f);\n+__rte_experimental\n+void rte_graph_obj_dump(FILE *f, struct rte_graph *graph, bool all);\n+\n+/* API to get rte_node*  after the graph creation */\n+#define rte_graph_foreach_node(count, off, graph, node)\t\t\t       \\\n+\tfor (count = 0, off = graph->nodes_start,\t\t\t       \\\n+\t     node = RTE_PTR_ADD(graph, off);\t\t\t\t       \\\n+\t     count < graph->nb_nodes;\t\t\t\t\t       \\\n+\t     off = node->next, node = RTE_PTR_ADD(graph, off), count++)\n+\n+/* Lookup functions */\n+__rte_experimental\n+struct rte_node *rte_graph_node_get(rte_graph_t graph_id, uint32_t node_id);\n+__rte_experimental\n+struct rte_node *rte_graph_node_get_by_name(const char *graph,\n+\t\t\t\t\t    const char *name);\n+\n+__rte_experimental\n+struct rte_graph_cluster_stats *\n+rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm);\n+__rte_experimental\n+void rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat);\n+__rte_experimental\n+void rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat,\n+\t\t\t\t bool skip_cb);\n+__rte_experimental\n+void rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat);\n+\n+/* Node creation functions */\n+__rte_experimental\n+rte_node_t __rte_node_register(const struct rte_node_register *node);\n+\n+#define RTE_NODE_REGISTER(node)\t\t\t\t\t\t       \\\n+RTE_INIT(rte_node_register_##node)\t\t\t\t\t       \\\n+{\t\t\t\t\t\t\t\t\t       \\\n+\tnode.parent_id = RTE_NODE_ID_INVALID;\t\t\t\t       \\\n+\tnode.id = __rte_node_register(&node);\t\t\t\t       \\\n+}\n+__rte_experimental\n+rte_node_t rte_node_clone(rte_node_t id, const char *name);\n+\n+/* Lookup functions */\n+__rte_experimental\n+rte_node_t rte_node_from_name(const char *name);\n+__rte_experimental\n+char *rte_node_id_to_name(rte_node_t id);\n+\n+/* Edge related functions */\n+__rte_experimental\n+rte_edge_t rte_node_edge_count(rte_node_t id);\n+\n+/* If from == RTE_EDGE_ID_INVALID then add end of the list */\n+/* return the number of updated number of edges */\n+__rte_experimental\n+rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,\n+\t\t\t\tconst char **next_nodes, uint16_t nb_edges);\n+__rte_experimental\n+rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);\n+\n+/* if next_nodes == NULL, return the size of array\n+ * else number of item copied.\n+ */\n+__rte_experimental\n+rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);\n+\n+/* Get node max count */\n+__rte_experimental\n+rte_node_t rte_node_max_count(void);\n+\n+/* Debug functions */\n+__rte_experimental\n+void rte_node_dump(FILE *f, rte_node_t id);\n+__rte_experimental\n+void rte_node_list_dump(FILE *f);\n+\n+void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);\n+void __rte_node_stream_alloc_size(struct rte_graph *graph,\n+\t\t\t\t  struct rte_node *node, uint16_t req_size);\n+\n+/* Helper functions */\n+static __rte_always_inline int\n+rte_node_is_invalid(rte_node_t id)\n+{\n+\treturn (id == RTE_NODE_ID_INVALID);\n+}\n+\n+static __rte_always_inline int\n+rte_edge_is_invalid(rte_edge_t id)\n+{\n+\treturn (id == RTE_EDGE_ID_INVALID);\n+}\n+\n+static __rte_always_inline int\n+rte_graph_is_invalid(rte_graph_t id)\n+{\n+\treturn (id == RTE_GRAPH_ID_INVALID);\n+}\n+\n+static __rte_always_inline int\n+rte_graph_has_stats_feature(void)\n+{\n+#ifdef RTE_LIBRTE_GRAPH_STATS\n+\treturn RTE_LIBRTE_GRAPH_STATS;\n+#else\n+\treturn 0;\n+#endif\n+}\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* _RTE_GRAPH_H_ */\ndiff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map\nnew file mode 100644\nindex 000000000..e07763786\n--- /dev/null\n+++ b/lib/librte_graph/rte_graph_version.map\n@@ -0,0 +1,46 @@\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\trte_graph_create;\n+\trte_graph_destroy;\n+\trte_graph_from_name;\n+\trte_graph_id_to_name;\n+\trte_graph_dump;\n+\trte_graph_list_dump;\n+\trte_graph_node_ctx;\n+\trte_graph_node_ctx_by_name;\n+\trte_graph_export;\n+\trte_graph_lookup;\n+\trte_graph_clone;\n+\trte_graph_enqueue;\n+\trte_graph_free;\n+\trte_graph_walk;\n+\trte_graph_logtype;\n+\trte_graph_obj_dump;\n+\trte_graph_max_count;\n+\trte_graph_node_get;\n+\trte_graph_node_get_by_name;\n+\n+\t__rte_node_register;\n+\t__rte_node_stream_alloc;\n+\t__rte_node_stream_alloc_size;\n+\trte_node_clone;\n+\n+\trte_node_from_name;\n+\trte_node_id_to_name;\n+\trte_node_edge_count;\n+\trte_node_edge_update;\n+\trte_node_edge_get;\n+\trte_node_edge_shrink;\n+\trte_node_dump;\n+\trte_node_list_dump;\n+\trte_node_edge_shrink;\n+\trte_node_max_count;\n+\n+\trte_graph_cluster_stats_create;\n+\trte_graph_cluster_stats_destroy;\n+\trte_graph_cluster_stats_get;\n+\trte_graph_cluster_stats_reset;\n+\n+\tlocal: *;\n+};\ndiff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h\nnew file mode 100644\nindex 000000000..38c418e3d\n--- /dev/null\n+++ b/lib/librte_graph/rte_graph_worker.h\n@@ -0,0 +1,280 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#ifndef _RTE_GRAPH_WORKER_H_\n+#define _RTE_GRAPH_WORKER_H_\n+\n+/**\n+ * @file rte_graph.h\n+ * @b EXPERIMENTAL: this API may change without prior notice\n+ *\n+ * RTE GRAPH support.\n+ * librte_graph provides a framework to <fill the remaning>\n+ * and need to add rte_experimental\n+ */\n+\n+#include <stdio.h>\n+#include <stdbool.h>\n+\n+#include <rte_common.h>\n+#include <rte_cycles.h>\n+#include <rte_graph.h>\n+#include <rte_prefetch.h>\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+struct rte_graph {\n+\tuint32_t tail;\n+\tuint32_t head;\n+\tuint32_t cir_mask;\n+\trte_node_t nb_nodes;\n+\trte_graph_off_t *cir_start;\n+\trte_graph_off_t nodes_start;\n+\trte_graph_t id;\n+\tint socket;\n+\tchar name[RTE_GRAPH_NAMESIZE];\t/* Name of the graph. */\n+\tuint64_t fence;\n+}__rte_cache_aligned;\n+\n+struct rte_node {\n+\t/* Slow path area  */\n+\tuint64_t fence; /* Fence */\n+\trte_graph_off_t next; /* Index to next node */\n+\trte_node_t id; /* ID */\n+\trte_node_t parent_id; /* Parent ID */\n+\trte_edge_t nb_edges; /* Number of edges from this node */\n+\tuint32_t realloc_count; /* Number of times realloced */\n+\n+\tchar parent[RTE_NODE_NAMESIZE]; /* Parent node name. */\n+\tchar name[RTE_NODE_NAMESIZE];   /* Name of the node. */\n+\t/* Fast path area  */\n+\t#define RTE_NODE_CTX_SZ 16\n+\tuint8_t ctx[RTE_NODE_CTX_SZ] __rte_cache_aligned;\n+\tuint16_t size;\n+\tuint16_t idx;\n+\trte_graph_off_t off;\n+\tuint64_t total_cycles;\n+\tuint64_t total_calls;\n+\tuint64_t total_objs;\n+\tRTE_STD_C11\n+\tunion {\n+\t\tvoid **objs;\n+\t\tuint64_t objs_u64;\n+\t};\n+\tRTE_STD_C11\n+\tunion {\n+\t\trte_node_process_t process;\n+\t\tuint64_t process_u64;\n+\t};\n+\tstruct rte_node *nodes[] __rte_cache_min_aligned;\n+} __rte_cache_aligned;\n+\n+/* Fast path API for Graph walk */\n+static inline void\n+rte_graph_walk(struct rte_graph *graph)\n+{\n+\tconst rte_graph_off_t *cir_start = graph->cir_start;\n+\tconst rte_node_t mask = graph->cir_mask;\n+\tuint32_t head = graph->head;\n+\tstruct rte_node *node;\n+\tuint64_t start;\n+\tuint16_t rc;\n+\tvoid **objs;\n+\n+\t/*\n+\t * Walk on the source node(s) ((cir_start - head) -> cir_start) and then\n+\t * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)\n+\t * in a circular buffer fashion.\n+\t *\n+\t *\t+-----+ <= cir_start - head [number of source nodes]\n+\t *\t|     |\n+\t *\t| ... | <= source nodes\n+\t *\t|     |\n+\t *\t+-----+ <= cir_start [head = 0] [tail = 0]\n+\t *\t|     |\n+\t *\t| ... | <= pending streams\n+\t *\t|     |\n+\t *\t+-----+ <= cir_start + mask\n+\t */\n+\twhile (likely(head != graph->tail)) {\n+\t\tnode = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);\n+\t\tRTE_ASSERT(node->fence == RTE_GRAPH_FENCE);\n+\t\tobjs = node->objs;\n+\t\trte_prefetch0(objs);\n+\n+\t\tif (rte_graph_has_stats_feature()) {\n+\t\t\tstart = rte_rdtsc();\n+\t\t\trc = node->process(graph, node, objs, node->idx);\n+\t\t\tnode->total_cycles += rte_rdtsc() - start;\n+\t\t\tnode->total_calls++;\n+\t\t\tnode->total_objs += rc;\n+\t\t} else {\n+\t\t\tnode->process(graph, node, objs, node->idx);\n+\t\t}\n+\t\tnode->idx = 0;\n+\t\thead = likely((int32_t)head > 0) ? head & mask : head;\n+\t}\n+\tgraph->tail = 0;\n+}\n+\n+/* Fast path helper functions */\n+static __rte_always_inline void\n+__rte_node_enqueue_tail_update(struct rte_graph *graph, struct rte_node *node)\n+{\n+\tuint32_t tail;\n+\n+\ttail = graph->tail;\n+\tgraph->cir_start[tail++] = node->off;\n+\tgraph->tail = tail & graph->cir_mask;\n+\n+}\n+\n+static __rte_always_inline void\n+__rte_node_enqueue_prolouge(struct rte_graph *graph, struct rte_node *node,\n+\t\t\t    const uint16_t idx, const uint16_t space)\n+{\n+\n+\t/* Add to the pending stream list if the node is new */\n+\tif (idx == 0)\n+\t\t__rte_node_enqueue_tail_update(graph, node);\n+\n+\tif (unlikely(node->size < (idx + space)))\n+\t\t__rte_node_stream_alloc(graph, node);\n+}\n+\n+static __rte_always_inline struct rte_node *\n+__rte_node_next_node_get(struct rte_node *node, rte_edge_t next)\n+{\n+\tRTE_ASSERT(next < node->nb_edges);\n+\tRTE_ASSERT(node->fence == RTE_GRAPH_FENCE);\n+\tnode = node->nodes[next];\n+\tRTE_ASSERT(node->fence == RTE_GRAPH_FENCE);\n+\n+\treturn node;\n+}\n+\n+/* Fast path enqueue functions */\n+static inline void\n+rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,\n+\t\t rte_edge_t next, void **objs, uint16_t nb_objs)\n+{\n+\tnode = __rte_node_next_node_get(node, next);\n+\tconst uint16_t idx = node->idx;\n+\n+\t__rte_node_enqueue_prolouge(graph, node, idx, nb_objs);\n+\n+\trte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));\n+\tnode->idx = idx + nb_objs;\n+}\n+\n+static inline void\n+rte_node_enqueue_x1(struct rte_graph *graph, struct rte_node *node,\n+\t\t    rte_edge_t next, void *obj)\n+{\n+\tnode = __rte_node_next_node_get(node, next);\n+\tuint16_t idx = node->idx;\n+\n+\t__rte_node_enqueue_prolouge(graph, node, idx, 1);\n+\n+\tnode->objs[idx++] = obj;\n+\tnode->idx = idx;\n+}\n+\n+static inline void\n+rte_node_enqueue_x2(struct rte_graph *graph, struct rte_node *node,\n+\t\t    rte_edge_t next, void *obj0, void *obj1)\n+{\n+\tnode = __rte_node_next_node_get(node, next);\n+\tuint16_t idx = node->idx;\n+\n+\t__rte_node_enqueue_prolouge(graph, node, idx, 2);\n+\n+\tnode->objs[idx++] = obj0;\n+\tnode->objs[idx++] = obj1;\n+\tnode->idx = idx;\n+}\n+\n+static inline void\n+rte_node_enqueue_x4(struct rte_graph *graph, struct rte_node *node, rte_edge_t\n+\t\t    next, void *obj0, void *obj1, void *obj2, void *obj3)\n+{\n+\tnode = __rte_node_next_node_get(node, next);\n+\tuint16_t idx = node->idx;\n+\n+\t__rte_node_enqueue_prolouge(graph, node, idx, 4);\n+\n+\tnode->objs[idx++] = obj0;\n+\tnode->objs[idx++] = obj1;\n+\tnode->objs[idx++] = obj2;\n+\tnode->objs[idx++] = obj3;\n+\tnode->idx = idx;\n+}\n+\n+static inline void\n+rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,\n+\t\t      rte_edge_t *nexts, void **objs, uint16_t nb_objs)\n+{\n+\tuint16_t i;\n+\n+\t/* Non optimizied implementation to get started !!! */\n+\tfor (i = 0; i < nb_objs; i++)\n+\t\trte_node_enqueue_x1(graph, node, nexts[i], objs[i]);\n+}\n+\n+static inline void **\n+rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,\n+\t\t\t rte_edge_t next, uint16_t nb_objs)\n+{\n+\tnode = __rte_node_next_node_get(node, next);\n+\tconst uint16_t idx = node->idx;\n+\tuint16_t free_space = node->size - idx;\n+\n+\tif (unlikely(free_space < nb_objs))\n+\t\t__rte_node_stream_alloc_size(graph, node, nb_objs);\n+\n+\treturn &node->objs[idx];\n+}\n+\n+static inline void\n+rte_node_next_stream_put(struct rte_graph *graph, struct rte_node *node,\n+\t\t\t rte_edge_t next, uint16_t idx)\n+{\n+\tif (unlikely(!idx))\n+\t\treturn;\n+\n+\tnode = __rte_node_next_node_get(node, next);\n+\tif (node->idx == 0)\n+\t\t__rte_node_enqueue_tail_update(graph, node);\n+\n+\tnode->idx += idx;\n+}\n+\n+static inline void\n+rte_node_next_stream_move(struct rte_graph *graph, struct rte_node *src,\n+\t\t\t  rte_edge_t next)\n+{\n+\tstruct rte_node *dst = __rte_node_next_node_get(src, next);\n+\n+\t/* Let swap the pointers if dst don't have valid objs */\n+\tif (likely(dst->idx == 0)) {\n+\t\tvoid **dobjs = dst->objs;\n+\t\tuint16_t dsz = dst->size;\n+\t\tdst->objs = src->objs;\n+\t\tdst->size = src->size;\n+\t\tsrc->objs = dobjs;\n+\t\tsrc->size = dsz;\n+\t\tdst->idx = src->idx;\n+\t\t__rte_node_enqueue_tail_update(graph, dst);\n+\t} else  { /* Move the objects from src node to dst node */\n+\t\trte_node_enqueue(graph, src, next, src->objs, src->idx);\n+\t}\n+}\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* _RTE_GRAPH_WORKER_H_ */\ndiff --git a/lib/meson.build b/lib/meson.build\nindex 0af3efab2..4089ce0c3 100644\n--- a/lib/meson.build\n+++ b/lib/meson.build\n@@ -30,7 +30,7 @@ libraries = [\n \t# add pkt framework libs which use other libs from above\n \t'port', 'table', 'pipeline',\n \t# flow_classify lib depends on pkt framework table lib\n-\t'flow_classify', 'bpf', 'telemetry']\n+\t'flow_classify', 'bpf', 'graph', 'telemetry']\n \n if is_windows\n \tlibraries = ['kvargs','eal'] # only supported libraries for windows\ndiff --git a/mk/rte.app.mk b/mk/rte.app.mk\nindex 15acf95db..e169d7a7b 100644\n--- a/mk/rte.app.mk\n+++ b/mk/rte.app.mk\n@@ -98,6 +98,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline\n _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder\n _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched\n _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu\n+_LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph\n \n ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)\n _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni\n",
    "prefixes": [
        "RFC",
        "1/5"
    ]
}