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