/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(C) 2019 Marvell International Ltd.
 */

#include "otx2_ethdev.h"
#include "otx2_flow.h"

const struct rte_flow_item *
otx2_flow_skip_void_and_any_items(const struct rte_flow_item *pattern)
{
	while ((pattern->type == RTE_FLOW_ITEM_TYPE_VOID) ||
	       (pattern->type == RTE_FLOW_ITEM_TYPE_ANY))
		pattern++;

	return pattern;
}

/*
 * Tunnel+ESP, Tunnel+ICMP4/6, Tunnel+TCP, Tunnel+UDP,
 * Tunnel+SCTP
 */
int
otx2_flow_parse_lh(struct otx2_parse_state *pst)
{
	struct otx2_flow_item_info info;
	char hw_mask[64];
	int lid, lt;
	int rc;

	if (!pst->tunnel)
		return 0;

	info.hw_mask = &hw_mask;
	info.spec = NULL;
	info.mask = NULL;
	info.hw_hdr_len = 0;
	lid = NPC_LID_LH;

	switch (pst->pattern->type) {
	case RTE_FLOW_ITEM_TYPE_UDP:
		lt = NPC_LT_LH_TU_UDP;
		info.def_mask = &rte_flow_item_udp_mask;
		info.len = sizeof(struct rte_flow_item_udp);
		break;
	case RTE_FLOW_ITEM_TYPE_TCP:
		lt = NPC_LT_LH_TU_TCP;
		info.def_mask = &rte_flow_item_tcp_mask;
		info.len = sizeof(struct rte_flow_item_tcp);
		break;
	case RTE_FLOW_ITEM_TYPE_SCTP:
		lt = NPC_LT_LH_TU_SCTP;
		info.def_mask = &rte_flow_item_sctp_mask;
		info.len = sizeof(struct rte_flow_item_sctp);
		break;
	case RTE_FLOW_ITEM_TYPE_ESP:
		lt = NPC_LT_LH_TU_ESP;
		info.def_mask = &rte_flow_item_esp_mask;
		info.len = sizeof(struct rte_flow_item_esp);
		break;
	default:
		return 0;
	}

	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc != 0)
		return rc;

	return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}

/* Tunnel+IPv4, Tunnel+IPv6 */
int
otx2_flow_parse_lg(struct otx2_parse_state *pst)
{
	struct otx2_flow_item_info info;
	char hw_mask[64];
	int lid, lt;
	int rc;

	if (!pst->tunnel)
		return 0;

	info.hw_mask = &hw_mask;
	info.spec = NULL;
	info.mask = NULL;
	info.hw_hdr_len = 0;
	lid = NPC_LID_LG;

	if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_IPV4) {
		lt = NPC_LT_LG_TU_IP;
		info.def_mask = &rte_flow_item_ipv4_mask;
		info.len = sizeof(struct rte_flow_item_ipv4);
	} else if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_IPV6) {
		lt = NPC_LT_LG_TU_IP6;
		info.def_mask = &rte_flow_item_ipv6_mask;
		info.len = sizeof(struct rte_flow_item_ipv6);
	} else {
		/* There is no tunneled IP header */
		return 0;
	}

	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc != 0)
		return rc;

	return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}

/* Tunnel+Ether */
int
otx2_flow_parse_lf(struct otx2_parse_state *pst)
{
	const struct rte_flow_item *pattern, *last_pattern;
	struct rte_flow_item_eth hw_mask;
	struct otx2_flow_item_info info;
	int lid, lt, lflags;
	int nr_vlans = 0;
	int rc;

	/* We hit this layer if there is a tunneling protocol */
	if (!pst->tunnel)
		return 0;

	if (pst->pattern->type != RTE_FLOW_ITEM_TYPE_ETH)
		return 0;

	lid = NPC_LID_LF;
	lt = NPC_LT_LF_TU_ETHER;
	lflags = 0;

	info.def_mask = &rte_flow_item_vlan_mask;
	/* No match support for vlan tags */
	info.hw_mask = NULL;
	info.len = sizeof(struct rte_flow_item_vlan);
	info.spec = NULL;
	info.mask = NULL;
	info.hw_hdr_len = 0;

	/* Look ahead and find out any VLAN tags. These can be
	 * detected but no data matching is available.
	 */
	last_pattern = pst->pattern;
	pattern = pst->pattern + 1;
	pattern = otx2_flow_skip_void_and_any_items(pattern);
	while (pattern->type == RTE_FLOW_ITEM_TYPE_VLAN) {
		nr_vlans++;
		rc = otx2_flow_parse_item_basic(pattern, &info, pst->error);
		if (rc != 0)
			return rc;
		last_pattern = pattern;
		pattern++;
		pattern = otx2_flow_skip_void_and_any_items(pattern);
	}
	otx2_npc_dbg("Nr_vlans = %d", nr_vlans);
	switch (nr_vlans) {
	case 0:
		break;
	case 1:
		lflags = NPC_F_TU_ETHER_CTAG;
		break;
	case 2:
		lflags = NPC_F_TU_ETHER_STAG_CTAG;
		break;
	default:
		rte_flow_error_set(pst->error, ENOTSUP,
				   RTE_FLOW_ERROR_TYPE_ITEM,
				   last_pattern,
				   "more than 2 vlans with tunneled Ethernet "
				   "not supported");
		return -rte_errno;
	}

	info.def_mask = &rte_flow_item_eth_mask;
	info.hw_mask = &hw_mask;
	info.len = sizeof(struct rte_flow_item_eth);
	info.hw_hdr_len = 0;
	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	info.spec = NULL;
	info.mask = NULL;

	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc != 0)
		return rc;

	pst->pattern = last_pattern;

	return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}

