[dpdk-dev] [PATCH v7 2/2] app/testpmd: support shared action

Andrey Vesnovaty andreyv at nvidia.com
Thu Oct 8 13:51:43 CEST 2020


This patch adds shared action support to testpmd CLI.

All shared actions created via testpmd CLI assigned ID for further
reference in other CLI commands. Shared action ID supplied as CLI
argument or assigned by testpmd is similar to flow ID & limited to
scope of testpdm CLI.

Create shared action syntax:
flow shared_action {port_id} create [action_id {shared_action_id}]
	[ingress] [egress] action {action} / end

Create shared action examples:
	flow shared_action 0 create action_id 100 \
		ingress action rss queues 1 2 end / end
	This creates shared rss action with id 100 on port 0.

	flow shared_action 0 create action_id \
		ingress action rss queues 0 1 end / end
	This creates shared rss action with id assigned by testpmd
	on port 0.

Update shared action syntax:
flow shared_action {port_id} update {shared_action_id}
	action {action} / end

Update shared action example:
	flow shared_action 0 update 100 \
		action rss queues 0 3 end / end
	This updates shared rss action having id 100 on port 0
	with rss to queues 0 3 (in create example rss queues were
	1 & 2).

Destroy shared action syntax:
flow shared_action {port_id} destroy action_id {shared_action_id} [...]

Destroy shared action example:
	flow shared_action 0 destroy action_id 100 action_id 101
	This destroys shared actions having id 100 & 101

Query shared action syntax:
flow shared_action {port} query {shared_action_id}

Query shared action example:
	flow shared_action 0 query 100
	This queries shared actions having id 100

Use shared action as flow action syntax:
flow create {port_id} ... / end actions [action / [...]]
	shared {action_id} / [action / [...]] end

Use shared action as flow action example:
	flow create 0 ingress pattern ... / end \
		actions shared 100 / end
	This creates flow rule where rss action is shared rss action
	having id 100.

All shared action CLIs report status of the command.
Shared action query CLI output depends on action type.

