DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH] app/testpmd: support flow aging
@ 2020-04-24 10:55 Bill Zhou
  2020-04-24 16:25 ` Ferruh Yigit
  2020-04-30 15:53 ` [dpdk-dev] [PATCH v2] " Bill Zhou
  0 siblings, 2 replies; 27+ messages in thread
From: Bill Zhou @ 2020-04-24 10:55 UTC (permalink / raw)
  To: wenzhuo.lu, jingjing.wu, bernard.iremonger, orika; +Cc: dev

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:
- Registering aging event based on verbose level, when set verbose > 0,
  will register this event, otherwise, remove this event. In this event
  only dump one line of log to user there is one aging event coming.
- Add new command to list all aged flows, meanwhile, we can set parameter
  to destroy it.

Signed-off-by: Bill Zhou <dongz@mellanox.com>
---
 app/test-pmd/cmdline.c      |   4 +
 app/test-pmd/cmdline_flow.c |  61 ++++++++++++++
 app/test-pmd/config.c       | 153 +++++++++++++++++++++++++++++++++---
 app/test-pmd/testpmd.c      |   2 +
 app/test-pmd/testpmd.h      |   9 +++
 5 files changed, 217 insertions(+), 12 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 22fb23a92d..01aed7cc1f 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -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 {boolean}\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"
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 45bcff3cf5..0349875591 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -67,6 +67,7 @@ enum index {
 	DUMP,
 	QUERY,
 	LIST,
+	AGED,
 	ISOLATE,
 
 	/* Destroy arguments. */
@@ -78,6 +79,9 @@ enum index {
 	/* List arguments. */
 	LIST_GROUP,
 
+	/* List aged 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 list 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 or 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,13 @@ 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",
+		.next = NEXT(NEXT_ENTRY(BOOLEAN)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.aged.destroy)),
+		.comp = comp_none,
+	},
 	/* Validate/create attributes. */
 	[GROUP] = {
 		.name = "group",
@@ -5367,6 +5398,33 @@ 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;
+	}
+	return len;
+}
+
 /** Parse tokens for isolate command. */
 static int
 parse_isolate(struct context *ctx, const struct token *token,
@@ -6367,6 +6425,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;
 	}
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 72f25d1521..b1133ece84 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -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,32 +1397,34 @@ 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;
 	pf->id = id;
+	pf->prev = NULL;
+	pf->next = port->flow_list;
+	if (pf->next)
+		pf->next->prev = pf;
 	pf->flow = flow;
 	port->flow_list = pf;
 	printf("Flow rule #%u created\n", pf->id);
@@ -1570,6 +1592,64 @@ 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)
+{
+	int nb_context, total = 0, idx;
+	void **contexts;
+	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)
+		return;
+	contexts = malloc(sizeof(void *) * total);
+	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)
+			continue;
+		if (pf->next)
+			pf->next->prev = pf->prev;
+		if (pf->prev)
+			pf->prev->next = pf->next;
+		else
+			(&ports[port_id])->flow_list = pf->next;
+		/*
+		 * Poisoning to make sure PMDs update it in case
+		 * of error.
+		 */
+		memset(&error, 0x33, sizeof(error));
+		if (rte_flow_destroy(port_id, pf->flow, &error))
+			port_flow_complain(&error);
+		free(pf);
+	}
+	free(contexts);
+}
+
 /** List flow rules. */
 void
 port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])
@@ -3162,6 +3242,54 @@ configure_rxtx_dump_callbacks(uint16_t verbose)
 	}
 }
 
+int
+aging_event_callback(uint16_t portid,
+		enum rte_eth_event_type event __rte_unused,
+		void *cb_arg __rte_unused,
+		void *ret_param __rte_unused)
+{
+	printf("port %u RTE_ETH_EVENT_FLOW_AGED triggered\n", portid);
+	return 0;
+}
+
+void
+add_aging_event_callbacks(portid_t portid)
+{
+	if (rte_eth_dev_callback_register(portid,
+					  RTE_ETH_EVENT_FLOW_AGED,
+					  aging_event_callback,
+					  NULL))
+		printf("port %u register RTE_ETH_EVENT_FLOW_AGED fail\n",
+			portid);
+}
+
+void
+remove_aging_event_callbacks(portid_t portid)
+{
+	if (rte_eth_dev_callback_unregister(portid,
+					RTE_ETH_EVENT_FLOW_AGED,
+					aging_event_callback,
+					NULL))
+		printf("port %u unregister RTE_ETH_EVENT_FLOW_AGED fail\n",
+			portid);
+}
+
+void
+configure_aging_event_callbacks(uint16_t verbose)
+{
+	portid_t portid;
+
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		if (port_id_is_invalid(portid, ENABLED_WARN))
+			continue;
+		if (verbose)
+			add_aging_event_callbacks(portid);
+		else
+			remove_aging_event_callbacks(portid);
+	}
+}
+
 void
 set_verbose_level(uint16_t vb_level)
 {
@@ -3169,6 +3297,7 @@ set_verbose_level(uint16_t vb_level)
 	       (unsigned int) verbose_level, (unsigned int) vb_level);
 	verbose_level = vb_level;
 	configure_rxtx_dump_callbacks(verbose_level);
+	configure_aging_event_callbacks(verbose_level);
 }
 
 void
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 99bacddbfd..0227ba1c30 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -2418,6 +2418,7 @@ start_port(portid_t pid)
 				}
 			}
 			configure_rxtx_dump_callbacks(0);
+			configure_aging_event_callbacks(0);
 			printf("Configuring Port %d (socket %u)\n", pi,
 					port->socket_id);
 			if (nb_hairpinq > 0 &&
@@ -2527,6 +2528,7 @@ start_port(portid_t pid)
 				return -1;
 		}
 		configure_rxtx_dump_callbacks(verbose_level);
+		configure_aging_event_callbacks(verbose_level);
 		if (clear_ptypes) {
 			diag = rte_eth_dev_set_ptypes(pi, RTE_PTYPE_UNKNOWN,
 					NULL, 0);
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7ff4c5dba3..64af8fa4be 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -146,6 +146,7 @@ struct fwd_stream {
 
 /** Descriptor for a single flow. */
 struct port_flow {
+	struct port_flow *prev; /**< Previous flow in list. */
 	struct port_flow *next; /**< Next flow in list. */
 	struct port_flow *tmp; /**< Temporary linking. */
 	uint32_t id; /**< Flow rule ID. */
@@ -747,12 +748,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);
@@ -895,6 +899,11 @@ void remove_rx_dump_callbacks(portid_t portid);
 void add_tx_dump_callbacks(portid_t portid);
 void remove_tx_dump_callbacks(portid_t portid);
 void configure_rxtx_dump_callbacks(uint16_t verbose);
+int aging_event_callback(uint16_t portid, enum rte_eth_event_type event,
+		      void *cb_arg, void *ret_param);
+void remove_aging_event_callbacks(portid_t portid);
+void add_aging_event_callbacks(portid_t portid);
+void configure_aging_event_callbacks(uint16_t verbose);
 
 uint16_t tx_pkt_set_md(uint16_t port_id, __rte_unused uint16_t queue,
 		       struct rte_mbuf *pkts[], uint16_t nb_pkts,
-- 
2.21.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
  2020-04-24 10:55 [dpdk-dev] [PATCH] app/testpmd: support flow aging Bill Zhou
@ 2020-04-24 16:25 ` Ferruh Yigit
  2020-04-26  7:23   ` Bill Zhou
  2020-04-30 15:53 ` [dpdk-dev] [PATCH v2] " Bill Zhou
  1 sibling, 1 reply; 27+ messages in thread
From: Ferruh Yigit @ 2020-04-24 16:25 UTC (permalink / raw)
  To: Bill Zhou, wenzhuo.lu, jingjing.wu, bernard.iremonger, orika; +Cc: dev

On 4/24/2020 11:55 AM, Bill Zhou wrote:
> 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:
> - Registering aging event based on verbose level, when set verbose > 0,
>   will register this event, otherwise, remove this event. In this event
>   only dump one line of log to user there is one aging event coming.
> - Add new command to list all aged flows, meanwhile, we can set parameter
>   to destroy it.

Can you please document new feature and command?

Instead of overloading the 'verbose', what do you think having explicit command
to register aging events? ("flow aged register <port_id>"?) I think many of the
verbose usage won't really interest in the flow aging.

> 
> Signed-off-by: Bill Zhou <dongz@mellanox.com>

<...>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
  2020-04-24 16:25 ` Ferruh Yigit
@ 2020-04-26  7:23   ` Bill Zhou
  2020-04-27 14:13     ` Ferruh Yigit
  0 siblings, 1 reply; 27+ messages in thread
From: Bill Zhou @ 2020-04-26  7:23 UTC (permalink / raw)
  To: Ferruh Yigit, wenzhuo.lu, jingjing.wu, bernard.iremonger,
	Ori Kam, Matan Azrad
  Cc: dev



> -----Original Message-----
> From: Ferruh Yigit <ferruh.yigit@intel.com>
> Sent: Saturday, April 25, 2020 12:25 AM
> To: Bill Zhou <dongz@mellanox.com>; wenzhuo.lu@intel.com;
> jingjing.wu@intel.com; bernard.iremonger@intel.com; Ori Kam
> <orika@mellanox.com>
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
> 
> On 4/24/2020 11:55 AM, Bill Zhou wrote:
> > 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:
> > - Registering aging event based on verbose level, when set verbose > 0,
> >   will register this event, otherwise, remove this event. In this event
> >   only dump one line of log to user there is one aging event coming.
> > - Add new command to list all aged flows, meanwhile, we can set
> parameter
> >   to destroy it.
> 
> Can you please document new feature and command?
> 
> Instead of overloading the 'verbose', what do you think having explicit
> command to register aging events? ("flow aged register <port_id>"?) I think
> many of the verbose usage won't really interest in the flow aging.
> 

Yes, some of the verbose usage indeed not interest in the flow aging. But If we use
register or unregister event to one port, sometime, it will repeat many times for
every ports.
What do you think if we register this event all the time, and introduce new global
var from command to control the event export to application ?
   for example: set aged_flow_event_print_en [0 | 1]

> >
> > Signed-off-by: Bill Zhou <dongz@mellanox.com>
> 
> <...>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
  2020-04-26  7:23   ` Bill Zhou
@ 2020-04-27 14:13     ` Ferruh Yigit
  2020-04-27 15:12       ` Matan Azrad
  0 siblings, 1 reply; 27+ messages in thread
From: Ferruh Yigit @ 2020-04-27 14:13 UTC (permalink / raw)
  To: Bill Zhou, wenzhuo.lu, jingjing.wu, bernard.iremonger, Ori Kam,
	Matan Azrad
  Cc: dev

On 4/26/2020 8:23 AM, Bill Zhou wrote:
> 
> 
>> -----Original Message-----
>> From: Ferruh Yigit <ferruh.yigit@intel.com>
>> Sent: Saturday, April 25, 2020 12:25 AM
>> To: Bill Zhou <dongz@mellanox.com>; wenzhuo.lu@intel.com;
>> jingjing.wu@intel.com; bernard.iremonger@intel.com; Ori Kam
>> <orika@mellanox.com>
>> Cc: dev@dpdk.org
>> Subject: Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
>>
>> On 4/24/2020 11:55 AM, Bill Zhou wrote:
>>> 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:
>>> - Registering aging event based on verbose level, when set verbose > 0,
>>>   will register this event, otherwise, remove this event. In this event
>>>   only dump one line of log to user there is one aging event coming.
>>> - Add new command to list all aged flows, meanwhile, we can set
>> parameter
>>>   to destroy it.
>>
>> Can you please document new feature and command?
>>
>> Instead of overloading the 'verbose', what do you think having explicit
>> command to register aging events? ("flow aged register <port_id>"?) I think
>> many of the verbose usage won't really interest in the flow aging.
>>
> 
> Yes, some of the verbose usage indeed not interest in the flow aging. But If we use
> register or unregister event to one port, sometime, it will repeat many times for
> every ports.
> What do you think if we register this event all the time, and introduce new global
> var from command to control the event export to application ?
>    for example: set aged_flow_event_print_en [0 | 1]

I am not also sure about registering this event always, you can register for all
ports with same command, as we do in many commands:
"flow aged register <port_id>|all"

When argument is "all" it registers for all ports, when it is a specific
port_id, registers for that port id.
And it is good to have 'unregister' command too.

> 
>>>
>>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
>>
>> <...>


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
  2020-04-27 14:13     ` Ferruh Yigit
@ 2020-04-27 15:12       ` Matan Azrad
  2020-04-30 22:26         ` Ferruh Yigit
  0 siblings, 1 reply; 27+ messages in thread
From: Matan Azrad @ 2020-04-27 15:12 UTC (permalink / raw)
  To: Ferruh Yigit, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam
  Cc: dev

Hi

From: Ferruh Yigit
> On 4/26/2020 8:23 AM, Bill Zhou wrote:
> >
> >
> >> -----Original Message-----
> >> From: Ferruh Yigit <ferruh.yigit@intel.com>
> >> Sent: Saturday, April 25, 2020 12:25 AM
> >> To: Bill Zhou <dongz@mellanox.com>; wenzhuo.lu@intel.com;
> >> jingjing.wu@intel.com; bernard.iremonger@intel.com; Ori Kam
> >> <orika@mellanox.com>
> >> Cc: dev@dpdk.org
> >> Subject: Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
> >>
> >> On 4/24/2020 11:55 AM, Bill Zhou wrote:
> >>> 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:
> >>> - Registering aging event based on verbose level, when set verbose > 0,
> >>>   will register this event, otherwise, remove this event. In this event
> >>>   only dump one line of log to user there is one aging event coming.
> >>> - Add new command to list all aged flows, meanwhile, we can set
> >> parameter
> >>>   to destroy it.
> >>
> >> Can you please document new feature and command?
> >>
> >> Instead of overloading the 'verbose', what do you think having
> >> explicit command to register aging events? ("flow aged register
> >> <port_id>"?) I think many of the verbose usage won't really interest in
> the flow aging.
> >>
> >
> > Yes, some of the verbose usage indeed not interest in the flow aging.
> > But If we use register or unregister event to one port, sometime, it
> > will repeat many times for every ports.
> > What do you think if we register this event all the time, and
> > introduce new global var from command to control the event export to
> application ?
> >    for example: set aged_flow_event_print_en [0 | 1]
> 
> I am not also sure about registering this event always, you can register for all
> ports with same command, as we do in many commands:
> "flow aged register <port_id>|all"
> 
> When argument is "all" it registers for all ports, when it is a specific port_id,
> registers for that port id.
> And it is good to have 'unregister' command too.

As I can see in testpmd default, it all the time registers to all events..
See register_eth_event_callback.

I suggest to catch the age event in callback and print it only when set aged_flow_event_print_en is on.


Matan


> 
> >
> >>>
> >>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
> >>
> >> <...>


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-04-24 10:55 [dpdk-dev] [PATCH] app/testpmd: support flow aging Bill Zhou
  2020-04-24 16:25 ` Ferruh Yigit
@ 2020-04-30 15:53 ` Bill Zhou
  2020-04-30 22:43   ` Ferruh Yigit
  2020-05-02 14:00   ` [dpdk-dev] [PATCH v3] " Bill Zhou
  1 sibling, 2 replies; 27+ messages in thread
From: Bill Zhou @ 2020-04-30 15:53 UTC (permalink / raw)
  To: wenzhuo.lu, jingjing.wu, bernard.iremonger, orika, john.mcnamara,
	marko.kovacevic, matan
  Cc: dev

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:
- Registering aging event when the testpmd application start, add new
  command to control if the event expose to the applications. If it's be
  set, when new flow be checked age out, there will be one-line output log.
- Add new command to list all aged flows, meanwhile, we can set parameter
  to destroy it.

Signed-off-by: Bill Zhou <dongz@mellanox.com>
---
v2: Update the way of registering aging event, add new command to control
if the event need be print or not. Update the output of the delete aged
flow command format.
---
 app/test-pmd/cmdline.c                      |  46 +++++++++
 app/test-pmd/cmdline_flow.c                 |  62 +++++++++++
 app/test-pmd/config.c                       | 108 ++++++++++++++++++--
 app/test-pmd/testpmd.c                      |  17 +++
 app/test-pmd/testpmd.h                      |   6 +-
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  77 ++++++++++++++
 6 files changed, 304 insertions(+), 12 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 1375f223eb..2b1dc8f8d7 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -738,6 +738,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"show port (port_id) queue-region\n"
 			"    show all queue region related configuration info\n\n"
 
+			"set aged_flow_event_log (on|off)\n"
+			"	 Enable or disable aged flow event logging\n\n"
+
 			, list_pkt_forwarding_modes()
 		);
 	}
@@ -1125,6 +1128,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"
@@ -19387,6 +19394,44 @@ cmdline_parse_inst_t cmd_showport_macs = {
 	},
 };
 
+/* Enable/Disable flow aging event log */
+struct cmd_set_aged_flow_event_log_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t keyword;
+	cmdline_fixed_string_t enable;
+};
+cmdline_parse_token_string_t cmd_set_aged_flow_event_log_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_aged_flow_event_log_result,
+		set, "set");
+cmdline_parse_token_string_t cmd_set_aged_flow_event_log_keyword =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_aged_flow_event_log_result,
+		keyword, "aged_flow_event_log");
+cmdline_parse_token_string_t cmd_set_aged_flow_event_log_enable =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_aged_flow_event_log_result,
+		enable, "on#off");
+
+static void
+cmd_set_aged_flow_event_log_parsed(void *parsed_result,
+				__rte_unused struct cmdline *cl,
+				__rte_unused void *data)
+{
+	struct cmd_set_aged_flow_event_log_result *res = parsed_result;
+	int enable = (strcmp(res->enable, "on") == 0) ? 1 : 0;
+	update_aging_event_log_status(enable);
+}
+
+cmdline_parse_inst_t cmd_set_aged_flow_event_log = {
+	.f = cmd_set_aged_flow_event_log_parsed,
+	.data = NULL,
+	.help_str = "set aged_flow_event_log on|off",
+	.tokens = {
+		(void *)&cmd_set_aged_flow_event_log_set,
+		(void *)&cmd_set_aged_flow_event_log_keyword,
+		(void *)&cmd_set_aged_flow_event_log_enable,
+		NULL,
+	},
+};
+
 /* ******************************************************************************** */
 
 /* list of instructions */
