[dpdk-dev] [PATCH v3] examples/l3fwd: exact-match rework

Tomasz Kulasek tomaszx.kulasek at intel.com
Mon Feb 29 11:33:07 CET 2016


Current implementation of Exact-Match uses different execution path than
for LPM. Unifying them allows to reuse big part of LPM code and sightly
increase performance of Exact-Match.

Main changes:
-------------
* Packet classification stage is separated from the rest of path for both
  LPM and EM.
* Packet processing, modifying and transmit part is the same for LPM and EM
  and mostly based on the current LPM implementation.
* Shared code is moved to the common file "l3fwd_sse.h".
* While sequential packet classification in EM path, seems to be faster
  than using multi hash lookup, used before, it is used by default. Old
  implementation is moved to the file l3fwd_em_hlm_sse.h and can be enabled
  with HASH_LOOKUP_MULTI global define in compilation time.

This patch depends of Ravi Kerur's "Modify and modularize l3fwd code" and
should be applied after it.

Changes in v3:
 - fixed error: unused function 'l3fwd_em_simple_forward'. This function is
   used only in l3fwd_em_no_opt_send_packets, and after moving it to new
   header file l3fwd_em.h in Ravi's patch, also should be moved there.

Changes in v2:
 - patch rebase to be applicable on top of "Modify and modularize l3fwd
   code" v3

Signed-off-by: Tomasz Kulasek <tomaszx.kulasek at intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev at intel.com>
---
 examples/l3fwd/l3fwd.h            |    8 +
 examples/l3fwd/l3fwd_em.c         |   80 +-----
 examples/l3fwd/l3fwd_em.h         |   68 +++++
 examples/l3fwd/l3fwd_em_hlm_sse.h |  341 +++++++++++++++++++++++++
 examples/l3fwd/l3fwd_em_sse.h     |  447 +++-----------------------------
 examples/l3fwd/l3fwd_lpm.c        |   15 +-
 examples/l3fwd/l3fwd_lpm_sse.h    |  507 ++++---------------------------------
 examples/l3fwd/l3fwd_sse.h        |  501 ++++++++++++++++++++++++++++++++++++
 8 files changed, 1011 insertions(+), 956 deletions(-)
 create mode 100644 examples/l3fwd/l3fwd_em_hlm_sse.h
 create mode 100644 examples/l3fwd/l3fwd_sse.h

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index f450269..da6d369 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -53,6 +53,14 @@
 /* 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
+
 /* Hash parameters. */
 #ifdef RTE_ARCH_X86_64
 /* default to 4 million hash entries (approx) */
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index ace06cf..f6a65d8 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -300,81 +300,17 @@ em_get_ipv6_dst_port(void *ipv6_hdr,  uint8_t portid, void *lookup_struct)
 	return (uint8_t)((ret < 0) ? portid : ipv6_l3fwd_out_if[ret]);
 }
 
-static inline __attribute__((always_inline)) void
-l3fwd_em_simple_forward(struct rte_mbuf *m, uint8_t portid,
-		struct lcore_conf *qconf)
-{
-	struct ether_hdr *eth_hdr;
-	struct ipv4_hdr *ipv4_hdr;
-	uint8_t dst_port;
-
-	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
-
-	if (RTE_ETH_IS_IPV4_HDR(m->packet_type)) {
-		/* Handle IPv4 headers.*/
-		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct ipv4_hdr *,
-						   sizeof(struct 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 = em_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 */
-		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 ipv6_hdr *ipv6_hdr;
-
-		ipv6_hdr = rte_pktmbuf_mtod_offset(m, struct ipv6_hdr *,
-						   sizeof(struct ether_hdr));
-
-		dst_port = em_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 */
-		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);
-	}
-}
-
 /*
  * Include header file if SSE4_1 is enabled for
  * buffer optimization i.e. ENABLE_MULTI_BUFFER_OPTIMIZE=1.
  */
 #if defined(__SSE4_1__)
+#ifndef HASH_MULTI_LOOKUP
 #include "l3fwd_em_sse.h"
 #else
+#include "l3fwd_em_hlm_sse.h"
+#endif
+#else
 #include "l3fwd_em.h"
 #endif
 
@@ -572,6 +508,7 @@ populate_ipv6_many_flow_into_table(const struct rte_hash *h,
 	printf("Hash: Adding 0x%x keys\n", nr_flow);
 }
 
