/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2023 Marvell. */ #include #include #include #include #include #include #include #include #include #include #include #include #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 << 15) | (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_BKTS, MAX_ENTRIES_PER_BKT, MAX_BKTS * 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); 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", ð_hdr->dst_addr); rte_ether_unformat_addr("02:00:00:00:00:00", ð_hdr->src_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); /* Using more than 32K flows will modify the 2nd octet of the IP. */ 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", ð_hdr->dst_addr); rte_ether_unformat_addr("02:00:00:00:00:00", ð_hdr->src_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_precise(); 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_precise(); 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_precise() - tstamp; frag_processed++; continue; } else { /*Packet out*/ total_reassembled_cyc += rte_rdtsc_precise() - tstamp; reassembled = 1; } } total_cyc += rte_rdtsc_precise() - 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_precise(); 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_precise(); buf_out = rte_ipv4_frag_reassemble_packet( frag_tbl, &death_row, buf, flow_tstamp, ip_hdr); total_empty_cyc += rte_rdtsc_precise() - 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_precise(); 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_precise(); 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_precise() - tstamp; frag_processed++; continue; } else { /*Packet out*/ total_reassembled_cyc += rte_rdtsc_precise() - tstamp; reassembled = 1; } } total_cyc += rte_rdtsc_precise() - 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_precise(); 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_precise(); 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_precise() - tstamp; frag_processed++; continue; } else { /*Packet out*/ total_reassembled_cyc += rte_rdtsc_precise() - tstamp; reassembled++; } } total_cyc += rte_rdtsc_precise() - 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_precise(); 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_precise(); 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_precise() - tstamp; frag_processed++; continue; } else { /*Packet out*/ total_reassembled_cyc += rte_rdtsc_precise() - tstamp; reassembled = 1; } } total_cyc += rte_rdtsc_precise() - 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_precise(); 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_precise(); buf_out = rte_ipv6_frag_reassemble_packet( frag_tbl, &death_row, buf, flow_tstamp, ip_hdr, frag_hdr); total_empty_cyc += rte_rdtsc_precise() - 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_precise(); 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_precise(); 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_precise() - tstamp; frag_processed++; continue; } else { /*Packet out*/ total_reassembled_cyc += rte_rdtsc_precise() - tstamp; reassembled = 1; } } total_cyc += rte_rdtsc_precise() - 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_precise(); 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_precise(); 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_precise() - tstamp; frag_processed++; continue; } else { /*Packet out*/ total_reassembled_cyc += rte_rdtsc_precise() - tstamp; reassembled++; } } total_cyc += rte_rdtsc_precise() - 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_PERF_TEST(reassembly_perf_autotest, test_reassembly_perf);