@@ -19684,6 +19729,7 @@ cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_show_set_raw,
 	(cmdline_parse_inst_t *)&cmd_show_set_raw_all,
 	(cmdline_parse_inst_t *)&cmd_config_tx_dynf_specific,
+	(cmdline_parse_inst_t *)&cmd_set_aged_flow_event_log,
 	NULL,
 };
 
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 45bcff3cf5..4e2006c543 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -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;
 	}
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 72f25d1521..035d336ab5 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -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])
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 99bacddbfd..585f5d156d 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -3068,6 +3068,21 @@ rmv_port_callback(void *arg)
 		start_packet_forwarding(0);
 }
 
+static int aged_flow_event_enable;
+void update_aging_event_log_status(int enable)
+{
+	aged_flow_event_enable = enable;
+}
+
+int aging_event_output(uint16_t portid)
+{
+	if (aged_flow_event_enable) {
+		printf("port %u RTE_ETH_EVENT_FLOW_AGED triggered\n", portid);
+		fflush(stdout);
+	}
+	return 0;
+}
+
 /* This function is used by the interrupt thread */
 static int
 eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
@@ -3098,6 +3113,8 @@ eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
 				rmv_port_callback, (void *)(intptr_t)port_id))
 			fprintf(stderr, "Could not set up deferred device removal\n");
 		break;
+	case RTE_ETH_EVENT_FLOW_AGED:
+		aging_event_output(port_id);
 	default:
 		break;
 	}
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7ff4c5dba3..046bde8c9a 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -747,14 +747,16 @@ 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);
 void tx_ring_desc_display(portid_t port_id, queueid_t txq_id, uint16_t txd_id);
 
@@ -895,6 +897,8 @@ void remove_rx_dump_callbacks(portid_t portid);
 void add_tx_dump_callbacks(portid_t portid);
 void remove_tx_dump_callbacks(portid_t portid);
 void configure_rxtx_dump_callbacks(uint16_t verbose);
+void update_aging_event_log_status(int enable);
+int aging_event_output(uint16_t portid);
 
 uint16_t tx_pkt_set_md(uint16_t port_id, __rte_unused uint16_t queue,
 		       struct rte_mbuf *pkts[], uint16_t nb_pkts,
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index a360ecccfd..9ca30eea9b 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -1062,6 +1062,21 @@ Where:
 
    Check the NIC Datasheet for hardware limits.
 
+aged flow event log set
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Currently, the flow aging event is registered when the testpmd application start, if new flow be checked
+age out, there will be one output log for it. But some applications do not interest this event, use this
+command can set if the event expose to the applications::
+
+   testpmd> set aged_flow_event_log (on|off)
+
+For examine::
+
+   testpmd> set aged_flow_event_log on
+   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
+   testpmd> set aged_flow_event_log off
+
 RSS queue region
 ~~~~~~~~~~~~~~~~
 
@@ -3616,6 +3631,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 +4522,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
 ~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
  2020-04-27 15:12       ` Matan Azrad
@ 2020-04-30 22:26         ` Ferruh Yigit
  0 siblings, 0 replies; 27+ messages in thread
From: Ferruh Yigit @ 2020-04-30 22:26 UTC (permalink / raw)
  To: Matan Azrad, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam
  Cc: dev

On 4/27/2020 4:12 PM, Matan Azrad wrote:
> Hi
> 
> From: Ferruh Yigit
>> On 4/26/2020 8:23 AM, Bill Zhou wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Ferruh Yigit <ferruh.yigit@intel.com>
>>>> Sent: Saturday, April 25, 2020 12:25 AM
>>>> To: Bill Zhou <dongz@mellanox.com>; wenzhuo.lu@intel.com;
>>>> jingjing.wu@intel.com; bernard.iremonger@intel.com; Ori Kam
>>>> <orika@mellanox.com>
>>>> Cc: dev@dpdk.org
>>>> Subject: Re: [dpdk-dev] [PATCH] app/testpmd: support flow aging
>>>>
>>>> On 4/24/2020 11:55 AM, Bill Zhou wrote:
>>>>> 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:
>>>>> - Registering aging event based on verbose level, when set verbose > 0,
>>>>>   will register this event, otherwise, remove this event. In this event
>>>>>   only dump one line of log to user there is one aging event coming.
>>>>> - Add new command to list all aged flows, meanwhile, we can set
>>>> parameter
>>>>>   to destroy it.
>>>>
>>>> Can you please document new feature and command?
>>>>
>>>> Instead of overloading the 'verbose', what do you think having
>>>> explicit command to register aging events? ("flow aged register
>>>> <port_id>"?) I think many of the verbose usage won't really interest in
>> the flow aging.
>>>>
>>>
>>> Yes, some of the verbose usage indeed not interest in the flow aging.
>>> But If we use register or unregister event to one port, sometime, it
>>> will repeat many times for every ports.
>>> What do you think if we register this event all the time, and
>>> introduce new global var from command to control the event export to
>> application ?
>>>    for example: set aged_flow_event_print_en [0 | 1]
>>
>> I am not also sure about registering this event always, you can register for all
>> ports with same command, as we do in many commands:
>> "flow aged register <port_id>|all"
>>
>> When argument is "all" it registers for all ports, when it is a specific port_id,
>> registers for that port id.
>> And it is good to have 'unregister' command too.
> 
> As I can see in testpmd default, it all the time registers to all events..
> See register_eth_event_callback.

You are right.

> 
> I suggest to catch the age event in callback and print it only when set aged_flow_event_print_en is on.

+1

> 
> 
> Matan
> 
> 
>>
>>>
>>>>>
>>>>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
>>>>
>>>> <...>
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-04-30 15:53 ` [dpdk-dev] [PATCH v2] " Bill Zhou
@ 2020-04-30 22:43   ` Ferruh Yigit
  2020-05-01  6:51     ` Matan Azrad
  2020-05-02 14:00   ` [dpdk-dev] [PATCH v3] " Bill Zhou
  1 sibling, 1 reply; 27+ messages in thread
From: Ferruh Yigit @ 2020-04-30 22:43 UTC (permalink / raw)
  To: Bill Zhou, wenzhuo.lu, jingjing.wu, bernard.iremonger, orika,
	john.mcnamara, marko.kovacevic, matan
  Cc: dev

On 4/30/2020 4:53 PM, Bill Zhou wrote:
> 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:
> - Registering aging event when the testpmd application start, add new
>   command to control if the event expose to the applications. If it's be
>   set, when new flow be checked age out, there will be one-line output log.
> - Add new command to list all aged flows, meanwhile, we can set parameter
>   to destroy it.
> 
> Signed-off-by: Bill Zhou <dongz@mellanox.com>
> ---
> v2: Update the way of registering aging event, add new command to control
> if the event need be print or not. Update the output of the delete aged
> flow command format.

<...>

> @@ -19387,6 +19394,44 @@ cmdline_parse_inst_t cmd_showport_macs = {
>  	},
>  };
>  
> +/* Enable/Disable flow aging event log */
> +struct cmd_set_aged_flow_event_log_result {
> +	cmdline_fixed_string_t set;
> +	cmdline_fixed_string_t keyword;
> +	cmdline_fixed_string_t enable;
> +};
> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_set =
> +	TOKEN_STRING_INITIALIZER(struct cmd_set_aged_flow_event_log_result,
> +		set, "set");
> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_keyword =
> +	TOKEN_STRING_INITIALIZER(struct cmd_set_aged_flow_event_log_result,
> +		keyword, "aged_flow_event_log");
> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_enable =
> +	TOKEN_STRING_INITIALIZER(struct cmd_set_aged_flow_event_log_result,
> +		enable, "on#off");
> +
> +static void
> +cmd_set_aged_flow_event_log_parsed(void *parsed_result,
> +				__rte_unused struct cmdline *cl,
> +				__rte_unused void *data)
> +{
> +	struct cmd_set_aged_flow_event_log_result *res = parsed_result;
> +	int enable = (strcmp(res->enable, "on") == 0) ? 1 : 0;
> +	update_aging_event_log_status(enable);
> +}
> +
> +cmdline_parse_inst_t cmd_set_aged_flow_event_log = {
> +	.f = cmd_set_aged_flow_event_log_parsed,
> +	.data = NULL,
> +	.help_str = "set aged_flow_event_log on|off",

What do you think "set aged_flow_verbose on|off" to be more similar to existing
verbose command?

<...>

> --- a/app/test-pmd/testpmd.c
> +++ b/app/test-pmd/testpmd.c
> @@ -3068,6 +3068,21 @@ rmv_port_callback(void *arg)
>  		start_packet_forwarding(0);
>  }
>  
> +static int aged_flow_event_enable;

Other global config values are at the beginning of the file, with a comment on
them, can you do same with variable?

> +void update_aging_event_log_status(int enable)
> +{
> +	aged_flow_event_enable = enable;
> +}
> +
> +int aging_event_output(uint16_t portid)

This can be a 'static' function.

> +{
> +	if (aged_flow_event_enable) {
> +		printf("port %u RTE_ETH_EVENT_FLOW_AGED triggered\n", portid);
> +		fflush(stdout);
> +	}
> +	return 0;
> +}
> +
>  /* This function is used by the interrupt thread */
>  static int
>  eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
> @@ -3098,6 +3113,8 @@ eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
>  				rmv_port_callback, (void *)(intptr_t)port_id))
>  			fprintf(stderr, "Could not set up deferred device removal\n");
>  		break;
> +	case RTE_ETH_EVENT_FLOW_AGED:
> +		aging_event_output(port_id);

Can't this provide more information than port_id, like flow id etc, what
event_process function provides? can we print it too?
And what is the intended usage in real application, when flow aged event
occurred, should application destroy the flow? So it should know the flow, right?

