DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH v2] test: add reassembly perf test
@ 2021-10-04 13:33 pbhagavatula
  2021-10-04 14:23 ` [dpdk-dev] [PATCH v3] " pbhagavatula
  0 siblings, 1 reply; 2+ messages in thread
From: pbhagavatula @ 2021-10-04 13:33 UTC (permalink / raw)
  To: david.marchand, jerinj; +Cc: dev, Pavan Nikhilesh

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add reassembly perf autotest for both ipv4 and ipv6 reassembly.
Each test is performed with vairable number of fragments per flow,
either ordered or unorderd fragments and interleaved flows.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 v2 Changes
 - Rebase to master, reduce memory consumption, set default mempool ops
 to ring_mp_mc.

 app/test/meson.build            |   2 +
 app/test/test_reassembly_perf.c | 991 ++++++++++++++++++++++++++++++++
 2 files changed, 993 insertions(+)
 create mode 100644 app/test/test_reassembly_perf.c

diff --git a/app/test/meson.build b/app/test/meson.build
index f144d8b8ed..f1957dc5b5 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -109,6 +109,7 @@ test_sources = files(
         'test_rawdev.c',
         'test_rcu_qsbr.c',
         'test_rcu_qsbr_perf.c',
+        'test_reassembly_perf.c',
         'test_reciprocal_division.c',
         'test_reciprocal_division_perf.c',
         'test_red.c',
@@ -315,6 +316,7 @@ perf_test_names = [
         'hash_readwrite_lf_perf_autotest',
         'trace_perf_autotest',
         'ipsec_perf_autotest',
+        'reassembly_perf_autotest',
 ]

 driver_test_names = [
diff --git a/app/test/test_reassembly_perf.c b/app/test/test_reassembly_perf.c
new file mode 100644
index 0000000000..da60c8bd7a
--- /dev/null
+++ b/app/test/test_reassembly_perf.c
@@ -0,0 +1,991 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Marvell.
+ */
+
+#include <rte_byteorder.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_ether.h>
+#include <rte_hexdump.h>
+#include <rte_ip.h>
+#include <rte_ip_frag.h>
+#include <rte_mbuf.h>
+#include <rte_mbuf_pool_ops.h>
+#include <rte_random.h>
+#include <rte_udp.h>
+
+#include "test.h"
+
+#define MAX_FLOWS	    1024 * 32
+#define MAX_BKTS	    MAX_FLOWS
+#define MAX_ENTRIES_PER_BKT 16
+#define MAX_FRAGMENTS	    RTE_LIBRTE_IP_FRAG_MAX_FRAG
+#define MIN_FRAGMENTS	    2
+#define MAX_PKTS	    MAX_FLOWS *MAX_FRAGMENTS
+
+#define MAX_PKT_LEN 2048
+#define MAX_TTL_MS  5 * MS_PER_S
+
+/* use RFC863 Discard Protocol */
+#define UDP_SRC_PORT 9
+#define UDP_DST_PORT 9
+
+/* use RFC5735 / RFC2544 reserved network test addresses */
+#define IP_SRC_ADDR(x) (198U << 24) | (18 << 16) | (0 << 8) | x
+#define IP_DST_ADDR(x) (198U << 24) | (18 << 16) | (1 << 8) | x
+
+/* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180) */
+static uint8_t ip6_addr[16] = {32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#define IP6_VERSION 6
+
+#define IP_DEFTTL 64 /* from RFC 1340. */
+
+static struct rte_ip_frag_tbl *frag_tbl;
+static struct rte_mempool *pkt_pool;
+static struct rte_mbuf *mbufs[MAX_FLOWS][MAX_FRAGMENTS];
+static uint8_t frag_per_flow[MAX_FLOWS];
+static uint32_t flow_cnt;
+
+#define FILL_MODE_LINEAR      0
+#define FILL_MODE_RANDOM      1
+#define FILL_MODE_INTERLEAVED 2
+
+static int
+reassembly_test_setup(void)
+{
+	uint64_t max_ttl_cyc = (MAX_TTL_MS * rte_get_timer_hz()) / 1E3;
+
+	frag_tbl = rte_ip_frag_table_create(MAX_FLOWS, MAX_ENTRIES_PER_BKT,
+					    MAX_FLOWS * MAX_ENTRIES_PER_BKT,
+					    max_ttl_cyc, rte_socket_id());
+	if (frag_tbl == NULL)
+		return TEST_FAILED;
+
+	rte_mbuf_set_user_mempool_ops("ring_mp_mc");
+	pkt_pool = rte_pktmbuf_pool_create(
+		"reassembly_perf_pool", MAX_FLOWS * MAX_FRAGMENTS, 0, 0,
+		RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+	if (pkt_pool == NULL) {
+		printf("[%s] Failed to create pkt pool\n", __func__);
+		rte_ip_frag_table_destroy(frag_tbl);
+		return TEST_FAILED;
+	}
+
+	return TEST_SUCCESS;
+}
+
+static void
+reassembly_test_teardown(void)
+{
+	if (frag_tbl != NULL)
+		rte_ip_frag_table_destroy(frag_tbl);
+
+	if (pkt_pool != NULL)
+		rte_mempool_free(pkt_pool);
+}
+
+static void
+randomize_array_positions(void **array, uint8_t sz)
+{
+	void *tmp;
+	int i, j;
+
+	if (sz == 2) {
+		tmp = array[0];
+		array[0] = array[1];
+		array[1] = tmp;
+	} else {
+		for (i = sz - 1; i > 0; i--) {
+			j = rte_rand_max(i + 1);
+			tmp = array[i];
+			array[i] = array[j];
+			array[j] = tmp;
+		}
+	}
+}
+
+static void
+reassembly_print_banner(const char *proto_str)
+{
+	printf("+=============================================================="
+	       "============================================+\n");
+	printf("| %-32s| %-3s : %-58d|\n", proto_str, "Flow Count", MAX_FLOWS);
+	printf("+================+================+=============+=============+"
+	       "========================+===================+\n");
+	printf("%-17s%-17s%-14s%-14s%-25s%-20s\n", "| Fragment Order",
+	       "| Fragments/Flow", "| Outstanding", "| Cycles/Flow",
+	       "| Cycles/Fragment insert", "| Cycles/Reassembly |");
+	printf("+================+================+=============+=============+"
+	       "========================+===================+\n");
+}
+
+static void
+ipv4_frag_fill_data(struct rte_mbuf **mbuf, uint8_t nb_frags, uint32_t flow_id,
+		    uint8_t fill_mode)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ipv4_hdr *ip_hdr;
+	struct rte_udp_hdr *udp_hdr;
+	uint16_t frag_len;
+	uint8_t i;
+
+	frag_len = MAX_PKT_LEN / nb_frags;
+	if (frag_len % 8)
+		frag_len = RTE_ALIGN_MUL_CEIL(frag_len, 8);
+
+	for (i = 0; i < nb_frags; i++) {
+		struct rte_mbuf *frag = mbuf[i];
+		uint16_t frag_offset = 0;
+		uint32_t ip_cksum;
+		uint16_t pkt_len;
+		uint16_t *ptr16;
+
+		frag_offset = i * (frag_len / 8);
+
+		if (i == nb_frags - 1)
+			frag_len = MAX_PKT_LEN - (frag_len * (nb_frags - 1));
+		else
+			frag_offset |= RTE_IPV4_HDR_MF_FLAG;
+
+		rte_pktmbuf_reset_headroom(frag);
+		eth_hdr = rte_pktmbuf_mtod(frag, struct rte_ether_hdr *);
+		ip_hdr = rte_pktmbuf_mtod_offset(frag, struct rte_ipv4_hdr *,
+						 sizeof(struct rte_ether_hdr));
+		udp_hdr = rte_pktmbuf_mtod_offset(
+			frag, struct rte_udp_hdr *,
+			sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv4_hdr));
+
+		rte_ether_unformat_addr("02:00:00:00:00:01", &eth_hdr->d_addr);
+		rte_ether_unformat_addr("02:00:00:00:00:00", &eth_hdr->s_addr);
+		eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+
+		pkt_len = frag_len;
+		/*
+		 * Initialize UDP header.
+		 */
+		if (i == 0) {
+			udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
+			udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
+			udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
+			udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
+		}
+
+		/*
+		 * Initialize IP header.
+		 */
+		pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv4_hdr));
+		ip_hdr->version_ihl = RTE_IPV4_VHL_DEF;
+		ip_hdr->type_of_service = 0;
+		ip_hdr->fragment_offset = rte_cpu_to_be_16(frag_offset);
+		ip_hdr->time_to_live = IP_DEFTTL;
+		ip_hdr->next_proto_id = IPPROTO_UDP;
+		ip_hdr->packet_id =
+			rte_cpu_to_be_16((flow_id + 1) % UINT16_MAX);
+		ip_hdr->total_length = rte_cpu_to_be_16(pkt_len);
+		ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR(flow_id));
+		ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR(flow_id));
+
+		/*
+		 * Compute IP header checksum.
+		 */
+		ptr16 = (unaligned_uint16_t *)ip_hdr;
+		ip_cksum = 0;
+		ip_cksum += ptr16[0];
+		ip_cksum += ptr16[1];
+		ip_cksum += ptr16[2];
+		ip_cksum += ptr16[3];
+		ip_cksum += ptr16[4];
+		ip_cksum += ptr16[6];
+		ip_cksum += ptr16[7];
+		ip_cksum += ptr16[8];
+		ip_cksum += ptr16[9];
+
+		/*
+		 * Reduce 32 bit checksum to 16 bits and complement it.
+		 */
+		ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) +
+			   (ip_cksum & 0x0000FFFF);
+		if (ip_cksum > 65535)
+			ip_cksum -= 65535;
+		ip_cksum = (~ip_cksum) & 0x0000FFFF;
+		if (ip_cksum == 0)
+			ip_cksum = 0xFFFF;
+		ip_hdr->hdr_checksum = (uint16_t)ip_cksum;
+
+		frag->data_len = sizeof(struct rte_ether_hdr) + pkt_len;
+		frag->pkt_len = frag->data_len;
+		frag->l2_len = sizeof(struct rte_ether_hdr);
+		frag->l3_len = sizeof(struct rte_ipv4_hdr);
+	}
+
+	if (fill_mode == FILL_MODE_RANDOM)
+		randomize_array_positions((void **)mbuf, nb_frags);
+}
+
+static uint8_t
+get_rand_frags(uint8_t max_frag)
+{
+	uint8_t frags = rte_rand_max(max_frag + 1);
+
+	return frags <= 1 ? MIN_FRAGMENTS : frags;
+}
+
+static int
+ipv4_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t max_frag)
+{
+	uint8_t nb_frag;
+	int i;
+
+	for (i = 0; i < MAX_FLOWS; i++) {
+		nb_frag = get_rand_frags(max_frag);
+		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
+		    0)
+			return TEST_FAILED;
+		ipv4_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+		frag_per_flow[i] = nb_frag;
+	}
+	flow_cnt = i;
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv4_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
+{
+	int i;
+
+	for (i = 0; i < MAX_FLOWS; i++) {
+		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
+		    0)
+			return TEST_FAILED;
+		ipv4_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+		frag_per_flow[i] = nb_frag;
+	}
+	flow_cnt = i;
+
+	return TEST_SUCCESS;
+}
+
+static void
+ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t nb_frags, uint32_t flow_id,
+		    uint8_t fill_mode)
+{
+	struct ipv6_extension_fragment *frag_hdr;
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ipv6_hdr *ip_hdr;
+	struct rte_udp_hdr *udp_hdr;
+	uint16_t frag_len;
+	uint8_t i;
+
+	frag_len = MAX_PKT_LEN / nb_frags;
+	if (frag_len % 8)
+		frag_len = RTE_ALIGN_MUL_CEIL(frag_len, 8);
+
+	for (i = 0; i < nb_frags; i++) {
+		struct rte_mbuf *frag = mbuf[i];
+		uint16_t frag_offset = 0;
+		uint16_t pkt_len;
+
+		frag_offset = i * (frag_len / 8);
+		frag_offset <<= 3;
+		if (i == nb_frags - 1) {
+			frag_len = MAX_PKT_LEN - (frag_len * (nb_frags - 1));
+			frag_offset = RTE_IPV6_SET_FRAG_DATA(frag_offset, 0);
+		} else {
+			frag_offset = RTE_IPV6_SET_FRAG_DATA(frag_offset, 1);
+		}
+
+		rte_pktmbuf_reset_headroom(frag);
+		eth_hdr = rte_pktmbuf_mtod(frag, struct rte_ether_hdr *);
+		ip_hdr = rte_pktmbuf_mtod_offset(frag, struct rte_ipv6_hdr *,
+						 sizeof(struct rte_ether_hdr));
+		udp_hdr = rte_pktmbuf_mtod_offset(
+			frag, struct rte_udp_hdr *,
+			sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv6_hdr) +
+				RTE_IPV6_FRAG_HDR_SIZE);
+		frag_hdr = rte_pktmbuf_mtod_offset(
+			frag, struct ipv6_extension_fragment *,
+			sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv6_hdr));
+
+		rte_ether_unformat_addr("02:00:00:00:00:01", &eth_hdr->d_addr);
+		rte_ether_unformat_addr("02:00:00:00:00:00", &eth_hdr->s_addr);
+		eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
+
+		pkt_len = frag_len;
+		/*
+		 * Initialize UDP header.
+		 */
+		if (i == 0) {
+			udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
+			udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
+			udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
+			udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
+		}
+
+		/*
+		 * Initialize IP header.
+		 */
+		pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv6_hdr) +
+				     RTE_IPV6_FRAG_HDR_SIZE);
+		ip_hdr->vtc_flow = rte_cpu_to_be_32(IP6_VERSION << 28);
+		ip_hdr->payload_len =
+			rte_cpu_to_be_16(pkt_len - sizeof(struct rte_ipv6_hdr));
+		ip_hdr->proto = IPPROTO_FRAGMENT;
+		ip_hdr->hop_limits = IP_DEFTTL;
+		memcpy(ip_hdr->src_addr, ip6_addr, sizeof(ip_hdr->src_addr));
+		memcpy(ip_hdr->dst_addr, ip6_addr, sizeof(ip_hdr->dst_addr));
+		ip_hdr->src_addr[7] = (flow_id >> 16) & 0xf;
+		ip_hdr->src_addr[7] |= 0x10;
+		ip_hdr->src_addr[8] = (flow_id >> 8) & 0xff;
+		ip_hdr->src_addr[9] = flow_id & 0xff;
+
+		ip_hdr->dst_addr[7] = (flow_id >> 16) & 0xf;
+		ip_hdr->dst_addr[7] |= 0x20;
+		ip_hdr->dst_addr[8] = (flow_id >> 8) & 0xff;
+		ip_hdr->dst_addr[9] = flow_id & 0xff;
+
+		frag_hdr->next_header = IPPROTO_UDP;
+		frag_hdr->reserved = 0;
+		frag_hdr->frag_data = rte_cpu_to_be_16(frag_offset);
+		frag_hdr->id = rte_cpu_to_be_32(flow_id + 1);
+
+		frag->data_len = sizeof(struct rte_ether_hdr) + pkt_len;
+		frag->pkt_len = frag->data_len;
+		frag->l2_len = sizeof(struct rte_ether_hdr);
+		frag->l3_len =
+			sizeof(struct rte_ipv6_hdr) + RTE_IPV6_FRAG_HDR_SIZE;
+	}
+
+	if (fill_mode == FILL_MODE_RANDOM)
+		randomize_array_positions((void **)mbuf, nb_frags);
+}
+
+static int
+ipv6_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t max_frag)
+{
+	uint8_t nb_frag;
+	int i;
+
+	for (i = 0; i < MAX_FLOWS; i++) {
+		nb_frag = get_rand_frags(max_frag);
+		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
+		    0)
+			return TEST_FAILED;
+		ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+		frag_per_flow[i] = nb_frag;
+	}
+	flow_cnt = i;
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv6_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
+{
+	int i;
+
+	for (i = 0; i < MAX_FLOWS; i++) {
+		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
+		    0)
+			return TEST_FAILED;
+		ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+		frag_per_flow[i] = nb_frag;
+	}
+	flow_cnt = i;
+
+	return TEST_SUCCESS;
+}
+
+static void
+frag_pkt_teardown(void)
+{
+	uint32_t i;
+
+	for (i = 0; i < flow_cnt; i++)
+		rte_pktmbuf_free(mbufs[i][0]);
+}
+
+static void
+reassembly_print_stats(int8_t nb_frags, uint8_t fill_order,
+		       uint32_t outstanding, uint64_t cyc_per_flow,
+		       uint64_t cyc_per_frag_insert,
+		       uint64_t cyc_per_reassembly)
+{
+	char frag_str[8], order_str[12];
+
+	if (nb_frags > 0)
+		snprintf(frag_str, sizeof(frag_str), "%d", nb_frags);
+	else
+		snprintf(frag_str, sizeof(frag_str), "RANDOM");
+
+	switch (fill_order) {
+	case FILL_MODE_LINEAR:
+		snprintf(order_str, sizeof(order_str), "LINEAR");
+		break;
+	case FILL_MODE_RANDOM:
+		snprintf(order_str, sizeof(order_str), "RANDOM");
+		break;
+	case FILL_MODE_INTERLEAVED:
+		snprintf(order_str, sizeof(order_str), "INTERLEAVED");
+		break;
+	default:
+		break;
+	}
+
+	printf("| %-14s | %-14s | %-11d | %-11" PRIu64 " | %-22" PRIu64
+	       " | %-17" PRIu64 " |\n",
+	       order_str, frag_str, outstanding, cyc_per_flow,
+	       cyc_per_frag_insert, cyc_per_reassembly);
+	printf("+================+================+=============+=============+"
+	       "========================+===================+\n");
+}
+
+static void
+join_array(struct rte_mbuf **dest_arr, struct rte_mbuf **src_arr,
+	   uint8_t offset, uint8_t sz)
+{
+	int i, j;
+
+	for (i = offset, j = 0; j < sz; i++, j++)
+		dest_arr[i] = src_arr[j];
+}
+
+static int
+ipv4_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j;
+
+	for (i = 0; i < flow_cnt; i++) {
+		struct rte_mbuf *buf_out = NULL;
+		uint8_t reassembled = 0;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < frag_per_flow[i]; j++) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv4_hdr *, buf->l2_len);
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv4_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
+
+			if (buf_out == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled = 1;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (!reassembled || buf_out->nb_segs != frag_per_flow[i])
+			return TEST_FAILED;
+		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+		mbufs[i][0] = buf_out;
+	}
+
+	reassembly_print_stats(nb_frags, fill_order, 0, total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv4_outstanding_reassembly_perf(int8_t nb_frags, uint8_t fill_order,
+				 uint32_t outstanding)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j, k;
+
+	k = outstanding;
+	/* Insert outstanding fragments */
+	for (i = 0; k && (i < flow_cnt); i++) {
+		struct rte_mbuf *buf_out = NULL;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = frag_per_flow[i] - 1; j > 0; j--) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv4_hdr *, buf->l2_len);
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv4_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
+			total_empty_cyc += rte_rdtsc() - tstamp;
+			frag_processed++;
+			if (buf_out != NULL)
+				return TEST_FAILED;
+
+			k--;
+		}
+		frag_per_flow[i] = 1;
+	}
+
+	for (i = 0; i < flow_cnt; i++) {
+		struct rte_mbuf *buf_out = NULL;
+		uint8_t reassembled = 0;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < frag_per_flow[i]; j++) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv4_hdr *, buf->l2_len);
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv4_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
+
+			if (buf_out == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled = 1;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (!reassembled)
+			return TEST_FAILED;
+		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+		mbufs[i][0] = buf_out;
+	}
+
+	reassembly_print_stats(nb_frags, fill_order, outstanding,
+			       total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv4_reassembly_interleaved_flows_perf(uint8_t nb_frags)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j;
+
+	for (i = 0; i < flow_cnt; i += 4) {
+		struct rte_mbuf *buf_out[4] = {NULL};
+		uint8_t reassembled = 0;
+		uint8_t nb_frags = 0;
+		uint8_t prev = 0;
+
+		for (j = 0; j < 4; j++)
+			nb_frags += frag_per_flow[i + j];
+
+		struct rte_mbuf *buf_arr[nb_frags];
+		for (j = 0; j < 4; j++) {
+			join_array(buf_arr, mbufs[i + j], prev,
+				   frag_per_flow[i + j]);
+			prev += frag_per_flow[i + j];
+		}
+		randomize_array_positions((void **)buf_arr, nb_frags);
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < nb_frags; j++) {
+			struct rte_mbuf *buf = buf_arr[j];
+			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv4_hdr *, buf->l2_len);
+
+			tstamp = rte_rdtsc();
+			buf_out[reassembled] = rte_ipv4_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
+
+			if (buf_out[reassembled] == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled++;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (reassembled != 4)
+			return TEST_FAILED;
+		for (j = 0; j < 4; j++) {
+			memset(mbufs[i + j], 0,
+			       sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+			mbufs[i + j][0] = buf_out[j];
+		}
+	}
+
+	reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0,
+			       total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv6_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j;
+
+	for (i = 0; i < flow_cnt; i++) {
+		struct rte_mbuf *buf_out = NULL;
+		uint8_t reassembled = 0;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < frag_per_flow[i]; j++) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv6_hdr *, buf->l2_len);
+			struct ipv6_extension_fragment *frag_hdr =
+				rte_pktmbuf_mtod_offset(
+					buf, struct ipv6_extension_fragment *,
+					buf->l2_len +
+						sizeof(struct rte_ipv6_hdr));
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv6_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
+				frag_hdr);
+
+			if (buf_out == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled = 1;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (!reassembled || buf_out->nb_segs != frag_per_flow[i])
+			return TEST_FAILED;
+		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+		mbufs[i][0] = buf_out;
+	}
+
+	reassembly_print_stats(nb_frags, fill_order, 0, total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv6_outstanding_reassembly_perf(int8_t nb_frags, uint8_t fill_order,
+				 uint32_t outstanding)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j, k;
+
+	k = outstanding;
+	/* Insert outstanding fragments */
+	for (i = 0; k && (i < flow_cnt); i++) {
+		struct rte_mbuf *buf_out = NULL;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = frag_per_flow[i] - 1; j > 0; j--) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv6_hdr *, buf->l2_len);
+			struct ipv6_extension_fragment *frag_hdr =
+				rte_pktmbuf_mtod_offset(
+					buf, struct ipv6_extension_fragment *,
+					buf->l2_len +
+						sizeof(struct rte_ipv6_hdr));
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv6_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
+				frag_hdr);
+			total_empty_cyc += rte_rdtsc() - tstamp;
+			frag_processed++;
+
+			if (buf_out != NULL)
+				return TEST_FAILED;
+
+			k--;
+		}
+		frag_per_flow[i] = 1;
+	}
+
+	for (i = 0; i < flow_cnt; i++) {
+		struct rte_mbuf *buf_out = NULL;
+		uint8_t reassembled = 0;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < frag_per_flow[i]; j++) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv6_hdr *, buf->l2_len);
+			struct ipv6_extension_fragment *frag_hdr =
+				rte_pktmbuf_mtod_offset(
+					buf, struct ipv6_extension_fragment *,
+					buf->l2_len +
+						sizeof(struct rte_ipv6_hdr));
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv6_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
+				frag_hdr);
+
+			if (buf_out == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled = 1;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (!reassembled)
+			return TEST_FAILED;
+		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+		mbufs[i][0] = buf_out;
+	}
+
+	reassembly_print_stats(nb_frags, fill_order, outstanding,
+			       total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv6_reassembly_interleaved_flows_perf(int8_t nb_frags)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j;
+
+	for (i = 0; i < flow_cnt; i += 4) {
+		struct rte_mbuf *buf_out[4] = {NULL};
+		uint8_t reassembled = 0;
+		uint8_t nb_frags = 0;
+		uint8_t prev = 0;
+
+		for (j = 0; j < 4; j++)
+			nb_frags += frag_per_flow[i + j];
+
+		struct rte_mbuf *buf_arr[nb_frags];
+		for (j = 0; j < 4; j++) {
+			join_array(buf_arr, mbufs[i + j], prev,
+				   frag_per_flow[i + j]);
+			prev += frag_per_flow[i + j];
+		}
+		randomize_array_positions((void **)buf_arr, nb_frags);
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < nb_frags; j++) {
+			struct rte_mbuf *buf = buf_arr[j];
+			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv6_hdr *, buf->l2_len);
+			struct ipv6_extension_fragment *frag_hdr =
+				rte_pktmbuf_mtod_offset(
+					buf, struct ipv6_extension_fragment *,
+					buf->l2_len +
+						sizeof(struct rte_ipv6_hdr));
+
+			tstamp = rte_rdtsc();
+			buf_out[reassembled] = rte_ipv6_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
+				frag_hdr);
+
+			if (buf_out[reassembled] == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled++;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (reassembled != 4)
+			return TEST_FAILED;
+		for (j = 0; j < 4; j++) {
+			memset(mbufs[i + j], 0,
+			       sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+			mbufs[i + j][0] = buf_out[j];
+		}
+	}
+
+	reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0,
+			       total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv4_reassembly_test(int8_t nb_frags, uint8_t fill_order, uint32_t outstanding)
+{
+	int rc;
+
+	if (nb_frags > 0)
+		rc = ipv4_frag_pkt_setup(fill_order, nb_frags);
+	else
+		rc = ipv4_rand_frag_pkt_setup(fill_order, MAX_FRAGMENTS);
+
+	if (rc)
+		return rc;
+
+	if (outstanding)
+		rc = ipv4_outstanding_reassembly_perf(nb_frags, fill_order,
+						      outstanding);
+	else if (fill_order == FILL_MODE_INTERLEAVED)
+		rc = ipv4_reassembly_interleaved_flows_perf(nb_frags);
+	else
+		rc = ipv4_reassembly_perf(nb_frags, fill_order);
+
+	frag_pkt_teardown();
+
+	return rc;
+}
+
+static int
+ipv6_reassembly_test(int8_t nb_frags, uint8_t fill_order, uint32_t outstanding)
+{
+	int rc;
+
+	if (nb_frags > 0)
+		rc = ipv6_frag_pkt_setup(fill_order, nb_frags);
+	else
+		rc = ipv6_rand_frag_pkt_setup(fill_order, MAX_FRAGMENTS);
+
+	if (rc)
+		return rc;
+
+	if (outstanding)
+		rc = ipv6_outstanding_reassembly_perf(nb_frags, fill_order,
+						      outstanding);
+	else if (fill_order == FILL_MODE_INTERLEAVED)
+		rc = ipv6_reassembly_interleaved_flows_perf(nb_frags);
+	else
+		rc = ipv6_reassembly_perf(nb_frags, fill_order);
+
+	frag_pkt_teardown();
+
+	return rc;
+}
+
+static int
+test_reassembly_perf(void)
+{
+	int8_t nb_fragments[] = {2, 3, MAX_FRAGMENTS, -1 /* Random */};
+	uint8_t order_type[] = {FILL_MODE_LINEAR, FILL_MODE_RANDOM};
+	uint32_t outstanding[] = {100, 500, 1000, 2000, 3000};
+	uint32_t i, j;
+	int rc;
+
+	rc = reassembly_test_setup();
+	if (rc)
+		return rc;
+
+	reassembly_print_banner("IPV4");
+	/* Test variable fragment count and ordering. */
+	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
+		for (j = 0; j < RTE_DIM(order_type); j++) {
+			rc = ipv4_reassembly_test(nb_fragments[i],
+						  order_type[j], 0);
+			if (rc)
+				return rc;
+		}
+	}
+
+	/* Test outstanding fragments in the table. */
+	for (i = 0; i < RTE_DIM(outstanding); i++) {
+		rc = ipv4_reassembly_test(2, 0, outstanding[i]);
+		if (rc)
+			return rc;
+	}
+	for (i = 0; i < RTE_DIM(outstanding); i++) {
+		rc = ipv4_reassembly_test(MAX_FRAGMENTS, 0, outstanding[i]);
+		if (rc)
+			return rc;
+	}
+
+	/* Test interleaved flow reassembly perf */
+	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
+		rc = ipv4_reassembly_test(nb_fragments[i],
+					  FILL_MODE_INTERLEAVED, 0);
+		if (rc)
+			return rc;
+	}
+	printf("\n");
+	reassembly_print_banner("IPV6");
+	/* Test variable fragment count and ordering. */
+	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
+		for (j = 0; j < RTE_DIM(order_type); j++) {
+			rc = ipv6_reassembly_test(nb_fragments[i],
+						  order_type[j], 0);
+			if (rc)
+				return rc;
+		}
+	}
+
+	/* Test outstanding fragments in the table. */
+	for (i = 0; i < RTE_DIM(outstanding); i++) {
+		rc = ipv6_reassembly_test(2, 0, outstanding[i]);
+		if (rc)
+			return rc;
+	}
+
+	for (i = 0; i < RTE_DIM(outstanding); i++) {
+		rc = ipv6_reassembly_test(MAX_FRAGMENTS, 0, outstanding[i]);
+		if (rc)
+			return rc;
+	}
+
+	/* Test interleaved flow reassembly perf */
+	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
+		rc = ipv6_reassembly_test(nb_fragments[i],
+					  FILL_MODE_INTERLEAVED, 0);
+		if (rc)
+			return rc;
+	}
+	reassembly_test_teardown();
+
+	return TEST_SUCCESS;
+}
+
+REGISTER_TEST_COMMAND(reassembly_perf_autotest, test_reassembly_perf);
--
2.17.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [dpdk-dev] [PATCH v3] test: add reassembly perf test
  2021-10-04 13:33 [dpdk-dev] [PATCH v2] test: add reassembly perf test pbhagavatula
@ 2021-10-04 14:23 ` pbhagavatula
  0 siblings, 0 replies; 2+ messages in thread
