xref: /dpdk/lib/gro/gro_udp4.c (revision 63a98ffe0d9d5786375bc262d357ea411a800524)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2020 Inspur Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson 
599a2dd95SBruce Richardson #include <rte_malloc.h>
699a2dd95SBruce Richardson #include <rte_mbuf.h>
799a2dd95SBruce Richardson #include <rte_ethdev.h>
899a2dd95SBruce Richardson 
999a2dd95SBruce Richardson #include "gro_udp4.h"
1099a2dd95SBruce Richardson 
1199a2dd95SBruce Richardson void *
gro_udp4_tbl_create(uint16_t socket_id,uint16_t max_flow_num,uint16_t max_item_per_flow)1299a2dd95SBruce Richardson gro_udp4_tbl_create(uint16_t socket_id,
1399a2dd95SBruce Richardson 		uint16_t max_flow_num,
1499a2dd95SBruce Richardson 		uint16_t max_item_per_flow)
1599a2dd95SBruce Richardson {
1699a2dd95SBruce Richardson 	struct gro_udp4_tbl *tbl;
1799a2dd95SBruce Richardson 	size_t size;
1899a2dd95SBruce Richardson 	uint32_t entries_num, i;
1999a2dd95SBruce Richardson 
2099a2dd95SBruce Richardson 	entries_num = max_flow_num * max_item_per_flow;
2199a2dd95SBruce Richardson 	entries_num = RTE_MIN(entries_num, GRO_UDP4_TBL_MAX_ITEM_NUM);
2299a2dd95SBruce Richardson 
2399a2dd95SBruce Richardson 	if (entries_num == 0)
2499a2dd95SBruce Richardson 		return NULL;
2599a2dd95SBruce Richardson 
2699a2dd95SBruce Richardson 	tbl = rte_zmalloc_socket(__func__,
2799a2dd95SBruce Richardson 			sizeof(struct gro_udp4_tbl),
2899a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE,
2999a2dd95SBruce Richardson 			socket_id);
3099a2dd95SBruce Richardson 	if (tbl == NULL)
3199a2dd95SBruce Richardson 		return NULL;
3299a2dd95SBruce Richardson 
3399a2dd95SBruce Richardson 	size = sizeof(struct gro_udp4_item) * entries_num;
3499a2dd95SBruce Richardson 	tbl->items = rte_zmalloc_socket(__func__,
3599a2dd95SBruce Richardson 			size,
3699a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE,
3799a2dd95SBruce Richardson 			socket_id);
3899a2dd95SBruce Richardson 	if (tbl->items == NULL) {
3999a2dd95SBruce Richardson 		rte_free(tbl);
4099a2dd95SBruce Richardson 		return NULL;
4199a2dd95SBruce Richardson 	}
4299a2dd95SBruce Richardson 	tbl->max_item_num = entries_num;
4399a2dd95SBruce Richardson 
4499a2dd95SBruce Richardson 	size = sizeof(struct gro_udp4_flow) * entries_num;
4599a2dd95SBruce Richardson 	tbl->flows = rte_zmalloc_socket(__func__,
4699a2dd95SBruce Richardson 			size,
4799a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE,
4899a2dd95SBruce Richardson 			socket_id);
4999a2dd95SBruce Richardson 	if (tbl->flows == NULL) {
5099a2dd95SBruce Richardson 		rte_free(tbl->items);
5199a2dd95SBruce Richardson 		rte_free(tbl);
5299a2dd95SBruce Richardson 		return NULL;
5399a2dd95SBruce Richardson 	}
5499a2dd95SBruce Richardson 	/* INVALID_ARRAY_INDEX indicates an empty flow */
5599a2dd95SBruce Richardson 	for (i = 0; i < entries_num; i++)
5699a2dd95SBruce Richardson 		tbl->flows[i].start_index = INVALID_ARRAY_INDEX;
5799a2dd95SBruce Richardson 	tbl->max_flow_num = entries_num;
5899a2dd95SBruce Richardson 
5999a2dd95SBruce Richardson 	return tbl;
6099a2dd95SBruce Richardson }
6199a2dd95SBruce Richardson 
6299a2dd95SBruce Richardson void
gro_udp4_tbl_destroy(void * tbl)6399a2dd95SBruce Richardson gro_udp4_tbl_destroy(void *tbl)
6499a2dd95SBruce Richardson {
6599a2dd95SBruce Richardson 	struct gro_udp4_tbl *udp_tbl = tbl;
6699a2dd95SBruce Richardson 
6799a2dd95SBruce Richardson 	if (udp_tbl) {
6899a2dd95SBruce Richardson 		rte_free(udp_tbl->items);
6999a2dd95SBruce Richardson 		rte_free(udp_tbl->flows);
7099a2dd95SBruce Richardson 	}
7199a2dd95SBruce Richardson 	rte_free(udp_tbl);
7299a2dd95SBruce Richardson }
7399a2dd95SBruce Richardson 
7499a2dd95SBruce Richardson static inline uint32_t
find_an_empty_item(struct gro_udp4_tbl * tbl)7599a2dd95SBruce Richardson find_an_empty_item(struct gro_udp4_tbl *tbl)
7699a2dd95SBruce Richardson {
7799a2dd95SBruce Richardson 	uint32_t i;
7899a2dd95SBruce Richardson 	uint32_t max_item_num = tbl->max_item_num;
7999a2dd95SBruce Richardson 
8099a2dd95SBruce Richardson 	for (i = 0; i < max_item_num; i++)
8199a2dd95SBruce Richardson 		if (tbl->items[i].firstseg == NULL)
8299a2dd95SBruce Richardson 			return i;
8399a2dd95SBruce Richardson 	return INVALID_ARRAY_INDEX;
8499a2dd95SBruce Richardson }
8599a2dd95SBruce Richardson 
8699a2dd95SBruce Richardson static inline uint32_t
find_an_empty_flow(struct gro_udp4_tbl * tbl)8799a2dd95SBruce Richardson find_an_empty_flow(struct gro_udp4_tbl *tbl)
8899a2dd95SBruce Richardson {
8999a2dd95SBruce Richardson 	uint32_t i;
9099a2dd95SBruce Richardson 	uint32_t max_flow_num = tbl->max_flow_num;
9199a2dd95SBruce Richardson 
9299a2dd95SBruce Richardson 	for (i = 0; i < max_flow_num; i++)
9399a2dd95SBruce Richardson 		if (tbl->flows[i].start_index == INVALID_ARRAY_INDEX)
9499a2dd95SBruce Richardson 			return i;
9599a2dd95SBruce Richardson 	return INVALID_ARRAY_INDEX;
9699a2dd95SBruce Richardson }
9799a2dd95SBruce Richardson 
9899a2dd95SBruce Richardson static inline uint32_t
insert_new_item(struct gro_udp4_tbl * tbl,struct rte_mbuf * pkt,uint64_t start_time,uint32_t prev_idx,uint16_t frag_offset,uint8_t is_last_frag)9999a2dd95SBruce Richardson insert_new_item(struct gro_udp4_tbl *tbl,
10099a2dd95SBruce Richardson 		struct rte_mbuf *pkt,
10199a2dd95SBruce Richardson 		uint64_t start_time,
10299a2dd95SBruce Richardson 		uint32_t prev_idx,
10399a2dd95SBruce Richardson 		uint16_t frag_offset,
10499a2dd95SBruce Richardson 		uint8_t is_last_frag)
10599a2dd95SBruce Richardson {
10699a2dd95SBruce Richardson 	uint32_t item_idx;
10799a2dd95SBruce Richardson 
10899a2dd95SBruce Richardson 	item_idx = find_an_empty_item(tbl);
10999a2dd95SBruce Richardson 	if (unlikely(item_idx == INVALID_ARRAY_INDEX))
11099a2dd95SBruce Richardson 		return INVALID_ARRAY_INDEX;
11199a2dd95SBruce Richardson 
11299a2dd95SBruce Richardson 	tbl->items[item_idx].firstseg = pkt;
11399a2dd95SBruce Richardson 	tbl->items[item_idx].lastseg = rte_pktmbuf_lastseg(pkt);
11499a2dd95SBruce Richardson 	tbl->items[item_idx].start_time = start_time;
11599a2dd95SBruce Richardson 	tbl->items[item_idx].next_pkt_idx = INVALID_ARRAY_INDEX;
11699a2dd95SBruce Richardson 	tbl->items[item_idx].frag_offset = frag_offset;
11799a2dd95SBruce Richardson 	tbl->items[item_idx].is_last_frag = is_last_frag;
11899a2dd95SBruce Richardson 	tbl->items[item_idx].nb_merged = 1;
11999a2dd95SBruce Richardson 	tbl->item_num++;
12099a2dd95SBruce Richardson 
12199a2dd95SBruce Richardson 	/* if the previous packet exists, chain them together. */
12299a2dd95SBruce Richardson 	if (prev_idx != INVALID_ARRAY_INDEX) {
12399a2dd95SBruce Richardson 		tbl->items[item_idx].next_pkt_idx =
12499a2dd95SBruce Richardson 			tbl->items[prev_idx].next_pkt_idx;
12599a2dd95SBruce Richardson 		tbl->items[prev_idx].next_pkt_idx = item_idx;
12699a2dd95SBruce Richardson 	}
12799a2dd95SBruce Richardson 
12899a2dd95SBruce Richardson 	return item_idx;
12999a2dd95SBruce Richardson }
13099a2dd95SBruce Richardson 
13199a2dd95SBruce Richardson static inline uint32_t
delete_item(struct gro_udp4_tbl * tbl,uint32_t item_idx,uint32_t prev_item_idx)13299a2dd95SBruce Richardson delete_item(struct gro_udp4_tbl *tbl, uint32_t item_idx,
13399a2dd95SBruce Richardson 		uint32_t prev_item_idx)
13499a2dd95SBruce Richardson {
13599a2dd95SBruce Richardson 	uint32_t next_idx = tbl->items[item_idx].next_pkt_idx;
13699a2dd95SBruce Richardson 
13799a2dd95SBruce Richardson 	/* NULL indicates an empty item */
13899a2dd95SBruce Richardson 	tbl->items[item_idx].firstseg = NULL;
13999a2dd95SBruce Richardson 	tbl->item_num--;
14099a2dd95SBruce Richardson 	if (prev_item_idx != INVALID_ARRAY_INDEX)
14199a2dd95SBruce Richardson 		tbl->items[prev_item_idx].next_pkt_idx = next_idx;
14299a2dd95SBruce Richardson 
14399a2dd95SBruce Richardson 	return next_idx;
14499a2dd95SBruce Richardson }
14599a2dd95SBruce Richardson 
14699a2dd95SBruce Richardson static inline uint32_t
insert_new_flow(struct gro_udp4_tbl * tbl,struct udp4_flow_key * src,uint32_t item_idx)14799a2dd95SBruce Richardson insert_new_flow(struct gro_udp4_tbl *tbl,
14899a2dd95SBruce Richardson 		struct udp4_flow_key *src,
14999a2dd95SBruce Richardson 		uint32_t item_idx)
15099a2dd95SBruce Richardson {
15199a2dd95SBruce Richardson 	struct udp4_flow_key *dst;
15299a2dd95SBruce Richardson 	uint32_t flow_idx;
15399a2dd95SBruce Richardson 
15499a2dd95SBruce Richardson 	flow_idx = find_an_empty_flow(tbl);
15599a2dd95SBruce Richardson 	if (unlikely(flow_idx == INVALID_ARRAY_INDEX))
15699a2dd95SBruce Richardson 		return INVALID_ARRAY_INDEX;
15799a2dd95SBruce Richardson 
15899a2dd95SBruce Richardson 	dst = &(tbl->flows[flow_idx].key);
15999a2dd95SBruce Richardson 
16099a2dd95SBruce Richardson 	rte_ether_addr_copy(&(src->eth_saddr), &(dst->eth_saddr));
16199a2dd95SBruce Richardson 	rte_ether_addr_copy(&(src->eth_daddr), &(dst->eth_daddr));
16299a2dd95SBruce Richardson 	dst->ip_src_addr = src->ip_src_addr;
16399a2dd95SBruce Richardson 	dst->ip_dst_addr = src->ip_dst_addr;
16499a2dd95SBruce Richardson 	dst->ip_id = src->ip_id;
16599a2dd95SBruce Richardson 
16699a2dd95SBruce Richardson 	tbl->flows[flow_idx].start_index = item_idx;
16799a2dd95SBruce Richardson 	tbl->flow_num++;
16899a2dd95SBruce Richardson 
16999a2dd95SBruce Richardson 	return flow_idx;
17099a2dd95SBruce Richardson }
17199a2dd95SBruce Richardson 
17299a2dd95SBruce Richardson /*
17399a2dd95SBruce Richardson  * update the packet length for the flushed packet.
17499a2dd95SBruce Richardson  */
17599a2dd95SBruce Richardson static inline void
update_header(struct gro_udp4_item * item)17699a2dd95SBruce Richardson update_header(struct gro_udp4_item *item)
17799a2dd95SBruce Richardson {
17899a2dd95SBruce Richardson 	struct rte_ipv4_hdr *ipv4_hdr;
17999a2dd95SBruce Richardson 	struct rte_mbuf *pkt = item->firstseg;
18099a2dd95SBruce Richardson 	uint16_t frag_offset;
18199a2dd95SBruce Richardson 
182*63a98ffeSStephen Hemminger 	ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
18399a2dd95SBruce Richardson 					   pkt->l2_len);
18499a2dd95SBruce Richardson 	ipv4_hdr->total_length = rte_cpu_to_be_16(pkt->pkt_len -
18599a2dd95SBruce Richardson 			pkt->l2_len);
18699a2dd95SBruce Richardson 
18799a2dd95SBruce Richardson 	/* Clear MF bit if it is last fragment */
18899a2dd95SBruce Richardson 	if (item->is_last_frag) {
18999a2dd95SBruce Richardson 		frag_offset = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);
19099a2dd95SBruce Richardson 		ipv4_hdr->fragment_offset =
19199a2dd95SBruce Richardson 			rte_cpu_to_be_16(frag_offset & ~RTE_IPV4_HDR_MF_FLAG);
19299a2dd95SBruce Richardson 	}
19399a2dd95SBruce Richardson }
19499a2dd95SBruce Richardson 
19599a2dd95SBruce Richardson int32_t
gro_udp4_reassemble(struct rte_mbuf * pkt,struct gro_udp4_tbl * tbl,uint64_t start_time)19699a2dd95SBruce Richardson gro_udp4_reassemble(struct rte_mbuf *pkt,
19799a2dd95SBruce Richardson 		struct gro_udp4_tbl *tbl,
19899a2dd95SBruce Richardson 		uint64_t start_time)
19999a2dd95SBruce Richardson {
20099a2dd95SBruce Richardson 	struct rte_ether_hdr *eth_hdr;
20199a2dd95SBruce Richardson 	struct rte_ipv4_hdr *ipv4_hdr;
20299a2dd95SBruce Richardson 	uint16_t ip_dl;
20399a2dd95SBruce Richardson 	uint16_t ip_id, hdr_len;
20499a2dd95SBruce Richardson 	uint16_t frag_offset = 0;
20599a2dd95SBruce Richardson 	uint8_t is_last_frag;
20699a2dd95SBruce Richardson 
20799a2dd95SBruce Richardson 	struct udp4_flow_key key;
20899a2dd95SBruce Richardson 	uint32_t cur_idx, prev_idx, item_idx;
20999a2dd95SBruce Richardson 	uint32_t i, max_flow_num, remaining_flow_num;
21099a2dd95SBruce Richardson 	int cmp;
21199a2dd95SBruce Richardson 	uint8_t find;
21299a2dd95SBruce Richardson 
21399a2dd95SBruce Richardson 	eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
21499a2dd95SBruce Richardson 	ipv4_hdr = (struct rte_ipv4_hdr *)((char *)eth_hdr + pkt->l2_len);
21599a2dd95SBruce Richardson 	hdr_len = pkt->l2_len + pkt->l3_len;
21699a2dd95SBruce Richardson 
21799a2dd95SBruce Richardson 	/*
21899a2dd95SBruce Richardson 	 * Don't process non-fragment packet.
21999a2dd95SBruce Richardson 	 */
22099a2dd95SBruce Richardson 	if (!is_ipv4_fragment(ipv4_hdr))
22199a2dd95SBruce Richardson 		return -1;
22299a2dd95SBruce Richardson 
22372f51b09SKumara Parameshwaran 	ip_dl = rte_be_to_cpu_16(ipv4_hdr->total_length);
22472f51b09SKumara Parameshwaran 	/* trim the tail padding bytes */
22572f51b09SKumara Parameshwaran 	if (pkt->pkt_len > (uint32_t)(ip_dl + pkt->l2_len))
22672f51b09SKumara Parameshwaran 		rte_pktmbuf_trim(pkt, pkt->pkt_len - ip_dl - pkt->l2_len);
22772f51b09SKumara Parameshwaran 
22899a2dd95SBruce Richardson 	/*
22999a2dd95SBruce Richardson 	 * Don't process the packet whose payload length is less than or
23099a2dd95SBruce Richardson 	 * equal to 0.
23199a2dd95SBruce Richardson 	 */
23299a2dd95SBruce Richardson 	if (pkt->pkt_len <= hdr_len)
23399a2dd95SBruce Richardson 		return -1;
23499a2dd95SBruce Richardson 
23599a2dd95SBruce Richardson 	if (ip_dl <= pkt->l3_len)
23699a2dd95SBruce Richardson 		return -1;
23799a2dd95SBruce Richardson 
23899a2dd95SBruce Richardson 	ip_dl -= pkt->l3_len;
23999a2dd95SBruce Richardson 	ip_id = rte_be_to_cpu_16(ipv4_hdr->packet_id);
24099a2dd95SBruce Richardson 	frag_offset = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);
24199a2dd95SBruce Richardson 	is_last_frag = ((frag_offset & RTE_IPV4_HDR_MF_FLAG) == 0) ? 1 : 0;
24299a2dd95SBruce Richardson 	frag_offset = (uint16_t)(frag_offset & RTE_IPV4_HDR_OFFSET_MASK) << 3;
24399a2dd95SBruce Richardson 
24404d43857SDmitry Kozlyuk 	rte_ether_addr_copy(&(eth_hdr->src_addr), &(key.eth_saddr));
24504d43857SDmitry Kozlyuk 	rte_ether_addr_copy(&(eth_hdr->dst_addr), &(key.eth_daddr));
24699a2dd95SBruce Richardson 	key.ip_src_addr = ipv4_hdr->src_addr;
24799a2dd95SBruce Richardson 	key.ip_dst_addr = ipv4_hdr->dst_addr;
24899a2dd95SBruce Richardson 	key.ip_id = ip_id;
24999a2dd95SBruce Richardson 
25099a2dd95SBruce Richardson 	/* Search for a matched flow. */
25199a2dd95SBruce Richardson 	max_flow_num = tbl->max_flow_num;
25299a2dd95SBruce Richardson 	remaining_flow_num = tbl->flow_num;
25399a2dd95SBruce Richardson 	find = 0;
25499a2dd95SBruce Richardson 	for (i = 0; i < max_flow_num && remaining_flow_num; i++) {
25599a2dd95SBruce Richardson 		if (tbl->flows[i].start_index != INVALID_ARRAY_INDEX) {
25699a2dd95SBruce Richardson 			if (is_same_udp4_flow(tbl->flows[i].key, key)) {
25799a2dd95SBruce Richardson 				find = 1;
25899a2dd95SBruce Richardson 				break;
25999a2dd95SBruce Richardson 			}
26099a2dd95SBruce Richardson 			remaining_flow_num--;
26199a2dd95SBruce Richardson 		}
26299a2dd95SBruce Richardson 	}
26399a2dd95SBruce Richardson 
26499a2dd95SBruce Richardson 	/*
26599a2dd95SBruce Richardson 	 * Fail to find a matched flow. Insert a new flow and store the
26699a2dd95SBruce Richardson 	 * packet into the flow.
26799a2dd95SBruce Richardson 	 */
26899a2dd95SBruce Richardson 	if (find == 0) {
26999a2dd95SBruce Richardson 		item_idx = insert_new_item(tbl, pkt, start_time,
27099a2dd95SBruce Richardson 				INVALID_ARRAY_INDEX, frag_offset,
27199a2dd95SBruce Richardson 				is_last_frag);
27299a2dd95SBruce Richardson 		if (unlikely(item_idx == INVALID_ARRAY_INDEX))
27399a2dd95SBruce Richardson 			return -1;
27499a2dd95SBruce Richardson 		if (insert_new_flow(tbl, &key, item_idx) ==
27599a2dd95SBruce Richardson 				INVALID_ARRAY_INDEX) {
27699a2dd95SBruce Richardson 			/*
27799a2dd95SBruce Richardson 			 * Fail to insert a new flow, so delete the
27899a2dd95SBruce Richardson 			 * stored packet.
27999a2dd95SBruce Richardson 			 */
28099a2dd95SBruce Richardson 			delete_item(tbl, item_idx, INVALID_ARRAY_INDEX);
28199a2dd95SBruce Richardson 			return -1;
28299a2dd95SBruce Richardson 		}
28399a2dd95SBruce Richardson 		return 0;
28499a2dd95SBruce Richardson 	}
28599a2dd95SBruce Richardson 
28699a2dd95SBruce Richardson 	/*
28799a2dd95SBruce Richardson 	 * Check all packets in the flow and try to find a neighbor for
28899a2dd95SBruce Richardson 	 * the input packet.
28999a2dd95SBruce Richardson 	 */
29099a2dd95SBruce Richardson 	cur_idx = tbl->flows[i].start_index;
29199a2dd95SBruce Richardson 	prev_idx = cur_idx;
29299a2dd95SBruce Richardson 	do {
29399a2dd95SBruce Richardson 		cmp = udp4_check_neighbor(&(tbl->items[cur_idx]),
29499a2dd95SBruce Richardson 				frag_offset, ip_dl, 0);
29599a2dd95SBruce Richardson 		if (cmp) {
29699a2dd95SBruce Richardson 			if (merge_two_udp4_packets(&(tbl->items[cur_idx]),
29799a2dd95SBruce Richardson 						pkt, cmp, frag_offset,
29899a2dd95SBruce Richardson 						is_last_frag, 0))
29999a2dd95SBruce Richardson 				return 1;
30099a2dd95SBruce Richardson 			/*
30199a2dd95SBruce Richardson 			 * Fail to merge the two packets, as the packet
30299a2dd95SBruce Richardson 			 * length is greater than the max value. Store
30399a2dd95SBruce Richardson 			 * the packet into the flow.
30499a2dd95SBruce Richardson 			 */
30599a2dd95SBruce Richardson 			if (insert_new_item(tbl, pkt, start_time, prev_idx,
30699a2dd95SBruce Richardson 						frag_offset, is_last_frag) ==
30799a2dd95SBruce Richardson 					INVALID_ARRAY_INDEX)
30899a2dd95SBruce Richardson 				return -1;
30999a2dd95SBruce Richardson 			return 0;
31099a2dd95SBruce Richardson 		}
31199a2dd95SBruce Richardson 
31299a2dd95SBruce Richardson 		/* Ensure inserted items are ordered by frag_offset */
31399a2dd95SBruce Richardson 		if (frag_offset
31499a2dd95SBruce Richardson 			< tbl->items[cur_idx].frag_offset) {
31599a2dd95SBruce Richardson 			break;
31699a2dd95SBruce Richardson 		}
31799a2dd95SBruce Richardson 
31899a2dd95SBruce Richardson 		prev_idx = cur_idx;
31999a2dd95SBruce Richardson 		cur_idx = tbl->items[cur_idx].next_pkt_idx;
32099a2dd95SBruce Richardson 	} while (cur_idx != INVALID_ARRAY_INDEX);
32199a2dd95SBruce Richardson 
32299a2dd95SBruce Richardson 	/* Fail to find a neighbor, so store the packet into the flow. */
32399a2dd95SBruce Richardson 	if (cur_idx == tbl->flows[i].start_index) {
32499a2dd95SBruce Richardson 		/* Insert it before the first packet of the flow */
32599a2dd95SBruce Richardson 		item_idx = insert_new_item(tbl, pkt, start_time,
32699a2dd95SBruce Richardson 				INVALID_ARRAY_INDEX, frag_offset,
32799a2dd95SBruce Richardson 				is_last_frag);
32899a2dd95SBruce Richardson 		if (unlikely(item_idx == INVALID_ARRAY_INDEX))
32999a2dd95SBruce Richardson 			return -1;
33099a2dd95SBruce Richardson 		tbl->items[item_idx].next_pkt_idx = cur_idx;
33199a2dd95SBruce Richardson 		tbl->flows[i].start_index = item_idx;
33299a2dd95SBruce Richardson 	} else {
33399a2dd95SBruce Richardson 		if (insert_new_item(tbl, pkt, start_time, prev_idx,
33499a2dd95SBruce Richardson 				frag_offset, is_last_frag)
33599a2dd95SBruce Richardson 			== INVALID_ARRAY_INDEX)
33699a2dd95SBruce Richardson 			return -1;
33799a2dd95SBruce Richardson 	}
33899a2dd95SBruce Richardson 
33999a2dd95SBruce Richardson 	return 0;
34099a2dd95SBruce Richardson }
34199a2dd95SBruce Richardson 
34299a2dd95SBruce Richardson static int
gro_udp4_merge_items(struct gro_udp4_tbl * tbl,uint32_t start_idx)34399a2dd95SBruce Richardson gro_udp4_merge_items(struct gro_udp4_tbl *tbl,
34499a2dd95SBruce Richardson 			   uint32_t start_idx)
34599a2dd95SBruce Richardson {
34699a2dd95SBruce Richardson 	uint16_t frag_offset;
34799a2dd95SBruce Richardson 	uint8_t is_last_frag;
34899a2dd95SBruce Richardson 	int16_t ip_dl;
34999a2dd95SBruce Richardson 	struct rte_mbuf *pkt;
35099a2dd95SBruce Richardson 	int cmp;
35199a2dd95SBruce Richardson 	uint32_t item_idx;
35299a2dd95SBruce Richardson 	uint16_t hdr_len;
35399a2dd95SBruce Richardson 
35499a2dd95SBruce Richardson 	item_idx = tbl->items[start_idx].next_pkt_idx;
35599a2dd95SBruce Richardson 	while (item_idx != INVALID_ARRAY_INDEX) {
35699a2dd95SBruce Richardson 		pkt = tbl->items[item_idx].firstseg;
35799a2dd95SBruce Richardson 		hdr_len = pkt->l2_len + pkt->l3_len;
35899a2dd95SBruce Richardson 		ip_dl = pkt->pkt_len - hdr_len;
35999a2dd95SBruce Richardson 		frag_offset = tbl->items[item_idx].frag_offset;
36099a2dd95SBruce Richardson 		is_last_frag = tbl->items[item_idx].is_last_frag;
36199a2dd95SBruce Richardson 		cmp = udp4_check_neighbor(&(tbl->items[start_idx]),
36299a2dd95SBruce Richardson 					frag_offset, ip_dl, 0);
36399a2dd95SBruce Richardson 		if (cmp) {
36499a2dd95SBruce Richardson 			if (merge_two_udp4_packets(
36599a2dd95SBruce Richardson 					&(tbl->items[start_idx]),
36699a2dd95SBruce Richardson 					pkt, cmp, frag_offset,
36799a2dd95SBruce Richardson 					is_last_frag, 0)) {
36899a2dd95SBruce Richardson 				item_idx = delete_item(tbl, item_idx,
36999a2dd95SBruce Richardson 							INVALID_ARRAY_INDEX);
37099a2dd95SBruce Richardson 				tbl->items[start_idx].next_pkt_idx
37199a2dd95SBruce Richardson 					= item_idx;
37299a2dd95SBruce Richardson 			} else
37399a2dd95SBruce Richardson 				return 0;
37499a2dd95SBruce Richardson 		} else
37599a2dd95SBruce Richardson 			return 0;
37699a2dd95SBruce Richardson 	}
37799a2dd95SBruce Richardson 
37899a2dd95SBruce Richardson 	return 0;
37999a2dd95SBruce Richardson }
38099a2dd95SBruce Richardson 
38199a2dd95SBruce Richardson uint16_t
gro_udp4_tbl_timeout_flush(struct gro_udp4_tbl * tbl,uint64_t flush_timestamp,struct rte_mbuf ** out,uint16_t nb_out)38299a2dd95SBruce Richardson gro_udp4_tbl_timeout_flush(struct gro_udp4_tbl *tbl,
38399a2dd95SBruce Richardson 		uint64_t flush_timestamp,
38499a2dd95SBruce Richardson 		struct rte_mbuf **out,
38599a2dd95SBruce Richardson 		uint16_t nb_out)
38699a2dd95SBruce Richardson {
38799a2dd95SBruce Richardson 	uint16_t k = 0;
38899a2dd95SBruce Richardson 	uint32_t i, j;
38999a2dd95SBruce Richardson 	uint32_t max_flow_num = tbl->max_flow_num;
39099a2dd95SBruce Richardson 
39199a2dd95SBruce Richardson 	for (i = 0; i < max_flow_num; i++) {
39299a2dd95SBruce Richardson 		if (unlikely(tbl->flow_num == 0))
39399a2dd95SBruce Richardson 			return k;
39499a2dd95SBruce Richardson 
39599a2dd95SBruce Richardson 		j = tbl->flows[i].start_index;
39699a2dd95SBruce Richardson 		while (j != INVALID_ARRAY_INDEX) {
39799a2dd95SBruce Richardson 			if (tbl->items[j].start_time <= flush_timestamp) {
39899a2dd95SBruce Richardson 				gro_udp4_merge_items(tbl, j);
39999a2dd95SBruce Richardson 				out[k++] = tbl->items[j].firstseg;
40099a2dd95SBruce Richardson 				if (tbl->items[j].nb_merged > 1)
40199a2dd95SBruce Richardson 					update_header(&(tbl->items[j]));
40299a2dd95SBruce Richardson 				/*
40399a2dd95SBruce Richardson 				 * Delete the packet and get the next
40499a2dd95SBruce Richardson 				 * packet in the flow.
40599a2dd95SBruce Richardson 				 */
40699a2dd95SBruce Richardson 				j = delete_item(tbl, j, INVALID_ARRAY_INDEX);
40799a2dd95SBruce Richardson 				tbl->flows[i].start_index = j;
40899a2dd95SBruce Richardson 				if (j == INVALID_ARRAY_INDEX)
40999a2dd95SBruce Richardson 					tbl->flow_num--;
41099a2dd95SBruce Richardson 
41199a2dd95SBruce Richardson 				if (unlikely(k == nb_out))
41299a2dd95SBruce Richardson 					return k;
41399a2dd95SBruce Richardson 			} else
41499a2dd95SBruce Richardson 				/*
41599a2dd95SBruce Richardson 				 * Flushing packets does not strictly follow
41699a2dd95SBruce Richardson 				 * timestamp. It does not flush left packets of
41799a2dd95SBruce Richardson 				 * the flow this time once it finds one item
41899a2dd95SBruce Richardson 				 * whose start_time is greater than
41999a2dd95SBruce Richardson 				 * flush_timestamp. So go to check other flows.
42099a2dd95SBruce Richardson 				 */
42199a2dd95SBruce Richardson 				break;
42299a2dd95SBruce Richardson 		}
42399a2dd95SBruce Richardson 	}
42499a2dd95SBruce Richardson 	return k;
42599a2dd95SBruce Richardson }
42699a2dd95SBruce Richardson 
42799a2dd95SBruce Richardson uint32_t
gro_udp4_tbl_pkt_count(void * tbl)42899a2dd95SBruce Richardson gro_udp4_tbl_pkt_count(void *tbl)
42999a2dd95SBruce Richardson {
43099a2dd95SBruce Richardson 	struct gro_udp4_tbl *gro_tbl = tbl;
43199a2dd95SBruce Richardson 
43299a2dd95SBruce Richardson 	if (gro_tbl)
43399a2dd95SBruce Richardson 		return gro_tbl->item_num;
43499a2dd95SBruce Richardson 
43599a2dd95SBruce Richardson 	return 0;
43699a2dd95SBruce Richardson }
437