<...>

> --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> @@ -1062,6 +1062,21 @@ Where:
>  
>     Check the NIC Datasheet for hardware limits.
>  
> +aged flow event log set
> +~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Currently, the flow aging event is registered when the testpmd application start, if new flow be checked
> +age out, there will be one output log for it. But some applications do not interest this event, use this
> +command can set if the event expose to the applications::
> +
> +   testpmd> set aged_flow_event_log (on|off)
> +
> +For examine::
> +
> +   testpmd> set aged_flow_event_log on
> +   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
> +   testpmd> set aged_flow_event_log off
> +

This can be moved below the "set verbose" command since they are in similar group.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-04-30 22:43   ` Ferruh Yigit
@ 2020-05-01  6:51     ` Matan Azrad
  2020-05-01  9:27       ` Ferruh Yigit
  0 siblings, 1 reply; 27+ messages in thread
From: Matan Azrad @ 2020-05-01  6:51 UTC (permalink / raw)
  To: Ferruh Yigit, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev

Hi Ferruh

From: Ferruh Yigit
> On 4/30/2020 4:53 PM, Bill Zhou wrote:
> > 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:
> > - Registering aging event when the testpmd application start, add new
> >   command to control if the event expose to the applications. If it's be
> >   set, when new flow be checked age out, there will be one-line output log.
> > - Add new command to list all aged flows, meanwhile, we can set
> parameter
> >   to destroy it.
> >
> > Signed-off-by: Bill Zhou <dongz@mellanox.com>
> > ---
> > v2: Update the way of registering aging event, add new command to
> > control if the event need be print or not. Update the output of the
> > delete aged flow command format.
> 
> <...>
> 
> > @@ -19387,6 +19394,44 @@ cmdline_parse_inst_t cmd_showport_macs =
> {
> >  	},
> >  };
> >
> > +/* Enable/Disable flow aging event log */ struct
> > +cmd_set_aged_flow_event_log_result {
> > +	cmdline_fixed_string_t set;
> > +	cmdline_fixed_string_t keyword;
> > +	cmdline_fixed_string_t enable;
> > +};
> > +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_set =
> > +	TOKEN_STRING_INITIALIZER(struct
> cmd_set_aged_flow_event_log_result,
> > +		set, "set");
> > +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_keyword
> =
> > +	TOKEN_STRING_INITIALIZER(struct
> cmd_set_aged_flow_event_log_result,
> > +		keyword, "aged_flow_event_log");
> > +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_enable =
> > +	TOKEN_STRING_INITIALIZER(struct
> cmd_set_aged_flow_event_log_result,
> > +		enable, "on#off");
> > +
> > +static void
> > +cmd_set_aged_flow_event_log_parsed(void *parsed_result,
> > +				__rte_unused struct cmdline *cl,
> > +				__rte_unused void *data)
> > +{
> > +	struct cmd_set_aged_flow_event_log_result *res = parsed_result;
> > +	int enable = (strcmp(res->enable, "on") == 0) ? 1 : 0;
> > +	update_aging_event_log_status(enable);
> > +}
> > +
> > +cmdline_parse_inst_t cmd_set_aged_flow_event_log = {
> > +	.f = cmd_set_aged_flow_event_log_parsed,
> > +	.data = NULL,
> > +	.help_str = "set aged_flow_event_log on|off",
> 
> What do you think "set aged_flow_verbose on|off" to be more similar to
> existing verbose command?

Please see my comments below regard it.

> <...>
> 
> > --- a/app/test-pmd/testpmd.c
> > +++ b/app/test-pmd/testpmd.c
> > @@ -3068,6 +3068,21 @@ rmv_port_callback(void *arg)
> >  		start_packet_forwarding(0);
> >  }
> >
> > +static int aged_flow_event_enable;
> 
> Other global config values are at the beginning of the file, with a comment on
> them, can you do same with variable?

+1

> > +void update_aging_event_log_status(int enable) {
> > +	aged_flow_event_enable = enable;
> > +}
> > +
> > +int aging_event_output(uint16_t portid)
> 
> This can be a 'static' function.

+1

> > +{
> > +	if (aged_flow_event_enable) {
> > +		printf("port %u RTE_ETH_EVENT_FLOW_AGED triggered\n",
> portid);
> > +		fflush(stdout);
> > +	}
> > +	return 0;
> > +}
> > +
> >  /* This function is used by the interrupt thread */  static int
> > eth_event_callback(portid_t port_id, enum rte_eth_event_type type,
> > void *param, @@ -3098,6 +3113,8 @@ eth_event_callback(portid_t
> port_id, enum rte_eth_event_type type, void *param,
> >  				rmv_port_callback, (void
> *)(intptr_t)port_id))
> >  			fprintf(stderr, "Could not set up deferred device
> removal\n");
> >  		break;
> > +	case RTE_ETH_EVENT_FLOW_AGED:
> > +		aging_event_output(port_id);
> 
> Can't this provide more information than port_id, like flow id etc, what
> event_process function provides? can we print it too?
> And what is the intended usage in real application, when flow aged event
> occurred, should application destroy the flow? So it should know the flow,
> right?

Probably Yes Ferruh, I will add details, maybe it will be clearer:

As described well in rte_flow_get_aged_flows API description, there are 2 suggested options for the application:

1. To call rte_flow_get_aged_flows from the AGED event callback in order to get the aged flow contexts of the triggered port.
2. To call rte_flow_get_aged_flows in any time application wants.

It is probably depends in the way the application wants to synchronize flow APIs calls.

The application just gets the information of the aged-out flows context from the above API and can do any flow operation for it, and yes, the most expected case is to destroy the flows.

Bill added 2 testpmd commands:

1. To use rte_flow_get_aged_flows and to print a list of all the aged-out flows (with option to destroy them directly by the command).
2. A Boolean to indicate the application whether to notify the testpmd user about new aged-out flows(by print).

By this way, the testpmd user can use the 2 approaches with minimum testpmd flow management change.

So, the Boolean var is more like "trigger testpmd user notification", not like regular verbose options.

Matan




> 
> <...>
> 
> > --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> > +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> > @@ -1062,6 +1062,21 @@ Where:
> >
> >     Check the NIC Datasheet for hardware limits.
> >
> > +aged flow event log set
> > +~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Currently, the flow aging event is registered when the testpmd
> > +application start, if new flow be checked age out, there will be one
> > +output log for it. But some applications do not interest this event, use this
> command can set if the event expose to the applications::
> > +
> > +   testpmd> set aged_flow_event_log (on|off)
> > +
> > +For examine::
> > +
> > +   testpmd> set aged_flow_event_log on
> > +   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
> > +   testpmd> set aged_flow_event_log off
> > +
> 
> This can be moved below the "set verbose" command since they are in
> similar group.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-05-01  6:51     ` Matan Azrad
@ 2020-05-01  9:27       ` Ferruh Yigit
  2020-05-01 11:28         ` Matan Azrad
  0 siblings, 1 reply; 27+ messages in thread
From: Ferruh Yigit @ 2020-05-01  9:27 UTC (permalink / raw)
  To: Matan Azrad, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev

