Hi Kiran,
On Wed, Jun 4, 2025 at 5:06 PM Kiran Kumar Kokkilagadda <kirankum...@marvell.com> wrote: > > > > > -----Original Message----- > > From: Nitin Saxena <nsax...@marvell.com> > > Sent: Wednesday, June 4, 2025 3:43 PM > > To: Jerin Jacob <jer...@marvell.com>; Kiran Kumar Kokkilagadda > > <kirankum...@marvell.com>; Nithin Kumar Dabilpuram > > <ndabilpu...@marvell.com>; Zhirun Yan <yanzhirun_...@163.com>; Robin > > Jarry <rja...@redhat.com>; Christophe Fontaine <cfont...@redhat.com> > > Cc: dev@dpdk.org; Nitin Saxena <nsaxen...@gmail.com> > > Subject: [PATCH v10 4/7] graph: add feature enable/disable APIs > > > > This patch also adds feature arc fast path APIs as well along with > > documentation > > > > Signed-off-by: Nitin Saxena <nsax...@marvell.com> > > --- > > doc/guides/prog_guide/graph_lib.rst | 180 ++++++ > > lib/graph/graph_feature_arc.c | 701 ++++++++++++++++++++++- > > lib/graph/meson.build | 2 +- > > lib/graph/rte_graph_feature_arc.h | 134 ++++- > > lib/graph/rte_graph_feature_arc_worker.h | 305 +++++++++- > > 5 files changed, 1314 insertions(+), 8 deletions(-) > > > > diff --git a/doc/guides/prog_guide/graph_lib.rst > > b/doc/guides/prog_guide/graph_lib.rst > > index c9ac9e7ae0..fef384d836 100644 > > --- a/doc/guides/prog_guide/graph_lib.rst > > +++ b/doc/guides/prog_guide/graph_lib.rst > > @@ -609,6 +609,8 @@ provides application to overload default node path by > > providing hook > > points(like netfilter) to insert out-of-tree or another protocol nodes in > > packet path. > > > > +.. _Control_Data_Plane_Synchronization: > > + > > Control/Data plane synchronization > > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > > Feature arc does not stop worker cores for any runtime control plane > > updates. > > @@ -839,6 +841,11 @@ which might have allocated during feature enable. > > notifier_cb() is called, at runtime, for every enable/disable of > > ``[feature, > > index]`` from control thread. > > > > +If RCU is provided to enable/disable APIs, notifier_cb() is called after > > +``rte_rcu_qsbr_synchronize()``. Application also needs to call > > +``rte_rcu_qsbr_quiescent()`` in worker thread (preferably after every > > +``rte_graph_walk()`` iteration) > > + > > override_index_cb() > > .................... > > A feature arc is :ref:`registered<Feature_Arc_Registration>` to operate on > > @@ -869,3 +876,176 @@ sub-system. If not called, feature arc has no impact > > on application. > > ``rte_graph_feature_arc_init()`` API should be called before > > ``rte_graph_create()``. If not called, feature arc is a ``NOP`` to > > application. > > + > > +Runtime feature enable/disable > > +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > > +A feature can be enabled or disabled at runtime from control thread using > > +``rte_graph_feature_enable()`` and ``rte_graph_feature_disable()`` APIs > > +respectively. > > + > > +.. code-block:: c > > + > > + struct rte_rcu_qsbr *rcu_qsbr = app_get_rcu_qsbr(); > > + rte_graph_feature_arc_t _arc; > > + uint16_t app_cookie; > > + > > + if (rte_graph_feature_arc_lookup_by_name("Arc1", &_arc) < 0) { > > + RTE_LOG(ERR, GRAPH, "Arc1 not found\n"); > > + return -ENOENT; > > + } > > + app_cookie = 100; /* Specific to ['Feature-1`, `port-0`]*/ > > + > > + /* Enable feature */ > > + rte_graph_feature_enable(_arc, 0 /* port-0 */, > > + "Feature-1" /* Name of the node feature */, > > + app_cookie, rcu_qsbr); > > + > > + /* Disable feature */ > > + rte_graph_feature_disable(_arc, 0 /* port-0 */, > > + "Feature-1" /* Name of the node feature*/, > > + rcu_qsbr); > > + > > +.. note:: > > + > > + RCU argument is optional argument to enable/disable APIs. See > > + :ref:`control/data plane > > + synchronization<Control_Data_Plane_Synchronization>` and > > + :ref:`notifier_cb<Feature_Notifier_Cb>` for more details on when RCU is > > + needed. > > + > > +Fast path traversal rules > > +^^^^^^^^^^^^^^^^^^^^^^^^^ > > +``Start node`` > > +************** > > +If feature arc is :ref:`initialized<Feature_Arc_Initialization>`, > > +``start_node_feature_process_fn()`` will be called by ``rte_graph_walk()`` > > +instead of node's original ``process()``. This function should allow > > packets to > > +enter arc path whenever any feature is enabled at runtime > > + > > +.. code-block:: c > > + > > + static int nodeA_init(const struct rte_graph *graph, struct rte_node > > *node) > > + { > > + rte_graph_feature_arc_t _arc; > > + > > + if (rte_graph_feature_arc_lookup_by_name("Arc1", &_arc) < 0) { > > + RTE_LOG(ERR, GRAPH, "Arc1 not found\n"); > > + return -ENOENT; > > + } > > + > > + /* Save arc in node context */ > > + node->ctx = _arc; > > + return 0; > > + } > > + > > + int nodeA_process_inline(struct rte_graph *graph, struct rte_node > > *node, > > + void **objs, uint16_t nb_objs, > > + struct rte_graph_feature_arc *arc, > > + const int do_arc_processing) > > + { > > + for(uint16_t i = 0; i < nb_objs; i++) { > > + struct rte_mbuf *mbuf = objs[i]; > > + rte_edge_t edge_to_child = 0; /* By default to Node-B */ > > + > > + if (do_arc_processing) { > > + struct rte_graph_feature_arc_mbuf_dynfields *dyn = > > + rte_graph_feature_arc_mbuf_dynfields_get(mbuf, arc- > > >mbuf_dyn_offset); > > + > > + if (rte_graph_feature_data_first_feature_get(mbuf, > > mbuf->port, > > + > > &dyn->feature_data, > > + > > &edge_to_child) < 0) { > > + > > + /* Some feature is enabled, edge_to_child is > > overloaded*/ > > + } > > + } > > + /* enqueue as usual */ > > + rte_node_enqueue_x1(graph, node, mbuf, edge_to_child); > > + } > > + } > > + > > + int nodeA_feature_process_fn(struct rte_graph *graph, struct rte_node > > *node, > > + void **objs, uint16_t nb_objs) > > + { > > + struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(node- > > >ctx); > > + > > + if (unlikely(rte_graph_feature_arc_has_any_feature(arc))) > > + return nodeA_process_inline(graph, node, objs, nb_objs, arc, 1 > > /* do > > arc processing */); > > + else > > + return nodeA_process_inline(graph, node, objs, nb_objs, NULL, > > 0 /* > > skip arc processing */); > > + } > > + > > +``Feature nodes`` > > +***************** > > +Following code-snippet explains fast path traversal rule for ``Feature-1`` > > +:ref:`feature node<Feature_Nodes>` shown in :ref:`figure<Figure_Arc_2>`. > > + > > +.. code-block:: c > > + > > + static int Feature1_node_init(const struct rte_graph *graph, struct > > rte_node *node) > > + { > > + rte_graph_feature_arc_t _arc; > > + > > + if (rte_graph_feature_arc_lookup_by_name("Arc1", &_arc) < 0) { > > + RTE_LOG(ERR, GRAPH, "Arc1 not found\n"); > > + return -ENOENT; > > + } > > + > > + /* Save arc in node context */ > > + node->ctx = _arc; > > + return 0; > > + } > > + > > + int feature1_process_inline(struct rte_graph *graph, struct rte_node > > *node, > > + void **objs, uint16_t nb_objs, > > + struct rte_graph_feature_arc *arc) > > + { > > + for(uint16_t i = 0; i < nb_objs; i++) { > > + struct rte_mbuf *mbuf = objs[i]; > > + rte_edge_t edge_to_child = 0; /* By default to Node-B */ > > + > > + struct rte_graph_feature_arc_mbuf_dynfields *dyn = > > + rte_graph_feature_arc_mbuf_dynfields_get(mbuf, arc- > > >mbuf_dyn_offset); > > + > > + /* Get feature app cookie for mbuf */ > > + uint16_t app_cookie = > > rte_graph_feature_data_app_cookie_get(mbuf, > > &dyn->feature_data); > > + > > + if (feature_local_lookup(app_cookie) { > > + > > + /* Packets is relevant to this feature. Move packet from > > arc path */ > > + edge_to_child = X; > > + > > + } else { > > + > > + /* Packet not relevant to this feature. Send this packet to > > + * next enabled feature > > + */ > > + rte_graph_feature_data_next_feature_get(mbuf, &dyn- > > >feature_data, > > + &edge_to_child); > > + } > > + > > + /* enqueue as usual */ > > + rte_node_enqueue_x1(graph, node, mbuf, edge_to_child); > > + } > > + } > > + > > + int feature1_process_fn(struct rte_graph *graph, struct rte_node *node, > > + void **objs, uint16_t nb_objs) > > + { > > + struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(node- > > >ctx); > > + > > + return feature1_process_inline(graph, node, objs, nb_objs, arc); > > + } > > + > > +``End feature node`` > > +******************** > > +An end feature node is a feature node through which packets exits feature > > arc > > +path. It should not use any feature arc fast path APIs. > > + > > +Feature arc destroy > > +^^^^^^^^^^^^^^^^^^^ > > +``rte_graph_feature_arc_destroy()`` can be used to free a arc object. > > + > > +Feature arc cleanup > > +^^^^^^^^^^^^^^^^^^^ > > +``rte_graph_feature_arc_cleanup()`` can be used to free all resources > > +associated with feature arc module. > > diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c > > index b28f0ec321..9cad82947a 100644 > > --- a/lib/graph/graph_feature_arc.c > > +++ b/lib/graph/graph_feature_arc.c > > @@ -19,6 +19,9 @@ > > > > #define graph_uint_cast(f) ((unsigned int)f) > > > > +#define fdata_fix_get(arc, feat, index) \ > > + RTE_GRAPH_FEATURE_TO_FEATURE_DATA(arc, feat, > > index) > > + > > #define feat_dbg graph_dbg > > > > #define FEAT_COND_ERR(cond, ...) > > \ > > @@ -61,6 +64,139 @@ static STAILQ_HEAD(, rte_graph_feature_arc_register) > > feature_arc_list = > > static STAILQ_HEAD(, rte_graph_feature_register) feature_list = > > > > STAILQ_HEAD_INITIALIZER(feature_list); > > > > + /* > > + * feature data index is not fixed for given [feature, index], although > > it can > > + * be, which is calculated as follows (fdata_fix_get()) > > + * > > + * fdata = (arc->max_features * feature ) + index; > > + * > > + * But feature data index should not be fixed for any index. i.e > > + * on any index, feature data can be placed. A slow path array is > > + * maintained and within a feature range [start, end] it is checked where > > + * feature_data_index is already placed. > > + * > > + * If is_release == false. feature_data_index is searched in a feature > > range. > > + * If found, index is returned. If not found, then reserve and return. > > + * > > + * If is_release == true, then feature_data_index is released for further > > + * usage > > + */ > > +static rte_graph_feature_data_t > > +fdata_dyn_reserve_or_rel(struct rte_graph_feature_arc *arc, > > rte_graph_feature_t f, > > + uint32_t index, bool is_release, > > + bool fdata_provided, rte_graph_feature_data_t fd) > > +{ > > + rte_graph_feature_data_t start, end, fdata; > > + rte_graph_feature_t next_feat; > > + > > + if (fdata_provided) > > + fdata = fd; > > + else > > + fdata = fdata_fix_get(arc, f, index); > > + > > + next_feat = f + 1; > > + /* Find in a given feature range, feature data is stored or not */ > > + for (start = fdata_fix_get(arc, f, 0), > > + end = fdata_fix_get(arc, next_feat, 0); > > + start < end; > > + start++) { > > + if (arc->feature_data_by_index[start] == fdata) { > > + if (is_release) > > + arc->feature_data_by_index[start] = > > RTE_GRAPH_FEATURE_DATA_INVALID; > > + > > + return start; > > + } > > + } > > + > > + if (is_release) > > + return RTE_GRAPH_FEATURE_DATA_INVALID; > > + > > + /* If not found, then reserve valid one */ > > + for (start = fdata_fix_get(arc, f, 0), > > + end = fdata_fix_get(arc, next_feat, 0); > > + start < end; > > + start++) { > > + if (arc->feature_data_by_index[start] == > > RTE_GRAPH_FEATURE_DATA_INVALID) { > > + arc->feature_data_by_index[start] = fdata; > > + return start; > > + } > > + } > > + > > + /* This should not happen */ > > + if (!fdata_provided) > > + RTE_VERIFY(0); > > + > > Why panic? Return error. Removed in v11. Have removed other referenced to RTE_VERIFY as well <snip> > > +/** > > + * Prefetch feature data related fast path cache line > > + * > > + * @param arc > > + * RTE_GRAPH feature arc object > > + * @param fdata > > + * Pointer to feature data object > > + */ > > +__rte_experimental > > +static __rte_always_inline void > > +rte_graph_feature_arc_feature_data_prefetch(struct rte_graph_feature_arc > > *arc, > > + rte_graph_feature_data_t fdata) > > +{ > > + if (unlikely(fdata == RTE_GRAPH_FEATURE_DATA_INVALID)) > > + return; > > + > Do we need above condition? Do we ever run into this? Avoid un necessary > checks in fast path. > Removed check in v11 > > > + rte_prefetch0((void *)__rte_graph_feature_data_get(arc, fdata)); > > +} > > + > > #ifdef __cplusplus > > } > > #endif > > -- > > 2.43.0 >