[v2,2/2] net/cpfl: add TDI to flow engine

Message ID 20240105081649.530384-3-wenjing.qiao@intel.com (mailing list archive)
State Changes Requested
Delegated to: Bruce Richardson
Headers
Series net/cpfl: support flow offloading for P4 |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation success Compilation OK
ci/github-robot: build success github build: passed
ci/intel-Testing success Testing PASS
ci/intel-Functional success Functional PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-abi-testing success Testing PASS
ci/iol-unit-arm64-testing success Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-compile-arm64-testing success Testing PASS
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-sample-apps-testing success Testing PASS

Commit Message

Wenjing Qiao Jan. 5, 2024, 8:16 a.m. UTC
  From: Wenjing Qiao <wenjing.qiao@intel.com>

Add TDI implementation to a flow engine.

Signed-off-by: Wenjing Qiao <wenjing.qiao@intel.com>
---
 doc/guides/nics/cpfl.rst                |   10 +
 doc/guides/nics/features/cpfl.ini       |    1 +
 drivers/net/cpfl/cpfl_ethdev.h          |    8 +
 drivers/net/cpfl/cpfl_flow.c            |    5 +-
 drivers/net/cpfl/cpfl_flow.h            |    1 +
 drivers/net/cpfl/cpfl_flow_engine_fxp.c |   12 -
 drivers/net/cpfl/cpfl_flow_parser.c     |    8 +
 drivers/net/cpfl/cpfl_fxp_rule.h        |   12 +
 drivers/net/cpfl/cpfl_tdi.c             | 1282 +++++++++++++++++++++++
 drivers/net/cpfl/cpfl_tdi.h             |  123 +++
 drivers/net/cpfl/meson.build            |    1 +
 11 files changed, 1450 insertions(+), 13 deletions(-)
 create mode 100644 drivers/net/cpfl/cpfl_tdi.c
 create mode 100644 drivers/net/cpfl/cpfl_tdi.h
  

Comments

Vladimir Medvedkin Feb. 14, 2024, 5:21 p.m. UTC | #1
Hi Wenjing,

Please find comments inlined

On 05/01/2024 08:16, wenjing.qiao@intel.com wrote:
> From: Wenjing Qiao<wenjing.qiao@intel.com>
>
> Add TDI implementation to a flow engine.
>
> Signed-off-by: Wenjing Qiao<wenjing.qiao@intel.com>
> ---
<snip>
> --- /dev/null
> +++ b/drivers/net/cpfl/cpfl_tdi.c
> @@ -0,0 +1,1282 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2023 Intel Corporation
> + */
> +#include <rte_hash_crc.h>
> +#include <rte_tailq.h>
RTE includes should be after system includes
> +#include <stdint.h>
> +#include <string.h>
> +
> +#include "cpfl_actions.h"
> +#include "cpfl_flow.h"
> +#include "cpfl_fxp_rule.h"
> +#include "cpfl_tdi.h"
> +#include "cpfl_tdi_parser.h"
> +#include "rte_branch_prediction.h"
> +#include "rte_common.h"
> +#include "rte_flow.h"
Those includes should be together with other RTE includes and have angle 
brackets
> +
> +#define CPFL_NO_FWD 0
> +uint64_t cpfl_tdi_rule_cookie = CPFL_COOKIE_DEF;
> +
> +/*help function to do left shift on a byte array */
> +static void
> +cpfl_tdi_shift_left(uint8_t *buf, int length, uint32_t shift_amount)
> +{
> +	uint32_t i;
> +	int j;
> +	uint32_t carry = 0;
> +
> +	if (shift_amount == 0)
> +		return;
> +
> +	for (i = 0; i < shift_amount; i += 8) {
> +		for (j = 0; j < length; j++) {
> +			uint32_t temp = (buf[j] << (shift_amount - i)) | carry;
> +
> +			carry = (temp >> 8) & 0xff;
> +			buf[j] = temp & 0xff;
> +		}
> +	}
> +}

This function does not looks correct to me, at least in case when 
shift_amount is larger than 8.

You can try something like:

static void
cpfl_tdi_shift_left(uint8_t *buf, int length, uint32_t shift_amount)
{
         int i;
         int bit_shift = shift_amount & 0x7;
         int byte_shift = shift_amount >> 3;

         if (shift_amount >= (length * 8))
                 return;

         uint8_t prev = 0;
         for (i = 0; i < length; i++) {
                 uint8_t val = (buf[i] << bit_shift) | prev;
                 prev = buf[i] >> (8 - bit_shift);
                 buf[i] = val;
         }

         for (i = length - 1; i >= byte_shift; i--) {
                 buf[i] = buf[i - byte_shift];
         }

         for(; i >= 0; i--)
                 buf[i] = 0;
}

> +
> +/* help function to init a mask array with bit_width */
> +static void
> +cpfl_tdi_init_msk_buf(uint8_t *buf, int length, uint32_t bit_width)
> +{
> +	uint32_t i;
> +
> +	memset(buf, 0, length);
> +	for (i = 0; i < bit_width; i++) {
> +		cpfl_tdi_shift_left(buf, length, 1);
> +		buf[0] += 1;
> +	}
> +}

It seems like this function sets bit_width bits into the buf starting 
from the LSB. If so I'd suggest to make it simpler and not use 
cpfl_tdi_shift_left(). Instead you can just write UINT8/16/32/64_MAX 
into the corresponding buffer and decrease bit_width accordingly if it 
is bigger than 8/16/32/64 and finally write (1 << bit_width) - 1.

