Skip to content

Commit

Permalink
datapath: Enable OVS GSO to be used up to 3.18 if necessary.
Browse files Browse the repository at this point in the history
There are two important GSO tunnel features that were introduced
after the 3.12 cutoff for our current out of tree GSO implementation:
 * 3.16 introduced support for outer UDP checksums.
 * 3.18 introduced support for verifying hardware support for protocols
   other than VXLAN.

In cases where these features are used, we should use OVS GSO to
ensure correct behavior. However, we also want to continue to use
kernel GSO or hardware TSO in existing situations. Therefore, this
extends the range of kernels where OVS GSO is available to 3.18 and
makes it easier to select which one to use.

Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Thomas Graf <tgraf@noironetworks.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
  • Loading branch information
jessegross committed Feb 20, 2015
1 parent cabd551 commit 9ffdbf4
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 170 deletions.
4 changes: 0 additions & 4 deletions acinclude.m4
Expand Up @@ -365,9 +365,6 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [netlink_has_listeners(net->genl_sock],
[OVS_DEFINE([HAVE_GENL_HAS_LISTENERS_TAKES_NET])])
OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_cisco_register])
OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_handle_offloads])
OVS_GREP_IFELSE([$KSRC/include/net/ip_tunnels.h], [iptunnel_xmit.*net],
[OVS_DEFINE([HAVE_IPTUNNEL_XMIT_NET])])
OVS_GREP_IFELSE([$KSRC/include/net/ipv6.h], [IP6_FH_F_SKIP_RH])
OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_get_be16])
OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be16])
Expand All @@ -388,7 +385,6 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
OVS_GREP_IFELSE([$KSRC/include/linux/openvswitch.h], [openvswitch_handle_frame_hook],
[OVS_DEFINE([HAVE_RHEL_OVS_HOOK])])
OVS_GREP_IFELSE([$KSRC/include/net/vxlan.h], [vxlan_xmit_skb])
OVS_GREP_IFELSE([$KSRC/include/net/vxlan.h], [struct vxlan_metadata],
[OVS_DEFINE([HAVE_VXLAN_METADATA])])
OVS_GREP_IFELSE([$KSRC/include/net/udp.h], [udp_flow_src_port],
Expand Down
11 changes: 5 additions & 6 deletions datapath/linux/compat/gre.c
Expand Up @@ -268,9 +268,9 @@ int gre_cisco_unregister(struct gre_cisco_protocol *proto)

#endif /* !HAVE_GRE_CISCO_REGISTER */

#ifndef USE_KERNEL_TUNNEL_API

/* GRE TX side. */
static void gre_nop_fix(struct sk_buff *skb) { }

static void gre_csum_fix(struct sk_buff *skb)
{
struct gre_base_hdr *greh;
Expand All @@ -287,15 +287,15 @@ static void gre_csum_fix(struct sk_buff *skb)

struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum)
{
int type = gre_csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE;
gso_fix_segment_t fix_segment;

if (gre_csum)
fix_segment = gre_csum_fix;
else
fix_segment = NULL;
fix_segment = gre_nop_fix;

skb_reset_inner_headers(skb);
return ovs_iptunnel_handle_offloads(skb, gre_csum, fix_segment);
return ovs_iptunnel_handle_offloads(skb, gre_csum, type, fix_segment);
}

static bool is_gre_gso(struct sk_buff *skb)
Expand Down Expand Up @@ -332,7 +332,6 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
}
}
}
#endif

#endif /* CONFIG_NET_IPGRE_DEMUX */

Expand Down
95 changes: 23 additions & 72 deletions datapath/linux/compat/gso.c
Expand Up @@ -167,7 +167,9 @@ int rpl_dev_queue_xmit(struct sk_buff *skb)
kfree_skb(skb);
return err;
}
#endif /* 3.16 */

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
static __be16 __skb_network_protocol(struct sk_buff *skb)
{
__be16 type = skb->protocol;
Expand All @@ -190,16 +192,6 @@ static __be16 __skb_network_protocol(struct sk_buff *skb)
return type;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0)
static void tnl_fix_segment(struct sk_buff *skb)
{
if (OVS_GSO_CB(skb)->fix_segment)
OVS_GSO_CB(skb)->fix_segment(skb);
}
#else
static void tnl_fix_segment(struct sk_buff *skb) { }
#endif

