gso: support VXLAN UDP/IPv4

As most NICs do not support segmentation for VXLAN-encapsulated
UDP/IPv4 packets, this patch adds VXLAN UDP/IPv4 GSO support.

Signed-off-by: Yi Yang <yangyi01@inspur.com>
Acked-by: Jiayu Hu <jiayu.hu@intel.com>
This commit is contained in:
Yi Yang 2020-11-19 14:43:31 +08:00 committed by Thomas Monjalon
parent b9d60b5434
commit 76f093948f
7 changed files with 168 additions and 10 deletions

View File

@ -45,8 +45,8 @@ Limitations
- TCP - TCP
- UDP - UDP
- VxLAN - VXLAN
- GRE - GRE TCP
See `Supported GSO Packet Types`_ for further details. See `Supported GSO Packet Types`_ for further details.
@ -157,14 +157,14 @@ does not modify it during segmentation. Therefore, after UDP GSO, only the
first output packet has the original UDP header, and others just have l2 first output packet has the original UDP header, and others just have l2
and l3 headers. and l3 headers.
VxLAN GSO VXLAN IPv4 GSO
~~~~~~~~~ ~~~~~~~~~~~~~~
VxLAN packets GSO supports segmentation of suitably large VxLAN packets, VXLAN packets GSO supports segmentation of suitably large VXLAN packets,
which contain an outer IPv4 header, inner TCP/IPv4 headers, and optional which contain an outer IPv4 header, inner TCP/IPv4 or UDP/IPv4 headers, and
inner and/or outer VLAN tag(s). optional inner and/or outer VLAN tag(s).
GRE GSO GRE TCP/IPv4 GSO
~~~~~~~ ~~~~~~~~~~~~~~~~
GRE GSO supports segmentation of suitably large GRE packets, which contain GRE GSO supports segmentation of suitably large GRE packets, which contain
an outer IPv4 header, inner TCP/IPv4 headers, and an optional VLAN tag. an outer IPv4 header, inner TCP/IPv4 headers, and an optional VLAN tag.

View File

@ -67,6 +67,10 @@ New Features
* Introduced basic support on Windows. * Introduced basic support on Windows.
* **Updated GSO support.**
* Added inner UDP/IPv4 support for VXLAN IPv4 GSO.
Removed Items Removed Items
------------- -------------

View File

