xref: /dpdk/lib/gro/gro_tcp6.c (revision 41d71aeb56694f06d60f0de20e5ac2d718ea5328)
174080d7dSKumara Parameshwaran /* SPDX-License-Identifier: BSD-3-Clause
274080d7dSKumara Parameshwaran  * Copyright(c) 2023 Intel Corporation
374080d7dSKumara Parameshwaran  */
474080d7dSKumara Parameshwaran 
574080d7dSKumara Parameshwaran #include <rte_malloc.h>
674080d7dSKumara Parameshwaran #include <rte_mbuf.h>
774080d7dSKumara Parameshwaran #include <rte_ethdev.h>
874080d7dSKumara Parameshwaran 
974080d7dSKumara Parameshwaran #include "gro_tcp6.h"
1074080d7dSKumara Parameshwaran #include "gro_tcp_internal.h"
1174080d7dSKumara Parameshwaran 
1274080d7dSKumara Parameshwaran void *
1374080d7dSKumara Parameshwaran gro_tcp6_tbl_create(uint16_t socket_id,
1474080d7dSKumara Parameshwaran 		uint16_t max_flow_num,
1574080d7dSKumara Parameshwaran 		uint16_t max_item_per_flow)
1674080d7dSKumara Parameshwaran {
1774080d7dSKumara Parameshwaran 	struct gro_tcp6_tbl *tbl;
1874080d7dSKumara Parameshwaran 	size_t size;
1974080d7dSKumara Parameshwaran 	uint32_t entries_num, i;
2074080d7dSKumara Parameshwaran 
2174080d7dSKumara Parameshwaran 	entries_num = max_flow_num * max_item_per_flow;
2274080d7dSKumara Parameshwaran 	entries_num = RTE_MIN(entries_num, GRO_TCP6_TBL_MAX_ITEM_NUM);
2374080d7dSKumara Parameshwaran 
2474080d7dSKumara Parameshwaran 	if (entries_num == 0)
2574080d7dSKumara Parameshwaran 		return NULL;
2674080d7dSKumara Parameshwaran 
2774080d7dSKumara Parameshwaran 	tbl = rte_zmalloc_socket(__func__,
2874080d7dSKumara Parameshwaran 			sizeof(struct gro_tcp6_tbl),
2974080d7dSKumara Parameshwaran 			RTE_CACHE_LINE_SIZE,
3074080d7dSKumara Parameshwaran 			socket_id);
3174080d7dSKumara Parameshwaran 	if (tbl == NULL)
3274080d7dSKumara Parameshwaran 		return NULL;
3374080d7dSKumara Parameshwaran 
3474080d7dSKumara Parameshwaran 	size = sizeof(struct gro_tcp_item) * entries_num;
3574080d7dSKumara Parameshwaran 	tbl->items = rte_zmalloc_socket(__func__,
3674080d7dSKumara Parameshwaran 			size,
3774080d7dSKumara Parameshwaran 			RTE_CACHE_LINE_SIZE,
3874080d7dSKumara Parameshwaran 			socket_id);
3974080d7dSKumara Parameshwaran 	if (tbl->items == NULL) {
4074080d7dSKumara Parameshwaran 		rte_free(tbl);
4174080d7dSKumara Parameshwaran 		return NULL;
4274080d7dSKumara Parameshwaran 	}
4374080d7dSKumara Parameshwaran 	tbl->max_item_num = entries_num;
4474080d7dSKumara Parameshwaran 
4574080d7dSKumara Parameshwaran 	size = sizeof(struct gro_tcp6_flow) * entries_num;
4674080d7dSKumara Parameshwaran 	tbl->flows = rte_zmalloc_socket(__func__,
4774080d7dSKumara Parameshwaran 			size,
4874080d7dSKumara Parameshwaran 			RTE_CACHE_LINE_SIZE,
4974080d7dSKumara Parameshwaran 			socket_id);
5074080d7dSKumara Parameshwaran 	if (tbl->flows == NULL) {
5174080d7dSKumara Parameshwaran 		rte_free(tbl->items);
5274080d7dSKumara Parameshwaran 		rte_free(tbl);
5374080d7dSKumara Parameshwaran 		return NULL;
5474080d7dSKumara Parameshwaran 	}
5574080d7dSKumara Parameshwaran 	/* INVALID_ARRAY_INDEX indicates an empty flow */
5674080d7dSKumara Parameshwaran 	for (i = 0; i < entries_num; i++)
5774080d7dSKumara Parameshwaran 		tbl->flows[i].start_index = INVALID_ARRAY_INDEX;
5874080d7dSKumara Parameshwaran 	tbl->max_flow_num = entries_num;
5974080d7dSKumara Parameshwaran 
6074080d7dSKumara Parameshwaran 	return tbl;
6174080d7dSKumara Parameshwaran }
6274080d7dSKumara Parameshwaran 
6374080d7dSKumara Parameshwaran void
6474080d7dSKumara Parameshwaran gro_tcp6_tbl_destroy(void *tbl)
6574080d7dSKumara Parameshwaran {
6674080d7dSKumara Parameshwaran 	struct gro_tcp6_tbl *tcp_tbl = tbl;
6774080d7dSKumara Parameshwaran 
6874080d7dSKumara Parameshwaran 	if (tcp_tbl) {
6974080d7dSKumara Parameshwaran 		rte_free(tcp_tbl->items);
7074080d7dSKumara Parameshwaran 		rte_free(tcp_tbl->flows);
7174080d7dSKumara Parameshwaran 	}
7274080d7dSKumara Parameshwaran 	rte_free(tcp_tbl);
7374080d7dSKumara Parameshwaran }
7474080d7dSKumara Parameshwaran 
7574080d7dSKumara Parameshwaran static inline uint32_t
7674080d7dSKumara Parameshwaran find_an_empty_flow(struct gro_tcp6_tbl *tbl)
7774080d7dSKumara Parameshwaran {
7874080d7dSKumara Parameshwaran 	uint32_t i;
7974080d7dSKumara Parameshwaran 	uint32_t max_flow_num = tbl->max_flow_num;
8074080d7dSKumara Parameshwaran 
8174080d7dSKumara Parameshwaran 	for (i = 0; i < max_flow_num; i++)
8274080d7dSKumara Parameshwaran 		if (tbl->flows[i].start_index == INVALID_ARRAY_INDEX)
8374080d7dSKumara Parameshwaran 			return i;
8474080d7dSKumara Parameshwaran 	return INVALID_ARRAY_INDEX;
8574080d7dSKumara Parameshwaran }
8674080d7dSKumara Parameshwaran 
8774080d7dSKumara Parameshwaran static inline uint32_t
8874080d7dSKumara Parameshwaran insert_new_flow(struct gro_tcp6_tbl *tbl,
8974080d7dSKumara Parameshwaran 		struct tcp6_flow_key *src,
9074080d7dSKumara Parameshwaran 		uint32_t item_idx)
9174080d7dSKumara Parameshwaran {
9274080d7dSKumara Parameshwaran 	struct tcp6_flow_key *dst;
9374080d7dSKumara Parameshwaran 	uint32_t flow_idx;
9474080d7dSKumara Parameshwaran 
9574080d7dSKumara Parameshwaran 	flow_idx = find_an_empty_flow(tbl);
9674080d7dSKumara Parameshwaran 	if (unlikely(flow_idx == INVALID_ARRAY_INDEX))
9774080d7dSKumara Parameshwaran 		return INVALID_ARRAY_INDEX;
9874080d7dSKumara Parameshwaran 
9974080d7dSKumara Parameshwaran 	dst = &(tbl->flows[flow_idx].key);
10074080d7dSKumara Parameshwaran 
10174080d7dSKumara Parameshwaran 	ASSIGN_COMMON_TCP_KEY((&src->cmn_key), (&dst->cmn_key));
102*41d71aebSRobin Jarry 	dst->src_addr = src->src_addr;
103*41d71aebSRobin Jarry 	dst->dst_addr = src->dst_addr;
10474080d7dSKumara Parameshwaran 	dst->vtc_flow = src->vtc_flow;
10574080d7dSKumara Parameshwaran 
10674080d7dSKumara Parameshwaran 	tbl->flows[flow_idx].start_index = item_idx;
10774080d7dSKumara Parameshwaran 	tbl->flow_num++;
10874080d7dSKumara Parameshwaran 
10974080d7dSKumara Parameshwaran 	return flow_idx;
11074080d7dSKumara Parameshwaran }
11174080d7dSKumara Parameshwaran 
11274080d7dSKumara Parameshwaran /*
11374080d7dSKumara Parameshwaran  * update the packet length for the flushed packet.
11474080d7dSKumara Parameshwaran  */
11574080d7dSKumara Parameshwaran static inline void
11674080d7dSKumara Parameshwaran update_header(struct gro_tcp_item *item)
11774080d7dSKumara Parameshwaran {
11874080d7dSKumara Parameshwaran 	struct rte_ipv6_hdr *ipv6_hdr;
11974080d7dSKumara Parameshwaran 	struct rte_mbuf *pkt = item->firstseg;
12074080d7dSKumara Parameshwaran 
12163a98ffeSStephen Hemminger 	ipv6_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv6_hdr *,
12274080d7dSKumara Parameshwaran 					   pkt->l2_len);
12374080d7dSKumara Parameshwaran 	ipv6_hdr->payload_len = rte_cpu_to_be_16(pkt->pkt_len -
12474080d7dSKumara Parameshwaran 			pkt->l2_len - pkt->l3_len);
12574080d7dSKumara Parameshwaran }
12674080d7dSKumara Parameshwaran 
12774080d7dSKumara Parameshwaran int32_t
12874080d7dSKumara Parameshwaran gro_tcp6_reassemble(struct rte_mbuf *pkt,
12974080d7dSKumara Parameshwaran 		struct gro_tcp6_tbl *tbl,
13074080d7dSKumara Parameshwaran 		uint64_t start_time)
13174080d7dSKumara Parameshwaran {
13274080d7dSKumara Parameshwaran 	struct rte_ether_hdr *eth_hdr;
13374080d7dSKumara Parameshwaran 	struct rte_ipv6_hdr *ipv6_hdr;
13474080d7dSKumara Parameshwaran 	int32_t tcp_dl;
13574080d7dSKumara Parameshwaran 	uint16_t ip_tlen;
13674080d7dSKumara Parameshwaran 	struct tcp6_flow_key key;
13774080d7dSKumara Parameshwaran 	uint32_t i, max_flow_num, remaining_flow_num;
13874080d7dSKumara Parameshwaran 	uint32_t sent_seq;
13974080d7dSKumara Parameshwaran 	struct rte_tcp_hdr *tcp_hdr;
14074080d7dSKumara Parameshwaran 	uint8_t find;
14174080d7dSKumara Parameshwaran 	uint32_t item_idx;
14274080d7dSKumara Parameshwaran 	/*
14374080d7dSKumara Parameshwaran 	 * Don't process the packet whose TCP header length is greater
14474080d7dSKumara Parameshwaran 	 * than 60 bytes or less than 20 bytes.
14574080d7dSKumara Parameshwaran 	 */
14674080d7dSKumara Parameshwaran 	if (unlikely(INVALID_TCP_HDRLEN(pkt->l4_len)))
14774080d7dSKumara Parameshwaran 		return -1;
14874080d7dSKumara Parameshwaran 
14974080d7dSKumara Parameshwaran 	eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
15074080d7dSKumara Parameshwaran 	ipv6_hdr = (struct rte_ipv6_hdr *)((char *)eth_hdr + pkt->l2_len);
15174080d7dSKumara Parameshwaran 	tcp_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_tcp_hdr *, pkt->l2_len + pkt->l3_len);
15274080d7dSKumara Parameshwaran 
15374080d7dSKumara Parameshwaran 	/*
15474080d7dSKumara Parameshwaran 	 * Don't process the packet which has FIN, SYN, RST, PSH, URG, ECE
15574080d7dSKumara Parameshwaran 	 * or CWR set.
15674080d7dSKumara Parameshwaran 	 */
15774080d7dSKumara Parameshwaran 	if (tcp_hdr->tcp_flags != RTE_TCP_ACK_FLAG)
15874080d7dSKumara Parameshwaran 		return -1;
15974080d7dSKumara Parameshwaran 
16074080d7dSKumara Parameshwaran 	ip_tlen = rte_be_to_cpu_16(ipv6_hdr->payload_len);
16174080d7dSKumara Parameshwaran 	/*
16274080d7dSKumara Parameshwaran 	 * Don't process the packet whose payload length is less than or
16374080d7dSKumara Parameshwaran 	 * equal to 0.
16474080d7dSKumara Parameshwaran 	 */
16574080d7dSKumara Parameshwaran 	tcp_dl = ip_tlen - pkt->l4_len;
16674080d7dSKumara Parameshwaran 	if (tcp_dl <= 0)
16774080d7dSKumara Parameshwaran 		return -1;
16874080d7dSKumara Parameshwaran 
16974080d7dSKumara Parameshwaran 	rte_ether_addr_copy(&(eth_hdr->src_addr), &(key.cmn_key.eth_saddr));
17074080d7dSKumara Parameshwaran 	rte_ether_addr_copy(&(eth_hdr->dst_addr), &(key.cmn_key.eth_daddr));
171*41d71aebSRobin Jarry 	key.src_addr = ipv6_hdr->src_addr;
172*41d71aebSRobin Jarry 	key.dst_addr = ipv6_hdr->dst_addr;
17374080d7dSKumara Parameshwaran 	key.cmn_key.src_port = tcp_hdr->src_port;
17474080d7dSKumara Parameshwaran 	key.cmn_key.dst_port = tcp_hdr->dst_port;
17574080d7dSKumara Parameshwaran 	key.cmn_key.recv_ack = tcp_hdr->recv_ack;
17674080d7dSKumara Parameshwaran 	key.vtc_flow = ipv6_hdr->vtc_flow;
17774080d7dSKumara Parameshwaran 
17874080d7dSKumara Parameshwaran 	/* Search for a matched flow. */
17974080d7dSKumara Parameshwaran 	max_flow_num = tbl->max_flow_num;
18074080d7dSKumara Parameshwaran 	remaining_flow_num = tbl->flow_num;
18174080d7dSKumara Parameshwaran 	find = 0;
18274080d7dSKumara Parameshwaran 	for (i = 0; i < max_flow_num && remaining_flow_num; i++) {
18374080d7dSKumara Parameshwaran 		if (tbl->flows[i].start_index != INVALID_ARRAY_INDEX) {
18474080d7dSKumara Parameshwaran 			if (is_same_tcp6_flow(&tbl->flows[i].key, &key)) {
18574080d7dSKumara Parameshwaran 				find = 1;
18674080d7dSKumara Parameshwaran 				break;
18774080d7dSKumara Parameshwaran 			}
18874080d7dSKumara Parameshwaran 			remaining_flow_num--;
18974080d7dSKumara Parameshwaran 		}
19074080d7dSKumara Parameshwaran 	}
19174080d7dSKumara Parameshwaran 
19274080d7dSKumara Parameshwaran 	if (find == 0) {
19374080d7dSKumara Parameshwaran 		sent_seq = rte_be_to_cpu_32(tcp_hdr->sent_seq);
19474080d7dSKumara Parameshwaran 		item_idx = insert_new_tcp_item(pkt, tbl->items, &tbl->item_num,
19574080d7dSKumara Parameshwaran 						tbl->max_item_num, start_time,
19674080d7dSKumara Parameshwaran 						INVALID_ARRAY_INDEX, sent_seq, 0, true);
19774080d7dSKumara Parameshwaran 		if (item_idx == INVALID_ARRAY_INDEX)
19874080d7dSKumara Parameshwaran 			return -1;
19974080d7dSKumara Parameshwaran 		if (insert_new_flow(tbl, &key, item_idx) ==
20074080d7dSKumara Parameshwaran 			INVALID_ARRAY_INDEX) {
20174080d7dSKumara Parameshwaran 			/*
20274080d7dSKumara Parameshwaran 			 * Fail to insert a new flow, so delete the
20374080d7dSKumara Parameshwaran 			 * stored packet.
20474080d7dSKumara Parameshwaran 			 */
20574080d7dSKumara Parameshwaran 			delete_tcp_item(tbl->items, item_idx, &tbl->item_num, INVALID_ARRAY_INDEX);
20674080d7dSKumara Parameshwaran 			return -1;
20774080d7dSKumara Parameshwaran 		}
20874080d7dSKumara Parameshwaran 		return 0;
20974080d7dSKumara Parameshwaran 	}
21074080d7dSKumara Parameshwaran 
21174080d7dSKumara Parameshwaran 	return process_tcp_item(pkt, tcp_hdr, tcp_dl, tbl->items, tbl->flows[i].start_index,
21274080d7dSKumara Parameshwaran 						&tbl->item_num, tbl->max_item_num,
21374080d7dSKumara Parameshwaran 						0, true, start_time);
21474080d7dSKumara Parameshwaran }
21574080d7dSKumara Parameshwaran 
21674080d7dSKumara Parameshwaran uint16_t
21774080d7dSKumara Parameshwaran gro_tcp6_tbl_timeout_flush(struct gro_tcp6_tbl *tbl,
21874080d7dSKumara Parameshwaran 		uint64_t flush_timestamp,
21974080d7dSKumara Parameshwaran 		struct rte_mbuf **out,
22074080d7dSKumara Parameshwaran 		uint16_t nb_out)
22174080d7dSKumara Parameshwaran {
22274080d7dSKumara Parameshwaran 	uint16_t k = 0;
22374080d7dSKumara Parameshwaran 	uint32_t i, j;
22474080d7dSKumara Parameshwaran 	uint32_t max_flow_num = tbl->max_flow_num;
22574080d7dSKumara Parameshwaran 
22674080d7dSKumara Parameshwaran 	for (i = 0; i < max_flow_num; i++) {
22774080d7dSKumara Parameshwaran 		if (unlikely(tbl->flow_num == 0))
22874080d7dSKumara Parameshwaran 			return k;
22974080d7dSKumara Parameshwaran 
23074080d7dSKumara Parameshwaran 		j = tbl->flows[i].start_index;
23174080d7dSKumara Parameshwaran 		while (j != INVALID_ARRAY_INDEX) {
23274080d7dSKumara Parameshwaran 			if (tbl->items[j].start_time <= flush_timestamp) {
23374080d7dSKumara Parameshwaran 				out[k++] = tbl->items[j].firstseg;
23474080d7dSKumara Parameshwaran 				if (tbl->items[j].nb_merged > 1)
23574080d7dSKumara Parameshwaran 					update_header(&(tbl->items[j]));
23674080d7dSKumara Parameshwaran 				/*
23774080d7dSKumara Parameshwaran 				 * Delete the packet and get the next
23874080d7dSKumara Parameshwaran 				 * packet in the flow.
23974080d7dSKumara Parameshwaran 				 */
24074080d7dSKumara Parameshwaran 				j = delete_tcp_item(tbl->items, j,
24174080d7dSKumara Parameshwaran 						&tbl->item_num, INVALID_ARRAY_INDEX);
24274080d7dSKumara Parameshwaran 				tbl->flows[i].start_index = j;
24374080d7dSKumara Parameshwaran 				if (j == INVALID_ARRAY_INDEX)
24474080d7dSKumara Parameshwaran 					tbl->flow_num--;
24574080d7dSKumara Parameshwaran 
24674080d7dSKumara Parameshwaran 				if (unlikely(k == nb_out))
24774080d7dSKumara Parameshwaran 					return k;
24874080d7dSKumara Parameshwaran 			} else
24974080d7dSKumara Parameshwaran 				/*
25074080d7dSKumara Parameshwaran 				 * The left packets in this flow won't be
25174080d7dSKumara Parameshwaran 				 * timeout. Go to check other flows.
25274080d7dSKumara Parameshwaran 				 */
25374080d7dSKumara Parameshwaran 				break;
25474080d7dSKumara Parameshwaran 		}
25574080d7dSKumara Parameshwaran 	}
25674080d7dSKumara Parameshwaran 	return k;
25774080d7dSKumara Parameshwaran }
25874080d7dSKumara Parameshwaran 
25974080d7dSKumara Parameshwaran uint32_t
26074080d7dSKumara Parameshwaran gro_tcp6_tbl_pkt_count(void *tbl)
26174080d7dSKumara Parameshwaran {
26274080d7dSKumara Parameshwaran 	struct gro_tcp6_tbl *gro_tbl = tbl;
26374080d7dSKumara Parameshwaran 
26474080d7dSKumara Parameshwaran 	if (gro_tbl)
26574080d7dSKumara Parameshwaran 		return gro_tbl->item_num;
26674080d7dSKumara Parameshwaran 
26774080d7dSKumara Parameshwaran 	return 0;
26874080d7dSKumara Parameshwaran }
269