static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
netdev_features_t features,
bool tx_path)
Expand Down Expand Up @@ -240,7 +232,7 @@ static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,

memcpy(ip_hdr(skb), iph, pkt_hlen);
memcpy(skb->cb, cb, sizeof(cb));
tnl_fix_segment(skb);
OVS_GSO_CB(skb)->fix_segment(skb);

skb->protocol = proto;
skb = skb->next;
Expand All @@ -250,11 +242,29 @@ static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
return segs;
}

static int output_ip(struct sk_buff *skb)
{
int ret = NETDEV_TX_OK;
int err;

memset(IPCB(skb), 0, sizeof(*IPCB(skb)));

#undef ip_local_out
err = ip_local_out(skb);
if (unlikely(net_xmit_eval(err)))
ret = err;

return ret;
}

int rpl_ip_local_out(struct sk_buff *skb)
{
int ret = NETDEV_TX_OK;
int id = -1;

if (!OVS_GSO_CB(skb)->fix_segment)
return output_ip(skb);

if (skb_is_gso(skb)) {
struct iphdr *iph;

Expand All @@ -274,75 +284,16 @@ int rpl_ip_local_out(struct sk_buff *skb)
while (skb) {
struct sk_buff *next_skb = skb->next;
struct iphdr *iph;
int err;

skb->next = NULL;

iph = ip_hdr(skb);
if (id >= 0)
iph->id = htons(id++);

memset(IPCB(skb), 0, sizeof(*IPCB(skb)));

#undef ip_local_out
err = ip_local_out(skb);
if (unlikely(net_xmit_eval(err)))
ret = err;

ret = output_ip(skb);
skb = next_skb;
}
return ret;
}
#endif /* 3.16 */

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0) || \
!defined USE_UPSTREAM_VXLAN
struct sk_buff *ovs_iptunnel_handle_offloads(struct sk_buff *skb,
bool csum_help,
void (*fix_segment)(struct sk_buff *))
{
int err;

/* XXX: synchronize inner header reset for compat and non compat code
* so that we can do it here.
*/
/*
skb_reset_inner_headers(skb);
*/

/* OVS compat code does not maintain encapsulation bit.
* skb->encapsulation = 1; */

if (skb_is_gso(skb)) {
if (skb_is_encapsulated(skb)) {
err = -ENOSYS;
goto error;
}

OVS_GSO_CB(skb)->fix_segment = fix_segment;
return skb;
}

/* If packet is not gso and we are resolving any partial checksum,
* clear encapsulation flag. This allows setting CHECKSUM_PARTIAL
* on the outer header without confusing devices that implement
* NETIF_F_IP_CSUM with encapsulation.
*/
/*
if (csum_help)
skb->encapsulation = 0;
*/

if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) {
err = skb_checksum_help(skb);
if (unlikely(err))
goto error;
} else if (skb->ip_summed != CHECKSUM_PARTIAL)
skb->ip_summed = CHECKSUM_NONE;

return skb;
error:
kfree_skb(skb);
return ERR_PTR(err);
}
#endif /* 3.12 || !USE_UPSTREAM_VXLAN */
#endif /* 3.18 */
85 changes: 42 additions & 43 deletions datapath/linux/compat/gso.h
Expand Up @@ -2,8 +2,7 @@
#define __LINUX_GSO_WRAPPER_H

#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0) || \
!defined USE_UPSTREAM_VXLAN
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)

#include <linux/netdevice.h>
#include <linux/skbuff.h>
Expand All @@ -15,75 +14,55 @@ typedef void (*gso_fix_segment_t)(struct sk_buff *);
struct ovs_gso_cb {
struct ovs_skb_cb dp_cb;
gso_fix_segment_t fix_segment;
sk_buff_data_t inner_mac_header; /* Offset from skb->head */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
__be16 inner_protocol;
#endif
u16 inner_network_header; /* Offset from
* inner_mac_header */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
unsigned int inner_mac_header;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
unsigned int inner_network_header;
#endif
};
#define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb)

#define skb_inner_network_header rpl_skb_inner_network_header
#endif