+/* main processing loop */
 int
 em_main_loop(__attribute__((unused)) void *dummy)
 {
@@ -615,11 +552,8 @@ em_main_loop(__attribute__((unused)) void *dummy)
 		diff_tsc = cur_tsc - prev_tsc;
 		if (unlikely(diff_tsc > drain_tsc)) {
 
-			/*
-			 * This could be optimized (use queueid instead of
-			 * portid), but it is not called so often
-			 */
-			for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+			for (i = 0; i < qconf->n_rx_queue; i++) {
+				portid = qconf->rx_queue_list[i].port_id;
 				if (qconf->tx_mbufs[portid].len == 0)
 					continue;
 				send_burst(qconf,
diff --git a/examples/l3fwd/l3fwd_em.h b/examples/l3fwd/l3fwd_em.h
index 3232d0a..64ea7f7 100644
--- a/examples/l3fwd/l3fwd_em.h
+++ b/examples/l3fwd/l3fwd_em.h
@@ -34,6 +34,74 @@
 #ifndef __L3FWD_EM_H__
 #define __L3FWD_EM_H__
 
+static inline __attribute__((always_inline)) void
+l3fwd_em_simple_forward(struct rte_mbuf *m, uint8_t portid,
+		struct lcore_conf *qconf)
+{
+	struct ether_hdr *eth_hdr;
+	struct ipv4_hdr *ipv4_hdr;
+	uint8_t dst_port;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+
+	if (RTE_ETH_IS_IPV4_HDR(m->packet_type)) {
+		/* Handle IPv4 headers.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct ipv4_hdr *,
+						   sizeof(struct 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 = em_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 */
+		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 ipv6_hdr *ipv6_hdr;
+
+		ipv6_hdr = rte_pktmbuf_mtod_offset(m, struct ipv6_hdr *,
+						   sizeof(struct ether_hdr));
+
+		dst_port = em_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 */
+		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);
+	}
+}
+
 /*
  * Buffer non-optimized handling of packets, invoked
  * from main_loop.
diff --git a/examples/l3fwd/l3fwd_em_hlm_sse.h b/examples/l3fwd/l3fwd_em_hlm_sse.h
new file mode 100644
index 0000000..d3388da
--- /dev/null
+++ b/examples/l3fwd/l3fwd_em_hlm_sse.h
@@ -0,0 +1,341 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __L3FWD_EM_HLM_SSE_H__
+#define __L3FWD_EM_HLM_SSE_H__
+
+/**
+ * @file
+ * This is an optional implementation of packet classification in Exact-Match
+ * path using rte_hash_lookup_multi method from previous implementation.
+ * While sequential classification seems to be faster, it's disabled by default
+ * and can be enabled with HASH_LOOKUP_MULTI global define in compilation time.
+ */
+
+#include "l3fwd_sse.h"
+
+static inline void
+em_get_dst_port_ipv4x8(struct lcore_conf *qconf, struct rte_mbuf *m[8],
+		uint8_t portid, uint16_t dst_port[8])
+{
+	int32_t ret[8];
+	union ipv4_5tuple_host key[8];
+	__m128i data[8];
+
+	data[0] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[0], __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv4_hdr, time_to_live)));
+	data[1] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[1], __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv4_hdr, time_to_live)));
+	data[2] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[2], __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv4_hdr, time_to_live)));
+	data[3] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[3], __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv4_hdr, time_to_live)));
+	data[4] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[4], __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv4_hdr, time_to_live)));
+	data[5] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[5], __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv4_hdr, time_to_live)));
+	data[6] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[6], __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv4_hdr, time_to_live)));
+	data[7] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[7], __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv4_hdr, time_to_live)));
+
+	key[0].xmm = _mm_and_si128(data[0], mask0);
+	key[1].xmm = _mm_and_si128(data[1], mask0);
+	key[2].xmm = _mm_and_si128(data[2], mask0);
+	key[3].xmm = _mm_and_si128(data[3], mask0);
+	key[4].xmm = _mm_and_si128(data[4], mask0);
+	key[5].xmm = _mm_and_si128(data[5], mask0);
+	key[6].xmm = _mm_and_si128(data[6], mask0);
+	key[7].xmm = _mm_and_si128(data[7], mask0);
+
+	const void *key_array[8] = {&key[0], &key[1], &key[2], &key[3],
+				&key[4], &key[5], &key[6], &key[7]};
+
+	rte_hash_lookup_multi(qconf->ipv4_lookup_struct, &key_array[0], 8, ret);
+
+	dst_port[0] = (uint8_t) ((ret[0] < 0) ?
+			portid : ipv4_l3fwd_out_if[ret[0]]);
+	dst_port[1] = (uint8_t) ((ret[1] < 0) ?
+			portid : ipv4_l3fwd_out_if[ret[1]]);
+	dst_port[2] = (uint8_t) ((ret[2] < 0) ?
+			portid : ipv4_l3fwd_out_if[ret[2]]);
+	dst_port[3] = (uint8_t) ((ret[3] < 0) ?
+			portid : ipv4_l3fwd_out_if[ret[3]]);
+	dst_port[4] = (uint8_t) ((ret[4] < 0) ?
+			portid : ipv4_l3fwd_out_if[ret[4]]);
+	dst_port[5] = (uint8_t) ((ret[5] < 0) ?
+			portid : ipv4_l3fwd_out_if[ret[5]]);
+	dst_port[6] = (uint8_t) ((ret[6] < 0) ?
+			portid : ipv4_l3fwd_out_if[ret[6]]);
+	dst_port[7] = (uint8_t) ((ret[7] < 0) ?
+			portid : ipv4_l3fwd_out_if[ret[7]]);
+
+	if (dst_port[0] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[0]) == 0)
+		dst_port[0] = portid;
+
+	if (dst_port[1] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[1]) == 0)
+		dst_port[1] = portid;
+
+	if (dst_port[2] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[2]) == 0)
+		dst_port[2] = portid;
+
+	if (dst_port[3] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[3]) == 0)
+		dst_port[3] = portid;
+
+	if (dst_port[4] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[4]) == 0)
+		dst_port[4] = portid;
+
+	if (dst_port[5] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[5]) == 0)
+		dst_port[5] = portid;
+
+	if (dst_port[6] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[6]) == 0)
+		dst_port[6] = portid;
+
+	if (dst_port[7] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[7]) == 0)
+		dst_port[7] = portid;
+
+}
+
+static inline void
+get_ipv6_5tuple(struct rte_mbuf *m0, __m128i mask0,
+		__m128i mask1, union ipv6_5tuple_host *key)
+{
+	__m128i tmpdata0 = _mm_loadu_si128(
+			rte_pktmbuf_mtod_offset(m0, __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv6_hdr, payload_len)));
+
+	__m128i tmpdata1 = _mm_loadu_si128(
+			rte_pktmbuf_mtod_offset(m0, __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv6_hdr, payload_len) +
+				sizeof(__m128i)));
+
+	__m128i tmpdata2 = _mm_loadu_si128(
+			rte_pktmbuf_mtod_offset(m0, __m128i *,
+				sizeof(struct ether_hdr) +
+				offsetof(struct ipv6_hdr, payload_len) +
+				sizeof(__m128i) + sizeof(__m128i)));
+
+	key->xmm[0] = _mm_and_si128(tmpdata0, mask0);
+	key->xmm[1] = tmpdata1;
+	key->xmm[2] = _mm_and_si128(tmpdata2, mask1);
+}
+
+static inline void
+em_get_dst_port_ipv6x8(struct lcore_conf *qconf, struct rte_mbuf *m[8],
+		uint8_t portid, uint16_t dst_port[8])
+{
+	int32_t ret[8];
+	union ipv6_5tuple_host key[8];
+
+	get_ipv6_5tuple(m[0], mask1, mask2, &key[0]);
+	get_ipv6_5tuple(m[1], mask1, mask2, &key[1]);
+	get_ipv6_5tuple(m[2], mask1, mask2, &key[2]);
+	get_ipv6_5tuple(m[3], mask1, mask2, &key[3]);
+	get_ipv6_5tuple(m[4], mask1, mask2, &key[4]);
+	get_ipv6_5tuple(m[5], mask1, mask2, &key[5]);
+	get_ipv6_5tuple(m[6], mask1, mask2, &key[6]);
+	get_ipv6_5tuple(m[7], mask1, mask2, &key[7]);
+
+	const void *key_array[8] = {&key[0], &key[1], &key[2], &key[3],
+			&key[4], &key[5], &key[6], &key[7]};
+
+	rte_hash_lookup_multi(qconf->ipv6_lookup_struct, &key_array[0], 8, ret);
+
+	dst_port[0] = (uint8_t) ((ret[0] < 0) ?
+			portid : ipv6_l3fwd_out_if[ret[0]]);
+	dst_port[1] = (uint8_t) ((ret[1] < 0) ?
+			portid : ipv6_l3fwd_out_if[ret[1]]);
+	dst_port[2] = (uint8_t) ((ret[2] < 0) ?
+			portid : ipv6_l3fwd_out_if[ret[2]]);
+	dst_port[3] = (uint8_t) ((ret[3] < 0) ?
+			portid : ipv6_l3fwd_out_if[ret[3]]);
+	dst_port[4] = (uint8_t) ((ret[4] < 0) ?
+			portid : ipv6_l3fwd_out_if[ret[4]]);
+	dst_port[5] = (uint8_t) ((ret[5] < 0) ?
+			portid : ipv6_l3fwd_out_if[ret[5]]);
+	dst_port[6] = (uint8_t) ((ret[6] < 0) ?
+			portid : ipv6_l3fwd_out_if[ret[6]]);
+	dst_port[7] = (uint8_t) ((ret[7] < 0) ?
+			portid : ipv6_l3fwd_out_if[ret[7]]);
+
+	if (dst_port[0] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[0]) == 0)
+		dst_port[0] = portid;
+
+	if (dst_port[1] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[1]) == 0)
+		dst_port[1] = portid;
+
+	if (dst_port[2] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[2]) == 0)
+		dst_port[2] = portid;
+
+	if (dst_port[3] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[3]) == 0)
+		dst_port[3] = portid;
+
+	if (dst_port[4] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[4]) == 0)
+		dst_port[4] = portid;
+
+	if (dst_port[5] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[5]) == 0)
+		dst_port[5] = portid;
+
+	if (dst_port[6] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[6]) == 0)
+		dst_port[6] = portid;
+
+	if (dst_port[7] >= RTE_MAX_ETHPORTS ||
+			(enabled_port_mask & 1 << dst_port[7]) == 0)
+		dst_port[7] = portid;
+
+}
+
+static inline __attribute__((always_inline)) uint16_t
+em_get_dst_port(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
+		uint8_t portid)
+{
+	uint8_t next_hop;
+	struct ipv4_hdr *ipv4_hdr;
+	struct ipv6_hdr *ipv6_hdr;
+
+	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
+
+		/* Handle IPv4 headers.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct ipv4_hdr *,
+				sizeof(struct ether_hdr));
+
+		next_hop = em_get_ipv4_dst_port(ipv4_hdr, portid,
+				qconf->ipv4_lookup_struct);
+
+		if (next_hop >= RTE_MAX_ETHPORTS ||
+				(enabled_port_mask & 1 << next_hop) == 0)
+			next_hop = portid;
+
+		return next_hop;
+
+	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
+
+		/* Handle IPv6 headers.*/
+		ipv6_hdr = rte_pktmbuf_mtod_offset(pkt, struct ipv6_hdr *,
+				sizeof(struct ether_hdr));
+
+		next_hop = em_get_ipv6_dst_port(ipv6_hdr, portid,
+				qconf->ipv6_lookup_struct);
+
+		if (next_hop >= RTE_MAX_ETHPORTS ||
+				(enabled_port_mask & 1 << next_hop) == 0)
+			next_hop = portid;
+
+		return next_hop;
+
+	}
+
+	return portid;
+}
+
+/*
+ * Buffer optimized handling of packets, invoked
+ * from main_loop.
+ */
+static inline void
+l3fwd_em_send_packets(int nb_rx, struct rte_mbuf **pkts_burst,
+		uint8_t portid, struct lcore_conf *qconf)
+{
+	int32_t j;
+	uint16_t dst_port[MAX_PKT_BURST];
+
+	/*
+	 * Send nb_rx - nb_rx%8 packets
+	 * in groups of 8.
+	 */
+	int32_t n = RTE_ALIGN_FLOOR(nb_rx, 8);
+
+	for (j = 0; j < n; j += 8) {
+
+		uint32_t pkt_type =
+			pkts_burst[j]->packet_type &
+			pkts_burst[j+1]->packet_type &
+			pkts_burst[j+2]->packet_type &
+			pkts_burst[j+3]->packet_type &
+			pkts_burst[j+4]->packet_type &
+			pkts_burst[j+5]->packet_type &
+			pkts_burst[j+6]->packet_type &
+			pkts_burst[j+7]->packet_type;
+
+		if (pkt_type & RTE_PTYPE_L3_IPV4) {
+
+			em_get_dst_port_ipv4x8(qconf, &pkts_burst[j], portid, &dst_port[j]);
+
+		} else if (pkt_type & RTE_PTYPE_L3_IPV6) {
+
+			em_get_dst_port_ipv6x8(qconf, &pkts_burst[j], portid, &dst_port[j]);
+
+		} else {
+			dst_port[j]   = em_get_dst_port(qconf, pkts_burst[j], portid);
+			dst_port[j+1] = em_get_dst_port(qconf, pkts_burst[j], portid);
+			dst_port[j+2] = em_get_dst_port(qconf, pkts_burst[j], portid);
+			dst_port[j+3] = em_get_dst_port(qconf, pkts_burst[j], portid);
+			dst_port[j+4] = em_get_dst_port(qconf, pkts_burst[j], portid);
+			dst_port[j+5] = em_get_dst_port(qconf, pkts_burst[j], portid);
+			dst_port[j+6] = em_get_dst_port(qconf, pkts_burst[j], portid);
+			dst_port[j+7] = em_get_dst_port(qconf, pkts_burst[j], portid);
+		}
+	}
+
+	for (; j < n; j++)
+		dst_port[j] = em_get_dst_port(qconf, pkts_burst[j], portid);
+
+	send_packets_multi(qconf, pkts_burst, dst_port, nb_rx);
+
+}
+#endif /* __L3FWD_EM_SSE_HLM_H__ */
diff --git a/examples/l3fwd/l3fwd_em_sse.h b/examples/l3fwd/l3fwd_em_sse.h
index b955685..4c6d14f 100644
--- a/examples/l3fwd/l3fwd_em_sse.h
+++ b/examples/l3fwd/l3fwd_em_sse.h
@@ -34,397 +34,49 @@
 #ifndef __L3FWD_EM_SSE_H__
 #define __L3FWD_EM_SSE_H__
 
-#define MASK_ALL_PKTS   0xff
-#define EXCLUDE_1ST_PKT 0xfe
-#define EXCLUDE_2ND_PKT 0xfd
-#define EXCLUDE_3RD_PKT 0xfb
-#define EXCLUDE_4TH_PKT 0xf7
-#define EXCLUDE_5TH_PKT 0xef
-#define EXCLUDE_6TH_PKT 0xdf
-#define EXCLUDE_7TH_PKT 0xbf
-#define EXCLUDE_8TH_PKT 0x7f
+#include "l3fwd_sse.h"
 