int
otx2_flow_parse_le(struct otx2_parse_state *pst)
{
	/*
	 * We are positioned at UDP. Scan ahead and look for
	 * UDP encapsulated tunnel protocols. If available,
	 * parse them. In that case handle this:
	 *	- RTE spec assumes we point to tunnel header.
	 *	- NPC parser provides offset from UDP header.
	 */

	/*
	 * Note: Add support to GENEVE, VXLAN_GPE when we
	 * upgrade DPDK
	 *
	 * Note: Better to split flags into two nibbles:
	 *	- Higher nibble can have flags
	 *	- Lower nibble to further enumerate protocols
	 *	  and have flags based extraction
	 */
	const struct rte_flow_item *pattern = pst->pattern;
	struct otx2_flow_item_info info;
	int lid, lt, lflags;
	char hw_mask[64];
	int rc;

	if (pst->tunnel)
		return 0;

	if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_MPLS)
		return otx2_flow_parse_mpls(pst, NPC_LID_LE);

	info.spec = NULL;
	info.mask = NULL;
	info.hw_mask = NULL;
	info.def_mask = NULL;
	info.len = 0;
	info.hw_hdr_len = 0;
	lid = NPC_LID_LE;
	lflags = 0;

	/* Ensure we are not matching anything in UDP */
	rc = otx2_flow_parse_item_basic(pattern, &info, pst->error);
	if (rc)
		return rc;

	info.hw_mask = &hw_mask;
	pattern = otx2_flow_skip_void_and_any_items(pattern);
	otx2_npc_dbg("Pattern->type = %d", pattern->type);
	switch (pattern->type) {
	case RTE_FLOW_ITEM_TYPE_VXLAN:
		lflags = NPC_F_UDP_VXLAN;
		info.def_mask = &rte_flow_item_vxlan_mask;
		info.len = sizeof(struct rte_flow_item_vxlan);
		lt = NPC_LT_LE_VXLAN;
		break;
	case RTE_FLOW_ITEM_TYPE_ESP:
		lt = NPC_LT_LE_ESP;
		info.def_mask = &rte_flow_item_esp_mask;
		info.len = sizeof(struct rte_flow_item_esp);
		break;
	case RTE_FLOW_ITEM_TYPE_GTPC:
		lflags = NPC_F_UDP_GTP_GTPC;
		info.def_mask = &rte_flow_item_gtp_mask;
		info.len = sizeof(struct rte_flow_item_gtp);
		lt = NPC_LT_LE_GTPC;
		break;
	case RTE_FLOW_ITEM_TYPE_GTPU:
		lflags = NPC_F_UDP_GTP_GTPU_G_PDU;
		info.def_mask = &rte_flow_item_gtp_mask;
		info.len = sizeof(struct rte_flow_item_gtp);
		lt = NPC_LT_LE_GTPU;
		break;
	case RTE_FLOW_ITEM_TYPE_GENEVE:
		lflags = NPC_F_UDP_GENEVE;
		info.def_mask = &rte_flow_item_geneve_mask;
		info.len = sizeof(struct rte_flow_item_geneve);
		lt = NPC_LT_LE_GENEVE;
		break;
	case RTE_FLOW_ITEM_TYPE_VXLAN_GPE:
		lflags = NPC_F_UDP_VXLANGPE;
		info.def_mask = &rte_flow_item_vxlan_gpe_mask;
		info.len = sizeof(struct rte_flow_item_vxlan_gpe);
		lt = NPC_LT_LE_VXLANGPE;
		break;
	default:
		return 0;
	}

	pst->tunnel = 1;

	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	rc = otx2_flow_parse_item_basic(pattern, &info, pst->error);
	if (rc != 0)
		return rc;

	return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}

static int
flow_parse_mpls_label_stack(struct otx2_parse_state *pst, int *flag)
{
	int nr_labels = 0;
	const struct rte_flow_item *pattern = pst->pattern;
	struct otx2_flow_item_info info;
	int rc;
	uint8_t flag_list[] = {0, NPC_F_MPLS_2_LABELS,
		NPC_F_MPLS_3_LABELS, NPC_F_MPLS_4_LABELS};

	/*
	 * pst->pattern points to first MPLS label. We only check
	 * that subsequent labels do not have anything to match.
	 */
	info.def_mask = &rte_flow_item_mpls_mask;
	info.hw_mask = NULL;
	info.len = sizeof(struct rte_flow_item_mpls);
	info.spec = NULL;
	info.mask = NULL;
	info.hw_hdr_len = 0;

	while (pattern->type == RTE_FLOW_ITEM_TYPE_MPLS) {
		nr_labels++;

		/* Basic validation of 2nd/3rd/4th mpls item */
		if (nr_labels > 1) {
			rc = otx2_flow_parse_item_basic(pattern, &info,
							pst->error);
			if (rc != 0)
				return rc;
		}
		pst->last_pattern = pattern;
		pattern++;
		pattern = otx2_flow_skip_void_and_any_items(pattern);
	}

	if (nr_labels > 4) {
		rte_flow_error_set(pst->error, ENOTSUP,
				   RTE_FLOW_ERROR_TYPE_ITEM,
				   pst->last_pattern,
				   "more than 4 mpls labels not supported");
		return -rte_errno;
	}

	*flag = flag_list[nr_labels - 1];
	return 0;
}