#ifdef NET_SKBUFF_DATA_USES_OFFSET
#define skb_inner_mac_header rpl_skb_inner_mac_header
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
{
return skb->head + OVS_GSO_CB(skb)->inner_mac_header;
}

#else

#define skb_inner_mac_header rpl_skb_inner_mac_header
static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
static inline void skb_set_inner_mac_header(const struct sk_buff *skb,
int offset)
{
return OVS_GSO_CB(skb)->inner_mac_header;
OVS_GSO_CB(skb)->inner_mac_header = (skb->data - skb->head) + offset;
}

#endif

#define skb_inner_network_header rpl_skb_inner_network_header
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
{
return skb_inner_mac_header(skb) +
OVS_GSO_CB(skb)->inner_network_header;
return skb->head + OVS_GSO_CB(skb)->inner_network_header;
}

#define skb_inner_network_offset rpl_skb_inner_network_offset
static inline int skb_inner_network_offset(const struct sk_buff *skb)
{
return skb_inner_network_header(skb) - skb->data;
}

#define skb_reset_inner_headers rpl_skb_reset_inner_headers
static inline void skb_reset_inner_headers(struct sk_buff *skb)
static inline void skb_set_inner_network_header(const struct sk_buff *skb,
int offset)
{
BUILD_BUG_ON(sizeof(struct ovs_gso_cb) > FIELD_SIZEOF(struct sk_buff, cb));
OVS_GSO_CB(skb)->inner_network_header = skb->network_header -
skb->mac_header;
OVS_GSO_CB(skb)->inner_mac_header = skb->mac_header;

OVS_GSO_CB(skb)->fix_segment = NULL;
OVS_GSO_CB(skb)->inner_network_header = (skb->data - skb->head)
+ offset;
}

struct sk_buff *ovs_iptunnel_handle_offloads(struct sk_buff *skb,
bool csum_help,
gso_fix_segment_t fix_segment);


#endif /* 3.12 || !USE_UPSTREAM_VXLAN */

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
#define ip_local_out rpl_ip_local_out
int ip_local_out(struct sk_buff *skb);

#define skb_inner_mac_offset rpl_skb_inner_mac_offset
static inline int skb_inner_mac_offset(const struct sk_buff *skb)
{
return skb_inner_mac_header(skb) - skb->data;
}
#endif /* 3.16 */
static inline void skb_set_inner_transport_header(const struct sk_buff *skb,
int offset)
{ }
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) {
Expand Down Expand Up @@ -128,4 +107,24 @@ static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
return skb->inner_protocol;
}
#endif /* 3.11 */

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
#define ip_local_out rpl_ip_local_out
int ip_local_out(struct sk_buff *skb);

static inline int skb_inner_mac_offset(const struct sk_buff *skb)
{
return skb_inner_mac_header(skb) - skb->data;
}

#define skb_reset_inner_headers rpl_skb_reset_inner_headers
static inline void skb_reset_inner_headers(struct sk_buff *skb)
{
BUILD_BUG_ON(sizeof(struct ovs_gso_cb) > FIELD_SIZEOF(struct sk_buff, cb));
skb_set_inner_mac_header(skb, skb_mac_header(skb) - skb->data);
skb_set_inner_network_header(skb, skb_network_offset(skb));
skb_set_inner_transport_header(skb, skb_transport_offset(skb));
}
#endif /* 3.18 */

#endif
8 changes: 8 additions & 0 deletions datapath/linux/compat/include/linux/skbuff.h
Expand Up @@ -6,6 +6,14 @@
#include <linux/jhash.h>
#include <linux/version.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
#define SKB_GSO_GRE 0
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
#define SKB_GSO_GRE_CSUM 0
#endif

#ifndef HAVE_IGNORE_DF_RENAME
#define ignore_df local_df
#endif
Expand Down
2 changes: 1 addition & 1 deletion datapath/linux/compat/include/net/gre.h
Expand Up @@ -81,7 +81,7 @@ static inline __be16 tnl_flags_to_gre_flags(__be16 tflags)
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) */
#endif /* HAVE_GRE_CISCO_REGISTER */

#ifndef USE_KERNEL_TUNNEL_API
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0)

#define gre_build_header rpl_gre_build_header
void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
Expand Down

0 comments on commit 9ffdbf4

Please sign in to comment.