-static inline void
-simple_ipv4_fwd_8pkts(struct rte_mbuf *m[8], uint8_t portid,
-			struct lcore_conf *qconf)
+static inline __attribute__((always_inline)) uint16_t
+em_get_dst_port(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
+		uint8_t portid)
 {
-	struct ether_hdr *eth_hdr[8];
-	struct ipv4_hdr *ipv4_hdr[8];
-	uint8_t dst_port[8];
-	int32_t ret[8];
-	union ipv4_5tuple_host key[8];
-	__m128i data[8];
-
-	eth_hdr[0] = rte_pktmbuf_mtod(m[0], struct ether_hdr *);
-	eth_hdr[1] = rte_pktmbuf_mtod(m[1], struct ether_hdr *);
-	eth_hdr[2] = rte_pktmbuf_mtod(m[2], struct ether_hdr *);
-	eth_hdr[3] = rte_pktmbuf_mtod(m[3], struct ether_hdr *);
-	eth_hdr[4] = rte_pktmbuf_mtod(m[4], struct ether_hdr *);
-	eth_hdr[5] = rte_pktmbuf_mtod(m[5], struct ether_hdr *);
-	eth_hdr[6] = rte_pktmbuf_mtod(m[6], struct ether_hdr *);
-	eth_hdr[7] = rte_pktmbuf_mtod(m[7], struct ether_hdr *);
-
-	/* Handle IPv4 headers.*/
-	ipv4_hdr[0] = rte_pktmbuf_mtod_offset(m[0], struct ipv4_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv4_hdr[1] = rte_pktmbuf_mtod_offset(m[1], struct ipv4_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv4_hdr[2] = rte_pktmbuf_mtod_offset(m[2], struct ipv4_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv4_hdr[3] = rte_pktmbuf_mtod_offset(m[3], struct ipv4_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv4_hdr[4] = rte_pktmbuf_mtod_offset(m[4], struct ipv4_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv4_hdr[5] = rte_pktmbuf_mtod_offset(m[5], struct ipv4_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv4_hdr[6] = rte_pktmbuf_mtod_offset(m[6], struct ipv4_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv4_hdr[7] = rte_pktmbuf_mtod_offset(m[7], struct ipv4_hdr *,
-					      sizeof(struct ether_hdr));
-
-#ifdef DO_RFC_1812_CHECKS
-	/* Check to make sure the packet is valid (RFC1812) */
-	uint8_t valid_mask = MASK_ALL_PKTS;
-
-	if (is_valid_ipv4_pkt(ipv4_hdr[0], m[0]->pkt_len) < 0) {
-		rte_pktmbuf_free(m[0]);
-		valid_mask &= EXCLUDE_1ST_PKT;
-	}
-	if (is_valid_ipv4_pkt(ipv4_hdr[1], m[1]->pkt_len) < 0) {
-		rte_pktmbuf_free(m[1]);
-		valid_mask &= EXCLUDE_2ND_PKT;
-	}
-	if (is_valid_ipv4_pkt(ipv4_hdr[2], m[2]->pkt_len) < 0) {
-		rte_pktmbuf_free(m[2]);
-		valid_mask &= EXCLUDE_3RD_PKT;
-	}
-	if (is_valid_ipv4_pkt(ipv4_hdr[3], m[3]->pkt_len) < 0) {
-		rte_pktmbuf_free(m[3]);
-		valid_mask &= EXCLUDE_4TH_PKT;
-	}
-	if (is_valid_ipv4_pkt(ipv4_hdr[4], m[4]->pkt_len) < 0) {
-		rte_pktmbuf_free(m[4]);
-		valid_mask &= EXCLUDE_5TH_PKT;
-	}
-	if (is_valid_ipv4_pkt(ipv4_hdr[5], m[5]->pkt_len) < 0) {
-		rte_pktmbuf_free(m[5]);
-		valid_mask &= EXCLUDE_6TH_PKT;
-	}
-	if (is_valid_ipv4_pkt(ipv4_hdr[6], m[6]->pkt_len) < 0) {
-		rte_pktmbuf_free(m[6]);
-		valid_mask &= EXCLUDE_7TH_PKT;
-	}
-	if (is_valid_ipv4_pkt(ipv4_hdr[7], m[7]->pkt_len) < 0) {
-		rte_pktmbuf_free(m[7]);
-		valid_mask &= EXCLUDE_8TH_PKT;
-	}
-	if (unlikely(valid_mask != MASK_ALL_PKTS)) {
-		if (valid_mask == 0) {
-			return;
-		} else {
-			uint8_t i = 0;
+	uint8_t next_hop;
+	struct ipv4_hdr *ipv4_hdr;
+	struct ipv6_hdr *ipv6_hdr;
 
-			for (i = 0; i < 8; i++) {
-				if ((0x1 << i) & valid_mask) {
-					l3fwd_em_simple_forward(m[i],
-							portid, qconf);
-				}
-			}
-			return;
-		}
-	}
-#endif /* End of #ifdef DO_RFC_1812_CHECKS */
-
-	data[0] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[0], __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv4_hdr, time_to_live)));
-	data[1] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[1], __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv4_hdr, time_to_live)));
-	data[2] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[2], __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv4_hdr, time_to_live)));
-	data[3] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[3], __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv4_hdr, time_to_live)));
-	data[4] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[4], __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv4_hdr, time_to_live)));
-	data[5] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[5], __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv4_hdr, time_to_live)));
-	data[6] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[6], __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv4_hdr, time_to_live)));
-	data[7] = _mm_loadu_si128(rte_pktmbuf_mtod_offset(m[7], __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv4_hdr, time_to_live)));
-
-	key[0].xmm = _mm_and_si128(data[0], mask0);
-	key[1].xmm = _mm_and_si128(data[1], mask0);
-	key[2].xmm = _mm_and_si128(data[2], mask0);
-	key[3].xmm = _mm_and_si128(data[3], mask0);
-	key[4].xmm = _mm_and_si128(data[4], mask0);
-	key[5].xmm = _mm_and_si128(data[5], mask0);
-	key[6].xmm = _mm_and_si128(data[6], mask0);
-	key[7].xmm = _mm_and_si128(data[7], mask0);
-
-	const void *key_array[8] = {&key[0], &key[1], &key[2], &key[3],
-				&key[4], &key[5], &key[6], &key[7]};
-
-	rte_hash_lookup_multi(qconf->ipv4_lookup_struct, &key_array[0], 8, ret);
-	dst_port[0] = (uint8_t) ((ret[0] < 0) ?
-			portid : ipv4_l3fwd_out_if[ret[0]]);
-	dst_port[1] = (uint8_t) ((ret[1] < 0) ?
-			portid : ipv4_l3fwd_out_if[ret[1]]);
-	dst_port[2] = (uint8_t) ((ret[2] < 0) ?
-			portid : ipv4_l3fwd_out_if[ret[2]]);
-	dst_port[3] = (uint8_t) ((ret[3] < 0) ?
-			portid : ipv4_l3fwd_out_if[ret[3]]);
-	dst_port[4] = (uint8_t) ((ret[4] < 0) ?
-			portid : ipv4_l3fwd_out_if[ret[4]]);
-	dst_port[5] = (uint8_t) ((ret[5] < 0) ?
-			portid : ipv4_l3fwd_out_if[ret[5]]);
-	dst_port[6] = (uint8_t) ((ret[6] < 0) ?
-			portid : ipv4_l3fwd_out_if[ret[6]]);
-	dst_port[7] = (uint8_t) ((ret[7] < 0) ?
-			portid : ipv4_l3fwd_out_if[ret[7]]);
-
-	if (dst_port[0] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[0]) == 0)
-		dst_port[0] = portid;
-
-	if (dst_port[1] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[1]) == 0)
-		dst_port[1] = portid;
+	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
 
-	if (dst_port[2] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[2]) == 0)
-		dst_port[2] = portid;
+		/* Handle IPv4 headers.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct ipv4_hdr *,
+				sizeof(struct ether_hdr));
 
-	if (dst_port[3] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[3]) == 0)
-		dst_port[3] = portid;
+		next_hop = em_get_ipv4_dst_port(ipv4_hdr, portid,
+				qconf->ipv4_lookup_struct);
 
-	if (dst_port[4] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[4]) == 0)
-		dst_port[4] = portid;
+		if (next_hop >= RTE_MAX_ETHPORTS ||
+				(enabled_port_mask & 1 << next_hop) == 0)
+			next_hop = portid;
 
-	if (dst_port[5] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[5]) == 0)
-		dst_port[5] = portid;
+		return next_hop;
 
-	if (dst_port[6] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[6]) == 0)
-		dst_port[6] = portid;
+	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
 
-	if (dst_port[7] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[7]) == 0)
-		dst_port[7] = portid;
+		/* Handle IPv6 headers.*/
+		ipv6_hdr = rte_pktmbuf_mtod_offset(pkt, struct ipv6_hdr *,
+				sizeof(struct ether_hdr));
 
-#ifdef DO_RFC_1812_CHECKS
-	/* Update time to live and header checksum */
-	--(ipv4_hdr[0]->time_to_live);
-	--(ipv4_hdr[1]->time_to_live);
-	--(ipv4_hdr[2]->time_to_live);
-	--(ipv4_hdr[3]->time_to_live);
-	++(ipv4_hdr[0]->hdr_checksum);
-	++(ipv4_hdr[1]->hdr_checksum);
-	++(ipv4_hdr[2]->hdr_checksum);
-	++(ipv4_hdr[3]->hdr_checksum);
-	--(ipv4_hdr[4]->time_to_live);
-	--(ipv4_hdr[5]->time_to_live);
-	--(ipv4_hdr[6]->time_to_live);
-	--(ipv4_hdr[7]->time_to_live);
-	++(ipv4_hdr[4]->hdr_checksum);
-	++(ipv4_hdr[5]->hdr_checksum);
-	++(ipv4_hdr[6]->hdr_checksum);
-	++(ipv4_hdr[7]->hdr_checksum);
-#endif
+		next_hop = em_get_ipv6_dst_port(ipv6_hdr, portid,
+				qconf->ipv6_lookup_struct);
 
-	/* dst addr */
-	*(uint64_t *)&eth_hdr[0]->d_addr = dest_eth_addr[dst_port[0]];
-	*(uint64_t *)&eth_hdr[1]->d_addr = dest_eth_addr[dst_port[1]];
-	*(uint64_t *)&eth_hdr[2]->d_addr = dest_eth_addr[dst_port[2]];
-	*(uint64_t *)&eth_hdr[3]->d_addr = dest_eth_addr[dst_port[3]];
-	*(uint64_t *)&eth_hdr[4]->d_addr = dest_eth_addr[dst_port[4]];
-	*(uint64_t *)&eth_hdr[5]->d_addr = dest_eth_addr[dst_port[5]];
-	*(uint64_t *)&eth_hdr[6]->d_addr = dest_eth_addr[dst_port[6]];
-	*(uint64_t *)&eth_hdr[7]->d_addr = dest_eth_addr[dst_port[7]];
-
-	/* src addr */
-	ether_addr_copy(&ports_eth_addr[dst_port[0]], &eth_hdr[0]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[1]], &eth_hdr[1]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[2]], &eth_hdr[2]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[3]], &eth_hdr[3]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[4]], &eth_hdr[4]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[5]], &eth_hdr[5]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[6]], &eth_hdr[6]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[7]], &eth_hdr[7]->s_addr);
-
-	send_single_packet(qconf, m[0], (uint8_t)dst_port[0]);
-	send_single_packet(qconf, m[1], (uint8_t)dst_port[1]);
-	send_single_packet(qconf, m[2], (uint8_t)dst_port[2]);
-	send_single_packet(qconf, m[3], (uint8_t)dst_port[3]);
-	send_single_packet(qconf, m[4], (uint8_t)dst_port[4]);
-	send_single_packet(qconf, m[5], (uint8_t)dst_port[5]);
-	send_single_packet(qconf, m[6], (uint8_t)dst_port[6]);
-	send_single_packet(qconf, m[7], (uint8_t)dst_port[7]);
-}
-
-static inline void
-get_ipv6_5tuple(struct rte_mbuf *m0, __m128i mask0,
-		__m128i mask1, union ipv6_5tuple_host *key)
-{
-	__m128i tmpdata0 = _mm_loadu_si128(
-			rte_pktmbuf_mtod_offset(m0, __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv6_hdr, payload_len)));
+		if (next_hop >= RTE_MAX_ETHPORTS ||
+				(enabled_port_mask & 1 << next_hop) == 0)
+			next_hop = portid;
 
