> -----Original Message-----
> From: Nitin Saxena <nsax...@marvell.com>
> Sent: Wednesday, June 4, 2025 3:43 PM
> To: Jerin Jacob <jer...@marvell.com>; Kiran Kumar Kokkilagadda
> <kirankum...@marvell.com>; Nithin Kumar Dabilpuram
> <ndabilpu...@marvell.com>; Zhirun Yan <yanzhirun_...@163.com>; Robin
> Jarry <rja...@redhat.com>; Christophe Fontaine <cfont...@redhat.com>
> Cc: dev@dpdk.org; Nitin Saxena <nsaxen...@gmail.com>
> Subject: [PATCH v10 3/7] graph: add feature arc init APIs
> 
> This patch adds feature arc init()/create()/destroy() APIs. It also add
> APIs for adding feature node to an arc.
> 
> Signed-off-by: Nitin Saxena <nsax...@marvell.com>
> ---
>  doc/api/doxy-api-index.md                |    1 +
>  doc/guides/prog_guide/graph_lib.rst      |   23 +-
>  lib/graph/graph_feature_arc.c            | 1329 +++++++++++++++++++++-
>  lib/graph/graph_private.h                |    4 +
>  lib/graph/meson.build                    |    2 +-
>  lib/graph/rte_graph_feature_arc.h        |  248 +++-
>  lib/graph/rte_graph_feature_arc_worker.h |  303 +++++
>  7 files changed, 1902 insertions(+), 8 deletions(-)
>  create mode 100644 lib/graph/rte_graph_feature_arc_worker.h
> 
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index a7bdbf892c..6d8b531344 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -215,6 +215,7 @@ The public API headers are grouped by topics:
>    * [graph](@ref rte_graph.h):
>      [graph_worker](@ref rte_graph_worker.h)
>      [graph_feature_arc](@ref rte_graph_feature_arc.h)
> +    [graph_feature_arc_worker](@ref rte_graph_feature_arc_worker.h)
>    * graph_nodes:
>      [eth_node](@ref rte_node_eth_api.h),
>      [ip4_node](@ref rte_node_ip4_api.h),
> diff --git a/doc/guides/prog_guide/graph_lib.rst
> b/doc/guides/prog_guide/graph_lib.rst
> index 191c8e8a0b..c9ac9e7ae0 100644
> --- a/doc/guides/prog_guide/graph_lib.rst
> +++ b/doc/guides/prog_guide/graph_lib.rst
> @@ -815,7 +815,7 @@ added to existing arc as follows:
>              ...
>              ...
>              ...
> -        .override_index_cb = Feature-3_override_index_cb(),
> +        .override_index_cb = Feature-2_override_index_cb(),
>          .runs_after = "Feature-1",
>          .runs_before = "Custom-Feature",
>      };
> @@ -848,3 +848,24 @@ this callback. In case of multiple features, largest
> value returned by any
>  feature would be selected for creating feature arc.
> 
>  .. _Feature_Arc_Initialization:
> +
> +Initializing Feature arc
> +^^^^^^^^^^^^^^^^^^^^^^^^
> +Following code shows how to initialize feature arc sub-system.
> +``rte_graph_feature_arc_init()`` API is used to initialize a feature arc
> +sub-system. If not called, feature arc has no impact on application.
> +
> +.. code-block:: c
> +
> +    struct rte_graph_param *graph_param = app_get_graph_param();
> +
> +    /* Initialize feature arc before graph create */
> +    rte_graph_feature_arc_init(0);
> +
> +    rte_graph_create(graph_param);
> +
> +.. note::
> +
> +   ``rte_graph_feature_arc_init()`` API should be called before
> +   ``rte_graph_create()``. If not called, feature arc is a ``NOP`` to
> +   application.
> diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
> index 6135b262d5..b28f0ec321 100644
> --- a/lib/graph/graph_feature_arc.c
> +++ b/lib/graph/graph_feature_arc.c
> @@ -2,10 +2,57 @@
>   * Copyright(C) 2025 Marvell International Ltd.
>   */
> 
> -#include <rte_graph_feature_arc.h>
> +#include <rte_graph_feature_arc_worker.h>
> +#include <rte_malloc.h>
> +#include <rte_string_fns.h>
>  #include <eal_export.h>
>  #include "graph_private.h"
> 
> +#define GRAPH_FEATURE_MAX_NUM_PER_ARC  (64)
> +
> +#define connect_graph_nodes(node1, node2, edge, arc_name) \
> +     __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
> +
> +#define FEATURE_ARC_MEMZONE_NAME "__rte_feature_arc_main_mz"
> +
> +#define NUM_EXTRA_FEATURE_DATA   (2)
> +
> +#define graph_uint_cast(f)           ((unsigned int)f)

Not used anywhere in the patch.



