get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 76317,
    "url": "http://patches.dpdk.org/api/patches/76317/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200902092643.49924-3-yang_y_yi@163.com/",
    "project": {
        "id": 1,
        "url": "http://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": "<20200902092643.49924-3-yang_y_yi@163.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200902092643.49924-3-yang_y_yi@163.com",
    "date": "2020-09-02T09:26:43",
    "name": "[V3,2/2] gro: add VXLAN UDP GRO support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "b830e401254a509f0b980fe18009f0cf8766dbf4",
    "submitter": {
        "id": 1806,
        "url": "http://patches.dpdk.org/api/people/1806/?format=api",
        "name": "yang_y_yi",
        "email": "yang_y_yi@163.com"
    },
    "delegate": {
        "id": 319,
        "url": "http://patches.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20200902092643.49924-3-yang_y_yi@163.com/mbox/",
    "series": [
        {
            "id": 11900,
            "url": "http://patches.dpdk.org/api/series/11900/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=11900",
            "date": "2020-09-02T09:26:41",
            "name": "gro: add UDP GRO and VXLAN UDP GRO support",
            "version": 3,
            "mbox": "http://patches.dpdk.org/series/11900/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/76317/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/76317/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 201C9A04B7;\n\tWed,  2 Sep 2020 11:27:01 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 005EB1C0AC;\n\tWed,  2 Sep 2020 11:26:53 +0200 (CEST)",
            "from mail-m974.mail.163.com (mail-m974.mail.163.com [123.126.97.4])\n by dpdk.org (Postfix) with ESMTP id 388FB1C066\n for <dev@dpdk.org>; Wed,  2 Sep 2020 11:26:49 +0200 (CEST)",
            "from yangyi0100.home.langchao.com (unknown [111.207.123.58])\n by smtp4 (Coremail) with SMTP id HNxpCgCnFs1TZU9fhk9LNw--.108S4;\n Wed, 02 Sep 2020 17:26:47 +0800 (CST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com;\n s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=Wz+m9\n waX2MBjGfbr3Yv1Jv83xjTY0LiE0W5sQcRf9qk=; b=ZICS4ISjxiaP89UGH6+TN\n TUXG8w16ee/6fFjD0f8onijWAHcsGZE6z59n+t3ilMsstnjc9NX3vuW4qO3MUIab\n IPrC9KJe5FLCmhRk8aXy6lwz2aCmGOMBATRBEXvHnFn0+sq7M6oEtlYwaW03c32g\n LGDmq+dCFk1f44mr1FC+4Q=",
        "From": "yang_y_yi@163.com",
        "To": "dev@dpdk.org",
        "Cc": "jiayu.hu@intel.com, thomas@monjalon.net, yangyi01@inspur.com,\n yang_y_yi@163.com",
        "Date": "Wed,  2 Sep 2020 17:26:43 +0800",
        "Message-Id": "<20200902092643.49924-3-yang_y_yi@163.com>",
        "X-Mailer": "git-send-email 2.19.2.windows.1",
        "In-Reply-To": "<20200902092643.49924-1-yang_y_yi@163.com>",
        "References": "<20200902092643.49924-1-yang_y_yi@163.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-CM-TRANSID": "HNxpCgCnFs1TZU9fhk9LNw--.108S4",
        "X-Coremail-Antispam": "1Uf129KBjvAXoWfury7JryftrWrKr47tFy7ZFb_yoW5AF1Duo\n WxXws8urykCrn8tr40yr1xurW2gw40gF17Cws0gws3Xwn2yryDG393Wa1fAa17Xr4YvF4F\n vws2ga17XF43AFs7n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3\n AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjxU2VbkUUUUU",
        "X-Originating-IP": "[111.207.123.58]",
        "X-CM-SenderInfo": "51dqwsp1b1xqqrwthudrp/1tbiMxeTi1Xl6EjQ2wAAsn",
        "Subject": "[dpdk-dev] [PATCH V3 2/2] gro: add VXLAN UDP 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": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "From: Yi Yang <yangyi01@inspur.com>\n\nVXLAN UDP GRO can help improve VM-to-VM UDP performance\nwhen VM is enabled UFO or GSO, GRO must be supported if\nGSO or UFO is enabled, otherwise, performance gain will\nbe hurt.\n\nWith this enabled in DPDK, OVS DPDK can leverage it to\nimprove VM-to-VM UDP performance, this will make sure\nIP fragments will be reassembled once it is received\nfrom physical NIC. It is very helpful in OVS DPDK VXLAN\nTSO case.\n\nSigned-off-by: Yi Yang <yangyi01@inspur.com>\n---\n lib/librte_gro/Makefile         |   1 +\n lib/librte_gro/gro_vxlan_udp4.c | 556 ++++++++++++++++++++++++++++++++++++++++\n lib/librte_gro/gro_vxlan_udp4.h | 152 +++++++++++\n lib/librte_gro/meson.build      |   2 +-\n lib/librte_gro/rte_gro.c        |  75 +++++-\n lib/librte_gro/rte_gro.h        |   3 +\n 6 files changed, 782 insertions(+), 7 deletions(-)\n create mode 100644 lib/librte_gro/gro_vxlan_udp4.c\n create mode 100644 lib/librte_gro/gro_vxlan_udp4.h",
    "diff": "diff --git a/lib/librte_gro/Makefile b/lib/librte_gro/Makefile\nindex 41ec29e..30dd8c7 100644\n--- a/lib/librte_gro/Makefile\n+++ b/lib/librte_gro/Makefile\n@@ -17,6 +17,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRO) += rte_gro.c\n SRCS-$(CONFIG_RTE_LIBRTE_GRO) += gro_tcp4.c\n SRCS-$(CONFIG_RTE_LIBRTE_GRO) += gro_udp4.c\n SRCS-$(CONFIG_RTE_LIBRTE_GRO) += gro_vxlan_tcp4.c\n+SRCS-$(CONFIG_RTE_LIBRTE_GRO) += gro_vxlan_udp4.c\n \n # install this header file\n SYMLINK-$(CONFIG_RTE_LIBRTE_GRO)-include += rte_gro.h\ndiff --git a/lib/librte_gro/gro_vxlan_udp4.c b/lib/librte_gro/gro_vxlan_udp4.c\nnew file mode 100644\nindex 0000000..a66eaf0\n--- /dev/null\n+++ b/lib/librte_gro/gro_vxlan_udp4.c\n@@ -0,0 +1,556 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2020 Inspur Corporation\n+ */\n+\n+#include <rte_malloc.h>\n+#include <rte_mbuf.h>\n+#include <rte_cycles.h>\n+#include <rte_ethdev.h>\n+#include <rte_udp.h>\n+\n+#include \"gro_vxlan_udp4.h\"\n+\n+void *\n+gro_vxlan_udp4_tbl_create(uint16_t socket_id,\n+\t\tuint16_t max_flow_num,\n+\t\tuint16_t max_item_per_flow)\n+{\n+\tstruct gro_vxlan_udp4_tbl *tbl;\n+\tsize_t size;\n+\tuint32_t entries_num, i;\n+\n+\tentries_num = max_flow_num * max_item_per_flow;\n+\tentries_num = RTE_MIN(entries_num, GRO_VXLAN_UDP4_TBL_MAX_ITEM_NUM);\n+\n+\tif (entries_num == 0)\n+\t\treturn NULL;\n+\n+\ttbl = rte_zmalloc_socket(__func__,\n+\t\t\tsizeof(struct gro_vxlan_udp4_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_vxlan_udp4_item) * entries_num;\n+\ttbl->items = rte_zmalloc_socket(__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_vxlan_udp4_flow) * entries_num;\n+\ttbl->flows = rte_zmalloc_socket(__func__,\n+\t\t\tsize,\n+\t\t\tRTE_CACHE_LINE_SIZE,\n+\t\t\tsocket_id);\n+\tif (tbl->flows == NULL) {\n+\t\trte_free(tbl->items);\n+\t\trte_free(tbl);\n+\t\treturn NULL;\n+\t}\n+\n+\tfor (i = 0; i < entries_num; i++)\n+\t\ttbl->flows[i].start_index = INVALID_ARRAY_INDEX;\n+\ttbl->max_flow_num = entries_num;\n+\n+\treturn tbl;\n+}\n+\n+void\n+gro_vxlan_udp4_tbl_destroy(void *tbl)\n+{\n+\tstruct gro_vxlan_udp4_tbl *vxlan_tbl = tbl;\n+\n+\tif (vxlan_tbl) {\n+\t\trte_free(vxlan_tbl->items);\n+\t\trte_free(vxlan_tbl->flows);\n+\t}\n+\trte_free(vxlan_tbl);\n+}\n+\n+static inline uint32_t\n+find_an_empty_item(struct gro_vxlan_udp4_tbl *tbl)\n+{\n+\tuint32_t max_item_num = tbl->max_item_num, i;\n+\n+\tfor (i = 0; i < max_item_num; i++)\n+\t\tif (tbl->items[i].inner_item.firstseg == NULL)\n+\t\t\treturn i;\n+\treturn INVALID_ARRAY_INDEX;\n+}\n+\n+static inline uint32_t\n+find_an_empty_flow(struct gro_vxlan_udp4_tbl *tbl)\n+{\n+\tuint32_t max_flow_num = tbl->max_flow_num, i;\n+\n+\tfor (i = 0; i < max_flow_num; i++)\n+\t\tif (tbl->flows[i].start_index == INVALID_ARRAY_INDEX)\n+\t\t\treturn i;\n+\treturn INVALID_ARRAY_INDEX;\n+}\n+\n+static inline uint32_t\n+insert_new_item(struct gro_vxlan_udp4_tbl *tbl,\n+\t\tstruct rte_mbuf *pkt,\n+\t\tuint64_t start_time,\n+\t\tuint32_t prev_idx,\n+\t\tuint16_t frag_offset,\n+\t\tuint8_t is_last_frag,\n+\t\tuint16_t outer_ip_id,\n+\t\tuint16_t ip_id,\n+\t\tuint8_t outer_is_atomic)\n+{\n+\tuint32_t item_idx;\n+\n+\titem_idx = find_an_empty_item(tbl);\n+\tif (unlikely(item_idx == INVALID_ARRAY_INDEX))\n+\t\treturn INVALID_ARRAY_INDEX;\n+\n+\ttbl->items[item_idx].inner_item.firstseg = pkt;\n+\ttbl->items[item_idx].inner_item.lastseg = rte_pktmbuf_lastseg(pkt);\n+\ttbl->items[item_idx].inner_item.start_time = start_time;\n+\ttbl->items[item_idx].inner_item.next_pkt_idx = INVALID_ARRAY_INDEX;\n+\ttbl->items[item_idx].inner_item.frag_offset = frag_offset;\n+\ttbl->items[item_idx].inner_item.is_last_frag = is_last_frag;\n+\ttbl->items[item_idx].inner_item.ip_id = ip_id;\n+\ttbl->items[item_idx].inner_item.nb_merged = 1;\n+\ttbl->items[item_idx].outer_ip_id = outer_ip_id;\n+\ttbl->items[item_idx].outer_is_atomic = outer_is_atomic;\n+\ttbl->item_num++;\n+\n+\t/* If the previous packet exists, chain the new one with it. */\n+\tif (prev_idx != INVALID_ARRAY_INDEX) {\n+\t\ttbl->items[item_idx].inner_item.next_pkt_idx =\n+\t\t\ttbl->items[prev_idx].inner_item.next_pkt_idx;\n+\t\ttbl->items[prev_idx].inner_item.next_pkt_idx = item_idx;\n+\t}\n+\n+\treturn item_idx;\n+}\n+\n+static inline uint32_t\n+delete_item(struct gro_vxlan_udp4_tbl *tbl,\n+\t\tuint32_t item_idx,\n+\t\tuint32_t prev_item_idx)\n+{\n+\tuint32_t next_idx = tbl->items[item_idx].inner_item.next_pkt_idx;\n+\n+\t/* NULL indicates an empty item. */\n+\ttbl->items[item_idx].inner_item.firstseg = NULL;\n+\ttbl->item_num--;\n+\tif (prev_item_idx != INVALID_ARRAY_INDEX)\n+\t\ttbl->items[prev_item_idx].inner_item.next_pkt_idx = next_idx;\n+\n+\treturn next_idx;\n+}\n+\n+static inline uint32_t\n+insert_new_flow(struct gro_vxlan_udp4_tbl *tbl,\n+\t\tstruct vxlan_udp4_flow_key *src,\n+\t\tuint32_t item_idx)\n+{\n+\tstruct vxlan_udp4_flow_key *dst;\n+\tuint32_t flow_idx;\n+\n+\tflow_idx = find_an_empty_flow(tbl);\n+\tif (unlikely(flow_idx == INVALID_ARRAY_INDEX))\n+\t\treturn INVALID_ARRAY_INDEX;\n+\n+\tdst = &(tbl->flows[flow_idx].key);\n+\n+\trte_ether_addr_copy(&(src->inner_key.eth_saddr),\n+\t\t\t&(dst->inner_key.eth_saddr));\n+\trte_ether_addr_copy(&(src->inner_key.eth_daddr),\n+\t\t\t&(dst->inner_key.eth_daddr));\n+\tdst->inner_key.ip_src_addr = src->inner_key.ip_src_addr;\n+\tdst->inner_key.ip_dst_addr = src->inner_key.ip_dst_addr;\n+\tdst->inner_key.ip_id = src->inner_key.ip_id;\n+\n+\tdst->vxlan_hdr.vx_flags = src->vxlan_hdr.vx_flags;\n+\tdst->vxlan_hdr.vx_vni = src->vxlan_hdr.vx_vni;\n+\trte_ether_addr_copy(&(src->outer_eth_saddr), &(dst->outer_eth_saddr));\n+\trte_ether_addr_copy(&(src->outer_eth_daddr), &(dst->outer_eth_daddr));\n+\tdst->outer_ip_src_addr = src->outer_ip_src_addr;\n+\tdst->outer_ip_dst_addr = src->outer_ip_dst_addr;\n+\tdst->outer_src_port = src->outer_src_port;\n+\tdst->outer_dst_port = src->outer_dst_port;\n+\n+\ttbl->flows[flow_idx].start_index = item_idx;\n+\ttbl->flow_num++;\n+\n+\treturn flow_idx;\n+}\n+\n+static inline int\n+is_same_vxlan_udp4_flow(struct vxlan_udp4_flow_key k1,\n+\t\tstruct vxlan_udp4_flow_key k2)\n+{\n+\t/* src port is changing, so shouldn't use it for flow check */\n+\treturn (rte_is_same_ether_addr(&k1.outer_eth_saddr,\n+\t\t\t\t\t&k2.outer_eth_saddr) &&\n+\t\t\trte_is_same_ether_addr(&k1.outer_eth_daddr,\n+\t\t\t\t&k2.outer_eth_daddr) &&\n+\t\t\t(k1.outer_ip_src_addr == k2.outer_ip_src_addr) &&\n+\t\t\t(k1.outer_ip_dst_addr == k2.outer_ip_dst_addr) &&\n+\t\t\t(k1.outer_dst_port == k2.outer_dst_port) &&\n+\t\t\t(k1.vxlan_hdr.vx_flags == k2.vxlan_hdr.vx_flags) &&\n+\t\t\t(k1.vxlan_hdr.vx_vni == k2.vxlan_hdr.vx_vni) &&\n+\t\t\tis_same_udp4_flow(k1.inner_key, k2.inner_key));\n+}\n+\n+static inline int\n+udp_check_vxlan_neighbor(struct gro_vxlan_udp4_item *item,\n+\t\tuint16_t frag_offset,\n+\t\tuint16_t ip_id,\n+\t\tuint16_t ip_dl)\n+{\n+\tstruct rte_mbuf *pkt = item->inner_item.firstseg;\n+\tint cmp;\n+\tuint16_t l2_offset;\n+\tint ret = 0;\n+\n+\tl2_offset = pkt->outer_l2_len + pkt->outer_l3_len;\n+\tcmp = udp_check_neighbor(&item->inner_item, frag_offset, ip_id,\n+\t\t\tip_dl, l2_offset);\n+\t/* VXLAN outer IP ID is out of order, so don't touch it and\n+\t * don't compare it.\n+\t */\n+\tif (cmp > 0)\n+\t\t/* Append the new packet. */\n+\t\tret = 1;\n+\telse if (cmp < 0)\n+\t\t/* Prepend the new packet. */\n+\t\tret = -1;\n+\n+\treturn ret;\n+}\n+\n+static inline int\n+merge_two_vxlan_udp4_packets(struct gro_vxlan_udp4_item *item,\n+\t\tstruct rte_mbuf *pkt,\n+\t\tint cmp,\n+\t\tuint16_t frag_offset,\n+\t\tuint8_t is_last_frag,\n+\t\tuint16_t ip_id)\n+{\n+\tif (merge_two_udp4_packets(&item->inner_item, pkt, cmp, frag_offset,\n+\t\t\t\tis_last_frag, ip_id,\n+\t\t\t\tpkt->outer_l2_len + pkt->outer_l3_len)) {\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static inline void\n+update_vxlan_header(struct gro_vxlan_udp4_item *item)\n+{\n+\tstruct rte_ipv4_hdr *ipv4_hdr;\n+\tstruct rte_udp_hdr *udp_hdr;\n+\tstruct rte_mbuf *pkt = item->inner_item.firstseg;\n+\tuint16_t len;\n+\tuint16_t frag_offset;\n+\n+\t/* Update the outer IPv4 header. */\n+\tlen = pkt->pkt_len - pkt->outer_l2_len;\n+\tipv4_hdr = (struct rte_ipv4_hdr *)(rte_pktmbuf_mtod(pkt, char *) +\n+\t\t\tpkt->outer_l2_len);\n+\tipv4_hdr->total_length = rte_cpu_to_be_16(len);\n+\n+\t/* Update the outer UDP header. */\n+\tlen -= pkt->outer_l3_len;\n+\tudp_hdr = (struct rte_udp_hdr *)((char *)ipv4_hdr + pkt->outer_l3_len);\n+\tudp_hdr->dgram_len = rte_cpu_to_be_16(len);\n+\n+\t/* Update the inner IPv4 header. */\n+\tlen -= pkt->l2_len;\n+\tipv4_hdr = (struct rte_ipv4_hdr *)((char *)udp_hdr + pkt->l2_len);\n+\tipv4_hdr->total_length = rte_cpu_to_be_16(len);\n+\n+\t/* Clear MF bit if it is last fragment */\n+\tif (item->inner_item.is_last_frag) {\n+\t\tfrag_offset = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);\n+\t\tipv4_hdr->fragment_offset =\n+\t\t\trte_cpu_to_be_16(frag_offset & ~RTE_IPV4_HDR_MF_FLAG);\n+\t}\n+}\n+\n+int32_t\n+gro_vxlan_udp4_reassemble(struct rte_mbuf *pkt,\n+\t\tstruct gro_vxlan_udp4_tbl *tbl,\n+\t\tuint64_t start_time)\n+{\n+\tstruct rte_ether_hdr *outer_eth_hdr, *eth_hdr;\n+\tstruct rte_ipv4_hdr *outer_ipv4_hdr, *ipv4_hdr;\n+\tstruct rte_udp_hdr *udp_hdr;\n+\tstruct rte_vxlan_hdr *vxlan_hdr;\n+\tuint16_t frag_offset;\n+\tuint8_t is_last_frag;\n+\tint16_t ip_dl;\n+\tuint16_t outer_ip_id, ip_id;\n+\tuint8_t outer_is_atomic;\n+\n+\tstruct vxlan_udp4_flow_key key;\n+\tuint32_t cur_idx, prev_idx, item_idx;\n+\tuint32_t i, max_flow_num, remaining_flow_num;\n+\tint cmp;\n+\tuint16_t hdr_len;\n+\tuint8_t find;\n+\n+\touter_eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);\n+\touter_ipv4_hdr = (struct rte_ipv4_hdr *)((char *)outer_eth_hdr +\n+\t\t\tpkt->outer_l2_len);\n+\n+\tudp_hdr = (struct rte_udp_hdr *)((char *)outer_ipv4_hdr +\n+\t\t\tpkt->outer_l3_len);\n+\tvxlan_hdr = (struct rte_vxlan_hdr *)((char *)udp_hdr +\n+\t\t\tsizeof(struct rte_udp_hdr));\n+\teth_hdr = (struct rte_ether_hdr *)((char *)vxlan_hdr +\n+\t\t\tsizeof(struct rte_vxlan_hdr));\n+\t/* l2_len = outer udp hdr len + vxlan hdr len + inner l2 len */\n+\tipv4_hdr = (struct rte_ipv4_hdr *)((char *)udp_hdr + pkt->l2_len);\n+\n+\t/*\n+\t * Don't process the packet which has non-fragment inner IP.\n+\t */\n+\tif (!is_ipv4_fragment(ipv4_hdr))\n+\t\treturn -1;\n+\n+\thdr_len = pkt->outer_l2_len + pkt->outer_l3_len + pkt->l2_len +\n+\t\tpkt->l3_len;\n+\t/*\n+\t * Don't process the packet whose payload length is less than or\n+\t * equal to 0.\n+\t */\n+\tip_dl = pkt->pkt_len - hdr_len;\n+\tif (ip_dl <= 0)\n+\t\treturn -1;\n+\n+\t/*\n+\t * Save IPv4 ID for the packet whose DF bit is 0. For the packet\n+\t * whose DF bit is 1, IPv4 ID is ignored.\n+\t */\n+\tfrag_offset = rte_be_to_cpu_16(outer_ipv4_hdr->fragment_offset);\n+\touter_is_atomic =\n+\t\t((frag_offset & RTE_IPV4_HDR_DF_FLAG) == RTE_IPV4_HDR_DF_FLAG);\n+\touter_ip_id = outer_is_atomic ? 0 :\n+\t\trte_be_to_cpu_16(outer_ipv4_hdr->packet_id);\n+\tip_id = rte_be_to_cpu_16(ipv4_hdr->packet_id);\n+\tfrag_offset = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);\n+\tis_last_frag = ((frag_offset & RTE_IPV4_HDR_MF_FLAG) == 0) ? 1 : 0;\n+\tfrag_offset = (uint16_t)(frag_offset & RTE_IPV4_HDR_OFFSET_MASK) << 3;\n+\n+\trte_ether_addr_copy(&(eth_hdr->s_addr), &(key.inner_key.eth_saddr));\n+\trte_ether_addr_copy(&(eth_hdr->d_addr), &(key.inner_key.eth_daddr));\n+\tkey.inner_key.ip_src_addr = ipv4_hdr->src_addr;\n+\tkey.inner_key.ip_dst_addr = ipv4_hdr->dst_addr;\n+\tkey.inner_key.ip_id = ip_id;\n+\n+\tkey.vxlan_hdr.vx_flags = vxlan_hdr->vx_flags;\n+\tkey.vxlan_hdr.vx_vni = vxlan_hdr->vx_vni;\n+\trte_ether_addr_copy(&(outer_eth_hdr->s_addr), &(key.outer_eth_saddr));\n+\trte_ether_addr_copy(&(outer_eth_hdr->d_addr), &(key.outer_eth_daddr));\n+\tkey.outer_ip_src_addr = outer_ipv4_hdr->src_addr;\n+\tkey.outer_ip_dst_addr = outer_ipv4_hdr->dst_addr;\n+\tkey.outer_src_port = udp_hdr->src_port;\n+\tkey.outer_dst_port = udp_hdr->dst_port;\n+\n+\t/* Search for a matched flow. */\n+\tmax_flow_num = tbl->max_flow_num;\n+\tremaining_flow_num = tbl->flow_num;\n+\tfind = 0;\n+\tfor (i = 0; i < max_flow_num && remaining_flow_num; i++) {\n+\t\tif (tbl->flows[i].start_index != INVALID_ARRAY_INDEX) {\n+\t\t\tif (is_same_vxlan_udp4_flow(tbl->flows[i].key, key)) {\n+\t\t\t\tfind = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tremaining_flow_num--;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Can't find a matched flow. Insert a new flow and store the\n+\t * packet into the flow.\n+\t */\n+\tif (find == 0) {\n+\t\titem_idx = insert_new_item(tbl, pkt, start_time,\n+\t\t\t\tINVALID_ARRAY_INDEX, frag_offset,\n+\t\t\t\tis_last_frag, outer_ip_id,\n+\t\t\t\tip_id, outer_is_atomic);\n+\t\tif (item_idx == INVALID_ARRAY_INDEX)\n+\t\t\treturn -1;\n+\t\tif (insert_new_flow(tbl, &key, item_idx) ==\n+\t\t\t\tINVALID_ARRAY_INDEX) {\n+\t\t\t/*\n+\t\t\t * Fail to insert a new flow, so\n+\t\t\t * delete the inserted packet.\n+\t\t\t */\n+\t\t\tdelete_item(tbl, item_idx, INVALID_ARRAY_INDEX);\n+\t\t\treturn -1;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\t/* Check all packets in the flow and try to find a neighbor. */\n+\tcur_idx = tbl->flows[i].start_index;\n+\tprev_idx = cur_idx;\n+\tdo {\n+\t\tcmp = udp_check_vxlan_neighbor(&(tbl->items[cur_idx]),\n+\t\t\t\tfrag_offset, ip_id, ip_dl);\n+\t\tif (cmp) {\n+\t\t\tif (merge_two_vxlan_udp4_packets(\n+\t\t\t\t\t\t&(tbl->items[cur_idx]),\n+\t\t\t\t\t\tpkt, cmp, frag_offset,\n+\t\t\t\t\t\tis_last_frag, ip_id)) {\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * Can't merge two packets, as the packet\n+\t\t\t * length will be greater than the max value.\n+\t\t\t * Insert the packet into the flow.\n+\t\t\t */\n+\t\t\tif (insert_new_item(tbl, pkt, start_time, prev_idx,\n+\t\t\t\t\t\tfrag_offset, is_last_frag,\n+\t\t\t\t\t\touter_ip_id, ip_id,\n+\t\t\t\t\t\touter_is_atomic) ==\n+\t\t\t\t\tINVALID_ARRAY_INDEX)\n+\t\t\t\treturn -1;\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\t/* Ensure inserted items are ordered by frag_offset */\n+\t\tif (frag_offset\n+\t\t\t< tbl->items[cur_idx].inner_item.frag_offset) {\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tprev_idx = cur_idx;\n+\t\tcur_idx = tbl->items[cur_idx].inner_item.next_pkt_idx;\n+\t} while (cur_idx != INVALID_ARRAY_INDEX);\n+\n+\t/* Can't find neighbor. Insert the packet into the flow. */\n+\tif (cur_idx == tbl->flows[i].start_index) {\n+\t\t/* Insert it before the first packet of the flow */\n+\t\titem_idx = insert_new_item(tbl, pkt, start_time,\n+\t\t\t\tINVALID_ARRAY_INDEX, frag_offset,\n+\t\t\t\tis_last_frag, outer_ip_id,\n+\t\t\t\tip_id, outer_is_atomic);\n+\t\tif (item_idx == INVALID_ARRAY_INDEX)\n+\t\t\treturn -1;\n+\t\ttbl->items[item_idx].inner_item.next_pkt_idx = cur_idx;\n+\t\ttbl->flows[i].start_index = item_idx;\n+\t} else {\n+\t\tif (insert_new_item(tbl, pkt, start_time, prev_idx,\n+\t\t\t\tfrag_offset, is_last_frag, outer_ip_id, ip_id,\n+\t\t\t\touter_is_atomic) == INVALID_ARRAY_INDEX)\n+\t\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+gro_vxlan_udp4_merge_items(struct gro_vxlan_udp4_tbl *tbl,\n+\t\t\t   uint32_t start_idx)\n+{\n+\tuint16_t frag_offset;\n+\tuint8_t is_last_frag;\n+\tint16_t ip_dl;\n+\tstruct rte_mbuf *pkt;\n+\tint cmp;\n+\tuint16_t ip_id;\n+\tuint32_t item_idx;\n+\tuint16_t hdr_len;\n+\n+\titem_idx = tbl->items[start_idx].inner_item.next_pkt_idx;\n+\twhile (item_idx != INVALID_ARRAY_INDEX) {\n+\t\tpkt = tbl->items[item_idx].inner_item.firstseg;\n+\t\thdr_len = pkt->outer_l2_len + pkt->outer_l3_len + pkt->l2_len +\n+\t\t\tpkt->l3_len;\n+\t\tip_dl = pkt->pkt_len - hdr_len;\n+\t\tfrag_offset = tbl->items[item_idx].inner_item.frag_offset;\n+\t\tis_last_frag = tbl->items[item_idx].inner_item.is_last_frag;\n+\t\tip_id = tbl->items[item_idx].inner_item.ip_id;\n+\t\tcmp = udp_check_vxlan_neighbor(&(tbl->items[start_idx]),\n+\t\t\t\t\tfrag_offset, ip_id, ip_dl);\n+\t\tif (cmp) {\n+\t\t\tif (merge_two_vxlan_udp4_packets(\n+\t\t\t\t\t&(tbl->items[start_idx]),\n+\t\t\t\t\tpkt, cmp, frag_offset,\n+\t\t\t\t\tis_last_frag, ip_id)) {\n+\t\t\t\titem_idx = delete_item(tbl, item_idx,\n+\t\t\t\t\t\t\tINVALID_ARRAY_INDEX);\n+\t\t\t\ttbl->items[start_idx].inner_item.next_pkt_idx\n+\t\t\t\t\t= item_idx;\n+\t\t\t} else {\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t} else {\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+uint16_t\n+gro_vxlan_udp4_tbl_timeout_flush(struct gro_vxlan_udp4_tbl *tbl,\n+\t\tuint64_t flush_timestamp,\n+\t\tstruct rte_mbuf **out,\n+\t\tuint16_t nb_out)\n+{\n+\tuint16_t k = 0;\n+\tuint32_t i, j;\n+\tuint32_t max_flow_num = tbl->max_flow_num;\n+\n+\tfor (i = 0; i < max_flow_num; i++) {\n+\t\tif (unlikely(tbl->flow_num == 0))\n+\t\t\treturn k;\n+\n+\t\tj = tbl->flows[i].start_index;\n+\t\twhile (j != INVALID_ARRAY_INDEX) {\n+\t\t\tif (tbl->items[j].inner_item.start_time <=\n+\t\t\t\t\tflush_timestamp) {\n+\t\t\t\tgro_vxlan_udp4_merge_items(tbl, j);\n+\t\t\t\tout[k++] = tbl->items[j].inner_item.firstseg;\n+\t\t\t\tif (tbl->items[j].inner_item.nb_merged > 1)\n+\t\t\t\t\tupdate_vxlan_header(&(tbl->items[j]));\n+\t\t\t\t/*\n+\t\t\t\t * Delete the item and get the next packet\n+\t\t\t\t * index.\n+\t\t\t\t */\n+\t\t\t\tj = delete_item(tbl, j, INVALID_ARRAY_INDEX);\n+\t\t\t\ttbl->flows[i].start_index = j;\n+\t\t\t\tif (j == INVALID_ARRAY_INDEX)\n+\t\t\t\t\ttbl->flow_num--;\n+\n+\t\t\t\tif (unlikely(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 * The left packets in the flow won't be\n+\t\t\t\t * timeout. Go to check other flows.\n+\t\t\t\t */\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\treturn k;\n+}\n+\n+uint32_t\n+gro_vxlan_udp4_tbl_pkt_count(void *tbl)\n+{\n+\tstruct gro_vxlan_udp4_tbl *gro_tbl = tbl;\n+\n+\tif (gro_tbl)\n+\t\treturn gro_tbl->item_num;\n+\n+\treturn 0;\n+}\ndiff --git a/lib/librte_gro/gro_vxlan_udp4.h b/lib/librte_gro/gro_vxlan_udp4.h\nnew file mode 100644\nindex 0000000..a16e327\n--- /dev/null\n+++ b/lib/librte_gro/gro_vxlan_udp4.h\n@@ -0,0 +1,152 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2020 Inspur Corporation\n+ */\n+\n+#ifndef _GRO_VXLAN_UDP4_H_\n+#define _GRO_VXLAN_UDP4_H_\n+\n+#include \"gro_udp4.h\"\n+\n+#define GRO_VXLAN_UDP4_TBL_MAX_ITEM_NUM (1024UL * 1024UL)\n+\n+/* Header fields representing a VxLAN flow */\n+struct vxlan_udp4_flow_key {\n+\tstruct udp4_flow_key inner_key;\n+\tstruct rte_vxlan_hdr vxlan_hdr;\n+\n+\tstruct rte_ether_addr outer_eth_saddr;\n+\tstruct rte_ether_addr outer_eth_daddr;\n+\n+\tuint32_t outer_ip_src_addr;\n+\tuint32_t outer_ip_dst_addr;\n+\n+\t/* Outer UDP ports */\n+\tuint16_t outer_src_port;\n+\tuint16_t outer_dst_port;\n+\n+};\n+\n+struct gro_vxlan_udp4_flow {\n+\tstruct vxlan_udp4_flow_key key;\n+\t/*\n+\t * The index of the first packet in the flow. INVALID_ARRAY_INDEX\n+\t * indicates an empty flow.\n+\t */\n+\tuint32_t start_index;\n+};\n+\n+struct gro_vxlan_udp4_item {\n+\tstruct gro_udp4_item inner_item;\n+\t/* IPv4 ID in the outer IPv4 header */\n+\tuint16_t outer_ip_id;\n+\t/* Indicate if outer IPv4 ID can be ignored */\n+\tuint8_t outer_is_atomic;\n+};\n+\n+/*\n+ * VxLAN (with an outer IPv4 header and an inner UDP/IPv4 packet)\n+ * reassembly table structure\n+ */\n+struct gro_vxlan_udp4_tbl {\n+\t/* item array */\n+\tstruct gro_vxlan_udp4_item *items;\n+\t/* flow array */\n+\tstruct gro_vxlan_udp4_flow *flows;\n+\t/* current item number */\n+\tuint32_t item_num;\n+\t/* current flow number */\n+\tuint32_t flow_num;\n+\t/* the maximum item number */\n+\tuint32_t max_item_num;\n+\t/* the maximum flow number */\n+\tuint32_t max_flow_num;\n+};\n+\n+/**\n+ * This function creates a VxLAN reassembly table for VxLAN packets\n+ * which have an outer IPv4 header and an inner UDP/IPv4 packet.\n+ *\n+ * @param socket_id\n+ *  Socket index for allocating the table\n+ * @param max_flow_num\n+ *  The maximum number of flows in the table\n+ * @param max_item_per_flow\n+ *  The maximum number of packets per flow\n+ *\n+ * @return\n+ *  - Return the table pointer on success.\n+ *  - Return NULL on failure.\n+ */\n+void *gro_vxlan_udp4_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 VxLAN reassembly table.\n+ *\n+ * @param tbl\n+ *  Pointer pointing to the VxLAN reassembly table\n+ */\n+void gro_vxlan_udp4_tbl_destroy(void *tbl);\n+\n+/**\n+ * This function merges a VxLAN packet which has an outer IPv4 header and\n+ * an inner UDP/IPv4 packet. It does not process the packet which does not\n+ * have payload.\n+ *\n+ * This function does not check if the packet has correct checksums and\n+ * does not re-calculate checksums for the merged packet. It returns the\n+ * packet if there is no available space in the table.\n+ *\n+ * @param pkt\n+ *  Packet to reassemble\n+ * @param tbl\n+ *  Pointer pointing to the VxLAN reassembly table\n+ * @start_time\n+ *  The time when the packet is inserted into the table\n+ *\n+ * @return\n+ *  - Return a positive value if the packet is merged.\n+ *  - Return zero if the packet isn't merged but stored in the table.\n+ *  - Return a negative value for invalid parameters or no available\n+ *    space in the table.\n+ */\n+int32_t gro_vxlan_udp4_reassemble(struct rte_mbuf *pkt,\n+\t\tstruct gro_vxlan_udp4_tbl *tbl,\n+\t\tuint64_t start_time);\n+\n+/**\n+ * This function flushes timeout packets in the VxLAN reassembly table,\n+ * and without updating checksums.\n+ *\n+ * @param tbl\n+ *  Pointer pointing to a VxLAN GRO table\n+ * @param flush_timestamp\n+ *  This function flushes packets which are inserted into the table\n+ *  before or at the flush_timestamp.\n+ * @param out\n+ *  Pointer array used to keep flushed packets\n+ * @param nb_out\n+ *  The element number in 'out'. It also determines the maximum number of\n+ *  packets that can be flushed finally.\n+ *\n+ * @return\n+ *  The number of flushed packets\n+ */\n+uint16_t gro_vxlan_udp4_tbl_timeout_flush(struct gro_vxlan_udp4_tbl *tbl,\n+\t\tuint64_t flush_timestamp,\n+\t\tstruct rte_mbuf **out,\n+\t\tuint16_t nb_out);\n+\n+/**\n+ * This function returns the number of the packets in a VxLAN\n+ * reassembly table.\n+ *\n+ * @param tbl\n+ *  Pointer pointing to the VxLAN reassembly table\n+ *\n+ * @return\n+ *  The number of packets in the table\n+ */\n+uint32_t gro_vxlan_udp4_tbl_pkt_count(void *tbl);\n+#endif\ndiff --git a/lib/librte_gro/meson.build b/lib/librte_gro/meson.build\nindex 0d18dc2..ea8b45c 100644\n--- a/lib/librte_gro/meson.build\n+++ b/lib/librte_gro/meson.build\n@@ -1,6 +1,6 @@\n # SPDX-License-Identifier: BSD-3-Clause\n # Copyright(c) 2017 Intel Corporation\n \n-sources = files('rte_gro.c', 'gro_tcp4.c', 'gro_udp4.c', 'gro_vxlan_tcp4.c')\n+sources = files('rte_gro.c', 'gro_tcp4.c', 'gro_udp4.c', 'gro_vxlan_tcp4.c', 'gro_vxlan_udp4.c')\n headers = files('rte_gro.h')\n deps += ['ethdev']\ndiff --git a/lib/librte_gro/rte_gro.c b/lib/librte_gro/rte_gro.c\nindex da849f7..6295fbb 100644\n--- a/lib/librte_gro/rte_gro.c\n+++ b/lib/librte_gro/rte_gro.c\n@@ -11,6 +11,7 @@\n #include \"gro_tcp4.h\"\n #include \"gro_udp4.h\"\n #include \"gro_vxlan_tcp4.h\"\n+#include \"gro_vxlan_udp4.h\"\n \n typedef void *(*gro_tbl_create_fn)(uint16_t socket_id,\n \t\tuint16_t max_flow_num,\n@@ -20,14 +21,14 @@\n \n static gro_tbl_create_fn tbl_create_fn[RTE_GRO_TYPE_MAX_NUM] = {\n \t\tgro_tcp4_tbl_create, gro_vxlan_tcp4_tbl_create,\n-\t\tgro_udp4_tbl_create, NULL};\n+\t\tgro_udp4_tbl_create, gro_vxlan_udp4_tbl_create, NULL};\n static gro_tbl_destroy_fn tbl_destroy_fn[RTE_GRO_TYPE_MAX_NUM] = {\n \t\t\tgro_tcp4_tbl_destroy, gro_vxlan_tcp4_tbl_destroy,\n-\t\t\tgro_udp4_tbl_destroy,\n+\t\t\tgro_udp4_tbl_destroy, gro_vxlan_udp4_tbl_destroy,\n \t\t\tNULL};\n static gro_tbl_pkt_count_fn tbl_pkt_count_fn[RTE_GRO_TYPE_MAX_NUM] = {\n \t\t\tgro_tcp4_tbl_pkt_count, gro_vxlan_tcp4_tbl_pkt_count,\n-\t\t\tgro_udp4_tbl_pkt_count,\n+\t\t\tgro_udp4_tbl_pkt_count, gro_vxlan_udp4_tbl_pkt_count,\n \t\t\tNULL};\n \n #define IS_IPV4_TCP_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \\\n@@ -47,6 +48,16 @@\n \t\t     RTE_PTYPE_INNER_L3_IPV4_EXT | \\\n \t\t     RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN)) != 0))\n \n+#define IS_IPV4_VXLAN_UDP4_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \\\n+\t\t((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \\\n+\t\t((ptype & RTE_PTYPE_TUNNEL_VXLAN) == \\\n+\t\t RTE_PTYPE_TUNNEL_VXLAN) && \\\n+\t\t ((ptype & RTE_PTYPE_INNER_L4_UDP) == \\\n+\t\t  RTE_PTYPE_INNER_L4_UDP) && \\\n+\t\t  (((ptype & RTE_PTYPE_INNER_L3_MASK) & \\\n+\t\t    (RTE_PTYPE_INNER_L3_IPV4 | \\\n+\t\t     RTE_PTYPE_INNER_L3_IPV4_EXT | \\\n+\t\t     RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN)) != 0))\n \n /*\n  * GRO context structure. It keeps the table structures, which are\n@@ -142,14 +153,22 @@ struct gro_ctx {\n \tstruct gro_vxlan_tcp4_item vxlan_tcp_items[RTE_GRO_MAX_BURST_ITEM_NUM]\n \t\t\t= {{{0}, 0, 0} };\n \n+\t/* Allocate a reassembly table for VXLAN UDP GRO */\n+\tstruct gro_vxlan_udp4_tbl vxlan_udp_tbl;\n+\tstruct gro_vxlan_udp4_flow vxlan_udp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];\n+\tstruct gro_vxlan_udp4_item vxlan_udp_items[RTE_GRO_MAX_BURST_ITEM_NUM]\n+\t\t\t= {{{0}, 0, 0} };\n+\n \tstruct rte_mbuf *unprocess_pkts[nb_pkts];\n \tuint32_t item_num;\n \tint32_t ret;\n \tuint16_t i, unprocess_num = 0, nb_after_gro = nb_pkts;\n-\tuint8_t do_tcp4_gro = 0, do_vxlan_tcp_gro = 0, do_udp4_gro = 0;\n+\tuint8_t do_tcp4_gro = 0, do_vxlan_tcp_gro = 0, do_udp4_gro = 0,\n+\t\tdo_vxlan_udp_gro = 0;\n \n \tif (unlikely((param->gro_types & (RTE_GRO_IPV4_VXLAN_TCP_IPV4 |\n \t\t\t\t\tRTE_GRO_TCP_IPV4 |\n+\t\t\t\t\tRTE_GRO_IPV4_VXLAN_UDP_IPV4 |\n \t\t\t\t\tRTE_GRO_UDP_IPV4)) == 0))\n \t\treturn nb_pkts;\n \n@@ -171,6 +190,19 @@ struct gro_ctx {\n \t\tdo_vxlan_tcp_gro = 1;\n \t}\n \n+\tif (param->gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) {\n+\t\tfor (i = 0; i < item_num; i++)\n+\t\t\tvxlan_udp_flows[i].start_index = INVALID_ARRAY_INDEX;\n+\n+\t\tvxlan_udp_tbl.flows = vxlan_udp_flows;\n+\t\tvxlan_udp_tbl.items = vxlan_udp_items;\n+\t\tvxlan_udp_tbl.flow_num = 0;\n+\t\tvxlan_udp_tbl.item_num = 0;\n+\t\tvxlan_udp_tbl.max_flow_num = item_num;\n+\t\tvxlan_udp_tbl.max_item_num = item_num;\n+\t\tdo_vxlan_udp_gro = 1;\n+\t}\n+\n \tif (param->gro_types & RTE_GRO_TCP_IPV4) {\n \t\tfor (i = 0; i < item_num; i++)\n \t\t\ttcp_flows[i].start_index = INVALID_ARRAY_INDEX;\n@@ -212,6 +244,15 @@ struct gro_ctx {\n \t\t\t\tnb_after_gro--;\n \t\t\telse if (ret < 0)\n \t\t\t\tunprocess_pkts[unprocess_num++] = pkts[i];\n+\t\t} else if (IS_IPV4_VXLAN_UDP4_PKT(pkts[i]->packet_type) &&\n+\t\t\t\tdo_vxlan_udp_gro) {\n+\t\t\tret = gro_vxlan_udp4_reassemble(pkts[i],\n+\t\t\t\t\t\t\t&vxlan_udp_tbl, 0);\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++] = pkts[i];\n \t\t} else if (IS_IPV4_TCP_PKT(pkts[i]->packet_type) &&\n \t\t\t\tdo_tcp4_gro) {\n \t\t\tret = gro_tcp4_reassemble(pkts[i], &tcp_tbl, 0);\n@@ -241,6 +282,12 @@ struct gro_ctx {\n \t\t\t\t\t0, pkts, nb_pkts);\n \t\t}\n \n+\t\tif (do_vxlan_udp_gro) {\n+\t\t\ti += gro_vxlan_udp4_tbl_timeout_flush(&vxlan_udp_tbl,\n+\t\t\t\t\t0, &pkts[i], nb_pkts - i);\n+\n+\t\t}\n+\n \t\tif (do_tcp4_gro) {\n \t\t\ti += gro_tcp4_tbl_timeout_flush(&tcp_tbl, 0,\n \t\t\t\t\t&pkts[i], nb_pkts - i);\n@@ -269,19 +316,21 @@ struct gro_ctx {\n {\n \tstruct rte_mbuf *unprocess_pkts[nb_pkts];\n \tstruct gro_ctx *gro_ctx = ctx;\n-\tvoid *tcp_tbl, *udp_tbl, *vxlan_tcp_tbl;\n+\tvoid *tcp_tbl, *udp_tbl, *vxlan_tcp_tbl, *vxlan_udp_tbl;\n \tuint64_t current_time;\n \tuint16_t i, unprocess_num = 0;\n-\tuint8_t do_tcp4_gro, do_vxlan_tcp_gro, do_udp4_gro;\n+\tuint8_t do_tcp4_gro, do_vxlan_tcp_gro, do_udp4_gro, do_vxlan_udp_gro;\n \n \tif (unlikely((gro_ctx->gro_types & (RTE_GRO_IPV4_VXLAN_TCP_IPV4 |\n \t\t\t\t\tRTE_GRO_TCP_IPV4 |\n+\t\t\t\t\tRTE_GRO_IPV4_VXLAN_UDP_IPV4 |\n \t\t\t\t\tRTE_GRO_UDP_IPV4)) == 0))\n \t\treturn nb_pkts;\n \n \ttcp_tbl = gro_ctx->tbls[RTE_GRO_TCP_IPV4_INDEX];\n \tvxlan_tcp_tbl = gro_ctx->tbls[RTE_GRO_IPV4_VXLAN_TCP_IPV4_INDEX];\n \tudp_tbl = gro_ctx->tbls[RTE_GRO_UDP_IPV4_INDEX];\n+\tvxlan_udp_tbl = gro_ctx->tbls[RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX];\n \n \tdo_tcp4_gro = (gro_ctx->gro_types & RTE_GRO_TCP_IPV4) ==\n \t\tRTE_GRO_TCP_IPV4;\n@@ -289,6 +338,8 @@ struct gro_ctx {\n \t\tRTE_GRO_IPV4_VXLAN_TCP_IPV4;\n \tdo_udp4_gro = (gro_ctx->gro_types & RTE_GRO_UDP_IPV4) ==\n \t\tRTE_GRO_UDP_IPV4;\n+\tdo_vxlan_udp_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) ==\n+\t\tRTE_GRO_IPV4_VXLAN_UDP_IPV4;\n \n \tcurrent_time = rte_rdtsc();\n \n@@ -298,6 +349,11 @@ struct gro_ctx {\n \t\t\tif (gro_vxlan_tcp4_reassemble(pkts[i], vxlan_tcp_tbl,\n \t\t\t\t\t\tcurrent_time) < 0)\n \t\t\t\tunprocess_pkts[unprocess_num++] = pkts[i];\n+\t\t} else if (IS_IPV4_VXLAN_UDP4_PKT(pkts[i]->packet_type) &&\n+\t\t\t\tdo_vxlan_udp_gro) {\n+\t\t\tif (gro_vxlan_udp4_reassemble(pkts[i], vxlan_udp_tbl,\n+\t\t\t\t\t\tcurrent_time) < 0)\n+\t\t\t\tunprocess_pkts[unprocess_num++] = pkts[i];\n \t\t} else if (IS_IPV4_TCP_PKT(pkts[i]->packet_type) &&\n \t\t\t\tdo_tcp4_gro) {\n \t\t\tif (gro_tcp4_reassemble(pkts[i], tcp_tbl,\n@@ -341,6 +397,13 @@ struct gro_ctx {\n \t\tleft_nb_out = max_nb_out - num;\n \t}\n \n+\tif ((gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) && max_nb_out > 0) {\n+\t\tnum += gro_vxlan_udp4_tbl_timeout_flush(gro_ctx->tbls[\n+\t\t\t\tRTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX],\n+\t\t\t\tflush_timestamp, &out[num], left_nb_out);\n+\t\tleft_nb_out = max_nb_out - num;\n+\t}\n+\n \t/* If no available space in 'out', stop flushing. */\n \tif ((gro_types & RTE_GRO_TCP_IPV4) && max_nb_out > 0) {\n \t\tnum += gro_tcp4_tbl_timeout_flush(\ndiff --git a/lib/librte_gro/rte_gro.h b/lib/librte_gro/rte_gro.h\nindex 470f3ed..9f9ed49 100644\n--- a/lib/librte_gro/rte_gro.h\n+++ b/lib/librte_gro/rte_gro.h\n@@ -35,6 +35,9 @@\n #define RTE_GRO_UDP_IPV4_INDEX 2\n #define RTE_GRO_UDP_IPV4 (1ULL << RTE_GRO_UDP_IPV4_INDEX)\n /**< UDP/IPv4 GRO flag */\n+#define RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX 3\n+#define RTE_GRO_IPV4_VXLAN_UDP_IPV4 (1ULL << RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX)\n+/**< VxLAN UDP/IPv4 GRO flag. */\n \n /**\n  * Structure used to create GRO context objects or used to pass\n",
    "prefixes": [
        "V3",
        "2/2"
    ]
}