-	__m128i tmpdata1 = _mm_loadu_si128(
-			rte_pktmbuf_mtod_offset(m0, __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv6_hdr, payload_len) +
-				sizeof(__m128i)));
+		return next_hop;
 
-	__m128i tmpdata2 = _mm_loadu_si128(
-			rte_pktmbuf_mtod_offset(m0, __m128i *,
-				sizeof(struct ether_hdr) +
-				offsetof(struct ipv6_hdr, payload_len) +
-				sizeof(__m128i) + sizeof(__m128i)));
-
-	key->xmm[0] = _mm_and_si128(tmpdata0, mask0);
-	key->xmm[1] = tmpdata1;
-	key->xmm[2] = _mm_and_si128(tmpdata2, mask1);
-}
-
-static inline void
-simple_ipv6_fwd_8pkts(struct rte_mbuf *m[8], uint8_t portid,
-			struct lcore_conf *qconf)
-{
-	struct ether_hdr *eth_hdr[8];
-	__attribute__((unused)) struct ipv6_hdr *ipv6_hdr[8];
-	uint8_t dst_port[8];
-	int32_t ret[8];
-	union ipv6_5tuple_host key[8];
-
-	eth_hdr[0] = rte_pktmbuf_mtod(m[0], struct ether_hdr *);
-	eth_hdr[1] = rte_pktmbuf_mtod(m[1], struct ether_hdr *);
-	eth_hdr[2] = rte_pktmbuf_mtod(m[2], struct ether_hdr *);
-	eth_hdr[3] = rte_pktmbuf_mtod(m[3], struct ether_hdr *);
-	eth_hdr[4] = rte_pktmbuf_mtod(m[4], struct ether_hdr *);
-	eth_hdr[5] = rte_pktmbuf_mtod(m[5], struct ether_hdr *);
-	eth_hdr[6] = rte_pktmbuf_mtod(m[6], struct ether_hdr *);
-	eth_hdr[7] = rte_pktmbuf_mtod(m[7], struct ether_hdr *);
-
-	/* Handle IPv6 headers.*/
-	ipv6_hdr[0] = rte_pktmbuf_mtod_offset(m[0], struct ipv6_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv6_hdr[1] = rte_pktmbuf_mtod_offset(m[1], struct ipv6_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv6_hdr[2] = rte_pktmbuf_mtod_offset(m[2], struct ipv6_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv6_hdr[3] = rte_pktmbuf_mtod_offset(m[3], struct ipv6_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv6_hdr[4] = rte_pktmbuf_mtod_offset(m[4], struct ipv6_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv6_hdr[5] = rte_pktmbuf_mtod_offset(m[5], struct ipv6_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv6_hdr[6] = rte_pktmbuf_mtod_offset(m[6], struct ipv6_hdr *,
-					      sizeof(struct ether_hdr));
-	ipv6_hdr[7] = rte_pktmbuf_mtod_offset(m[7], struct ipv6_hdr *,
-					      sizeof(struct ether_hdr));
-
-	get_ipv6_5tuple(m[0], mask1, mask2, &key[0]);
-	get_ipv6_5tuple(m[1], mask1, mask2, &key[1]);
-	get_ipv6_5tuple(m[2], mask1, mask2, &key[2]);
-	get_ipv6_5tuple(m[3], mask1, mask2, &key[3]);
-	get_ipv6_5tuple(m[4], mask1, mask2, &key[4]);
-	get_ipv6_5tuple(m[5], mask1, mask2, &key[5]);
-	get_ipv6_5tuple(m[6], mask1, mask2, &key[6]);
-	get_ipv6_5tuple(m[7], mask1, mask2, &key[7]);
-
-	const void *key_array[8] = {&key[0], &key[1], &key[2], &key[3],
-				&key[4], &key[5], &key[6], &key[7]};
-
-	rte_hash_lookup_multi(qconf->ipv6_lookup_struct, &key_array[0], 8, ret);
-	dst_port[0] = (uint8_t) ((ret[0] < 0) ?
-		portid : ipv6_l3fwd_out_if[ret[0]]);
-	dst_port[1] = (uint8_t) ((ret[1] < 0) ?
-		portid : ipv6_l3fwd_out_if[ret[1]]);
-	dst_port[2] = (uint8_t) ((ret[2] < 0) ?
-		portid : ipv6_l3fwd_out_if[ret[2]]);
-	dst_port[3] = (uint8_t) ((ret[3] < 0) ?
-		portid : ipv6_l3fwd_out_if[ret[3]]);
-	dst_port[4] = (uint8_t) ((ret[4] < 0) ?
-		portid : ipv6_l3fwd_out_if[ret[4]]);
-	dst_port[5] = (uint8_t) ((ret[5] < 0) ?
-		portid : ipv6_l3fwd_out_if[ret[5]]);
-	dst_port[6] = (uint8_t) ((ret[6] < 0) ?
-		portid : ipv6_l3fwd_out_if[ret[6]]);
-	dst_port[7] = (uint8_t) ((ret[7] < 0) ?
-		portid : ipv6_l3fwd_out_if[ret[7]]);
-
-	if (dst_port[0] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[0]) == 0)
-		dst_port[0] = portid;
-
-	if (dst_port[1] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[1]) == 0)
-		dst_port[1] = portid;
-
-	if (dst_port[2] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[2]) == 0)
-		dst_port[2] = portid;
-
-	if (dst_port[3] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[3]) == 0)
-		dst_port[3] = portid;
-
-	if (dst_port[4] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[4]) == 0)
-		dst_port[4] = portid;
-
-	if (dst_port[5] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[5]) == 0)
-		dst_port[5] = portid;
-
-	if (dst_port[6] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[6]) == 0)
-		dst_port[6] = portid;
-
-	if (dst_port[7] >= RTE_MAX_ETHPORTS ||
-		(enabled_port_mask & 1 << dst_port[7]) == 0)
-		dst_port[7] = portid;
-
-	/* dst addr */
-	*(uint64_t *)&eth_hdr[0]->d_addr = dest_eth_addr[dst_port[0]];
-	*(uint64_t *)&eth_hdr[1]->d_addr = dest_eth_addr[dst_port[1]];
-	*(uint64_t *)&eth_hdr[2]->d_addr = dest_eth_addr[dst_port[2]];
-	*(uint64_t *)&eth_hdr[3]->d_addr = dest_eth_addr[dst_port[3]];
-	*(uint64_t *)&eth_hdr[4]->d_addr = dest_eth_addr[dst_port[4]];
-	*(uint64_t *)&eth_hdr[5]->d_addr = dest_eth_addr[dst_port[5]];
-	*(uint64_t *)&eth_hdr[6]->d_addr = dest_eth_addr[dst_port[6]];
-	*(uint64_t *)&eth_hdr[7]->d_addr = dest_eth_addr[dst_port[7]];
-
-	/* src addr */
-	ether_addr_copy(&ports_eth_addr[dst_port[0]], &eth_hdr[0]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[1]], &eth_hdr[1]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[2]], &eth_hdr[2]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[3]], &eth_hdr[3]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[4]], &eth_hdr[4]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[5]], &eth_hdr[5]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[6]], &eth_hdr[6]->s_addr);
-	ether_addr_copy(&ports_eth_addr[dst_port[7]], &eth_hdr[7]->s_addr);
+	}
 
-	send_single_packet(qconf, m[0], (uint8_t)dst_port[0]);
-	send_single_packet(qconf, m[1], (uint8_t)dst_port[1]);
-	send_single_packet(qconf, m[2], (uint8_t)dst_port[2]);
-	send_single_packet(qconf, m[3], (uint8_t)dst_port[3]);
-	send_single_packet(qconf, m[4], (uint8_t)dst_port[4]);
-	send_single_packet(qconf, m[5], (uint8_t)dst_port[5]);
-	send_single_packet(qconf, m[6], (uint8_t)dst_port[6]);
-	send_single_packet(qconf, m[7], (uint8_t)dst_port[7]);
+	return portid;
 }
 
 /*
@@ -436,44 +88,11 @@ l3fwd_em_send_packets(int nb_rx, struct rte_mbuf **pkts_burst,
 			uint8_t portid, struct lcore_conf *qconf)
 {
 	int32_t j;
+	uint16_t dst_port[MAX_PKT_BURST];
 
-	/*
-	 * Send nb_rx - nb_rx%8 packets
-	 * in groups of 8.
-	 */
-	int32_t n = RTE_ALIGN_FLOOR(nb_rx, 8);
-
-	for (j = 0; j < n; j += 8) {
+	for (j = 0; j < nb_rx; j++)
+		dst_port[j] = em_get_dst_port(qconf, pkts_burst[j], portid);
 
-		uint32_t pkt_type =
-			pkts_burst[j]->packet_type &
-			pkts_burst[j+1]->packet_type &
-			pkts_burst[j+2]->packet_type &
-			pkts_burst[j+3]->packet_type &
-			pkts_burst[j+4]->packet_type &
-			pkts_burst[j+5]->packet_type &
-			pkts_burst[j+6]->packet_type &
-			pkts_burst[j+7]->packet_type;
-
-		if (pkt_type & RTE_PTYPE_L3_IPV4) {
-			simple_ipv4_fwd_8pkts(
-				&pkts_burst[j], portid, qconf);
-		} else if (pkt_type & RTE_PTYPE_L3_IPV6) {
-			simple_ipv6_fwd_8pkts(&pkts_burst[j],
-						portid, qconf);
-		} else {
-			l3fwd_em_simple_forward(pkts_burst[j], portid, qconf);
-			l3fwd_em_simple_forward(pkts_burst[j+1], portid, qconf);
-			l3fwd_em_simple_forward(pkts_burst[j+2], portid, qconf);
-			l3fwd_em_simple_forward(pkts_burst[j+3], portid, qconf);
-			l3fwd_em_simple_forward(pkts_burst[j+4], portid, qconf);
-			l3fwd_em_simple_forward(pkts_burst[j+5], portid, qconf);
-			l3fwd_em_simple_forward(pkts_burst[j+6], portid, qconf);
-			l3fwd_em_simple_forward(pkts_burst[j+7], portid, qconf);
-		}
-	}
-	for (; j < nb_rx ; j++)
-		l3fwd_em_simple_forward(pkts_burst[j], portid, qconf);
+	send_packets_multi(qconf, pkts_burst, dst_port, nb_rx);
 }
