Indirect flow action provides a handler to hardware
flow action object. The handler is  used in flow rules for sharing
hardware action object state.

Current  INDIRECT flow handler can reference a single
flow action type.

New INDIRECT_LIST extends existing functionality.
INDIRECT_LIST flow handler can reference one or many flow actions.

testpmd example:

set raw_encap 0 \
eth src is 11:00:00:00:00:11 dst is aa:00:00:00:00:aa / \
ipv4 src is 1.1.1.1 dst is 2.2.2.2 ttl is 64 proto is 17 / \
udp src is 0x1234 dst is 4789 / vxlan vni is 0xabcd / end_set
set raw_encap 1 \
eth src is 22:00:00:00:00:22 dst is bb:00:00:00:00:bb / \
ipv6 src is 2001::1111 dst is 2001::2222 proto is 17 / \
udp src is 0x1234 dst is 4789 / vxlan vni is 0xabcd / end_set

set sample_actions 0 \
raw_encap index 0 / represented_port ethdev_port_id 0 / end
set sample_actions 1 \
raw_encap index 1 / represented_port ethdev_port_id 0 / end

flow indirect_action 0 create transfer list actions \
sample ratio 1 index 0 / \
sample ratio 1 index 1 / \
jump group 0xcaca / end

flow actions_template 0 create transfer actions_template_id 10 \
template indirect_list 0 / end mask indirect_list / end

Signed-off-by: Gregory Etelson <getel...@nvidia.com>
---
 app/test-pmd/cmdline_flow.c            |  41 ++++++-
 app/test-pmd/config.c                  | 162 +++++++++++++++++++------
 app/test-pmd/testpmd.h                 |   7 +-
 doc/guides/nics/features/default.ini   |   1 +
 doc/guides/prog_guide/rte_flow.rst     |   6 +
 doc/guides/rel_notes/release_23_07.rst |   4 +
 lib/ethdev/rte_flow.c                  |  92 ++++++++++++++
 lib/ethdev/rte_flow.h                  | 149 +++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           |  27 ++++-
 lib/ethdev/version.map                 |   4 +
 10 files changed, 452 insertions(+), 41 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 58939ec321..956a39d167 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,7 @@ enum index {
 
        /* Indirect action arguments */
        INDIRECT_ACTION_CREATE,
+       INDIRECT_ACTION_LIST_CREATE,
        INDIRECT_ACTION_UPDATE,
        INDIRECT_ACTION_DESTROY,
        INDIRECT_ACTION_QUERY,
@@ -253,6 +256,7 @@ enum index {
        INDIRECT_ACTION_EGRESS,
        INDIRECT_ACTION_TRANSFER,
        INDIRECT_ACTION_SPEC,
+       INDIRECT_ACTION_LIST,
 
        /* Indirect action destroy arguments */
        INDIRECT_ACTION_DESTROY_ID,
@@ -626,6 +630,7 @@ enum index {
        ACTION_SAMPLE_INDEX,
        ACTION_SAMPLE_INDEX_VALUE,
        ACTION_INDIRECT,
+       ACTION_INDIRECT_LIST,
        ACTION_SHARED_INDIRECT,
        INDIRECT_ACTION_PORT,
        INDIRECT_ACTION_ID2PTR,
@@ -1266,6 +1271,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 +1300,7 @@ static const enum index next_ia_create_attr[] = {
        INDIRECT_ACTION_EGRESS,
        INDIRECT_ACTION_TRANSFER,
        INDIRECT_ACTION_SPEC,
+       INDIRECT_ACTION_LIST,
        ZERO,
 };
 
@@ -2013,6 +2020,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 +2297,7 @@ static const enum index next_action_sample[] = {
        ACTION_RAW_ENCAP,
        ACTION_VXLAN_ENCAP,
        ACTION_NVGRE_ENCAP,
+       ACTION_REPRESENTED_PORT,
        ACTION_NEXT,
        ZERO,
 };
@@ -3426,6 +3435,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 +6790,14 @@ 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, 0),
+               .next = NEXT(next_ia),
+               .args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
+               .call = parse_vc,
+       },
        [ACTION_SHARED_INDIRECT] = {
                .name = "shared_indirect",
                .help = "apply indirect action by id and port",
@@ -6823,6 +6846,12 @@ 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,
+       },
        [ACTION_POL_G] = {
                .name = "g_actions",
                .help = "submit a list of associated actions for green",
@@ -7181,6 +7210,9 @@ 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;
        default:
                return -1;
        }
@@ -7278,6 +7310,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 +7489,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;
@@ -11532,6 +11569,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,6 +11605,7 @@ 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,
                                &((const struct rte_flow_indir_action_conf) {
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 096c218c12..c220682ff9 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1764,6 +1764,44 @@ port_flow_configure(portid_t port_id,
        return 0;
 }
 
+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)
+{
+       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;
+       } else if (action->type == RTE_FLOW_ACTION_TYPE_CONNTRACK) {
+               struct rte_flow_action_conntrack *ct =
+                       (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,
@@ -1773,32 +1811,21 @@ port_action_handle_create(portid_t port_id, uint32_t id,
        struct port_indirect_action *pia;
        int ret;
        struct rte_flow_error error;
+       bool is_indirect_list = action[1].type != RTE_FLOW_ACTION_TYPE_END;
 
        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);
-
-               pia->age_type = ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION;
-               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);
-
-               memcpy(ct, &conntrack_context, sizeof(*ct));
-       }
        /* 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 = is_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 +1860,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 +1901,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 +2863,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 +2915,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 +2935,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 +2952,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 +3000,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..9786e62d28 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. */
 };
 
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..ed67e86c58 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -3300,6 +3300,12 @@ The ``quota`` value is reduced according to ``mode`` 
setting.
    | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
    +------------------+----------------------------------------------------+
 
+Action: ``INDIRECT_LIST``
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The new ``INDIRECT_LIST`` flow action references one or many flow actions.
+Extends the ``INDIRECT`` flow action.
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_07.rst 
b/doc/guides/rel_notes/release_23_07.rst
index a9b1293689..955493e445 100644
--- a/doc/guides/rel_notes/release_23_07.rst
+++ b/doc/guides/rel_notes/release_23_07.rst
@@ -55,6 +55,10 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+   * **Added indirect list flow action.**
+
+     * ``RTE_FLOW_ACTION_TYPE_INDIRECT_LIST``
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 69e6e749f7..73b31fc69f 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -259,6 +259,7 @@ 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, 0),
 };
 
 int
