app/testpmd: support flow aging
authorDong Zhou <dongz@mellanox.com>
Tue, 5 May 2020 09:49:06 +0000 (12:49 +0300)
committerFerruh Yigit <ferruh.yigit@intel.com>
Mon, 11 May 2020 20:27:39 +0000 (22:27 +0200)
Currently, there is no way to check the aging event or to get the
current aged flows in testpmd, this patch include those implements, it's
included:

- Add new item "flow_aged" to the current print event command arguments.
- Add new command to list all aged flows, meanwhile, we can set
  parameter to destroy it.

Signed-off-by: Dong Zhou <dongz@mellanox.com>
Acked-by: Matan Azrad <matan@mellanox.com>
Acked-by: Ori Kam <orika@mellanox.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
app/test-pmd/cmdline.c
app/test-pmd/cmdline_flow.c
app/test-pmd/config.c
app/test-pmd/parameters.c
app/test-pmd/testpmd.c
app/test-pmd/testpmd.h
doc/guides/testpmd_app_ug/run_app.rst
doc/guides/testpmd_app_ug/testpmd_funcs.rst

index 1375f22..bcf9080 100644 (file)
@@ -1125,6 +1125,10 @@ static void cmd_help_long_parsed(void *parsed_result,
                        "    Restrict ingress traffic to the defined"
                        " flow rules\n\n"
 
+                       "flow aged {port_id} [destroy]\n"
+                       "    List and destroy aged flows"
+                       " flow rules\n\n"
+
                        "set vxlan ip-version (ipv4|ipv6) vni (vni) udp-src"
                        " (udp-src) udp-dst (udp-dst) ip-src (ip-src) ip-dst"
                        " (ip-dst) eth-src (eth-src) eth-dst (eth-dst)\n"
index 45bcff3..4e2006c 100644 (file)
@@ -67,6 +67,7 @@ enum index {
        DUMP,
        QUERY,
        LIST,
+       AGED,
        ISOLATE,
 
        /* Destroy arguments. */
@@ -78,6 +79,9 @@ enum index {
        /* List arguments. */
        LIST_GROUP,
 
+       /* Destroy aged flow arguments. */
+       AGED_DESTROY,
+
        /* Validate/create arguments. */
        GROUP,
        PRIORITY,
@@ -664,6 +668,9 @@ struct buffer {
                struct {
                        int set;
                } isolate; /**< Isolated mode arguments. */
+               struct {
+                       int destroy;
+               } aged; /**< Aged arguments. */
        } args; /**< Command arguments. */
 };
 
@@ -719,6 +726,12 @@ static const enum index next_list_attr[] = {
        ZERO,
 };
 
+static const enum index next_aged_attr[] = {
+       AGED_DESTROY,
+       END,
+       ZERO,
+};
+
 static const enum index item_param[] = {
        ITEM_PARAM_IS,
        ITEM_PARAM_SPEC,
@@ -1466,6 +1479,9 @@ static int parse_action(struct context *, const struct token *,
 static int parse_list(struct context *, const struct token *,
                      const char *, unsigned int,
                      void *, unsigned int);
+static int parse_aged(struct context *, const struct token *,
+                     const char *, unsigned int,
+                     void *, unsigned int);
 static int parse_isolate(struct context *, const struct token *,
                         const char *, unsigned int,
                         void *, unsigned int);
@@ -1649,6 +1665,7 @@ static const struct token token_list[] = {
                              FLUSH,
                              DUMP,
                              LIST,
+                             AGED,
                              QUERY,
                              ISOLATE)),
                .call = parse_init,
@@ -1708,6 +1725,13 @@ static const struct token token_list[] = {
                .args = ARGS(ARGS_ENTRY(struct buffer, port)),
                .call = parse_list,
        },
+       [AGED] = {
+               .name = "aged",
+               .help = "list and destroy aged flows",
+               .next = NEXT(next_aged_attr, NEXT_ENTRY(PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_aged,
+       },
        [ISOLATE] = {
                .name = "isolate",
                .help = "restrict ingress traffic to the defined flow rules",
@@ -1741,6 +1765,12 @@ static const struct token token_list[] = {
                .args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)),
                .call = parse_list,
        },
+       [AGED_DESTROY] = {
+               .name = "destroy",
+               .help = "specify aged flows need be destroyed",
+               .call = parse_aged,
+               .comp = comp_none,
+       },
        /* Validate/create attributes. */
        [GROUP] = {
                .name = "group",
@@ -5367,6 +5397,35 @@ parse_list(struct context *ctx, const struct token *token,
        return len;
 }
 
+/** Parse tokens for list all aged flows command. */
+static int
+parse_aged(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 != AGED)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+       }
+       if (ctx->curr == AGED_DESTROY)
+               out->args.aged.destroy = 1;
+       return len;
+}
+
 /** Parse tokens for isolate command. */
 static int
 parse_isolate(struct context *ctx, const struct token *token,
@@ -6367,6 +6426,9 @@ cmd_flow_parsed(const struct buffer *in)
        case ISOLATE:
                port_flow_isolate(in->port, in->args.isolate.set);
                break;
+       case AGED:
+               port_flow_aged(in->port, in->args.aged.destroy);
+               break;
        default:
                break;
        }
index 72f25d1..035d336 100644 (file)
@@ -1367,6 +1367,26 @@ port_flow_validate(portid_t port_id,
        return 0;
 }
 
+/** Update age action context by port_flow pointer. */
+void
+update_age_action_context(const struct rte_flow_action *actions,
+                       struct port_flow *pf)
+{
+       struct rte_flow_action_age *age = NULL;
+
+       for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+               switch (actions->type) {
+               case RTE_FLOW_ACTION_TYPE_AGE:
+                       age = (struct rte_flow_action_age *)
+                               (uintptr_t)actions->conf;
+                       age->context = pf;
+                       return;
+               default:
+                       break;
+               }
+       }
+}
+
 /** Create flow rule. */
 int
 port_flow_create(portid_t port_id,
@@ -1377,28 +1397,27 @@ port_flow_create(portid_t port_id,
        struct rte_flow *flow;
        struct rte_port *port;
        struct port_flow *pf;
-       uint32_t id;
+       uint32_t id = 0;
        struct rte_flow_error error;
 
-       /* Poisoning to make sure PMDs update it in case of error. */
-       memset(&error, 0x22, sizeof(error));
-       flow = rte_flow_create(port_id, attr, pattern, actions, &error);
-       if (!flow)
-               return port_flow_complain(&error);
        port = &ports[port_id];
        if (port->flow_list) {
                if (port->flow_list->id == UINT32_MAX) {
                        printf("Highest rule ID is already assigned, delete"
                               " it first");
-                       rte_flow_destroy(port_id, flow, NULL);
                        return -ENOMEM;
                }
                id = port->flow_list->id + 1;
-       } else
-               id = 0;
+       }
        pf = port_flow_new(attr, pattern, actions, &error);
-       if (!pf) {
-               rte_flow_destroy(port_id, flow, NULL);
+       if (!pf)
+               return port_flow_complain(&error);
+       update_age_action_context(actions, pf);
+       /* Poisoning to make sure PMDs update it in case of error. */
+       memset(&error, 0x22, sizeof(error));
+       flow = rte_flow_create(port_id, attr, pattern, actions, &error);
+       if (!flow) {
+               free(pf);
                return port_flow_complain(&error);
        }
        pf->next = port->flow_list;
@@ -1570,6 +1589,73 @@ port_flow_query(portid_t port_id, uint32_t rule,
        return 0;
 }
 
+/** List simply and destroy all aged flows. */
+void
+port_flow_aged(portid_t port_id, uint8_t destroy)
+{
+       void **contexts;
+       int nb_context, total = 0, idx;
+       struct rte_flow_error error;
+       struct port_flow *pf;
+
+       if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+           port_id == (portid_t)RTE_PORT_ALL)
+               return;
+       total = rte_flow_get_aged_flows(port_id, NULL, 0, &error);
+       printf("Port %u total aged flows: %d\n", port_id, total);
+       if (total < 0) {
+               port_flow_complain(&error);
+               return;
+       }
+       if (total == 0)
+               return;
+       contexts = malloc(sizeof(void *) * total);
+       if (contexts == NULL) {
+               printf("Cannot allocate contexts for aged flow\n");
+               return;
+       }
+       printf("ID\tGroup\tPrio\tAttr\n");
+       nb_context = rte_flow_get_aged_flows(port_id, contexts, total, &error);
+       if (nb_context != total) {
+               printf("Port:%d get aged flows count(%d) != total(%d)\n",
+                       port_id, nb_context, total);
+               free(contexts);
+               return;
+       }
+       for (idx = 0; idx < nb_context; idx++) {
+               pf = (struct port_flow *)contexts[idx];
+               if (!pf) {
+                       printf("Error: get Null context in port %u\n", port_id);
+                       continue;
+               }
+               printf("%" PRIu32 "\t%" PRIu32 "\t%" PRIu32 "\t%c%c%c\t\n",
+                      pf->id,
+                      pf->rule.attr->group,
+                      pf->rule.attr->priority,
+                      pf->rule.attr->ingress ? 'i' : '-',
+                      pf->rule.attr->egress ? 'e' : '-',
+                      pf->rule.attr->transfer ? 't' : '-');
+       }
+       if (destroy) {
+               int ret;
+               uint32_t flow_id;
+
+               total = 0;
+               printf("\n");
+               for (idx = 0; idx < nb_context; idx++) {
+                       pf = (struct port_flow *)contexts[idx];
+                       if (!pf)
+                               continue;
+                       flow_id = pf->id;
+                       ret = port_flow_destroy(port_id, 1, &flow_id);
+                       if (!ret)
+                               total++;
+               }
+               printf("%d flows be destroyed\n", total);
+       }
+       free(contexts);
+}
+
 /** List flow rules. */
 void
 port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])
index 30c1753..92b5575 100644 (file)
@@ -187,9 +187,9 @@ usage(char* progname)
        printf("  --no-rmv-interrupt: disable device removal interrupt.\n");
        printf("  --bitrate-stats=N: set the logical core N to perform "
                "bit-rate calculation.\n");
-       printf("  --print-event <unknown|intr_lsc|queue_state|intr_reset|vf_mbox|macsec|intr_rmv|all>: "
+       printf("  --print-event <unknown|intr_lsc|queue_state|intr_reset|vf_mbox|macsec|intr_rmv|flow_aged|all>: "
               "enable print of designated event or all of them.\n");
-       printf("  --mask-event <unknown|intr_lsc|queue_state|intr_reset|vf_mbox|macsec|intr_rmv|all>: "
+       printf("  --mask-event <unknown|intr_lsc|queue_state|intr_reset|vf_mbox|macsec|intr_rmv|flow_aged|all>: "
               "disable print of designated event or all of them.\n");
        printf("  --flow-isolate-all: "
               "requests flow API isolated mode on all ports at initialization time.\n");
@@ -545,6 +545,8 @@ parse_event_printing_config(const char *optarg, int enable)
                mask = UINT32_C(1) << RTE_ETH_EVENT_NEW;
        else if (!strcmp(optarg, "dev_released"))
                mask = UINT32_C(1) << RTE_ETH_EVENT_DESTROY;
+       else if (!strcmp(optarg, "flow_aged"))
+               mask = UINT32_C(1) << RTE_ETH_EVENT_FLOW_AGED;
        else if (!strcmp(optarg, "all"))
                mask = ~UINT32_C(0);
        else {
index 99bacdd..a2d0be5 100644 (file)
@@ -375,6 +375,7 @@ static const char * const eth_event_desc[] = {
        [RTE_ETH_EVENT_INTR_RMV] = "device removal",
        [RTE_ETH_EVENT_NEW] = "device probed",
        [RTE_ETH_EVENT_DESTROY] = "device released",
+       [RTE_ETH_EVENT_FLOW_AGED] = "flow aged",
        [RTE_ETH_EVENT_MAX] = NULL,
 };
 
@@ -388,7 +389,8 @@ uint32_t event_print_mask = (UINT32_C(1) << RTE_ETH_EVENT_UNKNOWN) |
                            (UINT32_C(1) << RTE_ETH_EVENT_INTR_RESET) |
                            (UINT32_C(1) << RTE_ETH_EVENT_IPSEC) |
                            (UINT32_C(1) << RTE_ETH_EVENT_MACSEC) |
-                           (UINT32_C(1) << RTE_ETH_EVENT_INTR_RMV);
+                           (UINT32_C(1) << RTE_ETH_EVENT_INTR_RMV) |
+                           (UINT32_C(1) << RTE_ETH_EVENT_FLOW_AGED);
 /*
  * Decide if all memory are locked for performance.
  */
index 7ff4c5d..fb39167 100644 (file)
@@ -747,12 +747,15 @@ int port_flow_create(portid_t port_id,
                     const struct rte_flow_attr *attr,
                     const struct rte_flow_item *pattern,
                     const struct rte_flow_action *actions);
+void update_age_action_context(const struct rte_flow_action *actions,
+                    struct port_flow *pf);
 int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
 int port_flow_flush(portid_t port_id);
 int port_flow_dump(portid_t port_id, const char *file_name);
 int port_flow_query(portid_t port_id, uint32_t rule,
                    const struct rte_flow_action *action);
 void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
+void port_flow_aged(portid_t port_id, uint8_t destroy);
 int port_flow_isolate(portid_t port_id, int set);
 
 void rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id);
index 727ef52..7121336 100644 (file)
@@ -388,12 +388,12 @@ The command line options are:
 
     Set the logical core N to perform bitrate calculation.
 
-*   ``--print-event <unknown|intr_lsc|queue_state|intr_reset|vf_mbox|macsec|intr_rmv|dev_probed|dev_released|all>``
+*   ``--print-event <unknown|intr_lsc|queue_state|intr_reset|vf_mbox|macsec|intr_rmv|dev_probed|dev_released|flow_aged|all>``
 
     Enable printing the occurrence of the designated event. Using all will
     enable all of them.
 
-*   ``--mask-event <unknown|intr_lsc|queue_state|intr_reset|vf_mbox|macsec|intr_rmv|dev_probed|dev_released|all>``
+*   ``--mask-event <unknown|intr_lsc|queue_state|intr_reset|vf_mbox|macsec|intr_rmv|dev_probed|dev_released|flow_aged|all>``
 
     Disable printing the occurrence of the designated event. Using all will
     disable all of them.
index a360ecc..19260cc 100644 (file)
@@ -3616,6 +3616,10 @@ following sections.
 
    flow dump {port_id} {output_file}
 
+- List and destroy aged flow rules::
+
+   flow aged {port_id} [destroy]
+
 Validating flow rules
 ~~~~~~~~~~~~~~~~~~~~~
 
@@ -4503,6 +4507,64 @@ Otherwise, it will complain error occurred::
 
    Caught error type [...] ([...]): [...]
 
+Listing and destroying aged flow rules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow aged`` simply lists aged flow rules be get from api ``rte_flow_get_aged_flows``,
+and ``destroy`` parameter can be used to destroy those flow rules in PMD.
+
+   flow aged {port_id} [destroy]
+
+Listing current aged flow rules::
+
+   testpmd> flow aged 0
+   Port 0 total aged flows: 0
+   testpmd> flow create 0 ingress pattern eth / ipv4 src is 2.2.2.14 / end
+      actions age timeout 5 / queue index 0 /  end
+   Flow rule #0 created
+   testpmd> flow create 0 ingress pattern eth / ipv4 src is 2.2.2.15 / end
+      actions age timeout 4 / queue index 0 /  end
+   Flow rule #1 created
+   testpmd> flow create 0 ingress pattern eth / ipv4 src is 2.2.2.16 / end
+      actions age timeout 2 / queue index 0 /  end
+   Flow rule #2 created
+   testpmd> flow create 0 ingress pattern eth / ipv4 src is 2.2.2.17 / end
+      actions age timeout 3 / queue index 0 /  end
+   Flow rule #3 created
+
+
+Aged Rules are simply list as command ``flow list {port_id}``, but strip the detail rule
+information, all the aged flows are sorted by the longest timeout time. For example, if
+those rules be 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 aged 0
+   Port 0 total aged flows: 4
+   ID      Group   Prio    Attr
+   2       0       0       i--
+   3       0       0       i--
+   1       0       0       i--
+   0       0       0       i--
+
+If attach ``destroy`` parameter, the command will destroy all the list aged flow rules.
+
+   testpmd> flow aged 0 destroy
+   Port 0 total aged flows: 4
+   ID      Group   Prio    Attr
+   2       0       0       i--
+   3       0       0       i--
+   1       0       0       i--
+   0       0       0       i--
+
+   Flow rule #2 destroyed
+   Flow rule #3 destroyed
+   Flow rule #1 destroyed
+   Flow rule #0 destroyed
+   4 flows be destroyed
+   testpmd> flow aged 0
+   Port 0 total aged flows: 0
+
+
 Sample QinQ flow rules
 ~~~~~~~~~~~~~~~~~~~~~~