xref: /dpdk/lib/gro/gro_tcp4.c (revision 547f294357690ab8501f120457a82919b1217517)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2017 Intel 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_tcp4.h"
105c55e819SKumara Parameshwaran #include "gro_tcp_internal.h"
1199a2dd95SBruce Richardson 
1299a2dd95SBruce Richardson void *
gro_tcp4_tbl_create(uint16_t socket_id,uint16_t max_flow_num,uint16_t max_item_per_flow)1399a2dd95SBruce Richardson gro_tcp4_tbl_create(uint16_t socket_id,
1499a2dd95SBruce Richardson 		uint16_t max_flow_num,
1599a2dd95SBruce Richardson 		uint16_t max_item_per_flow)
1699a2dd95SBruce Richardson {
1799a2dd95SBruce Richardson 	struct gro_tcp4_tbl *tbl;
1899a2dd95SBruce Richardson 	size_t size;
1999a2dd95SBruce Richardson 	uint32_t entries_num, i;
2099a2dd95SBruce Richardson 
2199a2dd95SBruce Richardson 	entries_num = max_flow_num * max_item_per_flow;
2299a2dd95SBruce Richardson 	entries_num = RTE_MIN(entries_num, GRO_TCP4_TBL_MAX_ITEM_NUM);
2399a2dd95SBruce Richardson 
2499a2dd95SBruce Richardson 	if (entries_num == 0)
2599a2dd95SBruce Richardson 		return NULL;
2699a2dd95SBruce Richardson 
2799a2dd95SBruce Richardson 	tbl = rte_zmalloc_socket(__func__,
2899a2dd95SBruce Richardson 			sizeof(struct gro_tcp4_tbl),
2999a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE,
3099a2dd95SBruce Richardson 			socket_id);
3199a2dd95SBruce Richardson 	if (tbl == NULL)
3299a2dd95SBruce Richardson 		return NULL;
3399a2dd95SBruce Richardson 
345c55e819SKumara Parameshwaran 	size = sizeof(struct gro_tcp_item) * entries_num;
3599a2dd95SBruce Richardson 	tbl->items = rte_zmalloc_socket(__func__,
3699a2dd95SBruce Richardson 			size,
3799a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE,
3899a2dd95SBruce Richardson 			socket_id);
3999a2dd95SBruce Richardson 	if (tbl->items == NULL) {
4099a2dd95SBruce Richardson 		rte_free(tbl);
4199a2dd95SBruce Richardson 		return NULL;
4299a2dd95SBruce Richardson 	}
4399a2dd95SBruce Richardson 	tbl->max_item_num = entries_num;
4499a2dd95SBruce Richardson 
4599a2dd95SBruce Richardson 	size = sizeof(struct gro_tcp4_flow) * entries_num;
4699a2dd95SBruce Richardson 	tbl->flows = rte_zmalloc_socket(__func__,
4799a2dd95SBruce Richardson 			size,
4899a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE,
4999a2dd95SBruce Richardson 			socket_id);
5099a2dd95SBruce Richardson 	if (tbl->flows == NULL) {
5199a2dd95SBruce Richardson 		rte_free(tbl->items);
5299a2dd95SBruce Richardson 		rte_free(tbl);
5399a2dd95SBruce Richardson 		return NULL;
5499a2dd95SBruce Richardson 	}
5599a2dd95SBruce Richardson 	/* INVALID_ARRAY_INDEX indicates an empty flow */
5699a2dd95SBruce Richardson 	for (i = 0; i < entries_num; i++)
5799a2dd95SBruce Richardson 		tbl->flows[i].start_index = INVALID_ARRAY_INDEX;
5899a2dd95SBruce Richardson 	tbl->max_flow_num = entries_num;
5999a2dd95SBruce Richardson 
6099a2dd95SBruce Richardson 	return tbl;
6199a2dd95SBruce Richardson }
6299a2dd95SBruce Richardson 
6399a2dd95SBruce Richardson void
gro_tcp4_tbl_destroy(void * tbl)6499a2dd95SBruce Richardson gro_tcp4_tbl_destroy(void *tbl)
6599a2dd95SBruce Richardson {
6699a2dd95SBruce Richardson 	struct gro_tcp4_tbl *tcp_tbl = tbl;
6799a2dd95SBruce Richardson 
6899a2dd95SBruce Richardson 	if (tcp_tbl) {
6999a2dd95SBruce Richardson 		rte_free(tcp_tbl->items);
7099a2dd95SBruce Richardson 		rte_free(tcp_tbl->flows);
7199a2dd95SBruce Richardson 	}
7299a2dd95SBruce Richardson 	rte_free(tcp_tbl);
7399a2dd95SBruce Richardson }
7499a2dd95SBruce Richardson 
7599a2dd95SBruce Richardson static inline uint32_t
find_an_empty_flow(struct gro_tcp4_tbl * tbl)7699a2dd95SBruce Richardson find_an_empty_flow(struct gro_tcp4_tbl *tbl)
7799a2dd95SBruce Richardson {
7899a2dd95SBruce Richardson 	uint32_t i;
7999a2dd95SBruce Richardson 	uint32_t max_flow_num = tbl->max_flow_num;
8099a2dd95SBruce Richardson 
8199a2dd95SBruce Richardson 	for (i = 0; i < max_flow_num; i++)
8299a2dd95SBruce Richardson 		if (tbl->flows[i].start_index == INVALID_ARRAY_INDEX)
8399a2dd95SBruce Richardson 			return i;
8499a2dd95SBruce Richardson 	return INVALID_ARRAY_INDEX;
8599a2dd95SBruce Richardson }
8699a2dd95SBruce Richardson 
8799a2dd95SBruce Richardson static inline uint32_t
insert_new_flow(struct gro_tcp4_tbl * tbl,struct tcp4_flow_key * src,uint32_t item_idx)8899a2dd95SBruce Richardson insert_new_flow(struct gro_tcp4_tbl *tbl,
8999a2dd95SBruce Richardson 		struct tcp4_flow_key *src,
9099a2dd95SBruce Richardson 		uint32_t item_idx)
9199a2dd95SBruce Richardson {
9299a2dd95SBruce Richardson 	struct tcp4_flow_key *dst;
9399a2dd95SBruce Richardson 	uint32_t flow_idx;
9499a2dd95SBruce Richardson 
9599a2dd95SBruce Richardson 	flow_idx = find_an_empty_flow(tbl);
9699a2dd95SBruce Richardson 	if (unlikely(flow_idx == INVALID_ARRAY_INDEX))
9799a2dd95SBruce Richardson 		return INVALID_ARRAY_INDEX;
9899a2dd95SBruce Richardson 
9999a2dd95SBruce Richardson 	dst = &(tbl->flows[flow_idx].key);
10099a2dd95SBruce Richardson 
1015c55e819SKumara Parameshwaran 	ASSIGN_COMMON_TCP_KEY((&src->cmn_key), (&dst->cmn_key));
1025c55e819SKumara Parameshwaran 
10399a2dd95SBruce Richardson 	dst->ip_src_addr = src->ip_src_addr;
10499a2dd95SBruce Richardson 	dst->ip_dst_addr = src->ip_dst_addr;
10599a2dd95SBruce Richardson 
10699a2dd95SBruce Richardson 	tbl->flows[flow_idx].start_index = item_idx;
10799a2dd95SBruce Richardson 	tbl->flow_num++;
10899a2dd95SBruce Richardson 
10999a2dd95SBruce Richardson 	return flow_idx;
11099a2dd95SBruce Richardson }
11199a2dd95SBruce Richardson 
11299a2dd95SBruce Richardson int32_t
gro_tcp4_reassemble(struct rte_mbuf * pkt,struct gro_tcp4_tbl * tbl,uint64_t start_time)11399a2dd95SBruce Richardson gro_tcp4_reassemble(struct rte_mbuf *pkt,
11499a2dd95SBruce Richardson 		struct gro_tcp4_tbl *tbl,
11599a2dd95SBruce Richardson 		uint64_t start_time)
11699a2dd95SBruce Richardson {
11799a2dd95SBruce Richardson 	struct rte_ether_hdr *eth_hdr;
11899a2dd95SBruce Richardson 	struct rte_ipv4_hdr *ipv4_hdr;
11999a2dd95SBruce Richardson 	struct rte_tcp_hdr *tcp_hdr;
12099a2dd95SBruce Richardson 	uint32_t sent_seq;
12199a2dd95SBruce Richardson 	int32_t tcp_dl;
122b8a55871SJun Qiu 	uint16_t ip_id, hdr_len, frag_off, ip_tlen;
12399a2dd95SBruce Richardson 	uint8_t is_atomic;
12499a2dd95SBruce Richardson 
12599a2dd95SBruce Richardson 	struct tcp4_flow_key key;
1265c55e819SKumara Parameshwaran 	uint32_t item_idx;
12799a2dd95SBruce Richardson 	uint32_t i, max_flow_num, remaining_flow_num;
12899a2dd95SBruce Richardson 	uint8_t find;
129*547f2943SKumara Parameshwaran 	uint32_t item_start_idx;
13099a2dd95SBruce Richardson 
13199a2dd95SBruce Richardson 	/*
13299a2dd95SBruce Richardson 	 * Don't process the packet whose TCP header length is greater
13399a2dd95SBruce Richardson 	 * than 60 bytes or less than 20 bytes.
13499a2dd95SBruce Richardson 	 */
13599a2dd95SBruce Richardson 	if (unlikely(INVALID_TCP_HDRLEN(pkt->l4_len)))
13699a2dd95SBruce Richardson 		return -1;
13799a2dd95SBruce Richardson 
13899a2dd95SBruce Richardson 	eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
13999a2dd95SBruce Richardson 	ipv4_hdr = (struct rte_ipv4_hdr *)((char *)eth_hdr + pkt->l2_len);
14099a2dd95SBruce Richardson 	tcp_hdr = (struct rte_tcp_hdr *)((char *)ipv4_hdr + pkt->l3_len);
14199a2dd95SBruce Richardson 	hdr_len = pkt->l2_len + pkt->l3_len + pkt->l4_len;
14299a2dd95SBruce Richardson 
143*547f2943SKumara Parameshwaran 	/* Return early if the TCP flags are not handled in GRO layer */
144*547f2943SKumara Parameshwaran 	if (tcp_hdr->tcp_flags & ~VALID_GRO_TCP_FLAGS)
14599a2dd95SBruce Richardson 		return -1;
14672f51b09SKumara Parameshwaran 
14772f51b09SKumara Parameshwaran 	/* trim the tail padding bytes */
14872f51b09SKumara Parameshwaran 	ip_tlen = rte_be_to_cpu_16(ipv4_hdr->total_length);
14972f51b09SKumara Parameshwaran 	if (pkt->pkt_len > (uint32_t)(ip_tlen + pkt->l2_len))
15072f51b09SKumara Parameshwaran 		rte_pktmbuf_trim(pkt, pkt->pkt_len - ip_tlen - pkt->l2_len);
15172f51b09SKumara Parameshwaran 
15299a2dd95SBruce Richardson 	/*
15399a2dd95SBruce Richardson 	 * Don't process the packet whose payload length is less than or
15499a2dd95SBruce Richardson 	 * equal to 0.
15599a2dd95SBruce Richardson 	 */
15699a2dd95SBruce Richardson 	tcp_dl = pkt->pkt_len - hdr_len;
15799a2dd95SBruce Richardson 	if (tcp_dl <= 0)
15899a2dd95SBruce Richardson 		return -1;
15999a2dd95SBruce Richardson 
1605c55e819SKumara Parameshwaran 	rte_ether_addr_copy(&(eth_hdr->src_addr), &(key.cmn_key.eth_saddr));
1615c55e819SKumara Parameshwaran 	rte_ether_addr_copy(&(eth_hdr->dst_addr), &(key.cmn_key.eth_daddr));
1625c55e819SKumara Parameshwaran 	key.ip_src_addr = ipv4_hdr->src_addr;
1635c55e819SKumara Parameshwaran 	key.ip_dst_addr = ipv4_hdr->dst_addr;
1645c55e819SKumara Parameshwaran 	key.cmn_key.src_port = tcp_hdr->src_port;
1655c55e819SKumara Parameshwaran 	key.cmn_key.dst_port = tcp_hdr->dst_port;
1665c55e819SKumara Parameshwaran 	key.cmn_key.recv_ack = tcp_hdr->recv_ack;
1675c55e819SKumara Parameshwaran 
16899a2dd95SBruce Richardson 	/*
16999a2dd95SBruce Richardson 	 * Save IPv4 ID for the packet whose DF bit is 0. For the packet
17099a2dd95SBruce Richardson 	 * whose DF bit is 1, IPv4 ID is ignored.
17199a2dd95SBruce Richardson 	 */
17299a2dd95SBruce Richardson 	frag_off = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);
17399a2dd95SBruce Richardson 	is_atomic = (frag_off & RTE_IPV4_HDR_DF_FLAG) == RTE_IPV4_HDR_DF_FLAG;
17499a2dd95SBruce Richardson 	ip_id = is_atomic ? 0 : rte_be_to_cpu_16(ipv4_hdr->packet_id);
17599a2dd95SBruce Richardson 
17699a2dd95SBruce Richardson 	/* Search for a matched flow. */
17799a2dd95SBruce Richardson 	max_flow_num = tbl->max_flow_num;
17899a2dd95SBruce Richardson 	remaining_flow_num = tbl->flow_num;
17999a2dd95SBruce Richardson 	find = 0;
18099a2dd95SBruce Richardson 	for (i = 0; i < max_flow_num && remaining_flow_num; i++) {
18199a2dd95SBruce Richardson 		if (tbl->flows[i].start_index != INVALID_ARRAY_INDEX) {
18299a2dd95SBruce Richardson 			if (is_same_tcp4_flow(tbl->flows[i].key, key)) {
18399a2dd95SBruce Richardson 				find = 1;
184*547f2943SKumara Parameshwaran 				item_start_idx = tbl->flows[i].start_index;
18599a2dd95SBruce Richardson 				break;
18699a2dd95SBruce Richardson 			}
18799a2dd95SBruce Richardson 			remaining_flow_num--;
18899a2dd95SBruce Richardson 		}
18999a2dd95SBruce Richardson 	}
19099a2dd95SBruce Richardson 
191*547f2943SKumara Parameshwaran 	if (find == 1) {
192*547f2943SKumara Parameshwaran 		/*
193*547f2943SKumara Parameshwaran 		 * Any packet with additional flags like PSH,FIN should be processed
194*547f2943SKumara Parameshwaran 		 * and flushed immediately.
195*547f2943SKumara Parameshwaran 		 * Hence marking the start time to 0, so that the packets will be flushed
196*547f2943SKumara Parameshwaran 		 * immediately in timer mode.
197*547f2943SKumara Parameshwaran 		 */
198*547f2943SKumara Parameshwaran 		if (tcp_hdr->tcp_flags & (RTE_TCP_ACK_FLAG | RTE_TCP_PSH_FLAG | RTE_TCP_FIN_FLAG)) {
199*547f2943SKumara Parameshwaran 			if (tcp_hdr->tcp_flags != RTE_TCP_ACK_FLAG)
200*547f2943SKumara Parameshwaran 				tbl->items[item_start_idx].start_time = 0;
201*547f2943SKumara Parameshwaran 			return process_tcp_item(pkt, tcp_hdr, tcp_dl, tbl->items,
202*547f2943SKumara Parameshwaran 						tbl->flows[i].start_index, &tbl->item_num,
203*547f2943SKumara Parameshwaran 						tbl->max_item_num, ip_id, is_atomic, start_time);
204*547f2943SKumara Parameshwaran 		} else {
205*547f2943SKumara Parameshwaran 			return -1;
206*547f2943SKumara Parameshwaran 		}
207*547f2943SKumara Parameshwaran 	}
208*547f2943SKumara Parameshwaran 	/*
209*547f2943SKumara Parameshwaran 	 * Add new flow to the table only if contains ACK flag with data.
210*547f2943SKumara Parameshwaran 	 * Do not add any packets with additional tcp flags to the GRO table
211*547f2943SKumara Parameshwaran 	 */
212*547f2943SKumara Parameshwaran 	if (tcp_hdr->tcp_flags == RTE_TCP_ACK_FLAG) {
2135c55e819SKumara Parameshwaran 		sent_seq = rte_be_to_cpu_32(tcp_hdr->sent_seq);
2145c55e819SKumara Parameshwaran 		item_idx = insert_new_tcp_item(pkt, tbl->items, &tbl->item_num,
2155c55e819SKumara Parameshwaran 						tbl->max_item_num, start_time,
21699a2dd95SBruce Richardson 						INVALID_ARRAY_INDEX, sent_seq, ip_id,
21799a2dd95SBruce Richardson 						is_atomic);
21899a2dd95SBruce Richardson 		if (item_idx == INVALID_ARRAY_INDEX)
21999a2dd95SBruce Richardson 			return -1;
22099a2dd95SBruce Richardson 		if (insert_new_flow(tbl, &key, item_idx) ==
22199a2dd95SBruce Richardson 			INVALID_ARRAY_INDEX) {
22299a2dd95SBruce Richardson 			/*
22399a2dd95SBruce Richardson 			 * Fail to insert a new flow, so delete the
22499a2dd95SBruce Richardson 			 * stored packet.
22599a2dd95SBruce Richardson 			*/
2265c55e819SKumara Parameshwaran 			delete_tcp_item(tbl->items, item_idx, &tbl->item_num, INVALID_ARRAY_INDEX);
22799a2dd95SBruce Richardson 			return -1;
22899a2dd95SBruce Richardson 		}
22999a2dd95SBruce Richardson 		return 0;
23099a2dd95SBruce Richardson 	}
23199a2dd95SBruce Richardson 
232*547f2943SKumara Parameshwaran 	return -1;
2335c55e819SKumara Parameshwaran }
2345c55e819SKumara Parameshwaran 
23599a2dd95SBruce Richardson /*
2365c55e819SKumara Parameshwaran  * update the packet length for the flushed packet.
23799a2dd95SBruce Richardson  */
2385c55e819SKumara Parameshwaran static inline void
update_header(struct gro_tcp_item * item)2395c55e819SKumara Parameshwaran update_header(struct gro_tcp_item *item)
2405c55e819SKumara Parameshwaran {
2415c55e819SKumara Parameshwaran 	struct rte_ipv4_hdr *ipv4_hdr;
2425c55e819SKumara Parameshwaran 	struct rte_mbuf *pkt = item->firstseg;
24399a2dd95SBruce Richardson 
24463a98ffeSStephen Hemminger 	ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
2455c55e819SKumara Parameshwaran 					   pkt->l2_len);
2465c55e819SKumara Parameshwaran 	ipv4_hdr->total_length = rte_cpu_to_be_16(pkt->pkt_len -
2475c55e819SKumara Parameshwaran 			pkt->l2_len);
24899a2dd95SBruce Richardson }
24999a2dd95SBruce Richardson 
25099a2dd95SBruce Richardson uint16_t
gro_tcp4_tbl_timeout_flush(struct gro_tcp4_tbl * tbl,uint64_t flush_timestamp,struct rte_mbuf ** out,uint16_t nb_out)25199a2dd95SBruce Richardson gro_tcp4_tbl_timeout_flush(struct gro_tcp4_tbl *tbl,
25299a2dd95SBruce Richardson 		uint64_t flush_timestamp,
25399a2dd95SBruce Richardson 		struct rte_mbuf **out,
25499a2dd95SBruce Richardson 		uint16_t nb_out)
25599a2dd95SBruce Richardson {
25699a2dd95SBruce Richardson 	uint16_t k = 0;
25799a2dd95SBruce Richardson 	uint32_t i, j;
25899a2dd95SBruce Richardson 	uint32_t max_flow_num = tbl->max_flow_num;
25999a2dd95SBruce Richardson 
26099a2dd95SBruce Richardson 	for (i = 0; i < max_flow_num; i++) {
26199a2dd95SBruce Richardson 		if (unlikely(tbl->flow_num == 0))
26299a2dd95SBruce Richardson 			return k;
26399a2dd95SBruce Richardson 
26499a2dd95SBruce Richardson 		j = tbl->flows[i].start_index;
26599a2dd95SBruce Richardson 		while (j != INVALID_ARRAY_INDEX) {
26699a2dd95SBruce Richardson 			if (tbl->items[j].start_time <= flush_timestamp) {
26799a2dd95SBruce Richardson 				out[k++] = tbl->items[j].firstseg;
26899a2dd95SBruce Richardson 				if (tbl->items[j].nb_merged > 1)
26999a2dd95SBruce Richardson 					update_header(&(tbl->items[j]));
27099a2dd95SBruce Richardson 				/*
27199a2dd95SBruce Richardson 				 * Delete the packet and get the next
27299a2dd95SBruce Richardson 				 * packet in the flow.
27399a2dd95SBruce Richardson 				 */
2745c55e819SKumara Parameshwaran 				j = delete_tcp_item(tbl->items, j,
2755c55e819SKumara Parameshwaran 							&tbl->item_num, INVALID_ARRAY_INDEX);
27699a2dd95SBruce Richardson 				tbl->flows[i].start_index = j;
27799a2dd95SBruce Richardson 				if (j == INVALID_ARRAY_INDEX)
27899a2dd95SBruce Richardson 					tbl->flow_num--;
27999a2dd95SBruce Richardson 
28099a2dd95SBruce Richardson 				if (unlikely(k == nb_out))
28199a2dd95SBruce Richardson 					return k;
28299a2dd95SBruce Richardson 			} else
28399a2dd95SBruce Richardson 				/*
28499a2dd95SBruce Richardson 				 * The left packets in this flow won't be
28599a2dd95SBruce Richardson 				 * timeout. Go to check other flows.
28699a2dd95SBruce Richardson 				 */
28799a2dd95SBruce Richardson 				break;
28899a2dd95SBruce Richardson 		}
28999a2dd95SBruce Richardson 	}
29099a2dd95SBruce Richardson 	return k;
29199a2dd95SBruce Richardson }
29299a2dd95SBruce Richardson 
29399a2dd95SBruce Richardson uint32_t
gro_tcp4_tbl_pkt_count(void * tbl)29499a2dd95SBruce Richardson gro_tcp4_tbl_pkt_count(void *tbl)
29599a2dd95SBruce Richardson {
29699a2dd95SBruce Richardson 	struct gro_tcp4_tbl *gro_tbl = tbl;
29799a2dd95SBruce Richardson 
29899a2dd95SBruce Richardson 	if (gro_tbl)
29999a2dd95SBruce Richardson 		return gro_tbl->item_num;
30099a2dd95SBruce Richardson 
30199a2dd95SBruce Richardson 	return 0;
30299a2dd95SBruce Richardson }
303