On 5/1/2020 7:51 AM, Matan Azrad wrote:
> Hi Ferruh
> 
> From: Ferruh Yigit
>> On 4/30/2020 4:53 PM, Bill Zhou wrote:
>>> 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:
>>> - Registering aging event when the testpmd application start, add new
>>>   command to control if the event expose to the applications. If it's be
>>>   set, when new flow be checked age out, there will be one-line output log.
>>> - Add new command to list all aged flows, meanwhile, we can set
>> parameter
>>>   to destroy it.
>>>
>>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
>>> ---
>>> v2: Update the way of registering aging event, add new command to
>>> control if the event need be print or not. Update the output of the
>>> delete aged flow command format.
>>
>> <...>
>>
>>> @@ -19387,6 +19394,44 @@ cmdline_parse_inst_t cmd_showport_macs =
>> {
>>>  	},
>>>  };
>>>
>>> +/* Enable/Disable flow aging event log */ struct
>>> +cmd_set_aged_flow_event_log_result {
>>> +	cmdline_fixed_string_t set;
>>> +	cmdline_fixed_string_t keyword;
>>> +	cmdline_fixed_string_t enable;
>>> +};
>>> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_set =
>>> +	TOKEN_STRING_INITIALIZER(struct
>> cmd_set_aged_flow_event_log_result,
>>> +		set, "set");
>>> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_keyword
>> =
>>> +	TOKEN_STRING_INITIALIZER(struct
>> cmd_set_aged_flow_event_log_result,
>>> +		keyword, "aged_flow_event_log");
>>> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_enable =
>>> +	TOKEN_STRING_INITIALIZER(struct
>> cmd_set_aged_flow_event_log_result,
>>> +		enable, "on#off");
>>> +
>>> +static void
>>> +cmd_set_aged_flow_event_log_parsed(void *parsed_result,
>>> +				__rte_unused struct cmdline *cl,
>>> +				__rte_unused void *data)
>>> +{
>>> +	struct cmd_set_aged_flow_event_log_result *res = parsed_result;
>>> +	int enable = (strcmp(res->enable, "on") == 0) ? 1 : 0;
>>> +	update_aging_event_log_status(enable);
>>> +}
>>> +
>>> +cmdline_parse_inst_t cmd_set_aged_flow_event_log = {
>>> +	.f = cmd_set_aged_flow_event_log_parsed,
>>> +	.data = NULL,
>>> +	.help_str = "set aged_flow_event_log on|off",
>>
>> What do you think "set aged_flow_verbose on|off" to be more similar to
>> existing verbose command?
> 
> Please see my comments below regard it.
> 
>> <...>
>>
>>> --- a/app/test-pmd/testpmd.c
>>> +++ b/app/test-pmd/testpmd.c
>>> @@ -3068,6 +3068,21 @@ rmv_port_callback(void *arg)
>>>  		start_packet_forwarding(0);
>>>  }
>>>
>>> +static int aged_flow_event_enable;
>>
>> Other global config values are at the beginning of the file, with a comment on
>> them, can you do same with variable?
> 
> +1
> 
>>> +void update_aging_event_log_status(int enable) {
>>> +	aged_flow_event_enable = enable;
>>> +}
>>> +
>>> +int aging_event_output(uint16_t portid)
>>
>> This can be a 'static' function.
> 
> +1
> 
>>> +{
>>> +	if (aged_flow_event_enable) {
>>> +		printf("port %u RTE_ETH_EVENT_FLOW_AGED triggered\n",
>> portid);
>>> +		fflush(stdout);
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>>  /* This function is used by the interrupt thread */  static int
>>> eth_event_callback(portid_t port_id, enum rte_eth_event_type type,
>>> void *param, @@ -3098,6 +3113,8 @@ eth_event_callback(portid_t
>> port_id, enum rte_eth_event_type type, void *param,
>>>  				rmv_port_callback, (void
>> *)(intptr_t)port_id))
>>>  			fprintf(stderr, "Could not set up deferred device
>> removal\n");
>>>  		break;
>>> +	case RTE_ETH_EVENT_FLOW_AGED:
>>> +		aging_event_output(port_id);
>>
>> Can't this provide more information than port_id, like flow id etc, what
>> event_process function provides? can we print it too?
>> And what is the intended usage in real application, when flow aged event
>> occurred, should application destroy the flow? So it should know the flow,
>> right?
> 
> Probably Yes Ferruh, I will add details, maybe it will be clearer:
> 
> As described well in rte_flow_get_aged_flows API description, there are 2 suggested options for the application:
> 
> 1. To call rte_flow_get_aged_flows from the AGED event callback in order to get the aged flow contexts of the triggered port.
> 2. To call rte_flow_get_aged_flows in any time application wants.

I see, for the testpmd implementation what do you think getting the aged flow
list and print them in event callback, this can be good as sample of the
intended usage?

> 
> It is probably depends in the way the application wants to synchronize flow APIs calls.
> 
> The application just gets the information of the aged-out flows context from the above API and can do any flow operation for it, and yes, the most expected case is to destroy the flows.
> 
> Bill added 2 testpmd commands:
> 
> 1. To use rte_flow_get_aged_flows and to print a list of all the aged-out flows (with option to destroy them directly by the command).
> 2. A Boolean to indicate the application whether to notify the testpmd user about new aged-out flows(by print).
> 
> By this way, the testpmd user can use the 2 approaches with minimum testpmd flow management change.
> 
> So, the Boolean var is more like "trigger testpmd user notification", not like regular verbose options.

As you already know, event always registered and callback always called, this
command only defines to print a log in the callback or not, so I believe
'verbose' suits here,
main concern is there are many testpmd commands and it is hard to remember them
(usage is not intuitive, I had need to check source code to find a command many
times), trying to make them as consistent as possible to help usage.

But I think as see your concern, "set aged_flow_verbose on|off" command can be
confused as if changing "flow aged #" command verbosity level.

What do you think about a more generic "set event_verbose on|off" command, which
can control to logging for all event handlers, but right now only for aged events?

> 
> Matan
> 
> 
> 
> 
>>
>> <...>
>>
>>> --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
>>> +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
>>> @@ -1062,6 +1062,21 @@ Where:
>>>
>>>     Check the NIC Datasheet for hardware limits.
>>>
>>> +aged flow event log set
>>> +~~~~~~~~~~~~~~~~~~~~~~~
>>> +
>>> +Currently, the flow aging event is registered when the testpmd
>>> +application start, if new flow be checked age out, there will be one
>>> +output log for it. But some applications do not interest this event, use this
>> command can set if the event expose to the applications::
>>> +
>>> +   testpmd> set aged_flow_event_log (on|off)
>>> +
>>> +For examine::
>>> +
>>> +   testpmd> set aged_flow_event_log on
>>> +   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
>>> +   testpmd> set aged_flow_event_log off
>>> +
>>
>> This can be moved below the "set verbose" command since they are in
>> similar group.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-05-01  9:27       ` Ferruh Yigit
@ 2020-05-01 11:28         ` Matan Azrad
  2020-05-01 11:54           ` Ferruh Yigit
  0 siblings, 1 reply; 27+ messages in thread
From: Matan Azrad @ 2020-05-01 11:28 UTC (permalink / raw)
  To: Ferruh Yigit, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev


Hi Ferruh

From: Ferruh Yigit:
> On 5/1/2020 7:51 AM, Matan Azrad wrote:
> > Hi Ferruh
> >
> > From: Ferruh Yigit
> >> On 4/30/2020 4:53 PM, Bill Zhou wrote:
> >>> 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:
> >>> - Registering aging event when the testpmd application start, add new
> >>>   command to control if the event expose to the applications. If it's be
> >>>   set, when new flow be checked age out, there will be one-line output
> log.
> >>> - Add new command to list all aged flows, meanwhile, we can set
> >> parameter
> >>>   to destroy it.
> >>>
> >>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
> >>> ---
> >>> v2: Update the way of registering aging event, add new command to
> >>> control if the event need be print or not. Update the output of the
> >>> delete aged flow command format.
> >>
> >> <...>
> >>
> >>> @@ -19387,6 +19394,44 @@ cmdline_parse_inst_t
> cmd_showport_macs =
> >> {
> >>>  	},
> >>>  };
> >>>
> >>> +/* Enable/Disable flow aging event log */ struct
> >>> +cmd_set_aged_flow_event_log_result {
> >>> +	cmdline_fixed_string_t set;
> >>> +	cmdline_fixed_string_t keyword;
> >>> +	cmdline_fixed_string_t enable;
> >>> +};
> >>> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_set =
> >>> +	TOKEN_STRING_INITIALIZER(struct
> >> cmd_set_aged_flow_event_log_result,
> >>> +		set, "set");
> >>> +cmdline_parse_token_string_t
> cmd_set_aged_flow_event_log_keyword
> >> =
> >>> +	TOKEN_STRING_INITIALIZER(struct
> >> cmd_set_aged_flow_event_log_result,
> >>> +		keyword, "aged_flow_event_log");
> >>> +cmdline_parse_token_string_t
> cmd_set_aged_flow_event_log_enable =
> >>> +	TOKEN_STRING_INITIALIZER(struct
> >> cmd_set_aged_flow_event_log_result,
> >>> +		enable, "on#off");
> >>> +
> >>> +static void
> >>> +cmd_set_aged_flow_event_log_parsed(void *parsed_result,
> >>> +				__rte_unused struct cmdline *cl,
> >>> +				__rte_unused void *data)
> >>> +{
> >>> +	struct cmd_set_aged_flow_event_log_result *res = parsed_result;
> >>> +	int enable = (strcmp(res->enable, "on") == 0) ? 1 : 0;
> >>> +	update_aging_event_log_status(enable);
> >>> +}
> >>> +
> >>> +cmdline_parse_inst_t cmd_set_aged_flow_event_log = {
> >>> +	.f = cmd_set_aged_flow_event_log_parsed,
> >>> +	.data = NULL,
> >>> +	.help_str = "set aged_flow_event_log on|off",
> >>
> >> What do you think "set aged_flow_verbose on|off" to be more similar
> >> to existing verbose command?
> >
> > Please see my comments below regard it.
> >
> >> <...>
> >>
> >>> --- a/app/test-pmd/testpmd.c
> >>> +++ b/app/test-pmd/testpmd.c
> >>> @@ -3068,6 +3068,21 @@ rmv_port_callback(void *arg)
> >>>  		start_packet_forwarding(0);
> >>>  }
> >>>
> >>> +static int aged_flow_event_enable;
> >>
> >> Other global config values are at the beginning of the file, with a
> >> comment on them, can you do same with variable?
> >
> > +1
> >
> >>> +void update_aging_event_log_status(int enable) {
> >>> +	aged_flow_event_enable = enable;
> >>> +}
> >>> +
> >>> +int aging_event_output(uint16_t portid)
> >>
> >> This can be a 'static' function.
> >
> > +1
> >
> >>> +{
> >>> +	if (aged_flow_event_enable) {
> >>> +		printf("port %u RTE_ETH_EVENT_FLOW_AGED triggered\n",
> >> portid);
> >>> +		fflush(stdout);
> >>> +	}
> >>> +	return 0;
> >>> +}
> >>> +
> >>>  /* This function is used by the interrupt thread */  static int
> >>> eth_event_callback(portid_t port_id, enum rte_eth_event_type type,
> >>> void *param, @@ -3098,6 +3113,8 @@ eth_event_callback(portid_t
> >> port_id, enum rte_eth_event_type type, void *param,
> >>>  				rmv_port_callback, (void
> >> *)(intptr_t)port_id))
> >>>  			fprintf(stderr, "Could not set up deferred device
> >> removal\n");
> >>>  		break;
> >>> +	case RTE_ETH_EVENT_FLOW_AGED:
> >>> +		aging_event_output(port_id);
> >>
> >> Can't this provide more information than port_id, like flow id etc,
> >> what event_process function provides? can we print it too?
> >> And what is the intended usage in real application, when flow aged
> >> event occurred, should application destroy the flow? So it should
> >> know the flow, right?
> >
> > Probably Yes Ferruh, I will add details, maybe it will be clearer:
> >
> > As described well in rte_flow_get_aged_flows API description, there are 2
> suggested options for the application:
> >
> > 1. To call rte_flow_get_aged_flows from the AGED event callback in order
> to get the aged flow contexts of the triggered port.
> > 2. To call rte_flow_get_aged_flows in any time application wants.
> 
> I see, for the testpmd implementation what do you think getting the aged
> flow list and print them in event callback, this can be good as sample of the
> intended usage?

Yes, we thought on it and I understand you,
The issue with it is that we need to synchronize all the flow management in Testpmd and to protect any flow operation with a lock.
Because the callback is probably coming from the host thread while other flow operations from different Testpmd thread(command line thread).

It will do the patch very intrusive.

The current approach(like the second suggestion) moves the synchronization to the testpmd user to access flows only from the command line thread while hinting to the user when a port holds some aged-out flows.
I think it is better to stay the implementation simple.

> >
> > It is probably depends in the way the application wants to synchronize flow
> APIs calls.
> >
> > The application just gets the information of the aged-out flows context
> from the above API and can do any flow operation for it, and yes, the most
> expected case is to destroy the flows.
> >
> > Bill added 2 testpmd commands:
> >
> > 1. To use rte_flow_get_aged_flows and to print a list of all the aged-out
> flows (with option to destroy them directly by the command).
> > 2. A Boolean to indicate the application whether to notify the testpmd user
> about new aged-out flows(by print).
> >
> > By this way, the testpmd user can use the 2 approaches with minimum
> testpmd flow management change.
> >
> > So, the Boolean var is more like "trigger testpmd user notification", not like
> regular verbose options.
> 
> As you already know, event always registered and callback always called, this
> command only defines to print a log in the callback or not, so I believe
> 'verbose' suits here, main concern is there are many testpmd commands and
> it is hard to remember them (usage is not intuitive, I had need to check
> source code to find a command many times), trying to make them as
> consistent as possible to help usage.
> 
> But I think as see your concern, "set aged_flow_verbose on|off" command
> can be confused as if changing "flow aged #" command verbosity level.
> 
> What do you think about a more generic "set event_verbose on|off"
> command, which can control to logging for all event handlers, but right now
> only for aged events?

Looks good to me, but maybe instead of on | off it is better to use bitmap in order to indicate the event?

 
> >
> > Matan
> >
> >
> >
> >
> >>
> >> <...>
> >>
> >>> --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> >>> +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> >>> @@ -1062,6 +1062,21 @@ Where:
> >>>
> >>>     Check the NIC Datasheet for hardware limits.
> >>>
> >>> +aged flow event log set
> >>> +~~~~~~~~~~~~~~~~~~~~~~~
> >>> +
> >>> +Currently, the flow aging event is registered when the testpmd
> >>> +application start, if new flow be checked age out, there will be
> >>> +one output log for it. But some applications do not interest this
> >>> +event, use this
> >> command can set if the event expose to the applications::
> >>> +
> >>> +   testpmd> set aged_flow_event_log (on|off)
> >>> +
> >>> +For examine::
> >>> +
> >>> +   testpmd> set aged_flow_event_log on
> >>> +   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
> >>> +   testpmd> set aged_flow_event_log off
> >>> +
> >>
> >> This can be moved below the "set verbose" command since they are in
> >> similar group.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-05-01 11:28         ` Matan Azrad
@ 2020-05-01 11:54           ` Ferruh Yigit
  2020-05-01 12:45             ` Matan Azrad
  0 siblings, 1 reply; 27+ messages in thread
From: Ferruh Yigit @ 2020-05-01 11:54 UTC (permalink / raw)
  To: Matan Azrad, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev

On 5/1/2020 12:28 PM, Matan Azrad wrote:
> 
> Hi Ferruh
> 
> From: Ferruh Yigit:
>> On 5/1/2020 7:51 AM, Matan Azrad wrote:
>>> Hi Ferruh
>>>
>>> From: Ferruh Yigit
>>>> On 4/30/2020 4:53 PM, Bill Zhou wrote:
>>>>> 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:
>>>>> - Registering aging event when the testpmd application start, add new
>>>>>   command to control if the event expose to the applications. If it's be
>>>>>   set, when new flow be checked age out, there will be one-line output
>> log.
>>>>> - Add new command to list all aged flows, meanwhile, we can set
>>>> parameter
>>>>>   to destroy it.
>>>>>
>>>>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
>>>>> ---
>>>>> v2: Update the way of registering aging event, add new command to
>>>>> control if the event need be print or not. Update the output of the
>>>>> delete aged flow command format.
>>>>
>>>> <...>
>>>>
>>>>> @@ -19387,6 +19394,44 @@ cmdline_parse_inst_t
>> cmd_showport_macs =
>>>> {
>>>>>  	},
>>>>>  };
>>>>>
>>>>> +/* Enable/Disable flow aging event log */ struct
>>>>> +cmd_set_aged_flow_event_log_result {
>>>>> +	cmdline_fixed_string_t set;
>>>>> +	cmdline_fixed_string_t keyword;
>>>>> +	cmdline_fixed_string_t enable;
>>>>> +};
>>>>> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_set =
>>>>> +	TOKEN_STRING_INITIALIZER(struct
>>>> cmd_set_aged_flow_event_log_result,
>>>>> +		set, "set");
>>>>> +cmdline_parse_token_string_t
>> cmd_set_aged_flow_event_log_keyword
>>>> =
>>>>> +	TOKEN_STRING_INITIALIZER(struct
>>>> cmd_set_aged_flow_event_log_result,
>>>>> +		keyword, "aged_flow_event_log");
>>>>> +cmdline_parse_token_string_t
>> cmd_set_aged_flow_event_log_enable =
>>>>> +	TOKEN_STRING_INITIALIZER(struct
>>>> cmd_set_aged_flow_event_log_result,
>>>>> +		enable, "on#off");
>>>>> +
>>>>> +static void
>>>>> +cmd_set_aged_flow_event_log_parsed(void *parsed_result,
>>>>> +				__rte_unused struct cmdline *cl,
>>>>> +				__rte_unused void *data)
>>>>> +{
>>>>> +	struct cmd_set_aged_flow_event_log_result *res = parsed_result;
>>>>> +	int enable = (strcmp(res->enable, "on") == 0) ? 1 : 0;
>>>>> +	update_aging_event_log_status(enable);
>>>>> +}
>>>>> +
>>>>> +cmdline_parse_inst_t cmd_set_aged_flow_event_log = {
>>>>> +	.f = cmd_set_aged_flow_event_log_parsed,
>>>>> +	.data = NULL,
>>>>> +	.help_str = "set aged_flow_event_log on|off",
>>>>
>>>> What do you think "set aged_flow_verbose on|off" to be more similar
>>>> to existing verbose command?
>>>
>>> Please see my comments below regard it.
>>>
>>>> <...>
>>>>
>>>>> --- a/app/test-pmd/testpmd.c
>>>>> +++ b/app/test-pmd/testpmd.c
>>>>> @@ -3068,6 +3068,21 @@ rmv_port_callback(void *arg)
>>>>>  		start_packet_forwarding(0);
>>>>>  }
>>>>>
>>>>> +static int aged_flow_event_enable;
>>>>
>>>> Other global config values are at the beginning of the file, with a
>>>> comment on them, can you do same with variable?
>>>
>>> +1
>>>
>>>>> +void update_aging_event_log_status(int enable) {
>>>>> +	aged_flow_event_enable = enable;
>>>>> +}
>>>>> +
>>>>> +int aging_event_output(uint16_t portid)
>>>>
>>>> This can be a 'static' function.
>>>
>>> +1
>>>
>>>>> +{
>>>>> +	if (aged_flow_event_enable) {
>>>>> +		printf("port %u RTE_ETH_EVENT_FLOW_AGED triggered\n",
>>>> portid);
>>>>> +		fflush(stdout);
>>>>> +	}
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>>  /* This function is used by the interrupt thread */  static int
>>>>> eth_event_callback(portid_t port_id, enum rte_eth_event_type type,
>>>>> void *param, @@ -3098,6 +3113,8 @@ eth_event_callback(portid_t
>>>> port_id, enum rte_eth_event_type type, void *param,
>>>>>  				rmv_port_callback, (void
>>>> *)(intptr_t)port_id))
>>>>>  			fprintf(stderr, "Could not set up deferred device
>>>> removal\n");
>>>>>  		break;
>>>>> +	case RTE_ETH_EVENT_FLOW_AGED:
>>>>> +		aging_event_output(port_id);
>>>>
>>>> Can't this provide more information than port_id, like flow id etc,
>>>> what event_process function provides? can we print it too?
>>>> And what is the intended usage in real application, when flow aged
>>>> event occurred, should application destroy the flow? So it should
>>>> know the flow, right?
>>>
>>> Probably Yes Ferruh, I will add details, maybe it will be clearer:
>>>
>>> As described well in rte_flow_get_aged_flows API description, there are 2
>> suggested options for the application:
>>>
>>> 1. To call rte_flow_get_aged_flows from the AGED event callback in order
>> to get the aged flow contexts of the triggered port.
>>> 2. To call rte_flow_get_aged_flows in any time application wants.
>>
>> I see, for the testpmd implementation what do you think getting the aged
>> flow list and print them in event callback, this can be good as sample of the
>> intended usage?
> 
> Yes, we thought on it and I understand you,
> The issue with it is that we need to synchronize all the flow management in Testpmd and to protect any flow operation with a lock.
> Because the callback is probably coming from the host thread while other flow operations from different Testpmd thread(command line thread).
> 
> It will do the patch very intrusive.
> 
> The current approach(like the second suggestion) moves the synchronization to the testpmd user to access flows only from the command line thread while hinting to the user when a port holds some aged-out flows.
> I think it is better to stay the implementation simple.

OK

> 
>>>
>>> It is probably depends in the way the application wants to synchronize flow
>> APIs calls.
>>>
>>> The application just gets the information of the aged-out flows context
>> from the above API and can do any flow operation for it, and yes, the most
>> expected case is to destroy the flows.
>>>
>>> Bill added 2 testpmd commands:
>>>
>>> 1. To use rte_flow_get_aged_flows and to print a list of all the aged-out
>> flows (with option to destroy them directly by the command).
>>> 2. A Boolean to indicate the application whether to notify the testpmd user
>> about new aged-out flows(by print).
>>>
>>> By this way, the testpmd user can use the 2 approaches with minimum
>> testpmd flow management change.
>>>
>>> So, the Boolean var is more like "trigger testpmd user notification", not like
>> regular verbose options.
>>
>> As you already know, event always registered and callback always called, this
>> command only defines to print a log in the callback or not, so I believe
>> 'verbose' suits here, main concern is there are many testpmd commands and
>> it is hard to remember them (usage is not intuitive, I had need to check
>> source code to find a command many times), trying to make them as
>> consistent as possible to help usage.
>>
>> But I think as see your concern, "set aged_flow_verbose on|off" command
>> can be confused as if changing "flow aged #" command verbosity level.
>>
>> What do you think about a more generic "set event_verbose on|off"
>> command, which can control to logging for all event handlers, but right now
>> only for aged events?
> 
> Looks good to me, but maybe instead of on | off it is better to use bitmap in order to indicate the event?

No objection but not sure that level fine grain needed now, or later, why not
add the basic on/off now and add the bitmap when we need to control for each event.

> 
>  
>>>
>>> Matan
>>>
>>>
>>>
>>>
>>>>
>>>> <...>
>>>>
>>>>> --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
>>>>> +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
>>>>> @@ -1062,6 +1062,21 @@ Where:
>>>>>
>>>>>     Check the NIC Datasheet for hardware limits.
>>>>>
>>>>> +aged flow event log set
>>>>> +~~~~~~~~~~~~~~~~~~~~~~~
>>>>> +
>>>>> +Currently, the flow aging event is registered when the testpmd
>>>>> +application start, if new flow be checked age out, there will be
>>>>> +one output log for it. But some applications do not interest this
>>>>> +event, use this
>>>> command can set if the event expose to the applications::
>>>>> +
>>>>> +   testpmd> set aged_flow_event_log (on|off)
>>>>> +
>>>>> +For examine::
>>>>> +
>>>>> +   testpmd> set aged_flow_event_log on
>>>>> +   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
>>>>> +   testpmd> set aged_flow_event_log off
>>>>> +
>>>>
>>>> This can be moved below the "set verbose" command since they are in
>>>> similar group.
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-05-01 11:54           ` Ferruh Yigit
@ 2020-05-01 12:45             ` Matan Azrad
  2020-05-01 13:38               ` Ferruh Yigit
  0 siblings, 1 reply; 27+ messages in thread
From: Matan Azrad @ 2020-05-01 12:45 UTC (permalink / raw)
  To: Ferruh Yigit, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev



From: Ferruh Yigit:
> On 5/1/2020 12:28 PM, Matan Azrad wrote:
> >
> > Hi Ferruh
> >
> > From: Ferruh Yigit:
> >> On 5/1/2020 7:51 AM, Matan Azrad wrote:
> >>> Hi Ferruh
> >>>
> >>> From: Ferruh Yigit
> >>>> On 4/30/2020 4:53 PM, Bill Zhou wrote:
> >>>>> 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:
> >>>>> - Registering aging event when the testpmd application start, add new
> >>>>>   command to control if the event expose to the applications. If it's be
> >>>>>   set, when new flow be checked age out, there will be one-line
> >>>>> output
> >> log.
> >>>>> - Add new command to list all aged flows, meanwhile, we can set
> >>>> parameter
> >>>>>   to destroy it.
> >>>>>
> >>>>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
> >>>>> ---
> >>>>> v2: Update the way of registering aging event, add new command to
> >>>>> control if the event need be print or not. Update the output of
> >>>>> the delete aged flow command format.
> >>>>
> >>>> <...>
> >>>>
> >>>>> @@ -19387,6 +19394,44 @@ cmdline_parse_inst_t
> >> cmd_showport_macs =
> >>>> {
> >>>>>  	},
> >>>>>  };
> >>>>>
> >>>>> +/* Enable/Disable flow aging event log */ struct
> >>>>> +cmd_set_aged_flow_event_log_result {
> >>>>> +	cmdline_fixed_string_t set;
> >>>>> +	cmdline_fixed_string_t keyword;
> >>>>> +	cmdline_fixed_string_t enable;
> >>>>> +};
> >>>>> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_set
> =
> >>>>> +	TOKEN_STRING_INITIALIZER(struct
> >>>> cmd_set_aged_flow_event_log_result,
> >>>>> +		set, "set");
> >>>>> +cmdline_parse_token_string_t
> >> cmd_set_aged_flow_event_log_keyword
> >>>> =
> >>>>> +	TOKEN_STRING_INITIALIZER(struct
> >>>> cmd_set_aged_flow_event_log_result,
> >>>>> +		keyword, "aged_flow_event_log");
> cmdline_parse_token_string_t
> >> cmd_set_aged_flow_event_log_enable =
> >>>>> +	TOKEN_STRING_INITIALIZER(struct
> >>>> cmd_set_aged_flow_event_log_result,
> >>>>> +		enable, "on#off");
> >>>>> +
> >>>>> +static void
> >>>>> +cmd_set_aged_flow_event_log_parsed(void *parsed_result,
> >>>>> +				__rte_unused struct cmdline *cl,
> >>>>> +				__rte_unused void *data)
> >>>>> +{
> >>>>> +	struct cmd_set_aged_flow_event_log_result *res =
> parsed_result;
> >>>>> +	int enable = (strcmp(res->enable, "on") == 0) ? 1 : 0;
> >>>>> +	update_aging_event_log_status(enable);
> >>>>> +}
> >>>>> +
> >>>>> +cmdline_parse_inst_t cmd_set_aged_flow_event_log = {
> >>>>> +	.f = cmd_set_aged_flow_event_log_parsed,
> >>>>> +	.data = NULL,
> >>>>> +	.help_str = "set aged_flow_event_log on|off",
> >>>>
> >>>> What do you think "set aged_flow_verbose on|off" to be more similar
> >>>> to existing verbose command?
> >>>
> >>> Please see my comments below regard it.
> >>>
> >>>> <...>
> >>>>
> >>>>> --- a/app/test-pmd/testpmd.c
> >>>>> +++ b/app/test-pmd/testpmd.c
> >>>>> @@ -3068,6 +3068,21 @@ rmv_port_callback(void *arg)
> >>>>>  		start_packet_forwarding(0);
> >>>>>  }
> >>>>>
> >>>>> +static int aged_flow_event_enable;
> >>>>
> >>>> Other global config values are at the beginning of the file, with a
> >>>> comment on them, can you do same with variable?
> >>>
> >>> +1
> >>>
> >>>>> +void update_aging_event_log_status(int enable) {
> >>>>> +	aged_flow_event_enable = enable; }
> >>>>> +
> >>>>> +int aging_event_output(uint16_t portid)
> >>>>
> >>>> This can be a 'static' function.
> >>>
> >>> +1
> >>>
> >>>>> +{
> >>>>> +	if (aged_flow_event_enable) {
> >>>>> +		printf("port %u RTE_ETH_EVENT_FLOW_AGED
> triggered\n",
> >>>> portid);
> >>>>> +		fflush(stdout);
> >>>>> +	}
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>>  /* This function is used by the interrupt thread */  static int
> >>>>> eth_event_callback(portid_t port_id, enum rte_eth_event_type
> type,
> >>>>> void *param, @@ -3098,6 +3113,8 @@ eth_event_callback(portid_t
> >>>> port_id, enum rte_eth_event_type type, void *param,
> >>>>>  				rmv_port_callback, (void
> >>>> *)(intptr_t)port_id))
> >>>>>  			fprintf(stderr, "Could not set up deferred device
> >>>> removal\n");
> >>>>>  		break;
> >>>>> +	case RTE_ETH_EVENT_FLOW_AGED:
> >>>>> +		aging_event_output(port_id);
> >>>>
> >>>> Can't this provide more information than port_id, like flow id etc,
> >>>> what event_process function provides? can we print it too?
> >>>> And what is the intended usage in real application, when flow aged
> >>>> event occurred, should application destroy the flow? So it should
> >>>> know the flow, right?
> >>>
> >>> Probably Yes Ferruh, I will add details, maybe it will be clearer:
> >>>
> >>> As described well in rte_flow_get_aged_flows API description, there
> >>> are 2
> >> suggested options for the application:
> >>>
> >>> 1. To call rte_flow_get_aged_flows from the AGED event callback in
> >>> order
> >> to get the aged flow contexts of the triggered port.
> >>> 2. To call rte_flow_get_aged_flows in any time application wants.
> >>
> >> I see, for the testpmd implementation what do you think getting the
> >> aged flow list and print them in event callback, this can be good as
> >> sample of the intended usage?
> >
> > Yes, we thought on it and I understand you, The issue with it is that
> > we need to synchronize all the flow management in Testpmd and to
> protect any flow operation with a lock.
> > Because the callback is probably coming from the host thread while other
> flow operations from different Testpmd thread(command line thread).
> >
> > It will do the patch very intrusive.
> >
> > The current approach(like the second suggestion) moves the
> synchronization to the testpmd user to access flows only from the command
> line thread while hinting to the user when a port holds some aged-out flows.
> > I think it is better to stay the implementation simple.
> 
> OK
> 
> >
> >>>
> >>> It is probably depends in the way the application wants to
> >>> synchronize flow
> >> APIs calls.
> >>>
> >>> The application just gets the information of the aged-out flows
> >>> context
> >> from the above API and can do any flow operation for it, and yes, the
> >> most expected case is to destroy the flows.
> >>>
> >>> Bill added 2 testpmd commands:
> >>>
> >>> 1. To use rte_flow_get_aged_flows and to print a list of all the
> >>> aged-out
> >> flows (with option to destroy them directly by the command).
> >>> 2. A Boolean to indicate the application whether to notify the
> >>> testpmd user
> >> about new aged-out flows(by print).
> >>>
> >>> By this way, the testpmd user can use the 2 approaches with minimum
> >> testpmd flow management change.
> >>>
> >>> So, the Boolean var is more like "trigger testpmd user
> >>> notification", not like
> >> regular verbose options.
> >>
> >> As you already know, event always registered and callback always
> >> called, this command only defines to print a log in the callback or
> >> not, so I believe 'verbose' suits here, main concern is there are
> >> many testpmd commands and it is hard to remember them (usage is not
> >> intuitive, I had need to check source code to find a command many
> >> times), trying to make them as consistent as possible to help usage.
> >>
> >> But I think as see your concern, "set aged_flow_verbose on|off"
> >> command can be confused as if changing "flow aged #" command
> verbosity level.
> >>
> >> What do you think about a more generic "set event_verbose on|off"
> >> command, which can control to logging for all event handlers, but
> >> right now only for aged events?
> >
> > Looks good to me, but maybe instead of on | off it is better to use bitmap
> in order to indicate the event?
> 
> No objection but not sure that level fine grain needed now, or later, why not
> add the basic on/off now and add the bitmap when we need to control for
> each event.

It will be good to the user to reduce logs of non-interested events (maybe even more often) which makes his log bigger.


> 
> >
> >
> >>>
> >>> Matan
> >>>
> >>>
> >>>
> >>>
> >>>>
> >>>> <...>
> >>>>
> >>>>> --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> >>>>> +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> >>>>> @@ -1062,6 +1062,21 @@ Where:
> >>>>>
> >>>>>     Check the NIC Datasheet for hardware limits.
> >>>>>
> >>>>> +aged flow event log set
> >>>>> +~~~~~~~~~~~~~~~~~~~~~~~
> >>>>> +
> >>>>> +Currently, the flow aging event is registered when the testpmd
> >>>>> +application start, if new flow be checked age out, there will be
> >>>>> +one output log for it. But some applications do not interest this
> >>>>> +event, use this
> >>>> command can set if the event expose to the applications::
> >>>>> +
> >>>>> +   testpmd> set aged_flow_event_log (on|off)
> >>>>> +
> >>>>> +For examine::
> >>>>> +
> >>>>> +   testpmd> set aged_flow_event_log on
> >>>>> +   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
> >>>>> +   testpmd> set aged_flow_event_log off
> >>>>> +
> >>>>
> >>>> This can be moved below the "set verbose" command since they are in
> >>>> similar group.
> >


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-05-01 12:45             ` Matan Azrad
@ 2020-05-01 13:38               ` Ferruh Yigit
  2020-05-01 15:14                 ` Matan Azrad
  0 siblings, 1 reply; 27+ messages in thread
From: Ferruh Yigit @ 2020-05-01 13:38 UTC (permalink / raw)
  To: Matan Azrad, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev

On 5/1/2020 1:45 PM, Matan Azrad wrote:
> 
> 
> From: Ferruh Yigit:
>> On 5/1/2020 12:28 PM, Matan Azrad wrote:
>>>
>>> Hi Ferruh
>>>
>>> From: Ferruh Yigit:
>>>> On 5/1/2020 7:51 AM, Matan Azrad wrote:
>>>>> Hi Ferruh
>>>>>
>>>>> From: Ferruh Yigit
>>>>>> On 4/30/2020 4:53 PM, Bill Zhou wrote:
>>>>>>> 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:
>>>>>>> - Registering aging event when the testpmd application start, add new
>>>>>>>   command to control if the event expose to the applications. If it's be
>>>>>>>   set, when new flow be checked age out, there will be one-line
>>>>>>> output
>>>> log.
>>>>>>> - Add new command to list all aged flows, meanwhile, we can set
>>>>>> parameter
>>>>>>>   to destroy it.
>>>>>>>
>>>>>>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
>>>>>>> ---
>>>>>>> v2: Update the way of registering aging event, add new command to
>>>>>>> control if the event need be print or not. Update the output of
>>>>>>> the delete aged flow command format.
>>>>>>
>>>>>> <...>
>>>>>>
>>>>>>> @@ -19387,6 +19394,44 @@ cmdline_parse_inst_t
>>>> cmd_showport_macs =
>>>>>> {
>>>>>>>  	},
>>>>>>>  };
>>>>>>>
>>>>>>> +/* Enable/Disable flow aging event log */ struct
>>>>>>> +cmd_set_aged_flow_event_log_result {
>>>>>>> +	cmdline_fixed_string_t set;
>>>>>>> +	cmdline_fixed_string_t keyword;
>>>>>>> +	cmdline_fixed_string_t enable;
>>>>>>> +};
>>>>>>> +cmdline_parse_token_string_t cmd_set_aged_flow_event_log_set
>> =
>>>>>>> +	TOKEN_STRING_INITIALIZER(struct
>>>>>> cmd_set_aged_flow_event_log_result,
>>>>>>> +		set, "set");
>>>>>>> +cmdline_parse_token_string_t
>>>> cmd_set_aged_flow_event_log_keyword
>>>>>> =
>>>>>>> +	TOKEN_STRING_INITIALIZER(struct
>>>>>> cmd_set_aged_flow_event_log_result,
>>>>>>> +		keyword, "aged_flow_event_log");
>> cmdline_parse_token_string_t
>>>> cmd_set_aged_flow_event_log_enable =
>>>>>>> +	TOKEN_STRING_INITIALIZER(struct
>>>>>> cmd_set_aged_flow_event_log_result,
>>>>>>> +		enable, "on#off");
>>>>>>> +
>>>>>>> +static void
>>>>>>> +cmd_set_aged_flow_event_log_parsed(void *parsed_result,
>>>>>>> +				__rte_unused struct cmdline *cl,
>>>>>>> +				__rte_unused void *data)
>>>>>>> +{
>>>>>>> +	struct cmd_set_aged_flow_event_log_result *res =
>> parsed_result;
>>>>>>> +	int enable = (strcmp(res->enable, "on") == 0) ? 1 : 0;
>>>>>>> +	update_aging_event_log_status(enable);
>>>>>>> +}
>>>>>>> +
>>>>>>> +cmdline_parse_inst_t cmd_set_aged_flow_event_log = {
>>>>>>> +	.f = cmd_set_aged_flow_event_log_parsed,
>>>>>>> +	.data = NULL,
>>>>>>> +	.help_str = "set aged_flow_event_log on|off",
>>>>>>
>>>>>> What do you think "set aged_flow_verbose on|off" to be more similar
>>>>>> to existing verbose command?
>>>>>
>>>>> Please see my comments below regard it.
>>>>>
>>>>>> <...>
>>>>>>
>>>>>>> --- a/app/test-pmd/testpmd.c
>>>>>>> +++ b/app/test-pmd/testpmd.c
>>>>>>> @@ -3068,6 +3068,21 @@ rmv_port_callback(void *arg)
>>>>>>>  		start_packet_forwarding(0);
>>>>>>>  }
>>>>>>>
>>>>>>> +static int aged_flow_event_enable;
>>>>>>
>>>>>> Other global config values are at the beginning of the file, with a
>>>>>> comment on them, can you do same with variable?
>>>>>
>>>>> +1
>>>>>
>>>>>>> +void update_aging_event_log_status(int enable) {
>>>>>>> +	aged_flow_event_enable = enable; }
>>>>>>> +
>>>>>>> +int aging_event_output(uint16_t portid)
>>>>>>
>>>>>> This can be a 'static' function.
>>>>>
>>>>> +1
>>>>>
>>>>>>> +{
>>>>>>> +	if (aged_flow_event_enable) {
>>>>>>> +		printf("port %u RTE_ETH_EVENT_FLOW_AGED
>> triggered\n",
>>>>>> portid);
>>>>>>> +		fflush(stdout);
>>>>>>> +	}
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>>  /* This function is used by the interrupt thread */  static int
>>>>>>> eth_event_callback(portid_t port_id, enum rte_eth_event_type
>> type,
>>>>>>> void *param, @@ -3098,6 +3113,8 @@ eth_event_callback(portid_t
>>>>>> port_id, enum rte_eth_event_type type, void *param,
>>>>>>>  				rmv_port_callback, (void
>>>>>> *)(intptr_t)port_id))
>>>>>>>  			fprintf(stderr, "Could not set up deferred device
>>>>>> removal\n");
>>>>>>>  		break;
>>>>>>> +	case RTE_ETH_EVENT_FLOW_AGED:
>>>>>>> +		aging_event_output(port_id);
>>>>>>
>>>>>> Can't this provide more information than port_id, like flow id etc,
>>>>>> what event_process function provides? can we print it too?
>>>>>> And what is the intended usage in real application, when flow aged
>>>>>> event occurred, should application destroy the flow? So it should
>>>>>> know the flow, right?
>>>>>
>>>>> Probably Yes Ferruh, I will add details, maybe it will be clearer:
>>>>>
>>>>> As described well in rte_flow_get_aged_flows API description, there
>>>>> are 2
>>>> suggested options for the application:
>>>>>
>>>>> 1. To call rte_flow_get_aged_flows from the AGED event callback in
>>>>> order
>>>> to get the aged flow contexts of the triggered port.
>>>>> 2. To call rte_flow_get_aged_flows in any time application wants.
>>>>
>>>> I see, for the testpmd implementation what do you think getting the
>>>> aged flow list and print them in event callback, this can be good as
>>>> sample of the intended usage?
>>>
>>> Yes, we thought on it and I understand you, The issue with it is that
>>> we need to synchronize all the flow management in Testpmd and to
>> protect any flow operation with a lock.
>>> Because the callback is probably coming from the host thread while other
>> flow operations from different Testpmd thread(command line thread).
>>>
>>> It will do the patch very intrusive.
>>>
>>> The current approach(like the second suggestion) moves the
>> synchronization to the testpmd user to access flows only from the command
>> line thread while hinting to the user when a port holds some aged-out flows.
>>> I think it is better to stay the implementation simple.
>>
>> OK
>>
>>>
>>>>>
>>>>> It is probably depends in the way the application wants to
>>>>> synchronize flow
>>>> APIs calls.
>>>>>
>>>>> The application just gets the information of the aged-out flows
>>>>> context
>>>> from the above API and can do any flow operation for it, and yes, the
>>>> most expected case is to destroy the flows.
>>>>>
>>>>> Bill added 2 testpmd commands:
>>>>>
>>>>> 1. To use rte_flow_get_aged_flows and to print a list of all the
>>>>> aged-out
>>>> flows (with option to destroy them directly by the command).
>>>>> 2. A Boolean to indicate the application whether to notify the
>>>>> testpmd user
>>>> about new aged-out flows(by print).
>>>>>
>>>>> By this way, the testpmd user can use the 2 approaches with minimum
>>>> testpmd flow management change.
>>>>>
>>>>> So, the Boolean var is more like "trigger testpmd user
>>>>> notification", not like
>>>> regular verbose options.
>>>>
>>>> As you already know, event always registered and callback always
>>>> called, this command only defines to print a log in the callback or
>>>> not, so I believe 'verbose' suits here, main concern is there are
>>>> many testpmd commands and it is hard to remember them (usage is not
>>>> intuitive, I had need to check source code to find a command many
>>>> times), trying to make them as consistent as possible to help usage.
>>>>
>>>> But I think as see your concern, "set aged_flow_verbose on|off"
>>>> command can be confused as if changing "flow aged #" command
>> verbosity level.
>>>>
>>>> What do you think about a more generic "set event_verbose on|off"
>>>> command, which can control to logging for all event handlers, but
>>>> right now only for aged events?
>>>
>>> Looks good to me, but maybe instead of on | off it is better to use bitmap
>> in order to indicate the event?
>>
>> No objection but not sure that level fine grain needed now, or later, why not
>> add the basic on/off now and add the bitmap when we need to control for
>> each event.
> 
> It will be good to the user to reduce logs of non-interested events (maybe even more often) which makes his log bigger.

Right now, is there any other event that logs? And how much log do they produce?

> 
> 
>>
>>>
>>>
>>>>>
>>>>> Matan
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>>
>>>>>> <...>
>>>>>>
>>>>>>> --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
>>>>>>> +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
>>>>>>> @@ -1062,6 +1062,21 @@ Where:
>>>>>>>
>>>>>>>     Check the NIC Datasheet for hardware limits.
>>>>>>>
>>>>>>> +aged flow event log set
>>>>>>> +~~~~~~~~~~~~~~~~~~~~~~~
>>>>>>> +
>>>>>>> +Currently, the flow aging event is registered when the testpmd
>>>>>>> +application start, if new flow be checked age out, there will be
>>>>>>> +one output log for it. But some applications do not interest this
>>>>>>> +event, use this
>>>>>> command can set if the event expose to the applications::
>>>>>>> +
>>>>>>> +   testpmd> set aged_flow_event_log (on|off)
>>>>>>> +
>>>>>>> +For examine::
>>>>>>> +
>>>>>>> +   testpmd> set aged_flow_event_log on
>>>>>>> +   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
>>>>>>> +   testpmd> set aged_flow_event_log off
>>>>>>> +
>>>>>>
>>>>>> This can be moved below the "set verbose" command since they are in
>>>>>> similar group.
>>>
> 


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-05-01 13:38               ` Ferruh Yigit
@ 2020-05-01 15:14                 ` Matan Azrad
  2020-05-01 15:44                   ` Ferruh Yigit
  0 siblings, 1 reply; 27+ messages in thread
From: Matan Azrad @ 2020-05-01 15:14 UTC (permalink / raw)
  To: Ferruh Yigit, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev



From: Ferruh Yigit:
> On 5/1/2020 1:45 PM, Matan Azrad wrote:
> >
> >
> > From: Ferruh Yigit:
> >> On 5/1/2020 12:28 PM, Matan Azrad wrote:
> >>>
> >>> Hi Ferruh
> >>>
> >>> From: Ferruh Yigit:
> >>>> On 5/1/2020 7:51 AM, Matan Azrad wrote:
> >>>>> Hi Ferruh
> >>>>>
> >>>>> From: Ferruh Yigit
> >>>>>> On 4/30/2020 4:53 PM, Bill Zhou wrote:
> >>>>>>> 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:
> >>>>>>> - Registering aging event when the testpmd application start, add
> new
> >>>>>>>   command to control if the event expose to the applications. If it's
> be
> >>>>>>>   set, when new flow be checked age out, there will be one-line
> >>>>>>> output
> >>>> log.
> >>>>>>> - Add new command to list all aged flows, meanwhile, we can set
> >>>>>> parameter
> >>>>>>>   to destroy it.
> >>>>>>>
> >>>>>>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
> >>>>>>> ---
> >>>>>>> v2: Update the way of registering aging event, add new command
> >>>>>>> to control if the event need be print or not. Update the output
> >>>>>>> of the delete aged flow command format.
<...>
> >>>> What do you think about a more generic "set event_verbose on|off"
> >>>> command, which can control to logging for all event handlers, but
> >>>> right now only for aged events?
> >>>
> >>> Looks good to me, but maybe instead of on | off it is better to use
> >>> bitmap
> >> in order to indicate the event?
> >>
> >> No objection but not sure that level fine grain needed now, or later,
> >> why not add the basic on/off now and add the bitmap when we need to
> >> control for each event.
> >
> > It will be good to the user to reduce logs of non-interested events (maybe
> even more often) which makes his log bigger.
> 
> Right now, is there any other event that logs? And how much log do they
> produce?

I don't really familiar with all, but looks like IPSEC, VF_MBOX and AGED may make the log bigger than others.

So, if we open verbose for all, the events log bitmap may be very usable.

<....>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v2] app/testpmd: support flow aging
  2020-05-01 15:14                 ` Matan Azrad
@ 2020-05-01 15:44                   ` Ferruh Yigit
  0 siblings, 0 replies; 27+ messages in thread
From: Ferruh Yigit @ 2020-05-01 15:44 UTC (permalink / raw)
  To: Matan Azrad, Bill Zhou, wenzhuo.lu, jingjing.wu,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev

On 5/1/2020 4:14 PM, Matan Azrad wrote:
> 
> 
> From: Ferruh Yigit:
>> On 5/1/2020 1:45 PM, Matan Azrad wrote:
>>>
>>>
>>> From: Ferruh Yigit:
>>>> On 5/1/2020 12:28 PM, Matan Azrad wrote:
>>>>>
>>>>> Hi Ferruh
>>>>>
>>>>> From: Ferruh Yigit:
>>>>>> On 5/1/2020 7:51 AM, Matan Azrad wrote:
>>>>>>> Hi Ferruh
>>>>>>>
>>>>>>> From: Ferruh Yigit
>>>>>>>> On 4/30/2020 4:53 PM, Bill Zhou wrote:
>>>>>>>>> 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:
>>>>>>>>> - Registering aging event when the testpmd application start, add
>> new
>>>>>>>>>   command to control if the event expose to the applications. If it's
>> be
>>>>>>>>>   set, when new flow be checked age out, there will be one-line
>>>>>>>>> output
>>>>>> log.
>>>>>>>>> - Add new command to list all aged flows, meanwhile, we can set
>>>>>>>> parameter
>>>>>>>>>   to destroy it.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Bill Zhou <dongz@mellanox.com>
>>>>>>>>> ---
>>>>>>>>> v2: Update the way of registering aging event, add new command
>>>>>>>>> to control if the event need be print or not. Update the output
>>>>>>>>> of the delete aged flow command format.
> <...>
>>>>>> What do you think about a more generic "set event_verbose on|off"
>>>>>> command, which can control to logging for all event handlers, but
>>>>>> right now only for aged events?
>>>>>
>>>>> Looks good to me, but maybe instead of on | off it is better to use
>>>>> bitmap
>>>> in order to indicate the event?
>>>>
>>>> No objection but not sure that level fine grain needed now, or later,
>>>> why not add the basic on/off now and add the bitmap when we need to
>>>> control for each event.
>>>
>>> It will be good to the user to reduce logs of non-interested events (maybe
>> even more often) which makes his log bigger.
>>
>> Right now, is there any other event that logs? And how much log do they
>> produce?
> 
> I don't really familiar with all, but looks like IPSEC, VF_MBOX and AGED may make the log bigger than others.
> 
> So, if we open verbose for all, the events log bitmap may be very usable.
> 

I was thinking they are not printing anything at all, but if they do I agree
with you. Thanks.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [dpdk-dev] [PATCH v3] app/testpmd: support flow aging
  2020-04-30 15:53 ` [dpdk-dev] [PATCH v2] " Bill Zhou
  2020-04-30 22:43   ` Ferruh Yigit
@ 2020-05-02 14:00   ` Bill Zhou
  2020-05-03  8:59     ` [dpdk-dev] [PATCH v4] " Bill Zhou
  1 sibling, 1 reply; 27+ messages in thread
From: Bill Zhou @ 2020-05-02 14:00 UTC (permalink / raw)
  To: ferruh.yigit, matan, wenzhuo.lu, beilei.xing, bernard.iremonger,
	orika, john.mcnamara, marko.kovacevic
  Cc: dev

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:
- Registering aging event when the testpmd application start, add one new
  command to set verbose bitmaps for all events. If RTE_ETH_EVENT_FLOW_AGED
  bit is set, when new flow be checked age out, there will be output log
  for it.
- Add new command to list all aged flows, meanwhile, we can set parameter
  to destroy it.

Signed-off-by: Bill Zhou <dongz@mellanox.com>
---
v2: Update the way of registering aging event, add new command to control
if the event need be print or not. Update the output of the delete aged
flow command format.
v3: Change the command from only set aged flow output to set one gloable
verbose bitmap for all events output.
---
 app/test-pmd/cmdline.c                      |  49 +++++++++
 app/test-pmd/cmdline_flow.c                 |  62 +++++++++++
 app/test-pmd/config.c                       | 108 ++++++++++++++++++--
 app/test-pmd/testpmd.c                      |  12 +++
 app/test-pmd/testpmd.h                      |   4 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  79 ++++++++++++++
 6 files changed, 303 insertions(+), 11 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 1375f223eb..a257f25e01 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -263,6 +263,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"set verbose (level)\n"
 			"    Set the debug verbosity level X.\n\n"
 
+			"set event_verbose (bitmap)\n"
+			"    Set the event debug verbose 32 bits bitmap X.\n\n"
+
 			"set log global|(type) (level)\n"
 			"    Set the log level.\n\n"
 
@@ -1125,6 +1128,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"
@@ -19387,6 +19394,47 @@ cmdline_parse_inst_t cmd_showport_macs = {
 	},
 };
 
+/* Set event verbose bitmap*/
+struct cmd_set_event_verbose_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t keyword;
+	uint32_t bitmap;
+};
+cmdline_parse_token_string_t cmd_set_event_verbose_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_event_verbose_result,
+		set, "set");
+cmdline_parse_token_string_t cmd_set_event_verbose_keyword =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_event_verbose_result,
+		keyword, "event_verbose");
+cmdline_parse_token_num_t cmd_set_event_verbose_bitmap =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_event_verbose_result,
+		bitmap, UINT32);
+
+static void
+cmd_set_event_verbose_parsed(void *parsed_result,
+				__rte_unused struct cmdline *cl,
+				__rte_unused void *data)
+{
+	struct cmd_set_event_verbose_result *res = parsed_result;
+
+	printf("Change event verbose bitmap from 0x%x to 0x%x\n",
+	       (unsigned int) event_verbose_bitmap,
+	       (unsigned int) res->bitmap);
+	event_verbose_bitmap = res->bitmap;
+}
+
+cmdline_parse_inst_t cmd_set_event_verbose = {
+	.f = cmd_set_event_verbose_parsed,
+	.data = NULL,
+	.help_str = "set event_verbose (bitmap)",
+	.tokens = {
+		(void *)&cmd_set_event_verbose_set,
+		(void *)&cmd_set_event_verbose_keyword,
+		(void *)&cmd_set_event_verbose_bitmap,
+		NULL,
+	},
+};
+
 /* ******************************************************************************** */
 
 /* list of instructions */
@@ -19684,6 +19732,7 @@ cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_show_set_raw,
 	(cmdline_parse_inst_t *)&cmd_show_set_raw_all,
 	(cmdline_parse_inst_t *)&cmd_config_tx_dynf_specific,
+	(cmdline_parse_inst_t *)&cmd_set_event_verbose,
 	NULL,
 };
 
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 45bcff3cf5..4e2006c543 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -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;
 	}
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 72f25d1521..035d336ab5 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -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])
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 99bacddbfd..689467f736 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -81,6 +81,7 @@
 #define EXTBUF_ZONE_SIZE RTE_PGSIZE_2M
 
 uint16_t verbose_level = 0; /**< Silent by default. */
+uint32_t event_verbose_bitmap; /**< Verbose bitmap for all events */
 int testpmd_logtype; /**< Log type for testpmd logs */
 
 /* use master core for command line ? */
@@ -3068,6 +3069,15 @@ rmv_port_callback(void *arg)
 		start_packet_forwarding(0);
 }
 
+static void
+aging_event_output(uint16_t portid)
+{
+	if (event_verbose_bitmap & (1 << RTE_ETH_EVENT_FLOW_AGED)) {
+		printf("port %u RTE_ETH_EVENT_FLOW_AGED triggered\n", portid);
+		fflush(stdout);
+	}
+}
+
 /* This function is used by the interrupt thread */
 static int
 eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
@@ -3098,6 +3108,8 @@ eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
 				rmv_port_callback, (void *)(intptr_t)port_id))
 			fprintf(stderr, "Could not set up deferred device removal\n");
 		break;
+	case RTE_ETH_EVENT_FLOW_AGED:
+		aging_event_output(port_id);
 	default:
 		break;
 	}
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7ff4c5dba3..674c3670f9 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -323,6 +323,7 @@ extern uint8_t xstats_hide_zero; /**< Hide zero values for xstats display */
 
 /* globals used for configuration */
 extern uint16_t verbose_level; /**< Drives messages being displayed, if any. */
+extern uint32_t event_verbose_bitmap; /**< Verbose bitmap for all events */
 extern int testpmd_logtype; /**< Log type for testpmd logs */
 extern uint8_t  interactive;
 extern uint8_t  auto_start;
@@ -747,12 +748,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);
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index a360ecccfd..b5eb59ba46 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -632,6 +632,23 @@ Available levels are as following:
 * ``2`` fully verbose except for Rx packets.
 * ``> 2`` fully verbose.
 
+set event_verbose
+~~~~~~~~~~~~~~~~~
+
+Set the debug verbosity bitmaps for all events defined in enum rte_eth_event_type,
+the maximum bits of the bitmap is 32::
+
+   testpmd> set event_verbose (bitmap)
+
+For examine, to start the event:RTE_ETH_EVENT_FLOW_AGED log::
+
+   testpmd> set event_verbose 0x400
+   Change event verbose bitmap from 0x0 to 0x400
+
+When aged flow be checkout, there will be one output log for it::
+
+   testpmd> port 0 RTE_ETH_EVENT_FLOW_AGED triggered
+
 set log
 ~~~~~~~
 
@@ -3616,6 +3633,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 +4524,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
 ~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [dpdk-dev] [PATCH v4] app/testpmd: support flow aging
  2020-05-02 14:00   ` [dpdk-dev] [PATCH v3] " Bill Zhou
@ 2020-05-03  8:59     ` Bill Zhou
  2020-05-03  9:46       ` Matan Azrad
                         ` (3 more replies)
  0 siblings, 4 replies; 27+ messages in thread
From: Bill Zhou @ 2020-05-03  8:59 UTC (permalink / raw)
  To: ferruh.yigit, matan, wenzhuo.lu, beilei.xing, bernard.iremonger,
	orika, john.mcnamara, marko.kovacevic
  Cc: dev

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: Bill Zhou <dongz@mellanox.com>
---
v2: Update the way of registering aging event, add new command to control
if the event need be print or not. Update the output of the delete aged
flow command format.
v3: Change the command from only set aged flow output to set one gloable
verbose bitmap for all events output.
v4: Add the event output to current global print event arguments.
---
 app/test-pmd/cmdline.c                      |   4 +
 app/test-pmd/cmdline_flow.c                 |  62 +++++++++++
 app/test-pmd/config.c                       | 108 ++++++++++++++++++--
 app/test-pmd/parameters.c                   |   6 +-
 app/test-pmd/testpmd.c                      |   4 +-
 app/test-pmd/testpmd.h                      |   3 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  62 +++++++++++
 7 files changed, 235 insertions(+), 14 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 1375f223eb..bcf9080c48 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -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"
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 45bcff3cf5..4e2006c543 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -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;
 	}
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 72f25d1521..035d336ab5 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -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])
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 30c1753c32..92b5575626 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -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 {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 99bacddbfd..a2d0be56b3 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -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.
  */
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7ff4c5dba3..fb391672a8 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -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);
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index a360ecccfd..19260cc2d9 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -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
 ~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v4] app/testpmd: support flow aging
  2020-05-03  8:59     ` [dpdk-dev] [PATCH v4] " Bill Zhou
@ 2020-05-03  9:46       ` Matan Azrad
  2020-05-03 14:58       ` Ori Kam
                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 27+ messages in thread
From: Matan Azrad @ 2020-05-03  9:46 UTC (permalink / raw)
  To: Bill Zhou, ferruh.yigit, wenzhuo.lu, beilei.xing,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev



From: Bill Zhou:
> 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: Bill Zhou <dongz@mellanox.com>
Acked-by: Matan Azrad <matan@mellanox.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v4] app/testpmd: support flow aging
  2020-05-03  8:59     ` [dpdk-dev] [PATCH v4] " Bill Zhou
  2020-05-03  9:46       ` Matan Azrad
@ 2020-05-03 14:58       ` Ori Kam
  2020-05-05  8:37       ` Ferruh Yigit
  2020-05-05  9:49       ` [dpdk-dev] [PATCH v5] " Bill Zhou
  3 siblings, 0 replies; 27+ messages in thread
From: Ori Kam @ 2020-05-03 14:58 UTC (permalink / raw)
  To: Bill Zhou, ferruh.yigit, Matan Azrad, wenzhuo.lu, beilei.xing,
	bernard.iremonger, john.mcnamara, marko.kovacevic
  Cc: dev



> -----Original Message-----
> From: Bill Zhou <dongz@mellanox.com>
> Subject: [PATCH v4] 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: Bill Zhou <dongz@mellanox.com>
> ---
> v2: Update the way of registering aging event, add new command to control
> if the event need be print or not. Update the output of the delete aged
> flow command format.
> v3: Change the command from only set aged flow output to set one gloable
> verbose bitmap for all events output.
> v4: Add the event output to current global print event arguments.
> ---

Acked-by: Ori Kam <orika@mellanox.com>
Thanks,
Ori

>  app/test-pmd/cmdline.c                      |   4 +
>  app/test-pmd/cmdline_flow.c                 |  62 +++++++++++
>  app/test-pmd/config.c                       | 108 ++++++++++++++++++--
>  app/test-pmd/parameters.c                   |   6 +-
>  app/test-pmd/testpmd.c                      |   4 +-
>  app/test-pmd/testpmd.h                      |   3 +
>  doc/guides/testpmd_app_ug/testpmd_funcs.rst |  62 +++++++++++
>  7 files changed, 235 insertions(+), 14 deletions(-)
> 
> diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
> index 1375f223eb..bcf9080c48 100644
> --- a/app/test-pmd/cmdline.c
> +++ b/app/test-pmd/cmdline.c
> @@ -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"
> diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
> index 45bcff3cf5..4e2006c543 100644
> --- a/app/test-pmd/cmdline_flow.c
> +++ b/app/test-pmd/cmdline_flow.c
> @@ -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;
>  	}
> diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
> index 72f25d1521..035d336ab5 100644
> --- a/app/test-pmd/config.c
> +++ b/app/test-pmd/config.c
> @@ -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])
> diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
> index 30c1753c32..92b5575626 100644
> --- a/app/test-pmd/parameters.c
> +++ b/app/test-pmd/parameters.c
> @@ -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_a
> ged|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_a
> ged|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 {
> diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
> index 99bacddbfd..a2d0be56b3 100644
> --- a/app/test-pmd/testpmd.c
> +++ b/app/test-pmd/testpmd.c
> @@ -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.
>   */
> diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
> index 7ff4c5dba3..fb391672a8 100644
> --- a/app/test-pmd/testpmd.h
> +++ b/app/test-pmd/testpmd.h
> @@ -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);
> diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> index a360ecccfd..19260cc2d9 100644
> --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
> @@ -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
>  ~~~~~~~~~~~~~~~~~~~~~~
> 
> --
> 2.21.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v4] app/testpmd: support flow aging
  2020-05-03  8:59     ` [dpdk-dev] [PATCH v4] " Bill Zhou
  2020-05-03  9:46       ` Matan Azrad
  2020-05-03 14:58       ` Ori Kam
@ 2020-05-05  8:37       ` Ferruh Yigit
  2020-05-05  9:11         ` Matan Azrad
  2020-05-05  9:49       ` [dpdk-dev] [PATCH v5] " Bill Zhou
  3 siblings, 1 reply; 27+ messages in thread
From: Ferruh Yigit @ 2020-05-05  8:37 UTC (permalink / raw)
  To: Bill Zhou, matan, wenzhuo.lu, beilei.xing, bernard.iremonger,
	orika, john.mcnamara, marko.kovacevic
  Cc: dev

On 5/3/2020 9:59 AM, Bill Zhou wrote:
> 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: Bill Zhou <dongz@mellanox.com>
> ---
> v2: Update the way of registering aging event, add new command to control
> if the event need be print or not. Update the output of the delete aged
> flow command format.
> v3: Change the command from only set aged flow output to set one gloable
> verbose bitmap for all events output.
> v4: Add the event output to current global print event arguments.

<...>

> @@ -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>: "

+1 to '--print-event', can you please update the documentation for the change?

<...>

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

This is enabling the event logging by default, we are turning back to original
point, since '--print-event' can be used to enable it, can you please leave it
out by default?


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v4] app/testpmd: support flow aging
  2020-05-05  8:37       ` Ferruh Yigit
@ 2020-05-05  9:11         ` Matan Azrad
  2020-05-05  9:23           ` Ferruh Yigit
  0 siblings, 1 reply; 27+ messages in thread
From: Matan Azrad @ 2020-05-05  9:11 UTC (permalink / raw)
  To: Ferruh Yigit, Bill Zhou, wenzhuo.lu, beilei.xing,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev



From: Ferruh Yigit:
> On 5/3/2020 9:59 AM, Bill Zhou wrote:
> > 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: Bill Zhou <dongz@mellanox.com>
> > ---
> > v2: Update the way of registering aging event, add new command to
> > control if the event need be print or not. Update the output of the
> > delete aged flow command format.
> > v3: Change the command from only set aged flow output to set one
> > gloable verbose bitmap for all events output.
> > v4: Add the event output to current global print event arguments.
> 
> <...>
> 
> > @@ -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|flo
> w_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|flo
> w_aged|all>: "
> 
> +1 to '--print-event', can you please update the documentation for the
> change?

 +1

> <...>
> 
> > @@ -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);
> 
> This is enabling the event logging by default, we are turning back to original
> point, since '--print-event' can be used to enable it, can you please leave it
> out by default?

The aged event is triggered only when there is at least one flow which is configured with AGE action,
Don't you think it should be printed by default if the user requested aging in a flow?



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v4] app/testpmd: support flow aging
  2020-05-05  9:11         ` Matan Azrad
@ 2020-05-05  9:23           ` Ferruh Yigit
  0 siblings, 0 replies; 27+ messages in thread
From: Ferruh Yigit @ 2020-05-05  9:23 UTC (permalink / raw)
  To: Matan Azrad, Bill Zhou, wenzhuo.lu, beilei.xing,
	bernard.iremonger, Ori Kam, john.mcnamara, marko.kovacevic
  Cc: dev

On 5/5/2020 10:11 AM, Matan Azrad wrote:
> 
> 
> From: Ferruh Yigit:
>> On 5/3/2020 9:59 AM, Bill Zhou wrote:
>>> 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: Bill Zhou <dongz@mellanox.com>
>>> ---
>>> v2: Update the way of registering aging event, add new command to
>>> control if the event need be print or not. Update the output of the
>>> delete aged flow command format.
>>> v3: Change the command from only set aged flow output to set one
>>> gloable verbose bitmap for all events output.
>>> v4: Add the event output to current global print event arguments.
>>
>> <...>
>>
>>> @@ -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|flo
>> w_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|flo
>> w_aged|all>: "
>>
>> +1 to '--print-event', can you please update the documentation for the
>> change?
> 
>  +1
> 
>> <...>
>>
>>> @@ -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);
>>
>> This is enabling the event logging by default, we are turning back to original
>> point, since '--print-event' can be used to enable it, can you please leave it
>> out by default?
> 
> The aged event is triggered only when there is at least one flow which is configured with AGE action,
> Don't you think it should be printed by default if the user requested aging in a flow?
> 
Good point, OK to keep as it is.
So only above doc update please and feel free to keep existing acks.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [dpdk-dev] [PATCH v5] app/testpmd: support flow aging
  2020-05-03  8:59     ` [dpdk-dev] [PATCH v4] " Bill Zhou
                         ` (2 preceding siblings ...)
  2020-05-05  8:37       ` Ferruh Yigit
@ 2020-05-05  9:49       ` Bill Zhou
  2020-05-05 10:09         ` Ori Kam
  3 siblings, 1 reply; 27+ messages in thread
From: Bill Zhou @ 2020-05-05  9:49 UTC (permalink / raw)
  To: ferruh.yigit, matan, wenzhuo.lu, beilei.xing, bernard.iremonger,
	orika, john.mcnamara, marko.kovacevic
  Cc: dev

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: Bill Zhou <dongz@mellanox.com>
---
v2: Update the way of registering aging event, add new command to control
if the event need be print or not. Update the output of the delete aged
flow command format.
v3: Change the command from only set aged flow output to set one gloable
verbose bitmap for all events output.
v4: Add the event output to current global print event arguments.
v5: Update the documentation about print event command line change.
 app/test-pmd/cmdline.c                      |   4 +
 app/test-pmd/cmdline_flow.c                 |  62 +++++++++++
 app/test-pmd/config.c                       | 108 ++++++++++++++++++--
 app/test-pmd/parameters.c                   |   6 +-
 app/test-pmd/testpmd.c                      |   4 +-
 app/test-pmd/testpmd.h                      |   3 +
 doc/guides/testpmd_app_ug/run_app.rst       |   4 +-
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  62 +++++++++++
 8 files changed, 237 insertions(+), 16 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 1375f223eb..bcf9080c48 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -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"
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 45bcff3cf5..4e2006c543 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -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;
 	}
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 72f25d1521..035d336ab5 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -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])
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 30c1753c32..92b5575626 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -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 {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 99bacddbfd..a2d0be56b3 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -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.
  */
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7ff4c5dba3..fb391672a8 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -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);
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index 727ef52b8f..71213361b4 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -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.
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index a360ecccfd..19260cc2d9 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -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
 ~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v5] app/testpmd: support flow aging
  2020-05-05  9:49       ` [dpdk-dev] [PATCH v5] " Bill Zhou
@ 2020-05-05 10:09         ` Ori Kam
  2020-05-05 15:11           ` Ferruh Yigit
  0 siblings, 1 reply; 27+ messages in thread
From: Ori Kam @ 2020-05-05 10:09 UTC (permalink / raw)
  To: Bill Zhou, ferruh.yigit, Matan Azrad, wenzhuo.lu, beilei.xing,
	bernard.iremonger, john.mcnamara, marko.kovacevic
  Cc: dev



> -----Original Message-----
> From: Bill Zhou <dongz@mellanox.com>
> Sent: Tuesday, May 5, 2020 12:49 PM
> Subject: [PATCH v5] 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: Bill Zhou <dongz@mellanox.com>
> ---
> v2: Update the way of registering aging event, add new command to control
> if the event need be print or not. Update the output of the delete aged
> flow command format.
> v3: Change the command from only set aged flow output to set one gloable
> verbose bitmap for all events output.
> v4: Add the event output to current global print event arguments.
> v5: Update the documentation about print event command line change.
>  app/test-pmd/cmdline.c                      |   4 +
>  app/test-pmd/cmdline_flow.c                 |  62 +++++++++++
>  app/test-pmd/config.c                       | 108 ++++++++++++++++++--
>  app/test-pmd/parameters.c                   |   6 +-
>  app/test-pmd/testpmd.c                      |   4 +-
>  app/test-pmd/testpmd.h                      |   3 +
>  doc/guides/testpmd_app_ug/run_app.rst       |   4 +-
>  doc/guides/testpmd_app_ug/testpmd_funcs.rst |  62 +++++++++++
>  8 files changed, 237 insertions(+), 16 deletions(-)
> 


Acked-by: Ori Kam <orika@mellanox.com>
Thanks,
Ori


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v5] app/testpmd: support flow aging
  2020-05-05 10:09         ` Ori Kam
@ 2020-05-05 15:11           ` Ferruh Yigit
  2020-05-06  8:04             ` Matan Azrad
  0 siblings, 1 reply; 27+ messages in thread
From: Ferruh Yigit @ 2020-05-05 15:11 UTC (permalink / raw)
  To: Ori Kam, Bill Zhou, Matan Azrad, wenzhuo.lu, beilei.xing,
	bernard.iremonger, john.mcnamara, marko.kovacevic
  Cc: dev

On 5/5/2020 11:09 AM, Ori Kam wrote:
> 
> 
>> -----Original Message-----
>> From: Bill Zhou <dongz@mellanox.com>
>> Sent: Tuesday, May 5, 2020 12:49 PM
>> Subject: [PATCH v5] 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: Bill Zhou <dongz@mellanox.com>
>> ---
>> v2: Update the way of registering aging event, add new command to control
>> if the event need be print or not. Update the output of the delete aged
>> flow command format.
>> v3: Change the command from only set aged flow output to set one gloable
>> verbose bitmap for all events output.
>> v4: Add the event output to current global print event arguments.
>> v5: Update the documentation about print event command line change.
>>  app/test-pmd/cmdline.c                      |   4 +
>>  app/test-pmd/cmdline_flow.c                 |  62 +++++++++++
>>  app/test-pmd/config.c                       | 108 ++++++++++++++++++--
>>  app/test-pmd/parameters.c                   |   6 +-
>>  app/test-pmd/testpmd.c                      |   4 +-
>>  app/test-pmd/testpmd.h                      |   3 +
>>  doc/guides/testpmd_app_ug/run_app.rst       |   4 +-
>>  doc/guides/testpmd_app_ug/testpmd_funcs.rst |  62 +++++++++++
>>  8 files changed, 237 insertions(+), 16 deletions(-)
>>
> 
> Acked-by: Ori Kam <orika@mellanox.com>

Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>

Applied to dpdk-next-net/master, thanks.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [dpdk-dev] [PATCH v5] app/testpmd: support flow aging
  2020-05-05 15:11           ` Ferruh Yigit
