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:
parent
94a6f2def2
commit
0e459ffa08
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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])
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user