[RFC,1/3] graph: add feature arc support

Message ID 20240907073115.1531070-2-nsaxena@marvell.com (mailing list archive)
State Superseded
Delegated to: Thomas Monjalon
Headers
Series add feature arc in rte_graph |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Nitin Saxena Sept. 7, 2024, 7:31 a.m. UTC
add feature arc to allow dynamic steering of packets across graph nodes
based on protocol features enabled on incoming or outgoing interface

Signed-off-by: Nitin Saxena <nsaxena@marvell.com>
---
 lib/graph/graph_feature_arc.c            | 959 +++++++++++++++++++++++
 lib/graph/meson.build                    |   2 +
 lib/graph/rte_graph_feature_arc.h        | 373 +++++++++
 lib/graph/rte_graph_feature_arc_worker.h | 548 +++++++++++++
 lib/graph/version.map                    |  17 +
 5 files changed, 1899 insertions(+)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h
  

Comments

Kiran Kumar Kokkilagadda Sept. 11, 2024, 4:41 a.m. UTC | #1
> -----Original Message-----
> From: Nitin Saxena <nsaxena@marvell.com>
> Sent: Saturday, September 7, 2024 1:01 PM
> To: Jerin Jacob <jerinj@marvell.com>; Kiran Kumar Kokkilagadda
> <kirankumark@marvell.com>; Nithin Kumar Dabilpuram
> <ndabilpuram@marvell.com>; Zhirun Yan <yanzhirun_163@163.com>
> Cc: dev@dpdk.org; Nitin Saxena <nsaxena16@gmail.com>
> Subject: [RFC PATCH 1/3] graph: add feature arc support
> 
> add feature arc to allow dynamic steering of packets across graph nodes
> based on protocol features enabled on incoming or outgoing interface
> 
> Signed-off-by: Nitin Saxena <nsaxena@marvell.com>
> ---
>  lib/graph/graph_feature_arc.c            | 959 +++++++++++++++++++++++
>  lib/graph/meson.build                    |   2 +
>  lib/graph/rte_graph_feature_arc.h        | 373 +++++++++
>  lib/graph/rte_graph_feature_arc_worker.h | 548 +++++++++++++
>  lib/graph/version.map                    |  17 +
>  5 files changed, 1899 insertions(+)
>  create mode 100644 lib/graph/graph_feature_arc.c
>  create mode 100644 lib/graph/rte_graph_feature_arc.h
>  create mode 100644 lib/graph/rte_graph_feature_arc_worker.h
> 
> diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
> new file mode 100644
> index 0000000000..3b05bac137
> --- /dev/null
> +++ b/lib/graph/graph_feature_arc.c
> @@ -0,0 +1,959 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2024 Marvell International Ltd.
> + */
> +
> +#include "graph_private.h"
> +#include <rte_graph_feature_arc_worker.h>
> +#include <rte_malloc.h>
> +
> +#define __RTE_GRAPH_FEATURE_ARC_MAX 32
> +
> +#define ARC_PASSIVE_LIST(arc) (arc->active_feature_list ^ 0x1)
> +
> +#define rte_graph_uint_cast(x) ((unsigned int)x)
> +#define feat_dbg graph_err
> +
> +rte_graph_feature_arc_main_t *__feature_arc_main;
> +
> +/* Make sure fast path cache line is compact */
> +_Static_assert((offsetof(struct rte_graph_feature_arc, slow_path_variables)
> +		- offsetof(struct rte_graph_feature_arc, fast_path_variables))
> +	       <= RTE_CACHE_LINE_SIZE);
> +
> +
> +static int
> +feature_lookup(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;
> +	const char *name;
> +
> +	if (!feat_name)
> +		return -1;
> +
> +	if (slot)
> +		*slot = 0;
> +
> +	STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> +		RTE_VERIFY(finfo->feature_arc == arc);
> +		name = rte_node_id_to_name(finfo->feature_node->id);
> +		if (!strncmp(name, feat_name, RTE_GRAPH_NAMESIZE)) {
> +			if (ffinfo)
> +				*ffinfo = finfo;
> +			return 0;
> +		}
> +		if (slot)
> +			(*slot)++;
> +	}
> +	return -1;
> +}
> +
> +static int
> +feature_arc_node_info_lookup(struct rte_graph_feature_arc *arc, uint32_t
> feature_index,
> +			     struct rte_graph_feature_node_list **ppfinfo)
> +{
> +	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) {
> +		if (index == feature_index) {
> +			if (finfo->node_index == feature_index)
> +				return -1;
> +			*ppfinfo = finfo;
> +		}
> +		index++;
> +	}
> +	if (feature_index && (index >= feature_index))
> +		return -1;
> +
> +	return 0;
> +}
> +
> +static void
> +prepare_feature_arc(struct rte_graph_feature_arc *arc)
> +{
> +	struct rte_graph_feature_node_list *finfo = NULL;
> +	uint32_t index = 0;
> +
> +	STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> +		finfo->node_index = index;
> +		index++;
> +	}
> +}
> +
> +static int
> +feature_arc_lookup(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 = __feature_arc_main;
> +	uint32_t iter;
> +
> +	if (!__feature_arc_main)
> +		return -1;
> +
> +	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> +		if (dm->feature_arcs[iter] ==
> RTE_GRAPH_FEATURE_ARC_INITIALIZER)
> +			continue;
> +
> +		if (arc == (rte_graph_feature_arc_get(dm-
> >feature_arcs[iter])))
> +			return 0;
> +	}
> +	return -1;
> +}
> +
> +static int
> +get_existing_edge(const char *arc_name, struct rte_node_register
> *parent_node,
> +		  struct rte_node_register *child_node, rte_edge_t *_edge)
> +{
> +	char **next_edges = NULL;
> +	uint32_t count, i;
> +
> +	RTE_SET_USED(arc_name);
> +
> +	count = rte_node_edge_get(parent_node->id, NULL);
> +	next_edges = malloc(count);
> +
> +	if (!next_edges)
> +		return -1;
> +
> +	count = rte_node_edge_get(parent_node->id, next_edges);
> +	for (i = 0; i < count; i++) {
> +		if (strstr(child_node->name, next_edges[i])) {
> +			feat_dbg("%s: Edge exists [%s[%u]: \"%s\"]",
> arc_name,
> +				  parent_node->name, i, child_node->name);
> +			if (_edge)
> +				*_edge = (rte_edge_t)i;
> +
> +			free(next_edges);
> +			return 0;
> +		}
> +	}
> +	free(next_edges);
> +
> +	return -1;
> +}
> +
> +static int
> +connect_graph_nodes(struct rte_node_register *parent_node, struct
> rte_node_register *child_node,
> +		    rte_edge_t *_edge, char *arc_name)
> +{
> +	const char *next_node = NULL;
> +	rte_edge_t edge;
> +
> +	if (!get_existing_edge(arc_name, parent_node, child_node, &edge)) {
> +		feat_dbg("%s: add_feature: Edge reused [%s[%u]: \"%s\"]",
> arc_name,
> +			parent_node->name, edge, child_node->name);
> +
> +		if (_edge)
> +			*_edge = edge;
> +
> +		return 0;
> +	}
> +
> +	/* Node to be added */
> +	next_node = child_node->name;
> +
> +	edge = rte_node_edge_update(parent_node->id,
> 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->id) - 1;
> +
> +	feat_dbg("%s: add_feature: edge added [%s[%u]: \"%s\"]", arc_name,
> parent_node->name, edge,
> +		child_node->name);
> +
> +	if (_edge)
> +		*_edge = edge;
> +
> +	return 0;
> +}
> +
> +static int
> +feature_arc_init(rte_graph_feature_arc_main_t **pfl, uint32_t
> max_feature_arcs)
> +{
> +	rte_graph_feature_arc_main_t *pm = NULL;
> +	uint32_t i;
> +	size_t sz;
> +
> +	if (!pfl)
> +		return -1;
> +
> +	sz = sizeof(rte_graph_feature_arc_main_t) +
> +		(sizeof(pm->feature_arcs[0]) * max_feature_arcs);
> +
> +	pm = malloc(sz);
> +	if (!pm)
> +		return -1;
> +
> +	memset(pm, 0, sz);
> +
> +	for (i = 0; i < max_feature_arcs; i++)
> +		pm->feature_arcs[i] =
> RTE_GRAPH_FEATURE_ARC_INITIALIZER;
> +
> +	pm->max_feature_arcs = max_feature_arcs;
> +
> +	*pfl = pm;
> +
> +	return 0;
> +}
> +
> +int
> +rte_graph_feature_arc_init(int max_feature_arcs)
> +{
> +	if (!max_feature_arcs)
> +		return -1;
> +
> +	if (__feature_arc_main)
> +		return -1;
> +
> +	return feature_arc_init(&__feature_arc_main, max_feature_arcs);
> +}
> +
> +static void
> +feature_arc_list_reset(struct rte_graph_feature_arc *arc, uint32_t list_index)
> +{
> +	rte_graph_feature_data_t *fdata = NULL;
> +	rte_graph_feature_list_t *list = NULL;
> +	struct rte_graph_feature *feat = NULL;
> +	uint32_t i, j;
> +
> +	list = arc->feature_list[list_index];
> +	feat = arc->features[list_index];
> +
> +	/*Initialize variables*/
> +	memset(feat, 0, arc->feature_size);
> +	memset(list, 0, arc->feature_list_size);
> +
> +	/* Initialize feature and feature_data */
> +	for (i = 0; i < arc->max_features; i++) {
> +		feat = __rte_graph_feature_get(arc, i, list_index);
> +		feat->this_feature_index = i;
> +
> +		for (j = 0; j < arc->max_indexes; j++) {
> +			fdata = rte_graph_feature_data_get(arc, feat, j);
> +			fdata->next_enabled_feature =
> RTE_GRAPH_FEATURE_INVALID;
> +			fdata->next_edge = UINT16_MAX;
> +			fdata->user_data = UINT32_MAX;
> +		}
> +	}
> +
> +	for (i = 0; i < arc->max_indexes; i++)
> +		list->first_enabled_feature_by_index[i] =
> RTE_GRAPH_FEATURE_INVALID;
> +}
> +
> +static int
> +feature_arc_list_init(struct rte_graph_feature_arc *arc, const char
> *flist_name,
> +			  rte_graph_feature_list_t **pplist,
> +			  struct rte_graph_feature **ppfeature, uint32_t
> list_index)
> +{
> +	char fname[2 * RTE_GRAPH_FEATURE_ARC_NAMELEN];
> +	size_t list_size, feat_size, fdata_size;
> +	rte_graph_feature_list_t *list = NULL;
> +	struct rte_graph_feature *feat = NULL;
> +
> +	list_size = sizeof(list->first_enabled_feature_by_index[0]) * arc-
> >max_indexes;
> +
> +	list = rte_malloc(flist_name, list_size, RTE_CACHE_LINE_SIZE);
> +	if (!list)
> +		return -ENOMEM;
> +
> +	fdata_size = arc->max_indexes * sizeof(rte_graph_feature_data_t);
> +
> +	/* Let one feature capture complete cache lines */
> +	feat_size = RTE_ALIGN_CEIL(sizeof(struct rte_graph_feature) +
> fdata_size,
> +				   RTE_CACHE_LINE_SIZE);
> +
> +	snprintf(fname, sizeof(fname), "%s-%s", arc->feature_arc_name,
> "feat");
> +
> +	feat = rte_malloc(fname, feat_size * arc->max_features,
> RTE_CACHE_LINE_SIZE);
> +	if (!feat) {
> +		rte_free(list);
> +		return -ENOMEM;
> +	}
> +	arc->feature_size = feat_size;
> +	arc->feature_data_size = fdata_size;
> +	arc->feature_list_size = list_size;
> +
> +	/* Initialize list */
> +	list->indexed_by_features = feat;
> +	*pplist = list;
> +	*ppfeature = feat;
> +
> +	feature_arc_list_reset(arc, list_index);
> +
> +	return 0;
> +}
> +
> +static void
> +feature_arc_list_destroy(rte_graph_feature_list_t *list)
> +{
> +	rte_free(list->indexed_by_features);
 Do you need to free individual rte_graph_feature here, that is allocated in arc_list_init?

> +	rte_free(list);
> +}
> +
> +int
> +rte_graph_feature_arc_create(const char *feature_arc_name, int
> max_features, int max_indexes,
> +		       struct rte_node_register *start_node,
> rte_graph_feature_arc_t *_arc)
> +{
> +	char name[2 * RTE_GRAPH_FEATURE_ARC_NAMELEN];
> +	rte_graph_feature_arc_main_t *dfm = NULL;
> +	struct rte_graph_feature_arc *arc = NULL;
> +	struct rte_graph_feature_data *gfd = NULL;
> +	struct rte_graph_feature *df = NULL;
> +	uint32_t iter, j, arc_index;
> +	size_t sz;
> +
> +	if (!_arc)
> +		return -1;
> +
> +	if (max_features < 2)
> +		return -1;
> +
> +	if (!start_node)
> +		return -1;
> +
> +	if (!feature_arc_name)
> +		return -1;
> +
> +	if (max_features > RTE_GRAPH_FEATURE_MAX_PER_ARC) {
> +		graph_err("Invalid max features: %u", max_features);
> +		return -1;
> +	}
> +
> +	/*
> +	 * Application hasn't called rte_graph_feature_arc_init(). Initialize with
> +	 * default values
> +	 */
> +	if (!__feature_arc_main) {
> +		if
> (rte_graph_feature_arc_init((int)__RTE_GRAPH_FEATURE_ARC_MAX) < 0) {
> +			graph_err("rte_graph_feature_arc_init() failed");
> +			return -1;
> +		}
> +	}
> +
> +	dfm = __feature_arc_main;
> +
> +	/* threshold check */
> +	if (dfm->num_feature_arcs > (dfm->max_feature_arcs - 1)) {
> +		graph_err("max threshold for num_feature_arcs: %d
> reached",
> +			  dfm->max_feature_arcs - 1);
> +		return -1;
> +	}
> +	/* Find the free slot for feature arc */
> +	for (iter = 0; iter < dfm->max_feature_arcs; iter++) {
> +		if (dfm->feature_arcs[iter] ==
> RTE_GRAPH_FEATURE_ARC_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 */
> +	RTE_VERIFY(dfm->feature_arcs[arc_index] ==
> RTE_GRAPH_FEATURE_ARC_INITIALIZER);
> +
> +	/* size of feature arc + feature_bit_mask_by_index */
> +	sz = sizeof(*arc) + (sizeof(uint64_t) * max_indexes);
> +
> +	arc = rte_malloc(feature_arc_name, sz, RTE_CACHE_LINE_SIZE);
> +
> +	if (!arc) {
> +		graph_err("malloc failed for feature_arc_create()");
> +		return -1;
> +	}
> +
> +	memset(arc, 0, sz);
> +
> +	/* Initialize rte_graph port group fixed variables */
> +	STAILQ_INIT(&arc->all_features);
> +	strncpy(arc->feature_arc_name, feature_arc_name,
> RTE_GRAPH_FEATURE_ARC_NAMELEN - 1);
> +	arc->feature_arc_main = (void *)dfm;
> +	arc->start_node = start_node;
> +	arc->max_features = max_features;
> +	arc->max_indexes = max_indexes;
> +
> +	snprintf(name, sizeof(name), "%s-%s", feature_arc_name, "flist0");
> +
> +	if (feature_arc_list_init(arc, name, &arc->feature_list[0], &arc-
> >features[0], 0) < 0) {
> +		rte_free(arc);
> +		graph_err("feature_arc_list_init(0) failed");
> +		return -1;
> +	}
> +	snprintf(name, sizeof(name), "%s-%s", feature_arc_name, "flist1");
> +
> +	if (feature_arc_list_init(arc, name, &arc->feature_list[1], &arc-
> >features[1], 1) < 0) {
> +		feature_arc_list_destroy(arc->feature_list[0]);
> +		graph_err("feature_arc_list_init(1) failed");
> +		return -1;
> +	}
> +
> +	for (iter = 0; iter < arc->max_features; iter++) {
> +		df = rte_graph_feature_get(arc, iter);
> +		for (j = 0; j < arc->max_indexes; j++) {
> +			gfd = rte_graph_feature_data_get(arc, df, j);
> +			gfd->next_enabled_feature =
> RTE_GRAPH_FEATURE_INVALID;
> +		}
> +	}
> +	arc->feature_arc_index = arc_index;
> +	dfm->feature_arcs[arc->feature_arc_index] =
> (rte_graph_feature_arc_t)arc;
> +	dfm->num_feature_arcs++;
> +
> +	if (_arc)
> +		*_arc = (rte_graph_feature_arc_t)arc;
> +
> +	return 0;
> +}
> +
> +int
> +rte_graph_feature_add(rte_graph_feature_arc_t _arc, struct
> rte_node_register *feature_node,
> +		const char *after_feature, const char *before_feature)
> +{
> +	struct rte_graph_feature_node_list *after_finfo = NULL, *before_finfo
> = NULL;
> +	struct rte_graph_feature_node_list *temp = NULL, *finfo = NULL;
> +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +	uint32_t slot, add_flag;
> +	rte_edge_t edge = -1;
> +
> +	RTE_VERIFY(arc->feature_arc_main == __feature_arc_main);
> +
> +	if (feature_arc_lookup(_arc)) {
> +		graph_err("invalid feature arc: 0x%016" PRIx64,
> (uint64_t)_arc);
> +		return -1;
> +	}
> +
> +	if (arc->runtime_enabled_features) {
> +		graph_err("adding features after enabling any one of them is
> not supported");
> +		return -1;
> +	}
> +
> +	if ((after_feature != NULL) && (before_feature != NULL) &&
> +	    (after_feature == before_feature)) {
> +		graph_err("after_feature and before_feature are same
> '%s:%s]", after_feature,
> +			before_feature);
> +		return -1;
> +	}
> +
> +	if (!feature_node) {
> +		graph_err("feature_node: %p invalid", feature_node);
> +		return -1;
> +	}
> +
> +	arc = rte_graph_feature_arc_get(_arc);
> +
> +	if (feature_node->id == RTE_NODE_ID_INVALID) {
> +		graph_err("Invalid node: %s", feature_node->name);
> +		return -1;
> +	}
> +
> +	if (!feature_lookup(arc, feature_node->name, &finfo, &slot)) {
> +		graph_err("%s feature already added", feature_node->name);
> +		return -1;
> +	}
> +
> +	if (slot >= RTE_GRAPH_FEATURE_MAX_PER_ARC) {
> +		graph_err("Max slot %u reached for feature addition", slot);
> +		return -1;
> +	}
> +
> +	if (strstr(feature_node->name, arc->start_node->name)) {
> +		graph_err("Feature %s cannot point to itself: %s",
> feature_node->name,
> +			arc->start_node->name);
> +		return -1;
> +	}
> +
> +	if (connect_graph_nodes(arc->start_node, feature_node, &edge, arc-
> >feature_arc_name)) {
> +		graph_err("unable to connect %s -> %s", arc->start_node-
> >name, feature_node->name);
> +		return -1;
> +	}
> +
> +	finfo = malloc(sizeof(*finfo));
> +	if (!finfo)
> +		return -1;
> +
> +	memset(finfo, 0, sizeof(*finfo));
> +
> +	finfo->feature_arc = (void *)arc;
> +	finfo->feature_node = feature_node;
> +	finfo->edge_to_this_feature = edge;
> +
> +	/* Check for before and after constraints */
> +	if (before_feature) {
> +		/* before_feature sanity */
> +		if (feature_lookup(arc, before_feature, &before_finfo, NULL))
> +			SET_ERR_JMP(EINVAL, finfo_free,
> +				     "Invalid before feature name: %s",
> before_feature);
> +
> +		if (!before_finfo)
> +			SET_ERR_JMP(EINVAL, finfo_free,
> +				     "before_feature %s does not exist",
> before_feature);
> +
> +		/*
> +		 * Starting from 0 to before_feature, continue connecting
> edges
> +		 */
> +		add_flag = 1;
> +		STAILQ_FOREACH(temp, &arc->all_features, next_feature) {
> +			/*
> +			 * As soon as we see before_feature. stop adding
> edges
> +			 */
> +			if (!strncmp(temp->feature_node->name,
> before_feature,
> +				     RTE_GRAPH_NAMESIZE))
> +				if (!connect_graph_nodes(finfo-
> >feature_node, temp->feature_node,
> +							 &edge, arc-
> >feature_arc_name))
> +					add_flag = 0;
> +
> +			if (add_flag)
> +				connect_graph_nodes(temp->feature_node,
> finfo->feature_node, NULL,
> +						    arc->feature_arc_name);
> +		}
> +	}
> +
> +	if (after_feature) {
> +		if (feature_lookup(arc, after_feature, &after_finfo, NULL))
> +			SET_ERR_JMP(EINVAL, finfo_free,
> +				     "Invalid after feature_name %s",
> after_feature);
> +
> +		if (!after_finfo)
> +			SET_ERR_JMP(EINVAL, finfo_free,
> +				     "after_feature %s does not exist",
> after_feature);
> +
> +		/* Starting from after_feature to end continue connecting
> edges */
> +		add_flag = 0;
> +		STAILQ_FOREACH(temp, &arc->all_features, next_feature) {
> +			/* We have already seen after_feature now */
> +			if (add_flag)
> +				/* Add all features as next node to current
> feature*/
> +				connect_graph_nodes(finfo->feature_node,
> temp->feature_node, NULL,
> +						    arc->feature_arc_name);
> +
> +			/* as soon as we see after_feature. start adding edges
> +			 * from next iteration
> +			 */
> +			if (!strncmp(temp->feature_node->name,
> after_feature, RTE_GRAPH_NAMESIZE))
> +				/* connect after_feature to this feature */
> +				if (!connect_graph_nodes(temp-
> >feature_node, finfo->feature_node,
> +							 &edge, arc-
> >feature_arc_name))
> +					add_flag = 1;
> +		}
> +
> +		/* add feature next to after_feature */
> +		STAILQ_INSERT_AFTER(&arc->all_features, after_finfo, finfo,
> next_feature);
> +	} else {
> +		if (before_finfo) {
> +			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);
> +
> +					return 0;
> +				}
> +				after_finfo = temp;
> +			}
> +		} else {
> +			STAILQ_INSERT_TAIL(&arc->all_features, finfo,
> next_feature);
> +		}
> +	}
> +
> +	return 0;
> +
> +finfo_free:
> +	free(finfo);
> +
> +	return -1;
> +}
> +
> +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 (!feature_lookup(arc, feature_name, &finfo, &slot)) {
> +		*feat = (rte_graph_feature_t) slot;
> +		return 0;
> +	}
> +
> +	return -1;
> +}
> +
> +int
> +rte_graph_feature_validate(rte_graph_feature_arc_t _arc, uint32_t index,
> const char *feature_name,
> +			   int is_enable_disable)
> +{
> +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +	struct rte_graph_feature_node_list *finfo = NULL;
> +	struct rte_graph_feature *gf = NULL;
> +	uint32_t slot;
> +
> +	/* validate _arc */
> +	if (arc->feature_arc_main != __feature_arc_main) {
> +		graph_err("invalid feature arc: 0x%016" PRIx64,
> (uint64_t)_arc);
> +		return -EINVAL;
> +	}
> +
> +	/* validate index */
> +	if (index >= arc->max_indexes) {
> +		graph_err("%s: Invalid provided index: %u >= %u configured",
> arc->feature_arc_name,
> +			index, arc->max_indexes);
> +		return -1;
> +	}
> +
> +	/* validate feature_name is already added or not  */
> +	if (feature_lookup(arc, feature_name, &finfo, &slot)) {
> +		graph_err("%s: No feature %s added", arc-
> >feature_arc_name, feature_name);
> +		return -EINVAL;
> +	}
> +
> +	if (!finfo) {
> +		graph_err("%s: No feature: %s found", arc-
> >feature_arc_name, feature_name);
> +		return -EINVAL;
> +	}
> +
> +	/* slot should be in valid range */
> +	if (slot >= arc->max_features) {
> +		graph_err("%s/%s: Invalid free slot %u(max=%u) for feature",
> arc->feature_arc_name,
> +			feature_name, slot, arc->max_features);
> +		return -EINVAL;
> +	}
> +
> +	/* slot should be in range of 0 - 63 */
> +	if (slot > (RTE_GRAPH_FEATURE_MAX_PER_ARC - 1)) {
> +		graph_err("%s/%s: Invalid slot: %u", arc->feature_arc_name,
> +			  feature_name, slot);
> +		return -EINVAL;
> +	}
> +
> +	if (finfo->node_index != slot) {
> +		graph_err("%s/%s: feature lookup slot mismatch with finfo
> index: %u and lookup slot: %u",
> +			  arc->feature_arc_name, feature_name, finfo-
> >node_index, slot);
> +		return -1;
> +	}
> +
> +	/* Get feature from active list */
> +	gf = __rte_graph_feature_get(arc, slot, ARC_PASSIVE_LIST(arc));
> +	if (gf->this_feature_index != slot) {
> +		graph_err("%s: %s received feature_index: %u does not match
> with saved feature_index: %u",
> +			  arc->feature_arc_name, feature_name, slot, gf-
> >this_feature_index);
> +		return -1;
> +	}
> +
> +	if (is_enable_disable && (arc->feature_bit_mask_by_index[index] &
> +				  RTE_BIT64(slot))) {
> +		graph_err("%s: %s already enabled on index: %u",
> +			  arc->feature_arc_name, feature_name, index);
> +		return -1;
> +	}
> +
> +	if (!is_enable_disable && !arc->runtime_enabled_features) {
> +		graph_err("%s: No feature enabled to disable", arc-
> >feature_arc_name);
> +		return -1;
> +	}
> +
> +	if (!is_enable_disable && !(arc->feature_bit_mask_by_index[index] &
> RTE_BIT64(slot))) {
> +		graph_err("%s: %s not enabled in bitmask for index: %u",
> +			  arc->feature_arc_name, feature_name, index);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static void
> +copy_fastpath_user_data(struct rte_graph_feature_arc *arc, uint16_t
> dest_list_index,
> +			uint16_t src_list_index)
> +{
> +	rte_graph_feature_data_t *sgfd = NULL, *dgfd = NULL;
> +	struct rte_graph_feature *sgf = NULL, *dgf = NULL;
> +	uint32_t i, j;
> +
> +	for (i = 0; i < arc->max_features; i++) {
> +		sgf = __rte_graph_feature_get(arc, i, src_list_index);
> +		dgf = __rte_graph_feature_get(arc, i, dest_list_index);
> +		for (j = 0; j < arc->max_indexes; j++) {
> +			sgfd = rte_graph_feature_data_get(arc, sgf, j);
> +			dgfd = rte_graph_feature_data_get(arc, dgf, j);
> +			dgfd->user_data = sgfd->user_data;
> +		}
> +	}
> +}
> +
> +static void
> +refill_feature_fastpath_data(struct rte_graph_feature_arc *arc, uint16_t
> list_index)
> +{
> +	struct rte_graph_feature_node_list *finfo = NULL, *prev_finfo = NULL;
> +	struct rte_graph_feature_data *gfd = NULL, *prev_gfd = NULL;
> +	struct rte_graph_feature *gf = NULL, *prev_gf = NULL;
> +	rte_graph_feature_list_t *flist = NULL;
> +	uint32_t fi, di, prev_fi;
> +	uint64_t bitmask;
> +	rte_edge_t edge;
> +
> +	flist = arc->feature_list[list_index];
> +
> +	for (di = 0; di < arc->max_indexes; di++) {
> +		bitmask = arc->feature_bit_mask_by_index[di];
> +		prev_fi = RTE_GRAPH_FEATURE_INVALID;
> +		/* for each feature set for index, set fast path data */
> +		while (rte_bsf64_safe(bitmask, &fi)) {
> +			gf = __rte_graph_feature_get(arc, fi, list_index);
> +			gfd = rte_graph_feature_data_get(arc, gf, di);
> +			feature_arc_node_info_lookup(arc, fi, &finfo);
> +
> +			/* If previous feature_index was valid in last loop */
> +			if (prev_fi != RTE_GRAPH_FEATURE_INVALID) {
> +				prev_gf = __rte_graph_feature_get(arc,
> prev_fi, list_index);
> +				prev_gfd = rte_graph_feature_data_get(arc,
> prev_gf, di);
> +				/*
> +				 * Get edge of previous feature node
> connecting to this feature node
> +				 */
> +				feature_arc_node_info_lookup(arc, prev_fi,
> &prev_finfo);
> +				if (!get_existing_edge(arc->feature_arc_name,
> +						      prev_finfo->feature_node,
> +						      finfo->feature_node,
> &edge)) {
> +					feat_dbg("[%s/%s(%2u)/idx:%2u]:
> %s[%u] = %s",
> +						 arc->feature_arc_name,
> +						 prev_finfo->feature_node-
> >name, prev_fi, di,
> +						 prev_finfo->feature_node-
> >name,
> +						 edge, finfo->feature_node-
> >name);
> +					/* Copy feature index for next
> iteration*/
> +					gfd->next_edge = edge;
> +					prev_fi = fi;
> +					/*
> +					 * Fill current feature as next enabled
> +					 * feature to previous one
> +					 */
> +					prev_gfd->next_enabled_feature = fi;
> +				} else {
> +					/* Should not fail */
> +					RTE_VERIFY(0);
> +				}
> +			}
> +			/* On first feature edge of the node to be added */
> +			if (fi == rte_bsf64(arc-
> >feature_bit_mask_by_index[di])) {
> +				if (!get_existing_edge(arc->feature_arc_name,
> arc->start_node,
> +						      finfo->feature_node,
> +						      &edge)) {
> +					feat_dbg("[%s/%s/%2u/idx:%2u]: 1st
> feat %s[%u] = %s",
> +						 arc->feature_arc_name,
> +						 arc->start_node->name, fi, di,
> +						 arc->start_node->name,
> edge,
> +						 finfo->feature_node->name);
> +					/* Copy feature index for next
> iteration*/
> +					gfd->next_edge = edge;
> +					prev_fi = fi;
> +					/* Set first feature set array for
> index*/
> +					flist-
> >first_enabled_feature_by_index[di] = fi;
> +				} else {
> +					/* Should not fail */
> +					RTE_VERIFY(0);
> +				}
> +			}
> +			/* Clear current feature index */
> +			bitmask &= ~RTE_BIT64(fi);
> +		}
> +	}
> +}
> +
> +int
> +rte_graph_feature_enable(rte_graph_feature_arc_t _arc, uint32_t index,
> const
> +			 char *feature_name, int32_t user_data)
> +{
> +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +	struct rte_graph_feature_node_list *finfo = NULL;
> +	struct rte_graph_feature_data *gfd = NULL;
> +	rte_graph_feature_rt_list_t passive_list;
> +	struct rte_graph_feature *gf = NULL;
> +	uint64_t fp_bitmask;
> +	uint32_t slot;
> +
> +	if (rte_graph_feature_validate(_arc, index, feature_name, 1))
> +		return -1;
> +
> +	/** This should not fail as validate() has passed */
> +	if (feature_lookup(arc, feature_name, &finfo, &slot))
> +		RTE_VERIFY(0);
> +
> +	if (!arc->runtime_enabled_features)
> +		prepare_feature_arc(arc);
> +
> +	passive_list = ARC_PASSIVE_LIST(arc);
> +
> +	gf = __rte_graph_feature_get(arc, slot, passive_list);
> +	gfd = rte_graph_feature_data_get(arc, gf, index);
> +
> +	feat_dbg("%s/%s: Enabling feature on list: %u for index: %u at feature
> slot %u",
> +		 arc->feature_arc_name, feature_name, passive_list, index,
> slot);
> +
> +	/* Reset feature list */
> +	feature_arc_list_reset(arc, passive_list);
> +
> +	/* Copy user-data */
> +	copy_fastpath_user_data(arc, passive_list, arc->active_feature_list);
> +
> +	/* Set current user-data */
> +	gfd->user_data = user_data;
> +
> +	/* Set bitmask in control path bitmask */
> +	rte_bit_relaxed_set64(rte_graph_uint_cast(slot), &arc-
> >feature_bit_mask_by_index[index]);
> +	refill_feature_fastpath_data(arc, passive_list);
> +
> +	/* Set fast path enable bitmask */
> +	fp_bitmask = __atomic_load_n(&arc-
> >feature_enable_bitmask[passive_list], __ATOMIC_RELAXED);
> +	fp_bitmask |= RTE_BIT64(slot);
> +	__atomic_store(&arc->feature_enable_bitmask[passive_list],
> &fp_bitmask, __ATOMIC_RELAXED);
> +
> +	/* Slow path updates */
> +	arc->runtime_enabled_features++;
> +
> +	/* Increase feature node info reference count */
> +	finfo->ref_count++;
> +
> +	/* Store release semantics for active_list update */
> +	__atomic_store(&arc->active_feature_list, &passive_list,
> __ATOMIC_RELEASE);
> +
> +	return 0;
> +}
> +
> +int
> +rte_graph_feature_disable(rte_graph_feature_arc_t _arc, uint32_t index,
> const char *feature_name)
> +{
> +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +	struct rte_graph_feature_data *gfd = NULL;
> +	struct rte_graph_feature_node_list *finfo = NULL;
> +	rte_graph_feature_rt_list_t passive_list;
> +	struct rte_graph_feature *gf = NULL;
> +	uint32_t slot;
> +
> +	if (rte_graph_feature_validate(_arc, index, feature_name, 0))
> +		return -1;
> +
> +	if (feature_lookup(arc, feature_name, &finfo, &slot))
> +		return -1;
> +
> +	passive_list = ARC_PASSIVE_LIST(arc);
> +
> +	gf = __rte_graph_feature_get(arc, slot, passive_list);
> +	gfd = rte_graph_feature_data_get(arc, gf, index);
> +
> +	feat_dbg("%s/%s: Disabling feature for index: %u at feature slot %u",
> arc->feature_arc_name,
> +		feature_name, index, slot);
> +
> +	rte_bit_relaxed_clear64(rte_graph_uint_cast(slot), &arc-
> >feature_bit_mask_by_index[index]);
> +
> +	/* Set fast path enable bitmask */
> +	arc->feature_enable_bitmask[passive_list] &= ~(RTE_BIT64(slot));
> +
> +	/* Reset feature list */
> +	feature_arc_list_reset(arc, passive_list);
> +
> +	/* Copy user-data */
> +	copy_fastpath_user_data(arc, passive_list, arc->active_feature_list);
> +
> +	/* Reset current user-data */
> +	gfd->user_data = ~0;
> +
> +	refill_feature_fastpath_data(arc, passive_list);
> +
> +	finfo->ref_count--;
> +	arc->runtime_enabled_features--;
> +
> +	/* Store release semantics for active_list update */
> +	__atomic_store(&arc->active_feature_list, &passive_list,
> __ATOMIC_RELEASE);
> +
> +	return 0;
> +}
> +
> +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 = __feature_arc_main;
> +	struct rte_graph_feature_node_list *node_info = NULL;
> +
> +	while (!STAILQ_EMPTY(&arc->all_features)) {
> +		node_info = STAILQ_FIRST(&arc->all_features);
> +		STAILQ_REMOVE_HEAD(&arc->all_features, next_feature);
> +		free(node_info);
> +	}
> +	feature_arc_list_destroy(arc->feature_list[0]);
> +	feature_arc_list_destroy(arc->feature_list[1]);
> +	rte_free(arc->features[0]);
> +	rte_free(arc->features[1]);
> +
> +	dm->feature_arcs[arc->feature_arc_index] =
> RTE_GRAPH_FEATURE_ARC_INITIALIZER;
> +
> +	rte_free(arc);
> +	return 0;
> +}
> +
> +int
> +rte_graph_feature_arc_cleanup(void)
> +{
> +	rte_graph_feature_arc_main_t *dm = __feature_arc_main;
> +	uint32_t iter;
> +
> +	if (!__feature_arc_main)
> +		return -1;
> +
> +	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> +		if (dm->feature_arcs[iter] ==
> RTE_GRAPH_FEATURE_ARC_INITIALIZER)
> +			continue;
> +
> +		rte_graph_feature_arc_destroy((rte_graph_feature_arc_t)dm-
> >feature_arcs[iter]);
> +	}
> +	free(dm);
> +
> +	__feature_arc_main = NULL;
> +
> +	return 0;
> +}
> +
> +int
> +rte_graph_feature_arc_lookup_by_name(const char *arc_name,
> rte_graph_feature_arc_t *_arc)
> +{
> +	rte_graph_feature_arc_main_t *dm = __feature_arc_main;
> +	struct rte_graph_feature_arc *arc = NULL;
> +	uint32_t iter;
> +
> +	if (!__feature_arc_main)
> +		return -1;
> +
> +	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> +		if (dm->feature_arcs[iter] ==
> RTE_GRAPH_FEATURE_ARC_INITIALIZER)
> +			continue;
> +
> +		arc = rte_graph_feature_arc_get(dm->feature_arcs[iter]);
> +
> +		if (strstr(arc_name, arc->feature_arc_name)) {
> +			if (_arc)
> +				*_arc = (rte_graph_feature_arc_t)arc;
> +			return 0;
> +		}
> +	}
> +
> +	return -1;
> +}
> +
> +int
> +rte_graph_feature_arc_num_enabled_features(rte_graph_feature_arc_t
> _arc)
> +{
> +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> +
> +	return arc->runtime_enabled_features;
> +}
> +
> +
> diff --git a/lib/graph/meson.build b/lib/graph/meson.build
> index 0cb15442ab..d916176fb7 100644
> --- a/lib/graph/meson.build
> +++ b/lib/graph/meson.build
> @@ -14,11 +14,13 @@ sources = files(
>          'graph_debug.c',
>          'graph_stats.c',
>          'graph_populate.c',
> +        'graph_feature_arc.c',
>          'graph_pcap.c',
>          'rte_graph_worker.c',
>          'rte_graph_model_mcore_dispatch.c',
>  )
>  headers = files('rte_graph.h', 'rte_graph_worker.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
> new file mode 100644
> index 0000000000..e3bf4eb73d
> --- /dev/null
> +++ b/lib/graph/rte_graph_feature_arc.h
> @@ -0,0 +1,373 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2024 Marvell International Ltd.
> + */
> +
> +#ifndef _RTE_GRAPH_FEATURE_ARC_H_
> +#define _RTE_GRAPH_FEATURE_ARC_H_
> +
> +#include <assert.h>
> +#include <errno.h>
> +#include <signal.h>
> +#include <stddef.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_compat.h>
> +#include <rte_debug.h>
> +#include <rte_graph.h>
> +#include <rte_graph_worker.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * @file
> + *
> + * rte_graph_feature_arc.h
> + *
> + * Define APIs and structures/variables with respect to feature arc
> + *
> + * - Feature arc(s)
> + * - Feature(s)
> + *
> + * A feature arc represents an ordered list of features/protocol-nodes at a
> + * given networking layer. Feature arc provides a high level abstraction to
> + * connect various *rte_graph* nodes, designated as *feature nodes*, and
> + * allowing steering of packets across these feature nodes fast path
> processing
> + * in a generic manner. In a typical network stack, often a protocol or feature
> + * must be first enabled on a given interface, before any packet is steered
> + * towards it for feature processing. For eg: incoming IPv4 packets are sent to
> + * routing sub-system only after a valid IPv4 address is assigned to the
> + * received interface. In other words, often packets needs to be steered across
> + * features not based on the packet content but based on whether a feature is
> + * enable or disable on a given incoming/outgoing interface. Feature arc
> + * provides mechanism to enable/disable feature(s) on each interface at
> runtime
> + * and allow seamless packet steering across runtime enabled feature nodes
> in
> + * fast path.
> + *
> + * Feature arc also provides a way to steer packets from standard nodes to
> + * custom/user-defined *feature nodes* without any change in standard
> node's
> + * fast path functions
> + *
> + * On a given interface multiple feature(s) might be enabled in a particular
> + * feature arc. For instance, both "ipv4-output" and "IPsec policy output"
> + * features may be enabled on "eth0" interface in "L3-output" feature arc.
> + * Similarly, "ipv6-output" and "ipsec-output" may be enabled on "eth1"
> + * interface in same "L3-output" feature arc.
> + *
> + * When multiple features are present in a given feature arc, its imperative
> + * to allow each feature processing in a particular sequential order. For
> + * instance, in "L3-input" feature arc it may be required to run "IPsec
> + * input" feature first, for packet decryption, before "ip-lookup".  So a
> + * sequential order must be maintained among features present in a feature
> arc.
> + *
> + * Features are enabled/disabled multiple times at runtime to some or all
> + * available interfaces present in the system. Features can be
> enabled/disabled
> + * even after @b rte_graph_create() is called. Enable/disabling features on
> one
> + * interface is independent of other interface.
> + *
> + * A given feature might consume packet (if it's configured to consume) or
> may
> + * forward it to next enabled feature. For instance, "IPsec input" feature may
> + * consume/drop all packets with "Protect" policy action while all packets with
> + * policy action as "Bypass" may be forwarded to next enabled feature (with
> in
> + * same feature arc)
> + *
> + * This library facilitates rte graph based applications to steer packets in
> + * fast path to different feature nodes with-in a feature arc and support all
> + * functionalities described above
> + *
> + * In order to use feature-arc APIs, applications needs to do following in
> + * control path:
> + * - Initialize feature arc library via rte_graph_feature_arc_init()
> + * - Create feature arc via rte_graph_feature_arc_create()
> + * - *Before calling rte_graph_create()*, features must be added to feature-
> arc
> + *   via rte_graph_feature_add(). rte_graph_feature_add() allows adding
> + *   features in a sequential order with "runs_after" and "runs_before"
> + *   constraints.
> + * - Post rte_graph_create(), features can be enabled/disabled at runtime on
> + *   any interface via rte_graph_feature_enable()/rte_graph_feature_disable()
> + * - Feature arc can be destroyed via rte_graph_feature_arc_destroy()
> + *
> + * In fast path, APIs are provided to steer packets towards feature path from
> + * - start_node (provided as an argument to rte_graph_feature_arc_create())
> + * - feature nodes (which are added via rte_graph_feature_add())
> + *
> + * For typical steering of packets across feature nodes, application required
> + * to know "rte_edges" which are saved in feature data object. Feature data
> + * object is unique for every interface per feature with in a feature arc.
> + *
> + * When steering packets from start_node to feature node:
> + * - rte_graph_feature_arc_first_feature_get() provides first enabled feature.
> + * - Next rte_edge from start_node to first enabled feature can be obtained
> via
> + *   rte_graph_feature_arc_feature_set()
> + *
> + * rte_mbuf can carry [current feature, index] from start_node of an arc to
> other
> + * feature nodes
> + *
> + * In feature node, application can get 32-bit user_data
> + * via_rte_graph_feature_user_data_get() which is provided in
> + * rte_graph_feature_enable(). User data can hold feature specific cookie like
> + * IPsec policy database index (if more than one are supported)
> + *
> + * If feature node is not consuming packet, next enabled feature and next
> + * rte_edge can be obtained via rte_graph_feature_arc_next_feature_get()
> + *
> + * It is application responsibility to ensure that at-least *last feature*(or sink
> + * feature) must be enabled from where packet can exit feature-arc path, if
> + * *NO* intermediate feature is consuming the packet and it has reached till
> + * the end of feature arc path
> + *
> + * Synchronization among cores
> + * ---------------------------
> + * Subsequent calls to rte_graph_feature_enable() is allowed while worker
> cores
> + * are processing in rte_graph_walk() loop. However, for
> + * rte_graph_feature_disable() application must use RCU based
> synchronization
> + */
> +
> +/**< Initializer value for rte_graph_feature_arc_t */
> +#define RTE_GRAPH_FEATURE_ARC_INITIALIZER
> ((rte_graph_feature_arc_t)UINT64_MAX)
> +
> +/** Max number of features supported in a given feature arc */
> +#define RTE_GRAPH_FEATURE_MAX_PER_ARC 64
> +
> +/** Length of feature arc name */
> +#define RTE_GRAPH_FEATURE_ARC_NAMELEN RTE_NODE_NAMESIZE
> +
> +/** @internal */
> +#define rte_graph_feature_cast(x) ((rte_graph_feature_t)x)
> +
> +/**< Initializer value for rte_graph_feature_arc_t */
> +#define RTE_GRAPH_FEATURE_INVALID
> rte_graph_feature_cast(UINT8_MAX)
> +
> +/** rte_graph feature arc object */
> +typedef uint64_t rte_graph_feature_arc_t;
> +
> +/** rte_graph feature object */
> +typedef uint8_t rte_graph_feature_t;
> +
> +/** runtime active feature list index with in feature arc*/
> +typedef uint8_t rte_graph_feature_rt_list_t;
> +
> +/** per feature arc monotonically increasing counter to synchronize fast path
> APIs */
> +typedef uint16_t rte_graph_feature_counter_t;
> +
> +/**
> + * Initialize feature arc subsystem
> + *
> + * @param max_feature_arcs
> + *   Maximum number of feature arcs required to be supported
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_arc_init(int max_feature_arcs);
> +
> +/**
> + * Create a feature arc
> + *
> + * @param feature_arc_name
> + *   Feature arc name with max length of @ref
> RTE_GRAPH_FEATURE_ARC_NAMELEN
> + * @param max_features
> + *   Maximum number of features to be supported in this feature arc
> + * @param max_indexes
> + *   Maximum number of interfaces/ports/indexes to be supported
> + * @param start_node
> + *   Base node where this feature arc's features are checked in fast path
> + * @param[out] _arc
> + *  Feature arc object
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_arc_create(const char *feature_arc_name, int
> max_features, int max_indexes,
> +				 struct rte_node_register *start_node,
> +				 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
> + *
> + * @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. For instance
> + *
> + * 1. Add first feature node: "ipv4-input" to input arc
> + *    rte_graph_feature_add(ipv4_input_arc, "ipv4-input", NULL, NULL);
> + *
> + * 2. Add "ipsec-input" feature node after "ipv4-input" node
> + *    rte_graph_feature_add(ipv4_input_arc, "ipsec-input", "ipv4-input",
> NULL);
> + *
> + * 3. Add "ipv4-pre-classify-input" node before "ipv4-input" node
> + *    rte_graph_feature_add(ipv4_input_arc, "ipv4-pre-classify-input"", NULL,
> "ipv4-input");
> + *
> + * 4. Add "acl-classify-input" node after ipv4-input but before ipsec-input
> + *    rte_graph_feature_add(ipv4_input_arc, "acl-classify-input", "ipv4-input",
> "ipsec-input");
> + *
> + * @param _arc
> + *   Feature arc handle returned from @ref rte_graph_feature_arc_create()
> + * @param feature_node
> + *   Graph node representing feature. On success, feature_node is next_node
> of
> + *   feature_arc->start_node
> + * @param runs_after
> + *   Add this feature_node after already added "runs_after". Creates
> + *   start_node -> runs_after -> this_feature sequence
> + * @param runs_before
> + *  Add this feature_node before already added "runs_before". Creates
> + *  start_node -> this_feature -> runs_before sequence
> + *
> + * <I> Must be called before rte_graph_create() </I>
> + * <I> rte_graph_feature_add() is not allowed after call to
> + * rte_graph_feature_enable() so all features must be added before they can
> be
> + * enabled </I>
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_add(rte_graph_feature_arc_t _arc, struct
> rte_node_register *feature_node,
> +		    const char *runs_after, const char *runs_before);
> +
> +/**
> + * Enable feature within a feature arc
> + *
> + * Must be called after @b rte_graph_create().
> + *
> + * @param _arc
> + *   Feature arc object returned by @ref rte_graph_feature_arc_create or
> @ref
> + *   rte_graph_feature_arc_lookup_by_name
> + * @param index
> + *   Application specific index. Can be corresponding to interface_id/port_id
> etc
> + * @param feature_name
> + *   Name of the node which is already added via @ref rte_graph_feature_add
> + * @param user_data
> + *   Application specific data which is retrieved in fast path
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_enable(rte_graph_feature_arc_t _arc, uint32_t index,
> const char *feature_name,
> +			     int32_t user_data);
> +
> +/**
> + * Validate whether subsequent enable/disable feature would succeed or not.
> + * API is thread-safe
> + *
> + * @param _arc
> + *   Feature arc object returned by @ref rte_graph_feature_arc_create or
> @ref
> + *   rte_graph_feature_arc_lookup_by_name
> + * @param index
> + *   Application specific index. Can be corresponding to interface_id/port_id
> etc
> + * @param feature_name
> + *   Name of the node which is already added via @ref rte_graph_feature_add
> + * @param is_enable_disable
> + *   If 1, validate whether subsequent @ref rte_graph_feature_enable would
> pass or not
> + *   If 0, validate whether subsequent @ref rte_graph_feature_disable would
> pass or not
> + *
> + * @return
> + *  0: Subsequent enable/disable API would pass
> + * <0: Subsequent enable/disable API would not pass
> + */
> +__rte_experimental
> +int rte_graph_feature_validate(rte_graph_feature_arc_t _arc, uint32_t index,
> +			       const char *feature_name, int is_enable_disable);
> +
> +/**
> + * Disable already enabled feature within a feature arc
> + *
> + * Must be called after @b rte_graph_create(). API is *NOT* Thread-safe
> + *
> + * @param _arc
> + *   Feature arc object returned by @ref rte_graph_feature_arc_create or
> @ref
> + *   rte_graph_feature_arc_lookup_by_name
> + * @param index
> + *   Application specific index. Can be corresponding to interface_id/port_id
> etc
> + * @param feature_name
> + *   Name of the node which is already added via @ref rte_graph_feature_add
> + *
> + * @return
> + *  0: Success
> + * <0: Failure
> + */
> +__rte_experimental
> +int rte_graph_feature_disable(rte_graph_feature_arc_t _arc, uint32_t index,
> +			      const char *feature_name);
> +
> +/**
> + * 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 currently enabled within a
> featur-arc
> + *
> + * @param _arc
> + *  Feature arc object
> + *
> + * @return: Number of enabled features
> + */
> +__rte_experimental
> +int rte_graph_feature_arc_num_enabled_features(rte_graph_feature_arc_t
> _arc);
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> 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..6019d74853
> --- /dev/null
> +++ b/lib/graph/rte_graph_feature_arc_worker.h
> @@ -0,0 +1,548 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2024 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>
> +
> +/**
> + * @file
> + *
> + * rte_graph_feature_arc_worker.h
> + *
> + * Defines fast path structure
> + */
> +
> +#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;
> +
> +	/** node representing feature */
> +	struct rte_node_register *feature_node;
> +
> +	/** How many indexes/interfaces using this feature */
> +	int32_t ref_count;
> +
> +	/* node_index in list (after feature_enable())*/
> +	uint32_t node_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;
> +};
> +
> +/**
> + * Fast path holding rte_edge_t and next enabled feature for an feature
> + */
> +typedef struct __rte_packed rte_graph_feature_data {
> +	/* next node to which current mbuf should go*/
> +	rte_edge_t next_edge;
> +
> +	/* next enabled feature on this arc for current index */
> +	union {
> +		uint16_t reserved;
> +		struct {
> +			rte_graph_feature_t next_enabled_feature;
> +		};
> +	};
> +
> +	/* user_data */
> +	int32_t user_data;
> +} rte_graph_feature_data_t;
> +
> +/**
> + * Fast path feature structure. Holds re_graph_feature_data_t per index
> + */
> +struct __rte_cache_aligned rte_graph_feature {
> +	uint16_t this_feature_index;
> +
> +	/* Array of size arc->feature_data_size
> +	 * [data-index-0][data-index-1]...
> +	 * Each index of size: sizeof(rte_graph_feature_data_t)
> +	 */
> +	uint8_t feature_data_by_index[];
> +};
> +
> +/**
> + * fast path cache aligned feature list holding all features
> + * There are two feature lists: active, passive
> + *
> + * Fast APIs works on active list while control plane updates passive list
> + * A atomic update to arc->active_feature_list is done to switch between
> active
> + * and passive
> + */
> +typedef struct __rte_cache_aligned rte_graph_feature_list {
> +	/**
> +	 * fast path array holding per_feature data.
> +	 * Duplicate entry as feature-arc also hold this pointer
> +	 * arc->features[]
> +	 *
> +	 *<-------------feature-0 ---------><CEIL><---------feature-1 --------------
> >...
> +	 *[index-0][index-1]...[max_index-1]      [index-0][index-1]
> ...[max_index-1]...
> +	 */
> +	struct rte_graph_feature *indexed_by_features;
> +	/*
> +	 * fast path array holding first enabled feature per index
> +	 * (Required in start_node. In non start_node, mbuf can hold next
> enabled
> +	 * feature)
> +	 */
> +	rte_graph_feature_t first_enabled_feature_by_index[];
> +} rte_graph_feature_list_t;
> +
> +/**
> + * rte_graph feature arc object
> + *
> + * A feature-arc can only hold RTE_GRAPH_FEATURE_MAX_PER_ARC features
> but no
> + * limit to interface index
> + *
> + * Representing a feature arc holding all features which are enabled/disabled
> + * on any interfaces
> + */
> +struct __rte_cache_aligned rte_graph_feature_arc {
> +	/* First 64B is fast path variables */
> +	RTE_MARKER fast_path_variables;
> +
> +	/** runtime active feature list */
> +	rte_graph_feature_rt_list_t active_feature_list;
> +
> +	/* Actual Size of feature_list0 */
> +	uint16_t feature_list_size;
> +
> +	/**
> +	 * Size each feature in fastpath.
> +	 * sizeof(arc->active_list->indexed_by_feature[0])
> +	 */
> +	uint16_t feature_size;
> +
> +	/* Size of arc->max_index * sizeof(rte_graph_feature_data_t) */
> +	uint16_t feature_data_size;
> +
> +	/**
> +	 * Fast path bitmask indicating if a feature is enabled or not Number
> +	 * of bits: RTE_GRAPH_FEATURE_MAX_PER_ARC
> +	 */
> +	uint64_t feature_enable_bitmask[2];
> +	rte_graph_feature_list_t *feature_list[2];
> +	struct rte_graph_feature *features[2];
> +
> +	/** index in feature_arc_main */
> +	uint16_t feature_arc_index;
> +
> +	uint16_t reserved[3];
> +
> +	/** Slow path variables follows*/
> +	RTE_MARKER slow_path_variables;
> +
> +	/** feature arc name */
> +	char feature_arc_name[RTE_GRAPH_FEATURE_ARC_NAMELEN];
> +
> +	/** All feature lists */
> +	STAILQ_HEAD(, rte_graph_feature_node_list) all_features;
> +
> +	uint32_t runtime_enabled_features;
> +
> +	/** Back pointer to feature_arc_main */
> +	void *feature_arc_main;
> +
> +	/* start_node */
> +	struct rte_node_register *start_node;
> +
> +	/* maximum number of features supported by this arc */
> +	uint32_t max_features;
> +
> +	/* maximum number of index supported by this arc */
> +	uint32_t max_indexes;
> +
> +	/* Slow path bit mask per feature per index */
> +	uint64_t feature_bit_mask_by_index[];
> +};
> +
> +/** Feature arc main */
> +typedef struct feature_arc_main {
> +	/** number of feature arcs created by application */
> +	uint32_t num_feature_arcs;
> +
> +	/** max features arcs allowed */
> +	uint32_t max_feature_arcs;
> +
> +	/** feature arcs */
> +	rte_graph_feature_arc_t feature_arcs[];
> +} rte_graph_feature_arc_main_t;
> +
> +/** @internal Get feature arc pointer from object */
> +#define rte_graph_feature_arc_get(arc) ((struct rte_graph_feature_arc *)arc)
> +
> +extern rte_graph_feature_arc_main_t *__feature_arc_main;
> +
> +/**
> + * API to know if feature is valid or not
> + */
> +
> +static __rte_always_inline int
> +rte_graph_feature_is_valid(rte_graph_feature_t feature)
> +{
> +	return (feature != RTE_GRAPH_FEATURE_INVALID);
> +}
> +
> +/**
> + * Get rte_graph_feature object with no checks
> + *
> + * @param arc
> + *   Feature arc pointer
> + * @param feature
> + *   Feature index
> + * @param feature_list
> + *   active feature list retrieved from
> rte_graph_feature_arc_has_any_feature()
> + *   or rte_graph_feature_arc_has_feature()
> + *
> + *   @return
> + *     Internal feature object.
> + */
> +static __rte_always_inline struct rte_graph_feature *
> +__rte_graph_feature_get(struct rte_graph_feature_arc *arc,
> rte_graph_feature_t feature,
> +			const rte_graph_feature_rt_list_t feature_list)
> +{
> +	return ((struct rte_graph_feature *)((uint8_t *)(arc-
> >features[feature_list] +
> +					     (feature * arc->feature_size))));
> +}
> +
> +/**
> + * Get rte_graph_feature object for a given interface/index from feature arc
> + *
> + * @param arc
> + *   Feature arc pointer
> + * @param feature
> + *   Feature index
> + *
> + *   @return
> + *     Internal feature object.
> + */
> +static __rte_always_inline struct rte_graph_feature *
> +rte_graph_feature_get(struct rte_graph_feature_arc *arc,
> rte_graph_feature_t feature)
> +{
> +	RTE_VERIFY(feature < arc->max_features);
> +
> +	if (likely(rte_graph_feature_is_valid(feature)))
> +		return __rte_graph_feature_get(arc, feature, arc-
> >active_feature_list);
> +
> +	return NULL;
> +}
> +
> +static __rte_always_inline rte_graph_feature_data_t *
> +__rte_graph_feature_data_get(struct rte_graph_feature_arc *arc, struct
> rte_graph_feature *feature,
> +			     uint8_t index)
> +{
> +	RTE_SET_USED(arc);
> +	return ((rte_graph_feature_data_t *)(feature->feature_data_by_index
> +
> +					   (index *
> sizeof(rte_graph_feature_data_t))));
> +}
> +
> +/**
> + * Get rte_graph feature data object for a index in feature
> + *
> + * @param arc
> + *   feature arc
> + * @param feature
> + *  Pointer to feature object
> + * @param index
> + *  Index of feature maintained in slow path linked list
> + *
> + * @return
> + *   Valid feature data
> + */
> +static __rte_always_inline rte_graph_feature_data_t *
> +rte_graph_feature_data_get(struct rte_graph_feature_arc *arc, struct
> rte_graph_feature *feature,
> +			   uint8_t index)
> +{
> +	if (likely(index < arc->max_indexes))
> +		return __rte_graph_feature_data_get(arc, feature, index);
> +
> +	RTE_VERIFY(0);
> +}
> +
> +/**
> + * Fast path API to check if any feature enabled on a feature arc
> + * Typically from arc->start_node process function
> + *
> + * @param arc
> + *   Feature arc object
> + * @param[out] plist
> + *   Pointer to runtime active feature list which needs to be provided to other
> + *   fast path APIs
> + *
> + * @return
> + * 0: If no feature enabled
> + * Non-Zero: Bitmask of features enabled. plist is valid
> + *
> + */
> +static __rte_always_inline uint64_t
> +rte_graph_feature_arc_has_any_feature(struct rte_graph_feature_arc *arc,
> +				      rte_graph_feature_rt_list_t *plist)
> +{
> +	*plist = __atomic_load_n(&arc->active_feature_list,
> __ATOMIC_RELAXED);
> +
> +	return (__atomic_load_n(arc->feature_enable_bitmask +
> (uint8_t)*plist,
> +				__ATOMIC_RELAXED));
> +}
> +
> +/**
> + * Fast path API to check if provided feature is enabled on any interface/index
> + * or not
> + *
> + * @param arc
> + *   Feature arc object
> + * @param feature
> + *   Input rte_graph_feature_t that needs to be checked
> + * @param[out] plist
> + *  Returns active list to caller which needs to be provided to other fast path
> + *  APIs
> + *
> + * @return
> + * 1: If feature is enabled in arc
> + * 0: If feature is not enabled in arc
> + */
> +static __rte_always_inline int
> +rte_graph_feature_arc_has_feature(struct rte_graph_feature_arc *arc,
> +				  rte_graph_feature_t feature,
> +				  rte_graph_feature_rt_list_t *plist)
> +{
> +	uint64_t bitmask = RTE_BIT64(feature);
> +
> +	*plist = __atomic_load_n(&arc->active_feature_list,
> __ATOMIC_RELAXED);
> +
> +	return (bitmask & __atomic_load_n(arc->feature_enable_bitmask +
> (uint8_t)*plist,
> +					  __ATOMIC_RELAXED));
> +}
> +
> +/**
> + * Prefetch feature arc fast path cache line
> + *
> + * @param arc
> + *   RTE_GRAPH feature arc object
> + */
> +static __rte_always_inline void
> +rte_graph_feature_arc_prefetch(struct rte_graph_feature_arc *arc)
> +{
> +	rte_prefetch0((void *)&arc->fast_path_variables);
> +}
> +
> +/**
> + * Prefetch feature related fast path cache line
> + *
> + * @param arc
> + *   RTE_GRAPH feature arc object
> + * @param list
> + *  Pointer to runtime active feature list from
> rte_graph_feature_arc_has_any_feature();
> + * @param feature
> + *   Pointer to feature object
> + */
> +static __rte_always_inline void
> +rte_graph_feature_arc_feature_prefetch(struct rte_graph_feature_arc *arc,
> +				       const rte_graph_feature_rt_list_t list,
> +				       rte_graph_feature_t feature)
> +{
> +	/* feature cache line */
> +	if (likely(rte_graph_feature_is_valid(feature)))
> +		rte_prefetch0((void *)__rte_graph_feature_get(arc, feature,
> list));
> +}
> +
> +/**
> + * Prefetch feature data upfront. Perform sanity
> + *
> + * @param _arc
> + *   RTE_GRAPH feature arc object
> + * @param list
> + *  Pointer to runtime active feature list from
> rte_graph_feature_arc_has_any_feature();
> + * @param feature
> + *   Pointer to feature object returned from @ref
> + *   rte_graph_feature_arc_first_feature_get()
> + * @param index
> + *   Interface/index
> + */
> +static __rte_always_inline void
> +rte_graph_feature_arc_data_prefetch(struct rte_graph_feature_arc *arc,
> +				    const rte_graph_feature_rt_list_t list,
> +				    rte_graph_feature_t feature, uint32_t index)
> +{
> +	if (likely(rte_graph_feature_is_valid(feature)))
> +		rte_prefetch0((void *)((uint8_t *)arc->features[list] +
> +			      offsetof(struct rte_graph_feature,
> feature_data_by_index) +
> +			      (index * sizeof(rte_graph_feature_data_t))));
> +}
> +
> +/**
> + * Fast path API to get first enabled feature on interface index
> + * Typically required in arc->start_node so that from returned feature,
> + * feature-data can be retrieved to steer packets
> + *
> + * @param arc
> + *   Feature arc object
> + * @param list
> + *   Pointer to runtime active feature list from
> + *   rte_graph_feature_arc_has_any_feature() or
> + *   rte_graph_feature_arc_has_feature()
> + * @param index
> + *  Interface Index
> + * @param[out] feature
> + *  Pointer to rte_graph_feature_t.
> + *
> + * @return
> + * 0. Success. feature field is valid
> + * 1. Failure. feature field is invalid
> + *
> + */
> +static __rte_always_inline int
> +rte_graph_feature_arc_first_feature_get(struct rte_graph_feature_arc *arc,
> +					const rte_graph_feature_rt_list_t list,
> +					uint32_t index,
> +					rte_graph_feature_t *feature)
> +{
> +	struct rte_graph_feature_list *feature_list = arc->feature_list[list];
> +
> +	*feature = feature_list->first_enabled_feature_by_index[index];
> +
> +	return rte_graph_feature_is_valid(*feature);
> +}
> +
> +/**
> + * Fast path API to get next enabled feature on interface index with provided
> + * input feature
> + *
> + * @param arc
> + *   Feature arc object
> + * @param list
> + *   Pointer to runtime active feature list from
> + *   rte_graph_feature_arc_has_any_feature() or
> + * @param index
> + *   Interface Index
> + * @param[in][out] feature
> + *   Pointer to rte_graph_feature_t. Input feature set to next enabled feature
> + *   after success return
> + * @param[out] next_edge
> + *    Edge from current feature to next feature. Valid only if next feature is
> valid
> + *
> + * @return
> + * 0. Success. next enabled feature is valid.
> + * 1. Failure. next enabled feature is invalid
> + */
> +static __rte_always_inline int
> +rte_graph_feature_arc_next_feature_get(struct rte_graph_feature_arc *arc,
> +				       const rte_graph_feature_rt_list_t list,
> +				       uint32_t index,
> +				       rte_graph_feature_t *feature,
> +				       rte_edge_t *next_edge)
> +{
> +	rte_graph_feature_data_t *feature_data = NULL;
> +	struct rte_graph_feature *f = NULL;
> +
> +	if (likely(rte_graph_feature_is_valid(*feature))) {
> +		f = __rte_graph_feature_get(arc, *feature, list);
> +		feature_data = rte_graph_feature_data_get(arc, f, index);
> +		*feature = feature_data->next_enabled_feature;
> +		*next_edge = feature_data->next_edge;
> +		return (*feature == RTE_GRAPH_FEATURE_INVALID);
> +	}
> +
> +	return 1;
> +}
> +
> +/**
> + * Set fields with respect to first enabled feature in an arc and return edge
> + * Typically returned feature and interface index must be saved in rte_mbuf
> + * structure to pass this information to next feature node
> + *
> + * @param arc
> + *   Feature arc object
> + * @param list
> + *   Pointer to runtime active feature list from
> rte_graph_feature_arc_has_any_feature();
> + * @param index
> + *  Index (of interface)
> + * @param[out] gf
> + *  Pointer to rte_graph_feature_t. Valid if API returns Success
> + * @param[out] edge
> + *  Edge to steer packet from arc->start_node to first enabled feature. Valid
> + *  only if API returns Success
> + *
> + * @return
> + * 0: If valid feature is set by API
> + * 1: If valid feature is NOT set by API
> + */
> +static __rte_always_inline rte_graph_feature_t
> +rte_graph_feature_arc_feature_set(struct rte_graph_feature_arc *arc,
> +				  const rte_graph_feature_rt_list_t list,
> +				  uint32_t index,
> +				  rte_graph_feature_t *gf,
> +				  rte_edge_t *edge)
> +{
> +	struct rte_graph_feature_list *feature_list = arc->feature_list[list];
> +	struct rte_graph_feature_data *feature_data = NULL;
> +	struct rte_graph_feature *feature = NULL;
> +	rte_graph_feature_t f;
> +
> +	/* reset */
> +	*gf = RTE_GRAPH_FEATURE_INVALID;
> +	f = feature_list->first_enabled_feature_by_index[index];
> +
> +	if (unlikely(rte_graph_feature_is_valid(f))) {
> +		feature = __rte_graph_feature_get(arc, f, list);
> +		feature_data = rte_graph_feature_data_get(arc, feature,
> index);
> +		*gf = f;
> +		*edge = feature_data->next_edge;
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> +
> +/**
> + * Get user data corresponding to current feature set by application in
> + * rte_graph_feature_enable()
> + *
> + * @param arc
> + *  Feature arc object
> + * @param list
> + *  Pointer to runtime active feature list from
> rte_graph_feature_arc_has_any_feature();
> + * @param feature
> + *  Feature index
> + * @param index
> + *  Interface index
> + *
> + *  @return
> + *  UINT32_MAX: Failure
> + *  Valid user data: Success
> + */
> +static __rte_always_inline uint32_t
> +rte_graph_feature_user_data_get(struct rte_graph_feature_arc *arc,
> +				const rte_graph_feature_rt_list_t list,
> +				rte_graph_feature_t feature,
> +				uint32_t index)
> +{
> +	rte_graph_feature_data_t *fdata = NULL;
> +	struct rte_graph_feature *f = NULL;
> +
> +	if (likely(rte_graph_feature_is_valid(feature))) {
> +		f = __rte_graph_feature_get(arc, feature, list);
> +		fdata = rte_graph_feature_data_get(arc, f, index);
> +		return fdata->user_data;
> +	}
> +
> +	return UINT32_MAX;
> +}
> +#ifdef __cplusplus
> +}
> +#endif
> +#endif
> diff --git a/lib/graph/version.map b/lib/graph/version.map
> index 2c83425ddc..82b2469fba 100644
> --- a/lib/graph/version.map
> +++ b/lib/graph/version.map
> @@ -52,3 +52,20 @@ DPDK_25 {
> 
>  	local: *;
>  };
> +
> +EXPERIMENTAL {
> +	global:
> +
> +	# added in 24.11
> +	rte_graph_feature_arc_init;
> +	rte_graph_feature_arc_create;
> +	rte_graph_feature_arc_lookup_by_name;
> +	rte_graph_feature_add;
> +	rte_graph_feature_enable;
> +	rte_graph_feature_validate;
> +	rte_graph_feature_disable;
> +	rte_graph_feature_lookup;
> +	rte_graph_feature_arc_destroy;
> +	rte_graph_feature_arc_cleanup;
> +	rte_graph_feature_arc_num_enabled_features;
> +};
> --
> 2.43.0
  
Nitin Saxena Oct. 10, 2024, 4:42 a.m. UTC | #2
Hi Kiran,

See my inline comments. Somehow  I forgot to respond earlier

Thanks,
Nitin

> -----Original Message-----
> From: Kiran Kumar Kokkilagadda <kirankumark@marvell.com>
> Sent: Wednesday, September 11, 2024 10:11 AM
> To: Nitin Saxena <nsaxena@marvell.com>; Jerin Jacob <jerinj@marvell.com>;
> Nithin Kumar Dabilpuram <ndabilpuram@marvell.com>; Zhirun Yan
> <yanzhirun_163@163.com>
> Cc: dev@dpdk.org; Nitin Saxena <nsaxena16@gmail.com>
> Subject: RE: [RFC PATCH 1/3] graph: add feature arc support
> 
> 
> 
> > -----Original Message-----
> > From: Nitin Saxena <nsaxena@marvell.com>
> > Sent: Saturday, September 7, 2024 1:01 PM
> > To: Jerin Jacob <jerinj@marvell.com>; Kiran Kumar Kokkilagadda
> > <kirankumark@marvell.com>; Nithin Kumar Dabilpuram
> > <ndabilpuram@marvell.com>; Zhirun Yan <yanzhirun_163@163.com>
> > Cc: dev@dpdk.org; Nitin Saxena <nsaxena16@gmail.com>
> > Subject: [RFC PATCH 1/3] graph: add feature arc support
> >
> > add feature arc to allow dynamic steering of packets across graph nodes
> > based on protocol features enabled on incoming or outgoing interface
> >
> > Signed-off-by: Nitin Saxena <nsaxena@marvell.com>
> > ---
> >  lib/graph/graph_feature_arc.c            | 959 +++++++++++++++++++++++
> >  lib/graph/meson.build                    |   2 +
> >  lib/graph/rte_graph_feature_arc.h        | 373 +++++++++
> >  lib/graph/rte_graph_feature_arc_worker.h | 548 +++++++++++++
> >  lib/graph/version.map                    |  17 +
> >  5 files changed, 1899 insertions(+)
> >  create mode 100644 lib/graph/graph_feature_arc.c
> >  create mode 100644 lib/graph/rte_graph_feature_arc.h
> >  create mode 100644 lib/graph/rte_graph_feature_arc_worker.h
> >
> > diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
> > new file mode 100644
> > index 0000000000..3b05bac137
> > --- /dev/null
> > +++ b/lib/graph/graph_feature_arc.c
> > @@ -0,0 +1,959 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(C) 2024 Marvell International Ltd.
> > + */
> > +
> > +#include "graph_private.h"
> > +#include <rte_graph_feature_arc_worker.h>
> > +#include <rte_malloc.h>
> > +
> > +#define __RTE_GRAPH_FEATURE_ARC_MAX 32
> > +
> > +#define ARC_PASSIVE_LIST(arc) (arc->active_feature_list ^ 0x1)
> > +
> > +#define rte_graph_uint_cast(x) ((unsigned int)x)
> > +#define feat_dbg graph_err
> > +
> > +rte_graph_feature_arc_main_t *__feature_arc_main;
> > +
> > +/* Make sure fast path cache line is compact */
> > +_Static_assert((offsetof(struct rte_graph_feature_arc,
> slow_path_variables)
> > +		- offsetof(struct rte_graph_feature_arc, fast_path_variables))
> > +	       <= RTE_CACHE_LINE_SIZE);
> > +
> > +
> > +static int
> > +feature_lookup(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;
> > +	const char *name;
> > +
> > +	if (!feat_name)
> > +		return -1;
> > +
> > +	if (slot)
> > +		*slot = 0;
> > +
> > +	STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> > +		RTE_VERIFY(finfo->feature_arc == arc);
> > +		name = rte_node_id_to_name(finfo->feature_node->id);
> > +		if (!strncmp(name, feat_name, RTE_GRAPH_NAMESIZE)) {
> > +			if (ffinfo)
> > +				*ffinfo = finfo;
> > +			return 0;
> > +		}
> > +		if (slot)
> > +			(*slot)++;
> > +	}
> > +	return -1;
> > +}
> > +
> > +static int
> > +feature_arc_node_info_lookup(struct rte_graph_feature_arc *arc, uint32_t
> > feature_index,
> > +			     struct rte_graph_feature_node_list **ppfinfo)
> > +{
> > +	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) {
> > +		if (index == feature_index) {
> > +			if (finfo->node_index == feature_index)
> > +				return -1;
> > +			*ppfinfo = finfo;
> > +		}
> > +		index++;
> > +	}
> > +	if (feature_index && (index >= feature_index))
> > +		return -1;
> > +
> > +	return 0;
> > +}
> > +
> > +static void
> > +prepare_feature_arc(struct rte_graph_feature_arc *arc)
> > +{
> > +	struct rte_graph_feature_node_list *finfo = NULL;
> > +	uint32_t index = 0;
> > +
> > +	STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> > +		finfo->node_index = index;
> > +		index++;
> > +	}
> > +}
> > +
> > +static int
> > +feature_arc_lookup(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 = __feature_arc_main;
> > +	uint32_t iter;
> > +
> > +	if (!__feature_arc_main)
> > +		return -1;
> > +
> > +	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> > +		if (dm->feature_arcs[iter] ==
> > RTE_GRAPH_FEATURE_ARC_INITIALIZER)
> > +			continue;
> > +
> > +		if (arc == (rte_graph_feature_arc_get(dm-
> > >feature_arcs[iter])))
> > +			return 0;
> > +	}
> > +	return -1;
> > +}
> > +
> > +static int
> > +get_existing_edge(const char *arc_name, struct rte_node_register
> > *parent_node,
> > +		  struct rte_node_register *child_node, rte_edge_t *_edge)
> > +{
> > +	char **next_edges = NULL;
> > +	uint32_t count, i;
> > +
> > +	RTE_SET_USED(arc_name);
> > +
> > +	count = rte_node_edge_get(parent_node->id, NULL);
> > +	next_edges = malloc(count);
> > +
> > +	if (!next_edges)
> > +		return -1;
> > +
> > +	count = rte_node_edge_get(parent_node->id, next_edges);
> > +	for (i = 0; i < count; i++) {
> > +		if (strstr(child_node->name, next_edges[i])) {
> > +			feat_dbg("%s: Edge exists [%s[%u]: \"%s\"]",
> > arc_name,
> > +				  parent_node->name, i, child_node->name);
> > +			if (_edge)
> > +				*_edge = (rte_edge_t)i;
> > +
> > +			free(next_edges);
> > +			return 0;
> > +		}
> > +	}
> > +	free(next_edges);
> > +
> > +	return -1;
> > +}
> > +
> > +static int
> > +connect_graph_nodes(struct rte_node_register *parent_node, struct
> > rte_node_register *child_node,
> > +		    rte_edge_t *_edge, char *arc_name)
> > +{
> > +	const char *next_node = NULL;
> > +	rte_edge_t edge;
> > +
> > +	if (!get_existing_edge(arc_name, parent_node, child_node, &edge)) {
> > +		feat_dbg("%s: add_feature: Edge reused [%s[%u]: \"%s\"]",
> > arc_name,
> > +			parent_node->name, edge, child_node->name);
> > +
> > +		if (_edge)
> > +			*_edge = edge;
> > +
> > +		return 0;
> > +	}
> > +
> > +	/* Node to be added */
> > +	next_node = child_node->name;
> > +
> > +	edge = rte_node_edge_update(parent_node->id,
> > 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->id) - 1;
> > +
> > +	feat_dbg("%s: add_feature: edge added [%s[%u]: \"%s\"]", arc_name,
> > parent_node->name, edge,
> > +		child_node->name);
> > +
> > +	if (_edge)
> > +		*_edge = edge;
> > +
> > +	return 0;
> > +}
> > +
> > +static int
> > +feature_arc_init(rte_graph_feature_arc_main_t **pfl, uint32_t
> > max_feature_arcs)
> > +{
> > +	rte_graph_feature_arc_main_t *pm = NULL;
> > +	uint32_t i;
> > +	size_t sz;
> > +
> > +	if (!pfl)
> > +		return -1;
> > +
> > +	sz = sizeof(rte_graph_feature_arc_main_t) +
> > +		(sizeof(pm->feature_arcs[0]) * max_feature_arcs);
> > +
> > +	pm = malloc(sz);
> > +	if (!pm)
> > +		return -1;
> > +
> > +	memset(pm, 0, sz);
> > +
> > +	for (i = 0; i < max_feature_arcs; i++)
> > +		pm->feature_arcs[i] =
> > RTE_GRAPH_FEATURE_ARC_INITIALIZER;
> > +
> > +	pm->max_feature_arcs = max_feature_arcs;
> > +
> > +	*pfl = pm;
> > +
> > +	return 0;
> > +}
> > +
> > +int
> > +rte_graph_feature_arc_init(int max_feature_arcs)
> > +{
> > +	if (!max_feature_arcs)
> > +		return -1;
> > +
> > +	if (__feature_arc_main)
> > +		return -1;
> > +
> > +	return feature_arc_init(&__feature_arc_main, max_feature_arcs);
> > +}
> > +
> > +static void
> > +feature_arc_list_reset(struct rte_graph_feature_arc *arc, uint32_t
> list_index)
> > +{
> > +	rte_graph_feature_data_t *fdata = NULL;
> > +	rte_graph_feature_list_t *list = NULL;
> > +	struct rte_graph_feature *feat = NULL;
> > +	uint32_t i, j;
> > +
> > +	list = arc->feature_list[list_index];
> > +	feat = arc->features[list_index];
> > +
> > +	/*Initialize variables*/
> > +	memset(feat, 0, arc->feature_size);
> > +	memset(list, 0, arc->feature_list_size);
> > +
> > +	/* Initialize feature and feature_data */
> > +	for (i = 0; i < arc->max_features; i++) {
> > +		feat = __rte_graph_feature_get(arc, i, list_index);
> > +		feat->this_feature_index = i;
> > +
> > +		for (j = 0; j < arc->max_indexes; j++) {
> > +			fdata = rte_graph_feature_data_get(arc, feat, j);
> > +			fdata->next_enabled_feature =
> > RTE_GRAPH_FEATURE_INVALID;
> > +			fdata->next_edge = UINT16_MAX;
> > +			fdata->user_data = UINT32_MAX;
> > +		}
> > +	}
> > +
> > +	for (i = 0; i < arc->max_indexes; i++)
> > +		list->first_enabled_feature_by_index[i] =
> > RTE_GRAPH_FEATURE_INVALID;
> > +}
> > +
> > +static int
> > +feature_arc_list_init(struct rte_graph_feature_arc *arc, const char
> > *flist_name,
> > +			  rte_graph_feature_list_t **pplist,
> > +			  struct rte_graph_feature **ppfeature, uint32_t
> > list_index)
> > +{
> > +	char fname[2 * RTE_GRAPH_FEATURE_ARC_NAMELEN];
> > +	size_t list_size, feat_size, fdata_size;
> > +	rte_graph_feature_list_t *list = NULL;
> > +	struct rte_graph_feature *feat = NULL;
> > +
> > +	list_size = sizeof(list->first_enabled_feature_by_index[0]) * arc-
> > >max_indexes;
> > +
> > +	list = rte_malloc(flist_name, list_size, RTE_CACHE_LINE_SIZE);
> > +	if (!list)
> > +		return -ENOMEM;
> > +
> > +	fdata_size = arc->max_indexes * sizeof(rte_graph_feature_data_t);
> > +
> > +	/* Let one feature capture complete cache lines */
> > +	feat_size = RTE_ALIGN_CEIL(sizeof(struct rte_graph_feature) +
> > fdata_size,
> > +				   RTE_CACHE_LINE_SIZE);
> > +
> > +	snprintf(fname, sizeof(fname), "%s-%s", arc->feature_arc_name,
> > "feat");
> > +
> > +	feat = rte_malloc(fname, feat_size * arc->max_features,
> > RTE_CACHE_LINE_SIZE);
> > +	if (!feat) {
> > +		rte_free(list);
> > +		return -ENOMEM;
> > +	}
> > +	arc->feature_size = feat_size;
> > +	arc->feature_data_size = fdata_size;
> > +	arc->feature_list_size = list_size;
> > +
> > +	/* Initialize list */
> > +	list->indexed_by_features = feat;
> > +	*pplist = list;
> > +	*ppfeature = feat;
> > +
> > +	feature_arc_list_reset(arc, list_index);
> > +
> > +	return 0;
> > +}
> > +
> > +static void
> > +feature_arc_list_destroy(rte_graph_feature_list_t *list)
> > +{
> > +	rte_free(list->indexed_by_features);
>  Do you need to free individual rte_graph_feature here, that is allocated in
> arc_list_init?

Nitin> It seems correct to me. feature_arc_list_destroy() frees all memory allocated in feature_arc_list_init(). So feature_arc_list_destroy() is calling rte_free() for every rte_malloc() happened in feature_arc_list_init(). To make it clear, I have refactor the function and has added appropriate comment from v2 patch set onwards

> 
> > +	rte_free(list);
> > +}
> > +
> > +int
> > +rte_graph_feature_arc_create(const char *feature_arc_name, int
> > max_features, int max_indexes,
> > +		       struct rte_node_register *start_node,
> > rte_graph_feature_arc_t *_arc)
> > +{
> > +	char name[2 * RTE_GRAPH_FEATURE_ARC_NAMELEN];
> > +	rte_graph_feature_arc_main_t *dfm = NULL;
> > +	struct rte_graph_feature_arc *arc = NULL;
> > +	struct rte_graph_feature_data *gfd = NULL;
> > +	struct rte_graph_feature *df = NULL;
> > +	uint32_t iter, j, arc_index;
> > +	size_t sz;
> > +
> > +	if (!_arc)
> > +		return -1;
> > +
> > +	if (max_features < 2)
> > +		return -1;
> > +
> > +	if (!start_node)
> > +		return -1;
> > +
> > +	if (!feature_arc_name)
> > +		return -1;
> > +
> > +	if (max_features > RTE_GRAPH_FEATURE_MAX_PER_ARC) {
> > +		graph_err("Invalid max features: %u", max_features);
> > +		return -1;
> > +	}
> > +
> > +	/*
> > +	 * Application hasn't called rte_graph_feature_arc_init(). Initialize
> with
> > +	 * default values
> > +	 */
> > +	if (!__feature_arc_main) {
> > +		if
> > (rte_graph_feature_arc_init((int)__RTE_GRAPH_FEATURE_ARC_MAX) < 0) {
> > +			graph_err("rte_graph_feature_arc_init() failed");
> > +			return -1;
> > +		}
> > +	}
> > +
> > +	dfm = __feature_arc_main;
> > +
> > +	/* threshold check */
> > +	if (dfm->num_feature_arcs > (dfm->max_feature_arcs - 1)) {
> > +		graph_err("max threshold for num_feature_arcs: %d
> > reached",
> > +			  dfm->max_feature_arcs - 1);
> > +		return -1;
> > +	}
> > +	/* Find the free slot for feature arc */
> > +	for (iter = 0; iter < dfm->max_feature_arcs; iter++) {
> > +		if (dfm->feature_arcs[iter] ==
> > RTE_GRAPH_FEATURE_ARC_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 */
> > +	RTE_VERIFY(dfm->feature_arcs[arc_index] ==
> > RTE_GRAPH_FEATURE_ARC_INITIALIZER);
> > +
> > +	/* size of feature arc + feature_bit_mask_by_index */
> > +	sz = sizeof(*arc) + (sizeof(uint64_t) * max_indexes);
> > +
> > +	arc = rte_malloc(feature_arc_name, sz, RTE_CACHE_LINE_SIZE);
> > +
> > +	if (!arc) {
> > +		graph_err("malloc failed for feature_arc_create()");
> > +		return -1;
> > +	}
> > +
> > +	memset(arc, 0, sz);
> > +
> > +	/* Initialize rte_graph port group fixed variables */
> > +	STAILQ_INIT(&arc->all_features);
> > +	strncpy(arc->feature_arc_name, feature_arc_name,
> > RTE_GRAPH_FEATURE_ARC_NAMELEN - 1);
> > +	arc->feature_arc_main = (void *)dfm;
> > +	arc->start_node = start_node;
> > +	arc->max_features = max_features;
> > +	arc->max_indexes = max_indexes;
> > +
> > +	snprintf(name, sizeof(name), "%s-%s", feature_arc_name, "flist0");
> > +
> > +	if (feature_arc_list_init(arc, name, &arc->feature_list[0], &arc-
> > >features[0], 0) < 0) {
> > +		rte_free(arc);
> > +		graph_err("feature_arc_list_init(0) failed");
> > +		return -1;
> > +	}
> > +	snprintf(name, sizeof(name), "%s-%s", feature_arc_name, "flist1");
> > +
> > +	if (feature_arc_list_init(arc, name, &arc->feature_list[1], &arc-
> > >features[1], 1) < 0) {
> > +		feature_arc_list_destroy(arc->feature_list[0]);
> > +		graph_err("feature_arc_list_init(1) failed");
> > +		return -1;
> > +	}
> > +
> > +	for (iter = 0; iter < arc->max_features; iter++) {
> > +		df = rte_graph_feature_get(arc, iter);
> > +		for (j = 0; j < arc->max_indexes; j++) {
> > +			gfd = rte_graph_feature_data_get(arc, df, j);
> > +			gfd->next_enabled_feature =
> > RTE_GRAPH_FEATURE_INVALID;
> > +		}
> > +	}
> > +	arc->feature_arc_index = arc_index;
> > +	dfm->feature_arcs[arc->feature_arc_index] =
> > (rte_graph_feature_arc_t)arc;
> > +	dfm->num_feature_arcs++;
> > +
> > +	if (_arc)
> > +		*_arc = (rte_graph_feature_arc_t)arc;
> > +
> > +	return 0;
> > +}
> > +
> > +int
> > +rte_graph_feature_add(rte_graph_feature_arc_t _arc, struct
> > rte_node_register *feature_node,
> > +		const char *after_feature, const char *before_feature)
> > +{
> > +	struct rte_graph_feature_node_list *after_finfo = NULL, *before_finfo
> > = NULL;
> > +	struct rte_graph_feature_node_list *temp = NULL, *finfo = NULL;
> > +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> > +	uint32_t slot, add_flag;
> > +	rte_edge_t edge = -1;
> > +
> > +	RTE_VERIFY(arc->feature_arc_main == __feature_arc_main);
> > +
> > +	if (feature_arc_lookup(_arc)) {
> > +		graph_err("invalid feature arc: 0x%016" PRIx64,
> > (uint64_t)_arc);
> > +		return -1;
> > +	}
> > +
> > +	if (arc->runtime_enabled_features) {
> > +		graph_err("adding features after enabling any one of them is
> > not supported");
> > +		return -1;
> > +	}
> > +
> > +	if ((after_feature != NULL) && (before_feature != NULL) &&
> > +	    (after_feature == before_feature)) {
> > +		graph_err("after_feature and before_feature are same
> > '%s:%s]", after_feature,
> > +			before_feature);
> > +		return -1;
> > +	}
> > +
> > +	if (!feature_node) {
> > +		graph_err("feature_node: %p invalid", feature_node);
> > +		return -1;
> > +	}
> > +
> > +	arc = rte_graph_feature_arc_get(_arc);
> > +
> > +	if (feature_node->id == RTE_NODE_ID_INVALID) {
> > +		graph_err("Invalid node: %s", feature_node->name);
> > +		return -1;
> > +	}
> > +
> > +	if (!feature_lookup(arc, feature_node->name, &finfo, &slot)) {
> > +		graph_err("%s feature already added", feature_node->name);
> > +		return -1;
> > +	}
> > +
> > +	if (slot >= RTE_GRAPH_FEATURE_MAX_PER_ARC) {
> > +		graph_err("Max slot %u reached for feature addition", slot);
> > +		return -1;
> > +	}
> > +
> > +	if (strstr(feature_node->name, arc->start_node->name)) {
> > +		graph_err("Feature %s cannot point to itself: %s",
> > feature_node->name,
> > +			arc->start_node->name);
> > +		return -1;
> > +	}
> > +
> > +	if (connect_graph_nodes(arc->start_node, feature_node, &edge, arc-
> > >feature_arc_name)) {
> > +		graph_err("unable to connect %s -> %s", arc->start_node-
> > >name, feature_node->name);
> > +		return -1;
> > +	}
> > +
> > +	finfo = malloc(sizeof(*finfo));
> > +	if (!finfo)
> > +		return -1;
> > +
> > +	memset(finfo, 0, sizeof(*finfo));
> > +
> > +	finfo->feature_arc = (void *)arc;
> > +	finfo->feature_node = feature_node;
> > +	finfo->edge_to_this_feature = edge;
> > +
> > +	/* Check for before and after constraints */
> > +	if (before_feature) {
> > +		/* before_feature sanity */
> > +		if (feature_lookup(arc, before_feature, &before_finfo, NULL))
> > +			SET_ERR_JMP(EINVAL, finfo_free,
> > +				     "Invalid before feature name: %s",
> > before_feature);
> > +
> > +		if (!before_finfo)
> > +			SET_ERR_JMP(EINVAL, finfo_free,
> > +				     "before_feature %s does not exist",
> > before_feature);
> > +
> > +		/*
> > +		 * Starting from 0 to before_feature, continue connecting
> > edges
> > +		 */
> > +		add_flag = 1;
> > +		STAILQ_FOREACH(temp, &arc->all_features, next_feature) {
> > +			/*
> > +			 * As soon as we see before_feature. stop adding
> > edges
> > +			 */
> > +			if (!strncmp(temp->feature_node->name,
> > before_feature,
> > +				     RTE_GRAPH_NAMESIZE))
> > +				if (!connect_graph_nodes(finfo-
> > >feature_node, temp->feature_node,
> > +							 &edge, arc-
> > >feature_arc_name))
> > +					add_flag = 0;
> > +
> > +			if (add_flag)
> > +				connect_graph_nodes(temp->feature_node,
> > finfo->feature_node, NULL,
> > +						    arc->feature_arc_name);
> > +		}
> > +	}
> > +
> > +	if (after_feature) {
> > +		if (feature_lookup(arc, after_feature, &after_finfo, NULL))
> > +			SET_ERR_JMP(EINVAL, finfo_free,
> > +				     "Invalid after feature_name %s",
> > after_feature);
> > +
> > +		if (!after_finfo)
> > +			SET_ERR_JMP(EINVAL, finfo_free,
> > +				     "after_feature %s does not exist",
> > after_feature);
> > +
> > +		/* Starting from after_feature to end continue connecting
> > edges */
> > +		add_flag = 0;
> > +		STAILQ_FOREACH(temp, &arc->all_features, next_feature) {
> > +			/* We have already seen after_feature now */
> > +			if (add_flag)
> > +				/* Add all features as next node to current
> > feature*/
> > +				connect_graph_nodes(finfo->feature_node,
> > temp->feature_node, NULL,
> > +						    arc->feature_arc_name);
> > +
> > +			/* as soon as we see after_feature. start adding edges
> > +			 * from next iteration
> > +			 */
> > +			if (!strncmp(temp->feature_node->name,
> > after_feature, RTE_GRAPH_NAMESIZE))
> > +				/* connect after_feature to this feature */
> > +				if (!connect_graph_nodes(temp-
> > >feature_node, finfo->feature_node,
> > +							 &edge, arc-
> > >feature_arc_name))
> > +					add_flag = 1;
> > +		}
> > +
> > +		/* add feature next to after_feature */
> > +		STAILQ_INSERT_AFTER(&arc->all_features, after_finfo, finfo,
> > next_feature);
> > +	} else {
> > +		if (before_finfo) {
> > +			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);
> > +
> > +					return 0;
> > +				}
> > +				after_finfo = temp;
> > +			}
> > +		} else {
> > +			STAILQ_INSERT_TAIL(&arc->all_features, finfo,
> > next_feature);
> > +		}
> > +	}
> > +
> > +	return 0;
> > +
> > +finfo_free:
> > +	free(finfo);
> > +
> > +	return -1;
> > +}
> > +
> > +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 (!feature_lookup(arc, feature_name, &finfo, &slot)) {
> > +		*feat = (rte_graph_feature_t) slot;
> > +		return 0;
> > +	}
> > +
> > +	return -1;
> > +}
> > +
> > +int
> > +rte_graph_feature_validate(rte_graph_feature_arc_t _arc, uint32_t index,
> > const char *feature_name,
> > +			   int is_enable_disable)
> > +{
> > +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> > +	struct rte_graph_feature_node_list *finfo = NULL;
> > +	struct rte_graph_feature *gf = NULL;
> > +	uint32_t slot;
> > +
> > +	/* validate _arc */
> > +	if (arc->feature_arc_main != __feature_arc_main) {
> > +		graph_err("invalid feature arc: 0x%016" PRIx64,
> > (uint64_t)_arc);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* validate index */
> > +	if (index >= arc->max_indexes) {
> > +		graph_err("%s: Invalid provided index: %u >= %u configured",
> > arc->feature_arc_name,
> > +			index, arc->max_indexes);
> > +		return -1;
> > +	}
> > +
> > +	/* validate feature_name is already added or not  */
> > +	if (feature_lookup(arc, feature_name, &finfo, &slot)) {
> > +		graph_err("%s: No feature %s added", arc-
> > >feature_arc_name, feature_name);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (!finfo) {
> > +		graph_err("%s: No feature: %s found", arc-
> > >feature_arc_name, feature_name);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* slot should be in valid range */
> > +	if (slot >= arc->max_features) {
> > +		graph_err("%s/%s: Invalid free slot %u(max=%u) for feature",
> > arc->feature_arc_name,
> > +			feature_name, slot, arc->max_features);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* slot should be in range of 0 - 63 */
> > +	if (slot > (RTE_GRAPH_FEATURE_MAX_PER_ARC - 1)) {
> > +		graph_err("%s/%s: Invalid slot: %u", arc->feature_arc_name,
> > +			  feature_name, slot);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (finfo->node_index != slot) {
> > +		graph_err("%s/%s: feature lookup slot mismatch with finfo
> > index: %u and lookup slot: %u",
> > +			  arc->feature_arc_name, feature_name, finfo-
> > >node_index, slot);
> > +		return -1;
> > +	}
> > +
> > +	/* Get feature from active list */
> > +	gf = __rte_graph_feature_get(arc, slot, ARC_PASSIVE_LIST(arc));
> > +	if (gf->this_feature_index != slot) {
> > +		graph_err("%s: %s received feature_index: %u does not
> match
> > with saved feature_index: %u",
> > +			  arc->feature_arc_name, feature_name, slot, gf-
> > >this_feature_index);
> > +		return -1;
> > +	}
> > +
> > +	if (is_enable_disable && (arc->feature_bit_mask_by_index[index] &
> > +				  RTE_BIT64(slot))) {
> > +		graph_err("%s: %s already enabled on index: %u",
> > +			  arc->feature_arc_name, feature_name, index);
> > +		return -1;
> > +	}
> > +
> > +	if (!is_enable_disable && !arc->runtime_enabled_features) {
> > +		graph_err("%s: No feature enabled to disable", arc-
> > >feature_arc_name);
> > +		return -1;
> > +	}
> > +
> > +	if (!is_enable_disable && !(arc->feature_bit_mask_by_index[index] &
> > RTE_BIT64(slot))) {
> > +		graph_err("%s: %s not enabled in bitmask for index: %u",
> > +			  arc->feature_arc_name, feature_name, index);
> > +		return -1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void
> > +copy_fastpath_user_data(struct rte_graph_feature_arc *arc, uint16_t
> > dest_list_index,
> > +			uint16_t src_list_index)
> > +{
> > +	rte_graph_feature_data_t *sgfd = NULL, *dgfd = NULL;
> > +	struct rte_graph_feature *sgf = NULL, *dgf = NULL;
> > +	uint32_t i, j;
> > +
> > +	for (i = 0; i < arc->max_features; i++) {
> > +		sgf = __rte_graph_feature_get(arc, i, src_list_index);
> > +		dgf = __rte_graph_feature_get(arc, i, dest_list_index);
> > +		for (j = 0; j < arc->max_indexes; j++) {
> > +			sgfd = rte_graph_feature_data_get(arc, sgf, j);
> > +			dgfd = rte_graph_feature_data_get(arc, dgf, j);
> > +			dgfd->user_data = sgfd->user_data;
> > +		}
> > +	}
> > +}
> > +
> > +static void
> > +refill_feature_fastpath_data(struct rte_graph_feature_arc *arc, uint16_t
> > list_index)
> > +{
> > +	struct rte_graph_feature_node_list *finfo = NULL, *prev_finfo = NULL;
> > +	struct rte_graph_feature_data *gfd = NULL, *prev_gfd = NULL;
> > +	struct rte_graph_feature *gf = NULL, *prev_gf = NULL;
> > +	rte_graph_feature_list_t *flist = NULL;
> > +	uint32_t fi, di, prev_fi;
> > +	uint64_t bitmask;
> > +	rte_edge_t edge;
> > +
> > +	flist = arc->feature_list[list_index];
> > +
> > +	for (di = 0; di < arc->max_indexes; di++) {
> > +		bitmask = arc->feature_bit_mask_by_index[di];
> > +		prev_fi = RTE_GRAPH_FEATURE_INVALID;
> > +		/* for each feature set for index, set fast path data */
> > +		while (rte_bsf64_safe(bitmask, &fi)) {
> > +			gf = __rte_graph_feature_get(arc, fi, list_index);
> > +			gfd = rte_graph_feature_data_get(arc, gf, di);
> > +			feature_arc_node_info_lookup(arc, fi, &finfo);
> > +
> > +			/* If previous feature_index was valid in last loop */
> > +			if (prev_fi != RTE_GRAPH_FEATURE_INVALID) {
> > +				prev_gf = __rte_graph_feature_get(arc,
> > prev_fi, list_index);
> > +				prev_gfd = rte_graph_feature_data_get(arc,
> > prev_gf, di);
> > +				/*
> > +				 * Get edge of previous feature node
> > connecting to this feature node
> > +				 */
> > +				feature_arc_node_info_lookup(arc, prev_fi,
> > &prev_finfo);
> > +				if (!get_existing_edge(arc->feature_arc_name,
> > +						      prev_finfo->feature_node,
> > +						      finfo->feature_node,
> > &edge)) {
> > +					feat_dbg("[%s/%s(%2u)/idx:%2u]:
> > %s[%u] = %s",
> > +						 arc->feature_arc_name,
> > +						 prev_finfo->feature_node-
> > >name, prev_fi, di,
> > +						 prev_finfo->feature_node-
> > >name,
> > +						 edge, finfo->feature_node-
> > >name);
> > +					/* Copy feature index for next
> > iteration*/
> > +					gfd->next_edge = edge;
> > +					prev_fi = fi;
> > +					/*
> > +					 * Fill current feature as next enabled
> > +					 * feature to previous one
> > +					 */
> > +					prev_gfd->next_enabled_feature = fi;
> > +				} else {
> > +					/* Should not fail */
> > +					RTE_VERIFY(0);
> > +				}
> > +			}
> > +			/* On first feature edge of the node to be added */
> > +			if (fi == rte_bsf64(arc-
> > >feature_bit_mask_by_index[di])) {
> > +				if (!get_existing_edge(arc->feature_arc_name,
> > arc->start_node,
> > +						      finfo->feature_node,
> > +						      &edge)) {
> > +					feat_dbg("[%s/%s/%2u/idx:%2u]: 1st
> > feat %s[%u] = %s",
> > +						 arc->feature_arc_name,
> > +						 arc->start_node->name, fi, di,
> > +						 arc->start_node->name,
> > edge,
> > +						 finfo->feature_node->name);
> > +					/* Copy feature index for next
> > iteration*/
> > +					gfd->next_edge = edge;
> > +					prev_fi = fi;
> > +					/* Set first feature set array for
> > index*/
> > +					flist-
> > >first_enabled_feature_by_index[di] = fi;
> > +				} else {
> > +					/* Should not fail */
> > +					RTE_VERIFY(0);
> > +				}
> > +			}
> > +			/* Clear current feature index */
> > +			bitmask &= ~RTE_BIT64(fi);
> > +		}
> > +	}
> > +}
> > +
> > +int
> > +rte_graph_feature_enable(rte_graph_feature_arc_t _arc, uint32_t index,
> > const
> > +			 char *feature_name, int32_t user_data)
> > +{
> > +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> > +	struct rte_graph_feature_node_list *finfo = NULL;
> > +	struct rte_graph_feature_data *gfd = NULL;
> > +	rte_graph_feature_rt_list_t passive_list;
> > +	struct rte_graph_feature *gf = NULL;
> > +	uint64_t fp_bitmask;
> > +	uint32_t slot;
> > +
> > +	if (rte_graph_feature_validate(_arc, index, feature_name, 1))
> > +		return -1;
> > +
> > +	/** This should not fail as validate() has passed */
> > +	if (feature_lookup(arc, feature_name, &finfo, &slot))
> > +		RTE_VERIFY(0);
> > +
> > +	if (!arc->runtime_enabled_features)
> > +		prepare_feature_arc(arc);
> > +
> > +	passive_list = ARC_PASSIVE_LIST(arc);
> > +
> > +	gf = __rte_graph_feature_get(arc, slot, passive_list);
> > +	gfd = rte_graph_feature_data_get(arc, gf, index);
> > +
> > +	feat_dbg("%s/%s: Enabling feature on list: %u for index: %u at feature
> > slot %u",
> > +		 arc->feature_arc_name, feature_name, passive_list, index,
> > slot);
> > +
> > +	/* Reset feature list */
> > +	feature_arc_list_reset(arc, passive_list);
> > +
> > +	/* Copy user-data */
> > +	copy_fastpath_user_data(arc, passive_list, arc->active_feature_list);
> > +
> > +	/* Set current user-data */
> > +	gfd->user_data = user_data;
> > +
> > +	/* Set bitmask in control path bitmask */
> > +	rte_bit_relaxed_set64(rte_graph_uint_cast(slot), &arc-
> > >feature_bit_mask_by_index[index]);
> > +	refill_feature_fastpath_data(arc, passive_list);
> > +
> > +	/* Set fast path enable bitmask */
> > +	fp_bitmask = __atomic_load_n(&arc-
> > >feature_enable_bitmask[passive_list], __ATOMIC_RELAXED);
> > +	fp_bitmask |= RTE_BIT64(slot);
> > +	__atomic_store(&arc->feature_enable_bitmask[passive_list],
> > &fp_bitmask, __ATOMIC_RELAXED);
> > +
> > +	/* Slow path updates */
> > +	arc->runtime_enabled_features++;
> > +
> > +	/* Increase feature node info reference count */
> > +	finfo->ref_count++;
> > +
> > +	/* Store release semantics for active_list update */
> > +	__atomic_store(&arc->active_feature_list, &passive_list,
> > __ATOMIC_RELEASE);
> > +
> > +	return 0;
> > +}
> > +
> > +int
> > +rte_graph_feature_disable(rte_graph_feature_arc_t _arc, uint32_t index,
> > const char *feature_name)
> > +{
> > +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> > +	struct rte_graph_feature_data *gfd = NULL;
> > +	struct rte_graph_feature_node_list *finfo = NULL;
> > +	rte_graph_feature_rt_list_t passive_list;
> > +	struct rte_graph_feature *gf = NULL;
> > +	uint32_t slot;
> > +
> > +	if (rte_graph_feature_validate(_arc, index, feature_name, 0))
> > +		return -1;
> > +
> > +	if (feature_lookup(arc, feature_name, &finfo, &slot))
> > +		return -1;
> > +
> > +	passive_list = ARC_PASSIVE_LIST(arc);
> > +
> > +	gf = __rte_graph_feature_get(arc, slot, passive_list);
> > +	gfd = rte_graph_feature_data_get(arc, gf, index);
> > +
> > +	feat_dbg("%s/%s: Disabling feature for index: %u at feature slot %u",
> > arc->feature_arc_name,
> > +		feature_name, index, slot);
> > +
> > +	rte_bit_relaxed_clear64(rte_graph_uint_cast(slot), &arc-
> > >feature_bit_mask_by_index[index]);
> > +
> > +	/* Set fast path enable bitmask */
> > +	arc->feature_enable_bitmask[passive_list] &= ~(RTE_BIT64(slot));
> > +
> > +	/* Reset feature list */
> > +	feature_arc_list_reset(arc, passive_list);
> > +
> > +	/* Copy user-data */
> > +	copy_fastpath_user_data(arc, passive_list, arc->active_feature_list);
> > +
> > +	/* Reset current user-data */
> > +	gfd->user_data = ~0;
> > +
> > +	refill_feature_fastpath_data(arc, passive_list);
> > +
> > +	finfo->ref_count--;
> > +	arc->runtime_enabled_features--;
> > +
> > +	/* Store release semantics for active_list update */
> > +	__atomic_store(&arc->active_feature_list, &passive_list,
> > __ATOMIC_RELEASE);
> > +
> > +	return 0;
> > +}
> > +
> > +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 = __feature_arc_main;
> > +	struct rte_graph_feature_node_list *node_info = NULL;
> > +
> > +	while (!STAILQ_EMPTY(&arc->all_features)) {
> > +		node_info = STAILQ_FIRST(&arc->all_features);
> > +		STAILQ_REMOVE_HEAD(&arc->all_features, next_feature);
> > +		free(node_info);
> > +	}
> > +	feature_arc_list_destroy(arc->feature_list[0]);
> > +	feature_arc_list_destroy(arc->feature_list[1]);
> > +	rte_free(arc->features[0]);
> > +	rte_free(arc->features[1]);
> > +
> > +	dm->feature_arcs[arc->feature_arc_index] =
> > RTE_GRAPH_FEATURE_ARC_INITIALIZER;
> > +
> > +	rte_free(arc);
> > +	return 0;
> > +}
> > +
> > +int
> > +rte_graph_feature_arc_cleanup(void)
> > +{
> > +	rte_graph_feature_arc_main_t *dm = __feature_arc_main;
> > +	uint32_t iter;
> > +
> > +	if (!__feature_arc_main)
> > +		return -1;
> > +
> > +	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> > +		if (dm->feature_arcs[iter] ==
> > RTE_GRAPH_FEATURE_ARC_INITIALIZER)
> > +			continue;
> > +
> > +		rte_graph_feature_arc_destroy((rte_graph_feature_arc_t)dm-
> > >feature_arcs[iter]);
> > +	}
> > +	free(dm);
> > +
> > +	__feature_arc_main = NULL;
> > +
> > +	return 0;
> > +}
> > +
> > +int
> > +rte_graph_feature_arc_lookup_by_name(const char *arc_name,
> > rte_graph_feature_arc_t *_arc)
> > +{
> > +	rte_graph_feature_arc_main_t *dm = __feature_arc_main;
> > +	struct rte_graph_feature_arc *arc = NULL;
> > +	uint32_t iter;
> > +
> > +	if (!__feature_arc_main)
> > +		return -1;
> > +
> > +	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
> > +		if (dm->feature_arcs[iter] ==
> > RTE_GRAPH_FEATURE_ARC_INITIALIZER)
> > +			continue;
> > +
> > +		arc = rte_graph_feature_arc_get(dm->feature_arcs[iter]);
> > +
> > +		if (strstr(arc_name, arc->feature_arc_name)) {
> > +			if (_arc)
> > +				*_arc = (rte_graph_feature_arc_t)arc;
> > +			return 0;
> > +		}
> > +	}
> > +
> > +	return -1;
> > +}
> > +
> > +int
> > +rte_graph_feature_arc_num_enabled_features(rte_graph_feature_arc_t
> > _arc)
> > +{
> > +	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
> > +
> > +	return arc->runtime_enabled_features;
> > +}
> > +
> > +
> > diff --git a/lib/graph/meson.build b/lib/graph/meson.build
> > index 0cb15442ab..d916176fb7 100644
> > --- a/lib/graph/meson.build
> > +++ b/lib/graph/meson.build
> > @@ -14,11 +14,13 @@ sources = files(
> >          'graph_debug.c',
> >          'graph_stats.c',
> >          'graph_populate.c',
> > +        'graph_feature_arc.c',
> >          'graph_pcap.c',
> >          'rte_graph_worker.c',
> >          'rte_graph_model_mcore_dispatch.c',
> >  )
> >  headers = files('rte_graph.h', 'rte_graph_worker.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
> > new file mode 100644
> > index 0000000000..e3bf4eb73d
> > --- /dev/null
> > +++ b/lib/graph/rte_graph_feature_arc.h
> > @@ -0,0 +1,373 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(C) 2024 Marvell International Ltd.
> > + */
> > +
> > +#ifndef _RTE_GRAPH_FEATURE_ARC_H_
> > +#define _RTE_GRAPH_FEATURE_ARC_H_
> > +
> > +#include <assert.h>
> > +#include <errno.h>
> > +#include <signal.h>
> > +#include <stddef.h>
> > +#include <stdint.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_compat.h>
> > +#include <rte_debug.h>
> > +#include <rte_graph.h>
> > +#include <rte_graph_worker.h>
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +/**
> > + * @file
> > + *
> > + * rte_graph_feature_arc.h
> > + *
> > + * Define APIs and structures/variables with respect to feature arc
> > + *
> > + * - Feature arc(s)
> > + * - Feature(s)
> > + *
> > + * A feature arc represents an ordered list of features/protocol-nodes at a
> > + * given networking layer. Feature arc provides a high level abstraction to
> > + * connect various *rte_graph* nodes, designated as *feature nodes*, and
> > + * allowing steering of packets across these feature nodes fast path
> > processing
> > + * in a generic manner. In a typical network stack, often a protocol or
> feature
> > + * must be first enabled on a given interface, before any packet is steered
> > + * towards it for feature processing. For eg: incoming IPv4 packets are sent
> to
> > + * routing sub-system only after a valid IPv4 address is assigned to the
> > + * received interface. In other words, often packets needs to be steered
> across
> > + * features not based on the packet content but based on whether a
> feature is
> > + * enable or disable on a given incoming/outgoing interface. Feature arc
> > + * provides mechanism to enable/disable feature(s) on each interface at
> > runtime
> > + * and allow seamless packet steering across runtime enabled feature
> nodes
> > in
> > + * fast path.
> > + *
> > + * Feature arc also provides a way to steer packets from standard nodes to
> > + * custom/user-defined *feature nodes* without any change in standard
> > node's
> > + * fast path functions
> > + *
> > + * On a given interface multiple feature(s) might be enabled in a particular
> > + * feature arc. For instance, both "ipv4-output" and "IPsec policy output"
> > + * features may be enabled on "eth0" interface in "L3-output" feature arc.
> > + * Similarly, "ipv6-output" and "ipsec-output" may be enabled on "eth1"
> > + * interface in same "L3-output" feature arc.
> > + *
> > + * When multiple features are present in a given feature arc, its imperative
> > + * to allow each feature processing in a particular sequential order. For
> > + * instance, in "L3-input" feature arc it may be required to run "IPsec
> > + * input" feature first, for packet decryption, before "ip-lookup".  So a
> > + * sequential order must be maintained among features present in a
> feature
> > arc.
> > + *
> > + * Features are enabled/disabled multiple times at runtime to some or all
> > + * available interfaces present in the system. Features can be
> > enabled/disabled
> > + * even after @b rte_graph_create() is called. Enable/disabling features on
> > one
> > + * interface is independent of other interface.
> > + *
> > + * A given feature might consume packet (if it's configured to consume) or
> > may
> > + * forward it to next enabled feature. For instance, "IPsec input" feature
> may
> > + * consume/drop all packets with "Protect" policy action while all packets
> with
> > + * policy action as "Bypass" may be forwarded to next enabled feature
> (with
> > in
> > + * same feature arc)
> > + *
> > + * This library facilitates rte graph based applications to steer packets in
> > + * fast path to different feature nodes with-in a feature arc and support all
> > + * functionalities described above
> > + *
> > + * In order to use feature-arc APIs, applications needs to do following in
> > + * control path:
> > + * - Initialize feature arc library via rte_graph_feature_arc_init()
> > + * - Create feature arc via rte_graph_feature_arc_create()
> > + * - *Before calling rte_graph_create()*, features must be added to feature-
> > arc
> > + *   via rte_graph_feature_add(). rte_graph_feature_add() allows adding
> > + *   features in a sequential order with "runs_after" and "runs_before"
> > + *   constraints.
> > + * - Post rte_graph_create(), features can be enabled/disabled at runtime
> on
> > + *   any interface via
> rte_graph_feature_enable()/rte_graph_feature_disable()
> > + * - Feature arc can be destroyed via rte_graph_feature_arc_destroy()
> > + *
> > + * In fast path, APIs are provided to steer packets towards feature path
> from
> > + * - start_node (provided as an argument to
> rte_graph_feature_arc_create())
> > + * - feature nodes (which are added via rte_graph_feature_add())
> > + *
> > + * For typical steering of packets across feature nodes, application required
> > + * to know "rte_edges" which are saved in feature data object. Feature
> data
> > + * object is unique for every interface per feature with in a feature arc.
> > + *
> > + * When steering packets from start_node to feature node:
> > + * - rte_graph_feature_arc_first_feature_get() provides first enabled
> feature.
> > + * - Next rte_edge from start_node to first enabled feature can be obtained
> > via
> > + *   rte_graph_feature_arc_feature_set()
> > + *
> > + * rte_mbuf can carry [current feature, index] from start_node of an arc to
> > other
> > + * feature nodes
> > + *
> > + * In feature node, application can get 32-bit user_data
> > + * via_rte_graph_feature_user_data_get() which is provided in
> > + * rte_graph_feature_enable(). User data can hold feature specific cookie
> like
> > + * IPsec policy database index (if more than one are supported)
> > + *
> > + * If feature node is not consuming packet, next enabled feature and next
> > + * rte_edge can be obtained via rte_graph_feature_arc_next_feature_get()
> > + *
> > + * It is application responsibility to ensure that at-least *last feature*(or
> sink
> > + * feature) must be enabled from where packet can exit feature-arc path, if
> > + * *NO* intermediate feature is consuming the packet and it has reached
> till
> > + * the end of feature arc path
> > + *
> > + * Synchronization among cores
> > + * ---------------------------
> > + * Subsequent calls to rte_graph_feature_enable() is allowed while worker
> > cores
> > + * are processing in rte_graph_walk() loop. However, for
> > + * rte_graph_feature_disable() application must use RCU based
> > synchronization
> > + */
> > +
> > +/**< Initializer value for rte_graph_feature_arc_t */
> > +#define RTE_GRAPH_FEATURE_ARC_INITIALIZER
> > ((rte_graph_feature_arc_t)UINT64_MAX)
> > +
> > +/** Max number of features supported in a given feature arc */
> > +#define RTE_GRAPH_FEATURE_MAX_PER_ARC 64
> > +
> > +/** Length of feature arc name */
> > +#define RTE_GRAPH_FEATURE_ARC_NAMELEN RTE_NODE_NAMESIZE
> > +
> > +/** @internal */
> > +#define rte_graph_feature_cast(x) ((rte_graph_feature_t)x)
> > +
> > +/**< Initializer value for rte_graph_feature_arc_t */
> > +#define RTE_GRAPH_FEATURE_INVALID
> > rte_graph_feature_cast(UINT8_MAX)
> > +
> > +/** rte_graph feature arc object */
> > +typedef uint64_t rte_graph_feature_arc_t;
> > +
> > +/** rte_graph feature object */
> > +typedef uint8_t rte_graph_feature_t;
> > +
> > +/** runtime active feature list index with in feature arc*/
> > +typedef uint8_t rte_graph_feature_rt_list_t;
> > +
> > +/** per feature arc monotonically increasing counter to synchronize fast
> path
> > APIs */
> > +typedef uint16_t rte_graph_feature_counter_t;
> > +
> > +/**
> > + * Initialize feature arc subsystem
> > + *
> > + * @param max_feature_arcs
> > + *   Maximum number of feature arcs required to be supported
> > + *
> > + * @return
> > + *  0: Success
> > + * <0: Failure
> > + */
> > +__rte_experimental
> > +int rte_graph_feature_arc_init(int max_feature_arcs);
> > +
> > +/**
> > + * Create a feature arc
> > + *
> > + * @param feature_arc_name
> > + *   Feature arc name with max length of @ref
> > RTE_GRAPH_FEATURE_ARC_NAMELEN
> > + * @param max_features
> > + *   Maximum number of features to be supported in this feature arc
> > + * @param max_indexes
> > + *   Maximum number of interfaces/ports/indexes to be supported
> > + * @param start_node
> > + *   Base node where this feature arc's features are checked in fast path
> > + * @param[out] _arc
> > + *  Feature arc object
> > + *
> > + * @return
> > + *  0: Success
> > + * <0: Failure
> > + */
> > +__rte_experimental
> > +int rte_graph_feature_arc_create(const char *feature_arc_name, int
> > max_features, int max_indexes,
> > +				 struct rte_node_register *start_node,
> > +				 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
> > + *
> > + * @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. For instance
> > + *
> > + * 1. Add first feature node: "ipv4-input" to input arc
> > + *    rte_graph_feature_add(ipv4_input_arc, "ipv4-input", NULL, NULL);
> > + *
> > + * 2. Add "ipsec-input" feature node after "ipv4-input" node
> > + *    rte_graph_feature_add(ipv4_input_arc, "ipsec-input", "ipv4-input",
> > NULL);
> > + *
> > + * 3. Add "ipv4-pre-classify-input" node before "ipv4-input" node
> > + *    rte_graph_feature_add(ipv4_input_arc, "ipv4-pre-classify-input"",
> NULL,
> > "ipv4-input");
> > + *
> > + * 4. Add "acl-classify-input" node after ipv4-input but before ipsec-input
> > + *    rte_graph_feature_add(ipv4_input_arc, "acl-classify-input", "ipv4-
> input",
> > "ipsec-input");
> > + *
> > + * @param _arc
> > + *   Feature arc handle returned from @ref rte_graph_feature_arc_create()
> > + * @param feature_node
> > + *   Graph node representing feature. On success, feature_node is
> next_node
> > of
> > + *   feature_arc->start_node
> > + * @param runs_after
> > + *   Add this feature_node after already added "runs_after". Creates
> > + *   start_node -> runs_after -> this_feature sequence
> > + * @param runs_before
> > + *  Add this feature_node before already added "runs_before". Creates
> > + *  start_node -> this_feature -> runs_before sequence
> > + *
> > + * <I> Must be called before rte_graph_create() </I>
> > + * <I> rte_graph_feature_add() is not allowed after call to
> > + * rte_graph_feature_enable() so all features must be added before they
> can
> > be
> > + * enabled </I>
> > + *
> > + * @return
> > + *  0: Success
> > + * <0: Failure
> > + */
> > +__rte_experimental
> > +int rte_graph_feature_add(rte_graph_feature_arc_t _arc, struct
> > rte_node_register *feature_node,
> > +		    const char *runs_after, const char *runs_before);
> > +
> > +/**
> > + * Enable feature within a feature arc
> > + *
> > + * Must be called after @b rte_graph_create().
> > + *
> > + * @param _arc
> > + *   Feature arc object returned by @ref rte_graph_feature_arc_create or
> > @ref
> > + *   rte_graph_feature_arc_lookup_by_name
> > + * @param index
> > + *   Application specific index. Can be corresponding to
> interface_id/port_id
> > etc
> > + * @param feature_name
> > + *   Name of the node which is already added via @ref
> rte_graph_feature_add
> > + * @param user_data
> > + *   Application specific data which is retrieved in fast path
> > + *
> > + * @return
> > + *  0: Success
> > + * <0: Failure
> > + */
> > +__rte_experimental
> > +int rte_graph_feature_enable(rte_graph_feature_arc_t _arc, uint32_t
> index,
> > const char *feature_name,
> > +			     int32_t user_data);
> > +
> > +/**
> > + * Validate whether subsequent enable/disable feature would succeed or
> not.
> > + * API is thread-safe
> > + *
> > + * @param _arc
> > + *   Feature arc object returned by @ref rte_graph_feature_arc_create or
> > @ref
> > + *   rte_graph_feature_arc_lookup_by_name
> > + * @param index
> > + *   Application specific index. Can be corresponding to
> interface_id/port_id
> > etc
> > + * @param feature_name
> > + *   Name of the node which is already added via @ref
> rte_graph_feature_add
> > + * @param is_enable_disable
> > + *   If 1, validate whether subsequent @ref rte_graph_feature_enable
> would
> > pass or not
> > + *   If 0, validate whether subsequent @ref rte_graph_feature_disable
> would
> > pass or not
> > + *
> > + * @return
> > + *  0: Subsequent enable/disable API would pass
> > + * <0: Subsequent enable/disable API would not pass
> > + */
> > +__rte_experimental
> > +int rte_graph_feature_validate(rte_graph_feature_arc_t _arc, uint32_t
> index,
> > +			       const char *feature_name, int is_enable_disable);
> > +
> > +/**
> > + * Disable already enabled feature within a feature arc
> > + *
> > + * Must be called after @b rte_graph_create(). API is *NOT* Thread-safe
> > + *
> > + * @param _arc
> > + *   Feature arc object returned by @ref rte_graph_feature_arc_create or
> > @ref
> > + *   rte_graph_feature_arc_lookup_by_name
> > + * @param index
> > + *   Application specific index. Can be corresponding to
> interface_id/port_id
> > etc
> > + * @param feature_name
> > + *   Name of the node which is already added via @ref
> rte_graph_feature_add
> > + *
> > + * @return
> > + *  0: Success
> > + * <0: Failure
> > + */
> > +__rte_experimental
> > +int rte_graph_feature_disable(rte_graph_feature_arc_t _arc, uint32_t
> index,
> > +			      const char *feature_name);
> > +
> > +/**
> > + * 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 currently enabled within a
> > featur-arc
> > + *
> > + * @param _arc
> > + *  Feature arc object
> > + *
> > + * @return: Number of enabled features
> > + */
> > +__rte_experimental
> > +int
> rte_graph_feature_arc_num_enabled_features(rte_graph_feature_arc_t
> > _arc);
> > +#ifdef __cplusplus
> > +}
> > +#endif
> > +
> > +#endif
> > 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..6019d74853
> > --- /dev/null
> > +++ b/lib/graph/rte_graph_feature_arc_worker.h
> > @@ -0,0 +1,548 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(C) 2024 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>
> > +
> > +/**
> > + * @file
> > + *
> > + * rte_graph_feature_arc_worker.h
> > + *
> > + * Defines fast path structure
> > + */
> > +
> > +#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;
> > +
> > +	/** node representing feature */
> > +	struct rte_node_register *feature_node;
> > +
> > +	/** How many indexes/interfaces using this feature */
> > +	int32_t ref_count;
> > +
> > +	/* node_index in list (after feature_enable())*/
> > +	uint32_t node_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;
> > +};
> > +
> > +/**
> > + * Fast path holding rte_edge_t and next enabled feature for an feature
> > + */
> > +typedef struct __rte_packed rte_graph_feature_data {
> > +	/* next node to which current mbuf should go*/
> > +	rte_edge_t next_edge;
> > +
> > +	/* next enabled feature on this arc for current index */
> > +	union {
> > +		uint16_t reserved;
> > +		struct {
> > +			rte_graph_feature_t next_enabled_feature;
> > +		};
> > +	};
> > +
> > +	/* user_data */
> > +	int32_t user_data;
> > +} rte_graph_feature_data_t;
> > +
> > +/**
> > + * Fast path feature structure. Holds re_graph_feature_data_t per index
> > + */
> > +struct __rte_cache_aligned rte_graph_feature {
> > +	uint16_t this_feature_index;
> > +
> > +	/* Array of size arc->feature_data_size
> > +	 * [data-index-0][data-index-1]...
> > +	 * Each index of size: sizeof(rte_graph_feature_data_t)
> > +	 */
> > +	uint8_t feature_data_by_index[];
> > +};
> > +
> > +/**
> > + * fast path cache aligned feature list holding all features
> > + * There are two feature lists: active, passive
> > + *
> > + * Fast APIs works on active list while control plane updates passive list
> > + * A atomic update to arc->active_feature_list is done to switch between
> > active
> > + * and passive
> > + */
> > +typedef struct __rte_cache_aligned rte_graph_feature_list {
> > +	/**
> > +	 * fast path array holding per_feature data.
> > +	 * Duplicate entry as feature-arc also hold this pointer
> > +	 * arc->features[]
> > +	 *
> > +	 *<-------------feature-0 ---------><CEIL><---------feature-1 --------------
> > >...
> > +	 *[index-0][index-1]...[max_index-1]      [index-0][index-1]
> > ...[max_index-1]...
> > +	 */
> > +	struct rte_graph_feature *indexed_by_features;
> > +	/*
> > +	 * fast path array holding first enabled feature per index
> > +	 * (Required in start_node. In non start_node, mbuf can hold next
> > enabled
> > +	 * feature)
> > +	 */
> > +	rte_graph_feature_t first_enabled_feature_by_index[];
> > +} rte_graph_feature_list_t;
> > +
> > +/**
> > + * rte_graph feature arc object
> > + *
> > + * A feature-arc can only hold RTE_GRAPH_FEATURE_MAX_PER_ARC
> features
> > but no
> > + * limit to interface index
> > + *
> > + * Representing a feature arc holding all features which are
> enabled/disabled
> > + * on any interfaces
> > + */
> > +struct __rte_cache_aligned rte_graph_feature_arc {
> > +	/* First 64B is fast path variables */
> > +	RTE_MARKER fast_path_variables;
> > +
> > +	/** runtime active feature list */
> > +	rte_graph_feature_rt_list_t active_feature_list;
> > +
> > +	/* Actual Size of feature_list0 */
> > +	uint16_t feature_list_size;
> > +
> > +	/**
> > +	 * Size each feature in fastpath.
> > +	 * sizeof(arc->active_list->indexed_by_feature[0])
> > +	 */
> > +	uint16_t feature_size;
> > +
> > +	/* Size of arc->max_index * sizeof(rte_graph_feature_data_t) */
> > +	uint16_t feature_data_size;
> > +
> > +	/**
> > +	 * Fast path bitmask indicating if a feature is enabled or not Number
> > +	 * of bits: RTE_GRAPH_FEATURE_MAX_PER_ARC
> > +	 */
> > +	uint64_t feature_enable_bitmask[2];
> > +	rte_graph_feature_list_t *feature_list[2];
> > +	struct rte_graph_feature *features[2];
> > +
> > +	/** index in feature_arc_main */
> > +	uint16_t feature_arc_index;
> > +
> > +	uint16_t reserved[3];
> > +
> > +	/** Slow path variables follows*/
> > +	RTE_MARKER slow_path_variables;
> > +
> > +	/** feature arc name */
> > +	char feature_arc_name[RTE_GRAPH_FEATURE_ARC_NAMELEN];
> > +
> > +	/** All feature lists */
> > +	STAILQ_HEAD(, rte_graph_feature_node_list) all_features;
> > +
> > +	uint32_t runtime_enabled_features;
> > +
> > +	/** Back pointer to feature_arc_main */
> > +	void *feature_arc_main;
> > +
> > +	/* start_node */
> > +	struct rte_node_register *start_node;
> > +
> > +	/* maximum number of features supported by this arc */
> > +	uint32_t max_features;
> > +
> > +	/* maximum number of index supported by this arc */
> > +	uint32_t max_indexes;
> > +
> > +	/* Slow path bit mask per feature per index */
> > +	uint64_t feature_bit_mask_by_index[];
> > +};
> > +
> > +/** Feature arc main */
> > +typedef struct feature_arc_main {
> > +	/** number of feature arcs created by application */
> > +	uint32_t num_feature_arcs;
> > +
> > +	/** max features arcs allowed */
> > +	uint32_t max_feature_arcs;
> > +
> > +	/** feature arcs */
> > +	rte_graph_feature_arc_t feature_arcs[];
> > +} rte_graph_feature_arc_main_t;
> > +
> > +/** @internal Get feature arc pointer from object */
> > +#define rte_graph_feature_arc_get(arc) ((struct rte_graph_feature_arc
> *)arc)
> > +
> > +extern rte_graph_feature_arc_main_t *__feature_arc_main;
> > +
> > +/**
> > + * API to know if feature is valid or not
> > + */
> > +
> > +static __rte_always_inline int
> > +rte_graph_feature_is_valid(rte_graph_feature_t feature)
> > +{
> > +	return (feature != RTE_GRAPH_FEATURE_INVALID);
> > +}
> > +
> > +/**
> > + * Get rte_graph_feature object with no checks
> > + *
> > + * @param arc
> > + *   Feature arc pointer
> > + * @param feature
> > + *   Feature index
> > + * @param feature_list
> > + *   active feature list retrieved from
> > rte_graph_feature_arc_has_any_feature()
> > + *   or rte_graph_feature_arc_has_feature()
> > + *
> > + *   @return
> > + *     Internal feature object.
> > + */
> > +static __rte_always_inline struct rte_graph_feature *
> > +__rte_graph_feature_get(struct rte_graph_feature_arc *arc,
> > rte_graph_feature_t feature,
> > +			const rte_graph_feature_rt_list_t feature_list)
> > +{
> > +	return ((struct rte_graph_feature *)((uint8_t *)(arc-
> > >features[feature_list] +
> > +					     (feature * arc->feature_size))));
> > +}
> > +
> > +/**
> > + * Get rte_graph_feature object for a given interface/index from feature arc
> > + *
> > + * @param arc
> > + *   Feature arc pointer
> > + * @param feature
> > + *   Feature index
> > + *
> > + *   @return
> > + *     Internal feature object.
> > + */
> > +static __rte_always_inline struct rte_graph_feature *
> > +rte_graph_feature_get(struct rte_graph_feature_arc *arc,
> > rte_graph_feature_t feature)
> > +{
> > +	RTE_VERIFY(feature < arc->max_features);
> > +
> > +	if (likely(rte_graph_feature_is_valid(feature)))
> > +		return __rte_graph_feature_get(arc, feature, arc-
> > >active_feature_list);
> > +
> > +	return NULL;
> > +}
> > +
> > +static __rte_always_inline rte_graph_feature_data_t *
> > +__rte_graph_feature_data_get(struct rte_graph_feature_arc *arc, struct
> > rte_graph_feature *feature,
> > +			     uint8_t index)
> > +{
> > +	RTE_SET_USED(arc);
> > +	return ((rte_graph_feature_data_t *)(feature->feature_data_by_index
> > +
> > +					   (index *
> > sizeof(rte_graph_feature_data_t))));
> > +}
> > +
> > +/**
> > + * Get rte_graph feature data object for a index in feature
> > + *
> > + * @param arc
> > + *   feature arc
> > + * @param feature
> > + *  Pointer to feature object
> > + * @param index
> > + *  Index of feature maintained in slow path linked list
> > + *
> > + * @return
> > + *   Valid feature data
> > + */
> > +static __rte_always_inline rte_graph_feature_data_t *
> > +rte_graph_feature_data_get(struct rte_graph_feature_arc *arc, struct
> > rte_graph_feature *feature,
> > +			   uint8_t index)
> > +{
> > +	if (likely(index < arc->max_indexes))
> > +		return __rte_graph_feature_data_get(arc, feature, index);
> > +
> > +	RTE_VERIFY(0);
> > +}
> > +
> > +/**
> > + * Fast path API to check if any feature enabled on a feature arc
> > + * Typically from arc->start_node process function
> > + *
> > + * @param arc
> > + *   Feature arc object
> > + * @param[out] plist
> > + *   Pointer to runtime active feature list which needs to be provided to
> other
> > + *   fast path APIs
> > + *
> > + * @return
> > + * 0: If no feature enabled
> > + * Non-Zero: Bitmask of features enabled. plist is valid
> > + *
> > + */
> > +static __rte_always_inline uint64_t
> > +rte_graph_feature_arc_has_any_feature(struct rte_graph_feature_arc *arc,
> > +				      rte_graph_feature_rt_list_t *plist)
> > +{
> > +	*plist = __atomic_load_n(&arc->active_feature_list,
> > __ATOMIC_RELAXED);
> > +
> > +	return (__atomic_load_n(arc->feature_enable_bitmask +
> > (uint8_t)*plist,
> > +				__ATOMIC_RELAXED));
> > +}
> > +
> > +/**
> > + * Fast path API to check if provided feature is enabled on any
> interface/index
> > + * or not
> > + *
> > + * @param arc
> > + *   Feature arc object
> > + * @param feature
> > + *   Input rte_graph_feature_t that needs to be checked
> > + * @param[out] plist
> > + *  Returns active list to caller which needs to be provided to other fast
> path
> > + *  APIs
> > + *
> > + * @return
> > + * 1: If feature is enabled in arc
> > + * 0: If feature is not enabled in arc
> > + */
> > +static __rte_always_inline int
> > +rte_graph_feature_arc_has_feature(struct rte_graph_feature_arc *arc,
> > +				  rte_graph_feature_t feature,
> > +				  rte_graph_feature_rt_list_t *plist)
> > +{
> > +	uint64_t bitmask = RTE_BIT64(feature);
> > +
> > +	*plist = __atomic_load_n(&arc->active_feature_list,
> > __ATOMIC_RELAXED);
> > +
> > +	return (bitmask & __atomic_load_n(arc->feature_enable_bitmask +
> > (uint8_t)*plist,
> > +					  __ATOMIC_RELAXED));
> > +}
> > +
> > +/**
> > + * Prefetch feature arc fast path cache line
> > + *
> > + * @param arc
> > + *   RTE_GRAPH feature arc object
> > + */
> > +static __rte_always_inline void
> > +rte_graph_feature_arc_prefetch(struct rte_graph_feature_arc *arc)
> > +{
> > +	rte_prefetch0((void *)&arc->fast_path_variables);
> > +}
> > +
> > +/**
> > + * Prefetch feature related fast path cache line
> > + *
> > + * @param arc
> > + *   RTE_GRAPH feature arc object
> > + * @param list
> > + *  Pointer to runtime active feature list from
> > rte_graph_feature_arc_has_any_feature();
> > + * @param feature
> > + *   Pointer to feature object
> > + */
> > +static __rte_always_inline void
> > +rte_graph_feature_arc_feature_prefetch(struct rte_graph_feature_arc *arc,
> > +				       const rte_graph_feature_rt_list_t list,
> > +				       rte_graph_feature_t feature)
> > +{
> > +	/* feature cache line */
> > +	if (likely(rte_graph_feature_is_valid(feature)))
> > +		rte_prefetch0((void *)__rte_graph_feature_get(arc, feature,
> > list));
> > +}
> > +
> > +/**
> > + * Prefetch feature data upfront. Perform sanity
> > + *
> > + * @param _arc
> > + *   RTE_GRAPH feature arc object
> > + * @param list
> > + *  Pointer to runtime active feature list from
> > rte_graph_feature_arc_has_any_feature();
> > + * @param feature
> > + *   Pointer to feature object returned from @ref
> > + *   rte_graph_feature_arc_first_feature_get()
> > + * @param index
> > + *   Interface/index
> > + */
> > +static __rte_always_inline void
> > +rte_graph_feature_arc_data_prefetch(struct rte_graph_feature_arc *arc,
> > +				    const rte_graph_feature_rt_list_t list,
> > +				    rte_graph_feature_t feature, uint32_t
> index)
> > +{
> > +	if (likely(rte_graph_feature_is_valid(feature)))
> > +		rte_prefetch0((void *)((uint8_t *)arc->features[list] +
> > +			      offsetof(struct rte_graph_feature,
> > feature_data_by_index) +
> > +			      (index * sizeof(rte_graph_feature_data_t))));
> > +}
> > +
> > +/**
> > + * Fast path API to get first enabled feature on interface index
> > + * Typically required in arc->start_node so that from returned feature,
> > + * feature-data can be retrieved to steer packets
> > + *
> > + * @param arc
> > + *   Feature arc object
> > + * @param list
> > + *   Pointer to runtime active feature list from
> > + *   rte_graph_feature_arc_has_any_feature() or
> > + *   rte_graph_feature_arc_has_feature()
> > + * @param index
> > + *  Interface Index
> > + * @param[out] feature
> > + *  Pointer to rte_graph_feature_t.
> > + *
> > + * @return
> > + * 0. Success. feature field is valid
> > + * 1. Failure. feature field is invalid
> > + *
> > + */
> > +static __rte_always_inline int
> > +rte_graph_feature_arc_first_feature_get(struct rte_graph_feature_arc *arc,
> > +					const rte_graph_feature_rt_list_t list,
> > +					uint32_t index,
> > +					rte_graph_feature_t *feature)
> > +{
> > +	struct rte_graph_feature_list *feature_list = arc->feature_list[list];
> > +
> > +	*feature = feature_list->first_enabled_feature_by_index[index];
> > +
> > +	return rte_graph_feature_is_valid(*feature);
> > +}
> > +
> > +/**
> > + * Fast path API to get next enabled feature on interface index with
> provided
> > + * input feature
> > + *
> > + * @param arc
> > + *   Feature arc object
> > + * @param list
> > + *   Pointer to runtime active feature list from
> > + *   rte_graph_feature_arc_has_any_feature() or
> > + * @param index
> > + *   Interface Index
> > + * @param[in][out] feature
> > + *   Pointer to rte_graph_feature_t. Input feature set to next enabled
> feature
> > + *   after success return
> > + * @param[out] next_edge
> > + *    Edge from current feature to next feature. Valid only if next feature is
> > valid
> > + *
> > + * @return
> > + * 0. Success. next enabled feature is valid.
> > + * 1. Failure. next enabled feature is invalid
> > + */
> > +static __rte_always_inline int
> > +rte_graph_feature_arc_next_feature_get(struct rte_graph_feature_arc
> *arc,
> > +				       const rte_graph_feature_rt_list_t list,
> > +				       uint32_t index,
> > +				       rte_graph_feature_t *feature,
> > +				       rte_edge_t *next_edge)
> > +{
> > +	rte_graph_feature_data_t *feature_data = NULL;
> > +	struct rte_graph_feature *f = NULL;
> > +
> > +	if (likely(rte_graph_feature_is_valid(*feature))) {
> > +		f = __rte_graph_feature_get(arc, *feature, list);
> > +		feature_data = rte_graph_feature_data_get(arc, f, index);
> > +		*feature = feature_data->next_enabled_feature;
> > +		*next_edge = feature_data->next_edge;
> > +		return (*feature == RTE_GRAPH_FEATURE_INVALID);
> > +	}
> > +
> > +	return 1;
> > +}
> > +
> > +/**
> > + * Set fields with respect to first enabled feature in an arc and return edge
> > + * Typically returned feature and interface index must be saved in
> rte_mbuf
> > + * structure to pass this information to next feature node
> > + *
> > + * @param arc
> > + *   Feature arc object
> > + * @param list
> > + *   Pointer to runtime active feature list from
> > rte_graph_feature_arc_has_any_feature();
> > + * @param index
> > + *  Index (of interface)
> > + * @param[out] gf
> > + *  Pointer to rte_graph_feature_t. Valid if API returns Success
> > + * @param[out] edge
> > + *  Edge to steer packet from arc->start_node to first enabled feature. Valid
> > + *  only if API returns Success
> > + *
> > + * @return
> > + * 0: If valid feature is set by API
> > + * 1: If valid feature is NOT set by API
> > + */
> > +static __rte_always_inline rte_graph_feature_t
> > +rte_graph_feature_arc_feature_set(struct rte_graph_feature_arc *arc,
> > +				  const rte_graph_feature_rt_list_t list,
> > +				  uint32_t index,
> > +				  rte_graph_feature_t *gf,
> > +				  rte_edge_t *edge)
> > +{
> > +	struct rte_graph_feature_list *feature_list = arc->feature_list[list];
> > +	struct rte_graph_feature_data *feature_data = NULL;
> > +	struct rte_graph_feature *feature = NULL;
> > +	rte_graph_feature_t f;
> > +
> > +	/* reset */
> > +	*gf = RTE_GRAPH_FEATURE_INVALID;
> > +	f = feature_list->first_enabled_feature_by_index[index];
> > +
> > +	if (unlikely(rte_graph_feature_is_valid(f))) {
> > +		feature = __rte_graph_feature_get(arc, f, list);
> > +		feature_data = rte_graph_feature_data_get(arc, feature,
> > index);
> > +		*gf = f;
> > +		*edge = feature_data->next_edge;
> > +		return 0;
> > +	}
> > +
> > +	return 1;
> > +}
> > +
> > +/**
> > + * Get user data corresponding to current feature set by application in
> > + * rte_graph_feature_enable()
> > + *
> > + * @param arc
> > + *  Feature arc object
> > + * @param list
> > + *  Pointer to runtime active feature list from
> > rte_graph_feature_arc_has_any_feature();
> > + * @param feature
> > + *  Feature index
> > + * @param index
> > + *  Interface index
> > + *
> > + *  @return
> > + *  UINT32_MAX: Failure
> > + *  Valid user data: Success
> > + */
> > +static __rte_always_inline uint32_t
> > +rte_graph_feature_user_data_get(struct rte_graph_feature_arc *arc,
> > +				const rte_graph_feature_rt_list_t list,
> > +				rte_graph_feature_t feature,
> > +				uint32_t index)
> > +{
> > +	rte_graph_feature_data_t *fdata = NULL;
> > +	struct rte_graph_feature *f = NULL;
> > +
> > +	if (likely(rte_graph_feature_is_valid(feature))) {
> > +		f = __rte_graph_feature_get(arc, feature, list);
> > +		fdata = rte_graph_feature_data_get(arc, f, index);
> > +		return fdata->user_data;
> > +	}
> > +
> > +	return UINT32_MAX;
> > +}
> > +#ifdef __cplusplus
> > +}
> > +#endif
> > +#endif
> > diff --git a/lib/graph/version.map b/lib/graph/version.map
> > index 2c83425ddc..82b2469fba 100644
> > --- a/lib/graph/version.map
> > +++ b/lib/graph/version.map
> > @@ -52,3 +52,20 @@ DPDK_25 {
> >
> >  	local: *;
> >  };
> > +
> > +EXPERIMENTAL {
> > +	global:
> > +
> > +	# added in 24.11
> > +	rte_graph_feature_arc_init;
> > +	rte_graph_feature_arc_create;
> > +	rte_graph_feature_arc_lookup_by_name;
> > +	rte_graph_feature_add;
> > +	rte_graph_feature_enable;
> > +	rte_graph_feature_validate;
> > +	rte_graph_feature_disable;
> > +	rte_graph_feature_lookup;
> > +	rte_graph_feature_arc_destroy;
> > +	rte_graph_feature_arc_cleanup;
> > +	rte_graph_feature_arc_num_enabled_features;
> > +};
> > --
> > 2.43.0
  

Patch

diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 0000000000..3b05bac137
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,959 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include <rte_graph_feature_arc_worker.h>
+#include <rte_malloc.h>
+
+#define __RTE_GRAPH_FEATURE_ARC_MAX 32
+
+#define ARC_PASSIVE_LIST(arc) (arc->active_feature_list ^ 0x1)
+
+#define rte_graph_uint_cast(x) ((unsigned int)x)
+#define feat_dbg graph_err
+
+rte_graph_feature_arc_main_t *__feature_arc_main;
+
+/* Make sure fast path cache line is compact */
+_Static_assert((offsetof(struct rte_graph_feature_arc, slow_path_variables)
+		- offsetof(struct rte_graph_feature_arc, fast_path_variables))
+	       <= RTE_CACHE_LINE_SIZE);
+
+
+static int
+feature_lookup(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;
+	const char *name;
+
+	if (!feat_name)
+		return -1;
+
+	if (slot)
+		*slot = 0;
+
+	STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+		RTE_VERIFY(finfo->feature_arc == arc);
+		name = rte_node_id_to_name(finfo->feature_node->id);
+		if (!strncmp(name, feat_name, RTE_GRAPH_NAMESIZE)) {
+			if (ffinfo)
+				*ffinfo = finfo;
+			return 0;
+		}
+		if (slot)
+			(*slot)++;
+	}
+	return -1;
+}
+
+static int
+feature_arc_node_info_lookup(struct rte_graph_feature_arc *arc, uint32_t feature_index,
+			     struct rte_graph_feature_node_list **ppfinfo)
+{
+	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) {
+		if (index == feature_index) {
+			if (finfo->node_index == feature_index)
+				return -1;
+			*ppfinfo = finfo;
+		}
+		index++;
+	}
+	if (feature_index && (index >= feature_index))
+		return -1;
+
+	return 0;
+}
+
+static void
+prepare_feature_arc(struct rte_graph_feature_arc *arc)
+{
+	struct rte_graph_feature_node_list *finfo = NULL;
+	uint32_t index = 0;
+
+	STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+		finfo->node_index = index;
+		index++;
+	}
+}
+
+static int
+feature_arc_lookup(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 = __feature_arc_main;
+	uint32_t iter;
+
+	if (!__feature_arc_main)
+		return -1;
+
+	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
+		if (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)
+			continue;
+
+		if (arc == (rte_graph_feature_arc_get(dm->feature_arcs[iter])))
+			return 0;
+	}
+	return -1;
+}
+
+static int
+get_existing_edge(const char *arc_name, struct rte_node_register *parent_node,
+		  struct rte_node_register *child_node, rte_edge_t *_edge)
+{
+	char **next_edges = NULL;
+	uint32_t count, i;
+
+	RTE_SET_USED(arc_name);
+
+	count = rte_node_edge_get(parent_node->id, NULL);
+	next_edges = malloc(count);
+
+	if (!next_edges)
+		return -1;
+
+	count = rte_node_edge_get(parent_node->id, next_edges);
+	for (i = 0; i < count; i++) {
+		if (strstr(child_node->name, next_edges[i])) {
+			feat_dbg("%s: Edge exists [%s[%u]: \"%s\"]", arc_name,
+				  parent_node->name, i, child_node->name);
+			if (_edge)
+				*_edge = (rte_edge_t)i;
+
+			free(next_edges);
+			return 0;
+		}
+	}
+	free(next_edges);
+
+	return -1;
+}
+
+static int
+connect_graph_nodes(struct rte_node_register *parent_node, struct rte_node_register *child_node,
+		    rte_edge_t *_edge, char *arc_name)
+{
+	const char *next_node = NULL;
+	rte_edge_t edge;
+
+	if (!get_existing_edge(arc_name, parent_node, child_node, &edge)) {
+		feat_dbg("%s: add_feature: Edge reused [%s[%u]: \"%s\"]", arc_name,
+			parent_node->name, edge, child_node->name);
+
+		if (_edge)
+			*_edge = edge;
+
+		return 0;
+	}
+
+	/* Node to be added */
+	next_node = child_node->name;
+
+	edge = rte_node_edge_update(parent_node->id, 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->id) - 1;
+
+	feat_dbg("%s: add_feature: edge added [%s[%u]: \"%s\"]", arc_name, parent_node->name, edge,
+		child_node->name);
+
+	if (_edge)
+		*_edge = edge;
+
+	return 0;
+}
+
+static int
+feature_arc_init(rte_graph_feature_arc_main_t **pfl, uint32_t max_feature_arcs)
+{
+	rte_graph_feature_arc_main_t *pm = NULL;
+	uint32_t i;
+	size_t sz;
+
+	if (!pfl)
+		return -1;
+
+	sz = sizeof(rte_graph_feature_arc_main_t) +
+		(sizeof(pm->feature_arcs[0]) * max_feature_arcs);
+
+	pm = malloc(sz);
+	if (!pm)
+		return -1;
+
+	memset(pm, 0, sz);
+
+	for (i = 0; i < max_feature_arcs; i++)
+		pm->feature_arcs[i] = RTE_GRAPH_FEATURE_ARC_INITIALIZER;
+
+	pm->max_feature_arcs = max_feature_arcs;
+
+	*pfl = pm;
+
+	return 0;
+}
+
+int
+rte_graph_feature_arc_init(int max_feature_arcs)
+{
+	if (!max_feature_arcs)
+		return -1;
+
+	if (__feature_arc_main)
+		return -1;
+
+	return feature_arc_init(&__feature_arc_main, max_feature_arcs);
+}
+
+static void
+feature_arc_list_reset(struct rte_graph_feature_arc *arc, uint32_t list_index)
+{
+	rte_graph_feature_data_t *fdata = NULL;
+	rte_graph_feature_list_t *list = NULL;
+	struct rte_graph_feature *feat = NULL;
+	uint32_t i, j;
+
+	list = arc->feature_list[list_index];
+	feat = arc->features[list_index];
+
+	/*Initialize variables*/
+	memset(feat, 0, arc->feature_size);
+	memset(list, 0, arc->feature_list_size);
+
+	/* Initialize feature and feature_data */
+	for (i = 0; i < arc->max_features; i++) {
+		feat = __rte_graph_feature_get(arc, i, list_index);
+		feat->this_feature_index = i;
+
+		for (j = 0; j < arc->max_indexes; j++) {
+			fdata = rte_graph_feature_data_get(arc, feat, j);
+			fdata->next_enabled_feature = RTE_GRAPH_FEATURE_INVALID;
+			fdata->next_edge = UINT16_MAX;
+			fdata->user_data = UINT32_MAX;
+		}
+	}
+
+	for (i = 0; i < arc->max_indexes; i++)
+		list->first_enabled_feature_by_index[i] = RTE_GRAPH_FEATURE_INVALID;
+}
+
+static int
+feature_arc_list_init(struct rte_graph_feature_arc *arc, const char *flist_name,
+			  rte_graph_feature_list_t **pplist,
+			  struct rte_graph_feature **ppfeature, uint32_t list_index)
+{
+	char fname[2 * RTE_GRAPH_FEATURE_ARC_NAMELEN];
+	size_t list_size, feat_size, fdata_size;
+	rte_graph_feature_list_t *list = NULL;
+	struct rte_graph_feature *feat = NULL;
+
+	list_size = sizeof(list->first_enabled_feature_by_index[0]) * arc->max_indexes;
+
+	list = rte_malloc(flist_name, list_size, RTE_CACHE_LINE_SIZE);
+	if (!list)
+		return -ENOMEM;
+
+	fdata_size = arc->max_indexes * sizeof(rte_graph_feature_data_t);
+
+	/* Let one feature capture complete cache lines */
+	feat_size = RTE_ALIGN_CEIL(sizeof(struct rte_graph_feature) + fdata_size,
+				   RTE_CACHE_LINE_SIZE);
+
+	snprintf(fname, sizeof(fname), "%s-%s", arc->feature_arc_name, "feat");
+
+	feat = rte_malloc(fname, feat_size * arc->max_features, RTE_CACHE_LINE_SIZE);
+	if (!feat) {
+		rte_free(list);
+		return -ENOMEM;
+	}
+	arc->feature_size = feat_size;
+	arc->feature_data_size = fdata_size;
+	arc->feature_list_size = list_size;
+
+	/* Initialize list */
+	list->indexed_by_features = feat;
+	*pplist = list;
+	*ppfeature = feat;
+
+	feature_arc_list_reset(arc, list_index);
+
+	return 0;
+}
+
+static void
+feature_arc_list_destroy(rte_graph_feature_list_t *list)
+{
+	rte_free(list->indexed_by_features);
+	rte_free(list);
+}
+
+int
+rte_graph_feature_arc_create(const char *feature_arc_name, int max_features, int max_indexes,
+		       struct rte_node_register *start_node, rte_graph_feature_arc_t *_arc)
+{
+	char name[2 * RTE_GRAPH_FEATURE_ARC_NAMELEN];
+	rte_graph_feature_arc_main_t *dfm = NULL;
+	struct rte_graph_feature_arc *arc = NULL;
+	struct rte_graph_feature_data *gfd = NULL;
+	struct rte_graph_feature *df = NULL;
+	uint32_t iter, j, arc_index;
+	size_t sz;
+
+	if (!_arc)
+		return -1;
+
+	if (max_features < 2)
+		return -1;
+
+	if (!start_node)
+		return -1;
+
+	if (!feature_arc_name)
+		return -1;
+
+	if (max_features > RTE_GRAPH_FEATURE_MAX_PER_ARC) {
+		graph_err("Invalid max features: %u", max_features);
+		return -1;
+	}
+
+	/*
+	 * Application hasn't called rte_graph_feature_arc_init(). Initialize with
+	 * default values
+	 */
+	if (!__feature_arc_main) {
+		if (rte_graph_feature_arc_init((int)__RTE_GRAPH_FEATURE_ARC_MAX) < 0) {
+			graph_err("rte_graph_feature_arc_init() failed");
+			return -1;
+		}
+	}
+
+	dfm = __feature_arc_main;
+
+	/* threshold check */
+	if (dfm->num_feature_arcs > (dfm->max_feature_arcs - 1)) {
+		graph_err("max threshold for num_feature_arcs: %d reached",
+			  dfm->max_feature_arcs - 1);
+		return -1;
+	}
+	/* Find the free slot for feature arc */
+	for (iter = 0; iter < dfm->max_feature_arcs; iter++) {
+		if (dfm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_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 */
+	RTE_VERIFY(dfm->feature_arcs[arc_index] == RTE_GRAPH_FEATURE_ARC_INITIALIZER);
+
+	/* size of feature arc + feature_bit_mask_by_index */
+	sz = sizeof(*arc) + (sizeof(uint64_t) * max_indexes);
+
+	arc = rte_malloc(feature_arc_name, sz, RTE_CACHE_LINE_SIZE);
+
+	if (!arc) {
+		graph_err("malloc failed for feature_arc_create()");
+		return -1;
+	}
+
+	memset(arc, 0, sz);
+
+	/* Initialize rte_graph port group fixed variables */
+	STAILQ_INIT(&arc->all_features);
+	strncpy(arc->feature_arc_name, feature_arc_name, RTE_GRAPH_FEATURE_ARC_NAMELEN - 1);
+	arc->feature_arc_main = (void *)dfm;
+	arc->start_node = start_node;
+	arc->max_features = max_features;
+	arc->max_indexes = max_indexes;
+
+	snprintf(name, sizeof(name), "%s-%s", feature_arc_name, "flist0");
+
+	if (feature_arc_list_init(arc, name, &arc->feature_list[0], &arc->features[0], 0) < 0) {
+		rte_free(arc);
+		graph_err("feature_arc_list_init(0) failed");
+		return -1;
+	}
+	snprintf(name, sizeof(name), "%s-%s", feature_arc_name, "flist1");
+
+	if (feature_arc_list_init(arc, name, &arc->feature_list[1], &arc->features[1], 1) < 0) {
+		feature_arc_list_destroy(arc->feature_list[0]);
+		graph_err("feature_arc_list_init(1) failed");
+		return -1;
+	}
+
+	for (iter = 0; iter < arc->max_features; iter++) {
+		df = rte_graph_feature_get(arc, iter);
+		for (j = 0; j < arc->max_indexes; j++) {
+			gfd = rte_graph_feature_data_get(arc, df, j);
+			gfd->next_enabled_feature = RTE_GRAPH_FEATURE_INVALID;
+		}
+	}
+	arc->feature_arc_index = arc_index;
+	dfm->feature_arcs[arc->feature_arc_index] = (rte_graph_feature_arc_t)arc;
+	dfm->num_feature_arcs++;
+
+	if (_arc)
+		*_arc = (rte_graph_feature_arc_t)arc;
+
+	return 0;
+}
+
+int
+rte_graph_feature_add(rte_graph_feature_arc_t _arc, struct rte_node_register *feature_node,
+		const char *after_feature, const char *before_feature)
+{
+	struct rte_graph_feature_node_list *after_finfo = NULL, *before_finfo = NULL;
+	struct rte_graph_feature_node_list *temp = NULL, *finfo = NULL;
+	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
+	uint32_t slot, add_flag;
+	rte_edge_t edge = -1;
+
+	RTE_VERIFY(arc->feature_arc_main == __feature_arc_main);
+
+	if (feature_arc_lookup(_arc)) {
+		graph_err("invalid feature arc: 0x%016" PRIx64, (uint64_t)_arc);
+		return -1;
+	}
+
+	if (arc->runtime_enabled_features) {
+		graph_err("adding features after enabling any one of them is not supported");
+		return -1;
+	}
+
+	if ((after_feature != NULL) && (before_feature != NULL) &&
+	    (after_feature == before_feature)) {
+		graph_err("after_feature and before_feature are same '%s:%s]", after_feature,
+			before_feature);
+		return -1;
+	}
+
+	if (!feature_node) {
+		graph_err("feature_node: %p invalid", feature_node);
+		return -1;
+	}
+
+	arc = rte_graph_feature_arc_get(_arc);
+
+	if (feature_node->id == RTE_NODE_ID_INVALID) {
+		graph_err("Invalid node: %s", feature_node->name);
+		return -1;
+	}
+
+	if (!feature_lookup(arc, feature_node->name, &finfo, &slot)) {
+		graph_err("%s feature already added", feature_node->name);
+		return -1;
+	}
+
+	if (slot >= RTE_GRAPH_FEATURE_MAX_PER_ARC) {
+		graph_err("Max slot %u reached for feature addition", slot);
+		return -1;
+	}
+
+	if (strstr(feature_node->name, arc->start_node->name)) {
+		graph_err("Feature %s cannot point to itself: %s", feature_node->name,
+			arc->start_node->name);
+		return -1;
+	}
+
+	if (connect_graph_nodes(arc->start_node, feature_node, &edge, arc->feature_arc_name)) {
+		graph_err("unable to connect %s -> %s", arc->start_node->name, feature_node->name);
+		return -1;
+	}
+
+	finfo = malloc(sizeof(*finfo));
+	if (!finfo)
+		return -1;
+
+	memset(finfo, 0, sizeof(*finfo));
+
+	finfo->feature_arc = (void *)arc;
+	finfo->feature_node = feature_node;
+	finfo->edge_to_this_feature = edge;
+
+	/* Check for before and after constraints */
+	if (before_feature) {
+		/* before_feature sanity */
+		if (feature_lookup(arc, before_feature, &before_finfo, NULL))
+			SET_ERR_JMP(EINVAL, finfo_free,
+				     "Invalid before feature name: %s", before_feature);
+
+		if (!before_finfo)
+			SET_ERR_JMP(EINVAL, finfo_free,
+				     "before_feature %s does not exist", before_feature);
+
+		/*
+		 * Starting from 0 to before_feature, continue connecting edges
+		 */
+		add_flag = 1;
+		STAILQ_FOREACH(temp, &arc->all_features, next_feature) {
+			/*
+			 * As soon as we see before_feature. stop adding edges
+			 */
+			if (!strncmp(temp->feature_node->name, before_feature,
+				     RTE_GRAPH_NAMESIZE))
+				if (!connect_graph_nodes(finfo->feature_node, temp->feature_node,
+							 &edge, arc->feature_arc_name))
+					add_flag = 0;
+
+			if (add_flag)
+				connect_graph_nodes(temp->feature_node, finfo->feature_node, NULL,
+						    arc->feature_arc_name);
+		}
+	}
+
+	if (after_feature) {
+		if (feature_lookup(arc, after_feature, &after_finfo, NULL))
+			SET_ERR_JMP(EINVAL, finfo_free,
+				     "Invalid after feature_name %s", after_feature);
+
+		if (!after_finfo)
+			SET_ERR_JMP(EINVAL, finfo_free,
+				     "after_feature %s does not exist", after_feature);
+
+		/* Starting from after_feature to end continue connecting edges */
+		add_flag = 0;
+		STAILQ_FOREACH(temp, &arc->all_features, next_feature) {
+			/* We have already seen after_feature now */
+			if (add_flag)
+				/* Add all features as next node to current feature*/
+				connect_graph_nodes(finfo->feature_node, temp->feature_node, NULL,
+						    arc->feature_arc_name);
+
+			/* as soon as we see after_feature. start adding edges
+			 * from next iteration
+			 */
+			if (!strncmp(temp->feature_node->name, after_feature, RTE_GRAPH_NAMESIZE))
+				/* connect after_feature to this feature */
+				if (!connect_graph_nodes(temp->feature_node, finfo->feature_node,
+							 &edge, arc->feature_arc_name))
+					add_flag = 1;
+		}
+
+		/* add feature next to after_feature */
+		STAILQ_INSERT_AFTER(&arc->all_features, after_finfo, finfo, next_feature);
+	} else {
+		if (before_finfo) {
+			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);
+
+					return 0;
+				}
+				after_finfo = temp;
+			}
+		} else {
+			STAILQ_INSERT_TAIL(&arc->all_features, finfo, next_feature);
+		}
+	}
+
+	return 0;
+
+finfo_free:
+	free(finfo);
+
+	return -1;
+}
+
+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 (!feature_lookup(arc, feature_name, &finfo, &slot)) {
+		*feat = (rte_graph_feature_t) slot;
+		return 0;
+	}
+
+	return -1;
+}
+
+int
+rte_graph_feature_validate(rte_graph_feature_arc_t _arc, uint32_t index, const char *feature_name,
+			   int is_enable_disable)
+{
+	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
+	struct rte_graph_feature_node_list *finfo = NULL;
+	struct rte_graph_feature *gf = NULL;
+	uint32_t slot;
+
+	/* validate _arc */
+	if (arc->feature_arc_main != __feature_arc_main) {
+		graph_err("invalid feature arc: 0x%016" PRIx64, (uint64_t)_arc);
+		return -EINVAL;
+	}
+
+	/* validate index */
+	if (index >= arc->max_indexes) {
+		graph_err("%s: Invalid provided index: %u >= %u configured", arc->feature_arc_name,
+			index, arc->max_indexes);
+		return -1;
+	}
+
+	/* validate feature_name is already added or not  */
+	if (feature_lookup(arc, feature_name, &finfo, &slot)) {
+		graph_err("%s: No feature %s added", arc->feature_arc_name, feature_name);
+		return -EINVAL;
+	}
+
+	if (!finfo) {
+		graph_err("%s: No feature: %s found", arc->feature_arc_name, feature_name);
+		return -EINVAL;
+	}
+
+	/* slot should be in valid range */
+	if (slot >= arc->max_features) {
+		graph_err("%s/%s: Invalid free slot %u(max=%u) for feature", arc->feature_arc_name,
+			feature_name, slot, arc->max_features);
+		return -EINVAL;
+	}
+
+	/* slot should be in range of 0 - 63 */
+	if (slot > (RTE_GRAPH_FEATURE_MAX_PER_ARC - 1)) {
+		graph_err("%s/%s: Invalid slot: %u", arc->feature_arc_name,
+			  feature_name, slot);
+		return -EINVAL;
+	}
+
+	if (finfo->node_index != slot) {
+		graph_err("%s/%s: feature lookup slot mismatch with finfo index: %u and lookup slot: %u",
+			  arc->feature_arc_name, feature_name, finfo->node_index, slot);
+		return -1;
+	}
+
+	/* Get feature from active list */
+	gf = __rte_graph_feature_get(arc, slot, ARC_PASSIVE_LIST(arc));
+	if (gf->this_feature_index != slot) {
+		graph_err("%s: %s received feature_index: %u does not match with saved feature_index: %u",
+			  arc->feature_arc_name, feature_name, slot, gf->this_feature_index);
+		return -1;
+	}
+
+	if (is_enable_disable && (arc->feature_bit_mask_by_index[index] &
+				  RTE_BIT64(slot))) {
+		graph_err("%s: %s already enabled on index: %u",
+			  arc->feature_arc_name, feature_name, index);
+		return -1;
+	}
+
+	if (!is_enable_disable && !arc->runtime_enabled_features) {
+		graph_err("%s: No feature enabled to disable", arc->feature_arc_name);
+		return -1;
+	}
+
+	if (!is_enable_disable && !(arc->feature_bit_mask_by_index[index] & RTE_BIT64(slot))) {
+		graph_err("%s: %s not enabled in bitmask for index: %u",
+			  arc->feature_arc_name, feature_name, index);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+copy_fastpath_user_data(struct rte_graph_feature_arc *arc, uint16_t dest_list_index,
+			uint16_t src_list_index)
+{
+	rte_graph_feature_data_t *sgfd = NULL, *dgfd = NULL;
+	struct rte_graph_feature *sgf = NULL, *dgf = NULL;
+	uint32_t i, j;
+
+	for (i = 0; i < arc->max_features; i++) {
+		sgf = __rte_graph_feature_get(arc, i, src_list_index);
+		dgf = __rte_graph_feature_get(arc, i, dest_list_index);
+		for (j = 0; j < arc->max_indexes; j++) {
+			sgfd = rte_graph_feature_data_get(arc, sgf, j);
+			dgfd = rte_graph_feature_data_get(arc, dgf, j);
+			dgfd->user_data = sgfd->user_data;
+		}
+	}
+}
+
+static void
+refill_feature_fastpath_data(struct rte_graph_feature_arc *arc, uint16_t list_index)
+{
+	struct rte_graph_feature_node_list *finfo = NULL, *prev_finfo = NULL;
+	struct rte_graph_feature_data *gfd = NULL, *prev_gfd = NULL;
+	struct rte_graph_feature *gf = NULL, *prev_gf = NULL;
+	rte_graph_feature_list_t *flist = NULL;
+	uint32_t fi, di, prev_fi;
+	uint64_t bitmask;
+	rte_edge_t edge;
+
+	flist = arc->feature_list[list_index];
+
+	for (di = 0; di < arc->max_indexes; di++) {
+		bitmask = arc->feature_bit_mask_by_index[di];
+		prev_fi = RTE_GRAPH_FEATURE_INVALID;
+		/* for each feature set for index, set fast path data */
+		while (rte_bsf64_safe(bitmask, &fi)) {
+			gf = __rte_graph_feature_get(arc, fi, list_index);
+			gfd = rte_graph_feature_data_get(arc, gf, di);
+			feature_arc_node_info_lookup(arc, fi, &finfo);
+
+			/* If previous feature_index was valid in last loop */
+			if (prev_fi != RTE_GRAPH_FEATURE_INVALID) {
+				prev_gf = __rte_graph_feature_get(arc, prev_fi, list_index);
+				prev_gfd = rte_graph_feature_data_get(arc, prev_gf, di);
+				/*
+				 * Get edge of previous feature node connecting to this feature node
+				 */
+				feature_arc_node_info_lookup(arc, prev_fi, &prev_finfo);
+				if (!get_existing_edge(arc->feature_arc_name,
+						      prev_finfo->feature_node,
+						      finfo->feature_node, &edge)) {
+					feat_dbg("[%s/%s(%2u)/idx:%2u]: %s[%u] = %s",
+						 arc->feature_arc_name,
+						 prev_finfo->feature_node->name, prev_fi, di,
+						 prev_finfo->feature_node->name,
+						 edge, finfo->feature_node->name);
+					/* Copy feature index for next iteration*/
+					gfd->next_edge = edge;
+					prev_fi = fi;
+					/*
+					 * Fill current feature as next enabled
+					 * feature to previous one
+					 */
+					prev_gfd->next_enabled_feature = fi;
+				} else {
+					/* Should not fail */
+					RTE_VERIFY(0);
+				}
+			}
+			/* On first feature edge of the node to be added */
+			if (fi == rte_bsf64(arc->feature_bit_mask_by_index[di])) {
+				if (!get_existing_edge(arc->feature_arc_name, arc->start_node,
+						      finfo->feature_node,
+						      &edge)) {
+					feat_dbg("[%s/%s/%2u/idx:%2u]: 1st feat %s[%u] = %s",
+						 arc->feature_arc_name,
+						 arc->start_node->name, fi, di,
+						 arc->start_node->name, edge,
+						 finfo->feature_node->name);
+					/* Copy feature index for next iteration*/
+					gfd->next_edge = edge;
+					prev_fi = fi;
+					/* Set first feature set array for index*/
+					flist->first_enabled_feature_by_index[di] = fi;
+				} else {
+					/* Should not fail */
+					RTE_VERIFY(0);
+				}
+			}
+			/* Clear current feature index */
+			bitmask &= ~RTE_BIT64(fi);
+		}
+	}
+}
+
+int
+rte_graph_feature_enable(rte_graph_feature_arc_t _arc, uint32_t index, const
+			 char *feature_name, int32_t user_data)
+{
+	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
+	struct rte_graph_feature_node_list *finfo = NULL;
+	struct rte_graph_feature_data *gfd = NULL;
+	rte_graph_feature_rt_list_t passive_list;
+	struct rte_graph_feature *gf = NULL;
+	uint64_t fp_bitmask;
+	uint32_t slot;
+
+	if (rte_graph_feature_validate(_arc, index, feature_name, 1))
+		return -1;
+
+	/** This should not fail as validate() has passed */
+	if (feature_lookup(arc, feature_name, &finfo, &slot))
+		RTE_VERIFY(0);
+
+	if (!arc->runtime_enabled_features)
+		prepare_feature_arc(arc);
+
+	passive_list = ARC_PASSIVE_LIST(arc);
+
+	gf = __rte_graph_feature_get(arc, slot, passive_list);
+	gfd = rte_graph_feature_data_get(arc, gf, index);
+
+	feat_dbg("%s/%s: Enabling feature on list: %u for index: %u at feature slot %u",
+		 arc->feature_arc_name, feature_name, passive_list, index, slot);
+
+	/* Reset feature list */
+	feature_arc_list_reset(arc, passive_list);
+
+	/* Copy user-data */
+	copy_fastpath_user_data(arc, passive_list, arc->active_feature_list);
+
+	/* Set current user-data */
+	gfd->user_data = user_data;
+
+	/* Set bitmask in control path bitmask */
+	rte_bit_relaxed_set64(rte_graph_uint_cast(slot), &arc->feature_bit_mask_by_index[index]);
+	refill_feature_fastpath_data(arc, passive_list);
+
+	/* Set fast path enable bitmask */
+	fp_bitmask = __atomic_load_n(&arc->feature_enable_bitmask[passive_list], __ATOMIC_RELAXED);
+	fp_bitmask |= RTE_BIT64(slot);
+	__atomic_store(&arc->feature_enable_bitmask[passive_list], &fp_bitmask, __ATOMIC_RELAXED);
+
+	/* Slow path updates */
+	arc->runtime_enabled_features++;
+
+	/* Increase feature node info reference count */
+	finfo->ref_count++;
+
+	/* Store release semantics for active_list update */
+	__atomic_store(&arc->active_feature_list, &passive_list, __ATOMIC_RELEASE);
+
+	return 0;
+}
+
+int
+rte_graph_feature_disable(rte_graph_feature_arc_t _arc, uint32_t index, const char *feature_name)
+{
+	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
+	struct rte_graph_feature_data *gfd = NULL;
+	struct rte_graph_feature_node_list *finfo = NULL;
+	rte_graph_feature_rt_list_t passive_list;
+	struct rte_graph_feature *gf = NULL;
+	uint32_t slot;
+
+	if (rte_graph_feature_validate(_arc, index, feature_name, 0))
+		return -1;
+
+	if (feature_lookup(arc, feature_name, &finfo, &slot))
+		return -1;
+
+	passive_list = ARC_PASSIVE_LIST(arc);
+
+	gf = __rte_graph_feature_get(arc, slot, passive_list);
+	gfd = rte_graph_feature_data_get(arc, gf, index);
+
+	feat_dbg("%s/%s: Disabling feature for index: %u at feature slot %u", arc->feature_arc_name,
+		feature_name, index, slot);
+
+	rte_bit_relaxed_clear64(rte_graph_uint_cast(slot), &arc->feature_bit_mask_by_index[index]);
+
+	/* Set fast path enable bitmask */
+	arc->feature_enable_bitmask[passive_list] &= ~(RTE_BIT64(slot));
+
+	/* Reset feature list */
+	feature_arc_list_reset(arc, passive_list);
+
+	/* Copy user-data */
+	copy_fastpath_user_data(arc, passive_list, arc->active_feature_list);
+
+	/* Reset current user-data */
+	gfd->user_data = ~0;
+
+	refill_feature_fastpath_data(arc, passive_list);
+
+	finfo->ref_count--;
+	arc->runtime_enabled_features--;
+
+	/* Store release semantics for active_list update */
+	__atomic_store(&arc->active_feature_list, &passive_list, __ATOMIC_RELEASE);
+
+	return 0;
+}
+
+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 = __feature_arc_main;
+	struct rte_graph_feature_node_list *node_info = NULL;
+
+	while (!STAILQ_EMPTY(&arc->all_features)) {
+		node_info = STAILQ_FIRST(&arc->all_features);
+		STAILQ_REMOVE_HEAD(&arc->all_features, next_feature);
+		free(node_info);
+	}
+	feature_arc_list_destroy(arc->feature_list[0]);
+	feature_arc_list_destroy(arc->feature_list[1]);
+	rte_free(arc->features[0]);
+	rte_free(arc->features[1]);
+
+	dm->feature_arcs[arc->feature_arc_index] = RTE_GRAPH_FEATURE_ARC_INITIALIZER;
+
+	rte_free(arc);
+	return 0;
+}
+
+int
+rte_graph_feature_arc_cleanup(void)
+{
+	rte_graph_feature_arc_main_t *dm = __feature_arc_main;
+	uint32_t iter;
+
+	if (!__feature_arc_main)
+		return -1;
+
+	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
+		if (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)
+			continue;
+
+		rte_graph_feature_arc_destroy((rte_graph_feature_arc_t)dm->feature_arcs[iter]);
+	}
+	free(dm);
+
+	__feature_arc_main = NULL;
+
+	return 0;
+}
+
+int
+rte_graph_feature_arc_lookup_by_name(const char *arc_name, rte_graph_feature_arc_t *_arc)
+{
+	rte_graph_feature_arc_main_t *dm = __feature_arc_main;
+	struct rte_graph_feature_arc *arc = NULL;
+	uint32_t iter;
+
+	if (!__feature_arc_main)
+		return -1;
+
+	for (iter = 0; iter < dm->max_feature_arcs; iter++) {
+		if (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)
+			continue;
+
+		arc = rte_graph_feature_arc_get(dm->feature_arcs[iter]);
+
+		if (strstr(arc_name, arc->feature_arc_name)) {
+			if (_arc)
+				*_arc = (rte_graph_feature_arc_t)arc;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+int
+rte_graph_feature_arc_num_enabled_features(rte_graph_feature_arc_t _arc)
+{
+	struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
+
+	return arc->runtime_enabled_features;
+}
+
+
diff --git a/lib/graph/meson.build b/lib/graph/meson.build
index 0cb15442ab..d916176fb7 100644
--- a/lib/graph/meson.build
+++ b/lib/graph/meson.build
@@ -14,11 +14,13 @@  sources = files(
         'graph_debug.c',
         'graph_stats.c',
         'graph_populate.c',
+        'graph_feature_arc.c',
         'graph_pcap.c',
         'rte_graph_worker.c',
         'rte_graph_model_mcore_dispatch.c',
 )
 headers = files('rte_graph.h', 'rte_graph_worker.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
new file mode 100644
index 0000000000..e3bf4eb73d
--- /dev/null
+++ b/lib/graph/rte_graph_feature_arc.h
@@ -0,0 +1,373 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_FEATURE_ARC_H_
+#define _RTE_GRAPH_FEATURE_ARC_H_
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_compat.h>
+#include <rte_debug.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ *
+ * rte_graph_feature_arc.h
+ *
+ * Define APIs and structures/variables with respect to feature arc
+ *
+ * - Feature arc(s)
+ * - Feature(s)
+ *
+ * A feature arc represents an ordered list of features/protocol-nodes at a
+ * given networking layer. Feature arc provides a high level abstraction to
+ * connect various *rte_graph* nodes, designated as *feature nodes*, and
+ * allowing steering of packets across these feature nodes fast path processing
+ * in a generic manner. In a typical network stack, often a protocol or feature
+ * must be first enabled on a given interface, before any packet is steered
+ * towards it for feature processing. For eg: incoming IPv4 packets are sent to
+ * routing sub-system only after a valid IPv4 address is assigned to the
+ * received interface. In other words, often packets needs to be steered across
+ * features not based on the packet content but based on whether a feature is
+ * enable or disable on a given incoming/outgoing interface. Feature arc
+ * provides mechanism to enable/disable feature(s) on each interface at runtime
+ * and allow seamless packet steering across runtime enabled feature nodes in
+ * fast path.
+ *
+ * Feature arc also provides a way to steer packets from standard nodes to
+ * custom/user-defined *feature nodes* without any change in standard node's
+ * fast path functions
+ *
+ * On a given interface multiple feature(s) might be enabled in a particular
+ * feature arc. For instance, both "ipv4-output" and "IPsec policy output"
+ * features may be enabled on "eth0" interface in "L3-output" feature arc.
+ * Similarly, "ipv6-output" and "ipsec-output" may be enabled on "eth1"
+ * interface in same "L3-output" feature arc.
+ *
+ * When multiple features are present in a given feature arc, its imperative
+ * to allow each feature processing in a particular sequential order. For
+ * instance, in "L3-input" feature arc it may be required to run "IPsec
+ * input" feature first, for packet decryption, before "ip-lookup".  So a
+ * sequential order must be maintained among features present in a feature arc.
+ *
+ * Features are enabled/disabled multiple times at runtime to some or all
+ * available interfaces present in the system. Features can be enabled/disabled
+ * even after @b rte_graph_create() is called. Enable/disabling features on one
+ * interface is independent of other interface.
+ *
+ * A given feature might consume packet (if it's configured to consume) or may
+ * forward it to next enabled feature. For instance, "IPsec input" feature may
+ * consume/drop all packets with "Protect" policy action while all packets with
+ * policy action as "Bypass" may be forwarded to next enabled feature (with in
+ * same feature arc)
+ *
+ * This library facilitates rte graph based applications to steer packets in
+ * fast path to different feature nodes with-in a feature arc and support all
+ * functionalities described above
+ *
+ * In order to use feature-arc APIs, applications needs to do following in
+ * control path:
+ * - Initialize feature arc library via rte_graph_feature_arc_init()
+ * - Create feature arc via rte_graph_feature_arc_create()
+ * - *Before calling rte_graph_create()*, features must be added to feature-arc
+ *   via rte_graph_feature_add(). rte_graph_feature_add() allows adding
+ *   features in a sequential order with "runs_after" and "runs_before"
+ *   constraints.
+ * - Post rte_graph_create(), features can be enabled/disabled at runtime on
+ *   any interface via rte_graph_feature_enable()/rte_graph_feature_disable()
+ * - Feature arc can be destroyed via rte_graph_feature_arc_destroy()
+ *
+ * In fast path, APIs are provided to steer packets towards feature path from
+ * - start_node (provided as an argument to rte_graph_feature_arc_create())
+ * - feature nodes (which are added via rte_graph_feature_add())
+ *
+ * For typical steering of packets across feature nodes, application required
+ * to know "rte_edges" which are saved in feature data object. Feature data
+ * object is unique for every interface per feature with in a feature arc.
+ *
+ * When steering packets from start_node to feature node:
+ * - rte_graph_feature_arc_first_feature_get() provides first enabled feature.
+ * - Next rte_edge from start_node to first enabled feature can be obtained via
+ *   rte_graph_feature_arc_feature_set()
+ *
+ * rte_mbuf can carry [current feature, index] from start_node of an arc to other
+ * feature nodes
+ *
+ * In feature node, application can get 32-bit user_data
+ * via_rte_graph_feature_user_data_get() which is provided in
+ * rte_graph_feature_enable(). User data can hold feature specific cookie like
+ * IPsec policy database index (if more than one are supported)
+ *
+ * If feature node is not consuming packet, next enabled feature and next
+ * rte_edge can be obtained via rte_graph_feature_arc_next_feature_get()
+ *
+ * It is application responsibility to ensure that at-least *last feature*(or sink
+ * feature) must be enabled from where packet can exit feature-arc path, if
+ * *NO* intermediate feature is consuming the packet and it has reached till
+ * the end of feature arc path
+ *
+ * Synchronization among cores
+ * ---------------------------
+ * Subsequent calls to rte_graph_feature_enable() is allowed while worker cores
+ * are processing in rte_graph_walk() loop. However, for
+ * rte_graph_feature_disable() application must use RCU based synchronization
+ */
+
+/**< Initializer value for rte_graph_feature_arc_t */
+#define RTE_GRAPH_FEATURE_ARC_INITIALIZER ((rte_graph_feature_arc_t)UINT64_MAX)
+
+/** Max number of features supported in a given feature arc */
+#define RTE_GRAPH_FEATURE_MAX_PER_ARC 64
+
+/** Length of feature arc name */
+#define RTE_GRAPH_FEATURE_ARC_NAMELEN RTE_NODE_NAMESIZE
+
+/** @internal */
+#define rte_graph_feature_cast(x) ((rte_graph_feature_t)x)
+
+/**< Initializer value for rte_graph_feature_arc_t */
+#define RTE_GRAPH_FEATURE_INVALID rte_graph_feature_cast(UINT8_MAX)
+
+/** rte_graph feature arc object */
+typedef uint64_t rte_graph_feature_arc_t;
+
+/** rte_graph feature object */
+typedef uint8_t rte_graph_feature_t;
+
+/** runtime active feature list index with in feature arc*/
+typedef uint8_t rte_graph_feature_rt_list_t;
+
+/** per feature arc monotonically increasing counter to synchronize fast path APIs */
+typedef uint16_t rte_graph_feature_counter_t;
+
+/**
+ * Initialize feature arc subsystem
+ *
+ * @param max_feature_arcs
+ *   Maximum number of feature arcs required to be supported
+ *
+ * @return
+ *  0: Success
+ * <0: Failure
+ */
+__rte_experimental
+int rte_graph_feature_arc_init(int max_feature_arcs);
+
+/**
+ * Create a feature arc
+ *
+ * @param feature_arc_name
+ *   Feature arc name with max length of @ref RTE_GRAPH_FEATURE_ARC_NAMELEN
+ * @param max_features
+ *   Maximum number of features to be supported in this feature arc
+ * @param max_indexes
+ *   Maximum number of interfaces/ports/indexes to be supported
+ * @param start_node
+ *   Base node where this feature arc's features are checked in fast path
+ * @param[out] _arc
+ *  Feature arc object
+ *
+ * @return
+ *  0: Success
+ * <0: Failure
+ */
+__rte_experimental
+int rte_graph_feature_arc_create(const char *feature_arc_name, int max_features, int max_indexes,
+				 struct rte_node_register *start_node,
+				 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
+ *
+ * @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. For instance
+ *
+ * 1. Add first feature node: "ipv4-input" to input arc
+ *    rte_graph_feature_add(ipv4_input_arc, "ipv4-input", NULL, NULL);
+ *
+ * 2. Add "ipsec-input" feature node after "ipv4-input" node
+ *    rte_graph_feature_add(ipv4_input_arc, "ipsec-input", "ipv4-input", NULL);
+ *
+ * 3. Add "ipv4-pre-classify-input" node before "ipv4-input" node
+ *    rte_graph_feature_add(ipv4_input_arc, "ipv4-pre-classify-input"", NULL, "ipv4-input");
+ *
+ * 4. Add "acl-classify-input" node after ipv4-input but before ipsec-input
+ *    rte_graph_feature_add(ipv4_input_arc, "acl-classify-input", "ipv4-input", "ipsec-input");
+ *
+ * @param _arc
+ *   Feature arc handle returned from @ref rte_graph_feature_arc_create()
+ * @param feature_node
+ *   Graph node representing feature. On success, feature_node is next_node of
+ *   feature_arc->start_node
+ * @param runs_after
+ *   Add this feature_node after already added "runs_after". Creates
+ *   start_node -> runs_after -> this_feature sequence
+ * @param runs_before
+ *  Add this feature_node before already added "runs_before". Creates
+ *  start_node -> this_feature -> runs_before sequence
+ *
+ * <I> Must be called before rte_graph_create() </I>
+ * <I> rte_graph_feature_add() is not allowed after call to
+ * rte_graph_feature_enable() so all features must be added before they can be
+ * enabled </I>
+ *
+ * @return
+ *  0: Success
+ * <0: Failure
+ */
+__rte_experimental
+int rte_graph_feature_add(rte_graph_feature_arc_t _arc, struct rte_node_register *feature_node,
+		    const char *runs_after, const char *runs_before);
+
+/**
+ * Enable feature within a feature arc
+ *
+ * Must be called after @b rte_graph_create().
+ *
+ * @param _arc
+ *   Feature arc object returned by @ref rte_graph_feature_arc_create or @ref
+ *   rte_graph_feature_arc_lookup_by_name
+ * @param index
+ *   Application specific index. Can be corresponding to interface_id/port_id etc
+ * @param feature_name
+ *   Name of the node which is already added via @ref rte_graph_feature_add
+ * @param user_data
+ *   Application specific data which is retrieved in fast path
+ *
+ * @return
+ *  0: Success
+ * <0: Failure
+ */
+__rte_experimental
+int rte_graph_feature_enable(rte_graph_feature_arc_t _arc, uint32_t index, const char *feature_name,
+			     int32_t user_data);
+
+/**
+ * Validate whether subsequent enable/disable feature would succeed or not.
+ * API is thread-safe
+ *
+ * @param _arc
+ *   Feature arc object returned by @ref rte_graph_feature_arc_create or @ref
+ *   rte_graph_feature_arc_lookup_by_name
+ * @param index
+ *   Application specific index. Can be corresponding to interface_id/port_id etc
+ * @param feature_name
+ *   Name of the node which is already added via @ref rte_graph_feature_add
+ * @param is_enable_disable
+ *   If 1, validate whether subsequent @ref rte_graph_feature_enable would pass or not
+ *   If 0, validate whether subsequent @ref rte_graph_feature_disable would pass or not
+ *
+ * @return
+ *  0: Subsequent enable/disable API would pass
+ * <0: Subsequent enable/disable API would not pass
+ */
+__rte_experimental
+int rte_graph_feature_validate(rte_graph_feature_arc_t _arc, uint32_t index,
+			       const char *feature_name, int is_enable_disable);
+
+/**
+ * Disable already enabled feature within a feature arc
+ *
+ * Must be called after @b rte_graph_create(). API is *NOT* Thread-safe
+ *
+ * @param _arc
+ *   Feature arc object returned by @ref rte_graph_feature_arc_create or @ref
+ *   rte_graph_feature_arc_lookup_by_name
+ * @param index
+ *   Application specific index. Can be corresponding to interface_id/port_id etc
+ * @param feature_name
+ *   Name of the node which is already added via @ref rte_graph_feature_add
+ *
+ * @return
+ *  0: Success
+ * <0: Failure
+ */
+__rte_experimental
+int rte_graph_feature_disable(rte_graph_feature_arc_t _arc, uint32_t index,
+			      const char *feature_name);
+
+/**
+ * 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 currently enabled within a featur-arc
+ *
+ * @param _arc
+ *  Feature arc object
+ *
+ * @return: Number of enabled features
+ */
+__rte_experimental
+int rte_graph_feature_arc_num_enabled_features(rte_graph_feature_arc_t _arc);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
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..6019d74853
--- /dev/null
+++ b/lib/graph/rte_graph_feature_arc_worker.h
@@ -0,0 +1,548 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 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>
+
+/**
+ * @file
+ *
+ * rte_graph_feature_arc_worker.h
+ *
+ * Defines fast path structure
+ */
+
+#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;
+
+	/** node representing feature */
+	struct rte_node_register *feature_node;
+
+	/** How many indexes/interfaces using this feature */
+	int32_t ref_count;
+
+	/* node_index in list (after feature_enable())*/
+	uint32_t node_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;
+};
+
+/**
+ * Fast path holding rte_edge_t and next enabled feature for an feature
+ */
+typedef struct __rte_packed rte_graph_feature_data {
+	/* next node to which current mbuf should go*/
+	rte_edge_t next_edge;
+
+	/* next enabled feature on this arc for current index */
+	union {
+		uint16_t reserved;
+		struct {
+			rte_graph_feature_t next_enabled_feature;
+		};
+	};
+
+	/* user_data */
+	int32_t user_data;
+} rte_graph_feature_data_t;
+
+/**
+ * Fast path feature structure. Holds re_graph_feature_data_t per index
+ */
+struct __rte_cache_aligned rte_graph_feature {
+	uint16_t this_feature_index;
+
+	/* Array of size arc->feature_data_size
+	 * [data-index-0][data-index-1]...
+	 * Each index of size: sizeof(rte_graph_feature_data_t)
+	 */
+	uint8_t feature_data_by_index[];
+};
+
+/**
+ * fast path cache aligned feature list holding all features
+ * There are two feature lists: active, passive
+ *
+ * Fast APIs works on active list while control plane updates passive list
+ * A atomic update to arc->active_feature_list is done to switch between active
+ * and passive
+ */
+typedef struct __rte_cache_aligned rte_graph_feature_list {
+	/**
+	 * fast path array holding per_feature data.
+	 * Duplicate entry as feature-arc also hold this pointer
+	 * arc->features[]
+	 *
+	 *<-------------feature-0 ---------><CEIL><---------feature-1 -------------->...
+	 *[index-0][index-1]...[max_index-1]      [index-0][index-1] ...[max_index-1]...
+	 */
+	struct rte_graph_feature *indexed_by_features;
+	/*
+	 * fast path array holding first enabled feature per index
+	 * (Required in start_node. In non start_node, mbuf can hold next enabled
+	 * feature)
+	 */
+	rte_graph_feature_t first_enabled_feature_by_index[];
+} rte_graph_feature_list_t;
+
+/**
+ * rte_graph feature arc object
+ *
+ * A feature-arc can only hold RTE_GRAPH_FEATURE_MAX_PER_ARC features but no
+ * limit to interface index
+ *
+ * Representing a feature arc holding all features which are enabled/disabled
+ * on any interfaces
+ */
+struct __rte_cache_aligned rte_graph_feature_arc {
+	/* First 64B is fast path variables */
+	RTE_MARKER fast_path_variables;
+
+	/** runtime active feature list */
+	rte_graph_feature_rt_list_t active_feature_list;
+
+	/* Actual Size of feature_list0 */
+	uint16_t feature_list_size;
+
+	/**
+	 * Size each feature in fastpath.
+	 * sizeof(arc->active_list->indexed_by_feature[0])
+	 */
+	uint16_t feature_size;
+
+	/* Size of arc->max_index * sizeof(rte_graph_feature_data_t) */
+	uint16_t feature_data_size;
+
+	/**
+	 * Fast path bitmask indicating if a feature is enabled or not Number
+	 * of bits: RTE_GRAPH_FEATURE_MAX_PER_ARC
+	 */
+	uint64_t feature_enable_bitmask[2];
+	rte_graph_feature_list_t *feature_list[2];
+	struct rte_graph_feature *features[2];
+
+	/** index in feature_arc_main */
+	uint16_t feature_arc_index;
+
+	uint16_t reserved[3];
+
+	/** Slow path variables follows*/
+	RTE_MARKER slow_path_variables;
+
+	/** feature arc name */
+	char feature_arc_name[RTE_GRAPH_FEATURE_ARC_NAMELEN];
+
+	/** All feature lists */
+	STAILQ_HEAD(, rte_graph_feature_node_list) all_features;
+
+	uint32_t runtime_enabled_features;
+
+	/** Back pointer to feature_arc_main */
+	void *feature_arc_main;
+
+	/* start_node */
+	struct rte_node_register *start_node;
+
+	/* maximum number of features supported by this arc */
+	uint32_t max_features;
+
+	/* maximum number of index supported by this arc */
+	uint32_t max_indexes;
+
+	/* Slow path bit mask per feature per index */
+	uint64_t feature_bit_mask_by_index[];
+};
+
+/** Feature arc main */
+typedef struct feature_arc_main {
+	/** number of feature arcs created by application */
+	uint32_t num_feature_arcs;
+
+	/** max features arcs allowed */
+	uint32_t max_feature_arcs;
+
+	/** feature arcs */
+	rte_graph_feature_arc_t feature_arcs[];
+} rte_graph_feature_arc_main_t;
+
+/** @internal Get feature arc pointer from object */
+#define rte_graph_feature_arc_get(arc) ((struct rte_graph_feature_arc *)arc)
+
+extern rte_graph_feature_arc_main_t *__feature_arc_main;
+
+/**
+ * API to know if feature is valid or not
+ */
+
+static __rte_always_inline int
+rte_graph_feature_is_valid(rte_graph_feature_t feature)
+{
+	return (feature != RTE_GRAPH_FEATURE_INVALID);
+}
+
+/**
+ * Get rte_graph_feature object with no checks
+ *
+ * @param arc
+ *   Feature arc pointer
+ * @param feature
+ *   Feature index
+ * @param feature_list
+ *   active feature list retrieved from rte_graph_feature_arc_has_any_feature()
+ *   or rte_graph_feature_arc_has_feature()
+ *
+ *   @return
+ *     Internal feature object.
+ */
+static __rte_always_inline struct rte_graph_feature *
+__rte_graph_feature_get(struct rte_graph_feature_arc *arc, rte_graph_feature_t feature,
+			const rte_graph_feature_rt_list_t feature_list)
+{
+	return ((struct rte_graph_feature *)((uint8_t *)(arc->features[feature_list] +
+					     (feature * arc->feature_size))));
+}
+
+/**
+ * Get rte_graph_feature object for a given interface/index from feature arc
+ *
+ * @param arc
+ *   Feature arc pointer
+ * @param feature
+ *   Feature index
+ *
+ *   @return
+ *     Internal feature object.
+ */
+static __rte_always_inline struct rte_graph_feature *
+rte_graph_feature_get(struct rte_graph_feature_arc *arc, rte_graph_feature_t feature)
+{
+	RTE_VERIFY(feature < arc->max_features);
+
+	if (likely(rte_graph_feature_is_valid(feature)))
+		return __rte_graph_feature_get(arc, feature, arc->active_feature_list);
+
+	return NULL;
+}
+
+static __rte_always_inline rte_graph_feature_data_t *
+__rte_graph_feature_data_get(struct rte_graph_feature_arc *arc, struct rte_graph_feature *feature,
+			     uint8_t index)
+{
+	RTE_SET_USED(arc);
+	return ((rte_graph_feature_data_t *)(feature->feature_data_by_index +
+					   (index * sizeof(rte_graph_feature_data_t))));
+}
+
+/**
+ * Get rte_graph feature data object for a index in feature
+ *
+ * @param arc
+ *   feature arc
+ * @param feature
+ *  Pointer to feature object
+ * @param index
+ *  Index of feature maintained in slow path linked list
+ *
+ * @return
+ *   Valid feature data
+ */
+static __rte_always_inline rte_graph_feature_data_t *
+rte_graph_feature_data_get(struct rte_graph_feature_arc *arc, struct rte_graph_feature *feature,
+			   uint8_t index)
+{
+	if (likely(index < arc->max_indexes))
+		return __rte_graph_feature_data_get(arc, feature, index);
+
+	RTE_VERIFY(0);
+}
+
+/**
+ * Fast path API to check if any feature enabled on a feature arc
+ * Typically from arc->start_node process function
+ *
+ * @param arc
+ *   Feature arc object
+ * @param[out] plist
+ *   Pointer to runtime active feature list which needs to be provided to other
+ *   fast path APIs
+ *
+ * @return
+ * 0: If no feature enabled
+ * Non-Zero: Bitmask of features enabled. plist is valid
+ *
+ */
+static __rte_always_inline uint64_t
+rte_graph_feature_arc_has_any_feature(struct rte_graph_feature_arc *arc,
+				      rte_graph_feature_rt_list_t *plist)
+{
+	*plist = __atomic_load_n(&arc->active_feature_list, __ATOMIC_RELAXED);
+
+	return (__atomic_load_n(arc->feature_enable_bitmask + (uint8_t)*plist,
+				__ATOMIC_RELAXED));
+}
+
+/**
+ * Fast path API to check if provided feature is enabled on any interface/index
+ * or not
+ *
+ * @param arc
+ *   Feature arc object
+ * @param feature
+ *   Input rte_graph_feature_t that needs to be checked
+ * @param[out] plist
+ *  Returns active list to caller which needs to be provided to other fast path
+ *  APIs
+ *
+ * @return
+ * 1: If feature is enabled in arc
+ * 0: If feature is not enabled in arc
+ */
+static __rte_always_inline int
+rte_graph_feature_arc_has_feature(struct rte_graph_feature_arc *arc,
+				  rte_graph_feature_t feature,
+				  rte_graph_feature_rt_list_t *plist)
+{
+	uint64_t bitmask = RTE_BIT64(feature);
+
+	*plist = __atomic_load_n(&arc->active_feature_list, __ATOMIC_RELAXED);
+
+	return (bitmask & __atomic_load_n(arc->feature_enable_bitmask + (uint8_t)*plist,
+					  __ATOMIC_RELAXED));
+}
+
+/**
+ * Prefetch feature arc fast path cache line
+ *
+ * @param arc
+ *   RTE_GRAPH feature arc object
+ */
+static __rte_always_inline void
+rte_graph_feature_arc_prefetch(struct rte_graph_feature_arc *arc)
+{
+	rte_prefetch0((void *)&arc->fast_path_variables);
+}
+
+/**
+ * Prefetch feature related fast path cache line
+ *
+ * @param arc
+ *   RTE_GRAPH feature arc object
+ * @param list
+ *  Pointer to runtime active feature list from rte_graph_feature_arc_has_any_feature();
+ * @param feature
+ *   Pointer to feature object
+ */
+static __rte_always_inline void
+rte_graph_feature_arc_feature_prefetch(struct rte_graph_feature_arc *arc,
+				       const rte_graph_feature_rt_list_t list,
+				       rte_graph_feature_t feature)
+{
+	/* feature cache line */
+	if (likely(rte_graph_feature_is_valid(feature)))
+		rte_prefetch0((void *)__rte_graph_feature_get(arc, feature, list));
+}
+
+/**
+ * Prefetch feature data upfront. Perform sanity
+ *
+ * @param _arc
+ *   RTE_GRAPH feature arc object
+ * @param list
+ *  Pointer to runtime active feature list from rte_graph_feature_arc_has_any_feature();
+ * @param feature
+ *   Pointer to feature object returned from @ref
+ *   rte_graph_feature_arc_first_feature_get()
+ * @param index
+ *   Interface/index
+ */
+static __rte_always_inline void
+rte_graph_feature_arc_data_prefetch(struct rte_graph_feature_arc *arc,
+				    const rte_graph_feature_rt_list_t list,
+				    rte_graph_feature_t feature, uint32_t index)
+{
+	if (likely(rte_graph_feature_is_valid(feature)))
+		rte_prefetch0((void *)((uint8_t *)arc->features[list] +
+			      offsetof(struct rte_graph_feature, feature_data_by_index) +
+			      (index * sizeof(rte_graph_feature_data_t))));
+}
+
+/**
+ * Fast path API to get first enabled feature on interface index
+ * Typically required in arc->start_node so that from returned feature,
+ * feature-data can be retrieved to steer packets
+ *
+ * @param arc
+ *   Feature arc object
+ * @param list
+ *   Pointer to runtime active feature list from
+ *   rte_graph_feature_arc_has_any_feature() or
+ *   rte_graph_feature_arc_has_feature()
+ * @param index
+ *  Interface Index
+ * @param[out] feature
+ *  Pointer to rte_graph_feature_t.
+ *
+ * @return
+ * 0. Success. feature field is valid
+ * 1. Failure. feature field is invalid
+ *
+ */
+static __rte_always_inline int
+rte_graph_feature_arc_first_feature_get(struct rte_graph_feature_arc *arc,
+					const rte_graph_feature_rt_list_t list,
+					uint32_t index,
+					rte_graph_feature_t *feature)
+{
+	struct rte_graph_feature_list *feature_list = arc->feature_list[list];
+
+	*feature = feature_list->first_enabled_feature_by_index[index];
+
+	return rte_graph_feature_is_valid(*feature);
+}
+
+/**
+ * Fast path API to get next enabled feature on interface index with provided
+ * input feature
+ *
+ * @param arc
+ *   Feature arc object
+ * @param list
+ *   Pointer to runtime active feature list from
+ *   rte_graph_feature_arc_has_any_feature() or
+ * @param index
+ *   Interface Index
+ * @param[in][out] feature
+ *   Pointer to rte_graph_feature_t. Input feature set to next enabled feature
+ *   after success return
+ * @param[out] next_edge
+ *    Edge from current feature to next feature. Valid only if next feature is valid
+ *
+ * @return
+ * 0. Success. next enabled feature is valid.
+ * 1. Failure. next enabled feature is invalid
+ */
+static __rte_always_inline int
+rte_graph_feature_arc_next_feature_get(struct rte_graph_feature_arc *arc,
+				       const rte_graph_feature_rt_list_t list,
+				       uint32_t index,
+				       rte_graph_feature_t *feature,
+				       rte_edge_t *next_edge)
+{
+	rte_graph_feature_data_t *feature_data = NULL;
+	struct rte_graph_feature *f = NULL;
+
+	if (likely(rte_graph_feature_is_valid(*feature))) {
+		f = __rte_graph_feature_get(arc, *feature, list);
+		feature_data = rte_graph_feature_data_get(arc, f, index);
+		*feature = feature_data->next_enabled_feature;
+		*next_edge = feature_data->next_edge;
+		return (*feature == RTE_GRAPH_FEATURE_INVALID);
+	}
+
+	return 1;
+}
+
+/**
+ * Set fields with respect to first enabled feature in an arc and return edge
+ * Typically returned feature and interface index must be saved in rte_mbuf
+ * structure to pass this information to next feature node
+ *
+ * @param arc
+ *   Feature arc object
+ * @param list
+ *   Pointer to runtime active feature list from rte_graph_feature_arc_has_any_feature();
+ * @param index
+ *  Index (of interface)
+ * @param[out] gf
+ *  Pointer to rte_graph_feature_t. Valid if API returns Success
+ * @param[out] edge
+ *  Edge to steer packet from arc->start_node to first enabled feature. Valid
+ *  only if API returns Success
+ *
+ * @return
+ * 0: If valid feature is set by API
+ * 1: If valid feature is NOT set by API
+ */
+static __rte_always_inline rte_graph_feature_t
+rte_graph_feature_arc_feature_set(struct rte_graph_feature_arc *arc,
+				  const rte_graph_feature_rt_list_t list,
+				  uint32_t index,
+				  rte_graph_feature_t *gf,
+				  rte_edge_t *edge)
+{
+	struct rte_graph_feature_list *feature_list = arc->feature_list[list];
+	struct rte_graph_feature_data *feature_data = NULL;
+	struct rte_graph_feature *feature = NULL;
+	rte_graph_feature_t f;
+
+	/* reset */
+	*gf = RTE_GRAPH_FEATURE_INVALID;
+	f = feature_list->first_enabled_feature_by_index[index];
+
+	if (unlikely(rte_graph_feature_is_valid(f))) {
+		feature = __rte_graph_feature_get(arc, f, list);
+		feature_data = rte_graph_feature_data_get(arc, feature, index);
+		*gf = f;
+		*edge = feature_data->next_edge;
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * Get user data corresponding to current feature set by application in
+ * rte_graph_feature_enable()
+ *
+ * @param arc
+ *  Feature arc object
+ * @param list
+ *  Pointer to runtime active feature list from rte_graph_feature_arc_has_any_feature();
+ * @param feature
+ *  Feature index
+ * @param index
+ *  Interface index
+ *
+ *  @return
+ *  UINT32_MAX: Failure
+ *  Valid user data: Success
+ */
+static __rte_always_inline uint32_t
+rte_graph_feature_user_data_get(struct rte_graph_feature_arc *arc,
+				const rte_graph_feature_rt_list_t list,
+				rte_graph_feature_t feature,
+				uint32_t index)
+{
+	rte_graph_feature_data_t *fdata = NULL;
+	struct rte_graph_feature *f = NULL;
+
+	if (likely(rte_graph_feature_is_valid(feature))) {
+		f = __rte_graph_feature_get(arc, feature, list);
+		fdata = rte_graph_feature_data_get(arc, f, index);
+		return fdata->user_data;
+	}
+
+	return UINT32_MAX;
+}
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/graph/version.map b/lib/graph/version.map
index 2c83425ddc..82b2469fba 100644
--- a/lib/graph/version.map
+++ b/lib/graph/version.map
@@ -52,3 +52,20 @@  DPDK_25 {
 
 	local: *;
 };
+
+EXPERIMENTAL {
+	global:
+
+	# added in 24.11
+	rte_graph_feature_arc_init;
+	rte_graph_feature_arc_create;
+	rte_graph_feature_arc_lookup_by_name;
+	rte_graph_feature_add;
+	rte_graph_feature_enable;
+	rte_graph_feature_validate;
+	rte_graph_feature_disable;
+	rte_graph_feature_lookup;
+	rte_graph_feature_arc_destroy;
+	rte_graph_feature_arc_cleanup;
+	rte_graph_feature_arc_num_enabled_features;
+};