From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by dpdk.org (Postfix) with ESMTP id 146E42BF2 for ; Mon, 3 Jul 2017 07:14:03 +0200 (CEST) Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga105.jf.intel.com with ESMTP; 02 Jul 2017 22:14:01 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.40,301,1496127600"; d="scan'208";a="874190404" Received: from fmsmsx108.amr.corp.intel.com ([10.18.124.206]) by FMSMGA003.fm.intel.com with ESMTP; 02 Jul 2017 22:14:01 -0700 Received: from fmsmsx119.amr.corp.intel.com (10.18.124.207) by FMSMSX108.amr.corp.intel.com (10.18.124.206) with Microsoft SMTP Server (TLS) id 14.3.319.2; Sun, 2 Jul 2017 22:14:01 -0700 Received: from shsmsx152.ccr.corp.intel.com (10.239.6.52) by FMSMSX119.amr.corp.intel.com (10.18.124.207) with Microsoft SMTP Server (TLS) id 14.3.319.2; Sun, 2 Jul 2017 22:14:01 -0700 Received: from shsmsx102.ccr.corp.intel.com ([169.254.2.146]) by SHSMSX152.ccr.corp.intel.com ([169.254.6.122]) with mapi id 14.03.0319.002; Mon, 3 Jul 2017 13:13:59 +0800 From: "Hu, Jiayu" To: "Tan, Jianfeng" , "dev@dpdk.org" CC: "Ananyev, Konstantin" , "stephen@networkplumber.org" , "yliu@fridaylinux.org" , "Wu, Jingjing" , "Yao, Lei A" , "Bie, Tiwei" Thread-Topic: [PATCH v10 2/3] lib/gro: add TCP/IPv4 GRO support Thread-Index: AQHS8lo/MRljckFCyEKPm8VDcdhCIaI/zqEAgAGKllA= Date: Mon, 3 Jul 2017 05:13:58 +0000 Message-ID: References: <1498805618-63649-1-git-send-email-jiayu.hu@intel.com> <1498907323-17563-1-git-send-email-jiayu.hu@intel.com> <1498907323-17563-3-git-send-email-jiayu.hu@intel.com> <271152e2-7912-a2d5-2d22-d9a86596011e@intel.com> In-Reply-To: <271152e2-7912-a2d5-2d22-d9a86596011e@intel.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: dlp-product: dlpe-windows dlp-version: 10.0.102.7 dlp-reaction: no-action x-originating-ip: [10.239.127.40] Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Subject: Re: [dpdk-dev] [PATCH v10 2/3] lib/gro: add TCP/IPv4 GRO support X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 03 Jul 2017 05:14:05 -0000 Hi Jianfeng, > -----Original Message----- > From: Tan, Jianfeng > Sent: Sunday, July 2, 2017 6:20 PM > To: Hu, Jiayu ; dev@dpdk.org > Cc: Ananyev, Konstantin ; > stephen@networkplumber.org; yliu@fridaylinux.org; Wu, Jingjing > ; Yao, Lei A ; Bie, Tiwei > > Subject: Re: [PATCH v10 2/3] lib/gro: add TCP/IPv4 GRO support >=20 >=20 >=20 > On 7/1/2017 7:08 PM, Jiayu Hu wrote: > > In this patch, we introduce five APIs to support TCP/IPv4 GRO. > > - gro_tcp4_tbl_create: create a TCP/IPv4 reassembly table, which is use= d > > to merge packets. > > - gro_tcp4_tbl_destroy: free memory space of a TCP/IPv4 reassembly tabl= e. > > - gro_tcp4_tbl_timeout_flush: flush timeout packets from a TCP/IPv4 > > reassembly table. > > - gro_tcp4_tbl_item_num: return the number of packets in a TCP/IPv4 > > reassembly table. > > - gro_tcp4_reassemble: reassemble an inputted TCP/IPv4 packet. > > > > TCP/IPv4 GRO API assumes all inputted packets are with correct IPv4 > > and TCP checksums. And TCP/IPv4 GRO API doesn't update IPv4 and TCP > > checksums for merged packets. If inputted packets are IP fragmented, > > TCP/IPv4 GRO API assumes they are complete packets (i.e. with L4 > > headers). > > > > In TCP/IPv4 GRO, we use a table structure, called TCP/IPv4 reassembly > > table, to reassemble packets. A TCP/IPv4 reassembly table includes a ke= y > > array and a item array, where the key array keeps the criteria to merge > > packets and the item array keeps packet information. > > > > One key in the key array points to an item group, which consists of > > packets which have the same criteria value. If two packets are able to > > merge, they must be in the same item group. Each key in the key array > > includes two parts: > > - criteria: the criteria of merging packets. If two packets can be > > merged, they must have the same criteria value. > > - start_index: the index of the first incoming packet of the item group= . > > > > Each element in the item array keeps the information of one packet. It > > mainly includes two parts: > > - pkt: packet address > > - next_pkt_index: the index of the next packet in the same item group. > > All packets in the same item group are chained by next_pkt_index. > > With next_pkt_index, we can locate all packets in the same item > > group one by one. > > > > To process an incoming packet needs three steps: > > a. check if the packet should be processed. Packets with one of the > > following properties won't be processed: > > - SYN, FIN, RST or PSH bit is set; > > - packet payload length is 0. > > b. traverse the key array to find a key which has the same criteria > > value with the incoming packet. If find, goto step c. Otherwise, > > insert a new key and insert the packet into the item array. > > c. locate the first packet in the item group via the start_index in the > > key. Then traverse all packets in the item group via next_pkt_inde= x. > > If find one packet which can merge with the incoming one, merge th= em > > together. If can't find, insert the packet into this item group. > > > > Signed-off-by: Jiayu Hu > > --- > > doc/guides/rel_notes/release_17_08.rst | 7 + > > lib/librte_gro/Makefile | 1 + > > lib/librte_gro/rte_gro.c | 123 ++++++++- > > lib/librte_gro/rte_gro.h | 10 +- > > lib/librte_gro/rte_gro_tcp4.c | 439 > +++++++++++++++++++++++++++++++++ > > lib/librte_gro/rte_gro_tcp4.h | 172 +++++++++++++ > > 6 files changed, 736 insertions(+), 16 deletions(-) > > create mode 100644 lib/librte_gro/rte_gro_tcp4.c > > create mode 100644 lib/librte_gro/rte_gro_tcp4.h > > > > diff --git a/doc/guides/rel_notes/release_17_08.rst > b/doc/guides/rel_notes/release_17_08.rst > > index 842f46f..f067247 100644 > > --- a/doc/guides/rel_notes/release_17_08.rst > > +++ b/doc/guides/rel_notes/release_17_08.rst > > @@ -75,6 +75,13 @@ New Features > > > > Added support for firmwares with multiple Ethernet ports per physic= al > port. > > > > +* **Add Generic Receive Offload API support.** > > + > > + Generic Receive Offload (GRO) API supports to reassemble TCP/IPv4 > > + packets. GRO API assumes all inputted packets are with correct > > + checksums. GRO API doesn't update checksums for merged packets. If > > + inputted packets are IP fragmented, GRO API assumes they are complet= e > > + packets (i.e. with L4 headers). > > > > Resolved Issues > > --------------- > > diff --git a/lib/librte_gro/Makefile b/lib/librte_gro/Makefile > > index 7e0f128..43e276e 100644 > > --- a/lib/librte_gro/Makefile > > +++ b/lib/librte_gro/Makefile > > @@ -43,6 +43,7 @@ LIBABIVER :=3D 1 > > > > # source files > > SRCS-$(CONFIG_RTE_LIBRTE_GRO) +=3D rte_gro.c > > +SRCS-$(CONFIG_RTE_LIBRTE_GRO) +=3D rte_gro_tcp4.c > > > > # install this header file > > SYMLINK-$(CONFIG_RTE_LIBRTE_GRO)-include +=3D rte_gro.h > > diff --git a/lib/librte_gro/rte_gro.c b/lib/librte_gro/rte_gro.c > > index 648835b..a4641f9 100644 > > --- a/lib/librte_gro/rte_gro.c > > +++ b/lib/librte_gro/rte_gro.c > > @@ -32,8 +32,11 @@ > > > > #include > > #include > > +#include > > +#include > > > > #include "rte_gro.h" > > +#include "rte_gro_tcp4.h" > > > > typedef void *(*gro_tbl_create_fn)(uint16_t socket_id, > > uint16_t max_flow_num, > > @@ -41,9 +44,12 @@ typedef void *(*gro_tbl_create_fn)(uint16_t > socket_id, > > typedef void (*gro_tbl_destroy_fn)(void *tbl); > > typedef uint32_t (*gro_tbl_item_num_fn)(void *tbl); > > > > -static gro_tbl_create_fn > tbl_create_functions[RTE_GRO_TYPE_MAX_NUM]; > > -static gro_tbl_destroy_fn > tbl_destroy_functions[RTE_GRO_TYPE_MAX_NUM]; > > -static gro_tbl_item_num_fn > tbl_item_num_functions[RTE_GRO_TYPE_MAX_NUM]; > > +static gro_tbl_create_fn > tbl_create_functions[RTE_GRO_TYPE_MAX_NUM] =3D { > > + gro_tcp4_tbl_create, NULL}; > > +static gro_tbl_destroy_fn > tbl_destroy_functions[RTE_GRO_TYPE_MAX_NUM] =3D { > > + gro_tcp4_tbl_destroy, NULL}; > > +static gro_tbl_item_num_fn tbl_item_num_functions[ > > + RTE_GRO_TYPE_MAX_NUM] =3D {gro_tcp4_tbl_item_num, NULL}; > > > > /** > > * GRO table, which is used to merge packets. It keeps many reassembl= y > > @@ -130,27 +136,118 @@ void rte_gro_tbl_destroy(void *tbl) > > } > > > > uint16_t > > -rte_gro_reassemble_burst(struct rte_mbuf **pkts __rte_unused, > > +rte_gro_reassemble_burst(struct rte_mbuf **pkts, > > uint16_t nb_pkts, > > - const struct rte_gro_param *param __rte_unused) > > + const struct rte_gro_param *param) > > { > > - return nb_pkts; > > + uint16_t i; > > + uint16_t nb_after_gro =3D nb_pkts; > > + uint32_t item_num; > > + > > + /* allocate a reassembly table for TCP/IPv4 GRO */ > > + struct gro_tcp4_tbl tcp_tbl; > > + struct gro_tcp4_key tcp_keys[RTE_GRO_MAX_BURST_ITEM_NUM] > =3D {0}; > > + struct gro_tcp4_item tcp_items[RTE_GRO_MAX_BURST_ITEM_NUM] > =3D {0}; > > + > > + struct rte_mbuf *unprocess_pkts[nb_pkts]; > > + uint16_t unprocess_num =3D 0; > > + int32_t ret; > > + uint64_t current_time; > > + > > + if ((param->desired_gro_types & RTE_GRO_TCP_IPV4) =3D=3D 0) > > + return nb_pkts; > > + > > + /* get the actual number of items */ > > + item_num =3D RTE_MIN(nb_pkts, (param->max_flow_num * > > + param->max_item_per_flow)); > > + item_num =3D RTE_MIN(item_num, > RTE_GRO_MAX_BURST_ITEM_NUM); > > + > > + tcp_tbl.keys =3D tcp_keys; > > + tcp_tbl.items =3D tcp_items; > > + tcp_tbl.key_num =3D 0; > > + tcp_tbl.item_num =3D 0; > > + tcp_tbl.max_key_num =3D item_num; > > + tcp_tbl.max_item_num =3D item_num; > > + > > + current_time =3D rte_rdtsc(); > > + > > + for (i =3D 0; i < nb_pkts; i++) { > > + if (RTE_ETH_IS_IPV4_HDR(pkts[i]->packet_type) && > > + (pkts[i]->packet_type & > RTE_PTYPE_L4_TCP)) { > > + ret =3D gro_tcp4_reassemble(pkts[i], > > + &tcp_tbl, > > + param->max_packet_size, > > + current_time); > > + if (ret > 0) > > + /* merge successfully */ > > + nb_after_gro--; > > + else if (ret < 0) > > + unprocess_pkts[unprocess_num++] =3D > > + pkts[i]; > > + } else > > + unprocess_pkts[unprocess_num++] =3D > > + pkts[i]; > > + } > > + > > + /* re-arrange GROed packets */ > > + if (nb_after_gro < nb_pkts) { > > + i =3D gro_tcp4_tbl_timeout_flush(&tcp_tbl, 0, > > + pkts, nb_pkts); > > + if (unprocess_num > 0) > > + memcpy(&pkts[i], unprocess_pkts, > > + sizeof(struct rte_mbuf *) * > > + unprocess_num); > > + } > > + return nb_after_gro; > > } > > > > uint16_t > > -rte_gro_reassemble(struct rte_mbuf **pkts __rte_unused, > > +rte_gro_reassemble(struct rte_mbuf **pkts, > > uint16_t nb_pkts, > > - void *tbl __rte_unused) > > + void *tbl) > > { > > - return nb_pkts; > > + uint16_t i, unprocess_num =3D 0; > > + struct rte_mbuf *unprocess_pkts[nb_pkts]; > > + struct gro_tbl *gro_tbl =3D (struct gro_tbl *)tbl; > > + uint64_t current_time; > > + > > + if ((gro_tbl->desired_gro_types & RTE_GRO_TCP_IPV4) =3D=3D 0) > > + return nb_pkts; > > + > > + current_time =3D rte_rdtsc(); > > + for (i =3D 0; i < nb_pkts; i++) { > > + if (RTE_ETH_IS_IPV4_HDR(pkts[i]->packet_type) && > > + (pkts[i]->packet_type & > RTE_PTYPE_L4_TCP)) { > > + if (gro_tcp4_reassemble(pkts[i], > > + gro_tbl- > >tbls[RTE_GRO_TCP_IPV4_INDEX], > > + gro_tbl->max_packet_size, > > + current_time) < 0) > > + unprocess_pkts[unprocess_num++] =3D pkts[i]; > > + } else > > + unprocess_pkts[unprocess_num++] =3D pkts[i]; > > + } > > + if (unprocess_num > 0) > > + memcpy(pkts, unprocess_pkts, > > + sizeof(struct rte_mbuf *) * unprocess_num); > > + > > + return unprocess_num; > > } > > > > uint16_t > > -rte_gro_timeout_flush(void *tbl __rte_unused, > > - uint64_t desired_gro_types __rte_unused, > > - struct rte_mbuf **out __rte_unused, > > - uint16_t max_nb_out __rte_unused) > > +rte_gro_timeout_flush(void *tbl, > > + uint64_t desired_gro_types, > > + struct rte_mbuf **out, > > + uint16_t max_nb_out) > > { > > + struct gro_tbl *gro_tbl =3D (struct gro_tbl *)tbl; > > + > > + desired_gro_types =3D desired_gro_types & > > + gro_tbl->desired_gro_types; > > + if (desired_gro_types & RTE_GRO_TCP_IPV4) > > + return gro_tcp4_tbl_timeout_flush( > > + gro_tbl->tbls[RTE_GRO_TCP_IPV4_INDEX], > > + gro_tbl->max_timeout_cycles, > > + out, max_nb_out); > > return 0; > > } > > > > diff --git a/lib/librte_gro/rte_gro.h b/lib/librte_gro/rte_gro.h > > index 02c9113..68d2fc6 100644 > > --- a/lib/librte_gro/rte_gro.h > > +++ b/lib/librte_gro/rte_gro.h > > @@ -45,7 +45,11 @@ extern "C" { > > > > /* max number of supported GRO types */ > > #define RTE_GRO_TYPE_MAX_NUM 64 > > -#define RTE_GRO_TYPE_SUPPORT_NUM 0 /**< current supported > GRO num */ > > +#define RTE_GRO_TYPE_SUPPORT_NUM 1 /**< current supported > GRO num */ > > + > > +/* TCP/IPv4 GRO flag */ > > +#define RTE_GRO_TCP_IPV4_INDEX 0 > > +#define RTE_GRO_TCP_IPV4 (1ULL << RTE_GRO_TCP_IPV4_INDEX) > > > > > > struct rte_gro_param { > > @@ -118,14 +122,14 @@ uint16_t rte_gro_reassemble_burst(struct > rte_mbuf **pkts, > > * > > * @param pkts > > * packet to reassemble. Besides, after this function finishes, it > > - * keeps the unprocessed packets (i.e. without data or unsupported > > + * keeps the unprocessed packets (e.g. without data or unsupported > > * GRO types). > > * @param nb_pkts > > * the number of packets to reassemble. > > * @param tbl > > * a pointer points to a GRO table. > > * @return > > - * return the number of unprocessed packets (i.e. without data or > > + * return the number of unprocessed packets (e.g. without data or > > * unsupported GRO types). If all packets are processed (merged or > > * inserted into the table), return 0. > > */ > > diff --git a/lib/librte_gro/rte_gro_tcp4.c b/lib/librte_gro/rte_gro_tcp= 4.c > > new file mode 100644 > > index 0000000..8f2aa86 > > --- /dev/null > > +++ b/lib/librte_gro/rte_gro_tcp4.c > > @@ -0,0 +1,439 @@ > > +/*- > > + * BSD LICENSE > > + * > > + * Copyright(c) 2017 Intel Corporation. All rights reserved. > > + * > > + * Redistribution and use in source and binary forms, with or withou= t > > + * modification, are permitted provided that the following condition= s > > + * are met: > > + * > > + * * Redistributions of source code must retain the above copyrigh= t > > + * notice, this list of conditions and the following disclaimer. > > + * * Redistributions in binary form must reproduce the above copyr= ight > > + * notice, this list of conditions and the following disclaimer = in > > + * the documentation and/or other materials provided with the > > + * distribution. > > + * * Neither the name of Intel Corporation nor the names of its > > + * contributors may be used to endorse or promote products deriv= ed > > + * from this software without specific prior written permission. > > + * > > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND > CONTRIBUTORS > > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT > NOT > > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND > FITNESS FOR > > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT > > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, > > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, > BUT NOT > > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; > LOSS OF USE, > > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED > AND ON ANY > > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR > TORT > > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT > OF THE USE > > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > DAMAGE. > > + */ > > + > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > + > > +#include "rte_gro_tcp4.h" > > + > > +void *gro_tcp4_tbl_create(uint16_t socket_id, > > + uint16_t max_flow_num, > > + uint16_t max_item_per_flow) > > +{ > > + size_t size; > > + uint32_t entries_num; > > + struct gro_tcp4_tbl *tbl; > > + > > + entries_num =3D max_flow_num * max_item_per_flow; > > + entries_num =3D entries_num > GRO_TCP4_TBL_MAX_ITEM_NUM ? > > + GRO_TCP4_TBL_MAX_ITEM_NUM : entries_num; > > + > > + if (entries_num =3D=3D 0) > > + return NULL; > > + > > + tbl =3D (struct gro_tcp4_tbl *)rte_zmalloc_socket( > > + __func__, > > + sizeof(struct gro_tcp4_tbl), > > + RTE_CACHE_LINE_SIZE, > > + socket_id); > > + if (tbl =3D=3D NULL) > > + return NULL; > > + > > + size =3D sizeof(struct gro_tcp4_item) * entries_num; > > + tbl->items =3D (struct gro_tcp4_item *)rte_zmalloc_socket( > > + __func__, > > + size, > > + RTE_CACHE_LINE_SIZE, > > + socket_id); > > + if (tbl->items =3D=3D NULL) { > > + rte_free(tbl); > > + return NULL; > > + } > > + tbl->max_item_num =3D entries_num; > > + > > + size =3D sizeof(struct gro_tcp4_key) * entries_num; > > + tbl->keys =3D (struct gro_tcp4_key *)rte_zmalloc_socket( > > + __func__, > > + size, RTE_CACHE_LINE_SIZE, > > + socket_id); > > + if (tbl->keys =3D=3D NULL) { > > + rte_free(tbl->items); > > + rte_free(tbl); > > + return NULL; > > + } > > + tbl->max_key_num =3D entries_num; > > + return tbl; > > +} > > + > > +void gro_tcp4_tbl_destroy(void *tbl) > > +{ > > + struct gro_tcp4_tbl *tcp_tbl =3D (struct gro_tcp4_tbl *)tbl; > > + > > + if (tcp_tbl) { > > + rte_free(tcp_tbl->items); > > + rte_free(tcp_tbl->keys); > > + } > > + rte_free(tcp_tbl); > > +} > > + > > +static struct rte_mbuf *get_mbuf_lastseg(struct rte_mbuf *pkt) > > +{ > > + struct rte_mbuf *lastseg =3D pkt; > > + > > + while (lastseg->next) > > + lastseg =3D lastseg->next; > > + > > + return lastseg; > > +} > > + > > +/** > > + * merge two TCP/IPv4 packets without updating checksums. > > + */ > > +static int > > +merge_two_tcp4_packets(struct gro_tcp4_item *item_src, > > + struct rte_mbuf *pkt, > > + uint32_t max_packet_size, > > + int cmp) > > +{ > > + struct rte_mbuf *pkt_head, *pkt_tail, *lastseg; > > + struct ipv4_hdr *ipv4_hdr1, *ipv4_hdr0; > > + uint16_t tcp_dl1; > > + > > + if (cmp > 0) { > > + /* append the new packet into tail */ > > + pkt_head =3D item_src->pkt; > > + pkt_tail =3D pkt; > > + } else { > > + /* append the new packet into head */ >=20 > Typo: append -> prepend Thanks, I will modify it. >=20 > > + pkt_head =3D pkt; > > + pkt_tail =3D item_src->pkt; > > + } > > + > > + /* parse the tail packet */ > > + ipv4_hdr1 =3D (struct ipv4_hdr *)(rte_pktmbuf_mtod(pkt_tail, > > + char *) + pkt_tail->l2_len); > > + tcp_dl1 =3D rte_be_to_cpu_16(ipv4_hdr1->total_length) - > > + pkt_tail->l3_len - pkt_tail->l4_len; > > + > > + if (pkt_head->pkt_len + tcp_dl1 > max_packet_size) > > + return -1; > > + > > + /* remove packet header for the tail packet */ > > + rte_pktmbuf_adj(pkt_tail, pkt_tail->l2_len + > > + pkt_tail->l3_len + > > + pkt_tail->l4_len); > > + > > + if (cmp > 0) { > > + /* chain the new packet to the tail of the original packet */ > > + item_src->lastseg->next =3D pkt_tail; > > + /* update the lastseg for the item */ > > + item_src->lastseg =3D get_mbuf_lastseg(pkt_tail); > > + } else { > > + /* chain the original packet to the tail of the new packet */ > > + lastseg =3D get_mbuf_lastseg(pkt_head); > > + lastseg->next =3D pkt_tail; > > + /* update the item */ > > + item_src->pkt =3D pkt_head; > > + } > > + > > + /* parse the head packet */ > > + ipv4_hdr0 =3D (struct ipv4_hdr *)(rte_pktmbuf_mtod(pkt_head, > > + char *) + pkt_head->l2_len); > > + > > + /* update IP header for the head packet */ > > + ipv4_hdr0->total_length =3D rte_cpu_to_be_16( > > + rte_be_to_cpu_16( > > + ipv4_hdr0->total_length) > > + + tcp_dl1); > > + ipv4_hdr0->packet_id =3D ipv4_hdr1->packet_id; >=20 > Why do we bother to change id field in IP header? I think it's not > necessary. Currently, when merge packets, we check if IP ID is consecutive. If two packets (p1 and p2) are merged and we don't update the IP ID for the merged packet p1-p2, the IP ID of p1-p2 is the IP ID of p1. When p3 comes, we can't merge it with p1-p2, since their IP IDs are not consecutive= . So I update IP ID when two packets are merged. If we don't update IP ID, that means we shouldn't check if IP ID is consecutive when merge packets. But Konstantin suggests me to check the IP ID. Maybe we need to confirm with him again. >=20 > > + > > + /* update mbuf metadata for the merged packet */ > > + pkt_head->nb_segs +=3D pkt_tail->nb_segs; > > + pkt_head->pkt_len +=3D pkt_tail->pkt_len; > > + return 1; > > +} > > + > > +static int > > +check_seq_option(struct rte_mbuf *pkt, > > + struct tcp_hdr *tcp_hdr, > > + uint16_t ip_id, > > + uint16_t tcp_hl, > > + uint16_t tcp_dl) > > +{ > > + struct ipv4_hdr *ipv4_hdr0; > > + struct tcp_hdr *tcp_hdr0; > > + uint16_t tcp_hl0, tcp_dl0, ip_id0; > > + uint32_t sent_seq0, sent_seq; > > + uint16_t len; > > + > > + ipv4_hdr0 =3D (struct ipv4_hdr *)(rte_pktmbuf_mtod(pkt, > > + char *) + pkt->l2_len); > > + tcp_hdr0 =3D (struct tcp_hdr *)((char *)ipv4_hdr0 + pkt->l3_len); > > + > > + ip_id0 =3D rte_be_to_cpu_16(ipv4_hdr0->packet_id); > > + tcp_hl0 =3D pkt->l4_len; > > + tcp_dl0 =3D rte_be_to_cpu_16(ipv4_hdr0->total_length) - > > + pkt->l3_len - tcp_hl0; > > + > > + /* check if TCP option fields equal. If not, return 0. */ > > + len =3D RTE_MAX(tcp_hl, tcp_hl0) - sizeof(struct tcp_hdr); > > + if ((tcp_hl !=3D tcp_hl0) || ((len > 0) && > > + (memcmp(tcp_hdr + 1, > > + tcp_hdr0 + 1, > > + len) !=3D 0))) > > + return 0; > > + > > + /* check if the two packets are neighbors */ > > + sent_seq0 =3D rte_be_to_cpu_32(tcp_hdr0->sent_seq); > > + sent_seq =3D rte_be_to_cpu_32(tcp_hdr->sent_seq); > > + > > + if ((sent_seq =3D=3D (sent_seq0 + tcp_dl0)) && > > + (ip_id =3D=3D (ip_id0 + 1))) > > + /* append the new packet into tail */ > > + return 1; > > + else if (((sent_seq + tcp_dl) =3D=3D sent_seq0) && > > + ((ip_id + 1) =3D=3D ip_id0)) > > + /* append the new packet into head */ > > + return -1; > > + else > > + return 0; > > +} > > + > > +static uint32_t > > +find_an_empty_item(struct gro_tcp4_tbl *tbl) > > +{ > > + uint32_t i; > > + > > + for (i =3D 0; i < tbl->max_item_num; i++) > > + if (tbl->items[i].pkt =3D=3D NULL) > > + return i; > > + return INVALID_ARRAY_INDEX; > > +} > > + > > +static uint32_t > > +find_an_empty_key(struct gro_tcp4_tbl *tbl) > > +{ > > + uint32_t i; > > + > > + for (i =3D 0; i < tbl->max_key_num; i++) > > + if (tbl->keys[i].is_valid =3D=3D 0) > > + return i; > > + return INVALID_ARRAY_INDEX; > > +} > > + > > +int32_t > > +gro_tcp4_reassemble(struct rte_mbuf *pkt, > > + struct gro_tcp4_tbl *tbl, > > + uint32_t max_packet_size, > > + uint64_t start_time) > > +{ > > + struct ether_hdr *eth_hdr; > > + struct ipv4_hdr *ipv4_hdr; > > + struct tcp_hdr *tcp_hdr; > > + uint16_t tcp_dl, ip_id; > > + > > + struct tcp4_key key; > > + uint32_t cur_idx, prev_idx, item_idx; > > + uint32_t i, key_idx; > > + int cmp; > > + > > + eth_hdr =3D rte_pktmbuf_mtod(pkt, struct ether_hdr *); > > + ipv4_hdr =3D (struct ipv4_hdr *)((char *)eth_hdr + pkt->l2_len); > > + > > + /* check if the packet should be processed */ > > + if (pkt->l3_len < sizeof(struct ipv4_hdr)) > > + return -1; >=20 > Unnecessary precheck as you already checked it's a valid IPv4 + TCP > packet in the upper gro framework. Thanks, I will remove it. >=20 > > + tcp_hdr =3D (struct tcp_hdr *)((char *)ipv4_hdr + pkt->l3_len); > > + /* if SYN, FIN, RST, or URG is set, return immediately */ > > + if ((tcp_hdr->tcp_flags & (~((uint8_t)TCP_ACK_FLAG))) || > > + ((tcp_hdr->tcp_flags & TCP_ACK_FLAG) =3D=3D 0)) >=20 > This can be simplified to: if (tcp_hdr->tcp_flags !=3D TCP_ACK_FLAG) >=20 > And in the above comment, we state that CWR and ECE is also not in the > scope. Thanks, I will change it. >=20 > > + return -1; > > + tcp_dl =3D rte_be_to_cpu_16(ipv4_hdr->total_length) - pkt->l3_len > > + - pkt->l4_len; > > + if (tcp_dl =3D=3D 0) > > + return -1; > > + > > + ip_id =3D rte_be_to_cpu_16(ipv4_hdr->packet_id); > > + > > + /* find a key and traverse all packets in its item group */ > > + key.eth_saddr =3D eth_hdr->s_addr; > > + key.eth_daddr =3D eth_hdr->d_addr; > > + key.ip_src_addr =3D rte_be_to_cpu_32(ipv4_hdr->src_addr); > > + key.ip_dst_addr =3D rte_be_to_cpu_32(ipv4_hdr->dst_addr); > > + key.src_port =3D rte_be_to_cpu_16(tcp_hdr->src_port); > > + key.dst_port =3D rte_be_to_cpu_16(tcp_hdr->dst_port); > > + key.recv_ack =3D rte_be_to_cpu_32(tcp_hdr->recv_ack); >=20 > Why do we bother to care about the endian? We only use them for equality > comparison. Oh yes, it's unnecessary to change the endian here. I will modify it. >=20 > > + > > + for (i =3D 0; i < tbl->max_key_num; i++) { > > + /* search for a key */ > > + if ((tbl->keys[i].is_valid =3D=3D 0) || > > + (memcmp(&(tbl->keys[i].key), &key, > > + sizeof(struct tcp4_key)) !=3D > 0)) > > + continue; >=20 > Please put below code out of the for loop to reduce indent. And further, > better to keep this flow identification into an inline function as I > think finally we will optimize this when there are lots of flows. >=20 > As Stephen suggested about the memset, I think it also applies to memcpy > and memcmp here and anywhere for the key. Thanks, I will use an inline function to compare key. >=20 > > + > > + cur_idx =3D tbl->keys[i].start_index; > > + prev_idx =3D cur_idx; > > + while (cur_idx !=3D INVALID_ARRAY_INDEX) { >=20 > do ... while {} can help to reduce one comparison. Thanks, I will change it. >=20 > > + cmp =3D check_seq_option(tbl->items[cur_idx].pkt, > > + tcp_hdr, >=20 >=20 > > + ip_id, > > + pkt->l4_len, > > + tcp_dl); > > + if (cmp !=3D 0) { > > + if (merge_two_tcp4_packets( > > + &(tbl- > >items[cur_idx]), > > + pkt, > > + max_packet_size, > > + cmp) > 0) > > + return 1; > > + /** > > + * fail to merge two packets since > > + * it's beyond the max packet length. > > + * Insert it into the item group. > > + */ > > + item_idx =3D find_an_empty_item(tbl); > > + if (item_idx =3D=3D INVALID_ARRAY_INDEX) > > + return -1; > > + tbl->items[prev_idx].next_pkt_idx =3D > item_idx; > > + tbl->items[item_idx].pkt =3D pkt; > > + tbl->items[item_idx].lastseg =3D > > + get_mbuf_lastseg(pkt); > > + tbl->items[item_idx].next_pkt_idx =3D > > + INVALID_ARRAY_INDEX; > > + tbl->items[item_idx].start_time =3D start_time; > > + tbl->item_num++; >=20 > Above code piece will be duplicated in the following. Please abstract > them into a function named, for example, gro_tcp4_add_new_item(). Thanks, I will modify it. >=20 > > + return 0; > > + } > > + prev_idx =3D cur_idx; > > + cur_idx =3D tbl->items[cur_idx].next_pkt_idx; > > + } > > + /** > > + * find a corresponding item group but fails to find > > + * one packet to merge. Insert it into this item group. > > + */ > > + item_idx =3D find_an_empty_item(tbl); > > + if (item_idx =3D=3D INVALID_ARRAY_INDEX) > > + return -1; > > + tbl->items[prev_idx].next_pkt_idx =3D item_idx; > > + tbl->items[item_idx].pkt =3D pkt; > > + tbl->items[item_idx].lastseg =3D > > + get_mbuf_lastseg(pkt); > > + tbl->items[item_idx].next_pkt_idx =3D INVALID_ARRAY_INDEX; > > + tbl->items[item_idx].start_time =3D start_time; > > + tbl->item_num++; > > + return 0; > > + } > > + > > + /** > > + * merge fail as the given packet has > > + * a new key. So insert a new key. > > + */ > > + item_idx =3D find_an_empty_item(tbl); > > + key_idx =3D find_an_empty_key(tbl); > > + /** > > + * if current key or item number is greater than the max > > + * value, don't insert the packet into the table and return > > + * immediately. > > + */ > > + if (item_idx =3D=3D INVALID_ARRAY_INDEX || > > + key_idx =3D=3D INVALID_ARRAY_INDEX) > > + return -1; > > + tbl->items[item_idx].pkt =3D pkt; > > + tbl->items[item_idx].lastseg =3D get_mbuf_lastseg(pkt); > > + tbl->items[item_idx].next_pkt_idx =3D INVALID_ARRAY_INDEX; > > + tbl->items[item_idx].start_time =3D start_time; > > + tbl->item_num++; > > + > > + memcpy(&(tbl->keys[key_idx].key), > > + &key, sizeof(struct tcp4_key)); > > + tbl->keys[key_idx].start_index =3D item_idx; > > + tbl->keys[key_idx].is_valid =3D 1; > > + tbl->key_num++; > > + > > + return 0; > > +} > > + > > +uint16_t > > +gro_tcp4_tbl_timeout_flush(struct gro_tcp4_tbl *tbl, > > + uint64_t timeout_cycles, > > + struct rte_mbuf **out, > > + uint16_t nb_out) > > +{ > > + uint16_t k =3D 0; > > + uint32_t i, j; > > + uint64_t current_time; > > + > > + current_time =3D rte_rdtsc(); > > + for (i =3D 0; i < tbl->max_key_num; i++) { > > + /* all keys have been checked, return immediately */ > > + if (tbl->key_num =3D=3D 0) > > + return k; > > + > > + if (tbl->keys[i].is_valid =3D=3D 0) > > + continue; > > + > > + j =3D tbl->keys[i].start_index; > > + while (j !=3D INVALID_ARRAY_INDEX) { > > + if (current_time - tbl->items[j].start_time >=3D > > + timeout_cycles) { > > + out[k++] =3D tbl->items[j].pkt; > > + tbl->items[j].pkt =3D NULL; > > + tbl->item_num--; > > + j =3D tbl->items[j].next_pkt_idx; > > + > > + /** > > + * delete the key as all of > > + * its packets are flushed. > > + */ > > + if (j =3D=3D INVALID_ARRAY_INDEX) { > > + tbl->keys[i].is_valid =3D 0; > > + tbl->key_num--; > > + } else > > + /* update start_index of the key */ > > + tbl->keys[i].start_index =3D j; > > + > > + if (k =3D=3D nb_out) > > + return k; > > + } else > > + /** > > + * left packets of this key won't be > > + * timeout, so go to check other keys. > > + */ > > + break; > > + } > > + } > > + return k; > > +} > > + > > +uint32_t gro_tcp4_tbl_item_num(void *tbl) > > +{ > > + struct gro_tcp4_tbl *gro_tbl =3D (struct gro_tcp4_tbl *)tbl; > > + > > + if (gro_tbl) > > + return gro_tbl->item_num; > > + return 0; > > +} > > diff --git a/lib/librte_gro/rte_gro_tcp4.h b/lib/librte_gro/rte_gro_tcp= 4.h > > new file mode 100644 > > index 0000000..27856f8 > > --- /dev/null > > +++ b/lib/librte_gro/rte_gro_tcp4.h > > @@ -0,0 +1,172 @@ > > +/*- > > + * BSD LICENSE > > + * > > + * Copyright(c) 2017 Intel Corporation. All rights reserved. > > + * > > + * Redistribution and use in source and binary forms, with or withou= t > > + * modification, are permitted provided that the following condition= s > > + * are met: > > + * > > + * * Redistributions of source code must retain the above copyrigh= t > > + * notice, this list of conditions and the following disclaimer. > > + * * Redistributions in binary form must reproduce the above copyr= ight > > + * notice, this list of conditions and the following disclaimer = in > > + * the documentation and/or other materials provided with the > > + * distribution. > > + * * Neither the name of Intel Corporation nor the names of its > > + * contributors may be used to endorse or promote products deriv= ed > > + * from this software without specific prior written permission. > > + * > > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND > CONTRIBUTORS > > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT > NOT > > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND > FITNESS FOR > > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT > > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, > > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, > BUT NOT > > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; > LOSS OF USE, > > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED > AND ON ANY > > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR > TORT > > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT > OF THE USE > > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > DAMAGE. > > + */ > > + > > +#ifndef _RTE_GRO_TCP4_H_ > > +#define _RTE_GRO_TCP4_H_ > > + > > +#define INVALID_ARRAY_INDEX 0xffffffffUL > > +#define GRO_TCP4_TBL_MAX_ITEM_NUM (UINT32_MAX - 1) > > + > > +/* criteria of mergeing packets */ > > +struct tcp4_key { > > + struct ether_addr eth_saddr; > > + struct ether_addr eth_daddr; >=20 > Why we need to keep ether addr in the key? Suppose we are running a > bonding port combined with phy port 0 and phy port1. Then no matter > which dst mac addr is used, these packets belongs to the same flow. >=20 Currently, we can't handle this case. And as I know, linux also checks ethernet addresses. You mean we need to handle this case? > > + uint32_t ip_src_addr; > > + uint32_t ip_dst_addr; > > + > > + uint32_t recv_ack; /**< acknowledgment sequence number. */ > > + uint16_t src_port; > > + uint16_t dst_port; > > +}; > > + > > +struct gro_tcp4_key { > > + struct tcp4_key key; > > + uint32_t start_index; /**< the first packet index of the flow */ > > + uint8_t is_valid; > > +}; > > + > > +struct gro_tcp4_item { > > + struct rte_mbuf *pkt; /**< packet address. */ > > + struct rte_mbuf *lastseg; /**< last segment of the packet */ > > + /* the time when the packet in added into the table */ > > + uint64_t start_time; >=20 > I think the comment should be the time of the eldest packet in this > item. And an explicit unit statement would be useful for understanding. Thanks, I will modify it. >=20 > > + uint32_t next_pkt_idx; /**< next packet index. */ >=20 > The comment is not good enough. We use this field to chain all packets > belonging to the same flow but not mergeable in sequence. Thanks, I will change the comment. >=20 > What's more, if we change this field to pointer, we can make this > structure universal for all gro engines, for example: >=20 > struct gro_item { > struct gro_item *next; > struct rte_mbuf *first_seg; > struct rte_mbuf *last_seg; > uint64_t start_time; > }; >=20 > And I think if we store two more fields, seq_begin, and seq_end, in the > item, we can avoid touching any mbufs' metadata. Yes, thanks. >=20 > > +}; > > + > > +/** > > + * TCP/IPv4 reassembly table. > > + */ > > +struct gro_tcp4_tbl { > > + struct gro_tcp4_item *items; /**< item array */ > > + struct gro_tcp4_key *keys; /**< key array */ > > + uint32_t item_num; /**< current item number */ > > + uint32_t key_num; /**< current key num */ > > + uint32_t max_item_num; /**< item array size */ > > + uint32_t max_key_num; /**< key array size */ > > +}; >=20 > This structure could be universal for all gro engines; >=20 > struct gro_tbl { > void *items; /* will use different initializer for different gro > engines */ > void *keys; /* will use different intializer for different gro > engines */ > uint32_t item_num; > ... > } But I still think it's not a good idea to define a same table structure for all GRO types, since we only have TCP/IPv4 GRO now and other GRO types may use different reassembly algorithm. >=20 > > + > > +/** > > + * This function creates a TCP/IPv4 reassembly table. > > + * > > + * @param socket_id > > + * socket index where the Ethernet port connects to. > > + * @param max_flow_num > > + * the maximum number of flows in the TCP/IPv4 GRO table > > + * @param max_item_per_flow > > + * the maximum packet number per flow. > > + * @return > > + * if create successfully, return a pointer which points to the > > + * created TCP/IPv4 GRO table. Otherwise, return NULL. > > + */ > > +void *gro_tcp4_tbl_create(uint16_t socket_id, > > + uint16_t max_flow_num, > > + uint16_t max_item_per_flow); >=20 > Fix the alignment. Thanks, I will modify it. >=20 > > + > > +/** > > + * This function destroys a TCP/IPv4 reassembly table. > > + * @param tbl > > + * a pointer points to the TCP/IPv4 reassembly table. > > + */ > > +void gro_tcp4_tbl_destroy(void *tbl); > > + > > +/** > > + * This function searches for a packet in the TCP/IPv4 reassembly tabl= e > > + * to merge with the inputted one. To merge two packets is to chain th= em > > + * together and update packet headers. Packets, whose SYN, FIN, RST, > PSH > > + * or URG bit is set, are returned immediately. Packets which only hav= e > > + * packet headers (i.e. without data) are also returned immediately. > > + * Otherwise, the packet is either merged, or inserted into the table. > > + * Besides, if there is no available space to insert the packet, this > > + * function returns immediately too. > > + * > > + * This function assumes the inputted packet is with correct IPv4 and > > + * TCP checksums. And if two packets are merged, it won't re-calculate > > + * IPv4 and TCP checksums. Besides, if the inputted packet is IP > > + * fragmented, it assumes the packet is complete (with TCP header). > > + * > > + * @param pkt > > + * packet to reassemble. > > + * @param tbl > > + * a pointer that points to a TCP/IPv4 reassembly table. > > + * @param max_packet_size > > + * max packet length after merged > > + * @start_time > > + * the start time that the packet is inserted into the table > > + * @return > > + * if the packet doesn't have data, or SYN, FIN, RST, PSH or URG bit = is > > + * set, or there is no available space in the table to insert a new > > + * item or a new key, return a negative value. If the packet is merge= d > > + * successfully, return an positive value. If the packet is inserted > > + * into the table, return 0. > > + */ > > +int32_t > > +gro_tcp4_reassemble(struct rte_mbuf *pkt, > > + struct gro_tcp4_tbl *tbl, > > + uint32_t max_packet_size, > > + uint64_t start_time); >=20 > Ditto. >=20 > > + > > +/** > > + * This function flushes timeout packets in a TCP/IPv4 reassembly tabl= e > > + * to applications, and without updating checksums for merged packets. > > + * The max number of flushed timeout packets is the element number of > > + * the array which is used to keep flushed packets. > > + * > > + * @param tbl > > + * a pointer that points to a TCP GRO table. > > + * @param timeout_cycles > > + * the maximum time that packets can stay in the table. > > + * @param out > > + * pointer array which is used to keep flushed packets. > > + * @param nb_out > > + * the element number of out. It's also the max number of timeout > > + * packets that can be flushed finally. > > + * @return > > + * the number of packets that are returned. > > + */ > > +uint16_t > > +gro_tcp4_tbl_timeout_flush(struct gro_tcp4_tbl *tbl, > > + uint64_t timeout_cycles, > > + struct rte_mbuf **out, > > + uint16_t nb_out); >=20 > Ditto. >=20 > > + > > +/** > > + * This function returns the number of the packets in a TCP/IPv4 > > + * reassembly table. > > + * > > + * @param tbl > > + * pointer points to a TCP/IPv4 reassembly table. > > + * @return > > + * the number of packets in the table > > + */ > > +uint32_t > > +gro_tcp4_tbl_item_num(void *tbl); > > +#endif