-
 #endif /* __L3FWD_EM_SSE_H__ */
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index 0640ce7..e0ed3c4 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -101,14 +101,6 @@ static struct ipv6_l3fwd_lpm_route ipv6_l3fwd_lpm_route_array[] = {
 #define IPV6_L3FWD_LPM_MAX_RULES         1024
 #define IPV6_L3FWD_LPM_NUMBER_TBL8S (1 << 16)
 
-/* 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 rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 
@@ -166,11 +158,8 @@ lpm_main_loop(__attribute__((unused)) void *dummy)
 		diff_tsc = cur_tsc - prev_tsc;
 		if (unlikely(diff_tsc > drain_tsc)) {
 
-			/*
-			 * This could be optimized (use queueid instead of
-			 * portid), but it is not called so often
-			 */
-			for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+			for (i = 0; i < qconf->n_rx_queue; i++) {
+				portid = qconf->rx_queue_list[i].port_id;
 				if (qconf->tx_mbufs[portid].len == 0)
 					continue;
 				send_burst(qconf,
diff --git a/examples/l3fwd/l3fwd_lpm_sse.h b/examples/l3fwd/l3fwd_lpm_sse.h
index be78cf1..77326c1 100644
--- a/examples/l3fwd/l3fwd_lpm_sse.h
+++ b/examples/l3fwd/l3fwd_lpm_sse.h
@@ -34,129 +34,46 @@
 #ifndef __L3FWD_LPM_SSE_H__
 #define __L3FWD_LPM_SSE_H__
 
-static inline __attribute__((always_inline)) void
-send_packetsx4(struct lcore_conf *qconf, uint8_t port,
-	struct rte_mbuf *m[], uint32_t num)
+#include "l3fwd_sse.h"
+
+static inline __attribute__((always_inline)) uint16_t
+lpm_get_dst_port(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
+		uint8_t portid)
 {
-	uint32_t len, j, n;
-
-	len = qconf->tx_mbufs[port].len;
-
-	/*
-	 * If TX buffer for that queue is empty, and we have enough packets,
-	 * then send them straightway.
-	 */
-	if (num >= MAX_TX_BURST && len == 0) {
-		n = rte_eth_tx_burst(port, qconf->tx_queue_id[port], m, num);
-		if (unlikely(n < num)) {
-			do {
-				rte_pktmbuf_free(m[n]);
-			} while (++n < num);
-		}
-		return;
-	}
+	uint8_t next_hop;
+	struct ipv6_hdr *ipv6_hdr;
+	struct ipv4_hdr *ipv4_hdr;
+	struct ether_hdr *eth_hdr;
 
-	/*
-	 * Put packets into TX buffer for that queue.
-	 */
+	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
 
-	n = len + num;
-	n = (n > MAX_PKT_BURST) ? MAX_PKT_BURST - len : num;
+		eth_hdr = rte_pktmbuf_mtod(pkt, struct ether_hdr *);
+		ipv4_hdr = (struct ipv4_hdr *)(eth_hdr + 1);
 
-	j = 0;
-	switch (n % FWDSTEP) {
-	while (j < n) {
-	case 0:
-		qconf->tx_mbufs[port].m_table[len + j] = m[j];
-		j++;
-	case 3:
-		qconf->tx_mbufs[port].m_table[len + j] = m[j];
-		j++;
-	case 2:
-		qconf->tx_mbufs[port].m_table[len + j] = m[j];
-		j++;
-	case 1:
-		qconf->tx_mbufs[port].m_table[len + j] = m[j];
-		j++;
-	}
-	}
+		return (uint16_t) ((rte_lpm_lookup(qconf->ipv4_lookup_struct,
+				rte_be_to_cpu_32(ipv4_hdr->dst_addr), &next_hop) == 0) ?
+						next_hop : portid);
 
-	len += n;
-
-	/* enough pkts to be sent */
-	if (unlikely(len == MAX_PKT_BURST)) {
-
-		send_burst(qconf, MAX_PKT_BURST, port);
-
-		/* copy rest of the packets into the TX buffer. */
-		len = num - n;
-		j = 0;
-		switch (len % FWDSTEP) {
-		while (j < len) {
-		case 0:
-			qconf->tx_mbufs[port].m_table[j] = m[n + j];
-			j++;
-		case 3:
-			qconf->tx_mbufs[port].m_table[j] = m[n + j];
-			j++;
-		case 2:
-			qconf->tx_mbufs[port].m_table[j] = m[n + j];
-			j++;
-		case 1:
-			qconf->tx_mbufs[port].m_table[j] = m[n + j];
-			j++;
-		}
-		}
-	}
+	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
 
-	qconf->tx_mbufs[port].len = len;
-}
+		eth_hdr = rte_pktmbuf_mtod(pkt, struct ether_hdr *);
+		ipv6_hdr = (struct ipv6_hdr *)(eth_hdr + 1);
 
-#ifdef DO_RFC_1812_CHECKS
+		return (uint16_t) ((rte_lpm6_lookup(qconf->ipv6_lookup_struct,
+				ipv6_hdr->dst_addr, &next_hop) == 0) ? next_hop : portid);
 
-#define	IPV4_MIN_VER_IHL	0x45
-#define	IPV4_MAX_VER_IHL	0x4f
-#define	IPV4_MAX_VER_IHL_DIFF	(IPV4_MAX_VER_IHL - IPV4_MIN_VER_IHL)
+	}
 
-/* Minimum value of IPV4 total length (20B) in network byte order. */
-#define	IPV4_MIN_LEN_BE	(sizeof(struct ipv4_hdr) << 8)
+	return portid;
+}
 
 /*
- * From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2:
- * - The IP version number must be 4.
- * - The IP header length field must be large enough to hold the
- *    minimum length legal IP datagram (20 bytes = 5 words).
- * - 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 we encounter invalid IPV4 packet, then set destination port for it
- * to BAD_PORT value.
+ * lpm_get_dst_port optimized routine for packets where dst_ipv4 is already
+ * precalculated. If packet is ipv6 dst_addr is taken directly from packet
+ * header and dst_ipv4 value is not used.
  */
-static inline __attribute__((always_inline)) void
-rfc1812_process(struct ipv4_hdr *ipv4_hdr, uint16_t *dp, uint32_t ptype)
-{
-	uint8_t ihl;
-
-	if (RTE_ETH_IS_IPV4_HDR(ptype)) {
-		ihl = ipv4_hdr->version_ihl - IPV4_MIN_VER_IHL;
-
-		ipv4_hdr->time_to_live--;
-		ipv4_hdr->hdr_checksum++;
-
-		if (ihl > IPV4_MAX_VER_IHL_DIFF ||
-				((uint8_t)ipv4_hdr->total_length == 0 &&
-				ipv4_hdr->total_length < IPV4_MIN_LEN_BE)) {
-			dp[0] = BAD_PORT;
-		}
-	}
-}
-
-#else
-#define	rfc1812_process(mb, dp)	do { } while (0)
-#endif /* DO_RFC_1812_CHECKS */
-
 static inline __attribute__((always_inline)) uint16_t
-get_dst_port(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
+lpm_get_dst_port_with_ipv4(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
 	uint32_t dst_ipv4, uint8_t portid)
 {
 	uint8_t next_hop;
@@ -164,47 +81,21 @@ get_dst_port(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
 	struct ether_hdr *eth_hdr;
 
 	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
-		if (rte_lpm_lookup(qconf->ipv4_lookup_struct, dst_ipv4,
-				&next_hop) != 0)
-			next_hop = portid;
+		return (uint16_t) ((rte_lpm_lookup(qconf->ipv4_lookup_struct, dst_ipv4,
+			&next_hop) == 0) ? next_hop : portid);
+
 	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
+
 		eth_hdr = rte_pktmbuf_mtod(pkt, struct ether_hdr *);
 		ipv6_hdr = (struct ipv6_hdr *)(eth_hdr + 1);
-		if (rte_lpm6_lookup(qconf->ipv6_lookup_struct,
-				ipv6_hdr->dst_addr, &next_hop) != 0)
-			next_hop = portid;
-	} else {
-		next_hop = portid;
-	}
-
-	return next_hop;
-}
-
-static inline void
-process_packet(struct lcore_conf *qconf, struct rte_mbuf *pkt,
-	uint16_t *dst_port, uint8_t portid)
-{
-	struct ether_hdr *eth_hdr;
-	struct ipv4_hdr *ipv4_hdr;
-	uint32_t dst_ipv4;
-	uint16_t dp;
-	__m128i te, ve;
-
-	eth_hdr = rte_pktmbuf_mtod(pkt, struct ether_hdr *);
-	ipv4_hdr = (struct ipv4_hdr *)(eth_hdr + 1);
 
-	dst_ipv4 = ipv4_hdr->dst_addr;
-	dst_ipv4 = rte_be_to_cpu_32(dst_ipv4);
-	dp = get_dst_port(qconf, pkt, dst_ipv4, portid);
+		return (uint16_t) ((rte_lpm6_lookup(qconf->ipv6_lookup_struct,
+				ipv6_hdr->dst_addr, &next_hop) == 0) ? next_hop : portid);
 
-	te = _mm_loadu_si128((__m128i *)eth_hdr);
-	ve = val_eth[dp];
+	}
 
-	dst_port[0] = dp;
-	rfc1812_process(ipv4_hdr, dst_port, pkt->packet_type);
+	return portid;
 
-	te =  _mm_blend_epi16(te, ve, MASK_ETH);
-	_mm_storeu_si128((__m128i *)eth_hdr, te);
 }
 
 /*
@@ -266,225 +157,14 @@ processx4_step2(const struct lcore_conf *qconf,
 		rte_lpm_lookupx4(qconf->ipv4_lookup_struct, dip, dprt, portid);
 	} else {
 		dst.x = dip;
-		dprt[0] = get_dst_port(qconf, pkt[0], dst.u32[0], portid);
-		dprt[1] = get_dst_port(qconf, pkt[1], dst.u32[1], portid);
-		dprt[2] = get_dst_port(qconf, pkt[2], dst.u32[2], portid);
-		dprt[3] = get_dst_port(qconf, pkt[3], dst.u32[3], portid);
+		dprt[0] = lpm_get_dst_port_with_ipv4(qconf, pkt[0], dst.u32[0], portid);
+		dprt[1] = lpm_get_dst_port_with_ipv4(qconf, pkt[1], dst.u32[1], portid);
+		dprt[2] = lpm_get_dst_port_with_ipv4(qconf, pkt[2], dst.u32[2], portid);
+		dprt[3] = lpm_get_dst_port_with_ipv4(qconf, pkt[3], dst.u32[3], portid);
 	}
 }
 
 /*
- * Update source and destination MAC addresses in the ethernet header.
- * Perform RFC1812 checks and updates for IPV4 packets.
- */
-static inline void
-processx4_step3(struct rte_mbuf *pkt[FWDSTEP], uint16_t dst_port[FWDSTEP])
-{
-	__m128i te[FWDSTEP];
-	__m128i ve[FWDSTEP];
-	__m128i *p[FWDSTEP];
-
-	p[0] = rte_pktmbuf_mtod(pkt[0], __m128i *);
-	p[1] = rte_pktmbuf_mtod(pkt[1], __m128i *);
-	p[2] = rte_pktmbuf_mtod(pkt[2], __m128i *);
-	p[3] = rte_pktmbuf_mtod(pkt[3], __m128i *);
-
-	ve[0] = val_eth[dst_port[0]];
-	te[0] = _mm_loadu_si128(p[0]);
-
-	ve[1] = val_eth[dst_port[1]];
-	te[1] = _mm_loadu_si128(p[1]);
-
-	ve[2] = val_eth[dst_port[2]];
-	te[2] = _mm_loadu_si128(p[2]);
-
-	ve[3] = val_eth[dst_port[3]];
-	te[3] = _mm_loadu_si128(p[3]);
-
-	/* Update first 12 bytes, keep rest bytes intact. */
-	te[0] =  _mm_blend_epi16(te[0], ve[0], MASK_ETH);
-	te[1] =  _mm_blend_epi16(te[1], ve[1], MASK_ETH);
-	te[2] =  _mm_blend_epi16(te[2], ve[2], MASK_ETH);
-	te[3] =  _mm_blend_epi16(te[3], ve[3], MASK_ETH);
-
-	_mm_storeu_si128(p[0], te[0]);
-	_mm_storeu_si128(p[1], te[1]);
-	_mm_storeu_si128(p[2], te[2]);
-	_mm_storeu_si128(p[3], te[3]);
-
-	rfc1812_process((struct ipv4_hdr *)((struct ether_hdr *)p[0] + 1),
-		&dst_port[0], pkt[0]->packet_type);
-	rfc1812_process((struct ipv4_hdr *)((struct ether_hdr *)p[1] + 1),
-		&dst_port[1], pkt[1]->packet_type);
-	rfc1812_process((struct ipv4_hdr *)((struct ether_hdr *)p[2] + 1),
-		&dst_port[2], pkt[2]->packet_type);
-	rfc1812_process((struct ipv4_hdr *)((struct ether_hdr *)p[3] + 1),
-		&dst_port[3], pkt[3]->packet_type);
-}
-
-/*
- * We group consecutive packets with the same destionation port into one burst.
- * To avoid extra latency this is done together with some other packet
- * processing, but after we made a final decision about packet's destination.
- * To do this we maintain:
- * pnum - array of number of consecutive packets with the same dest port for
- * each packet in the input burst.
- * lp - pointer to the last updated element in the pnum.
- * dlp - dest port value lp corresponds to.
- */
-
-#define	GRPSZ	(1 << FWDSTEP)
-#define	GRPMSK	(GRPSZ - 1)
-
-#define GROUP_PORT_STEP(dlp, dcp, lp, pn, idx)	do { \
-	if (likely((dlp) == (dcp)[(idx)])) {         \
-		(lp)[0]++;                           \
-	} else {                                     \
-		(dlp) = (dcp)[idx];                  \
-		(lp) = (pn) + (idx);                 \
-		(lp)[0] = 1;                         \
-	}                                            \
-} while (0)
-
-/*
- * Group consecutive packets with the same destination port in bursts of 4.
- * Suppose we have array of destionation ports:
- * dst_port[] = {a, b, c, d,, e, ... }
- * dp1 should contain: <a, b, c, d>, dp2: <b, c, d, e>.
- * We doing 4 comparisions at once and the result is 4 bit mask.
- * This mask is used as an index into prebuild array of pnum values.
- */
-static inline uint16_t *
-port_groupx4(uint16_t pn[FWDSTEP + 1], uint16_t *lp, __m128i dp1, __m128i dp2)
-{
-	static const struct {
-		uint64_t pnum; /* prebuild 4 values for pnum[]. */
-		int32_t  idx;  /* index for new last updated elemnet. */
-		uint16_t lpv;  /* add value to the last updated element. */
-	} gptbl[GRPSZ] = {
-	{
-		/* 0: a != b, b != c, c != d, d != e */
-		.pnum = UINT64_C(0x0001000100010001),
-		.idx = 4,
-		.lpv = 0,
-	},
-	{
-		/* 1: a == b, b != c, c != d, d != e */
-		.pnum = UINT64_C(0x0001000100010002),
-		.idx = 4,
-		.lpv = 1,
-	},
-	{
-		/* 2: a != b, b == c, c != d, d != e */
-		.pnum = UINT64_C(0x0001000100020001),
-		.idx = 4,
-		.lpv = 0,
-	},
-	{
-		/* 3: a == b, b == c, c != d, d != e */
-		.pnum = UINT64_C(0x0001000100020003),
-		.idx = 4,
-		.lpv = 2,
-	},
-	{
-		/* 4: a != b, b != c, c == d, d != e */
-		.pnum = UINT64_C(0x0001000200010001),
-		.idx = 4,
-		.lpv = 0,
-	},
-	{
-		/* 5: a == b, b != c, c == d, d != e */
-		.pnum = UINT64_C(0x0001000200010002),
-		.idx = 4,
-		.lpv = 1,
-	},
-	{
-		/* 6: a != b, b == c, c == d, d != e */
-		.pnum = UINT64_C(0x0001000200030001),
-		.idx = 4,
-		.lpv = 0,
-	},
-	{
-		/* 7: a == b, b == c, c == d, d != e */
-		.pnum = UINT64_C(0x0001000200030004),
-		.idx = 4,
-		.lpv = 3,
-	},
-	{
-		/* 8: a != b, b != c, c != d, d == e */
-		.pnum = UINT64_C(0x0002000100010001),
-		.idx = 3,
-		.lpv = 0,
-	},
-	{
-		/* 9: a == b, b != c, c != d, d == e */
-		.pnum = UINT64_C(0x0002000100010002),
-		.idx = 3,
-		.lpv = 1,
-	},
-	{
-		/* 0xa: a != b, b == c, c != d, d == e */
-		.pnum = UINT64_C(0x0002000100020001),
-		.idx = 3,
-		.lpv = 0,
-	},
-	{
-		/* 0xb: a == b, b == c, c != d, d == e */
-		.pnum = UINT64_C(0x0002000100020003),
-		.idx = 3,
-		.lpv = 2,
-	},
-	{
-		/* 0xc: a != b, b != c, c == d, d == e */
-		.pnum = UINT64_C(0x0002000300010001),
-		.idx = 2,
-		.lpv = 0,
-	},
-	{
-		/* 0xd: a == b, b != c, c == d, d == e */
-		.pnum = UINT64_C(0x0002000300010002),
-		.idx = 2,
-		.lpv = 1,
-	},
-	{
-		/* 0xe: a != b, b == c, c == d, d == e */
-		.pnum = UINT64_C(0x0002000300040001),
-		.idx = 1,
-		.lpv = 0,
-	},
-	{
-		/* 0xf: a == b, b == c, c == d, d == e */
-		.pnum = UINT64_C(0x0002000300040005),
-		.idx = 0,
-		.lpv = 4,
-	},
-	};
-
-	union {
-		uint16_t u16[FWDSTEP + 1];
-		uint64_t u64;
-	} *pnum = (void *)pn;
-
-	int32_t v;
-
-	dp1 = _mm_cmpeq_epi16(dp1, dp2);
-	dp1 = _mm_unpacklo_epi16(dp1, dp1);
-	v = _mm_movemask_ps((__m128)dp1);
-
-	/* update last port counter. */
-	lp[0] += gptbl[v].lpv;
-
-	/* if dest port value has changed. */
-	if (v != GRPMSK) {
-		lp = pnum->u16 + gptbl[v].idx;
-		lp[0] = 1;
-		pnum->u64 = gptbl[v].pnum;
-	}
-
-	return lp;
-}
-
-/*
  * Buffer optimized handling of packets, invoked
  * from main_loop.
  */