int
otx2_flow_parse_mpls(struct otx2_parse_state *pst, int lid)
{
	/* Find number of MPLS labels */
	struct rte_flow_item_mpls hw_mask;
	struct otx2_flow_item_info info;
	int lt, lflags;
	int rc;

	lflags = 0;

	if (lid == NPC_LID_LC)
		lt = NPC_LT_LC_MPLS;
	else if (lid == NPC_LID_LD)
		lt = NPC_LT_LD_TU_MPLS_IN_IP;
	else
		lt = NPC_LT_LE_TU_MPLS_IN_UDP;

	/* Prepare for parsing the first item */
	info.def_mask = &rte_flow_item_mpls_mask;
	info.hw_mask = &hw_mask;
	info.len = sizeof(struct rte_flow_item_mpls);
	info.spec = NULL;
	info.mask = NULL;
	info.hw_hdr_len = 0;

	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc != 0)
		return rc;

	/*
	 * Parse for more labels.
	 * This sets lflags and pst->last_pattern correctly.
	 */
	rc = flow_parse_mpls_label_stack(pst, &lflags);
	if (rc != 0)
		return rc;

	pst->tunnel = 1;
	pst->pattern = pst->last_pattern;

	return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}

/*
 * ICMP, ICMP6, UDP, TCP, SCTP, VXLAN, GRE, NVGRE,
 * GTP, GTPC, GTPU, ESP
 *
 * Note: UDP tunnel protocols are identified by flags.
 *       LPTR for these protocol still points to UDP
 *       header. Need flag based extraction to support
 *       this.
 */
int
otx2_flow_parse_ld(struct otx2_parse_state *pst)
{
	char hw_mask[NPC_MAX_EXTRACT_DATA_LEN];
	uint32_t gre_key_mask = 0xffffffff;
	struct otx2_flow_item_info info;
	int lid, lt, lflags;
	int rc;

	if (pst->tunnel) {
		/* We have already parsed MPLS or IPv4/v6 followed
		 * by MPLS or IPv4/v6. Subsequent TCP/UDP etc
		 * would be parsed as tunneled versions. Skip
		 * this layer, except for tunneled MPLS. If LC is
		 * MPLS, we have anyway skipped all stacked MPLS
		 * labels.
		 */
		if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_MPLS)
			return otx2_flow_parse_mpls(pst, NPC_LID_LD);
		return 0;
	}
	info.hw_mask = &hw_mask;
	info.spec = NULL;
	info.mask = NULL;
	info.def_mask = NULL;
	info.len = 0;
	info.hw_hdr_len = 0;

	lid = NPC_LID_LD;
	lflags = 0;

	otx2_npc_dbg("Pst->pattern->type = %d", pst->pattern->type);
	switch (pst->pattern->type) {
	case RTE_FLOW_ITEM_TYPE_ICMP:
		if (pst->lt[NPC_LID_LC] == NPC_LT_LC_IP6)
			lt = NPC_LT_LD_ICMP6;
		else
			lt = NPC_LT_LD_ICMP;
		info.def_mask = &rte_flow_item_icmp_mask;
		info.len = sizeof(struct rte_flow_item_icmp);
		break;
	case RTE_FLOW_ITEM_TYPE_UDP:
		lt = NPC_LT_LD_UDP;
		info.def_mask = &rte_flow_item_udp_mask;
		info.len = sizeof(struct rte_flow_item_udp);
		break;
	case RTE_FLOW_ITEM_TYPE_TCP:
		lt = NPC_LT_LD_TCP;
		info.def_mask = &rte_flow_item_tcp_mask;
		info.len = sizeof(struct rte_flow_item_tcp);
		break;
	case RTE_FLOW_ITEM_TYPE_SCTP:
		lt = NPC_LT_LD_SCTP;
		info.def_mask = &rte_flow_item_sctp_mask;
		info.len = sizeof(struct rte_flow_item_sctp);
		break;
	case RTE_FLOW_ITEM_TYPE_GRE:
		lt = NPC_LT_LD_GRE;
		info.def_mask = &rte_flow_item_gre_mask;
		info.len = sizeof(struct rte_flow_item_gre);
		break;
	case RTE_FLOW_ITEM_TYPE_GRE_KEY:
		lt = NPC_LT_LD_GRE;
		info.def_mask = &gre_key_mask;
		info.len = sizeof(gre_key_mask);
		info.hw_hdr_len = 4;
		break;
	case RTE_FLOW_ITEM_TYPE_NVGRE:
		lt = NPC_LT_LD_NVGRE;
		lflags = NPC_F_GRE_NVGRE;
		info.def_mask = &rte_flow_item_nvgre_mask;
		info.len = sizeof(struct rte_flow_item_nvgre);
		/* Further IP/Ethernet are parsed as tunneled */
		pst->tunnel = 1;
		break;
	default:
		return 0;
	}

	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc != 0)
		return rc;

	return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}

static inline void
flow_check_lc_ip_tunnel(struct otx2_parse_state *pst)
{
	const struct rte_flow_item *pattern = pst->pattern + 1;

	pattern = otx2_flow_skip_void_and_any_items(pattern);
	if (pattern->type == RTE_FLOW_ITEM_TYPE_MPLS ||
	    pattern->type == RTE_FLOW_ITEM_TYPE_IPV4 ||
	    pattern->type == RTE_FLOW_ITEM_TYPE_IPV6)
		pst->tunnel = 1;
}

