summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-09-02 04:36:35 (GMT)
committerDavid S. Miller <davem@davemloft.net>2014-09-02 04:36:35 (GMT)
commitc5a65680b3c29ddf8f4a0bfb4ba75ea230735a38 (patch)
tree134db2f49c45f69e06f625d3d6c6924d5cfa4c2d /net
parent52aec126c42819899793b9ffbd3f2e25705ba096 (diff)
parent72297c59f7188d12f24daee1877e7dcca787cf1f (diff)
downloadlinux-c5a65680b3c29ddf8f4a0bfb4ba75ea230735a38.tar.xz
Merge branch 'csums-next'
Tom Herbert says: ==================== net: Checksum offload changes - Part VI I am working on overhauling RX checksum offload. Goals of this effort are: - Specify what exactly it means when driver returns CHECKSUM_UNNECESSARY - Preserve CHECKSUM_COMPLETE through encapsulation layers - Don't do skb_checksum more than once per packet - Unify GRO and non-GRO csum verification as much as possible - Unify the checksum functions (checksum_init) - Simplify code What is in this seventh patch set: - Add skb->csum. This allows a device or GRO to indicate that an invalid checksum was detected. - Checksum unncessary to checksum complete conversions. With these changes, I believe that the third goal of the overhaul is now mostly achieved. In the case of no encapsulation or one layer of encapsulation, there should only be at most one skb_checksum over each packet (between GRO and normal path). In the case of two layers of encapsulation, it is still possible with the right combination of non-zero and zero UDP checksums to have >1 skb_checksum. For instance: IP>GRE(with csum)>IP>UDP(zero csum)>VXLAN>IP>UDP(non-zero csum), would likely necessiate an skb_checksum in GRO and normal path. This doesn't seem like a common scenario at all so I'm inclined to not address this now, if multiple layers of encapsulation becomes popular we can reassess. Note that checksum conversion shows a nice improvement for RX VXLAN when outer UDP checksum is enabled (12.65% CPU compared to 20.94%). This is not only from the fact that we don't need checksum calculation on the host, but also allows GRO for VXLAN in this case. Checksum conversion does not help send side (which still needs to perform a checksum on host). For that we will implement remote checksum offload in a later patch (http://tools.ietf.org/html/draft-herbert-remotecsumoffload-00). Please review carefully and test if possible, mucking with basic checksum functions is always a little precarious :-) ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/dev.c2
-rw-r--r--net/ipv4/gre_demux.c4
-rw-r--r--net/ipv4/gre_offload.c8
-rw-r--r--net/ipv4/udp.c4
-rw-r--r--net/ipv4/udp_offload.c25
-rw-r--r--net/ipv6/udp.c4
-rw-r--r--net/ipv6/udp_offload.c24
-rw-r--r--net/l2tp/l2tp_core.c2
8 files changed, 55 insertions, 18 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 6857d57..3774afc 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3918,7 +3918,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
if (!(skb->dev->features & NETIF_F_GRO))
goto normal;
- if (skb_is_gso(skb) || skb_has_frag_list(skb))
+ if (skb_is_gso(skb) || skb_has_frag_list(skb) || skb->csum_bad)
goto normal;
gro_list_prepare(napi, skb);
diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c
index 0485bf7..7e0756d 100644
--- a/net/ipv4/gre_demux.c
+++ b/net/ipv4/gre_demux.c
@@ -125,6 +125,10 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
*csum_err = true;
return -EINVAL;
}
+
+ skb_checksum_try_convert(skb, IPPROTO_GRE, 0,
+ null_compute_pseudo);
+
options++;
}
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
index a4d7965..d3fe2ac 100644
--- a/net/ipv4/gre_offload.c
+++ b/net/ipv4/gre_offload.c
@@ -172,10 +172,14 @@ static struct sk_buff **gre_gro_receive(struct sk_buff **head,
}
/* Don't bother verifying checksum if we're going to flush anyway. */
- if ((greh->flags & GRE_CSUM) && !NAPI_GRO_CB(skb)->flush &&
- skb_gro_checksum_simple_validate(skb))
+ if ((greh->flags & GRE_CSUM) && !NAPI_GRO_CB(skb)->flush) {
+ if (skb_gro_checksum_simple_validate(skb))
goto out_unlock;
+ skb_gro_checksum_try_convert(skb, IPPROTO_GRE, 0,
+ null_compute_pseudo);
+ }
+
flush = 0;
for (p = *head; p; p = p->next) {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 3549c21..0da3849 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1788,6 +1788,10 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
if (sk != NULL) {
int ret;
+ if (udp_sk(sk)->convert_csum && uh->check && !IS_UDPLITE(sk))
+ skb_checksum_try_convert(skb, IPPROTO_UDP, uh->check,
+ inet_compute_pseudo);
+
ret = udp_queue_rcv_skb(sk, skb);
sock_put(sk);
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index a6adff9..84e0e05 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -290,16 +290,25 @@ static struct sk_buff **udp4_gro_receive(struct sk_buff **head,
{
struct udphdr *uh = udp_gro_udphdr(skb);
- /* Don't bother verifying checksum if we're going to flush anyway. */
- if (unlikely(!uh) ||
- (!NAPI_GRO_CB(skb)->flush &&
- skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check,
- inet_gro_compute_pseudo))) {
- NAPI_GRO_CB(skb)->flush = 1;
- return NULL;
- }
+ if (unlikely(!uh))
+ goto flush;
+ /* Don't bother verifying checksum if we're going to flush anyway. */
+ if (!NAPI_GRO_CB(skb)->flush)
+ goto skip;
+
+ if (skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check,
+ inet_gro_compute_pseudo))
+ goto flush;
+ else if (uh->check)
+ skb_gro_checksum_try_convert(skb, IPPROTO_UDP, uh->check,
+ inet_gro_compute_pseudo);
+skip:
return udp_gro_receive(head, skb, uh);
+
+flush:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
}
int udp_gro_complete(struct sk_buff *skb, int nhoff)
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 12fcce8f..f6ba535 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -891,6 +891,10 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
goto csum_error;
}
+ if (udp_sk(sk)->convert_csum && uh->check && !IS_UDPLITE(sk))
+ skb_checksum_try_convert(skb, IPPROTO_UDP, uh->check,
+ ip6_compute_pseudo);
+
ret = udpv6_queue_rcv_skb(sk, skb);
sock_put(sk);
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index b13e377..89cb9a9 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -134,16 +134,26 @@ static struct sk_buff **udp6_gro_receive(struct sk_buff **head,
{
struct udphdr *uh = udp_gro_udphdr(skb);
+ if (unlikely(!uh))
+ goto flush;
+
/* Don't bother verifying checksum if we're going to flush anyway. */
- if (unlikely(!uh) ||
- (!NAPI_GRO_CB(skb)->flush &&
- skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check,
- ip6_gro_compute_pseudo))) {
- NAPI_GRO_CB(skb)->flush = 1;
- return NULL;
- }
+ if (!NAPI_GRO_CB(skb)->flush)
+ goto skip;
+ if (skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check,
+ ip6_gro_compute_pseudo))
+ goto flush;
+ else if (uh->check)
+ skb_gro_checksum_try_convert(skb, IPPROTO_UDP, uh->check,
+ ip6_gro_compute_pseudo);
+
+skip:
return udp_gro_receive(head, skb, uh);
+
+flush:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
}
int udp6_gro_complete(struct sk_buff *skb, int nhoff)
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 1109d3b..797c0af 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1392,6 +1392,8 @@ static int l2tp_tunnel_sock_create(struct net *net,
if (err < 0)
goto out;
+ udp_set_convert_csum(sock->sk, true);
+
break;
case L2TP_ENCAPTYPE_IP: