app/testpmd: support flow aging

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>
This commit is contained in:
Dong Zhou 2020-05-05 12:49:06 +03:00 committed by Ferruh Yigit
parent 94a6f2def2
commit 0e459ffa08
8 changed files with 237 additions and 16 deletions

View 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"

View 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;
}

View 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])

View 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 {

View 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.
*/

View 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);

View 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.

View 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
~~~~~~~~~~~~~~~~~~~~~~