xref: /dpdk/app/test/test_reassembly_perf.c (revision 7917b0d38e92e8b9ec5a870415b791420e10f11a)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2023 Marvell.
3  */
4 
5 #include <rte_byteorder.h>
6 #include <rte_common.h>
7 #include <rte_cycles.h>
8 #include <rte_ether.h>
9 #include <rte_hexdump.h>
10 #include <rte_ip.h>
11 #include <rte_ip_frag.h>
12 #include <rte_mbuf.h>
13 #include <rte_mbuf_pool_ops.h>
14 #include <rte_os_shim.h>
15 #include <rte_random.h>
16 #include <rte_udp.h>
17 
18 #include "test.h"
19 
20 #define MAX_FLOWS	    (1024 * 32)
21 #define MAX_BKTS	    MAX_FLOWS
22 #define MAX_ENTRIES_PER_BKT 16
23 #define MAX_FRAGMENTS	    RTE_LIBRTE_IP_FRAG_MAX_FRAG
24 #define MIN_FRAGMENTS	    2
25 #define MAX_PKTS	    (MAX_FLOWS * MAX_FRAGMENTS)
26 
27 #define MAX_PKT_LEN 2048
28 #define MAX_TTL_MS  (5 * MS_PER_S)
29 
30 /* use RFC863 Discard Protocol */
31 #define UDP_SRC_PORT 9
32 #define UDP_DST_PORT 9
33 
34 /* use RFC5735 / RFC2544 reserved network test addresses */
35 #define IP_SRC_ADDR(x) ((198U << 24) | (18 << 16) | (0 << 8) | (x))
36 #define IP_DST_ADDR(x) ((198U << 24) | (18 << 16) | (1 << 15) | (x))
37 
38 /* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180) */
39 static uint8_t ip6_addr[16] = {32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
40 #define IP6_VERSION 6
41 
42 #define IP_DEFTTL 64 /* from RFC 1340. */
43 
44 static struct rte_ip_frag_tbl *frag_tbl;
45 static struct rte_mempool *pkt_pool;
46 static struct rte_mbuf *mbufs[MAX_FLOWS][MAX_FRAGMENTS];
47 static uint8_t frag_per_flow[MAX_FLOWS];
48 static uint32_t flow_cnt;
49 
50 #define FILL_MODE_LINEAR      0
51 #define FILL_MODE_RANDOM      1
52 #define FILL_MODE_INTERLEAVED 2
53 
54 static int
55 reassembly_test_setup(void)
56 {
57 	uint64_t max_ttl_cyc = (MAX_TTL_MS * rte_get_timer_hz()) / 1E3;
58 
59 	frag_tbl = rte_ip_frag_table_create(MAX_BKTS, MAX_ENTRIES_PER_BKT,
60 					    MAX_BKTS * MAX_ENTRIES_PER_BKT, max_ttl_cyc,
61 					    rte_socket_id());
62 	if (frag_tbl == NULL)
63 		return TEST_FAILED;
64 
65 	rte_mbuf_set_user_mempool_ops("ring_mp_mc");
66 	pkt_pool = rte_pktmbuf_pool_create(
67 		"reassembly_perf_pool", MAX_FLOWS * MAX_FRAGMENTS, 0, 0,
68 		RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
69 	if (pkt_pool == NULL) {
70 		printf("[%s] Failed to create pkt pool\n", __func__);
71 		rte_ip_frag_table_destroy(frag_tbl);
72 		return TEST_FAILED;
73 	}
74 
75 	return TEST_SUCCESS;
76 }
77 
78 static void
79 reassembly_test_teardown(void)
80 {
81 	if (frag_tbl != NULL)
82 		rte_ip_frag_table_destroy(frag_tbl);
83 
84 	rte_mempool_free(pkt_pool);
85 }
86 
87 static void
88 randomize_array_positions(void **array, uint8_t sz)
89 {
90 	void *tmp;
91 	int i, j;
92 
93 	if (sz == 2) {
94 		tmp = array[0];
95 		array[0] = array[1];
96 		array[1] = tmp;
97 	} else {
98 		for (i = sz - 1; i > 0; i--) {
99 			j = rte_rand_max(i + 1);
100 			tmp = array[i];
101 			array[i] = array[j];
102 			array[j] = tmp;
103 		}
104 	}
105 }
106 
107 static void
108 reassembly_print_banner(const char *proto_str)
109 {
110 	printf("+=============================================================="
111 	       "============================================+\n");
112 	printf("| %-32s| %-3s : %-58d|\n", proto_str, "Flow Count", MAX_FLOWS);
113 	printf("+================+================+=============+=============+"
114 	       "========================+===================+\n");
115 	printf("%-17s%-17s%-14s%-14s%-25s%-20s\n", "| Fragment Order",
116 	       "| Fragments/Flow", "| Outstanding", "| Cycles/Flow",
117 	       "| Cycles/Fragment insert", "| Cycles/Reassembly |");
118 	printf("+================+================+=============+=============+"
119 	       "========================+===================+\n");
120 }
121 
122 static void
123 ipv4_frag_fill_data(struct rte_mbuf **mbuf, uint8_t nb_frags, uint32_t flow_id,
124 		    uint8_t fill_mode)
125 {
126 	struct rte_ether_hdr *eth_hdr;
127 	struct rte_ipv4_hdr *ip_hdr;
128 	struct rte_udp_hdr *udp_hdr;
129 	uint16_t frag_len;
130 	uint8_t i;
131 
132 	frag_len = MAX_PKT_LEN / nb_frags;
133 	if (frag_len % 8)
134 		frag_len = RTE_ALIGN_MUL_CEIL(frag_len, 8);
135 
136 	for (i = 0; i < nb_frags; i++) {
137 		struct rte_mbuf *frag = mbuf[i];
138 		uint16_t frag_offset = 0;
139 		uint32_t ip_cksum;
140 		uint16_t pkt_len;
141 		uint16_t *ptr16;
142 
143 		frag_offset = i * (frag_len / 8);
144 
145 		if (i == nb_frags - 1)
146 			frag_len = MAX_PKT_LEN - (frag_len * (nb_frags - 1));
147 		else
148 			frag_offset |= RTE_IPV4_HDR_MF_FLAG;
149 
150 		rte_pktmbuf_reset_headroom(frag);
151 		eth_hdr = rte_pktmbuf_mtod(frag, struct rte_ether_hdr *);
152 		ip_hdr = rte_pktmbuf_mtod_offset(frag, struct rte_ipv4_hdr *,
153 						 sizeof(struct rte_ether_hdr));
154 		udp_hdr = rte_pktmbuf_mtod_offset(
155 			frag, struct rte_udp_hdr *,
156 			sizeof(struct rte_ether_hdr) +
157 				sizeof(struct rte_ipv4_hdr));
158 
159 		rte_ether_unformat_addr("02:00:00:00:00:01",
160 					&eth_hdr->dst_addr);
161 		rte_ether_unformat_addr("02:00:00:00:00:00",
162 					&eth_hdr->src_addr);
163 		eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
164 
165 		pkt_len = frag_len;
166 		/*
167 		 * Initialize UDP header.
168 		 */
169 		if (i == 0) {
170 			udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
171 			udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
172 			udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
173 			udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
174 		}
175 
176 		/*
177 		 * Initialize IP header.
178 		 */
179 		pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv4_hdr));
180 		ip_hdr->version_ihl = RTE_IPV4_VHL_DEF;
181 		ip_hdr->type_of_service = 0;
182 		ip_hdr->fragment_offset = rte_cpu_to_be_16(frag_offset);
183 		ip_hdr->time_to_live = IP_DEFTTL;
184 		ip_hdr->next_proto_id = IPPROTO_UDP;
185 		ip_hdr->packet_id =
186 			rte_cpu_to_be_16((flow_id + 1) % UINT16_MAX);
187 		ip_hdr->total_length = rte_cpu_to_be_16(pkt_len);
188 		/* Using more than 32K flows will modify the 2nd octet of the IP. */
189 		ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR(flow_id));
190 		ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR(flow_id));
191 
192 		/*
193 		 * Compute IP header checksum.
194 		 */
195 		ptr16 = (unaligned_uint16_t *)ip_hdr;
196 		ip_cksum = 0;
197 		ip_cksum += ptr16[0];
198 		ip_cksum += ptr16[1];
199 		ip_cksum += ptr16[2];
200 		ip_cksum += ptr16[3];
201 		ip_cksum += ptr16[4];
202 		ip_cksum += ptr16[6];
203 		ip_cksum += ptr16[7];
204 		ip_cksum += ptr16[8];
205 		ip_cksum += ptr16[9];
206 
207 		/*
208 		 * Reduce 32 bit checksum to 16 bits and complement it.
209 		 */
210 		ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) +
211 			   (ip_cksum & 0x0000FFFF);
212 		if (ip_cksum > 65535)
213 			ip_cksum -= 65535;
214 		ip_cksum = (~ip_cksum) & 0x0000FFFF;
215 		if (ip_cksum == 0)
216 			ip_cksum = 0xFFFF;
217 		ip_hdr->hdr_checksum = (uint16_t)ip_cksum;
218 
219 		frag->data_len = sizeof(struct rte_ether_hdr) + pkt_len;
220 		frag->pkt_len = frag->data_len;
221 		frag->l2_len = sizeof(struct rte_ether_hdr);
222 		frag->l3_len = sizeof(struct rte_ipv4_hdr);
223 	}
224 
225 	if (fill_mode == FILL_MODE_RANDOM)
226 		randomize_array_positions((void **)mbuf, nb_frags);
227 }
228 
229 static uint8_t
230 get_rand_frags(uint8_t max_frag)
231 {
232 	uint8_t frags = rte_rand_max(max_frag + 1);
233 
234 	return frags <= 1 ? MIN_FRAGMENTS : frags;
235 }
236 
237 static int
238 ipv4_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t max_frag)
239 {
240 	uint8_t nb_frag;
241 	int i;
242 
243 	for (i = 0; i < MAX_FLOWS; i++) {
244 		nb_frag = get_rand_frags(max_frag);
245 		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
246 		    0)
247 			return TEST_FAILED;
248 		ipv4_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
249 		frag_per_flow[i] = nb_frag;
250 	}
251 	flow_cnt = i;
252 
253 	return TEST_SUCCESS;
254 }
255 
256 static int
257 ipv4_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
258 {
259 	int i;
260 
261 	for (i = 0; i < MAX_FLOWS; i++) {
262 		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
263 		    0)
264 			return TEST_FAILED;
265 		ipv4_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
266 		frag_per_flow[i] = nb_frag;
267 	}
268 	flow_cnt = i;
269 
270 	return TEST_SUCCESS;
271 }
272 
273 static void
274 ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t nb_frags, uint32_t flow_id,
275 		    uint8_t fill_mode)
276 {
277 	struct ipv6_extension_fragment *frag_hdr;
278 	struct rte_ether_hdr *eth_hdr;
279 	struct rte_ipv6_hdr *ip_hdr;
280 	struct rte_udp_hdr *udp_hdr;
281 	uint16_t frag_len;
282 	uint8_t i;
283 
284 	frag_len = MAX_PKT_LEN / nb_frags;
285 	if (frag_len % 8)
286 		frag_len = RTE_ALIGN_MUL_CEIL(frag_len, 8);
287 
288 	for (i = 0; i < nb_frags; i++) {
289 		struct rte_mbuf *frag = mbuf[i];
290 		uint16_t frag_offset = 0;
291 		uint16_t pkt_len;
292 
293 		frag_offset = i * (frag_len / 8);
294 		frag_offset <<= 3;
295 		if (i == nb_frags - 1) {
296 			frag_len = MAX_PKT_LEN - (frag_len * (nb_frags - 1));
297 			frag_offset = RTE_IPV6_SET_FRAG_DATA(frag_offset, 0);
298 		} else {
299 			frag_offset = RTE_IPV6_SET_FRAG_DATA(frag_offset, 1);
300 		}
301 
302 		rte_pktmbuf_reset_headroom(frag);
303 		eth_hdr = rte_pktmbuf_mtod(frag, struct rte_ether_hdr *);
304 		ip_hdr = rte_pktmbuf_mtod_offset(frag, struct rte_ipv6_hdr *,
305 						 sizeof(struct rte_ether_hdr));
306 		udp_hdr = rte_pktmbuf_mtod_offset(
307 			frag, struct rte_udp_hdr *,
308 			sizeof(struct rte_ether_hdr) +
309 				sizeof(struct rte_ipv6_hdr) +
310 				RTE_IPV6_FRAG_HDR_SIZE);
311 		frag_hdr = rte_pktmbuf_mtod_offset(
312 			frag, struct ipv6_extension_fragment *,
313 			sizeof(struct rte_ether_hdr) +
314 				sizeof(struct rte_ipv6_hdr));
315 
316 		rte_ether_unformat_addr("02:00:00:00:00:01",
317 					&eth_hdr->dst_addr);
318 		rte_ether_unformat_addr("02:00:00:00:00:00",
319 					&eth_hdr->src_addr);
320 		eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
321 
322 		pkt_len = frag_len;
323 		/*
324 		 * Initialize UDP header.
325 		 */
326 		if (i == 0) {
327 			udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
328 			udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
329 			udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
330 			udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
331 		}
332 
333 		/*
334 		 * Initialize IP header.
335 		 */
336 		pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv6_hdr) +
337 				     RTE_IPV6_FRAG_HDR_SIZE);
338 		ip_hdr->vtc_flow = rte_cpu_to_be_32(IP6_VERSION << 28);
339 		ip_hdr->payload_len =
340 			rte_cpu_to_be_16(pkt_len - sizeof(struct rte_ipv6_hdr));
341 		ip_hdr->proto = IPPROTO_FRAGMENT;
342 		ip_hdr->hop_limits = IP_DEFTTL;
343 		memcpy(ip_hdr->src_addr, ip6_addr, sizeof(ip_hdr->src_addr));
344 		memcpy(ip_hdr->dst_addr, ip6_addr, sizeof(ip_hdr->dst_addr));
345 		ip_hdr->src_addr[7] = (flow_id >> 16) & 0xf;
346 		ip_hdr->src_addr[7] |= 0x10;
347 		ip_hdr->src_addr[8] = (flow_id >> 8) & 0xff;
348 		ip_hdr->src_addr[9] = flow_id & 0xff;
349 
350 		ip_hdr->dst_addr[7] = (flow_id >> 16) & 0xf;
351 		ip_hdr->dst_addr[7] |= 0x20;
352 		ip_hdr->dst_addr[8] = (flow_id >> 8) & 0xff;
353 		ip_hdr->dst_addr[9] = flow_id & 0xff;
354 
355 		frag_hdr->next_header = IPPROTO_UDP;
356 		frag_hdr->reserved = 0;
357 		frag_hdr->frag_data = rte_cpu_to_be_16(frag_offset);
358 		frag_hdr->id = rte_cpu_to_be_32(flow_id + 1);
359 
360 		frag->data_len = sizeof(struct rte_ether_hdr) + pkt_len;
361 		frag->pkt_len = frag->data_len;
362 		frag->l2_len = sizeof(struct rte_ether_hdr);
363 		frag->l3_len =
364 			sizeof(struct rte_ipv6_hdr) + RTE_IPV6_FRAG_HDR_SIZE;
365 	}
366 
367 	if (fill_mode == FILL_MODE_RANDOM)
368 		randomize_array_positions((void **)mbuf, nb_frags);
369 }
370 
371 static int
372 ipv6_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t max_frag)
373 {
374 	uint8_t nb_frag;
375 	int i;
376 
377 	for (i = 0; i < MAX_FLOWS; i++) {
378 		nb_frag = get_rand_frags(max_frag);
379 		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
380 		    0)
381 			return TEST_FAILED;
382 		ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
383 		frag_per_flow[i] = nb_frag;
384 	}
385 	flow_cnt = i;
386 
387 	return TEST_SUCCESS;
388 }
389 
390 static int
391 ipv6_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
392 {
393 	int i;
394 
395 	for (i = 0; i < MAX_FLOWS; i++) {
396 		if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
397 		    0)
398 			return TEST_FAILED;
399 		ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
400 		frag_per_flow[i] = nb_frag;
401 	}
402 	flow_cnt = i;
403 
404 	return TEST_SUCCESS;
405 }
406 
407 static void
408 frag_pkt_teardown(void)
409 {
410 	uint32_t i;
411 
412 	for (i = 0; i < flow_cnt; i++)
413 		rte_pktmbuf_free(mbufs[i][0]);
414 }
415 
416 static void
417 reassembly_print_stats(int8_t nb_frags, uint8_t fill_order,
418 		       uint32_t outstanding, uint64_t cyc_per_flow,
419 		       uint64_t cyc_per_frag_insert,
420 		       uint64_t cyc_per_reassembly)
421 {
422 	char frag_str[8], order_str[12];
423 
424 	if (nb_frags > 0)
425 		snprintf(frag_str, sizeof(frag_str), "%d", nb_frags);
426 	else
427 		snprintf(frag_str, sizeof(frag_str), "RANDOM");
428 
429 	switch (fill_order) {
430 	case FILL_MODE_LINEAR:
431 		snprintf(order_str, sizeof(order_str), "LINEAR");
432 		break;
433 	case FILL_MODE_RANDOM:
434 		snprintf(order_str, sizeof(order_str), "RANDOM");
435 		break;
436 	case FILL_MODE_INTERLEAVED:
437 		snprintf(order_str, sizeof(order_str), "INTERLEAVED");
438 		break;
439 	default:
440 		break;
441 	}
442 
443 	printf("| %-14s | %-14s | %-11d | %-11" PRIu64 " | %-22" PRIu64
444 	       " | %-17" PRIu64 " |\n",
445 	       order_str, frag_str, outstanding, cyc_per_flow,
446 	       cyc_per_frag_insert, cyc_per_reassembly);
447 	printf("+================+================+=============+=============+"
448 	       "========================+===================+\n");
449 }
450 
451 static void
452 join_array(struct rte_mbuf **dest_arr, struct rte_mbuf **src_arr,
453 	   uint8_t offset, uint8_t sz)
454 {
455 	int i, j;
456 
457 	for (i = offset, j = 0; j < sz; i++, j++)
458 		dest_arr[i] = src_arr[j];
459 }
460 
461 static int
462 ipv4_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
463 {
464 	struct rte_ip_frag_death_row death_row;
465 	uint64_t total_reassembled_cyc = 0;
466 	uint64_t total_empty_cyc = 0;
467 	uint64_t tstamp, flow_tstamp;
468 	uint64_t frag_processed = 0;
469 	uint64_t total_cyc = 0;
470 	uint32_t i, j;
471 
472 	for (i = 0; i < flow_cnt; i++) {
473 		struct rte_mbuf *buf_out = NULL;
474 		uint8_t reassembled = 0;
475 
476 		flow_tstamp = rte_rdtsc_precise();
477 		for (j = 0; j < frag_per_flow[i]; j++) {
478 			struct rte_mbuf *buf = mbufs[i][j];
479 			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
480 				buf, struct rte_ipv4_hdr *, buf->l2_len);
481 
482 			tstamp = rte_rdtsc_precise();
483 			buf_out = rte_ipv4_frag_reassemble_packet(
484 				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
485 
486 			if (buf_out == NULL) {
487 				total_empty_cyc += rte_rdtsc_precise() - tstamp;
488 				frag_processed++;
489 				continue;
490 			} else {
491 				/*Packet out*/
492 				total_reassembled_cyc +=
493 					rte_rdtsc_precise() - tstamp;
494 				reassembled = 1;
495 			}
496 		}
497 		total_cyc += rte_rdtsc_precise() - flow_tstamp;
498 		if (!reassembled || buf_out->nb_segs != frag_per_flow[i])
499 			return TEST_FAILED;
500 		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
501 		mbufs[i][0] = buf_out;
502 	}
503 
504 	reassembly_print_stats(nb_frags, fill_order, 0, total_cyc / flow_cnt,
505 			       total_empty_cyc / frag_processed,
506 			       total_reassembled_cyc / flow_cnt);
507 
508 	return TEST_SUCCESS;
509 }
510 
511 static int
512 ipv4_outstanding_reassembly_perf(int8_t nb_frags, uint8_t fill_order,
513 				 uint32_t outstanding)
514 {
515 	struct rte_ip_frag_death_row death_row;
516 	uint64_t total_reassembled_cyc = 0;
517 	uint64_t total_empty_cyc = 0;
518 	uint64_t tstamp, flow_tstamp;
519 	uint64_t frag_processed = 0;
520 	uint64_t total_cyc = 0;
521 	uint32_t i, j, k;
522 
523 	k = outstanding;
524 	/* Insert outstanding fragments */
525 	for (i = 0; k && (i < flow_cnt); i++) {
526 		struct rte_mbuf *buf_out = NULL;
527 
528 		flow_tstamp = rte_rdtsc_precise();
529 		for (j = frag_per_flow[i] - 1; j > 0; j--) {
530 			struct rte_mbuf *buf = mbufs[i][j];
531 			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
532 				buf, struct rte_ipv4_hdr *, buf->l2_len);
533 
534 			tstamp = rte_rdtsc_precise();
535 			buf_out = rte_ipv4_frag_reassemble_packet(
536 				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
537 			total_empty_cyc += rte_rdtsc_precise() - tstamp;
538 			frag_processed++;
539 			if (buf_out != NULL)
540 				return TEST_FAILED;
541 
542 			k--;
543 		}
544 		frag_per_flow[i] = 1;
545 	}
546 
547 	for (i = 0; i < flow_cnt; i++) {
548 		struct rte_mbuf *buf_out = NULL;
549 		uint8_t reassembled = 0;
550 
551 		flow_tstamp = rte_rdtsc_precise();
552 		for (j = 0; j < frag_per_flow[i]; j++) {
553 			struct rte_mbuf *buf = mbufs[i][j];
554 			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
555 				buf, struct rte_ipv4_hdr *, buf->l2_len);
556 
557 			tstamp = rte_rdtsc_precise();
558 			buf_out = rte_ipv4_frag_reassemble_packet(
559 				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
560 
561 			if (buf_out == NULL) {
562 				total_empty_cyc += rte_rdtsc_precise() - tstamp;
563 				frag_processed++;
564 				continue;
565 			} else {
566 				/*Packet out*/
567 				total_reassembled_cyc +=
568 					rte_rdtsc_precise() - tstamp;
569 				reassembled = 1;
570 			}
571 		}
572 		total_cyc += rte_rdtsc_precise() - flow_tstamp;
573 		if (!reassembled)
574 			return TEST_FAILED;
575 		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
576 		mbufs[i][0] = buf_out;
577 	}
578 
579 	reassembly_print_stats(nb_frags, fill_order, outstanding,
580 			       total_cyc / flow_cnt,
581 			       total_empty_cyc / frag_processed,
582 			       total_reassembled_cyc / flow_cnt);
583 
584 	return TEST_SUCCESS;
585 }
586 
587 static int
588 ipv4_reassembly_interleaved_flows_perf(uint8_t nb_frags)
589 {
590 	struct rte_ip_frag_death_row death_row;
591 	uint64_t total_reassembled_cyc = 0;
592 	uint64_t total_empty_cyc = 0;
593 	uint64_t tstamp, flow_tstamp;
594 	uint64_t frag_processed = 0;
595 	uint64_t total_cyc = 0;
596 	uint32_t i, j;
597 
598 	for (i = 0; i < flow_cnt; i += 4) {
599 		struct rte_mbuf *buf_out[4] = {NULL};
600 		uint8_t reassembled = 0;
601 		uint8_t nb_frags = 0;
602 		uint8_t prev = 0;
603 
604 		for (j = 0; j < 4; j++)
605 			nb_frags += frag_per_flow[i + j];
606 
607 		struct rte_mbuf *buf_arr[nb_frags];
608 		for (j = 0; j < 4; j++) {
609 			join_array(buf_arr, mbufs[i + j], prev,
610 				   frag_per_flow[i + j]);
611 			prev += frag_per_flow[i + j];
612 		}
613 		randomize_array_positions((void **)buf_arr, nb_frags);
614 		flow_tstamp = rte_rdtsc_precise();
615 		for (j = 0; j < nb_frags; j++) {
616 			struct rte_mbuf *buf = buf_arr[j];
617 			struct rte_ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
618 				buf, struct rte_ipv4_hdr *, buf->l2_len);
619 
620 			tstamp = rte_rdtsc_precise();
621 			buf_out[reassembled] = rte_ipv4_frag_reassemble_packet(
622 				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr);
623 
624 			if (buf_out[reassembled] == NULL) {
625 				total_empty_cyc += rte_rdtsc_precise() - tstamp;
626 				frag_processed++;
627 				continue;
628 			} else {
629 				/*Packet out*/
630 				total_reassembled_cyc +=
631 					rte_rdtsc_precise() - tstamp;
632 				reassembled++;
633 			}
634 		}
635 		total_cyc += rte_rdtsc_precise() - flow_tstamp;
636 		if (reassembled != 4)
637 			return TEST_FAILED;
638 		for (j = 0; j < 4; j++) {
639 			memset(mbufs[i + j], 0,
640 			       sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
641 			mbufs[i + j][0] = buf_out[j];
642 		}
643 	}
644 
645 	reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0,
646 			       total_cyc / flow_cnt,
647 			       total_empty_cyc / frag_processed,
648 			       total_reassembled_cyc / flow_cnt);
649 
650 	return TEST_SUCCESS;
651 }
652 
653 static int
654 ipv6_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
655 {
656 	struct rte_ip_frag_death_row death_row;
657 	uint64_t total_reassembled_cyc = 0;
658 	uint64_t total_empty_cyc = 0;
659 	uint64_t tstamp, flow_tstamp;
660 	uint64_t frag_processed = 0;
661 	uint64_t total_cyc = 0;
662 	uint32_t i, j;
663 
664 	for (i = 0; i < flow_cnt; i++) {
665 		struct rte_mbuf *buf_out = NULL;
666 		uint8_t reassembled = 0;
667 
668 		flow_tstamp = rte_rdtsc_precise();
669 		for (j = 0; j < frag_per_flow[i]; j++) {
670 			struct rte_mbuf *buf = mbufs[i][j];
671 			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
672 				buf, struct rte_ipv6_hdr *, buf->l2_len);
673 			struct ipv6_extension_fragment *frag_hdr =
674 				rte_pktmbuf_mtod_offset(
675 					buf, struct ipv6_extension_fragment *,
676 					buf->l2_len +
677 						sizeof(struct rte_ipv6_hdr));
678 
679 			tstamp = rte_rdtsc_precise();
680 			buf_out = rte_ipv6_frag_reassemble_packet(
681 				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
682 				frag_hdr);
683 
684 			if (buf_out == NULL) {
685 				total_empty_cyc += rte_rdtsc_precise() - tstamp;
686 				frag_processed++;
687 				continue;
688 			} else {
689 				/*Packet out*/
690 				total_reassembled_cyc +=
691 					rte_rdtsc_precise() - tstamp;
692 				reassembled = 1;
693 			}
694 		}
695 		total_cyc += rte_rdtsc_precise() - flow_tstamp;
696 		if (!reassembled || buf_out->nb_segs != frag_per_flow[i])
697 			return TEST_FAILED;
698 		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
699 		mbufs[i][0] = buf_out;
700 	}
701 
702 	reassembly_print_stats(nb_frags, fill_order, 0, total_cyc / flow_cnt,
703 			       total_empty_cyc / frag_processed,
704 			       total_reassembled_cyc / flow_cnt);
705 
706 	return TEST_SUCCESS;
707 }
708 
709 static int
710 ipv6_outstanding_reassembly_perf(int8_t nb_frags, uint8_t fill_order,
711 				 uint32_t outstanding)
712 {
713 	struct rte_ip_frag_death_row death_row;
714 	uint64_t total_reassembled_cyc = 0;
715 	uint64_t total_empty_cyc = 0;
716 	uint64_t tstamp, flow_tstamp;
717 	uint64_t frag_processed = 0;
718 	uint64_t total_cyc = 0;
719 	uint32_t i, j, k;
720 
721 	k = outstanding;
722 	/* Insert outstanding fragments */
723 	for (i = 0; k && (i < flow_cnt); i++) {
724 		struct rte_mbuf *buf_out = NULL;
725 
726 		flow_tstamp = rte_rdtsc_precise();
727 		for (j = frag_per_flow[i] - 1; j > 0; j--) {
728 			struct rte_mbuf *buf = mbufs[i][j];
729 			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
730 				buf, struct rte_ipv6_hdr *, buf->l2_len);
731 			struct ipv6_extension_fragment *frag_hdr =
732 				rte_pktmbuf_mtod_offset(
733 					buf, struct ipv6_extension_fragment *,
734 					buf->l2_len +
735 						sizeof(struct rte_ipv6_hdr));
736 
737 			tstamp = rte_rdtsc_precise();
738 			buf_out = rte_ipv6_frag_reassemble_packet(
739 				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
740 				frag_hdr);
741 			total_empty_cyc += rte_rdtsc_precise() - tstamp;
742 			frag_processed++;
743 
744 			if (buf_out != NULL)
745 				return TEST_FAILED;
746 
747 			k--;
748 		}
749 		frag_per_flow[i] = 1;
750 	}
751 
752 	for (i = 0; i < flow_cnt; i++) {
753 		struct rte_mbuf *buf_out = NULL;
754 		uint8_t reassembled = 0;
755 
756 		flow_tstamp = rte_rdtsc_precise();
757 		for (j = 0; j < frag_per_flow[i]; j++) {
758 			struct rte_mbuf *buf = mbufs[i][j];
759 			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
760 				buf, struct rte_ipv6_hdr *, buf->l2_len);
761 			struct ipv6_extension_fragment *frag_hdr =
762 				rte_pktmbuf_mtod_offset(
763 					buf, struct ipv6_extension_fragment *,
764 					buf->l2_len +
765 						sizeof(struct rte_ipv6_hdr));
766 
767 			tstamp = rte_rdtsc_precise();
768 			buf_out = rte_ipv6_frag_reassemble_packet(
769 				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
770 				frag_hdr);
771 
772 			if (buf_out == NULL) {
773 				total_empty_cyc += rte_rdtsc_precise() - tstamp;
774 				frag_processed++;
775 				continue;
776 			} else {
777 				/*Packet out*/
778 				total_reassembled_cyc +=
779 					rte_rdtsc_precise() - tstamp;
780 				reassembled = 1;
781 			}
782 		}
783 		total_cyc += rte_rdtsc_precise() - flow_tstamp;
784 		if (!reassembled)
785 			return TEST_FAILED;
786 		memset(mbufs[i], 0, sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
787 		mbufs[i][0] = buf_out;
788 	}
789 
790 	reassembly_print_stats(nb_frags, fill_order, outstanding,
791 			       total_cyc / flow_cnt,
792 			       total_empty_cyc / frag_processed,
793 			       total_reassembled_cyc / flow_cnt);
794 
795 	return TEST_SUCCESS;
796 }
797 
798 static int
799 ipv6_reassembly_interleaved_flows_perf(int8_t nb_frags)
800 {
801 	struct rte_ip_frag_death_row death_row;
802 	uint64_t total_reassembled_cyc = 0;
803 	uint64_t total_empty_cyc = 0;
804 	uint64_t tstamp, flow_tstamp;
805 	uint64_t frag_processed = 0;
806 	uint64_t total_cyc = 0;
807 	uint32_t i, j;
808 
809 	for (i = 0; i < flow_cnt; i += 4) {
810 		struct rte_mbuf *buf_out[4] = {NULL};
811 		uint8_t reassembled = 0;
812 		uint8_t nb_frags = 0;
813 		uint8_t prev = 0;
814 
815 		for (j = 0; j < 4; j++)
816 			nb_frags += frag_per_flow[i + j];
817 
818 		struct rte_mbuf *buf_arr[nb_frags];
819 		for (j = 0; j < 4; j++) {
820 			join_array(buf_arr, mbufs[i + j], prev,
821 				   frag_per_flow[i + j]);
822 			prev += frag_per_flow[i + j];
823 		}
824 		randomize_array_positions((void **)buf_arr, nb_frags);
825 		flow_tstamp = rte_rdtsc_precise();
826 		for (j = 0; j < nb_frags; j++) {
827 			struct rte_mbuf *buf = buf_arr[j];
828 			struct rte_ipv6_hdr *ip_hdr = rte_pktmbuf_mtod_offset(
829 				buf, struct rte_ipv6_hdr *, buf->l2_len);
830 			struct ipv6_extension_fragment *frag_hdr =
831 				rte_pktmbuf_mtod_offset(
832 					buf, struct ipv6_extension_fragment *,
833 					buf->l2_len +
834 						sizeof(struct rte_ipv6_hdr));
835 
836 			tstamp = rte_rdtsc_precise();
837 			buf_out[reassembled] = rte_ipv6_frag_reassemble_packet(
838 				frag_tbl, &death_row, buf, flow_tstamp, ip_hdr,
839 				frag_hdr);
840 
841 			if (buf_out[reassembled] == NULL) {
842 				total_empty_cyc += rte_rdtsc_precise() - tstamp;
843 				frag_processed++;
844 				continue;
845 			} else {
846 				/*Packet out*/
847 				total_reassembled_cyc +=
848 					rte_rdtsc_precise() - tstamp;
849 				reassembled++;
850 			}
851 		}
852 		total_cyc += rte_rdtsc_precise() - flow_tstamp;
853 		if (reassembled != 4)
854 			return TEST_FAILED;
855 		for (j = 0; j < 4; j++) {
856 			memset(mbufs[i + j], 0,
857 			       sizeof(struct rte_mbuf *) * MAX_FRAGMENTS);
858 			mbufs[i + j][0] = buf_out[j];
859 		}
860 	}
861 
862 	reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0,
863 			       total_cyc / flow_cnt,
864 			       total_empty_cyc / frag_processed,
865 			       total_reassembled_cyc / flow_cnt);
866 
867 	return TEST_SUCCESS;
868 }
869 
870 static int
871 ipv4_reassembly_test(int8_t nb_frags, uint8_t fill_order, uint32_t outstanding)
872 {
873 	int rc;
874 
875 	if (nb_frags > 0)
876 		rc = ipv4_frag_pkt_setup(fill_order, nb_frags);
877 	else
878 		rc = ipv4_rand_frag_pkt_setup(fill_order, MAX_FRAGMENTS);
879 
880 	if (rc)
881 		return rc;
882 
883 	if (outstanding)
884 		rc = ipv4_outstanding_reassembly_perf(nb_frags, fill_order,
885 						      outstanding);
886 	else if (fill_order == FILL_MODE_INTERLEAVED)
887 		rc = ipv4_reassembly_interleaved_flows_perf(nb_frags);
888 	else
889 		rc = ipv4_reassembly_perf(nb_frags, fill_order);
890 
891 	frag_pkt_teardown();
892 
893 	return rc;
894 }
895 
896 static int
897 ipv6_reassembly_test(int8_t nb_frags, uint8_t fill_order, uint32_t outstanding)
898 {
899 	int rc;
900 
901 	if (nb_frags > 0)
902 		rc = ipv6_frag_pkt_setup(fill_order, nb_frags);
903 	else
904 		rc = ipv6_rand_frag_pkt_setup(fill_order, MAX_FRAGMENTS);
905 
906 	if (rc)
907 		return rc;
908 
909 	if (outstanding)
910 		rc = ipv6_outstanding_reassembly_perf(nb_frags, fill_order,
911 						      outstanding);
912 	else if (fill_order == FILL_MODE_INTERLEAVED)
913 		rc = ipv6_reassembly_interleaved_flows_perf(nb_frags);
914 	else
915 		rc = ipv6_reassembly_perf(nb_frags, fill_order);
916 
917 	frag_pkt_teardown();
918 
919 	return rc;
920 }
921 
922 static int
923 test_reassembly_perf(void)
924 {
925 	int8_t nb_fragments[] = {2, 3, MAX_FRAGMENTS, -1 /* Random */};
926 	uint8_t order_type[] = {FILL_MODE_LINEAR, FILL_MODE_RANDOM};
927 	uint32_t outstanding[] = {100, 500, 1000, 2000, 3000};
928 	uint32_t i, j;
929 	int rc;
930 
931 	rc = reassembly_test_setup();
932 	if (rc)
933 		return rc;
934 
935 	reassembly_print_banner("IPV4");
936 	/* Test variable fragment count and ordering. */
937 	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
938 		for (j = 0; j < RTE_DIM(order_type); j++) {
939 			rc = ipv4_reassembly_test(nb_fragments[i],
940 						  order_type[j], 0);
941 			if (rc)
942 				return rc;
943 		}
944 	}
945 
946 	/* Test outstanding fragments in the table. */
947 	for (i = 0; i < RTE_DIM(outstanding); i++) {
948 		rc = ipv4_reassembly_test(2, 0, outstanding[i]);
949 		if (rc)
950 			return rc;
951 	}
952 	for (i = 0; i < RTE_DIM(outstanding); i++) {
953 		rc = ipv4_reassembly_test(MAX_FRAGMENTS, 0, outstanding[i]);
954 		if (rc)
955 			return rc;
956 	}
957 
958 	/* Test interleaved flow reassembly perf */
959 	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
960 		rc = ipv4_reassembly_test(nb_fragments[i],
961 					  FILL_MODE_INTERLEAVED, 0);
962 		if (rc)
963 			return rc;
964 	}
965 	printf("\n");
966 	reassembly_print_banner("IPV6");
967 	/* Test variable fragment count and ordering. */
968 	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
969 		for (j = 0; j < RTE_DIM(order_type); j++) {
970 			rc = ipv6_reassembly_test(nb_fragments[i],
971 						  order_type[j], 0);
972 			if (rc)
973 				return rc;
974 		}
975 	}
976 
977 	/* Test outstanding fragments in the table. */
978 	for (i = 0; i < RTE_DIM(outstanding); i++) {
979 		rc = ipv6_reassembly_test(2, 0, outstanding[i]);
980 		if (rc)
981 			return rc;
982 	}
983 
984 	for (i = 0; i < RTE_DIM(outstanding); i++) {
985 		rc = ipv6_reassembly_test(MAX_FRAGMENTS, 0, outstanding[i]);
986 		if (rc)
987 			return rc;
988 	}
989 
990 	/* Test interleaved flow reassembly perf */
991 	for (i = 0; i < RTE_DIM(nb_fragments); i++) {
992 		rc = ipv6_reassembly_test(nb_fragments[i],
993 					  FILL_MODE_INTERLEAVED, 0);
994 		if (rc)
995 			return rc;
996 	}
997 	reassembly_test_teardown();
998 
999 	return TEST_SUCCESS;
1000 }
1001 
1002 REGISTER_PERF_TEST(reassembly_perf_autotest, test_reassembly_perf);
1003