All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 05/10] app/testpmd: implement rte flow item/action template
@ 2022-01-18  5:07 Alexander Kozyrev
  2022-01-18  5:07 ` [PATCH 06/10] app/testpmd: implement rte flow table Alexander Kozyrev
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Alexander Kozyrev @ 2022-01-18  5:07 UTC (permalink / raw)
  To: dev
  Cc: orika, thomas, ivan.malov, andrew.rybchenko, ferruh.yigit,
	mohammad.abdul.awal, qi.z.zhang, jerinj, ajit.khaparde

Add testpmd support for the rte_flow_item_template and
rte_flow_action_template APIs. Provide the command line interface
for the template creation/destruction. Usage example:
  testpmd> flow item_template 0 create item_template_id 2
           template eth dst is 00:16:3e:31:15:c3 / end
  testpmd> flow action_template 0 create action_template_id 4
           template drop / end mask drop / end
  testpmd> flow action_template 0 destroy action_template 4
  testpmd> flow item_template 0 destroy item_template 2

Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
---
 app/test-pmd/cmdline_flow.c                 | 376 +++++++++++++++++++-
 app/test-pmd/config.c                       | 204 +++++++++++
 app/test-pmd/testpmd.h                      |  22 ++
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  97 +++++
 4 files changed, 697 insertions(+), 2 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index ea4af8dd45..fb27a97855 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -56,6 +56,8 @@ enum index {
 	COMMON_POLICY_ID,
 	COMMON_FLEX_HANDLE,
 	COMMON_FLEX_TOKEN,
+	COMMON_ITEM_TEMPLATE_ID,
+	COMMON_ACTION_TEMPLATE_ID,
 
 	/* TOP-level command. */
 	ADD,
@@ -73,6 +75,8 @@ enum index {
 	FLOW,
 	/* Sub-level commands. */
 	CONFIGURE,
+	ITEM_TEMPLATE,
+	ACTION_TEMPLATE,
 	INDIRECT_ACTION,
 	VALIDATE,
 	CREATE,
@@ -91,6 +95,22 @@ enum index {
 	FLEX_ITEM_CREATE,
 	FLEX_ITEM_DESTROY,
 
+	/* Item template arguments. */
+	ITEM_TEMPLATE_CREATE,
+	ITEM_TEMPLATE_DESTROY,
+	ITEM_TEMPLATE_CREATE_ID,
+	ITEM_TEMPLATE_DESTROY_ID,
+	ITEM_TEMPLATE_RELAXED_MATCHING,
+	ITEM_TEMPLATE_SPEC,
+
+	/* Action template arguments. */
+	ACTION_TEMPLATE_CREATE,
+	ACTION_TEMPLATE_DESTROY,
+	ACTION_TEMPLATE_CREATE_ID,
+	ACTION_TEMPLATE_DESTROY_ID,
+	ACTION_TEMPLATE_SPEC,
+	ACTION_TEMPLATE_MASK,
+
 	/* Tunnel arguments. */
 	TUNNEL_CREATE,
 	TUNNEL_CREATE_TYPE,
@@ -858,6 +878,10 @@ struct buffer {
 			struct rte_flow_port_attr port_attr;
 			struct rte_flow_queue_attr queue_attr;
 		} configure; /**< Configuration arguments. */
+		struct {
+			uint32_t *template_id;
+			uint32_t template_id_n;
+		} templ_destroy; /**< Template destroy arguments. */
 		struct {
 			uint32_t *action_id;
 			uint32_t action_id_n;
@@ -866,10 +890,13 @@ struct buffer {
 			uint32_t action_id;
 		} ia; /* Indirect action query arguments */
 		struct {
+			uint32_t it_id;
+			uint32_t at_id;
 			struct rte_flow_attr attr;
 			struct tunnel_ops tunnel_ops;
 			struct rte_flow_item *pattern;
 			struct rte_flow_action *actions;
+			struct rte_flow_action *masks;
 			uint32_t pattern_n;
 			uint32_t actions_n;
 			uint8_t *data;
@@ -949,6 +976,43 @@ static const enum index next_config_attr[] = {
 	ZERO,
 };
 
+static const enum index next_it_subcmd[] = {
+	ITEM_TEMPLATE_CREATE,
+	ITEM_TEMPLATE_DESTROY,
+	ZERO,
+};
+
+static const enum index next_it_attr[] = {
+	ITEM_TEMPLATE_CREATE_ID,
+	ITEM_TEMPLATE_RELAXED_MATCHING,
+	ITEM_TEMPLATE_SPEC,
+	ZERO,
+};
+
+static const enum index next_it_destroy_attr[] = {
+	ITEM_TEMPLATE_DESTROY_ID,
+	END,
+	ZERO,
+};
+
+static const enum index next_at_subcmd[] = {
+	ACTION_TEMPLATE_CREATE,
+	ACTION_TEMPLATE_DESTROY,
+	ZERO,
+};
+
+static const enum index next_at_attr[] = {
+	ACTION_TEMPLATE_CREATE_ID,
+	ACTION_TEMPLATE_SPEC,
+	ZERO,
+};
+
+static const enum index next_at_destroy_attr[] = {
+	ACTION_TEMPLATE_DESTROY_ID,
+	END,
+	ZERO,
+};
+
 static const enum index next_ia_create_attr[] = {
 	INDIRECT_ACTION_CREATE_ID,
 	INDIRECT_ACTION_INGRESS,
@@ -1987,6 +2051,12 @@ static int parse_isolate(struct context *, const struct token *,
 static int parse_configure(struct context *, const struct token *,
 			   const char *, unsigned int,
 			   void *, unsigned int);
+static int parse_template(struct context *, const struct token *,
+			  const char *, unsigned int,
+			  void *, unsigned int);
+static int parse_template_destroy(struct context *, const struct token *,
+				  const char *, unsigned int,
+				  void *, unsigned int);
 static int parse_tunnel(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
@@ -2056,6 +2126,10 @@ static int comp_set_modify_field_op(struct context *, const struct token *,
 			      unsigned int, char *, unsigned int);
 static int comp_set_modify_field_id(struct context *, const struct token *,
 			      unsigned int, char *, unsigned int);
+static int comp_item_template_id(struct context *, const struct token *,
+				 unsigned int, char *, unsigned int);
+static int comp_action_template_id(struct context *, const struct token *,
+				   unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2206,6 +2280,20 @@ static const struct token token_list[] = {
 		.call = parse_flex_handle,
 		.comp = comp_none,
 	},
+	[COMMON_ITEM_TEMPLATE_ID] = {
+		.name = "{item_template_id}",
+		.type = "ITEM_TEMPLATE_ID",
+		.help = "item template id",
+		.call = parse_int,
+		.comp = comp_item_template_id,
+	},
+	[COMMON_ACTION_TEMPLATE_ID] = {
+		.name = "{action_template_id}",
+		.type = "ACTION_TEMPLATE_ID",
+		.help = "action template id",
+		.call = parse_int,
+		.comp = comp_action_template_id,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
@@ -2213,6 +2301,8 @@ static const struct token token_list[] = {
 		.help = "manage ingress/egress flow rules",
 		.next = NEXT(NEXT_ENTRY
 			     (CONFIGURE,
+			      ITEM_TEMPLATE,
+			      ACTION_TEMPLATE,
 			      INDIRECT_ACTION,
 			      VALIDATE,
 			      CREATE,
@@ -2278,6 +2368,112 @@ static const struct token token_list[] = {
 					args.configure.port_attr.nb_meters)),
 	},
 	/* Top-level command. */
+	[ITEM_TEMPLATE] = {
+		.name = "item_template",
+		.type = "{command} {port_id} [{arg} [...]]",
+		.help = "manage item templates",
+		.next = NEXT(next_it_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_template,
+	},
+	/* Sub-level commands. */
+	[ITEM_TEMPLATE_CREATE] = {
+		.name = "create",
+		.help = "create item template",
+		.next = NEXT(next_it_attr),
+		.call = parse_template,
+	},
+	[ITEM_TEMPLATE_DESTROY] = {
+		.name = "destroy",
+		.help = "destroy item template",
+		.next = NEXT(NEXT_ENTRY(ITEM_TEMPLATE_DESTROY_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_template_destroy,
+	},
+	/* Item arguments. */
+	[ITEM_TEMPLATE_CREATE_ID] = {
+		.name = "item_template_id",
+		.help = "specify a item template id to create",
+		.next = NEXT(next_it_attr,
+			     NEXT_ENTRY(COMMON_ITEM_TEMPLATE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.it_id)),
+	},
+	[ITEM_TEMPLATE_DESTROY_ID] = {
+		.name = "item_template",
+		.help = "specify an item template id to destroy",
+		.next = NEXT(next_it_destroy_attr,
+			     NEXT_ENTRY(COMMON_ITEM_TEMPLATE_ID)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+					    args.templ_destroy.template_id)),
+		.call = parse_template_destroy,
+	},
+	[ITEM_TEMPLATE_RELAXED_MATCHING] = {
+		.name = "relaxed",
+		.help = "is matching relaxed",
+		.next = NEXT(next_it_attr,
+			     NEXT_ENTRY(COMMON_BOOLEAN)),
+		.args = ARGS(ARGS_ENTRY_BF(struct buffer,
+			     args.vc.attr.reserved, 1)),
+	},
+	[ITEM_TEMPLATE_SPEC] = {
+		.name = "template",
+		.help = "specify item to create item template",
+		.next = NEXT(next_item),
+	},
+	/* Top-level command. */
+	[ACTION_TEMPLATE] = {
+		.name = "action_template",
+		.type = "{command} {port_id} [{arg} [...]]",
+		.help = "manage action templates",
+		.next = NEXT(next_at_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_template,
+	},
+	/* Sub-level commands. */
+	[ACTION_TEMPLATE_CREATE] = {
+		.name = "create",
+		.help = "create action template",
+		.next = NEXT(next_at_attr),
+		.call = parse_template,
+	},
+	[ACTION_TEMPLATE_DESTROY] = {
+		.name = "destroy",
+		.help = "destroy action template",
+		.next = NEXT(NEXT_ENTRY(ACTION_TEMPLATE_DESTROY_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_template_destroy,
+	},
+	/* Action  arguments. */
+	[ACTION_TEMPLATE_CREATE_ID] = {
+		.name = "action_template_id",
+		.help = "specify a action template id to create",
+		.next = NEXT(NEXT_ENTRY(ACTION_TEMPLATE_MASK),
+			     NEXT_ENTRY(ACTION_TEMPLATE_SPEC),
+			     NEXT_ENTRY(COMMON_ACTION_TEMPLATE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.at_id)),
+	},
+	[ACTION_TEMPLATE_DESTROY_ID] = {
+		.name = "action_template",
+		.help = "specify an action template id to destroy",
+		.next = NEXT(next_at_destroy_attr,
+			     NEXT_ENTRY(COMMON_ACTION_TEMPLATE_ID)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+					    args.templ_destroy.template_id)),
+		.call = parse_template_destroy,
+	},
+	[ACTION_TEMPLATE_SPEC] = {
+		.name = "template",
+		.help = "specify action to create action template",
+		.next = NEXT(next_action),
+		.call = parse_template,
+	},
+	[ACTION_TEMPLATE_MASK] = {
+		.name = "mask",
+		.help = "specify action mask to create action template",
+		.next = NEXT(next_action),
+		.call = parse_template,
+	},
+	/* Top-level command. */
 	[INDIRECT_ACTION] = {
 		.name = "indirect_action",
 		.type = "{command} {port_id} [{arg} [...]]",
@@ -2600,7 +2796,7 @@ static const struct token token_list[] = {
 		.name = "end",
 		.help = "end list of pattern items",
 		.priv = PRIV_ITEM(END, 0),
-		.next = NEXT(NEXT_ENTRY(ACTIONS)),
+		.next = NEXT(NEXT_ENTRY(ACTIONS, END)),
 		.call = parse_vc,
 	},
 	[ITEM_VOID] = {
@@ -5704,7 +5900,9 @@ parse_vc(struct context *ctx, const struct token *token,
 	if (!out)
 		return len;
 	if (!out->command) {
-		if (ctx->curr != VALIDATE && ctx->curr != CREATE)
+		if (ctx->curr != VALIDATE && ctx->curr != CREATE &&
+		    ctx->curr != ITEM_TEMPLATE_CREATE &&
+		    ctx->curr != ACTION_TEMPLATE_CREATE)
 			return -1;
 		if (sizeof(*out) > size)
 			return -1;
@@ -7568,6 +7766,114 @@ parse_configure(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for template create command. */
+static int
+parse_template(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 != ITEM_TEMPLATE &&
+		    ctx->curr != ACTION_TEMPLATE)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.vc.data = (uint8_t *)out + size;
+		return len;
+	}
+	switch (ctx->curr) {
+	case ITEM_TEMPLATE_CREATE:
+		out->args.vc.pattern =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		out->args.vc.it_id = UINT32_MAX;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		return len;
+	case ACTION_TEMPLATE_CREATE:
+		out->args.vc.at_id = UINT32_MAX;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		return len;
+	case ACTION_TEMPLATE_SPEC:
+		out->args.vc.actions =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		ctx->object = out->args.vc.actions;
+		ctx->objmask = NULL;
+		return len;
+	case ACTION_TEMPLATE_MASK:
+		out->args.vc.masks =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)
+					       (out->args.vc.actions +
+						out->args.vc.actions_n),
+					       sizeof(double));
+		ctx->object = out->args.vc.masks;
+		ctx->objmask = NULL;
+		return len;
+	default:
+		return -1;
+	}
+}
+
+/** Parse tokens for template destroy command. */
+static int
+parse_template_destroy(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len,
+		       void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	uint32_t *template_id;
+
+	/* 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 ||
+		out->command == ITEM_TEMPLATE ||
+		out->command == ACTION_TEMPLATE) {
+		if (ctx->curr != ITEM_TEMPLATE_DESTROY &&
+			ctx->curr != ACTION_TEMPLATE_DESTROY)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.templ_destroy.template_id =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		return len;
+	}
+	template_id = out->args.templ_destroy.template_id
+		    + out->args.templ_destroy.template_id_n++;
+	if ((uint8_t *)template_id > (uint8_t *)out + size)
+		return -1;
+	ctx->objdata = 0;
+	ctx->object = template_id;
+	ctx->objmask = NULL;
+	return len;
+}
+
 static int
 parse_flex(struct context *ctx, const struct token *token,
 	     const char *str, unsigned int len,
@@ -8535,6 +8841,54 @@ comp_set_modify_field_id(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/** Complete available item template IDs. */
+static int
+comp_item_template_id(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i = 0;
+	struct rte_port *port;
+	struct port_template *pt;
+
+	(void)token;
+	if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+	    ctx->port == (portid_t)RTE_PORT_ALL)
+		return -1;
+	port = &ports[ctx->port];
+	for (pt = port->item_templ_list; pt != NULL; pt = pt->next) {
+		if (buf && i == ent)
+			return snprintf(buf, size, "%u", pt->id);
+		++i;
+	}
+	if (buf)
+		return -1;
+	return i;
+}
+
+/** Complete available iaction template IDs. */
+static int
+comp_action_template_id(struct context *ctx, const struct token *token,
+			unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i = 0;
+	struct rte_port *port;
+	struct port_template *pt;
+
+	(void)token;
+	if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+	    ctx->port == (portid_t)RTE_PORT_ALL)
+		return -1;
+	port = &ports[ctx->port];
+	for (pt = port->action_templ_list; pt != NULL; pt = pt->next) {
+		if (buf && i == ent)
+			return snprintf(buf, size, "%u", pt->id);
+		++i;
+	}
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -8798,6 +9152,24 @@ cmd_flow_parsed(const struct buffer *in)
 		port_flow_configure(in->port, &in->args.configure.port_attr,
 				    &in->args.configure.queue_attr);
 		break;
+	case ITEM_TEMPLATE_CREATE:
+		port_flow_item_template_create(in->port, in->args.vc.it_id,
+				in->args.vc.attr.reserved, in->args.vc.pattern);
+		break;
+	case ITEM_TEMPLATE_DESTROY:
+		port_flow_item_template_destroy(in->port,
+				in->args.templ_destroy.template_id_n,
+				in->args.templ_destroy.template_id);
+		break;
+	case ACTION_TEMPLATE_CREATE:
+		port_flow_action_template_create(in->port, in->args.vc.at_id,
+				in->args.vc.actions, in->args.vc.masks);
+		break;
+	case ACTION_TEMPLATE_DESTROY:
+		port_flow_action_template_destroy(in->port,
+				in->args.templ_destroy.template_id_n,
+				in->args.templ_destroy.template_id);
+		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
 				in->port, in->args.vc.attr.group,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 85d31de7f7..80678d851f 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1595,6 +1595,49 @@ action_alloc(portid_t port_id, uint32_t id,
 	return 0;
 }
 
+static int
+template_alloc(uint32_t id, struct port_template **template,
+	       struct port_template **list)
+{
+	struct port_template *lst = *list;
+	struct port_template **ppt;
+	struct port_template *pt = NULL;
+
+	*template = NULL;
+	if (id == UINT32_MAX) {
+		/* taking first available ID */
+		if (lst) {
+			if (lst->id == UINT32_MAX - 1) {
+				printf("Highest item template ID is already"
+				" assigned, delete it first\n");
+				return -ENOMEM;
+			}
+			id = lst->id + 1;
+		} else {
+			id = 0;
+		}
+	}
+	pt = calloc(1, sizeof(*pt));
+	if (!pt) {
+		printf("Allocation of port template failed\n");
+		return -ENOMEM;
+	}
+	ppt = list;
+	while (*ppt && (*ppt)->id > id)
+		ppt = &(*ppt)->next;
+	if (*ppt && (*ppt)->id == id) {
+		printf("Template #%u is already assigned,"
+			" delete it first\n", id);
+		free(pt);
+		return -EINVAL;
+	}
+	pt->next = *ppt;
+	pt->id = id;
+	*ppt = pt;
+	*template = pt;
+	return 0;
+}
+
 /** Configure flow management resources. */
 int
 port_flow_configure(portid_t port_id,
@@ -2039,6 +2082,167 @@ age_action_get(const struct rte_flow_action *actions)
 	return NULL;
 }
 
+/** Create item template */
+int
+port_flow_item_template_create(portid_t port_id, uint32_t id, bool relaxed,
+			       const struct rte_flow_item *pattern)
+{
+	struct rte_port *port;
+	struct port_template *pit;
+	int ret;
+	struct rte_flow_item_template_attr attr = {
+					.relaxed_matching = relaxed };
+	struct rte_flow_error error;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	ret = template_alloc(id, &pit, &port->item_templ_list);
+	if (ret)
+		return ret;
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x22, sizeof(error));
+	pit->template.itempl = rte_flow_item_template_create(port_id,
+						&attr, pattern, &error);
+	if (!pit->template.itempl) {
+		uint32_t destroy_id = pit->id;
+		port_flow_item_template_destroy(port_id, 1, &destroy_id);
+		return port_flow_complain(&error);
+	}
+	printf("Item template #%u created\n", pit->id);
+	return 0;
+}
+
+/** Destroy item template */
+int
+port_flow_item_template_destroy(portid_t port_id, uint32_t n,
+				const uint32_t *template)
+{
+	struct rte_port *port;
+	struct port_template **tmp;
+	uint32_t c = 0;
+	int ret = 0;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	tmp = &port->item_templ_list;
+	while (*tmp) {
+		uint32_t i;
+
+		for (i = 0; i != n; ++i) {
+			struct rte_flow_error error;
+			struct port_template *pit = *tmp;
+
+			if (template[i] != pit->id)
+				continue;
+			/*
+			 * Poisoning to make sure PMDs update it in case
+			 * of error.
+			 */
+			memset(&error, 0x33, sizeof(error));
+
+			if (pit->template.itempl &&
+			    rte_flow_item_template_destroy(port_id,
+							   pit->template.itempl,
+							   &error)) {
+				ret = port_flow_complain(&error);
+				continue;
+			}
+			*tmp = pit->next;
+			printf("Item template #%u destroyed\n", pit->id);
+			free(pit);
+			break;
+		}
+		if (i == n)
+			tmp = &(*tmp)->next;
+		++c;
+	}
+	return ret;
+}
+
+/** Create action template */
+int
+port_flow_action_template_create(portid_t port_id, uint32_t id,
+				 const struct rte_flow_action *actions,
+				 const struct rte_flow_action *masks)
+{
+	struct rte_port *port;
+	struct port_template *pat;
+	int ret;
+	struct rte_flow_action_template_attr attr = { 0 };
+	struct rte_flow_error error;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	ret = template_alloc(id, &pat, &port->action_templ_list);
+	if (ret)
+		return ret;
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x22, sizeof(error));
+	pat->template.atempl = rte_flow_action_template_create(port_id,
+						&attr, actions, masks, &error);
+	if (!pat->template.atempl) {
+		uint32_t destroy_id = pat->id;
+		port_flow_action_template_destroy(port_id, 1, &destroy_id);
+		return port_flow_complain(&error);
+	}
+	printf("Action template #%u created\n", pat->id);
+	return 0;
+}
+
+/** Destroy action template */
+int
+port_flow_action_template_destroy(portid_t port_id, uint32_t n,
+				  const uint32_t *template)
+{
+	struct rte_port *port;
+	struct port_template **tmp;
+	uint32_t c = 0;
+	int ret = 0;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	tmp = &port->action_templ_list;
+	while (*tmp) {
+		uint32_t i;
+
+		for (i = 0; i != n; ++i) {
+			struct rte_flow_error error;
+			struct port_template *pat = *tmp;
+
+			if (template[i] != pat->id)
+				continue;
+			/*
+			 * Poisoning to make sure PMDs update it in case
+			 * of error.
+			 */
+			memset(&error, 0x33, sizeof(error));
+
+			if (pat->template.atempl &&
+			    rte_flow_action_template_destroy(port_id,
+					pat->template.atempl, &error)) {
+				ret = port_flow_complain(&error);
+				continue;
+			}
+			*tmp = pat->next;
+			printf("Action template #%u destroyed\n", pat->id);
+			free(pat);
+			break;
+		}
+		if (i == n)
+			tmp = &(*tmp)->next;
+		++c;
+	}
+	return ret;
+}
+
 /** Create flow rule. */
 int
 port_flow_create(portid_t port_id,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index ce80a00193..4befa6d7a4 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -166,6 +166,17 @@ enum age_action_context_type {
 	ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION,
 };
 
+/** Descriptor for a template. */
+struct port_template {
+	struct port_template *next; /**< Next template in list. */
+	struct port_template *tmp; /**< Temporary linking. */
+	uint32_t id; /**< Template ID. */
+	union {
+		struct rte_flow_item_template *itempl;
+		struct rte_flow_action_template *atempl;
+	} template; /**< PMD opaque template object */
+};
+
 /** Descriptor for a single flow. */
 struct port_flow {
 	struct port_flow *next; /**< Next flow in list. */
@@ -246,6 +257,8 @@ struct rte_port {
 	queueid_t               queue_nb; /**< nb. of queues for flow rules */
 	uint32_t                queue_sz; /**< size of a queue for flow rules */
 	uint8_t                 slave_flag; /**< bonding slave port */
+	struct port_template    *item_templ_list; /**< Item templates. */
+	struct port_template    *action_templ_list; /**< Action templates. */
 	struct port_flow        *flow_list; /**< Associated flows. */
 	struct port_indirect_action *actions_list;
 	/**< Associated indirect actions. */
@@ -890,6 +903,15 @@ int port_action_handle_update(portid_t port_id, uint32_t id,
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
 			const struct rte_flow_queue_attr *queue_attr);
+int port_flow_item_template_create(portid_t port_id, uint32_t id, bool relaxed,
+				   const struct rte_flow_item *pattern);
+int port_flow_item_template_destroy(portid_t port_id, uint32_t n,
+				    const uint32_t *template);
+int port_flow_action_template_create(portid_t port_id, uint32_t id,
+				     const struct rte_flow_action *actions,
+				     const struct rte_flow_action *masks);
+int port_flow_action_template_destroy(portid_t port_id, uint32_t n,
+				      const uint32_t *template);
 int port_flow_validate(portid_t port_id,
 		       const struct rte_flow_attr *attr,
 		       const struct rte_flow_item *pattern,
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 8af28bd3b3..d23cfa6572 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -3317,6 +3317,24 @@ following sections.
        [aging_counters_number {number}]
        [meters_number {number}]
 
+- Create an item template::
+   flow item_template {port_id} create [item_template_id {id}]
+       [relaxed {boolean}] template {item} [/ {item} [...]] / end
+
+- Destroy an item template::
+
+   flow item_template {port_id} destroy item_template {id} [...]
+
+- Create an action template::
+
+   flow action_template {port_id} create [action_template_id {id}]
+       template {action} [/ {action} [...]] / end
+       mask {action} [/ {action} [...]] / end
+
+- Destroy an action template::
+
+   flow action_template {port_id} destroy action_template {id} [...]
+
 - Check whether a flow rule can be created::
 
    flow validate {port_id}
@@ -3398,6 +3416,85 @@ Otherwise it will show an error message of the form::
 
    Caught error type [...] ([...]): [...]
 
+Creating item templates
+~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow item_template create`` creates the specified item template.
+It is bound to ``rte_flow_item_template_create()``::
+
+   flow item_template {port_id} create [item_template_id {id}]
+       [relaxed {boolean}] template {item} [/ {item} [...]] / end
+
+If successful, it will show::
+
+   Item template #[...] created
+
+Otherwise it will show an error message of the form::
+
+   Caught error type [...] ([...]): [...]
+
+This command uses the same pattern items as ``flow create``,
+their format is described in `Creating flow rules`_.
+
+Destroying item templates
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow item_template destroy`` destroys one or more item templates
+from their template ID (as returned by ``flow item_template create``),
+this command calls ``rte_flow_item_template_destroy()`` as many
+times as necessary::
+
+   flow item_template {port_id} destroy item_template {id} [...]
+
+If successful, it will show::
+
+   Item template #[...] destroyed
+
+It does not report anything for item template IDs that do not exist.
+The usual error message is shown when an item template cannot be destroyed::
+
+   Caught error type [...] ([...]): [...]
+
+Creating action templates
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow action_template create`` creates the specified action template.
+It is bound to ``rte_flow_action_template_create()``::
+
+   flow action_template {port_id} create [action_template_id {id}]
+       template {action} [/ {action} [...]] / end
+       mask {action} [/ {action} [...]] / end
+
+If successful, it will show::
+
+   Action template #[...] created
+
+Otherwise it will show an error message of the form::
+
+   Caught error type [...] ([...]): [...]
+
+This command uses the same actions as ``flow create``,
+their format is described in `Creating flow rules`_.
+
+Destroying action templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow action_template destroy`` destroys one or more action templates
+from their template ID (as returned by ``flow action_template create``),
+this command calls ``rte_flow_action_template_destroy()`` as many
+times as necessary::
+
+   flow action_template {port_id} destroy action_template {id} [...]
+
+If successful, it will show::
+
+   Action template #[...] destroyed
+
+It does not report anything for item template IDs that do not exist.
+The usual error message is shown when an item template cannot be destroyed::
+
+   Caught error type [...] ([...]): [...]
+
 Creating a tunnel stub for offload
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.18.2


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

* [PATCH 06/10] app/testpmd: implement rte flow table
  2022-01-18  5:07 [PATCH 05/10] app/testpmd: implement rte flow item/action template Alexander Kozyrev
@ 2022-01-18  5:07 ` Alexander Kozyrev
  2022-01-18  5:07 ` [PATCH 07/10] app/testpmd: implement rte flow queue create flow Alexander Kozyrev
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Alexander Kozyrev @ 2022-01-18  5:07 UTC (permalink / raw)
  To: dev
  Cc: orika, thomas, ivan.malov, andrew.rybchenko, ferruh.yigit,
	mohammad.abdul.awal, qi.z.zhang, jerinj, ajit.khaparde

Add testpmd support for the rte_flow_table API.
Provide the command line interface for the flow
table creation/destruction. Usage example:
  testpmd> flow table 0 create table_id 6
    group 9 priority 4 ingress mode 1
    rules_number 64 item_template 2 action_template 4
  testpmd> flow table 0 destroy table 6

Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
---
 app/test-pmd/cmdline_flow.c                 | 315 ++++++++++++++++++++
 app/test-pmd/config.c                       | 168 +++++++++++
 app/test-pmd/testpmd.h                      |  15 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  53 ++++
 4 files changed, 551 insertions(+)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index fb27a97855..4dc2a2aaeb 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -58,6 +58,7 @@ enum index {
 	COMMON_FLEX_TOKEN,
 	COMMON_ITEM_TEMPLATE_ID,
 	COMMON_ACTION_TEMPLATE_ID,
+	COMMON_TABLE_ID,
 
 	/* TOP-level command. */
 	ADD,
@@ -77,6 +78,7 @@ enum index {
 	CONFIGURE,
 	ITEM_TEMPLATE,
 	ACTION_TEMPLATE,
+	TABLE,
 	INDIRECT_ACTION,
 	VALIDATE,
 	CREATE,
@@ -111,6 +113,20 @@ enum index {
 	ACTION_TEMPLATE_SPEC,
 	ACTION_TEMPLATE_MASK,
 
+	/* Table arguments. */
+	TABLE_CREATE,
+	TABLE_DESTROY,
+	TABLE_CREATE_ID,
+	TABLE_DESTROY_ID,
+	TABLE_GROUP,
+	TABLE_PRIORITY,
+	TABLE_INGRESS,
+	TABLE_EGRESS,
+	TABLE_TRANSFER,
+	TABLE_RULES_NUMBER,
+	TABLE_ITEM_TEMPLATE,
+	TABLE_ACTION_TEMPLATE,
+
 	/* Tunnel arguments. */
 	TUNNEL_CREATE,
 	TUNNEL_CREATE_TYPE,
@@ -882,6 +898,18 @@ struct buffer {
 			uint32_t *template_id;
 			uint32_t template_id_n;
 		} templ_destroy; /**< Template destroy arguments. */
+		struct {
+			uint32_t id;
+			struct rte_flow_table_attr attr;
+			uint32_t *item_id;
+			uint32_t item_id_n;
+			uint32_t *action_id;
+			uint32_t action_id_n;
+		} table; /**< Table arguments. */
+		struct {
+			uint32_t *table_id;
+			uint32_t table_id_n;
+		} table_destroy; /**< Template destroy arguments. */
 		struct {
 			uint32_t *action_id;
 			uint32_t action_id_n;
@@ -1013,6 +1041,32 @@ static const enum index next_at_destroy_attr[] = {
 	ZERO,
 };
 
+static const enum index next_table_subcmd[] = {
+	TABLE_CREATE,
+	TABLE_DESTROY,
+	ZERO,
+};
+
+static const enum index next_table_attr[] = {
+	TABLE_CREATE_ID,
+	TABLE_GROUP,
+	TABLE_PRIORITY,
+	TABLE_INGRESS,
+	TABLE_EGRESS,
+	TABLE_TRANSFER,
+	TABLE_RULES_NUMBER,
+	TABLE_ITEM_TEMPLATE,
+	TABLE_ACTION_TEMPLATE,
+	END,
+	ZERO,
+};
+
+static const enum index next_table_destroy_attr[] = {
+	TABLE_DESTROY_ID,
+	END,
+	ZERO,
+};
+
 static const enum index next_ia_create_attr[] = {
 	INDIRECT_ACTION_CREATE_ID,
 	INDIRECT_ACTION_INGRESS,
@@ -2057,6 +2111,11 @@ static int parse_template(struct context *, const struct token *,
 static int parse_template_destroy(struct context *, const struct token *,
 				  const char *, unsigned int,
 				  void *, unsigned int);
+static int parse_table(struct context *, const struct token *,
+		       const char *, unsigned int, void *, unsigned int);
+static int parse_table_destroy(struct context *, const struct token *,
+			       const char *, unsigned int,
+			       void *, unsigned int);
 static int parse_tunnel(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
@@ -2130,6 +2189,8 @@ static int comp_item_template_id(struct context *, const struct token *,
 				 unsigned int, char *, unsigned int);
 static int comp_action_template_id(struct context *, const struct token *,
 				   unsigned int, char *, unsigned int);
+static int comp_table_id(struct context *, const struct token *,
+			 unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2294,6 +2355,13 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_action_template_id,
 	},
+	[COMMON_TABLE_ID] = {
+		.name = "{table_id}",
+		.type = "TABLE_ID",
+		.help = "table id",
+		.call = parse_int,
+		.comp = comp_table_id,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
@@ -2303,6 +2371,7 @@ static const struct token token_list[] = {
 			     (CONFIGURE,
 			      ITEM_TEMPLATE,
 			      ACTION_TEMPLATE,
+			      TABLE,
 			      INDIRECT_ACTION,
 			      VALIDATE,
 			      CREATE,
@@ -2474,6 +2543,104 @@ static const struct token token_list[] = {
 		.call = parse_template,
 	},
 	/* Top-level command. */
+	[TABLE] = {
+		.name = "table",
+		.type = "{command} {port_id} [{arg} [...]]",
+		.help = "manage tables",
+		.next = NEXT(next_table_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_table,
+	},
+	/* Sub-level commands. */
+	[TABLE_CREATE] = {
+		.name = "create",
+		.help = "create table",
+		.next = NEXT(next_table_attr),
+		.call = parse_table,
+	},
+	[TABLE_DESTROY] = {
+		.name = "destroy",
+		.help = "destroy table",
+		.next = NEXT(NEXT_ENTRY(TABLE_DESTROY_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_table_destroy,
+	},
+	/* Table  arguments. */
+	[TABLE_CREATE_ID] = {
+		.name = "table_id",
+		.help = "specify table id to create",
+		.next = NEXT(next_table_attr,
+			     NEXT_ENTRY(COMMON_TABLE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.table.id)),
+	},
+	[TABLE_DESTROY_ID] = {
+		.name = "table",
+		.help = "specify table id to destroy",
+		.next = NEXT(next_table_destroy_attr,
+			     NEXT_ENTRY(COMMON_TABLE_ID)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+					    args.table_destroy.table_id)),
+		.call = parse_table_destroy,
+	},
+	[TABLE_GROUP] = {
+		.name = "group",
+		.help = "specify a group",
+		.next = NEXT(next_table_attr, NEXT_ENTRY(COMMON_GROUP_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+					args.table.attr.flow_attr.group)),
+	},
+	[TABLE_PRIORITY] = {
+		.name = "priority",
+		.help = "specify a priority level",
+		.next = NEXT(next_table_attr, NEXT_ENTRY(COMMON_PRIORITY_LEVEL)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+					args.table.attr.flow_attr.priority)),
+	},
+	[TABLE_EGRESS] = {
+		.name = "egress",
+		.help = "affect rule to egress",
+		.next = NEXT(next_table_attr),
+		.call = parse_table,
+	},
+	[TABLE_INGRESS] = {
+		.name = "ingress",
+		.help = "affect rule to ingress",
+		.next = NEXT(next_table_attr),
+		.call = parse_table,
+	},
+	[TABLE_TRANSFER] = {
+		.name = "transfer",
+		.help = "affect rule to transfer",
+		.next = NEXT(next_table_attr),
+		.call = parse_table,
+	},
+	[TABLE_RULES_NUMBER] = {
+		.name = "rules_number",
+		.help = "number of rules in table",
+		.next = NEXT(next_table_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+					args.table.attr.nb_flows)),
+	},
+	[TABLE_ITEM_TEMPLATE] = {
+		.name = "item_template",
+		.help = "specify item template id",
+		.next = NEXT(next_table_attr,
+			     NEXT_ENTRY(COMMON_ITEM_TEMPLATE_ID)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+					    args.table.item_id)),
+		.call = parse_table,
+	},
+	[TABLE_ACTION_TEMPLATE] = {
+		.name = "action_template",
+		.help = "specify action template id",
+		.next = NEXT(next_table_attr,
+			     NEXT_ENTRY(COMMON_ACTION_TEMPLATE_ID)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+					    args.table.action_id)),
+		.call = parse_table,
+	},
+	/* Top-level command. */
 	[INDIRECT_ACTION] = {
 		.name = "indirect_action",
 		.type = "{command} {port_id} [{arg} [...]]",
@@ -7874,6 +8041,119 @@ parse_template_destroy(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for indirect action commands. */
+static int
+parse_table(struct context *ctx, const struct token *token,
+	    const char *str, unsigned int len,
+	    void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	uint32_t *template_id;
+
+	/* 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 != TABLE)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		return len;
+	}
+	switch (ctx->curr) {
+	case TABLE_CREATE:
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.table.id = UINT32_MAX;
+		return len;
+	case TABLE_ITEM_TEMPLATE:
+		out->args.table.item_id =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		template_id = out->args.table.item_id
+				+ out->args.table.item_id_n++;
+		if ((uint8_t *)template_id > (uint8_t *)out + size)
+			return -1;
+		ctx->objdata = 0;
+		ctx->object = template_id;
+		ctx->objmask = NULL;
+		return len;
+	case TABLE_ACTION_TEMPLATE:
+		out->args.table.action_id =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)
+					       (out->args.table.item_id +
+						out->args.table.item_id_n),
+					       sizeof(double));
+		template_id = out->args.table.action_id
+				+ out->args.table.action_id_n++;
+		if ((uint8_t *)template_id > (uint8_t *)out + size)
+			return -1;
+		ctx->objdata = 0;
+		ctx->object = template_id;
+		ctx->objmask = NULL;
+		return len;
+	case TABLE_INGRESS:
+		out->args.table.attr.flow_attr.ingress = 1;
+		return len;
+	case TABLE_EGRESS:
+		out->args.table.attr.flow_attr.egress = 1;
+		return len;
+	case TABLE_TRANSFER:
+		out->args.table.attr.flow_attr.transfer = 1;
+		return len;
+	default:
+		return -1;
+	}
+}
+
+/** Parse tokens for indirect action destroy command. */
+static int
+parse_table_destroy(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len,
+		    void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	uint32_t *table_id;
+
+	/* 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 || out->command == TABLE) {
+		if (ctx->curr != TABLE_DESTROY)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.table_destroy.table_id =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		return len;
+	}
+	table_id = out->args.table_destroy.table_id
+		    + out->args.table_destroy.table_id_n++;
+	if ((uint8_t *)table_id > (uint8_t *)out + size)
+		return -1;
+	ctx->objdata = 0;
+	ctx->object = table_id;
+	ctx->objmask = NULL;
+	return len;
+}
+
 static int
 parse_flex(struct context *ctx, const struct token *token,
 	     const char *str, unsigned int len,
@@ -8889,6 +9169,30 @@ comp_action_template_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+/** Complete available table IDs. */
+static int
+comp_table_id(struct context *ctx, const struct token *token,
+	      unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i = 0;
+	struct rte_port *port;
+	struct port_table *pt;
+
+	(void)token;
+	if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+	    ctx->port == (portid_t)RTE_PORT_ALL)
+		return -1;
+	port = &ports[ctx->port];
+	for (pt = port->table_list; pt != NULL; pt = pt->next) {
+		if (buf && i == ent)
+			return snprintf(buf, size, "%u", pt->id);
+		++i;
+	}
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -9170,6 +9474,17 @@ cmd_flow_parsed(const struct buffer *in)
 				in->args.templ_destroy.template_id_n,
 				in->args.templ_destroy.template_id);
 		break;
+	case TABLE_CREATE:
+		port_flow_table_create(in->port, in->args.table.id,
+			&in->args.table.attr, in->args.table.item_id_n,
+			in->args.table.item_id, in->args.table.action_id_n,
+			in->args.table.action_id);
+		break;
+	case TABLE_DESTROY:
+		port_flow_table_destroy(in->port,
+					in->args.table_destroy.table_id_n,
+					in->args.table_destroy.table_id);
+		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
 				in->port, in->args.vc.attr.group,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 80678d851f..07582fa552 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1638,6 +1638,49 @@ template_alloc(uint32_t id, struct port_template **template,
 	return 0;
 }
 
+static int
+table_alloc(uint32_t id, struct port_table **table,
+	    struct port_table **list)
+{
+	struct port_table *lst = *list;
+	struct port_table **ppt;
+	struct port_table *pt = NULL;
+
+	*table = NULL;
+	if (id == UINT32_MAX) {
+		/* taking first available ID */
+		if (lst) {
+			if (lst->id == UINT32_MAX - 1) {
+				printf("Highest table ID is already"
+				" assigned, delete it first\n");
+				return -ENOMEM;
+			}
+			id = lst->id + 1;
+		} else {
+			id = 0;
+		}
+	}
+	pt = calloc(1, sizeof(*pt));
+	if (!pt) {
+		printf("Allocation of table failed\n");
+		return -ENOMEM;
+	}
+	ppt = list;
+	while (*ppt && (*ppt)->id > id)
+		ppt = &(*ppt)->next;
+	if (*ppt && (*ppt)->id == id) {
+		printf("Table #%u is already assigned,"
+			" delete it first\n", id);
+		free(pt);
+		return -EINVAL;
+	}
+	pt->next = *ppt;
+	pt->id = id;
+	*ppt = pt;
+	*table = pt;
+	return 0;
+}
+
 /** Configure flow management resources. */
 int
 port_flow_configure(portid_t port_id,
@@ -2243,6 +2286,131 @@ port_flow_action_template_destroy(portid_t port_id, uint32_t n,
 	return ret;
 }
 
+/** Create table */
+int
+port_flow_table_create(portid_t port_id, uint32_t id,
+		       const struct rte_flow_table_attr *table_attr,
+		       uint32_t nb_item_templates, uint32_t *item_templates,
+		       uint32_t nb_action_templates, uint32_t *action_templates)
+{
+	struct rte_port *port;
+	struct port_table *pt;
+	struct port_template *temp = NULL;
+	int ret;
+	uint32_t i;
+	struct rte_flow_error error;
+	struct rte_flow_item_template
+			*flow_item_templates[nb_item_templates];
+	struct rte_flow_action_template
+			*flow_action_templates[nb_action_templates];
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	for (i = 0; i < nb_item_templates; ++i) {
+		bool found = false;
+		temp = port->item_templ_list;
+		while (temp) {
+			if (item_templates[i] == temp->id) {
+				flow_item_templates[i] = temp->template.itempl;
+				found = true;
+				break;
+			}
+			temp = temp->next;
+		}
+		if (!found) {
+			printf("Item template #%u is invalid\n",
+			       item_templates[i]);
+			return -EINVAL;
+		}
+	}
+	for (i = 0; i < nb_action_templates; ++i) {
+		bool found = false;
+		temp = port->action_templ_list;
+		while (temp) {
+			if (action_templates[i] == temp->id) {
+				flow_action_templates[i] =
+					temp->template.atempl;
+				found = true;
+				break;
+			}
+			temp = temp->next;
+		}
+		if (!found) {
+			printf("Action template #%u is invalid\n",
+			       action_templates[i]);
+			return -EINVAL;
+		}
+	}
+	ret = table_alloc(id, &pt, &port->table_list);
+	if (ret)
+		return ret;
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x22, sizeof(error));
+	pt->table = rte_flow_table_create(port_id, table_attr,
+		      flow_item_templates, nb_item_templates,
+		      flow_action_templates, nb_action_templates,
+		      &error);
+
+	if (!pt->table) {
+		uint32_t destroy_id = pt->id;
+		port_flow_table_destroy(port_id, 1, &destroy_id);
+		return port_flow_complain(&error);
+	}
+	printf("Table #%u created\n", pt->id);
+	return 0;
+}
+
+/** Destroy table */
+int
+port_flow_table_destroy(portid_t port_id,
+			uint32_t n, const uint32_t *table)
+{
+	struct rte_port *port;
+	struct port_table **tmp;
+	uint32_t c = 0;
+	int ret = 0;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	tmp = &port->table_list;
+	while (*tmp) {
+		uint32_t i;
+
+		for (i = 0; i != n; ++i) {
+			struct rte_flow_error error;
+			struct port_table *pt = *tmp;
+
+			if (table[i] != pt->id)
+				continue;
+			/*
+			 * Poisoning to make sure PMDs update it in case
+			 * of error.
+			 */
+			memset(&error, 0x33, sizeof(error));
+
+			if (pt->table &&
+			    rte_flow_table_destroy(port_id,
+						   pt->table,
+						   &error)) {
+				ret = port_flow_complain(&error);
+				continue;
+			}
+			*tmp = pt->next;
+			printf("Table #%u destroyed\n", pt->id);
+			free(pt);
+			break;
+		}
+		if (i == n)
+			tmp = &(*tmp)->next;
+		++c;
+	}
+	return ret;
+}
+
 /** Create flow rule. */
 int
 port_flow_create(portid_t port_id,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 4befa6d7a4..b8655b9987 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -177,6 +177,14 @@ struct port_template {
 	} template; /**< PMD opaque template object */
 };
 
+/** Descriptor for a flow table. */
+struct port_table {
+	struct port_table *next; /**< Next table in list. */
+	struct port_table *tmp; /**< Temporary linking. */
+	uint32_t id; /**< Table ID. */
+	struct rte_flow_table *table; /**< PMD opaque template object */
+};
+
 /** Descriptor for a single flow. */
 struct port_flow {
 	struct port_flow *next; /**< Next flow in list. */
@@ -259,6 +267,7 @@ struct rte_port {
 	uint8_t                 slave_flag; /**< bonding slave port */
 	struct port_template    *item_templ_list; /**< Item templates. */
 	struct port_template    *action_templ_list; /**< Action templates. */
+	struct port_table       *table_list; /**< Flow tables. */
 	struct port_flow        *flow_list; /**< Associated flows. */
 	struct port_indirect_action *actions_list;
 	/**< Associated indirect actions. */
@@ -912,6 +921,12 @@ int port_flow_action_template_create(portid_t port_id, uint32_t id,
 				     const struct rte_flow_action *masks);
 int port_flow_action_template_destroy(portid_t port_id, uint32_t n,
 				      const uint32_t *template);
+int port_flow_table_create(portid_t port_id, uint32_t id,
+		   const struct rte_flow_table_attr *table_attr,
+		   uint32_t nb_item_templates, uint32_t *item_templates,
+		   uint32_t nb_action_templates, uint32_t *action_templates);
+int port_flow_table_destroy(portid_t port_id,
+			    uint32_t n, const uint32_t *table);
 int port_flow_validate(portid_t port_id,
 		       const struct rte_flow_attr *attr,
 		       const struct rte_flow_item *pattern,
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index d23cfa6572..f8a87564be 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -3335,6 +3335,19 @@ following sections.
 
    flow action_template {port_id} destroy action_template {id} [...]
 
+- Create a table::
+
+   flow table {port_id} create
+       [table_id {id}]
+       [group {group_id}] [priority {level}] [ingress] [egress] [transfer]
+       rules_number {number}
+       item_template {item_template_id}
+       action_template {action_template_id}
+
+- Destroy a table::
+
+   flow table {port_id} destroy table {id} [...]
+
 - Check whether a flow rule can be created::
 
    flow validate {port_id}
@@ -3495,6 +3508,46 @@ The usual error message is shown when an item template cannot be destroyed::
 
    Caught error type [...] ([...]): [...]
 
+Creating flow table
+~~~~~~~~~~~~~~~~~~~
+
+``flow table create`` creates the specified flow table.
+It is bound to ``rte_flow_table_create()``::
+
+   flow table {port_id} create
+       [table_id {id}] [group {group_id}]
+	   [priority {level}] [ingress] [egress] [transfer]
+       rules_number {number}
+       item_template {item_template_id}
+       action_template {action_template_id}
+
+If successful, it will show::
+
+   Table #[...] created
+
+Otherwise it will show an error message of the form::
+
+   Caught error type [...] ([...]): [...]
+
+Destroying flow table
+~~~~~~~~~~~~~~~~~~~~~
+
+``flow table destroy`` destroys one or more flow tables
+from their table ID (as returned by ``flow table create``),
+this command calls ``rte_flow_table_destroy()`` as many
+times as necessary::
+
+   flow table {port_id} destroy table {id} [...]
+
+If successful, it will show::
+
+   Table #[...] destroyed
+
+It does not report anything for table IDs that do not exist.
+The usual error message is shown when a table cannot be destroyed::
+
+   Caught error type [...] ([...]): [...]
+
 Creating a tunnel stub for offload
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.18.2


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

* [PATCH 07/10] app/testpmd: implement rte flow queue create flow
  2022-01-18  5:07 [PATCH 05/10] app/testpmd: implement rte flow item/action template Alexander Kozyrev
  2022-01-18  5:07 ` [PATCH 06/10] app/testpmd: implement rte flow table Alexander Kozyrev
