Add testpmd support for the rte_flow_q_action_handle API.
Provide the command line interface for operations dequeue.
Usage example:
  flow queue 0 indirect_action 0 create action_id 9
    ingress drain yes action rss / end
  flow queue 0 indirect_action 0 update action_id 9
    action queue index 0 / end
flow queue 0 indirect_action 0 destroy action_id 9

Signed-off-by: Alexander Kozyrev <akozy...@nvidia.com>
---
 app/test-pmd/cmdline_flow.c                 | 276 ++++++++++++++++++++
 app/test-pmd/config.c                       | 131 ++++++++++
 app/test-pmd/testpmd.h                      |  10 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  65 +++++
 4 files changed, 482 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 507eb87984..50b6424933 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -120,6 +120,7 @@ enum index {
        /* Queue arguments. */
        QUEUE_CREATE,
        QUEUE_DESTROY,
+       QUEUE_INDIRECT_ACTION,
 
        /* Queue create arguments. */
        QUEUE_CREATE_ID,
@@ -133,6 +134,26 @@ enum index {
        QUEUE_DESTROY_ID,
        QUEUE_DESTROY_DRAIN,
 
+       /* Queue indirect action arguments */
+       QUEUE_INDIRECT_ACTION_CREATE,
+       QUEUE_INDIRECT_ACTION_UPDATE,
+       QUEUE_INDIRECT_ACTION_DESTROY,
+
+       /* Queue indirect action create arguments */
+       QUEUE_INDIRECT_ACTION_CREATE_ID,
+       QUEUE_INDIRECT_ACTION_INGRESS,
+       QUEUE_INDIRECT_ACTION_EGRESS,
+       QUEUE_INDIRECT_ACTION_TRANSFER,
+       QUEUE_INDIRECT_ACTION_CREATE_DRAIN,
+       QUEUE_INDIRECT_ACTION_SPEC,
+
+       /* Queue indirect action update arguments */
+       QUEUE_INDIRECT_ACTION_UPDATE_DRAIN,
+
+       /* Queue indirect action destroy arguments */
+       QUEUE_INDIRECT_ACTION_DESTROY_ID,
+       QUEUE_INDIRECT_ACTION_DESTROY_DRAIN,
+
        /* Dequeue arguments. */
        DEQUEUE_QUEUE,
 
@@ -1099,6 +1120,7 @@ static const enum index next_table_destroy_attr[] = {
 static const enum index next_queue_subcmd[] = {
        QUEUE_CREATE,
        QUEUE_DESTROY,
+       QUEUE_INDIRECT_ACTION,
        ZERO,
 };
 
@@ -1108,6 +1130,36 @@ static const enum index next_queue_destroy_attr[] = {
        ZERO,
 };
 
+static const enum index next_qia_subcmd[] = {
+       QUEUE_INDIRECT_ACTION_CREATE,
+       QUEUE_INDIRECT_ACTION_UPDATE,
+       QUEUE_INDIRECT_ACTION_DESTROY,
+       ZERO,
+};
+
+static const enum index next_qia_create_attr[] = {
+       QUEUE_INDIRECT_ACTION_CREATE_ID,
+       QUEUE_INDIRECT_ACTION_INGRESS,
+       QUEUE_INDIRECT_ACTION_EGRESS,
+       QUEUE_INDIRECT_ACTION_TRANSFER,
+       QUEUE_INDIRECT_ACTION_CREATE_DRAIN,
+       QUEUE_INDIRECT_ACTION_SPEC,
+       ZERO,
+};
+
+static const enum index next_qia_update_attr[] = {
+       QUEUE_INDIRECT_ACTION_UPDATE_DRAIN,
+       QUEUE_INDIRECT_ACTION_SPEC,
+       ZERO,
+};
+
+static const enum index next_qia_destroy_attr[] = {
+       QUEUE_INDIRECT_ACTION_DESTROY_DRAIN,
+       QUEUE_INDIRECT_ACTION_DESTROY_ID,
+       END,
+       ZERO,
+};
+
 static const enum index next_ia_create_attr[] = {
        INDIRECT_ACTION_CREATE_ID,
        INDIRECT_ACTION_INGRESS,
@@ -2163,6 +2215,12 @@ static int parse_qo(struct context *, const struct token 
*,
 static int parse_qo_destroy(struct context *, const struct token *,
                            const char *, unsigned int,
                            void *, unsigned int);
+static int parse_qia(struct context *, const struct token *,
+                    const char *, unsigned int,
+                    void *, unsigned int);
+static int parse_qia_destroy(struct context *, const struct token *,
+                            const char *, unsigned int,
+                            void *, unsigned int);
 static int parse_dequeue(struct context *, const struct token *,
                         const char *, unsigned int,
                         void *, unsigned int);
@@ -2729,6 +2787,13 @@ static const struct token token_list[] = {
                .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
                .call = parse_qo_destroy,
        },
+       [QUEUE_INDIRECT_ACTION] = {
+               .name = "indirect_action",
+               .help = "queue indirect actions",
+               .next = NEXT(next_qia_subcmd, NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+               .call = parse_qia,
+       },
        /* Queue  arguments. */
        [QUEUE_TABLE] = {
                .name = "table",
@@ -2782,6 +2847,90 @@ static const struct token token_list[] = {
                                            args.destroy.rule)),
                .call = parse_qo_destroy,
        },
+       /* Queue indirect action arguments */
+       [QUEUE_INDIRECT_ACTION_CREATE] = {
+               .name = "create",
+               .help = "create indirect action",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_UPDATE] = {
+               .name = "update",
+               .help = "update indirect action",
+               .next = NEXT(next_qia_update_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy indirect action",
+               .next = NEXT(next_qia_destroy_attr),
+               .call = parse_qia_destroy,
+       },
+       /* Indirect action destroy arguments. */
+       [QUEUE_INDIRECT_ACTION_DESTROY_DRAIN] = {
+               .name = "drain",
+               .help = "drain operation immediately",
+               .next = NEXT(next_qia_destroy_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, drain)),
+       },
+       [QUEUE_INDIRECT_ACTION_DESTROY_ID] = {
+               .name = "action_id",
+               .help = "specify a indirect action id to destroy",
+               .next = NEXT(next_qia_destroy_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.ia_destroy.action_id)),
+               .call = parse_qia_destroy,
+       },
+       /* Indirect action update arguments. */
+       [QUEUE_INDIRECT_ACTION_UPDATE_DRAIN] = {
+               .name = "drain",
+               .help = "drain operation immediately",
+               .next = NEXT(next_qia_update_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, drain)),
+       },
+       /* Indirect action create arguments. */
+       [QUEUE_INDIRECT_ACTION_CREATE_ID] = {
+               .name = "action_id",
+               .help = "specify a indirect action id to create",
+               .next = NEXT(next_qia_create_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+       },
+       [QUEUE_INDIRECT_ACTION_INGRESS] = {
+               .name = "ingress",
+               .help = "affect rule to ingress",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_EGRESS] = {
+               .name = "egress",
+               .help = "affect rule to egress",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_TRANSFER] = {
+               .name = "transfer",
+               .help = "affect rule to transfer",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_CREATE_DRAIN] = {
+               .name = "drain",
+               .help = "drain operation immediately",
+               .next = NEXT(next_qia_create_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, drain)),
+       },
+       [QUEUE_INDIRECT_ACTION_SPEC] = {
+               .name = "action",
+               .help = "specify action to create indirect handle",
+               .next = NEXT(next_action),
+       },
        /* Top-level command. */
        [DEQUEUE] = {
                .name = "dequeue",
@@ -6181,6 +6330,110 @@ parse_ia_destroy(struct context *ctx, const struct 
token *token,
        return len;
 }
 
+/** Parse tokens for indirect action commands. */
+static int
+parse_qia(struct context *ctx, const struct token *token,
+         const char *str, unsigned int len,
+         void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+               return -1;
+       /* Nothing else to do if there is no buffer. */
+       if (!out)
+               return len;
+       if (!out->command) {
+               if (ctx->curr != QUEUE)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->args.vc.data = (uint8_t *)out + size;
+               return len;
+       }
+       switch (ctx->curr) {
+       case QUEUE_INDIRECT_ACTION:
+               return len;
+       case QUEUE_INDIRECT_ACTION_CREATE:
+       case QUEUE_INDIRECT_ACTION_UPDATE:
+               out->args.vc.actions =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               out->args.vc.attr.group = UINT32_MAX;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_INDIRECT_ACTION_EGRESS:
+               out->args.vc.attr.egress = 1;
+               return len;
+       case QUEUE_INDIRECT_ACTION_INGRESS:
+               out->args.vc.attr.ingress = 1;
+               return len;
+       case QUEUE_INDIRECT_ACTION_TRANSFER:
+               out->args.vc.attr.transfer = 1;
+               return len;
+       case QUEUE_INDIRECT_ACTION_CREATE_DRAIN:
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for indirect action destroy command. */
+static int
+parse_qia_destroy(struct context *ctx, const struct token *token,
+                 const char *str, unsigned int len,
+                 void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       uint32_t *action_id;
+
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+               return -1;
+       /* Nothing else to do if there is no buffer. */
+       if (!out)
+               return len;
+       if (!out->command || out->command == QUEUE) {
+               if (ctx->curr != QUEUE_INDIRECT_ACTION_DESTROY)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.ia_destroy.action_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               return len;
+       }
+       switch (ctx->curr) {
+       case QUEUE_INDIRECT_ACTION:
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_INDIRECT_ACTION_DESTROY_ID:
+               action_id = out->args.ia_destroy.action_id
+                               + out->args.ia_destroy.action_id_n++;
+               if ((uint8_t *)action_id > (uint8_t *)out + size)
+                       return -1;
+               ctx->objdata = 0;
+               ctx->object = action_id;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_INDIRECT_ACTION_DESTROY_DRAIN:
+               return len;
+       default:
+               return -1;
+       }
+}
+
 /** Parse tokens for meter policy action commands. */
 static int
 parse_mp(struct context *ctx, const struct token *token,
@@ -9857,6 +10110,29 @@ cmd_flow_parsed(const struct buffer *in)
        case DRAIN:
                port_queue_flow_drain(in->port, in->queue);
                break;
+       case QUEUE_INDIRECT_ACTION_CREATE:
+               port_queue_action_handle_create(
+                               in->port, in->queue, in->drain,
+                               in->args.vc.attr.group,
+                               &((const struct rte_flow_indir_action_conf) {
+                                       .ingress = in->args.vc.attr.ingress,
+                                       .egress = in->args.vc.attr.egress,
+                                       .transfer = in->args.vc.attr.transfer,
+                               }),
+                               in->args.vc.actions);
+               break;
+       case QUEUE_INDIRECT_ACTION_DESTROY:
+               port_queue_action_handle_destroy(in->port,
+                                          in->queue, in->drain,
+                                          in->args.ia_destroy.action_id_n,
+                                          in->args.ia_destroy.action_id);
+               break;
+       case QUEUE_INDIRECT_ACTION_UPDATE:
+               port_queue_action_handle_update(in->port,
+                                               in->queue, in->drain,
+                                               in->args.vc.attr.group,
+                                               in->args.vc.actions);
+               break;
        case INDIRECT_ACTION_CREATE:
                port_action_handle_create(
                                in->port, in->args.vc.attr.group,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 5d23edf562..634174eec6 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -2536,6 +2536,137 @@ port_queue_flow_destroy(portid_t port_id, queueid_t 
queue_id,
        return ret;
 }
 
+/** Enqueue indirect action create operation*/
+int
+port_queue_action_handle_create(portid_t port_id, uint32_t queue_id,
+                               bool drain, uint32_t id,
+                               const struct rte_flow_indir_action_conf *conf,
+                               const struct rte_flow_action *action)
+{
+       const struct rte_flow_q_ops_attr attr = { .drain = drain};
+       struct rte_port *port;
+       struct port_indirect_action *pia;
+       int ret;
+       struct rte_flow_error error;
+
+       ret = action_alloc(port_id, id, &pia);
+       if (ret)
+               return ret;
+
+       port = &ports[port_id];
+       if (queue_id >= port->queue_nb) {
+               printf("Queue #%u is invalid\n", queue_id);
+               return -EINVAL;
+       }
+
+       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_q_action_handle_create(port_id, queue_id, &attr,
+                                                     conf, action, &error);
+       if (!pia->handle) {
+               uint32_t destroy_id = pia->id;
+               port_queue_action_handle_destroy(port_id, queue_id,
+                                                drain, 1, &destroy_id);
+               return port_flow_complain(&error);
+       }
+       pia->type = action->type;
+       printf("Indirect action #%u creation queued\n", pia->id);
+       return 0;
+}
+
+/** Enqueue indirect action destroy operation*/
+int
+port_queue_action_handle_destroy(portid_t port_id,
+                                uint32_t queue_id, bool drain,
+                                uint32_t n, const uint32_t *actions)
+{
+       const struct rte_flow_q_ops_attr attr = { .drain = drain};
+       struct rte_port *port;
+       struct port_indirect_action **tmp;
+       uint32_t c = 0;
+       int ret = 0;
+
+       if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+           port_id == (portid_t)RTE_PORT_ALL)
+               return -EINVAL;
+       port = &ports[port_id];
+
+       if (queue_id >= port->queue_nb) {
+               printf("Queue #%u is invalid\n", queue_id);
+               return -EINVAL;
+       }
+
+       tmp = &port->actions_list;
+       while (*tmp) {
+               uint32_t i;
+
+               for (i = 0; i != n; ++i) {
+                       struct rte_flow_error error;
+                       struct port_indirect_action *pia = *tmp;
+
+                       if (actions[i] != pia->id)
+                               continue;
+                       /*
+                        * Poisoning to make sure PMDs update it in case
+                        * of error.
+                        */
+                       memset(&error, 0x99, sizeof(error));
+
+                       if (pia->handle &&
+                           rte_flow_q_action_handle_destroy(port_id, queue_id,
+                                               &attr, pia->handle, &error)) {
+                               ret = port_flow_complain(&error);
+                               continue;
+                       }
+                       *tmp = pia->next;
+                       printf("Indirect action #%u destruction queued\n",
+                              pia->id);
+                       free(pia);
+                       break;
+               }
+               if (i == n)
+                       tmp = &(*tmp)->next;
+               ++c;
+       }
+       return ret;
+}
+
+/** Enqueue indirect action update operation*/
+int
+port_queue_action_handle_update(portid_t port_id,
+                               uint32_t queue_id, bool drain, uint32_t id,
+                               const struct rte_flow_action *action)
+{
+       const struct rte_flow_q_ops_attr attr = { .drain = drain};
+       struct rte_port *port;
+       struct rte_flow_error error;
+       struct rte_flow_action_handle *action_handle;
+
+       action_handle = port_action_handle_get_by_id(port_id, id);
+       if (!action_handle)
+               return -EINVAL;
+
+       port = &ports[port_id];
+       if (queue_id >= port->queue_nb) {
+               printf("Queue #%u is invalid\n", queue_id);
+               return -EINVAL;
+       }
+
+       if (rte_flow_q_action_handle_update(port_id, queue_id, &attr,
+                                           action_handle, action, &error)) {
+               return port_flow_complain(&error);
+       }
+       printf("Indirect action #%u update queued\n", id);
+       return 0;
+}
+
 /** Drain all the queue operations down the queue. */
 int
 port_queue_flow_drain(portid_t port_id, queueid_t queue_id)
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 3cf336dbae..eeaf1864cd 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -934,6 +934,16 @@ int port_queue_flow_create(portid_t port_id, queueid_t 
queue_id,
                           const struct rte_flow_action *actions);
 int port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
                            bool drain, uint32_t n, const uint32_t *rule);
+int port_queue_action_handle_create(portid_t port_id, uint32_t queue_id,
+                       bool drain, uint32_t id,
+                       const struct rte_flow_indir_action_conf *conf,
+                       const struct rte_flow_action *action);
+int port_queue_action_handle_destroy(portid_t port_id,
+                                    uint32_t queue_id, bool drain,
+                                    uint32_t n, const uint32_t *action);
+int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
+                                   bool drain, uint32_t id,
+                                   const struct rte_flow_action *action);
 int port_queue_flow_drain(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_dequeue(portid_t port_id, queueid_t queue_id);
 int port_flow_validate(portid_t port_id,
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst 
b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index fff4de8f00..dfb81d56d8 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -4728,6 +4728,31 @@ port 0::
        testpmd> flow indirect_action 0 create action_id \
                ingress action rss queues 0 1 end / end
 
+Enqueueing creation of indirect actions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow queue indirect_action create`` adds creation operation of an indirect
+action to a queue. It is bound to ``rte_flow_q_action_handle_create()``::
+
+   flow queue {port_id} create {queue_id} [drain {boolean}]
+       table {table_id} item_template {item_template_id}
+       action_template {action_template_id}
+       pattern {item} [/ {item} [...]] / end
+       actions {action} [/ {action} [...]] / end
+
+If successful, it will show::
+
+   Indirect action #[...] creation queued
+
+Otherwise it will show an error message of the form::
+
+   Caught error type [...] ([...]): [...]
+
+This command uses the same parameters as  ``flow indirect_action create``,
+described in `Creating indirect actions`_.
+
+``flow queue dequeue`` must be called to retrieve the operation status.
+
 Updating indirect actions
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -4757,6 +4782,25 @@ Update indirect rss action having id 100 on port 0 with 
rss to queues 0 and 3
 
    testpmd> flow indirect_action 0 update 100 action rss queues 0 3 end / end
 
+Enqueueing update of indirect actions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow queue indirect_action update`` adds update operation for an indirect
+action to a queue. It is bound to ``rte_flow_q_action_handle_update()``::
+
+   flow queue {port_id} indirect_action {queue_id} update
+      {indirect_action_id} [drain {boolean}] action {action} / end
+
+If successful, it will return a flow rule ID usable with other commands::
+
+   Indirect action #[...] update queued
+
+Otherwise it will show an error message of the form::
+
+   Caught error type [...] ([...]): [...]
+
+``flow queue dequeue`` must be called to retrieve the operation status.
+
 Destroying indirect actions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -4780,6 +4824,27 @@ Destroy indirect actions having id 100 & 101::
 
    testpmd> flow indirect_action 0 destroy action_id 100 action_id 101
 
+Enqueueing destruction of indirect actions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow queue indirect_action destroy`` adds destruction operation to destroy
+one or more indirect actions from their indirect action IDs (as returned by
+``flow queue {port_id} indirect_action {queue_id} create``) to a queue.
+It is bound to ``rte_flow_q_action_handle_destroy()``::
+
+   flow queue {port_id} indirect_action {queue_id} destroy
+      [drain {boolean}] action_id {indirect_action_id} [...]
+
+If successful, it will return a flow rule ID usable with other commands::
+
+   Indirect action #[...] destruction queued
+
+Otherwise it will show an error message of the form::
+
+   Caught error type [...] ([...]): [...]
+
+``flow queue dequeue`` must be called to retrieve the operation status.
+
 Query indirect actions
 ~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.18.2

Reply via email to