> +
> +/* help function to OR a byte array with value and mask */
> +static void
> +cpfl_tdi_or_buf(uint8_t *buf, int length, uint8_t *values, uint8_t *mask)
> +{
> +	int i;
> +
> +	for (i = 0; i < length; i++)
> +		buf[i] = (values[i] & mask[i]) | (buf[i] & ~mask[i]);
I guess "& ~mask[i]" is not necessary here if this function is really OR.
> +}
<snip>
> +
> +static void
> +cpfl_tdi_pack_sem_entry(struct cpfl_tdi_rule_info *rinfo,
> +			struct cpfl_tdi_ma_hardware_block *hb,
> +			enum cpfl_tdi_table_entry_op op,
> +			struct idpf_dma_mem *dma,
> +			struct idpf_ctlq_msg *msg)
> +{
> +	union cpfl_rule_cfg_pkt_record *blob;
> +	struct cpfl_rule_cfg_data cfg = {0};
> +	uint16_t cfg_ctrl;
> +	enum cpfl_ctlq_rule_cfg_opc opc = 0;
> +	const struct cpfl_tdi_table_key_obj *key = &rinfo->kobj;
> +	const struct cpfl_tdi_action_obj *action = &rinfo->aobj;
> +
> +	blob = (void *)dma->va;
Why not cast to (union cpfl_rule_cfg_pkt_record *)?
> +	memset(blob, 0, sizeof(*blob));
> +
> +	cfg_ctrl = CPFL_GET_MEV_SEM_RULE_CFG_CTRL(hb->profile[0], hb->sem.sub_profile, 0, 0);
> +
<snip>
> +static int
> +cpfl_tdi_rule_process(struct cpfl_itf *itf,
> +		      struct idpf_ctlq_info *tx_cq,
> +		      struct idpf_ctlq_info *rx_cq,
> +		      struct cpfl_tdi_rule_info *rinfo,
> +		      int rule_num,
> +		      enum cpfl_tdi_table_entry_op op)
> +{
> +	const struct cpfl_tdi_table_key_obj *kobj;
> +	struct idpf_hw *hw = &itf->adapter->base.hw;
> +	struct cpfl_tdi_ma_hardware_block *hb;
> +	const struct cpfl_tdi_table *table;
> +	int ret = 0;
> +
> +	if (rule_num == 0)
> +		return 0;

Is it necessary to check this value? This function is called with 
nonzero value always.

As a general question, do we need to check input parameters in all these 
static functions? It seems some of them are callbacks and their 
parameters are probably checked by API, but for others it may be the case.

> +
> +	kobj = &rinfo->kobj;
> +
> +	table = kobj->tnode->table;
<snip>
> +static int
> +cpfl_tdi_fxp_rule_destroy(struct rte_eth_dev *dev,
> +			  struct rte_flow *flow,
> +			  struct rte_flow_error *error)
> +{
> +	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
> +	struct cpfl_adapter_ext *ad = itf->adapter;
> +	struct cpfl_vport *vport;
> +	struct cpfl_repr *repr;
> +	struct cpfl_tdi_rule_info *rinfo = (struct cpfl_tdi_rule_info *)flow->rule;
> +	int ret = 0;
> +	uint32_t cpq_id = 0;
> +
> +	if (itf->type == CPFL_ITF_TYPE_VPORT) {
> +		vport = (struct cpfl_vport *)itf;
> +		cpq_id = vport->base.devarg_id * 2;
> +	} else if (itf->type == CPFL_ITF_TYPE_REPRESENTOR) {
> +		repr = (struct cpfl_repr *)itf;
> +		cpq_id = ((repr->repr_id.pf_id + repr->repr_id.vf_id) & (CPFL_TX_CFGQ_NUM - 1)) * 2;
> +	} else {
> +		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
> +				   "fail to find correct control queue");
> +		ret = -rte_errno;
> +		goto err;
> +	}
> +
> +	ret = cpfl_tdi_rule_process(itf, ad->ctlqp[cpq_id], ad->ctlqp[cpq_id + 1], rinfo, 1,
> +				    CPFL_TDI_TABLE_ENTRY_OP_DEL);
> +	if (ret)
> +		goto err;
What's the purpose of this goto here?
> +
> +err:
> +	rte_free(rinfo);
> +	flow->rule = NULL;
> +	return ret;
> +}
> +
> +void
> +cpfl_tdi_free_table_list(struct cpfl_flow_parser *flow_parser)
> +{
> +	struct cpfl_tdi_table_node *node;
Should this parameter be checked since this is non static function?
> +
> +	while ((node = TAILQ_FIRST(&flow_parser->tdi_table_list))) {
> +		TAILQ_REMOVE(&flow_parser->tdi_table_list, node, next);
> +		rte_free(node);
> +	}
> +}
> +
<snip>
> +
> +static int
> +cpfl_tdi_table_key_create(struct rte_eth_dev *dev,
> +			  uint32_t table_id,
> +			  struct cpfl_tdi_table_node **table_node,
> +			  struct cpfl_tdi_table_key_obj *kobj)
> +{
> +	int ret;
> +
> +	if (!kobj)
> +		return -EINVAL;

is it required for static function? in cpfl_tdi_parse_pattern_action() 
kobj is zero inited, so in can not be NULL here. Otherwise it would make 
sense to check all other arguments as well. I'd suggest to replace 
runtime if check with RTE_ASSERT on all input arguments instead.

> +
> +	ret = cpfl_tdi_table_info_get(dev, table_id, table_node);
> +	if (ret != 0)
> +		return -EINVAL;
> +
> +	ret = cpfl_tdi_table_key_node_init(dev, *table_node, kobj);
> +	if (ret != 0)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int
> +cpfl_tdi_table_key_field_info_get(__rte_unused struct rte_eth_dev *dev,
why not remove this argument is it is unused? Same applied to several 
other functions
> +				  struct cpfl_tdi_table_node *node,
> +				  uint32_t field_id,
> +				  struct cpfl_tdi_table_key_field_info **key_field_info)
> +{
<snip>
> +static int
> +cpfl_tdi_table_key_field_set(struct rte_eth_dev *dev,
> +			     struct cpfl_tdi_table_node *table_node,
> +			     struct cpfl_tdi_table_key_obj *kobj,
> +			     uint32_t field_id,
> +			     const uint8_t *value,
> +			     uint16_t size)
> +{
> +	struct cpfl_tdi_table_key_field_info *key_field_info;
> +	int ret;
> +
> +	if (!kobj || !value)
> +		return -EINVAL;
> +
> +	ret = cpfl_tdi_table_key_field_info_get(dev, table_node, field_id, &key_field_info);
> +	if (ret != 0)
> +		return -EINVAL;
> +
> +	if (key_field_info->field->match_type != CPFL_TDI_MATCH_TYPE_EXACT)
> +		return -EINVAL;
possible memory leak since cpfl_tdi_table_key_field_info_get() allocated 
memory for key_field_info. Also it would be better to name properly 
functions where memory allocation was done.
> +
> +	if (unlikely(key_field_info->field->is_vsi)) {
No need to use branch predictor hints since this is not a fast path
> +		struct cpfl_itf *src_itf;
> +		uint16_t vsi_id, port_id;
> +		uint8_t ids[2];
> +
> +		if (size != 1 && size != 2) /* input port id should be 8/16 bits */
> +			return -EINVAL;
> +		port_id = size == 1 ? value[0] : value[0] << 8 | value[1];
please use parenthesis for easier reading
> +		src_itf = cpfl_get_itf_by_port_id(port_id);
> +		if (!src_itf)
> +			return -EINVAL;
> +		vsi_id = cpfl_get_vsi_id(src_itf);
> +		if (vsi_id == CPFL_INVALID_HW_ID)
> +			return -EINVAL;
> +		ids[0] = (uint8_t)vsi_id;
> +		ids[1] = (uint8_t)vsi_id >> 8;
> +
> +		ret = _cpfl_tdi_table_key_field_set(dev, kobj, key_field_info, ids, size);
I think here it would be safer to set size = 2. otherwise stack overflow 
can happen
> +		if (ret != 0)
> +			return -EINVAL;
> +	} else {
> +		ret = _cpfl_tdi_table_key_field_set(dev, kobj, key_field_info, value, size);
> +		if (ret != 0)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
<snip>
> +
> +static inline bool __rte_unused
is this function necessary in this patch series?
> +verify_action(struct cpfl_tdi_table_node *table_node, uint32_t spec_id)
> +{
> +	int i;
> +	const struct cpfl_tdi_table *table = table_node->table;
> +
> +	for (i = 0; i < table->action_num; i++)
> +		if (spec_id == table->actions[i].handle)
> +			return true;
> +
> +	return false;
> +}
> +
> +static int
> +cpfl_tdi_action_node_get_by_spec_id(struct rte_eth_dev *dev,
> +				    uint32_t table_id __rte_unused,
> +				    uint32_t spec_id,
> +				    struct cpfl_tdi_action_node **action_node)
> +{
> +	return cpfl_tdi_action_spec_info_get(dev, spec_id, action_node);
> +}
What's the purpose of this static wrapper function?
> +
> +static int
> +cpfl_tdi_action_spec_field_info_get(struct rte_eth_dev *dev __rte_unused,
> +				    struct cpfl_tdi_action_node *anode,
> +				    uint32_t field_id,
> +				    struct cpfl_tdi_action_spec_field_info **info)
> +{
> +	int ret = -EINVAL;
> +	int i, j;
> +
> +	if (anode->format == NULL)
> +		goto err;
> +
> +	for (i = 0; i < anode->format->immediate_field_num; i++) {
> +		struct cpfl_tdi_immediate_field *field = &anode->format->immediate_fields[i];
> +		struct cpfl_tdi_action_spec_field_info *asfinfo;
> +
> +		if (field->param_handle != field_id)
> +			continue;
> +
> +		asfinfo = rte_malloc(NULL, sizeof(struct cpfl_tdi_action_spec_field_info), 0);
> +		if (asfinfo == NULL) {
> +			ret = -ENOMEM;
> +			goto err;
why not just return from here?
> +		}
> +
> +		asfinfo->field = field;
> +		asfinfo->param = anode->params[i];
> +
<snip>
> +static int
> +_cpfl_tdi_action_field_set(struct rte_eth_dev *dev __rte_unused,
> +			   struct cpfl_tdi_action_obj *aobj,
> +			   struct cpfl_tdi_action_spec_field_info *asfinfo,
> +			   const uint8_t *value,
> +			   uint16_t size,
> +			   enum cpfl_act_fwd_type fwd_type)
> +{
> +	struct cpfl_tdi_action_node *node = aobj->node;
> +	struct cpfl_tdi_param_info *pi = &asfinfo->param;
> +	uint8_t val_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
> +	uint8_t msk_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
> +
> +	memcpy(val_buf, value, size);
> +
> +	if (node->format->mod_content_format.mod_field_num > 0) {
> +		struct cpfl_tdi_mod_field *mf = asfinfo->mod_field;
> +
> +		cpfl_tdi_shift_left(val_buf, pi->size, mf->start_bit_offset);

how it is guaranteed that pi->size is less than CPFL_TDI_VALUE_SIZE_MAX?

What is the difference between size argument and pi->size here?

> +		cpfl_tdi_init_msk_buf(msk_buf, pi->size, mf->bit_width);
> +		cpfl_tdi_shift_left(msk_buf, pi->size, mf->start_bit_offset);
> +		cpfl_tdi_or_buf(&aobj->buf[pi->offset], pi->size, val_buf, msk_buf);
> +	} else {
> +		struct cpfl_tdi_hw_action *ha = asfinfo->hw_action;
> +		uint32_t action_code = cpfl_tdi_to_action_code(ha, val_buf, fwd_type);
> +
> +		memcpy(&aobj->buf[pi->offset], &action_code, 4);
> +	}
> +
> +	return 0;
why this function returns anything when it always returns 0?
> +}
> +
> +static int
> +cpfl_tdi_action_field_set(struct rte_eth_dev *dev,
> +			  struct cpfl_tdi_action_obj *aobj,
> +			  struct cpfl_tdi_action_node *action_node,
> +			  uint32_t field_id,
> +			  const struct rte_flow_action_prog_argument *arg)
> +{
> +	struct cpfl_tdi_action_spec_field_info *asfinfo;
> +	enum rte_flow_action_type action_type;
> +	/* used when action is PORT_REPRESENTOR type */
> +	struct cpfl_itf *dst_itf;
> +	uint16_t dev_id; /* vsi id */
> +	uint16_t port_id;
> +	uint8_t value;
> +	bool is_vsi;
> +	int ret;
> +	enum cpfl_act_fwd_type fwd_type;
> +
> +	if (!aobj || !arg)
> +		return -EINVAL;
> +
> +	ret = cpfl_tdi_action_spec_field_info_get(dev, action_node, field_id, &asfinfo);

it seems like asfinfo that was allocated inside 
cpfl_tdi_action_spec_field_info_get() is only used locally inside the 
cpfl_tdi_action_field_set(). It should be freed somewhere in this 
function before it returns.

Or as a better solution why not just have this struct as stack 
allocated, don't allocate memory for it at all and just init it inside 
the cpfl_tdi_action_spec_field_info_get()? Otherwise you'll have 
problems with possible memory leak.

> +	if (ret != 0)
> +		return -EINVAL;
> +
> +	if (!strcmp(arg->name, "port_representor"))
> +		action_type = RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR;
> +	else if (!strcmp(arg->name, "represented_port"))
> +		action_type = RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT;
> +	else
> +		return _cpfl_tdi_action_field_set(dev, aobj, asfinfo, arg->value, arg->size,
> +						  CPFL_NO_FWD);
> +
> +	if (arg->size != 1 && arg->size != 2) /* input port_id should be 8/16 bits */
> +		return -EINVAL;
same problem with memory leak after cpfl_tdi_action_spec_field_info_get()
> +
> +	port_id = arg->size == 1 ? arg->value[0] : arg->value[0] << 8 | arg->value[1];
> +	dst_itf = cpfl_get_itf_by_port_id(port_id);
> +	if (!dst_itf)
> +		goto err;
> +
> +	is_vsi = (action_type == RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR ||
> +		  dst_itf->type == CPFL_ITF_TYPE_REPRESENTOR);
> +	if (is_vsi)
> +		dev_id = cpfl_get_vsi_id(dst_itf);
> +	else
> +		dev_id = cpfl_get_port_id(dst_itf);
> +
> +	if (dev_id == CPFL_INVALID_HW_ID)
> +		goto err;
> +
> +	value = (uint8_t)dev_id;
> +	fwd_type = is_vsi ? CPFL_ACT_FWD_VSI : CPFL_ACT_FWD_PORT;
> +
> +	return _cpfl_tdi_action_field_set(dev, aobj, asfinfo, &value, arg->size, fwd_type);
> +err:
> +	PMD_DRV_LOG(ERR, "Can not get dev id.");
> +	return -EINVAL;
> +}
> +
> +int
> +cpfl_tdi_build(struct cpfl_flow_parser *flow_parser)
> +{
> +	int ret;
> +
for non static function input arguments should probably be checked.
> +	ret = cpfl_tdi_build_table_list(flow_parser);
> +	if (ret != 0) {
> +		PMD_INIT_LOG(ERR, "Failed to build tdi table list");
> +		return ret;
> +	}
> +
<snip>
> +static int
> +cpfl_tdi_parse_action(struct rte_eth_dev *dev,
> +		      const struct rte_flow_action actions[],
> +		      uint32_t table_id,
> +		      struct cpfl_tdi_table_node *table_node,
> +		      struct cpfl_tdi_action_node **action_node,
> +		      struct cpfl_tdi_action_obj *aobj)
> +{
> +	const struct rte_flow_action_prog *prog;
> +	const struct rte_flow_action_prog_argument *arg;
> +	uint32_t action_spec_id;
> +	int i, ret;
> +	uint32_t j;
> +
> +	for (i = 0; actions[i].type != RTE_FLOW_ACTION_TYPE_END; i++) {
> +		if (actions[i].type != RTE_FLOW_ACTION_TYPE_PROG)
> +			continue;
> +
> +		prog = actions[i].conf;
> +		action_spec_id = atoi(prog->name);
> +		/* Get action node */
> +		ret =
> +		    cpfl_tdi_action_node_get_by_spec_id(dev, table_id, action_spec_id, action_node);
> +		if (ret) {
> +			PMD_INIT_LOG(ERR, "Failed to get action node.");
> +			return -EINVAL;
> +		}
> +
> +		ret = cpfl_tdi_action_obj_init(dev, table_node, *action_node, aobj);
> +		if (ret != 0) {
nothing can happen inside cpfl_tdi_action_obj_init(), it always returns 0
> +			PMD_INIT_LOG(ERR, "Failed to init action obj.");
> +			return -EINVAL;
> +		}
> +
> +		for (j = 0; j < prog->args_num; j++) {
> +			arg = &prog->args[j];
> +			/* Set the action fields */
> +			ret = cpfl_tdi_action_field_set(dev, aobj, *action_node, j, arg);
> +			if (ret) {
> +				PMD_INIT_LOG(ERR, "Failed to set action field.");
> +				return -EINVAL;
> +			}
> +		}
> +	}
> +	return 0;
> +}
> +
<snip>
> -- 

Regards,
Vladimir
  

Patch

diff --git a/doc/guides/nics/cpfl.rst b/doc/guides/nics/cpfl.rst
index 9b7a99c894..591bd496e6 100644
--- a/doc/guides/nics/cpfl.rst
+++ b/doc/guides/nics/cpfl.rst
@@ -213,6 +213,16 @@  low level hardware resources.
       flow create X ingress group M pattern eth dst is 00:01:00:00:03:14 / ipv4 src is 192.168.0.1 \
       dst is 192.168.0.2 / tcp / end actions port_representor port_id Y / end
 
+#. Create one flow for TDI engine to forward ETH-IPV4-TCP from I/O port to a local(CPF's) vport. Flow should
+   be created on vport X. Group M should be table id. Prog name N should be action id. Prog arguments
+   port_representor Y means forward packet to local vport Y::
+
+   .. code-block:: console
+
+      flow create X ingress group M pattern prog key is 0x00 / prog key is 0x000100000314 / prog key
+      is 0x001122334455 / prog key is 0xC0A80001 / prog key is 0xC0A80002 / prog key is 0x1451 / prog key
+      is 0x157C / end actions prog name N arguments port_representor Y  end / end
+
 #. Send a matched packet, and it should be displayed on PMD::
 
    .. code-block:: console
diff --git a/doc/guides/nics/features/cpfl.ini b/doc/guides/nics/features/cpfl.ini
index 4eadaca6e7..85b8011a54 100644
--- a/doc/guides/nics/features/cpfl.ini
+++ b/doc/guides/nics/features/cpfl.ini
@@ -33,6 +33,7 @@  tcp                  = Y
 udp                  = Y
 vlan                 = Y
 vxlan                = Y
+flex                 = Y
 
 [rte_flow actions]
 count                = Y
diff --git a/drivers/net/cpfl/cpfl_ethdev.h b/drivers/net/cpfl/cpfl_ethdev.h
index e580f80f2f..7dfa4a0183 100644
--- a/drivers/net/cpfl/cpfl_ethdev.h
+++ b/drivers/net/cpfl/cpfl_ethdev.h
@@ -185,10 +185,18 @@  struct cpfl_repr {
 	bool func_up; /* If the represented function is up */
 };
 
+struct cpfl_tdi_table_node;
+TAILQ_HEAD(cpfl_tdi_table_list, cpfl_tdi_table_node);
+
+struct cpfl_tdi_action_node;
+TAILQ_HEAD(cpfl_tdi_action_list, cpfl_tdi_action_node);
+
 struct cpfl_flow_parser {
 	struct cpfl_flow_js_parser *fixed_parser;
 	struct cpfl_tdi_program *p4_parser;
 	bool is_p4_parser;
+	struct cpfl_tdi_table_list tdi_table_list;
+	struct cpfl_tdi_action_list tdi_action_list;
 };
 
 struct cpfl_metadata_chunk {
diff --git a/drivers/net/cpfl/cpfl_flow.c b/drivers/net/cpfl/cpfl_flow.c
index 1c4131da2c..15c7cc6d8b 100644
--- a/drivers/net/cpfl/cpfl_flow.c
+++ b/drivers/net/cpfl/cpfl_flow.c
@@ -6,6 +6,7 @@ 
 
 #include "cpfl_flow.h"
 #include "cpfl_flow_parser.h"
+#include "cpfl_tdi.h"
 #include "cpfl_tdi_parser.h"
 
 TAILQ_HEAD(cpfl_flow_engine_list, cpfl_flow_engine);
@@ -338,8 +339,10 @@  cpfl_flow_uninit(struct cpfl_adapter_ext *ad)
 	if (ad->flow_parser.fixed_parser)
 		cpfl_parser_destroy(ad->flow_parser.fixed_parser);
 
-	if (ad->flow_parser.p4_parser)
+	if (ad->flow_parser.p4_parser) {
+		cpfl_tdi_free_table_list(&ad->flow_parser);
 		cpfl_tdi_program_destroy(ad->flow_parser.p4_parser);
+	}
 
 	cpfl_flow_engine_uninit(ad);
 }
diff --git a/drivers/net/cpfl/cpfl_flow.h b/drivers/net/cpfl/cpfl_flow.h
index 1bde847763..1de9c25b17 100644
--- a/drivers/net/cpfl/cpfl_flow.h
+++ b/drivers/net/cpfl/cpfl_flow.h
@@ -15,6 +15,7 @@  extern const struct rte_flow_ops cpfl_flow_ops;
 enum cpfl_flow_engine_type {
 	CPFL_FLOW_ENGINE_NONE = 0,
 	CPFL_FLOW_ENGINE_FXP,
+	CPFL_FLOW_ENGINE_TDI,
 };
 
 typedef int (*engine_init_t)(struct cpfl_adapter_ext *ad);
diff --git a/drivers/net/cpfl/cpfl_flow_engine_fxp.c b/drivers/net/cpfl/cpfl_flow_engine_fxp.c
index f269ff97e1..6a5e7ed770 100644
--- a/drivers/net/cpfl/cpfl_flow_engine_fxp.c
+++ b/drivers/net/cpfl/cpfl_flow_engine_fxp.c
@@ -27,23 +27,11 @@ 
 #include "cpfl_fxp_rule.h"
 #include "cpfl_flow_parser.h"
 
-#define CPFL_COOKIE_DEF		0x1000
-#define CPFL_MOD_COOKIE_DEF	0x1237561
 #define CPFL_PREC_DEF		1
 #define CPFL_PREC_SET		5
 #define CPFL_TYPE_ID		3
 #define CPFL_OFFSET		0x0a
-#define CPFL_HOST_ID_DEF	0
 #define CPFL_PF_NUM_DEF		0
-#define CPFL_PORT_NUM_DEF	0
-#define CPFL_RESP_REQ_DEF	2
-#define CPFL_PIN_TO_CACHE_DEF	0
-#define CPFL_CLEAR_MIRROR_1ST_STATE_DEF	0
-#define CPFL_FIXED_FETCH_DEF	0
-#define CPFL_PTI_DEF		0
-#define CPFL_MOD_OBJ_SIZE_DEF	0
-#define CPFL_PIN_MOD_CONTENT_DEF	0
-
 #define CPFL_MAX_MOD_CONTENT_INDEX	256
 #define CPFL_MAX_MR_ACTION_NUM	8
 
diff --git a/drivers/net/cpfl/cpfl_flow_parser.c b/drivers/net/cpfl/cpfl_flow_parser.c
index e7f8a8a6cc..a3572e32d5 100644
--- a/drivers/net/cpfl/cpfl_flow_parser.c
+++ b/drivers/net/cpfl/cpfl_flow_parser.c
@@ -6,6 +6,7 @@ 
 
 #include "cpfl_flow_parser.h"
 #include "cpfl_tdi_parser.h"
+#include "cpfl_tdi.h"
 
 static enum rte_flow_item_type
 cpfl_get_item_type_by_str(const char *type)
@@ -976,6 +977,13 @@  cpfl_parser_create(struct cpfl_flow_parser *flow_parser, const char *filename)
 		flow_parser->p4_parser = prog;
 		flow_parser->fixed_parser = NULL;
 		flow_parser->is_p4_parser = true;
+
+		ret = cpfl_tdi_build(flow_parser);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR, "Failed to build tdi program %s", filename);
+			rte_free(prog);
+			return -EINVAL;
+		}
 	} else {
 		PMD_DRV_LOG(NOTICE, "flow parser mode is fixed function mode.");
 		parser = rte_zmalloc("flow_parser", sizeof(struct cpfl_flow_js_parser), 0);
diff --git a/drivers/net/cpfl/cpfl_fxp_rule.h b/drivers/net/cpfl/cpfl_fxp_rule.h
index ed757b80b1..f089155353 100644
--- a/drivers/net/cpfl/cpfl_fxp_rule.h
+++ b/drivers/net/cpfl/cpfl_fxp_rule.h
@@ -9,6 +9,18 @@ 
 
 #define CPFL_MAX_KEY_LEN 128
 #define CPFL_MAX_RULE_ACTIONS 32
+#define CPFL_RESP_REQ_DEF	2
+#define CPFL_PIN_TO_CACHE_DEF	0
+#define CPFL_CLEAR_MIRROR_1ST_STATE_DEF	0
+#define CPFL_FIXED_FETCH_DEF	0
+#define CPFL_PTI_DEF		0
+#define CPFL_MOD_OBJ_SIZE_DEF	0
+#define CPFL_PIN_MOD_CONTENT_DEF	0
+#define CPFL_HOST_ID_DEF	0
+#define CPFL_PORT_NUM_DEF	0
+#define CPFL_VSI_DEF	0
+#define CPFL_COOKIE_DEF		0x1000
+#define CPFL_MOD_COOKIE_DEF	0x1237561
 
 struct cpfl_sem_rule_info {
 	uint16_t prof_id;
diff --git a/drivers/net/cpfl/cpfl_tdi.c b/drivers/net/cpfl/cpfl_tdi.c
new file mode 100644
index 0000000000..ac681ecc01
--- /dev/null
+++ b/drivers/net/cpfl/cpfl_tdi.c
@@ -0,0 +1,1282 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+#include <rte_hash_crc.h>
+#include <rte_tailq.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "cpfl_actions.h"
+#include "cpfl_flow.h"
+#include "cpfl_fxp_rule.h"
+#include "cpfl_tdi.h"
+#include "cpfl_tdi_parser.h"
+#include "rte_branch_prediction.h"
+#include "rte_common.h"
+#include "rte_flow.h"
+
+#define CPFL_NO_FWD 0
+uint64_t cpfl_tdi_rule_cookie = CPFL_COOKIE_DEF;
+
+/*help function to do left shift on a byte array */
+static void
+cpfl_tdi_shift_left(uint8_t *buf, int length, uint32_t shift_amount)
+{
+	uint32_t i;
+	int j;
+	uint32_t carry = 0;
+
+	if (shift_amount == 0)
+		return;
+
+	for (i = 0; i < shift_amount; i += 8) {
+		for (j = 0; j < length; j++) {
+			uint32_t temp = (buf[j] << (shift_amount - i)) | carry;
+
+			carry = (temp >> 8) & 0xff;
+			buf[j] = temp & 0xff;
+		}
+	}
+}
+
+/* help function to init a mask array with bit_width */
+static void
+cpfl_tdi_init_msk_buf(uint8_t *buf, int length, uint32_t bit_width)
+{
+	uint32_t i;
+
+	memset(buf, 0, length);
+	for (i = 0; i < bit_width; i++) {
+		cpfl_tdi_shift_left(buf, length, 1);
+		buf[0] += 1;
+	}
+}
+
+/* help function to OR a byte array with value and mask */
+static void
+cpfl_tdi_or_buf(uint8_t *buf, int length, uint8_t *values, uint8_t *mask)
+{
+	int i;
+
+	for (i = 0; i < length; i++)
+		buf[i] = (values[i] & mask[i]) | (buf[i] & ~mask[i]);
+}
+
+static uint32_t
+cpfl_tdi_to_action_code(struct cpfl_tdi_hw_action *ha,
+			uint8_t *val,
+			enum cpfl_act_fwd_type fwd_type)
+{
+	switch (ha->action_code) {
+	case CPFL_TDI_ACTION_CODE_SET1A_24b:
+		switch (ha->index) {
+		case 0: /* mod addr */
+			return cpfl_act_mod_addr(ha->prec, (uint32_t)*val).data;
+		case 8: /* todo */
+			break;
+		default:
+			PMD_DRV_LOG(WARNING, "Unsupported SET1A_24b index %d", ha->index);
+			break;
+		}
+		break;
+	case CPFL_TDI_ACTION_CODE_SET1_16b:
+		switch (ha->index) {
+		case 2: /* set vsi */
+			if (fwd_type == CPFL_ACT_FWD_VSI)
+				return cpfl_act_fwd_vsi(0, ha->prec, CPFL_PE_LAN, (uint16_t)*val)
+				    .data;
+			else if (fwd_type == CPFL_ACT_FWD_PORT)
+				return cpfl_act_fwd_port(0, ha->prec, CPFL_PE_LAN, (uint16_t)*val)
+				    .data;
+			else
+				PMD_DRV_LOG(WARNING, "Unsupported fwd type %d", fwd_type);
+			break;
+		default:
+			PMD_DRV_LOG(WARNING, "Unsupported SET1_16b index %d", ha->index);
+			break;
+		}
+		break;
+	case CPFL_TDI_ACTION_CODE_SET1B_24b: /* set metadata */
+		switch (ha->setmd_action_code) {
+		case CPFL_TDI_SETMD_ACTION_CODE_SET_16b:
+			return cpfl_act_set_md16(ha->index, ha->prec, ha->type_id, ha->offset,
+						 (uint16_t)*val)
+			    .data;
+		case CPFL_TDI_SETMD_ACTION_CODE_SET_32b_AUX: /* todo */
+			break;
+		default:
+			PMD_DRV_LOG(WARNING, "Unsupported SET1b_24b setmd code %d",
+				    ha->setmd_action_code);
+			break;
+		}
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Unsupported action code %d", ha->action_code);
+		break;
+	}
+
+	return 0;
+}
+
+static void
+cpfl_tdi_pack_sem_entry(struct cpfl_tdi_rule_info *rinfo,
+			struct cpfl_tdi_ma_hardware_block *hb,
+			enum cpfl_tdi_table_entry_op op,
+			struct idpf_dma_mem *dma,
+			struct idpf_ctlq_msg *msg)
+{
+	union cpfl_rule_cfg_pkt_record *blob;
+	struct cpfl_rule_cfg_data cfg = {0};
+	uint16_t cfg_ctrl;
+	enum cpfl_ctlq_rule_cfg_opc opc = 0;
+	const struct cpfl_tdi_table_key_obj *key = &rinfo->kobj;
+	const struct cpfl_tdi_action_obj *action = &rinfo->aobj;
+
+	blob = (void *)dma->va;
+	memset(blob, 0, sizeof(*blob));
+
+	cfg_ctrl = CPFL_GET_MEV_SEM_RULE_CFG_CTRL(hb->profile[0], hb->sem.sub_profile, 0, 0);
+
+	switch (op) {
+	case CPFL_TDI_TABLE_ENTRY_OP_ADD:
+		cpfl_prep_sem_rule_blob(key->buf, key->buf_len, action->buf, action->buf_len,
+					cfg_ctrl, blob);
+		opc = cpfl_ctlq_sem_add_rule;
+		break;
+	case CPFL_TDI_TABLE_ENTRY_OP_DEL:
+		cpfl_prep_sem_rule_blob(key->buf, key->buf_len, NULL, 0, cfg_ctrl, blob);
+		opc = cpfl_ctlq_sem_del_rule;
+		break;
+	case CPFL_TDI_TABLE_ENTRY_OP_QRY:
+		cpfl_prep_sem_rule_blob(key->buf, key->buf_len, NULL, 0, cfg_ctrl, blob);
+		opc = cpfl_ctlq_sem_query_rule;
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "Unknown ops, this is a bug.");
+		break;
+	}
+
+	cpfl_fill_rule_cfg_data_common(opc,
+				       rinfo->cookie,
+				       rinfo->vsi,
+				       rinfo->port_num,
+				       rinfo->host_id,
+				       0, /* time_sel */
+				       0, /* time_sel_val */
+				       0, /* cache_wr_thru */
+				       rinfo->resp_req,
+				       sizeof(union cpfl_rule_cfg_pkt_record),
+				       dma,
+				       &cfg.common);
+
+	cpfl_prep_rule_desc(&cfg, msg);
+}
+
+static void
+cpfl_tdi_pack_mod_entry(struct cpfl_tdi_rule_info *rinfo,
+			enum cpfl_tdi_table_entry_op op,
+			struct idpf_dma_mem *dma,
+			struct idpf_ctlq_msg *msg)
+{
+	union cpfl_rule_cfg_pkt_record *blob;
+	struct cpfl_rule_cfg_data cfg = {0};
+	uint32_t mod_index;
+	enum cpfl_ctlq_rule_cfg_opc opc = 0;
+	const struct cpfl_tdi_table_key_obj *key = &rinfo->kobj;
+	const struct cpfl_tdi_action_obj *action = &rinfo->aobj;
+
+	blob = (void *)dma->va;
+	memset(blob, 0, sizeof(*blob));
+
+	mod_index = *(const uint32_t *)&key->buf[0];
+
+	switch (op) {
+	case CPFL_TDI_TABLE_ENTRY_OP_ADD:
+		cpfl_fill_rule_mod_content(CPFL_MOD_OBJ_SIZE_DEF, CPFL_PIN_MOD_CONTENT_DEF,
+					   mod_index, &cfg.ext.mod_content);
+
+		memcpy(blob->mod_blob, action->buf, action->buf_len);
+		opc = cpfl_ctlq_mod_add_update_rule;
+		break;
+	case CPFL_TDI_TABLE_ENTRY_OP_QRY:
+		opc = cpfl_ctlq_mod_query_rule;
+		break;
+	default:
+		break;
+	}
+
+	cpfl_fill_rule_cfg_data_common(opc,
+				       CPFL_MOD_COOKIE_DEF,
+				       0, /* vsi_id not used for mod */
+				       CPFL_PORT_NUM_DEF,
+				       0,
+				       0,	    /* time_sel */
+				       0,			    /* time_sel_val */
+				       0,			    /* cache_wr_thru */
+				       CPFL_RESP_REQ_DEF,
+				       sizeof(union cpfl_rule_cfg_pkt_record),
+				       dma,
+				       &cfg.common);
+
+	cpfl_prep_rule_desc(&cfg, msg);
+}
+
+static int
+cpfl_tdi_rule_process(struct cpfl_itf *itf,
+		      struct idpf_ctlq_info *tx_cq,
+		      struct idpf_ctlq_info *rx_cq,
+		      struct cpfl_tdi_rule_info *rinfo,
+		      int rule_num,
+		      enum cpfl_tdi_table_entry_op op)
+{
+	const struct cpfl_tdi_table_key_obj *kobj;
+	struct idpf_hw *hw = &itf->adapter->base.hw;
+	struct cpfl_tdi_ma_hardware_block *hb;
+	const struct cpfl_tdi_table *table;
+	int ret = 0;
+
+	if (rule_num == 0)
+		return 0;
+
+	kobj = &rinfo->kobj;
+
+	table = kobj->tnode->table;
+	if (table->match_attributes.hardware_block_num == 0) {
+		PMD_DRV_LOG(ERR, "No valid hardware block be specified");
+		return -EINVAL;
+	}
+	hb = &table->match_attributes.hardware_blocks[0];
+	switch (hb->hw_block) {
+	case CPFL_TDI_HW_BLOCK_SEM:
+		cpfl_tdi_pack_sem_entry(rinfo, hb, op, &itf->dma[0], &itf->msg[0]);
+		break;
+	case CPFL_TDI_HW_BLOCK_MOD:
+		if (op == CPFL_TDI_TABLE_ENTRY_OP_DEL)
+			/* do nothing */
+			return 0;
+		cpfl_tdi_pack_mod_entry(rinfo, op, &itf->dma[0], &itf->msg[0]);
+		break;
+	default:
+		PMD_DRV_LOG(ERR, "Unsupported hardware block %d", hb->hw_block);
+		return -EINVAL;
+	}
+
+	ret = cpfl_send_ctlq_msg(hw, tx_cq, 1, itf->msg);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Failed to send control message");
+		return -EINVAL;
+	}
+
+	ret = cpfl_receive_ctlq_msg(hw, rx_cq, 1, itf->msg);
+	if (ret) {
+		PMD_INIT_LOG(ERR, "Failed to update rule");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+cpfl_tdi_fxp_rule_create(struct rte_eth_dev *dev,
+			 struct rte_flow *flow,
+			 void *meta,
+			 struct rte_flow_error *error)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *ad = itf->adapter;
+	int ret;
+	uint32_t cpq_id = 0;
+	struct cpfl_vport *vport;
+	struct cpfl_repr *repr;
+	struct cpfl_tdi_rule_info *rinfo = meta;
+
+	if (!rinfo)
+		goto err;
+
+	if (itf->type == CPFL_ITF_TYPE_VPORT) {
+		vport = (struct cpfl_vport *)itf;
+		/* Every vport has one pair control queues configured to handle message.
+		 * Even index is tx queue and odd index is rx queue.
+		 */
+		cpq_id = vport->base.devarg_id * 2;
+	} else if (itf->type == CPFL_ITF_TYPE_REPRESENTOR) {
+		repr = (struct cpfl_repr *)itf;
+		cpq_id = ((repr->repr_id.pf_id + repr->repr_id.vf_id) & (CPFL_TX_CFGQ_NUM - 1)) * 2;
+	} else {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				   "fail to find correct control queue");
+		return -rte_errno;
+	}
+
+	ret = cpfl_tdi_rule_process(itf, ad->ctlqp[cpq_id], ad->ctlqp[cpq_id + 1], rinfo, 1,
+				    CPFL_TDI_TABLE_ENTRY_OP_ADD);
+	if (ret)
+		goto err;
+
+	flow->rule = rinfo;
+
+	return 0;
+
+err:
+	return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				  "cpfl filter create flow fail");
+}
+
+static int
+cpfl_tdi_fxp_rule_destroy(struct rte_eth_dev *dev,
+			  struct rte_flow *flow,
+			  struct rte_flow_error *error)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *ad = itf->adapter;
+	struct cpfl_vport *vport;
+	struct cpfl_repr *repr;
+	struct cpfl_tdi_rule_info *rinfo = (struct cpfl_tdi_rule_info *)flow->rule;
+	int ret = 0;
+	uint32_t cpq_id = 0;
+
+	if (itf->type == CPFL_ITF_TYPE_VPORT) {
+		vport = (struct cpfl_vport *)itf;
+		cpq_id = vport->base.devarg_id * 2;
+	} else if (itf->type == CPFL_ITF_TYPE_REPRESENTOR) {
+		repr = (struct cpfl_repr *)itf;
+		cpq_id = ((repr->repr_id.pf_id + repr->repr_id.vf_id) & (CPFL_TX_CFGQ_NUM - 1)) * 2;
+	} else {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				   "fail to find correct control queue");
+		ret = -rte_errno;
+		goto err;
+	}
+
+	ret = cpfl_tdi_rule_process(itf, ad->ctlqp[cpq_id], ad->ctlqp[cpq_id + 1], rinfo, 1,
+				    CPFL_TDI_TABLE_ENTRY_OP_DEL);
+	if (ret)
+		goto err;
+
+err:
+	rte_free(rinfo);
+	flow->rule = NULL;
+	return ret;
+}
+
+void
+cpfl_tdi_free_table_list(struct cpfl_flow_parser *flow_parser)
+{
+	struct cpfl_tdi_table_node *node;
+
+	while ((node = TAILQ_FIRST(&flow_parser->tdi_table_list))) {
+		TAILQ_REMOVE(&flow_parser->tdi_table_list, node, next);
+		rte_free(node);
+	}
+}
+
+static int
+cpfl_tdi_build_table_list(struct cpfl_flow_parser *flow_parser)
+{
+	struct cpfl_tdi_program *prog = flow_parser->p4_parser;
+	int i;
+
+	TAILQ_INIT(&flow_parser->tdi_table_list);
+
+	for (i = 0; i < prog->table_num; i++) {
+		struct cpfl_tdi_table *table = &prog->tables[i];
+		struct cpfl_tdi_table_node *node;
+
+		node = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_table_node), 0);
+		if (node == NULL)
+			return -ENOMEM;
+
+		node->table = table;
+		TAILQ_INSERT_TAIL(&flow_parser->tdi_table_list, node, next);
+	}
+
+	return 0;
+}
+
+static void
+cpfl_tdi_build_hw_action_buf(struct cpfl_tdi_action_node *node)
+{
+	int i, j;
+
+	for (i = 0; i < node->format->hw_action_num; i++) {
+		struct cpfl_tdi_hw_action *ha = &node->format->hw_actions_list[i];
+		struct cpfl_tdi_hw_action_parameter *hap = &ha->parameters[0];
+		uint32_t msk = UINT32_MAX;
+		uint32_t action_code = 0;
+		uint16_t offset;
+		uint16_t size;
+
+		switch (ha->action_code) {
+		case CPFL_TDI_ACTION_CODE_SET10_1b:
+		case CPFL_TDI_ACTION_CODE_SET1_16b:
+		case CPFL_TDI_ACTION_CODE_SET1A_24b:
+		case CPFL_TDI_ACTION_CODE_SET1B_24b:
+			offset = node->buf_len;
+			size = 4;
+			node->buf_len += 4; /* 32 bit action encode */
+			break;
+		default:
+			continue;
+		}
+
+		if (ha->parameter_num == 0) {
+			switch (ha->action_code) {
+			case CPFL_TDI_ACTION_CODE_SET10_1b:
+				switch (ha->index) {
+				case 0: /* drop */
+					action_code = CPFL_ACT_MAKE_1B(ha->prec,
+								       CPFL_ACT_1B_OP_DROP,
+								       ha->value & ha->mask);
+					break;
+				default:
+					continue;
+				}
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1A_24b:
+				switch (ha->index) {
+				case 9: /* mod profile */
+					action_code =
+					    cpfl_act_mod_profile(ha->prec, ha->mod_profile, 0, 0, 0,
+								 CPFL_ACT_MOD_PROFILE_PREFETCH_256B)
+						.data;
+					break;
+				case 8: /* queue */
+					/* todo */
+					break;
+				default:
+					break;
+				}
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1B_24b: /* set metadata */
+				switch (ha->setmd_action_code) {
+				case CPFL_TDI_SETMD_ACTION_CODE_SET_8b:
+					action_code =
+					    cpfl_act_set_md8(ha->index, ha->prec, ha->type_id,
+							     ha->offset, ha->value, ha->mask)
+						.data;
+					break;
+				default:
+					break;
+				}
+				break;
+			default:
+				continue;
+			}
+
+			memcpy(&node->init_buf[offset], &action_code, 4);
+			memcpy(&node->query_msk[offset], &msk, 4);
+			continue;
+		} else {
+			uint32_t code_msk = 0;
+			uint32_t dummy = 0;
+			uint32_t action_code =
+			    cpfl_tdi_to_action_code(ha, (void *)&dummy, CPFL_NO_FWD);
+
+			switch (ha->action_code) {
+			case CPFL_TDI_ACTION_CODE_SET10_1b:
+				code_msk = ~CPFL_ACT_1B_VAL_M;
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1_16b:
+				code_msk = ~CPFL_ACT_16B_VAL_M;
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1A_24b:
+				code_msk = ~CPFL_ACT_24B_A_VAL_M;
+				break;
+			case CPFL_TDI_ACTION_CODE_SET1B_24b: /* set metadata */
+				code_msk = ~CPFL_ACT_24B_B_VAL_M;
+				break;
+			default:
+				continue;
+			}
+
+			memcpy(&node->init_buf[offset], &action_code, 4);
+			memcpy(&node->query_msk[offset], &code_msk, 4);
+		}
+
+		/* only check the first parameter */
+		for (j = 0; j < node->format->immediate_field_num; j++) {
+			struct cpfl_tdi_immediate_field *imf = &node->format->immediate_fields[j];
+
+			if (imf->param_handle == hap->param_handle) {
+				node->params[j].id = imf->param_handle;
+				node->params[j].offset = offset;
+				node->params[j].size = size;
+			}
+		}
+	}
+}
+
+static void
+cpfl_tdi_build_mod_content_format_buf(struct cpfl_tdi_action_node *node)
+{
+	int i, j;
+	uint8_t val_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
+	uint8_t msk_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
+
+	for (i = 0; i < node->format->mod_content_format.mod_field_num; i++) {
+		struct cpfl_tdi_mod_field *mf = &node->format->mod_content_format.mod_fields[i];
+		uint16_t size = (uint16_t)((mf->start_bit_offset + mf->bit_width) >> 3);
+
+		node->buf_len += size;
+
+		if (mf->type == CPFL_TDI_MOD_FIELD_TYPE_CONSTANT) {
+			memcpy(val_buf, mf->value, size);
+			cpfl_tdi_shift_left(val_buf, size, mf->start_bit_offset);
+			cpfl_tdi_init_msk_buf(msk_buf, size, mf->bit_width);
+			cpfl_tdi_shift_left(msk_buf, size, mf->start_bit_offset);
+			cpfl_tdi_or_buf(&node->init_buf[mf->byte_array_index], size, val_buf,
+					msk_buf);
+			continue;
+		}
+
+		for (j = 0; j < node->format->immediate_field_num; j++) {
+			struct cpfl_tdi_immediate_field *imf = &node->format->immediate_fields[j];
+
+			if (imf->param_handle == mf->param_handle) {
+				node->params[j].id = imf->param_handle;
+				node->params[j].offset = mf->byte_array_index;
+				node->params[j].size = size;
+				break;
+			}
+		}
+	}
+}
+
+static void
+cpfl_tdi_build_action_params(struct cpfl_tdi_action_node *node)
+{
+	node->buf_len = 0;
+	/* build mod content layout */
+	if (node->format->mod_content_format.mod_field_num > 0) {
+		cpfl_tdi_build_mod_content_format_buf(node);
+		/* build action buffer layout */
+	} else if (node->format->hw_action_num > 0) {
+		cpfl_tdi_build_hw_action_buf(node);
+	}
+}
+
+static void
+cpfl_tdi_free_action_list(struct cpfl_flow_parser *flow_parser)
+{
+	struct cpfl_tdi_action_node *action;
+
+	while ((action = TAILQ_FIRST(&flow_parser->tdi_action_list))) {
+		TAILQ_REMOVE(&flow_parser->tdi_action_list, action, next);
+		rte_free(action);
+	}
+}
+
+static int
+cpfl_tdi_build_action_list(struct cpfl_flow_parser *flow_parser)
+{
+#define _HASH_TABLE_NAME_SIZE 32
+#define _HASH_TABLE_ENTRY_SIZE 1024
+	struct cpfl_tdi_program *prog = flow_parser->p4_parser;
+	char hname[_HASH_TABLE_NAME_SIZE];
+	struct rte_hash *ht;
+	int ret = 0;
+	int i, j, k;
+
+	snprintf(hname, _HASH_TABLE_NAME_SIZE, "cpfl_tdi_action_hash");
+
+	struct rte_hash_parameters params = {
+	    .name = hname,
+	    .entries = _HASH_TABLE_ENTRY_SIZE,
+	    .key_len = sizeof(uint32_t),
+	    .hash_func = rte_hash_crc,
+	    .hash_func_init_val = 0,
+	    .socket_id = SOCKET_ID_ANY,
+	    .extra_flag = 0,
+	};
+
+	ht = rte_hash_create(&params);
+
+	if (ht == NULL) {
+		PMD_INIT_LOG(ERR, "Failed to create hash table %s", hname);
+		return -EINVAL;
+	}
+
+	TAILQ_INIT(&flow_parser->tdi_action_list);
+
+	for (i = 0; i < prog->table_num; i++) {
+		struct cpfl_tdi_table *table = &prog->tables[i];
+
+		for (j = 0; j < table->action_num; j++) {
+			struct cpfl_tdi_action *action = &table->actions[j];
+			uint32_t handle = action->handle;
+			struct cpfl_tdi_action_node *node;
+
+			/* skip if already exist */
+			if (rte_hash_lookup(ht, &handle) >= 0)
+				continue;
+
+			node = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_action_node), 0);
+			if (node == NULL) {
+				ret = -ENOMEM;
+				goto err;
+			}
+
+			node->action = action;
+			ret = rte_hash_add_key_data(ht, &handle, node);
+			if (ret != 0)
+				goto err;
+
+			TAILQ_INSERT_TAIL(&flow_parser->tdi_action_list, node, next);
+		}
+
+		for (j = 0; j < table->match_attributes.hardware_block_num; j++) {
+			struct cpfl_tdi_ma_hardware_block *hb =
+			    &table->match_attributes.hardware_blocks[j];
+
+			for (k = 0; k < hb->action_format_num; k++) {
+				struct cpfl_tdi_action_format *format = &hb->action_format[k];
+				uint32_t handle = format->action_handle;
+				struct cpfl_tdi_action_node *node = NULL;
+
+				if (rte_hash_lookup_data(ht, &handle, (void **)&node) >= 0) {
+					node->hw_block_type = hb->hw_block;
+					if (node->format == NULL) {
+						node->format = format;
+						cpfl_tdi_build_action_params(node);
+					}
+				}
+			}
+		}
+	}
+
+	rte_hash_free(ht);
+
+	return 0;
+
+err:
+
+	rte_hash_free(ht);
+	cpfl_tdi_free_action_list(flow_parser);
+	return ret;
+}
+
+static int
+cpfl_tdi_table_info_get(struct rte_eth_dev *dev,
+			uint32_t table_id,
+			struct cpfl_tdi_table_node **table_node)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *adapter = itf->adapter;
+	struct cpfl_tdi_table_node *node;
+	void *temp;
+	int i;
+
+	RTE_TAILQ_FOREACH_SAFE(node, &adapter->flow_parser.tdi_table_list, next, temp)
+	{
+		const struct cpfl_tdi_table *table = node->table;
+
+		if (table->handle != table_id)
+			continue;
+
+		if (table->match_key_field_num > CPFL_TDI_KEY_FIELD_NUM_MAX) {
+			PMD_DRV_LOG(ERR, "Too many fields (%d) in tdi table %s",
+				    table->match_key_field_num, table->name);
+			goto err;
+		}
+
+		if (table->action_num > CPFL_TDI_ACTION_SPEC_NUM_MAX) {
+			PMD_DRV_LOG(ERR, "Too many action types (%d) in tdi table %s",
+				    table->action_num, table->name);
+			goto err;
+		}
+
+		/* match_key_field first */
+		if (table->match_key_format_num == 0) {
+			node->buf_len = 0;
+			for (i = 0; i < table->match_key_field_num; i++) {
+				struct cpfl_tdi_match_key_field *field =
+				    &table->match_key_fields[i];
+				uint32_t size = (uint16_t)(field->bit_width >> 3);
+
+				node->params[i].id = field->index;
+				node->params[i].offset =
+				    node->buf_len; /* equal with field->position */
+				node->params[i].size = size;
+				node->buf_len += size;
+			}
+		} else {
+			for (i = 0; i < table->match_key_format_num; i++) {
+				struct cpfl_tdi_match_key_format *format =
+				    &table->match_key_format[i];
+
+				node->buf_len =
+				    format->byte_array_index + (uint16_t)(format->bit_width >> 3);
+				node->params[i].id = format->match_key_handle;
+				node->params[i].offset = format->byte_array_index;
+				node->params[i].size = (uint16_t)(format->bit_width >> 3);
+			}
+		}
+		*table_node = node;
+
+		return 0;
+	}
+
+err:
+	return -EINVAL;
+}
+
+static int
+cpfl_tdi_table_key_node_init(struct rte_eth_dev *dev __rte_unused,
+			     struct cpfl_tdi_table_node *node,
+			     struct cpfl_tdi_table_key_obj *kobj)
+{
+	kobj->tnode = node;
+	kobj->buf_len = node->buf_len;
+	kobj->sem.pin_to_cache = CPFL_PIN_TO_CACHE_DEF;
+	kobj->sem.fixed_fetch = CPFL_FIXED_FETCH_DEF;
+	return 0;
+}
+
+static int
+cpfl_tdi_table_key_create(struct rte_eth_dev *dev,
+			  uint32_t table_id,
+			  struct cpfl_tdi_table_node **table_node,
+			  struct cpfl_tdi_table_key_obj *kobj)
+{
+	int ret;
+
+	if (!kobj)
+		return -EINVAL;
+
+	ret = cpfl_tdi_table_info_get(dev, table_id, table_node);
+	if (ret != 0)
+		return -EINVAL;
+
+	ret = cpfl_tdi_table_key_node_init(dev, *table_node, kobj);
+	if (ret != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_table_key_field_info_get(__rte_unused struct rte_eth_dev *dev,
+				  struct cpfl_tdi_table_node *node,
+				  uint32_t field_id,
+				  struct cpfl_tdi_table_key_field_info **key_field_info)
+{
+	const struct cpfl_tdi_table *table = node->table;
+	int i, j;
+
+	for (i = 0; i < table->match_key_field_num; i++) {
+		struct cpfl_tdi_match_key_field *field = &table->match_key_fields[i];
+		struct cpfl_tdi_table_key_field_info *tkfinfo;
+
+		if (field->index != field_id)
+			continue;
+
+		tkfinfo = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_table_key_field_info), 0);
+		if (tkfinfo == NULL)
+			return -ENOMEM;
+
+		tkfinfo->field = field;
+		tkfinfo->param = node->params[i];
+		*key_field_info = tkfinfo;
+
+		/* adjust byte width */
+		for (j = 0; j < table->match_key_format_num; j++) {
+			struct cpfl_tdi_match_key_format *format = &table->match_key_format[j];
+
+			if (format->match_key_handle != field_id)
+				continue;
+			tkfinfo->format = format;
+			return 0;
+		}
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+_cpfl_tdi_table_key_field_set(struct rte_eth_dev *dev __rte_unused,
+			      struct cpfl_tdi_table_key_obj *kobj,
+			      struct cpfl_tdi_table_key_field_info *tkfinfo,
+			      const uint8_t *value,
+			      uint16_t size)
+{
+	struct cpfl_tdi_param_info *pi = &tkfinfo->param;
+	uint8_t *target = &kobj->buf[pi->offset];
+
+	memcpy(target, value, size);
+
+	/* Need to fix as this will overwrite. */
+	if (tkfinfo->format != NULL)
+		cpfl_tdi_shift_left(target, pi->size, tkfinfo->format->start_bit_offset);
+
+	return 0;
+}
+
+static int
+cpfl_tdi_table_key_field_set(struct rte_eth_dev *dev,
+			     struct cpfl_tdi_table_node *table_node,
+			     struct cpfl_tdi_table_key_obj *kobj,
+			     uint32_t field_id,
+			     const uint8_t *value,
+			     uint16_t size)
+{
+	struct cpfl_tdi_table_key_field_info *key_field_info;
+	int ret;
+
+	if (!kobj || !value)
+		return -EINVAL;
+
+	ret = cpfl_tdi_table_key_field_info_get(dev, table_node, field_id, &key_field_info);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (key_field_info->field->match_type != CPFL_TDI_MATCH_TYPE_EXACT)
+		return -EINVAL;
+
+	if (unlikely(key_field_info->field->is_vsi)) {
+		struct cpfl_itf *src_itf;
+		uint16_t vsi_id, port_id;
+		uint8_t ids[2];
+
+		if (size != 1 && size != 2) /* input port id should be 8/16 bits */
+			return -EINVAL;
+		port_id = size == 1 ? value[0] : value[0] << 8 | value[1];
+		src_itf = cpfl_get_itf_by_port_id(port_id);
+		if (!src_itf)
+			return -EINVAL;
+		vsi_id = cpfl_get_vsi_id(src_itf);
+		if (vsi_id == CPFL_INVALID_HW_ID)
+			return -EINVAL;
+		ids[0] = (uint8_t)vsi_id;
+		ids[1] = (uint8_t)vsi_id >> 8;
+
+		ret = _cpfl_tdi_table_key_field_set(dev, kobj, key_field_info, ids, size);
+		if (ret != 0)
+			return -EINVAL;
+	} else {
+		ret = _cpfl_tdi_table_key_field_set(dev, kobj, key_field_info, value, size);
+		if (ret != 0)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_action_spec_info_get(struct rte_eth_dev *dev,
+			      uint32_t spec_id,
+			      struct cpfl_tdi_action_node **action_node)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *adapter = itf->adapter;
+	struct cpfl_tdi_action_node *node;
+	void *temp;
+
+	RTE_TAILQ_FOREACH_SAFE(node, &adapter->flow_parser.tdi_action_list, next, temp)
+	{
+		if (node->action->handle != spec_id)
+			continue;
+		*action_node = node;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+cpfl_tdi_action_obj_init(struct rte_eth_dev *dev __rte_unused,
+			 struct cpfl_tdi_table_node *tnode,
+			 struct cpfl_tdi_action_node *anode,
+			 struct cpfl_tdi_action_obj *aobj)
+{
+	aobj->table = tnode->table;
+	aobj->node = anode;
+	aobj->buf_len = anode->buf_len;
+
+	memcpy(aobj->buf, anode->init_buf, anode->buf_len);
+
+	return 0;
+}
+
+static inline bool __rte_unused
+verify_action(struct cpfl_tdi_table_node *table_node, uint32_t spec_id)
+{
+	int i;
+	const struct cpfl_tdi_table *table = table_node->table;
+
+	for (i = 0; i < table->action_num; i++)
+		if (spec_id == table->actions[i].handle)
+			return true;
+
+	return false;
+}
+
+static int
+cpfl_tdi_action_node_get_by_spec_id(struct rte_eth_dev *dev,
+				    uint32_t table_id __rte_unused,
+				    uint32_t spec_id,
+				    struct cpfl_tdi_action_node **action_node)
+{
+	return cpfl_tdi_action_spec_info_get(dev, spec_id, action_node);
+}
+
+static int
+cpfl_tdi_action_spec_field_info_get(struct rte_eth_dev *dev __rte_unused,
+				    struct cpfl_tdi_action_node *anode,
+				    uint32_t field_id,
+				    struct cpfl_tdi_action_spec_field_info **info)
+{
+	int ret = -EINVAL;
+	int i, j;
+
+	if (anode->format == NULL)
+		goto err;
+
+	for (i = 0; i < anode->format->immediate_field_num; i++) {
+		struct cpfl_tdi_immediate_field *field = &anode->format->immediate_fields[i];
+		struct cpfl_tdi_action_spec_field_info *asfinfo;
+
+		if (field->param_handle != field_id)
+			continue;
+
+		asfinfo = rte_malloc(NULL, sizeof(struct cpfl_tdi_action_spec_field_info), 0);
+		if (asfinfo == NULL) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		asfinfo->field = field;
+		asfinfo->param = anode->params[i];
+
+		for (j = 0; j < anode->format->mod_content_format.mod_field_num; j++) {
+			struct cpfl_tdi_mod_field *mod_field =
+			    &anode->format->mod_content_format.mod_fields[j];
+
+			if (mod_field->type != CPFL_TDI_MOD_FIELD_TYPE_PARAMETER)
+				continue;
+
+			if (mod_field->param_handle != field_id)
+				continue;
+
+			asfinfo->mod_field = mod_field;
+		}
+
+		for (i = 0; i < anode->format->hw_action_num; i++) {
+			struct cpfl_tdi_hw_action *hw_action = &anode->format->hw_actions_list[i];
+
+			if (hw_action->parameter_num == 0)
+				continue;
+
+			if (hw_action->parameters[0].param_handle != field_id)
+				continue;
+
+			asfinfo->hw_action = hw_action;
+		}
+
+		*info = asfinfo;
+
+		return 0;
+	}
+	PMD_DRV_LOG(WARNING, "No immediate_field_num!!!");
+err:
+	return ret;
+}
+
+static int
+_cpfl_tdi_action_field_set(struct rte_eth_dev *dev __rte_unused,
+			   struct cpfl_tdi_action_obj *aobj,
+			   struct cpfl_tdi_action_spec_field_info *asfinfo,
+			   const uint8_t *value,
+			   uint16_t size,
+			   enum cpfl_act_fwd_type fwd_type)
+{
+	struct cpfl_tdi_action_node *node = aobj->node;
+	struct cpfl_tdi_param_info *pi = &asfinfo->param;
+	uint8_t val_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
+	uint8_t msk_buf[CPFL_TDI_VALUE_SIZE_MAX] = {0};
+
+	memcpy(val_buf, value, size);
+
+	if (node->format->mod_content_format.mod_field_num > 0) {
+		struct cpfl_tdi_mod_field *mf = asfinfo->mod_field;
+
+		cpfl_tdi_shift_left(val_buf, pi->size, mf->start_bit_offset);
+		cpfl_tdi_init_msk_buf(msk_buf, pi->size, mf->bit_width);
+		cpfl_tdi_shift_left(msk_buf, pi->size, mf->start_bit_offset);
+		cpfl_tdi_or_buf(&aobj->buf[pi->offset], pi->size, val_buf, msk_buf);
+	} else {
+		struct cpfl_tdi_hw_action *ha = asfinfo->hw_action;
+		uint32_t action_code = cpfl_tdi_to_action_code(ha, val_buf, fwd_type);
+
+		memcpy(&aobj->buf[pi->offset], &action_code, 4);
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_action_field_set(struct rte_eth_dev *dev,
+			  struct cpfl_tdi_action_obj *aobj,
+			  struct cpfl_tdi_action_node *action_node,
+			  uint32_t field_id,
+			  const struct rte_flow_action_prog_argument *arg)
+{
+	struct cpfl_tdi_action_spec_field_info *asfinfo;
+	enum rte_flow_action_type action_type;
+	/* used when action is PORT_REPRESENTOR type */
+	struct cpfl_itf *dst_itf;
+	uint16_t dev_id; /* vsi id */
+	uint16_t port_id;
+	uint8_t value;
+	bool is_vsi;
+	int ret;
+	enum cpfl_act_fwd_type fwd_type;
+
+	if (!aobj || !arg)
+		return -EINVAL;
+
+	ret = cpfl_tdi_action_spec_field_info_get(dev, action_node, field_id, &asfinfo);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(arg->name, "port_representor"))
+		action_type = RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR;
+	else if (!strcmp(arg->name, "represented_port"))
+		action_type = RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT;
+	else
+		return _cpfl_tdi_action_field_set(dev, aobj, asfinfo, arg->value, arg->size,
+						  CPFL_NO_FWD);
+
+	if (arg->size != 1 && arg->size != 2) /* input port_id should be 8/16 bits */
+		return -EINVAL;
+
+	port_id = arg->size == 1 ? arg->value[0] : arg->value[0] << 8 | arg->value[1];
+	dst_itf = cpfl_get_itf_by_port_id(port_id);
+	if (!dst_itf)
+		goto err;
+
+	is_vsi = (action_type == RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR ||
+		  dst_itf->type == CPFL_ITF_TYPE_REPRESENTOR);
+	if (is_vsi)
+		dev_id = cpfl_get_vsi_id(dst_itf);
+	else
+		dev_id = cpfl_get_port_id(dst_itf);
+
+	if (dev_id == CPFL_INVALID_HW_ID)
+		goto err;
+
+	value = (uint8_t)dev_id;
+	fwd_type = is_vsi ? CPFL_ACT_FWD_VSI : CPFL_ACT_FWD_PORT;
+
+	return _cpfl_tdi_action_field_set(dev, aobj, asfinfo, &value, arg->size, fwd_type);
+err:
+	PMD_DRV_LOG(ERR, "Can not get dev id.");
+	return -EINVAL;
+}
+
+int
+cpfl_tdi_build(struct cpfl_flow_parser *flow_parser)
+{
+	int ret;
+
+	ret = cpfl_tdi_build_table_list(flow_parser);
+	if (ret != 0) {
+		PMD_INIT_LOG(ERR, "Failed to build tdi table list");
+		return ret;
+	}
+
+	ret = cpfl_tdi_build_action_list(flow_parser);
+	if (ret != 0) {
+		PMD_INIT_LOG(ERR, "Failed to build tdi action list");
+		cpfl_tdi_free_table_list(flow_parser);
+		return ret;
+	}
+
+	return 0;
+}
+
+static bool
+cpfl_flow_items_all_is_flex(const struct rte_flow_item pattern[])
+{
+	int i;
+
+	for (i = 0; pattern[i].type != RTE_FLOW_ITEM_TYPE_END; i++) {
+		if (pattern[i].type != RTE_FLOW_ITEM_TYPE_FLEX)
+			return false;
+	}
+	return true;
+}
+
+static void
+cpfl_fill_rinfo_default_value(struct cpfl_tdi_rule_info *rinfo)
+{
+	if (cpfl_tdi_rule_cookie == ~0llu)
+		cpfl_tdi_rule_cookie = CPFL_COOKIE_DEF;
+	rinfo->cookie = cpfl_tdi_rule_cookie++;
+	rinfo->host_id = CPFL_HOST_ID_DEF;
+	rinfo->port_num = CPFL_PORT_NUM_DEF;
+	rinfo->resp_req = CPFL_RESP_REQ_DEF;
+	rinfo->vsi = CPFL_VSI_DEF;
+	rinfo->clear_mirror_1st_state = CPFL_CLEAR_MIRROR_1ST_STATE_DEF;
+}
+
+static int
+cpfl_tdi_parse_pattern(struct rte_eth_dev *dev,
+		       const struct rte_flow_item pattern[],
+		       uint32_t table_id,
+		       struct cpfl_tdi_table_node **table_node,
+		       struct cpfl_tdi_table_key_obj *kobj)
+{
+	const struct rte_flow_item_flex *flex;
+	int i, ret;
+
+	/* Create the key */
+	ret = cpfl_tdi_table_key_create(dev, table_id, table_node, kobj);
+	if (ret) {
+		PMD_INIT_LOG(ERR, "Failed to create table key obj.");
+		return -EINVAL;
+	}
+
+	for (i = 0; pattern[i].type != RTE_FLOW_ITEM_TYPE_END; i++) {
+		if (pattern[i].type != RTE_FLOW_ITEM_TYPE_FLEX) {
+			PMD_INIT_LOG(ERR, "All pattern type should be RTE_FLOW_ITEM_TYPE_FLEX.");
+			return -EINVAL;
+		}
+		flex = pattern[i].spec;
+		/* Set the key fields */
+		ret = cpfl_tdi_table_key_field_set(dev, *table_node, kobj, i, flex->pattern,
+						   flex->length);
+		if (ret) {
+			PMD_INIT_LOG(ERR, "Failed to set table key field.");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_action(struct rte_eth_dev *dev,
+		      const struct rte_flow_action actions[],
+		      uint32_t table_id,
+		      struct cpfl_tdi_table_node *table_node,
+		      struct cpfl_tdi_action_node **action_node,
+		      struct cpfl_tdi_action_obj *aobj)
+{
+	const struct rte_flow_action_prog *prog;
+	const struct rte_flow_action_prog_argument *arg;
+	uint32_t action_spec_id;
+	int i, ret;
+	uint32_t j;
+
+	for (i = 0; actions[i].type != RTE_FLOW_ACTION_TYPE_END; i++) {
+		if (actions[i].type != RTE_FLOW_ACTION_TYPE_PROG)
+			continue;
+
+		prog = actions[i].conf;
+		action_spec_id = atoi(prog->name);
+		/* Get action node */
+		ret =
+		    cpfl_tdi_action_node_get_by_spec_id(dev, table_id, action_spec_id, action_node);
+		if (ret) {
+			PMD_INIT_LOG(ERR, "Failed to get action node.");
+			return -EINVAL;
+		}
+
+		ret = cpfl_tdi_action_obj_init(dev, table_node, *action_node, aobj);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR, "Failed to init action obj.");
+			return -EINVAL;
+		}
+
+		for (j = 0; j < prog->args_num; j++) {
+			arg = &prog->args[j];
+			/* Set the action fields */
+			ret = cpfl_tdi_action_field_set(dev, aobj, *action_node, j, arg);
+			if (ret) {
+				PMD_INIT_LOG(ERR, "Failed to set action field.");
+				return -EINVAL;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_pattern_action(struct rte_eth_dev *dev,
+			      const struct rte_flow_attr *attr,
+			      const struct rte_flow_item pattern[],
+			      const struct rte_flow_action actions[],
+			      void **meta)
+{
+	struct cpfl_itf *itf = CPFL_DEV_TO_ITF(dev);
+	struct cpfl_adapter_ext *adapter = itf->adapter;
+	int ret;
+	struct cpfl_tdi_rule_info *rinfo;
+	struct cpfl_tdi_table_node *table_node;
+	struct cpfl_tdi_action_node *action_node;
+	struct cpfl_tdi_table_key_obj *kobj;
+	struct cpfl_tdi_action_obj *aobj;
+	uint32_t table_id = attr->group;
+
+	if (!adapter->flow_parser.is_p4_parser || !cpfl_flow_items_all_is_flex(pattern))
+		return -EINVAL;
+
+	rinfo = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_rule_info), 0);
+	if (!rinfo)
+		return -ENOMEM;
+
+	kobj = &rinfo->kobj;
+	aobj = &rinfo->aobj;
+	memset(kobj, 0, sizeof(struct cpfl_tdi_table_key_obj));
+	memset(aobj, 0, sizeof(struct cpfl_tdi_action_obj));
+
+	ret = cpfl_tdi_parse_pattern(dev, pattern, table_id, &table_node, kobj);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Invalid pattern");
+		rte_free(rinfo);
+		return -EINVAL;
+	}
+
+	ret = cpfl_tdi_parse_action(dev, actions, table_id, table_node, &action_node, aobj);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "Invalid action");
+		rte_free(rinfo);
+		return -EINVAL;
+	}
+
+	cpfl_fill_rinfo_default_value(rinfo);
+	if (!meta)
+		rte_free(rinfo);
+	else
+		*meta = rinfo;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_fxp_init(struct cpfl_adapter_ext *ad __rte_unused)
+{
+	return 0;
+}
+
+static void
+cpfl_tdi_fxp_uninit(struct cpfl_adapter_ext *ad __rte_unused)
+{
+}
+
+static struct cpfl_flow_engine cpfl_tdi_engine = {
+	.type = CPFL_FLOW_ENGINE_TDI,
+	.init = cpfl_tdi_fxp_init,
+	.uninit = cpfl_tdi_fxp_uninit,
+	.create = cpfl_tdi_fxp_rule_create,
+	.destroy = cpfl_tdi_fxp_rule_destroy,
+	.parse_pattern_action = cpfl_tdi_parse_pattern_action,
+};
+
+RTE_INIT(cpfl_sw_engine_init)
+{
+	struct cpfl_flow_engine *engine = &cpfl_tdi_engine;
+
+	cpfl_flow_engine_register(engine);
+}
diff --git a/drivers/net/cpfl/cpfl_tdi.h b/drivers/net/cpfl/cpfl_tdi.h
new file mode 100644
index 0000000000..c7b8c38657
--- /dev/null
+++ b/drivers/net/cpfl/cpfl_tdi.h
@@ -0,0 +1,123 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+#ifndef _CPFL_TDI_H_
+#define _CPFL_TDI_H_
+
+#include "cpfl_ethdev.h"
+#include "cpfl_fxp_rule.h"
+#include "cpfl_tdi_parser.h"
+
+#define CPFL_TDI_KEY_FIELD_NUM_MAX 256	/* Max number of key field in a table. */
+#define CPFL_TDI_ACTION_SPEC_NUM_MAX 64 /* Max number of action spec in a table. */
+#define CPFL_TDI_ACTION_PARAMETER_NUM_MAX 16
+#define CPFL_TDI_ACTION_BUF_SIZE_MAX 256
+#define CPFL_TDI_TABLE_KEY_FIELD_MAX 32
+#define CPFL_TDI_MAX_TABLE_KEY_SIZE 128
+
+/**
+ *
+ * Table entry operation type.
+ */
+
+enum cpfl_tdi_table_entry_op {
+	CPFL_TDI_TABLE_ENTRY_OP_ADD, /* Add an entry */
+	CPFL_TDI_TABLE_ENTRY_OP_DEL, /* Delete an entry */
+	CPFL_TDI_TABLE_ENTRY_OP_QRY, /* Query an entry */
+};
+
+/**
+ * Table key match type.
+ *
+ * To specify the key match type of a table.
+ */
+enum cpfl_tdi_table_key_match_type {
+	CPFL_TDI_TABLE_KEY_MATCH_TYPE_EXACT,	/**< Exact match. */
+	CPFL_TDI_TABLE_KEY_MATCH_TYPE_WILDCARD, /**< Wildcard match. */
+	CPFL_TDI_TABLE_KEY_MATCH_TYPE_RANGE,	/**< Range match. */
+	CPFL_TDI_TABLE_KEY_MATCH_TYPE_LPM,	/**< longest prefix match. */
+};
+
+struct cpfl_tdi_param_info {
+	uint32_t id;
+	uint16_t offset;
+	uint16_t size;
+};
+
+struct cpfl_tdi_action_spec_field_info {
+	struct cpfl_tdi_immediate_field *field;
+	struct cpfl_tdi_param_info param;
+	struct cpfl_tdi_mod_field *mod_field;
+	struct cpfl_tdi_hw_action *hw_action;
+};
+
+struct cpfl_tdi_table_key_field_info {
+	struct cpfl_tdi_match_key_field *field;
+	struct cpfl_tdi_match_key_format *format;
+	struct cpfl_tdi_param_info param;
+};
+
+struct cpfl_tdi_action_node {
+	TAILQ_ENTRY(cpfl_tdi_action_node) next;
+	enum cpfl_tdi_hw_block hw_block_type;
+	const struct cpfl_tdi_action *action;
+	const struct cpfl_tdi_action_format *format;
+	uint32_t buf_len;
+	struct cpfl_tdi_param_info params[CPFL_TDI_ACTION_PARAMETER_NUM_MAX];
+	uint8_t init_buf[CPFL_TDI_ACTION_BUF_SIZE_MAX];
+	uint8_t query_msk[CPFL_TDI_ACTION_BUF_SIZE_MAX];
+};
+
+struct cpfl_tdi_table_node {
+	TAILQ_ENTRY(cpfl_tdi_table_node) next;
+	const struct cpfl_tdi_table *table;
+	struct cpfl_tdi_action_node **actions;
+	uint16_t buf_len;
+	struct cpfl_tdi_param_info params[CPFL_TDI_TABLE_KEY_FIELD_MAX];
+};
+
+struct cpfl_tdi_table_key_obj {
+	uint16_t buf_len;
+	uint8_t buf[CPFL_TDI_MAX_TABLE_KEY_SIZE];
+	const struct cpfl_tdi_table_node *tnode;
+	union {
+		struct {
+			uint16_t prof_id;
+			uint8_t sub_prof_id;
+			uint8_t pin_to_cache;
+			uint8_t fixed_fetch;
+		} sem;
+		struct {
+			uint32_t mod_index;
+			uint8_t pin_mod_content;
+			uint8_t mod_obj_size;
+		} mod;
+	};
+};
+
+struct cpfl_tdi_action_obj {
+	const struct cpfl_tdi_table *table;
+	struct cpfl_tdi_action_node *node;
+	uint16_t buf_len;
+	uint8_t buf[CPFL_TDI_ACTION_BUF_SIZE_MAX];
+};
+
+TAILQ_HEAD(tdi_table_key_obj_list, tdi_table_key_obj);
+TAILQ_HEAD(tdi_action_obj_list, tdi_action_obj);
+
+struct cpfl_tdi_rule_info {
+	enum cpfl_rule_type type;
+	struct cpfl_tdi_table_key_obj kobj;
+	struct cpfl_tdi_action_obj aobj;
+	uint64_t cookie;
+	uint8_t host_id;
+	uint8_t port_num;
+	uint8_t resp_req;
+	/* vsi is used for lem and lpm rules */
+	uint16_t vsi;
+	uint8_t clear_mirror_1st_state;
+};
+
+void cpfl_tdi_free_table_list(struct cpfl_flow_parser *flow_parser);
+int cpfl_tdi_build(struct cpfl_flow_parser *flow_parser);
+#endif
diff --git a/drivers/net/cpfl/meson.build b/drivers/net/cpfl/meson.build
index f948033f1f..a2e5c9d25b 100644
--- a/drivers/net/cpfl/meson.build
+++ b/drivers/net/cpfl/meson.build
@@ -48,6 +48,7 @@  if dpdk_conf.has('RTE_HAS_JANSSON')
             'cpfl_flow_parser.c',
             'cpfl_fxp_rule.c',
             'cpfl_tdi_parser.c',
+            'cpfl_tdi.c',
     )
     ext_deps += jansson_dep
 endif