Skip to content

Commit

Permalink
Merge branch 'ipv6-gre-offloads'
Browse files Browse the repository at this point in the history
Alexander Duyck says:

====================
Add support for offloads with IPv6 GRE tunnels

This patch series enables the use of segmentation and checksum offloads
with IPv6 based GRE tunnels.

In order to enable this series I had to make a change to
iptunnel_handle_offloads so that it would no longer free the skb.  This was
necessary as there were multiple paths in the IPv6 GRE code that required
the skb to still be present so it could be freed.  As it turned out I
believe this actually fixes a bug that was present in FOU/GUE based tunnels
anyway.

Below is a quick breakdown of the performance gains seen with a simple
netperf test passing traffic through a ip6gretap tunnel and then an i40e
interface:

Throughput Throughput  Local Local   Result
           Units       CPU   Service Tag
                       Util  Demand
                       %
3544.93    10^6bits/s  6.30  4.656   "before"
13081.75   10^6bits/s  3.75  0.752   "after"
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Apr 16, 2016
2 parents ec9dcd3 + 3a80e1f commit ac97992
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 98 deletions.
32 changes: 12 additions & 20 deletions drivers/net/geneve.c
Original file line number Diff line number Diff line change
Expand Up @@ -696,16 +696,12 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
+ GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr);
err = skb_cow_head(skb, min_headroom);
if (unlikely(err)) {
kfree_skb(skb);
if (unlikely(err))
goto free_rt;
}

skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
err = udp_tunnel_handle_offloads(skb, udp_sum);
if (err)
goto free_rt;
}

gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
Expand Down Expand Up @@ -733,16 +729,12 @@ static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
err = skb_cow_head(skb, min_headroom);
if (unlikely(err)) {
kfree_skb(skb);
if (unlikely(err))
goto free_dst;
}

skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
err = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb))
goto free_dst;
}

gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
Expand Down Expand Up @@ -937,7 +929,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
err = geneve_build_skb(rt, skb, key->tun_flags, vni,
info->options_len, opts, flags, xnet);
if (unlikely(err))
goto err;
goto tx_error;

tos = ip_tunnel_ecn_encap(key->tos, iip, skb);
ttl = key->ttl;
Expand All @@ -946,7 +938,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
err = geneve_build_skb(rt, skb, 0, geneve->vni,
0, NULL, flags, xnet);
if (unlikely(err))
goto err;
goto tx_error;

tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, iip, skb);
ttl = geneve->ttl;
Expand All @@ -964,7 +956,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,

tx_error:
dev_kfree_skb(skb);
err:

if (err == -ELOOP)
dev->stats.collisions++;
else if (err == -ENETUNREACH)
Expand Down Expand Up @@ -1026,7 +1018,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
info->options_len, opts,
flags, xnet);
if (unlikely(err))
goto err;
goto tx_error;

prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
ttl = key->ttl;
Expand All @@ -1035,7 +1027,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
err = geneve6_build_skb(dst, skb, 0, geneve->vni,
0, NULL, flags, xnet);
if (unlikely(err))
goto err;
goto tx_error;

prio = ip_tunnel_ecn_encap(ip6_tclass(fl6.flowlabel),
iip, skb);
Expand All @@ -1054,7 +1046,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,

tx_error:
dev_kfree_skb(skb);
err:

if (err == -ELOOP)
dev->stats.collisions++;
else if (err == -ENETUNREACH)
Expand Down
6 changes: 3 additions & 3 deletions drivers/net/vxlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -1797,9 +1797,9 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst,
if (WARN_ON(!skb))
return -ENOMEM;

skb = iptunnel_handle_offloads(skb, type);
if (IS_ERR(skb))
return PTR_ERR(skb);
err = iptunnel_handle_offloads(skb, type);
if (err)
goto out_free;

vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
vxh->vx_flags = VXLAN_HF_VNI;
Expand Down
2 changes: 1 addition & 1 deletion include/net/ip_tunnels.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
gfp_t flags);

struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, int gso_type_mask);
int iptunnel_handle_offloads(struct sk_buff *skb, int gso_type_mask);

static inline int iptunnel_pull_offloads(struct sk_buff *skb)
{
Expand Down
3 changes: 1 addition & 2 deletions include/net/udp_tunnel.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ struct metadata_dst *udp_tun_rx_dst(struct sk_buff *skb, unsigned short family,
__be16 flags, __be64 tunnel_id,
int md_size);

static inline struct sk_buff *udp_tunnel_handle_offloads(struct sk_buff *skb,
bool udp_csum)
static inline int udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum)
{
int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;

Expand Down
16 changes: 8 additions & 8 deletions net/ipv4/fou.c
Original file line number Diff line number Diff line change
Expand Up @@ -802,11 +802,11 @@ int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
SKB_GSO_UDP_TUNNEL;
__be16 sport;
int err;

skb = iptunnel_handle_offloads(skb, type);

if (IS_ERR(skb))
return PTR_ERR(skb);
err = iptunnel_handle_offloads(skb, type);
if (err)
return err;

sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
skb, 0, 0, false);
Expand All @@ -826,6 +826,7 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
__be16 sport;
void *data;
bool need_priv = false;
int err;