static int
otx2_flow_raw_item_prepare(const struct rte_flow_item_raw *raw_spec,
			   const struct rte_flow_item_raw *raw_mask,
			   struct otx2_flow_item_info *info,
			   uint8_t *spec_buf, uint8_t *mask_buf)
{
	uint32_t custom_hdr_size = 0;

	memset(spec_buf, 0, NPC_MAX_RAW_ITEM_LEN);
	memset(mask_buf, 0, NPC_MAX_RAW_ITEM_LEN);
	custom_hdr_size = raw_spec->offset + raw_spec->length;

	memcpy(spec_buf + raw_spec->offset, raw_spec->pattern,
	       raw_spec->length);

	if (raw_mask->pattern) {
		memcpy(mask_buf + raw_spec->offset, raw_mask->pattern,
		       raw_spec->length);
	} else {
		memset(mask_buf + raw_spec->offset, 0xFF, raw_spec->length);
	}

	info->len = custom_hdr_size;
	info->spec = spec_buf;
	info->mask = mask_buf;

	return 0;
}

/* Outer IPv4, Outer IPv6, MPLS, ARP */
int
otx2_flow_parse_lc(struct otx2_parse_state *pst)
{
	uint8_t raw_spec_buf[NPC_MAX_RAW_ITEM_LEN];
	uint8_t raw_mask_buf[NPC_MAX_RAW_ITEM_LEN];
	uint8_t hw_mask[NPC_MAX_EXTRACT_DATA_LEN];
	const struct rte_flow_item_raw *raw_spec;
	struct otx2_flow_item_info info;
	int lid, lt, len;
	int rc;

	if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_MPLS)
		return otx2_flow_parse_mpls(pst, NPC_LID_LC);

	info.hw_mask = &hw_mask;
	info.spec = NULL;
	info.mask = NULL;
	info.hw_hdr_len = 0;
	lid = NPC_LID_LC;

	switch (pst->pattern->type) {
	case RTE_FLOW_ITEM_TYPE_IPV4:
		lt = NPC_LT_LC_IP;
		info.def_mask = &rte_flow_item_ipv4_mask;
		info.len = sizeof(struct rte_flow_item_ipv4);
		break;
	case RTE_FLOW_ITEM_TYPE_IPV6:
		lid = NPC_LID_LC;
		lt = NPC_LT_LC_IP6;
		info.def_mask = &rte_flow_item_ipv6_mask;
		info.len = sizeof(struct rte_flow_item_ipv6);
		break;
	case RTE_FLOW_ITEM_TYPE_ARP_ETH_IPV4:
		lt = NPC_LT_LC_ARP;
		info.def_mask = &rte_flow_item_arp_eth_ipv4_mask;
		info.len = sizeof(struct rte_flow_item_arp_eth_ipv4);
		break;
	case RTE_FLOW_ITEM_TYPE_IPV6_EXT:
		lid = NPC_LID_LC;
		lt = NPC_LT_LC_IP6_EXT;
		info.def_mask = &rte_flow_item_ipv6_ext_mask;
		info.len = sizeof(struct rte_flow_item_ipv6_ext);
		info.hw_hdr_len = 40;
		break;
	case RTE_FLOW_ITEM_TYPE_RAW:
		raw_spec = pst->pattern->spec;
		if (!raw_spec->relative)
			return 0;

		len = raw_spec->length + raw_spec->offset;
		if (len > NPC_MAX_RAW_ITEM_LEN) {
			rte_flow_error_set(pst->error, EINVAL,
					   RTE_FLOW_ERROR_TYPE_ITEM, NULL,
					   "Spec length too big");
			return -rte_errno;
		}

		otx2_flow_raw_item_prepare((const struct rte_flow_item_raw *)
					   pst->pattern->spec,
					   (const struct rte_flow_item_raw *)
					   pst->pattern->mask, &info,
					   raw_spec_buf, raw_mask_buf);

		lid = NPC_LID_LC;
		lt = NPC_LT_LC_NGIO;
		info.hw_mask = &hw_mask;
		otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
		break;
	default:
		/* No match at this layer */
		return 0;
	}

	/* Identify if IP tunnels MPLS or IPv4/v6 */
	flow_check_lc_ip_tunnel(pst);

	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc != 0)
		return rc;

	return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}