Signed-off-by: Andrey Vesnovaty <andreyv at nvidia.com>
Acked-by: Ori Kam <orika at nvidia.com>
---
 app/test-pmd/cmdline.c                      |  18 ++
 app/test-pmd/cmdline_flow.c                 | 294 +++++++++++++++++++-
 app/test-pmd/config.c                       | 216 ++++++++++++++
 app/test-pmd/testpmd.h                      |  20 ++
 doc/guides/testpmd_app_ug/testpmd_funcs.rst | 112 ++++++++
 5 files changed, 659 insertions(+), 1 deletion(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 524c75b267..32ba08a590 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -1143,6 +1143,24 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"    List and destroy aged flows"
 			" flow rules\n\n"
 
+			"flow shared_action {port_id} create"
+			" [action_id {shared_action_id}]"
+			" [ingress] [egress]"
+			" action {action} / end\n"
+			"    Create shared action.\n\n"
+
+			"flow shared_action {port_id} update"
+			" {shared_action_id} action {action} / end\n"
+			"    Update shared action.\n\n"
+
+			"flow shared_action {port_id} destroy"
+			" action_id {shared_action_id} [...]\n"
+			"    Destroy specific shared actions.\n\n"
+
+			"flow shared_action {port_id} query"
+			" {shared_action_id}\n"
+			"    Query an existing shared action.\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 6e04d538ea..ff6b188a6d 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -49,6 +49,7 @@ enum index {
 	PORT_ID,
 	GROUP_ID,
 	PRIORITY_LEVEL,
+	SHARED_ACTION_ID,
 
 	/* Top-level command. */
 	SET,
@@ -60,6 +61,7 @@ enum index {
 	/* Top-level command. */
 	FLOW,
 	/* Sub-level commands. */
+	SHARED_ACTION,
 	VALIDATE,
 	CREATE,
 	DESTROY,
@@ -89,6 +91,21 @@ enum index {
 	EGRESS,
 	TRANSFER,
 
+	/* Shared action arguments */
+	SHARED_ACTION_CREATE,
+	SHARED_ACTION_UPDATE,
+	SHARED_ACTION_DESTROY,
+	SHARED_ACTION_QUERY,
+
+	/* Shared action create arguments */
+	SHARED_ACTION_CREATE_ID,
+	SHARED_ACTION_INGRESS,
+	SHARED_ACTION_EGRESS,
+	SHARED_ACTION_SPEC,
+
+	/* Shared action destroy arguments */
+	SHARED_ACTION_DESTROY_ID,
+
 	/* Validate/create pattern. */
 	PATTERN,
 	ITEM_PARAM_IS,
@@ -360,6 +377,8 @@ enum index {
 	ACTION_SET_IPV6_DSCP_VALUE,
 	ACTION_AGE,
 	ACTION_AGE_TIMEOUT,
+	ACTION_SHARED,
+	SHARED_ACTION_ID2PTR,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -653,6 +672,13 @@ struct buffer {
 	enum index command; /**< Flow command. */
 	portid_t port; /**< Affected port ID. */
 	union {
+		struct {
+			uint32_t *action_id;
+			uint32_t action_id_n;
+		} sa_destroy; /**< Shared action destroy arguments. */
+		struct {
+			uint32_t action_id;
+		} sa; /* Shared action query arguments */
 		struct {
 			struct rte_flow_attr attr;
 			struct rte_flow_item *pattern;
@@ -709,6 +735,22 @@ struct parse_action_priv {
 		.size = s, \
 	})
 
+static const enum index next_sa_create_attr[] = {
+	SHARED_ACTION_CREATE_ID,
+	SHARED_ACTION_INGRESS,
+	SHARED_ACTION_EGRESS,
+	SHARED_ACTION_SPEC,
+	ZERO,
+};
+
+static const enum index next_sa_subcmd[] = {
+	SHARED_ACTION_CREATE,
+	SHARED_ACTION_UPDATE,
+	SHARED_ACTION_DESTROY,
+	SHARED_ACTION_QUERY,
+	ZERO,
+};
+
 static const enum index next_vc_attr[] = {
 	GROUP,
 	PRIORITY,
@@ -743,6 +785,12 @@ static const enum index next_aged_attr[] = {
 	ZERO,
 };
 
+static const enum index next_sa_destroy_attr[] = {
+	SHARED_ACTION_DESTROY_ID,
+	END,
+	ZERO,
+};
+
 static const enum index item_param[] = {
 	ITEM_PARAM_IS,
 	ITEM_PARAM_SPEC,
@@ -1193,6 +1241,7 @@ static const enum index next_action[] = {
 	ACTION_SET_IPV4_DSCP,
 	ACTION_SET_IPV6_DSCP,
 	ACTION_AGE,
+	ACTION_SHARED,
 	ZERO,
 };
 
@@ -1550,6 +1599,15 @@ static int parse_ipv6_addr(struct context *, const struct token *,
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
+static int parse_sa(struct context *, const struct token *,
+		    const char *, unsigned int,
+		    void *, unsigned int);
+static int parse_sa_destroy(struct context *ctx, const struct token *token,
+			    const char *str, unsigned int len,
+			    void *buf, unsigned int size);
+static int parse_sa_id2ptr(struct context *ctx, const struct token *token,
+			   const char *str, unsigned int len, void *buf,
+			   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -1688,13 +1746,21 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_none,
 	},
+	[SHARED_ACTION_ID] = {
+		.name = "{shared_action_id}",
+		.type = "SHARED_ACTION_ID",
+		.help = "shared action id",
+		.call = parse_int,
+		.comp = comp_none,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
 		.type = "{command} {port_id} [{arg} [...]]",
 		.help = "manage ingress/egress flow rules",
 		.next = NEXT(NEXT_ENTRY
-			     (VALIDATE,
+			     (SHARED_ACTION,
+			      VALIDATE,
 			      CREATE,
 			      DESTROY,
 			      FLUSH,
@@ -1705,7 +1771,44 @@ static const struct token token_list[] = {
 			      ISOLATE)),
 		.call = parse_init,
 	},
+	/* Top-level command. */
+	[SHARED_ACTION] = {
+		.name = "shared_action",
+		.type = "{command} {port_id} [{arg} [...]]",
+		.help = "manage shared actions",
+		.next = NEXT(next_sa_subcmd, NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_sa,
+	},
 	/* Sub-level commands. */
+	[SHARED_ACTION_CREATE] = {
+		.name = "create",
+		.help = "create shared action",
+		.next = NEXT(next_sa_create_attr),
+		.call = parse_sa,
+	},
+	[SHARED_ACTION_UPDATE] = {
+		.name = "update",
+		.help = "update shared action",
+		.next = NEXT(NEXT_ENTRY(SHARED_ACTION_SPEC),
+			     NEXT_ENTRY(SHARED_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.call = parse_sa,
+	},
+	[SHARED_ACTION_DESTROY] = {
+		.name = "destroy",
+		.help = "destroy shared action",
+		.next = NEXT(NEXT_ENTRY(SHARED_ACTION_DESTROY_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_sa_destroy,
+	},
+	[SHARED_ACTION_QUERY] = {
+		.name = "query",
+		.help = "query shared action",
+		.next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(SHARED_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.sa.action_id)),
+		.call = parse_sa,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -3859,6 +3962,57 @@ static const struct token token_list[] = {
 		.next = NEXT(action_age, NEXT_ENTRY(UNSIGNED)),
 		.call = parse_vc_conf,
 	},
+	/* Shared action destroy arguments. */
+	[SHARED_ACTION_DESTROY_ID] = {
+		.name = "action_id",
+		.help = "specify a shared action id to destroy",
+		.next = NEXT(next_sa_destroy_attr,
+			     NEXT_ENTRY(SHARED_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+					    args.sa_destroy.action_id)),
+		.call = parse_sa_destroy,
+	},
+	/* Shared action create arguments. */
+	[SHARED_ACTION_CREATE_ID] = {
+		.name = "action_id",
+		.help = "specify a shared action id to create",
+		.next = NEXT(next_sa_create_attr,
+			     NEXT_ENTRY(SHARED_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+	},
+	[ACTION_SHARED] = {
+		.name = "shared",
+		.help = "apply shared action by id",
+		.priv = PRIV_ACTION(SHARED, 0),
+		.next = NEXT(NEXT_ENTRY(SHARED_ACTION_ID2PTR)),
+		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
+		.call = parse_vc,
+	},
+	[SHARED_ACTION_ID2PTR] = {
+		.name = "{action_id}",
+		.type = "SHARED_ACTION_ID",
+		.help = "shared action id",
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_sa_id2ptr,
+		.comp = comp_none,
+	},
+	[SHARED_ACTION_INGRESS] = {
+		.name = "ingress",
+		.help = "affect rule to ingress",
+		.next = NEXT(next_sa_create_attr),
+		.call = parse_sa,
+	},
+	[SHARED_ACTION_EGRESS] = {
+		.name = "egress",
+		.help = "affect rule to egress",
+		.next = NEXT(next_sa_create_attr),
+		.call = parse_sa,
+	},
+	[SHARED_ACTION_SPEC] = {
+		.name = "action",
+		.help = "specify action to share",
+		.next = NEXT(next_action),
+	},
 };
 
 /** Remove and return last entry from argument stack. */
@@ -4043,6 +4197,97 @@ parse_init(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for shared action commands. */
+static int
+parse_sa(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 != SHARED_ACTION)
+			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 SHARED_ACTION_CREATE:
+	case SHARED_ACTION_UPDATE:
+		out->args.vc.actions =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		out->args.vc.attr.group = UINT32_MAX;
+		/* fallthrough */
+	case SHARED_ACTION_QUERY:
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		return len;
+	case SHARED_ACTION_EGRESS:
+		out->args.vc.attr.egress = 1;
+		return len;
+	case SHARED_ACTION_INGRESS:
+		out->args.vc.attr.ingress = 1;
+		return len;
+	default:
+		return -1;
+	}
+}
+
+
+/** Parse tokens for shared action destroy command. */
+static int
+parse_sa_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 *action_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 == SHARED_ACTION) {
+		if (ctx->curr != SHARED_ACTION_DESTROY)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+		out->args.sa_destroy.action_id =
+			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+					       sizeof(double));
+		return len;
+	}
+	action_id = out->args.sa_destroy.action_id
+		    + out->args.sa_destroy.action_id_n++;
+	if ((uint8_t *)action_id > (uint8_t *)out + size)
+		return -1;
+	ctx->objdata = 0;
+	ctx->object = action_id;
+	ctx->objmask = NULL;
+	return len;
+}
+
 /** Parse tokens for validate/create commands. */
 static int
 parse_vc(struct context *ctx, const struct token *token,
@@ -6110,6 +6355,32 @@ parse_port(struct context *ctx, const struct token *token,
 	return ret;
 }
 
+static int
+parse_sa_id2ptr(struct context *ctx, const struct token *token,
+		const char *str, unsigned int len,
+		void *buf, unsigned int size)
+{
+	struct rte_flow_action *action = ctx->object;
+	uint32_t id;
+	int ret;
+
+	(void)buf;
+	(void)size;
+	ctx->objdata = 0;
+	ctx->object = &id;
+	ctx->objmask = NULL;
+	ret = parse_int(ctx, token, str, len, ctx->object, sizeof(id));
+	ctx->object = action;
+	if (ret != (int)len)
+		return ret;
+	/* set shared action */
+	if (action) {
+		action->conf = port_shared_action_get_by_id(ctx->port, id);
+		ret = (action->conf) ? ret : -1;
+	}
+	return ret;
+}
+
 /** Parse set command, initialize output buffer for subsequent tokens. */
 static int
 parse_set_raw_encap_decap(struct context *ctx, const struct token *token,
@@ -6559,6 +6830,27 @@ static void
 cmd_flow_parsed(const struct buffer *in)
 {
 	switch (in->command) {
+	case SHARED_ACTION_CREATE:
+		port_shared_action_create(
+				in->port, in->args.vc.attr.group,
+				&((const struct rte_flow_shared_action_conf) {
+					.ingress = in->args.vc.attr.ingress,
+					.egress = in->args.vc.attr.egress,
+				}),
+				in->args.vc.actions);
+		break;
+	case SHARED_ACTION_DESTROY:
+		port_shared_action_destroy(in->port,
+					   in->args.sa_destroy.action_id_n,
+					   in->args.sa_destroy.action_id);
+		break;
+	case SHARED_ACTION_UPDATE:
+		port_shared_action_update(in->port, in->args.vc.attr.group,
+					  in->args.vc.actions);
+		break;
+	case SHARED_ACTION_QUERY:
+		port_shared_action_query(in->port, in->args.sa.action_id);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions);
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 418ea6dda4..89b5721d6f 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1580,6 +1580,222 @@ rss_config_display(struct rte_flow_action_rss *rss_conf)
 	}
 }
 
+static struct port_shared_action *
+action_get_by_id(portid_t port_id, uint32_t id)
+{
+	struct rte_port *port;
+	struct port_shared_action **ppsa;
+	struct port_shared_action *psa = NULL;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return NULL;
+	port = &ports[port_id];
+	ppsa = &port->actions_list;
+	while (*ppsa) {
+		if ((*ppsa)->id == id) {
+			psa = *ppsa;
+			break;
+		}
+		ppsa = &(*ppsa)->next;
+	}
+	if (!psa)
+		printf("Failed to find shared action #%u on port %u\n",
+		       id, port_id);
+	return psa;
+}
+
+static int
+action_alloc(portid_t port_id, uint32_t id,
+	     struct port_shared_action **action)
+{
+	struct rte_port *port;
+	struct port_shared_action **ppsa;
+	struct port_shared_action *psa = NULL;
+
+	*action = NULL;
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	if (id == UINT32_MAX) {
+		/* taking first available ID */
+		if (port->actions_list) {
+			if (port->actions_list->id == UINT32_MAX - 1) {
+				printf("Highest shared action ID is already"
+				" assigned, delete it first\n");
+				return -ENOMEM;
+			}
+			id = port->actions_list->id + 1;
+		} else {
+			id = 0;
+		}
+	}
+	psa = calloc(1, sizeof(*psa));
+	if (!psa) {
+		printf("Allocation of port %u shared action failed\n",
+		       port_id);
+		return -ENOMEM;
+	}
+	ppsa = &port->actions_list;
+	while (*ppsa && (*ppsa)->id > id)
+		ppsa = &(*ppsa)->next;
+	if (*ppsa && (*ppsa)->id == id) {
+		printf("Shared action #%u is already assigned,"
+			" delete it first\n", id);
+		free(psa);
+		return -EINVAL;
+	}
+	psa->next = *ppsa;
+	psa->id = id;
+	*ppsa = psa;
+	*action = psa;
+	return 0;
+}
+
+/** Create shared action */
+int
+port_shared_action_create(portid_t port_id, uint32_t id,
+			  const struct rte_flow_shared_action_conf *conf,
+			  const struct rte_flow_action *action)
+{
+	struct port_shared_action *psa;
+	int ret;
+	struct rte_flow_error error;
+
+	ret = action_alloc(port_id, id, &psa);
+	if (ret)
+		return ret;
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x22, sizeof(error));
+	psa->action = rte_flow_shared_action_create(port_id, conf, action,
+						    &error);
+	if (!psa->action) {
+		uint32_t destroy_id = psa->id;
+		port_shared_action_destroy(port_id, 1, &destroy_id);
+		return port_flow_complain(&error);
+	}
+	psa->type = action->type;
+	printf("Shared action #%u created\n", psa->id);
+	return 0;
+}
+
+/** Destroy shared action */
+int
+port_shared_action_destroy(portid_t port_id,
+			   uint32_t n,
+			   const uint32_t *actions)
+{
+	struct rte_port *port;
+	struct port_shared_action **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->actions_list;
+	while (*tmp) {
+		uint32_t i;
+
+		for (i = 0; i != n; ++i) {
+			struct rte_flow_error error;
+			struct port_shared_action *psa = *tmp;
+
+			if (actions[i] != psa->id)
+				continue;
+			/*
+			 * Poisoning to make sure PMDs update it in case
+			 * of error.
+			 */
+			memset(&error, 0x33, sizeof(error));
+
+			if (psa->action && rte_flow_shared_action_destroy(
+					port_id, psa->action, &error)) {
+				ret = port_flow_complain(&error);
+				continue;
+			}
+			*tmp = psa->next;
+			free(psa);
+			printf("Shared action #%u destroyed\n", psa->id);
+			break;
+		}
+		if (i == n)
+			tmp = &(*tmp)->next;
+		++c;
+	}
+	return ret;
+}
+
+
+/** Get shared action by port + id */
+struct rte_flow_shared_action *
+port_shared_action_get_by_id(portid_t port_id, uint32_t id)
+{
+
+	struct port_shared_action *psa = action_get_by_id(port_id, id);
+
+	return (psa) ? psa->action : NULL;
+}
+
+/** Update shared action */
+int
+port_shared_action_update(portid_t port_id, uint32_t id,
+			  const struct rte_flow_action *action)
+{
+	struct rte_flow_error error;
+	struct rte_flow_shared_action *shared_action;
+
+	shared_action = port_shared_action_get_by_id(port_id, id);
+	if (!shared_action)
+		return -EINVAL;
+	if (rte_flow_shared_action_update(port_id, shared_action, action,
+					  &error)) {
+		return port_flow_complain(&error);
+	}
+	printf("Shared action #%u updated\n", id);
+	return 0;
+}
+
+int
+port_shared_action_query(portid_t port_id, uint32_t id)
+{
+	struct rte_flow_error error;
+	struct port_shared_action *psa;
+	uint64_t default_data;
+	void *data = NULL;
+	int ret = 0;
+
+	psa = action_get_by_id(port_id, id);
+	if (!psa)
+		return -EINVAL;
+	switch (psa->type) {
+	case RTE_FLOW_ACTION_TYPE_RSS:
+		data = &default_data;
+		break;
+	default:
+		printf("Shared action %u (type: %d) on port %u doesn't support"
+		       " query\n", id, psa->type, port_id);
+		return -1;
+	}
+	if (rte_flow_shared_action_query(port_id, psa->action, data, &error))
+		ret = port_flow_complain(&error);
+	switch (psa->type) {
+	case RTE_FLOW_ACTION_TYPE_RSS:
+		if (!ret)
+			printf("Shared RSS action:\n\trefs:%u\n",
+			       *((uint32_t *)data));
+		data = NULL;
+		break;
+	default:
+		printf("Shared action %u (type: %d) on port %u doesn't support"
+		       " query\n", id, psa->type, port_id);
+		ret = -1;
+	}
+	return ret;
+}
+
 /** Validate flow rule. */
 int
 port_flow_validate(portid_t port_id,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index c7e7e41a97..f26e0aad03 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -142,6 +142,14 @@ struct port_flow {
 	uint8_t data[]; /**< Storage for flow rule description */
 };
 
+/* Descriptor for shared action */
+struct port_shared_action {
+	struct port_shared_action *next; /**< Next flow in list. */
+	uint32_t id; /**< Shared action ID. */
+	enum rte_flow_action_type type; /**< Action type. */
+	struct rte_flow_shared_action *action;	/**< Shared action handle. */
+};
+
 /**
  * The data structure associated with each port.
  */
@@ -172,6 +180,8 @@ struct rte_port {
 	uint32_t                mc_addr_nb; /**< nb. of addr. in mc_addr_pool */
 	uint8_t                 slave_flag; /**< bonding slave port */
 	struct port_flow        *flow_list; /**< Associated flows. */
+	struct port_shared_action *actions_list;
+	/**< Associated shared actions. */
 	const struct rte_eth_rxtx_callback *rx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1];
 	const struct rte_eth_rxtx_callback *tx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1];
 	/**< metadata value to insert in Tx packets. */
@@ -748,6 +758,15 @@ void port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,
 			    uint8_t bit1_pos, uint8_t bit2_pos, uint32_t value);
 void port_reg_display(portid_t port_id, uint32_t reg_off);
 void port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t value);
+int port_shared_action_create(portid_t port_id, uint32_t id,
+			      const struct rte_flow_shared_action_conf *conf,
+			      const struct rte_flow_action *action);
+int port_shared_action_destroy(portid_t port_id,
+			       uint32_t n, const uint32_t *action);
+struct rte_flow_shared_action *port_shared_action_get_by_id(portid_t port_id,
+							    uint32_t id);
+int port_shared_action_update(portid_t port_id, uint32_t id,
+			      const struct rte_flow_action *action);
 int port_flow_validate(portid_t port_id,
 		       const struct rte_flow_attr *attr,
 		       const struct rte_flow_item *pattern,
@@ -756,6 +775,7 @@ 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);
+int port_shared_action_query(portid_t port_id, uint32_t id);
 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);
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 72bdb1be43..e912c5b54b 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -4382,6 +4382,11 @@ This section lists supported actions and their attributes, if any.
 
   - ``dscp_value {unsigned}``: The new DSCP value to be set
 
+- ``shared``: Use shared action created via
+  ``flow shared_action {port_id} create``
+
+  - ``shared_action_id {unsigned}``: Shared action ID to use
+
 Destroying flow rules
 ~~~~~~~~~~~~~~~~~~~~~
 
@@ -4671,6 +4676,113 @@ If attach ``destroy`` parameter, the command will destroy all the list aged flow
    testpmd> flow aged 0
    Port 0 total aged flows: 0
 
+Creating shared actions
+~~~~~~~~~~~~~~~~~~~~~~~
+``flow shared_action {port_id} create`` creates shared action with optional
+shared action ID. It is bound to ``rte_flow_shared_action_create()``::
+
+   flow shared_action {port_id} create [action_id {shared_action_id}]
+      [ingress] [egress] action {action} / end
+
+If successful, it will show::
+
+   Shared action #[...] created
+
+Otherwise, it will complain either that shared action already exists or that
+some error occurred::
+
+   Shared action #[...] is already assigned, delete it first
+
+::
+
+   Caught error type [...] ([...]): [...]
+
+Create shared rss action with id 100 to queues 1 and 2 on port 0::
+
+   testpmd> flow shared_action 0 create action_id 100 \
+      ingress action rss queues 1 2 end / end
+
+Create shared rss action with id assigned by testpmd to queues 1 and 2 on
+port 0::
+
+	testpmd> flow shared_action 0 create action_id \
+		ingress action rss queues 0 1 end / end
+
+Updating shared actions
+~~~~~~~~~~~~~~~~~~~~~~~
+``flow shared_action {port_id} update`` updates configuration of the shared
+action from its shared action ID (as returned by
+``flow shared_action {port_id} create``). It is bound to
+``rte_flow_shared_action_update()``::
+
+   flow shared_action {port_id} update {shared_action_id}
+      action {action} / end
+
+If successful, it will show::
+
+   Shared action #[...] updated
+
+Otherwise, it will complain either that shared action not found or that some
+error occurred::
+
+   Failed to find shared action #[...] on port [...]
+
+::
+
+   Caught error type [...] ([...]): [...]
+
+Update shared rss action having id 100 on port 0 with rss to queues 0 and 3
+(in create example above rss queues were 1 and 2)::
+
+   testpmd> flow shared_action 0 update 100 action rss queues 0 3 end / end
+
+Destroying shared actions
+~~~~~~~~~~~~~~~~~~~~~~~~~
+``flow shared_action {port_id} update`` destroys one or more shared actions
+from their shared action IDs (as returned by
+``flow shared_action {port_id} create``). It is bound to
+``rte_flow_shared_action_destroy()``::
+
+   flow shared_action {port_id} destroy action_id {shared_action_id} [...]
+
+If successful, it will show::
+
+   Shared action #[...] destroyed
+
+It does not report anything for shared action IDs that do not exist.
+The usual error message is shown when a shared action cannot be destroyed::
+
+   Caught error type [...] ([...]): [...]
+
+Destroy shared actions having id 100 & 101::
+
+   testpmd> flow shared_action 0 destroy action_id 100 action_id 101
+
+Query shared actions
+~~~~~~~~~~~~~~~~~~~~
+``flow shared_action {port_id} query`` queries the shared action from its
+shared action ID (as returned by ``flow shared_action {port_id} create``).
+It is bound to ``rte_flow_shared_action_query()``::
+
+  flow shared_action {port_id} query {shared_action_id}
+
+Currently only rss shared action supported. If successful, it will show::
+
+   Shared RSS action:
+      refs:[...]
+
+Otherwise, it will complain either that shared action not found or that some
+error occurred::
+
+   Failed to find shared action #[...] on port [...]
+
+::
+
+   Caught error type [...] ([...]): [...]
+
+Query shared action having id 100::
+
+   testpmd> flow shared_action 0 query 100
 
 Sample QinQ flow rules
 ~~~~~~~~~~~~~~~~~~~~~~
-- 
2.26.2



More information about the dev mailing list