[v4] ethdev: add indirect list flow action

Message ID 20230519115947.331304-1-getelson@nvidia.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series [v4] ethdev: add indirect list flow action |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS
ci/github-robot: build success github build: passed
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/intel-Functional success Functional PASS
ci/iol-abi-testing success Testing PASS
ci/iol-aarch64-unit-testing success Testing PASS
ci/iol-unit-testing success Testing PASS
ci/iol-aarch64-compile-testing success Testing PASS
ci/iol-x86_64-compile-testing success Testing PASS
ci/iol-testing success Testing PASS
ci/iol-x86_64-unit-testing success Testing PASS

Commit Message

Etelson, Gregory May 19, 2023, 11:59 a.m. UTC
Indirect API creates a shared flow action with unique action handle.
Flow rules can access the shared flow action and resources related to
that action through the indirect action handle.
In addition, the API allows to update existing shared flow action
configuration.  After the update completes, new action configuration
is available to all flows that reference that shared action.

Indirect actions list expands the indirect action API:
• Indirect action list creates a handle for one or several
  flow actions, while legacy indirect action handle references
  single action only.
  Input flow actions arranged in END terminated list.
• Flow rule can provide rule specific configuration parameters to
  existing shared handle.
  Updates of flow rule specific configuration will not change the base
  action configuration.
  Base action configuration was set during the action creation.

Indirect action list handle defines 2 types of resources:
• Mutable handle resource can be changed during handle lifespan.
• Immutable handle resource value is set during handle creation
  and cannot be changed.

There are 2 types of mutable indirect handle contexts:
• Action mutable context is always shared between all flows
  that referenced indirect actions list handle.
  Action mutable context can be changed by explicit invocation
  of indirect handle update function.
• Flow mutable context is private to a flow.
  Flow mutable context can be updated by indirect list handle
  flow rule configuration.

flow 1:
 / indirect handle H conf C1 /
                   |       |
                   |       |
                   |       |         flow 2:
                   |       |         / indirect handle H conf C2 /
                   |       |                           |      |
                   |       |                           |      |
                   |       |                           |      |
            =========================================================
            ^      |       |                           |      |
            |      |       V                           |      V
            |    ~~~~~~~~~~~~~~                      ~~~~~~~~~~~~~~~
            |     flow mutable                        flow mutable
            |     context 1                           context 2
            |    ~~~~~~~~~~~~~~                      ~~~~~~~~~~~~~~~
  indirect  |      |                                   |
  action    |      |                                   |
  context   |      V                                   V
            |   -----------------------------------------------------
            |                 action mutable context
            |   -----------------------------------------------------
            v                action immutable context
            =========================================================

Indirect action types - immutable, action / flow mutable, are mutually
exclusive and depend on the action definition.
For example:
• Indirect METER_MARK policy is immutable action member and profile is
  action mutable action member.
• Indirect METER_MARK flow action defines init_color as flow mutable
  member.
• Indirect QUOTA flow action does not define flow mutable members.

If indirect list handle was created from a list of actions
A1 / A2 ... An / END
indirect list flow action can update Ai flow mutable context in the
action configuration parameter.
Indirect list action configuration is and array [C1, C2,  .., Cn]
where Ci corresponds to Ai in the action handle source.
Ci configuration element points Ai flow mutable update, or it's NULL
if Ai has no flow mutable update.
Indirect list action configuration can be NULL if the action
has no flow mutable updates.

Template API:

Action template format:

	template .. indirect_list handle Htmpl conf Ctmpl ..
	mask     .. indirect_list handle Hmask conf Cmask ..

1 If Htmpl was masked (Hmask != 0), it will be fixed in that template.
  Otherwise, indirect action value is set in a flow rule.

2 If Htmpl and Ctmpl[i] were masked (Hmask !=0 and Cmask[i] != 0),
  Htmpl's Ai action flow mutable context fill be updated to
  Ctmpl[i] values and will be fixed in that template.

Flow rule format:

	actions .. indirect_list handle Hflow conf Cflow ..

3 If Htmpl was not masked in actions template, Hflow references an
  action of the same type as Htmpl.

4 Cflow[i] updates handle's Ai flow mutable configuration if
  the Ci was not masked in action template.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
 app/test-pmd/cmdline_flow.c            | 207 ++++++++++++++++++-
 app/test-pmd/config.c                  | 163 +++++++++++----
 app/test-pmd/testpmd.h                 |   9 +-
 doc/guides/nics/features/default.ini   |   1 +
 doc/guides/prog_guide/rte_flow.rst     | 119 +++++++++++
 doc/guides/rel_notes/release_23_07.rst |   2 +
 lib/ethdev/ethdev_trace.h              |  88 ++++++++
 lib/ethdev/ethdev_trace_points.c       |  18 ++
 lib/ethdev/rte_flow.c                  | 171 ++++++++++++++++
 lib/ethdev/rte_flow.h                  | 269 +++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           |  41 ++++
 lib/ethdev/version.map                 |   8 +
 12 files changed, 1054 insertions(+), 42 deletions(-)
  

Comments

Ori Kam May 24, 2023, 5:46 p.m. UTC | #1
Hi Gregory

> -----Original Message-----
> From: Gregory Etelson <getelson@nvidia.com>
> Sent: Friday, May 19, 2023 3:00 PM
> 
> Indirect API creates a shared flow action with unique action handle.
> Flow rules can access the shared flow action and resources related to
> that action through the indirect action handle.
> In addition, the API allows to update existing shared flow action
> configuration.  After the update completes, new action configuration
> is available to all flows that reference that shared action.
> 
> Indirect actions list expands the indirect action API:
> • Indirect action list creates a handle for one or several
>   flow actions, while legacy indirect action handle references
>   single action only.
>   Input flow actions arranged in END terminated list.
> • Flow rule can provide rule specific configuration parameters to
>   existing shared handle.
>   Updates of flow rule specific configuration will not change the base
>   action configuration.
>   Base action configuration was set during the action creation.
> 
> Indirect action list handle defines 2 types of resources:
> • Mutable handle resource can be changed during handle lifespan.
> • Immutable handle resource value is set during handle creation
>   and cannot be changed.
> 
> There are 2 types of mutable indirect handle contexts:
> • Action mutable context is always shared between all flows
>   that referenced indirect actions list handle.
>   Action mutable context can be changed by explicit invocation
>   of indirect handle update function.
> • Flow mutable context is private to a flow.
>   Flow mutable context can be updated by indirect list handle
>   flow rule configuration.
> 
> flow 1:
>  / indirect handle H conf C1 /
>                    |       |
>                    |       |
>                    |       |         flow 2:
>                    |       |         / indirect handle H conf C2 /
>                    |       |                           |      |
>                    |       |                           |      |
>                    |       |                           |      |
>             =========================================================
>             ^      |       |                           |      |
>             |      |       V                           |      V
>             |    ~~~~~~~~~~~~~~                      ~~~~~~~~~~~~~~~
>             |     flow mutable                        flow mutable
>             |     context 1                           context 2
>             |    ~~~~~~~~~~~~~~                      ~~~~~~~~~~~~~~~
>   indirect  |      |                                   |
>   action    |      |                                   |
>   context   |      V                                   V
>             |   -----------------------------------------------------
>             |                 action mutable context
>             |   -----------------------------------------------------
>             v                action immutable context
>             =========================================================
> 
> Indirect action types - immutable, action / flow mutable, are mutually
> exclusive and depend on the action definition.
> For example:
> • Indirect METER_MARK policy is immutable action member and profile is
>   action mutable action member.
> • Indirect METER_MARK flow action defines init_color as flow mutable
>   member.
> • Indirect QUOTA flow action does not define flow mutable members.
> 
> If indirect list handle was created from a list of actions
> A1 / A2 ... An / END
> indirect list flow action can update Ai flow mutable context in the
> action configuration parameter.
> Indirect list action configuration is and array [C1, C2,  .., Cn]
> where Ci corresponds to Ai in the action handle source.
> Ci configuration element points Ai flow mutable update, or it's NULL
> if Ai has no flow mutable update.
> Indirect list action configuration can be NULL if the action
> has no flow mutable updates.
> 
> Template API:
> 
> Action template format:
> 
> 	template .. indirect_list handle Htmpl conf Ctmpl ..
> 	mask     .. indirect_list handle Hmask conf Cmask ..
> 
> 1 If Htmpl was masked (Hmask != 0), it will be fixed in that template.
>   Otherwise, indirect action value is set in a flow rule.
> 
> 2 If Htmpl and Ctmpl[i] were masked (Hmask !=0 and Cmask[i] != 0),
>   Htmpl's Ai action flow mutable context fill be updated to
>   Ctmpl[i] values and will be fixed in that template.
> 
> Flow rule format:
> 
> 	actions .. indirect_list handle Hflow conf Cflow ..
> 
> 3 If Htmpl was not masked in actions template, Hflow references an
>   action of the same type as Htmpl.
> 
> 4 Cflow[i] updates handle's Ai flow mutable configuration if
>   the Ci was not masked in action template.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> ---

[Snip]

> diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> index 713ba8b65c..b65096b3b7 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -2912,6 +2912,13 @@ enum rte_flow_action_type {
>  	 * applied to the given ethdev Rx queue.
>  	 */
>  	RTE_FLOW_ACTION_TYPE_SKIP_CMAN,
> +
> +	/**
> +	 * Action handle to reference flow actions list.
> +	 *
> +	 * @see struct rte_flow_action_indirect_list
> +	 */
> +	RTE_FLOW_ACTION_TYPE_INDIRECT_LIST,
>  };
> 
>  /**
> @@ -6118,6 +6125,268 @@
> rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
>  					  void *user_data,
>  					  struct rte_flow_error *error);
> 
> +struct rte_flow_action_list_handle;
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Configure INDIRECT_LIST flow action.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_INDIRECT_LIST
> + */
> +struct rte_flow_action_indirect_list {
> +	/** Indirect action list handle */
> +	struct rte_flow_action_list_handle *handle;
> +	/**
> +	 * Flow mutable configuration array.
> +	 * NULL if the handle has no flow mutable configuration update.
> +	 * Otherwise, if the handle was created with list A1 / A2 .. An / END
> +	 * size of conf is n.
> +	 * conf[i] points to flow mutable update of Ai in the handle
> +	 * actions list or NULL if Ai has no update.
> +	 */
> +	const void **conf;
> +};
> +
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Create an indirect flow action object from flow actions list.
> + * The object is identified by a unique handle.
> + * The handle has single state and configuration
> + * across all the flow rules using it.
> + *
> + * @param[in] port_id
> + *    The port identifier of the Ethernet device.
> + * @param[in] conf
> + *   Action configuration for the indirect action list creation.
> + * @param[in] actions
> + *   Specific configuration of the indirect action lists.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL. PMDs initialize this
> + *   structure in case of error only.
> + * @return
> + *   A valid handle in case of success, NULL otherwise and rte_errno is set
> + *   to one of the error codes defined:
> + *   - (-ENODEV) if *port_id* invalid.
> + *   - (-ENOSYS) if underlying device does not support this functionality.
> + *   - (-EIO) if underlying device is removed.
> + *   - (-EINVAL) if *actions* list invalid.
> + *   - (-ENOTSUP) if *action* list element valid but unsupported.
> + */
> +__rte_experimental
> +struct rte_flow_action_list_handle *
> +rte_flow_action_list_handle_create(uint16_t port_id,
> +				   const
> +				   struct rte_flow_indir_action_conf *conf,
> +				   const struct rte_flow_action *actions,
> +				   struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Async function call to create an indirect flow action object
> + * from flow actions list.
> + * The object is identified by a unique handle.
> + * The handle has single state and configuration
> + * across all the flow rules using it.
> + *
> + * @param[in] port_id
> + *    The port identifier of the Ethernet device.
> + * @param[in] queue_id
> + *   Flow queue which is used to update the rule.
> + * @param[in] attr
> + *   Indirect action update operation attributes.
> + * @param[in] conf
> + *   Action configuration for the indirect action list creation.
> + * @param[in] actions
> + *   Specific configuration of the indirect action list.
> + * @param[in] user_data
> + *   The user data that will be returned on async completion event.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL. PMDs initialize this
> + *   structure in case of error only.
> + * @return
> + *   A valid handle in case of success, NULL otherwise and rte_errno is set
> + *   to one of the error codes defined:
> + *   - (-ENODEV) if *port_id* invalid.
> + *   - (-ENOSYS) if underlying device does not support this functionality.
> + *   - (-EIO) if underlying device is removed.
> + *   - (-EINVAL) if *actions* list invalid.
> + *   - (-ENOTSUP) if *action* list element valid but unsupported.
> + */
> +__rte_experimental
> +struct rte_flow_action_list_handle *
> +rte_flow_async_action_list_handle_create(uint16_t port_id, uint32_t
> queue_id,
> +					 const struct rte_flow_op_attr *attr,
> +					 const struct
> +					 rte_flow_indir_action_conf *conf,
> +					 const struct rte_flow_action
> *actions,
> +					 void *user_data,
> +					 struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Destroy indirect actions list by handle.
> + *
> + * @param[in] port_id
> + *    The port identifier of the Ethernet device.
> + * @param[in] handle
> + *   Handle for the indirect actions list to be destroyed.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL. PMDs initialize this
> + *   structure in case of error only.
> + * @return
> + *   - (0) if success.
> + *   - (-ENODEV) if *port_id* invalid.
> + *   - (-ENOSYS) if underlying device does not support this functionality.
> + *   - (-EIO) if underlying device is removed.
> + *   - (-ENOENT) if actions list pointed by *action* handle was not found.
> + *   - (-EBUSY) if actions list pointed by *action* handle still used
> + */
> +__rte_experimental
> +int
> +rte_flow_action_list_handle_destroy(uint16_t port_id,
> +				    struct rte_flow_action_list_handle
> *handle,
> +				    struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue indirect action list destruction operation.
> + * The destroy queue must be the same
> + * as the queue on which the action was created.
> + *
> + * @param[in] port_id
> + *   Port identifier of Ethernet device.
> + * @param[in] queue_id
> + *   Flow queue which is used to destroy the rule.
> + * @param[in] op_attr
> + *   Indirect action destruction operation attributes.
> + * @param[in] handle
> + *   Handle for the indirect action object to be destroyed.
> + * @param[in] user_data
> + *   The user data that will be returned on the completion events.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   - (0) if success.
> + *   - (-ENODEV) if *port_id* invalid.
> + *   - (-ENOSYS) if underlying device does not support this functionality.
> + *   - (-EIO) if underlying device is removed.
> + *   - (-ENOENT) if actions list pointed by *action* handle was not found.
> + *   - (-EBUSY) if actions list pointed by *action* handle still used
> + */
> +__rte_experimental
> +int
> +rte_flow_async_action_list_handle_destroy
> +		(uint16_t port_id, uint32_t queue_id,
> +		 const struct rte_flow_op_attr *op_attr,
> +		 struct rte_flow_action_list_handle *handle,
> +		 void *user_data, struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query and/or update indirect flow actions list.
> + * If both query and update not NULL, the function atomically
> + * queries and updates indirect action. Query and update are carried in
> order
> + * specified in the mode parameter.
> + * If ether query or update is NULL, the function executes
> + * complementing operation.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param handle
> + *   Handle for the indirect actions list object to be updated.
> + * @param update
> + *   If not NULL, update profile specification used to modify the action

It should be explained that the upate/query points always to array with the size
of number of actions, the the query/update query/upate all the actions at the same time

This comment is for all such parameters in async and synced functions.

> + *   pointed by handle.
> + *   @see struct rte_flow_action_indirect_list
> + * @param query
> + *   If not NULL pointer to storage for the associated query data type.
> + *   @see struct rte_flow_action_indirect_list
> + * @param mode
> + *   Operational mode.
> + * @param error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   - (0) if success.
> + * - (-ENODEV) if *port_id* invalid.
> + * - (-ENOTSUP) if underlying device does not support this functionality.
> + * - (-EINVAL) if *handle* or *mode* invalid or
> + *             both *query* and *update* are NULL.
> + */
> +__rte_experimental
> +int
> +rte_flow_action_list_handle_query_update(uint16_t port_id,
> +					 const struct
> +					 rte_flow_action_list_handle
> *handle,
> +					 const void **update, void **query,
> +					 enum
> rte_flow_query_update_mode mode,
> +					 struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue async indirect flow actions list query and/or update
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue which is used to update the rule.
> + * @param attr
> + *   Indirect action update operation attributes.
> + * @param handle
> + *   Handle for the indirect actions list object to be updated.
> + * @param update
> + *   If not NULL, update profile specification used to modify the action
> + *   pointed by handle.
> + *   @see struct rte_flow_action_indirect_list
> + * @param query
> + *   If not NULL, pointer to storage for the associated query data type.
> + *   Query result returned on async completion event.
> + *   @see struct rte_flow_action_indirect_list
> + * @param mode
> + *   Operational mode.
> + * @param user_data
> + *   The user data that will be returned on async completion event.
> + * @param error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   - (0) if success.
> + * - (-ENODEV) if *port_id* invalid.
> + * - (-ENOTSUP) if underlying device does not support this functionality.
> + * - (-EINVAL) if *handle* or *mode* invalid or
> + *             both *update* and *query* are NULL.
> + */
> +__rte_experimental
> +int
> +rte_flow_async_action_list_handle_query_update(uint16_t port_id,
> uint32_t queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  const struct
> +					  rte_flow_action_list_handle
> *handle,
> +					  const void **update, void **query,
> +					  enum
> rte_flow_query_update_mode mode,
> +					  void *user_data,
> +					  struct rte_flow_error *error);
> +
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/lib/ethdev/rte_flow_driver.h b/lib/ethdev/rte_flow_driver.h
> index a129a4605d..af63ef9b5c 100644
> --- a/lib/ethdev/rte_flow_driver.h
> +++ b/lib/ethdev/rte_flow_driver.h
> @@ -121,6 +121,17 @@ struct rte_flow_ops {
>  		 const void *update, void *query,
>  		 enum rte_flow_query_update_mode qu_mode,
>  		 struct rte_flow_error *error);
> +	/** @see rte_flow_action_list_handle_create() */
> +	struct rte_flow_action_list_handle *(*action_list_handle_create)
> +		(struct rte_eth_dev *dev,
> +		 const struct rte_flow_indir_action_conf *conf,
> +		 const struct rte_flow_action actions[],
> +		 struct rte_flow_error *error);
> +	/** @see rte_flow_action_list_handle_destroy() */
> +	int (*action_list_handle_destroy)
> +		(struct rte_eth_dev *dev,
> +		 struct rte_flow_action_list_handle *handle,
> +		 struct rte_flow_error *error);
>  	/** See rte_flow_tunnel_decap_set() */
>  	int (*tunnel_decap_set)
>  		(struct rte_eth_dev *dev,
> @@ -302,6 +313,36 @@ struct rte_flow_ops {
>  		 const void *update, void *query,
>  		 enum rte_flow_query_update_mode qu_mode,
>  		 void *user_data, struct rte_flow_error *error);
> +	/** @see rte_flow_async_action_list_handle_create() */
> +	struct rte_flow_action_list_handle *
> +	(*async_action_list_handle_create)
> +		(struct rte_eth_dev *dev, uint32_t queue_id,
> +		 const struct rte_flow_op_attr *attr,
> +		 const struct rte_flow_indir_action_conf *conf,
> +		 const struct rte_flow_action *actions,
> +		 void *user_data, struct rte_flow_error *error);
> +	/** @see rte_flow_async_action_list_handle_destroy() */
> +	int (*async_action_list_handle_destroy)
> +		(struct rte_eth_dev *dev, uint32_t queue_id,
> +		 const struct rte_flow_op_attr *op_attr,
> +		 struct rte_flow_action_list_handle *action_handle,
> +		 void *user_data, struct rte_flow_error *error);
> +	/** @see rte_flow_action_list_handle_query_update() */
> +	int (*action_list_handle_query_update)
> +		(struct rte_eth_dev *dev,
> +		 const struct rte_flow_action_list_handle *handle,
> +		 const void **update, void **query,
> +		 enum rte_flow_query_update_mode mode,
> +		 struct rte_flow_error *error);
> +	/** @see rte_flow_async_action_list_handle_query_update() */
> +	int (*async_action_list_handle_query_update)
> +		(struct rte_eth_dev *dev, uint32_t queue_id,
> +		 const struct rte_flow_op_attr *attr,
> +		 const struct rte_flow_action_list_handle *handle,
> +		 const void **update, void **query,
> +		 enum rte_flow_query_update_mode mode,
> +		 void *user_data, struct rte_flow_error *error);
> +
>  };
> 
>  /**
> diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
> index 357d1a88c0..02372b3a7e 100644
> --- a/lib/ethdev/version.map
> +++ b/lib/ethdev/version.map
> @@ -299,6 +299,14 @@ EXPERIMENTAL {
>  	rte_flow_action_handle_query_update;
>  	rte_flow_async_action_handle_query_update;
>  	rte_flow_async_create_by_index;
> +
> +	# added in 23.07
> +    rte_flow_action_list_handle_create;
> +    rte_flow_action_list_handle_destroy;
> +    rte_flow_action_list_handle_query_update;
> +    rte_flow_async_action_list_handle_create;
> +    rte_flow_async_action_list_handle_destroy;
> +    rte_flow_async_action_list_handle_query_update;
>  };
> 
>  INTERNAL {
> --
> 2.34.1
  
Etelson, Gregory May 25, 2023, 8:14 a.m. UTC | #2
Hello Ori,

[snip]

> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query and/or update indirect flow actions list.
> > + * If both query and update not NULL, the function atomically
> > + * queries and updates indirect action. Query and update are carried in
> > order
> > + * specified in the mode parameter.
> > + * If ether query or update is NULL, the function executes
> > + * complementing operation.
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param handle
> > + *   Handle for the indirect actions list object to be updated.
> > + * @param update
> > + *   If not NULL, update profile specification used to modify the action
> 
> It should be explained that the upate/query points always to array with the size
> of number of actions, the the query/update query/upate all the actions at the
> same time
> 

I added update and query parameters description in v5

> This comment is for all such parameters in async and synced functions.
> 
> > + *   pointed by handle.
> > + *   @see struct rte_flow_action_indirect_list
> > + * @param query
> > + *   If not NULL pointer to storage for the associated query data type.
> > + *   @see struct rte_flow_action_indirect_list
> > + * @param mode
> > + *   Operational mode.
> > + * @param error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *   - (0) if success.
> > + * - (-ENODEV) if *port_id* invalid.
> > + * - (-ENOTSUP) if underlying device does not support this functionality.
> > + * - (-EINVAL) if *handle* or *mode* invalid or
> > + *             both *query* and *update* are NULL.
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_action_list_handle_query_update(uint16_t port_id,
> > +					 const struct
> > +					 rte_flow_action_list_handle
> > *handle,
> > +					 const void **update, void **query,
> > +					 enum
> > rte_flow_query_update_mode mode,
> > +					 struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue async indirect flow actions list query and/or update
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue which is used to update the rule.
> > + * @param attr
> > + *   Indirect action update operation attributes.
> > + * @param handle
> > + *   Handle for the indirect actions list object to be updated.
> > + * @param update
> > + *   If not NULL, update profile specification used to modify the action
> > + *   pointed by handle.
> > + *   @see struct rte_flow_action_indirect_list
> > + * @param query
> > + *   If not NULL, pointer to storage for the associated query data type.
> > + *   Query result returned on async completion event.
> > + *   @see struct rte_flow_action_indirect_list
> > + * @param mode
> > + *   Operational mode.
> > + * @param user_data
> > + *   The user data that will be returned on async completion event.
> > + * @param error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *   - (0) if success.
> > + * - (-ENODEV) if *port_id* invalid.
> > + * - (-ENOTSUP) if underlying device does not support this functionality.
> > + * - (-EINVAL) if *handle* or *mode* invalid or
> > + *             both *update* and *query* are NULL.
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_async_action_list_handle_query_update(uint16_t port_id,
> > uint32_t queue_id,
> > +					  const struct rte_flow_op_attr *attr,
> > +					  const struct
> > +					  rte_flow_action_list_handle
> > *handle,
> > +					  const void **update, void **query,
> > +					  enum
> > rte_flow_query_update_mode mode,
> > +					  void *user_data,
> > +					  struct rte_flow_error *error);
> > +
> > +
> >  #ifdef __cplusplus
> >  }
> >  #endif
> > diff --git a/lib/ethdev/rte_flow_driver.h b/lib/ethdev/rte_flow_driver.h
> > index a129a4605d..af63ef9b5c 100644
> > --- a/lib/ethdev/rte_flow_driver.h
> > +++ b/lib/ethdev/rte_flow_driver.h
> > @@ -121,6 +121,17 @@ struct rte_flow_ops {
> >  		 const void *update, void *query,
> >  		 enum rte_flow_query_update_mode qu_mode,
> >  		 struct rte_flow_error *error);
> > +	/** @see rte_flow_action_list_handle_create() */
> > +	struct rte_flow_action_list_handle *(*action_list_handle_create)
> > +		(struct rte_eth_dev *dev,
> > +		 const struct rte_flow_indir_action_conf *conf,
> > +		 const struct rte_flow_action actions[],
> > +		 struct rte_flow_error *error);
> > +	/** @see rte_flow_action_list_handle_destroy() */
> > +	int (*action_list_handle_destroy)
> > +		(struct rte_eth_dev *dev,
> > +		 struct rte_flow_action_list_handle *handle,
> > +		 struct rte_flow_error *error);
> >  	/** See rte_flow_tunnel_decap_set() */
> >  	int (*tunnel_decap_set)
> >  		(struct rte_eth_dev *dev,
> > @@ -302,6 +313,36 @@ struct rte_flow_ops {
> >  		 const void *update, void *query,
> >  		 enum rte_flow_query_update_mode qu_mode,
> >  		 void *user_data, struct rte_flow_error *error);
> > +	/** @see rte_flow_async_action_list_handle_create() */
> > +	struct rte_flow_action_list_handle *
> > +	(*async_action_list_handle_create)
> > +		(struct rte_eth_dev *dev, uint32_t queue_id,
> > +		 const struct rte_flow_op_attr *attr,
> > +		 const struct rte_flow_indir_action_conf *conf,
> > +		 const struct rte_flow_action *actions,
> > +		 void *user_data, struct rte_flow_error *error);
> > +	/** @see rte_flow_async_action_list_handle_destroy() */
> > +	int (*async_action_list_handle_destroy)
> > +		(struct rte_eth_dev *dev, uint32_t queue_id,
> > +		 const struct rte_flow_op_attr *op_attr,
> > +		 struct rte_flow_action_list_handle *action_handle,
> > +		 void *user_data, struct rte_flow_error *error);
> > +	/** @see rte_flow_action_list_handle_query_update() */
> > +	int (*action_list_handle_query_update)
> > +		(struct rte_eth_dev *dev,
> > +		 const struct rte_flow_action_list_handle *handle,
> > +		 const void **update, void **query,
> > +		 enum rte_flow_query_update_mode mode,
> > +		 struct rte_flow_error *error);
> > +	/** @see rte_flow_async_action_list_handle_query_update() */
> > +	int (*async_action_list_handle_query_update)
> > +		(struct rte_eth_dev *dev, uint32_t queue_id,
> > +		 const struct rte_flow_op_attr *attr,
> > +		 const struct rte_flow_action_list_handle *handle,
> > +		 const void **update, void **query,
> > +		 enum rte_flow_query_update_mode mode,
> > +		 void *user_data, struct rte_flow_error *error);
> > +
> >  };
> >
> >  /**
> > diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
> > index 357d1a88c0..02372b3a7e 100644
> > --- a/lib/ethdev/version.map
> > +++ b/lib/ethdev/version.map
> > @@ -299,6 +299,14 @@ EXPERIMENTAL {
> >  	rte_flow_action_handle_query_update;
> >  	rte_flow_async_action_handle_query_update;
> >  	rte_flow_async_create_by_index;
> > +
> > +	# added in 23.07
> > +    rte_flow_action_list_handle_create;
> > +    rte_flow_action_list_handle_destroy;
> > +    rte_flow_action_list_handle_query_update;
> > +    rte_flow_async_action_list_handle_create;
> > +    rte_flow_async_action_list_handle_destroy;
> > +    rte_flow_async_action_list_handle_query_update;
> >  };
> >
> >  INTERNAL {
> > --
> > 2.34.1
  

Patch

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 58939ec321..4663b6217f 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -145,6 +145,7 @@  enum index {
 
 	/* Queue indirect action arguments */
 	QUEUE_INDIRECT_ACTION_CREATE,
+	QUEUE_INDIRECT_ACTION_LIST_CREATE,
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
@@ -157,6 +158,7 @@  enum index {
 	QUEUE_INDIRECT_ACTION_TRANSFER,
 	QUEUE_INDIRECT_ACTION_CREATE_POSTPONE,
 	QUEUE_INDIRECT_ACTION_SPEC,
+	QUEUE_INDIRECT_ACTION_LIST,
 
 	/* Queue indirect action update arguments */
 	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
@@ -242,6 +244,8 @@  enum index {
 
 	/* Indirect action arguments */
 	INDIRECT_ACTION_CREATE,
+	INDIRECT_ACTION_LIST_CREATE,
+	INDIRECT_ACTION_FLOW_CONF_CREATE,
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
@@ -253,6 +257,8 @@  enum index {
 	INDIRECT_ACTION_EGRESS,
 	INDIRECT_ACTION_TRANSFER,
 	INDIRECT_ACTION_SPEC,
+	INDIRECT_ACTION_LIST,
+	INDIRECT_ACTION_FLOW_CONF,
 
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
@@ -626,6 +632,11 @@  enum index {
 	ACTION_SAMPLE_INDEX,
 	ACTION_SAMPLE_INDEX_VALUE,
 	ACTION_INDIRECT,
+	ACTION_INDIRECT_LIST,
+	ACTION_INDIRECT_LIST_HANDLE,
+	ACTION_INDIRECT_LIST_CONF,
+	INDIRECT_LIST_ACTION_ID2PTR_HANDLE,
+	INDIRECT_LIST_ACTION_ID2PTR_CONF,
 	ACTION_SHARED_INDIRECT,
 	INDIRECT_ACTION_PORT,
 	INDIRECT_ACTION_ID2PTR,
@@ -1266,6 +1277,7 @@  static const enum index next_qia_create_attr[] = {
 	QUEUE_INDIRECT_ACTION_TRANSFER,
 	QUEUE_INDIRECT_ACTION_CREATE_POSTPONE,
 	QUEUE_INDIRECT_ACTION_SPEC,
+	QUEUE_INDIRECT_ACTION_LIST,
 	ZERO,
 };
 
@@ -1294,6 +1306,8 @@  static const enum index next_ia_create_attr[] = {
 	INDIRECT_ACTION_EGRESS,
 	INDIRECT_ACTION_TRANSFER,
 	INDIRECT_ACTION_SPEC,
+	INDIRECT_ACTION_LIST,
+	INDIRECT_ACTION_FLOW_CONF,
 	ZERO,
 };
 
@@ -1303,6 +1317,13 @@  static const enum index next_ia[] = {
 	ZERO
 };
 
+static const enum index next_ial[] = {
+	ACTION_INDIRECT_LIST_HANDLE,
+	ACTION_INDIRECT_LIST_CONF,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index next_qia_qu_attr[] = {
 	QUEUE_INDIRECT_ACTION_QU_MODE,
 	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
@@ -2013,6 +2034,7 @@  static const enum index next_action[] = {
 	ACTION_AGE_UPDATE,
 	ACTION_SAMPLE,
 	ACTION_INDIRECT,
+	ACTION_INDIRECT_LIST,
 	ACTION_SHARED_INDIRECT,
 	ACTION_MODIFY_FIELD,
 	ACTION_CONNTRACK,
@@ -2289,6 +2311,7 @@  static const enum index next_action_sample[] = {
 	ACTION_RAW_ENCAP,
 	ACTION_VXLAN_ENCAP,
 	ACTION_NVGRE_ENCAP,
+	ACTION_REPRESENTED_PORT,
 	ACTION_NEXT,
 	ZERO,
 };
@@ -2539,6 +2562,10 @@  static int parse_ia_destroy(struct context *ctx, const struct token *token,
 static int parse_ia_id2ptr(struct context *ctx, const struct token *token,
 			   const char *str, unsigned int len, void *buf,
 			   unsigned int size);
+
+static int parse_indlst_id2ptr(struct context *ctx, const struct token *token,
+			       const char *str, unsigned int len, void *buf,
+			       unsigned int size);
 static int parse_ia_port(struct context *ctx, const struct token *token,
 			 const char *str, unsigned int len, void *buf,
 			 unsigned int size);
@@ -2627,6 +2654,16 @@  static int
 comp_qu_mode_name(struct context *ctx, const struct token *token,
 		  unsigned int ent, char *buf, unsigned int size);
 
+struct indlst_conf {
+	uint32_t id;
+	uint32_t conf_num;
+	struct rte_flow_action *actions;
+	const void **conf;
+	SLIST_ENTRY(indlst_conf) next;
+};
+
+static const struct indlst_conf *indirect_action_list_conf_get(uint32_t conf_id);
+
 /** Token definitions. */
 static const struct token token_list[] = {
 	/* Special tokens. */
@@ -3426,6 +3463,12 @@  static const struct token token_list[] = {
 		.help = "specify action to create indirect handle",
 		.next = NEXT(next_action),
 	},
+	[QUEUE_INDIRECT_ACTION_LIST] = {
+		.name = "list",
+		.help = "specify actions for indirect handle list",
+		.next = NEXT(NEXT_ENTRY(ACTIONS, END)),
+		.call = parse_qia,
+	},
 	/* Top-level command. */
 	[PUSH] = {
 		.name = "push",
@@ -6775,6 +6818,37 @@  static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
+	[ACTION_INDIRECT_LIST] = {
+		.name = "indirect_list",
+		.help = "apply indirect list action by id",
+		.priv = PRIV_ACTION(INDIRECT_LIST,
+				    sizeof(struct
+				           rte_flow_action_indirect_list)),
+		.next = NEXT(next_ial),
+		.call = parse_vc,
+	},
+	[ACTION_INDIRECT_LIST_HANDLE] = {
+		.name = "handle",
+		.help = "indirect list handle",
+		.next = NEXT(next_ial, NEXT_ENTRY(INDIRECT_LIST_ACTION_ID2PTR_HANDLE)),
+		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uintptr_t))),
+	},
+	[ACTION_INDIRECT_LIST_CONF] = {
+		.name = "conf",
+		.help = "indirect list configuration",
+		.next = NEXT(next_ial, NEXT_ENTRY(INDIRECT_LIST_ACTION_ID2PTR_CONF)),
+		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uintptr_t))),
+	},
+	[INDIRECT_LIST_ACTION_ID2PTR_HANDLE] = {
+		.type = "UNSIGNED",
+		.help = "unsigned integer value",
+		.call = parse_indlst_id2ptr,
+	},
+	[INDIRECT_LIST_ACTION_ID2PTR_CONF] = {
+		.type = "UNSIGNED",
+		.help = "unsigned integer value",
+		.call = parse_indlst_id2ptr,
+	},
 	[ACTION_SHARED_INDIRECT] = {
 		.name = "shared_indirect",
 		.help = "apply indirect action by id and port",
@@ -6823,6 +6897,18 @@  static const struct token token_list[] = {
 		.help = "specify action to create indirect handle",
 		.next = NEXT(next_action),
 	},
+	[INDIRECT_ACTION_LIST] = {
+		.name = "list",
+		.help = "specify actions for indirect handle list",
+		.next = NEXT(NEXT_ENTRY(ACTIONS, END)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_FLOW_CONF] = {
+		.name = "flow_conf",
+		.help = "specify actions configuration for indirect handle list",
+		.next = NEXT(NEXT_ENTRY(ACTIONS, END)),
+		.call = parse_ia,
+	},
 	[ACTION_POL_G] = {
 		.name = "g_actions",
 		.help = "submit a list of associated actions for green",
@@ -7181,6 +7267,12 @@  parse_ia(struct context *ctx, const struct token *token,
 		return len;
 	case INDIRECT_ACTION_QU_MODE:
 		return len;
+	case INDIRECT_ACTION_LIST:
+		out->command = INDIRECT_ACTION_LIST_CREATE;
+		return len;
+        case INDIRECT_ACTION_FLOW_CONF:
+		out->command = INDIRECT_ACTION_FLOW_CONF_CREATE;
+		return len;
 	default:
 		return -1;
 	}
@@ -7278,6 +7370,9 @@  parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_QU_MODE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_LIST:
+		out->command = QUEUE_INDIRECT_ACTION_LIST_CREATE;
+		return len;
 	default:
 		return -1;
 	}
@@ -7454,10 +7549,12 @@  parse_vc(struct context *ctx, const struct token *token,
 			return -1;
 		break;
 	case ACTIONS:
-		out->args.vc.actions =
+		out->args.vc.actions = out->args.vc.pattern ?
 			(void *)RTE_ALIGN_CEIL((uintptr_t)
 					       (out->args.vc.pattern +
 						out->args.vc.pattern_n),
+					       sizeof(double)) :
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
 		ctx->object = out->args.vc.actions;
 		ctx->objmask = NULL;
@@ -10412,6 +10509,49 @@  parse_ia_id2ptr(struct context *ctx, const struct token *token,
 	return ret;
 }
 
+static int
+parse_indlst_id2ptr(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len,
+		    void __rte_unused *buf, unsigned int __rte_unused size)
+{
+	struct rte_flow_action *action = ctx->object;
+	struct rte_flow_action_indirect_list *action_conf;
+	const struct indlst_conf *indlst_conf;
+	uint32_t id;
+	int ret;
+
+	if (!action)
+		return -1;
+	ctx->objdata = 0;
+	ctx->object = &id;
+	ctx->objmask = NULL;
+	ret = parse_int(ctx, token, str, len, ctx->object, sizeof(id));
+	if (ret != (int)len)
+		return ret;
+	ctx->object = action;
+	action_conf = (void *)(uintptr_t)action->conf;
+	action_conf->conf = NULL;
+	switch (ctx->curr) {
+	case INDIRECT_LIST_ACTION_ID2PTR_HANDLE:
+        action_conf->handle = (typeof(action_conf->handle))
+				port_action_handle_get_by_id(ctx->port, id);
+		if (!action_conf->handle) {
+			printf("no indirect list handle for id %u\n", id);
+			return -1;
+		}
+		break;
+	case INDIRECT_LIST_ACTION_ID2PTR_CONF:
+		indlst_conf = indirect_action_list_conf_get(id);
+		if (!indlst_conf)
+			return -1;
+		action_conf->conf = (const void **)indlst_conf->conf;
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
 static int
 parse_meter_profile_id2ptr(struct context *ctx, const struct token *token,
 		const char *str, unsigned int len,
@@ -11453,6 +11593,64 @@  cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
 	*hdr = &cmd_flow_token_hdr;
 }
 
+static SLIST_HEAD(, indlst_conf) indlst_conf_head =
+	SLIST_HEAD_INITIALIZER();
+
+static void
+indirect_action_flow_conf_create(const struct buffer *in)
+{
+	int len, ret;
+	uint32_t i;
+	struct indlst_conf *indlst_conf = NULL;
+	size_t base = RTE_ALIGN(sizeof(*indlst_conf), 8);
+	struct rte_flow_action *src = in->args.vc.actions;
+
+	if (!in->args.vc.actions_n)
+		goto end;
+	len = rte_flow_conv(RTE_FLOW_CONV_OP_ACTIONS, NULL, 0, src, NULL);
+	if (len <= 0)
+		goto end;
+	len = RTE_ALIGN(len, 16);
+
+	indlst_conf = calloc(1, base + len +
+			     in->args.vc.actions_n * sizeof(uintptr_t));
+	if (!indlst_conf)
+		goto end;
+	indlst_conf->id = in->args.vc.attr.group;
+	indlst_conf->conf_num = in->args.vc.actions_n - 1;
+	indlst_conf->actions = RTE_PTR_ADD(indlst_conf, base);
+	ret = rte_flow_conv(RTE_FLOW_CONV_OP_ACTIONS, indlst_conf->actions,
+			    len, src, NULL);
+	if (ret <= 0) {
+		free(indlst_conf);
+		indlst_conf = NULL;
+		goto end;
+	}
+	indlst_conf->conf = RTE_PTR_ADD(indlst_conf, base + len);
+	for (i = 0; i < indlst_conf->conf_num; i++)
+		indlst_conf->conf[i] = indlst_conf->actions[i].conf;
+	SLIST_INSERT_HEAD(&indlst_conf_head, indlst_conf, next);
+end:
+	if (indlst_conf)
+        printf("created indirect action list configuration %u\n",
+               in->args.vc.attr.group);
+	else
+        printf("cannot create indirect action list configuration %u\n",
+               in->args.vc.attr.group);
+}
+
+static const struct indlst_conf *
+indirect_action_list_conf_get(uint32_t conf_id)
+{
+	const struct indlst_conf *conf;
+
+	SLIST_FOREACH(conf, &indlst_conf_head, next) {
+		if (conf->id == conf_id)
+			return conf;
+	}
+	return NULL;
+}
+
 /** Dispatch parsed buffer to function calls. */
 static void
 cmd_flow_parsed(const struct buffer *in)
@@ -11532,6 +11730,7 @@  cmd_flow_parsed(const struct buffer *in)
 				     in->args.aged.destroy);
 		break;
 	case QUEUE_INDIRECT_ACTION_CREATE:
+	case QUEUE_INDIRECT_ACTION_LIST_CREATE:
 		port_queue_action_handle_create(
 				in->port, in->queue, in->postpone,
 				in->args.vc.attr.group,
@@ -11567,8 +11766,10 @@  cmd_flow_parsed(const struct buffer *in)
 						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
+	case INDIRECT_ACTION_LIST_CREATE:
 		port_action_handle_create(
 				in->port, in->args.vc.attr.group,
+				in->command == INDIRECT_ACTION_LIST_CREATE,
 				&((const struct rte_flow_indir_action_conf) {
 					.ingress = in->args.vc.attr.ingress,
 					.egress = in->args.vc.attr.egress,
@@ -11576,6 +11777,9 @@  cmd_flow_parsed(const struct buffer *in)
 				}),
 				in->args.vc.actions);
 		break;
+	case INDIRECT_ACTION_FLOW_CONF_CREATE:
+		indirect_action_flow_conf_create(in);
+		break;
 	case INDIRECT_ACTION_DESTROY:
 		port_action_handle_destroy(in->port,
 					   in->args.ia_destroy.action_id_n,
@@ -11653,6 +11857,7 @@  cmd_flow_parsed(const struct buffer *in)
 	default:
 		break;
 	}
+	fflush(stdout);
 }
 
 /** Token generator and output processing callback (cmdline API). */
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 096c218c12..f35088a6e9 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1764,19 +1764,13 @@  port_flow_configure(portid_t port_id,
 	return 0;
 }
 
-/** Create indirect action */
-int
-port_action_handle_create(portid_t port_id, uint32_t id,
-			  const struct rte_flow_indir_action_conf *conf,
-			  const struct rte_flow_action *action)
+static int
+action_handle_create(portid_t port_id,
+		     struct port_indirect_action *pia,
+		     const struct rte_flow_indir_action_conf *conf,
+		     const struct rte_flow_action *action,
+		     struct rte_flow_error *error)
 {
-	struct port_indirect_action *pia;
-	int ret;
-	struct rte_flow_error error;
-
-	ret = action_alloc(port_id, id, &pia);
-	if (ret)
-		return ret;
 	if (action->type == RTE_FLOW_ACTION_TYPE_AGE) {
 		struct rte_flow_action_age *age =
 			(struct rte_flow_action_age *)(uintptr_t)(action->conf);
@@ -1785,20 +1779,52 @@  port_action_handle_create(portid_t port_id, uint32_t id,
 		age->context = &pia->age_type;
 	} else if (action->type == RTE_FLOW_ACTION_TYPE_CONNTRACK) {
 		struct rte_flow_action_conntrack *ct =
-		(struct rte_flow_action_conntrack *)(uintptr_t)(action->conf);
+			(struct rte_flow_action_conntrack *)(uintptr_t)(action->conf);
 
 		memcpy(ct, &conntrack_context, sizeof(*ct));
 	}
+	pia->type = action->type;
+	pia->handle = rte_flow_action_handle_create(port_id, conf, action,
+						    error);
+	return pia->handle ? 0 : -1;
+}
+
+static int
+action_list_handle_create(portid_t port_id,
+			  struct port_indirect_action *pia,
+			  const struct rte_flow_indir_action_conf *conf,
+			  const struct rte_flow_action *actions,
+			  struct rte_flow_error *error)
+{
+	pia->type = RTE_FLOW_ACTION_TYPE_INDIRECT_LIST;
+	pia->list_handle =
+		rte_flow_action_list_handle_create(port_id, conf,
+						   actions, error);
+	return pia->list_handle ? 0 : -1;
+}
+/** Create indirect action */
+int
+port_action_handle_create(portid_t port_id, uint32_t id, bool indirect_list,
+			  const struct rte_flow_indir_action_conf *conf,
+			  const struct rte_flow_action *action)
+{
+	struct port_indirect_action *pia;
+	int ret;
+	struct rte_flow_error error;
+
+	ret = action_alloc(port_id, id, &pia);
+	if (ret)
+		return ret;
 	/* Poisoning to make sure PMDs update it in case of error. */
 	memset(&error, 0x22, sizeof(error));
-	pia->handle = rte_flow_action_handle_create(port_id, conf, action,
-						    &error);
-	if (!pia->handle) {
+	ret = indirect_list ?
+	       action_list_handle_create(port_id, pia, conf, action, &error) :
+	       action_handle_create(port_id, pia, conf, action, &error);
+	if (ret) {
 		uint32_t destroy_id = pia->id;
 		port_action_handle_destroy(port_id, 1, &destroy_id);
 		return port_flow_complain(&error);
 	}
-	pia->type = action->type;
 	printf("Indirect action #%u created\n", pia->id);
 	return 0;
 }
@@ -1833,10 +1859,17 @@  port_action_handle_destroy(portid_t port_id,
 			 */
 			memset(&error, 0x33, sizeof(error));
 
-			if (pia->handle && rte_flow_action_handle_destroy(
-					port_id, pia->handle, &error)) {
-				ret = port_flow_complain(&error);
-				continue;
+			if (pia->handle) {
+				ret = pia->type ==
+				      RTE_FLOW_ACTION_TYPE_INDIRECT_LIST ?
+					rte_flow_action_list_handle_destroy
+					(port_id, pia->list_handle, &error) :
+					rte_flow_action_handle_destroy
+					(port_id, pia->handle, &error);
+				if (ret) {
+					ret = port_flow_complain(&error);
+					continue;
+				}
 			}
 			*tmp = pia->next;
 			printf("Indirect action #%u destroyed\n", pia->id);
@@ -1867,11 +1900,18 @@  port_action_handle_flush(portid_t port_id)
 
 		/* Poisoning to make sure PMDs update it in case of error. */
 		memset(&error, 0x44, sizeof(error));
-		if (pia->handle != NULL &&
-		    rte_flow_action_handle_destroy
-					(port_id, pia->handle, &error) != 0) {
-			printf("Indirect action #%u not destroyed\n", pia->id);
-			ret = port_flow_complain(&error);
+		if (pia->handle != NULL) {
+			ret = pia->type ==
+			      RTE_FLOW_ACTION_TYPE_INDIRECT_LIST ?
+			      rte_flow_action_list_handle_destroy
+				      (port_id, pia->list_handle, &error) :
+			      rte_flow_action_handle_destroy
+				      (port_id, pia->handle, &error);
+			if (ret) {
+				printf("Indirect action #%u not destroyed\n",
+				       pia->id);
+				ret = port_flow_complain(&error);
+			}
 			tmp = &pia->next;
 		} else {
 			*tmp = pia->next;
@@ -2822,6 +2862,45 @@  port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
 	return ret;
 }
 
+static void
+queue_action_handle_create(portid_t port_id, uint32_t queue_id,
+			   struct port_indirect_action *pia,
+			   struct queue_job *job,
+			   const struct rte_flow_op_attr *attr,
+			   const struct rte_flow_indir_action_conf *conf,
+			   const struct rte_flow_action *action,
+			   struct rte_flow_error *error)
+{
+	if (action->type == RTE_FLOW_ACTION_TYPE_AGE) {
+		struct rte_flow_action_age *age =
+			(struct rte_flow_action_age *)(uintptr_t)(action->conf);
+
+		pia->age_type = ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION;
+		age->context = &pia->age_type;
+	}
+	/* Poisoning to make sure PMDs update it in case of error. */
+	pia->handle = rte_flow_async_action_handle_create(port_id, queue_id,
+							  attr, conf, action,
+							  job, error);
+	pia->type = action->type;
+}
+
+static void
+queue_action_list_handle_create(portid_t port_id, uint32_t queue_id,
+				struct port_indirect_action *pia,
+				struct queue_job *job,
+				const struct rte_flow_op_attr *attr,
+				const struct rte_flow_indir_action_conf *conf,
+				const struct rte_flow_action *action,
+				struct rte_flow_error *error)
+{
+	/* Poisoning to make sure PMDs update it in case of error. */
+	pia->type = RTE_FLOW_ACTION_TYPE_INDIRECT_LIST;
+	pia->list_handle = rte_flow_async_action_list_handle_create
+		(port_id, queue_id, attr, conf, action,
+		 job, error);
+}
+
 /** Enqueue indirect action create operation. */
 int
 port_queue_action_handle_create(portid_t port_id, uint32_t queue_id,
@@ -2835,6 +2914,8 @@  port_queue_action_handle_create(portid_t port_id, uint32_t queue_id,
 	int ret;
 	struct rte_flow_error error;
 	struct queue_job *job;
+	bool is_indirect_list = action[1].type != RTE_FLOW_ACTION_TYPE_END;
+
 
 	ret = action_alloc(port_id, id, &pia);
 	if (ret)
@@ -2853,17 +2934,16 @@  port_queue_action_handle_create(portid_t port_id, uint32_t queue_id,
 	job->type = QUEUE_JOB_TYPE_ACTION_CREATE;
 	job->pia = pia;
 
-	if (action->type == RTE_FLOW_ACTION_TYPE_AGE) {
-		struct rte_flow_action_age *age =
-			(struct rte_flow_action_age *)(uintptr_t)(action->conf);
-
-		pia->age_type = ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION;
-		age->context = &pia->age_type;
-	}
 	/* Poisoning to make sure PMDs update it in case of error. */
 	memset(&error, 0x88, sizeof(error));
-	pia->handle = rte_flow_async_action_handle_create(port_id, queue_id,
-					&attr, conf, action, job, &error);
+
+	if (is_indirect_list)
+		queue_action_list_handle_create(port_id, queue_id, pia, job,
+						&attr, conf, action, &error);
+	else
+		queue_action_handle_create(port_id, queue_id, pia, job, &attr,
+					   conf, action, &error);
+
 	if (!pia->handle) {
 		uint32_t destroy_id = pia->id;
 		port_queue_action_handle_destroy(port_id, queue_id,
@@ -2871,7 +2951,6 @@  port_queue_action_handle_create(portid_t port_id, uint32_t queue_id,
 		free(job);
 		return port_flow_complain(&error);
 	}
-	pia->type = action->type;
 	printf("Indirect action #%u creation queued\n", pia->id);
 	return 0;
 }
@@ -2920,9 +2999,15 @@  port_queue_action_handle_destroy(portid_t port_id,
 			}
 			job->type = QUEUE_JOB_TYPE_ACTION_DESTROY;
 			job->pia = pia;
-
-			if (rte_flow_async_action_handle_destroy(port_id,
-				queue_id, &attr, pia->handle, job, &error)) {
+			ret = pia->type == RTE_FLOW_ACTION_TYPE_INDIRECT_LIST ?
+			      rte_flow_async_action_list_handle_destroy
+				      (port_id, queue_id,
+				       &attr, pia->list_handle,
+				       job, &error) :
+			      rte_flow_async_action_handle_destroy
+				      (port_id, queue_id, &attr, pia->handle,
+				       job, &error);
+			if (ret) {
 				free(job);
 				ret = port_flow_complain(&error);
 				continue;
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index bdfbfd36d3..5c43b4db0b 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -228,7 +228,12 @@  struct port_indirect_action {
 	struct port_indirect_action *next; /**< Next flow in list. */
 	uint32_t id; /**< Indirect action ID. */
 	enum rte_flow_action_type type; /**< Action type. */
-	struct rte_flow_action_handle *handle;	/**< Indirect action handle. */
+	union {
+		struct rte_flow_action_handle *handle;
+		/**< Indirect action handle. */
+		struct rte_flow_action_list_handle *list_handle;
+		/**< Indirect action list handle*/
+	};
 	enum age_action_context_type age_type; /**< Age action context type. */
 };
 
@@ -921,7 +926,7 @@  void update_fwd_ports(portid_t new_pid);
 void set_fwd_eth_peer(portid_t port_id, char *peer_addr);
 
 void port_mtu_set(portid_t port_id, uint16_t mtu);
-int port_action_handle_create(portid_t port_id, uint32_t id,
+int port_action_handle_create(portid_t port_id, uint32_t id, bool indirect_list,
 			      const struct rte_flow_indir_action_conf *conf,
 			      const struct rte_flow_action *action);
 int port_action_handle_destroy(portid_t port_id,
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 1a5087abad..10a1c1af77 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -158,6 +158,7 @@  drop                 =
 flag                 =
 inc_tcp_ack          =
 inc_tcp_seq          =
+indirect_list        =
 jump                 =
 mac_swap             =
 mark                 =
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 32fc45516a..25699ebaec 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -3300,6 +3300,125 @@  The ``quota`` value is reduced according to ``mode`` setting.
    | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
    +------------------+----------------------------------------------------+
 
+Action: ``INDIRECT_LIST``
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Indirect API creates a shared flow action with unique action handle.
+Flow rules can access the shared flow action and resources related to
+that action through the indirect action handle.
+In addition, the API allows to update existing shared flow action
+configuration.  After the update completes, new action configuration
+is available to all flows that reference that shared action.
+
+Indirect actions list expands the indirect action API:
+
+- Indirect action list creates a handle for one or several
+  flow actions, while legacy indirect action handle references
+  single action only.
+  Input flow actions arranged in END terminated list.
+
+- Flow rule can provide rule specific configuration parameters to
+  existing shared handle.
+  Updates of flow rule specific configuration will not change the base
+  action configuration.
+  Base action configuration was set during the action creation.
+
+Indirect action list handle defines 2 types of resources:
+
+- Mutable handle resource can be changed during handle lifespan.
+
+- Immutable handle resource value is set during handle creation
+  and cannot be changed.
+
+There are 2 types of mutable indirect handle contexts:
+
+- Action mutable context is always shared between all flows
+  that referenced indirect actions list handle.
+  Action mutable context can be changed by explicit invocation
+  of indirect handle update function.
+
+- Flow mutable context is private to a flow.
+  Flow mutable context can be updated by indirect list handle
+  flow rule configuration.
+
+Indirect action types - immutable, action / flow mutable, are mutually
+exclusive and depend on the action definition.
+
+If indirect list handle was created from a list of actions A1 / A2 ... An / END
+indirect list flow action can update Ai flow mutable context in the
+action configuration parameter.
+Indirect list action configuration is and array [C1, C2,  .., Cn]
+where Ci corresponds to Ai in the action handle source.
+Ci configuration element points Ai flow mutable update, or it's NULL
+if Ai has no flow mutable update.
+Indirect list action configuration is NULL if the action has no flow
+mutable updates. Otherwise it points to an array of n flow mutable
+configuration pointers.
+
+**Template API:**
+
+*Action template format:*
+
+``template .. indirect_list handle Htmpl conf Ctmpl ..``
+
+``mask     .. indirect_list handle Hmask conf Cmask ..``
+
+- If Htmpl was masked (Hmask != 0), it will be fixed in that template.
+  Otherwise, indirect action value is set in a flow rule.
+
+- If Htmpl and Ctmpl[i] were masked (Hmask !=0 and Cmask[i] != 0),
+  Htmpl's Ai action flow mutable context fill be updated to
+  Ctmpl[i] values and will be fixed in that template.
+
+*Flow rule format:*
+
+``actions .. indirect_list handle Hflow conf Cflow ..``
+
+- If Htmpl was not masked in actions template, Hflow references an
+  action of the same type as Htmpl.
+
+- Cflow[i] updates handle's Ai flow mutable configuration if
+  the Ci was not masked in action template.
+
+.. _table_rte_flow_action_indirect_list:
+
+.. table:: INDIRECT_LIST
+
+   +------------------+----------------------------------+
+   | Field            | Value                            |
+   +==================+==================================+
+   | ``handle``       | Indirect action list handle      |
+   +------------------+----------------------------------+
+   | ``conf``         | Flow mutable configuration array |
+   +------------------+----------------------------------+
+
+.. code-block:: text
+
+ flow 1:
+  / indirect handle H conf C1 /
+                    |       |
+                    |       |
+                    |       |         flow 2:
+                    |       |         / indirect handle H conf C2 /
+                    |       |                           |      |
+                    |       |                           |      |
+                    |       |                           |      |
+            =========================================================
+            ^       |       |                           |      |
+            |       |       V                           |      V
+            |    ~~~~~~~~~~~~~~                      ~~~~~~~~~~~~~~~
+            |     flow mutable                        flow mutable
+            |     context 1                           context 2
+            |    ~~~~~~~~~~~~~~                      ~~~~~~~~~~~~~~~
+  indirect  |       |                                   |
+  action    |       |                                   |
+  context   |       V                                   V
+            |   -----------------------------------------------------
+            |                 action mutable context
+            |   -----------------------------------------------------
+            v                action immutable context
+            =========================================================
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_07.rst b/doc/guides/rel_notes/release_23_07.rst
index a9b1293689..d9643a35ef 100644
--- a/doc/guides/rel_notes/release_23_07.rst
+++ b/doc/guides/rel_notes/release_23_07.rst
@@ -55,6 +55,8 @@  New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+   * **Added INDIRECT_LIST flow action.**
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 3dc7d028b8..7577faf8ed 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -2447,6 +2447,94 @@  RTE_TRACE_POINT_FP(
 	rte_trace_point_emit_int(ret);
 )
 
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_action_list_handle_create,
+	RTE_TRACE_POINT_ARGS
+		(uint16_t port_id,
+		 const struct rte_flow_indir_action_conf *conf,
+		 const struct rte_flow_action *actions, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_ptr(conf);
+	rte_trace_point_emit_ptr(actions);
+	rte_trace_point_emit_int(ret);
+)
+
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_action_list_handle_destroy,
+	RTE_TRACE_POINT_ARGS
+		(uint16_t port_id,
+		 const struct rte_flow_action_list_handle *handle, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_ptr(handle);
+	rte_trace_point_emit_int(ret);
+)
+
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_async_action_list_handle_create,
+	RTE_TRACE_POINT_ARGS
+		(uint16_t port_id, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 const struct rte_flow_indir_action_conf *conf,
+		 const struct rte_flow_action *action,
+		 const void *user_data, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_u32(queue_id);
+	rte_trace_point_emit_ptr(op_attr);
+	rte_trace_point_emit_ptr(conf);
+	rte_trace_point_emit_ptr(action);
+	rte_trace_point_emit_ptr(user_data);
+	rte_trace_point_emit_int(ret);
+)
+
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_async_action_list_handle_destroy,
+	RTE_TRACE_POINT_ARGS
+		(uint16_t port_id, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 const struct rte_flow_action_list_handle *handle,
+		 const void *user_data, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_u32(queue_id);
+	rte_trace_point_emit_ptr(op_attr);
+	rte_trace_point_emit_ptr(handle);
+	rte_trace_point_emit_ptr(user_data);
+	rte_trace_point_emit_int(ret);
+)
+
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_action_list_handle_query_update,
+	RTE_TRACE_POINT_ARGS
+		(uint16_t port_id,
+		 const struct rte_flow_action_list_handle *handle,
+		 const void **update, void **query,
+		 enum rte_flow_query_update_mode mode, int ret),
+		rte_trace_point_emit_u16(port_id);
+		rte_trace_point_emit_ptr(handle);
+		rte_trace_point_emit_ptr(update);
+		rte_trace_point_emit_ptr(query);
+		rte_trace_point_emit_int(mode);
+		rte_trace_point_emit_int(ret);
+)
+
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_async_action_list_handle_query_update,
+	RTE_TRACE_POINT_ARGS
+		(uint16_t port_id, uint32_t queue_id,
+		 const struct rte_flow_op_attr *attr,
+		 const struct rte_flow_action_list_handle *handle,
+		 const void **update, void **query,
+		 enum rte_flow_query_update_mode mode,
+		 void *user_data, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_u32(queue_id);
+	rte_trace_point_emit_ptr(attr);
+	rte_trace_point_emit_ptr(handle);
+	rte_trace_point_emit_ptr(update);
+	rte_trace_point_emit_ptr(query);
+	rte_trace_point_emit_int(mode);
+	rte_trace_point_emit_ptr(user_data);
+	rte_trace_point_emit_int(ret);
+)
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
index 61010cae56..5e1462a6fb 100644
--- a/lib/ethdev/ethdev_trace_points.c
+++ b/lib/ethdev/ethdev_trace_points.c
@@ -750,3 +750,21 @@  RTE_TRACE_POINT_REGISTER(rte_tm_trace_wred_profile_add,
 
 RTE_TRACE_POINT_REGISTER(rte_tm_trace_wred_profile_delete,
 	lib.ethdev.tm.wred_profile_delete)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_action_list_handle_create,
+			 lib.ethdev.flow.action_list_handle_create)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_action_list_handle_destroy,
+			 lib.ethdev.flow.action_list_handle_destroy)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_action_list_handle_query_update,
+			 lib.ethdev.flow.action_list_handle_query_update)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_async_action_list_handle_create,
+	lib.ethdev.flow.async_action_list_handle_create)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_async_action_list_handle_destroy,
+	lib.ethdev.flow.async_action_list_handle_destroy)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_async_action_list_handle_query_update,
+			 lib.ethdev.flow.async_action_list_handle_query_update)
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 69e6e749f7..71c9b6cb84 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -259,6 +259,8 @@  static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
 	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
+	MK_FLOW_ACTION(INDIRECT_LIST,
+		       sizeof(struct rte_flow_action_indirect_list)),
 };
 
 int
@@ -2171,3 +2173,172 @@  rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
 						    user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+struct rte_flow_action_list_handle *
+rte_flow_action_list_handle_create(uint16_t port_id,
+				   const
+				   struct rte_flow_indir_action_conf *conf,
+				   const struct rte_flow_action *actions,
+				   struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+	struct rte_flow_action_list_handle *handle;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->action_list_handle_create) {
+		rte_flow_error_set(error, ENOTSUP,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				   "action_list handle not supported");
+		return NULL;
+	}
+	dev = &rte_eth_devices[port_id];
+	handle = ops->action_list_handle_create(dev, conf, actions, error);
+	ret = flow_err(port_id, -rte_errno, error);
+	rte_flow_trace_action_list_handle_create(port_id, conf, actions, ret);
+	return handle;
+}
+
+int
+rte_flow_action_list_handle_destroy(uint16_t port_id,
+				    struct rte_flow_action_list_handle *handle,
+				    struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->action_list_handle_destroy)
+		return rte_flow_error_set(error, ENOTSUP,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "action_list handle not supported");
+	dev = &rte_eth_devices[port_id];
+	ret = ops->action_list_handle_destroy(dev, handle, error);
+	ret = flow_err(port_id, ret, error);
+	rte_flow_trace_action_list_handle_destroy(port_id, handle, ret);
+	return ret;
+}
+
+struct rte_flow_action_list_handle *
+rte_flow_async_action_list_handle_create(uint16_t port_id, uint32_t queue_id,
+					 const struct rte_flow_op_attr *attr,
+					 const struct
+					      rte_flow_indir_action_conf *conf,
+					 const struct rte_flow_action *actions,
+					 void *user_data,
+					 struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+	struct rte_flow_action_list_handle *handle;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->async_action_list_handle_create) {
+		rte_flow_error_set(error, ENOTSUP,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				   "action_list handle not supported");
+		return NULL;
+	}
+	dev = &rte_eth_devices[port_id];
+	handle = ops->async_action_list_handle_create(dev, queue_id, attr, conf,
+						      actions, user_data,
+						      error);
+	ret = flow_err(port_id, -rte_errno, error);
+	rte_flow_trace_async_action_list_handle_create(port_id, queue_id, attr,
+						       conf, actions, user_data,
+						       ret);
+	return handle;
+}
+
+int
+rte_flow_async_action_list_handle_destroy
+				(uint16_t port_id, uint32_t queue_id,
+				 const struct rte_flow_op_attr *op_attr,
+				 struct rte_flow_action_list_handle *handle,
+				 void *user_data, struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->async_action_list_handle_destroy)
+		return rte_flow_error_set(error, ENOTSUP,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "async action_list handle not supported");
+	dev = &rte_eth_devices[port_id];
+	ret = ops->async_action_list_handle_destroy(dev, queue_id, op_attr,
+						    handle, user_data, error);
+	ret = flow_err(port_id, ret, error);
+	rte_flow_trace_async_action_list_handle_destroy(port_id, queue_id,
+							op_attr, handle,
+							user_data, ret);
+	return ret;
+}
+
+int
+rte_flow_action_list_handle_query_update
+			(uint16_t port_id,
+			 const struct rte_flow_action_list_handle *handle,
+			 const void **update, void **query,
+			 enum rte_flow_query_update_mode mode,
+			 struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->action_list_handle_query_update)
+		return rte_flow_error_set(error, ENOTSUP,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "action_list query_update not supported");
+	dev = &rte_eth_devices[port_id];
+	ret = ops->action_list_handle_query_update(dev, handle, update, query,
+						   mode, error);
+	ret = flow_err(port_id, ret, error);
+	rte_flow_trace_action_list_handle_query_update(port_id, handle, update,
+						       query, mode, ret);
+	return ret;
+}
+
+int
+rte_flow_async_action_list_handle_query_update
+			(uint16_t port_id, uint32_t queue_id,
+			 const struct rte_flow_op_attr *attr,
+			 const struct rte_flow_action_list_handle *handle,
+			 const void **update, void **query,
+			 enum rte_flow_query_update_mode mode,
+			 void *user_data, struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->async_action_list_handle_query_update)
+		return rte_flow_error_set(error, ENOTSUP,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "action_list async query_update not supported");
+	dev = &rte_eth_devices[port_id];
+	ret = ops->async_action_list_handle_query_update(dev, queue_id, attr,
+							 handle, update, query,
+							 mode, user_data,
+							 error);
+	ret = flow_err(port_id, ret, error);
+	rte_flow_trace_async_action_list_handle_query_update(port_id, queue_id,
+							     attr, handle,
+							     update, query,
+							     mode, user_data,
+							     ret);
+	return ret;
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index 713ba8b65c..b65096b3b7 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -2912,6 +2912,13 @@  enum rte_flow_action_type {
 	 * applied to the given ethdev Rx queue.
 	 */
 	RTE_FLOW_ACTION_TYPE_SKIP_CMAN,
+
+	/**
+	 * Action handle to reference flow actions list.
+	 *
+	 * @see struct rte_flow_action_indirect_list
+	 */
+	RTE_FLOW_ACTION_TYPE_INDIRECT_LIST,
 };
 
 /**
@@ -6118,6 +6125,268 @@  rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
 					  void *user_data,
 					  struct rte_flow_error *error);
 
+struct rte_flow_action_list_handle;
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Configure INDIRECT_LIST flow action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_INDIRECT_LIST
+ */
+struct rte_flow_action_indirect_list {
+	/** Indirect action list handle */
+	struct rte_flow_action_list_handle *handle;
+	/**
+	 * Flow mutable configuration array.
+	 * NULL if the handle has no flow mutable configuration update.
+	 * Otherwise, if the handle was created with list A1 / A2 .. An / END
+	 * size of conf is n.
+	 * conf[i] points to flow mutable update of Ai in the handle
+	 * actions list or NULL if Ai has no update.
+	 */
+	const void **conf;
+};
+
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create an indirect flow action object from flow actions list.
+ * The object is identified by a unique handle.
+ * The handle has single state and configuration
+ * across all the flow rules using it.
+ *
+ * @param[in] port_id
+ *    The port identifier of the Ethernet device.
+ * @param[in] conf
+ *   Action configuration for the indirect action list creation.
+ * @param[in] actions
+ *   Specific configuration of the indirect action lists.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   A valid handle in case of success, NULL otherwise and rte_errno is set
+ *   to one of the error codes defined:
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-ENOSYS) if underlying device does not support this functionality.
+ *   - (-EIO) if underlying device is removed.
+ *   - (-EINVAL) if *actions* list invalid.
+ *   - (-ENOTSUP) if *action* list element valid but unsupported.
+ */
+__rte_experimental
+struct rte_flow_action_list_handle *
+rte_flow_action_list_handle_create(uint16_t port_id,
+				   const
+				   struct rte_flow_indir_action_conf *conf,
+				   const struct rte_flow_action *actions,
+				   struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Async function call to create an indirect flow action object
+ * from flow actions list.
+ * The object is identified by a unique handle.
+ * The handle has single state and configuration
+ * across all the flow rules using it.
+ *
+ * @param[in] port_id
+ *    The port identifier of the Ethernet device.
+ * @param[in] queue_id
+ *   Flow queue which is used to update the rule.
+ * @param[in] attr
+ *   Indirect action update operation attributes.
+ * @param[in] conf
+ *   Action configuration for the indirect action list creation.
+ * @param[in] actions
+ *   Specific configuration of the indirect action list.
+ * @param[in] user_data
+ *   The user data that will be returned on async completion event.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   A valid handle in case of success, NULL otherwise and rte_errno is set
+ *   to one of the error codes defined:
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-ENOSYS) if underlying device does not support this functionality.
+ *   - (-EIO) if underlying device is removed.
+ *   - (-EINVAL) if *actions* list invalid.
+ *   - (-ENOTSUP) if *action* list element valid but unsupported.
+ */
+__rte_experimental
+struct rte_flow_action_list_handle *
+rte_flow_async_action_list_handle_create(uint16_t port_id, uint32_t queue_id,
+					 const struct rte_flow_op_attr *attr,
+					 const struct
+					 rte_flow_indir_action_conf *conf,
+					 const struct rte_flow_action *actions,
+					 void *user_data,
+					 struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Destroy indirect actions list by handle.
+ *
+ * @param[in] port_id
+ *    The port identifier of the Ethernet device.
+ * @param[in] handle
+ *   Handle for the indirect actions list to be destroyed.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   - (0) if success.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-ENOSYS) if underlying device does not support this functionality.
+ *   - (-EIO) if underlying device is removed.
+ *   - (-ENOENT) if actions list pointed by *action* handle was not found.
+ *   - (-EBUSY) if actions list pointed by *action* handle still used
+ */
+__rte_experimental
+int
+rte_flow_action_list_handle_destroy(uint16_t port_id,
+				    struct rte_flow_action_list_handle *handle,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue indirect action list destruction operation.
+ * The destroy queue must be the same
+ * as the queue on which the action was created.
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] queue_id
+ *   Flow queue which is used to destroy the rule.
+ * @param[in] op_attr
+ *   Indirect action destruction operation attributes.
+ * @param[in] handle
+ *   Handle for the indirect action object to be destroyed.
+ * @param[in] user_data
+ *   The user data that will be returned on the completion events.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   - (0) if success.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-ENOSYS) if underlying device does not support this functionality.
+ *   - (-EIO) if underlying device is removed.
+ *   - (-ENOENT) if actions list pointed by *action* handle was not found.
+ *   - (-EBUSY) if actions list pointed by *action* handle still used
+ */
+__rte_experimental
+int
+rte_flow_async_action_list_handle_destroy
+		(uint16_t port_id, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_list_handle *handle,
+		 void *user_data, struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow actions list.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update are carried in order
+ * specified in the mode parameter.
+ * If ether query or update is NULL, the function executes
+ * complementing operation.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect actions list object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ *   @see struct rte_flow_action_indirect_list
+ * @param query
+ *   If not NULL pointer to storage for the associated query data type.
+ *   @see struct rte_flow_action_indirect_list
+ * @param mode
+ *   Operational mode.
+ * @param error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   - (0) if success.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) if *handle* or *mode* invalid or
+ *             both *query* and *update* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_action_list_handle_query_update(uint16_t port_id,
+					 const struct
+					 rte_flow_action_list_handle *handle,
+					 const void **update, void **query,
+					 enum rte_flow_query_update_mode mode,
+					 struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow actions list query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect actions list object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ *   @see struct rte_flow_action_indirect_list
+ * @param query
+ *   If not NULL, pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ *   @see struct rte_flow_action_indirect_list
+ * @param mode
+ *   Operational mode.
+ * @param user_data
+ *   The user data that will be returned on async completion event.
+ * @param error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   - (0) if success.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) if *handle* or *mode* invalid or
+ *             both *update* and *query* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_async_action_list_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  const struct
+					  rte_flow_action_list_handle *handle,
+					  const void **update, void **query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  struct rte_flow_error *error);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/ethdev/rte_flow_driver.h b/lib/ethdev/rte_flow_driver.h
index a129a4605d..af63ef9b5c 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -121,6 +121,17 @@  struct rte_flow_ops {
 		 const void *update, void *query,
 		 enum rte_flow_query_update_mode qu_mode,
 		 struct rte_flow_error *error);
+	/** @see rte_flow_action_list_handle_create() */
+	struct rte_flow_action_list_handle *(*action_list_handle_create)
+		(struct rte_eth_dev *dev,
+		 const struct rte_flow_indir_action_conf *conf,
+		 const struct rte_flow_action actions[],
+		 struct rte_flow_error *error);
+	/** @see rte_flow_action_list_handle_destroy() */
+	int (*action_list_handle_destroy)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_list_handle *handle,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -302,6 +313,36 @@  struct rte_flow_ops {
 		 const void *update, void *query,
 		 enum rte_flow_query_update_mode qu_mode,
 		 void *user_data, struct rte_flow_error *error);
+	/** @see rte_flow_async_action_list_handle_create() */
+	struct rte_flow_action_list_handle *
+	(*async_action_list_handle_create)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *attr,
+		 const struct rte_flow_indir_action_conf *conf,
+		 const struct rte_flow_action *actions,
+		 void *user_data, struct rte_flow_error *error);
+	/** @see rte_flow_async_action_list_handle_destroy() */
+	int (*async_action_list_handle_destroy)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_list_handle *action_handle,
+		 void *user_data, struct rte_flow_error *error);
+	/** @see rte_flow_action_list_handle_query_update() */
+	int (*action_list_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 const struct rte_flow_action_list_handle *handle,
+		 const void **update, void **query,
+		 enum rte_flow_query_update_mode mode,
+		 struct rte_flow_error *error);
+	/** @see rte_flow_async_action_list_handle_query_update() */
+	int (*async_action_list_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *attr,
+		 const struct rte_flow_action_list_handle *handle,
+		 const void **update, void **query,
+		 enum rte_flow_query_update_mode mode,
+		 void *user_data, struct rte_flow_error *error);
+
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 357d1a88c0..02372b3a7e 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -299,6 +299,14 @@  EXPERIMENTAL {
 	rte_flow_action_handle_query_update;
 	rte_flow_async_action_handle_query_update;
 	rte_flow_async_create_by_index;
+
+	# added in 23.07
+    rte_flow_action_list_handle_create;
+    rte_flow_action_list_handle_destroy;
+    rte_flow_action_list_handle_query_update;
+    rte_flow_async_action_list_handle_create;
+    rte_flow_async_action_list_handle_destroy;
+    rte_flow_async_action_list_handle_query_update;
 };
 
 INTERNAL {