/* VLAN, ETAG */
int
otx2_flow_parse_lb(struct otx2_parse_state *pst)
{
	const struct rte_flow_item *pattern = pst->pattern;
	uint8_t raw_spec_buf[NPC_MAX_RAW_ITEM_LEN];
	uint8_t raw_mask_buf[NPC_MAX_RAW_ITEM_LEN];
	const struct rte_flow_item *last_pattern;
	const struct rte_flow_item_raw *raw_spec;
	char hw_mask[NPC_MAX_EXTRACT_DATA_LEN];
	struct otx2_flow_item_info info;
	int lid, lt, lflags, len;
	int nr_vlans = 0;
	int rc;

	info.spec = NULL;
	info.mask = NULL;
	info.hw_hdr_len = NPC_TPID_LENGTH;

	lid = NPC_LID_LB;
	lflags = 0;
	last_pattern = pattern;

	if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_VLAN) {
		/* RTE vlan is either 802.1q or 802.1ad,
		 * this maps to either CTAG/STAG. We need to decide
		 * based on number of VLANS present. Matching is
		 * supported on first tag only.
		 */
		info.def_mask = &rte_flow_item_vlan_mask;
		info.hw_mask = NULL;
		info.len = sizeof(struct rte_flow_item_vlan);

		pattern = pst->pattern;
		while (pattern->type == RTE_FLOW_ITEM_TYPE_VLAN) {
			nr_vlans++;

			/* Basic validation of 2nd/3rd vlan item */
			if (nr_vlans > 1) {
				otx2_npc_dbg("Vlans  = %d", nr_vlans);
				rc = otx2_flow_parse_item_basic(pattern, &info,
								pst->error);
				if (rc != 0)
					return rc;
			}
			last_pattern = pattern;
			pattern++;
			pattern = otx2_flow_skip_void_and_any_items(pattern);
		}

		switch (nr_vlans) {
		case 1:
			lt = NPC_LT_LB_CTAG;
			break;
		case 2:
			lt = NPC_LT_LB_STAG_QINQ;
			lflags = NPC_F_STAG_CTAG;
			break;
		case 3:
			lt = NPC_LT_LB_STAG_QINQ;
			lflags = NPC_F_STAG_STAG_CTAG;
			break;
		default:
			rte_flow_error_set(pst->error, ENOTSUP,
					   RTE_FLOW_ERROR_TYPE_ITEM,
					   last_pattern,
					   "more than 3 vlans not supported");
			return -rte_errno;
		}
	} else if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_E_TAG) {
		/* we can support ETAG and match a subsequent CTAG
		 * without any matching support.
		 */
		lt = NPC_LT_LB_ETAG;
		lflags = 0;

		last_pattern = pst->pattern;
		pattern = otx2_flow_skip_void_and_any_items(pst->pattern + 1);
		if (pattern->type == RTE_FLOW_ITEM_TYPE_VLAN) {
			info.def_mask = &rte_flow_item_vlan_mask;
			/* set supported mask to NULL for vlan tag */
			info.hw_mask = NULL;
			info.len = sizeof(struct rte_flow_item_vlan);
			rc = otx2_flow_parse_item_basic(pattern, &info,
							pst->error);
			if (rc != 0)
				return rc;

			lflags = NPC_F_ETAG_CTAG;
			last_pattern = pattern;
		}

		info.def_mask = &rte_flow_item_e_tag_mask;
		info.len = sizeof(struct rte_flow_item_e_tag);
	} else if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_RAW) {
		raw_spec = pst->pattern->spec;
		if (raw_spec->relative)
			return 0;
		len = raw_spec->length + raw_spec->offset;
		if (len > NPC_MAX_RAW_ITEM_LEN) {
			rte_flow_error_set(pst->error, EINVAL,
					   RTE_FLOW_ERROR_TYPE_ITEM, NULL,
					   "Spec length too big");
			return -rte_errno;
		}

		if (pst->npc->switch_header_type ==
		    OTX2_PRIV_FLAGS_VLAN_EXDSA) {
			lt = NPC_LT_LB_VLAN_EXDSA;
		} else if (pst->npc->switch_header_type ==
			   OTX2_PRIV_FLAGS_EXDSA) {
			lt = NPC_LT_LB_EXDSA;
		} else {
			rte_flow_error_set(pst->error, ENOTSUP,
					   RTE_FLOW_ERROR_TYPE_ITEM, NULL,
					   "exdsa or vlan_exdsa not enabled on"
					   " port");
			return -rte_errno;
		}

		otx2_flow_raw_item_prepare((const struct rte_flow_item_raw *)
					   pst->pattern->spec,
					   (const struct rte_flow_item_raw *)
					   pst->pattern->mask, &info,
					   raw_spec_buf, raw_mask_buf);

		info.hw_hdr_len = 0;
	} else {
		return 0;
	}

	info.hw_mask = &hw_mask;
	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);

	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc != 0)
		return rc;

	/* Point pattern to last item consumed */
	pst->pattern = last_pattern;
	return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}


int
otx2_flow_parse_la(struct otx2_parse_state *pst)
{
	struct rte_flow_item_eth hw_mask;
	struct otx2_flow_item_info info;
	int lid, lt;
	int rc;

	/* Identify the pattern type into lid, lt */
	if (pst->pattern->type != RTE_FLOW_ITEM_TYPE_ETH)
		return 0;

	lid = NPC_LID_LA;
	lt = NPC_LT_LA_ETHER;
	info.hw_hdr_len = 0;

	if (pst->flow->nix_intf == NIX_INTF_TX) {
		lt = NPC_LT_LA_IH_NIX_ETHER;
		info.hw_hdr_len = NPC_IH_LENGTH;
		if (pst->npc->switch_header_type == OTX2_PRIV_FLAGS_HIGIG) {
			lt = NPC_LT_LA_IH_NIX_HIGIG2_ETHER;
			info.hw_hdr_len += NPC_HIGIG2_LENGTH;
		}
	} else {
		if (pst->npc->switch_header_type == OTX2_PRIV_FLAGS_HIGIG) {
			lt = NPC_LT_LA_HIGIG2_ETHER;
			info.hw_hdr_len = NPC_HIGIG2_LENGTH;
		}
	}

	/* Prepare for parsing the item */
	info.def_mask = &rte_flow_item_eth_mask;
	info.hw_mask = &hw_mask;
	info.len = sizeof(struct rte_flow_item_eth);
	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	info.spec = NULL;
	info.mask = NULL;

	/* Basic validation of item parameters */
	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc)
		return rc;

	/* Update pst if not validate only? clash check? */
	return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}