@ 2020-05-06  8:04             ` Matan Azrad
  0 siblings, 0 replies; 27+ messages in thread
From: Matan Azrad @ 2020-05-06  8:04 UTC (permalink / raw)
  To: Ferruh Yigit, Ori Kam, Bill Zhou, wenzhuo.lu, beilei.xing,
	bernard.iremonger, john.mcnamara, marko.kovacevic
  Cc: dev



From: Ferruh Yigit:
> On 5/5/2020 11:09 AM, Ori Kam wrote:
> >
> >
> >> -----Original Message-----
> >> From: Bill Zhou <dongz@mellanox.com>
> >> Sent: Tuesday, May 5, 2020 12:49 PM
> >> Subject: [PATCH v5] 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: Bill Zhou <dongz@mellanox.com>
> >> ---
> >> v2: Update the way of registering aging event, add new command to
> >> control if the event need be print or not. Update the output of the
> >> delete aged flow command format.
> >> v3: Change the command from only set aged flow output to set one
> >> gloable verbose bitmap for all events output.
> >> v4: Add the event output to current global print event arguments.
> >> v5: Update the documentation about print event command line change.
> >>  app/test-pmd/cmdline.c                      |   4 +
> >>  app/test-pmd/cmdline_flow.c                 |  62 +++++++++++
> >>  app/test-pmd/config.c                       | 108 ++++++++++++++++++--
> >>  app/test-pmd/parameters.c                   |   6 +-
> >>  app/test-pmd/testpmd.c                      |   4 +-
> >>  app/test-pmd/testpmd.h                      |   3 +
> >>  doc/guides/testpmd_app_ug/run_app.rst       |   4 +-
> >>  doc/guides/testpmd_app_ug/testpmd_funcs.rst |  62 +++++++++++
> >>  8 files changed, 237 insertions(+), 16 deletions(-)
> >>
> >
> > Acked-by: Ori Kam <orika@mellanox.com>
> 
> Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
Acked-by: Matan Azrad <matan@mellanox.com>
> Applied to dpdk-next-net/master, thanks.

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2020-05-06  8:04 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-24 10:55 [dpdk-dev] [PATCH] app/testpmd: support flow aging Bill Zhou
2020-04-24 16:25 ` Ferruh Yigit
2020-04-26  7:23   ` Bill Zhou
2020-04-27 14:13     ` Ferruh Yigit
2020-04-27 15:12       ` Matan Azrad
2020-04-30 22:26         ` Ferruh Yigit
2020-04-30 15:53 ` [dpdk-dev] [PATCH v2] " Bill Zhou
2020-04-30 22:43   ` Ferruh Yigit
2020-05-01  6:51     ` Matan Azrad
2020-05-01  9:27       ` Ferruh Yigit
2020-05-01 11:28         ` Matan Azrad
2020-05-01 11:54           ` Ferruh Yigit
2020-05-01 12:45             ` Matan Azrad
2020-05-01 13:38               ` Ferruh Yigit
2020-05-01 15:14                 ` Matan Azrad
2020-05-01 15:44                   ` Ferruh Yigit
2020-05-02 14:00   ` [dpdk-dev] [PATCH v3] " Bill Zhou
2020-05-03  8:59     ` [dpdk-dev] [PATCH v4] " Bill Zhou
2020-05-03  9:46       ` Matan Azrad
2020-05-03 14:58       ` Ori Kam
2020-05-05  8:37       ` Ferruh Yigit
2020-05-05  9:11         ` Matan Azrad
2020-05-05  9:23           ` Ferruh Yigit
2020-05-05  9:49       ` [dpdk-dev] [PATCH v5] " Bill Zhou
2020-05-05 10:09         ` Ori Kam
2020-05-05 15:11           ` Ferruh Yigit
2020-05-06  8:04             ` Matan Azrad

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).