[dpdk-dev] [PATCH 3/3] lib/cmdline: Cross platform fixes for cmdline_flow

Georgios Katsikas george.dit at gmail.com
Fri Jan 12 22:01:27 CET 2018


Signed-off-by: Georgios Katsikas <george.dit at gmail.com>
---
 app/test-pmd/Makefile             |   2 +
 app/test-pmd/cmdline.c            |   4 +
 app/test-pmd/testpmd.h            |  14 -
 lib/librte_cmdline/Makefile       |   3 +
 lib/librte_cmdline/cmdline_flow.c | 555 ++++++--------------------------
 lib/librte_cmdline/cmdline_flow.h | 653 +++++++++++++++++++++++++++++++-------
 lib/librte_ether/rte_flow.c       |   4 +-
 lib/librte_ether/rte_flow.h       |  10 -
 8 files changed, 640 insertions(+), 605 deletions(-)

diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
index ec48773..26de9f3 100644
--- a/app/test-pmd/Makefile
+++ b/app/test-pmd/Makefile
@@ -38,6 +38,8 @@ endif
 
 ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
 
+LDLIBS += -lrte_cmdline
+
 ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y)
 LDLIBS += -lrte_pmd_bond
 endif
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 5b2e2ef..d1c6428 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -10921,7 +10921,11 @@ cmdline_parse_inst_t cmd_set_flow_director_flex_payload = {
 };
 
 /* Generic flow interface command. */
+#ifdef RTE_BUILD_SHARED_LIB
+cmdline_parse_inst_t cmd_flow;
+#else
 extern cmdline_parse_inst_t cmd_flow;
+#endif
 
 /* *** Classification Filters Control *** */
 /* *** Get symmetric hash enable per port *** */
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 303a9ec..71da7ef 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -498,20 +498,6 @@ 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_flow_validate(portid_t port_id,
-		       const struct rte_flow_attr *attr,
-		       const struct rte_flow_item *pattern,
-		       const struct rte_flow_action *actions);
-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_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
-int port_flow_flush(portid_t port_id);
-int port_flow_query(portid_t port_id, uint32_t rule,
-		    enum rte_flow_action_type action);
-void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
-int port_flow_isolate(portid_t port_id, int set);
 
 void rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id);
 void tx_ring_desc_display(portid_t port_id, queueid_t txq_id, uint16_t txd_id);
diff --git a/lib/librte_cmdline/Makefile b/lib/librte_cmdline/Makefile
index cf46b22..475b1f7 100644
--- a/lib/librte_cmdline/Makefile
+++ b/lib/librte_cmdline/Makefile
@@ -28,6 +28,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_CMDLINE) += cmdline_parse_portlist.c
 
 CFLAGS += -D_GNU_SOURCE
 LDLIBS += -lrte_eal
+ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
+LDLIBS += -lrte_ethdev
+endif
 
 # install includes
 INCS := cmdline.h cmdline_parse.h cmdline_parse_num.h cmdline_parse_ipaddr.h
diff --git a/lib/librte_cmdline/cmdline_flow.c b/lib/librte_cmdline/cmdline_flow.c
index b43c10f..e4a8230 100644
--- a/lib/librte_cmdline/cmdline_flow.c
+++ b/lib/librte_cmdline/cmdline_flow.c
@@ -43,21 +43,6 @@
 
 #include "cmdline_flow.h"
 
-int
-port_id_is_invalid(portid_t port_id, enum print_warning warning)
-{
-	if (port_id == (portid_t)RTE_PORT_ALL)
-		return 0;
-
-	if (rte_eth_dev_is_valid_port(port_id))
-		return 0;
-
-	if (warning == ENABLED_WARN)
-		printf("Invalid port %d\n", port_id);
-
-	return 1;
-}
-
 /** Remove and return last entry from argument stack. */
 const struct arg *
 pop_args(struct context *ctx)
@@ -134,7 +119,7 @@ strcmp_partial(const char *full, const char *partial, size_t partial_len)
  * location and whether the result must use network byte ordering.
  */
 int
