get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 77951,
    "url": "https://patches.dpdk.org/api/patches/77951/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20200917034959.194372-3-yang_y_yi@163.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": "<20200917034959.194372-3-yang_y_yi@163.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200917034959.194372-3-yang_y_yi@163.com",
    "date": "2020-09-17T03:49:58",
    "name": "[v6,2/3] gro: add VXLAN UDP/IPv4 GRO support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "f540797335ab26caa877f00d37a48ab4f8f02e40",
    "submitter": {
        "id": 1806,
        "url": "https://patches.dpdk.org/api/people/1806/?format=api",
        "name": "yang_y_yi",
        "email": "yang_y_yi@163.com"
    },
    "delegate": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20200917034959.194372-3-yang_y_yi@163.com/mbox/",
    "series": [
        {
            "id": 12293,
            "url": "https://patches.dpdk.org/api/series/12293/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=12293",
            "date": "2020-09-17T03:49:56",
            "name": "gro: add UDP/IPv4 GRO and VXLAN UDP/IPv4 GRO support",
            "version": 6,
            "mbox": "https://patches.dpdk.org/series/12293/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/77951/comments/",
    "check": "success",
    "checks": "https://patches.dpdk.org/api/patches/77951/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 A84C0A04B5;\n\tThu, 17 Sep 2020 05:50:34 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 625491D514;\n\tThu, 17 Sep 2020 05:50:10 +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 CD5FA1D451\n for <dev@dpdk.org>; Thu, 17 Sep 2020 05:50:04 +0200 (CEST)",
            "from yangyi0100.home.langchao.com (unknown [111.207.123.58])\n by smtp4 (Coremail) with SMTP id HNxpCgBHKOLn3GJfpGpBQA--.12527S4;\n Thu, 17 Sep 2020 11:50:01 +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=zeuz+\n E8lnpGyCgt7VF7iErQwCT5nP3XRrRUIgBrgpms=; b=cF3OuRLJczLaV5qwy1Lcx\n +FXM7T8YbZl2PNNEq4G344omy5Pd96CfD3eBr3j/+cLxIcoCLtJp+zAJ/X86XHof\n T5nCAkZLFvaEK+j0sJg0/X2Hkrz2O8D0VZ4lOXd/IYh3aPjhllgazpz5EMWPKb0m\n 1bifBNo96t0gIzvK6SqmI0=",
        "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": "Thu, 17 Sep 2020 11:49:58 +0800",
        "Message-Id": "<20200917034959.194372-3-yang_y_yi@163.com>",
        "X-Mailer": "git-send-email 2.19.2.windows.1",
        "In-Reply-To": "<20200917034959.194372-1-yang_y_yi@163.com>",
        "References": "<20200917034959.194372-1-yang_y_yi@163.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-CM-TRANSID": "HNxpCgBHKOLn3GJfpGpBQA--.12527S4",
        "X-Coremail-Antispam": "1Uf129KBjvAXoWfZFyruFWrWF45Gr4kXr4kXrb_yoW5CFy3Co\n WxXws0934kCrn0yr40yryxurW2gw40gF17Cws0grs3Xwn2y34DC393Ww4fAa17Xr4YvF1F\n 9w4xua17XF47AFs7n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3\n AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjxUeSdyDUUUU",
        "X-Originating-IP": "[111.207.123.58]",
        "X-CM-SenderInfo": "51dqwsp1b1xqqrwthudrp/1tbiEAmii18YDI+ZnQABsx",
        "Subject": "[dpdk-dev] [PATCH v6 2/3] gro: add VXLAN UDP/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": "<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/IPv4 GRO can help improve VM-to-VM UDP\nperformance when UFO or GSO is enabled in VM, GRO\nmust be supported if UFO or GSO is enabled,\notherwise, performance can't get big improvement\nif only GSO is there.\n\nWith this enabled in DPDK, OVS DPDK can leverage it\nto improve VM-to-VM UDP performance, it will reassemble\nVXLAN UDP/IPv4 fragments immediate after they are\nreceived from a physical NIC. It is very helpful in\nOVS DPDK VXLAN use case.\n\nNote: outer IP ID isn't used to check if two packets\nare same flow and can be merged because the difference\nbetween outer IP IDs of two packets isn't always +/-1\nin case of OVS DPDK.\n\nSigned-off-by: Yi Yang <yangyi01@inspur.com>\n---\n lib/librte_gro/gro_udp4.h       |   1 +\n lib/librte_gro/gro_vxlan_udp4.c | 542 ++++++++++++++++++++++++++++++++++++++++\n lib/librte_gro/gro_vxlan_udp4.h | 154 ++++++++++++\n lib/librte_gro/meson.build      |   2 +-\n lib/librte_gro/rte_gro.c        | 115 +++++++--\n lib/librte_gro/rte_gro.h        |   3 +\n 6 files changed, 790 insertions(+), 27 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/gro_udp4.h b/lib/librte_gro/gro_udp4.h\nindex 0a078e4..d38b393 100644\n--- a/lib/librte_gro/gro_udp4.h\n+++ b/lib/librte_gro/gro_udp4.h\n@@ -7,6 +7,7 @@\n \n #include <rte_ip.h>\n #include <rte_udp.h>\n+#include <rte_vxlan.h>\n \n #define INVALID_ARRAY_INDEX 0xffffffffUL\n #define GRO_UDP4_TBL_MAX_ITEM_NUM (1024UL * 1024UL)\ndiff --git a/lib/librte_gro/gro_vxlan_udp4.c b/lib/librte_gro/gro_vxlan_udp4.c\nnew file mode 100644\nindex 0000000..4eece56\n--- /dev/null\n+++ b/lib/librte_gro/gro_vxlan_udp4.c\n@@ -0,0 +1,542 @@\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+{\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.nb_merged = 1;\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_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/* For VxLAN packet, outer udp src port is calculated from\n+\t * inner packet RSS hash, udp src port of the first UDP\n+\t * fragment is different from one of other UDP fragments\n+\t * even if they are same flow, so we have to skip outer udp\n+\t * src port comparison here.\n+\t */\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+udp4_check_vxlan_neighbor(struct gro_vxlan_udp4_item *item,\n+\t\tuint16_t frag_offset,\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+\t/* Note: if outer DF bit is set, i.e outer_is_atomic is 0,\n+\t * we needn't compare outer_ip_id because they are same,\n+\t * for the case outer_is_atomic is 1, we also have no way\n+\t * to compare outer_ip_id because the difference between\n+\t * outer_ip_ids of two received packets isn't always +/-1.\n+\t * So skip outer_ip_id comparison here.\n+\t */\n+\n+\tl2_offset = pkt->outer_l2_len + pkt->outer_l3_len;\n+\tcmp = udp4_check_neighbor(&item->inner_item, frag_offset, ip_dl,\n+\t\t\t\t\tl2_offset);\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+{\n+\tif (merge_two_udp4_packets(&item->inner_item, pkt, cmp, frag_offset,\n+\t\t\t\tis_last_frag,\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 ip_id;\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+\tif (pkt->pkt_len <= hdr_len)\n+\t\treturn -1;\n+\n+\tip_dl = pkt->pkt_len - hdr_len;\n+\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+\t/* Note: It is unnecessary to save outer_src_port here because it can\n+\t * be different for VxLAN UDP fragments from the same flow.\n+\t */\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);\n+\t\tif (unlikely(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 = udp4_check_vxlan_neighbor(&(tbl->items[cur_idx]),\n+\t\t\t\tfrag_offset, 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)) {\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\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);\n+\t\tif (unlikely(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\t\tfrag_offset, is_last_frag\n+\t\t\t\t\t) == 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+\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\tcmp = udp4_check_vxlan_neighbor(&(tbl->items[start_idx]),\n+\t\t\t\t\tfrag_offset, 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)) {\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} else\n+\t\t\treturn 0;\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..6a42fb3\n--- /dev/null\n+++ b/lib/librte_gro/gro_vxlan_udp4.h\n@@ -0,0 +1,154 @@\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/* Note: It is unnecessary to save outer_src_port here because it can\n+\t * be different for VxLAN UDP fragments from the same flow.\n+\t */\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/* Note: VXLAN UDP/IPv4 GRO needn't check outer_ip_id because\n+\t * the difference between outer_ip_ids of two received packets\n+\t * isn't always +/-1 in case of OVS DPDK. So no outer_ip_id\n+\t * and outer_is_atomic fields here.\n+\t */\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 f623230..db990cf 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@@ -137,19 +148,27 @@ struct gro_ctx {\n \tstruct gro_udp4_item udp_items[RTE_GRO_MAX_BURST_ITEM_NUM] = {{0} };\n \n \t/* Allocate a reassembly table for VXLAN TCP GRO */\n-\tstruct gro_vxlan_tcp4_tbl vxlan_tbl;\n-\tstruct gro_vxlan_tcp4_flow vxlan_flows[RTE_GRO_MAX_BURST_ITEM_NUM];\n-\tstruct gro_vxlan_tcp4_item vxlan_items[RTE_GRO_MAX_BURST_ITEM_NUM]\n+\tstruct gro_vxlan_tcp4_tbl vxlan_tcp_tbl;\n+\tstruct gro_vxlan_tcp4_flow vxlan_tcp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];\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}} };\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_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@@ -160,15 +179,28 @@ struct gro_ctx {\n \n \tif (param->gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) {\n \t\tfor (i = 0; i < item_num; i++)\n-\t\t\tvxlan_flows[i].start_index = INVALID_ARRAY_INDEX;\n-\n-\t\tvxlan_tbl.flows = vxlan_flows;\n-\t\tvxlan_tbl.items = vxlan_items;\n-\t\tvxlan_tbl.flow_num = 0;\n-\t\tvxlan_tbl.item_num = 0;\n-\t\tvxlan_tbl.max_flow_num = item_num;\n-\t\tvxlan_tbl.max_item_num = item_num;\n-\t\tdo_vxlan_gro = 1;\n+\t\t\tvxlan_tcp_flows[i].start_index = INVALID_ARRAY_INDEX;\n+\n+\t\tvxlan_tcp_tbl.flows = vxlan_tcp_flows;\n+\t\tvxlan_tcp_tbl.items = vxlan_tcp_items;\n+\t\tvxlan_tcp_tbl.flow_num = 0;\n+\t\tvxlan_tcp_tbl.item_num = 0;\n+\t\tvxlan_tcp_tbl.max_flow_num = item_num;\n+\t\tvxlan_tcp_tbl.max_item_num = item_num;\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@@ -204,9 +236,18 @@ struct gro_ctx {\n \t\t * will be flushed from the tables.\n \t\t */\n \t\tif (IS_IPV4_VXLAN_TCP4_PKT(pkts[i]->packet_type) &&\n-\t\t\t\tdo_vxlan_gro) {\n+\t\t\t\tdo_vxlan_tcp_gro) {\n \t\t\tret = gro_vxlan_tcp4_reassemble(pkts[i],\n-\t\t\t\t\t\t\t&vxlan_tbl, 0);\n+\t\t\t\t\t\t\t&vxlan_tcp_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_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@@ -236,11 +277,17 @@ struct gro_ctx {\n \t\t || (unprocess_num < nb_pkts)) {\n \t\ti = 0;\n \t\t/* Flush all packets from the tables */\n-\t\tif (do_vxlan_gro) {\n-\t\t\ti = gro_vxlan_tcp4_tbl_timeout_flush(&vxlan_tbl,\n+\t\tif (do_vxlan_tcp_gro) {\n+\t\t\ti = gro_vxlan_tcp4_tbl_timeout_flush(&vxlan_tcp_tbl,\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,33 +316,42 @@ 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_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_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_tbl = gro_ctx->tbls[RTE_GRO_IPV4_VXLAN_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-\tdo_vxlan_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) ==\n+\tdo_vxlan_tcp_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) ==\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 \tfor (i = 0; i < nb_pkts; i++) {\n \t\tif (IS_IPV4_VXLAN_TCP4_PKT(pkts[i]->packet_type) &&\n-\t\t\t\tdo_vxlan_gro) {\n-\t\t\tif (gro_vxlan_tcp4_reassemble(pkts[i], vxlan_tbl,\n+\t\t\t\tdo_vxlan_tcp_gro) {\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@@ -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": [
        "v6",
        "2/3"
    ]
}