[v2,1/2] examples/l3fwd-regex: add regex based l3fwd

Message ID 20200909120853.11715-2-guyk@marvell.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series Add example l3fwd-regex |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Guy Kaneti Sept. 9, 2020, 12:08 p.m. UTC
  From: Guy Kaneti <guyk@marvell.com>

Add regex based l3fwd application
inline with normal l3fwd. only LPM is supported.

Signed-off-by: Guy Kaneti <guyk@marvell.com>
---
 MAINTAINERS                        |    2 +
 examples/l3fwd-regex/l3fwd.h       |  207 ++++++
 examples/l3fwd-regex/l3fwd_lpm.c   |  484 ++++++++++++
 examples/l3fwd-regex/l3fwd_regex.c |  487 ++++++++++++
 examples/l3fwd-regex/l3fwd_regex.h |   38 +
 examples/l3fwd-regex/main.c        | 1117 ++++++++++++++++++++++++++++
 examples/l3fwd-regex/meson.build   |   10 +
 examples/meson.build               |    2 +-
 8 files changed, 2346 insertions(+), 1 deletion(-)
 create mode 100644 examples/l3fwd-regex/l3fwd.h
 create mode 100644 examples/l3fwd-regex/l3fwd_lpm.c
 create mode 100644 examples/l3fwd-regex/l3fwd_regex.c
 create mode 100644 examples/l3fwd-regex/l3fwd_regex.h
 create mode 100644 examples/l3fwd-regex/main.c
 create mode 100644 examples/l3fwd-regex/meson.build
  

Comments

Ori Kam Oct. 7, 2020, 1:44 p.m. UTC | #1
Hi Guy,

Sorry for late reply.


> -----Original Message-----
> From: guyk@marvell.com <guyk@marvell.com>
> Sent: Wednesday, September 9, 2020 3:09 PM
> Subject: [PATCH v2 1/2] examples/l3fwd-regex: add regex based l3fwd
> 
> From: Guy Kaneti <guyk@marvell.com>
> 
> Add regex based l3fwd application
> inline with normal l3fwd. only LPM is supported.
> 
> Signed-off-by: Guy Kaneti <guyk@marvell.com>
> ---

Acked-by: Ori Kam <orika@nvidia.com>
Thanks,
Ori
  
Thomas Monjalon Oct. 14, 2020, 8:50 a.m. UTC | #2
(repeating question asked on v1 by mistake)

09/09/2020 14:08, guyk@marvell.com:
> Add regex based l3fwd application
> inline with normal l3fwd. only LPM is supported.

The trend is to reduce the number of example applications.
Do you think regex could be an option in the original l3fwd?
  

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 3b16d7a4b..e30b1ad56 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -433,6 +433,8 @@  F: lib/librte_regexdev/
 F: app/test-regex/
 F: doc/guides/prog_guide/regexdev.rst
 F: doc/guides/regexdevs/features/default.ini
+M: Guy Kaneti <guyk@marvell.com>
+F: examples/l3fwd-regex/
 
 Eventdev API
 M: Jerin Jacob <jerinj@marvell.com>
