[dpdk-dev] [PATCH] ip_pipeline: add flow actions pipeline

Jasvinder Singh jasvinder.singh at intel.com
Wed Sep 30 19:06:00 CEST 2015


Flow actions pipeline is an extension of
flow-classification pipeline. Some of the
operations of flow classification pipeline such
as traffic metering/marking(for e.g. Single
Rate Three Color Marker (srTCM), Two Rate Three
Color Marker trTCM)), policer can be performed
separately in flow action pipeline to avoid
excessive computational burden on the CPU core
running the flow-classification pipeline. The
Flow action pipeline implements various
function such as traffic metering, policer,
stats. Traffic mettering can configured as
per the required context, for examples- per
user, per traffic class or both. These
contexts can be applied by specifying
parameters in configuration file as shown
below;

[PIPELINE1]
type = FLOW_ACTIONS
core = 1
pktq_in = RXQ0.0 RXQ1.0 RXQ2.0 RXQ3.0
pktq_out = TXQ0.0 TXQ1.0 TXQ2.0 TXQ3.0
n_flows = 65536
n_meters_per_flow = 1
flow_id_offset = 158
ip_hdr_offset = 142
color_offset = 64

The entries of flow and dscp tables of flow
actions pipeline can be modified through
command-line interface. The commands to
add or delete entries to the flow table,
DSCP(differentiated services code point) table
and for statistics collection, etc have been
included. The key functions such as
Traffic Metering/marking and policer functions
have been implemented as flow-table action
handler.

Signed-off-by: Jasvinder Singh <jasvinder.singh at intel.com>
---
 examples/ip_pipeline/Makefile                      |    2 +
 examples/ip_pipeline/init.c                        |    2 +
 .../ip_pipeline/pipeline/pipeline_actions_common.h |   83 +
 .../ip_pipeline/pipeline/pipeline_flow_actions.c   | 1808 ++++++++++++++++++++
 .../ip_pipeline/pipeline/pipeline_flow_actions.h   |   78 +
 .../pipeline/pipeline_flow_actions_be.c            |  973 +++++++++++
 .../pipeline/pipeline_flow_actions_be.h            |  168 ++
 7 files changed, 3114 insertions(+)
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_actions.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_actions.h
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_actions_be.c
 create mode 100644 examples/ip_pipeline/pipeline/pipeline_flow_actions_be.h

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index f3ff1ec..13d31de 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -66,6 +66,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_firewall.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_classification_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_classification.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_actions_be.c
+SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_flow_actions.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing_be.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += pipeline_routing.c
 
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 3f9c68d..0088ece 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -49,6 +49,7 @@
 #include "pipeline_passthrough.h"
 #include "pipeline_firewall.h"
 #include "pipeline_flow_classification.h"
+#include "pipeline_flow_actions.h"
 #include "pipeline_routing.h"
 
 #define APP_NAME_SIZE	32
@@ -1291,6 +1292,7 @@ int app_init(struct app_params *app)
 	app_pipeline_type_register(app, &pipeline_master);
 	app_pipeline_type_register(app, &pipeline_passthrough);
 	app_pipeline_type_register(app, &pipeline_flow_classification);
+	app_pipeline_type_register(app, &pipeline_flow_actions);
 	app_pipeline_type_register(app, &pipeline_firewall);
 	app_pipeline_type_register(app, &pipeline_routing);
 
diff --git a/examples/ip_pipeline/pipeline/pipeline_actions_common.h b/examples/ip_pipeline/pipeline/pipeline_actions_common.h
index 4b5d5c4..aa1dd59 100644
--- a/examples/ip_pipeline/pipeline/pipeline_actions_common.h
+++ b/examples/ip_pipeline/pipeline/pipeline_actions_common.h
@@ -116,4 +116,87 @@ f_ah(									\
 	return 0;							\
 }
 
+#define PIPELINE_TABLE_AH_HIT_DROP_TIME(f_ah, f_pkt_work, f_pkt4_work)	\
+static int								\
+f_ah(									\
+	struct rte_mbuf **pkts,						\
+	uint64_t *pkts_mask,						\
+	struct rte_pipeline_table_entry **entries,			\
+	void *arg)							\
+{									\
+	uint64_t pkts_in_mask = *pkts_mask;				\
+	uint64_t pkts_out_mask = *pkts_mask;				\
+	uint64_t time = rte_rdtsc();					\
+									\
+	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {			\
+		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);	\
+		uint32_t i;						\
+									\
+		for (i = 0; i < (n_pkts & (~0x3LLU)); i += 4) {		\
+			uint64_t mask = f_pkt4_work(&pkts[i],		\
+				&entries[i], arg, time);	\
+			pkts_out_mask ^= mask << i;			\
+		}							\
+									\
+		for ( ; i < n_pkts; i++) {				\
+			uint64_t mask = f_pkt_work(pkts[i],		\
+				entries[i], arg, time);		\
+			pkts_out_mask ^= mask << i;			\
+		}							\
+	} else								\
+		for ( ; pkts_in_mask; ) {				\
+			uint32_t pos = __builtin_ctzll(pkts_in_mask);	\
+			uint64_t pkt_mask = 1LLU << pos;		\
+			uint64_t mask = f_pkt_work(pkts[pos],		\
+				entries[pos], arg, time);		\
+									\
+			pkts_in_mask &= ~pkt_mask;			\
+			pkts_out_mask ^= mask << pos;			\
+		}							\
+									\
+	*pkts_mask = pkts_out_mask;					\
+	return 0;							\
+}
+
+#define PIPELINE_TABLE_AH_MISS_DROP_TIME(f_ah, f_pkt_work, f_pkt4_work)	\
+static int								\
+f_ah(									\
+	struct rte_mbuf **pkts,						\
+	uint64_t *pkts_mask,						\
+	struct rte_pipeline_table_entry *entry,				\
+	void *arg)							\
+{									\
+	uint64_t pkts_in_mask = *pkts_mask;				\
+	uint64_t pkts_out_mask = *pkts_mask;				\
+	uint64_t time = rte_rdtsc();					\
+									\
+	if ((pkts_in_mask & (pkts_in_mask + 1)) == 0) {			\
+		uint64_t n_pkts = __builtin_popcountll(pkts_in_mask);	\
+		uint32_t i;						\
+									\
+		for (i = 0; i < (n_pkts & (~0x3LLU)); i += 4) {		\
+			uint64_t mask = f_pkt4_work(&pkts[i],		\
+				entry, arg, time);			\
+			pkts_out_mask ^= mask << i;			\
+		}							\
+									\
+		for ( ; i < n_pkts; i++) {				\
+			uint64_t mask = f_pkt_work(pkts[i], entry, arg, time);\
+			pkts_out_mask ^= mask << i;			\
+		}							\
+	} else								\
+		for ( ; pkts_in_mask; ) {				\
+			uint32_t pos = __builtin_ctzll(pkts_in_mask);	\
+			uint64_t pkt_mask = 1LLU << pos;		\
+			uint64_t mask = f_pkt_work(pkts[pos],		\
+				entry, arg, time);		\
+									\
+			pkts_in_mask &= ~pkt_mask;			\
+			pkts_out_mask ^= mask << pos;			\
+		}							\
+									\
+	*pkts_mask = pkts_out_mask;					\
+	return 0;							\
+}
+
 #endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_actions.c b/examples/ip_pipeline/pipeline/pipeline_flow_actions.c