@@ -492,119 +172,34 @@ static inline void
 l3fwd_lpm_send_packets(int nb_rx, struct rte_mbuf **pkts_burst,
 			uint8_t portid, struct lcore_conf *qconf)
 {
-	int32_t j, k;
-	uint16_t dlp;
-	uint16_t *lp;
+	int32_t j;
 	uint16_t dst_port[MAX_PKT_BURST];
 	__m128i dip[MAX_PKT_BURST / FWDSTEP];
 	uint32_t ipv4_flag[MAX_PKT_BURST / FWDSTEP];
-	uint16_t pnum[MAX_PKT_BURST + 1];
+	const int32_t k = RTE_ALIGN_FLOOR(nb_rx, FWDSTEP);
 
-	k = RTE_ALIGN_FLOOR(nb_rx, FWDSTEP);
-	for (j = 0; j != k; j += FWDSTEP) {
-		processx4_step1(&pkts_burst[j],
-			&dip[j / FWDSTEP],
-			&ipv4_flag[j / FWDSTEP]);
-	}
+	for (j = 0; j != k; j += FWDSTEP)
+		processx4_step1(&pkts_burst[j], &dip[j / FWDSTEP],
+				&ipv4_flag[j / FWDSTEP]);
 
-	k = RTE_ALIGN_FLOOR(nb_rx, FWDSTEP);
-	for (j = 0; j != k; j += FWDSTEP) {
+	for (j = 0; j != k; j += FWDSTEP)
 		processx4_step2(qconf, dip[j / FWDSTEP],
-			ipv4_flag[j / FWDSTEP], portid,
-			&pkts_burst[j], &dst_port[j]);
-	}
+				ipv4_flag[j / FWDSTEP], portid, &pkts_burst[j], &dst_port[j]);
 