@ -26,6 +26,11 @@
(PKT_TX_TCP_SEG | PKT_TX_IPV4 | PKT_TX_OUTER_IPV4 | \ (PKT_TX_TCP_SEG | PKT_TX_IPV4 | PKT_TX_OUTER_IPV4 | \
PKT_TX_TUNNEL_VXLAN)) PKT_TX_TUNNEL_VXLAN))
#define IS_IPV4_VXLAN_UDP4(flag) (((flag) & (PKT_TX_UDP_SEG | PKT_TX_IPV4 | \
PKT_TX_OUTER_IPV4 | PKT_TX_TUNNEL_MASK)) == \
(PKT_TX_UDP_SEG | PKT_TX_IPV4 | PKT_TX_OUTER_IPV4 | \
PKT_TX_TUNNEL_VXLAN))
#define IS_IPV4_GRE_TCP4(flag) (((flag) & (PKT_TX_TCP_SEG | PKT_TX_IPV4 | \ #define IS_IPV4_GRE_TCP4(flag) (((flag) & (PKT_TX_TCP_SEG | PKT_TX_IPV4 | \
PKT_TX_OUTER_IPV4 | PKT_TX_TUNNEL_MASK)) == \ PKT_TX_OUTER_IPV4 | PKT_TX_TUNNEL_MASK)) == \
(PKT_TX_TCP_SEG | PKT_TX_IPV4 | PKT_TX_OUTER_IPV4 | \ (PKT_TX_TCP_SEG | PKT_TX_IPV4 | PKT_TX_OUTER_IPV4 | \

View File

@ -0,0 +1,97 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2020 Inspur Corporation
*/
#include "gso_common.h"
#include "gso_tunnel_udp4.h"
#define IPV4_HDR_MF_BIT (1U << 13)
static void
update_tunnel_ipv4_udp_headers(struct rte_mbuf *pkt, struct rte_mbuf **segs,
uint16_t nb_segs)
{
struct rte_ipv4_hdr *ipv4_hdr;
uint16_t outer_id, inner_id, tail_idx, i, length;
uint16_t outer_ipv4_offset, inner_ipv4_offset;
uint16_t outer_udp_offset;
uint16_t frag_offset = 0, is_mf;
outer_ipv4_offset = pkt->outer_l2_len;
outer_udp_offset = outer_ipv4_offset + pkt->outer_l3_len;
inner_ipv4_offset = outer_udp_offset + pkt->l2_len;
/* Outer IPv4 header. */
ipv4_hdr = (struct rte_ipv4_hdr *)(rte_pktmbuf_mtod(pkt, char *) +
outer_ipv4_offset);
outer_id = rte_be_to_cpu_16(ipv4_hdr->packet_id);
/* Inner IPv4 header. */
ipv4_hdr = (struct rte_ipv4_hdr *)(rte_pktmbuf_mtod(pkt, char *) +
inner_ipv4_offset);
inner_id = rte_be_to_cpu_16(ipv4_hdr->packet_id);
tail_idx = nb_segs - 1;
for (i = 0; i < nb_segs; i++) {
update_ipv4_header(segs[i], outer_ipv4_offset, outer_id);
update_udp_header(segs[i], outer_udp_offset);
update_ipv4_header(segs[i], inner_ipv4_offset, inner_id);
/* For the case inner packet is UDP, we must keep UDP
* datagram boundary, it must be handled as IP fragment.
*
* Set IP fragment offset for inner IP header.
*/
ipv4_hdr = (struct rte_ipv4_hdr *)
(rte_pktmbuf_mtod(segs[i], char *) +
inner_ipv4_offset);
is_mf = i < tail_idx ? IPV4_HDR_MF_BIT : 0;
ipv4_hdr->fragment_offset =
rte_cpu_to_be_16(frag_offset | is_mf);
length = segs[i]->pkt_len - inner_ipv4_offset - pkt->l3_len;
frag_offset += (length >> 3);
outer_id++;
}
}
int
gso_tunnel_udp4_segment(struct rte_mbuf *pkt,
uint16_t gso_size,
struct rte_mempool *direct_pool,
struct rte_mempool *indirect_pool,
struct rte_mbuf **pkts_out,
uint16_t nb_pkts_out)
{
struct rte_ipv4_hdr *inner_ipv4_hdr;
uint16_t pyld_unit_size, hdr_offset, frag_off;
int ret;
hdr_offset = pkt->outer_l2_len + pkt->outer_l3_len + pkt->l2_len;
inner_ipv4_hdr = (struct rte_ipv4_hdr *)(rte_pktmbuf_mtod(pkt, char *) +
hdr_offset);
/*
* Don't process the packet whose MF bit or offset in the inner
* IPv4 header are non-zero.
*/
frag_off = rte_be_to_cpu_16(inner_ipv4_hdr->fragment_offset);
if (unlikely(IS_FRAGMENTED(frag_off)))
return 0;
hdr_offset += pkt->l3_len;
/* Don't process the packet without data */
if ((hdr_offset + pkt->l4_len) >= pkt->pkt_len)
return 0;
/* pyld_unit_size must be a multiple of 8 because frag_off
* uses 8 bytes as unit.
*/
pyld_unit_size = (gso_size - hdr_offset) & ~7U;
/* Segment the payload */
ret = gso_do_segment(pkt, hdr_offset, pyld_unit_size, direct_pool,
indirect_pool, pkts_out, nb_pkts_out);
if (ret > 1)
update_tunnel_ipv4_udp_headers(pkt, pkts_out, ret);
return ret;
}

View File

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2020 Inspur Corporation
*/
#ifndef _GSO_TUNNEL_UDP4_H_
#define _GSO_TUNNEL_UDP4_H_
#include <stdint.h>
#include <rte_mbuf.h>
/**
* Segment a tunneling packet with inner UDP/IPv4 headers. This function
* does not check if the input packet has correct checksums, and does not
* update checksums for output GSO segments. Furthermore, it does not
* process IP fragment packets.
*
* @param pkt
* The packet mbuf to segment.
* @param gso_size
* The max length of a GSO segment, measured in bytes.
* @param direct_pool
* MBUF pool used for allocating direct buffers for output segments.
* @param indirect_pool
* MBUF pool used for allocating indirect buffers for output segments.
* @param pkts_out
* Pointer array used to store the MBUF addresses of output GSO
* segments, when it succeeds. If the memory space in pkts_out is
* insufficient, it fails and returns -EINVAL.
* @param nb_pkts_out
* The max number of items that 'pkts_out' can keep.
*
* @return
* - The number of GSO segments filled in pkts_out on success.
* - Return 0 if it needn't GSO.
* - Return -ENOMEM if run out of memory in MBUF pools.
* - Return -EINVAL for invalid parameters.
*/
int gso_tunnel_udp4_segment(struct rte_mbuf *pkt,
uint16_t gso_size,
struct rte_mempool *direct_pool,
struct rte_mempool *indirect_pool,
struct rte_mbuf **pkts_out,
uint16_t nb_pkts_out);
#endif

View File

@ -2,6 +2,6 @@
# Copyright(c) 2017 Intel Corporation # Copyright(c) 2017 Intel Corporation
sources = files('gso_common.c', 'gso_tcp4.c', 'gso_udp4.c', sources = files('gso_common.c', 'gso_tcp4.c', 'gso_udp4.c',
'gso_tunnel_tcp4.c', 'rte_gso.c') 'gso_tunnel_tcp4.c', 'gso_tunnel_udp4.c', 'rte_gso.c')
headers = files('rte_gso.h') headers = files('rte_gso.h')
deps += ['ethdev'] deps += ['ethdev']

View File

@ -11,6 +11,7 @@
#include "gso_common.h" #include "gso_common.h"
#include "gso_tcp4.h" #include "gso_tcp4.h"
#include "gso_tunnel_tcp4.h" #include "gso_tunnel_tcp4.h"
#include "gso_tunnel_udp4.h"
#include "gso_udp4.h" #include "gso_udp4.h"
#define ILLEGAL_UDP_GSO_CTX(ctx) \ #define ILLEGAL_UDP_GSO_CTX(ctx) \
@ -60,6 +61,13 @@ rte_gso_segment(struct rte_mbuf *pkt,
ret = gso_tunnel_tcp4_segment(pkt, gso_size, ipid_delta, ret = gso_tunnel_tcp4_segment(pkt, gso_size, ipid_delta,
direct_pool, indirect_pool, direct_pool, indirect_pool,
pkts_out, nb_pkts_out); pkts_out, nb_pkts_out);
} else if (IS_IPV4_VXLAN_UDP4(pkt->ol_flags) &&
(gso_ctx->gso_types & DEV_TX_OFFLOAD_VXLAN_TNL_TSO) &&
(gso_ctx->gso_types & DEV_TX_OFFLOAD_UDP_TSO)) {
pkt->ol_flags &= (~PKT_TX_UDP_SEG);
ret = gso_tunnel_udp4_segment(pkt, gso_size,
direct_pool, indirect_pool,
pkts_out, nb_pkts_out);
} else if (IS_IPV4_TCP(pkt->ol_flags) && } else if (IS_IPV4_TCP(pkt->ol_flags) &&
(gso_ctx->gso_types & DEV_TX_OFFLOAD_TCP_TSO)) { (gso_ctx->gso_types & DEV_TX_OFFLOAD_TCP_TSO)) {
pkt->ol_flags &= (~PKT_TX_TCP_SEG); pkt->ol_flags &= (~PKT_TX_TCP_SEG);