if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) &&
skb->ip_summed == CHECKSUM_PARTIAL) {
Expand All @@ -836,10 +837,9 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,

optlen += need_priv ? GUE_LEN_PRIV : 0;

skb = iptunnel_handle_offloads(skb, type);

if (IS_ERR(skb))
return PTR_ERR(skb);
err = iptunnel_handle_offloads(skb, type);
if (err)
return err;

/* Get source port (based on flow hash) before skb_push */
sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
Expand Down
14 changes: 13 additions & 1 deletion net/ipv4/gre_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,18 @@ static const struct net_offload gre_offload = {

static int __init gre_offload_init(void)
{
return inet_add_offload(&gre_offload, IPPROTO_GRE);
int err;

err = inet_add_offload(&gre_offload, IPPROTO_GRE);
#if IS_ENABLED(CONFIG_IPV6)
if (err)
return err;

err = inet6_add_offload(&gre_offload, IPPROTO_GRE);
if (err)
inet_del_offload(&gre_offload, IPPROTO_GRE);
#endif

return err;
}
device_initcall(gre_offload_init);
20 changes: 6 additions & 14 deletions net/ipv4/ip_gre.c
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,7 @@ static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
ip_tunnel_xmit(skb, dev, tnl_params, tnl_params->protocol);
}

static struct sk_buff *gre_handle_offloads(struct sk_buff *skb,
bool csum)
static int gre_handle_offloads(struct sk_buff *skb, bool csum)
{
return iptunnel_handle_offloads(skb, csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
}
Expand Down Expand Up @@ -568,11 +567,8 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev)
}

/* Push Tunnel header. */
skb = gre_handle_offloads(skb, !!(tun_info->key.tun_flags & TUNNEL_CSUM));
if (IS_ERR(skb)) {
skb = NULL;
if (gre_handle_offloads(skb, !!(tun_info->key.tun_flags & TUNNEL_CSUM)))
goto err_free_rt;
}

flags = tun_info->key.tun_flags & (TUNNEL_CSUM | TUNNEL_KEY);
build_header(skb, tunnel_hlen, flags, htons(ETH_P_TEB),
Expand Down Expand Up @@ -640,16 +636,14 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
tnl_params = &tunnel->parms.iph;
}

skb = gre_handle_offloads(skb, !!(tunnel->parms.o_flags&TUNNEL_CSUM));
if (IS_ERR(skb))
goto out;
if (gre_handle_offloads(skb, !!(tunnel->parms.o_flags & TUNNEL_CSUM)))
goto free_skb;

__gre_xmit(skb, dev, tnl_params, skb->protocol);
return NETDEV_TX_OK;

free_skb:
kfree_skb(skb);
out:
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
Expand All @@ -664,9 +658,8 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}

skb = gre_handle_offloads(skb, !!(tunnel->parms.o_flags&TUNNEL_CSUM));
if (IS_ERR(skb))
goto out;
if (gre_handle_offloads(skb, !!(tunnel->parms.o_flags & TUNNEL_CSUM)))
goto free_skb;

if (skb_cow_head(skb, dev->needed_headroom))
goto free_skb;
Expand All @@ -676,7 +669,6 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,

free_skb:
kfree_skb(skb);
out:
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
Expand Down
13 changes: 5 additions & 8 deletions net/ipv4/ip_tunnel_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
}
EXPORT_SYMBOL_GPL(iptunnel_metadata_reply);

struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
int gso_type_mask)
int iptunnel_handle_offloads(struct sk_buff *skb,
int gso_type_mask)
{
int err;

Expand All @@ -159,9 +159,9 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
if (skb_is_gso(skb)) {
err = skb_unclone(skb, GFP_ATOMIC);
if (unlikely(err))
goto error;
return err;
skb_shinfo(skb)->gso_type |= gso_type_mask;
return skb;
return 0;
}

if (skb->ip_summed != CHECKSUM_PARTIAL) {
Expand All @@ -174,10 +174,7 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
skb->encapsulation = 0;
}

return skb;
error:
kfree_skb(skb);
return ERR_PTR(err);
return 0;
}
EXPORT_SYMBOL_GPL(iptunnel_handle_offloads);

Expand Down
7 changes: 3 additions & 4 deletions net/ipv4/ipip.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(skb->protocol != htons(ETH_P_IP)))
goto tx_error;

skb = iptunnel_handle_offloads(skb, SKB_GSO_IPIP);
if (IS_ERR(skb))
goto out;
if (iptunnel_handle_offloads(skb, SKB_GSO_IPIP))
goto tx_error;

skb_set_inner_ipproto(skb, IPPROTO_IPIP);

Expand All @@ -230,7 +229,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)

tx_error:
kfree_skb(skb);
out:

dev->stats.tx_errors++;
return NETDEV_TX_OK;
}
Expand Down
Loading

0 comments on commit ac97992

Please sign in to comment.