-	/*
-	 * Finish packet processing and group consecutive
-	 * packets with the same destination port.
-	 */
-	k = RTE_ALIGN_FLOOR(nb_rx, FWDSTEP);
-	if (k != 0) {
-		__m128i dp1, dp2;
-
-		lp = pnum;
-		lp[0] = 1;
-
-		processx4_step3(pkts_burst, dst_port);
-
-		/* dp1: <d[0], d[1], d[2], d[3], ... > */
-		dp1 = _mm_loadu_si128((__m128i *)dst_port);
-
-		for (j = FWDSTEP; j != k; j += FWDSTEP) {
-			processx4_step3(&pkts_burst[j], &dst_port[j]);
-
-			/*
-			 * dp2:
-			 * <d[j-3], d[j-2], d[j-1], d[j], ... >
-			 */
-			dp2 = _mm_loadu_si128((__m128i *)
-					&dst_port[j - FWDSTEP + 1]);
-			lp  = port_groupx4(&pnum[j - FWDSTEP], lp, dp1, dp2);
-
-			/*
-			 * dp1:
-			 * <d[j], d[j+1], d[j+2], d[j+3], ... >
-			 */
-			dp1 = _mm_srli_si128(dp2, (FWDSTEP - 1) *
-						sizeof(dst_port[0]));
-		}
-
-		/*
-		 * dp2: <d[j-3], d[j-2], d[j-1], d[j-1], ... >
-		 */
-		dp2 = _mm_shufflelo_epi16(dp1, 0xf9);
-		lp  = port_groupx4(&pnum[j - FWDSTEP], lp, dp1, dp2);
-
-		/*
-		 * remove values added by the last repeated
-		 * dst port.
-		 */
-		lp[0]--;
-		dlp = dst_port[j - 1];
-	} else {
-		/* set dlp and lp to the never used values. */
-		dlp = BAD_PORT - 1;
-		lp = pnum + MAX_PKT_BURST;
-	}
-
-	/* Process up to last 3 packets one by one. */
+	/* Classify last up to 3 packets one by one */
 	switch (nb_rx % FWDSTEP) {
 	case 3:
-		process_packet(qconf, pkts_burst[j], dst_port + j, portid);
-		GROUP_PORT_STEP(dlp, dst_port, lp, pnum, j);
+		dst_port[j] = lpm_get_dst_port(qconf, pkts_burst[j], portid);
 		j++;
 	case 2:
-		process_packet(qconf, pkts_burst[j], dst_port + j, portid);
-		GROUP_PORT_STEP(dlp, dst_port, lp, pnum, j);
+		dst_port[j] = lpm_get_dst_port(qconf, pkts_burst[j], portid);
 		j++;
 	case 1:
-		process_packet(qconf, pkts_burst[j], dst_port + j, portid);
-		GROUP_PORT_STEP(dlp, dst_port, lp, pnum, j);
+		dst_port[j] = lpm_get_dst_port(qconf, pkts_burst[j], portid);
 		j++;
 	}
 
-	/*
-	 * Send packets out, through destination port.
-	 * Consecuteve pacekts with the same destination port
-	 * are already grouped together.
-	 * If destination port for the packet equals BAD_PORT,
-	 * then free the packet without sending it out.
-	 */
-	for (j = 0; j < nb_rx; j += k) {
-
-		int32_t m;
-		uint16_t pn;
-
-		pn = dst_port[j];
-		k = pnum[j];
-
-		if (likely(pn != BAD_PORT)) {
-			send_packetsx4(qconf, pn, pkts_burst + j, k);
-		} else {
-			for (m = j; m != j + k; m++)
-				rte_pktmbuf_free(pkts_burst[m]);
-		}
-	}
+	send_packets_multi(qconf, pkts_burst, dst_port, nb_rx);
 }
 
 #endif /* __L3FWD_LPM_SSE_H__ */