From: pbhagavatula @ 2021-10-04 14:23 UTC (permalink / raw)
  To: david.marchand, jerinj; +Cc: dev, Pavan Nikhilesh

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add reassembly perf autotest for both ipv4 and ipv6 reassembly.
Each test is performed with variable number of fragments per flow,
either ordered or unordered fragments and interleaved flows.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 v3 Changes:
 - Fix checkpatch issues.
 v2 Changes
 - Rebase to master, reduce memory consumption, set default mempool ops
 to ring_mp_mc.

 app/test/meson.build            |   2 +
 app/test/test_reassembly_perf.c | 991 ++++++++++++++++++++++++++++++++
 2 files changed, 993 insertions(+)
 create mode 100644 app/test/test_reassembly_perf.c

diff --git a/app/test/meson.build b/app/test/meson.build
index f144d8b8ed..f1957dc5b5 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -109,6 +109,7 @@ test_sources = files(
         'test_rawdev.c',
         'test_rcu_qsbr.c',
         'test_rcu_qsbr_perf.c',
+        'test_reassembly_perf.c',
         'test_reciprocal_division.c',
         'test_reciprocal_division_perf.c',
         'test_red.c',
@@ -315,6 +316,7 @@ perf_test_names = [
         'hash_readwrite_lf_perf_autotest',
         'trace_perf_autotest',
         'ipsec_perf_autotest',
+        'reassembly_perf_autotest',
 ]

 driver_test_names = [
diff --git a/app/test/test_reassembly_perf.c b/app/test/test_reassembly_perf.c
new file mode 100644
index 0000000000..833f50ff6f
--- /dev/null
+++ b/app/test/test_reassembly_perf.c
@@ -0,0 +1,991 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Marvell.
+ */
+
+#include <rte_byteorder.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_ether.h>
+#include <rte_hexdump.h>
+#include <rte_ip.h>
+#include <rte_ip_frag.h>
+#include <rte_mbuf.h>
+#include <rte_mbuf_pool_ops.h>
+#include <rte_random.h>
+#include <rte_udp.h>
+
+#include "test.h"
+
+#define MAX_FLOWS	    (1024 * 32)
+#define MAX_BKTS	    MAX_FLOWS
+#define MAX_ENTRIES_PER_BKT 16
+#define MAX_FRAGMENTS	    RTE_LIBRTE_IP_FRAG_MAX_FRAG
+#define MIN_FRAGMENTS	    2
+#define MAX_PKTS	    (MAX_FLOWS * MAX_FRAGMENTS)
+
+#define MAX_PKT_LEN 2048
+#define MAX_TTL_MS  (5 * MS_PER_S)
+
+/* use RFC863 Discard Protocol */
+#define UDP_SRC_PORT 9
+#define UDP_DST_PORT 9
+
+/* use RFC5735 / RFC2544 reserved network test addresses */
+#define IP_SRC_ADDR(x) ((198U << 24) | (18 << 16) | (0 << 8) | (x))
+#define IP_DST_ADDR(x) ((198U << 24) | (18 << 16) | (1 << 8) | (x))
+
+/* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180) */
+static uint8_t ip6_addr[16] = {32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#define IP6_VERSION 6
+
+#define IP_DEFTTL 64 /* from RFC 1340. */
+
+static struct rte_ip_frag_tbl *frag_tbl;
+static struct rte_mempool *pkt_pool;
+static struct rte_mbuf *mbufs[MAX_FLOWS][MAX_FRAGMENTS];
+static uint8_t frag_per_flow[MAX_FLOWS];
+static uint32_t flow_cnt;
+
+#define FILL_MODE_LINEAR      0
+#define FILL_MODE_RANDOM      1
+#define FILL_MODE_INTERLEAVED 2
+
+static int
+reassembly_test_setup(void)
+{
+	uint64_t max_ttl_cyc = (MAX_TTL_MS * rte_get_timer_hz()) / 1E3;
+
+	frag_tbl = rte_ip_frag_table_create(MAX_FLOWS, MAX_ENTRIES_PER_BKT,
+					    MAX_FLOWS * MAX_ENTRIES_PER_BKT,
+					    max_ttl_cyc, rte_socket_id());
+	if (frag_tbl == NULL)
+		return TEST_FAILED;
+
+	rte_mbuf_set_user_mempool_ops("ring_mp_mc");
+	pkt_pool = rte_pktmbuf_pool_create(
+		"reassembly_perf_pool", MAX_FLOWS * MAX_FRAGMENTS, 0, 0,
+		RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+	if (pkt_pool == NULL) {
+		printf("[%s] Failed to create pkt pool\n", __func__);
+		rte_ip_frag_table_destroy(frag_tbl);
+		return TEST_FAILED;
+	}
+
+	return TEST_SUCCESS;
+}
+
+static void
+reassembly_test_teardown(void)
+{
+	if (frag_tbl != NULL)
+		rte_ip_frag_table_destroy(frag_tbl);
+
+	if (pkt_pool != NULL)
+		rte_mempool_free(pkt_pool);
+}
+
+static void
+randomize_array_positions(void **array, uint8_t sz)
+{
+	void *tmp;
+	int i, j;
+
+	if (sz == 2) {
+		tmp = array[0];
+		array[0] = array[1];
+		array[1] = tmp;
+	} else {
+		for (i = sz - 1; i > 0; i--) {
+			j = rte_rand_max(i + 1);
+			tmp = array[i];
+			array[i] = array[j];
+			array[j] = tmp;
+		}
+	}
+}
+
+static void
+reassembly_print_banner(const char *proto_str)
+{
+	printf("+=============================================================="
+	       "============================================+\n");
+	printf("| %-32s| %-3s : %-58d|\n", proto_str, "Flow Count", MAX_FLOWS);
+	printf("+================+================+=============+=============+"
+	       "========================+===================+\n");
+	printf("%-17s%-17s%-14s%-14s%-25s%-20s\n", "| Fragment Order",
+	       "| Fragments/Flow", "| Outstanding", "| Cycles/Flow",
+	       "| Cycles/Fragment insert", "| Cycles/Reassembly |");
+	printf("+================+================+=============+=============+"
+	       "========================+===================+\n");
+}
+
+static void
+ipv4_frag_fill_data(struct rte_mbuf **mbuf, uint8_t nb_frags, uint32_t flow_id,
+		    uint8_t fill_mode)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ipv4_hdr *ip_hdr;
+	struct rte_udp_hdr *udp_hdr;
+	uint16_t frag_len;
+	uint8_t i;
+
+	frag_len = MAX_PKT_LEN / nb_frags;
+	if (frag_len % 8)
+		frag_len = RTE_ALIGN_MUL_CEIL(frag_len, 8);
+
+	for (i = 0; i < nb_frags; i++) {
+		struct rte_mbuf *frag = mbuf[i];
+		uint16_t frag_offset = 0;
+		uint32_t ip_cksum;
+		uint16_t pkt_len;
+		uint16_t *ptr16;
+
+		frag_offset = i * (frag_len / 8);
+
+		if (i == nb_frags - 1)
+			frag_len = MAX_PKT_LEN - (frag_len * (nb_frags - 1));
+		else
+			frag_offset |= RTE_IPV4_HDR_MF_FLAG;
+
+		rte_pktmbuf_reset_headroom(frag);
+		eth_hdr = rte_pktmbuf_mtod(frag, struct rte_ether_hdr *);
+		ip_hdr = rte_pktmbuf_mtod_offset(frag, struct rte_ipv4_hdr *,
+						 sizeof(struct rte_ether_hdr));
+		udp_hdr = rte_pktmbuf_mtod_offset(
+			frag, struct rte_udp_hdr *,
+			sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv4_hdr));
+
+		rte_ether_unformat_addr("02:00:00:00:00:01", &eth_hdr->d_addr);
+		rte_ether_unformat_addr("02:00:00:00:00:00", &eth_hdr->s_addr);
+		eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+
+		pkt_len = frag_len;
+		/*
+		 * Initialize UDP header.
+		 */
+		if (i == 0) {
+			udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
+			udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
+			udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
+			udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
+		}
+
+		/*
+		 * Initialize IP header.
+		 */
+		pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv4_hdr));
+		ip_hdr->version_ihl = RTE_IPV4_VHL_DEF;
+		ip_hdr->type_of_service = 0;
+		ip_hdr->fragment_offset = rte_cpu_to_be_16(frag_offset);
+		ip_hdr->time_to_live = IP_DEFTTL;
+		ip_hdr->next_proto_id = IPPROTO_UDP;
+		ip_hdr->packet_id =
+			rte_cpu_to_be_16((flow_id + 1) % UINT16_MAX);
+		ip_hdr->total_length = rte_cpu_to_be_16(pkt_len);
+		ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR(flow_id));
+		ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR(flow_id));
+
+		/*
+		 * Compute IP header checksum.
+		 */
+		ptr16 = (unaligned_uint16_t *)ip_hdr;
+		ip_cksum = 0;
+		ip_cksum += ptr16[0];
+		ip_cksum += ptr16[1];
+		ip_cksum += ptr16[2];
+		ip_cksum += ptr16[3];
+		ip_cksum += ptr16[4];
+		ip_cksum += ptr16[6];
+		ip_cksum += ptr16[7];
+		ip_cksum += ptr16[8];
+		ip_cksum += ptr16[9];
+
+		/*
+		 * Reduce 32 bit checksum to 16 bits and complement it.
+		 */
+		ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) +
+			   (ip_cksum & 0x0000FFFF);
+		if (ip_cksum > 65535)
+			ip_cksum -= 65535;
+		ip_cksum = (~ip_cksum) & 0x0000FFFF;
+		if (ip_cksum == 0)
+			ip_cksum = 0xFFFF;
+		ip_hdr->hdr_checksum = (uint16_t)ip_cksum;
+
+		frag->data_len = sizeof(struct rte_ether_hdr) + pkt_len;
+		frag->pkt_len = frag->data_len;
+		frag->l2_len = sizeof(struct rte_ether_hdr);
+		frag->l3_len = sizeof(struct rte_ipv4_hdr);
+	}
+
+	if (fill_mode == FILL_MODE_RANDOM)
+		randomize_array_positions((void **)mbuf, nb_frags);
+}
+
+static uint8_t
+get_rand_frags(uint8_t max_frag)
+{
+	uint8_t frags = rte_rand_max(max_frag + 1);
+
+	return frags <= 1 ? MIN_FRAGMENTS : frags;
+}
+
+static int
+ipv4_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t max_frag)
+{
+	uint8_t nb_frag;
+	int i;
+
+	for (i = 0; i < MAX_FLOWS; i++) {
+		nb_frag = get_rand_frags(max_frag);
+		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
+		    0)
+			return TEST_FAILED;
+		ipv4_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+		frag_per_flow[i] = nb_frag;
+	}
+	flow_cnt = i;
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv4_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
+{
+	int i;
+
+	for (i = 0; i < MAX_FLOWS; i++) {
+		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
+		    0)
+			return TEST_FAILED;
+		ipv4_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+		frag_per_flow[i] = nb_frag;
+	}
+	flow_cnt = i;
+
+	return TEST_SUCCESS;
+}
+
+static void
+ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t nb_frags, uint32_t flow_id,
+		    uint8_t fill_mode)
+{
+	struct ipv6_extension_fragment *frag_hdr;
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ipv6_hdr *ip_hdr;
+	struct rte_udp_hdr *udp_hdr;
+	uint16_t frag_len;
+	uint8_t i;
+
+	frag_len = MAX_PKT_LEN / nb_frags;
+	if (frag_len % 8)
+		frag_len = RTE_ALIGN_MUL_CEIL(frag_len, 8);
+
+	for (i = 0; i < nb_frags; i++) {
+		struct rte_mbuf *frag = mbuf[i];
+		uint16_t frag_offset = 0;
+		uint16_t pkt_len;
+
+		frag_offset = i * (frag_len / 8);
+		frag_offset <<= 3;
+		if (i == nb_frags - 1) {
+			frag_len = MAX_PKT_LEN - (frag_len * (nb_frags - 1));
+			frag_offset = RTE_IPV6_SET_FRAG_DATA(frag_offset, 0);
+		} else {
+			frag_offset = RTE_IPV6_SET_FRAG_DATA(frag_offset, 1);
+		}
+
+		rte_pktmbuf_reset_headroom(frag);
+		eth_hdr = rte_pktmbuf_mtod(frag, struct rte_ether_hdr *);
+		ip_hdr = rte_pktmbuf_mtod_offset(frag, struct rte_ipv6_hdr *,
+						 sizeof(struct rte_ether_hdr));
+		udp_hdr = rte_pktmbuf_mtod_offset(
+			frag, struct rte_udp_hdr *,
+			sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv6_hdr) +
+				RTE_IPV6_FRAG_HDR_SIZE);
+		frag_hdr = rte_pktmbuf_mtod_offset(
+			frag, struct ipv6_extension_fragment *,
+			sizeof(struct rte_ether_hdr) +
+				sizeof(struct rte_ipv6_hdr));
+
+		rte_ether_unformat_addr("02:00:00:00:00:01", &eth_hdr->d_addr);
+		rte_ether_unformat_addr("02:00:00:00:00:00", &eth_hdr->s_addr);
+		eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
+
+		pkt_len = frag_len;
+		/*
+		 * Initialize UDP header.
+		 */
+		if (i == 0) {
+			udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
+			udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
+			udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
+			udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
+		}
+
+		/*
+		 * Initialize IP header.
+		 */
+		pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv6_hdr) +
+				     RTE_IPV6_FRAG_HDR_SIZE);
+		ip_hdr->vtc_flow = rte_cpu_to_be_32(IP6_VERSION << 28);
+		ip_hdr->payload_len =
+			rte_cpu_to_be_16(pkt_len - sizeof(struct rte_ipv6_hdr));
+		ip_hdr->proto = IPPROTO_FRAGMENT;
+		ip_hdr->hop_limits = IP_DEFTTL;
+		memcpy(ip_hdr->src_addr, ip6_addr, sizeof(ip_hdr->src_addr));
+		memcpy(ip_hdr->dst_addr, ip6_addr, sizeof(ip_hdr->dst_addr));
+		ip_hdr->src_addr[7] = (flow_id >> 16) & 0xf;
+		ip_hdr->src_addr[7] |= 0x10;
+		ip_hdr->src_addr[8] = (flow_id >> 8) & 0xff;
+		ip_hdr->src_addr[9] = flow_id & 0xff;
+
+		ip_hdr->dst_addr[7] = (flow_id >> 16) & 0xf;
+		ip_hdr->dst_addr[7] |= 0x20;
+		ip_hdr->dst_addr[8] = (flow_id >> 8) & 0xff;
+		ip_hdr->dst_addr[9] = flow_id & 0xff;
+
+		frag_hdr->next_header = IPPROTO_UDP;
+		frag_hdr->reserved = 0;
+		frag_hdr->frag_data = rte_cpu_to_be_16(frag_offset);
+		frag_hdr->id = rte_cpu_to_be_32(flow_id + 1);
+
+		frag->data_len = sizeof(struct rte_ether_hdr) + pkt_len;
+		frag->pkt_len = frag->data_len;
+		frag->l2_len = sizeof(struct rte_ether_hdr);
+		frag->l3_len =
+			sizeof(struct rte_ipv6_hdr) + RTE_IPV6_FRAG_HDR_SIZE;
+	}
+
+	if (fill_mode == FILL_MODE_RANDOM)
+		randomize_array_positions((void **)mbuf, nb_frags);
+}
+
+static int
+ipv6_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t max_frag)
+{
+	uint8_t nb_frag;
+	int i;
+
+	for (i = 0; i < MAX_FLOWS; i++) {
+		nb_frag = get_rand_frags(max_frag);
+		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
+		    0)
+			return TEST_FAILED;
+		ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+		frag_per_flow[i] = nb_frag;
+	}
+	flow_cnt = i;
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv6_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
+{
+	int i;
+
+	for (i = 0; i < MAX_FLOWS; i++) {
+		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
+		    0)
+			return TEST_FAILED;
+		ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+		frag_per_flow[i] = nb_frag;
+	}
+	flow_cnt = i;
+
+	return TEST_SUCCESS;
+}
+
+static void
+frag_pkt_teardown(void)
+{
+	uint32_t i;
+
+	for (i = 0; i < flow_cnt; i++)
+		rte_pktmbuf_free(mbufs[i][0]);
+}
+
+static void
+reassembly_print_stats(int8_t nb_frags, uint8_t fill_order,
+		       uint32_t outstanding, uint64_t cyc_per_flow,
+		       uint64_t cyc_per_frag_insert,
+		       uint64_t cyc_per_reassembly)
+{
+	char frag_str[8], order_str[12];
+
+	if (nb_frags > 0)
+		snprintf(frag_str, sizeof(frag_str), "%d", nb_frags);
+	else
+		snprintf(frag_str, sizeof(frag_str), "RANDOM");
+
+	switch (fill_order) {
+	case FILL_MODE_LINEAR:
+		snprintf(order_str, sizeof(order_str), "LINEAR");
+		break;
+	case FILL_MODE_RANDOM:
+		snprintf(order_str, sizeof(order_str), "RANDOM");
+		break;
+	case FILL_MODE_INTERLEAVED:
+		snprintf(order_str, sizeof(order_str), "INTERLEAVED");
+		break;
+	default:
+		break;
+	}
+
+	printf("| %-14s | %-14s | %-11d | %-11" PRIu64 " | %-22" PRIu64
+	       " | %-17" PRIu64 " |\n",
+	       order_str, frag_str, outstanding, cyc_per_flow,
+	       cyc_per_frag_insert, cyc_per_reassembly);
+	printf("+================+================+=============+=============+"
+	       "========================+===================+\n");
+}
+
+static void
+join_array(struct rte_mbuf **dest_arr, struct rte_mbuf **src_arr,
+	   uint8_t offset, uint8_t sz)
+{
+	int i, j;
+
+	for (i = offset, j = 0; j < sz; i++, j++)
+		dest_arr[i] = src_arr[j];
+}
+
+static int
+ipv4_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j;
+
+	for (i = 0; i < flow_cnt; i++) {
+		struct rte_mbuf *buf_out = NULL;
+		uint8_t reassembled = 0;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < frag_per_flow[i]; j++) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv4_hdr *, buf->l2_len);
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv4_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
+
+			if (buf_out == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled = 1;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (!reassembled || buf_out->nb_segs != frag_per_flow[i])
+			return TEST_FAILED;
+		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+		mbufs[i][0] = buf_out;
+	}
+
+	reassembly_print_stats(nb_frags, fill_order, 0, total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv4_outstanding_reassembly_perf(int8_t nb_frags, uint8_t fill_order,
+				 uint32_t outstanding)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j, k;
+
+	k = outstanding;
+	/* Insert outstanding fragments */
+	for (i = 0; k && (i < flow_cnt); i++) {
+		struct rte_mbuf *buf_out = NULL;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = frag_per_flow[i] - 1; j > 0; j--) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv4_hdr *, buf->l2_len);
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv4_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
+			total_empty_cyc += rte_rdtsc() - tstamp;
+			frag_processed++;
+			if (buf_out != NULL)
+				return TEST_FAILED;
+
+			k--;
+		}
+		frag_per_flow[i] = 1;
+	}
+
+	for (i = 0; i < flow_cnt; i++) {
+		struct rte_mbuf *buf_out = NULL;
+		uint8_t reassembled = 0;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < frag_per_flow[i]; j++) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv4_hdr *, buf->l2_len);
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv4_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
+
+			if (buf_out == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled = 1;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (!reassembled)
+			return TEST_FAILED;
+		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+		mbufs[i][0] = buf_out;
+	}
+
+	reassembly_print_stats(nb_frags, fill_order, outstanding,
+			       total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv4_reassembly_interleaved_flows_perf(uint8_t nb_frags)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j;
+
+	for (i = 0; i < flow_cnt; i += 4) {
+		struct rte_mbuf *buf_out[4] = {NULL};
+		uint8_t reassembled = 0;
+		uint8_t nb_frags = 0;
+		uint8_t prev = 0;
+
+		for (j = 0; j < 4; j++)
+			nb_frags += frag_per_flow[i + j];
+
+		struct rte_mbuf *buf_arr[nb_frags];
+		for (j = 0; j < 4; j++) {
+			join_array(buf_arr, mbufs[i + j], prev,
+				   frag_per_flow[i + j]);
+			prev += frag_per_flow[i + j];
+		}
+		randomize_array_positions((void **)buf_arr, nb_frags);
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < nb_frags; j++) {
+			struct rte_mbuf *buf = buf_arr[j];
+			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv4_hdr *, buf->l2_len);
+
+			tstamp = rte_rdtsc();
+			buf_out[reassembled] = rte_ipv4_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
+
+			if (buf_out[reassembled] == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled++;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (reassembled != 4)
+			return TEST_FAILED;
+		for (j = 0; j < 4; j++) {
+			memset(mbufs[i + j], 0,
+			       sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+			mbufs[i + j][0] = buf_out[j];
+		}
+	}
+
+	reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0,
+			       total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv6_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j;
+
+	for (i = 0; i < flow_cnt; i++) {
+		struct rte_mbuf *buf_out = NULL;
+		uint8_t reassembled = 0;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < frag_per_flow[i]; j++) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv6_hdr *, buf->l2_len);
+			struct ipv6_extension_fragment *frag_hdr =
+				rte_pktmbuf_mtod_offset(
+					buf, struct ipv6_extension_fragment *,
+					buf->l2_len +
+						sizeof(struct rte_ipv6_hdr));
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv6_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
+				frag_hdr);
+
+			if (buf_out == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled = 1;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (!reassembled || buf_out->nb_segs != frag_per_flow[i])
+			return TEST_FAILED;
+		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+		mbufs[i][0] = buf_out;
+	}
+
+	reassembly_print_stats(nb_frags, fill_order, 0, total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv6_outstanding_reassembly_perf(int8_t nb_frags, uint8_t fill_order,
+				 uint32_t outstanding)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j, k;
+
+	k = outstanding;
+	/* Insert outstanding fragments */
+	for (i = 0; k && (i < flow_cnt); i++) {
+		struct rte_mbuf *buf_out = NULL;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = frag_per_flow[i] - 1; j > 0; j--) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv6_hdr *, buf->l2_len);
+			struct ipv6_extension_fragment *frag_hdr =
+				rte_pktmbuf_mtod_offset(
+					buf, struct ipv6_extension_fragment *,
+					buf->l2_len +
+						sizeof(struct rte_ipv6_hdr));
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv6_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
+				frag_hdr);
+			total_empty_cyc += rte_rdtsc() - tstamp;
+			frag_processed++;
+
+			if (buf_out != NULL)
+				return TEST_FAILED;
+
+			k--;
+		}
+		frag_per_flow[i] = 1;
+	}
+
+	for (i = 0; i < flow_cnt; i++) {
+		struct rte_mbuf *buf_out = NULL;
+		uint8_t reassembled = 0;
+
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < frag_per_flow[i]; j++) {
+			struct rte_mbuf *buf = mbufs[i][j];
+			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv6_hdr *, buf->l2_len);
+			struct ipv6_extension_fragment *frag_hdr =
+				rte_pktmbuf_mtod_offset(
+					buf, struct ipv6_extension_fragment *,
+					buf->l2_len +
+						sizeof(struct rte_ipv6_hdr));
+
+			tstamp = rte_rdtsc();
+			buf_out = rte_ipv6_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
+				frag_hdr);
+
+			if (buf_out == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled = 1;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (!reassembled)
+			return TEST_FAILED;
+		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+		mbufs[i][0] = buf_out;
+	}
+
+	reassembly_print_stats(nb_frags, fill_order, outstanding,
+			       total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv6_reassembly_interleaved_flows_perf(int8_t nb_frags)
+{
+	struct rte_ip_frag_death_row death_row;
+	uint64_t total_reassembled_cyc = 0;
+	uint64_t total_empty_cyc = 0;
+	uint64_t tstamp, flow_tstamp;
+	uint64_t frag_processed = 0;
+	uint64_t total_cyc = 0;
+	uint32_t i, j;
+
+	for (i = 0; i < flow_cnt; i += 4) {
+		struct rte_mbuf *buf_out[4] = {NULL};
+		uint8_t reassembled = 0;
+		uint8_t nb_frags = 0;
+		uint8_t prev = 0;
+
+		for (j = 0; j < 4; j++)
+			nb_frags += frag_per_flow[i + j];
+
+		struct rte_mbuf *buf_arr[nb_frags];
+		for (j = 0; j < 4; j++) {
+			join_array(buf_arr, mbufs[i + j], prev,
+				   frag_per_flow[i + j]);
+			prev += frag_per_flow[i + j];
+		}
+		randomize_array_positions((void **)buf_arr, nb_frags);
+		flow_tstamp = rte_rdtsc();
+		for (j = 0; j < nb_frags; j++) {
+			struct rte_mbuf *buf = buf_arr[j];
+			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
+				buf, struct rte_ipv6_hdr *, buf->l2_len);
+			struct ipv6_extension_fragment *frag_hdr =
+				rte_pktmbuf_mtod_offset(
+					buf, struct ipv6_extension_fragment *,
+					buf->l2_len +
+						sizeof(struct rte_ipv6_hdr));
+
+			tstamp = rte_rdtsc();
+			buf_out[reassembled] = rte_ipv6_frag_reassemble_packet(
+				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
+				frag_hdr);
+
+			if (buf_out[reassembled] == NULL) {
+				total_empty_cyc += rte_rdtsc() - tstamp;
+				frag_processed++;
+				continue;
+			} else {
+				/*Packet out*/
+				total_reassembled_cyc += rte_rdtsc() - tstamp;
+				reassembled++;
+			}
+		}
+		total_cyc += rte_rdtsc() - flow_tstamp;
+		if (reassembled != 4)
+			return TEST_FAILED;
+		for (j = 0; j < 4; j++) {
+			memset(mbufs[i + j], 0,
+			       sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
+			mbufs[i + j][0] = buf_out[j];
+		}
+	}
+
+	reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0,
+			       total_cyc / flow_cnt,
+			       total_empty_cyc / frag_processed,
+			       total_reassembled_cyc / flow_cnt);
+
+	return TEST_SUCCESS;
+}
+
+static int
+ipv4_reassembly_test(int8_t nb_frags, uint8_t fill_order, uint32_t outstanding)
+{
+	int rc;
+
+	if (nb_frags > 0)
+		rc = ipv4_frag_pkt_setup(fill_order, nb_frags);
+	else
+		rc = ipv4_rand_frag_pkt_setup(fill_order, MAX_FRAGMENTS);
+
+	if (rc)
+		return rc;
+
+	if (outstanding)
+		rc = ipv4_outstanding_reassembly_perf(nb_frags, fill_order,
+						      outstanding);
+	else if (fill_order == FILL_MODE_INTERLEAVED)
+		rc = ipv4_reassembly_interleaved_flows_perf(nb_frags);
+	else
+		rc = ipv4_reassembly_perf(nb_frags, fill_order);
+
+	frag_pkt_teardown();
+
+	return rc;
+}
+
+static int
+ipv6_reassembly_test(int8_t nb_frags, uint8_t fill_order, uint32_t outstanding)
+{
+	int rc;
+
+	if (nb_frags > 0)
+		rc = ipv6_frag_pkt_setup(fill_order, nb_frags);
+	else
+		rc = ipv6_rand_frag_pkt_setup(fill_order, MAX_FRAGMENTS);
+
+	if (rc)
+		return rc;
+
+	if (outstanding)
+		rc = ipv6_outstanding_reassembly_perf(nb_frags, fill_order,
+						      outstanding);
+	else if (fill_order == FILL_MODE_INTERLEAVED)
+		rc = ipv6_reassembly_interleaved_flows_perf(nb_frags);
+	else
+		rc = ipv6_reassembly_perf(nb_frags, fill_order);
+
+	frag_pkt_teardown();
+
+	return rc;
+}
+
+static int
+test_reassembly_perf(void)
+{
+	int8_t nb_fragments[] = {2, 3, MAX_FRAGMENTS, -1 /* Random */};
+	uint8_t order_type[] = {FILL_MODE_LINEAR, FILL_MODE_RANDOM};
+	uint32_t outstanding[] = {100, 500, 1000, 2000, 3000};
+	uint32_t i, j;
+	int rc;
+
+	rc = reassembly_test_setup();
+	if (rc)
+		return rc;
+
+	reassembly_print_banner("IPV4");
+	/* Test variable fragment count and ordering. */
+	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
+		for (j = 0; j < RTE_DIM(order_type); j++) {
+			rc = ipv4_reassembly_test(nb_fragments[i],
+						  order_type[j], 0);
+			if (rc)
+				return rc;
+		}
+	}
+
+	/* Test outstanding fragments in the table. */
+	for (i = 0; i < RTE_DIM(outstanding); i++) {
+		rc = ipv4_reassembly_test(2, 0, outstanding[i]);
+		if (rc)
+			return rc;
+	}
+	for (i = 0; i < RTE_DIM(outstanding); i++) {
+		rc = ipv4_reassembly_test(MAX_FRAGMENTS, 0, outstanding[i]);
+		if (rc)
+			return rc;
+	}
+
+	/* Test interleaved flow reassembly perf */
+	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
+		rc = ipv4_reassembly_test(nb_fragments[i],
+					  FILL_MODE_INTERLEAVED, 0);
+		if (rc)
+			return rc;
+	}
+	printf("\n");
+	reassembly_print_banner("IPV6");
+	/* Test variable fragment count and ordering. */
+	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
+		for (j = 0; j < RTE_DIM(order_type); j++) {
+			rc = ipv6_reassembly_test(nb_fragments[i],
+						  order_type[j], 0);
+			if (rc)
+				return rc;
+		}
+	}
+
+	/* Test outstanding fragments in the table. */
+	for (i = 0; i < RTE_DIM(outstanding); i++) {
+		rc = ipv6_reassembly_test(2, 0, outstanding[i]);
+		if (rc)
+			return rc;
+	}
+
+	for (i = 0; i < RTE_DIM(outstanding); i++) {
+		rc = ipv6_reassembly_test(MAX_FRAGMENTS, 0, outstanding[i]);
+		if (rc)
+			return rc;
+	}
+
+	/* Test interleaved flow reassembly perf */
+	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
+		rc = ipv6_reassembly_test(nb_fragments[i],
+					  FILL_MODE_INTERLEAVED, 0);
+		if (rc)
+			return rc;
+	}
+	reassembly_test_teardown();
+
+	return TEST_SUCCESS;
+}
+
+REGISTER_TEST_COMMAND(reassembly_perf_autotest, test_reassembly_perf);
--
2.17.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-10-04 14:23 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-04 13:33 [dpdk-dev] [PATCH v2] test: add reassembly perf test pbhagavatula
2021-10-04 14:23 ` [dpdk-dev] [PATCH v3] " pbhagavatula

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).