diff --git a/examples/l3fwd-regex/l3fwd.h b/examples/l3fwd-regex/l3fwd.h
new file mode 100644
index 000000000..3fb3647bb
--- /dev/null
+++ b/examples/l3fwd-regex/l3fwd.h
@@ -0,0 +1,207 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2016 Intel Corporation
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __L3_FWD_H__
+#define __L3_FWD_H__
+
+#include <rte_ethdev.h>
+#include <rte_vect.h>
+
+#define DO_RFC_1812_CHECKS
+
+#define RTE_LOGTYPE_L3FWD RTE_LOGTYPE_USER1
+
+/*
+ * Configurable number of RX/TX ring descriptors
+ */
+#define RTE_TEST_RX_DESC_DEFAULT 1024
+#define RTE_TEST_TX_DESC_DEFAULT 1024
+
+#define MAX_PKT_BURST     32
+#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
+
+#define MEMPOOL_CACHE_SIZE 256
+#define MAX_RX_QUEUE_PER_LCORE 16
+
+/*
+ * Try to avoid TX buffering if we have at least MAX_TX_BURST packets to send.
+ */
+#define	MAX_TX_BURST	  (MAX_PKT_BURST / 2)
+
+#define NB_SOCKETS        8
+
+/* Configure how many packets ahead to prefetch, when reading packets */
+#define PREFETCH_OFFSET	  3
+
+/* Used to mark destination port as 'invalid'. */
+#define	BAD_PORT ((uint16_t)-1)
+
+#define FWDSTEP	4
+
+/* replace first 12B of the ethernet header. */
+#define	MASK_ETH 0x3f
+
+struct mbuf_table {
+	uint16_t len;
+	struct rte_mbuf *m_table[MAX_PKT_BURST];
+};
+
+struct lcore_rx_queue {
+	uint16_t port_id;
+	uint8_t queue_id;
+} __rte_cache_aligned;
+
+struct lcore_conf {
+	uint16_t n_rx_queue;
+	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+	uint8_t regex_dev_id;
+	uint16_t regex_qp_id;
+	uint16_t n_tx_port;
+	uint16_t tx_port_id[RTE_MAX_ETHPORTS];
+	uint16_t tx_queue_id[RTE_MAX_ETHPORTS];
+	struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS];
+	struct rte_mbuf **pkts_burst;
+	void *ipv4_lookup_struct;
+	void *ipv6_lookup_struct;
+} __rte_cache_aligned;
+
+extern volatile bool force_quit;
+
+/* ethernet addresses of ports */
+extern uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+extern struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
+
+/* mask of enabled ports */
+extern uint32_t enabled_port_mask;
+
+/* Used only in exact match mode. */
+extern int ipv6; /**< ipv6 is false by default. */
+extern uint32_t hash_entry_number;
+
+extern xmm_t val_eth[RTE_MAX_ETHPORTS];
+
+extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+
+/* Send burst of packets on an output interface */
+static inline int
+send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port)
+{
+	struct rte_mbuf **m_table;
+	int ret;
+	uint16_t queueid;
+
+	queueid = qconf->tx_queue_id[port];
+	m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table;
+
+	ret = rte_eth_tx_burst(port, queueid, m_table, n);
+	if (unlikely(ret < n)) {
+		do {
+			rte_pktmbuf_free(m_table[ret]);
+		} while (++ret < n);
+	}
+
+	return 0;
+}
+
+/* Enqueue a single packet, and send burst if queue is filled */
+static inline int
+send_single_packet(struct lcore_conf *qconf,
+		   struct rte_mbuf *m, uint16_t port)
+{
+	uint16_t len;
+
+	len = qconf->tx_mbufs[port].len;
+	qconf->tx_mbufs[port].m_table[len] = m;
+	len++;
+
+	/* enough pkts to be sent */
+	if (unlikely(len == MAX_PKT_BURST)) {
+		send_burst(qconf, MAX_PKT_BURST, port);
+		len = 0;
+	}
+
+	qconf->tx_mbufs[port].len = len;
+	return 0;
+}
+
+#ifdef DO_RFC_1812_CHECKS
+static inline int
+is_valid_ipv4_pkt(struct rte_ipv4_hdr *pkt, uint32_t link_len)
+{
+	/* From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2 */
+	/*
+	 * 1. The packet length reported by the Link Layer must be large
+	 * enough to hold the minimum length legal IP datagram (20 bytes).
+	 */
+	if (link_len < sizeof(struct rte_ipv4_hdr))
+		return -1;
+
+	/* 2. The IP checksum must be correct. */
+	/* this is checked in H/W */
+
+	/*
+	 * 3. The IP version number must be 4. If the version number is not 4
+	 * then the packet may be another version of IP, such as IPng or
+	 * ST-II.
+	 */
+	if (((pkt->version_ihl) >> 4) != 4)
+		return -3;
+	/*
+	 * 4. The IP header length field must be large enough to hold the
+	 * minimum length legal IP datagram (20 bytes = 5 words).
+	 */
+	if ((pkt->version_ihl & 0xf) < 5)
+		return -4;
+
+	/*
+	 * 5. The IP total length field must be large enough to hold the IP
+	 * datagram header, whose length is specified in the IP header length
+	 * field.
+	 */
+	if (rte_cpu_to_be_16(pkt->total_length) < sizeof(struct rte_ipv4_hdr))
+		return -5;
+
+	return 0;
+}
+#endif /* DO_RFC_1812_CHECKS */
+
+int
+init_mem(uint16_t portid, unsigned int nb_mbuf);
+
+/* Function pointers for LPM functionality. */
+void
+setup_lpm(const int socketid);
+
+void
+setup_hash(const int socketid);
+
+int
+lpm_check_ptype(int portid);
+
+uint16_t
+lpm_cb_parse_ptype(uint16_t port, uint16_t queue, struct rte_mbuf *pkts[],
+		   uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
+
+int
+lpm_main_loop(__rte_unused void *dummy);
+
+int
+lpm_event_main_loop_tx_d(__rte_unused void *dummy);
+int
+lpm_event_main_loop_tx_d_burst(__rte_unused void *dummy);
+int
+lpm_event_main_loop_tx_q(__rte_unused void *dummy);
+int
+lpm_event_main_loop_tx_q_burst(__rte_unused void *dummy);
+
+
+/* Return ipv4/ipv6 fwd lookup struct for LPM*/
+void *
+lpm_get_ipv4_l3fwd_lookup_struct(const int socketid);
+
+void *
+lpm_get_ipv6_l3fwd_lookup_struct(const int socketid);
+
+#endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd-regex/l3fwd_lpm.c b/examples/l3fwd-regex/l3fwd_lpm.c
new file mode 100644
index 000000000..5af69403c
--- /dev/null
+++ b/examples/l3fwd-regex/l3fwd_lpm.c
@@ -0,0 +1,484 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2016 Intel Corporation
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <rte_debug.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_lpm.h>
+#include <rte_lpm6.h>
+
+#include "l3fwd.h"
+#include "l3fwd_regex.h"
+
+struct ipv4_l3fwd_lpm_route {
+	uint32_t ip;
+	uint8_t  depth;
+	uint8_t  if_out;
+};
+
+struct ipv6_l3fwd_lpm_route {
+	uint8_t ip[16];
+	uint8_t  depth;
+	uint8_t  if_out;
+};
+
+/* 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735). */
+static const struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
+	{RTE_IPV4(198, 18, 0, 0), 24, 0},
+	{RTE_IPV4(198, 18, 1, 0), 24, 1},
+	{RTE_IPV4(198, 18, 2, 0), 24, 2},
+	{RTE_IPV4(198, 18, 3, 0), 24, 3},
+	{RTE_IPV4(198, 18, 4, 0), 24, 4},
+	{RTE_IPV4(198, 18, 5, 0), 24, 5},
+	{RTE_IPV4(198, 18, 6, 0), 24, 6},
+	{RTE_IPV4(198, 18, 7, 0), 24, 7},
+};
+
+/* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180) */
+static const struct ipv6_l3fwd_lpm_route ipv6_l3fwd_lpm_route_array[] = {
+	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 48, 0},
+	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 48, 1},
+	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, 48, 2},
+	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0}, 48, 3},
+	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, 48, 4},
+	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0}, 48, 5},
+	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0}, 48, 6},
+	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0}, 48, 7},
+};
+
+#define IPV4_L3FWD_LPM_MAX_RULES         1024
+#define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
+#define IPV6_L3FWD_LPM_MAX_RULES         1024
+#define IPV6_L3FWD_LPM_NUMBER_TBL8S (1 << 16)
+
+static struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+static struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+
+static inline uint16_t
+lpm_get_ipv4_dst_port(const struct rte_ipv4_hdr *ipv4_hdr,
+		      uint16_t portid,
+		      struct rte_lpm *ipv4_l3fwd_lookup_struct)
+{
+	uint32_t dst_ip = rte_be_to_cpu_32(ipv4_hdr->dst_addr);
+	uint32_t next_hop;
+
+	if (rte_lpm_lookup(ipv4_l3fwd_lookup_struct, dst_ip, &next_hop) == 0)
+		return next_hop;
+	else
+		return portid;
+}
+
+static inline uint16_t
+lpm_get_ipv6_dst_port(const struct rte_ipv6_hdr *ipv6_hdr,
+		      uint16_t portid,
+		      struct rte_lpm6 *ipv6_l3fwd_lookup_struct)
+{
+	const uint8_t *dst_ip = ipv6_hdr->dst_addr;
+	uint32_t next_hop;
+
+	if (rte_lpm6_lookup(ipv6_l3fwd_lookup_struct, dst_ip, &next_hop) == 0)
+		return next_hop;
+	else
+		return portid;
+}
+
+static __rte_always_inline void
+l3fwd_lpm_simple_forward(struct rte_mbuf *m, uint16_t portid,
+		struct lcore_conf *qconf)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	uint16_t dst_port;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
+
+	if (RTE_ETH_IS_IPV4_HDR(m->packet_type)) {
+		/* Handle IPv4 headers.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+
+#ifdef DO_RFC_1812_CHECKS
+		/* Check to make sure the packet is valid (RFC1812) */
+		if (is_valid_ipv4_pkt(ipv4_hdr, m->pkt_len) < 0) {
+			rte_pktmbuf_free(m);
+			return;
+		}
+#endif
+		 dst_port = lpm_get_ipv4_dst_port(ipv4_hdr, portid,
+						qconf->ipv4_lookup_struct);
+
+		if (dst_port >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port) == 0)
+			dst_port = portid;
+
+#ifdef DO_RFC_1812_CHECKS
+		/* Update time to live and header checksum */
+		--(ipv4_hdr->time_to_live);
+		++(ipv4_hdr->hdr_checksum);
+#endif
+		/* dst addr */
+		*(uint64_t *)&eth_hdr->d_addr = dest_eth_addr[dst_port];
+
+		/* src addr */
+		rte_ether_addr_copy(&ports_eth_addr[dst_port],
+				&eth_hdr->s_addr);
+
+		send_single_packet(qconf, m, dst_port);
+	} else if (RTE_ETH_IS_IPV6_HDR(m->packet_type)) {
+		/* Handle IPv6 headers.*/
+		struct rte_ipv6_hdr *ipv6_hdr;
+
+		ipv6_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv6_hdr *,
+						sizeof(struct rte_ether_hdr));
+
+		dst_port = lpm_get_ipv6_dst_port(ipv6_hdr, portid,
+					qconf->ipv6_lookup_struct);
+
+		if (dst_port >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port) == 0)
+			dst_port = portid;
+
+		/* dst addr */
+		*(uint64_t *)&eth_hdr->d_addr = dest_eth_addr[dst_port];
+
+		/* src addr */
+		rte_ether_addr_copy(&ports_eth_addr[dst_port],
+				&eth_hdr->s_addr);
+
+		send_single_packet(qconf, m, dst_port);
+	} else {
+		/* Free the mbuf that contains non-IPV4/IPV6 packet */
+		rte_pktmbuf_free(m);
+	}
+}
+
+static inline void
+l3fwd_lpm_no_opt_send_packets(int nb_rx, struct rte_mbuf **pkts_burst,
+				uint16_t portid, struct lcore_conf *qconf)
+{
+	int32_t j;
+
+	/* Prefetch first packets */
+	for (j = 0; j < PREFETCH_OFFSET && j < nb_rx; j++)
+		rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[j], void *));
+
+	/* Prefetch and forward already prefetched packets. */
+	for (j = 0; j < (nb_rx - PREFETCH_OFFSET); j++) {
+		rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[
+				j + PREFETCH_OFFSET], void *));
+		l3fwd_lpm_simple_forward(pkts_burst[j], portid, qconf);
+	}
+
+	/* Forward remaining prefetched packets */
+	for (; j < nb_rx; j++)
+		l3fwd_lpm_simple_forward(pkts_burst[j], portid, qconf);
+}
+
+/* main processing loop */
+int
+lpm_main_loop(__rte_unused void *dummy)
+{
+	struct rte_mbuf **pkts_burst;
+	unsigned int lcore_id, regex_nb_ops = 0;
+	uint64_t prev_tsc, diff_tsc, cur_tsc;
+	int i, nb_rx, nb_ops, deq_cnt;
+	uint16_t portid, regex_qp_id;
+	uint8_t queueid, regex_dev_id;
+	struct lcore_conf *qconf;
+	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) /
+		US_PER_S * BURST_TX_DRAIN_US;
+
+	prev_tsc = 0;
+
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_conf[lcore_id];
+
+	if (qconf->n_rx_queue == 0) {
+		RTE_LOG(INFO, L3FWD, "lcore %u has nothing to do\n", lcore_id);
+		return 0;
+	}
+
+	RTE_LOG(INFO, L3FWD, "entering main loop on lcore %u\n", lcore_id);
+
+	for (i = 0; i < qconf->n_rx_queue; i++) {
+
+		portid = qconf->rx_queue_list[i].port_id;
+		queueid = qconf->rx_queue_list[i].queue_id;
+		RTE_LOG(INFO, L3FWD,
+			" -- lcoreid=%u portid=%u rxqueueid=%hhu\n",
+			lcore_id, portid, queueid);
+	}
+	regex_dev_id = qconf->regex_dev_id;
+	regex_qp_id = qconf->regex_qp_id;
+	pkts_burst = qconf->pkts_burst;
+
+	while (!force_quit) {
+
+		cur_tsc = rte_rdtsc();
+
+		/*
+		 * TX burst queue drain
+		 */
+		diff_tsc = cur_tsc - prev_tsc;
+		if (unlikely(diff_tsc > drain_tsc)) {
+
+			if (regex_nb_ops) {
+				deq_cnt = regex_dequeue_burst_ops(regex_dev_id,
+						lcore_id, regex_qp_id,
+						pkts_burst, REGEX_NB_OPS);
+				if (deq_cnt) {
+					/* only one rx queue is supported */
+					portid =
+						qconf->rx_queue_list[0].port_id;
+					l3fwd_lpm_no_opt_send_packets(deq_cnt,
+							pkts_burst,
+							portid, qconf);
+					regex_nb_ops -= deq_cnt;
+				}
+			}
+
+			for (i = 0; i < qconf->n_tx_port; ++i) {
+				portid = qconf->tx_port_id[i];
+				if (qconf->tx_mbufs[portid].len == 0)
+					continue;
+				send_burst(qconf,
+					qconf->tx_mbufs[portid].len,
+					portid);
+				qconf->tx_mbufs[portid].len = 0;
+			}
+
+			prev_tsc = cur_tsc;
+		}
+
+		/*
+		 * Read packet from RX queues
+		 */
+		for (i = 0; i < qconf->n_rx_queue; ++i) {
+			portid = qconf->rx_queue_list[i].port_id;
+			queueid = qconf->rx_queue_list[i].queue_id;
+			nb_rx = rte_eth_rx_burst(portid, queueid, pkts_burst,
+				MAX_PKT_BURST);
+			if (nb_rx == 0)
+				continue;
+			nb_ops = regex_enqueue_burst_ops(regex_dev_id,
+					lcore_id, regex_qp_id,
+					pkts_burst, nb_rx);
+			if (unlikely(nb_ops != nb_rx))
+				printf("failed to enqueue all ops, %d/%d",
+						nb_ops, nb_rx);
+
+			regex_nb_ops += nb_ops;
+
+			deq_cnt = regex_dequeue_burst_ops(regex_dev_id,
+					lcore_id, regex_qp_id,
+					pkts_burst, REGEX_NB_OPS);
+			if (deq_cnt) {
+				l3fwd_lpm_no_opt_send_packets(deq_cnt,
+						pkts_burst,
+						portid, qconf);
+				regex_nb_ops -= deq_cnt;
+			}
+
+		}
+	}
+	regex_stats_print(lcore_id);
+
+	return 0;
+}
+
+
+void
+setup_lpm(const int socketid)
+{
+	struct rte_lpm6_config config;
+	struct rte_lpm_config config_ipv4;
+	unsigned int i;
+	int ret;
+	char s[64];
+	char abuf[INET6_ADDRSTRLEN];
+
+	/* create the LPM table */
+	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
+	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
+	config_ipv4.flags = 0;
+	snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socketid);
+	ipv4_l3fwd_lpm_lookup_struct[socketid] =
+			rte_lpm_create(s, socketid, &config_ipv4);
+	if (ipv4_l3fwd_lpm_lookup_struct[socketid] == NULL)
+		rte_exit(EXIT_FAILURE,
+			"Unable to create the l3fwd LPM table on socket %d\n",
+			socketid);
+
+	/* populate the LPM table */
+	for (i = 0; i < RTE_DIM(ipv4_l3fwd_lpm_route_array); i++) {
+		struct in_addr in;
+
+		/* skip unused ports */
+		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
+			ipv4_l3fwd_lpm_route_array[i].ip,
+			ipv4_l3fwd_lpm_route_array[i].depth,
+			ipv4_l3fwd_lpm_route_array[i].if_out);
+
+		if (ret < 0) {
+			rte_exit(EXIT_FAILURE,
+				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
+				i, socketid);
+		}
+
+		in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip);
+		printf("LPM: Adding route %s / %d (%d)\n",
+		       inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
+			ipv4_l3fwd_lpm_route_array[i].depth,
+			ipv4_l3fwd_lpm_route_array[i].if_out);
+	}
+
+	/* create the LPM6 table */
+	snprintf(s, sizeof(s), "IPV6_L3FWD_LPM_%d", socketid);
+
+	config.max_rules = IPV6_L3FWD_LPM_MAX_RULES;
+	config.number_tbl8s = IPV6_L3FWD_LPM_NUMBER_TBL8S;
+	config.flags = 0;
+	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
+				&config);
+	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
+		rte_exit(EXIT_FAILURE,
+			"Unable to create the l3fwd LPM table on socket %d\n",
+			socketid);
+
+	/* populate the LPM table */
+	for (i = 0; i < RTE_DIM(ipv6_l3fwd_lpm_route_array); i++) {
+
+		/* skip unused ports */
+		if ((1 << ipv6_l3fwd_lpm_route_array[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
+			ipv6_l3fwd_lpm_route_array[i].ip,
+			ipv6_l3fwd_lpm_route_array[i].depth,
+			ipv6_l3fwd_lpm_route_array[i].if_out);
+
+		if (ret < 0) {
+			rte_exit(EXIT_FAILURE,
+				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
+				i, socketid);
+		}
+
+		printf("LPM: Adding route %s / %d (%d)\n",
+		       inet_ntop(AF_INET6, ipv6_l3fwd_lpm_route_array[i].ip,
+				 abuf, sizeof(abuf)),
+		       ipv6_l3fwd_lpm_route_array[i].depth,
+		       ipv6_l3fwd_lpm_route_array[i].if_out);
+	}
+}
+
+int
+lpm_check_ptype(int portid)
+{
+	int i, ret;
+	int ptype_l3_ipv4 = 0, ptype_l3_ipv6 = 0;
+	uint32_t ptype_mask = RTE_PTYPE_L3_MASK;
+
+	ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, NULL, 0);
+	if (ret <= 0)
+		return 0;
+
+	uint32_t ptypes[ret];
+
+	ret = rte_eth_dev_get_supported_ptypes(portid, ptype_mask, ptypes, ret);
+	for (i = 0; i < ret; ++i) {
+		if (ptypes[i] & RTE_PTYPE_L3_IPV4)
+			ptype_l3_ipv4 = 1;
+		if (ptypes[i] & RTE_PTYPE_L3_IPV6)
+			ptype_l3_ipv6 = 1;
+	}
+
+	if (ptype_l3_ipv4 == 0)
+		printf("port %d cannot parse RTE_PTYPE_L3_IPV4\n", portid);
+
+	if (ptype_l3_ipv6 == 0)
+		printf("port %d cannot parse RTE_PTYPE_L3_IPV6\n", portid);
+
+	if (ptype_l3_ipv4 && ptype_l3_ipv6)
+		return 1;
+
+	return 0;
+
+}
+
+static inline void
+lpm_parse_ptype(struct rte_mbuf *m)
+{
+	struct rte_ether_hdr *eth_hdr;
+	uint32_t packet_type = RTE_PTYPE_UNKNOWN;
+	uint16_t ether_type;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
+	ether_type = eth_hdr->ether_type;
+	if (ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
+		packet_type |= RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
+	else if (ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6))
+		packet_type |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+
+	m->packet_type = packet_type;
+}
+
+uint16_t
+lpm_cb_parse_ptype(uint16_t port __rte_unused, uint16_t queue __rte_unused,
+		   struct rte_mbuf *pkts[], uint16_t nb_pkts,
+		   uint16_t max_pkts __rte_unused,
+		   void *user_param __rte_unused)
+{
+	unsigned int i;
+
+	if (unlikely(nb_pkts == 0))
+		return nb_pkts;
+	rte_prefetch0(rte_pktmbuf_mtod(pkts[0], struct ether_hdr *));
+	for (i = 0; i < (unsigned int) (nb_pkts - 1); ++i) {
+		rte_prefetch0(rte_pktmbuf_mtod(pkts[i+1],
+			struct ether_hdr *));
+		lpm_parse_ptype(pkts[i]);
+	}
+	lpm_parse_ptype(pkts[i]);
+
+	return nb_pkts;
+}
+
+/* Return ipv4/ipv6 lpm fwd lookup struct. */
+void *
+lpm_get_ipv4_l3fwd_lookup_struct(const int socketid)
+{
+	return ipv4_l3fwd_lpm_lookup_struct[socketid];
+}
+
+void *
+lpm_get_ipv6_l3fwd_lookup_struct(const int socketid)
+{
+	return ipv6_l3fwd_lpm_lookup_struct[socketid];
+}
diff --git a/examples/l3fwd-regex/l3fwd_regex.c b/examples/l3fwd-regex/l3fwd_regex.c
new file mode 100644
index 000000000..c24cb6d17
--- /dev/null
+++ b/examples/l3fwd-regex/l3fwd_regex.c
@@ -0,0 +1,487 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <rte_debug.h>
+#include <rte_eal.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+#include <rte_regexdev.h>
+
+#include "l3fwd.h"
+#include "l3fwd_regex.h"
+
+#define REGEX_OPS_DATA_SIZE    (0x80 +\
+			254*sizeof(struct rte_regexdev_match))
+/* The optimum size (in terms of memory usage) for a mempool
+ * is when n is a power of two minus one: n = (2^q - 1).
+ */
+#define REGEX_RULE_FILE_GROUP_ID_STR "subset_id"
+#define REGEX_RULE_FILE_GROUP_ID_LEN 9
+
+#define test_bitmap(i, val) (val & (1ull << i))
+#define REGEX_MOD_INC(i, l)   ((i) == (l - 1) ? (i) = 0 : (i)++)
+
+#define REGEX_DEBUG(fmt, args...) \
+		do {\
+			if (unlikely(conf->debug_print))\
+				printf("regex %d:"fmt, rte_lcore_id(), ##args);\
+		} while (0)
+#define REGEX_LOG(fmt, args...) printf("regex %d:"fmt, rte_lcore_id(), ##args)
+
+#define REGEX_ERR(fmt, args...) printf("error %d:"fmt, rte_lcore_id(), ##args)
+
+const char *
+regex_dev_capa_strings[] = {
+	[0]	= "compilation",
+};
+
+
+const char *
+rule_flags_strings[] = {
+	[0]	= "ALLOW_EMPTY",
+	[1]	= "ANCHORED",
+	[2]	= "CASELESS",
+	[3]	= "DOTALL",
+	[4]	= "DUPNAMES",
+	[5]	= "EXTENDED",
+	[6]	= "MATCH_UNSET_BACKREF",
+	[7]	= "MULTILINE",
+	[8]	= "NO_AUTO_CAPTURE",
+	[9]	= "UCP",
+	[10]	= "UNGREEDY",
+	[11]	= "UTF",
+	[12]	= "BACKSLASH",
+};
+
+struct regex_rule_db_entry {
+	uint8_t		type;
+	uint32_t	addr;
+	uint64_t	value;
+};
+
+struct regex_rule_db {
+	uint32_t version;
+	uint32_t revision;
+	uint32_t number_of_entries;
+	struct regex_rule_db_entry *entries;
+};
+
+struct regex_stats {
+	uint64_t matches;
+};
+
+struct regex_conf {
+	uint32_t rule_db_len;
+	char *rule_db;
+	uint8_t debug_print;
+	uint8_t nb_lcore;
+	uint8_t drop_on_match;
+};
+
+struct regex_lcore_conf {
+	uint16_t dev_id;
+	uint16_t qp_id;
+	struct rte_regex_ops **ops;
+	struct rte_regex_ops **ops_pool;
+};
+
+struct regex_lcore_params {
+	uint32_t ops_head;
+	uint32_t ops_tail;
+	uint32_t ops_avail;
+	struct regex_stats stats;
+};
+
+static struct regex_lcore_params regex_lcore_params[RTE_MAX_LCORE];
+static struct regex_lcore_conf regex_lcore_conf[RTE_MAX_LCORE];
+
+struct regex_conf conf[] = {
+		{
+				.rule_db_len = 0,
+				.rule_db = NULL,
+				.debug_print = 0,
+				.nb_lcore = 0,
+				.drop_on_match = 0,
+		}
+};
+
+int
+regex_read_rule_db_file(char *filename)
+{
+	uint32_t new_len;
+	long buf_len;
+
+	FILE *fp = fopen(filename, "rb");
+	if (fp == NULL) {
+		printf("Error opening file\n");
+		return -EIO;
+	}
+	if (fseek(fp, 0L, SEEK_END) == 0) {
+		buf_len = ftell(fp);
+		if (buf_len == -1)
+			goto error;
+		conf->rule_db = rte_malloc(NULL, sizeof(char) * (buf_len + 1),
+				0);
+		if (conf->rule_db == NULL)
+			goto error;
+
+		if (fseek(fp, 0L, SEEK_SET) != 0)
+			goto error;
+		new_len = fread(conf->rule_db, sizeof(char), buf_len, fp);
+		if (new_len != buf_len)
+			goto error;
+	} else
+		goto error;
+
+	fclose(fp);
+	conf->rule_db_len = buf_len;
+
+	return 0;
+error:
+	if (fp)
+		fclose(fp);
+	if (conf->rule_db)
+		rte_free(conf->rule_db);
+	return -EIO;
+}
+
+void
+regex_debug_enable(void)
+{
+	conf->debug_print = 1;
+}
+
+void
+regex_drop_on_match(void)
+{
+	conf->drop_on_match = 1;
+}
+
+static inline int
+regex_opspool_get_bulk(uint32_t lcore, struct rte_regex_ops **ops, uint32_t n)
+{
+	struct rte_regex_ops **ops_pool;
+	uint32_t i, ops_head;
+
+	ops_pool = regex_lcore_conf[lcore].ops_pool;
+	ops_head = regex_lcore_params[lcore].ops_head;
+
+	if (regex_lcore_params[lcore].ops_avail < n) {
+		REGEX_LOG("cannot allocate ops buffer\n");
+		return 0;
+	}
+
+	for (i = 0; i < n; i++) {
+		ops[i] = ops_pool[ops_head];
+		REGEX_MOD_INC(ops_head, REGEX_NB_OPS);
+	}
+
+	regex_lcore_params[lcore].ops_avail -= n;
+	regex_lcore_params[lcore].ops_head = ops_head;
+	return n;
+}
+
+static inline void
+regex_opspool_put_bulk(uint32_t lcore, struct rte_regex_ops **ops, uint32_t n)
+{
+	struct rte_regex_ops **ops_pool;
+	uint32_t i, ops_tail;
+
+	ops_pool = regex_lcore_conf[lcore].ops_pool;
+	ops_tail = regex_lcore_params[lcore].ops_tail;
+	for (i = 0; i < n; i++) {
+		if (ops_pool[ops_tail] != ops[i]) {
+			REGEX_ERR("ops pool out of sync\n"
+					"ops_pool[%d] = %p\n"
+					"ops[%d] = %p\n"
+					"exiting...\n", ops_tail,
+					ops_pool[ops_tail], i, ops[i]);
+			force_quit = true;
+			return;
+		}
+		ops_pool[ops_tail] = ops[i];
+		REGEX_MOD_INC(ops_tail, REGEX_NB_OPS);
+	}
+	regex_lcore_params[lcore].ops_avail += n;
+	regex_lcore_params[lcore].ops_tail = ops_tail;
+}
+
+static inline void
+regex_opspool_put(uint32_t lcore, struct rte_regex_ops *ops)
+{
+	struct rte_regex_ops **ops_pool;
+	uint32_t ops_tail;
+
+	ops_pool = regex_lcore_conf[lcore].ops_pool;
+	ops_tail = regex_lcore_params[lcore].ops_tail;
+	if (ops_pool[ops_tail] != ops) {
+		REGEX_ERR("ops pool out of sync\n"
+				"ops_pool[%d] = %p\n"
+				"ops = %p\n"
+				"exiting...\n", ops_tail,
+				ops_pool[ops_tail], ops);
+		force_quit = true;
+		return;
+	}
+	ops_pool[ops_tail] = ops;
+	REGEX_MOD_INC(ops_tail, REGEX_NB_OPS);
+	regex_lcore_params[lcore].ops_avail++;
+	regex_lcore_params[lcore].ops_tail = ops_tail;
+}
+
+static inline uint32_t
+regex_fill_ops(uint32_t lcore, struct rte_regex_ops **ops, uint16_t nb_ops,
+		struct rte_mbuf **pkts_burst)
+{
+	struct rte_mbuf *mbuf;
+	uint32_t i;
+	int ret;
+
+	ret = regex_opspool_get_bulk(lcore, ops, nb_ops);
+	if (unlikely(!ret)) {
+		REGEX_LOG("cannot allocate ops buffer\n");
+		return 0;
+	}
+
+	for (i = 0; i < nb_ops; i++) {
+
+		mbuf = pkts_burst[i];
+		if (unlikely(mbuf == NULL)) {
+			REGEX_LOG("Cannot allocate more mbuf, %d allocated\n",
+					i);
+			regex_opspool_put(lcore, ops[i]);
+			return i;
+		}
+
+		ops[i]->mbuf = mbuf;
+		ops[i]->user_ptr = mbuf;
+		ops[i]->req_flags = 0;
+		ops[i]->group_id0 = 1;
+		ops[i]->group_id1 = 0;
+		ops[i]->group_id2 = 0;
+		ops[i]->group_id3 = 0;
+	}
+
+	return i;
+}
+
+static inline void
+regex_check_match(struct rte_regex_ops **ops, uint32_t deq_cnt,
+		struct rte_mbuf **pkts_burst, uint32_t lcore)
+{
+	uint32_t i;
+
+	for (i = 0; i < deq_cnt; i++) {
+		pkts_burst[i] = ops[i]->user_ptr;
+		if (ops[i]->nb_matches != 0) {
+			REGEX_DEBUG("op %d matches %d\n",
+					i, ops[i]->nb_matches);
+			regex_lcore_params[lcore].stats.matches++;
+			/* mark packet to be dropped
+			 * in l3fwd_lpm_simple_forward() non-IP packets are
+			 * dropped.
+			 */
+			if (conf->drop_on_match)
+				pkts_burst[i]->packet_type = RTE_PTYPE_UNKNOWN;
+		}
+	}
+}
+
+uint32_t
+regex_enqueue_burst_ops(int dev_id, uint32_t lcore_id, uint16_t qp_id,
+		struct rte_mbuf **pkts_burst, uint16_t nb_pkts)
+{
+	uint32_t nb_ops, ret;
+	struct rte_regex_ops **ops = regex_lcore_conf[lcore_id].ops;
+
+	nb_ops = regex_fill_ops(lcore_id, ops, nb_pkts, pkts_burst);
+
+	if (unlikely(nb_ops < nb_pkts))
+		return 0;
+
+	REGEX_DEBUG("Enqueue single burst %d\n", nb_ops);
+	ret = rte_regexdev_enqueue_burst(dev_id, qp_id, ops, nb_ops);
+	if (unlikely(ret != nb_ops)) {
+		REGEX_ERR("rte_regexdev_enqueue_burst(): Failed, %d/%d enqueue\n",
+				ret, nb_ops);
+		return 0;
+	}
+	return nb_ops;
+}
+
+uint32_t
+regex_dequeue_burst_ops(int dev_id, uint32_t lcore_id, uint16_t qp_id,
+		struct rte_mbuf **pkts_burst, uint16_t nb_pkts)
+{
+	struct rte_regex_ops **ops = regex_lcore_conf[lcore_id].ops;
+	uint32_t deq_cnt;
+
+	deq_cnt = rte_regexdev_dequeue_burst(dev_id, qp_id,
+			ops, nb_pkts);
+	REGEX_DEBUG("dequeue burst %d\n", deq_cnt);
+	if (deq_cnt)
+		regex_check_match(ops, deq_cnt, pkts_burst, lcore_id);
+
+
+	regex_opspool_put_bulk(lcore_id, ops, deq_cnt);
+
+	return deq_cnt;
+}
+
+
+void
+regex_stats_print(uint32_t lcore)
+{
+	REGEX_LOG("Number of matches: %"PRIu64"\n",
+			regex_lcore_params[lcore].stats.matches);
+}
+
+void
+regex_dev_uninit(uint32_t dev_id)
+{
+	if (rte_regexdev_close(dev_id) < 0)
+		printf("rte_regexdev_close(dev %d): Failed\n", dev_id);
+}
+
+void
+regex_lcore_uninit(uint32_t lcore_id)
+{
+	uint32_t i;
+	if (regex_lcore_conf[lcore_id].ops_pool) {
+		for (i = 0; i < REGEX_NB_OPS; i++) {
+			if (regex_lcore_conf[lcore_id].ops_pool[i])
+				rte_free(
+					regex_lcore_conf[lcore_id].ops_pool[i]);
+		}
+		rte_free(regex_lcore_conf[lcore_id].ops_pool);
+	}
+	if (regex_lcore_conf[lcore_id].ops)
+		rte_free(regex_lcore_conf[lcore_id].ops);
+}
+
+void
+regex_mem_free(void)
+{
+	if (conf->rule_db)
+		rte_free(conf->rule_db);
+}
+
+int
+regex_dev_init(uint32_t dev_id, uint16_t nb_queue_pairs)
+{
+	struct rte_regexdev_qp_conf qp_conf;
+	struct rte_regexdev_info dev_info;
+	struct rte_regexdev_config cfg;
+	uint32_t i;
+	int ret;
+
+	printf("info: dev id is %d\n", dev_id);
+	ret = rte_regexdev_info_get(dev_id, &dev_info);
+	if (ret < 0) {
+		printf("rte_regexdev_info_get(): Failed\n");
+		return ret;
+	}
+	printf("get info:\n");
+	printf("driver_name          %s\n", dev_info.driver_name);
+	printf("max_matches          %d\n", dev_info.max_matches);
+	printf("max_queue_pairs      %d\n", dev_info.max_queue_pairs);
+	printf("max_payloadsize      %d\n", dev_info.max_payload_size);
+	printf("max_rules_per_group %d\n",
+			dev_info.max_rules_per_group);
+	printf("max_groups          %d\n", dev_info.max_groups);
+	printf("regex_dev_capa       0x%x\n", dev_info.regexdev_capa);
+	for (i = 0; i < 32; i++)
+		if (test_bitmap(i, dev_info.regexdev_capa))
+			printf("%d           %s\n", i,
+					regex_dev_capa_strings[i]);
+	printf("rule_flags           0x%lx\n", dev_info.rule_flags);
+	for (i = 0; i < 64; i++)
+		if (test_bitmap(i, dev_info.rule_flags))
+			printf("%d           %s\n", i,
+					rule_flags_strings[i]);
+
+	cfg.dev_cfg_flags = 0;
+	cfg.nb_max_matches = dev_info.max_matches;
+	cfg.nb_queue_pairs = nb_queue_pairs;
+	cfg.nb_rules_per_group = dev_info.max_rules_per_group;
+	cfg.nb_groups = dev_info.max_groups;
+	cfg.rule_db = conf->rule_db;
+	cfg.rule_db_len = conf->rule_db_len;
+	ret = rte_regexdev_configure(dev_id, &cfg);
+	if (ret < 0) {
+		printf("rte_regexdev_configure(): Failed\n");
+		return ret;
+	}
+
+	qp_conf.qp_conf_flags = 0;
+	qp_conf.nb_desc = 8192;
+	qp_conf.cb = NULL;
+	for (i = 0; i < nb_queue_pairs; i++) {
+		ret = rte_regexdev_queue_pair_setup(dev_id, i,
+				&qp_conf);
+		if (ret < 0) {
+			printf("rte_regexdev_queue_pair_setup(): Failed for queue %d\n",
+					i);
+			return ret;
+		}
+	}
+	ret = rte_regexdev_start(dev_id);
+	if (ret < 0) {
+		printf("rte_regexdev_start(): Failed\n");
+		return ret;
+	}
+	return 0;
+}
+
+int
+regex_lcore_init(uint32_t lcore_id, uint32_t dev_id, uint32_t qp_id)
+{
+	uint32_t i;
+
+	printf("%s lcore %u dev_id %d qp %d\n", __func__,
+			lcore_id, dev_id, qp_id);
+
+	regex_lcore_conf[lcore_id].qp_id = qp_id;
+	regex_lcore_conf[lcore_id].dev_id = dev_id;
+
+	memset(&regex_lcore_params[lcore_id].stats, 0,
+			sizeof(struct regex_stats));
+
+	regex_lcore_conf[lcore_id].ops = rte_malloc("regex_ops",
+			REGEX_NB_OPS*sizeof(struct rte_regex_ops *),
+			0);
+	if (regex_lcore_conf[lcore_id].ops == NULL) {
+		REGEX_ERR("cannot allocate ops memory");
+		return -1;
+	}
+
+	regex_lcore_conf[lcore_id].ops_pool = rte_malloc("regex_ops_pool",
+			REGEX_NB_OPS*sizeof(struct rte_regex_ops *),
+			0);
+	if (regex_lcore_conf[lcore_id].ops_pool == NULL) {
+		REGEX_ERR("cannot allocate ops pool memory");
+		return -1;
+	}
+	for (i = 0; i < REGEX_NB_OPS; i++) {
+		regex_lcore_conf[lcore_id].ops_pool[i] = rte_malloc("",
+				REGEX_OPS_DATA_SIZE, 0);
+		if (regex_lcore_conf[lcore_id].ops_pool[i] == NULL) {
+			REGEX_ERR("cannot allocate ops memory");
+			return -1;
+		}
+	}
+	regex_lcore_params[lcore_id].ops_head = 0;
+	regex_lcore_params[lcore_id].ops_tail = 0;
+	regex_lcore_params[lcore_id].ops_avail = REGEX_NB_OPS;
+	conf->nb_lcore++;
+
+	return 0;
+}
diff --git a/examples/l3fwd-regex/l3fwd_regex.h b/examples/l3fwd-regex/l3fwd_regex.h
new file mode 100644
index 000000000..d01a8e6e1
--- /dev/null
+++ b/examples/l3fwd-regex/l3fwd_regex.h
@@ -0,0 +1,38 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __L3FWD_REGEX_H__
+#define __L3FWD_REGEX_H__
+
+#define REGEX_NB_OPS (8192)
+
+int
+regex_dev_init(uint32_t dev_id, uint16_t nb_queue_pairs);
+
+int
+regex_lcore_init(uint32_t lcore_id, uint32_t dev_id, uint32_t qp_id);
+
+uint32_t
+regex_enqueue_burst_ops(int dev_id, uint32_t lcore_id, uint16_t qp_id,
+		struct rte_mbuf **pkts_burst, uint16_t nb_pkts);
+uint32_t
+regex_dequeue_burst_ops(int dev_id, uint32_t lcore_id, uint16_t qp_id,
+		struct rte_mbuf **pkts_burst, uint16_t nb_pkts);
+int
+regex_read_rule_db_file(char *filename);
+void
+regex_debug_enable(void);
+void
+regex_drop_on_match(void);
+
+void
+regex_stats_print(uint32_t lcore);
+void
+regex_dev_uninit(uint32_t dev_id);
+void
+regex_lcore_uninit(uint32_t lcore_id);
+void
+regex_mem_free(void);
+
+#endif /* __L3FWD_REGEX_H__ */
diff --git a/examples/l3fwd-regex/main.c b/examples/l3fwd-regex/main.c
new file mode 100644
index 000000000..eed3a1d32
--- /dev/null
+++ b/examples/l3fwd-regex/main.c
@@ -0,0 +1,1117 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2016 Intel Corporation
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_vect.h>
+#include <rte_byteorder.h>
+#include <rte_log.h>
+#include <rte_malloc.h>
+#include <rte_memory.h>
+#include <rte_memcpy.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_atomic.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_lcore.h>
+#include <rte_per_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_interrupts.h>
+#include <rte_random.h>
+#include <rte_debug.h>
+#include <rte_ether.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_string_fns.h>
+#include <rte_cpuflags.h>
+#include <rte_regexdev.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
+
+#include "l3fwd.h"
+#include "l3fwd_regex.h"
+
+#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
+#define MAX_RX_QUEUE_PER_PORT 128
+
+#define MAX_LCORE_PARAMS 1024
+
+/* Static global variables used within this file. */
+static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
+static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
+
+/**< Ports set in promiscuous mode off by default. */
+static int promiscuous_on;
+
+/* Global variables. */
+
+static int numa_on = 1; /**< NUMA is enabled by default. */
+static int parse_ptype; /**< Parse packet type using rx callback, and */
+			/**< disabled by default */
+static int per_port_pool; /**< Use separate buffer pools per port; disabled */
+			  /**< by default */
+
+volatile bool force_quit;
+
+/* ethernet addresses of ports */
+uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
+
+xmm_t val_eth[RTE_MAX_ETHPORTS];
+
+/* mask of enabled ports */
+uint32_t enabled_port_mask;
+
+struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+
+struct lcore_params {
+	uint16_t port_id;
+	uint8_t queue_id;
+	uint8_t lcore_id;
+	uint8_t regex_dev_id;
+	uint16_t regex_qp_id;
+} __rte_cache_aligned;
+
+
+static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
+static struct lcore_params lcore_params_array_default[] = {
+	{0, 0, 1, 0, 0},
+};
+
+
+static struct lcore_params *lcore_params = lcore_params_array_default;
+static uint16_t nb_lcore_params = sizeof(lcore_params_array_default) /
+				sizeof(lcore_params_array_default[0]);
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.mq_mode = ETH_MQ_RX_RSS,
+		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
+		.split_hdr_size = 0,
+		.offloads = 0,
+	},
+	.rx_adv_conf = {
+		.rss_conf = {
+			.rss_key = NULL,
+			.rss_hf = ETH_RSS_IP,
+		},
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+};
+
+static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
+static uint8_t lkp_per_socket[NB_SOCKETS];
+
+struct l3fwd_lkp_mode {
+	void  (*setup)(int socketid);
+	int   (*check_ptype)(int portid);
+	rte_rx_callback_fn cb_parse_ptype;
+	int   (*main_loop)(void *arg);
+	void* (*get_ipv4_lookup_struct)(int socketid);
+	void* (*get_ipv6_lookup_struct)(int socketid);
+};
+
+static struct l3fwd_lkp_mode l3fwd_lkp;
+
+static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
+	.setup                  = setup_lpm,
+	.check_ptype		= lpm_check_ptype,
+	.cb_parse_ptype		= lpm_cb_parse_ptype,
+	.main_loop              = lpm_main_loop,
+	.get_ipv4_lookup_struct = lpm_get_ipv4_l3fwd_lookup_struct,
+	.get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct,
+};
+
+/*
+ * Setup lookup methods for forwarding.
+ * Currently longest-prefix-match is supported.
+ */
+static void
+setup_l3fwd_lookup_tables(void)
+{
+	/* Setup LPM lookup functions. */
+		l3fwd_lkp = l3fwd_lpm_lkp;
+}
+
+static int
+check_lcore_params(void)
+{
+	uint8_t queue, lcore, regex_queue, regex_dev;
+	struct rte_regexdev_info dev_info;
+	uint16_t i;
+	int socketid;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		queue = lcore_params[i].queue_id;
+		if (queue >= MAX_RX_QUEUE_PER_PORT) {
+			printf("invalid queue number: %hhu\n", queue);
+			return -1;
+		}
+		lcore = lcore_params[i].lcore_id;
+		if (!rte_lcore_is_enabled(lcore)) {
+			printf("error: lcore %hhu is not enabled in lcore mask\n",
+					lcore);
+			return -1;
+		}
+		socketid = rte_lcore_to_socket_id(lcore);
+		if ((socketid != 0) && (numa_on == 0)) {
+			printf("warning: lcore %hhu is on socket %d with numa off\n",
+				lcore, socketid);
+		}
+		regex_dev = lcore_params[i].regex_dev_id;
+		if ((rte_regexdev_is_valid_dev(regex_dev) == 0)) {
+			printf("invalid regex dev number: %hhu\n", regex_dev);
+			return -1;
+		}
+		regex_queue =  lcore_params[i].regex_qp_id;
+		rte_regexdev_info_get(regex_dev, &dev_info);
+		if (regex_queue >= dev_info.max_queue_pairs) {
+			printf("invalid regex queue number: %hhu\n",
+					regex_queue);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int
+check_port_config(void)
+{
+	uint16_t portid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		portid = lcore_params[i].port_id;
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("port %u is not enabled in port mask\n", portid);
+			return -1;
+		}
+		if (!rte_eth_dev_is_valid_port(portid)) {
+			printf("port %u is not present on the board\n", portid);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static uint8_t
+get_port_n_rx_queues(const uint16_t port)
+{
+	int queue = -1;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		if (lcore_params[i].port_id == port) {
+			if (lcore_params[i].queue_id == queue+1)
+				queue = lcore_params[i].queue_id;
+			else
+				rte_exit(EXIT_FAILURE, "queue ids of the port %d must be"
+						" in sequence and must start with 0\n",
+						lcore_params[i].port_id);
+		}
+	}
+	return (uint8_t)(++queue);
+}
+
+static uint8_t
+get_regex_dev_n_queues(const uint8_t dev_id)
+{
+	int queue = -1;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		if (lcore_params[i].regex_dev_id == dev_id) {
+			if (lcore_params[i].regex_qp_id == queue+1)
+				queue = lcore_params[i].regex_qp_id;
+			else
+				rte_exit(EXIT_FAILURE, "regex queue ids of a Regex device %d must be"
+						" in sequence and must start with 0\n",
+						lcore_params[i].regex_dev_id);
+		}
+	}
+	return (uint8_t)(++queue);
+}
+
+static int
+init_lcore_rx_queues(void)
+{
+	uint16_t i, nb_rx_queue;
+	uint8_t lcore;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		lcore = lcore_params[i].lcore_id;
+		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
+			printf("error: too many queues (%u) for lcore: %u\n",
+				(uint16_t)nb_rx_queue + 1, (uint8_t)lcore);
+			return -1;
+		}
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+				lcore_params[i].port_id;
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+				lcore_params[i].queue_id;
+		lcore_conf[lcore].regex_dev_id = lcore_params[i].regex_dev_id;
+		lcore_conf[lcore].regex_qp_id = lcore_params[i].regex_qp_id;
+		lcore_conf[lcore].n_rx_queue++;
+	}
+	return 0;
+}
+
+/* display usage */
+static void
+print_usage(const char *prgname)
+{
+	fprintf(stderr, "%s [EAL options] --"
+		" -p PORTMASK"
+		" [-P]"
+		" --config (port,queue,lcore,regexdev,regex q)[,(port,queue,lcore,regexdev,regex q)]"
+		" --regex-rule-db-file FILENAME"
+		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
+		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
+		" [--no-numa]"
+		" [--parse-ptype]"
+		" [--per-port-pool]"
+		" [--regex-drop]"
+		" [--regex-debug]\n\n"
+
+		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
+		"  -P : Enable promiscuous mode\n"
+		"  --config (port,queue,lcore,regexdev,regex q): Rx queue configuration\n"
+		"  --regex-rule-db-file FILENAME prebuilt rule database for regex device.\n"
+		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n"
+		"  --enable-jumbo: Enable jumbo frames\n"
+		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"                 maximum packet length in decimal (64-9600)\n"
+		"  --no-numa: Disable numa awareness\n"
+		"  --parse-ptype: Set to use software to analyze packet type\n"
+		"  --per-port-pool: Use separate buffer pool per port\n"
+		"  --regex-drop: Enable regex drop on match\n"
+		"  --regex-debug: Enable regex debug printing\n",
+		prgname);
+}
+
+static int
+parse_max_pkt_len(const char *pktlen)
+{
+	char *end = NULL;
+	unsigned long len;
+
+	/* parse decimal string */
+	len = strtoul(pktlen, &end, 10);
+	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (len == 0)
+		return -1;
+
+	return len;
+}
+
+static int
+parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_config(const char *q_arg)
+{
+	char s[256];
+	const char *p, *p0 = q_arg;
+	char *end;
+	enum fieldnames {
+		FLD_PORT = 0,
+		FLD_QUEUE,
+		FLD_LCORE,
+		FLD_REGEX_DEV,
+		FLD_REGEX_QUEUE,
+		_NUM_FLD
+	};
+	unsigned long int_fld[_NUM_FLD];
+	char *str_fld[_NUM_FLD];
+	int i;
+	unsigned int size;
+
+	nb_lcore_params = 0;
+
+	while ((p = strchr(p0, '(')) != NULL) {
+		++p;
+		p0 = strchr(p, ')');
+		if (p0 == NULL)
+			return -1;
+
+		size = p0 - p;
+		if (size >= sizeof(s))
+			return -1;
+
+		snprintf(s, sizeof(s), "%.*s", size, p);
+		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
+				_NUM_FLD)
+			return -1;
+		for (i = 0; i < _NUM_FLD; i++) {
+			errno = 0;
+			int_fld[i] = strtoul(str_fld[i], &end, 0);
+			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
+				return -1;
+		}
+		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
+			printf("exceeded max number of lcore params: %hu\n",
+				nb_lcore_params);
+			return -1;
+		}
+		lcore_params_array[nb_lcore_params].port_id =
+			(uint8_t)int_fld[FLD_PORT];
+		lcore_params_array[nb_lcore_params].queue_id =
+			(uint8_t)int_fld[FLD_QUEUE];
+		lcore_params_array[nb_lcore_params].lcore_id =
+			(uint8_t)int_fld[FLD_LCORE];
+		lcore_params_array[nb_lcore_params].regex_dev_id =
+				(uint8_t)int_fld[FLD_REGEX_DEV];
+		lcore_params_array[nb_lcore_params].regex_qp_id =
+				(uint8_t)int_fld[FLD_REGEX_QUEUE];
+		++nb_lcore_params;
+	}
+	lcore_params = lcore_params_array;
+	return 0;
+}
+
+static void
+parse_eth_dest(const char *optarg)
+{
+	uint16_t portid;
+	char *port_end;
+	uint8_t c, *dest, peer_addr[6];
+
+	errno = 0;
+	portid = strtoul(optarg, &port_end, 10);
+	if (errno != 0 || port_end == optarg || *port_end++ != ',')
+		rte_exit(EXIT_FAILURE,
+		"Invalid eth-dest: %s", optarg);
+	if (portid >= RTE_MAX_ETHPORTS)
+		rte_exit(EXIT_FAILURE,
+		"eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n",
+		portid, RTE_MAX_ETHPORTS);
+
+	if (cmdline_parse_etheraddr(NULL, port_end,
+		&peer_addr, sizeof(peer_addr)) < 0)
+		rte_exit(EXIT_FAILURE,
+		"Invalid ethernet address: %s\n",
+		port_end);
+	dest = (uint8_t *)&dest_eth_addr[portid];
+	for (c = 0; c < 6; c++)
+		dest[c] = peer_addr[c];
+	*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+}
+
+#define MAX_JUMBO_PKT_LEN  9600
+
+static const char short_options[] =
+	"p:"  /* portmask */
+	"P"   /* promiscuous */
+	;
+
+#define CMD_LINE_OPT_CONFIG "config"
+#define CMD_LINE_OPT_ETH_DEST "eth-dest"
+#define CMD_LINE_OPT_NO_NUMA "no-numa"
+#define CMD_LINE_OPT_ENABLE_JUMBO "enable-jumbo"
+#define CMD_LINE_OPT_PARSE_PTYPE "parse-ptype"
+#define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool"
+#define CMD_LINE_OPT_REGEX_RULE_DB_FILE "regex-rule-db-file"
+#define CMD_LINE_OPT_REGEX_RULE_FILE "regex-rule-file"
+#define CMD_LINE_OPT_REGEX_DEBUG "regex-debug"
+#define CMD_LINE_OPT_REGEX_DROP "regex-drop"
+
+enum {
+	/* long options mapped to a short option
+	 */
+
+	/* first long only option value must be >= 256, so that we won't
+	 * conflict with short options
+	 */
+	CMD_LINE_OPT_MIN_NUM = 256,
+	CMD_LINE_OPT_CONFIG_NUM,
+	CMD_LINE_OPT_ETH_DEST_NUM,
+	CMD_LINE_OPT_NO_NUMA_NUM,
+	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
+	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
+	CMD_LINE_OPT_REGEX_RULE_DB_FILE_NUM,
+	CMD_LINE_OPT_REGEX_DEBUG_NUM,
+	CMD_LINE_OPT_REGEX_DROP_NUM
+};
+
+static const struct option lgopts[] = {
+	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{CMD_LINE_OPT_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM},
+	{CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL},
+	{CMD_LINE_OPT_REGEX_RULE_DB_FILE, 1, 0,
+			CMD_LINE_OPT_REGEX_RULE_DB_FILE_NUM},
+	{CMD_LINE_OPT_REGEX_DEBUG, 0, 0, CMD_LINE_OPT_REGEX_DEBUG_NUM},
+	{CMD_LINE_OPT_REGEX_DROP, 0, 0, CMD_LINE_OPT_REGEX_DROP_NUM},
+	{NULL, 0, 0, 0}
+};
+
+/*
+ * This expression is used to calculate the number of mbufs needed
+ * depending on user input, taking  into account memory for rx and
+ * tx hardware rings, cache per lcore and mtable per port per lcore.
+ * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum
+ * value of 8192
+ */
+#define NB_MBUF(nports) RTE_MAX(	\
+	(nports*nb_rx_queue*nb_rxd +		\
+	nports*nb_lcores*MAX_PKT_BURST +	\
+	nports*n_tx_queue*nb_txd +		\
+	nb_lcores*MEMPOOL_CACHE_SIZE),		\
+	8192U)
+
+/* Parse the argument given in the command line of the application */
+static int
+parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	argvopt = argv;
+
+	/* Error or normal output strings. */
+	while ((opt = getopt_long(argc, argvopt, short_options,
+				lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+		/* portmask */
+		case 'p':
+			enabled_port_mask = parse_portmask(optarg);
+			if (enabled_port_mask == 0) {
+				fprintf(stderr, "Invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case 'P':
+			promiscuous_on = 1;
+			break;
+
+		/* long options */
+		case CMD_LINE_OPT_CONFIG_NUM:
+			ret = parse_config(optarg);
+			if (ret) {
+				fprintf(stderr, "Invalid config\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case CMD_LINE_OPT_ETH_DEST_NUM:
+			parse_eth_dest(optarg);
+			break;
+
+		case CMD_LINE_OPT_NO_NUMA_NUM:
+			numa_on = 0;
+			break;
+
+
+		case CMD_LINE_OPT_ENABLE_JUMBO_NUM: {
+			const struct option lenopts = {
+				"max-pkt-len", required_argument, 0, 0
+			};
+
+			port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME;
+			port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS;
+
+			/*
+			 * if no max-pkt-len set, use the default
+			 * value RTE_ETHER_MAX_LEN.
+			 */
+			if (getopt_long(argc, argvopt, "",
+					&lenopts, &option_index) == 0) {
+				ret = parse_max_pkt_len(optarg);
+				if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) {
+					fprintf(stderr,
+						"invalid maximum packet length\n");
+					print_usage(prgname);
+					return -1;
+				}
+				port_conf.rxmode.max_rx_pkt_len = ret;
+			}
+			break;
+		}
+
+		case CMD_LINE_OPT_PARSE_PTYPE_NUM:
+			printf("soft parse-ptype is enabled\n");
+			parse_ptype = 1;
+			break;
+
+		case CMD_LINE_OPT_PARSE_PER_PORT_POOL:
+			printf("per port buffer pool is enabled\n");
+			per_port_pool = 1;
+			break;
+		case CMD_LINE_OPT_REGEX_RULE_DB_FILE_NUM:
+			ret = regex_read_rule_db_file(optarg);
+			if (ret) {
+				fprintf(stderr, "%s", rte_strerror(ret));
+				return -1;
+			}
+			break;
+		case CMD_LINE_OPT_REGEX_DEBUG_NUM:
+			regex_debug_enable();
+			break;
+		case CMD_LINE_OPT_REGEX_DROP_NUM:
+			regex_drop_on_match();
+			break;
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	if (optind >= 0)
+		argv[optind-1] = prgname;
+
+	ret = optind-1;
+	optind = 1; /* reset getopt lib */
+	return ret;
+}
+
+static void
+print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
+{
+	char buf[RTE_ETHER_ADDR_FMT_SIZE];
+	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
+	printf("%s%s", name, buf);
+}
+
+int
+init_mem(uint16_t portid, unsigned int nb_mbuf)
+{
+	struct lcore_conf *qconf;
+	unsigned int lcore_id;
+	int socketid;
+	char s[64];
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		if (numa_on)
+			socketid = rte_lcore_to_socket_id(lcore_id);
+		else
+			socketid = 0;
+
+		if (socketid >= NB_SOCKETS) {
+			rte_exit(EXIT_FAILURE,
+				"Socket %d of lcore %u is out of range %d\n",
+				socketid, lcore_id, NB_SOCKETS);
+		}
+
+		if (pktmbuf_pool[portid][socketid] == NULL) {
+			snprintf(s, sizeof(s), "mbuf_pool_%d:%d",
+				 portid, socketid);
+			pktmbuf_pool[portid][socketid] =
+				rte_pktmbuf_pool_create(s, nb_mbuf,
+					MEMPOOL_CACHE_SIZE, 0,
+					RTE_MBUF_DEFAULT_BUF_SIZE, socketid);
+			if (pktmbuf_pool[portid][socketid] == NULL)
+				rte_exit(EXIT_FAILURE,
+					"Cannot init mbuf pool on socket %d\n",
+					socketid);
+			else
+				printf("Allocated mbuf pool on socket %d\n",
+					socketid);
+
+			/* Setup either LPM or EM(f.e Hash). But, only once per
+			 * available socket.
+			 */
+			if (!lkp_per_socket[socketid]) {
+				l3fwd_lkp.setup(socketid);
+				lkp_per_socket[socketid] = 1;
+			}
+		}
+		qconf = &lcore_conf[lcore_id];
+		qconf->ipv4_lookup_struct =
+			l3fwd_lkp.get_ipv4_lookup_struct(socketid);
+		qconf->ipv6_lookup_struct =
+			l3fwd_lkp.get_ipv6_lookup_struct(socketid);
+		qconf->pkts_burst = rte_malloc("pkts_burst",
+				REGEX_NB_OPS*sizeof(struct rte_mbuf *),
+				0);
+		if (qconf->pkts_burst == NULL)
+			rte_exit(EXIT_FAILURE,
+				"Cannot allocate memory for pkts_burst\n");
+	}
+	return 0;
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
+	uint16_t portid;
+	uint8_t count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+	int ret;
+
+	printf("\nChecking link status");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		if (force_quit)
+			return;
+		all_ports_up = 1;
+		RTE_ETH_FOREACH_DEV(portid) {
+			if (force_quit)
+				return;
+			if ((port_mask & (1 << portid)) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			ret = rte_eth_link_get_nowait(portid, &link);
+			if (ret < 0) {
+				all_ports_up = 0;
+				if (print_flag == 1)
+					printf("Port %u link get failed: %s\n",
+						portid, rte_strerror(-ret));
+				continue;
+			}
+			/* print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status)
+					printf(
+					"Port%d Link Up. Speed %u Mbps -%s\n",
+						portid, link.link_speed,
+				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
+					("full-duplex") : ("half-duplex"));
+				else
+					printf("Port %d Link Down\n", portid);
+				continue;
+			}
+			/* clear all_ports_up flag if any link down */
+			if (link.link_status == ETH_LINK_DOWN) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* after finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("done\n");
+		}
+	}
+}
+
+static void
+signal_handler(int signum)
+{
+	if (signum == SIGINT || signum == SIGTERM) {
+		printf("\n\nSignal %d received, preparing to exit...\n",
+				signum);
+		force_quit = true;
+	}
+}
+
+static int
+prepare_ptype_parser(uint16_t portid, uint16_t queueid)
+{
+	if (parse_ptype) {
+		printf("Port %d: softly parse packet type info\n", portid);
+		if (rte_eth_add_rx_callback(portid, queueid,
+					    l3fwd_lkp.cb_parse_ptype,
+					    NULL))
+			return 1;
+
+		printf("Failed to add rx callback: port=%d\n", portid);
+		return 0;
+	}
+
+	if (l3fwd_lkp.check_ptype(portid))
+		return 1;
+
+	printf("port %d cannot parse packet type, please add --%s\n",
+	       portid, CMD_LINE_OPT_PARSE_PTYPE);
+	return 0;
+}
+
+static void
+l3fwd_poll_resource_setup(void)
+{
+	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_eth_dev_info dev_info;
+	uint32_t n_tx_queue, nb_lcores, nb_regex_queue;
+	struct rte_eth_txconf *txconf;
+	struct lcore_conf *qconf;
+	uint16_t queueid, portid;
+	unsigned int nb_ports;
+	unsigned int lcore_id, i;
+	int ret;
+
+	if (check_lcore_params() < 0)
+		rte_exit(EXIT_FAILURE, "check_lcore_params failed\n");
+
+	ret = init_lcore_rx_queues();
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues failed\n");
+
+	nb_ports = rte_eth_dev_count_avail();
+
+	if (check_port_config() < 0)
+		rte_exit(EXIT_FAILURE, "check_port_config failed\n");
+
+	nb_lcores = rte_lcore_count();
+
+	/* initialize all ports */
+	RTE_ETH_FOREACH_DEV(portid) {
+		struct rte_eth_conf local_port_conf = port_conf;
+
+		/* skip ports that are not enabled */
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("\nSkipping disabled port %d\n", portid);
+			continue;
+		}
+
+		/* init port */
+		printf("Initializing port %d ... ", portid);
+		fflush(stdout);
+
+		nb_rx_queue = get_port_n_rx_queues(portid);
+		if (nb_rx_queue > 1)
+			rte_exit(EXIT_FAILURE,
+				"only one rx queue per (port %u) is supported (queue num %u)\n",
+				portid, nb_rx_queue);
+		n_tx_queue = nb_lcores;
+		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
+			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
+		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
+			nb_rx_queue, (uint32_t)n_tx_queue);
+
+		ret = rte_eth_dev_info_get(portid, &dev_info);
+		if (ret != 0)
+			rte_exit(EXIT_FAILURE,
+				"Error during getting device (port %u) info: %s\n",
+				portid, strerror(-ret));
+
+		if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
+			local_port_conf.txmode.offloads |=
+				DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+
+		local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
+			dev_info.flow_type_rss_offloads;
+		if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
+				port_conf.rx_adv_conf.rss_conf.rss_hf) {
+			printf("Port %u modified RSS hash function based on hardware support,"
+				"requested:%#"PRIx64" configured:%#"PRIx64"\n",
+				portid,
+				port_conf.rx_adv_conf.rss_conf.rss_hf,
+				local_port_conf.rx_adv_conf.rss_conf.rss_hf);
+		}
+
+		ret = rte_eth_dev_configure(portid, nb_rx_queue,
+					(uint16_t)n_tx_queue, &local_port_conf);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				"Cannot configure device: err=%d, port=%d\n",
+				ret, portid);
+
+		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
+						       &nb_txd);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot adjust number of descriptors: err=%d, "
+				 "port=%d\n", ret, portid);
+
+		ret = rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot get MAC address: err=%d, port=%d\n",
+				 ret, portid);
+
+		print_ethaddr(" Address:", &ports_eth_addr[portid]);
+		printf(", ");
+		print_ethaddr("Destination:",
+			(const struct rte_ether_addr *)&dest_eth_addr[portid]);
+		printf(", ");
+
+		/*
+		 * prepare src MACs for each port.
+		 */
+		rte_ether_addr_copy(&ports_eth_addr[portid],
+			(struct rte_ether_addr *)(val_eth + portid) + 1);
+
+		/* init memory */
+		if (!per_port_pool) {
+			/* portid = 0; this is *not* signifying the first port,
+			 * rather, it signifies that portid is ignored.
+			 */
+			ret = init_mem(0, NB_MBUF(nb_ports));
+		} else {
+			ret = init_mem(portid, NB_MBUF(1));
+		}
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "init_mem failed\n");
+
+		/* init one TX queue per couple (lcore,port) */
+		queueid = 0;
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			if (numa_on)
+				socketid =
+				(uint8_t)rte_lcore_to_socket_id(lcore_id);
+			else
+				socketid = 0;
+
+			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
+			fflush(stdout);
+
+			txconf = &dev_info.default_txconf;
+			txconf->offloads = local_port_conf.txmode.offloads;
+			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+						     socketid, txconf);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					"rte_eth_tx_queue_setup: err=%d, "
+					"port=%d\n", ret, portid);
+
+			qconf = &lcore_conf[lcore_id];
+			qconf->tx_queue_id[portid] = queueid;
+			queueid++;
+
+			qconf->tx_port_id[qconf->n_tx_port] = portid;
+			qconf->n_tx_port++;
+		}
+		printf("\n");
+	}
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+		qconf = &lcore_conf[lcore_id];
+		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
+		fflush(stdout);
+		/* init RX queues */
+		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+			struct rte_eth_rxconf rxq_conf;
+
+			portid = qconf->rx_queue_list[queue].port_id;
+			queueid = qconf->rx_queue_list[queue].queue_id;
+
+			if (numa_on)
+				socketid =
+				(uint8_t)rte_lcore_to_socket_id(lcore_id);
+			else
+				socketid = 0;
+
+			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
+			fflush(stdout);
+
+			ret = rte_eth_dev_info_get(portid, &dev_info);
+			if (ret != 0)
+				rte_exit(EXIT_FAILURE,
+					"Error during getting device (port %u) info: %s\n",
+					portid, strerror(-ret));
+
+			rxq_conf = dev_info.default_rxconf;
+			rxq_conf.offloads = port_conf.rxmode.offloads;
+			if (!per_port_pool)
+				ret = rte_eth_rx_queue_setup(portid, queueid,
+						nb_rxd, socketid,
+						&rxq_conf,
+						pktmbuf_pool[0][socketid]);
+			else
+				ret = rte_eth_rx_queue_setup(portid, queueid,
+						nb_rxd, socketid,
+						&rxq_conf,
+						pktmbuf_pool[portid][socketid]);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+				"rte_eth_rx_queue_setup: err=%d, port=%d\n",
+				ret, portid);
+		}
+	}
+	for (i = 0; i < RTE_MAX_REGEXDEV_DEVS; i++) {
+		if (rte_regexdev_is_valid_dev(i) == 0)
+			continue;
+		nb_regex_queue = get_regex_dev_n_queues(i);
+		if (nb_regex_queue) {
+			ret = regex_dev_init(i, nb_regex_queue);
+			if (ret != 0)
+				rte_exit(EXIT_FAILURE,
+					"regex_dev_init: err=%s\n",
+					rte_strerror(-ret));
+		}
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	struct lcore_conf *qconf;
+	uint16_t queueid, portid;
+	unsigned int lcore_id, i;
+	uint8_t queue, regex_nb_q;
+	int ret;
+
+	/* init EAL */
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+	argc -= ret;
+	argv += ret;
+
+	force_quit = false;
+	signal(SIGINT, signal_handler);
+	signal(SIGTERM, signal_handler);
+
+	/* pre-init dst MACs for all ports to 02:00:00:00:00:xx */
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+		dest_eth_addr[portid] =
+			RTE_ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40);
+		*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+	}
+
+	/* parse application arguments (after the EAL ones) */
+	ret = parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
+
+	/* Setup function pointers for lookup method. */
+	setup_l3fwd_lookup_tables();
+
+	l3fwd_poll_resource_setup();
+
+	/* start ports */
+	RTE_ETH_FOREACH_DEV(portid) {
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		/* Start device */
+		ret = rte_eth_dev_start(portid);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				"rte_eth_dev_start: err=%d, port=%d\n",
+				ret, portid);
+
+		/*
+		 * If enabled, put device in promiscuous mode.
+		 * This allows IO forwarding mode to forward packets
+		 * to itself through 2 cross-connected  ports of the
+		 * target machine.
+		 */
+		if (promiscuous_on) {
+			ret = rte_eth_promiscuous_enable(portid);
+			if (ret != 0)
+				rte_exit(EXIT_FAILURE,
+					"rte_eth_promiscuous_enable: err=%s, port=%u\n",
+					rte_strerror(-ret), portid);
+		}
+	}
+
+	printf("\n");
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+		qconf = &lcore_conf[lcore_id];
+		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+			portid = qconf->rx_queue_list[queue].port_id;
+			queueid = qconf->rx_queue_list[queue].queue_id;
+			if (prepare_ptype_parser(portid, queueid) == 0)
+				rte_exit(EXIT_FAILURE, "ptype check fails\n");
+		}
+
+		ret = regex_lcore_init(lcore_id, qconf->regex_dev_id,
+				qconf->regex_qp_id);
+		if (ret != 0)
+			rte_exit(EXIT_FAILURE,
+				"regex_lcore_init: err=%s, dev_id=%u, queueid=%u\n",
+				rte_strerror(-ret), qconf->regex_dev_id,
+				qconf->regex_qp_id);
+	}
+
+	check_all_ports_link_status(enabled_port_mask);
+
+	ret = 0;
+	/* launch per-lcore init on every lcore */
+	rte_eal_mp_remote_launch(l3fwd_lkp.main_loop, NULL, CALL_MASTER);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_ETH_FOREACH_DEV(portid) {
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+		printf("Closing port %d...", portid);
+		rte_eth_dev_stop(portid);
+		rte_eth_dev_close(portid);
+		printf(" Done\n");
+	}
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+		regex_lcore_uninit(lcore_id);
+		rte_free(lcore_conf[lcore_id].pkts_burst);
+	}
+
+	for (i = 0; i < RTE_MAX_REGEXDEV_DEVS; i++) {
+		if (rte_regexdev_is_valid_dev(i) == 0)
+			continue;
+		regex_nb_q = get_regex_dev_n_queues(i);
+		if (regex_nb_q) {
+			printf("Closing regegdev %d...", i);
+			regex_dev_uninit(i);
+			printf(" Done\n");
+		}
+	}
+	regex_mem_free();
+
+	printf("Bye...\n");
+
+	return ret;
+}
diff --git a/examples/l3fwd-regex/meson.build b/examples/l3fwd-regex/meson.build
new file mode 100644
index 000000000..a5f602510
--- /dev/null
+++ b/examples/l3fwd-regex/meson.build
@@ -0,0 +1,10 @@ 
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# meson file, for building this example as part of a main DPDK build.
+
+allow_experimental_apis = true
+deps += ['lpm', 'regexdev']
+sources = files(
+	'l3fwd_lpm.c', 'l3fwd_regex.c', 'main.c'
+)
diff --git a/examples/meson.build b/examples/meson.build
index eb13e8210..b61acd4bc 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -23,7 +23,7 @@  all_examples = [
 	'l2fwd', 'l2fwd-cat', 'l2fwd-event',
 	'l2fwd-crypto', 'l2fwd-jobstats',
 	'l2fwd-keepalive', 'l3fwd',
-	'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph',
+	'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph', 'l3fwd-regex',
 	'link_status_interrupt',
 	'multi_process/client_server_mp/mp_client',
 	'multi_process/client_server_mp/mp_server',