diff --git a/examples/l3fwd/l3fwd_sse.h b/examples/l3fwd/l3fwd_sse.h
new file mode 100644
index 0000000..f9cf50a
--- /dev/null
+++ b/examples/l3fwd/l3fwd_sse.h
@@ -0,0 +1,501 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef _L3FWD_COMMON_H_
+#define _L3FWD_COMMON_H_
+
+#include "l3fwd.h"
+
+#ifdef DO_RFC_1812_CHECKS
+
+#define	IPV4_MIN_VER_IHL	0x45
+#define	IPV4_MAX_VER_IHL	0x4f
+#define	IPV4_MAX_VER_IHL_DIFF	(IPV4_MAX_VER_IHL - IPV4_MIN_VER_IHL)
+
+/* Minimum value of IPV4 total length (20B) in network byte order. */
+#define	IPV4_MIN_LEN_BE	(sizeof(struct ipv4_hdr) << 8)
+
+/*
+ * From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2:
+ * - The IP version number must be 4.
+ * - The IP header length field must be large enough to hold the
+ *    minimum length legal IP datagram (20 bytes = 5 words).
+ * - 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 we encounter invalid IPV4 packet, then set destination port for it
+ * to BAD_PORT value.
+ */
+static inline __attribute__((always_inline)) void
+rfc1812_process(struct ipv4_hdr *ipv4_hdr, uint16_t *dp, uint32_t ptype)
+{
+	uint8_t ihl;
+
+	if (RTE_ETH_IS_IPV4_HDR(ptype)) {
+		ihl = ipv4_hdr->version_ihl - IPV4_MIN_VER_IHL;
+
+		ipv4_hdr->time_to_live--;
+		ipv4_hdr->hdr_checksum++;
+
+		if (ihl > IPV4_MAX_VER_IHL_DIFF ||
+				((uint8_t)ipv4_hdr->total_length == 0 &&
+				ipv4_hdr->total_length < IPV4_MIN_LEN_BE))
+			dp[0] = BAD_PORT;
+
+	}
+}
+
+#else
+#define	rfc1812_process(mb, dp, ptype)	do { } while (0)
+#endif /* DO_RFC_1812_CHECKS */
+
+/*
+ * Update source and destination MAC addresses in the ethernet header.
+ * Perform RFC1812 checks and updates for IPV4 packets.
+ */
+static inline void
+processx4_step3(struct rte_mbuf *pkt[FWDSTEP], uint16_t dst_port[FWDSTEP])
+{
+	__m128i te[FWDSTEP];
+	__m128i ve[FWDSTEP];
+	__m128i *p[FWDSTEP];
+
+	p[0] = rte_pktmbuf_mtod(pkt[0], __m128i *);
+	p[1] = rte_pktmbuf_mtod(pkt[1], __m128i *);
+	p[2] = rte_pktmbuf_mtod(pkt[2], __m128i *);
+	p[3] = rte_pktmbuf_mtod(pkt[3], __m128i *);
+
+	ve[0] = val_eth[dst_port[0]];
+	te[0] = _mm_loadu_si128(p[0]);
+
+	ve[1] = val_eth[dst_port[1]];
+	te[1] = _mm_loadu_si128(p[1]);
+
+	ve[2] = val_eth[dst_port[2]];
+	te[2] = _mm_loadu_si128(p[2]);
+
+	ve[3] = val_eth[dst_port[3]];
+	te[3] = _mm_loadu_si128(p[3]);
+
+	/* Update first 12 bytes, keep rest bytes intact. */
+	te[0] =  _mm_blend_epi16(te[0], ve[0], MASK_ETH);
+	te[1] =  _mm_blend_epi16(te[1], ve[1], MASK_ETH);
+	te[2] =  _mm_blend_epi16(te[2], ve[2], MASK_ETH);
+	te[3] =  _mm_blend_epi16(te[3], ve[3], MASK_ETH);
+
+	_mm_storeu_si128(p[0], te[0]);
+	_mm_storeu_si128(p[1], te[1]);
+	_mm_storeu_si128(p[2], te[2]);
+	_mm_storeu_si128(p[3], te[3]);
+
+	rfc1812_process((struct ipv4_hdr *)((struct ether_hdr *)p[0] + 1),
+		&dst_port[0], pkt[0]->packet_type);
+	rfc1812_process((struct ipv4_hdr *)((struct ether_hdr *)p[1] + 1),
+		&dst_port[1], pkt[1]->packet_type);
+	rfc1812_process((struct ipv4_hdr *)((struct ether_hdr *)p[2] + 1),
+		&dst_port[2], pkt[2]->packet_type);
+	rfc1812_process((struct ipv4_hdr *)((struct ether_hdr *)p[3] + 1),
+		&dst_port[3], pkt[3]->packet_type);
+}
+
+/*
+ * We group consecutive packets with the same destionation port into one burst.
+ * To avoid extra latency this is done together with some other packet
+ * processing, but after we made a final decision about packet's destination.
+ * To do this we maintain:
+ * pnum - array of number of consecutive packets with the same dest port for
+ * each packet in the input burst.
+ * lp - pointer to the last updated element in the pnum.
+ * dlp - dest port value lp corresponds to.
+ */
+
+#define	GRPSZ	(1 << FWDSTEP)
+#define	GRPMSK	(GRPSZ - 1)
+
+#define GROUP_PORT_STEP(dlp, dcp, lp, pn, idx)	do { \
+	if (likely((dlp) == (dcp)[(idx)])) {             \
+		(lp)[0]++;                                   \
+	} else {                                         \
+		(dlp) = (dcp)[idx];                          \
+		(lp) = (pn) + (idx);                         \
+		(lp)[0] = 1;                                 \
+	}                                                \
+} while (0)
+
+/*
+ * Group consecutive packets with the same destination port in bursts of 4.
+ * Suppose we have array of destionation ports:
+ * dst_port[] = {a, b, c, d,, e, ... }
+ * dp1 should contain: <a, b, c, d>, dp2: <b, c, d, e>.
+ * We doing 4 comparisions at once and the result is 4 bit mask.
+ * This mask is used as an index into prebuild array of pnum values.
+ */
+static inline uint16_t *
+port_groupx4(uint16_t pn[FWDSTEP + 1], uint16_t *lp, __m128i dp1, __m128i dp2)
+{
+	static const struct {
+		uint64_t pnum; /* prebuild 4 values for pnum[]. */
+		int32_t  idx;  /* index for new last updated elemnet. */
+		uint16_t lpv;  /* add value to the last updated element. */
+	} gptbl[GRPSZ] = {
+	{
+		/* 0: a != b, b != c, c != d, d != e */
+		.pnum = UINT64_C(0x0001000100010001),
+		.idx = 4,
+		.lpv = 0,
+	},
+	{
+		/* 1: a == b, b != c, c != d, d != e */
+		.pnum = UINT64_C(0x0001000100010002),
+		.idx = 4,
+		.lpv = 1,
+	},
+	{
+		/* 2: a != b, b == c, c != d, d != e */
+		.pnum = UINT64_C(0x0001000100020001),
+		.idx = 4,
+		.lpv = 0,
+	},
+	{
+		/* 3: a == b, b == c, c != d, d != e */
+		.pnum = UINT64_C(0x0001000100020003),
+		.idx = 4,
+		.lpv = 2,
+	},
+	{
+		/* 4: a != b, b != c, c == d, d != e */
+		.pnum = UINT64_C(0x0001000200010001),
+		.idx = 4,
+		.lpv = 0,
+	},
+	{
+		/* 5: a == b, b != c, c == d, d != e */
+		.pnum = UINT64_C(0x0001000200010002),
+		.idx = 4,
+		.lpv = 1,
+	},
+	{
+		/* 6: a != b, b == c, c == d, d != e */
+		.pnum = UINT64_C(0x0001000200030001),
+		.idx = 4,
+		.lpv = 0,
+	},
+	{
+		/* 7: a == b, b == c, c == d, d != e */
+		.pnum = UINT64_C(0x0001000200030004),
+		.idx = 4,
+		.lpv = 3,
+	},
+	{
+		/* 8: a != b, b != c, c != d, d == e */
+		.pnum = UINT64_C(0x0002000100010001),
+		.idx = 3,
+		.lpv = 0,
+	},
+	{
+		/* 9: a == b, b != c, c != d, d == e */
+		.pnum = UINT64_C(0x0002000100010002),
+		.idx = 3,
+		.lpv = 1,
+	},
+	{
+		/* 0xa: a != b, b == c, c != d, d == e */
+		.pnum = UINT64_C(0x0002000100020001),
+		.idx = 3,
+		.lpv = 0,
+	},
+	{
+		/* 0xb: a == b, b == c, c != d, d == e */
+		.pnum = UINT64_C(0x0002000100020003),
+		.idx = 3,
+		.lpv = 2,
+	},
+	{
+		/* 0xc: a != b, b != c, c == d, d == e */
+		.pnum = UINT64_C(0x0002000300010001),
+		.idx = 2,
+		.lpv = 0,
+	},
+	{
+		/* 0xd: a == b, b != c, c == d, d == e */
+		.pnum = UINT64_C(0x0002000300010002),
+		.idx = 2,
+		.lpv = 1,
+	},
+	{
+		/* 0xe: a != b, b == c, c == d, d == e */
+		.pnum = UINT64_C(0x0002000300040001),
+		.idx = 1,
+		.lpv = 0,
+	},
+	{
+		/* 0xf: a == b, b == c, c == d, d == e */
+		.pnum = UINT64_C(0x0002000300040005),
+		.idx = 0,
+		.lpv = 4,
+	},
+	};
+
+	union {
+		uint16_t u16[FWDSTEP + 1];
+		uint64_t u64;
+	} *pnum = (void *)pn;
+
+	int32_t v;
+
+	dp1 = _mm_cmpeq_epi16(dp1, dp2);
+	dp1 = _mm_unpacklo_epi16(dp1, dp1);
+	v = _mm_movemask_ps((__m128)dp1);
+
+	/* update last port counter. */
+	lp[0] += gptbl[v].lpv;
+
+	/* if dest port value has changed. */
+	if (v != GRPMSK) {
+		lp = pnum->u16 + gptbl[v].idx;
+		lp[0] = 1;
+		pnum->u64 = gptbl[v].pnum;
+	}
+
+	return lp;
+}
+
+/**
+ * Process one packet:
+ * Update source and destination MAC addresses in the ethernet header.
+ * Perform RFC1812 checks and updates for IPV4 packets.
+ */
+static inline void
+process_packet(struct rte_mbuf *pkt, uint16_t *dst_port)
+{
+	struct ether_hdr *eth_hdr;
+	__m128i te, ve;
+
+	eth_hdr = rte_pktmbuf_mtod(pkt, struct ether_hdr *);
+
+	te = _mm_loadu_si128((__m128i *)eth_hdr);
+	ve = val_eth[dst_port[0]];
+
+	rfc1812_process((struct ipv4_hdr *)(eth_hdr + 1), dst_port,
+			pkt->packet_type);
+
+	te =  _mm_blend_epi16(te, ve, MASK_ETH);
+	_mm_storeu_si128((__m128i *)eth_hdr, te);
+}
+
+static inline __attribute__((always_inline)) void
+send_packetsx4(struct lcore_conf *qconf, uint8_t port, struct rte_mbuf *m[],
+		uint32_t num)
+{
+	uint32_t len, j, n;
+
+	len = qconf->tx_mbufs[port].len;
+
+	/*
+	 * If TX buffer for that queue is empty, and we have enough packets,
+	 * then send them straightway.
+	 */
+	if (num >= MAX_TX_BURST && len == 0) {
+		n = rte_eth_tx_burst(port, qconf->tx_queue_id[port], m, num);
+		if (unlikely(n < num)) {
+			do {
+				rte_pktmbuf_free(m[n]);
+			} while (++n < num);
+		}
+		return;
+	}
+
+	/*
+	 * Put packets into TX buffer for that queue.
+	 */
+
+	n = len + num;
+	n = (n > MAX_PKT_BURST) ? MAX_PKT_BURST - len : num;
+
+	j = 0;
+	switch (n % FWDSTEP) {
+	while (j < n) {
+	case 0:
+		qconf->tx_mbufs[port].m_table[len + j] = m[j];
+		j++;
+	case 3:
+		qconf->tx_mbufs[port].m_table[len + j] = m[j];
+		j++;
+	case 2:
+		qconf->tx_mbufs[port].m_table[len + j] = m[j];
+		j++;
+	case 1:
+		qconf->tx_mbufs[port].m_table[len + j] = m[j];
+		j++;
+	}
+	}
+
+	len += n;
+
+	/* enough pkts to be sent */
+	if (unlikely(len == MAX_PKT_BURST)) {
+
+		send_burst(qconf, MAX_PKT_BURST, port);
+
+		/* copy rest of the packets into the TX buffer. */
+		len = num - n;
+		j = 0;
+		switch (len % FWDSTEP) {
+		while (j < len) {
+		case 0:
+			qconf->tx_mbufs[port].m_table[j] = m[n + j];
+			j++;
+		case 3:
+			qconf->tx_mbufs[port].m_table[j] = m[n + j];
+			j++;
+		case 2:
+			qconf->tx_mbufs[port].m_table[j] = m[n + j];
+			j++;
+		case 1:
+			qconf->tx_mbufs[port].m_table[j] = m[n + j];
+			j++;
+		}
+		}
+	}
+
+	qconf->tx_mbufs[port].len = len;
+}
+
+/**
+ * Send packets burst from pkts_burst to the ports in dst_port array
+ */
+static inline __attribute__((always_inline)) void
+send_packets_multi(struct lcore_conf *qconf, struct rte_mbuf **pkts_burst,
+		uint16_t dst_port[MAX_PKT_BURST], int nb_rx)
+{
+	int32_t k;
+	int j = 0;
+	uint16_t dlp;
+	uint16_t *lp;
+	uint16_t pnum[MAX_PKT_BURST + 1];
+
+	/*
+	 * Finish packet processing and group consecutive
+	 * packets with the same destination port.
+	 */
+	k = RTE_ALIGN_FLOOR(nb_rx, FWDSTEP);
+	if (k != 0) {
+		__m128i dp1, dp2;
+
+		lp = pnum;
+		lp[0] = 1;
+
+		processx4_step3(pkts_burst, dst_port);
+
+		/* dp1: <d[0], d[1], d[2], d[3], ... > */
+		dp1 = _mm_loadu_si128((__m128i *)dst_port);
+
+		for (j = FWDSTEP; j != k; j += FWDSTEP) {
+			processx4_step3(&pkts_burst[j], &dst_port[j]);
+
+			/*
+			 * dp2:
+			 * <d[j-3], d[j-2], d[j-1], d[j], ... >
+			 */
+			dp2 = _mm_loadu_si128((__m128i *)
+					&dst_port[j - FWDSTEP + 1]);
+			lp  = port_groupx4(&pnum[j - FWDSTEP], lp, dp1, dp2);
+
+			/*
+			 * dp1:
+			 * <d[j], d[j+1], d[j+2], d[j+3], ... >
+			 */
+			dp1 = _mm_srli_si128(dp2, (FWDSTEP - 1) *
+						sizeof(dst_port[0]));
+		}
+
+		/*
+		 * dp2: <d[j-3], d[j-2], d[j-1], d[j-1], ... >
+		 */
+		dp2 = _mm_shufflelo_epi16(dp1, 0xf9);
+		lp  = port_groupx4(&pnum[j - FWDSTEP], lp, dp1, dp2);
+
+		/*
+		 * remove values added by the last repeated
+		 * dst port.
+		 */
+		lp[0]--;
+		dlp = dst_port[j - 1];
+	} else {
+		/* set dlp and lp to the never used values. */
+		dlp = BAD_PORT - 1;
+		lp = pnum + MAX_PKT_BURST;
+	}
+
+	/* Process up to last 3 packets one by one. */
+	switch (nb_rx % FWDSTEP) {
+	case 3:
+		process_packet(pkts_burst[j], dst_port + j);
+		GROUP_PORT_STEP(dlp, dst_port, lp, pnum, j);
+		j++;
+	case 2:
+		process_packet(pkts_burst[j], dst_port + j);
+		GROUP_PORT_STEP(dlp, dst_port, lp, pnum, j);
+		j++;
+	case 1:
+		process_packet(pkts_burst[j], dst_port + j);
+		GROUP_PORT_STEP(dlp, dst_port, lp, pnum, j);
+		j++;
+	}
+
+	/*
+	 * Send packets out, through destination port.
+	 * Consecutive packets with the same destination port
+	 * are already grouped together.
+	 * If destination port for the packet equals BAD_PORT,
+	 * then free the packet without sending it out.
+	 */
+	for (j = 0; j < nb_rx; j += k) {
+
+		int32_t m;
+		uint16_t pn;
+
+		pn = dst_port[j];
+		k = pnum[j];
+
+		if (likely(pn != BAD_PORT))
+			send_packetsx4(qconf, pn, pkts_burst + j, k);
+		else
+			for (m = j; m != j + k; m++)
+				rte_pktmbuf_free(pkts_burst[m]);
+
+	}
+}
+
+#endif /* _L3FWD_COMMON_H_ */
-- 
1.7.9.5



More information about the dev mailing list