[v2,4/4] common/cnxk: fix dual VLAN parsing issue

Message ID 20230111053814.979400-4-psatheesh@marvell.com (mailing list archive)
State Accepted, archived
Delegated to: Jerin Jacob
Headers
Series [v2,1/4] common/cnxk: support SPI to SA index |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/github-robot: build success github build: passed
ci/iol-aarch64-unit-testing success Testing PASS
ci/iol-x86_64-compile-testing success Testing PASS
ci/iol-aarch64-compile-testing success Testing PASS
ci/iol-abi-testing success Testing PASS
ci/iol-testing fail Testing issues
ci/iol-x86_64-unit-testing fail Testing issues
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-intel-Performance success Performance Testing PASS

Commit Message

Satheesh Paul Antonysamy Jan. 11, 2023, 5:38 a.m. UTC
  From: Satheesh Paul <psatheesh@marvell.com>

Flow parsing was not handling pattern matching on the
fields of inner VLAN even though MKEX profile could be
extracting inner VLAN fields. Code has been modified
to handle matching fields on two VLAN tags.

Fixes: c34ea71b878 ("common/cnxk: add NPC parsing API")
Cc: stable@dpdk.org

Signed-off-by: Satheesh Paul <psatheesh@marvell.com>
Reviewed-by: Kiran Kumar K <kirankumark@marvell.com>
---
 drivers/common/cnxk/roc_npc_parse.c | 240 +++++++++++++++++++++-------
 drivers/common/cnxk/roc_npc_priv.h  |  16 +-
 drivers/common/cnxk/roc_npc_utils.c |   2 +-
 3 files changed, 186 insertions(+), 72 deletions(-)
  

Comments

Jerin Jacob Jan. 11, 2023, 4:58 p.m. UTC | #1
On Wed, Jan 11, 2023 at 11:09 AM <psatheesh@marvell.com> wrote:
>
> From: Satheesh Paul <psatheesh@marvell.com>
>
> Flow parsing was not handling pattern matching on the
> fields of inner VLAN even though MKEX profile could be
> extracting inner VLAN fields. Code has been modified
> to handle matching fields on two VLAN tags.
>
> Fixes: c34ea71b878 ("common/cnxk: add NPC parsing API")
> Cc: stable@dpdk.org
>
> Signed-off-by: Satheesh Paul <psatheesh@marvell.com>
> Reviewed-by: Kiran Kumar K <kirankumark@marvell.com>


Series applied to dpdk-next-net-mrvl/for-next-net. Thanks

