get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/26051/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 26051,
    "url": "https://patches.dpdk.org/api/patches/26051/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/1498805618-63649-3-git-send-email-jiayu.hu@intel.com/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<1498805618-63649-3-git-send-email-jiayu.hu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1498805618-63649-3-git-send-email-jiayu.hu@intel.com",
    "date": "2017-06-30T06:53:37",
    "name": "[dpdk-dev,v9,2/3] lib/gro: add TCP/IPv4 GRO support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "7ec5900dfb435814876b944e6728fb49783d7433",
    "submitter": {
        "id": 539,
        "url": "https://patches.dpdk.org/api/people/539/?format=api",
        "name": "Hu, Jiayu",
        "email": "jiayu.hu@intel.com"
    },
    "delegate": null,
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/1498805618-63649-3-git-send-email-jiayu.hu@intel.com/mbox/",
    "series": [],
    "comments": "https://patches.dpdk.org/api/patches/26051/comments/",
    "check": "fail",
    "checks": "https://patches.dpdk.org/api/patches/26051/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [IPv6:::1])\n\tby dpdk.org (Postfix) with ESMTP id 24B025689;\n\tFri, 30 Jun 2017 08:52:34 +0200 (CEST)",
            "from mga14.intel.com (mga14.intel.com [192.55.52.115])\n\tby dpdk.org (Postfix) with ESMTP id CACF64C57\n\tfor <dev@dpdk.org>; Fri, 30 Jun 2017 08:52:19 +0200 (CEST)",
            "from orsmga002.jf.intel.com ([10.7.209.21])\n\tby fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t29 Jun 2017 23:52:19 -0700",
            "from dpdk15.sh.intel.com ([10.67.111.77])\n\tby orsmga002.jf.intel.com with ESMTP; 29 Jun 2017 23:52:17 -0700"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.40,285,1496127600\"; d=\"scan'208\";a=\"105519007\"",
        "From": "Jiayu Hu <jiayu.hu@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "stephen@networkplumber.org, konstantin.ananyev@intel.com,\n\tjianfeng.tan@intel.com, yliu@fridaylinux.org, jingjing.wu@intel.com, \n\tkeith.wiles@intel.com, tiwei.bie@intel.com, lei.a.yao@intel.com,\n\tJiayu Hu <jiayu.hu@intel.com>",
        "Date": "Fri, 30 Jun 2017 14:53:37 +0800",
        "Message-Id": "<1498805618-63649-3-git-send-email-jiayu.hu@intel.com>",
        "X-Mailer": "git-send-email 2.7.4",
        "In-Reply-To": "<1498805618-63649-1-git-send-email-jiayu.hu@intel.com>",
        "References": "<1498733940-117800-1-git-send-email-jiayu.hu@intel.com>\n\t<1498805618-63649-1-git-send-email-jiayu.hu@intel.com>",
        "Subject": "[dpdk-dev] [PATCH v9 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 <dev.dpdk.org>",
        "List-Unsubscribe": "<http://dpdk.org/ml/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://dpdk.org/ml/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<http://dpdk.org/ml/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "In this patch, we introduce five APIs to support TCP/IPv4 GRO.\n- gro_tcp_tbl_create: create a TCP reassembly table, which is used to\n    merge packets.\n- gro_tcp_tbl_destroy: free memory space of a TCP reassembly table.\n- gro_tcp_tbl_timeout_flush: flush timeout packets from a TCP\n    reassembly table.\n- gro_tcp_tbl_item_num: return the number of packets in a TCP reassembly\n    table.\n- gro_tcp4_reassemble: reassemble an inputted TCP/IPv4 packet.\n\nTCP/IPv4 GRO API assumes all inputted packets are with correct IPv4\nand TCP checksums. And TCP/IPv4 GRO API doesn't update IPv4 and TCP\nchecksums for merged packets. If inputted packets are IP fragmented,\nTCP/IPv4 GRO API assumes they are complete packets (i.e. with L4\nheaders).\n\nIn TCP GRO, we use a table structure, called TCP reassembly table, to\nreassemble packets. Both TCP/IPv4 and TCP/IPv6 GRO use the same table\nstructure. A TCP reassembly table includes a key array and a item array,\nwhere the key array keeps the criteria to merge packets and the item\narray keeps packet information.\n\nOne key in the key array points to an item group, which consists of\npackets which have the same criteria value. If two packets are able to\nmerge, they must be in the same item group. Each key in the key array\nincludes two parts:\n- criteria: the criteria of merging packets. If two packets can be\n    merged, they must have the same criteria value.\n- start_index: the index of the first incoming packet of the item group.\n\nEach element in the item array keeps the information of one packet. It\nmainly includes two parts:\n- pkt: packet address\n- next_pkt_index: the index of the next packet in the same item group.\n    All packets in the same item group are chained by next_pkt_index.\n    With next_pkt_index, we can locate all packets in the same item\n    group one by one.\n\nTo process an incoming packet needs three steps:\na. check if the packet should be processed. Packets with the following\n    properties won't be processed:\n\t- packets without data (e.g. SYN, SYN-ACK)\nb. traverse the key array to find a key which has the same criteria\n    value with the incoming packet. If find, goto step c. Otherwise,\n    insert a new key and insert the packet into the item array.\nc. locate the first packet in the item group via the start_index in the\n    key. Then traverse all packets in the item group via next_pkt_index.\n    If find one packet which can merge with the incoming one, merge them\n    together. If can't find, insert the packet into this item group.\n\nSigned-off-by: Jiayu Hu <jiayu.hu@intel.com>\n---\n doc/guides/rel_notes/release_17_08.rst |   7 +\n lib/librte_gro/Makefile                |   1 +\n lib/librte_gro/rte_gro.c               | 123 ++++++++--\n lib/librte_gro/rte_gro.h               |   6 +-\n lib/librte_gro/rte_gro_tcp.c           | 395 +++++++++++++++++++++++++++++++++\n lib/librte_gro/rte_gro_tcp.h           | 172 ++++++++++++++\n 6 files changed, 690 insertions(+), 14 deletions(-)\n create mode 100644 lib/librte_gro/rte_gro_tcp.c\n create mode 100644 lib/librte_gro/rte_gro_tcp.h",
    "diff": "diff --git a/doc/guides/rel_notes/release_17_08.rst b/doc/guides/rel_notes/release_17_08.rst\nindex 842f46f..f067247 100644\n--- a/doc/guides/rel_notes/release_17_08.rst\n+++ b/doc/guides/rel_notes/release_17_08.rst\n@@ -75,6 +75,13 @@ New Features\n \n   Added support for firmwares with multiple Ethernet ports per physical port.\n \n+* **Add Generic Receive Offload API support.**\n+\n+  Generic Receive Offload (GRO) API supports to reassemble TCP/IPv4\n+  packets. GRO API assumes all inputted packets are with correct\n+  checksums. GRO API doesn't update checksums for merged packets. If\n+  inputted packets are IP fragmented, GRO API assumes they are complete\n+  packets (i.e. with L4 headers).\n \n Resolved Issues\n ---------------\ndiff --git a/lib/librte_gro/Makefile b/lib/librte_gro/Makefile\nindex 7e0f128..e89344d 100644\n--- a/lib/librte_gro/Makefile\n+++ b/lib/librte_gro/Makefile\n@@ -43,6 +43,7 @@ LIBABIVER := 1\n \n # source files\n SRCS-$(CONFIG_RTE_LIBRTE_GRO) += rte_gro.c\n+SRCS-$(CONFIG_RTE_LIBRTE_GRO) += rte_gro_tcp.c\n \n # install this header file\n SYMLINK-$(CONFIG_RTE_LIBRTE_GRO)-include += rte_gro.h\ndiff --git a/lib/librte_gro/rte_gro.c b/lib/librte_gro/rte_gro.c\nindex 648835b..993cf29 100644\n--- a/lib/librte_gro/rte_gro.c\n+++ b/lib/librte_gro/rte_gro.c\n@@ -32,8 +32,11 @@\n \n #include <rte_malloc.h>\n #include <rte_mbuf.h>\n+#include <rte_cycles.h>\n+#include <rte_ethdev.h>\n \n #include \"rte_gro.h\"\n+#include \"rte_gro_tcp.h\"\n \n typedef void *(*gro_tbl_create_fn)(uint16_t socket_id,\n \t\tuint16_t max_flow_num,\n@@ -41,9 +44,12 @@ typedef void *(*gro_tbl_create_fn)(uint16_t socket_id,\n typedef void (*gro_tbl_destroy_fn)(void *tbl);\n typedef uint32_t (*gro_tbl_item_num_fn)(void *tbl);\n \n-static gro_tbl_create_fn tbl_create_functions[RTE_GRO_TYPE_MAX_NUM];\n-static gro_tbl_destroy_fn tbl_destroy_functions[RTE_GRO_TYPE_MAX_NUM];\n-static gro_tbl_item_num_fn tbl_item_num_functions[RTE_GRO_TYPE_MAX_NUM];\n+static gro_tbl_create_fn tbl_create_functions[RTE_GRO_TYPE_MAX_NUM] = {\n+\tgro_tcp_tbl_create, NULL};\n+static gro_tbl_destroy_fn tbl_destroy_functions[RTE_GRO_TYPE_MAX_NUM] = {\n+\tgro_tcp_tbl_destroy, NULL};\n+static gro_tbl_item_num_fn tbl_item_num_functions[\n+\tRTE_GRO_TYPE_MAX_NUM] = {gro_tcp_tbl_item_num, NULL};\n \n /**\n  * GRO table, which is used to merge packets. It keeps many reassembly\n@@ -130,27 +136,118 @@ void rte_gro_tbl_destroy(void *tbl)\n }\n \n uint16_t\n-rte_gro_reassemble_burst(struct rte_mbuf **pkts __rte_unused,\n+rte_gro_reassemble_burst(struct rte_mbuf **pkts,\n \t\tuint16_t nb_pkts,\n-\t\tconst struct rte_gro_param *param __rte_unused)\n+\t\tconst struct rte_gro_param *param)\n {\n-\treturn nb_pkts;\n+\tuint16_t i;\n+\tuint16_t nb_after_gro = nb_pkts;\n+\tuint32_t item_num;\n+\n+\t/* allocate a reassembly table for TCP/IPv4 GRO */\n+\tstruct gro_tcp_tbl tcp_tbl;\n+\tstruct gro_tcp_key tcp_keys[RTE_GRO_MAX_BURST_ITEM_NUM] = {0};\n+\tstruct gro_tcp_item tcp_items[RTE_GRO_MAX_BURST_ITEM_NUM] = {0};\n+\n+\tstruct rte_mbuf *unprocess_pkts[nb_pkts];\n+\tuint16_t unprocess_num = 0;\n+\tint32_t ret;\n+\tuint64_t current_time;\n+\n+\tif ((param->desired_gro_types & RTE_GRO_TCP_IPV4) == 0)\n+\t\treturn nb_pkts;\n+\n+\t/* get the actual number of items */\n+\titem_num = RTE_MIN(nb_pkts, (param->max_flow_num *\n+\t\t\tparam->max_item_per_flow));\n+\titem_num = RTE_MIN(item_num, RTE_GRO_MAX_BURST_ITEM_NUM);\n+\n+\ttcp_tbl.keys = tcp_keys;\n+\ttcp_tbl.items = tcp_items;\n+\ttcp_tbl.key_num = 0;\n+\ttcp_tbl.item_num = 0;\n+\ttcp_tbl.max_key_num = item_num;\n+\ttcp_tbl.max_item_num = item_num;\n+\n+\tcurrent_time = rte_rdtsc();\n+\n+\tfor (i = 0; i < nb_pkts; i++) {\n+\t\tif (RTE_ETH_IS_IPV4_HDR(pkts[i]->packet_type) &&\n+\t\t\t\t(pkts[i]->packet_type & RTE_PTYPE_L4_TCP)) {\n+\t\t\tret = gro_tcp4_reassemble(pkts[i],\n+\t\t\t\t\t&tcp_tbl,\n+\t\t\t\t\tparam->max_packet_size,\n+\t\t\t\t\tcurrent_time);\n+\t\t\tif (ret > 0)\n+\t\t\t\t/* merge successfully */\n+\t\t\t\tnb_after_gro--;\n+\t\t\telse if (ret < 0)\n+\t\t\t\tunprocess_pkts[unprocess_num++] =\n+\t\t\t\t\tpkts[i];\n+\t\t} else\n+\t\t\tunprocess_pkts[unprocess_num++] =\n+\t\t\t\tpkts[i];\n+\t}\n+\n+\t/* re-arrange GROed packets */\n+\tif (nb_after_gro < nb_pkts) {\n+\t\ti = gro_tcp_tbl_timeout_flush(&tcp_tbl, 0,\n+\t\t\t\tpkts, nb_pkts);\n+\t\tif (unprocess_num > 0)\n+\t\t\tmemcpy(&pkts[i], unprocess_pkts,\n+\t\t\t\t\tsizeof(struct rte_mbuf *) *\n+\t\t\t\t\tunprocess_num);\n+\t}\n+\treturn nb_after_gro;\n }\n \n uint16_t\n-rte_gro_reassemble(struct rte_mbuf **pkts __rte_unused,\n+rte_gro_reassemble(struct rte_mbuf **pkts,\n \t\tuint16_t nb_pkts,\n-\t\tvoid *tbl __rte_unused)\n+\t\tvoid *tbl)\n {\n-\treturn nb_pkts;\n+\tuint16_t i, unprocess_num = 0;\n+\tstruct rte_mbuf *unprocess_pkts[nb_pkts];\n+\tstruct gro_tbl *gro_tbl = (struct gro_tbl *)tbl;\n+\tuint64_t current_time;\n+\n+\tif ((gro_tbl->desired_gro_types & RTE_GRO_TCP_IPV4) == 0)\n+\t\treturn nb_pkts;\n+\n+\tcurrent_time = rte_rdtsc();\n+\tfor (i = 0; i < nb_pkts; i++) {\n+\t\tif (RTE_ETH_IS_IPV4_HDR(pkts[i]->packet_type) &&\n+\t\t\t\t(pkts[i]->packet_type & RTE_PTYPE_L4_TCP)) {\n+\t\t\tif (gro_tcp4_reassemble(pkts[i],\n+\t\t\t\t\t\tgro_tbl->tbls[RTE_GRO_TCP_IPV4_INDEX],\n+\t\t\t\t\t\tgro_tbl->max_packet_size,\n+\t\t\t\t\t\tcurrent_time) < 0)\n+\t\t\t\tunprocess_pkts[unprocess_num++] = pkts[i];\n+\t\t} else\n+\t\t\tunprocess_pkts[unprocess_num++] = pkts[i];\n+\t}\n+\tif (unprocess_num > 0)\n+\t\tmemcpy(pkts, unprocess_pkts,\n+\t\t\t\tsizeof(struct rte_mbuf *) * unprocess_num);\n+\n+\treturn unprocess_num;\n }\n \n uint16_t\n-rte_gro_timeout_flush(void *tbl __rte_unused,\n-\t\tuint64_t desired_gro_types __rte_unused,\n-\t\tstruct rte_mbuf **out __rte_unused,\n-\t\tuint16_t max_nb_out __rte_unused)\n+rte_gro_timeout_flush(void *tbl,\n+\t\tuint64_t desired_gro_types,\n+\t\tstruct rte_mbuf **out,\n+\t\tuint16_t max_nb_out)\n {\n+\tstruct gro_tbl *gro_tbl = (struct gro_tbl *)tbl;\n+\n+\tdesired_gro_types = desired_gro_types &\n+\t\tgro_tbl->desired_gro_types;\n+\tif (desired_gro_types & RTE_GRO_TCP_IPV4)\n+\t\treturn gro_tcp_tbl_timeout_flush(\n+\t\t\t\tgro_tbl->tbls[RTE_GRO_TCP_IPV4_INDEX],\n+\t\t\t\tgro_tbl->max_timeout_cycles,\n+\t\t\t\tout, max_nb_out);\n \treturn 0;\n }\n \ndiff --git a/lib/librte_gro/rte_gro.h b/lib/librte_gro/rte_gro.h\nindex 02c9113..2cd06ee 100644\n--- a/lib/librte_gro/rte_gro.h\n+++ b/lib/librte_gro/rte_gro.h\n@@ -45,7 +45,11 @@ extern \"C\" {\n \n /* max number of supported GRO types */\n #define RTE_GRO_TYPE_MAX_NUM 64\n-#define RTE_GRO_TYPE_SUPPORT_NUM 0\t/**< current supported GRO num */\n+#define RTE_GRO_TYPE_SUPPORT_NUM 1\t/**< current supported GRO num */\n+\n+/* TCP/IPv4 GRO flag */\n+#define RTE_GRO_TCP_IPV4_INDEX 0\n+#define RTE_GRO_TCP_IPV4 (1ULL << RTE_GRO_TCP_IPV4_INDEX)\n \n \n struct rte_gro_param {\ndiff --git a/lib/librte_gro/rte_gro_tcp.c b/lib/librte_gro/rte_gro_tcp.c\nnew file mode 100644\nindex 0000000..cf5cea2\n--- /dev/null\n+++ b/lib/librte_gro/rte_gro_tcp.c\n@@ -0,0 +1,395 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright(c) 2017 Intel Corporation. All rights reserved.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of Intel Corporation nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <rte_malloc.h>\n+#include <rte_mbuf.h>\n+#include <rte_cycles.h>\n+\n+#include <rte_ethdev.h>\n+#include <rte_ip.h>\n+#include <rte_tcp.h>\n+\n+#include \"rte_gro_tcp.h\"\n+\n+void *gro_tcp_tbl_create(uint16_t socket_id,\n+\t\tuint16_t max_flow_num,\n+\t\tuint16_t max_item_per_flow)\n+{\n+\tsize_t size;\n+\tuint32_t entries_num;\n+\tstruct gro_tcp_tbl *tbl;\n+\n+\tentries_num = max_flow_num * max_item_per_flow;\n+\tentries_num = entries_num > GRO_TCP_TBL_MAX_ITEM_NUM ?\n+\t\tGRO_TCP_TBL_MAX_ITEM_NUM : entries_num;\n+\n+\tif (entries_num == 0)\n+\t\treturn NULL;\n+\n+\ttbl = (struct gro_tcp_tbl *)rte_zmalloc_socket(\n+\t\t\t__func__,\n+\t\t\tsizeof(struct gro_tcp_tbl),\n+\t\t\tRTE_CACHE_LINE_SIZE,\n+\t\t\tsocket_id);\n+\tif (tbl == NULL)\n+\t\treturn NULL;\n+\n+\tsize = sizeof(struct gro_tcp_item) * entries_num;\n+\ttbl->items = (struct gro_tcp_item *)rte_zmalloc_socket(\n+\t\t\t__func__,\n+\t\t\tsize,\n+\t\t\tRTE_CACHE_LINE_SIZE,\n+\t\t\tsocket_id);\n+\tif (tbl->items == NULL) {\n+\t\trte_free(tbl);\n+\t\treturn NULL;\n+\t}\n+\ttbl->max_item_num = entries_num;\n+\n+\tsize = sizeof(struct gro_tcp_key) * entries_num;\n+\ttbl->keys = (struct gro_tcp_key *)rte_zmalloc_socket(\n+\t\t\t__func__,\n+\t\t\tsize, RTE_CACHE_LINE_SIZE,\n+\t\t\tsocket_id);\n+\tif (tbl->keys == NULL) {\n+\t\trte_free(tbl->items);\n+\t\trte_free(tbl);\n+\t\treturn NULL;\n+\t}\n+\ttbl->max_key_num = entries_num;\n+\treturn tbl;\n+}\n+\n+void gro_tcp_tbl_destroy(void *tbl)\n+{\n+\tstruct gro_tcp_tbl *tcp_tbl = (struct gro_tcp_tbl *)tbl;\n+\n+\tif (tcp_tbl) {\n+\t\trte_free(tcp_tbl->items);\n+\t\trte_free(tcp_tbl->keys);\n+\t}\n+\trte_free(tcp_tbl);\n+}\n+\n+static struct rte_mbuf *get_mbuf_lastseg(struct rte_mbuf *pkt)\n+{\n+\tstruct rte_mbuf *lastseg = pkt;\n+\n+\twhile (lastseg->next)\n+\t\tlastseg = lastseg->next;\n+\n+\treturn lastseg;\n+}\n+\n+/**\n+ * merge two TCP/IPv4 packets without updating checksums.\n+ */\n+static int\n+merge_two_tcp4_packets(struct gro_tcp_item *item_src,\n+\t\tstruct rte_mbuf *pkt,\n+\t\tuint32_t max_packet_size)\n+{\n+\tstruct ipv4_hdr *ipv4_hdr1, *ipv4_hdr2;\n+\tuint16_t tcp_dl1;\n+\tstruct rte_mbuf *pkt_src = item_src->pkt;\n+\n+\t/* parse the given packet */\n+\tipv4_hdr1 = (struct ipv4_hdr *)(rte_pktmbuf_mtod(pkt,\n+\t\t\t\tchar *) + pkt->l2_len);\n+\ttcp_dl1 = rte_be_to_cpu_16(ipv4_hdr1->total_length) -\n+\t\tpkt->l3_len - pkt->l4_len;\n+\n+\t/* parse the original packet */\n+\tipv4_hdr2 = (struct ipv4_hdr *)(rte_pktmbuf_mtod(pkt_src,\n+\t\t\t\tchar *) + pkt_src->l2_len);\n+\n+\tif (pkt_src->pkt_len + tcp_dl1 > max_packet_size)\n+\t\treturn -1;\n+\n+\t/* remove the header of the incoming packet */\n+\trte_pktmbuf_adj(pkt, pkt->l2_len + pkt->l3_len + pkt->l4_len);\n+\n+\t/* chain the two packet together and update lastseg */\n+\titem_src->lastseg->next = pkt;\n+\titem_src->lastseg = get_mbuf_lastseg(pkt);\n+\n+\t/* update IP header */\n+\tipv4_hdr2->total_length = rte_cpu_to_be_16(\n+\t\t\trte_be_to_cpu_16(\n+\t\t\t\tipv4_hdr2->total_length)\n+\t\t\t+ tcp_dl1);\n+\n+\t/* update mbuf metadata for the merged packet */\n+\tpkt_src->nb_segs += pkt->nb_segs;\n+\tpkt_src->pkt_len += pkt->pkt_len;\n+\treturn 1;\n+}\n+\n+static int\n+check_seq_option(struct rte_mbuf *pkt,\n+\t\tstruct tcp_hdr *tcp_hdr,\n+\t\tuint16_t tcp_hl)\n+{\n+\tstruct ipv4_hdr *ipv4_hdr1;\n+\tstruct tcp_hdr *tcp_hdr1;\n+\tuint16_t tcp_hl1, tcp_dl1;\n+\tuint32_t sent_seq1, sent_seq;\n+\tuint16_t len;\n+\tint ret = -1;\n+\n+\tipv4_hdr1 = (struct ipv4_hdr *)(rte_pktmbuf_mtod(pkt,\n+\t\t\t\tchar *) + pkt->l2_len);\n+\ttcp_hl1 = pkt->l4_len;\n+\ttcp_hdr1 = (struct tcp_hdr *)((char *)ipv4_hdr1 + pkt->l3_len);\n+\ttcp_dl1 = rte_be_to_cpu_16(ipv4_hdr1->total_length) -\n+\t\tpkt->l3_len - tcp_hl1;\n+\tsent_seq1 = rte_be_to_cpu_32(tcp_hdr1->sent_seq) + tcp_dl1;\n+\tsent_seq = rte_be_to_cpu_32(tcp_hdr->sent_seq);\n+\n+\t/* check if the two packets are neighbor */\n+\tif (sent_seq == sent_seq1) {\n+\t\tret = 1;\n+\t\tlen = RTE_MAX(tcp_hl, tcp_hl1) - sizeof(struct tcp_hdr);\n+\t\t/* check if TCP option field equals */\n+\t\tif ((tcp_hl1 != tcp_hl) || ((len > 0) &&\n+\t\t\t\t\t(memcmp(tcp_hdr1 + 1,\n+\t\t\t\t\t\t\ttcp_hdr + 1,\n+\t\t\t\t\t\t\tlen) != 0)))\n+\t\t\tret = -1;\n+\t}\n+\treturn ret;\n+}\n+\n+static uint32_t\n+find_an_empty_item(struct gro_tcp_tbl *tbl)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < tbl->max_item_num; i++)\n+\t\tif (tbl->items[i].pkt == NULL)\n+\t\t\treturn i;\n+\treturn INVALID_ARRAY_INDEX;\n+}\n+\n+static uint32_t\n+find_an_empty_key(struct gro_tcp_tbl *tbl)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < tbl->max_key_num; i++)\n+\t\tif (tbl->keys[i].is_valid == 0)\n+\t\t\treturn i;\n+\treturn INVALID_ARRAY_INDEX;\n+}\n+\n+int32_t\n+gro_tcp4_reassemble(struct rte_mbuf *pkt,\n+\t\tstruct gro_tcp_tbl *tbl,\n+\t\tuint32_t max_packet_size,\n+\t\tuint64_t start_time)\n+{\n+\tstruct ether_hdr *eth_hdr;\n+\tstruct ipv4_hdr *ipv4_hdr;\n+\tstruct tcp_hdr *tcp_hdr;\n+\tuint16_t tcp_dl;\n+\n+\tstruct tcp_key key;\n+\tuint32_t cur_idx, prev_idx, item_idx;\n+\tuint32_t i, key_idx;\n+\n+\teth_hdr = rte_pktmbuf_mtod(pkt, struct ether_hdr *);\n+\tipv4_hdr = (struct ipv4_hdr *)((char *)eth_hdr + pkt->l2_len);\n+\n+\t/* check if the packet should be processed */\n+\tif (pkt->l3_len < sizeof(struct ipv4_hdr))\n+\t\treturn -1;\n+\ttcp_hdr = (struct tcp_hdr *)((char *)ipv4_hdr + pkt->l3_len);\n+\ttcp_dl = rte_be_to_cpu_16(ipv4_hdr->total_length) - pkt->l3_len\n+\t\t- pkt->l4_len;\n+\tif (tcp_dl == 0)\n+\t\treturn -1;\n+\n+\t/* find a key and traverse all packets in its item group */\n+\tkey.eth_saddr = eth_hdr->s_addr;\n+\tkey.eth_daddr = eth_hdr->d_addr;\n+\tkey.ip_src_addr[0] = rte_be_to_cpu_32(ipv4_hdr->src_addr);\n+\tkey.ip_dst_addr[0] = rte_be_to_cpu_32(ipv4_hdr->dst_addr);\n+\tkey.src_port = rte_be_to_cpu_16(tcp_hdr->src_port);\n+\tkey.dst_port = rte_be_to_cpu_16(tcp_hdr->dst_port);\n+\tkey.recv_ack = rte_be_to_cpu_32(tcp_hdr->recv_ack);\n+\tkey.tcp_flags = tcp_hdr->tcp_flags;\n+\n+\tfor (i = 0; i < tbl->max_key_num; i++) {\n+\t\t/* search for a key */\n+\t\tif ((tbl->keys[i].is_valid == 0) ||\n+\t\t\t\t(memcmp(&(tbl->keys[i].key), &key,\n+\t\t\t\t\t\tsizeof(struct tcp_key)) != 0))\n+\t\t\tcontinue;\n+\n+\t\tcur_idx = tbl->keys[i].start_index;\n+\t\tprev_idx = cur_idx;\n+\t\twhile (cur_idx != INVALID_ARRAY_INDEX) {\n+\t\t\tif (check_seq_option(tbl->items[cur_idx].pkt,\n+\t\t\t\t\t\ttcp_hdr,\n+\t\t\t\t\t\tpkt->l4_len) > 0) {\n+\t\t\t\tif (merge_two_tcp4_packets(\n+\t\t\t\t\t\t\t&(tbl->items[cur_idx]),\n+\t\t\t\t\t\t\tpkt,\n+\t\t\t\t\t\t\tmax_packet_size) > 0)\n+\t\t\t\t\treturn 1;\n+\t\t\t\t/**\n+\t\t\t\t * fail to merge two packets since\n+\t\t\t\t * it's beyond the max packet length.\n+\t\t\t\t * Insert it into the item group.\n+\t\t\t\t */\n+\t\t\t\titem_idx = find_an_empty_item(tbl);\n+\t\t\t\tif (item_idx == INVALID_ARRAY_INDEX)\n+\t\t\t\t\treturn -1;\n+\t\t\t\ttbl->items[prev_idx].next_pkt_idx = item_idx;\n+\t\t\t\ttbl->items[item_idx].pkt = pkt;\n+\t\t\t\ttbl->items[item_idx].lastseg =\n+\t\t\t\t\tget_mbuf_lastseg(pkt);\n+\t\t\t\ttbl->items[item_idx].next_pkt_idx =\n+\t\t\t\t\tINVALID_ARRAY_INDEX;\n+\t\t\t\ttbl->items[item_idx].start_time = start_time;\n+\t\t\t\ttbl->item_num++;\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t\tprev_idx = cur_idx;\n+\t\t\tcur_idx = tbl->items[cur_idx].next_pkt_idx;\n+\t\t}\n+\t\t/**\n+\t\t * find a corresponding item group but fails to find\n+\t\t * one packet to merge. Insert it into this item group.\n+\t\t */\n+\t\titem_idx = find_an_empty_item(tbl);\n+\t\tif (item_idx == INVALID_ARRAY_INDEX)\n+\t\t\treturn -1;\n+\t\ttbl->items[prev_idx].next_pkt_idx = item_idx;\n+\t\ttbl->items[item_idx].pkt = pkt;\n+\t\ttbl->items[item_idx].lastseg =\n+\t\t\tget_mbuf_lastseg(pkt);\n+\t\ttbl->items[item_idx].next_pkt_idx = INVALID_ARRAY_INDEX;\n+\t\ttbl->items[item_idx].start_time = start_time;\n+\t\ttbl->item_num++;\n+\t\treturn 0;\n+\t}\n+\n+\t/**\n+\t * merge fail as the given packet has\n+\t * a new key. So insert a new key.\n+\t */\n+\titem_idx = find_an_empty_item(tbl);\n+\tkey_idx = find_an_empty_key(tbl);\n+\t/**\n+\t * if current key or item number is greater than the max\n+\t * value, don't insert the packet into the table and return\n+\t * immediately.\n+\t */\n+\tif (item_idx == INVALID_ARRAY_INDEX ||\n+\t\t\tkey_idx == INVALID_ARRAY_INDEX)\n+\t\treturn -1;\n+\ttbl->items[item_idx].pkt = pkt;\n+\ttbl->items[item_idx].lastseg = get_mbuf_lastseg(pkt);\n+\ttbl->items[item_idx].next_pkt_idx = INVALID_ARRAY_INDEX;\n+\ttbl->items[item_idx].start_time = start_time;\n+\ttbl->item_num++;\n+\n+\tmemcpy(&(tbl->keys[key_idx].key),\n+\t\t\t&key, sizeof(struct tcp_key));\n+\ttbl->keys[key_idx].start_index = item_idx;\n+\ttbl->keys[key_idx].is_valid = 1;\n+\ttbl->key_num++;\n+\n+\treturn 0;\n+}\n+\n+uint16_t\n+gro_tcp_tbl_timeout_flush(struct gro_tcp_tbl *tbl,\n+\t\tuint64_t timeout_cycles,\n+\t\tstruct rte_mbuf **out,\n+\t\tuint16_t nb_out)\n+{\n+\tuint16_t k = 0;\n+\tuint32_t i, j;\n+\tuint64_t current_time;\n+\n+\tcurrent_time = rte_rdtsc();\n+\tfor (i = 0; i < tbl->max_key_num; i++) {\n+\t\t/* all keys have been checked, return immediately */\n+\t\tif (tbl->key_num == 0)\n+\t\t\treturn k;\n+\n+\t\tif (tbl->keys[i].is_valid == 0)\n+\t\t\tcontinue;\n+\n+\t\tj = tbl->keys[i].start_index;\n+\t\twhile (j != INVALID_ARRAY_INDEX) {\n+\t\t\tif (current_time - tbl->items[j].start_time >=\n+\t\t\t\t\ttimeout_cycles) {\n+\t\t\t\tout[k++] = tbl->items[j].pkt;\n+\t\t\t\ttbl->items[j].pkt = NULL;\n+\t\t\t\ttbl->item_num--;\n+\t\t\t\tj = tbl->items[j].next_pkt_idx;\n+\n+\t\t\t\t/**\n+\t\t\t\t * delete the key as all of\n+\t\t\t\t * its packets are flushed.\n+\t\t\t\t */\n+\t\t\t\tif (j == INVALID_ARRAY_INDEX) {\n+\t\t\t\t\ttbl->keys[i].is_valid = 0;\n+\t\t\t\t\ttbl->key_num--;\n+\t\t\t\t} else\n+\t\t\t\t\t/* update start_index of the key */\n+\t\t\t\t\ttbl->keys[i].start_index = j;\n+\n+\t\t\t\tif (k == nb_out)\n+\t\t\t\t\treturn k;\n+\t\t\t} else\n+\t\t\t\t/**\n+\t\t\t\t * left packets of this key won't be\n+\t\t\t\t * timeout, so go to check other keys.\n+\t\t\t\t */\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\treturn k;\n+}\n+\n+uint32_t gro_tcp_tbl_item_num(void *tbl)\n+{\n+\tstruct gro_tcp_tbl *gro_tbl = (struct gro_tcp_tbl *)tbl;\n+\n+\tif (gro_tbl)\n+\t\treturn gro_tbl->item_num;\n+\treturn 0;\n+}\ndiff --git a/lib/librte_gro/rte_gro_tcp.h b/lib/librte_gro/rte_gro_tcp.h\nnew file mode 100644\nindex 0000000..2000318\n--- /dev/null\n+++ b/lib/librte_gro/rte_gro_tcp.h\n@@ -0,0 +1,172 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright(c) 2017 Intel Corporation. All rights reserved.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of Intel Corporation nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#ifndef _RTE_GRO_TCP_H_\n+#define _RTE_GRO_TCP_H_\n+\n+#define INVALID_ARRAY_INDEX 0xffffffffUL\n+#define GRO_TCP_TBL_MAX_ITEM_NUM (UINT32_MAX - 1)\n+\n+/* criteria of mergeing packets */\n+struct tcp_key {\n+\tstruct ether_addr eth_saddr;\n+\tstruct ether_addr eth_daddr;\n+\tuint32_t ip_src_addr[4];\t/**< IPv4 uses the first 4B */\n+\tuint32_t ip_dst_addr[4];\n+\n+\tuint32_t recv_ack;\t/**< acknowledgment sequence number. */\n+\tuint16_t src_port;\n+\tuint16_t dst_port;\n+\tuint8_t tcp_flags;\t/**< TCP flags. */\n+};\n+\n+struct gro_tcp_key {\n+\tstruct tcp_key key;\n+\tuint32_t start_index;\t/**< the first packet index of the flow */\n+\tuint8_t is_valid;\n+};\n+\n+struct gro_tcp_item {\n+\tstruct rte_mbuf *pkt;\t/**< packet address. */\n+\tstruct rte_mbuf *lastseg;\t/**< last segment of the packet */\n+\t/* the time when the packet in added into the table */\n+\tuint64_t start_time;\n+\tuint32_t next_pkt_idx;\t/**< next packet index. */\n+};\n+\n+/**\n+ * TCP reassembly table. Both TCP/IPv4 and TCP/IPv6 use the same table\n+ * structure.\n+ */\n+struct gro_tcp_tbl {\n+\tstruct gro_tcp_item *items;\t/**< item array */\n+\tstruct gro_tcp_key *keys;\t/**< key array */\n+\tuint32_t item_num;\t/**< current item number */\n+\tuint32_t key_num;\t/**< current key num */\n+\tuint32_t max_item_num;\t/**< item array size */\n+\tuint32_t max_key_num;\t/**< key array size */\n+};\n+\n+/**\n+ * This function creates a TCP reassembly table.\n+ *\n+ * @param socket_id\n+ *  socket index where the Ethernet port connects to.\n+ * @param max_flow_num\n+ *  the maximum number of flows in the TCP GRO table\n+ * @param max_item_per_flow\n+ *  the maximum packet number per flow.\n+ * @return\n+ *  if create successfully, return a pointer which points to the\n+ *  created TCP GRO table. Otherwise, return NULL.\n+ */\n+void *gro_tcp_tbl_create(uint16_t socket_id,\n+\t\tuint16_t max_flow_num,\n+\t\tuint16_t max_item_per_flow);\n+\n+/**\n+ * This function destroys a TCP reassembly table.\n+ * @param tbl\n+ *  a pointer points to the TCP reassembly table.\n+ */\n+void gro_tcp_tbl_destroy(void *tbl);\n+\n+/**\n+ * This function searches for a packet in the TCP reassembly table to\n+ * merge with the inputted one. To merge two packets is to chain them\n+ * together and update packet headers. If the packet is without data\n+ * (e.g. SYN, SYN-ACK packet), this function returns immediately.\n+ * Otherwise, the packet is either merged, or inserted into the table.\n+ * Besides, if there is no available space to insert the packet, this\n+ * function returns immediately too.\n+ *\n+ * This function assumes the inputted packet is with correct IPv4 and\n+ * TCP checksums. And if two packets are merged, it won't re-calculate\n+ * IPv4 and TCP checksums. Besides, if the inputted packet is IP\n+ * fragmented, it assumes the packet is complete (with TCP header).\n+ *\n+ * @param pkt\n+ *  packet to reassemble.\n+ * @param tbl\n+ *  a pointer that points to a TCP reassembly table.\n+ * @param max_packet_size\n+ *  max packet length after merged\n+ * @start_time\n+ *  the start time that the packet is inserted into the table\n+ * @return\n+ *  if the packet doesn't have data, or there is no available space\n+ *  in the table to insert a new item or a new key, return a negative\n+ *  value. If the packet is merged successfully, return an positive\n+ *  value. If the packet is inserted into the table, return 0.\n+ */\n+int32_t\n+gro_tcp4_reassemble(struct rte_mbuf *pkt,\n+\t\tstruct gro_tcp_tbl *tbl,\n+\t\tuint32_t max_packet_size,\n+\t\tuint64_t start_time);\n+\n+/**\n+ * This function flushes timeout packets in a TCP reassembly table to\n+ * applications, and without updating checksums for merged packets.\n+ * The max number of flushed timeout packets is the element number of\n+ * the array which is used to keep flushed packets.\n+ *\n+ * @param tbl\n+ *  a pointer that points to a TCP GRO table.\n+ * @param timeout_cycles\n+ *  the maximum time that packets can stay in the table.\n+ * @param out\n+ *  pointer array which is used to keep flushed packets.\n+ * @param nb_out\n+ *  the element number of out. It's also the max number of timeout\n+ *  packets that can be flushed finally.\n+ * @return\n+ *  the number of packets that are returned.\n+ */\n+uint16_t\n+gro_tcp_tbl_timeout_flush(struct gro_tcp_tbl *tbl,\n+\t\tuint64_t timeout_cycles,\n+\t\tstruct rte_mbuf **out,\n+\t\tuint16_t nb_out);\n+\n+/**\n+ * This function returns the number of the packets in the TCP\n+ * reassembly table.\n+ *\n+ * @param tbl\n+ *  pointer points to a TCP reassembly table.\n+ * @return\n+ *  the number of packets in the table\n+ */\n+uint32_t\n+gro_tcp_tbl_item_num(void *tbl);\n+#endif\n",
    "prefixes": [
        "dpdk-dev",
        "v9",
        "2/3"
    ]
}