> +
> +#define feat_dbg graph_dbg
> +
> +#define FEAT_COND_ERR(cond, ...)                                           \
> +     do {                                                               \
> +             if (cond)                                                  \
> +                     graph_err(__VA_ARGS__);                            \
> +     } while (0)
> +
> +#define FEAT_ERR(fn, ln, ...)                                              \
> +             GRAPH_LOG2(ERR, fn, ln, __VA_ARGS__)
> +
> +#define FEAT_ERR_JMP(_err, fn, ln, ...)                                    \
> +     do {                                                               \
> +             FEAT_ERR(fn, ln, __VA_ARGS__);                             \
> +             rte_errno = _err;                                          \
> +     } while (0)
> +
> +#define COND_ERR_JMP(_err, cond, fn, ln, ...)                              \
> +     do {                                                               \
> +             if (cond)                                                  \
> +                     FEAT_ERR(fn, ln, __VA_ARGS__);                     \
> +             rte_errno = _err;                                          \
> +     } while (0)
> +
> +
> +static struct rte_mbuf_dynfield rte_graph_feature_arc_mbuf_desc = {
> +     .name = RTE_GRAPH_FEATURE_ARC_DYNFIELD_NAME,
> +     .size = sizeof(struct rte_graph_feature_arc_mbuf_dynfields),
> +     .align = alignof(struct rte_graph_feature_arc_mbuf_dynfields),
> +};
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(__rte_graph_feature_arc_main,
> 25.07);
> +rte_graph_feature_arc_main_t *__rte_graph_feature_arc_main;
> +
>  /* global feature arc list */
>  static STAILQ_HEAD(, rte_graph_feature_arc_register) feature_arc_list =
> 
>       STAILQ_HEAD_INITIALIZER(feature_arc_list);
> @@ -14,6 +61,1251 @@ static STAILQ_HEAD(,
> rte_graph_feature_arc_register) feature_arc_list =
>  static STAILQ_HEAD(, rte_graph_feature_register) feature_list =
> 
>       STAILQ_HEAD_INITIALIZER(feature_list);
> 
> +/* feature registration validate */
> +static int
> +feature_registration_validate(struct rte_graph_feature_register *feat_entry,
> +                           const char *caller_name, int lineno,
> +                           int check_node_reg_id, /* check feature_node->id
> */
> +                           int check_feat_reg_id, /* check feature-
> >feature_node_id */
> +                           bool verbose /* print error */)
> +{
> +     if (!feat_entry) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno, "NULL
> feature reg");
> +             return -1;
> +     }
> +
> +     if (!feat_entry->feature_name) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "%p: NULL feature name", feat_entry);
> +             return -1;
> +     }
> +
> +     if (!feat_entry->arc_name) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "feature-\"%s\": No associated arc provided",
> +                          feat_entry->feature_name);
> +             return -1;
> +     }
> +
> +     if (!feat_entry->feature_process_fn) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "feature-\"%s\": No process function provided",
> +                          feat_entry->feature_name);
> +             return -1;
> +     }
> +
> +     if (!feat_entry->feature_node) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "feature-\"%s\": No feature_node provided",
> +                          feat_entry->feature_name);
> +             return -1;
> +     }
> +
> +     if (check_node_reg_id && (feat_entry->feature_node->id ==
> RTE_NODE_ID_INVALID)) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "feature-\"%s\": feature_node with invalid node-id
> found",
> +                          feat_entry->feature_name);
> +             return -1;
> +     }
> +
> +     if (check_feat_reg_id && (feat_entry->feature_node_id ==
> RTE_NODE_ID_INVALID)) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "feature-\"%s\": feature_node_id found invalid",
> +                          feat_entry->feature_name);
> +             return -1;
> +     }
> +     if (check_feat_reg_id && feat_entry->feature_node) {
> +             if (feat_entry->feature_node_id != feat_entry->feature_node-
> >id) {
> +                     COND_ERR_JMP(EINVAL, verbose, caller_name,
> lineno,
> +                                  "feature-\"%s\": feature_node_id(%u) not
> corresponding to %s->id(%u)",
> +                                  feat_entry->feature_name, feat_entry-
> >feature_node_id,
> +                                  feat_entry->feature_node->name,
> feat_entry->feature_node->id);
> +                     return -1;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +/* validate arc registration */
> +static int
> +arc_registration_validate(struct rte_graph_feature_arc_register *reg,
> +                       const char *caller_name, int lineno,
> +                       bool verbose)
> +{
> +
> +     if (reg == NULL) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "arc registration cannot be NULL");
> +             return -1;
> +     }
> +
> +     if (!reg->arc_name) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "feature_arc name cannot be NULL");
> +             return -1;
> +     }
> +
> +     if (reg->max_features > GRAPH_FEATURE_MAX_NUM_PER_ARC) {
> +             COND_ERR_JMP(EAGAIN, verbose, caller_name, lineno,
> +                          "arc-\"%s\", invalid number of features (found: %u,
> exp: %u)",
> +                          reg->arc_name, reg->max_features,
> GRAPH_FEATURE_MAX_NUM_PER_ARC);
> +             return -1;
> +     }
> +
> +     if (!reg->max_indexes) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "arc-\"%s\": Zero max_indexes found",
> +                          reg->arc_name);
> +             return -1;
> +     }
> +
> +     if (!reg->start_node) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "arc-\"%s\": start node cannot be NULL",
> +                          reg->arc_name);
> +             return -1;
> +     }
> +
> +     if (!reg->start_node_feature_process_fn) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "arc-\"%s\": start node feature_process_fn()
> cannot be NULL",
> +                          reg->arc_name);
> +             return -1;
> +     }
> +
> +     if (!reg->end_feature) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "arc-\"%s\": end_feature cannot be NULL",
> +                          reg->arc_name);
> +             return -1;
> +     }
> +
> +     if (feature_registration_validate(reg->end_feature, caller_name,
> lineno, 1, 0, verbose))
> +             return -1;
> +
> +     if (strncmp(reg->arc_name, reg->end_feature->arc_name,
> +                 RTE_GRAPH_FEATURE_ARC_NAMELEN)) {
> +             COND_ERR_JMP(EINVAL, verbose, caller_name, lineno,
> +                          "arc-\"%s\"/feature-\"%s\": mismatch in arc_name
> in end_feature",
> +                          reg->arc_name, reg->end_feature->feature_name);
> +             return -1;
> +     }
> +
> +     return 0;
> +}
> +
> +/* lookup arc registration by name */
> +static int arc_registration_num(void)
> +{
> +     struct rte_graph_feature_arc_register *entry = NULL;
> +     int num = 0;
> +
> +     STAILQ_FOREACH(entry, &feature_arc_list, next_arc)
> +             num++;
> +
> +     return num;
> +}
> +
> +
> +/* lookup arc registration by name */
> +static int arc_registration_lookup(const char *arc_name,
> +                                struct rte_graph_feature_arc_register
> **arc_entry,
> +                                bool verbose /* print error */)
> +{
> +     struct rte_graph_feature_arc_register *entry = NULL;
> +
> +     STAILQ_FOREACH(entry, &feature_arc_list, next_arc) {
> +             if (arc_registration_validate(entry, __func__, __LINE__,
> verbose) < 0)
> +                     continue;
> +
> +             if (!strncmp(entry->arc_name, arc_name,
> RTE_GRAPH_FEATURE_ARC_NAMELEN)) {
> +                     if (arc_entry)
> +                             *arc_entry = entry;
> +                     return 1;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +
> +/* Number of features registered for an ARC
> + *
> + * i.e number of RTE_GRAPH_FEATURE_REGISTER() for an arc
> + */
> +static int
> +arc_registered_features_num(const char *arc_name, uint16_t
> *num_features)
> +{
> +     struct rte_graph_feature_arc_register *arc_reg = NULL;
> +     struct rte_graph_feature_register *feat_entry = NULL;
> +     uint16_t num = 0;
> +
> +     /* Check if arc is registered with end_feature */
> +     if (!arc_registration_lookup(arc_name, &arc_reg, false))
> +             return -1;
> +
> +     if (arc_reg->end_feature)
> +             num++;
> +
> +     /* Calculate features other than end_feature added in arc */
> +     STAILQ_FOREACH(feat_entry, &feature_list, next_feature) {
> +             if (feature_registration_validate(feat_entry, __func__,
> __LINE__, 1, 0, false) < 0)
> +                     continue;
> +
> +             if (!strncmp(feat_entry->arc_name, arc_name,
> strlen(feat_entry->arc_name)))
> +                     num++;
> +     }
> +
> +     if (num_features)
> +             *num_features = num;
> +
> +     return 0;
> +}
> +
> +static int
> +arc_max_index_get(const char *arc_name, uint16_t *max_indexes)
> +{
> +     struct rte_graph_feature_arc_register *arc_reg = NULL;
> +     struct rte_graph_feature_register *feat_entry = NULL;
> +     uint16_t index;
> +
> +     if (!max_indexes)
> +             return -1;
> +
> +     /* Check if arc is registered with end_feature */
> +     if (!arc_registration_lookup(arc_name, &arc_reg, false))
> +             return -1;
> +
> +     index = *max_indexes;
> +
> +     /* Call features override_index_cb(), if set */
> +     STAILQ_FOREACH(feat_entry, &feature_list, next_feature) {
> +             if (!feat_entry->override_index_cb)
> +                     continue;
> +
> +             if (feature_registration_validate(feat_entry, __func__,
> __LINE__, 1, 0, false) < 0)
> +                     continue;
> +
> +             index = RTE_MAX(index, feat_entry->override_index_cb());
> +     }
> +
> +     *max_indexes = index;
> +
> +     return 0;
> +}
> +
> +/* calculate arc size to be allocated */
> +static int
> +feature_arc_reg_calc_size(struct rte_graph_feature_arc_register *reg, size_t
> *sz,
> +                       uint16_t *feat_off, uint16_t *fdata_off, uint32_t 
> *fsz,
> +                       uint16_t *num_index)
> +{
> +     size_t ff_size = 0, fdata_size = 0;
> +
> +     /* first feature array per index */
> +     ff_size = RTE_ALIGN_CEIL(sizeof(rte_graph_feature_data_t) * reg-
> >max_indexes,
> +                              RTE_CACHE_LINE_SIZE);
> +
> +
> +     /* fdata size per feature */
> +     *fsz = (uint32_t)RTE_ALIGN_CEIL(sizeof(struct
> rte_graph_feature_data) * reg->max_indexes,
> +                                     RTE_CACHE_LINE_SIZE);
> +
> +     *num_index = (*fsz)/sizeof(struct rte_graph_feature_data);
> +
> +     /* Allocate feature_data extra by 2.
> +      * 0th index is used for first feature data from start_node
> +      * Last feature data is used for extra_fdata for end_feature
> +      */
> +     fdata_size = (*fsz) * (reg->max_features +
> NUM_EXTRA_FEATURE_DATA);
> +
> +     if (sz)
> +             *sz = fdata_size + ff_size + sizeof(struct
> rte_graph_feature_arc);
> +     if (feat_off)
> +             *feat_off = sizeof(struct rte_graph_feature_arc);
> +     if (fdata_off)
> +             *fdata_off = ff_size + sizeof(struct rte_graph_feature_arc);
> +
> +     return 0;
> +}
> +
> +static rte_graph_feature_data_t *
> +graph_first_feature_data_ptr_get(struct rte_graph_feature_arc *arc,
> +                              uint32_t index)
> +{
> +     return (rte_graph_feature_data_t *)((uint8_t *)arc + arc-
> >fp_first_feature_offset +
> +                                         (sizeof(rte_graph_feature_data_t) *
> index));
> +}
> +
> +static int
> +feature_arc_data_reset(struct rte_graph_feature_arc *arc)
> +{
> +     rte_graph_feature_data_t *f = NULL;
> +     uint16_t index;
> +
> +     arc->runtime_enabled_features = 0;
> +
> +     for (index = 0; index < arc->max_indexes; index++) {
> +             f = graph_first_feature_data_ptr_get(arc, index);
> +             *f = RTE_GRAPH_FEATURE_DATA_INVALID;
> +     }
> +
> +     return 0;
> +}
> +
> +/*
> + * lookup feature name and get control path node_list as well as feature 
> index
> + * at which it is inserted
> + */
> +static int
> +nodeinfo_lkup_by_name(struct rte_graph_feature_arc *arc, const char
> *feat_name,
> +                   struct rte_graph_feature_node_list **ffinfo, uint32_t 
> *slot)
> +{
> +     struct rte_graph_feature_node_list *finfo = NULL;
> +     uint32_t fi = 0;
> +
> +     if (!feat_name)
> +             return -1;
> +
> +     if (slot)
> +             *slot = UINT32_MAX;
> +
> +     STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> +             RTE_VERIFY(finfo->feature_arc == arc);
> +             if (!strncmp(finfo->feature_name, feat_name, strlen(finfo-
> >feature_name))) {
> +                     if (ffinfo)
> +                             *ffinfo = finfo;
> +                     if (slot)
> +                             *slot = fi;
> +                     return 0;
> +             }
> +             fi++;
> +     }
> +     return -1;
> +}
> +
> +/* Lookup used only during rte_graph_feature_add() */
> +static int
> +nodeinfo_add_lookup(struct rte_graph_feature_arc *arc, const char
> *feat_node_name,
> +                 struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
> +{
> +     struct rte_graph_feature_node_list *finfo = NULL;
> +     uint32_t fi = 0;
> +
> +     if (!feat_node_name)
> +             return -1;
> +
> +     if (slot)
> +             *slot = 0;
> +
> +     STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> +             RTE_VERIFY(finfo->feature_arc == arc);
> +             if (!strncmp(finfo->feature_name, feat_node_name,
> strlen(finfo->feature_name))) {
> +                     if (ffinfo)
> +                             *ffinfo = finfo;
> +                     if (slot)
> +                             *slot = fi;
> +                     return 0;
> +             }
> +             /* Update slot where new feature can be added */
> +             if (slot)
> +                     *slot = fi;
> +             fi++;
> +     }
> +
> +     return -1;
> +}
> +
> +/* Get control path node info from provided input feature_index */
> +static int
> +nodeinfo_lkup_by_index(struct rte_graph_feature_arc *arc, uint32_t
> feature_index,
> +                    struct rte_graph_feature_node_list **ppfinfo,
> +                    const int do_sanity_check)
> +{
> +     struct rte_graph_feature_node_list *finfo = NULL;
> +     uint32_t index = 0;
> +
> +     if (!ppfinfo)
> +             return -1;
> +
> +     *ppfinfo = NULL;
> +     STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> +             /* Check sanity */
> +             if (do_sanity_check)
> +                     if (finfo->finfo_index != index)
> +                             RTE_VERIFY(0);
> +             if (index == feature_index) {
> +                     *ppfinfo = finfo;
> +                     return 0;
> +             }
> +             index++;
> +     }
> +     return -1;
> +}
> +
> +/* get existing edge from parent_node -> child_node */
> +static int
> +get_existing_edge(const char *arc_name, rte_node_t parent_node,
> +               rte_node_t child_node, rte_edge_t *_edge)
> +{
> +     char **next_edges = NULL;
> +     uint32_t i, count = 0;
> +
> +     RTE_SET_USED(arc_name);
> +
> +     count = rte_node_edge_get(parent_node, NULL);
> +
> +     if (!count)
> +             return -1;
> +
> +     next_edges = malloc(count);
> +
> +     if (!next_edges)
> +             return -1;
> +
> +     count = rte_node_edge_get(parent_node, next_edges);
> +     for (i = 0; i < count; i++) {
> +             if (strstr(rte_node_id_to_name(child_node), next_edges[i])) {
> +                     if (_edge)
> +                             *_edge = (rte_edge_t)i;
> +
> +                     free(next_edges);
> +                     return 0;
> +             }
> +     }
> +     free(next_edges);
> +
> +     return -1;
> +}
> +
> +/* feature arc sanity */
> +static int
> +feature_arc_sanity(rte_graph_feature_arc_t _arc)
> +{
> +     struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +     rte_graph_feature_arc_main_t *dm = __rte_graph_feature_arc_main;
> +     uint16_t iter;
> +
> +     if (!__rte_graph_feature_arc_main)
> +             return -1;
> +
> +     if (!arc)
> +             return -1;
> +
> +     for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> +             if (arc == rte_graph_feature_arc_get(iter)) {
> +                     if (arc->feature_arc_index != iter)
> +                             return -1;
> +                     if (arc->feature_arc_main != dm)
> +                             return -1;
> +
> +                     return 0;
> +             }
> +     }
> +     return -1;
> +}
> +
> +/* create or retrieve already existing edge from parent_node -> child_node */
> +static int
> +__connect_graph_nodes(rte_node_t parent_node, rte_node_t child_node,
> +                 rte_edge_t *_edge, char *arc_name, int lineno)
> +{
> +     const char *next_node = NULL;
> +     rte_edge_t edge;
> +
> +     if (!get_existing_edge(arc_name, parent_node, child_node, &edge)) {
> +             feat_dbg("\t%s/%d: %s[%u]: \"%s\", edge reused", arc_name,
> lineno,
> +                      rte_node_id_to_name(parent_node), edge,
> rte_node_id_to_name(child_node));
> +
> +             if (_edge)
> +                     *_edge = edge;
> +
> +             return 0;
> +     }
> +
> +     /* Node to be added */
> +     next_node = rte_node_id_to_name(child_node);
> +
> +     edge = rte_node_edge_update(parent_node, RTE_EDGE_ID_INVALID,
> &next_node, 1);
> +
> +     if (edge == RTE_EDGE_ID_INVALID) {
> +             graph_err("edge invalid");
> +             return -1;
> +     }
> +     edge = rte_node_edge_count(parent_node) - 1;
> +
> +     feat_dbg("\t%s/%d: %s[%u]: \"%s\", new edge added", arc_name,
> lineno,
> +              rte_node_id_to_name(parent_node), edge,
> rte_node_id_to_name(child_node));
> +
> +     if (_edge)
> +             *_edge = edge;
> +
> +     return 0;
> +}
> +
> +/* feature arc initialization */
> +static int
> +feature_arc_main_init(rte_graph_feature_arc_main_t **pfl, uint32_t
> max_feature_arcs)
> +{
> +     rte_graph_feature_arc_main_t *pm = NULL;
> +     const struct rte_memzone *mz = NULL;
> +     uint32_t i;
> +     size_t sz;
> +
> +     if (!pfl) {
> +             graph_err("Invalid input");
> +             return -1;
> +     }
> +
> +     sz = sizeof(rte_graph_feature_arc_main_t) +
> +             (sizeof(pm->feature_arcs[0]) * max_feature_arcs);
> +
> +     mz = rte_memzone_reserve(FEATURE_ARC_MEMZONE_NAME, sz,
> SOCKET_ID_ANY, 0);
> +     if (!mz) {
> +             graph_err("memzone reserve failed for feature arc main");
> +             return -1;
> +     }
> +
> +     pm = mz->addr;
> +     memset(pm, 0, sz);
> +
> +     pm->arc_mbuf_dyn_offset = -1;
> +     pm->arc_mbuf_dyn_offset =
> rte_mbuf_dynfield_register(&rte_graph_feature_arc_mbuf_desc);
> +
> +     if (pm->arc_mbuf_dyn_offset < 0) {
> +             graph_err("rte_graph_feature_arc_dynfield_register failed");
> +             rte_memzone_free(mz);
> +             return -1;
> +     }
> +     for (i = 0; i < max_feature_arcs; i++)
> +             pm->feature_arcs[i] =
> GRAPH_FEATURE_ARC_PTR_INITIALIZER;
> +
> +     pm->max_feature_arcs = max_feature_arcs;
> +
> +     *pfl = pm;
> +
> +     return 0;
> +}
> +
> +/* feature arc initialization, public API */
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_init, 25.07);
> +int
> +rte_graph_feature_arc_init(uint16_t num_feature_arcs)
> +{
> +     struct rte_graph_feature_arc_register *arc_reg = NULL;
> +     struct rte_graph_feature_register *feat_reg = NULL;
> +     const struct rte_memzone *mz = NULL;
> +     int max_feature_arcs;
> +     int rc = -1;
> +
> +     if (!__rte_graph_feature_arc_main) {
> +             mz =
> rte_memzone_lookup(FEATURE_ARC_MEMZONE_NAME);
> +             if (mz) {
> +                     __rte_graph_feature_arc_main = mz->addr;
> +                     return 0;
> +             }
> +             max_feature_arcs = num_feature_arcs +
> arc_registration_num();
> +             if (!max_feature_arcs) {
> +                     graph_err("No feature arcs registered");
> +                     return -1;
> +             }
> +             rc = feature_arc_main_init(&__rte_graph_feature_arc_main,
> max_feature_arcs);
> +             if (rc < 0)
> +                     return rc;
> +     }
> +
> +     STAILQ_FOREACH(arc_reg, &feature_arc_list, next_arc) {
> +             if (arc_registration_validate(arc_reg, __func__, __LINE__, true)
> < 0)
> +                     continue;
> +
> +             /* arc lookup validates feature and arc both*/
> +             if (!arc_registration_lookup(arc_reg->arc_name, NULL, false))
> +                     continue;
> +
> +             /* If feature name not set, use node name as feature */
> +             if (!arc_reg->end_feature->feature_name)
> +                     arc_reg->end_feature->feature_name =
> +                             rte_node_id_to_name(arc_reg->end_feature-
> >feature_node_id);
> +
> +             /* Compute number of max_features if not provided */
> +             if (!arc_reg->max_features)
> +                     arc_registered_features_num(arc_reg->arc_name,
> &arc_reg->max_features);
> +
> +             rc = arc_max_index_get(arc_reg->arc_name, &arc_reg-
> >max_indexes);
> +             if (rc < 0) {
> +                     graph_err("arc_max_index_get failed for arc: %s",
> +                               arc_reg->arc_name);
> +                     continue;
> +             }
> +
> +             arc_reg->end_feature->feature_node_id = arc_reg-
> >end_feature->feature_node->id;
> +
> +             rc = rte_graph_feature_arc_create(arc_reg, NULL);
> +
> +             if (rc < 0)
> +                     goto arc_cleanup;
> +     }
> +
> +     /* First add those features which has no runs_after and runs_before
> restriction */
> +     STAILQ_FOREACH(feat_reg, &feature_list, next_feature) {
> +             /* Skip if arc not registered yet */
> +             if (!arc_registration_lookup(feat_reg->arc_name, NULL, false))
> +                     continue;
> +
> +             if (feat_reg->runs_after || feat_reg->runs_before)
> +                     continue;
> +
> +             if (feature_registration_validate(feat_reg, __func__, __LINE__,
> 1, 0, false) < 0)
> +                     continue;
> +
> +             feat_reg->feature_node_id = feat_reg->feature_node->id;
> +
> +             rc = rte_graph_feature_add(feat_reg);
> +
> +             if (rc < 0)
> +                     goto arc_cleanup;
> +     }
> +     /* Add those features which has either runs_after or runs_before
> restrictions */
> +     STAILQ_FOREACH(feat_reg, &feature_list, next_feature) {
> +             /* Skip if arc not registered yet */
> +             if (!arc_registration_lookup(feat_reg->arc_name, NULL, false))
> +                     continue;
> +
> +             if (!feat_reg->runs_after && !feat_reg->runs_before)
> +                     continue;
> +
> +             if (feat_reg->runs_after && feat_reg->runs_before)
> +                     continue;
> +
> +             if (feature_registration_validate(feat_reg, __func__, __LINE__,
> 1, 0, false) < 0)
> +                     continue;
> +
> +             feat_reg->feature_node_id = feat_reg->feature_node->id;
> +
> +             rc = rte_graph_feature_add(feat_reg);
> +
> +             if (rc < 0)
> +                     goto arc_cleanup;
> +     }
> +     /* Add those features with both runs_after and runs_before
> restrictions */
> +     STAILQ_FOREACH(feat_reg, &feature_list, next_feature) {
> +             /* Skip if arc not registered yet */
> +             if (!arc_registration_lookup(feat_reg->arc_name, NULL, false))
> +                     continue;
> +
> +             if (!feat_reg->runs_after && !feat_reg->runs_before)
> +                     continue;
> +
> +             if ((feat_reg->runs_after && !feat_reg->runs_before) ||
> +                 (!feat_reg->runs_after && feat_reg->runs_before))
> +                     continue;
> +
> +             if (feature_registration_validate(feat_reg, __func__, __LINE__,
> 1, 0, false) < 0)
> +                     continue;
> +
> +             feat_reg->feature_node_id = feat_reg->feature_node->id;
> +
> +             rc = rte_graph_feature_add(feat_reg);
> +
> +             if (rc < 0)
> +                     goto arc_cleanup;
> +     }
> +
> +     return 0;
> +
> +arc_cleanup:
> +     rte_graph_feature_arc_cleanup();
> +
> +     return rc;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_create,
> 25.07);
> +int
> +rte_graph_feature_arc_create(struct rte_graph_feature_arc_register *reg,
> +                          rte_graph_feature_arc_t *_arc)
> +{
> +     rte_graph_feature_arc_main_t *dfm = NULL;
> +     struct rte_graph_feature_arc *arc = NULL;
> +     uint16_t first_feat_off, fdata_off;
> +     const struct rte_memzone *mz = NULL;
> +     uint16_t iter, arc_index, num_index;
> +     uint32_t feat_sz = 0;
> +     size_t sz;
> +
> +     if (arc_registration_validate(reg, __func__, __LINE__, true) < 0)
> +             return -1;
> +
> +     if (!reg->end_feature ||
> +         (feature_registration_validate(reg->end_feature, __func__,
> __LINE__, 0, 1, true) < 0))
> +             return -1;
> +
> +     if (!reg->max_features)
> +             graph_err("Zero features found for arc \"%s\" create",
> +                       reg->arc_name);
> +
> +     if (!__rte_graph_feature_arc_main) {
> +             graph_err("Call to rte_graph_feature_arc_init() API missing");
> +             return -1;
> +     }
> +
> +     /* See if arc memory is already created */
> +     mz = rte_memzone_lookup(reg->arc_name);
> +     if (mz) {
> +             graph_err("Feature arc %s already created", reg->arc_name);
> +             arc = mz->addr;
> +             return -1;
> +     }
> +
> +     dfm = __rte_graph_feature_arc_main;
> +
> +     /* threshold check */
> +     if (dfm->num_feature_arcs > (dfm->max_feature_arcs - 1))
> +             SET_ERR_JMP(EAGAIN, arc_create_err,
> +                         "%s: max number (%u) of feature arcs reached",
> +                         reg->arc_name, dfm->max_feature_arcs);
> +
> +     /* Find the free slot for feature arc */
> +     for (iter = 0; iter < dfm->max_feature_arcs; iter++) {
> +             if (dfm->feature_arcs[iter] ==
> GRAPH_FEATURE_ARC_PTR_INITIALIZER)
> +                     break;
> +     }
> +     arc_index = iter;
> +
> +     if (arc_index >= dfm->max_feature_arcs) {
> +             graph_err("No free slot found for num_feature_arc");
> +             return -1;
> +     }
> +
> +     /* This should not happen */
> +     if (dfm->feature_arcs[arc_index] !=
> GRAPH_FEATURE_ARC_PTR_INITIALIZER) {
> +             graph_err("Free arc_index: %u is not found free: %p",
> +                       arc_index, (void *)dfm->feature_arcs[arc_index]);
> +             return -1;
> +     }
> +
> +     /* Calculate size of feature arc */
> +     feature_arc_reg_calc_size(reg, &sz, &first_feat_off, &fdata_off,
> &feat_sz, &num_index);
> +
> +     mz = rte_memzone_reserve(reg->arc_name, sz, SOCKET_ID_ANY, 0);
> +
> +     if (!mz) {
> +             graph_err("memzone reserve failed for arc: %s of size:
> %"PRIu64,
> +                       reg->arc_name, (uint64_t)sz);
> +             return -1;
> +     }
> +
> +     arc = mz->addr;
> +
> +     memset(arc, 0, sz);
> +
> +     arc->feature_bit_mask_by_index = rte_malloc(reg->arc_name,
> +                                                 sizeof(uint64_t) *
> num_index, 0);
> +
> +     if (!arc->feature_bit_mask_by_index) {
> +             graph_err("%s: rte_malloc failed for feature_bit_mask_alloc",
> reg->arc_name);
> +             goto mz_free;
> +     }
> +
> +     memset(arc->feature_bit_mask_by_index, 0, sizeof(uint64_t) *
> num_index);
> +
> +     /* override process function with start_node */
> +     if (node_override_process_func(reg->start_node->id, reg-
> >start_node_feature_process_fn)) {
> +             graph_err("node_override_process_func failed for %s", reg-
> >start_node->name);
> +             goto feat_bitmask_free;
> +     }
> +     feat_dbg("arc-%s: node-%s process() overridden with %p",
> +               reg->arc_name, reg->start_node->name,
> +               reg->start_node_feature_process_fn);
> +
> +     /* Initialize rte_graph port group fixed variables */
> +     STAILQ_INIT(&arc->all_features);
> +     rte_strscpy(arc->feature_arc_name, reg->arc_name,
> RTE_GRAPH_FEATURE_ARC_NAMELEN - 1);
> +     arc->feature_arc_main = (void *)dfm;
> +     arc->start_node = reg->start_node;
> +     memcpy(&arc->end_feature, reg->end_feature, sizeof(arc-
> >end_feature));
> +     arc->arc_start_process = reg->start_node_feature_process_fn;
> +     arc->feature_arc_index = arc_index;
> +     arc->arc_size = sz;
> +
> +     /* reset fast path arc variables */
> +     arc->max_features = reg->max_features;
> +     arc->max_indexes = num_index;
> +     arc->fp_first_feature_offset = first_feat_off;
> +     arc->fp_feature_data_offset = fdata_off;
> +     arc->feature_size = feat_sz;
> +     arc->mbuf_dyn_offset = dfm->arc_mbuf_dyn_offset;
> +
> +     feature_arc_data_reset(arc);
> +
> +     dfm->feature_arcs[arc->feature_arc_index] = (uintptr_t)arc;
> +     dfm->num_feature_arcs++;
> +
> +     if (rte_graph_feature_add(reg->end_feature) < 0)
> +             goto arc_destroy;
> +
> +     if (_arc)
> +             *_arc = (rte_graph_feature_arc_t)arc_index;
> +
> +     feat_dbg("Feature arc %s[%p] created with max_features: %u and
> indexes: %u",
> +              arc->feature_arc_name, (void *)arc, arc->max_features, arc-
> >max_indexes);
> +
> +     return 0;
> +
> +arc_destroy:
> +     rte_graph_feature_arc_destroy(arc_index);
> +feat_bitmask_free:
> +     rte_free(arc->feature_bit_mask_by_index);
> +mz_free:
> +     rte_memzone_free(mz);
> +arc_create_err:
> +     return -1;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_add, 25.07);
> +int
> +rte_graph_feature_add(struct rte_graph_feature_register *freg)
> +{
> +     struct rte_graph_feature_node_list *after_finfo = NULL, *before_finfo
> = NULL;
> +     struct rte_graph_feature_node_list *temp = NULL, *finfo = NULL;
> +     char feature_name[3 * RTE_GRAPH_FEATURE_ARC_NAMELEN];
> +     const char *runs_after = NULL, *runs_before = NULL;
> +     struct rte_graph_feature_arc *arc = NULL;
> +     uint32_t slot = UINT32_MAX, add_flag;
> +     rte_graph_feature_arc_t _arc;
> +     uint32_t num_features = 0;
> +     const char *nodename = NULL;
> +     rte_edge_t edge = -1;
> +     int rc = 0;
> +
> +     if (feature_registration_validate(freg, __func__, __LINE__, 0, 1, true) 
> <
> 0)
> +             return -1;
> +
> +     /* arc is valid */
> +     if (rte_graph_feature_arc_lookup_by_name(freg->arc_name, &_arc)) {
> +             graph_err("%s_add: feature arc %s not found",
> +                       freg->feature_name, freg->arc_name);
> +             return -1;
> +     }
> +
> +     if (feature_arc_sanity(_arc)) {
> +             graph_err("invalid feature arc: 0x%x", _arc);
> +             return -1;
> +     }
> +
> +     arc = rte_graph_feature_arc_get(_arc);
> +
> +     if (arc->runtime_enabled_features) {
> +             graph_err("adding features after enabling any one of them is
> not supported");
> +             return -1;
> +     }
> +
> +     /* When application calls rte_graph_feature_add() directly*/
> +     if (freg->feature_node_id == RTE_NODE_ID_INVALID) {
> +             graph_err("%s/%s: Invalid feature_node_id set for %s",
> +                       freg->arc_name, freg->feature_name, __func__);
> +             return -1;
> +     }
> +
> +     if ((freg->runs_after != NULL) && (freg->runs_before != NULL) &&
> +         (freg->runs_after == freg->runs_before)) {
> +             graph_err("runs_after and runs_before cannot be same
> '%s:%s]", freg->runs_after,
> +                       freg->runs_before);
> +             return -1;
> +     }
> +
> +     num_features = rte_graph_feature_arc_num_features(_arc);
> +     if (num_features) {
> +             nodeinfo_lkup_by_index(arc, num_features - 1, &temp, 0);
> +             /* Check if feature is not added after end_feature */
> +             if ((freg->runs_after != NULL) &&
> +                 (strncmp(freg->runs_after, temp->feature_name,
> +                          RTE_GRAPH_FEATURE_ARC_NAMELEN) == 0)) {
> +                     graph_err("Feature %s cannot be added after
> end_feature %s",
> +                               freg->feature_name, freg->runs_after);
> +                     return -1;
> +             }
> +     }
> +
> +     if (!nodeinfo_add_lookup(arc, freg->feature_name, &finfo, &slot)) {
> +             graph_err("%s/%s feature already added", arc-
> >feature_arc_name, freg->feature_name);
> +             return -1;
> +     }
> +
> +     if (slot >= arc->max_features) {
> +             graph_err("%s: Max features %u added to feature arc",
> +                       arc->feature_arc_name, slot);
> +             return -1;
> +     }
> +
> +     if (freg->feature_node_id == arc->start_node->id) {
> +             graph_err("%s/%s: Feature node and start node are same
> %u",
> +                       freg->arc_name, freg->feature_name, freg-
> >feature_node_id);
> +             return -1;
> +     }
> +
> +     nodename = rte_node_id_to_name(freg->feature_node_id);
> +
> +     feat_dbg("%s: adding feature node: %s at feature index: %u", arc-
> >feature_arc_name,
> +              nodename, slot);
> +
> +     if (connect_graph_nodes(arc->start_node->id, freg->feature_node_id,
> &edge,
> +                             arc->feature_arc_name)) {
> +             graph_err("unable to connect %s -> %s", arc->start_node-
> >name, nodename);
> +             return -1;
> +     }
> +
> +     snprintf(feature_name, sizeof(feature_name), "%s-%s-finfo",
> +              arc->feature_arc_name, freg->feature_name);
> +
> +     finfo = rte_malloc(feature_name, sizeof(*finfo), 0);
> +     if (!finfo) {
> +             graph_err("%s/%s: rte_malloc failed", arc->feature_arc_name,
> freg->feature_name);
> +             return -1;
> +     }
> +
> +     memset(finfo, 0, sizeof(*finfo));
> +
> +     rte_strscpy(finfo->feature_name, freg->feature_name,
> RTE_GRAPH_FEATURE_ARC_NAMELEN - 1);
> +     finfo->feature_arc = (void *)arc;
> +     finfo->feature_node_id = freg->feature_node_id;
> +     finfo->feature_node_process_fn = freg->feature_process_fn;
> +     finfo->edge_to_this_feature = RTE_EDGE_ID_INVALID;
> +     finfo->edge_to_last_feature = RTE_EDGE_ID_INVALID;
> +     finfo->notifier_cb = freg->notifier_cb;
> +
> +     runs_before = freg->runs_before;
> +     runs_after = freg->runs_after;
> +
> +     /*
> +      * if no constraints given and provided feature is not the first 
> feature,
> +      * explicitly set "runs_before" as end_feature.
> +      *
> +      * Handles the case:
> +      * arc_create(f1);
> +      * add(f2, NULL, NULL);
> +      */
> +     if (!runs_after && !runs_before && num_features)
> +             runs_before = rte_graph_feature_arc_feature_to_name(_arc,
> num_features - 1);
> +
> +     /* Check for before and after constraints */
> +     if (runs_before) {
> +             /* runs_before sanity */
> +             if (nodeinfo_lkup_by_name(arc, runs_before, &before_finfo,
> NULL))
> +                     SET_ERR_JMP(EINVAL, finfo_free,
> +                                  "runs_before feature name: %s does not
> exist", runs_before);
> +
> +             if (!before_finfo)
> +                     SET_ERR_JMP(EINVAL, finfo_free,
> +                                  "runs_before %s does not exist",
> runs_before);
> +
> +             /*
> +              * Starting from 0 to runs_before, continue connecting edges
> +              */
> +             add_flag = 1;
> +             STAILQ_FOREACH(temp, &arc->all_features, next_feature) {
> +                     if (!add_flag)
> +                             /* Nodes after seeing "runs_before", finfo
> connects to temp*/
> +                             connect_graph_nodes(finfo-
> >feature_node_id, temp->feature_node_id,
> +                                                 NULL, arc-
> >feature_arc_name);
> +                     /*
> +                      * As soon as we see runs_before. stop adding edges
> +                      */
> +                     if (!strncmp(temp->feature_name, runs_before,
> RTE_GRAPH_NAMESIZE)) {
> +                             if (!connect_graph_nodes(finfo-
> >feature_node_id,
> +                                                      temp-
> >feature_node_id,
> +                                                      &edge, arc-
> >feature_arc_name))
> +                                     add_flag = 0;
> +                     }
> +
> +                     if (add_flag)
> +                             /* Nodes before seeing "run_before" are
> connected to finfo */
> +                             connect_graph_nodes(temp-
> >feature_node_id, finfo->feature_node_id,
> +                                                 NULL, arc-
> >feature_arc_name);
> +             }
> +     }
> +
> +     if (runs_after) {
> +             if (nodeinfo_lkup_by_name(arc, runs_after, &after_finfo,
> NULL))
> +                     SET_ERR_JMP(EINVAL, finfo_free,
> +                                  "Invalid after feature_name %s",
> runs_after);
> +
> +             if (!after_finfo)
> +                     SET_ERR_JMP(EINVAL, finfo_free,
> +                                  "runs_after %s does not exist", 
> runs_after);
> +
> +             /* Starting from runs_after to end continue connecting edges
> */
> +             add_flag = 0;
> +             STAILQ_FOREACH(temp, &arc->all_features, next_feature) {
> +                     if (add_flag)
> +                             /* We have already seen runs_after now */
> +                             /* Add all features as next node to current
> feature*/
> +                             connect_graph_nodes(finfo-
> >feature_node_id, temp->feature_node_id,
> +                                                 NULL, arc-
> >feature_arc_name);
> +                     else
> +                             /* Connect initial nodes to newly added
> node*/
> +                             connect_graph_nodes(temp-
> >feature_node_id, finfo->feature_node_id,
> +                                                 NULL, arc-
> >feature_arc_name);
> +
> +                     /* as soon as we see runs_after. start adding edges
> +                      * from next iteration
> +                      */
> +                     if (!strncmp(temp->feature_name, runs_after,
> RTE_GRAPH_NAMESIZE))
> +                             add_flag = 1;
> +             }
> +
> +             /* add feature next to runs_after */
> +             STAILQ_INSERT_AFTER(&arc->all_features, after_finfo, finfo,
> next_feature);
> +     } else {
> +             if (before_finfo) {
> +                     /* add finfo before "before_finfo" element in the list 
> */
> +                     after_finfo = NULL;
> +                     STAILQ_FOREACH(temp, &arc->all_features,
> next_feature) {
> +                             if (before_finfo == temp) {
> +                                     if (after_finfo)
> +                                             STAILQ_INSERT_AFTER(&arc-
> >all_features, after_finfo,
> +                                                                 finfo,
> next_feature);
> +                                     else
> +                                             STAILQ_INSERT_HEAD(&arc-
> >all_features, finfo,
> +
> next_feature);
> +
> +                                     /* override node process fn */
> +                                     rc =
> node_override_process_func(finfo->feature_node_id,
> +                                                                     freg-
> >feature_process_fn);
> +
> +                                     if (rc < 0) {
> +
>       graph_err("node_override_process_func failed for %s",
> +                                                       freg-
> >feature_name);
> +                                             goto finfo_free;
> +                                     }
> +                                     return 0;
> +                             }
> +                             after_finfo = temp;
> +                     }
> +             } else {
> +                     /* Very first feature just needs to be added to list */
> +                     STAILQ_INSERT_TAIL(&arc->all_features, finfo,
> next_feature);
> +             }
> +     }
> +     /* override node_process_fn */
> +     rc = node_override_process_func(finfo->feature_node_id, freg-
> >feature_process_fn);
> +     if (rc < 0) {
> +             graph_err("node_override_process_func failed for %s", freg-
> >feature_name);
> +             goto finfo_free;
> +     }
> +
> +     if (freg->feature_node)
> +             feat_dbg("arc-%s: feature %s node %s process() overridden
> with %p",
> +                       freg->arc_name, freg->feature_name, freg-
> >feature_node->name,
> +                       freg->feature_process_fn);
> +     else
> +             feat_dbg("arc-%s: feature %s nodeid %u process() overriding
> with %p",
> +                       freg->arc_name, freg->feature_name,
> +                       freg->feature_node_id, freg->feature_process_fn);
> +
> +     return 0;
> +finfo_free:
> +     rte_free(finfo);
> +
> +     return -1;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_lookup, 25.07);
> +int
> +rte_graph_feature_lookup(rte_graph_feature_arc_t _arc, const char
> *feature_name,
> +                      rte_graph_feature_t *feat)
> +{
> +     struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +     struct rte_graph_feature_node_list *finfo = NULL;
> +     uint32_t slot;
> +
> +     if (!arc)
> +             return -1;
> +
> +     if (!nodeinfo_lkup_by_name(arc, feature_name, &finfo, &slot)) {
> +             *feat = (rte_graph_feature_t) slot;
> +             return 0;
> +     }
> +
> +     return -1;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_destroy,
> 25.07);
> +int
> +rte_graph_feature_arc_destroy(rte_graph_feature_arc_t _arc)
> +{
> +     struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +     rte_graph_feature_arc_main_t *dm = __rte_graph_feature_arc_main;
> +     struct rte_graph_feature_node_list *node_info = NULL;
> +     int iter;
> +
> +     if (!arc) {
> +             graph_err("Invalid feature arc: 0x%x", _arc);
> +             return -1;
> +     }
> +
> +     while (!STAILQ_EMPTY(&arc->all_features)) {
> +             node_info = STAILQ_FIRST(&arc->all_features);
> +             STAILQ_REMOVE_HEAD(&arc->all_features, next_feature);
> +             /* Notify application */
> +             if (node_info->notifier_cb) {
> +                     for (iter = 0; iter < arc->max_indexes; iter++) {
> +                             /* If feature is not enabled on this index, skip
> */
> +                             if (!(arc->feature_bit_mask_by_index[iter] &
> +                                 RTE_BIT64(node_info->finfo_index)))
> +                                     continue;
> +
> +                             node_info->notifier_cb(arc-
> >feature_arc_name,
> +                                                    node_info->feature_name,
> +                                                    node_info-
> >feature_node_id,
> +                                                    iter, false /* disable 
> */,
> +                                                    UINT16_MAX /* invalid
> cookie */);
> +                     }
> +             }
> +             rte_free(node_info);
> +     }
> +
> +     dm->feature_arcs[arc->feature_arc_index] =
> GRAPH_FEATURE_ARC_PTR_INITIALIZER;
> +
> +     rte_free(arc->feature_data_by_index);
> +
> +     rte_free(arc->feature_bit_mask_by_index);
> +
> +     rte_memzone_free(rte_memzone_lookup(arc->feature_arc_name));
> +
> +     return 0;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_cleanup,
> 25.07);
> +int
> +rte_graph_feature_arc_cleanup(void)
> +{
> +     rte_graph_feature_arc_main_t *dm = __rte_graph_feature_arc_main;
> +     struct rte_graph_feature_arc *arc = NULL;
> +     uint32_t iter;
> +
> +     if (!__rte_graph_feature_arc_main)
> +             return -1;
> +
> +     for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> +             arc = rte_graph_feature_arc_get(iter);
> +
> +             if (!arc)
> +                     continue;
> +
> +             rte_graph_feature_arc_destroy(arc->feature_arc_index);
> +     }
> +
>       rte_memzone_free(rte_memzone_lookup(FEATURE_ARC_MEMZONE
> _NAME));
> +     __rte_graph_feature_arc_main = NULL;
> +
> +     return 0;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_lookup_by_n
> ame, 25.07);
> +int
> +rte_graph_feature_arc_lookup_by_name(const char *arc_name,
> rte_graph_feature_arc_t *_arc)
> +{
> +     struct rte_graph_feature_arc *arc = NULL;
> +     const struct rte_memzone *mz = NULL;
> +     rte_graph_feature_arc_main_t *dm;
> +     uint32_t iter;
> +
> +     if (_arc)
> +             *_arc = RTE_GRAPH_FEATURE_ARC_INITIALIZER;
> +
> +     if (!__rte_graph_feature_arc_main) {
> +             mz =
> rte_memzone_lookup(FEATURE_ARC_MEMZONE_NAME);
> +             if (mz)
> +                     __rte_graph_feature_arc_main = mz->addr;
> +             else
> +                     return -1;
> +     }
> +
> +     dm = __rte_graph_feature_arc_main;
> +
> +     for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> +             arc = rte_graph_feature_arc_get(iter);
> +             if (!arc)
> +                     continue;
> +
> +             if ((strstr(arc->feature_arc_name, arc_name)) &&
> +                 (strlen(arc->feature_arc_name) == strlen(arc_name))) {
> +                     if (_arc)
> +                             *_arc = arc->feature_arc_index;
> +                     return 0;
> +             }
> +     }
> +
> +     return -1;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_num_feature
> s, 25.07);
> +uint32_t
> +rte_graph_feature_arc_num_features(rte_graph_feature_arc_t _arc)
> +{
> +     struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +     struct rte_graph_feature_node_list *finfo = NULL;
> +     uint32_t count = 0;
> +
> +     if (!arc) {
> +             graph_err("Invalid feature arc: 0x%x", _arc);
> +             return 0;
> +     }
> +
> +     STAILQ_FOREACH(finfo, &arc->all_features, next_feature)
> +             count++;
> +
> +     return count;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_feature_to_n
> ame, 25.07);
> +char *
> +rte_graph_feature_arc_feature_to_name(rte_graph_feature_arc_t _arc,
> rte_graph_feature_t feat)
> +{
> +     struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +     struct rte_graph_feature_node_list *finfo = NULL;
> +     uint32_t slot = feat;
> +
> +     if (!arc)
> +             return NULL;
> +
> +     if (feat >= rte_graph_feature_arc_num_features(_arc)) {
> +             graph_err("%s: feature %u does not exist", arc-
> >feature_arc_name, feat);
> +             return NULL;
> +     }
> +     if (!nodeinfo_lkup_by_index(arc, slot, &finfo, 0/* ignore sanity*/))
> +             return finfo->feature_name;
> +
> +     return NULL;
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_feature_to_n
> ode, 25.07);
> +int
> +rte_graph_feature_arc_feature_to_node(rte_graph_feature_arc_t _arc,
> rte_graph_feature_t feat,
> +                                   rte_node_t *node)
> +{
> +     struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +     struct rte_graph_feature_node_list *finfo = NULL;
> +     uint32_t slot = feat;
> +
> +     if (!arc)
> +             return -1;
> +
> +     if (node)
> +             *node = RTE_NODE_ID_INVALID;
> +
> +     if (feat >= rte_graph_feature_arc_num_features(_arc)) {
> +             graph_err("%s: feature %u does not exist", arc-
> >feature_arc_name, feat);
> +             return -1;
> +     }
> +     if (!nodeinfo_lkup_by_index(arc, slot, &finfo, 0/* ignore sanity*/)) {
> +             if (node)
> +                     *node = finfo->feature_node_id;
> +             return 0;
> +     }
> +     return -1;
> +}
> +
>  RTE_EXPORT_EXPERIMENTAL_SYMBOL(__rte_graph_feature_arc_register,
> 25.07);
>  void __rte_graph_feature_arc_register(struct rte_graph_feature_arc_register
> *reg,
>                                     const char *caller_name, int lineno)
> @@ -28,9 +1320,40 @@
> RTE_EXPORT_EXPERIMENTAL_SYMBOL(__rte_graph_feature_register, 25.07);
>  void __rte_graph_feature_register(struct rte_graph_feature_register *reg,
>                                 const char *caller_name, int lineno)
>  {
> -     RTE_SET_USED(caller_name);
> -     RTE_SET_USED(lineno);
> +     if (feature_registration_validate(reg, caller_name, lineno, 0, 0, true) 
> <
> 0)
> +             return;
> 
>       /* Add to the feature_list*/
>       STAILQ_INSERT_TAIL(&feature_list, reg, next_feature);
>  }
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_graph_feature_arc_names_get,
> 25.07);
> +uint32_t
> +rte_graph_feature_arc_names_get(char *arc_names[])
> +{
> +     rte_graph_feature_arc_main_t *dm = __rte_graph_feature_arc_main;
> +     struct rte_graph_feature_arc *arc = NULL;
> +     uint32_t count, num_arcs;
> +
> +     if (!__rte_graph_feature_arc_main)
> +             return 0;
> +
> +     for (count = 0, num_arcs = 0; count < dm->max_feature_arcs;
> count++)
> +             if (dm->feature_arcs[count] !=
> GRAPH_FEATURE_ARC_PTR_INITIALIZER)
> +                     num_arcs++;
> +
> +     if (!num_arcs)
> +             return 0;
> +
> +     if (!arc_names)
> +             return sizeof(char *) * num_arcs;
> +
> +     for (count = 0, num_arcs = 0; count < dm->max_feature_arcs;
> count++) {
> +             if (dm->feature_arcs[count] !=
> GRAPH_FEATURE_ARC_PTR_INITIALIZER) {
> +                     arc = rte_graph_feature_arc_get(count);
> +                     arc_names[num_arcs] = arc->feature_arc_name;
> +                     num_arcs++;
> +             }
> +     }
> +     return num_arcs;
> +}
> diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
> index 579546e658..5728933a88 100644
> --- a/lib/graph/graph_private.h
> +++ b/lib/graph/graph_private.h
> @@ -24,6 +24,10 @@ extern int rte_graph_logtype;
>       RTE_LOG_LINE_PREFIX(level, GRAPH,                                      \
>               "%s():%u ", __func__ RTE_LOG_COMMA __LINE__,
> __VA_ARGS__)
> 
> +#define GRAPH_LOG2(level, _fname, _linenum, ...)                             
>   \
> +     RTE_LOG_LINE_PREFIX(level, GRAPH,                                      \
> +             "%s():%u ", _fname RTE_LOG_COMMA _linenum,
> __VA_ARGS__)
> +
>  #define graph_err(...) GRAPH_LOG(ERR, __VA_ARGS__)
>  #define graph_warn(...) GRAPH_LOG(WARNING, __VA_ARGS__)
>  #define graph_info(...) GRAPH_LOG(INFO, __VA_ARGS__)
> diff --git a/lib/graph/meson.build b/lib/graph/meson.build
> index 1b2f493037..6a6d570290 100644
> --- a/lib/graph/meson.build
> +++ b/lib/graph/meson.build
> @@ -20,7 +20,7 @@ sources = files(
>          'graph_feature_arc.c',
>  )
>  headers = files('rte_graph.h', 'rte_graph_worker.h')
> -headers += files('rte_graph_feature_arc.h')
> +headers += files('rte_graph_feature_arc.h', 'rte_graph_feature_arc_worker.h')
>  indirect_headers += files(
>          'rte_graph_model_mcore_dispatch.h',
>          'rte_graph_model_rtc.h',
> diff --git a/lib/graph/rte_graph_feature_arc.h
> b/lib/graph/rte_graph_feature_arc.h
> index 56d8f2f34c..f25f77df3c 100644
> --- a/lib/graph/rte_graph_feature_arc.h
> +++ b/lib/graph/rte_graph_feature_arc.h
> @@ -87,16 +87,70 @@ extern "C" {
>   *
>   * A feature arc in a graph is represented via *start_node* and *end_node*.
>   * Feature nodes are added between start_node and end_node. Packets enter
> - * feature arc traversal via start_node while they exits from end_node. 
> Packets
> - * steering from start_node to feature nodes are controlled in control plane
> - * via rte_graph_feature_enable()/rte_graph_feature_disable().
> + * feature arc traversal via start_node while they exits from end_node.
>   *
>   * This library facilitates rte graph based applications to implement stack
>   * functionalities described above by providing "edge" to the next enabled
>   * feature node in fast path
>   *
> + * In order to use feature-arc APIs, applications needs to do following in
> + * control plane:
> + * - Create feature arc object using RTE_GRAPH_FEATURE_ARC_REGISTER()
> + * - New feature nodes (In-built/Out-of-tree) can be added to an arc via
> + *   RTE_GRAPH_FEATURE_REGISTER(). RTE_GRAPH_FEATURE_REGISTER()
> has
> + *   "runs_after" and "runs_before" fields to specify protocol ordering
> + *   constraints.
> + * - Before calling rte_graph_create(), rte_graph_feature_arc_init() API must
> + *   be called. If rte_graph_feature_arc_init() is not called by application,
> + *   feature arc library has no affect.
> + * - Feature arc can be destroyed via rte_graph_feature_arc_destroy()
> + *
> + * If a given feature likes to control number of indexes (which is higher 
> than
> + * RTE_GRAPH_FEATURE_ARC_REGISTER::max_indexes) it can do so by using
> + * RTE_GRAPH_FEATURE_REGISTER():override_index_cb(). As part of
> + * rte_graph_feature_arc_init(), all feature's override_index_cb(), if set, 
> are
> + * called and with maximum value returned by any of the feature is used for
> + * rte_graph_feature_arc_create()
> + *
> + * Constraints
> + * -----------
> + *  - rte_graph_feature_arc_init(), rte_graph_feature_create() and
> + *  rte_graph_feature_add() must be called before rte_graph_create().
> + *  - Not more than 63 features can be added to a feature arc. There is no
> + *  limit to number of feature arcs i.e. number of
> + *  RTE_GRAPH_FEATURE_ARC_REGISTER()
> + *  - There is also no limit for number of indexes
> (RTE_GRAPH_FEATURE_ARC_REGISTER():
> + *  max_indexes). There is also a provision for each
> + *  RTE_GRAPH_FEATURE_REGISTER() to override number of indexes via
> + *  override_index_cb()
> + *  - A feature node cannot be part of more than one arc due to
> + *  performance reason.
> + *
> + * Optional Usage
> + * --------------
> + * Feature arc is added as an optional functionality to the graph library 
> hence
> + * an application may choose not to use it by skipping explicit call to
> + * rte_graph_feature_arc_init(). In that case feature arc would be a no-op 
> for
> + * application.
>   */
> 
> +/** Length of feature arc name */
> +#define RTE_GRAPH_FEATURE_ARC_NAMELEN RTE_NODE_NAMESIZE
> +
> +/** Initializer values for ARC, Feature, Feature data */
> +#define RTE_GRAPH_FEATURE_ARC_INITIALIZER
> ((rte_graph_feature_arc_t)UINT16_MAX)
> +#define RTE_GRAPH_FEATURE_DATA_INVALID
> ((rte_graph_feature_data_t)UINT32_MAX)
> +#define RTE_GRAPH_FEATURE_INVALID  ((rte_graph_feature_t)UINT8_MAX)
> +
> +/** rte_graph feature arc object */
> +typedef uint16_t rte_graph_feature_arc_t;
> +
> +/** rte_graph feature object */
> +typedef uint8_t rte_graph_feature_t;
> +
> +/** rte_graph feature data object */
> +typedef uint32_t rte_graph_feature_data_t;
> +
>  /** feature notifier callback called when feature is enabled/disabled */
>  typedef void (*rte_graph_feature_change_notifier_cb_t)(const char
> *arc_name,
>                                                      const char *feature_name,
> @@ -230,6 +284,194 @@ struct rte_graph_feature_arc_register {
>       {                                                                       
>         \
>               __rte_graph_feature_arc_register(&reg, __func__, __LINE__);
> \
>       }
> +/**
> + * Initialize feature arc subsystem
> + *
> + * This API
> + * - Initializes feature arc module and alloc associated memory
> + * - creates feature arc for every RTE_GRAPH_FEATURE_ARC_REGISTER()
> + * - Add feature node to a feature arc for every
> RTE_GRAPH_FEATURE_REGISTER()
> + * - Replaces all RTE_NODE_REGISTER()->process() functions for
> + *   - Every start_node/end_node provided in arc registration
> + *   - Every feature node provided in feature registration
> + *
> + * @param num_feature_arcs
> + *  Number of feature arcs that application wants to create by explicitly 
> using
> + *  "rte_graph_feature_arc_create()" API.
> + *
> + *  Number of RTE_GRAPH_FEATURE_ARC_REGISTER() should be excluded
> from this
> + *  count as API internally calculates number of
> + *  RTE_GRAPH_FEATURE_ARC_REGISTER().
> + *
> + *  So,
> + *  total number of supported arcs = num_feature_arcs +
> + *                                   
> NUMBER_OF(RTE_GRAPH_FEATURE_ARC_REGISTER())
> + *
> + *  @return
> + *   0: Success
> + *   <0: Failure
> + *
> + *  rte_graph_feature_arc_init(0) is valid call which will accommodates
> + *  constructor based arc registration
> + */
> +__rte_experimental
> +int rte_graph_feature_arc_init(uint16_t num_feature_arcs);
> +
> +/**
> + * Create a feature arc.
> + *
> + * This API can be skipped if RTE_GRAPH_FEATURE_ARC_REGISTER() is used
> + *
> + * @param reg
> + *   Pointer to struct rte_graph_feature_arc_register
> + * @param[out] _arc
> + *  Feature arc object
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_arc_create(struct rte_graph_feature_arc_register *reg,
> +                              rte_graph_feature_arc_t *_arc);
> +
> +/**
> + * Get feature arc object with name
> + *
> + * @param arc_name
> + *   Feature arc name provided to successful @ref
> rte_graph_feature_arc_create
> + * @param[out] _arc
> + *   Feature arc object returned. Valid only when API returns SUCCESS
> + *
> + * @return
> + *  0: Success
> + * <0: Failure.
> + */
> +__rte_experimental
> +int rte_graph_feature_arc_lookup_by_name(const char *arc_name,
> rte_graph_feature_arc_t *_arc);
> +
> +/**
> + * Add a feature to already created feature arc.
> + *
> + * This API is not required in case RTE_GRAPH_FEATURE_REGISTER() is used
> + *
> + * @param feat_reg
> + * Pointer to struct rte_graph_feature_register
> + *
> + * <I> Must be called before rte_graph_create() </I>
> + * <I> When called by application, then feature_node_id should be
> appropriately set as
> + *     freg->feature_node_id = freg->feature_node->id;
> + * </I>
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_add(struct rte_graph_feature_register *feat_reg);
> +
> +/**
> + * Get rte_graph_feature_t object from feature name
> + *
> + * @param arc
> + *   Feature arc object returned by @ref rte_graph_feature_arc_create or
> @ref
> + *   rte_graph_feature_arc_lookup_by_name
> + * @param feature_name
> + *   Feature name provided to @ref rte_graph_feature_add
> + * @param[out] feature
> + *   Feature object
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_lookup(rte_graph_feature_arc_t arc, const char
> *feature_name,
> +                          rte_graph_feature_t *feature);
> +
> +/**
> + * Delete feature_arc object
> + *
> + * @param _arc
> + *   Feature arc object returned by @ref rte_graph_feature_arc_create or
> @ref
> + *   rte_graph_feature_arc_lookup_by_name
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_arc_destroy(rte_graph_feature_arc_t _arc);
> +
> +/**
> + * Cleanup all feature arcs
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_arc_cleanup(void);
> +
> +/**
> + * Slow path API to know how many features are added (NOT enabled)
> within a
> + * feature arc
> + *
> + * @param _arc
> + *  Feature arc object
> + *
> + * @return: Number of added features to arc
> + */
> +__rte_experimental
> +uint32_t rte_graph_feature_arc_num_features(rte_graph_feature_arc_t
> _arc);
> +
> +/**
> + * Slow path API to get feature node name from rte_graph_feature_t object
> + *
> + * @param _arc
> + *   Feature arc object
> + * @param feature
> + *   Feature object
> + *
> + * @return: Name of the feature node
> + */
> +__rte_experimental
> +char *rte_graph_feature_arc_feature_to_name(rte_graph_feature_arc_t
> _arc,
> +                                         rte_graph_feature_t feature);
> +
> +/**
> + * Slow path API to get corresponding rte_node_t from
> + * rte_graph_feature_t
> + *
> + * @param _arc
> + *   Feature arc object
> + * @param feature
> + *   Feature object
> + * @param[out] node
> + *   rte_node_t of feature node, Valid only when API returns SUCCESS
> + *
> + * @return: 0 on success, < 0 on failure
> + */
> +__rte_experimental
> +int
> +rte_graph_feature_arc_feature_to_node(rte_graph_feature_arc_t _arc,
> +                                   rte_graph_feature_t feature,
> +                                   rte_node_t *node);
> +
> +/**
> + * Slow path API to dump valid feature arc names
> + *
> + *  @param[out] arc_names
> + *   Buffer to copy the arc names. The NULL value is allowed in that case,
> + * the function returns the size of the array that needs to be allocated.
> + *
> + * @return
> + *   When next_nodes == NULL, it returns the size of the array else
> + *  number of item copied.
> + */
> +__rte_experimental
> +uint32_t
> +rte_graph_feature_arc_names_get(char *arc_names[]);
> 
>  /**
>   * @internal
> diff --git a/lib/graph/rte_graph_feature_arc_worker.h
> b/lib/graph/rte_graph_feature_arc_worker.h
> new file mode 100644
> index 0000000000..b2fc539402
> --- /dev/null
> +++ b/lib/graph/rte_graph_feature_arc_worker.h
> @@ -0,0 +1,303 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2025 Marvell International Ltd.
> + */
> +
> +#ifndef _RTE_GRAPH_FEATURE_ARC_WORKER_H_
> +#define _RTE_GRAPH_FEATURE_ARC_WORKER_H_
> +
> +#include <stddef.h>
> +#include <rte_graph_feature_arc.h>
> +#include <rte_bitops.h>
> +#include <rte_mbuf.h>
> +#include <rte_mbuf_dyn.h>
> +
> +/**
> + * @file
> + *
> + * rte_graph_feature_arc_worker.h
> + *
> + * Defines fast path structure for feature arc
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * @internal
> + *
> + * Slow path feature node info list
> + */
> +struct rte_graph_feature_node_list {
> +     /** Next feature */
> +     STAILQ_ENTRY(rte_graph_feature_node_list) next_feature;
> +
> +     char feature_name[RTE_GRAPH_FEATURE_ARC_NAMELEN];
> +
> +     /** node id representing feature */
> +     rte_node_t feature_node_id;
> +
> +     /** How many indexes/interfaces using this feature */
> +     int32_t ref_count;
> +
> +     /**
> +      * feature arc process function overrides to feature node's original
> +      * process function
> +      */
> +     rte_node_process_t feature_node_process_fn;
> +
> +     /** Callback for freeing application resources when */
> +     rte_graph_feature_change_notifier_cb_t notifier_cb;
> +
> +     /** finfo_index in list. same as rte_graph_feature_t */
> +     uint32_t finfo_index;
> +
> +     /** Back pointer to feature arc */
> +     void *feature_arc;
> +
> +     /** rte_edge_t to this feature node from feature_arc->start_node */
> +     rte_edge_t edge_to_this_feature;
> +
> +     /** rte_edge_t from this feature node to last feature node */
> +     rte_edge_t edge_to_last_feature;
> +};
> +
> +/**
> + * rte_graph Feature arc object
> + *
> + * Feature arc object holds control plane and fast path information for all
> + * features and all interface index information for steering packets across
> + * feature nodes
> + *
> + * Within a feature arc, only RTE_GRAPH_FEATURE_MAX_PER_ARC features
> can be
> + * added. If more features needs to be added, another feature arc can be
> + * created
> + *
> + * In fast path, rte_graph_feature_arc_t can be translated to (struct
> + * rte_graph_feature_arc *) via rte_graph_feature_arc_get(). Later is needed
> to
> + * add as an input argument to all fast path feature arc APIs
> + */
> +struct __rte_cache_aligned rte_graph_feature_arc {
> +     /** Slow path variables follows*/
> +     RTE_MARKER slow_path_variables;
> +
> +     /** All feature lists */
> +     STAILQ_HEAD(, rte_graph_feature_node_list) all_features;
> +
> +     /** feature arc name */
> +     char feature_arc_name[RTE_GRAPH_FEATURE_ARC_NAMELEN];
> +
> +     /** control plane counter to track enabled features */
> +     uint32_t runtime_enabled_features;
> +
> +     /** maximum number of features supported by this arc
> +      *  Immutable during fast path
> +      */
> +     uint16_t max_features;
> +
> +     /** index in feature_arc_main */
> +     rte_graph_feature_arc_t feature_arc_index;
> +
> +     /** Back pointer to feature_arc_main */
> +     void *feature_arc_main;
> +
> +     /** Arc's start/end node */
> +     struct rte_node_register *start_node;
> +     struct rte_graph_feature_register end_feature;
> +
> +     /** arc start process function */
> +     rte_node_process_t arc_start_process;
> +
> +     /** total arc_size allocated */
> +     size_t arc_size;
> +
> +     /** slow path: feature data array maintained per [feature, index] */
> +     rte_graph_feature_data_t *feature_data_by_index;
> +
> +     /**
> +      * Size of all feature data for each feature
> +      * ALIGN(sizeof(struct rte_graph_feature_data) * arc->max_indexes)
> +      * Not used in fastpath
> +      */
> +     uint32_t feature_size;
> +
> +     /** Slow path bit mask per feature per index */
> +     uint64_t *feature_bit_mask_by_index;
> +
> +     /** Cache aligned fast path variables */
> +     alignas(RTE_CACHE_LINE_SIZE) RTE_MARKER fast_path_variables;
> +
> +     /**
> +      * Quick fast path bitmask indicating if any feature enabled. Each bit
> +      * corresponds to single feature. Helps in optimally process packets for
> +      * the case when features are added but not enabled
> +      */
> +     RTE_ATOMIC(uint64_t) fp_feature_enable_bitmask;
> +
> +     /**
> +      * Number of added features. <= max_features
> +      */
> +     uint16_t num_added_features;
> +     /** maximum number of index supported by this arc
> +      *  Immutable during fast path
> +      */
> +     uint16_t max_indexes;
> +
> +     /** first feature offset in fast path
> +      * Immutable during fast path
> +      */
> +     uint16_t fp_first_feature_offset;
> +
> +     /** arc + fp_feature_data_arr_offset
> +      * Immutable during fast path
> +      */
> +     uint16_t fp_feature_data_offset;
> +
> +     /**
> +      * mbuf dynamic offset saved for faster access
> +      * See rte_graph_feature_arc_mbuf_dynfields_get() for more details
> +      */
> +     int mbuf_dyn_offset;
> +
> +     RTE_MARKER8 fp_arc_data;
> +};
> +
> +/**
> + * Feature arc main object
> + *
> + * Holds all feature arcs created by application
> + */
> +typedef struct rte_feature_arc_main {
> +     /** number of feature arcs created by application */
> +     uint32_t num_feature_arcs;
> +
> +     /** max features arcs allowed */
> +     uint32_t max_feature_arcs;
> +
> +     /** arc_mbuf_dyn_offset for saving feature arc specific
> +      * mbuf dynfield offset.
> +      *
> +      * See rte_graph_feature_arc_mbuf_dynfields_get() for more details
> +      */
> +     int arc_mbuf_dyn_offset;
> +
> +     /** Pointer to all feature arcs */
> +     uintptr_t feature_arcs[];
> +} rte_graph_feature_arc_main_t;
> +
> +/**
> + *  Fast path feature data object
> + *
> + *  Used by fast path inline feature arc APIs
> + *  Corresponding to rte_graph_feature_data_t
> + *  It holds
> + *  - edge to reach to next feature node
> + *  - next_feature_data corresponding to next enabled feature
> + */
> +struct rte_graph_feature_data {
> +     /** edge from this feature node to next enabled feature node */
> +     RTE_ATOMIC(rte_edge_t) next_edge;
> +
> +     /**
> +      * app_cookie
> +      */
> +     RTE_ATOMIC(uint16_t) app_cookie;
> +
> +     /** Next feature data from this feature data */
> +     RTE_ATOMIC(rte_graph_feature_data_t) next_feature_data;
> +};
> +
> +/** feature arc specific mbuf dynfield structure. */
> +struct rte_graph_feature_arc_mbuf_dynfields {
> +     /** each mbuf carries feature data */
> +     rte_graph_feature_data_t feature_data;
> +};
> +
> +/** Name of dynamic mbuf field offset registered in
> rte_graph_feature_arc_init() */
> +#define RTE_GRAPH_FEATURE_ARC_DYNFIELD_NAME
> "__rte_graph_feature_arc_mbuf_dynfield"
> +
> +/**
> + * @internal macro
> + */
> +#define GRAPH_FEATURE_ARC_PTR_INITIALIZER  ((uintptr_t)UINTPTR_MAX)
> +
> +/** extern variables */
> +extern rte_graph_feature_arc_main_t *__rte_graph_feature_arc_main;
> +
> +/**
> + * Get dynfield offset to feature arc specific fields in mbuf
> + *
> + * Feature arc mbuf dynamic field is separate to utilize mbuf->dynfield2
> + * instead of dynfield1
> + *
> + * This arc specific dynamic offset is registered as part of
> + * rte_graph_feature_arc_init() and copied in each arc for fast path access.
> + * This avoids node maintaining dynamic offset for feature arc and if we are
> + * lucky, field would be allocated from mbuf->dynfield2. Otherwise each
> node
> + * has to maintain at least two dynamic offset in fast path
> + *
> + * @param mbuf
> + *  Pointer to mbuf
> + * @param dyn_offset
> + *  Retrieved from arc->mbuf_dyn_offset
> + *
> + * @return
> + *  NULL: On Failure
> + *  Non-NULL pointer on Success
> + */
> +__rte_experimental
> +static __rte_always_inline struct rte_graph_feature_arc_mbuf_dynfields *
> +rte_graph_feature_arc_mbuf_dynfields_get(struct rte_mbuf *mbuf,
> +                                      const int dyn_offset)
> +{
> +     return RTE_MBUF_DYNFIELD(mbuf, dyn_offset,
> +                              struct rte_graph_feature_arc_mbuf_dynfields
> *);
> +}
> +
> +/**
> + * API to know if feature is valid or not
> + *
> + * @param feature
> + *  rte_graph_feature_t
> + *
> + * @return
> + *  1: If feature is valid
> + *  0: If feature is invalid
> + */
> +__rte_experimental
> +static __rte_always_inline int
> +rte_graph_feature_is_valid(rte_graph_feature_t feature)
> +{
> +     return (feature != RTE_GRAPH_FEATURE_INVALID);
> +}
> +
> +/**
> + * Get pointer to feature arc object from rte_graph_feature_arc_t
> + *
> + * @param arc
> + *  feature arc
> + *
> + * @return
> + *  NULL: On Failure
> + *  Non-NULL pointer on Success
> + */
> +__rte_experimental
> +static __rte_always_inline struct rte_graph_feature_arc *
> +rte_graph_feature_arc_get(rte_graph_feature_arc_t arc)
> +{
> +     uintptr_t fa = GRAPH_FEATURE_ARC_PTR_INITIALIZER;
> +     rte_graph_feature_arc_main_t *fm = NULL;
> +
> +     fm = __rte_graph_feature_arc_main;
> +
> +     if (likely((fm != NULL) && (arc < fm->max_feature_arcs)))
> +             fa = fm->feature_arcs[arc];
> +
> +     return (fa == GRAPH_FEATURE_ARC_PTR_INITIALIZER) ?
> +             NULL : (struct rte_graph_feature_arc *)fa;
> +}
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +#endif
> --
> 2.43.0

Reply via email to