@ 2022-01-18  5:07 ` Alexander Kozyrev
  2022-01-18  5:08 ` [PATCH 08/10] app/testpmd: implement rte flow queue drain Alexander Kozyrev
  2022-01-18  5:08 ` [PATCH 09/10] app/testpmd: implement rte flow queue dequeue Alexander Kozyrev
  3 siblings, 0 replies; 5+ messages in thread
From: Alexander Kozyrev @ 2022-01-18  5:07 UTC (permalink / raw)
  To: dev
  Cc: orika, thomas, ivan.malov, andrew.rybchenko, ferruh.yigit,
	mohammad.abdul.awal, qi.z.zhang, jerinj, ajit.khaparde

Add testpmd support for the rte_flow_q_create/rte_flow_q_destroy API.
Provide the command line interface for enqueueing flow creation/destruction
operations. Usage example:
  testpmd> flow queue 0 create 0 drain yes table 6
           item_template 0 action_template 0
           pattern eth dst is 00:16:3e:31:15:c3 / end actions drop / end
  testpmd> flow queue 0 destroy 0 drain yes rule 0

Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
---
 app/test-pmd/cmdline_flow.c                 | 266 +++++++++++++++++++-
 app/test-pmd/config.c                       | 153 +++++++++++
 app/test-pmd/testpmd.h                      |   7 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  55 ++++
 4 files changed, 480 insertions(+), 1 deletion(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 4dc2a2aaeb..6a8e6fc683 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -59,6 +59,7 @@ enum index {
 	COMMON_ITEM_TEMPLATE_ID,
 	COMMON_ACTION_TEMPLATE_ID,
 	COMMON_TABLE_ID,
+	COMMON_QUEUE_ID,
 
 	/* TOP-level command. */
 	ADD,
@@ -91,6 +92,7 @@ enum index {
 	ISOLATE,
 	TUNNEL,
 	FLEX,
+	QUEUE,
 
 	/* Flex arguments */
 	FLEX_ITEM_INIT,
@@ -113,6 +115,22 @@ enum index {
 	ACTION_TEMPLATE_SPEC,
 	ACTION_TEMPLATE_MASK,
 
+	/* Queue arguments. */
+	QUEUE_CREATE,
+	QUEUE_DESTROY,
+
+	/* Queue create arguments. */
+	QUEUE_CREATE_ID,
+	QUEUE_CREATE_DRAIN,
+	QUEUE_TABLE,
+	QUEUE_ITEM_TEMPLATE,
+	QUEUE_ACTION_TEMPLATE,
+	QUEUE_SPEC,
+
+	/* Queue destroy arguments. */
+	QUEUE_DESTROY_ID,
+	QUEUE_DESTROY_DRAIN,
+
 	/* Table arguments. */
 	TABLE_CREATE,
 	TABLE_DESTROY,
@@ -889,6 +907,8 @@ struct token {
 struct buffer {
 	enum index command; /**< Flow command. */
 	portid_t port; /**< Affected port ID. */
+	queueid_t queue; /** Async queue ID. */
+	bool drain; /** Drain the queue on async oparation */
 	union {
 		struct {
 			struct rte_flow_port_attr port_attr;
@@ -918,6 +938,7 @@ struct buffer {
 			uint32_t action_id;
 		} ia; /* Indirect action query arguments */
 		struct {
+			uint32_t table_id;
 			uint32_t it_id;
 			uint32_t at_id;
 			struct rte_flow_attr attr;
@@ -1067,6 +1088,18 @@ static const enum index next_table_destroy_attr[] = {
 	ZERO,
 };
 
+static const enum index next_queue_subcmd[] = {
+	QUEUE_CREATE,
+	QUEUE_DESTROY,
+	ZERO,
+};
+
+static const enum index next_queue_destroy_attr[] = {
+	QUEUE_DESTROY_ID,
+	END,
+	ZERO,
+};
+
 static const enum index next_ia_create_attr[] = {
 	INDIRECT_ACTION_CREATE_ID,
 	INDIRECT_ACTION_INGRESS,
@@ -2116,6 +2149,12 @@ static int parse_table(struct context *, const struct token *,
 static int parse_table_destroy(struct context *, const struct token *,
 			       const char *, unsigned int,
 			       void *, unsigned int);
+static int parse_qo(struct context *, const struct token *,
+		    const char *, unsigned int,
+		    void *, unsigned int);
+static int parse_qo_destroy(struct context *, const struct token *,
+			    const char *, unsigned int,
+			    void *, unsigned int);
 static int parse_tunnel(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
@@ -2191,6 +2230,8 @@ static int comp_action_template_id(struct context *, const struct token *,
 				   unsigned int, char *, unsigned int);
 static int comp_table_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
+static int comp_queue_id(struct context *, const struct token *,
+			 unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2362,6 +2403,13 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_table_id,
 	},
+	[COMMON_QUEUE_ID] = {
+		.name = "{queue_id}",
+		.type = "QUEUE_ID",
+		.help = "queue id",
+		.call = parse_int,
+		.comp = comp_queue_id,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
@@ -2383,7 +2431,8 @@ static const struct token token_list[] = {
 			      QUERY,
 			      ISOLATE,
 			      TUNNEL,
-			      FLEX)),
+			      FLEX,
+			      QUEUE)),
 		.call = parse_init,
 	},
 	/* Top-level command. */
@@ -2641,6 +2690,83 @@ static const struct token token_list[] = {
 		.call = parse_table,
 	},
 	/* Top-level command. */
+	[QUEUE] = {
+		.name = "queue",
+		.help = "queue a flow rule operation",
+		.next = NEXT(next_queue_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_qo,
+	},
+	/* Sub-level commands. */
+	[QUEUE_CREATE] = {
+		.name = "create",
+		.help = "create a flow rule",
+		.next = NEXT(NEXT_ENTRY(QUEUE_TABLE), NEXT_ENTRY(COMMON_QUEUE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+		.call = parse_qo,
+	},
+	[QUEUE_DESTROY] = {
+		.name = "destroy",
+		.help = "destroy a flow rule",
+		.next = NEXT(NEXT_ENTRY(QUEUE_DESTROY_ID),
+			     NEXT_ENTRY(COMMON_QUEUE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+		.call = parse_qo_destroy,
+	},
+	/* Queue  arguments. */
+	[QUEUE_TABLE] = {
+		.name = "table",
+		.help = "specify table id",
+		.next = NEXT(NEXT_ENTRY(QUEUE_ITEM_TEMPLATE),
+			     NEXT_ENTRY(COMMON_TABLE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+					args.vc.table_id)),
+		.call = parse_qo,
+	},
+	[QUEUE_ITEM_TEMPLATE] = {
+		.name = "item_template",
+		.help = "specify item template id",
+		.next = NEXT(NEXT_ENTRY(QUEUE_ACTION_TEMPLATE),
+			     NEXT_ENTRY(COMMON_ITEM_TEMPLATE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+					args.vc.it_id)),
+		.call = parse_qo,
+	},
+	[QUEUE_ACTION_TEMPLATE] = {
+		.name = "action_template",
+		.help = "specify action template id",
+		.next = NEXT(NEXT_ENTRY(QUEUE_CREATE_DRAIN),
+			     NEXT_ENTRY(COMMON_ACTION_TEMPLATE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+					args.vc.at_id)),
+		.call = parse_qo,
+	},
+	[QUEUE_CREATE_DRAIN] = {
+		.name = "drain",
+		.help = "drain queue immediately",
+		.next = NEXT(NEXT_ENTRY(ITEM_PATTERN),
+			     NEXT_ENTRY(COMMON_BOOLEAN)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, drain)),
+		.call = parse_qo,
+	},
+	[QUEUE_DESTROY_DRAIN] = {
+		.name = "drain",
+		.help = "drain queue immediately",
+		.next = NEXT(NEXT_ENTRY(QUEUE_DESTROY_ID),
+			     NEXT_ENTRY(COMMON_BOOLEAN)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, drain)),
+		.call = parse_qo_destroy,
+	},
+	[QUEUE_DESTROY_ID] = {
+		.name = "rule",
+		.help = "specify rule id to destroy",
+		.next = NEXT(next_queue_destroy_attr,
+			NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+					    args.destroy.rule)),
+		.call = parse_qo_destroy,
+	},
+	/* Top-level command. */
 	[INDIRECT_ACTION] = {
 		.name = "indirect_action",
 		.type = "{command} {port_id} [{arg} [...]]",
@@ -8154,6 +8280,111 @@ parse_table_destroy(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for queue create commands. */
+static int
+parse_qo(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 != QUEUE)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.vc.data = (uint8_t *)out + size;
+		return len;
+	}
+	switch (ctx->curr) {
+	case QUEUE_CREATE:
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		return len;
+	case QUEUE_TABLE:
+	case QUEUE_ITEM_TEMPLATE:
+	case QUEUE_ACTION_TEMPLATE:
+	case QUEUE_CREATE_DRAIN:
+		return len;
+	case ITEM_PATTERN:
+		out->args.vc.pattern =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		ctx->object = out->args.vc.pattern;
+		ctx->objmask = NULL;
+		return len;
+	case ACTIONS:
+		out->args.vc.actions =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)
+					       (out->args.vc.pattern +
+						out->args.vc.pattern_n),
+					       sizeof(double));
+		ctx->object = out->args.vc.actions;
+		ctx->objmask = NULL;
+		return len;
+	default:
+		return -1;
+	}
+}
+
+/** Parse tokens for queue destroy command. */
+static int
+parse_qo_destroy(struct context *ctx, const struct token *token,
+		 const char *str, unsigned int len,
+		 void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	uint32_t *flow_id;
+
+	/* 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 || out->command == QUEUE) {
+		if (ctx->curr != QUEUE_DESTROY)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.destroy.rule =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		return len;
+	}
+	switch (ctx->curr) {
+	case QUEUE_DESTROY_ID:
+		flow_id = out->args.destroy.rule
+				+ out->args.destroy.rule_n++;
+		if ((uint8_t *)flow_id > (uint8_t *)out + size)
+			return -1;
+		ctx->objdata = 0;
+		ctx->object = flow_id;
+		ctx->objmask = NULL;
+		return len;
+	case QUEUE_DESTROY_DRAIN:
+		return len;
+	default:
+		return -1;
+	}
+}
+
 static int
 parse_flex(struct context *ctx, const struct token *token,
 	     const char *str, unsigned int len,
@@ -9193,6 +9424,28 @@ comp_table_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+/** Complete available queue IDs. */
+static int
+comp_queue_id(struct context *ctx, const struct token *token,
+	      unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i = 0;
+	struct rte_port *port;
+
+	(void)token;
+	if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+	    ctx->port == (portid_t)RTE_PORT_ALL)
+		return -1;
+	port = &ports[ctx->port];
+	for (i = 0; i < port->queue_nb; i++) {
+		if (buf && i == ent)
+			return snprintf(buf, size, "%u", i);
+	}
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -9485,6 +9738,17 @@ cmd_flow_parsed(const struct buffer *in)
 					in->args.table_destroy.table_id_n,
 					in->args.table_destroy.table_id);
 		break;
+	case QUEUE_CREATE:
+		port_queue_flow_create(in->port, in->queue, in->drain,
+				       in->args.vc.table_id, in->args.vc.it_id,
+				       in->args.vc.at_id, in->args.vc.pattern,
+				       in->args.vc.actions);
+		break;
+	case QUEUE_DESTROY:
+		port_queue_flow_destroy(in->port, in->queue, in->drain,
+					in->args.destroy.rule_n,
+					in->args.destroy.rule);
+		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
 				in->port, in->args.vc.attr.group,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 07582fa552..31164d6bf6 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -2411,6 +2411,159 @@ port_flow_table_destroy(portid_t port_id,
 	return ret;
 }
 
+/** Enqueue create flow rule operation. */
+int
+port_queue_flow_create(portid_t port_id, queueid_t queue_id,
+		       bool drain, uint32_t table_id,
+		       uint32_t item_id, uint32_t action_id,
+		       const struct rte_flow_item *pattern,
+		       const struct rte_flow_action *actions)
+{
+	struct rte_flow_q_ops_attr ops_attr = { .drain = drain };
+	struct rte_flow_q_op_res comp = { 0 };
+	struct rte_flow *flow;
+	struct rte_port *port;
+	struct port_flow *pf;
+	struct port_table *pt;
+	uint32_t id = 0;
+	bool found;
+	int ret = 0;
+	struct rte_flow_error error;
+	struct rte_flow_action_age *age = age_action_get(actions);
+
+	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");
+			return -ENOMEM;
+		}
+		id = port->flow_list->id + 1;
+	}
+
+	if (queue_id >= port->queue_nb) {
+		printf("Queue #%u is invalid\n", queue_id);
+		return -EINVAL;
+	}
+
+	found = false;
+	pt = port->table_list;
+	while (pt) {
+		if (table_id == pt->id) {
+			found = true;
+			break;
+		}
+		pt = pt->next;
+	}
+	if (!found) {
+		printf("Table #%u is invalid\n", table_id);
+		return -EINVAL;
+	}
+
+	pf = port_flow_new(NULL, pattern, actions, &error);
+	if (!pf)
+		return port_flow_complain(&error);
+	if (age) {
+		pf->age_type = ACTION_AGE_CONTEXT_TYPE_FLOW;
+		age->context = &pf->age_type;
+	}
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x11, sizeof(error));
+	flow = rte_flow_q_flow_create(port_id, queue_id, &ops_attr,
+		pt->table, pattern, item_id, actions, action_id, &error);
+	if (!flow) {
+		uint32_t flow_id = pf->id;
+		port_queue_flow_destroy(port_id, queue_id, true, 1, &flow_id);
+		return port_flow_complain(&error);
+	}
+
+	while (ret == 0) {
+		/* Poisoning to make sure PMDs update it in case of error. */
+		memset(&error, 0x22, sizeof(error));
+		ret = rte_flow_q_dequeue(port_id, queue_id, &comp, 1, &error);
+		if (ret < 0) {
+			printf("Failed to poll queue\n");
+			return -EINVAL;
+		}
+	}
+
+	pf->next = port->flow_list;
+	pf->id = id;
+	pf->flow = flow;
+	port->flow_list = pf;
+	printf("Flow rule #%u creation enqueued\n", pf->id);
+	return 0;
+}
+
+/** Enqueue number of destroy flow rules operations. */
+int
+port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
+			bool drain, uint32_t n, const uint32_t *rule)
+{
+	struct rte_flow_q_ops_attr op_attr = { .drain = drain };
+	struct rte_flow_q_op_res comp = { 0 };
+	struct rte_port *port;
+	struct port_flow **tmp;
+	uint32_t c = 0;
+	int ret = 0;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+
+	if (queue_id >= port->queue_nb) {
+		printf("Queue #%u is invalid\n", queue_id);
+		return -EINVAL;
+	}
+
+	tmp = &port->flow_list;
+	while (*tmp) {
+		uint32_t i;
+
+		for (i = 0; i != n; ++i) {
+			struct rte_flow_error error;
+			struct port_flow *pf = *tmp;
+
+			if (rule[i] != pf->id)
+				continue;
+			/*
+			 * Poisoning to make sure PMD
+			 * update it in case of error.
+			 */
+			memset(&error, 0x33, sizeof(error));
+			if (rte_flow_q_flow_destroy(port_id, queue_id, &op_attr,
+						    pf->flow, &error)) {
+				ret = port_flow_complain(&error);
+				continue;
+			}
+
+			while (ret == 0) {
+				/*
+				 * Poisoning to make sure PMD
+				 * update it in case of error.
+				 */
+				memset(&error, 0x44, sizeof(error));
+				ret = rte_flow_q_dequeue(port_id, queue_id,
+							 &comp, 1, &error);
+				if (ret < 0) {
+					printf("Failed to poll queue\n");
+					return -EINVAL;
+				}
+			}
+
+			printf("Flow rule #%u destruction enqueued\n", pf->id);
+			*tmp = pf->next;
+			free(pf);
+			break;
+		}
+		if (i == n)
+			tmp = &(*tmp)->next;
+		++c;
+	}
+	return ret;
+}
+
 /** Create flow rule. */
 int
 port_flow_create(portid_t port_id,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index b8655b9987..99845b9e2f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -927,6 +927,13 @@ int port_flow_table_create(portid_t port_id, uint32_t id,
 		   uint32_t nb_action_templates, uint32_t *action_templates);
 int port_flow_table_destroy(portid_t port_id,
 			    uint32_t n, const uint32_t *table);
+int port_queue_flow_create(portid_t port_id, queueid_t queue_id,
+			   bool drain, uint32_t table_id,
+			   uint32_t item_id, uint32_t action_id,
+			   const struct rte_flow_item *pattern,
+			   const struct rte_flow_action *actions);
+int port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
+			    bool drain, uint32_t n, const uint32_t *rule);
 int port_flow_validate(portid_t port_id,
 		       const struct rte_flow_attr *attr,
 		       const struct rte_flow_item *pattern,
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index f8a87564be..eb9dff7221 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -3355,6 +3355,19 @@ following sections.
        pattern {item} [/ {item} [...]] / end
        actions {action} [/ {action} [...]] / end
 
+- Enqueue creation of a flow rule::
+
+   flow queue {port_id} create {queue_id} [drain {boolean}]
+       table {table_id} item_template {item_template_id}
+       action_template {action_template_id}
+       pattern {item} [/ {item} [...]] / end
+       actions {action} [/ {action} [...]] / end
+
+- Enqueue destruction of specific flow rules::
+
+   flow queue {port_id} destroy {queue_id}
+       [drain {boolean}] rule {rule_id} [...]
+
 - Create a flow rule::
 
    flow create {port_id}
@@ -3654,6 +3667,29 @@ one.
 
 **All unspecified object values are automatically initialized to 0.**
 
+Enqueueing creation of flow rules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow queue create`` adds creation operation of a flow rule to a queue.
+It is bound to ``rte_flow_q_flow_create()``::
+
+   flow queue {port_id} create {queue_id} [drain {boolean}]
+       table {table_id} item_template {item_template_id}
+       action_template {action_template_id}
+       pattern {item} [/ {item} [...]] / end
+       actions {action} [/ {action} [...]] / end
+
+If successful, it will return a flow rule ID usable with other commands::
+
+   Flow rule #[...] created
+
+Otherwise it will show an error message of the form::
+
+   Caught error type [...] ([...]): [...]
+
+This command uses the same pattern items and actions as ``flow create``,
+their format is described in `Creating flow rules`_.
+
 Attributes
 ^^^^^^^^^^
 
@@ -4368,6 +4404,25 @@ Non-existent rule IDs are ignored::
    Flow rule #0 destroyed
    testpmd>
 
+Enqueueing destruction of flow rules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow queue destroy`` adds destruction operations to destroy one or more rules
+from their rule ID (as returned by ``flow queue create``) to a queue,
+this command calls ``rte_flow_q_flow_destroy()`` as many times as necessary::
+
+   flow queue {port_id} destroy {queue_id}
+        [drain {boolean}] rule {rule_id} [...]
+
+If successful, it will show::
+
+   Flow rule #[...] destruction enqueued
+
+It does not report anything for rule IDs that do not exist. The usual error
+message is shown when a rule cannot be destroyed::
+
+   Caught error type [...] ([...]): [...]
+
 Querying flow rules
 ~~~~~~~~~~~~~~~~~~~
 
-- 
2.18.2


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

* [PATCH 08/10] app/testpmd: implement rte flow queue drain
  2022-01-18  5:07 [PATCH 05/10] app/testpmd: implement rte flow item/action template Alexander Kozyrev
  2022-01-18  5:07 ` [PATCH 06/10] app/testpmd: implement rte flow table Alexander Kozyrev
  2022-01-18  5:07 ` [PATCH 07/10] app/testpmd: implement rte flow queue create flow Alexander Kozyrev
@ 2022-01-18  5:08 ` Alexander Kozyrev
  2022-01-18  5:08 ` [PATCH 09/10] app/testpmd: implement rte flow queue dequeue Alexander Kozyrev
  3 siblings, 0 replies; 5+ messages in thread
From: Alexander Kozyrev @ 2022-01-18  5:08 UTC (permalink / raw)
  To: dev
  Cc: orika, thomas, ivan.malov, andrew.rybchenko, ferruh.yigit,
	mohammad.abdul.awal, qi.z.zhang, jerinj, ajit.khaparde

Add testpmd support for the rte_flow_q_drain API.
Provide the command line interface for the queue draining.
Usage example: flow queue 0 drain 0

Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
---
 app/test-pmd/cmdline_flow.c                 | 56 ++++++++++++++++++++-
 app/test-pmd/config.c                       | 28 +++++++++++
 app/test-pmd/testpmd.h                      |  1 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst | 21 ++++++++
 4 files changed, 105 insertions(+), 1 deletion(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 6a8e6fc683..e94c01cf75 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -93,6 +93,7 @@ enum index {
 	TUNNEL,
 	FLEX,
 	QUEUE,
+	DRAIN,
 
 	/* Flex arguments */
 	FLEX_ITEM_INIT,
@@ -131,6 +132,9 @@ enum index {
 	QUEUE_DESTROY_ID,
 	QUEUE_DESTROY_DRAIN,
 
+	/* Drain arguments. */
+	DRAIN_QUEUE,
+
 	/* Table arguments. */
 	TABLE_CREATE,
 	TABLE_DESTROY,
@@ -2155,6 +2159,9 @@ static int parse_qo(struct context *, const struct token *,
 static int parse_qo_destroy(struct context *, const struct token *,
 			    const char *, unsigned int,
 			    void *, unsigned int);
+static int parse_drain(struct context *, const struct token *,
+		       const char *, unsigned int,
+		       void *, unsigned int);
 static int parse_tunnel(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
@@ -2432,7 +2439,8 @@ static const struct token token_list[] = {
 			      ISOLATE,
 			      TUNNEL,
 			      FLEX,
-			      QUEUE)),
+			      QUEUE,
+			      DRAIN)),
 		.call = parse_init,
 	},
 	/* Top-level command. */
@@ -2767,6 +2775,21 @@ static const struct token token_list[] = {
 		.call = parse_qo_destroy,
 	},
 	/* Top-level command. */
+	[DRAIN] = {
+		.name = "drain",
+		.help = "drain a flow queue",
+		.next = NEXT(NEXT_ENTRY(DRAIN_QUEUE), NEXT_ENTRY(COMMON_PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_drain,
+	},
+	/* Sub-level commands. */
+	[DRAIN_QUEUE] = {
+		.name = "queue",
+		.help = "specify queue id",
+		.next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(COMMON_QUEUE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+	},
+	/* Top-level command. */
 	[INDIRECT_ACTION] = {
 		.name = "indirect_action",
 		.type = "{command} {port_id} [{arg} [...]]",
@@ -8385,6 +8408,34 @@ parse_qo_destroy(struct context *ctx, const struct token *token,
 	}
 }
 
+/** Parse tokens for drain queue command. */
+static int
+parse_drain(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 != DRAIN)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.vc.data = (uint8_t *)out + size;
+	}
+	return len;
+}
+
 static int
 parse_flex(struct context *ctx, const struct token *token,
 	     const char *str, unsigned int len,
@@ -9749,6 +9800,9 @@ cmd_flow_parsed(const struct buffer *in)
 					in->args.destroy.rule_n,
 					in->args.destroy.rule);
 		break;
+	case DRAIN:
+		port_queue_flow_drain(in->port, in->queue);
+		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
 				in->port, in->args.vc.attr.group,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 31164d6bf6..c6469dd06f 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -2564,6 +2564,34 @@ port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
 	return ret;
 }
 
+/** Drain all the queue operations down the queue. */
+int
+port_queue_flow_drain(portid_t port_id, queueid_t queue_id)
+{
+	struct rte_port *port;
+	struct rte_flow_error error;
+	int ret = 0;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+
+	if (queue_id >= port->queue_nb) {
+		printf("Queue #%u is invalid\n", queue_id);
+		return -EINVAL;
+	}
+
+	memset(&error, 0x55, sizeof(error));
+	ret = rte_flow_q_drain(port_id, queue_id, &error);
+	if (ret < 0) {
+		printf("Failed to drain queue\n");
+		return -EINVAL;
+	}
+	printf("Queue #%u drained\n", queue_id);
+	return ret;
+}
+
 /** Create flow rule. */
 int
 port_flow_create(portid_t port_id,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 99845b9e2f..bf4597e7ba 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -934,6 +934,7 @@ int port_queue_flow_create(portid_t port_id, queueid_t queue_id,
 			   const struct rte_flow_action *actions);
 int port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
 			    bool drain, uint32_t n, const uint32_t *rule);
+int port_queue_flow_drain(portid_t port_id, queueid_t queue_id);
 int port_flow_validate(portid_t port_id,
 		       const struct rte_flow_attr *attr,
 		       const struct rte_flow_item *pattern,
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index eb9dff7221..2ff4e4aef1 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -3368,6 +3368,10 @@ following sections.
    flow queue {port_id} destroy {queue_id}
        [drain {boolean}] rule {rule_id} [...]
 
+- Drain a queue::
+
+   flow drain {port_id} queue {queue_id}
+
 - Create a flow rule::
 
    flow create {port_id}
@@ -3561,6 +3565,23 @@ The usual error message is shown when a table cannot be destroyed::
 
    Caught error type [...] ([...]): [...]
 
+Draining a flow queue
+~~~~~~~~~~~~~~~~~~~~~
+
+``flow drain`` drains the specific queue to push all the
+outstanding queued operations to the underlying device immediately.
+It is bound to ``rte_flow_q_drain()``::
+
+   flow drain {port_id} queue {queue_id}
+
+If successful, it will show::
+
+   Queue #[...] drained
+
+The usual error message is shown when a queue cannot be drained::
+
+   Caught error type [...] ([...]): [...]
+
 Creating a tunnel stub for offload
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.18.2


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

* [PATCH 09/10] app/testpmd: implement rte flow queue dequeue
  2022-01-18  5:07 [PATCH 05/10] app/testpmd: implement rte flow item/action template Alexander Kozyrev
                   ` (2 preceding siblings ...)
  2022-01-18  5:08 ` [PATCH 08/10] app/testpmd: implement rte flow queue drain Alexander Kozyrev
@ 2022-01-18  5:08 ` Alexander Kozyrev
  3 siblings, 0 replies; 5+ messages in thread