@@ -2171,3 +2172,94 @@ 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)
+{
+       struct rte_eth_dev *dev;
+       const struct rte_flow_ops *ops;
+
+       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];
+       return ops->action_list_handle_create(dev, conf, actions, error);
+}
+
+int
+rte_flow_action_list_handle_destroy(uint16_t port_id,
+                                   struct rte_flow_action_list_handle *handle,
+                                   struct rte_flow_error *error)
+{
+       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];
+       return ops->action_list_handle_destroy(dev, handle, error);
+}
+
+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)
+{
+       struct rte_eth_dev *dev;
+       const struct rte_flow_ops *ops;
+
+       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];
+       return ops->async_action_list_handle_create(dev, queue_id, attr, conf,
+                                                   actions, user_data, error);
+}
+
+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);
+       return flow_err(port_id, ret, error);
+}
+
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index 713ba8b65c..deb5dc2f9d 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -2912,6 +2912,11 @@ enum rte_flow_action_type {
         * applied to the given ethdev Rx queue.
         */
        RTE_FLOW_ACTION_TYPE_SKIP_CMAN,
+
+       /**
+        * RTE_FLOW_ACTION_TYPE_INDIRECT_LIST
+        */
+       RTE_FLOW_ACTION_TYPE_INDIRECT_LIST,
 };
 
 /**
@@ -6118,6 +6123,150 @@ 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.
+ *
+ * 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.
+ *   - (-E2BIG) to many elements in *actions*
+ */
+__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.
+ *   - (-E2BIG) to many elements in *actions*
+ */
+__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_errno is also set.
+ */
+__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 on success, a negative errno value otherwise and rte_errno is set.
+ */
+__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);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/ethdev/rte_flow_driver.h b/lib/ethdev/rte_flow_driver.h
index a129a4605d..71d9b4b0a7 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,
@@ -294,7 +305,7 @@ struct rte_flow_ops {
                 void *data,
                 void *user_data,
                 struct rte_flow_error *error);
-       /** See rte_flow_async_action_handle_query_update */
+       /** @see rte_flow_async_action_handle_query_update */
        int (*async_action_handle_query_update)
                (struct rte_eth_dev *dev, uint32_t queue_id,
                 const struct rte_flow_op_attr *op_attr,
@@ -302,6 +313,20 @@ 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);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 357d1a88c0..d6c0b927f1 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -299,6 +299,10 @@ EXPERIMENTAL {
        rte_flow_action_handle_query_update;
        rte_flow_async_action_handle_query_update;
        rte_flow_async_create_by_index;
+       rte_flow_action_list_handle_create;
+       rte_flow_action_list_handle_destroy;
+       rte_flow_async_action_list_handle_create;
+       rte_flow_async_action_list_handle_destroy;
 };
 
 INTERNAL {
-- 
2.34.1


Reply via email to