-parse_prefix(struct context *ctx, const struct token *token,
+cmd_parse_prefix(struct context *ctx, const struct token *token,
 	     const char *str, unsigned int len,
 	     void *buf, unsigned int size)
 {
@@ -202,7 +187,7 @@ parse_prefix(struct context *ctx, const struct token *token,
 
 /** Default parsing function for token name matching. */
 int
-parse_default(struct context *ctx, const struct token *token,
+cmd_parse_default(struct context *ctx, const struct token *token,
 	      const char *str, unsigned int len,
 	      void *buf, unsigned int size)
 {
@@ -216,14 +201,14 @@ parse_default(struct context *ctx, const struct token *token,
 
 /** Parse flow command, initialize output buffer for subsequent tokens. */
 int
-parse_init(struct context *ctx, const struct token *token,
+cmd_parse_init(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)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Nothing else to do if there is no buffer. */
 	if (!out)
@@ -242,7 +227,7 @@ parse_init(struct context *ctx, const struct token *token,
 
 /** Parse tokens for validate/create commands. */
 int
-parse_vc(struct context *ctx, const struct token *token,
+cmd_parse_vc(struct context *ctx, const struct token *token,
 	 const char *str, unsigned int len,
 	 void *buf, unsigned int size)
 {
@@ -251,7 +236,7 @@ parse_vc(struct context *ctx, const struct token *token,
 	uint32_t data_size;
 
 	/* Token name must match. */
-	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Nothing else to do if there is no buffer. */
 	if (!out)
@@ -345,7 +330,7 @@ parse_vc(struct context *ctx, const struct token *token,
 
 /** Parse pattern item parameter type. */
 int
-parse_vc_spec(struct context *ctx, const struct token *token,
+cmd_parse_vc_spec(struct context *ctx, const struct token *token,
 	      const char *str, unsigned int len,
 	      void *buf, unsigned int size)
 {
@@ -357,7 +342,7 @@ parse_vc_spec(struct context *ctx, const struct token *token,
 
 	(void)size;
 	/* Token name must match. */
-	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Parse parameter types. */
 	switch (ctx->curr) {
@@ -407,7 +392,7 @@ parse_vc_spec(struct context *ctx, const struct token *token,
 
 /** Parse action configuration field. */
 int
-parse_vc_conf(struct context *ctx, const struct token *token,
+cmd_parse_vc_conf(struct context *ctx, const struct token *token,
 	      const char *str, unsigned int len,
 	      void *buf, unsigned int size)
 {
@@ -416,7 +401,7 @@ parse_vc_conf(struct context *ctx, const struct token *token,
 
 	(void)size;
 	/* Token name must match. */
-	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Nothing else to do if there is no buffer. */
 	if (!out)
@@ -438,7 +423,7 @@ parse_vc_conf(struct context *ctx, const struct token *token,
  * Valid tokens are queue indices and the "end" token.
  */
 int
-parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
+cmd_parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
 			  const char *str, unsigned int len,
 			  void *buf, unsigned int size)
 {
@@ -460,7 +445,7 @@ parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
 		return -1;
 	if (push_args(ctx, ARGS_ENTRY(struct rte_flow_action_rss, queue[i])))
 		return -1;
-	ret = parse_int(ctx, token, str, len, NULL, 0);
+	ret = cmd_parse_int(ctx, token, str, len, NULL, 0);
 	if (ret < 0) {
 		pop_args(ctx);
 		return -1;
@@ -479,14 +464,14 @@ parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
 
 /** Parse tokens for destroy command. */
 int
-parse_destroy(struct context *ctx, const struct token *token,
+cmd_parse_destroy(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)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Nothing else to do if there is no buffer. */
 	if (!out)
@@ -516,14 +501,14 @@ parse_destroy(struct context *ctx, const struct token *token,
 
 /** Parse tokens for flush command. */
 int
-parse_flush(struct context *ctx, const struct token *token,
+cmd_parse_flush(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)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Nothing else to do if there is no buffer. */
 	if (!out)
@@ -543,14 +528,14 @@ parse_flush(struct context *ctx, const struct token *token,
 
 /** Parse tokens for query command. */
 int
-parse_query(struct context *ctx, const struct token *token,
+cmd_parse_query(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)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Nothing else to do if there is no buffer. */
 	if (!out)
@@ -570,7 +555,7 @@ parse_query(struct context *ctx, const struct token *token,
 
 /** Parse action names. */
 int
-parse_action(struct context *ctx, const struct token *token,
+cmd_parse_action(struct context *ctx, const struct token *token,
 	     const char *str, unsigned int len,
 	     void *buf, unsigned int size)
 {
@@ -605,14 +590,14 @@ parse_action(struct context *ctx, const struct token *token,
 
 /** Parse tokens for list command. */
 int
-parse_list(struct context *ctx, const struct token *token,
+cmd_parse_list(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)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Nothing else to do if there is no buffer. */
 	if (!out)
@@ -642,14 +627,14 @@ parse_list(struct context *ctx, const struct token *token,
 
 /** Parse tokens for isolate command. */
 int
-parse_isolate(struct context *ctx, const struct token *token,
+cmd_parse_isolate(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)
+	if (cmd_parse_default(ctx, token, str, len, NULL, 0) < 0)
 		return -1;
 	/* Nothing else to do if there is no buffer. */
 	if (!out)
@@ -674,7 +659,7 @@ parse_isolate(struct context *ctx, const struct token *token,
  * storage location.
  */
 int
-parse_int(struct context *ctx, const struct token *token,
+cmd_parse_int(struct context *ctx, const struct token *token,
 	  const char *str, unsigned int len,
 	  void *buf, unsigned int size)
 {
@@ -750,7 +735,7 @@ parse_int(struct context *ctx, const struct token *token,
  * its length (in that order).
  */
 int
-parse_string(struct context *ctx, const struct token *token,
+cmd_parse_string(struct context *ctx, const struct token *token,
 	     const char *str, unsigned int len,
 	     void *buf, unsigned int size)
 {
@@ -772,12 +757,12 @@ parse_string(struct context *ctx, const struct token *token,
 		goto error;
 	if (!ctx->object)
 		return len;
-	/* Let parse_int() fill length information first. */
+	/* Let cmd_parse_int() fill length information first. */
 	ret = snprintf(tmp, sizeof(tmp), "%u", len);
 	if (ret < 0)
 		goto error;
 	push_args(ctx, arg_len);
-	ret = parse_int(ctx, token, tmp, ret, NULL, 0);
+	ret = cmd_parse_int(ctx, token, tmp, ret, NULL, 0);
 	if (ret < 0) {
 		pop_args(ctx);
 		goto error;
@@ -802,7 +787,7 @@ parse_string(struct context *ctx, const struct token *token,
  * location.
  */
 int
-parse_mac_addr(struct context *ctx, const struct token *token,
+cmd_parse_mac_addr(struct context *ctx, const struct token *token,
 	       const char *str, unsigned int len,
 	       void *buf, unsigned int size)
 {
@@ -843,7 +828,7 @@ parse_mac_addr(struct context *ctx, const struct token *token,
  * location.
  */
 int
-parse_ipv4_addr(struct context *ctx, const struct token *token,
+cmd_parse_ipv4_addr(struct context *ctx, const struct token *token,
 		const char *str, unsigned int len,
 		void *buf, unsigned int size)
 {
@@ -868,7 +853,7 @@ parse_ipv4_addr(struct context *ctx, const struct token *token,
 	if (ret != 1) {
 		/* Attempt integer parsing. */
 		push_args(ctx, arg);
-		return parse_int(ctx, token, str, len, buf, size);
+		return cmd_parse_int(ctx, token, str, len, buf, size);
 	}
 	if (!ctx->object)
 		return len;
@@ -889,7 +874,7 @@ parse_ipv4_addr(struct context *ctx, const struct token *token,
  * location.
  */
 int
-parse_ipv6_addr(struct context *ctx, const struct token *token,
+cmd_parse_ipv6_addr(struct context *ctx, const struct token *token,
 		const char *str, unsigned int len,
 		void *buf, unsigned int size)
 {
@@ -942,7 +927,7 @@ static const char *const boolean_name[] = {
  * location.
  */
 int
-parse_boolean(struct context *ctx, const struct token *token,
+cmd_parse_boolean(struct context *ctx, const struct token *token,
 	      const char *str, unsigned int len,
 	      void *buf, unsigned int size)
 {
@@ -960,13 +945,13 @@ parse_boolean(struct context *ctx, const struct token *token,
 	if (boolean_name[i])
 		str = i & 1 ? "1" : "0";
 	push_args(ctx, arg);
-	ret = parse_int(ctx, token, str, strlen(str), buf, size);
+	ret = cmd_parse_int(ctx, token, str, strlen(str), buf, size);
 	return ret > 0 ? (int)len : ret;
 }
 
 /** Parse port and update context. */
 int
-parse_port(struct context *ctx, const struct token *token,
+cmd_parse_port(struct context *ctx, const struct token *token,
 	   const char *str, unsigned int len,
 	   void *buf, unsigned int size)
 {
@@ -981,7 +966,7 @@ parse_port(struct context *ctx, const struct token *token,
 		ctx->objmask = NULL;
 		size = sizeof(*out);
 	}
-	ret = parse_int(ctx, token, str, len, out, size);
+	ret = cmd_parse_int(ctx, token, str, len, out, size);
 	if (ret >= 0)
 		ctx->port = out->port;
 	if (!buf)
@@ -1102,9 +1087,6 @@ comp_vc_action_rss_queue(struct context *ctx, const struct token *token,
 /** Internal context. */
 static struct context cmd_flow_context;
 
-/** Global parser instance (cmdline API). */
-cmdline_parse_inst_t cmd_flow;
-
 /** Initialize context. */
 void
 cmd_flow_context_init(struct context *ctx)
@@ -1173,7 +1155,7 @@ cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
 		if (next->call)
 			tmp = next->call(ctx, next, src, len, result, size);
 		else
-			tmp = parse_default(ctx, next, src, len, result, size);
+			tmp = cmd_parse_default(ctx, next, src, len, result, size);
 		if (tmp == -1 || tmp != len)
 			continue;
 		token = next;
@@ -1328,6 +1310,64 @@ cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
 	*hdr = &cmd_flow_token_hdr;
 }
 
+/* Generic flow management functions. */
+
+/** Compute storage space needed by item specification. */
+void
+flow_item_spec_size(const struct rte_flow_item *item,
+		    size_t *size, size_t *pad)
+{
+	if (!item->spec) {
+		*size = 0;
+		goto empty;
+	}
+	switch (item->type) {
+		union {
+			const struct rte_flow_item_raw *raw;
+		} spec;
+
+	/* Not a fall-through */
+	case RTE_FLOW_ITEM_TYPE_RAW:
+		spec.raw = item->spec;
+		*size = offsetof(struct rte_flow_item_raw, pattern) +
+			spec.raw->length * sizeof(*spec.raw->pattern);
+		break;
+	default:
+		*size = flow_item[item->type].size;
+		break;
+	}
+empty:
+	*pad = RTE_ALIGN_CEIL(*size, sizeof(double)) - *size;
+}
+
+/** Compute storage space needed by action configuration. */
+void
+flow_action_conf_size(const struct rte_flow_action *action,
+		      size_t *size, size_t *pad)
+{
+	if (!action->conf) {
+		*size = 0;
+		goto empty;
+	}
+	switch (action->type) {
+		union {
+			const struct rte_flow_action_rss *rss;
+		} conf;
+
+	/* Not a fall-through. */
+	case RTE_FLOW_ACTION_TYPE_RSS:
+		conf.rss = action->conf;
+		*size = offsetof(struct rte_flow_action_rss, queue) +
+			conf.rss->num * sizeof(*conf.rss->queue);
+		break;
+	default:
+		*size = flow_action[action->type].size;
+		break;
+	}
+empty:
+	*pad = RTE_ALIGN_CEIL(*size, sizeof(double)) - *size;
+}
+
 /** Dispatch parsed buffer to function calls. */
 void
 cmd_flow_parsed(const struct buffer *in)
@@ -1374,7 +1414,7 @@ cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2)
 		cmd_flow_parsed(arg0);
 }
 
-/** Global parser instance (cmdline API). */
+/** Global parser instance (properly initialized). */
 cmdline_parse_inst_t cmd_flow = {
 	.f = cmd_flow_cb,
 	.data = NULL, /**< Unused. */
@@ -1383,408 +1423,3 @@ cmdline_parse_inst_t cmd_flow = {
 		NULL,
 	}, /**< Tokens are returned by cmd_flow_tok(). */
 };
-
-/* Generic flow management functions. */
-
-/** Generate a port_flow entry from attributes/pattern/actions. */
-static struct port_flow *
-port_flow_new(const struct rte_flow_attr *attr,
-	      const struct rte_flow_item *pattern,
-	      const struct rte_flow_action *actions)
-{
-	const struct rte_flow_item *item;
-	const struct rte_flow_action *action;
-	struct port_flow *pf = NULL;
-	size_t tmp;
-	size_t pad;
-	size_t off1 = 0;
-	size_t off2 = 0;
-	int err = ENOTSUP;
-
-store:
-	item = pattern;
-	if (pf)
-		pf->pattern = (void *)&pf->data[off1];
-	do {
-		struct rte_flow_item *dst = NULL;
-
-		if ((unsigned int)item->type >= RTE_DIM(flow_item) ||
-		    !flow_item[item->type].name)
-			goto notsup;
-		if (pf)
-			dst = memcpy(pf->data + off1, item, sizeof(*item));
-		off1 += sizeof(*item);
-		flow_item_spec_size(item, &tmp, &pad);
-		if (item->spec) {
-			if (pf)
-				dst->spec = memcpy(pf->data + off2,
-						   item->spec, tmp);
-			off2 += tmp + pad;
-		}
-		if (item->last) {
-			if (pf)
-				dst->last = memcpy(pf->data + off2,
-						   item->last, tmp);
-			off2 += tmp + pad;
-		}
-		if (item->mask) {
-			if (pf)
-				dst->mask = memcpy(pf->data + off2,
-						   item->mask, tmp);
-			off2 += tmp + pad;
-		}
-		off2 = RTE_ALIGN_CEIL(off2, sizeof(double));
-	} while ((item++)->type != RTE_FLOW_ITEM_TYPE_END);
-	off1 = RTE_ALIGN_CEIL(off1, sizeof(double));
-	action = actions;
-	if (pf)
-		pf->actions = (void *)&pf->data[off1];
-	do {
-		struct rte_flow_action *dst = NULL;
-
-		if ((unsigned int)action->type >= RTE_DIM(flow_action) ||
-		    !flow_action[action->type].name)
-			goto notsup;
-		if (pf)
-			dst = memcpy(pf->data + off1, action, sizeof(*action));
-		off1 += sizeof(*action);
-		flow_action_conf_size(action, &tmp, &pad);
-		if (action->conf) {
-			if (pf)
-				dst->conf = memcpy(pf->data + off2,
-						   action->conf, tmp);
-			off2 += tmp + pad;
-		}
-		off2 = RTE_ALIGN_CEIL(off2, sizeof(double));
-	} while ((action++)->type != RTE_FLOW_ACTION_TYPE_END);
-	if (pf != NULL)
-		return pf;
-	off1 = RTE_ALIGN_CEIL(off1, sizeof(double));
-	tmp = RTE_ALIGN_CEIL(offsetof(struct port_flow, data), sizeof(double));
-	pf = calloc(1, tmp + off1 + off2);
-	if (pf == NULL)
-		err = errno;
-	else {
-		*pf = (const struct port_flow){
-			.size = tmp + off1 + off2,
-			.attr = *attr,
-		};
-		tmp -= offsetof(struct port_flow, data);
-		off2 = tmp + off1;
-		off1 = tmp;
-		goto store;
-	}
-notsup:
-	rte_errno = err;
-	return NULL;
-}
-
-/** Print a message out of a flow error. */
-static int
-port_flow_complain(struct rte_flow_error *error)
-{
-	static const char *const errstrlist[] = {
-		[RTE_FLOW_ERROR_TYPE_NONE] = "no error",
-		[RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = "cause unspecified",
-		[RTE_FLOW_ERROR_TYPE_HANDLE] = "flow rule (handle)",
-		[RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = "group field",
-		[RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = "priority field",
-		[RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = "ingress field",
-		[RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = "egress field",
-		[RTE_FLOW_ERROR_TYPE_ATTR] = "attributes structure",
-		[RTE_FLOW_ERROR_TYPE_ITEM_NUM] = "pattern length",
-		[RTE_FLOW_ERROR_TYPE_ITEM] = "specific pattern item",
-		[RTE_FLOW_ERROR_TYPE_ACTION_NUM] = "number of actions",
-		[RTE_FLOW_ERROR_TYPE_ACTION] = "specific action",
-	};
-	const char *errstr;
-	char buf[32];
-	int err = rte_errno;
-
-	if ((unsigned int)error->type >= RTE_DIM(errstrlist) ||
-	    !errstrlist[error->type])
-		errstr = "unknown type";
-	else
-		errstr = errstrlist[error->type];
-	printf("Caught error type %d (%s): %s%s\n",
-	       error->type, errstr,
-	       error->cause ? (snprintf(buf, sizeof(buf), "cause: %p, ",
-					error->cause), buf) : "",
-	       error->message ? error->message : "(no stated reason)");
-	return -err;
-}
-
-/** Validate flow rule. */
-int
-port_flow_validate(portid_t port_id,
-		   const struct rte_flow_attr *attr,
-		   const struct rte_flow_item *pattern,
-		   const struct rte_flow_action *actions)
-{
-	struct rte_flow_error error;
-
-	/* Poisoning to make sure PMDs update it in case of error. */
-	memset(&error, 0x11, sizeof(error));
-	if (rte_flow_validate(port_id, attr, pattern, actions, &error))
-		return port_flow_complain(&error);
-	printf("Flow rule validated\n");
-	return 0;
-}
-
-/** Create flow rule. */
-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)
-{
-	struct rte_flow *flow;
-	struct rte_port *port;
-	struct port_flow *pf;
-	uint32_t id;
-	struct rte_flow_error error;
-
-	/* Poisoning to make sure PMDs update it in case of error. */
-	memset(&error, 0x22, sizeof(error));
-	flow = rte_flow_create(port_id, attr, pattern, actions, &error);
-	if (!flow)
-		return port_flow_complain(&error);
-	port = &ports[port_id];
-	if (port->flow_list) {
-		if (port->flow_list->id == UINT32_MAX) {
-			printf("Highest rule ID is already assigned, delete"
-			       " it first");
-			rte_flow_destroy(port_id, flow, NULL);
-			return -ENOMEM;
-		}
-		id = port->flow_list->id + 1;
-	} else
-		id = 0;
-	pf = port_flow_new(attr, pattern, actions);
-	if (!pf) {
-		int err = rte_errno;
-
-		printf("Cannot allocate flow: %s\n", rte_strerror(err));
-		rte_flow_destroy(port_id, flow, NULL);
-		return -err;
-	}
-	pf->next = port->flow_list;
-	pf->id = id;
-	pf->flow = flow;
-	port->flow_list = pf;
-	printf("Flow rule #%u created\n", pf->id);
-	return 0;
-}
-
-/** Destroy a number of flow rules. */
-int
-port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule)
-{
-	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];
-	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 PMDs update it in case
-			 * of error.
-			 */
-			memset(&error, 0x33, sizeof(error));
-			if (rte_flow_destroy(port_id, pf->flow, &error)) {
-				ret = port_flow_complain(&error);
-				continue;
-			}
-			printf("Flow rule #%u destroyed\n", pf->id);
-			*tmp = pf->next;
-			free(pf);
-			break;
-		}
-		if (i == n)
-			tmp = &(*tmp)->next;
-		++c;
-	}
-	return ret;
-}
-
-/** Remove all flow rules. */
-int
-port_flow_flush(portid_t port_id)
-{
-	struct rte_flow_error error;
-	struct rte_port *port;
-	int ret = 0;
-
-	/* Poisoning to make sure PMDs update it in case of error. */
-	memset(&error, 0x44, sizeof(error));
-	if (rte_flow_flush(port_id, &error)) {
-		ret = port_flow_complain(&error);
-		if (port_id_is_invalid(port_id, DISABLED_WARN) ||
-		    port_id == (portid_t)RTE_PORT_ALL)
-			return ret;
-	}
-	port = &ports[port_id];
-	while (port->flow_list) {
-		struct port_flow *pf = port->flow_list->next;
-
-		free(port->flow_list);
-		port->flow_list = pf;
-	}
-	return ret;
-}
-
-/** Query a flow rule. */
-int
-port_flow_query(portid_t port_id, uint32_t rule,
-		enum rte_flow_action_type action)
-{
-	struct rte_flow_error error;
-	struct rte_port *port;
-	struct port_flow *pf;
-	const char *name;
-	union {
-		struct rte_flow_query_count count;
-	} query;
-
-	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
-	    port_id == (portid_t)RTE_PORT_ALL)
-		return -EINVAL;
-	port = &ports[port_id];
-	for (pf = port->flow_list; pf; pf = pf->next)
-		if (pf->id == rule)
-			break;
-	if (!pf) {
-		printf("Flow rule #%u not found\n", rule);
-		return -ENOENT;
-	}
-	if ((unsigned int)action >= RTE_DIM(flow_action) ||
-	    !flow_action[action].name)
-		name = "unknown";
-	else
-		name = flow_action[action].name;
-	switch (action) {
-	case RTE_FLOW_ACTION_TYPE_COUNT:
-		break;
-	default:
-		printf("Cannot query action type %d (%s)\n", action, name);
-		return -ENOTSUP;
-	}
-	/* Poisoning to make sure PMDs update it in case of error. */
-	memset(&error, 0x55, sizeof(error));
-	memset(&query, 0, sizeof(query));
-	if (rte_flow_query(port_id, pf->flow, action, &query, &error))
-		return port_flow_complain(&error);
-	switch (action) {
-	case RTE_FLOW_ACTION_TYPE_COUNT:
-		printf("%s:\n"
-		       " hits_set: %u\n"
-		       " bytes_set: %u\n"
-		       " hits: %" PRIu64 "\n"
-		       " bytes: %" PRIu64 "\n",
-		       name,
-		       query.count.hits_set,
-		       query.count.bytes_set,
-		       query.count.hits,
-		       query.count.bytes);
-		break;
-	default:
-		printf("Cannot display result for action type %d (%s)\n",
-		       action, name);
-		break;
-	}
-	return 0;
-}
-
-/** List flow rules. */
-void
-port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])
-{
-	struct rte_port *port;
-	struct port_flow *pf;
-	struct port_flow *list = NULL;
-	uint32_t i;
-
-	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
-	    port_id == (portid_t)RTE_PORT_ALL)
-		return;
-	port = &ports[port_id];
-	if (!port->flow_list)
-		return;
-	/* Sort flows by group, priority and ID. */
-	for (pf = port->flow_list; pf != NULL; pf = pf->next) {
-		struct port_flow **tmp;
-
-		if (n) {
-			/* Filter out unwanted groups. */
-			for (i = 0; i != n; ++i)
-				if (pf->attr.group == group[i])
-					break;
-			if (i == n)
-				continue;
-		}
-		tmp = &list;
-		while (*tmp &&
-		       (pf->attr.group > (*tmp)->attr.group ||
-			(pf->attr.group == (*tmp)->attr.group &&
-			 pf->attr.priority > (*tmp)->attr.priority) ||
-			(pf->attr.group == (*tmp)->attr.group &&
-			 pf->attr.priority == (*tmp)->attr.priority &&
-			 pf->id > (*tmp)->id)))
-			tmp = &(*tmp)->tmp;
-		pf->tmp = *tmp;
-		*tmp = pf;
-	}
-	printf("ID\tGroup\tPrio\tAttr\tRule\n");
-	for (pf = list; pf != NULL; pf = pf->tmp) {
-		const struct rte_flow_item *item = pf->pattern;
-		const struct rte_flow_action *action = pf->actions;
-
-		printf("%" PRIu32 "\t%" PRIu32 "\t%" PRIu32 "\t%c%c\t",
-		       pf->id,
-		       pf->attr.group,
-		       pf->attr.priority,
-		       pf->attr.ingress ? 'i' : '-',
-		       pf->attr.egress ? 'e' : '-');
-		while (item->type != RTE_FLOW_ITEM_TYPE_END) {
-			if (item->type != RTE_FLOW_ITEM_TYPE_VOID)
-				printf("%s ", flow_item[item->type].name);
-			++item;
-		}
-		printf("=>");
-		while (action->type != RTE_FLOW_ACTION_TYPE_END) {
-			if (action->type != RTE_FLOW_ACTION_TYPE_VOID)
-				printf(" %s", flow_action[action->type].name);
-			++action;
-		}
-		printf("\n");
-	}
-}
-
-/** Restrict ingress traffic to the defined flow rules. */
-int
-port_flow_isolate(portid_t port_id, int set)
-{
-	struct rte_flow_error error;
-
-	/* Poisoning to make sure PMDs update it in case of error. */
-	memset(&error, 0x66, sizeof(error));
-	if (rte_flow_isolate(port_id, set, &error))
-		return port_flow_complain(&error);
-	printf("Ingress traffic on port %u is %s to the defined flow rules\n",
-	       port_id,
-	       set ? "now restricted" : "not restricted anymore");
-	return 0;
-}
diff --git a/lib/librte_cmdline/cmdline_flow.h b/lib/librte_cmdline/cmdline_flow.h
index 7066254..9cb8045 100644
--- a/lib/librte_cmdline/cmdline_flow.h
+++ b/lib/librte_cmdline/cmdline_flow.h
@@ -814,8 +814,21 @@ static const struct {
 };
 
 /** Helper functions for parsing. */
-int port_id_is_invalid(portid_t port_id,
-			enum print_warning warning);
+inline int
+port_id_is_invalid(portid_t port_id, enum print_warning warning)
+{
+	if (port_id == (portid_t)RTE_PORT_ALL)
+		return 0;
+
+	if (rte_eth_dev_is_valid_port(port_id))
+		return 0;
+
+	if (warning == ENABLED_WARN)
+		printf("Invalid port %d\n", port_id);
+
+	return 1;
+}
+
 const struct arg *pop_args(struct context *ctx);
 int push_args(struct context *ctx,
 			const struct arg *arg);
@@ -823,68 +836,75 @@ size_t arg_entry_bf_fill(void *dst, uintmax_t val,
 			const struct arg *arg);
 int strcmp_partial(const char *full,
 			const char *partial, size_t partial_len);
+/** Compute storage space needed by item specification. */
+void flow_item_spec_size(const struct rte_flow_item *item,
+			size_t *size, size_t *pad);
+
+/** Compute storage space needed by action configuration. */
+void flow_action_conf_size(const struct rte_flow_action *action,
+			size_t *size, size_t *pad);
 
 /** Parsing functions. */
-int parse_prefix(struct context *, const struct token *,
+int cmd_parse_prefix(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_default(struct context *ctx,
+int cmd_parse_default(struct context *ctx,
 			const struct token *token,
 			const char *str, unsigned int len,
 			void *buf, unsigned int size);
-int parse_init(struct context *, const struct token *,
+int cmd_parse_init(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_vc(struct context *, const struct token *,
+int cmd_parse_vc(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_vc_spec(struct context *, const struct token *,
+int cmd_parse_vc_spec(struct context *, const struct token *,
 			const char *, unsigned int, void *,
 			unsigned int);
-int parse_vc_conf(struct context *, const struct token *,
+int cmd_parse_vc_conf(struct context *, const struct token *,
 			const char *, unsigned int, void *,
 			unsigned int);
-int parse_vc_action_rss_queue(struct context *,
+int cmd_parse_vc_action_rss_queue(struct context *,
 			const struct token *,
 			const char *, unsigned int, void *,
 			unsigned int);
-int parse_destroy(struct context *, const struct token *,
+int cmd_parse_destroy(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_flush(struct context *, const struct token *,
+int cmd_parse_flush(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_query(struct context *, const struct token *,
+int cmd_parse_query(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_action(struct context *, const struct token *,
+int cmd_parse_action(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_list(struct context *, const struct token *,
+int cmd_parse_list(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_isolate(struct context *, const struct token *,
+int cmd_parse_isolate(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_int(struct context *, const struct token *,
+int cmd_parse_int(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_string(struct context *, const struct token *,
+int cmd_parse_string(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_mac_addr(struct context *, const struct token *,
+int cmd_parse_mac_addr(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_ipv4_addr(struct context *, const struct token *,
+int cmd_parse_ipv4_addr(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_ipv6_addr(struct context *, const struct token *,
+int cmd_parse_ipv6_addr(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_boolean(struct context *, const struct token *,
+int cmd_parse_boolean(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
-int parse_port(struct context *, const struct token *,
+int cmd_parse_port(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
 
@@ -920,23 +940,418 @@ void cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
 void cmd_flow_parsed(const struct buffer *in);
 void cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2);
 
-/** Generic flow management functions. */
-int port_flow_validate(portid_t port_id,
-			const struct rte_flow_attr *attr,
-			const struct rte_flow_item *pattern,
-			const struct rte_flow_action *actions);
-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_flow_destroy(portid_t port_id, uint32_t n,
-			const uint32_t *rule);
-int port_flow_flush(portid_t port_id);
-int port_flow_query(portid_t port_id, uint32_t rule,
-		    enum rte_flow_action_type action);
-void port_flow_list(portid_t port_id, uint32_t n,
-			const uint32_t *group);
-int port_flow_isolate(portid_t port_id, int set);
+/**
+ * Generic flow management functions.
+ *
+ * Allow the creation, validation, insertion, query,
+ * list, and deletion of a NIC's flows.
+ */
+
+/** Generate a port_flow entry from attributes/pattern/actions. */
+static inline struct port_flow *
+port_flow_new(const struct rte_flow_attr *attr,
+	      const struct rte_flow_item *pattern,
+	      const struct rte_flow_action *actions)
+{
+	const struct rte_flow_item *item;
+	const struct rte_flow_action *action;
+	struct port_flow *pf = NULL;
+	size_t tmp;
+	size_t pad;
+	size_t off1 = 0;
+	size_t off2 = 0;
+	int err = ENOTSUP;
+
+store:
+	item = pattern;
+	if (pf)
+		pf->pattern = (void *)&pf->data[off1];
+	do {
+		struct rte_flow_item *dst = NULL;
+
+		if ((unsigned int)item->type >= RTE_DIM(flow_item) ||
+		    !flow_item[item->type].name)
+			goto notsup;
+		if (pf)
+			dst = memcpy(pf->data + off1, item, sizeof(*item));
+		off1 += sizeof(*item);
+		flow_item_spec_size(item, &tmp, &pad);
+		if (item->spec) {
+			if (pf)
+				dst->spec = memcpy(pf->data + off2,
+						   item->spec, tmp);
+			off2 += tmp + pad;
+		}
+		if (item->last) {
+			if (pf)
+				dst->last = memcpy(pf->data + off2,
+						   item->last, tmp);
+			off2 += tmp + pad;
+		}
+		if (item->mask) {
+			if (pf)
+				dst->mask = memcpy(pf->data + off2,
+						   item->mask, tmp);
+			off2 += tmp + pad;
+		}
+		off2 = RTE_ALIGN_CEIL(off2, sizeof(double));
+	} while ((item++)->type != RTE_FLOW_ITEM_TYPE_END);
+	off1 = RTE_ALIGN_CEIL(off1, sizeof(double));
+	action = actions;
+	if (pf)
+		pf->actions = (void *)&pf->data[off1];
+	do {
+		struct rte_flow_action *dst = NULL;
+
+		if ((unsigned int)action->type >= RTE_DIM(flow_action) ||
+		    !flow_action[action->type].name)
+			goto notsup;
+		if (pf)
+			dst = memcpy(pf->data + off1, action, sizeof(*action));
+		off1 += sizeof(*action);
+		flow_action_conf_size(action, &tmp, &pad);
+		if (action->conf) {
+			if (pf)
+				dst->conf = memcpy(pf->data + off2,
+						   action->conf, tmp);
+			off2 += tmp + pad;
+		}
+		off2 = RTE_ALIGN_CEIL(off2, sizeof(double));
+	} while ((action++)->type != RTE_FLOW_ACTION_TYPE_END);
+	if (pf != NULL)
+		return pf;
+	off1 = RTE_ALIGN_CEIL(off1, sizeof(double));
+	tmp = RTE_ALIGN_CEIL(offsetof(struct port_flow, data), sizeof(double));
+	pf = calloc(1, tmp + off1 + off2);
+	if (pf == NULL)
+		err = errno;
+	else {
+		*pf = (const struct port_flow){
+			.size = tmp + off1 + off2,
+			.attr = *attr,
+		};
+		tmp -= offsetof(struct port_flow, data);
+		off2 = tmp + off1;
+		off1 = tmp;
+		goto store;
+	}
+notsup:
+	rte_errno = err;
+	return NULL;
+}
+
+/** Print a message out of a flow error. */
+inline int
+port_flow_complain(struct rte_flow_error *error)
+{
+	static const char *const errstrlist[] = {
+		[RTE_FLOW_ERROR_TYPE_NONE] = "no error",
+		[RTE_FLOW_ERROR_TYPE_UNSPECIFIED] = "cause unspecified",
+		[RTE_FLOW_ERROR_TYPE_HANDLE] = "flow rule (handle)",
+		[RTE_FLOW_ERROR_TYPE_ATTR_GROUP] = "group field",
+		[RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY] = "priority field",
+		[RTE_FLOW_ERROR_TYPE_ATTR_INGRESS] = "ingress field",
+		[RTE_FLOW_ERROR_TYPE_ATTR_EGRESS] = "egress field",
+		[RTE_FLOW_ERROR_TYPE_ATTR] = "attributes structure",
+		[RTE_FLOW_ERROR_TYPE_ITEM_NUM] = "pattern length",
+		[RTE_FLOW_ERROR_TYPE_ITEM] = "specific pattern item",
+		[RTE_FLOW_ERROR_TYPE_ACTION_NUM] = "number of actions",
+		[RTE_FLOW_ERROR_TYPE_ACTION] = "specific action",
+	};
+	const char *errstr;
+	char buf[32];
+	int err = rte_errno;
+
+	if ((unsigned int)error->type >= RTE_DIM(errstrlist) ||
+	    !errstrlist[error->type])
+		errstr = "unknown type";
+	else
+		errstr = errstrlist[error->type];
+	printf("Caught error type %d (%s): %s%s\n",
+	       error->type, errstr,
+	       error->cause ? (snprintf(buf, sizeof(buf), "cause: %p, ",
+					error->cause), buf) : "",
+	       error->message ? error->message : "(no stated reason)");
+	return -err;
+}
+
+/** Validate flow rule. */
+inline int
+port_flow_validate(portid_t port_id,
+		   const struct rte_flow_attr *attr,
+		   const struct rte_flow_item *pattern,
+		   const struct rte_flow_action *actions)
+{
+	struct rte_flow_error error;
+
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x11, sizeof(error));
+	if (rte_flow_validate(port_id, attr, pattern, actions, &error))
+		return port_flow_complain(&error);
+	printf("Flow rule validated\n");
+	return 0;
+}
+
+/** Create flow rule. */
+static inline 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)
+{
+	struct rte_flow *flow;
+	struct rte_port *port;
+	struct port_flow *pf;
+	uint32_t id;
+	struct rte_flow_error error;
+
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x22, sizeof(error));
+	flow = rte_flow_create(port_id, attr, pattern, actions, &error);
+	if (!flow)
+		return port_flow_complain(&error);
+	port = &ports[port_id];
+	if (port->flow_list) {
+		if (port->flow_list->id == UINT32_MAX) {
+			printf("Highest rule ID is already assigned, delete"
+			       " it first");
+			rte_flow_destroy(port_id, flow, NULL);
+			return -ENOMEM;
+		}
+		id = port->flow_list->id + 1;
+	} else
+		id = 0;
+	pf = port_flow_new(attr, pattern, actions);
+	if (!pf) {
+		int err = rte_errno;
+
+		printf("Cannot allocate flow: %s\n", rte_strerror(err));
+		rte_flow_destroy(port_id, flow, NULL);
+		return -err;
+	}
+	pf->next = port->flow_list;
+	pf->id = id;
+	pf->flow = flow;
+	port->flow_list = pf;
+	printf("Flow rule #%u created\n", pf->id);
+	return 0;
+}
+
+/** Destroy a number of flow rules. */
+inline int
+port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule)
+{
+	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];
+	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 PMDs update it in case
+			 * of error.
+			 */
+			memset(&error, 0x33, sizeof(error));
+			if (rte_flow_destroy(port_id, pf->flow, &error)) {
+				ret = port_flow_complain(&error);
+				continue;
+			}
+			printf("Flow rule #%u destroyed\n", pf->id);
+			*tmp = pf->next;
+			free(pf);
+			break;
+		}
+		if (i == n)
+			tmp = &(*tmp)->next;
+		++c;
+	}
+	return ret;
+}
+
+/** Remove all flow rules. */
+inline int
+port_flow_flush(portid_t port_id)
+{
+	struct rte_flow_error error;
+	struct rte_port *port;
+	int ret = 0;
+
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x44, sizeof(error));
+	if (rte_flow_flush(port_id, &error)) {
+		ret = port_flow_complain(&error);
+		if (port_id_is_invalid(port_id, DISABLED_WARN) ||
+		    port_id == (portid_t)RTE_PORT_ALL)
+			return ret;
+	}
+	port = &ports[port_id];
+	while (port->flow_list) {
+		struct port_flow *pf = port->flow_list->next;
+
+		free(port->flow_list);
+		port->flow_list = pf;
+	}
+	return ret;
+}
+
+/** Query a flow rule. */
+static inline int
+port_flow_query(portid_t port_id, uint32_t rule,
+		enum rte_flow_action_type action)
+{
+	struct rte_flow_error error;
+	struct rte_port *port;
+	struct port_flow *pf;
+	const char *name;
+	union {
+		struct rte_flow_query_count count;
+	} query;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	for (pf = port->flow_list; pf; pf = pf->next)
+		if (pf->id == rule)
+			break;
+	if (!pf) {
+		printf("Flow rule #%u not found\n", rule);
+		return -ENOENT;
+	}
+	if ((unsigned int)action >= RTE_DIM(flow_action) ||
+	    !flow_action[action].name)
+		name = "unknown";
+	else
+		name = flow_action[action].name;
+	switch (action) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		break;
+	default:
+		printf("Cannot query action type %d (%s)\n", action, name);
+		return -ENOTSUP;
+	}
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x55, sizeof(error));
+	memset(&query, 0, sizeof(query));
+	if (rte_flow_query(port_id, pf->flow, action, &query, &error))
+		return port_flow_complain(&error);
+	switch (action) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		printf("%s:\n"
+		       " hits_set: %u\n"
+		       " bytes_set: %u\n"
+		       " hits: %" PRIu64 "\n"
+		       " bytes: %" PRIu64 "\n",
+		       name,
+		       query.count.hits_set,
+		       query.count.bytes_set,
+		       query.count.hits,
+		       query.count.bytes);
+		break;
+	default:
+		printf("Cannot display result for action type %d (%s)\n",
+		       action, name);
+		break;
+	}
+	return 0;
+}
+
+/** List flow rules. */
+static inline void
+port_flow_list(portid_t port_id, uint32_t n, const uint32_t group[n])
+{
+	struct rte_port *port;
+	struct port_flow *pf;
+	struct port_flow *list = NULL;
+	uint32_t i;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return;
+	port = &ports[port_id];
+	if (!port->flow_list)
+		return;
+	/* Sort flows by group, priority and ID. */
+	for (pf = port->flow_list; pf != NULL; pf = pf->next) {
+		struct port_flow **tmp;
+
+		if (n) {
+			/* Filter out unwanted groups. */
+			for (i = 0; i != n; ++i)
+				if (pf->attr.group == group[i])
+					break;
+			if (i == n)
+				continue;
+		}
+		tmp = &list;
+		while (*tmp &&
+		       (pf->attr.group > (*tmp)->attr.group ||
+			(pf->attr.group == (*tmp)->attr.group &&
+			 pf->attr.priority > (*tmp)->attr.priority) ||
+			(pf->attr.group == (*tmp)->attr.group &&
+			 pf->attr.priority == (*tmp)->attr.priority &&
+			 pf->id > (*tmp)->id)))
+			tmp = &(*tmp)->tmp;
+		pf->tmp = *tmp;
+		*tmp = pf;
+	}
+	printf("ID\tGroup\tPrio\tAttr\tRule\n");
+	for (pf = list; pf != NULL; pf = pf->tmp) {
+		const struct rte_flow_item *item = pf->pattern;
+		const struct rte_flow_action *action = pf->actions;
+
+		printf("%" PRIu32 "\t%" PRIu32 "\t%" PRIu32 "\t%c%c\t",
+		       pf->id,
+		       pf->attr.group,
+		       pf->attr.priority,
+		       pf->attr.ingress ? 'i' : '-',
+		       pf->attr.egress ? 'e' : '-');
+		while (item->type != RTE_FLOW_ITEM_TYPE_END) {
+			if (item->type != RTE_FLOW_ITEM_TYPE_VOID)
+				printf("%s ", flow_item[item->type].name);
+			++item;
+		}
+		printf("=>");
+		while (action->type != RTE_FLOW_ACTION_TYPE_END) {
+			if (action->type != RTE_FLOW_ACTION_TYPE_VOID)
+				printf(" %s", flow_action[action->type].name);
+			++action;
+		}
+		printf("\n");
+	}
+}
+
+/** Restrict ingress traffic to the defined flow rules. */
+inline int
+port_flow_isolate(portid_t port_id, int set)
+{
+	struct rte_flow_error error;
+
+	/* Poisoning to make sure PMDs update it in case of error. */
+	memset(&error, 0x66, sizeof(error));
+	if (rte_flow_isolate(port_id, set, &error))
+		return port_flow_complain(&error);
+	printf("Ingress traffic on port %u is %s to the defined flow rules\n",
+	       port_id,
+	       set ? "now restricted" : "not restricted anymore");
+	return 0;
+}
+
+/** Global parser instance (cmdline API). */
+extern cmdline_parse_inst_t cmd_flow;
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -956,84 +1371,84 @@ static const struct token token_list[] = {
 		.name = "{int}",
 		.type = "INTEGER",
 		.help = "integer value",
-		.call = parse_int,
+		.call = cmd_parse_int,
 		.comp = comp_none,
 	},
 	[UNSIGNED] = {
 		.name = "{unsigned}",
 		.type = "UNSIGNED",
 		.help = "unsigned integer value",
-		.call = parse_int,
+		.call = cmd_parse_int,
 		.comp = comp_none,
 	},
 	[PREFIX] = {
 		.name = "{prefix}",
 		.type = "PREFIX",
 		.help = "prefix length for bit-mask",
-		.call = parse_prefix,
+		.call = cmd_parse_prefix,
 		.comp = comp_none,
 	},
 	[BOOLEAN] = {
 		.name = "{boolean}",
 		.type = "BOOLEAN",
 		.help = "any boolean value",
-		.call = parse_boolean,
+		.call = cmd_parse_boolean,
 		.comp = comp_boolean,
 	},
 	[STRING] = {
 		.name = "{string}",
 		.type = "STRING",
 		.help = "fixed string",
-		.call = parse_string,
+		.call = cmd_parse_string,
 		.comp = comp_none,
 	},
 	[MAC_ADDR] = {
 		.name = "{MAC address}",
 		.type = "MAC-48",
 		.help = "standard MAC address notation",
-		.call = parse_mac_addr,
+		.call = cmd_parse_mac_addr,
 		.comp = comp_none,
 	},
 	[IPV4_ADDR] = {
 		.name = "{IPv4 address}",
 		.type = "IPV4 ADDRESS",
 		.help = "standard IPv4 address notation",
-		.call = parse_ipv4_addr,
+		.call = cmd_parse_ipv4_addr,
 		.comp = comp_none,
 	},
 	[IPV6_ADDR] = {
 		.name = "{IPv6 address}",
 		.type = "IPV6 ADDRESS",
 		.help = "standard IPv6 address notation",
-		.call = parse_ipv6_addr,
+		.call = cmd_parse_ipv6_addr,
 		.comp = comp_none,
 	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
 		.help = "rule identifier",
-		.call = parse_int,
+		.call = cmd_parse_int,
 		.comp = comp_rule_id,
 	},
 	[PORT_ID] = {
 		.name = "{port_id}",
 		.type = "PORT ID",
 		.help = "port identifier",
-		.call = parse_port,
+		.call = cmd_parse_port,
 		.comp = comp_port,
 	},
 	[GROUP_ID] = {
 		.name = "{group_id}",
 		.type = "GROUP ID",
 		.help = "group identifier",
-		.call = parse_int,
+		.call = cmd_parse_int,
 		.comp = comp_none,
 	},
 	[PRIORITY_LEVEL] = {
 		.name = "{level}",
 		.type = "PRIORITY",
 		.help = "priority level",
-		.call = parse_int,
+		.call = cmd_parse_int,
 		.comp = comp_none,
 	},
 	/* Top-level command. */
@@ -1049,7 +1464,7 @@ static const struct token token_list[] = {
 				  LIST,
 				  QUERY,
 				  ISOLATE)),
-		.call = parse_init,
+		.call = cmd_parse_init,
 	},
 	/* Sub-level commands. */
 	[VALIDATE] = {
@@ -1057,28 +1472,28 @@ static const struct token token_list[] = {
 		.help = "check whether a flow rule can be created",
 		.next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[CREATE] = {
 		.name = "create",
 		.help = "create a flow rule",
 		.next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[DESTROY] = {
 		.name = "destroy",
 		.help = "destroy specific flow rules",
 		.next = NEXT(NEXT_ENTRY(DESTROY_RULE), NEXT_ENTRY(PORT_ID)),
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
-		.call = parse_destroy,
+		.call = cmd_parse_destroy,
 	},
 	[FLUSH] = {
 		.name = "flush",
 		.help = "destroy all flow rules",
 		.next = NEXT(NEXT_ENTRY(PORT_ID)),
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
-		.call = parse_flush,
+		.call = cmd_parse_flush,
 	},
 	[QUERY] = {
 		.name = "query",
@@ -1089,14 +1504,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.query.action),
 				 ARGS_ENTRY(struct buffer, args.query.rule),
 				 ARGS_ENTRY(struct buffer, port)),
-		.call = parse_query,
+		.call = cmd_parse_query,
 	},
 	[LIST] = {
 		.name = "list",
 		.help = "list existing flow rules",
 		.next = NEXT(next_list_attr, NEXT_ENTRY(PORT_ID)),
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
-		.call = parse_list,
+		.call = cmd_parse_list,
 	},
 	[ISOLATE] = {
 		.name = "isolate",
@@ -1105,7 +1520,7 @@ static const struct token token_list[] = {
 				 NEXT_ENTRY(PORT_ID)),
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.isolate.set),
 				 ARGS_ENTRY(struct buffer, port)),
-		.call = parse_isolate,
+		.call = cmd_parse_isolate,
 	},
 	/* Destroy arguments. */
 	[DESTROY_RULE] = {
@@ -1113,14 +1528,14 @@ static const struct token token_list[] = {
 		.help = "specify a rule identifier",
 		.next = NEXT(next_destroy_attr, NEXT_ENTRY(RULE_ID)),
 		.args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.destroy.rule)),
-		.call = parse_destroy,
+		.call = cmd_parse_destroy,
 	},
 	/* Query arguments. */
 	[QUERY_ACTION] = {
 		.name = "{action}",
 		.type = "ACTION",
 		.help = "action to query, must be part of the rule",
-		.call = parse_action,
+		.call = cmd_parse_action,
 		.comp = comp_action,
 	},
 	/* List arguments. */
@@ -1129,7 +1544,7 @@ static const struct token token_list[] = {
 		.help = "specify a group",
 		.next = NEXT(next_list_attr, NEXT_ENTRY(GROUP_ID)),
 		.args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)),
-		.call = parse_list,
+		.call = cmd_parse_list,
 	},
 	/* Validate/create attributes. */
 	[GROUP] = {
@@ -1137,58 +1552,58 @@ static const struct token token_list[] = {
 		.help = "specify a group",
 		.next = NEXT(next_vc_attr, NEXT_ENTRY(GROUP_ID)),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_attr, group)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[PRIORITY] = {
 		.name = "priority",
 		.help = "specify a priority level",
 		.next = NEXT(next_vc_attr, NEXT_ENTRY(PRIORITY_LEVEL)),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_attr, priority)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[INGRESS] = {
 		.name = "ingress",
 		.help = "affect rule to ingress",
 		.next = NEXT(next_vc_attr),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[EGRESS] = {
 		.name = "egress",
 		.help = "affect rule to egress",
 		.next = NEXT(next_vc_attr),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	/* Validate/create pattern. */
 	[PATTERN] = {
 		.name = "pattern",
 		.help = "submit a list of pattern items",
 		.next = NEXT(next_item),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_PARAM_IS] = {
 		.name = "is",
 		.help = "match value perfectly (with full bit-mask)",
-		.call = parse_vc_spec,
+		.call = cmd_parse_vc_spec,
 	},
 	[ITEM_PARAM_SPEC] = {
 		.name = "spec",
 		.help = "match value according to configured bit-mask",
-		.call = parse_vc_spec,
+		.call = cmd_parse_vc_spec,
 	},
 	[ITEM_PARAM_LAST] = {
 		.name = "last",
 		.help = "specify upper bound to establish a range",
-		.call = parse_vc_spec,
+		.call = cmd_parse_vc_spec,
 	},
 	[ITEM_PARAM_MASK] = {
 		.name = "mask",
 		.help = "specify bit-mask with relevant bits set to one",
-		.call = parse_vc_spec,
+		.call = cmd_parse_vc_spec,
 	},
 	[ITEM_PARAM_PREFIX] = {
 		.name = "prefix",
 		.help = "generate bit-mask from a prefix length",
-		.call = parse_vc_spec,
+		.call = cmd_parse_vc_spec,
 	},
 	[ITEM_NEXT] = {
 		.name = "/",
@@ -1200,28 +1615,28 @@ static const struct token token_list[] = {
 		.help = "end list of pattern items",
 		.priv = PRIV_ITEM(END, 0),
 		.next = NEXT(NEXT_ENTRY(ACTIONS)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_VOID] = {
 		.name = "void",
 		.help = "no-op pattern item",
 		.priv = PRIV_ITEM(VOID, 0),
 		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_INVERT] = {
 		.name = "invert",
 		.help = "perform actions when pattern does not match",
 		.priv = PRIV_ITEM(INVERT, 0),
 		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_ANY] = {
 		.name = "any",
 		.help = "match any protocol for the current layer",
 		.priv = PRIV_ITEM(ANY, sizeof(struct rte_flow_item_any)),
 		.next = NEXT(item_any),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_ANY_NUM] = {
 		.name = "num",
@@ -1234,14 +1649,14 @@ static const struct token token_list[] = {
 		.help = "match packets addressed to the physical function",
 		.priv = PRIV_ITEM(PF, 0),
 		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_VF] = {
 		.name = "vf",
 		.help = "match packets addressed to a virtual function ID",
 		.priv = PRIV_ITEM(VF, sizeof(struct rte_flow_item_vf)),
 		.next = NEXT(item_vf),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_VF_ID] = {
 		.name = "id",
@@ -1254,7 +1669,7 @@ static const struct token token_list[] = {
 		.help = "device-specific physical port index to use",
 		.priv = PRIV_ITEM(PORT, sizeof(struct rte_flow_item_port)),
 		.next = NEXT(item_port),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_PORT_INDEX] = {
 		.name = "index",
@@ -1267,7 +1682,7 @@ static const struct token token_list[] = {
 		.help = "match an arbitrary byte string",
 		.priv = PRIV_ITEM(RAW, ITEM_RAW_SIZE),
 		.next = NEXT(item_raw),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_RAW_RELATIVE] = {
 		.name = "relative",
@@ -1313,7 +1728,7 @@ static const struct token token_list[] = {
 		.help = "match Ethernet header",
 		.priv = PRIV_ITEM(ETH, sizeof(struct rte_flow_item_eth)),
 		.next = NEXT(item_eth),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_ETH_DST] = {
 		.name = "dst",
@@ -1338,7 +1753,7 @@ static const struct token token_list[] = {
 		.help = "match 802.1Q/ad VLAN tag",
 		.priv = PRIV_ITEM(VLAN, sizeof(struct rte_flow_item_vlan)),
 		.next = NEXT(item_vlan),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_VLAN_TPID] = {
 		.name = "tpid",
@@ -1378,7 +1793,7 @@ static const struct token token_list[] = {
 		.help = "match IPv4 header",
 		.priv = PRIV_ITEM(IPV4, sizeof(struct rte_flow_item_ipv4)),
 		.next = NEXT(item_ipv4),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_IPV4_TOS] = {
 		.name = "tos",
@@ -1420,7 +1835,7 @@ static const struct token token_list[] = {
 		.help = "match IPv6 header",
 		.priv = PRIV_ITEM(IPV6, sizeof(struct rte_flow_item_ipv6)),
 		.next = NEXT(item_ipv6),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_IPV6_TC] = {
 		.name = "tc",
@@ -1471,7 +1886,7 @@ static const struct token token_list[] = {
 		.help = "match ICMP header",
 		.priv = PRIV_ITEM(ICMP, sizeof(struct rte_flow_item_icmp)),
 		.next = NEXT(item_icmp),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_ICMP_TYPE] = {
 		.name = "type",
@@ -1492,7 +1907,7 @@ static const struct token token_list[] = {
 		.help = "match UDP header",
 		.priv = PRIV_ITEM(UDP, sizeof(struct rte_flow_item_udp)),
 		.next = NEXT(item_udp),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_UDP_SRC] = {
 		.name = "src",
@@ -1513,7 +1928,7 @@ static const struct token token_list[] = {
 		.help = "match TCP header",
 		.priv = PRIV_ITEM(TCP, sizeof(struct rte_flow_item_tcp)),
 		.next = NEXT(item_tcp),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_TCP_SRC] = {
 		.name = "src",
@@ -1541,7 +1956,7 @@ static const struct token token_list[] = {
 		.help = "match SCTP header",
 		.priv = PRIV_ITEM(SCTP, sizeof(struct rte_flow_item_sctp)),
 		.next = NEXT(item_sctp),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_SCTP_SRC] = {
 		.name = "src",
@@ -1576,7 +1991,7 @@ static const struct token token_list[] = {
 		.help = "match VXLAN header",
 		.priv = PRIV_ITEM(VXLAN, sizeof(struct rte_flow_item_vxlan)),
 		.next = NEXT(item_vxlan),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_VXLAN_VNI] = {
 		.name = "vni",
@@ -1589,7 +2004,7 @@ static const struct token token_list[] = {
 		.help = "match E-Tag header",
 		.priv = PRIV_ITEM(E_TAG, sizeof(struct rte_flow_item_e_tag)),
 		.next = NEXT(item_e_tag),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_E_TAG_GRP_ECID_B] = {
 		.name = "grp_ecid_b",
@@ -1604,7 +2019,7 @@ static const struct token token_list[] = {
 		.help = "match NVGRE header",
 		.priv = PRIV_ITEM(NVGRE, sizeof(struct rte_flow_item_nvgre)),
 		.next = NEXT(item_nvgre),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_NVGRE_TNI] = {
 		.name = "tni",
@@ -1617,7 +2032,7 @@ static const struct token token_list[] = {
 		.help = "match MPLS header",
 		.priv = PRIV_ITEM(MPLS, sizeof(struct rte_flow_item_mpls)),
 		.next = NEXT(item_mpls),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_MPLS_LABEL] = {
 		.name = "label",
@@ -1632,7 +2047,7 @@ static const struct token token_list[] = {
 		.help = "match GRE header",
 		.priv = PRIV_ITEM(GRE, sizeof(struct rte_flow_item_gre)),
 		.next = NEXT(item_gre),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_GRE_PROTO] = {
 		.name = "protocol",
@@ -1647,7 +2062,7 @@ static const struct token token_list[] = {
 		.priv = PRIV_ITEM(FUZZY,
 				sizeof(struct rte_flow_item_fuzzy)),
 		.next = NEXT(item_fuzzy),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_FUZZY_THRESH] = {
 		.name = "thresh",
@@ -1661,7 +2076,7 @@ static const struct token token_list[] = {
 		.help = "match GTP header",
 		.priv = PRIV_ITEM(GTP, sizeof(struct rte_flow_item_gtp)),
 		.next = NEXT(item_gtp),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_GTP_TEID] = {
 		.name = "teid",
@@ -1674,14 +2089,14 @@ static const struct token token_list[] = {
 		.help = "match GTP header",
 		.priv = PRIV_ITEM(GTPC, sizeof(struct rte_flow_item_gtp)),
 		.next = NEXT(item_gtp),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ITEM_GTPU] = {
 		.name = "gtpu",
 		.help = "match GTP header",
 		.priv = PRIV_ITEM(GTPU, sizeof(struct rte_flow_item_gtp)),
 		.next = NEXT(item_gtp),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 
 	/* Validate/create actions. */
@@ -1689,7 +2104,7 @@ static const struct token token_list[] = {
 		.name = "actions",
 		.help = "submit a list of associated actions",
 		.next = NEXT(next_action),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_NEXT] = {
 		.name = "/",
@@ -1700,42 +2115,42 @@ static const struct token token_list[] = {
 		.name = "end",
 		.help = "end list of actions",
 		.priv = PRIV_ACTION(END, 0),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_VOID] = {
 		.name = "void",
 		.help = "no-op action",
 		.priv = PRIV_ACTION(VOID, 0),
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_PASSTHRU] = {
 		.name = "passthru",
 		.help = "let subsequent rule process matched packets",
 		.priv = PRIV_ACTION(PASSTHRU, 0),
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_MARK] = {
 		.name = "mark",
 		.help = "attach 32 bit value to packets",
 		.priv = PRIV_ACTION(MARK, sizeof(struct rte_flow_action_mark)),
 		.next = NEXT(action_mark),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_MARK_ID] = {
 		.name = "id",
 		.help = "32 bit value to return with packets",
 		.next = NEXT(action_mark, NEXT_ENTRY(UNSIGNED)),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_mark, id)),
-		.call = parse_vc_conf,
+		.call = cmd_parse_vc_conf,
 	},
 	[ACTION_FLAG] = {
 		.name = "flag",
 		.help = "flag packets",
 		.priv = PRIV_ACTION(FLAG, 0),
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_QUEUE] = {
 		.name = "queue",
@@ -1743,60 +2158,60 @@ static const struct token token_list[] = {
 		.priv = PRIV_ACTION(QUEUE,
 					sizeof(struct rte_flow_action_queue)),
 		.next = NEXT(action_queue),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_QUEUE_INDEX] = {
 		.name = "index",
 		.help = "queue index to use",
 		.next = NEXT(action_queue, NEXT_ENTRY(UNSIGNED)),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_queue, index)),
-		.call = parse_vc_conf,
+		.call = cmd_parse_vc_conf,
 	},
 	[ACTION_DROP] = {
 		.name = "drop",
 		.help = "drop packets (note: passthru has priority)",
 		.priv = PRIV_ACTION(DROP, 0),
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_COUNT] = {
 		.name = "count",
 		.help = "enable counters for this rule",
 		.priv = PRIV_ACTION(COUNT, 0),
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_DUP] = {
 		.name = "dup",
 		.help = "duplicate packets to a given queue index",
 		.priv = PRIV_ACTION(DUP, sizeof(struct rte_flow_action_dup)),
 		.next = NEXT(action_dup),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_DUP_INDEX] = {
 		.name = "index",
 		.help = "queue index to duplicate packets to",
 		.next = NEXT(action_dup, NEXT_ENTRY(UNSIGNED)),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_dup, index)),
-		.call = parse_vc_conf,
+		.call = cmd_parse_vc_conf,
 	},
 	[ACTION_RSS] = {
 		.name = "rss",
 		.help = "spread packets among several queues",
 		.priv = PRIV_ACTION(RSS, ACTION_RSS_SIZE),
 		.next = NEXT(action_rss),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_RSS_QUEUES] = {
 		.name = "queues",
 		.help = "queue indices to use",
 		.next = NEXT(action_rss, NEXT_ENTRY(ACTION_RSS_QUEUE)),
-		.call = parse_vc_conf,
+		.call = cmd_parse_vc_conf,
 	},
 	[ACTION_RSS_QUEUE] = {
 		.name = "{queue}",
 		.help = "queue index",
-		.call = parse_vc_action_rss_queue,
+		.call = cmd_parse_vc_action_rss_queue,
 		.comp = comp_vc_action_rss_queue,
 	},
 	[ACTION_PF] = {
@@ -1804,14 +2219,14 @@ static const struct token token_list[] = {
 		.help = "redirect packets to physical device function",
 		.priv = PRIV_ACTION(PF, 0),
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_VF] = {
 		.name = "vf",
 		.help = "redirect packets to virtual device function",
 		.priv = PRIV_ACTION(VF, sizeof(struct rte_flow_action_vf)),
 		.next = NEXT(action_vf),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_VF_ORIGINAL] = {
 		.name = "original",
@@ -1819,14 +2234,14 @@ static const struct token token_list[] = {
 		.next = NEXT(action_vf, NEXT_ENTRY(BOOLEAN)),
 		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_action_vf,
 					   original, 1)),
-		.call = parse_vc_conf,
+		.call = cmd_parse_vc_conf,
 	},
 	[ACTION_VF_ID] = {
 		.name = "id",
 		.help = "VF ID to redirect packets to",
 		.next = NEXT(action_vf, NEXT_ENTRY(UNSIGNED)),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_vf, id)),
-		.call = parse_vc_conf,
+		.call = cmd_parse_vc_conf,
 	},
 	[ACTION_METER] = {
 		.name = "meter",
@@ -1834,14 +2249,14 @@ static const struct token token_list[] = {
 		.priv = PRIV_ACTION(METER,
 					sizeof(struct rte_flow_action_meter)),
 		.next = NEXT(action_meter),
-		.call = parse_vc,
+		.call = cmd_parse_vc,
 	},
 	[ACTION_METER_ID] = {
 		.name = "mtr_id",
 		.help = "meter id to use",
 		.next = NEXT(action_meter, NEXT_ENTRY(UNSIGNED)),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_meter, mtr_id)),
-		.call = parse_vc_conf,
+		.call = cmd_parse_vc_conf,
 	},
 };
 
diff --git a/lib/librte_ether/rte_flow.c b/lib/librte_ether/rte_flow.c
index 3302208..e437d03 100644
--- a/lib/librte_ether/rte_flow.c
+++ b/lib/librte_ether/rte_flow.c
@@ -249,7 +249,7 @@ rte_flow_error_set(struct rte_flow_error *error,
 }
 
 /** Compute storage space needed by item specification. */
-void
+static void
 flow_item_spec_size(const struct rte_flow_item *item,
 		    size_t *size, size_t *pad)
 {
@@ -277,7 +277,7 @@ flow_item_spec_size(const struct rte_flow_item *item,
 }
 
 /** Compute storage space needed by action configuration. */
-void
+static void
 flow_action_conf_size(const struct rte_flow_action *action,
 		      size_t *size, size_t *pad)
 {
diff --git a/lib/librte_ether/rte_flow.h b/lib/librte_ether/rte_flow.h
index c2184b6..4178203 100644
--- a/lib/librte_ether/rte_flow.h
+++ b/lib/librte_ether/rte_flow.h
@@ -1180,16 +1180,6 @@ struct rte_flow_action {
  */
 struct rte_flow;
 
-/** Compute storage space needed by item specification. */
-void
-flow_item_spec_size(const struct rte_flow_item *item,
-		    size_t *size, size_t *pad);
-
-/** Compute storage space needed by action configuration. */
-void
-flow_action_conf_size(const struct rte_flow_action *action,
-		    size_t *size, size_t *pad);
-
 /**
  * Verbose error types.
  *
-- 
2.7.4



More information about the dev mailing list