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