int
otx2_flow_parse_higig2_hdr(struct otx2_parse_state *pst)
{
	struct rte_flow_item_higig2_hdr hw_mask;
	struct otx2_flow_item_info info;
	int lid, lt;
	int rc;

	/* Identify the pattern type into lid, lt */
	if (pst->pattern->type != RTE_FLOW_ITEM_TYPE_HIGIG2)
		return 0;

	lid = NPC_LID_LA;
	lt = NPC_LT_LA_HIGIG2_ETHER;
	info.hw_hdr_len = 0;

	if (pst->flow->nix_intf == NIX_INTF_TX) {
		lt = NPC_LT_LA_IH_NIX_HIGIG2_ETHER;
		info.hw_hdr_len = NPC_IH_LENGTH;
	}

	/* Prepare for parsing the item */
	info.def_mask = &rte_flow_item_higig2_hdr_mask;
	info.hw_mask = &hw_mask;
	info.len = sizeof(struct rte_flow_item_higig2_hdr);
	otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
	info.spec = NULL;
	info.mask = NULL;

	/* Basic validation of item parameters */
	rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
	if (rc)
		return rc;

	/* Update pst if not validate only? clash check? */
	return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}

static int
parse_rss_action(struct rte_eth_dev *dev,
		 const struct rte_flow_attr *attr,
		 const struct rte_flow_action *act,
		 struct rte_flow_error *error)
{
	struct otx2_eth_dev *hw = dev->data->dev_private;
	struct otx2_rss_info *rss_info = &hw->rss_info;
	const struct rte_flow_action_rss *rss;
	uint32_t i;

	rss = (const struct rte_flow_action_rss *)act->conf;

	/* Not supported */
	if (attr->egress) {
		return rte_flow_error_set(error, EINVAL,
					  RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
					  attr, "No support of RSS in egress");
	}

	if (dev->data->dev_conf.rxmode.mq_mode != RTE_ETH_MQ_RX_RSS)
		return rte_flow_error_set(error, ENOTSUP,
					  RTE_FLOW_ERROR_TYPE_ACTION,
					  act, "multi-queue mode is disabled");

	/* Parse RSS related parameters from configuration */
	if (!rss || !rss->queue_num)
		return rte_flow_error_set(error, EINVAL,
					  RTE_FLOW_ERROR_TYPE_ACTION,
					  act, "no valid queues");

	if (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT)
		return rte_flow_error_set(error, ENOTSUP,
					  RTE_FLOW_ERROR_TYPE_ACTION, act,
					  "non-default RSS hash functions"
					  " are not supported");

	if (rss->key_len && rss->key_len > RTE_DIM(rss_info->key))
		return rte_flow_error_set(error, ENOTSUP,
					  RTE_FLOW_ERROR_TYPE_ACTION, act,
					  "RSS hash key too large");

	if (rss->queue_num > rss_info->rss_size)
		return rte_flow_error_set
			(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, act,
			 "too many queues for RSS context");

	for (i = 0; i < rss->queue_num; i++) {
		if (rss->queue[i] >= dev->data->nb_rx_queues)
			return rte_flow_error_set(error, EINVAL,
						  RTE_FLOW_ERROR_TYPE_ACTION,
						  act,
						  "queue id > max number"
						  " of queues");
	}

	return 0;
}

int
otx2_flow_parse_actions(struct rte_eth_dev *dev,
			const struct rte_flow_attr *attr,
			const struct rte_flow_action actions[],
			struct rte_flow_error *error,
			struct rte_flow *flow)
{
	struct otx2_eth_dev *hw = dev->data->dev_private;
	struct otx2_npc_flow_info *npc = &hw->npc_flow;
	const struct rte_flow_action_mark *act_mark;
	const struct rte_flow_action_queue *act_q;
	const struct rte_flow_action_vf *vf_act;
	uint16_t pf_func, vf_id, port_id, pf_id;
	char if_name[RTE_ETH_NAME_MAX_LEN];
	bool vlan_insert_action = false;
	struct rte_eth_dev *eth_dev;
	const char *errmsg = NULL;
	int sel_act, req_act = 0;
	int errcode = 0;
	int mark = 0;
	int rq = 0;

	/* Initialize actions */
	flow->ctr_id = NPC_COUNTER_NONE;
	pf_func = otx2_pfvf_func(hw->pf, hw->vf);

	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
		otx2_npc_dbg("Action type = %d", actions->type);

