When application use queue-based flow rule management and operate the
same flow rule on the same queue, e.g create/destroy/query, API of
querying aged flow rules should also have queue id parameter just like
other queue-based flow APIs.

By this way, PMD can work in more optimized way since resources are
isolated by queue and needn't synchronize.

If application do use queue-based flow management but configure port
without RTE_FLOW_PORT_FLAG_STRICT_QUEUE, which means application operate
a given flow rule on different queues, the queue id parameter will
be ignored.

Signed-off-by: Michael Baum <michae...@nvidia.com>
Acked-by: Ori Kam <or...@nvidia.com>
Acked-by: Andrew Rybchenko <andrew.rybche...@oktetlabs.ru>
---
 app/test-pmd/cmdline_flow.c                 |  17 ++-
 app/test-pmd/config.c                       | 159 +++++++++++++++++++-
 app/test-pmd/testpmd.h                      |   1 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  75 +++++++++
 lib/ethdev/rte_flow.c                       |  22 +++
 lib/ethdev/rte_flow.h                       |  49 +++++-
 lib/ethdev/rte_flow_driver.h                |   7 +
 lib/ethdev/version.map                      |   1 +
 8 files changed, 326 insertions(+), 5 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 59829371d4..992aeb95b3 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -129,6 +129,7 @@ enum index {
        /* Queue arguments. */
        QUEUE_CREATE,
        QUEUE_DESTROY,
+       QUEUE_AGED,
        QUEUE_INDIRECT_ACTION,
 
        /* Queue create arguments. */
@@ -1170,6 +1171,7 @@ static const enum index next_table_destroy_attr[] = {
 static const enum index next_queue_subcmd[] = {
        QUEUE_CREATE,
        QUEUE_DESTROY,
+       QUEUE_AGED,
        QUEUE_INDIRECT_ACTION,
        ZERO,
 };
@@ -2967,6 +2969,13 @@ static const struct token token_list[] = {
                .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
                .call = parse_qo_destroy,
        },
+       [QUEUE_AGED] = {
+               .name = "aged",
+               .help = "list and destroy aged flows",
+               .next = NEXT(next_aged_attr, NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+               .call = parse_aged,
+       },
        [QUEUE_INDIRECT_ACTION] = {
                .name = "indirect_action",
                .help = "queue indirect actions",
@@ -8654,8 +8663,8 @@ parse_aged(struct context *ctx, const struct token *token,
        /* Nothing else to do if there is no buffer. */
        if (!out)
                return len;
-       if (!out->command) {
-               if (ctx->curr != AGED)
+       if (!out->command || out->command == QUEUE) {
+               if (ctx->curr != AGED && ctx->curr != QUEUE_AGED)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -10610,6 +10619,10 @@ cmd_flow_parsed(const struct buffer *in)
        case PULL:
                port_queue_flow_pull(in->port, in->queue);
                break;
+       case QUEUE_AGED:
+               port_queue_flow_aged(in->port, in->queue,
+                                    in->args.aged.destroy);
+               break;
        case QUEUE_INDIRECT_ACTION_CREATE:
                port_queue_action_handle_create(
                                in->port, in->queue, in->postpone,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 0f7dbd698f..18f3543887 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -2509,6 +2509,7 @@ port_queue_flow_create(portid_t port_id, queueid_t 
queue_id,
                       const struct rte_flow_action *actions)
 {
        struct rte_flow_op_attr op_attr = { .postpone = postpone };
+       struct rte_flow_attr flow_attr = { 0 };
        struct rte_flow *flow;
        struct rte_port *port;
        struct port_flow *pf;
@@ -2568,7 +2569,7 @@ port_queue_flow_create(portid_t port_id, queueid_t 
queue_id,
        }
        job->type = QUEUE_JOB_TYPE_FLOW_CREATE;
 
-       pf = port_flow_new(NULL, pattern, actions, &error);
+       pf = port_flow_new(&flow_attr, pattern, actions, &error);
        if (!pf) {
                free(job);
                return port_flow_complain(&error);
@@ -2905,6 +2906,162 @@ port_queue_flow_push(portid_t port_id, queueid_t 
queue_id)
        return ret;
 }
 
+/** Pull queue operation results from the queue. */
+static int
+port_queue_aged_flow_destroy(portid_t port_id, queueid_t queue_id,
+                            const uint32_t *rule, int nb_flows)
+{
+       struct rte_port *port = &ports[port_id];
+       struct rte_flow_op_result *res;
+       struct rte_flow_error error;
+       uint32_t n = nb_flows;
+       int ret = 0;
+       int i;
+
+       res = calloc(port->queue_sz, sizeof(struct rte_flow_op_result));
+       if (!res) {
+               printf("Failed to allocate memory for pulled results\n");
+               return -ENOMEM;
+       }
+
+       memset(&error, 0x66, sizeof(error));
+       while (nb_flows > 0) {
+               int success = 0;
+
+               if (n > port->queue_sz)
+                       n = port->queue_sz;
+               ret = port_queue_flow_destroy(port_id, queue_id, true, n, rule);
+               if (ret < 0) {
+                       free(res);
+                       return ret;
+               }
+               ret = rte_flow_push(port_id, queue_id, &error);
+               if (ret < 0) {
+                       printf("Failed to push operations in the queue: %s\n",
+                              strerror(-ret));
+                       free(res);
+                       return ret;
+               }
+               while (success < nb_flows) {
+                       ret = rte_flow_pull(port_id, queue_id, res,
+                                           port->queue_sz, &error);
+                       if (ret < 0) {
+                               printf("Failed to pull a operation results: 
%s\n",
+                                      strerror(-ret));
+                               free(res);
+                               return ret;
+                       }
+
+                       for (i = 0; i < ret; i++) {
+                               if (res[i].status == RTE_FLOW_OP_SUCCESS)
+                                       success++;
+                       }
+               }
+               rule += n;
+               nb_flows -= n;
+               n = nb_flows;
+       }
+
+       free(res);
+       return ret;
+}
+
+/** List simply and destroy all aged flows per queue. */
+void
+port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy)
+{
+       void **contexts;
+       int nb_context, total = 0, idx;
+       uint32_t *rules = NULL;
+       struct rte_port *port;
+       struct rte_flow_error error;
+       enum age_action_context_type *type;
+       union {
+               struct port_flow *pf;
+               struct port_indirect_action *pia;
+       } ctx;
+
+       if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+           port_id == (portid_t)RTE_PORT_ALL)
+               return;
+       port = &ports[port_id];
+       if (queue_id >= port->queue_nb) {
+               printf("Error: queue #%u is invalid\n", queue_id);
+               return;
+       }
+       total = rte_flow_get_q_aged_flows(port_id, queue_id, NULL, 0, &error);
+       if (total < 0) {
+               port_flow_complain(&error);
+               return;
+       }
+       printf("Port %u queue %u total aged flows: %d\n",
+              port_id, queue_id, total);
+       if (total == 0)
+               return;
+       contexts = calloc(total, sizeof(void *));
+       if (contexts == NULL) {
+               printf("Cannot allocate contexts for aged flow\n");
+               return;
+       }
+       printf("%-20s\tID\tGroup\tPrio\tAttr\n", "Type");
+       nb_context = rte_flow_get_q_aged_flows(port_id, queue_id, contexts,
+                                              total, &error);
+       if (nb_context > total) {
+               printf("Port %u queue %u get aged flows count(%d) > 
total(%d)\n",
+                      port_id, queue_id, nb_context, total);
+               free(contexts);
+               return;
+       }
+       if (destroy) {
+               rules = malloc(sizeof(uint32_t) * nb_context);
+               if (rules == NULL)
+                       printf("Cannot allocate memory for destroy aged 
flow\n");
+       }
+       total = 0;
+       for (idx = 0; idx < nb_context; idx++) {
+               if (!contexts[idx]) {
+                       printf("Error: get Null context in port %u queue %u\n",
+                              port_id, queue_id);
+                       continue;
+               }
+               type = (enum age_action_context_type *)contexts[idx];
+               switch (*type) {
+               case ACTION_AGE_CONTEXT_TYPE_FLOW:
+                       ctx.pf = container_of(type, struct port_flow, age_type);
+                       printf("%-20s\t%" PRIu32 "\t%" PRIu32 "\t%" PRIu32
+                                                                "\t%c%c%c\t\n",
+                              "Flow",
+                              ctx.pf->id,
+                              ctx.pf->rule.attr->group,
+                              ctx.pf->rule.attr->priority,
+                              ctx.pf->rule.attr->ingress ? 'i' : '-',
+                              ctx.pf->rule.attr->egress ? 'e' : '-',
+                              ctx.pf->rule.attr->transfer ? 't' : '-');
+                       if (rules != NULL) {
+                               rules[total] = ctx.pf->id;
+                               total++;
+                       }
+                       break;
+               case ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION:
+                       ctx.pia = container_of(type,
+                                              struct port_indirect_action,
+                                              age_type);
+                       printf("%-20s\t%" PRIu32 "\n", "Indirect action",
+                              ctx.pia->id);
+                       break;
+               default:
+                       printf("Error: invalid context type %u\n", port_id);
+                       break;
+               }
+       }
+       if (rules != NULL) {
+               port_queue_aged_flow_destroy(port_id, queue_id, rules, total);
+               free(rules);
+       }
+       printf("\n%d flows destroyed\n", total);
+       free(contexts);
+}
+
 /** Pull queue operation results from the queue. */
 int
 port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7fef96f9b1..e1efd34be9 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -941,6 +941,7 @@ int port_queue_action_handle_query(portid_t port_id, 
uint32_t queue_id,
                                   bool postpone, uint32_t id);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
+void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t 
destroy);
 int port_flow_validate(portid_t port_id,
                       const struct rte_flow_attr *attr,
                       const struct rte_flow_item *pattern,
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst 
b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 81e502b369..96c5ae0fe4 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -2995,6 +2995,10 @@ following sections.
 
    flow aged {port_id} [destroy]
 
+- Enqueue list and destroy aged flow rules::
+
+   flow queue {port_id} aged {queue_id} [destroy]
+
 - Tunnel offload - create a tunnel stub::
 
    flow tunnel create {port_id} type {tunnel_type}
@@ -4308,6 +4312,77 @@ If attach ``destroy`` parameter, the command will 
destroy all the list aged flow
    testpmd> flow aged 0
    Port 0 total aged flows: 0
 
+
+Enqueueing listing and destroying aged flow rules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow queue aged`` simply lists aged flow rules be get from
+``rte_flow_get_q_aged_flows`` API, and ``destroy`` parameter can be used to
+destroy those flow rules in PMD::
+
+   flow queue {port_id} aged {queue_id} [destroy]
+
+Listing current aged flow rules::
+
+   testpmd> flow queue 0 aged 0
+   Port 0 queue 0 total aged flows: 0
+   testpmd> flow queue 0 create 0 ingress tanle 0 item_template 0 
action_template 0
+      pattern eth / ipv4 src is 2.2.2.14 / end
+      actions age timeout 5 / queue index 0 /  end
+   Flow rule #0 creation enqueued
+   testpmd> flow queue 0 create 0 ingress tanle 0 item_template 0 
action_template 0
+      pattern eth / ipv4 src is 2.2.2.15 / end
+      actions age timeout 4 / queue index 0 /  end
+   Flow rule #1 creation enqueued
+   testpmd> flow queue 0 create 0 ingress tanle 0 item_template 0 
action_template 0
+      pattern eth / ipv4 src is 2.2.2.16 / end
+      actions age timeout 4 / queue index 0 /  end
+   Flow rule #2 creation enqueued
+   testpmd> flow queue 0 create 0 ingress tanle 0 item_template 0 
action_template 0
+      pattern eth / ipv4 src is 2.2.2.17 / end
+      actions age timeout 4 / queue index 0 /  end
+   Flow rule #3 creation enqueued
+   testpmd> flow pull 0 queue 0
+   Queue #0 pulled 4 operations (0 failed, 4 succeeded)
+
+Aged Rules are simply list as command ``flow queue {port_id} list {queue_id}``,
+but strip the detail rule information, all the aged flows are sorted by the
+longest timeout time. For example, if those rules is configured in the same 
time,
+ID 2 will be the first aged out rule, the next will be ID 3, ID 1, ID 0::
+
+   testpmd> flow queue 0 aged 0
+   Port 0 queue 0 total aged flows: 4
+   ID      Group   Prio    Attr
+   2       0       0       ---
+   3       0       0       ---
+   1       0       0       ---
+   0       0       0       ---
+
+   0 flows destroyed
+
+If attach ``destroy`` parameter, the command will destroy all the list aged 
flow rules::
+
+   testpmd> flow queue 0 aged 0 destroy
+   Port 0 queue 0 total aged flows: 4
+   ID      Group   Prio    Attr
+   2       0       0       ---
+   3       0       0       ---
+   1       0       0       ---
+   0       0       0       ---
+   Flow rule #2 destruction enqueued
+   Flow rule #3 destruction enqueued
+   Flow rule #1 destruction enqueued
+   Flow rule #0 destruction enqueued
+
+   4 flows destroyed
+   testpmd> flow queue 0 aged 0
+   Port 0 total aged flows: 0
+
+.. note::
+
+   The queue must be empty before attaching ``destroy`` parameter.
+
+
 Creating indirect actions
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index d11ba270db..7d0c24366c 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1132,6 +1132,28 @@ rte_flow_get_aged_flows(uint16_t port_id, void 
**contexts,
                                  NULL, rte_strerror(ENOTSUP));
 }
 
+int
+rte_flow_get_q_aged_flows(uint16_t port_id, uint32_t queue_id, void **contexts,
+                         uint32_t nb_contexts, struct rte_flow_error *error)
+{
+       struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+       const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+       int ret;
+
+       if (unlikely(!ops))
+               return -rte_errno;
+       if (likely(!!ops->get_q_aged_flows)) {
+               fts_enter(dev);
+               ret = ops->get_q_aged_flows(dev, queue_id, contexts,
+                                           nb_contexts, error);
+               fts_exit(dev);
+               return flow_err(port_id, ret, error);
+       }
+       return rte_flow_error_set(error, ENOTSUP,
+                                 RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+                                 NULL, rte_strerror(ENOTSUP));
+}
+
 struct rte_flow_action_handle *
 rte_flow_action_handle_create(uint16_t port_id,
                              const struct rte_flow_indir_action_conf *conf,
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index a93ec796cb..64ec8f0903 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -2639,6 +2639,7 @@ enum rte_flow_action_type {
         * flow.
         *
         * See struct rte_flow_action_age.
+        * See function rte_flow_get_q_aged_flows
         * See function rte_flow_get_aged_flows
         * see enum RTE_ETH_EVENT_FLOW_AGED
         * See struct rte_flow_query_age
@@ -2784,8 +2785,8 @@ struct rte_flow_action_queue {
  * on the flow. RTE_ETH_EVENT_FLOW_AGED event is triggered when a
  * port detects new aged-out flows.
  *
- * The flow context and the flow handle will be reported by the
- * rte_flow_get_aged_flows API.
+ * The flow context and the flow handle will be reported by the either
+ * rte_flow_get_aged_flows or rte_flow_get_q_aged_flows APIs.
  */
 struct rte_flow_action_age {
        uint32_t timeout:24; /**< Time in seconds. */
@@ -4314,6 +4315,50 @@ int
 rte_flow_get_aged_flows(uint16_t port_id, void **contexts,
                        uint32_t nb_contexts, struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Get aged-out flows of a given port on the given flow queue.
+ *
+ * If application configure port attribute with 
RTE_FLOW_PORT_FLAG_STRICT_QUEUE,
+ * there is no RTE_ETH_EVENT_FLOW_AGED event and this function must be called 
to
+ * get the aged flows synchronously.
+ *
+ * If application configure port attribute without
+ * RTE_FLOW_PORT_FLAG_STRICT_QUEUE, RTE_ETH_EVENT_FLOW_AGED event will be
+ * triggered at least one new aged out flow was detected on any flow queue 
after
+ * the last call to rte_flow_get_q_aged_flows.
+ * In addition, the @p queue_id will be ignored.
+ * This function can be called to get the aged flows asynchronously from the
+ * event callback or synchronously regardless the event.
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] queue_id
+ *   Flow queue to query. Ignored when RTE_FLOW_PORT_FLAG_STRICT_QUEUE not set.
+ * @param[in, out] contexts
+ *   The address of an array of pointers to the aged-out flows contexts.
+ * @param[in] nb_contexts
+ *   The length of context array pointers.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. Initialized in case of
+ *   error only.
+ *
+ * @return
+ *   if nb_contexts is 0, return the amount of all aged contexts.
+ *   if nb_contexts is not 0 , return the amount of aged flows reported
+ *   in the context array, otherwise negative errno value.
+ *
+ * @see rte_flow_action_age
+ * @see RTE_ETH_EVENT_FLOW_AGED
+ * @see rte_flow_port_flag
+ */
+__rte_experimental
+int
+rte_flow_get_q_aged_flows(uint16_t port_id, uint32_t queue_id, void **contexts,
+                         uint32_t nb_contexts, struct rte_flow_error *error);
+
 /**
  * Specify indirect action object configuration
  */
diff --git a/lib/ethdev/rte_flow_driver.h b/lib/ethdev/rte_flow_driver.h
index 7289deb538..c7d0699c91 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -84,6 +84,13 @@ struct rte_flow_ops {
                 void **context,
                 uint32_t nb_contexts,
                 struct rte_flow_error *err);
+       /** See rte_flow_get_q_aged_flows() */
+       int (*get_q_aged_flows)
+               (struct rte_eth_dev *dev,
+                uint32_t queue_id,
+                void **contexts,
+                uint32_t nb_contexts,
+                struct rte_flow_error *error);
        /** See rte_flow_action_handle_create() */
        struct rte_flow_action_handle *(*action_handle_create)
                (struct rte_eth_dev *dev,
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index e749678b96..17201fbe0f 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -295,6 +295,7 @@ EXPERIMENTAL {
        rte_eth_rx_descriptor_dump;
        rte_eth_tx_descriptor_dump;
        rte_flow_async_action_handle_query;
+       rte_flow_get_q_aged_flows;
        rte_mtr_meter_policy_get;
        rte_mtr_meter_profile_get;
 };
-- 
2.25.1

Reply via email to