new file mode 100644
index 0000000..c29172c
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_actions.c
@@ -0,0 +1,1808 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+
+#include <rte_common.h>
+#include <rte_hexdump.h>
+#include <rte_malloc.h>
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_etheraddr.h>
+
+#include "app.h"
+#include "pipeline_common_fe.h"
+#include "pipeline_flow_actions.h"
+#include "hash_func.h"
+
+/*
+ * Flow actions pipeline
+ */
+#ifndef N_FLOWS_BULK
+#define N_FLOWS_BULK					4096
+#endif
+
+struct app_pipeline_fa_flow {
+	struct pipeline_fa_flow_params params;
+	void *entry_ptr;
+};
+
+struct app_pipeline_fa_dscp {
+	uint32_t traffic_class;
+	enum rte_meter_color color;
+};
+
+struct app_pipeline_fa {
+	/* Parameters */
+	uint32_t n_ports_in;
+	uint32_t n_ports_out;
+	struct pipeline_fa_params params;
+
+	/* Flows */
+	struct app_pipeline_fa_dscp dscp[PIPELINE_FA_N_DSCP];
+	struct app_pipeline_fa_flow *flows;
+} __rte_cache_aligned;
+
+static void*
+app_pipeline_fa_init(struct pipeline_params *params,
+	__rte_unused void *arg)
+{
+	struct app_pipeline_fa *p;
+	uint32_t size, i;
+
+	/* Check input arguments */
+	if ((params == NULL) ||
+		(params->n_ports_in == 0) ||
+		(params->n_ports_out == 0))
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct app_pipeline_fa));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	if (p == NULL)
+		return NULL;
+
+	/* Initialization */
+	p->n_ports_in = params->n_ports_in;
+	p->n_ports_out = params->n_ports_out;
+	if (pipeline_fa_parse_args(&p->params, params)) {
+		rte_free(p);
+		return NULL;
+	}
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(
+		p->params.n_flows * sizeof(struct app_pipeline_fa_flow));
+	p->flows = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	if (p->flows == NULL) {
+		rte_free(p);
+		return NULL;
+	}
+
+	/* Initialization of flow table */
+	for (i = 0; i < p->params.n_flows; i++)
+		pipeline_fa_flow_params_set_default(&p->flows[i].params);
+
+	/* Initialization of DSCP table */
+	for (i = 0; i < RTE_DIM(p->dscp); i++) {
+		p->dscp[i].traffic_class = 0;
+		p->dscp[i].color = e_RTE_METER_GREEN;
+	}
+
+	return (void *) p;
+}
+
+static int
+app_pipeline_fa_free(void *pipeline)
+{
+	struct app_pipeline_fa *p = pipeline;
+
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
+
+	/* Free resources */
+	rte_free(p->flows);
+	rte_free(p);
+
+	return 0;
+}
+
+static int
+flow_params_check(struct app_pipeline_fa *p,
+	__rte_unused uint32_t meter_update_mask,
+	uint32_t policer_update_mask,
+	uint32_t port_update,
+	struct pipeline_fa_flow_params *params)
+{
+	uint32_t mask, i;
+
+	/* Meter */
+
+	/* Policer */
+	for (i = 0, mask = 1; i < PIPELINE_FA_N_TC_MAX; i++, mask <<= 1) {
+		struct pipeline_fa_policer_params *p = &params->p[i];
+		uint32_t j;
+
+		if ((mask & policer_update_mask) == 0)
+			continue;
+
+		for (j = 0; j < e_RTE_METER_COLORS; j++) {
+			struct pipeline_fa_policer_action *action =
+				&p->action[j];
+
+			if ((action->drop == 0) &&
+				(action->color >= e_RTE_METER_COLORS))
+				return -1;
+		}
+	}
+
+	/* Port */
+	if (port_update && (params->port_id >= p->n_ports_out))
+		return -1;
+
+	return 0;
+}
+
+int
+app_pipeline_fa_flow_config(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t flow_id,
+	uint32_t meter_update_mask,
+	uint32_t policer_update_mask,
+	uint32_t port_update,
+	struct pipeline_fa_flow_params *params)
+{
+	struct app_pipeline_fa *p;
+	struct app_pipeline_fa_flow *flow;
+
+	struct pipeline_fa_flow_config_msg_req *req;
+	struct pipeline_fa_flow_config_msg_rsp *rsp;
+
+	uint32_t i, mask;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		((meter_update_mask == 0) &&
+		(policer_update_mask == 0) &&
+		(port_update == 0)) ||
+		(meter_update_mask >= (1 << PIPELINE_FA_N_TC_MAX)) ||
+		(policer_update_mask >= (1 << PIPELINE_FA_N_TC_MAX)) ||
+		(params == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (flow_params_check(p,
+		meter_update_mask,
+		policer_update_mask,
+		port_update,
+		params) != 0)
+		return -1;
+
+	flow_id %= p->params.n_flows;
+	flow = &p->flows[flow_id];
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FA_MSG_REQ_FLOW_CONFIG;
+	req->entry_ptr = flow->entry_ptr;
+	req->flow_id = flow_id;
+	req->meter_update_mask = meter_update_mask;
+	req->policer_update_mask = policer_update_mask;
+	req->port_update = port_update;
+	memcpy(&req->params, params, sizeof(*params));
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response */
+	if (rsp->status ||
+		(rsp->entry_ptr == NULL)) {
+		app_msg_free(app, rsp);
+		return -1;
+	}
+
+	/* Commit flow */
+	for (i = 0, mask = 1; i < PIPELINE_FA_N_TC_MAX; i++, mask <<= 1) {
+		if ((mask & meter_update_mask) == 0)
+			continue;
+
+		memcpy(&flow->params.m[i], &params->m[i], sizeof(params->m[i]));
+	}
+
+	for (i = 0, mask = 1; i < PIPELINE_FA_N_TC_MAX; i++, mask <<= 1) {
+		if ((mask & policer_update_mask) == 0)
+			continue;
+
+		memcpy(&flow->params.p[i], &params->p[i], sizeof(params->p[i]));
+	}
+
+	if (port_update)
+		flow->params.port_id = params->port_id;
+
+	flow->entry_ptr = rsp->entry_ptr;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+int
+app_pipeline_fa_flow_config_bulk(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t *flow_id,
+	uint32_t n_flows,
+	uint32_t meter_update_mask,
+	uint32_t policer_update_mask,
+	uint32_t port_update,
+	struct pipeline_fa_flow_params *params)
+{
+	struct app_pipeline_fa *p;
+	struct pipeline_fa_flow_config_bulk_msg_req *req;
+	struct pipeline_fa_flow_config_bulk_msg_rsp *rsp;
+	void **req_entry_ptr;
+	uint32_t *req_flow_id;
+	uint32_t i;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(flow_id == NULL) ||
+		(n_flows == 0) ||
+		((meter_update_mask == 0) &&
+		(policer_update_mask == 0) &&
+		(port_update == 0)) ||
+		(meter_update_mask >= (1 << PIPELINE_FA_N_TC_MAX)) ||
+		(policer_update_mask >= (1 << PIPELINE_FA_N_TC_MAX)) ||
+		(params == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	for (i = 0; i < n_flows; i++) {
+		struct pipeline_fa_flow_params *flow_params = &params[i];
+
+		if (flow_params_check(p,
+			meter_update_mask,
+			policer_update_mask,
+			port_update,
+			flow_params) != 0)
+			return -1;
+	}
+
+	/* Allocate and write request */
+	req_entry_ptr = (void **) rte_malloc(NULL,
+		n_flows * sizeof(void *),
+		RTE_CACHE_LINE_SIZE);
+	if (req_entry_ptr == NULL)
+		return -1;
+
+	req_flow_id = (uint32_t *) rte_malloc(NULL,
+		n_flows * sizeof(uint32_t),
+		RTE_CACHE_LINE_SIZE);
+	if (req_flow_id == NULL) {
+		rte_free(req_entry_ptr);
+		return -1;
+	}
+
+	for (i = 0; i < n_flows; i++) {
+		uint32_t fid = flow_id[i] % p->params.n_flows;
+		struct app_pipeline_fa_flow *flow = &p->flows[fid];
+
+		req_flow_id[i] = fid;
+		req_entry_ptr[i] = flow->entry_ptr;
+	}
+
+	req = app_msg_alloc(app);
+	if (req == NULL) {
+		rte_free(req_flow_id);
+		rte_free(req_entry_ptr);
+		return -1;
+	}
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FA_MSG_REQ_FLOW_CONFIG_BULK;
+	req->entry_ptr = req_entry_ptr;
+	req->flow_id = req_flow_id;
+	req->n_flows = n_flows;
+	req->meter_update_mask = meter_update_mask;
+	req->policer_update_mask = policer_update_mask;
+	req->port_update = port_update;
+	req->params = params;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL) {
+		rte_free(req_flow_id);
+		rte_free(req_entry_ptr);
+		return -1;
+	}
+
+	/* Read response */
+
+	/* Commit flows */
+	for (i = 0; i < rsp->n_flows; i++) {
+		uint32_t fid = flow_id[i] % p->params.n_flows;
+		struct app_pipeline_fa_flow *flow = &p->flows[fid];
+		struct pipeline_fa_flow_params *flow_params = &params[i];
+		void *entry_ptr = req_entry_ptr[i];
+		uint32_t j, mask;
+
+		for (j = 0, mask = 1; j < PIPELINE_FA_N_TC_MAX;
+			j++, mask <<= 1) {
+			if ((mask & meter_update_mask) == 0)
+				continue;
+
+			memcpy(&flow->params.m[j],
+				&flow_params->m[j],
+				sizeof(flow_params->m[j]));
+		}
+
+		for (j = 0, mask = 1; j < PIPELINE_FA_N_TC_MAX;
+			j++, mask <<= 1) {
+			if ((mask & policer_update_mask) == 0)
+				continue;
+
+			memcpy(&flow->params.p[j],
+				&flow_params->p[j],
+				sizeof(flow_params->p[j]));
+		}
+
+		if (port_update)
+			flow->params.port_id = flow_params->port_id;
+
+		flow->entry_ptr = entry_ptr;
+	}
+
+	/* Free response */
+	app_msg_free(app, rsp);
+	rte_free(req_flow_id);
+	rte_free(req_entry_ptr);
+
+	return (rsp->n_flows == n_flows) ? 0 : -1;
+}
+
+int
+app_pipeline_fa_dscp_config(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t dscp,
+	uint32_t traffic_class,
+	enum rte_meter_color color)
+{
+	struct app_pipeline_fa *p;
+
+	struct pipeline_fa_dscp_config_msg_req *req;
+	struct pipeline_fa_dscp_config_msg_rsp *rsp;
+
+	/* Check input arguments */
+	if ((app == NULL) ||
+		(dscp >= PIPELINE_FA_N_DSCP) ||
+		(traffic_class >= PIPELINE_FA_N_TC_MAX) ||
+		(color >= e_RTE_METER_COLORS))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (p->params.dscp_enabled == 0)
+		return -1;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FA_MSG_REQ_DSCP_CONFIG;
+	req->dscp = dscp;
+	req->traffic_class = traffic_class;
+	req->color = color;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response */
+	if (rsp->status) {
+		app_msg_free(app, rsp);
+		return -1;
+	}
+
+	/* Commit DSCP */
+	p->dscp[dscp].traffic_class = traffic_class;
+	p->dscp[dscp].color = color;
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+int
+app_pipeline_fa_flow_policer_stats_read(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t flow_id,
+	uint32_t policer_id,
+	int clear,
+	struct pipeline_fa_policer_stats *stats)
+{
+	struct app_pipeline_fa *p;
+	struct app_pipeline_fa_flow *flow;
+
+	struct pipeline_fa_policer_stats_msg_req *req;
+	struct pipeline_fa_policer_stats_msg_rsp *rsp;
+
+	/* Check input arguments */
+	if ((app == NULL) || (stats == NULL))
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	flow_id %= p->params.n_flows;
+	flow = &p->flows[flow_id];
+
+	if ((policer_id >= p->params.n_meters_per_flow) ||
+		(flow->entry_ptr == NULL))
+		return -1;
+
+	/* Allocate and write request */
+	req = app_msg_alloc(app);
+	if (req == NULL)
+		return -1;
+
+	req->type = PIPELINE_MSG_REQ_CUSTOM;
+	req->subtype = PIPELINE_FA_MSG_REQ_POLICER_STATS_READ;
+	req->entry_ptr = flow->entry_ptr;
+	req->policer_id = policer_id;
+	req->clear = clear;
+
+	/* Send request and wait for response */
+	rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
+	if (rsp == NULL)
+		return -1;
+
+	/* Read response */
+	if (rsp->status) {
+		app_msg_free(app, rsp);
+		return -1;
+	}
+
+	memcpy(stats, &rsp->stats, sizeof(*stats));
+
+	/* Free response */
+	app_msg_free(app, rsp);
+
+	return 0;
+}
+
+static const char *
+color_to_string(enum rte_meter_color color)
+{
+	switch (color) {
+	case e_RTE_METER_GREEN: return "G";
+	case e_RTE_METER_YELLOW: return "Y";
+	case e_RTE_METER_RED: return "R";
+	default: return "?";
+	}
+}
+
+static int
+string_to_color(char *s, enum rte_meter_color *c)
+{
+	if (strcmp(s, "G") == 0) {
+		*c = e_RTE_METER_GREEN;
+		return 0;
+	}
+
+	if (strcmp(s, "Y") == 0) {
+		*c = e_RTE_METER_YELLOW;
+		return 0;
+	}
+
+	if (strcmp(s, "R") == 0) {
+		*c = e_RTE_METER_RED;
+		return 0;
+	}
+
+	return -1;
+}
+
+static const char *
+policer_action_to_string(struct pipeline_fa_policer_action *a)
+{
+	if (a->drop)
+		return "D";
+
+	return color_to_string(a->color);
+}
+
+static int
+string_to_policer_action(char *s, struct pipeline_fa_policer_action *a)
+{
+	if (strcmp(s, "G") == 0) {
+		a->drop = 0;
+		a->color = e_RTE_METER_GREEN;
+		return 0;
+	}
+
+	if (strcmp(s, "Y") == 0) {
+		a->drop = 0;
+		a->color = e_RTE_METER_YELLOW;
+		return 0;
+	}
+
+	if (strcmp(s, "R") == 0) {
+		a->drop = 0;
+		a->color = e_RTE_METER_RED;
+		return 0;
+	}
+
+	if (strcmp(s, "D") == 0) {
+		a->drop = 1;
+		a->color = e_RTE_METER_GREEN;
+		return 0;
+	}
+
+	return -1;
+}
+
+static void
+print_flow(struct app_pipeline_fa *p,
+	uint32_t flow_id,
+	struct app_pipeline_fa_flow *flow)
+{
+	uint32_t i;
+
+	printf("Flow ID = %" PRIu32 "\n", flow_id);
+
+	for (i = 0; i < p->params.n_meters_per_flow; i++) {
+		struct rte_meter_trtcm_params *meter = &flow->params.m[i];
+		struct pipeline_fa_policer_params *policer = &flow->params.p[i];
+
+	printf("\ttrTCM [CIR = %" PRIu64
+		", CBS = %" PRIu64 ", PIR = %" PRIu64
+		", PBS = %" PRIu64	"] Policer [G : %s, Y : %s, R : %s]\n",
+		meter->cir,
+		meter->cbs,
+		meter->pir,
+		meter->pbs,
+		policer_action_to_string(&policer->action[e_RTE_METER_GREEN]),
+		policer_action_to_string(&policer->action[e_RTE_METER_YELLOW]),
+		policer_action_to_string(&policer->action[e_RTE_METER_RED]));
+	}
+
+	printf("\tPort %u (entry_ptr = %p)\n",
+		flow->params.port_id,
+		flow->entry_ptr);
+}
+
+
+static int
+app_pipeline_fa_flow_ls(struct app_params *app,
+		uint32_t pipeline_id)
+{
+	struct app_pipeline_fa *p;
+	uint32_t i;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	for (i = 0; i < p->params.n_flows; i++) {
+		struct app_pipeline_fa_flow *flow = &p->flows[i];
+
+		print_flow(p, i, flow);
+	}
+
+	return 0;
+}
+
+static int
+app_pipeline_fa_dscp_ls(struct app_params *app,
+		uint32_t pipeline_id)
+{
+	struct app_pipeline_fa *p;
+	uint32_t i;
+
+	/* Check input arguments */
+	if (app == NULL)
+		return -1;
+
+	p = app_pipeline_data_fe(app, pipeline_id);
+	if (p == NULL)
+		return -1;
+
+	if (p->params.dscp_enabled == 0)
+		return -1;
+
+	for (i = 0; i < RTE_DIM(p->dscp); i++) {
+		struct app_pipeline_fa_dscp *dscp =	&p->dscp[i];
+
+		printf("DSCP = %2" PRIu32 ": Traffic class = %" PRIu32
+			", Color = %s\n",
+			i,
+			dscp->traffic_class,
+			color_to_string(dscp->color));
+	}
+
+	return 0;
+}
+
+/*
+ * Flow meter configuration (single flow)
+ *
+ * p <pipeline ID> flow <flow ID> meter <meter ID> trtcm <trtcm params>
+ */
+
+struct cmd_fa_meter_config_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	uint32_t flow_id;
+	cmdline_fixed_string_t meter_string;
+	uint32_t meter_id;
+	cmdline_fixed_string_t trtcm_string;
+	uint64_t cir;
+	uint64_t pir;
+	uint64_t cbs;
+	uint64_t pbs;
+};
+
+static void
+cmd_fa_meter_config_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_meter_config_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fa_flow_params flow_params;
+	int status;
+
+	if (params->meter_id >= PIPELINE_FA_N_TC_MAX) {
+		printf("Command failed\n");
+		return;
+	}
+
+	flow_params.m[params->meter_id].cir = params->cir;
+	flow_params.m[params->meter_id].pir = params->pir;
+	flow_params.m[params->meter_id].cbs = params->cbs;
+	flow_params.m[params->meter_id].pbs = params->pbs;
+
+	status = app_pipeline_fa_flow_config(app,
+		params->pipeline_id,
+		params->flow_id,
+		1 << params->meter_id,
+		0,
+		0,
+		&flow_params);
+
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fa_meter_config_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_meter_config_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_meter_config_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_meter_config_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_meter_config_result,
+	flow_string, "flow");
+
+cmdline_parse_token_num_t cmd_fa_meter_config_flow_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_result,
+	flow_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_meter_config_meter_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_meter_config_result,
+	meter_string, "meter");
+
+cmdline_parse_token_num_t cmd_fa_meter_config_meter_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_result,
+	meter_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_meter_config_trtcm_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_meter_config_result,
+	trtcm_string, "trtcm");
+
+cmdline_parse_token_num_t cmd_fa_meter_config_cir =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_result, cir, UINT64);
+
+cmdline_parse_token_num_t cmd_fa_meter_config_pir =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_result, pir, UINT64);
+
+cmdline_parse_token_num_t cmd_fa_meter_config_cbs =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_result, cbs, UINT64);
+
+cmdline_parse_token_num_t cmd_fa_meter_config_pbs =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_result, pbs, UINT64);
+
+cmdline_parse_inst_t cmd_fa_meter_config = {
+	.f = cmd_fa_meter_config_parsed,
+	.data = NULL,
+	.help_str = "Flow meter configuration (single flow) ",
+	.tokens = {
+		(void *) &cmd_fa_meter_config_p_string,
+		(void *) &cmd_fa_meter_config_pipeline_id,
+		(void *) &cmd_fa_meter_config_flow_string,
+		(void *) &cmd_fa_meter_config_flow_id,
+		(void *) &cmd_fa_meter_config_meter_string,
+		(void *) &cmd_fa_meter_config_meter_id,
+		(void *) &cmd_fa_meter_config_trtcm_string,
+		(void *) &cmd_fa_meter_config_cir,
+		(void *) &cmd_fa_meter_config_pir,
+		(void *) &cmd_fa_meter_config_cbs,
+		(void *) &cmd_fa_meter_config_pbs,
+		NULL,
+	},
+};
+
+/*
+ * Flow meter configuration (multiple flows)
+ *
+ * p <pipeline ID> flows <n_flows> meter <meter ID> trtcm <trtcm params>
+ */
+
+struct cmd_fa_meter_config_bulk_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flows_string;
+	uint32_t n_flows;
+	cmdline_fixed_string_t meter_string;
+	uint32_t meter_id;
+	cmdline_fixed_string_t trtcm_string;
+	uint64_t cir;
+	uint64_t pir;
+	uint64_t cbs;
+	uint64_t pbs;
+};
+
+static void
+cmd_fa_meter_config_bulk_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_meter_config_bulk_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fa_flow_params flow_template, *flow_params;
+	uint32_t *flow_id;
+	uint32_t i;
+
+	if ((params->n_flows == 0) ||
+		(params->meter_id >= PIPELINE_FA_N_TC_MAX)) {
+		printf("Invalid arguments\n");
+		return;
+	}
+
+	flow_id = (uint32_t *) rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(uint32_t),
+		RTE_CACHE_LINE_SIZE);
+	if (flow_id == NULL) {
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	flow_params = (struct pipeline_fa_flow_params *) rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(struct pipeline_fa_flow_params),
+		RTE_CACHE_LINE_SIZE);
+	if (flow_params == NULL) {
+		rte_free(flow_id);
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	memset(&flow_template, 0, sizeof(flow_template));
+	flow_template.m[params->meter_id].cir = params->cir;
+	flow_template.m[params->meter_id].pir = params->pir;
+	flow_template.m[params->meter_id].cbs = params->cbs;
+	flow_template.m[params->meter_id].pbs = params->pbs;
+
+	for (i = 0; i < params->n_flows; i++) {
+		uint32_t pos = i % N_FLOWS_BULK;
+
+		flow_id[pos] = i;
+		memcpy(&flow_params[pos],
+			&flow_template,
+			sizeof(flow_template));
+
+		if ((pos == N_FLOWS_BULK - 1) ||
+			(i == params->n_flows - 1)) {
+			int status;
+
+			status = app_pipeline_fa_flow_config_bulk(app,
+				params->pipeline_id,
+				flow_id,
+				pos + 1,
+				1 << params->meter_id,
+				0,
+				0,
+				flow_params);
+
+			if (status != 0) {
+				printf("Command failed\n");
+
+				break;
+			}
+		}
+	}
+
+	rte_free(flow_params);
+	rte_free(flow_id);
+
+}
+
+cmdline_parse_token_string_t cmd_fa_meter_config_bulk_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_meter_config_bulk_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_meter_config_bulk_flows_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	flows_string, "flows");
+
+cmdline_parse_token_num_t cmd_fa_meter_config_bulk_n_flows =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	n_flows, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_meter_config_bulk_meter_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	meter_string, "meter");
+
+cmdline_parse_token_num_t cmd_fa_meter_config_bulk_meter_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	meter_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_meter_config_bulk_trtcm_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	trtcm_string, "trtcm");
+
+cmdline_parse_token_num_t cmd_fa_meter_config_bulk_cir =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	cir, UINT64);
+
+cmdline_parse_token_num_t cmd_fa_meter_config_bulk_pir =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	pir, UINT64);
+
+cmdline_parse_token_num_t cmd_fa_meter_config_bulk_cbs =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	cbs, UINT64);
+
+cmdline_parse_token_num_t cmd_fa_meter_config_bulk_pbs =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_meter_config_bulk_result,
+	pbs, UINT64);
+
+cmdline_parse_inst_t cmd_fa_meter_config_bulk = {
+	.f = cmd_fa_meter_config_bulk_parsed,
+	.data = NULL,
+	.help_str = "Flow meter configuration (multiple flows)",
+	.tokens = {
+		(void *) &cmd_fa_meter_config_bulk_p_string,
+		(void *) &cmd_fa_meter_config_bulk_pipeline_id,
+		(void *) &cmd_fa_meter_config_bulk_flows_string,
+		(void *) &cmd_fa_meter_config_bulk_n_flows,
+		(void *) &cmd_fa_meter_config_bulk_meter_string,
+		(void *) &cmd_fa_meter_config_bulk_meter_id,
+		(void *) &cmd_fa_meter_config_bulk_trtcm_string,
+		(void *) &cmd_fa_meter_config_cir,
+		(void *) &cmd_fa_meter_config_pir,
+		(void *) &cmd_fa_meter_config_cbs,
+		(void *) &cmd_fa_meter_config_pbs,
+		NULL,
+	},
+};
+
+/*
+ * Flow policer configuration (single flow)
+ *
+ * p <pipeline ID> flow <flow ID> policer <policer ID>
+ *    G <action> Y <action> R <action>
+ *
+ * <action> = G (green) | Y (yellow) | R (red) | D (drop)
+ */
+
+struct cmd_fa_policer_config_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	uint32_t flow_id;
+	cmdline_fixed_string_t policer_string;
+	uint32_t policer_id;
+	cmdline_fixed_string_t green_string;
+	cmdline_fixed_string_t g_action;
+	cmdline_fixed_string_t yellow_string;
+	cmdline_fixed_string_t y_action;
+	cmdline_fixed_string_t red_string;
+	cmdline_fixed_string_t r_action;
+};
+
+static void
+cmd_fa_policer_config_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_policer_config_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fa_flow_params flow_params;
+	int status;
+
+	if (params->policer_id >= PIPELINE_FA_N_TC_MAX) {
+		printf("Command failed\n");
+		return;
+	}
+
+	status = string_to_policer_action(params->g_action,
+		&flow_params.p[params->policer_id].action[e_RTE_METER_GREEN]);
+	if (status)
+		printf("Invalid policer green action\n");
+
+	status = string_to_policer_action(params->y_action,
+		&flow_params.p[params->policer_id].action[e_RTE_METER_YELLOW]);
+	if (status)
+		printf("Invalid policer yellow action\n");
+
+	status = string_to_policer_action(params->r_action,
+		&flow_params.p[params->policer_id].action[e_RTE_METER_RED]);
+	if (status)
+		printf("Invalid policer red action\n");
+
+	status = app_pipeline_fa_flow_config(app,
+		params->pipeline_id,
+		params->flow_id,
+		0,
+		1 << params->policer_id,
+		0,
+		&flow_params);
+
+	if (status != 0)
+		printf("Command failed\n");
+
+}
+
+cmdline_parse_token_string_t cmd_fa_policer_config_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_policer_config_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_config_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_config_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	flow_string, "flow");
+
+cmdline_parse_token_num_t cmd_fa_policer_config_flow_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_config_result,
+	flow_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_config_policer_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	policer_string, "policer");
+
+cmdline_parse_token_num_t cmd_fa_policer_config_policer_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_config_result,
+	policer_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_config_green_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	green_string, "G");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_g_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	g_action, "R#Y#G#D");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_yellow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	yellow_string, "Y");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_y_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	y_action, "R#Y#G#D");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_red_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	red_string, "R");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_r_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_result,
+	r_action, "R#Y#G#D");
+
+cmdline_parse_inst_t cmd_fa_policer_config = {
+	.f = cmd_fa_policer_config_parsed,
+	.data = NULL,
+	.help_str = "Flow policer configuration (single flow)",
+	.tokens = {
+		(void *) &cmd_fa_policer_config_p_string,
+		(void *) &cmd_fa_policer_config_pipeline_id,
+		(void *) &cmd_fa_policer_config_flow_string,
+		(void *) &cmd_fa_policer_config_flow_id,
+		(void *) &cmd_fa_policer_config_policer_string,
+		(void *) &cmd_fa_policer_config_policer_id,
+		(void *) &cmd_fa_policer_config_green_string,
+		(void *) &cmd_fa_policer_config_g_action,
+		(void *) &cmd_fa_policer_config_yellow_string,
+		(void *) &cmd_fa_policer_config_y_action,
+		(void *) &cmd_fa_policer_config_red_string,
+		(void *) &cmd_fa_policer_config_r_action,
+		NULL,
+	},
+};
+
+/*
+ * Flow policer configuration (multiple flows)
+ *
+ * p <pipeline ID> flows <n_flows> policer <policer ID>
+ *    G <action> Y <action> R <action>
+ *
+ * <action> = G (green) | Y (yellow) | R (red) | D (drop)
+ */
+
+struct cmd_fa_policer_config_bulk_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flows_string;
+	uint32_t n_flows;
+	cmdline_fixed_string_t policer_string;
+	uint32_t policer_id;
+	cmdline_fixed_string_t green_string;
+	cmdline_fixed_string_t g_action;
+	cmdline_fixed_string_t yellow_string;
+	cmdline_fixed_string_t y_action;
+	cmdline_fixed_string_t red_string;
+	cmdline_fixed_string_t r_action;
+};
+
+static void
+cmd_fa_policer_config_bulk_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_policer_config_bulk_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fa_flow_params flow_template, *flow_params;
+	uint32_t *flow_id, i;
+	int status;
+
+	if ((params->n_flows == 0) ||
+		(params->policer_id >= PIPELINE_FA_N_TC_MAX)) {
+		printf("Invalid arguments\n");
+		return;
+	}
+
+	flow_id = (uint32_t *) rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(uint32_t),
+		RTE_CACHE_LINE_SIZE);
+	if (flow_id == NULL) {
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	flow_params = (struct pipeline_fa_flow_params *) rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(struct pipeline_fa_flow_params),
+		RTE_CACHE_LINE_SIZE);
+	if (flow_params == NULL) {
+		rte_free(flow_id);
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	memset(&flow_template, 0, sizeof(flow_template));
+
+	status = string_to_policer_action(params->g_action,
+		&flow_template.p[params->policer_id].action[e_RTE_METER_GREEN]);
+	if (status)
+		printf("Invalid policer green action\n");
+
+	status = string_to_policer_action(params->y_action,
+	 &flow_template.p[params->policer_id].action[e_RTE_METER_YELLOW]);
+	if (status)
+		printf("Invalid policer yellow action\n");
+
+	status = string_to_policer_action(params->r_action,
+		&flow_template.p[params->policer_id].action[e_RTE_METER_RED]);
+	if (status)
+		printf("Invalid policer red action\n");
+
+	for (i = 0; i < params->n_flows; i++) {
+		uint32_t pos = i % N_FLOWS_BULK;
+
+		flow_id[pos] = i;
+		memcpy(&flow_params[pos], &flow_template,
+			sizeof(flow_template));
+
+		if ((pos == N_FLOWS_BULK - 1) ||
+			(i == params->n_flows - 1)) {
+			int status;
+
+			status = app_pipeline_fa_flow_config_bulk(app,
+				params->pipeline_id,
+				flow_id,
+				pos + 1,
+				0,
+				1 << params->policer_id,
+				0,
+				flow_params);
+
+			if (status != 0) {
+				printf("Command failed\n");
+
+				break;
+			}
+		}
+	}
+
+	rte_free(flow_params);
+	rte_free(flow_id);
+
+}
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_policer_config_bulk_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_flows_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	flows_string, "flows");
+
+cmdline_parse_token_num_t cmd_fa_policer_config_bulk_n_flows =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	n_flows, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_policer_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	policer_string, "policer");
+
+cmdline_parse_token_num_t cmd_fa_policer_config_bulk_policer_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	policer_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_green_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	green_string, "G");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_g_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	g_action, "R#Y#G#D");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_yellow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	yellow_string, "Y");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_y_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	y_action, "R#Y#G#D");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_red_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	red_string, "R");
+
+cmdline_parse_token_string_t cmd_fa_policer_config_bulk_r_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_config_bulk_result,
+	r_action, "R#Y#G#D");
+
+cmdline_parse_inst_t cmd_fa_policer_config_bulk = {
+	.f = cmd_fa_policer_config_bulk_parsed,
+	.data = NULL,
+	.help_str = "Flow policer configuration (multiple flows)",
+	.tokens = {
+		(void *) &cmd_fa_policer_config_bulk_p_string,
+		(void *) &cmd_fa_policer_config_bulk_pipeline_id,
+		(void *) &cmd_fa_policer_config_bulk_flows_string,
+		(void *) &cmd_fa_policer_config_bulk_n_flows,
+		(void *) &cmd_fa_policer_config_bulk_policer_string,
+		(void *) &cmd_fa_policer_config_bulk_policer_id,
+		(void *) &cmd_fa_policer_config_bulk_green_string,
+		(void *) &cmd_fa_policer_config_bulk_g_action,
+		(void *) &cmd_fa_policer_config_bulk_yellow_string,
+		(void *) &cmd_fa_policer_config_bulk_y_action,
+		(void *) &cmd_fa_policer_config_bulk_red_string,
+		(void *) &cmd_fa_policer_config_bulk_r_action,
+		NULL,
+	},
+};
+
+/*
+ * Flow output port configuration (single flow)
+ *
+ * p <pipeline ID> flow <flow ID> port <port ID>
+ */
+
+struct cmd_fa_output_port_config_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	uint32_t flow_id;
+	cmdline_fixed_string_t port_string;
+	uint32_t port_id;
+};
+
+static void
+cmd_fa_output_port_config_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_output_port_config_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fa_flow_params flow_params;
+	int status;
+
+	flow_params.port_id = params->port_id;
+
+	status = app_pipeline_fa_flow_config(app,
+		params->pipeline_id,
+		params->flow_id,
+		0,
+		0,
+		1,
+		&flow_params);
+
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fa_output_port_config_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_output_port_config_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_output_port_config_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_output_port_config_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_output_port_config_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_output_port_config_result,
+	flow_string, "flow");
+
+cmdline_parse_token_num_t cmd_fa_output_port_config_flow_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_output_port_config_result,
+	flow_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_output_port_config_port_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_output_port_config_result,
+	port_string, "port");
+
+cmdline_parse_token_num_t cmd_fa_output_port_config_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_output_port_config_result,
+	port_id, UINT32);
+
+cmdline_parse_inst_t cmd_fa_output_port_config = {
+	.f = cmd_fa_output_port_config_parsed,
+	.data = NULL,
+	.help_str = "Flow output port configuration (single flow)",
+	.tokens = {
+		(void *) &cmd_fa_output_port_config_p_string,
+		(void *) &cmd_fa_output_port_config_pipeline_id,
+		(void *) &cmd_fa_output_port_config_flow_string,
+		(void *) &cmd_fa_output_port_config_flow_id,
+		(void *) &cmd_fa_output_port_config_port_string,
+		(void *) &cmd_fa_output_port_config_port_id,
+		NULL,
+	},
+};
+
+/*
+ * Flow output port configuration (multiple flows)
+ *
+ * p <pipeline ID> flows <n_flows> ports <n_ports>
+ */
+
+struct cmd_fa_output_port_config_bulk_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flows_string;
+	uint32_t n_flows;
+	cmdline_fixed_string_t ports_string;
+	uint32_t n_ports;
+};
+
+static void
+cmd_fa_output_port_config_bulk_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_output_port_config_bulk_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fa_flow_params *flow_params;
+	uint32_t *flow_id;
+	uint32_t i;
+
+	if (params->n_flows == 0) {
+		printf("Invalid arguments\n");
+		return;
+	}
+
+	flow_id = (uint32_t *) rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(uint32_t),
+		RTE_CACHE_LINE_SIZE);
+	if (flow_id == NULL) {
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	flow_params = (struct pipeline_fa_flow_params *) rte_malloc(NULL,
+		N_FLOWS_BULK * sizeof(struct pipeline_fa_flow_params),
+		RTE_CACHE_LINE_SIZE);
+	if (flow_params == NULL) {
+		rte_free(flow_id);
+		printf("Memory allocation failed\n");
+		return;
+	}
+
+	for (i = 0; i < params->n_flows; i++) {
+		uint32_t pos = i % N_FLOWS_BULK;
+		uint32_t port_id = i % params->n_ports;
+
+		flow_id[pos] = i;
+
+		memset(&flow_params[pos], 0, sizeof(flow_params[pos]));
+		flow_params[pos].port_id = port_id;
+
+		if ((pos == N_FLOWS_BULK - 1) ||
+			(i == params->n_flows - 1)) {
+			int status;
+
+			status = app_pipeline_fa_flow_config_bulk(app,
+				params->pipeline_id,
+				flow_id,
+				pos + 1,
+				0,
+				0,
+				1,
+				flow_params);
+
+			if (status != 0) {
+				printf("Command failed\n");
+
+				break;
+			}
+		}
+	}
+
+	rte_free(flow_params);
+	rte_free(flow_id);
+
+}
+
+cmdline_parse_token_string_t cmd_fa_output_port_config_bulk_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_output_port_config_bulk_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_output_port_config_bulk_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_output_port_config_bulk_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_output_port_config_bulk_flows_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_output_port_config_bulk_result,
+	flows_string, "flows");
+
+cmdline_parse_token_num_t cmd_fa_output_port_config_bulk_n_flows =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_output_port_config_bulk_result,
+	n_flows, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_output_port_config_bulk_ports_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_output_port_config_bulk_result,
+	ports_string, "ports");
+
+cmdline_parse_token_num_t cmd_fa_output_port_config_bulk_n_ports =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_output_port_config_bulk_result,
+	n_ports, UINT32);
+
+cmdline_parse_inst_t cmd_fa_output_port_config_bulk = {
+	.f = cmd_fa_output_port_config_bulk_parsed,
+	.data = NULL,
+	.help_str = "Flow output port configuration (multiple flows)",
+	.tokens = {
+		(void *) &cmd_fa_output_port_config_bulk_p_string,
+		(void *) &cmd_fa_output_port_config_bulk_pipeline_id,
+		(void *) &cmd_fa_output_port_config_bulk_flows_string,
+		(void *) &cmd_fa_output_port_config_bulk_n_flows,
+		(void *) &cmd_fa_output_port_config_bulk_ports_string,
+		(void *) &cmd_fa_output_port_config_bulk_n_ports,
+		NULL,
+	},
+};
+
+/*
+ * Flow DiffServ Code Point (DSCP) translation table configuration
+ *
+ * p <pipeline ID> dscp <DSCP ID> class <traffic class ID> color <color>
+ *
+ * <color> = G (green) | Y (yellow) | R (red)
+*/
+
+struct cmd_fa_dscp_config_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t dscp_string;
+	uint32_t dscp_id;
+	cmdline_fixed_string_t class_string;
+	uint32_t traffic_class_id;
+	cmdline_fixed_string_t color_string;
+	cmdline_fixed_string_t color;
+
+};
+
+static void
+cmd_fa_dscp_config_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_dscp_config_result *params = parsed_result;
+	struct app_params *app = data;
+	enum rte_meter_color color;
+	int status;
+
+	status = string_to_color(params->color, &color);
+	if (status) {
+		printf("Invalid color\n");
+		return;
+	}
+
+	status = app_pipeline_fa_dscp_config(app,
+		params->pipeline_id,
+		params->dscp_id,
+		params->traffic_class_id,
+		color);
+
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fa_dscp_config_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_dscp_config_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_dscp_config_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_dscp_config_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_dscp_config_dscp_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_dscp_config_result,
+	dscp_string, "dscp");
+
+cmdline_parse_token_num_t cmd_fa_dscp_config_dscp_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_dscp_config_result,
+	dscp_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_dscp_config_class_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_dscp_config_result,
+	class_string, "class");
+
+cmdline_parse_token_num_t cmd_fa_dscp_config_traffic_class_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_dscp_config_result,
+	traffic_class_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_dscp_config_color_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_dscp_config_result,
+	color_string, "color");
+
+cmdline_parse_token_string_t cmd_fa_dscp_config_color =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_dscp_config_result,
+	color, "G#Y#R");
+
+cmdline_parse_inst_t cmd_fa_dscp_config = {
+	.f = cmd_fa_dscp_config_parsed,
+	.data = NULL,
+	.help_str = "Flow DSCP translation table configuration",
+	.tokens = {
+		(void *) &cmd_fa_dscp_config_p_string,
+		(void *) &cmd_fa_dscp_config_pipeline_id,
+		(void *) &cmd_fa_dscp_config_dscp_string,
+		(void *) &cmd_fa_dscp_config_dscp_id,
+		(void *) &cmd_fa_dscp_config_class_string,
+		(void *) &cmd_fa_dscp_config_traffic_class_id,
+		(void *) &cmd_fa_dscp_config_color_string,
+		(void *) &cmd_fa_dscp_config_color,
+		NULL,
+	},
+};
+
+/*
+ * Flow policer stats read
+ *
+ * p <pipeline ID> flow <flow ID> policer <policer ID> stats
+ */
+
+struct cmd_fa_policer_stats_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	uint32_t flow_id;
+	cmdline_fixed_string_t policer_string;
+	uint32_t policer_id;
+	cmdline_fixed_string_t stats_string;
+};
+
+static void
+cmd_fa_policer_stats_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_policer_stats_result *params = parsed_result;
+	struct app_params *app = data;
+	struct pipeline_fa_policer_stats stats;
+	int status;
+
+	status = app_pipeline_fa_flow_policer_stats_read(app,
+		params->pipeline_id,
+		params->flow_id,
+		params->policer_id,
+		1,
+		&stats);
+	if (status != 0) {
+		printf("Command failed\n");
+		return;
+	}
+
+	/* Display stats */
+	printf("\tPkts G: %" PRIu64
+		"\tPkts Y: %" PRIu64
+		"\tPkts R: %" PRIu64
+		"\tPkts D: %" PRIu64 "\n",
+		stats.n_pkts[e_RTE_METER_GREEN],
+		stats.n_pkts[e_RTE_METER_YELLOW],
+		stats.n_pkts[e_RTE_METER_RED],
+		stats.n_pkts_drop);
+}
+
+cmdline_parse_token_string_t cmd_fa_policer_stats_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_stats_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_policer_stats_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_stats_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_stats_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_stats_result,
+	flow_string, "flow");
+
+cmdline_parse_token_num_t cmd_fa_policer_stats_flow_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_stats_result,
+	flow_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_stats_policer_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_stats_result,
+	policer_string, "policer");
+
+cmdline_parse_token_num_t cmd_fa_policer_stats_policer_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_policer_stats_result,
+	policer_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_policer_stats_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_policer_stats_result,
+	stats_string, "stats");
+
+cmdline_parse_inst_t cmd_fa_policer_stats = {
+	.f = cmd_fa_policer_stats_parsed,
+	.data = NULL,
+	.help_str = "Flow policer stats read",
+	.tokens = {
+		(void *) &cmd_fa_policer_stats_p_string,
+		(void *) &cmd_fa_policer_stats_pipeline_id,
+		(void *) &cmd_fa_policer_stats_flow_string,
+		(void *) &cmd_fa_policer_stats_flow_id,
+		(void *) &cmd_fa_policer_stats_policer_string,
+		(void *) &cmd_fa_policer_stats_policer_id,
+		(void *) &cmd_fa_policer_stats_string,
+		NULL,
+	},
+};
+
+/*
+ * Flow list
+ *
+ * p <pipeline ID> flow ls
+ */
+
+struct cmd_fa_flow_ls_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t flow_string;
+	cmdline_fixed_string_t actions_string;
+	cmdline_fixed_string_t ls_string;
+};
+
+static void
+cmd_fa_flow_ls_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_flow_ls_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_fa_flow_ls(app, params->pipeline_id);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fa_flow_ls_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_flow_ls_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_flow_ls_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_flow_ls_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_flow_ls_flow_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_flow_ls_result,
+	flow_string, "flow");
+
+cmdline_parse_token_string_t cmd_fa_flow_ls_actions_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_flow_ls_result,
+	actions_string, "actions");
+
+cmdline_parse_token_string_t cmd_fa_flow_ls_ls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_flow_ls_result,
+	ls_string, "ls");
+
+cmdline_parse_inst_t cmd_fa_flow_ls = {
+	.f = cmd_fa_flow_ls_parsed,
+	.data = NULL,
+	.help_str = "Flow actions list",
+	.tokens = {
+		(void *) &cmd_fa_flow_ls_p_string,
+		(void *) &cmd_fa_flow_ls_pipeline_id,
+		(void *) &cmd_fa_flow_ls_flow_string,
+		(void *) &cmd_fa_flow_ls_actions_string,
+		(void *) &cmd_fa_flow_ls_ls_string,
+		NULL,
+	},
+};
+
+/*
+ * Flow DiffServ Code Point (DSCP) translation table list
+ *
+ * p <pipeline ID> dscp ls
+ */
+
+struct cmd_fa_dscp_ls_result {
+	cmdline_fixed_string_t p_string;
+	uint32_t pipeline_id;
+	cmdline_fixed_string_t dscp_string;
+	cmdline_fixed_string_t ls_string;
+};
+
+static void
+cmd_fa_dscp_ls_parsed(
+	void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	void *data)
+{
+	struct cmd_fa_dscp_ls_result *params = parsed_result;
+	struct app_params *app = data;
+	int status;
+
+	status = app_pipeline_fa_dscp_ls(app, params->pipeline_id);
+	if (status != 0)
+		printf("Command failed\n");
+}
+
+cmdline_parse_token_string_t cmd_fa_dscp_ls_p_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_dscp_ls_result,
+	p_string, "p");
+
+cmdline_parse_token_num_t cmd_fa_dscp_ls_pipeline_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_fa_dscp_ls_result,
+	pipeline_id, UINT32);
+
+cmdline_parse_token_string_t cmd_fa_dscp_ls_dscp_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_dscp_ls_result,
+	dscp_string, "dscp");
+
+cmdline_parse_token_string_t cmd_fa_dscp_ls_string =
+	TOKEN_STRING_INITIALIZER(struct cmd_fa_dscp_ls_result, ls_string,
+	"ls");
+
+cmdline_parse_inst_t cmd_fa_dscp_ls = {
+	.f = cmd_fa_dscp_ls_parsed,
+	.data = NULL,
+	.help_str = "Flow DSCP translaton table list",
+	.tokens = {
+		(void *) &cmd_fa_dscp_ls_p_string,
+		(void *) &cmd_fa_dscp_ls_pipeline_id,
+		(void *) &cmd_fa_dscp_ls_dscp_string,
+		(void *) &cmd_fa_dscp_ls_string,
+		NULL,
+	},
+};
+
+static cmdline_parse_ctx_t pipeline_cmds[] = {
+	(cmdline_parse_inst_t *) &cmd_fa_meter_config,
+	(cmdline_parse_inst_t *) &cmd_fa_meter_config_bulk,
+	(cmdline_parse_inst_t *) &cmd_fa_policer_config,
+	(cmdline_parse_inst_t *) &cmd_fa_policer_config_bulk,
+	(cmdline_parse_inst_t *) &cmd_fa_output_port_config,
+	(cmdline_parse_inst_t *) &cmd_fa_output_port_config_bulk,
+	(cmdline_parse_inst_t *) &cmd_fa_dscp_config,
+	(cmdline_parse_inst_t *) &cmd_fa_policer_stats,
+	(cmdline_parse_inst_t *) &cmd_fa_flow_ls,
+	(cmdline_parse_inst_t *) &cmd_fa_dscp_ls,
+	NULL,
+};
+
+static struct pipeline_fe_ops pipeline_flow_actions_fe_ops = {
+	.f_init = app_pipeline_fa_init,
+	.f_free = app_pipeline_fa_free,
+	.cmds = pipeline_cmds,
+};
+
+struct pipeline_type pipeline_flow_actions = {
+	.name = "FLOW_ACTIONS",
+	.be_ops = &pipeline_flow_actions_be_ops,
+	.fe_ops = &pipeline_flow_actions_fe_ops,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_actions.h b/examples/ip_pipeline/pipeline/pipeline_flow_actions.h
new file mode 100644
index 0000000..f2cd0cb
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_actions.h
@@ -0,0 +1,78 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_FLOW_ACTIONS_H__
+#define __INCLUDE_PIPELINE_FLOW_ACTIONS_H__
+
+#include <rte_meter.h>
+
+#include "pipeline.h"
+#include "pipeline_flow_actions_be.h"
+
+int
+app_pipeline_fa_flow_config(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t flow_id,
+	uint32_t meter_update_mask,
+	uint32_t policer_update_mask,
+	uint32_t port_update,
+	struct pipeline_fa_flow_params *params);
+
+int
+app_pipeline_fa_flow_config_bulk(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t *flow_id,
+	uint32_t n_flows,
+	uint32_t meter_update_mask,
+	uint32_t policer_update_mask,
+	uint32_t port_update,
+	struct pipeline_fa_flow_params *params);
+
+int
+app_pipeline_fa_dscp_config(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t dscp,
+	uint32_t traffic_class,
+	enum rte_meter_color color);
+
+int
+app_pipeline_fa_flow_policer_stats_read(struct app_params *app,
+	uint32_t pipeline_id,
+	uint32_t flow_id,
+	uint32_t policer_id,
+	int clear,
+	struct pipeline_fa_policer_stats *stats);
+
+extern struct pipeline_type pipeline_flow_actions;
+
+#endif
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_actions_be.c b/examples/ip_pipeline/pipeline/pipeline_flow_actions_be.c
new file mode 100644
index 0000000..73dbe7a
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_actions_be.c
@@ -0,0 +1,973 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_cycles.h>
+#include <rte_table_array.h>
+#include <rte_byteorder.h>
+#include <rte_ip.h>
+
+#include "pipeline_actions_common.h"
+#include "pipeline_flow_actions_be.h"
+#include "hash_func.h"
+
+int
+pipeline_fa_flow_params_set_default(struct pipeline_fa_flow_params *params)
+{
+	uint32_t i;
+
+	if (params == NULL)
+		return -1;
+
+	for (i = 0; i < PIPELINE_FA_N_TC_MAX; i++) {
+		struct rte_meter_trtcm_params *m = &params->m[i];
+
+		m->cir = 1;
+		m->cbs = 1;
+		m->pir = 1;
+		m->pbs = 2;
+	}
+
+	for (i = 0; i < PIPELINE_FA_N_TC_MAX; i++) {
+		struct pipeline_fa_policer_params *p = &params->p[i];
+		uint32_t j;
+
+		for (j = 0; j < e_RTE_METER_COLORS; j++) {
+			struct pipeline_fa_policer_action *a = &p->action[j];
+
+			a->drop = 0;
+			a->color = (enum rte_meter_color) j;
+		}
+	}
+
+	params->port_id = 0;
+
+	return 0;
+}
+
+struct dscp_entry {
+	uint32_t traffic_class;
+	enum rte_meter_color color;
+};
+
+struct pipeline_flow_actions {
+	struct pipeline p;
+	struct pipeline_fa_params params;
+	pipeline_msg_req_handler custom_handlers[PIPELINE_FA_MSG_REQS];
+
+	struct dscp_entry dscp[PIPELINE_FA_N_DSCP];
+} __rte_cache_aligned;
+
+static void *
+pipeline_fa_msg_req_custom_handler(struct pipeline *p, void *msg);
+
+static pipeline_msg_req_handler handlers[] = {
+	[PIPELINE_MSG_REQ_PING] =
+		pipeline_msg_req_ping_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_IN] =
+		pipeline_msg_req_stats_port_in_handler,
+	[PIPELINE_MSG_REQ_STATS_PORT_OUT] =
+		pipeline_msg_req_stats_port_out_handler,
+	[PIPELINE_MSG_REQ_STATS_TABLE] =
+		pipeline_msg_req_stats_table_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_ENABLE] =
+		pipeline_msg_req_port_in_enable_handler,
+	[PIPELINE_MSG_REQ_PORT_IN_DISABLE] =
+		pipeline_msg_req_port_in_disable_handler,
+	[PIPELINE_MSG_REQ_CUSTOM] =
+		pipeline_fa_msg_req_custom_handler,
+};
+
+static void *
+pipeline_fa_msg_req_flow_config_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_fa_msg_req_flow_config_bulk_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_fa_msg_req_dscp_config_handler(struct pipeline *p, void *msg);
+
+static void *
+pipeline_fa_msg_req_policer_stats_read_handler(struct pipeline *p, void *msg);
+
+static pipeline_msg_req_handler custom_handlers[] = {
+	[PIPELINE_FA_MSG_REQ_FLOW_CONFIG] =
+		pipeline_fa_msg_req_flow_config_handler,
+	[PIPELINE_FA_MSG_REQ_FLOW_CONFIG_BULK] =
+		pipeline_fa_msg_req_flow_config_bulk_handler,
+	[PIPELINE_FA_MSG_REQ_DSCP_CONFIG] =
+		pipeline_fa_msg_req_dscp_config_handler,
+	[PIPELINE_FA_MSG_REQ_POLICER_STATS_READ] =
+		pipeline_fa_msg_req_policer_stats_read_handler,
+};
+
+/*
+ * Flow table
+ */
+struct meter_policer {
+	struct rte_meter_trtcm meter;
+	struct pipeline_fa_policer_params policer;
+	struct pipeline_fa_policer_stats stats;
+};
+
+struct flow_table_entry {
+	struct rte_pipeline_table_entry head;
+	struct meter_policer mp[PIPELINE_FA_N_TC_MAX];
+};
+
+static int
+flow_table_entry_set_meter(struct flow_table_entry *entry,
+	uint32_t meter_id,
+	struct pipeline_fa_flow_params *params)
+{
+	struct rte_meter_trtcm *meter = &entry->mp[meter_id].meter;
+	struct rte_meter_trtcm_params *meter_params = &params->m[meter_id];
+
+	return rte_meter_trtcm_config(meter, meter_params);
+}
+
+static void
+flow_table_entry_set_policer(struct flow_table_entry *entry,
+	uint32_t policer_id,
+	struct pipeline_fa_flow_params *params)
+{
+	struct pipeline_fa_policer_params *p0 = &entry->mp[policer_id].policer;
+	struct pipeline_fa_policer_params *p1 = &params->p[policer_id];
+
+	memcpy(p0, p1, sizeof(*p0));
+}
+
+static void
+flow_table_entry_set_port_id(struct pipeline_flow_actions *p,
+	struct flow_table_entry *entry,
+	struct pipeline_fa_flow_params *params)
+{
+	entry->head.action = RTE_PIPELINE_ACTION_PORT;
+	entry->head.port_id = p->p.port_out_id[params->port_id];
+}
+
+static int
+flow_table_entry_set_default(struct pipeline_flow_actions *p,
+	struct flow_table_entry *entry)
+{
+	struct pipeline_fa_flow_params params;
+	uint32_t i;
+
+	pipeline_fa_flow_params_set_default(&params);
+
+	memset(entry, 0, sizeof(*entry));
+
+	flow_table_entry_set_port_id(p, entry, &params);
+
+	for (i = 0; i < PIPELINE_FA_N_TC_MAX; i++) {
+		int status;
+
+		status = flow_table_entry_set_meter(entry, i, &params);
+		if (status)
+			return status;
+	}
+
+	for (i = 0; i < PIPELINE_FA_N_TC_MAX; i++)
+		flow_table_entry_set_policer(entry, i, &params);
+
+	return 0;
+}
+
+static inline uint64_t
+pkt_work(
+	struct rte_mbuf *pkt,
+	struct rte_pipeline_table_entry *table_entry,
+	void *arg,
+	uint64_t time)
+{
+	struct pipeline_flow_actions *p = arg;
+	struct flow_table_entry *entry =
+		(struct flow_table_entry *) table_entry;
+
+	struct ipv4_hdr *pkt_ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkt, p->params.ip_hdr_offset);
+	enum rte_meter_color *pkt_color = (enum rte_meter_color *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkt, p->params.color_offset);
+
+	/* Read (IP header) */
+	uint32_t total_length = rte_bswap16(pkt_ip->total_length);
+	uint32_t dscp = pkt_ip->type_of_service >> 2;
+
+	uint32_t tc = p->dscp[dscp].traffic_class;
+	enum rte_meter_color color = p->dscp[dscp].color;
+
+	struct rte_meter_trtcm *meter = &entry->mp[tc].meter;
+	struct pipeline_fa_policer_params *policer = &entry->mp[tc].policer;
+	struct pipeline_fa_policer_stats *stats = &entry->mp[tc].stats;
+
+	/* Read (entry), compute */
+	enum rte_meter_color color2 = rte_meter_trtcm_color_aware_check(meter,
+		time,
+		total_length,
+		color);
+
+	enum rte_meter_color color3 = policer->action[color2].color;
+	uint64_t drop = policer->action[color2].drop;
+
+	/* Read (entry), write (entry, color) */
+	stats->n_pkts[color3] += drop ^ 1LLU;
+	stats->n_pkts_drop += drop;
+	*pkt_color = color3;
+
+	return drop;
+}
+
+static inline uint64_t
+pkt4_work(
+	struct rte_mbuf **pkts,
+	struct rte_pipeline_table_entry **table_entries,
+	void *arg,
+	uint64_t time)
+{
+	struct pipeline_flow_actions *p = arg;
+
+	struct flow_table_entry *entry0 =
+		(struct flow_table_entry *) table_entries[0];
+	struct flow_table_entry *entry1 =
+		(struct flow_table_entry *) table_entries[1];
+	struct flow_table_entry *entry2 =
+		(struct flow_table_entry *) table_entries[2];
+	struct flow_table_entry *entry3 =
+		(struct flow_table_entry *) table_entries[3];
+
+	struct ipv4_hdr *pkt0_ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkts[0], p->params.ip_hdr_offset);
+	struct ipv4_hdr *pkt1_ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkts[1], p->params.ip_hdr_offset);
+	struct ipv4_hdr *pkt2_ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkts[2], p->params.ip_hdr_offset);
+	struct ipv4_hdr *pkt3_ip = (struct ipv4_hdr *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkts[3], p->params.ip_hdr_offset);
+
+	enum rte_meter_color *pkt0_color = (enum rte_meter_color *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkts[0], p->params.color_offset);
+	enum rte_meter_color *pkt1_color = (enum rte_meter_color *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkts[1], p->params.color_offset);
+	enum rte_meter_color *pkt2_color = (enum rte_meter_color *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkts[2], p->params.color_offset);
+	enum rte_meter_color *pkt3_color = (enum rte_meter_color *)
+		RTE_MBUF_METADATA_UINT32_PTR(pkts[3], p->params.color_offset);
+
+	/* Read (IP header) */
+	uint32_t total_length0 = rte_bswap16(pkt0_ip->total_length);
+	uint32_t dscp0 = pkt0_ip->type_of_service >> 2;
+
+	uint32_t total_length1 = rte_bswap16(pkt1_ip->total_length);
+	uint32_t dscp1 = pkt1_ip->type_of_service >> 2;
+
+	uint32_t total_length2 = rte_bswap16(pkt2_ip->total_length);
+	uint32_t dscp2 = pkt2_ip->type_of_service >> 2;
+
+	uint32_t total_length3 = rte_bswap16(pkt3_ip->total_length);
+	uint32_t dscp3 = pkt3_ip->type_of_service >> 2;
+
+	uint32_t tc0 = p->dscp[dscp0].traffic_class;
+	enum rte_meter_color color0 = p->dscp[dscp0].color;
+
+	uint32_t tc1 = p->dscp[dscp1].traffic_class;
+	enum rte_meter_color color1 = p->dscp[dscp1].color;
+
+	uint32_t tc2 = p->dscp[dscp2].traffic_class;
+	enum rte_meter_color color2 = p->dscp[dscp2].color;
+
+	uint32_t tc3 = p->dscp[dscp3].traffic_class;
+	enum rte_meter_color color3 = p->dscp[dscp3].color;
+
+	struct rte_meter_trtcm *meter0 = &entry0->mp[tc0].meter;
+	struct pipeline_fa_policer_params *policer0 = &entry0->mp[tc0].policer;
+	struct pipeline_fa_policer_stats *stats0 = &entry0->mp[tc0].stats;
+
+	struct rte_meter_trtcm *meter1 = &entry1->mp[tc1].meter;
+	struct pipeline_fa_policer_params *policer1 = &entry1->mp[tc1].policer;
+	struct pipeline_fa_policer_stats *stats1 = &entry1->mp[tc1].stats;
+
+	struct rte_meter_trtcm *meter2 = &entry2->mp[tc2].meter;
+	struct pipeline_fa_policer_params *policer2 = &entry2->mp[tc2].policer;
+	struct pipeline_fa_policer_stats *stats2 = &entry2->mp[tc2].stats;
+
+	struct rte_meter_trtcm *meter3 = &entry3->mp[tc3].meter;
+	struct pipeline_fa_policer_params *policer3 = &entry3->mp[tc3].policer;
+	struct pipeline_fa_policer_stats *stats3 = &entry3->mp[tc3].stats;
+
+	/* Read (entry), compute, write (entry) */
+	enum rte_meter_color color2_0 = rte_meter_trtcm_color_aware_check(
+		meter0,
+		time,
+		total_length0,
+		color0);
+
+	enum rte_meter_color color2_1 = rte_meter_trtcm_color_aware_check(
+		meter1,
+		time,
+		total_length1,
+		color1);
+
+	enum rte_meter_color color2_2 = rte_meter_trtcm_color_aware_check(
+		meter2,
+		time,
+		total_length2,
+		color2);
+
+	enum rte_meter_color color2_3 = rte_meter_trtcm_color_aware_check(
+		meter3,
+		time,
+		total_length3,
+		color3);
+
+	enum rte_meter_color color3_0 = policer0->action[color2_0].color;
+	enum rte_meter_color color3_1 = policer1->action[color2_1].color;
+	enum rte_meter_color color3_2 = policer2->action[color2_2].color;
+	enum rte_meter_color color3_3 = policer3->action[color2_3].color;
+
+	uint64_t drop0 = policer0->action[color2_0].drop;
+	uint64_t drop1 = policer1->action[color2_1].drop;
+	uint64_t drop2 = policer2->action[color2_2].drop;
+	uint64_t drop3 = policer3->action[color2_3].drop;
+
+	/* Read (entry), write (entry, color) */
+	stats0->n_pkts[color3_0] += drop0 ^ 1LLU;
+	stats0->n_pkts_drop += drop0;
+
+	stats1->n_pkts[color3_1] += drop1 ^ 1LLU;
+	stats1->n_pkts_drop += drop1;
+
+	stats2->n_pkts[color3_2] += drop2 ^ 1LLU;
+	stats2->n_pkts_drop += drop2;
+
+	stats3->n_pkts[color3_3] += drop3 ^ 1LLU;
+	stats3->n_pkts_drop += drop3;
+
+	*pkt0_color = color3_0;
+	*pkt1_color = color3_1;
+	*pkt2_color = color3_2;
+	*pkt3_color = color3_3;
+
+	return (drop0 | (drop1 << 1) | (drop2 << 2) | (drop3 << 3));
+}
+
+PIPELINE_TABLE_AH_HIT_DROP_TIME(fa_table_ah_hit, pkt_work, pkt4_work);
+
+static rte_pipeline_table_action_handler_hit
+get_fa_table_ah_hit(__rte_unused struct pipeline_flow_actions *p)
+{
+	return fa_table_ah_hit;
+}
+
+/*
+ * Argument parsing
+ */
+int
+pipeline_fa_parse_args(struct pipeline_fa_params *p,
+	struct pipeline_params *params)
+{
+	uint32_t n_flows_present = 0;
+	uint32_t n_meters_per_flow_present = 0;
+	uint32_t flow_id_offset_present = 0;
+	uint32_t ip_hdr_offset_present = 0;
+	uint32_t color_offset_present = 0;
+	uint32_t i;
+
+	/* Default values */
+	p->n_meters_per_flow = 1;
+	p->dscp_enabled = 0;
+
+	for (i = 0; i < params->n_args; i++) {
+		char *arg_name = params->args_name[i];
+		char *arg_value = params->args_value[i];
+
+		/* n_flows */
+		if (strcmp(arg_name, "n_flows") == 0) {
+			if (n_flows_present)
+				return -1;
+
+			n_flows_present = 1;
+
+			p->n_flows = atoi(arg_value);
+			if (p->n_flows == 0)
+				return -1;
+
+			continue;
+		}
+
+		/* n_meters_per_flow */
+		if (strcmp(arg_name, "n_meters_per_flow") == 0) {
+			if (n_meters_per_flow_present)
+				return -1;
+
+			n_meters_per_flow_present = 1;
+
+			p->n_meters_per_flow = atoi(arg_value);
+			if ((p->n_meters_per_flow == 0) ||
+				(p->n_meters_per_flow > PIPELINE_FA_N_TC_MAX))
+				return -1;
+
+			continue;
+		}
+
+		/* flow_id_offset */
+		if (strcmp(arg_name, "flow_id_offset") == 0) {
+			if (flow_id_offset_present)
+				return -1;
+
+			flow_id_offset_present = 1;
+
+			p->flow_id_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* ip_hdr_offset */
+		if (strcmp(arg_name, "ip_hdr_offset") == 0) {
+			if (ip_hdr_offset_present)
+				return -1;
+
+			ip_hdr_offset_present = 1;
+
+			p->ip_hdr_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* color_offset */
+		if (strcmp(arg_name, "color_offset") == 0) {
+			if (color_offset_present)
+				return -1;
+
+			color_offset_present = 1;
+
+			p->dscp_enabled = 1;
+			p->color_offset = atoi(arg_value);
+
+			continue;
+		}
+
+		/* Unknown argument */
+		return -1;
+	}
+
+	/* Check that mandatory arguments are present */
+	if ((n_flows_present == 0) ||
+		(flow_id_offset_present == 0) ||
+		(ip_hdr_offset_present == 0) ||
+		(color_offset_present == 0))
+		return -1;
+
+	return 0;
+}
+
+static void
+dscp_init(struct pipeline_flow_actions *p)
+{
+	uint32_t i;
+
+	for (i = 0; i < PIPELINE_FA_N_DSCP; i++) {
+		p->dscp[i].traffic_class = 0;
+		p->dscp[i].color = e_RTE_METER_GREEN;
+	}
+}
+
+static void *pipeline_fa_init(struct pipeline_params *params,
+	__rte_unused void *arg)
+{
+	struct pipeline *p;
+	struct pipeline_flow_actions *p_fa;
+	uint32_t size, i;
+
+	/* Check input arguments */
+	if (params == NULL)
+		return NULL;
+
+	if (params->n_ports_in != params->n_ports_out)
+		return NULL;
+
+	/* Memory allocation */
+	size = RTE_CACHE_LINE_ROUNDUP(
+		sizeof(struct pipeline_flow_actions));
+	p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
+	if (p == NULL)
+		return NULL;
+	p_fa = (struct pipeline_flow_actions *) p;
+
+	strcpy(p->name, params->name);
+	p->log_level = params->log_level;
+
+	PLOG(p, HIGH, "Flow actions");
+
+	/* Parse arguments */
+	if (pipeline_fa_parse_args(&p_fa->params, params))
+		return NULL;
+
+	dscp_init(p_fa);
+
+	/* Pipeline */
+	{
+		struct rte_pipeline_params pipeline_params = {
+			.name = params->name,
+			.socket_id = params->socket_id,
+			.offset_port_id = 0,
+		};
+
+		p->p = rte_pipeline_create(&pipeline_params);
+		if (p->p == NULL) {
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Input ports */
+	p->n_ports_in = params->n_ports_in;
+	for (i = 0; i < p->n_ports_in; i++) {
+		struct rte_pipeline_port_in_params port_params = {
+			.ops = pipeline_port_in_params_get_ops(
+				&params->port_in[i]),
+			.arg_create = pipeline_port_in_params_convert(
+				&params->port_in[i]),
+			.f_action = NULL,
+			.arg_ah = NULL,
+			.burst_size = params->port_in[i].burst_size,
+		};
+
+		int status = rte_pipeline_port_in_create(p->p,
+			&port_params,
+			&p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Output ports */
+	p->n_ports_out = params->n_ports_out;
+	for (i = 0; i < p->n_ports_out; i++) {
+		struct rte_pipeline_port_out_params port_params = {
+			.ops = pipeline_port_out_params_get_ops(
+				&params->port_out[i]),
+			.arg_create = pipeline_port_out_params_convert(
+				&params->port_out[i]),
+			.f_action = NULL,
+			.f_action_bulk = NULL,
+			.arg_ah = NULL,
+		};
+
+		int status = rte_pipeline_port_out_create(p->p,
+			&port_params,
+			&p->port_out_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Tables */
+	p->n_tables = 1;
+	{
+		struct rte_table_array_params table_array_params = {
+			.n_entries = p_fa->params.n_flows,
+			.offset = p_fa->params.flow_id_offset,
+		};
+
+		struct rte_pipeline_table_params table_params = {
+			.ops = &rte_table_array_ops,
+			.arg_create = &table_array_params,
+			.f_action_hit = get_fa_table_ah_hit(p_fa),
+			.f_action_miss = NULL,
+			.arg_ah = p_fa,
+			.action_data_size =
+				sizeof(struct flow_table_entry) -
+				sizeof(struct rte_pipeline_table_entry),
+		};
+
+		int status;
+
+		status = rte_pipeline_table_create(p->p,
+			&table_params,
+			&p->table_id[0]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Connecting input ports to tables */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_connect_to_table(p->p,
+			p->port_in_id[i],
+			p->table_id[0]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Enable input ports */
+	for (i = 0; i < p->n_ports_in; i++) {
+		int status = rte_pipeline_port_in_enable(p->p,
+			p->port_in_id[i]);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Initialize table entries */
+	for (i = 0; i < p_fa->params.n_flows; i++) {
+		struct rte_table_array_key key = {
+			.pos = i,
+		};
+
+		struct flow_table_entry entry;
+		struct rte_pipeline_table_entry *entry_ptr;
+		int key_found, status;
+
+		flow_table_entry_set_default(p_fa, &entry);
+
+		status = rte_pipeline_table_entry_add(p->p,
+			p->table_id[0],
+			&key,
+			(struct rte_pipeline_table_entry *) &entry,
+			&key_found,
+			&entry_ptr);
+
+		if (status) {
+			rte_pipeline_free(p->p);
+			rte_free(p);
+			return NULL;
+		}
+	}
+
+	/* Check pipeline consistency */
+	if (rte_pipeline_check(p->p) < 0) {
+		rte_pipeline_free(p->p);
+		rte_free(p);
+		return NULL;
+	}
+
+	/* Message queues */
+	p->n_msgq = params->n_msgq;
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_in[i] = params->msgq_in[i];
+	for (i = 0; i < p->n_msgq; i++)
+		p->msgq_out[i] = params->msgq_out[i];
+
+	/* Message handlers */
+	memcpy(p->handlers, handlers, sizeof(p->handlers));
+	memcpy(p_fa->custom_handlers,
+		custom_handlers,
+		sizeof(p_fa->custom_handlers));
+
+	return p;
+}
+
+static int
+pipeline_fa_free(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if (p == NULL)
+		return -1;
+
+	/* Free resources */
+	rte_pipeline_free(p->p);
+	rte_free(p);
+	return 0;
+}
+
+static int
+pipeline_fa_track(void *pipeline,
+	__rte_unused uint32_t port_in,
+	uint32_t *port_out)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	/* Check input arguments */
+	if ((p == NULL) ||
+		(port_in >= p->n_ports_in) ||
+		(port_out == NULL))
+		return -1;
+
+	if (p->n_ports_in == 1) {
+		*port_out = 0;
+		return 0;
+	}
+
+	return -1;
+}
+
+static int
+pipeline_fa_timer(void *pipeline)
+{
+	struct pipeline *p = (struct pipeline *) pipeline;
+
+	pipeline_msg_req_handle(p);
+	rte_pipeline_flush(p->p);
+
+	return 0;
+}
+
+void *
+pipeline_fa_msg_req_custom_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_flow_actions *p_fa =
+			(struct pipeline_flow_actions *) p;
+	struct pipeline_custom_msg_req *req = msg;
+	pipeline_msg_req_handler f_handle;
+
+	f_handle = (req->subtype < PIPELINE_FA_MSG_REQS) ?
+		p_fa->custom_handlers[req->subtype] :
+		pipeline_msg_req_invalid_handler;
+
+	if (f_handle == NULL)
+		f_handle = pipeline_msg_req_invalid_handler;
+
+	return f_handle(p, req);
+}
+
+void *
+pipeline_fa_msg_req_flow_config_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_flow_actions *p_fa = (struct pipeline_flow_actions *) p;
+	struct pipeline_fa_flow_config_msg_req *req = msg;
+	struct pipeline_fa_flow_config_msg_rsp *rsp = msg;
+	struct flow_table_entry *entry;
+	uint32_t mask, i;
+
+	/* Set flow table entry to default if not configured before */
+	if (req->entry_ptr == NULL) {
+		struct rte_table_array_key key = {
+			.pos = req->flow_id % p_fa->params.n_flows,
+		};
+
+		struct flow_table_entry default_entry;
+
+		int key_found, status;
+
+		flow_table_entry_set_default(p_fa, &default_entry);
+
+		status = rte_pipeline_table_entry_add(p->p,
+			p->table_id[0],
+			&key,
+			(struct rte_pipeline_table_entry *) &default_entry,
+			&key_found,
+			(struct rte_pipeline_table_entry **) &entry);
+		if (status) {
+			rsp->status = -1;
+			return rsp;
+		}
+	} else
+		entry = (struct flow_table_entry *) req->entry_ptr;
+
+	/* Meter */
+	for (i = 0, mask = 1; i < PIPELINE_FA_N_TC_MAX; i++, mask <<= 1) {
+		int status;
+
+		if ((mask & req->meter_update_mask) == 0)
+			continue;
+
+		status = flow_table_entry_set_meter(entry, i, &req->params);
+		if (status) {
+			rsp->status = -1;
+			return rsp;
+		}
+	}
+
+	/* Policer */
+	for (i = 0, mask = 1; i < PIPELINE_FA_N_TC_MAX; i++, mask <<= 1) {
+		if ((mask & req->policer_update_mask) == 0)
+			continue;
+
+		flow_table_entry_set_policer(entry, i, &req->params);
+	}
+
+	/* Port */
+	if (req->port_update)
+		flow_table_entry_set_port_id(p_fa, entry, &req->params);
+
+	/* Response */
+	rsp->status = 0;
+	rsp->entry_ptr = (void *) entry;
+	return rsp;
+}
+
+void *
+pipeline_fa_msg_req_flow_config_bulk_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_flow_actions *p_fa = (struct pipeline_flow_actions *) p;
+	struct pipeline_fa_flow_config_bulk_msg_req *req = msg;
+	struct pipeline_fa_flow_config_bulk_msg_rsp *rsp = msg;
+	uint32_t i;
+
+	for (i = 0; i < req->n_flows; i++) {
+		struct flow_table_entry *entry;
+		uint32_t j, mask;
+
+		/* Set flow table entry to default if not configured before */
+		if (req->entry_ptr[i] == NULL) {
+			struct rte_table_array_key key = {
+				.pos = req->flow_id[i] % p_fa->params.n_flows,
+			};
+
+			struct flow_table_entry entry_to_add;
+
+			int key_found, status;
+
+			flow_table_entry_set_default(p_fa, &entry_to_add);
+
+			status = rte_pipeline_table_entry_add(p->p,
+			 p->table_id[0],
+			 &key,
+			 (struct rte_pipeline_table_entry *) &entry_to_add,
+			 &key_found,
+			 (struct rte_pipeline_table_entry **) &entry);
+			if (status) {
+				rsp->n_flows = i;
+				return rsp;
+			}
+
+			req->entry_ptr[i] = (void *) entry;
+		} else
+			entry = (struct flow_table_entry *) req->entry_ptr[i];
+
+		/* Meter */
+		for (j = 0, mask = 1;
+			j < PIPELINE_FA_N_TC_MAX;
+			j++, mask <<= 1) {
+			int status;
+
+			if ((mask & req->meter_update_mask) == 0)
+				continue;
+
+			status = flow_table_entry_set_meter(entry,
+				j, &req->params[i]);
+			if (status) {
+				rsp->n_flows = i;
+				return rsp;
+			}
+		}
+
+		/* Policer */
+		for (j = 0, mask = 1;
+			j < PIPELINE_FA_N_TC_MAX;
+			j++, mask <<= 1) {
+			if ((mask & req->policer_update_mask) == 0)
+				continue;
+
+			flow_table_entry_set_policer(entry,
+			 j, &req->params[i]);
+		}
+
+		/* Port */
+		if (req->port_update)
+			flow_table_entry_set_port_id(p_fa,
+			 entry, &req->params[i]);
+	}
+
+	/* Response */
+	rsp->n_flows = i;
+	return rsp;
+}
+
+void *
+pipeline_fa_msg_req_dscp_config_handler(struct pipeline *p, void *msg)
+{
+	struct pipeline_flow_actions *p_fa = (struct pipeline_flow_actions *) p;
+	struct pipeline_fa_dscp_config_msg_req *req = msg;
+	struct pipeline_fa_dscp_config_msg_rsp *rsp = msg;
+
+	/* Check request */
+	if ((req->dscp >= PIPELINE_FA_N_DSCP) ||
+		(req->traffic_class >= PIPELINE_FA_N_TC_MAX) ||
+		(req->color >= e_RTE_METER_COLORS)) {
+		rsp->status = -1;
+		return rsp;
+	}
+
+	p_fa->dscp[req->dscp].traffic_class = req->traffic_class;
+	p_fa->dscp[req->dscp].color = req->color;
+	rsp->status = 0;
+	return rsp;
+}
+
+void *
+pipeline_fa_msg_req_policer_stats_read_handler(__rte_unused struct pipeline *p,
+	void *msg)
+{
+	struct pipeline_fa_policer_stats_msg_req *req = msg;
+	struct pipeline_fa_policer_stats_msg_rsp *rsp = msg;
+
+	struct flow_table_entry *entry = req->entry_ptr;
+	uint32_t policer_id = req->policer_id;
+	int clear = req->clear;
+
+	/* Check request */
+	if ((req->entry_ptr == NULL) ||
+		(req->policer_id >= PIPELINE_FA_N_TC_MAX)) {
+		rsp->status = -1;
+		return rsp;
+	}
+
+	memcpy(&rsp->stats,
+		&entry->mp[policer_id].stats,
+		sizeof(rsp->stats));
+	if (clear)
+		memset(&entry->mp[policer_id].stats,
+			0, sizeof(entry->mp[policer_id].stats));
+	rsp->status = 0;
+	return rsp;
+}
+
+struct pipeline_be_ops pipeline_flow_actions_be_ops = {
+	.f_init = pipeline_fa_init,
+	.f_free = pipeline_fa_free,
+	.f_run = NULL,
+	.f_timer = pipeline_fa_timer,
+	.f_track = pipeline_fa_track,
+};
diff --git a/examples/ip_pipeline/pipeline/pipeline_flow_actions_be.h b/examples/ip_pipeline/pipeline/pipeline_flow_actions_be.h
new file mode 100644
index 0000000..456f2cc
--- /dev/null
+++ b/examples/ip_pipeline/pipeline/pipeline_flow_actions_be.h
@@ -0,0 +1,168 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __INCLUDE_PIPELINE_FLOW_ACTIONS_BE_H__
+#define __INCLUDE_PIPELINE_FLOW_ACTIONS_BE_H__
+
+#include <rte_meter.h>
+
+#include "pipeline_common_be.h"
+
+#ifndef PIPELINE_FA_N_TC_MAX
+#define PIPELINE_FA_N_TC_MAX                               4
+#endif
+
+#define PIPELINE_FA_N_DSCP                                 64
+
+struct pipeline_fa_params {
+	uint32_t n_flows;
+	uint32_t n_meters_per_flow;
+	uint32_t flow_id_offset;
+	uint32_t ip_hdr_offset;
+	uint32_t color_offset;
+	uint32_t dscp_enabled;
+};
+
+int
+pipeline_fa_parse_args(struct pipeline_fa_params *p,
+	struct pipeline_params *params);
+
+struct pipeline_fa_policer_action {
+	uint32_t drop;
+	enum rte_meter_color color;
+};
+
+struct pipeline_fa_policer_params {
+	struct pipeline_fa_policer_action action[e_RTE_METER_COLORS];
+};
+
+struct pipeline_fa_flow_params {
+	struct rte_meter_trtcm_params m[PIPELINE_FA_N_TC_MAX];
+	struct pipeline_fa_policer_params p[PIPELINE_FA_N_TC_MAX];
+	uint32_t port_id;
+};
+
+int
+pipeline_fa_flow_params_set_default(struct pipeline_fa_flow_params *params);
+
+struct pipeline_fa_policer_stats {
+	uint64_t n_pkts[e_RTE_METER_COLORS];
+	uint64_t n_pkts_drop;
+};
+
+enum pipeline_fa_msg_req_type {
+	PIPELINE_FA_MSG_REQ_FLOW_CONFIG = 0,
+	PIPELINE_FA_MSG_REQ_FLOW_CONFIG_BULK,
+	PIPELINE_FA_MSG_REQ_DSCP_CONFIG,
+	PIPELINE_FA_MSG_REQ_POLICER_STATS_READ,
+	PIPELINE_FA_MSG_REQS,
+};
+
+/*
+ * MSG FLOW CONFIG
+ */
+struct pipeline_fa_flow_config_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fa_msg_req_type subtype;
+
+	void *entry_ptr;
+	uint32_t flow_id;
+
+	uint32_t meter_update_mask;
+	uint32_t policer_update_mask;
+	uint32_t port_update;
+	struct pipeline_fa_flow_params params;
+};
+
+struct pipeline_fa_flow_config_msg_rsp {
+	int status;
+	void *entry_ptr;
+};
+
+/*
+ * MSG FLOW CONFIG BULK
+ */
+struct pipeline_fa_flow_config_bulk_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fa_msg_req_type subtype;
+
+	void **entry_ptr;
+	uint32_t *flow_id;
+	uint32_t n_flows;
+
+	uint32_t meter_update_mask;
+	uint32_t policer_update_mask;
+	uint32_t port_update;
+	struct pipeline_fa_flow_params *params;
+};
+
+struct pipeline_fa_flow_config_bulk_msg_rsp {
+	uint32_t n_flows;
+};
+
+/*
+ * MSG DSCP CONFIG
+ */
+struct pipeline_fa_dscp_config_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fa_msg_req_type subtype;
+
+	uint32_t dscp;
+	uint32_t traffic_class;
+	enum rte_meter_color color;
+};
+
+struct pipeline_fa_dscp_config_msg_rsp {
+	int status;
+};
+
+/*
+ * MSG POLICER STATS READ
+ */
+struct pipeline_fa_policer_stats_msg_req {
+	enum pipeline_msg_req_type type;
+	enum pipeline_fa_msg_req_type subtype;
+
+	void *entry_ptr;
+	uint32_t policer_id;
+	int clear;
+};
+
+struct pipeline_fa_policer_stats_msg_rsp {
+	int status;
+	struct pipeline_fa_policer_stats stats;
+};
+
+extern struct pipeline_be_ops pipeline_flow_actions_be_ops;
+
+#endif
-- 
2.1.0



More information about the dev mailing list