From: Alexander Kozyrev @ 2022-01-18  5:08 UTC (permalink / raw)
  To: dev
  Cc: orika, thomas, ivan.malov, andrew.rybchenko, ferruh.yigit,
	mohammad.abdul.awal, qi.z.zhang, jerinj, ajit.khaparde

Add testpmd support for the rte_flow_q_dequeue API.
Provide the command line interface for operations dequeue.
Usage example: flow dequeue 0 queue 0

Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
---
 app/test-pmd/cmdline_flow.c                 | 54 +++++++++++++++
 app/test-pmd/config.c                       | 74 +++++++++++++--------
 app/test-pmd/testpmd.h                      |  1 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst | 25 +++++++
 4 files changed, 126 insertions(+), 28 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index e94c01cf75..507eb87984 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -93,6 +93,7 @@ enum index {
 	TUNNEL,
 	FLEX,
 	QUEUE,
+	DEQUEUE,
 	DRAIN,
 
 	/* Flex arguments */
@@ -132,6 +133,9 @@ enum index {
 	QUEUE_DESTROY_ID,
 	QUEUE_DESTROY_DRAIN,
 
+	/* Dequeue arguments. */
+	DEQUEUE_QUEUE,
+
 	/* Drain arguments. */
 	DRAIN_QUEUE,
 
@@ -2159,6 +2163,9 @@ static int parse_qo(struct context *, const struct token *,
 static int parse_qo_destroy(struct context *, const struct token *,
 			    const char *, unsigned int,
 			    void *, unsigned int);
+static int parse_dequeue(struct context *, const struct token *,
+			 const char *, unsigned int,
+			 void *, unsigned int);
 static int parse_drain(struct context *, const struct token *,
 		       const char *, unsigned int,
 		       void *, unsigned int);
@@ -2440,6 +2447,7 @@ static const struct token token_list[] = {
 			      TUNNEL,
 			      FLEX,
 			      QUEUE,
+			      DEQUEUE,
 			      DRAIN)),
 		.call = parse_init,
 	},
@@ -2775,6 +2783,21 @@ static const struct token token_list[] = {
 		.call = parse_qo_destroy,
 	},
 	/* Top-level command. */
+	[DEQUEUE] = {
+		.name = "dequeue",
+		.help = "dequeue flow operations",
+		.next = NEXT(NEXT_ENTRY(DEQUEUE_QUEUE), NEXT_ENTRY(COMMON_PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_dequeue,
+	},
+	/* Sub-level commands. */
+	[DEQUEUE_QUEUE] = {
+		.name = "queue",
+		.help = "specify queue id",
+		.next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(COMMON_QUEUE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+	},
+	/* Top-level command. */
 	[DRAIN] = {
 		.name = "drain",
 		.help = "drain a flow queue",
@@ -8408,6 +8431,34 @@ parse_qo_destroy(struct context *ctx, const struct token *token,
 	}
 }
 
+/** Parse tokens for dequeue command. */
+static int
+parse_dequeue(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 != DEQUEUE)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.vc.data = (uint8_t *)out + size;
+	}
+	return len;
+}
+
 /** Parse tokens for drain queue command. */
 static int
 parse_drain(struct context *ctx, const struct token *token,
@@ -9800,6 +9851,9 @@ cmd_flow_parsed(const struct buffer *in)
 					in->args.destroy.rule_n,
 					in->args.destroy.rule);
 		break;
+	case DEQUEUE:
+		port_queue_flow_dequeue(in->port, in->queue);
+		break;
 	case DRAIN:
 		port_queue_flow_drain(in->port, in->queue);
 		break;
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index c6469dd06f..5d23edf562 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -2420,14 +2420,12 @@ port_queue_flow_create(portid_t port_id, queueid_t queue_id,
 		       const struct rte_flow_action *actions)
 {
 	struct rte_flow_q_ops_attr ops_attr = { .drain = drain };
-	struct rte_flow_q_op_res comp = { 0 };
 	struct rte_flow *flow;
 	struct rte_port *port;
 	struct port_flow *pf;
 	struct port_table *pt;
 	uint32_t id = 0;
 	bool found;
-	int ret = 0;
 	struct rte_flow_error error;
 	struct rte_flow_action_age *age = age_action_get(actions);
 
@@ -2477,16 +2475,6 @@ port_queue_flow_create(portid_t port_id, queueid_t queue_id,
 		return port_flow_complain(&error);
 	}
 
-	while (ret == 0) {
-		/* Poisoning to make sure PMDs update it in case of error. */
-		memset(&error, 0x22, sizeof(error));
-		ret = rte_flow_q_dequeue(port_id, queue_id, &comp, 1, &error);
-		if (ret < 0) {
-			printf("Failed to poll queue\n");
-			return -EINVAL;
-		}
-	}
-
 	pf->next = port->flow_list;
 	pf->id = id;
 	pf->flow = flow;
@@ -2501,7 +2489,6 @@ port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
 			bool drain, uint32_t n, const uint32_t *rule)
 {
 	struct rte_flow_q_ops_attr op_attr = { .drain = drain };
-	struct rte_flow_q_op_res comp = { 0 };
 	struct rte_port *port;
 	struct port_flow **tmp;
 	uint32_t c = 0;
@@ -2537,21 +2524,6 @@ port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
 				ret = port_flow_complain(&error);
 				continue;
 			}
-
-			while (ret == 0) {
-				/*
-				 * Poisoning to make sure PMD
-				 * update it in case of error.
-				 */
-				memset(&error, 0x44, sizeof(error));
-				ret = rte_flow_q_dequeue(port_id, queue_id,
-							 &comp, 1, &error);
-				if (ret < 0) {
-					printf("Failed to poll queue\n");
-					return -EINVAL;
-				}
-			}
-
 			printf("Flow rule #%u destruction enqueued\n", pf->id);
 			*tmp = pf->next;
 			free(pf);
@@ -2592,6 +2564,52 @@ port_queue_flow_drain(portid_t port_id, queueid_t queue_id)
 	return ret;
 }
 
+/** Dequeue a queue operation from the queue. */
+int
+port_queue_flow_dequeue(portid_t port_id, queueid_t queue_id)
+{
+	struct rte_port *port;
+	struct rte_flow_q_op_res *res;
+	struct rte_flow_error error;
+	int ret = 0;
+	int success = 0;
+	int i;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+
+	if (queue_id >= port->queue_nb) {
+		printf("Queue #%u is invalid\n", queue_id);
+		return -EINVAL;
+	}
+
+	res = malloc(sizeof(struct rte_flow_q_op_res) * port->queue_sz);
+	if (!res) {
+		printf("Failed to allocate memory for dequeue results\n");
+		return -ENOMEM;
+	}
+
+	memset(&error, 0x66, sizeof(error));
+	ret = rte_flow_q_dequeue(port_id, queue_id, res,
+				 port->queue_sz, &error);
+	if (ret < 0) {
+		printf("Failed to dequeue a queue\n");
+		free(res);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ret; i++) {
+		if (res[i].status == RTE_FLOW_Q_OP_SUCCESS)
+			success++;
+	}
+	printf("Queue #%u dequeued %u operations (%u failed, %u succeeded)\n",
+	       queue_id, ret, ret - success, success);
+	free(res);
+	return ret;
+}
+
 /** Create flow rule. */
 int
 port_flow_create(portid_t port_id,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index bf4597e7ba..3cf336dbae 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -935,6 +935,7 @@ int port_queue_flow_create(portid_t port_id, queueid_t queue_id,
 int port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
 			    bool drain, uint32_t n, const uint32_t *rule);
 int port_queue_flow_drain(portid_t port_id, queueid_t queue_id);
+int port_queue_flow_dequeue(portid_t port_id, queueid_t queue_id);
 int port_flow_validate(portid_t port_id,
 		       const struct rte_flow_attr *attr,
 		       const struct rte_flow_item *pattern,
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 2ff4e4aef1..fff4de8f00 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -3372,6 +3372,10 @@ following sections.
 
    flow drain {port_id} queue {queue_id}
 
+- Dequeue all operations from a queue::
+
+   flow dequeue {port_id} queue {queue_id}
+
 - Create a flow rule::
 
    flow create {port_id}
@@ -3582,6 +3586,23 @@ The usual error message is shown when a queue cannot be drained::
 
    Caught error type [...] ([...]): [...]
 
+Dequeueing flow operations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``flow dequeue`` asks the underlying device about flow queue operations
+results and return all the processed (successfully or not) operations.
+It is bound to ``rte_flow_q_dequeue()``::
+
+   flow dequeue {port_id} queue {queue_id}
+
+If successful, it will show::
+
+   Queue #[...] dequeued #[...] operations (#[...] failed, #[...] succeeded)
+
+The usual error message is shown when a queue cannot be drained::
+
+   Caught error type [...] ([...]): [...]
+
 Creating a tunnel stub for offload
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -3711,6 +3732,8 @@ Otherwise it will show an error message of the form::
 This command uses the same pattern items and actions as ``flow create``,
 their format is described in `Creating flow rules`_.
 
+``flow queue dequeue`` must be called to retrieve the operation status.
+
 Attributes
 ^^^^^^^^^^
 
@@ -4444,6 +4467,8 @@ message is shown when a rule cannot be destroyed::
 
    Caught error type [...] ([...]): [...]
 
+``flow queue dequeue`` must be called to retrieve the operation status.
+
 Querying flow rules
 ~~~~~~~~~~~~~~~~~~~
 
-- 
2.18.2


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

end of thread, other threads:[~2022-01-18  5:09 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-18  5:07 [PATCH 05/10] app/testpmd: implement rte flow item/action template Alexander Kozyrev
2022-01-18  5:07 ` [PATCH 06/10] app/testpmd: implement rte flow table Alexander Kozyrev
2022-01-18  5:07 ` [PATCH 07/10] app/testpmd: implement rte flow queue create flow Alexander Kozyrev
2022-01-18  5:08 ` [PATCH 08/10] app/testpmd: implement rte flow queue drain Alexander Kozyrev
2022-01-18  5:08 ` [PATCH 09/10] app/testpmd: implement rte flow queue dequeue Alexander Kozyrev

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.