> ---
>  drivers/common/cnxk/roc_npc_parse.c | 240 +++++++++++++++++++++-------
>  drivers/common/cnxk/roc_npc_priv.h  |  16 +-
>  drivers/common/cnxk/roc_npc_utils.c |   2 +-
>  3 files changed, 186 insertions(+), 72 deletions(-)
>
> diff --git a/drivers/common/cnxk/roc_npc_parse.c b/drivers/common/cnxk/roc_npc_parse.c
> index c1af5f3087..d8f9271fa8 100644
> --- a/drivers/common/cnxk/roc_npc_parse.c
> +++ b/drivers/common/cnxk/roc_npc_parse.c
> @@ -239,10 +239,185 @@ npc_parse_la(struct npc_parse_state *pst)
>
>  #define NPC_MAX_SUPPORTED_VLANS 3
>
> +static int
> +npc_parse_vlan_count(const struct roc_npc_item_info *pattern,
> +                    const struct roc_npc_item_info **pattern_list,
> +                    const struct roc_npc_flow_item_vlan **vlan_items, int *vlan_count)
> +{
> +       *vlan_count = 0;
> +       while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) {
> +               if (*vlan_count > NPC_MAX_SUPPORTED_VLANS - 1)
> +                       return NPC_ERR_PATTERN_NOTSUP;
> +
> +               /* Don't support ranges */
> +               if (pattern->last != NULL)
> +                       return NPC_ERR_INVALID_RANGE;
> +
> +               /* If spec is NULL, both mask and last must be NULL, this
> +                * makes it to match ANY value (eq to mask = 0).
> +                * Setting either mask or last without spec is an error
> +                */
> +               if (pattern->spec == NULL) {
> +                       if (pattern->last != NULL && pattern->mask != NULL)
> +                               return NPC_ERR_INVALID_SPEC;
> +               }
> +
> +               pattern_list[*vlan_count] = pattern;
> +               vlan_items[*vlan_count] = pattern->spec;
> +               (*vlan_count)++;
> +
> +               pattern++;
> +               pattern = npc_parse_skip_void_and_any_items(pattern);
> +       }
> +
> +       return 0;
> +}
> +
> +static int
> +npc_parse_vlan_ltype_get(struct npc_parse_state *pst,
> +                        const struct roc_npc_flow_item_vlan **vlan_item, int vlan_count,
> +                        int *ltype, int *lflags)
> +{
> +       switch (vlan_count) {
> +       case 1:
> +               *ltype = NPC_LT_LB_CTAG;
> +               if (vlan_item[0] && vlan_item[0]->has_more_vlan)
> +                       *ltype = NPC_LT_LB_STAG_QINQ;
> +               break;
> +       case 2:
> +               if (vlan_item[1] && vlan_item[1]->has_more_vlan) {
> +                       if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] &
> +                             0x3ULL << NPC_LFLAG_LB_OFFSET))
> +                               return NPC_ERR_PATTERN_NOTSUP;
> +
> +                       /* This lflag value will match either one of
> +                        * NPC_F_LB_L_WITH_STAG_STAG,
> +                        * NPC_F_LB_L_WITH_QINQ_CTAG,
> +                        * NPC_F_LB_L_WITH_QINQ_QINQ and
> +                        * NPC_F_LB_L_WITH_ITAG (0b0100 to 0b0111). For
> +                        * NPC_F_LB_L_WITH_ITAG, ltype is NPC_LT_LB_ETAG
> +                        * hence will not match.
> +                        */
> +
> +                       *lflags = NPC_F_LB_L_WITH_QINQ_CTAG & NPC_F_LB_L_WITH_QINQ_QINQ &
> +                                 NPC_F_LB_L_WITH_STAG_STAG;
> +               }
> +               *ltype = NPC_LT_LB_STAG_QINQ;
> +               break;
> +       case 3:
> +               if (vlan_item[2] && vlan_item[2]->has_more_vlan)
> +                       return NPC_ERR_PATTERN_NOTSUP;
> +               if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] & 0x3ULL << NPC_LFLAG_LB_OFFSET))
> +                       return NPC_ERR_PATTERN_NOTSUP;
> +               *ltype = NPC_LT_LB_STAG_QINQ;
> +               *lflags = NPC_F_STAG_STAG_CTAG;
> +               break;
> +       default:
> +               return NPC_ERR_PATTERN_NOTSUP;
> +       }
> +
> +       return 0;
> +}
> +
> +static int
> +npc_update_vlan_parse_state(struct npc_parse_state *pst, const struct roc_npc_item_info *pattern,
> +                           int lid, int lt, uint8_t lflags, int vlan_count)
> +{
> +       uint8_t vlan_spec[NPC_MAX_SUPPORTED_VLANS * sizeof(struct roc_vlan_hdr)];
> +       uint8_t vlan_mask[NPC_MAX_SUPPORTED_VLANS * sizeof(struct roc_vlan_hdr)];
> +       int rc = 0, i, offset = NPC_TPID_LENGTH;
> +       struct npc_parse_item_info parse_info;
> +       char hw_mask[NPC_MAX_EXTRACT_HW_LEN];
> +
> +       memset(vlan_spec, 0, sizeof(struct roc_vlan_hdr) * NPC_MAX_SUPPORTED_VLANS);
> +       memset(vlan_mask, 0, sizeof(struct roc_vlan_hdr) * NPC_MAX_SUPPORTED_VLANS);
> +       memset(&parse_info, 0, sizeof(parse_info));
> +
> +       if (vlan_count > 2)
> +               vlan_count = 2;
> +
> +       for (i = 0; i < vlan_count; i++) {
> +               if (pattern[i].spec)
> +                       memcpy(vlan_spec + offset, pattern[i].spec, sizeof(struct roc_vlan_hdr));
> +               if (pattern[i].mask)
> +                       memcpy(vlan_mask + offset, pattern[i].mask, sizeof(struct roc_vlan_hdr));
> +
> +               offset += 4;
> +       }
> +
> +       parse_info.def_mask = NULL;
> +       parse_info.spec = vlan_spec;
> +       parse_info.mask = vlan_mask;
> +       parse_info.def_mask = NULL;
> +       parse_info.hw_hdr_len = 0;
> +
> +       lid = NPC_LID_LB;
> +       parse_info.hw_mask = hw_mask;
> +
> +       if (lt == NPC_LT_LB_CTAG)
> +               parse_info.len = sizeof(struct roc_vlan_hdr) + NPC_TPID_LENGTH;
> +
> +       if (lt == NPC_LT_LB_STAG_QINQ)
> +               parse_info.len = sizeof(struct roc_vlan_hdr) * 2 + NPC_TPID_LENGTH;
> +
> +       memset(hw_mask, 0, sizeof(hw_mask));
> +
> +       parse_info.hw_mask = &hw_mask;
> +       npc_get_hw_supp_mask(pst, &parse_info, lid, lt);
> +
> +       rc = npc_mask_is_supported(parse_info.mask, parse_info.hw_mask, parse_info.len);
> +       if (!rc)
> +               return NPC_ERR_INVALID_MASK;
> +
> +       /* Point pattern to last item consumed */
> +       pst->pattern = pattern;
> +       return npc_update_parse_state(pst, &parse_info, lid, lt, lflags);
> +}
> +
> +static int
> +npc_parse_lb_vlan(struct npc_parse_state *pst)
> +{
> +       const struct roc_npc_flow_item_vlan *vlan_items[NPC_MAX_SUPPORTED_VLANS];
> +       const struct roc_npc_item_info *pattern_list[NPC_MAX_SUPPORTED_VLANS];
> +       const struct roc_npc_item_info *last_pattern;
> +       int vlan_count = 0, rc = 0;
> +       int lid, lt, lflags;
> +
> +       lid = NPC_LID_LB;
> +       lflags = 0;
> +       last_pattern = pst->pattern;
> +
> +       rc = npc_parse_vlan_count(pst->pattern, pattern_list, vlan_items, &vlan_count);
> +       if (rc)
> +               return rc;
> +
> +       rc = npc_parse_vlan_ltype_get(pst, vlan_items, vlan_count, &lt, &lflags);
> +       if (rc)
> +               return rc;
> +
> +       if (vlan_count == 3) {
> +               if (pattern_list[2]->spec != NULL && pattern_list[2]->mask != NULL &&
> +                   pattern_list[2]->last != NULL)
> +                       return NPC_ERR_PATTERN_NOTSUP;
> +
> +               /* Matching can be done only for two tags. */
> +               vlan_count = 2;
> +               last_pattern++;
> +       }
> +
> +       rc = npc_update_vlan_parse_state(pst, pattern_list[0], lid, lt, lflags, vlan_count);
> +       if (rc)
> +               return rc;
> +
> +       if (vlan_count > 1)
> +               pst->pattern = last_pattern + vlan_count;
> +
> +       return 0;
> +}
> +
>  int
>  npc_parse_lb(struct npc_parse_state *pst)
>  {
> -       const struct roc_npc_flow_item_vlan *vlan_item[NPC_MAX_SUPPORTED_VLANS];
>         const struct roc_npc_item_info *pattern = pst->pattern;
>         const struct roc_npc_item_info *last_pattern;
>         const struct roc_npc_flow_item_raw *raw_spec;
> @@ -251,7 +426,6 @@ npc_parse_lb(struct npc_parse_state *pst)
>         char hw_mask[NPC_MAX_EXTRACT_HW_LEN];
>         struct npc_parse_item_info info;
>         int lid, lt, lflags, len = 0;
> -       int nr_vlans = 0;
>         int rc;
>
>         info.def_mask = NULL;
> @@ -268,68 +442,10 @@ npc_parse_lb(struct npc_parse_state *pst)
>                 /* 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.
> +                * supported on first two tags.
>                  */
> -               info.hw_mask = NULL;
> -               info.len = sizeof(vlan_item[0]->hdr);
> -
> -               pattern = pst->pattern;
> -               while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) {
> -                       if (nr_vlans > NPC_MAX_SUPPORTED_VLANS - 1)
> -                               return NPC_ERR_PATTERN_NOTSUP;
> -
> -                       vlan_item[nr_vlans] = pattern->spec;
> -                       nr_vlans++;
>
> -                       /* Basic validation of Second/Third vlan item */
> -                       if (nr_vlans > 1) {
> -                               rc = npc_parse_item_basic(pattern, &info);
> -                               if (rc != 0)
> -                                       return rc;
> -                       }
> -                       last_pattern = pattern;
> -                       pattern++;
> -                       pattern = npc_parse_skip_void_and_any_items(pattern);
> -               }
> -
> -               switch (nr_vlans) {
> -               case 1:
> -                       lt = NPC_LT_LB_CTAG;
> -                       if (vlan_item[0] && vlan_item[0]->has_more_vlan)
> -                               lt = NPC_LT_LB_STAG_QINQ;
> -                       break;
> -               case 2:
> -                       if (vlan_item[1] && vlan_item[1]->has_more_vlan) {
> -                               if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] &
> -                                     0x3ULL << NPC_LFLAG_LB_OFFSET))
> -                                       return NPC_ERR_PATTERN_NOTSUP;
> -
> -                               /* This lflag value will match either one of
> -                                * NPC_F_LB_L_WITH_STAG_STAG,
> -                                * NPC_F_LB_L_WITH_QINQ_CTAG,
> -                                * NPC_F_LB_L_WITH_QINQ_QINQ and
> -                                * NPC_F_LB_L_WITH_ITAG (0b0100 to 0b0111). For
> -                                * NPC_F_LB_L_WITH_ITAG, ltype is NPC_LT_LB_ETAG
> -                                * hence will not match.
> -                                */
> -
> -                               lflags = NPC_F_LB_L_WITH_QINQ_CTAG &
> -                                        NPC_F_LB_L_WITH_QINQ_QINQ &
> -                                        NPC_F_LB_L_WITH_STAG_STAG;
> -                       } else {
> -                               lflags = NPC_F_LB_L_WITH_CTAG;
> -                       }
> -                       lt = NPC_LT_LB_STAG_QINQ;
> -                       break;
> -               case 3:
> -                       if (vlan_item[2] && vlan_item[2]->has_more_vlan)
> -                               return NPC_ERR_PATTERN_NOTSUP;
> -                       lt = NPC_LT_LB_STAG_QINQ;
> -                       lflags = NPC_F_STAG_STAG_CTAG;
> -                       break;
> -               default:
> -                       return NPC_ERR_PATTERN_NOTSUP;
> -               }
> +               return npc_parse_lb_vlan(pst);
>         } else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_E_TAG) {
>                 /* we can support ETAG and match a subsequent CTAG
>                  * without any matching support.
> diff --git a/drivers/common/cnxk/roc_npc_priv.h b/drivers/common/cnxk/roc_npc_priv.h
> index fe19329a7f..2a7d3137fb 100644
> --- a/drivers/common/cnxk/roc_npc_priv.h
> +++ b/drivers/common/cnxk/roc_npc_priv.h
> @@ -418,17 +418,15 @@ int npc_mcam_alloc_entry(struct npc *npc, struct roc_npc_flow *mcam,
>  int npc_mcam_alloc_entries(struct mbox *mbox, int ref_mcam, int *alloc_entry, int req_count,
>                            int prio, int *resp_count, bool is_conti);
>
> -int npc_mcam_ena_dis_entry(struct npc *npc, struct roc_npc_flow *mcam,
> -                          bool enable);
> +int npc_mcam_ena_dis_entry(struct npc *npc, struct roc_npc_flow *mcam, bool enable);
>  int npc_mcam_write_entry(struct mbox *mbox, struct roc_npc_flow *mcam);
>  int npc_flow_enable_all_entries(struct npc *npc, bool enable);
> -int npc_update_parse_state(struct npc_parse_state *pst,
> -                          struct npc_parse_item_info *info, int lid, int lt,
> -                          uint8_t flags);
> -void npc_get_hw_supp_mask(struct npc_parse_state *pst,
> -                         struct npc_parse_item_info *info, int lid, int lt);
> -int npc_parse_item_basic(const struct roc_npc_item_info *item,
> -                        struct npc_parse_item_info *info);
> +int npc_update_parse_state(struct npc_parse_state *pst, struct npc_parse_item_info *info, int lid,
> +                          int lt, uint8_t flags);
> +void npc_get_hw_supp_mask(struct npc_parse_state *pst, struct npc_parse_item_info *info, int lid,
> +                         int lt);
> +int npc_mask_is_supported(const char *mask, const char *hw_mask, int len);
> +int npc_parse_item_basic(const struct roc_npc_item_info *item, struct npc_parse_item_info *info);
>  int npc_parse_meta_items(struct npc_parse_state *pst);
>  int npc_parse_mark_item(struct npc_parse_state *pst);
>  int npc_parse_pre_l2(struct npc_parse_state *pst);
> diff --git a/drivers/common/cnxk/roc_npc_utils.c b/drivers/common/cnxk/roc_npc_utils.c
> index 8bdabc116d..fda3073cba 100644
> --- a/drivers/common/cnxk/roc_npc_utils.c
> +++ b/drivers/common/cnxk/roc_npc_utils.c
> @@ -88,7 +88,7 @@ npc_get_hw_supp_mask(struct npc_parse_state *pst,
>         }
>  }
>
> -static inline int
> +inline int
>  npc_mask_is_supported(const char *mask, const char *hw_mask, int len)
>  {
>         /*
> --
> 2.35.3
>
  

Patch

diff --git a/drivers/common/cnxk/roc_npc_parse.c b/drivers/common/cnxk/roc_npc_parse.c
index c1af5f3087..d8f9271fa8 100644
--- a/drivers/common/cnxk/roc_npc_parse.c
+++ b/drivers/common/cnxk/roc_npc_parse.c
@@ -239,10 +239,185 @@  npc_parse_la(struct npc_parse_state *pst)
 
 #define NPC_MAX_SUPPORTED_VLANS 3
 
+static int
+npc_parse_vlan_count(const struct roc_npc_item_info *pattern,
+		     const struct roc_npc_item_info **pattern_list,
+		     const struct roc_npc_flow_item_vlan **vlan_items, int *vlan_count)
+{
+	*vlan_count = 0;
+	while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) {
+		if (*vlan_count > NPC_MAX_SUPPORTED_VLANS - 1)
+			return NPC_ERR_PATTERN_NOTSUP;
+
+		/* Don't support ranges */
+		if (pattern->last != NULL)
+			return NPC_ERR_INVALID_RANGE;
+
+		/* If spec is NULL, both mask and last must be NULL, this
+		 * makes it to match ANY value (eq to mask = 0).
+		 * Setting either mask or last without spec is an error
+		 */
+		if (pattern->spec == NULL) {
+			if (pattern->last != NULL && pattern->mask != NULL)
+				return NPC_ERR_INVALID_SPEC;
+		}
+
+		pattern_list[*vlan_count] = pattern;
+		vlan_items[*vlan_count] = pattern->spec;
+		(*vlan_count)++;
+
+		pattern++;
+		pattern = npc_parse_skip_void_and_any_items(pattern);
+	}
+
+	return 0;
+}
+
+static int
+npc_parse_vlan_ltype_get(struct npc_parse_state *pst,
+			 const struct roc_npc_flow_item_vlan **vlan_item, int vlan_count,
+			 int *ltype, int *lflags)
+{
+	switch (vlan_count) {
+	case 1:
+		*ltype = NPC_LT_LB_CTAG;
+		if (vlan_item[0] && vlan_item[0]->has_more_vlan)
+			*ltype = NPC_LT_LB_STAG_QINQ;
+		break;
+	case 2:
+		if (vlan_item[1] && vlan_item[1]->has_more_vlan) {
+			if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] &
+			      0x3ULL << NPC_LFLAG_LB_OFFSET))
+				return NPC_ERR_PATTERN_NOTSUP;
+
+			/* This lflag value will match either one of
+			 * NPC_F_LB_L_WITH_STAG_STAG,
+			 * NPC_F_LB_L_WITH_QINQ_CTAG,
+			 * NPC_F_LB_L_WITH_QINQ_QINQ and
+			 * NPC_F_LB_L_WITH_ITAG (0b0100 to 0b0111). For
+			 * NPC_F_LB_L_WITH_ITAG, ltype is NPC_LT_LB_ETAG
+			 * hence will not match.
+			 */
+
+			*lflags = NPC_F_LB_L_WITH_QINQ_CTAG & NPC_F_LB_L_WITH_QINQ_QINQ &
+				  NPC_F_LB_L_WITH_STAG_STAG;
+		}
+		*ltype = NPC_LT_LB_STAG_QINQ;
+		break;
+	case 3:
+		if (vlan_item[2] && vlan_item[2]->has_more_vlan)
+			return NPC_ERR_PATTERN_NOTSUP;
+		if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] & 0x3ULL << NPC_LFLAG_LB_OFFSET))
+			return NPC_ERR_PATTERN_NOTSUP;
+		*ltype = NPC_LT_LB_STAG_QINQ;
+		*lflags = NPC_F_STAG_STAG_CTAG;
+		break;
+	default:
+		return NPC_ERR_PATTERN_NOTSUP;
+	}
+
+	return 0;
+}
+
+static int
+npc_update_vlan_parse_state(struct npc_parse_state *pst, const struct roc_npc_item_info *pattern,
+			    int lid, int lt, uint8_t lflags, int vlan_count)
+{
+	uint8_t vlan_spec[NPC_MAX_SUPPORTED_VLANS * sizeof(struct roc_vlan_hdr)];
+	uint8_t vlan_mask[NPC_MAX_SUPPORTED_VLANS * sizeof(struct roc_vlan_hdr)];
+	int rc = 0, i, offset = NPC_TPID_LENGTH;
+	struct npc_parse_item_info parse_info;
+	char hw_mask[NPC_MAX_EXTRACT_HW_LEN];
+
+	memset(vlan_spec, 0, sizeof(struct roc_vlan_hdr) * NPC_MAX_SUPPORTED_VLANS);
+	memset(vlan_mask, 0, sizeof(struct roc_vlan_hdr) * NPC_MAX_SUPPORTED_VLANS);
+	memset(&parse_info, 0, sizeof(parse_info));
+
+	if (vlan_count > 2)
+		vlan_count = 2;
+
+	for (i = 0; i < vlan_count; i++) {
+		if (pattern[i].spec)
+			memcpy(vlan_spec + offset, pattern[i].spec, sizeof(struct roc_vlan_hdr));
+		if (pattern[i].mask)
+			memcpy(vlan_mask + offset, pattern[i].mask, sizeof(struct roc_vlan_hdr));
+
+		offset += 4;
+	}
+
+	parse_info.def_mask = NULL;
+	parse_info.spec = vlan_spec;
+	parse_info.mask = vlan_mask;
+	parse_info.def_mask = NULL;
+	parse_info.hw_hdr_len = 0;
+
+	lid = NPC_LID_LB;
+	parse_info.hw_mask = hw_mask;
+
+	if (lt == NPC_LT_LB_CTAG)
+		parse_info.len = sizeof(struct roc_vlan_hdr) + NPC_TPID_LENGTH;
+
+	if (lt == NPC_LT_LB_STAG_QINQ)
+		parse_info.len = sizeof(struct roc_vlan_hdr) * 2 + NPC_TPID_LENGTH;
+
+	memset(hw_mask, 0, sizeof(hw_mask));
+
+	parse_info.hw_mask = &hw_mask;
+	npc_get_hw_supp_mask(pst, &parse_info, lid, lt);
+
+	rc = npc_mask_is_supported(parse_info.mask, parse_info.hw_mask, parse_info.len);
+	if (!rc)
+		return NPC_ERR_INVALID_MASK;
+
+	/* Point pattern to last item consumed */
+	pst->pattern = pattern;
+	return npc_update_parse_state(pst, &parse_info, lid, lt, lflags);
+}
+
+static int
+npc_parse_lb_vlan(struct npc_parse_state *pst)
+{
+	const struct roc_npc_flow_item_vlan *vlan_items[NPC_MAX_SUPPORTED_VLANS];
+	const struct roc_npc_item_info *pattern_list[NPC_MAX_SUPPORTED_VLANS];
+	const struct roc_npc_item_info *last_pattern;
+	int vlan_count = 0, rc = 0;
+	int lid, lt, lflags;
+
+	lid = NPC_LID_LB;
+	lflags = 0;
+	last_pattern = pst->pattern;
+
+	rc = npc_parse_vlan_count(pst->pattern, pattern_list, vlan_items, &vlan_count);
+	if (rc)
+		return rc;
+
+	rc = npc_parse_vlan_ltype_get(pst, vlan_items, vlan_count, &lt, &lflags);
+	if (rc)
+		return rc;
+
+	if (vlan_count == 3) {
+		if (pattern_list[2]->spec != NULL && pattern_list[2]->mask != NULL &&
+		    pattern_list[2]->last != NULL)
+			return NPC_ERR_PATTERN_NOTSUP;
+
+		/* Matching can be done only for two tags. */
+		vlan_count = 2;
+		last_pattern++;
+	}
+
+	rc = npc_update_vlan_parse_state(pst, pattern_list[0], lid, lt, lflags, vlan_count);
+	if (rc)
+		return rc;
+
+	if (vlan_count > 1)
+		pst->pattern = last_pattern + vlan_count;
+
+	return 0;
+}
+
 int
 npc_parse_lb(struct npc_parse_state *pst)
 {
-	const struct roc_npc_flow_item_vlan *vlan_item[NPC_MAX_SUPPORTED_VLANS];
 	const struct roc_npc_item_info *pattern = pst->pattern;
 	const struct roc_npc_item_info *last_pattern;
 	const struct roc_npc_flow_item_raw *raw_spec;
@@ -251,7 +426,6 @@  npc_parse_lb(struct npc_parse_state *pst)
 	char hw_mask[NPC_MAX_EXTRACT_HW_LEN];
 	struct npc_parse_item_info info;
 	int lid, lt, lflags, len = 0;
-	int nr_vlans = 0;
 	int rc;
 
 	info.def_mask = NULL;
@@ -268,68 +442,10 @@  npc_parse_lb(struct npc_parse_state *pst)
 		/* 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.
+		 * supported on first two tags.
 		 */
-		info.hw_mask = NULL;
-		info.len = sizeof(vlan_item[0]->hdr);
-
-		pattern = pst->pattern;
-		while (pattern->type == ROC_NPC_ITEM_TYPE_VLAN) {
-			if (nr_vlans > NPC_MAX_SUPPORTED_VLANS - 1)
-				return NPC_ERR_PATTERN_NOTSUP;
-
-			vlan_item[nr_vlans] = pattern->spec;
-			nr_vlans++;
 
-			/* Basic validation of Second/Third vlan item */
-			if (nr_vlans > 1) {
-				rc = npc_parse_item_basic(pattern, &info);
-				if (rc != 0)
-					return rc;
-			}
-			last_pattern = pattern;
-			pattern++;
-			pattern = npc_parse_skip_void_and_any_items(pattern);
-		}
-
-		switch (nr_vlans) {
-		case 1:
-			lt = NPC_LT_LB_CTAG;
-			if (vlan_item[0] && vlan_item[0]->has_more_vlan)
-				lt = NPC_LT_LB_STAG_QINQ;
-			break;
-		case 2:
-			if (vlan_item[1] && vlan_item[1]->has_more_vlan) {
-				if (!(pst->npc->keyx_supp_nmask[pst->nix_intf] &
-				      0x3ULL << NPC_LFLAG_LB_OFFSET))
-					return NPC_ERR_PATTERN_NOTSUP;
-
-				/* This lflag value will match either one of
-				 * NPC_F_LB_L_WITH_STAG_STAG,
-				 * NPC_F_LB_L_WITH_QINQ_CTAG,
-				 * NPC_F_LB_L_WITH_QINQ_QINQ and
-				 * NPC_F_LB_L_WITH_ITAG (0b0100 to 0b0111). For
-				 * NPC_F_LB_L_WITH_ITAG, ltype is NPC_LT_LB_ETAG
-				 * hence will not match.
-				 */
-
-				lflags = NPC_F_LB_L_WITH_QINQ_CTAG &
-					 NPC_F_LB_L_WITH_QINQ_QINQ &
-					 NPC_F_LB_L_WITH_STAG_STAG;
-			} else {
-				lflags = NPC_F_LB_L_WITH_CTAG;
-			}
-			lt = NPC_LT_LB_STAG_QINQ;
-			break;
-		case 3:
-			if (vlan_item[2] && vlan_item[2]->has_more_vlan)
-				return NPC_ERR_PATTERN_NOTSUP;
-			lt = NPC_LT_LB_STAG_QINQ;
-			lflags = NPC_F_STAG_STAG_CTAG;
-			break;
-		default:
-			return NPC_ERR_PATTERN_NOTSUP;
-		}
+		return npc_parse_lb_vlan(pst);
 	} else if (pst->pattern->type == ROC_NPC_ITEM_TYPE_E_TAG) {
 		/* we can support ETAG and match a subsequent CTAG
 		 * without any matching support.
diff --git a/drivers/common/cnxk/roc_npc_priv.h b/drivers/common/cnxk/roc_npc_priv.h
index fe19329a7f..2a7d3137fb 100644
--- a/drivers/common/cnxk/roc_npc_priv.h
+++ b/drivers/common/cnxk/roc_npc_priv.h
@@ -418,17 +418,15 @@  int npc_mcam_alloc_entry(struct npc *npc, struct roc_npc_flow *mcam,
 int npc_mcam_alloc_entries(struct mbox *mbox, int ref_mcam, int *alloc_entry, int req_count,
 			   int prio, int *resp_count, bool is_conti);
 
-int npc_mcam_ena_dis_entry(struct npc *npc, struct roc_npc_flow *mcam,
-			   bool enable);
+int npc_mcam_ena_dis_entry(struct npc *npc, struct roc_npc_flow *mcam, bool enable);
 int npc_mcam_write_entry(struct mbox *mbox, struct roc_npc_flow *mcam);
 int npc_flow_enable_all_entries(struct npc *npc, bool enable);
-int npc_update_parse_state(struct npc_parse_state *pst,
-			   struct npc_parse_item_info *info, int lid, int lt,
-			   uint8_t flags);
-void npc_get_hw_supp_mask(struct npc_parse_state *pst,
-			  struct npc_parse_item_info *info, int lid, int lt);
-int npc_parse_item_basic(const struct roc_npc_item_info *item,
-			 struct npc_parse_item_info *info);
+int npc_update_parse_state(struct npc_parse_state *pst, struct npc_parse_item_info *info, int lid,
+			   int lt, uint8_t flags);
+void npc_get_hw_supp_mask(struct npc_parse_state *pst, struct npc_parse_item_info *info, int lid,
+			  int lt);
+int npc_mask_is_supported(const char *mask, const char *hw_mask, int len);
+int npc_parse_item_basic(const struct roc_npc_item_info *item, struct npc_parse_item_info *info);
 int npc_parse_meta_items(struct npc_parse_state *pst);
 int npc_parse_mark_item(struct npc_parse_state *pst);
 int npc_parse_pre_l2(struct npc_parse_state *pst);
diff --git a/drivers/common/cnxk/roc_npc_utils.c b/drivers/common/cnxk/roc_npc_utils.c
index 8bdabc116d..fda3073cba 100644
--- a/drivers/common/cnxk/roc_npc_utils.c
+++ b/drivers/common/cnxk/roc_npc_utils.c
@@ -88,7 +88,7 @@  npc_get_hw_supp_mask(struct npc_parse_state *pst,
 	}
 }
 
-static inline int
+inline int
 npc_mask_is_supported(const char *mask, const char *hw_mask, int len)
 {
 	/*