get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 139704,
    "url": "http://patches.dpdk.org/api/patches/139704/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20240426122203.32357-2-nsaxena@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": "<20240426122203.32357-2-nsaxena@marvell.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20240426122203.32357-2-nsaxena@marvell.com",
    "date": "2024-04-26T12:22:02",
    "name": "[RFC,1/2] graph: add feature arc support",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "ef255c584e25de4c90ae768e1b0019263d8ae07b",
    "submitter": {
        "id": 3320,
        "url": "http://patches.dpdk.org/api/people/3320/?format=api",
        "name": "Nitin Saxena",
        "email": "nsaxena@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/20240426122203.32357-2-nsaxena@marvell.com/mbox/",
    "series": [
        {
            "id": 31832,
            "url": "http://patches.dpdk.org/api/series/31832/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=31832",
            "date": "2024-04-26T12:22:03",
            "name": "add feature arc in rte_graph",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/31832/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/139704/comments/",
    "check": "pending",
    "checks": "http://patches.dpdk.org/api/patches/139704/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 mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id EB1AF43F15;\n\tFri, 26 Apr 2024 14:22:58 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id BB60743DBC;\n\tFri, 26 Apr 2024 14:22:54 +0200 (CEST)",
            "from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com\n [67.231.148.174])\n by mails.dpdk.org (Postfix) with ESMTP id 9F62543DB0\n for <dev@dpdk.org>; Fri, 26 Apr 2024 14:22:51 +0200 (CEST)",
            "from pps.filterd (m0045849.ppops.net [127.0.0.1])\n by mx0a-0016f401.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id\n 43Q9hLbq027079;\n Fri, 26 Apr 2024 05:22:45 -0700",
            "from dc6wp-exch02.marvell.com ([4.21.29.225])\n by mx0a-0016f401.pphosted.com (PPS) with ESMTPS id 3xr9vp0cw1-1\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT);\n Fri, 26 Apr 2024 05:22:45 -0700 (PDT)",
            "from DC6WP-EXCH02.marvell.com (10.76.176.209) by\n DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server\n (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n 15.2.1544.4; Fri, 26 Apr 2024 05:22:09 -0700",
            "from maili.marvell.com (10.69.176.80) by DC6WP-EXCH02.marvell.com\n (10.76.176.209) with Microsoft SMTP Server id 15.2.1544.4 via Frontend\n Transport; Fri, 26 Apr 2024 05:22:09 -0700",
            "from localhost.localdomain (unknown [10.28.36.207])\n by maili.marvell.com (Postfix) with ESMTP id 243A43F706D;\n Fri, 26 Apr 2024 05:22:06 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h=\n from:to:cc:subject:date:message-id:in-reply-to:references\n :mime-version:content-transfer-encoding:content-type; s=\n pfpt0220; bh=zgi+stlr7B2wH8I3XqwszmcETdOJEg2+SxZVa0paEuU=; b=PXM\n 8LWWfwP8b5Ygpj7gDWXN3SueToTJ34UtvD8NI6LTbzdOQS1LBolsKu5NccR9+PlP\n XCJfeW3NJTIbPzv9j5RUklmzOGXCxjuUzUrVU0L2SrukvyDHGpqYufyxSxpkZuDB\n XCeqv4DsOqQFKEUBgyCpjzu+BtCydt7rAFthSTdFRv4CGCvmrOA9Bd18IFR4haMG\n 2u+oEUT7vmzE0q10YJzMaInCQmsbyLPVovBRGvorzXwByCshHgkip2q4Ph2PURvx\n ziShdLgq9viBNWIIiKRWcbrIdA8MPbS6G9OvUAw1U7mk4eVz8pnKAY64mG96FBXN\n ya3IO5MqH6XDT2EevEg==",
        "From": "Nitin Saxena <nsaxena@marvell.com>",
        "To": "Jerin Jacob <jerinj@marvell.com>, Kiran Kumar K <kirankumark@marvell.com>,\n Nithin Dabilpuram <ndabilpuram@marvell.com>, Zhirun Yan\n <yanzhirun_163@163.com>",
        "CC": "<dev@dpdk.org>",
        "Subject": "[RFC PATCH 1/2] graph: add feature arc support",
        "Date": "Fri, 26 Apr 2024 17:52:02 +0530",
        "Message-ID": "<20240426122203.32357-2-nsaxena@marvell.com>",
        "X-Mailer": "git-send-email 2.25.1",
        "In-Reply-To": "<20240426122203.32357-1-nsaxena@marvell.com>",
        "References": "<20240426122203.32357-1-nsaxena@marvell.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-Proofpoint-ORIG-GUID": "1XWikibCKjb5Q2nMjjt4yspyF6RKKgor",
        "X-Proofpoint-GUID": "1XWikibCKjb5Q2nMjjt4yspyF6RKKgor",
        "X-Proofpoint-Virus-Version": "vendor=baseguard\n engine=ICAP:2.0.293,Aquarius:18.0.1011,Hydra:6.0.650,FMLib:17.11.176.26\n definitions=2024-04-26_12,2024-04-26_02,2023-05-22_02",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "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"
    },
    "content": "Signed-off-by: Nitin Saxena <nsaxena@marvell.com>\nChange-Id: I8ca9d6285aaeedabc75131af8d28f8b2f63f614b\n---\n lib/graph/graph_feature_arc.c            | 834 +++++++++++++++++++++++\n lib/graph/meson.build                    |   2 +\n lib/graph/rte_graph_feature_arc.h        | 310 +++++++++\n lib/graph/rte_graph_feature_arc_worker.h | 483 +++++++++++++\n lib/graph/version.map                    |  16 +\n 5 files changed, 1645 insertions(+)\n create mode 100644 lib/graph/graph_feature_arc.c\n create mode 100644 lib/graph/rte_graph_feature_arc.h\n create mode 100644 lib/graph/rte_graph_feature_arc_worker.h",
    "diff": "diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c\nnew file mode 100644\nindex 0000000000..fc3662727d\n--- /dev/null\n+++ b/lib/graph/graph_feature_arc.c\n@@ -0,0 +1,834 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2024 Marvell International Ltd.\n+ */\n+\n+#include \"graph_private.h\"\n+#include <rte_graph_feature_arc_worker.h>\n+#include <rte_malloc.h>\n+\n+#define __RTE_GRAPH_FEATURE_ARC_MAX 32\n+\n+#define rte_graph_uint_cast(x) ((unsigned int)x)\n+\n+rte_graph_feature_arc_main_t *__feature_arc_main;\n+\n+static int\n+feature_lookup(struct rte_graph_feature_arc *dfl, const char *feat_name,\n+\t       struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)\n+{\n+\tstruct rte_graph_feature_node_list *finfo = NULL;\n+\tconst char *name;\n+\n+\tif (!feat_name)\n+\t\treturn -1;\n+\n+\tif (slot)\n+\t\t*slot = 0;\n+\n+\tSTAILQ_FOREACH(finfo, &dfl->all_features, next_feature) {\n+\t\tRTE_VERIFY(finfo->feature_arc == dfl);\n+\t\tname = rte_node_id_to_name(finfo->feature_node->id);\n+\t\tif (!strncmp(name, feat_name, RTE_GRAPH_NAMESIZE)) {\n+\t\t\tif (ffinfo)\n+\t\t\t\t*ffinfo = finfo;\n+\t\t\treturn 0;\n+\t\t}\n+\t\tif (slot)\n+\t\t\t(*slot)++;\n+\t}\n+\treturn -1;\n+}\n+\n+static int\n+feature_arc_lookup(rte_graph_feature_arc_t _dfl)\n+{\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\trte_graph_feature_arc_main_t *dm = __feature_arc_main;\n+\tuint32_t iter;\n+\n+\tif (!__feature_arc_main)\n+\t\treturn -1;\n+\n+\tfor (iter = 0; iter < dm->max_feature_arcs; iter++) {\n+\t\tif (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)\n+\t\t\tcontinue;\n+\n+\t\tif (dfl == (rte_graph_feature_arc_get(dm->feature_arcs[iter])))\n+\t\t\treturn 0;\n+\t}\n+\treturn -1;\n+}\n+\n+static int\n+get_existing_edge(const char *arc_name, struct rte_node_register *parent_node,\n+\t\t  struct rte_node_register *child_node, rte_edge_t *_edge)\n+{\n+\tchar **next_edges = NULL;\n+\tuint32_t count, i;\n+\n+\tRTE_SET_USED(arc_name);\n+\n+\tcount = rte_node_edge_get(parent_node->id, NULL);\n+\tnext_edges = malloc(count);\n+\n+\tif (!next_edges)\n+\t\treturn -1;\n+\n+\tcount = rte_node_edge_get(parent_node->id, next_edges);\n+\tfor (i = 0; i < count; i++) {\n+\t\tif (strstr(child_node->name, next_edges[i])) {\n+\t\t\tgraph_dbg(\"%s: Edge exists [%s[%u]: \\\"%s\\\"]\", arc_name,\n+\t\t\t\t  parent_node->name, i, child_node->name);\n+\t\t\tif (_edge)\n+\t\t\t\t*_edge = (rte_edge_t)i;\n+\n+\t\t\tfree(next_edges);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\tfree(next_edges);\n+\n+\treturn -1;\n+}\n+\n+static int\n+connect_graph_nodes(struct rte_node_register *parent_node, struct rte_node_register *child_node,\n+\t\t    rte_edge_t *_edge, char *arc_name)\n+{\n+\tconst char *next_node = NULL;\n+\trte_edge_t edge;\n+\n+\tif (!get_existing_edge(arc_name, parent_node, child_node, &edge)) {\n+\t\tgraph_dbg(\"%s: add_feature: Edge reused [%s[%u]: \\\"%s\\\"]\", arc_name,\n+\t\t\tparent_node->name, edge, child_node->name);\n+\n+\t\tif (_edge)\n+\t\t\t*_edge = edge;\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* Node to be added */\n+\tnext_node = child_node->name;\n+\n+\tedge = rte_node_edge_update(parent_node->id, RTE_EDGE_ID_INVALID, &next_node, 1);\n+\n+\tif (edge == RTE_EDGE_ID_INVALID) {\n+\t\tgraph_err(\"edge invalid\");\n+\t\treturn -1;\n+\t}\n+\tedge = rte_node_edge_count(parent_node->id) - 1;\n+\n+\tgraph_dbg(\"%s: add_feature: edge added [%s[%u]: \\\"%s\\\"]\", arc_name, parent_node->name, edge,\n+\t\tchild_node->name);\n+\n+\tif (_edge)\n+\t\t*_edge = edge;\n+\n+\treturn 0;\n+}\n+\n+static int\n+feature_arc_init(rte_graph_feature_arc_main_t **pfl, uint32_t max_feature_arcs)\n+{\n+\trte_graph_feature_arc_main_t *pm = NULL;\n+\tuint32_t i;\n+\tsize_t sz;\n+\n+\tif (!pfl)\n+\t\treturn -1;\n+\n+\tsz = sizeof(rte_graph_feature_arc_main_t) +\n+\t\t(sizeof(pm->feature_arcs[0]) * max_feature_arcs);\n+\n+\tpm = malloc(sz);\n+\tif (!pm)\n+\t\treturn -1;\n+\n+\tmemset(pm, 0, sz);\n+\n+\tfor (i = 0; i < max_feature_arcs; i++)\n+\t\tpm->feature_arcs[i] = RTE_GRAPH_FEATURE_ARC_INITIALIZER;\n+\n+\tpm->max_feature_arcs = max_feature_arcs;\n+\n+\t*pfl = pm;\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_graph_feature_arc_init(int max_feature_arcs)\n+{\n+\tif (!max_feature_arcs)\n+\t\treturn -1;\n+\n+\tif (__feature_arc_main)\n+\t\treturn -1;\n+\n+\treturn feature_arc_init(&__feature_arc_main, max_feature_arcs);\n+}\n+\n+int\n+rte_graph_feature_arc_create(const char *feature_arc_name, int max_features, int max_indexes,\n+\t\t       struct rte_node_register *start_node, rte_graph_feature_arc_t *_dfl)\n+{\n+\tchar name[2 * RTE_GRAPH_FEATURE_ARC_NAMELEN];\n+\trte_graph_feature_arc_main_t *dfm = NULL;\n+\tstruct rte_graph_feature_arc *dfl = NULL;\n+\tstruct rte_graph_feature_data *dfd = NULL;\n+\tstruct rte_graph_feature *df = NULL;\n+\tuint32_t iter, j, arc_index;\n+\tsize_t sz;\n+\n+\tif (!_dfl)\n+\t\treturn -1;\n+\n+\tif (max_features < 1)\n+\t\treturn -1;\n+\n+\tif (!start_node)\n+\t\treturn -1;\n+\n+\tif (!feature_arc_name)\n+\t\treturn -1;\n+\n+\tif (max_features > RTE_GRAPH_FEATURE_MAX_PER_ARC) {\n+\t\tgraph_err(\"Invalid max features: %u\", max_features);\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * Application hasn't called rte_graph_feature_arc_init(). Initialize with\n+\t * default values\n+\t */\n+\tif (!__feature_arc_main) {\n+\t\tif (rte_graph_feature_arc_init((int)__RTE_GRAPH_FEATURE_ARC_MAX) < 0) {\n+\t\t\tgraph_err(\"rte_graph_feature_arc_init() failed\");\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+\tdfm = __feature_arc_main;\n+\n+\t/* threshold check */\n+\tif (dfm->num_feature_arcs > (dfm->max_feature_arcs - 1)) {\n+\t\tgraph_err(\"max threshold for num_feature_arcs: %d reached\",\n+\t\t\tdfm->max_feature_arcs - 1);\n+\t\treturn -1;\n+\t}\n+\t/* Find the free slot for feature arc */\n+\tfor (iter = 0; iter < dfm->max_feature_arcs; iter++) {\n+\t\tif (dfm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)\n+\t\t\tbreak;\n+\t}\n+\tarc_index = iter;\n+\n+\tif (arc_index >= dfm->max_feature_arcs) {\n+\t\tgraph_err(\"No free slot found for num_feature_arc\");\n+\t\treturn -1;\n+\t}\n+\n+\t/* This should not happen */\n+\tRTE_VERIFY(dfm->feature_arcs[arc_index] == RTE_GRAPH_FEATURE_ARC_INITIALIZER);\n+\n+\tsz = sizeof(*dfl) + (sizeof(uint64_t) * max_indexes);\n+\n+\tdfl = rte_malloc(feature_arc_name, sz, RTE_CACHE_LINE_SIZE);\n+\n+\tif (!dfl) {\n+\t\tgraph_err(\"malloc failed for feature_arc_create()\");\n+\t\treturn -1;\n+\t}\n+\n+\tmemset(dfl, 0, sz);\n+\n+\tsnprintf(name, sizeof(name), \"%s-%s\", feature_arc_name, \"feat\");\n+\n+\tdfl->features_by_index =\n+\t\trte_malloc(name, sizeof(struct rte_graph_feature) * max_indexes,\n+\t\t\t   RTE_CACHE_LINE_SIZE);\n+\n+\tif (!dfl->features_by_index) {\n+\t\trte_free(dfl);\n+\t\tgraph_err(\"rte_malloc failed for allocating features_by_index()\");\n+\t\treturn -ENOMEM;\n+\t}\n+\tmemset(dfl->features_by_index, 0, sizeof(rte_graph_feature_t) * max_indexes);\n+\n+\t/* Initialize rte_graph port group fixed variables */\n+\tSTAILQ_INIT(&dfl->all_features);\n+\tstrncpy(dfl->feature_arc_name, feature_arc_name, RTE_GRAPH_FEATURE_ARC_NAMELEN - 1);\n+\tdfl->feature_arc_main = (void *)dfm;\n+\tdfl->start_node = start_node;\n+\tdfl->max_features = max_features;\n+\tdfl->max_indexes = max_indexes;\n+\n+\tfor (iter = 0; iter < dfl->max_indexes; iter++) {\n+\t\tdf = rte_graph_feature_get(dfl, iter);\n+\t\tfor (j = 0; j < dfl->max_features; j++) {\n+\t\t\tdfd = rte_graph_feature_data_get(df, j);\n+\t\t\tdfd->feature_data_index = RTE_GRAPH_FEATURE_INVALID_VALUE;\n+\t\t}\n+\t}\n+\tdfl->feature_arc_index = arc_index;\n+\tdfm->feature_arcs[dfl->feature_arc_index] = (rte_graph_feature_arc_t)dfl;\n+\tdfm->num_feature_arcs++;\n+\n+\tif (_dfl)\n+\t\t*_dfl = (rte_graph_feature_arc_t)dfl;\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_graph_feature_add(rte_graph_feature_arc_t _dfl, struct rte_node_register *feature_node,\n+\t\tconst char *after_feature, const char *before_feature)\n+{\n+\tstruct rte_graph_feature_node_list *after_finfo = NULL, *before_finfo = NULL;\n+\tstruct rte_graph_feature_node_list *temp = NULL, *finfo = NULL;\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\tuint32_t slot, add_flag;\n+\trte_edge_t edge = -1;\n+\n+\tRTE_VERIFY(dfl->feature_arc_main == __feature_arc_main);\n+\n+\tif (feature_arc_lookup(_dfl)) {\n+\t\tgraph_err(\"invalid feature arc: 0x%016\" PRIx64, (uint64_t)_dfl);\n+\t\treturn -1;\n+\t}\n+\n+\tif (dfl->feature_enable_started) {\n+\t\tgraph_err(\"adding features after enabling any one of them is not supported\");\n+\t\treturn -1;\n+\t}\n+\n+\tif ((after_feature != NULL) && (before_feature != NULL) &&\n+\t    (after_feature == before_feature)) {\n+\t\tgraph_err(\"after_feature and before_feature are same '%s:%s]\", after_feature,\n+\t\t\tbefore_feature);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!feature_node) {\n+\t\tgraph_err(\"feature_node: %p invalid\", feature_node);\n+\t\treturn -1;\n+\t}\n+\n+\tdfl = rte_graph_feature_arc_get(_dfl);\n+\n+\tif (feature_node->id == RTE_NODE_ID_INVALID) {\n+\t\tgraph_err(\"Invalid node: %s\", feature_node->name);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!feature_lookup(dfl, feature_node->name, &finfo, &slot)) {\n+\t\tgraph_err(\"%s feature already added\", feature_node->name);\n+\t\treturn -1;\n+\t}\n+\n+\tif (slot >= RTE_GRAPH_FEATURE_MAX_PER_ARC) {\n+\t\tgraph_err(\"Max slot %u reached for feature addition\", slot);\n+\t\treturn -1;\n+\t}\n+\n+\tif (strstr(feature_node->name, dfl->start_node->name)) {\n+\t\tgraph_err(\"Feature %s cannot point to itself: %s\", feature_node->name,\n+\t\t\tdfl->start_node->name);\n+\t\treturn -1;\n+\t}\n+\n+\tif (connect_graph_nodes(dfl->start_node, feature_node, &edge, dfl->feature_arc_name)) {\n+\t\tgraph_err(\"unable to connect %s -> %s\", dfl->start_node->name, feature_node->name);\n+\t\treturn -1;\n+\t}\n+\n+\tfinfo = malloc(sizeof(*finfo));\n+\tif (!finfo)\n+\t\treturn -1;\n+\n+\tmemset(finfo, 0, sizeof(*finfo));\n+\n+\tfinfo->feature_arc = (void *)dfl;\n+\tfinfo->feature_node = feature_node;\n+\tfinfo->edge_to_this_feature = edge;\n+\n+\t/* Check for before and after constraints */\n+\tif (before_feature) {\n+\t\t/* before_feature sanity */\n+\t\tif (feature_lookup(dfl, before_feature, &before_finfo, NULL))\n+\t\t\tSET_ERR_JMP(EINVAL, finfo_free,\n+\t\t\t\t     \"Invalid before feature name: %s\", before_feature);\n+\n+\t\tif (!before_finfo)\n+\t\t\tSET_ERR_JMP(EINVAL, finfo_free,\n+\t\t\t\t     \"before_feature %s does not exist\", before_feature);\n+\n+\t\t/*\n+\t\t * Starting from 0 to before_feature, continue connecting edges\n+\t\t */\n+\t\tadd_flag = 1;\n+\t\tSTAILQ_FOREACH(temp, &dfl->all_features, next_feature) {\n+\t\t\t/*\n+\t\t\t * As soon as we see before_feature. stop adding edges\n+\t\t\t */\n+\t\t\tif (!strncmp(temp->feature_node->name, before_feature,\n+\t\t\t\t     RTE_GRAPH_NAMESIZE))\n+\t\t\t\tif (!connect_graph_nodes(finfo->feature_node, temp->feature_node,\n+\t\t\t\t\t\t\t &edge, dfl->feature_arc_name))\n+\t\t\t\t\tadd_flag = 0;\n+\n+\t\t\tif (add_flag)\n+\t\t\t\tconnect_graph_nodes(temp->feature_node, finfo->feature_node, NULL,\n+\t\t\t\t\t\t    dfl->feature_arc_name);\n+\t\t}\n+\t}\n+\n+\tif (after_feature) {\n+\t\tif (feature_lookup(dfl, after_feature, &after_finfo, NULL))\n+\t\t\tSET_ERR_JMP(EINVAL, finfo_free,\n+\t\t\t\t     \"Invalid after feature_name %s\", after_feature);\n+\n+\t\tif (!after_finfo)\n+\t\t\tSET_ERR_JMP(EINVAL, finfo_free,\n+\t\t\t\t     \"after_feature %s does not exist\", after_feature);\n+\n+\t\t/* Starting from after_feature to end continue connecting edges */\n+\t\tadd_flag = 0;\n+\t\tSTAILQ_FOREACH(temp, &dfl->all_features, next_feature) {\n+\t\t\t/* We have already seen after_feature now */\n+\t\t\tif (add_flag)\n+\t\t\t\t/* Add all features as next node to current feature*/\n+\t\t\t\tconnect_graph_nodes(finfo->feature_node, temp->feature_node, NULL,\n+\t\t\t\t\t\t    dfl->feature_arc_name);\n+\n+\t\t\t/* as soon as we see after_feature. start adding edges\n+\t\t\t * from next iteration\n+\t\t\t */\n+\t\t\tif (!strncmp(temp->feature_node->name, after_feature, RTE_GRAPH_NAMESIZE))\n+\t\t\t\t/* connect after_feature to this feature */\n+\t\t\t\tif (!connect_graph_nodes(temp->feature_node, finfo->feature_node,\n+\t\t\t\t\t\t\t &edge, dfl->feature_arc_name))\n+\t\t\t\t\tadd_flag = 1;\n+\t\t}\n+\n+\t\t/* add feature next to after_feature */\n+\t\tSTAILQ_INSERT_AFTER(&dfl->all_features, after_finfo, finfo, next_feature);\n+\t} else {\n+\t\tif (before_finfo) {\n+\t\t\tafter_finfo = NULL;\n+\t\t\tSTAILQ_FOREACH(temp, &dfl->all_features, next_feature) {\n+\t\t\t\tif (before_finfo == temp) {\n+\t\t\t\t\tif (after_finfo)\n+\t\t\t\t\t\tSTAILQ_INSERT_AFTER(&dfl->all_features, after_finfo,\n+\t\t\t\t\t\t\t\t    finfo, next_feature);\n+\t\t\t\t\telse\n+\t\t\t\t\t\tSTAILQ_INSERT_HEAD(&dfl->all_features, finfo,\n+\t\t\t\t\t\t\t\t   next_feature);\n+\n+\t\t\t\t\treturn 0;\n+\t\t\t\t}\n+\t\t\t\tafter_finfo = temp;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tSTAILQ_INSERT_TAIL(&dfl->all_features, finfo, next_feature);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+\n+finfo_free:\n+\tfree(finfo);\n+\n+\treturn -1;\n+}\n+\n+int\n+rte_graph_feature_destroy(rte_graph_feature_arc_t _dfl, const char *feature_name)\n+{\n+\tRTE_SET_USED(_dfl);\n+\tRTE_SET_USED(feature_name);\n+\treturn 0;\n+}\n+\n+int\n+rte_graph_feature_validate(rte_graph_feature_arc_t _dfl, uint32_t index, const char *feature_name,\n+\t\t     int is_enable_disable)\n+{\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\tstruct rte_graph_feature_node_list *finfo = NULL;\n+\tstruct rte_graph_feature_data *dfd = NULL;\n+\tstruct rte_graph_feature *df = NULL;\n+\tuint32_t slot;\n+\n+\t/* validate _dfl */\n+\tif (dfl->feature_arc_main != __feature_arc_main) {\n+\t\tgraph_err(\"invalid feature arc: 0x%016\" PRIx64, (uint64_t)_dfl);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* validate index */\n+\tif (index >= dfl->max_indexes) {\n+\t\tgraph_err(\"%s: Invalid provided index: %u >= %u configured\", dfl->feature_arc_name,\n+\t\t\tindex, dfl->max_indexes);\n+\t\treturn -1;\n+\t}\n+\n+\t/* validate feature_name is already added or not  */\n+\tif (feature_lookup(dfl, feature_name, &finfo, &slot)) {\n+\t\tgraph_err(\"%s: No feature %s added\", dfl->feature_arc_name, feature_name);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!finfo) {\n+\t\tgraph_err(\"%s: No feature: %s found\", dfl->feature_arc_name, feature_name);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* slot should be in valid range */\n+\tif (slot >= dfl->max_features) {\n+\t\tgraph_err(\"%s/%s: Invalid free slot %u(max=%u) for feature\", dfl->feature_arc_name,\n+\t\t\tfeature_name, slot, dfl->max_features);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tdf = rte_graph_feature_get(dfl, index);\n+\n+\t/* Exceeded all enabled features for index */\n+\tif (is_enable_disable && (df->num_enabled_features >= dfl->max_features)) {\n+\t\tgraph_err(\"%s: Index: %u has already enabled all features(%d/%d)\",\n+\t\t\tdfl->feature_arc_name, index, df->num_enabled_features, dfl->max_features);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tdfd = rte_graph_feature_data_get(df, slot);\n+\n+\t/* validate via bitmask if asked feature is already enabled on index */\n+\tif (is_enable_disable && (dfl->feature_bit_mask_by_index[index] &\n+\t\t\t\t  RTE_BIT64(slot))) {\n+\t\tgraph_err(\"%s: %s already enabled on index: %u\",\n+\t\t\t  dfl->feature_arc_name, feature_name, index);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!is_enable_disable && !(dfl->feature_bit_mask_by_index[index] & RTE_BIT64(slot))) {\n+\t\tgraph_err(\"%s: %s not enabled in bitmask for index: %u\", dfl->feature_arc_name,\n+\t\t\tfeature_name, index);\n+\t\treturn -1;\n+\t}\n+\n+\t/* validate via feature data that feature_data not in use */\n+\tif (is_enable_disable && (dfd->feature_data_index !=\n+\t\t\t\t  RTE_GRAPH_FEATURE_INVALID_VALUE)) {\n+\t\tgraph_err(\"%s/%s: slot: %u already in use by %s\",\n+\t\t\t  dfl->feature_arc_name, feature_name, slot,\n+\t\t\t  dfd->node_info->feature_node->name);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!is_enable_disable && (dfd->feature_data_index == RTE_GRAPH_FEATURE_INVALID_VALUE)) {\n+\t\tgraph_err(\"%s/%s: feature data slot: %u not in use \", dfl->feature_arc_name,\n+\t\t\tfeature_name, slot);\n+\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n+int\n+rte_graph_feature_enable(rte_graph_feature_arc_t _dfl, uint32_t index, const\n+\t\t\t char *feature_name, int64_t data)\n+{\n+\tstruct rte_graph_feature_data *dfd = NULL, *prev_dfd = NULL, *next_dfd = NULL;\n+\tuint64_t original_mask, lower_feature_mask, upper_feature_mask;\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\tstruct rte_graph_feature_node_list *finfo = NULL;\n+\tuint32_t slot, prev_feature, next_feature;\n+\tstruct rte_graph_feature *df = NULL;\n+\trte_edge_t edge = 0;\n+\tint rc = 0;\n+\n+\tif (rte_graph_feature_validate(_dfl, index, feature_name, 1))\n+\t\treturn -1;\n+\n+\tif (feature_lookup(dfl, feature_name, &finfo, &slot))\n+\t\treturn -1;\n+\n+\tdf = rte_graph_feature_get(dfl, index);\n+\tdfd = rte_graph_feature_data_get(df, slot);\n+\n+\tgraph_dbg(\"%s: Enabling feature %s in index: %u at slot %u\", dfl->feature_arc_name,\n+\t\tfeature_name, index, slot);\n+\n+\tmemset(dfd, 0, sizeof(*dfd));\n+\n+\t/* app data */\n+\tdfd->data = data;\n+\t/* First fill invalid value until everything succeeds */\n+\tdfd->feature_data_index = RTE_GRAPH_FEATURE_INVALID_VALUE;\n+\tdfd->node_info = finfo;\n+\n+\t/* edge from base feature arc node to this feature */\n+\tdfd->edge_to_this_feature = finfo->edge_to_this_feature;\n+\tdfd->edge_to_next_feature = RTE_GRAPH_FEATURE_INVALID_VALUE;\n+\n+\t/* This should be the case */\n+\tRTE_VERIFY(slot == (dfd - df->feature_data));\n+\n+\t/* Adjust next edge for previous enabled feature and next enabled\n+\t * feature for this index\n+\t */\n+\toriginal_mask = dfl->feature_bit_mask_by_index[index];\n+\n+\t/* If slot == 0, no lower feature is enabled\n+\t * if slot = 1, lower_feature_mask = 0x1,\n+\t * if slot = 2, lower_feature_mask = 0x3,\n+\t * if slot = 3, lower_feature_mask = 0x7,\n+\t */\n+\tlower_feature_mask = (slot) ? (RTE_BIT64(slot) - 1) : 0;\n+\n+\t/*\n+\t * If slot =0, upper_feature_mask = (0xff ff ff ff ff ff ff ff) & ~lower_feature_mask\n+\t * If slot =1, upper_feature_mask = (0xff ff ff ff ff ff ff fe) & ~lower_feature_mask\n+\t * If slot =2, upper_feature_mask = (0xff ff ff ff ff ff ff fc) & ~lower_feature_mask\n+\t * If slot =3, upper_feature_mask = (0xff ff ff ff ff ff ff f8) & ~lower_feature_mask\n+\t * If slot =4, upper_feature_mask = (0xff ff ff ff ff ff ff f0) & ~lower_feature_mask\n+\t */\n+\tupper_feature_mask = ~(RTE_BIT64(slot)) & (~lower_feature_mask);\n+\n+\t/* And with original bit mask */\n+\tupper_feature_mask &= original_mask;\n+\n+\t/* set bits lesser than slot */\n+\tlower_feature_mask &= original_mask;\n+\n+\t/* immediate lower enabled feature wrt slot is most significant bit in\n+\t * lower_feature_mask\n+\t */\n+\tprev_feature = rte_fls_u64(lower_feature_mask);\n+\n+\tif (prev_feature) {\n+\t\t/* for us slot starts from 0 instead of 1 */\n+\t\tprev_feature--;\n+\t\tprev_dfd = rte_graph_feature_data_get(df, prev_feature);\n+\n+\t\tgraph_dbg(\"%s: enabling for index: %u, %s[] = %s\", dfl->feature_arc_name, index,\n+\t\t\tprev_dfd->node_info->feature_node->name,\n+\t\t\tdfd->node_info->feature_node->name);\n+\t\tRTE_VERIFY(prev_dfd->feature_data_index != RTE_GRAPH_FEATURE_INVALID_VALUE);\n+\t\tif (get_existing_edge(dfl->feature_arc_name, prev_dfd->node_info->feature_node,\n+\t\t\t\t      dfd->node_info->feature_node, &edge)) {\n+\t\t\tgraph_err(\"%s: index: %u, Could not add next edge from %s to %s\",\n+\t\t\t\tdfl->feature_arc_name, index,\n+\t\t\t\tprev_dfd->node_info->feature_node->name,\n+\t\t\t\tdfd->node_info->feature_node->name);\n+\t\t\trc = -1;\n+\t\t} else {\n+\t\t\tgraph_dbg(\"%s: enabled for index: %u, slot %u, %s[%u] = %s\",\n+\t\t\t\tdfl->feature_arc_name, index, slot,\n+\t\t\t\tprev_dfd->node_info->feature_node->name, edge,\n+\t\t\t\tdfd->node_info->feature_node->name);\n+\t\t\tprev_dfd->edge_to_next_feature = edge;\n+\t\t}\n+\t\tif (rc < 0)\n+\t\t\treturn -1;\n+\t}\n+\n+\t/* immediate next upper feature wrt slot is least significant bit in\n+\t * upper_feature_mask\n+\t */\n+\trc = 0;\n+\tif (rte_bsf64_safe(upper_feature_mask, &next_feature)) {\n+\t\tnext_dfd = rte_graph_feature_data_get(df, next_feature);\n+\n+\t\tgraph_dbg(\"%s: enabling for index: %u, %s[] = %s \", dfl->feature_arc_name, index,\n+\t\t\tdfd->node_info->feature_node->name,\n+\t\t\tnext_dfd->node_info->feature_node->name);\n+\t\tRTE_VERIFY(next_dfd->feature_data_index != RTE_GRAPH_FEATURE_INVALID_VALUE);\n+\t\tif (get_existing_edge(dfl->feature_arc_name, dfd->node_info->feature_node,\n+\t\t\t\t      next_dfd->node_info->feature_node, &edge)) {\n+\t\t\tgraph_err(\"%s: index: %u, Could not add next edge from %s to %s\",\n+\t\t\t\tdfl->feature_arc_name, index, dfd->node_info->feature_node->name,\n+\t\t\t\tnext_dfd->node_info->feature_node->name);\n+\t\t\trc = -1;\n+\t\t} else {\n+\t\t\tgraph_dbg(\"%s: enabled for index: %u, slot %u, %s[%u] = %s\",\n+\t\t\t\tdfl->feature_arc_name, index, slot,\n+\t\t\t\tdfd->node_info->feature_node->name, edge,\n+\t\t\t\tnext_dfd->node_info->feature_node->name);\n+\t\t\tdfd->edge_to_next_feature = edge;\n+\t\t}\n+\t\tif (rc < 0)\n+\t\t\treturn -1;\n+\t}\n+\n+\tgraph_dbg(\"%s: enabled for index: %u, slot %u, %s[%u] = %s\", dfl->feature_arc_name, index,\n+\t\tslot, dfl->start_node->name, dfd->edge_to_this_feature,\n+\t\tdfd->node_info->feature_node->name);\n+\n+\t/* Make dfd valid now */\n+\tdfd->feature_data_index = dfd - df->feature_data;\n+\n+\t/* Increase feature node info reference count */\n+\tfinfo->ref_count++;\n+\n+\t/* Increment number of enabled feature on this index */\n+\tdf->num_enabled_features++;\n+\n+\t/* Make rte_graph_feature_add() disable for this feature arc now */\n+\tdfl->feature_enable_started++;\n+\n+\t/* Update bitmask feature arc bit mask */\n+\trte_bit_relaxed_set64(rte_graph_uint_cast(slot), &dfl->feature_bit_mask_by_index[index]);\n+\n+\t/* Make sure changes made into affect */\n+\tRTE_VERIFY(dfl->feature_bit_mask_by_index[index] & RTE_BIT64(slot));\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_graph_feature_disable(rte_graph_feature_arc_t _dfl, uint32_t index, const char *feature_name)\n+{\n+\tstruct rte_graph_feature_data *dfd = NULL, *prev_dfd = NULL, *next_dfd = NULL;\n+\tuint64_t original_mask, lower_feature_mask, upper_feature_mask;\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\tstruct rte_graph_feature_node_list *finfo = NULL;\n+\tuint32_t slot, prev_feature, next_feature;\n+\tstruct rte_graph_feature *df = NULL;\n+\trte_edge_t edge = 0;\n+\tint rc = 0;\n+\n+\tif (rte_graph_feature_validate(_dfl, index, feature_name, 0))\n+\t\treturn -1;\n+\n+\tif (feature_lookup(dfl, feature_name, &finfo, &slot))\n+\t\treturn -1;\n+\n+\tdf = rte_graph_feature_get(dfl, index);\n+\tdfd = rte_graph_feature_data_get(df, slot);\n+\n+\t/* This should be the case */\n+\tRTE_VERIFY(slot == (dfd - df->feature_data));\n+\n+\tgraph_dbg(\"%s: Disbling feature %s in index: %u at slot %u\", dfl->feature_arc_name,\n+\t\tfeature_name, index, slot);\n+\n+\t/* Adjust next edge for previous enabled feature and next enabled\n+\t * feature for this index\n+\t */\n+\toriginal_mask = dfl->feature_bit_mask_by_index[index];\n+\n+\tlower_feature_mask = (slot) ? (RTE_BIT64(slot) - 1) : 0;\n+\tupper_feature_mask = ~(RTE_BIT64(slot)) & (~lower_feature_mask);\n+\tupper_feature_mask &= original_mask;\n+\tlower_feature_mask &= original_mask;\n+\n+\t/* immediate lower enabled feature wrt slot is most significant bit in\n+\t * lower_feature_mask\n+\t */\n+\tprev_feature = rte_fls_u64(lower_feature_mask);\n+\n+\tif (prev_feature) {\n+\t\t/* for us slot starts from 0 instead of 1 */\n+\t\tprev_feature--;\n+\t\tprev_dfd = rte_graph_feature_data_get(df, prev_feature);\n+\n+\t\t/* Adjust later to next enabled feature below */\n+\t\tprev_dfd->edge_to_next_feature = RTE_GRAPH_FEATURE_INVALID_VALUE;\n+\n+\t\t/* If we also have next enable feature */\n+\t\tif (rte_bsf64_safe(upper_feature_mask, &next_feature)) {\n+\t\t\tnext_dfd = rte_graph_feature_data_get(df, next_feature);\n+\n+\t\t\tgraph_dbg(\"%s: index: %u updating next enabled feature for %s to %s \",\n+\t\t\t\tdfl->feature_arc_name, index,\n+\t\t\t\tprev_dfd->node_info->feature_node->name,\n+\t\t\t\tnext_dfd->node_info->feature_node->name);\n+\t\t\tif (get_existing_edge(dfl->feature_arc_name,\n+\t\t\t\t\t      prev_dfd->node_info->feature_node,\n+\t\t\t\t\t      next_dfd->node_info->feature_node, &edge)) {\n+\t\t\t\tgraph_err(\"%s: index: %u, Could not get next edge from %s to %s\",\n+\t\t\t\t\tdfl->feature_arc_name, index,\n+\t\t\t\t\tprev_dfd->node_info->feature_node->name,\n+\t\t\t\t\tnext_dfd->node_info->feature_node->name);\n+\t\t\t\trc = -1;\n+\t\t\t} else {\n+\t\t\t\tgraph_dbg(\"%s: index: %u updated next enable feature for %s to %s at edge %u\",\n+\t\t\t\t\tdfl->feature_arc_name, index,\n+\t\t\t\t\tprev_dfd->node_info->feature_node->name,\n+\t\t\t\t\tnext_dfd->node_info->feature_node->name, edge);\n+\t\t\t\tprev_dfd->edge_to_next_feature = edge;\n+\t\t\t}\n+\t\t\tif (rc < 9)\n+\t\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+\t/* First fill invalid value until everything succeeds */\n+\tdfd->feature_data_index = RTE_GRAPH_FEATURE_INVALID_VALUE;\n+\tdfd->edge_to_this_feature = RTE_GRAPH_FEATURE_INVALID_VALUE;\n+\tdfd->edge_to_next_feature = RTE_GRAPH_FEATURE_INVALID_VALUE;\n+\n+\t/* Decrease feature node info reference count */\n+\tfinfo->ref_count--;\n+\n+\t/* Decrement  number of enabled feature on this index */\n+\tdf->num_enabled_features++;\n+\n+\t/* Update bitmask feature arc bit mask */\n+\trte_bit_relaxed_clear64(rte_graph_uint_cast(slot), &dfl->feature_bit_mask_by_index[index]);\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_graph_feature_arc_destroy(rte_graph_feature_arc_t epg)\n+{\n+\tRTE_SET_USED(epg);\n+\treturn 0;\n+}\n+\n+int\n+rte_graph_feature_arc_cleanup(void)\n+{\n+\trte_graph_feature_arc_main_t *dm = __feature_arc_main;\n+\tuint32_t iter;\n+\n+\tif (!__feature_arc_main)\n+\t\treturn -1;\n+\n+\tfor (iter = 0; iter < dm->max_feature_arcs; iter++) {\n+\t\tif (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)\n+\t\t\tcontinue;\n+\n+\t\trte_graph_feature_arc_destroy((rte_graph_feature_arc_t)dm->feature_arcs[iter]);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_graph_feature_arc_lookup_by_name(const char *arc_name, rte_graph_feature_arc_t *_dfl)\n+{\n+\trte_graph_feature_arc_main_t *dm = __feature_arc_main;\n+\tstruct rte_graph_feature_arc *dfl = NULL;\n+\tuint32_t iter;\n+\n+\tif (!__feature_arc_main)\n+\t\treturn -1;\n+\n+\tfor (iter = 0; iter < dm->max_feature_arcs; iter++) {\n+\t\tif (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)\n+\t\t\tcontinue;\n+\n+\t\tdfl = rte_graph_feature_arc_get(dm->feature_arcs[iter]);\n+\n+\t\tif (strstr(arc_name, dfl->feature_arc_name)) {\n+\t\t\tif (_dfl)\n+\t\t\t\t*_dfl = (rte_graph_feature_arc_t)dfl;\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\treturn -1;\n+}\ndiff --git a/lib/graph/meson.build b/lib/graph/meson.build\nindex 0cb15442ab..d916176fb7 100644\n--- a/lib/graph/meson.build\n+++ b/lib/graph/meson.build\n@@ -14,11 +14,13 @@ sources = files(\n         'graph_debug.c',\n         'graph_stats.c',\n         'graph_populate.c',\n+        'graph_feature_arc.c',\n         'graph_pcap.c',\n         'rte_graph_worker.c',\n         'rte_graph_model_mcore_dispatch.c',\n )\n headers = files('rte_graph.h', 'rte_graph_worker.h')\n+headers += files('rte_graph_feature_arc.h', 'rte_graph_feature_arc_worker.h')\n indirect_headers += files(\n         'rte_graph_model_mcore_dispatch.h',\n         'rte_graph_model_rtc.h',\ndiff --git a/lib/graph/rte_graph_feature_arc.h b/lib/graph/rte_graph_feature_arc.h\nnew file mode 100644\nindex 0000000000..f2b428eb1e\n--- /dev/null\n+++ b/lib/graph/rte_graph_feature_arc.h\n@@ -0,0 +1,310 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2024 Marvell International Ltd.\n+ */\n+\n+#ifndef _RTE_GRAPH_FEATURE_ARC_H_\n+#define _RTE_GRAPH_FEATURE_ARC_H_\n+\n+#include <assert.h>\n+#include <errno.h>\n+#include <signal.h>\n+#include <stddef.h>\n+#include <stdint.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include <rte_common.h>\n+#include <rte_compat.h>\n+#include <rte_debug.h>\n+#include <rte_graph.h>\n+#include <rte_graph_worker.h>\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * @file\n+ *\n+ * rte_graph_feature_arc.h\n+ *\n+ * Define APIs and structures/variables with respect to\n+ *\n+ * - Feature arc(s)\n+ * - Feature(s)\n+ *\n+ * A feature arc represents an ordered list of features/protocols at a given\n+ * networking layer. Feature arc provides a high level abstraction to connect\n+ * various rte_graph nodes, designated as *feature nodes*, and allowing\n+ * steering of packets across these feature nodes fast path processing in a\n+ * generic manner. In a typical network stack, often a protocol or feature must\n+ * be first enabled on a given interface, before any packet received on that\n+ * interface is steered to feature processing. For eg: incoming IPv4 packets\n+ * are sent to routing sub-system only after a valid IPv4 address is assigned\n+ * to the received interface. In other words, often packets needs to be steered\n+ * across features not based on the packet content but based on whether feature\n+ * is enable or disable on a given incoming/outgoing interface. Feature arc\n+ * provides mechanism to enable/disable feature(s) on each interface and\n+ * allowing seamless packet steering across enabled feature nodes in fast path.\n+ *\n+ * Feature arc also provides a way to steer packets from standard nodes to\n+ * custom/user-defined *feature nodes* without any change in standard node's\n+ * fast path functions\n+ *\n+ * On a given interface multiple feature(s) might be enabled in a particular\n+ * feature arc. For instance, both \"ipv4-output\" and \"IPsec policy output\"\n+ * features may be enabled on \"eth0\" interface in \"L3-output\" feature arc.\n+ * Similarly, \"ipv6-output\" and \"ipsec-output\" may be enabled on \"eth1\"\n+ * interface in same \"L3-output\" feature arc.\n+ *\n+ * When multiple features are present in a given feature arc, its imperative\n+ * to allow each feature processing in a particular sequential order. For\n+ * instance, in \"L3-input\" feature arc it may be required to run \"IPsec\n+ * input\" feature first, for packet decryption, before \"ip-lookup\".  So a\n+ * sequential order must be maintained among features present in a feature arc.\n+ *\n+ * Features are enabled/disabled multiple times at runtime to some or all\n+ * available interfaces present in the system. Features can be enabled/disabled\n+ * even after @b rte_graph_create() is called. Enable/disabling features on one\n+ * interface is independent of other interface.\n+ *\n+ * A given feature might consume packet (if it's configured to consume) or may\n+ * forward it to next enabled feature. For instance, \"IPsec input\" feature may\n+ * consume/drop all packets with \"Protect\" policy action while all packets with\n+ * policy action as \"Bypass\" may be forwarded to next enabled feature (with in\n+ * same feature arc)\n+ *\n+ * This library facilitates rte graph based applications to steer packets in\n+ * fast path to different feature nodes with-in a feature arc and support all\n+ * functionalities described above\n+ *\n+ * In order to use feature-arc APIs, applications needs to do following in\n+ * control path:\n+ * - Initialize feature arc library via rte_graph_feature_arc_init()\n+ * - Create feature arc via rte_graph_feature_arc_create()\n+ * - Before calling rte_graph_create(), features must be added to feature-arc\n+ *   via rte_graph_feature_add(). rte_graph_feature_add() allows adding\n+ *   features in a sequential order with \"runs_after\" and \"runs_before\"\n+ *   constraints.\n+ * - Post rte_graph_create(), features can be enabled/disabled at runtime on\n+ *   any interface via rte_graph_feature_enable()/rte_graph_feature_disable()\n+ *\n+ * In fast path, nodes uses\n+ * - rte_graph_feature_arc_has_feature() and\n+ *   rte_graph_feature_arc_feature_data_get() APIs to steer packets across\n+ *   feature nodes\n+ *\n+ * rte_graph_feature_enable()/rte_graph_feature_disable() APIs are not\n+ * thread-safe hence must be called by single core while other cores are not\n+ * using any fast path feature arc APIs.\n+ */\n+\n+/**< Initializer value for rte_graph_feature_arc_t */\n+#define RTE_GRAPH_FEATURE_ARC_INITIALIZER ((rte_graph_feature_arc_t)UINT64_MAX)\n+\n+/**< Initializer value for rte_graph_feature_arc_t */\n+#define RTE_GRAPH_FEATURE_INVALID_VALUE UINT16_MAX\n+\n+/** Max number of features supported in a given feature arc */\n+#define RTE_GRAPH_FEATURE_MAX_PER_ARC 64\n+\n+/** Length of feature arc name */\n+#define RTE_GRAPH_FEATURE_ARC_NAMELEN RTE_NODE_NAMESIZE\n+\n+/** @internal */\n+#define rte_graph_feature_cast(x) ((rte_graph_feature_t)x)\n+\n+/** rte_graph feature arc object */\n+typedef uint64_t rte_graph_feature_arc_t;\n+\n+/** rte_graph feature object */\n+typedef uint32_t rte_graph_feature_t;\n+\n+/**\n+ * Initialize feature arc subsystem\n+ *\n+ * @param max_feature_arcs\n+ *   Maximum number of feature arcs required to be supported\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_arc_init(int max_feature_arcs);\n+\n+/**\n+ * Create a feature arc\n+ *\n+ * @param feature_arc_name\n+ *   Feature arc name with max length of @ref RTE_GRAPH_FEATURE_ARC_NAMELEN\n+ * @param max_features\n+ *   Maximum number of features to be supported in this feature arc\n+ * @param max_indexes\n+ *   Maximum number of interfaces/ports/indexes to be supported\n+ * @param start_node\n+ *   Base node where this feature arc's features are checked in fast path\n+ * @param[out] _dfl\n+ *  Feature arc object\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_arc_create(const char *feature_arc_name, int max_features, int max_indexes,\n+\t\t\t   struct rte_node_register *start_node, rte_graph_feature_arc_t *_dfl);\n+\n+/**\n+ * Get feature arc object with name\n+ *\n+ * @param arc_name\n+ *   Feature arc name provided to successful @ref rte_graph_feature_arc_create\n+ * @param[out] _dfl\n+ *   Feature arc object returned\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_arc_lookup_by_name(const char *arc_name, rte_graph_feature_arc_t *_dfl);\n+\n+/**\n+ * Add a feature to already created feature arc\n+ *\n+ * @param _dfl\n+ *   Feature arc handle returned from @ref rte_graph_feature_arc_create()\n+ * @param feature_node\n+ *   Graph node representing feature. On success, feature_node is next_node of\n+ *   feature_arc->start_node\n+ * @param runs_after\n+ *   Add this feature_node after already added \"runs_after\". Creates\n+ *   start_node -> runs_after -> this_feature sequence\n+ * @param runs_before\n+ *  Add this feature_node before already added \"runs_before\". Creates\n+ *  start_node -> this_feature -> runs_before sequence\n+ *\n+ * <I> Must be called before rte_graph_create </I>\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_add(rte_graph_feature_arc_t _dfl, struct rte_node_register *feature_node,\n+\t\t    const char *runs_after, const char *runs_before);\n+\n+/**\n+ * Enable feature within a feature arc\n+ *\n+ * Must be called after @b rte_graph_create(). API is NOT Thread-safe\n+ *\n+ * @param _dfl\n+ *   Feature arc object returned by @ref rte_graph_feature_arc_create or @ref\n+ *   rte_graph_feature_arc_lookup_by_name\n+ * @param index\n+ *   Application specific index. Can be corresponding to interface_id/port_id etc\n+ * @param feature_name\n+ *   Name of the node which is already added via @ref rte_graph_feature_add\n+ * @param data\n+ *   Application specific data which is retrieved in fast path\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_enable(rte_graph_feature_arc_t _dfl, uint32_t index, const char *feature_name,\n+\t\t       int64_t data);\n+\n+/**\n+ * Validate whether subsequent enable/disable feature would succeed or not\n+ * API is thread-safe\n+ *\n+ * @param _dfl\n+ *   Feature arc object returned by @ref rte_graph_feature_arc_create or @ref\n+ *   rte_graph_feature_arc_lookup_by_name\n+ * @param index\n+ *   Application specific index. Can be corresponding to interface_id/port_id etc\n+ * @param feature_name\n+ *   Name of the node which is already added via @ref rte_graph_feature_add\n+ * @param is_enable_disable\n+ *   If 1, validate whether subsequent @ref rte_graph_feature_enable would pass or not\n+ *   If 0, validate whether subsequent @ref rte_graph_feature_disable would pass or not\n+ *\n+ * @return\n+ *  0: Subsequent enable/disable API would pass\n+ * <0: Subsequent enable/disable API would not pass\n+ */\n+__rte_experimental\n+int rte_graph_feature_validate(rte_graph_feature_arc_t _dfl, uint32_t index,\n+\t\t\t       const char *feature_name, int is_enable_disable);\n+\n+/**\n+ * Disable already enabled feature within a feature arc\n+ *\n+ * Must be called after @b rte_graph_create(). API is NOT Thread-safe\n+ *\n+ * @param _dfl\n+ *   Feature arc object returned by @ref rte_graph_feature_arc_create or @ref\n+ *   rte_graph_feature_arc_lookup_by_name\n+ * @param index\n+ *   Application specific index. Can be corresponding to interface_id/port_id etc\n+ * @param feature_name\n+ *   Name of the node which is already added via @ref rte_graph_feature_add\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_disable(rte_graph_feature_arc_t _dfl, uint32_t index,\n+\t\t\t      const char *feature_name);\n+\n+/**\n+ * Destroy Feature\n+ *\n+ * @param _dfl\n+ *   Feature arc object returned by @ref rte_graph_feature_arc_create or @ref\n+ *   rte_graph_feature_arc_lookup_by_name\n+ * @param feature_name\n+ *   Feature name provided to @ref rte_graph_feature_add\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_destroy(rte_graph_feature_arc_t _dfl, const char *feature_name);\n+\n+/**\n+ * Delete feature_arc object\n+ *\n+ * @param _dfl\n+ *   Feature arc object returned by @ref rte_graph_feature_arc_create or @ref\n+ *   rte_graph_feature_arc_lookup_by_name\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_arc_destroy(rte_graph_feature_arc_t _dfl);\n+\n+/**\n+ * Cleanup all feature arcs\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+__rte_experimental\n+int rte_graph_feature_arc_cleanup(void);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif\ndiff --git a/lib/graph/rte_graph_feature_arc_worker.h b/lib/graph/rte_graph_feature_arc_worker.h\nnew file mode 100644\nindex 0000000000..92ae7072bd\n--- /dev/null\n+++ b/lib/graph/rte_graph_feature_arc_worker.h\n@@ -0,0 +1,483 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2024 Marvell International Ltd.\n+ */\n+\n+#ifndef _RTE_GRAPH_FEATURE_ARC_WORKER_H_\n+#define _RTE_GRAPH_FEATURE_ARC_WORKER_H_\n+\n+#include <rte_graph_feature_arc.h>\n+#include <rte_bitops.h>\n+\n+/**\n+ * @file\n+ *\n+ * rte_graph_feature_arc_worker.h\n+ *\n+ * Defines fast path structure\n+ */\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/** @internal\n+ *\n+ * Slow path feature node info list\n+ */\n+struct __rte_cache_aligned rte_graph_feature_node_list {\n+\t/** Next feature */\n+\tSTAILQ_ENTRY(rte_graph_feature_node_list) next_feature;\n+\n+\t/** node representing feature */\n+\tstruct rte_node_register *feature_node;\n+\n+\t/** How many indexes/interfaces using this feature */\n+\tint32_t ref_count;\n+\n+\t/** Back pointer to feature arc */\n+\tvoid *feature_arc;\n+\n+\t/** rte_edge_t to this feature node from feature_arc->start_node */\n+\trte_edge_t edge_to_this_feature;\n+};\n+\n+/**\n+ * RTE_GRAPH feature data representing a fast path feature object on an interface/index\n+ */\n+typedef struct rte_graph_feature_data {\n+\t/** Data provided by application during @ref rte_graph_feature_enable on interface */\n+\tint64_t data;\n+\n+\t/** this feature data index */\n+\tuint32_t feature_data_index;\n+\n+\t/** Edge to this feature node from feature_arc->start_node */\n+\trte_edge_t edge_to_this_feature;\n+\n+\t/**\n+\t * Edge to next enabled feature on a given interface/index. This field\n+\t * keeps on changing as @ref rte_graph_feature_enable()/@ref\n+\t * rte_graph_feature_disable() are called on a given interface/index\n+\t */\n+\trte_edge_t edge_to_next_feature;\n+\n+\t/** Slow path node_info object */\n+\tstruct rte_graph_feature_node_list *node_info;\n+} rte_graph_feature_data_t;\n+\n+/**\n+ * RTE_GRAPH Feature object\n+ *\n+ * Holds all feature related data of a given feature on *all* interfaces\n+ */\n+struct __rte_cache_aligned rte_graph_feature {\n+\t/**\n+\t * Slow path node_info\n+\t * 1st DWORD\n+\t */\n+\tstruct rte_graph_feature_node_list *node_info;\n+\n+\t/** Feature arc back pointer\n+\t *  2nd DWORD\n+\t */\n+\tvoid *feature_arc;\n+\n+\t/**\n+\t * Number of enabled features in this feature_arc\n+\t * 3rd WORD\n+\t */\n+\tuint32_t num_enabled_features;\n+\n+\t/* uint32_t reserved; */\n+\n+\t/**\n+\t * Array of feature_data by index/interface\n+\t *\n+\t */\n+\tstruct rte_graph_feature_data feature_data[RTE_GRAPH_FEATURE_MAX_PER_ARC];\n+};\n+\n+/**\n+ * RTE_GRAPH Feature arc object\n+ *\n+ * Representing a feature arc holding all features which are enabled/disabled on any interfaces\n+ */\n+struct __rte_cache_aligned rte_graph_feature_arc {\n+\t/** All feature lists */\n+\tSTAILQ_HEAD(, rte_graph_feature_node_list) all_features;\n+\n+\t/** feature arc name */\n+\tchar feature_arc_name[RTE_GRAPH_FEATURE_ARC_NAMELEN];\n+\n+\t/** this feature group index in feature_arc_main */\n+\tuint32_t feature_arc_index;\n+\n+\t/** Back pointer to feature_arc_main */\n+\tvoid *feature_arc_main;\n+\n+\t/**\n+\t * Start_node or Base node where this feature arc is checked for any feature\n+\t */\n+\tstruct rte_node_register *start_node;\n+\n+\t/** Max features supported in this arc */\n+\tuint32_t max_features;\n+\n+\t/** Boolean indicating @ref rte_graph_feature_enable has started and not\n+\t * further addition is allowed\n+\t */\n+\tint feature_enable_started;\n+\n+\t/* Fast path stuff*/\n+\talignas(RTE_CACHE_LINE_SIZE) RTE_MARKER c0;\n+\n+\t/** RTE_GRAPH feature by interface */\n+\tstruct rte_graph_feature *features_by_index;\n+\n+\t/** Max interfaces supported */\n+\tuint32_t max_indexes;\n+\n+\t/** Bitmask by interface. Set bit indicates feature is enabled on interface */\n+\tuint64_t feature_bit_mask_by_index[];\n+};\n+\n+/** Feature arc main */\n+typedef struct feature_arc_main {\n+\t/** number of feature arcs created by application */\n+\tuint32_t num_feature_arcs;\n+\n+\t/** max features arcs allowed */\n+\tuint32_t max_feature_arcs;\n+\n+\t/** feature arcs */\n+\trte_graph_feature_arc_t feature_arcs[];\n+} rte_graph_feature_arc_main_t;\n+\n+/** @internal Get feature arc pointer from object */\n+#define rte_graph_feature_arc_get(dfl) ((struct rte_graph_feature_arc *)dfl)\n+\n+extern rte_graph_feature_arc_main_t *__feature_arc_main;\n+\n+/**\n+ * Get rte_graph feature data object for a index in feature\n+ *\n+ * @param df\n+ *   Feature pointer\n+ * @param feature_index\n+ *  Index of feature maintained in slow path linked list\n+ *\n+ * @return\n+ *   Valid feature data\n+ */\n+static inline struct rte_graph_feature_data *\n+rte_graph_feature_data_get(struct rte_graph_feature *df, uint32_t feature_index)\n+{\n+\treturn (df->feature_data + feature_index);\n+}\n+\n+/**\n+ * Get rte_graph_feature object for a given interface/index from feature arc\n+ *\n+ * @param dfl\n+ *   Feature arc pointer\n+ * @param index\n+ *   Interface index\n+ *\n+ * @return\n+ *   Valid feature pointer\n+ */\n+static inline struct rte_graph_feature *\n+rte_graph_feature_get(struct rte_graph_feature_arc *dfl, uint32_t index)\n+{\n+\treturn (dfl->features_by_index + index);\n+}\n+\n+/**\n+ * Fast path API to check if first feature enabled on a feature arc\n+ *\n+ * Must be called in feature_arc->start_node processing\n+ *\n+ * @param dfl\n+ *   Feature arc object\n+ * @param index\n+ *   Interface/Index\n+ * @param[out] feature\n+ *   Pointer to rte_graph_feature_t. Valid if API returns 1\n+ *\n+ * @return\n+ * 1: If feature is enabled\n+ * 0: If feature is not enabled\n+ *\n+ */\n+static inline int\n+rte_graph_feature_arc_has_first_feature(struct rte_graph_feature_arc *dfl,\n+\t\t\t\t\tuint32_t index, rte_graph_feature_t *feature)\n+{\n+\treturn rte_bsf64_safe(dfl->feature_bit_mask_by_index[index], feature);\n+}\n+\n+/**\n+ * Fast path API to get next feature when current node is already on an feature\n+ * arc and not consuming packet. This feature must forward the packet to next\n+ * enabled feature by passing returned rte_graph_feature_t to\n+ * rte_graph_feature_arc_next_feature_data_get()\n+ *\n+ * @param dfl\n+ *   Feature arc object\n+ * @param index\n+ *   Interface/Index\n+ * @param[out] feature\n+ *   Pointer to rte_graph_feature_t. Valid if API returns 1\n+ *\n+ * @return\n+ * 1: If next feature is enabled\n+ * 0: If next feature is not enabled\n+ */\n+static inline int\n+rte_graph_feature_arc_has_next_feature(struct rte_graph_feature_arc *dfl,\n+\t\t\t\t       uint32_t index, rte_graph_feature_t *feature)\n+{\n+\tuint32_t next_feature;\n+\tuint64_t bitmask;\n+\n+#ifdef RTE_GRAPH_FEATURE_ARC_DEBUG\n+\tstruct rte_graph_feature *df = rte_graph_feature_get(dfl, index);\n+\tstruct rte_graph_feature_data *dfd = NULL;\n+\n+\tdfd = rte_graph_feature_data_get(df, *feature);\n+\t/** Check feature sanity */\n+\tif (unlikely(dfd->feature_data_index != *feature))\n+\t\treturn 0;\n+#endif\n+\n+\t/* Create bitmask where current feature is cleared to get next feature\n+\t * bit set\n+\t */\n+\tnext_feature = (uint32_t)*feature;\n+\tbitmask = UINT64_MAX << (next_feature + 1);\n+\tbitmask = dfl->feature_bit_mask_by_index[index] & bitmask;\n+\n+\treturn rte_bsf64_safe(bitmask, feature);\n+}\n+\n+/**\n+ * Fast path API to check if any feature enabled on a feature arc\n+ *\n+ * @param _dfl\n+ *   Feature arc object\n+ * @param index\n+ *   Interface/Index\n+ * @param[out] feature\n+ *   Pointer to rte_graph_feature_t. Valid if API returns 1\n+ *\n+ * @return\n+ * 1: If feature is enabled\n+ * 0: If feature is not enabled\n+ *\n+ */\n+static inline int\n+rte_graph_feature_arc_has_feature(rte_graph_feature_arc_t _dfl, uint32_t index,\n+\t\t\t\t  rte_graph_feature_t *feature)\n+{\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\n+#ifdef RTE_GRAPH_FEATURE_ARC_DEBUG\n+\tif (unlikely(dfl->max_indexes < index))\n+\t\treturn 0;\n+\n+\tif (unlikely(!feature))\n+\t\treturn 0;\n+#endif\n+\t/* Look for first feature */\n+\tif (*feature == RTE_GRAPH_FEATURE_INVALID_VALUE)\n+\t\treturn rte_graph_feature_arc_has_first_feature(dfl, index, feature);\n+\telse\n+\t\treturn rte_graph_feature_arc_has_next_feature(dfl, index, feature);\n+}\n+\n+\n+/**\n+ * Prefetch feature data upfront\n+ *\n+ * @param _dfl\n+ *   RTE_GRAPH feature arc object\n+ * @param index\n+ *   Interface/index\n+ * @param feature\n+ *   Pointer to feature object returned from @ref\n+ *   rte_graph_feature_arc_has_feature() or @ref\n+ *   rte_graph_feature_arc_first_feature_data_get()\n+ */\n+static inline void\n+__rte_graph_prefetch_data_prefetch(rte_graph_feature_arc_t _dfl, int index,\n+\t\t\t\t   rte_graph_feature_t feature)\n+{\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\tstruct rte_graph_feature *df = rte_graph_feature_get(dfl, index);\n+\n+\trte_prefetch0((void *)rte_graph_feature_data_get(df, feature));\n+}\n+\n+/**\n+ * Prefetch feature data upfront. Perform sanity\n+ *\n+ * @param _dfl\n+ *   RTE_GRAPH feature arc object\n+ * @param index\n+ *   Interface/index\n+ * @param feature\n+ *   Pointer to feature object returned from @ref\n+ *   rte_graph_feature_arc_has_feature() or @ref\n+ *   rte_graph_feature_arc_first_feature_data_get()\n+ */\n+static inline void\n+rte_graph_feature_data_prefetch(rte_graph_feature_arc_t _dfl, uint32_t index,\n+\t\t\t\trte_graph_feature_t feature)\n+{\n+#ifdef RTE_GRAPH_FEATURE_ARC_DEBUG\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\n+\tif (unlikely(index >= dfl->max_indexes))\n+\t\treturn;\n+\n+\tif (unlikely(feature >= rte_graph_feature_cast(dfl->max_features)))\n+\t\treturn;\n+#endif\n+\n+\tif (feature != RTE_GRAPH_FEATURE_INVALID_VALUE)\n+\t\t__rte_graph_prefetch_data_prefetch(_dfl, index, feature);\n+}\n+\n+/**\n+ * Fast path API to get first feature data aka {edge, int32_t data}\n+ *\n+ * Must be called in feature_arc->start_node processing\n+ *\n+ * @param _dfl\n+ *   Feature arc object\n+ * @param feature\n+ *  returned from rte_graph_feature_arc_has_feature()\n+ * @param index\n+ *   Interface/Index\n+ * @param[out] edge\n+ *   Pointer to rte_node edge. Valid if API returns Success\n+ * @param[out] data\n+ *   Pointer to int64_t data set via rte_graph_feature_enable(). Valid if API returns\n+ *   Success\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+static inline int\n+rte_graph_feature_arc_first_feature_data_get(struct rte_graph_feature_arc *dfl,\n+\t\t\t\t\t     rte_graph_feature_t feature,\n+\t\t\t\t\t     uint32_t index, rte_edge_t *edge,\n+\t\t\t\t\t     int64_t *data)\n+{\n+\tstruct rte_graph_feature *df = rte_graph_feature_get(dfl, index);\n+\tstruct rte_graph_feature_data *dfd = NULL;\n+\n+\tdfd = rte_graph_feature_data_get(df, feature);\n+\n+#ifdef RTE_GRAPH_FEATURE_ARC_DEBUG\n+\t/** Check feature sanity */\n+\tif (unlikely(dfd->feature_data_index != feature))\n+\t\treturn -1;\n+\n+\tif (unlikely(!edge && !data))\n+\t\treturn -1;\n+#endif\n+\n+\t*edge = dfd->edge_to_this_feature;\n+\t*data = dfd->data;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * Fast path API to get next feature data aka {edge, int32_t data}\n+ *\n+ * Must NOT be called in feature_arc->start_node processing instead must be\n+ * called in intermediate feature nodes on a featur-arc.\n+ *\n+ * @param _dfl\n+ *   Feature arc object\n+ * @param feature\n+ *  returned from rte_graph_feature_arc_has_next_feature()\n+ * @param index\n+ *   Interface/Index\n+ * @param[out] edge\n+ *   Pointer to rte_node edge. Valid if API returns Success\n+ * @param[out] data\n+ *   Pointer to int64_t data set via rte_graph_feature_enable(). Valid if API returns\n+ *   Success\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+static inline int\n+rte_graph_feature_arc_next_feature_data_get(struct rte_graph_feature_arc *dfl,\n+\t\t\t\t\t    rte_graph_feature_t feature,\n+\t\t\t\t\t    uint32_t index, rte_edge_t *edge,\n+\t\t\t\t\t    int64_t *data)\n+{\n+\tstruct rte_graph_feature *df = rte_graph_feature_get(dfl, index);\n+\tstruct rte_graph_feature_data *dfd = NULL;\n+\n+\tdfd = rte_graph_feature_data_get(df, feature);\n+\n+#ifdef RTE_GRAPH_FEATURE_ARC_DEBUG\n+\t/** Check feature sanity */\n+\tif (unlikely(dfd->feature_data_index != feature))\n+\t\treturn -1;\n+\n+\tif (unlikely(!edge && !data))\n+\t\treturn -1;\n+#endif\n+\n+\t*edge = dfd->edge_to_next_feature;\n+\t*data = dfd->data;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * Fast path API to get next feature data aka {edge, int32_t data}\n+ *\n+ * @param _dfl\n+ *   Feature arc object\n+ * @param feature\n+ *  returned from rte_graph_feature_arc_has_feature()\n+ * @param index\n+ *   Interface/Index\n+ * @param[out] edge\n+ *   Pointer to rte_node edge. Valid if API returns Success\n+ * @param[out] data\n+ *   Pointer to int64_t data set via rte_graph_feature_enable(). Valid if API returns\n+ *   Success\n+ *\n+ * @return\n+ *  0: Success\n+ * <0: Failure\n+ */\n+\n+static inline int\n+rte_graph_feature_arc_feature_data_get(rte_graph_feature_arc_t _dfl,\n+\t\t\t\t       rte_graph_feature_t feature, uint32_t\n+\t\t\t\t       index, rte_edge_t *edge, int64_t *data)\n+{\n+\tstruct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);\n+\n+\tif (feature == RTE_GRAPH_FEATURE_INVALID_VALUE)\n+\t\treturn rte_graph_feature_arc_first_feature_data_get(dfl, feature, index, edge,\n+\t\t\t\t\t\t\t\t    data);\n+\telse\n+\t\treturn rte_graph_feature_arc_next_feature_data_get(dfl, feature, index, edge, data);\n+}\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+#endif\ndiff --git a/lib/graph/version.map b/lib/graph/version.map\nindex c84446cdba..b409a2425f 100644\n--- a/lib/graph/version.map\n+++ b/lib/graph/version.map\n@@ -52,3 +52,19 @@ DPDK_24 {\n \n \tlocal: *;\n };\n+\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\t# added in 24.07\n+\trte_graph_feature_arc_init;\n+\trte_graph_feature_arc_create;\n+\trte_graph_feature_arc_lookup_by_name;\n+\trte_graph_feature_add;\n+\trte_graph_feature_enable;\n+\trte_graph_feature_validate;\n+\trte_graph_feature_disable;\n+\trte_graph_feature_destroy;\n+\trte_graph_feature_arc_destroy;\n+\trte_graph_feature_arc_cleanup;\n+};\n",
    "prefixes": [
        "RFC",
        "1/2"
    ]
}