		switch (actions->type) {
		case RTE_FLOW_ACTION_TYPE_VOID:
			break;
		case RTE_FLOW_ACTION_TYPE_MARK:
			act_mark =
			    (const struct rte_flow_action_mark *)actions->conf;

			/* We have only 16 bits. Use highest val for flag */
			if (act_mark->id > (OTX2_FLOW_FLAG_VAL - 2)) {
				errmsg = "mark value must be < 0xfffe";
				errcode = ENOTSUP;
				goto err_exit;
			}
			mark = act_mark->id + 1;
			req_act |= OTX2_FLOW_ACT_MARK;
			rte_atomic32_inc(&npc->mark_actions);
			break;

		case RTE_FLOW_ACTION_TYPE_FLAG:
			mark = OTX2_FLOW_FLAG_VAL;
			req_act |= OTX2_FLOW_ACT_FLAG;
			rte_atomic32_inc(&npc->mark_actions);
			break;

		case RTE_FLOW_ACTION_TYPE_COUNT:
			/* Indicates, need a counter */
			flow->ctr_id = 1;
			req_act |= OTX2_FLOW_ACT_COUNT;
			break;

		case RTE_FLOW_ACTION_TYPE_DROP:
			req_act |= OTX2_FLOW_ACT_DROP;
			break;

		case RTE_FLOW_ACTION_TYPE_PF:
			req_act |= OTX2_FLOW_ACT_PF;
			pf_func &= (0xfc00);
			break;

		case RTE_FLOW_ACTION_TYPE_VF:
			vf_act = (const struct rte_flow_action_vf *)
				actions->conf;
			req_act |= OTX2_FLOW_ACT_VF;
			if (vf_act->original == 0) {
				vf_id = vf_act->id & RVU_PFVF_FUNC_MASK;
				if (vf_id  >= hw->maxvf) {
					errmsg = "invalid vf specified";
					errcode = EINVAL;
					goto err_exit;
				}
				pf_func &= (0xfc00);
				pf_func = (pf_func | (vf_id + 1));
			}
			break;

		case RTE_FLOW_ACTION_TYPE_PORT_ID:
		case RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR:
			if (actions->type == RTE_FLOW_ACTION_TYPE_PORT_ID) {
				const struct rte_flow_action_port_id *port_act;

				port_act = actions->conf;
				port_id = port_act->id;
			} else {
				const struct rte_flow_action_ethdev *ethdev_act;

				ethdev_act = actions->conf;
				port_id = ethdev_act->port_id;
			}
			if (rte_eth_dev_get_name_by_port(port_id, if_name)) {
				errmsg = "Name not found for output port id";
				errcode = EINVAL;
				goto err_exit;
			}
			eth_dev = rte_eth_dev_allocated(if_name);
			if (!eth_dev) {
				errmsg = "eth_dev not found for output port id";
				errcode = EINVAL;
				goto err_exit;
			}
			if (!otx2_ethdev_is_same_driver(eth_dev)) {
				errmsg = "Output port id unsupported type";
				errcode = ENOTSUP;
				goto err_exit;
			}
			if (!otx2_dev_is_vf(otx2_eth_pmd_priv(eth_dev))) {
				errmsg = "Output port should be VF";
				errcode = ENOTSUP;
				goto err_exit;
			}
			vf_id = otx2_eth_pmd_priv(eth_dev)->vf;
			if (vf_id  >= hw->maxvf) {
				errmsg = "Invalid vf for output port";
				errcode = EINVAL;
				goto err_exit;
			}
			pf_id = otx2_eth_pmd_priv(eth_dev)->pf;
			if (pf_id != hw->pf) {
				errmsg = "Output port unsupported PF";
				errcode = ENOTSUP;
				goto err_exit;
			}
			pf_func &= (0xfc00);
			pf_func = (pf_func | (vf_id + 1));
			req_act |= OTX2_FLOW_ACT_VF;
			break;

		case RTE_FLOW_ACTION_TYPE_QUEUE:
			/* Applicable only to ingress flow */
			act_q = (const struct rte_flow_action_queue *)
				actions->conf;
			rq = act_q->index;
			if (rq >= dev->data->nb_rx_queues) {
				errmsg = "invalid queue index";
				errcode = EINVAL;
				goto err_exit;
			}
			req_act |= OTX2_FLOW_ACT_QUEUE;
			break;

		case RTE_FLOW_ACTION_TYPE_RSS:
			errcode = parse_rss_action(dev,	attr, actions, error);
			if (errcode)
				return -rte_errno;

			req_act |= OTX2_FLOW_ACT_RSS;
			break;

		case RTE_FLOW_ACTION_TYPE_SECURITY:
			/* Assumes user has already configured security
			 * session for this flow. Associated conf is
			 * opaque. When RTE security is implemented for otx2,
			 * we need to verify that for specified security
			 * session:
			 *  action_type ==
			 *    RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL &&
			 *  session_protocol ==
			 *    RTE_SECURITY_PROTOCOL_IPSEC
			 *
			 * RSS is not supported with inline ipsec. Get the
			 * rq from associated conf, or make
			 * RTE_FLOW_ACTION_TYPE_QUEUE compulsory with this
			 * action.
			 * Currently, rq = 0 is assumed.
			 */
			req_act |= OTX2_FLOW_ACT_SEC;
			rq = 0;
			break;
		case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID:
			req_act |= OTX2_FLOW_ACT_VLAN_INSERT;
			break;
		case RTE_FLOW_ACTION_TYPE_OF_POP_VLAN:
			req_act |= OTX2_FLOW_ACT_VLAN_STRIP;
			break;
		case RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN:
			req_act |= OTX2_FLOW_ACT_VLAN_ETHTYPE_INSERT;
			break;
		case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP:
			req_act |= OTX2_FLOW_ACT_VLAN_PCP_INSERT;
			break;
		default:
			errmsg = "Unsupported action specified";
			errcode = ENOTSUP;
			goto err_exit;
		}
	}

	if (req_act &
	    (OTX2_FLOW_ACT_VLAN_INSERT | OTX2_FLOW_ACT_VLAN_ETHTYPE_INSERT |
	     OTX2_FLOW_ACT_VLAN_PCP_INSERT))
		vlan_insert_action = true;

	if ((req_act &
	     (OTX2_FLOW_ACT_VLAN_INSERT | OTX2_FLOW_ACT_VLAN_ETHTYPE_INSERT |
	      OTX2_FLOW_ACT_VLAN_PCP_INSERT)) ==
	    OTX2_FLOW_ACT_VLAN_PCP_INSERT) {
		errmsg = " PCP insert action can't be supported alone";
		errcode = ENOTSUP;
		goto err_exit;
	}

	/* Both STRIP and INSERT actions are not supported */
	if (vlan_insert_action && (req_act & OTX2_FLOW_ACT_VLAN_STRIP)) {
		errmsg = "Both VLAN insert and strip actions not supported"
			" together";
		errcode = ENOTSUP;
		goto err_exit;
	}

	/* Check if actions specified are compatible */
	if (attr->egress) {
		if (req_act & OTX2_FLOW_ACT_VLAN_STRIP) {
			errmsg = "VLAN pop action is not supported on Egress";
			errcode = ENOTSUP;
			goto err_exit;
		}

		if (req_act & OTX2_FLOW_ACT_DROP) {
			flow->npc_action = NIX_TX_ACTIONOP_DROP;
		} else if ((req_act & OTX2_FLOW_ACT_COUNT) ||
			   vlan_insert_action) {
			flow->npc_action = NIX_TX_ACTIONOP_UCAST_DEFAULT;
		} else {
			errmsg = "Unsupported action for egress";
			errcode = EINVAL;
			goto err_exit;
		}
		goto set_pf_func;
	}

	/* We have already verified the attr, this is ingress.
	 * - Exactly one terminating action is supported
	 * - Exactly one of MARK or FLAG is supported
	 * - If terminating action is DROP, only count is valid.
	 */
	sel_act = req_act & OTX2_FLOW_ACT_TERM;
	if ((sel_act & (sel_act - 1)) != 0) {
		errmsg = "Only one terminating action supported";
		errcode = EINVAL;
		goto err_exit;
	}

	if (req_act & OTX2_FLOW_ACT_DROP) {
		sel_act = req_act & ~OTX2_FLOW_ACT_COUNT;
		if ((sel_act & (sel_act - 1)) != 0) {
			errmsg = "Only COUNT action is supported "
				"with DROP ingress action";
			errcode = ENOTSUP;
			goto err_exit;
		}
	}

	if ((req_act & (OTX2_FLOW_ACT_FLAG | OTX2_FLOW_ACT_MARK))
	    == (OTX2_FLOW_ACT_FLAG | OTX2_FLOW_ACT_MARK)) {
		errmsg = "Only one of FLAG or MARK action is supported";
		errcode = ENOTSUP;
		goto err_exit;
	}

	if (vlan_insert_action) {
		errmsg = "VLAN push/Insert action is not supported on Ingress";
		errcode = ENOTSUP;
		goto err_exit;
	}

	if (req_act & OTX2_FLOW_ACT_VLAN_STRIP)
		npc->vtag_actions++;

	/* Only VLAN action is provided */
	if (req_act == OTX2_FLOW_ACT_VLAN_STRIP)
		flow->npc_action = NIX_RX_ACTIONOP_UCAST;
	/* Set NIX_RX_ACTIONOP */
	else if (req_act & (OTX2_FLOW_ACT_PF | OTX2_FLOW_ACT_VF)) {
		flow->npc_action = NIX_RX_ACTIONOP_UCAST;
		if (req_act & OTX2_FLOW_ACT_QUEUE)
			flow->npc_action |= (uint64_t)rq << 20;
	} else if (req_act & OTX2_FLOW_ACT_DROP) {
		flow->npc_action = NIX_RX_ACTIONOP_DROP;
	} else if (req_act & OTX2_FLOW_ACT_QUEUE) {
		flow->npc_action = NIX_RX_ACTIONOP_UCAST;
		flow->npc_action |= (uint64_t)rq << 20;
	} else if (req_act & OTX2_FLOW_ACT_RSS) {
		/* When user added a rule for rss, first we will add the
		 *rule in MCAM and then update the action, once if we have
		 *FLOW_KEY_ALG index. So, till we update the action with
		 *flow_key_alg index, set the action to drop.
		 */
		if (dev->data->dev_conf.rxmode.mq_mode == RTE_ETH_MQ_RX_RSS)
			flow->npc_action = NIX_RX_ACTIONOP_DROP;
		else
			flow->npc_action = NIX_RX_ACTIONOP_UCAST;
	} else if (req_act & OTX2_FLOW_ACT_SEC) {
		flow->npc_action = NIX_RX_ACTIONOP_UCAST_IPSEC;
		flow->npc_action |= (uint64_t)rq << 20;
	} else if (req_act & (OTX2_FLOW_ACT_FLAG | OTX2_FLOW_ACT_MARK)) {
		flow->npc_action = NIX_RX_ACTIONOP_UCAST;
	} else if (req_act & OTX2_FLOW_ACT_COUNT) {
		/* Keep OTX2_FLOW_ACT_COUNT always at the end
		 * This is default action, when user specify only
		 * COUNT ACTION
		 */
		flow->npc_action = NIX_RX_ACTIONOP_UCAST;
	} else {
		/* Should never reach here */
		errmsg = "Invalid action specified";
		errcode = EINVAL;
		goto err_exit;
	}

	if (mark)
		flow->npc_action |= (uint64_t)mark << 40;

	if (rte_atomic32_read(&npc->mark_actions) == 1) {
		hw->rx_offload_flags |=
			NIX_RX_OFFLOAD_MARK_UPDATE_F;
		otx2_eth_set_rx_function(dev);
	}

	if (npc->vtag_actions == 1) {
		hw->rx_offload_flags |= NIX_RX_OFFLOAD_VLAN_STRIP_F;
		otx2_eth_set_rx_function(dev);
	}

set_pf_func:
	/* Ideally AF must ensure that correct pf_func is set */
	if (attr->egress)
		flow->npc_action |= (uint64_t)pf_func << 48;
	else
		flow->npc_action |= (uint64_t)pf_func << 4;

	return 0;

err_exit:
	rte_flow_error_set(error, errcode,
			   RTE_FLOW_ERROR_TYPE_ACTION_NUM, NULL,
			   errmsg);
	return -rte_errno;
}
