summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-06-15 12:00:56 +0400
committerDavid S. Miller <davem@davemloft.net>2014-06-15 12:00:56 +0400
commite4f7ae930afafd4d9d67449657fefe51010c3349 (patch)
treee1b1b85a1b30f00acf147818537b7e7bd7b3b86c
parent63c6f81cdde58c41da62a8d8a209592e42a0203e (diff)
parentf79b064c15068176e3f6f67715aafd7fe183120c (diff)
downloadlinux-e4f7ae930afafd4d9d67449657fefe51010c3349.tar.xz
Merge branch 'csum_fixes'
Tom Herbert says: ==================== Fixes related to some recent checksum modifications. - Fix GSO constants to match NETIF flags - Fix logic in saving checksum complete in __skb_checksum_complete - Call __skb_checksum_complete from UDP if we are checksumming over whole packet in order to save checksum. - Fixes to VXLAN to work correctly with checksum complete ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/vxlan.c11
-rw-r--r--include/linux/netdev_features.h1
-rw-r--r--include/linux/netdevice.h7
-rw-r--r--include/linux/skbuff.h23
-rw-r--r--include/net/udp.h4
-rw-r--r--net/core/datagram.c36
-rw-r--r--net/core/skbuff.c3
7 files changed, 60 insertions, 25 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index e0995ffd046d..ade33ef82823 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1156,15 +1156,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
if (!vs)
goto drop;
- /* If the NIC driver gave us an encapsulated packet
- * with the encapsulation mark, the device checksummed it
- * for us. Otherwise force the upper layers to verify it.
- */
- if ((skb->ip_summed != CHECKSUM_UNNECESSARY && skb->ip_summed != CHECKSUM_PARTIAL) ||
- !skb->encapsulation)
- skb->ip_summed = CHECKSUM_NONE;
-
- skb->encapsulation = 0;
+ skb_pop_rcv_encapsulation(skb);
vs->rcv(vs, skb, vxh->vx_vni);
return 0;
@@ -1201,6 +1193,7 @@ static void vxlan_rcv(struct vxlan_sock *vs,
skb_reset_mac_header(skb);
skb_scrub_packet(skb, !net_eq(vxlan->net, dev_net(vxlan->dev)));
skb->protocol = eth_type_trans(skb, vxlan->dev);
+ skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
/* Ignore packet loops (and multicast echo) */
if (ether_addr_equal(eth_hdr(skb)->h_source, vxlan->dev->dev_addr))
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index e5a589435e2b..d99800cbdcf3 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -117,6 +117,7 @@ enum {
#define NETIF_F_GSO_IPIP __NETIF_F(GSO_IPIP)
#define NETIF_F_GSO_SIT __NETIF_F(GSO_SIT)
#define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
+#define NETIF_F_GSO_UDP_TUNNEL_CSUM __NETIF_F(GSO_UDP_TUNNEL_CSUM)
#define NETIF_F_GSO_MPLS __NETIF_F(GSO_MPLS)
#define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
#define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index abe3de1db932..66f9a04ec270 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3305,6 +3305,13 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type)
BUILD_BUG_ON(SKB_GSO_TCP_ECN != (NETIF_F_TSO_ECN >> NETIF_F_GSO_SHIFT));
BUILD_BUG_ON(SKB_GSO_TCPV6 != (NETIF_F_TSO6 >> NETIF_F_GSO_SHIFT));
BUILD_BUG_ON(SKB_GSO_FCOE != (NETIF_F_FSO >> NETIF_F_GSO_SHIFT));
+ BUILD_BUG_ON(SKB_GSO_GRE != (NETIF_F_GSO_GRE >> NETIF_F_GSO_SHIFT));
+ BUILD_BUG_ON(SKB_GSO_GRE_CSUM != (NETIF_F_GSO_GRE_CSUM >> NETIF_F_GSO_SHIFT));
+ BUILD_BUG_ON(SKB_GSO_IPIP != (NETIF_F_GSO_IPIP >> NETIF_F_GSO_SHIFT));
+ BUILD_BUG_ON(SKB_GSO_SIT != (NETIF_F_GSO_SIT >> NETIF_F_GSO_SHIFT));
+ BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL != (NETIF_F_GSO_UDP_TUNNEL >> NETIF_F_GSO_SHIFT));
+ BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >> NETIF_F_GSO_SHIFT));
+ BUILD_BUG_ON(SKB_GSO_MPLS != (NETIF_F_GSO_MPLS >> NETIF_F_GSO_SHIFT));
return (features & feature) == feature;
}
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 5b5cd3189c98..ec89301ada41 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -338,17 +338,18 @@ enum {
SKB_GSO_GRE = 1 << 6,
- SKB_GSO_IPIP = 1 << 7,
+ SKB_GSO_GRE_CSUM = 1 << 7,
- SKB_GSO_SIT = 1 << 8,
+ SKB_GSO_IPIP = 1 << 8,
- SKB_GSO_UDP_TUNNEL = 1 << 9,
+ SKB_GSO_SIT = 1 << 9,
- SKB_GSO_MPLS = 1 << 10,
+ SKB_GSO_UDP_TUNNEL = 1 << 10,
SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11,
- SKB_GSO_GRE_CSUM = 1 << 12,
+ SKB_GSO_MPLS = 1 << 12,
+
};
#if BITS_PER_LONG > 32
@@ -1853,6 +1854,18 @@ static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len)
return pskb_may_pull(skb, skb_network_offset(skb) + len);
}
+static inline void skb_pop_rcv_encapsulation(struct sk_buff *skb)
+{
+ /* Only continue with checksum unnecessary if device indicated
+ * it is valid across encapsulation (skb->encapsulation was set).
+ */
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY && !skb->encapsulation)
+ skb->ip_summed = CHECKSUM_NONE;
+
+ skb->encapsulation = 0;
+ skb->csum_valid = 0;
+}
+
/*
* CPUs often take a performance hit when accessing unaligned memory
* locations. The actual performance hit varies, it can be small if the
diff --git a/include/net/udp.h b/include/net/udp.h
index 2ecfc6e15609..68a1fefe3dfe 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -111,7 +111,9 @@ struct sk_buff;
*/
static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb)
{
- return __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov);
+ return (UDP_SKB_CB(skb)->cscov == skb->len ?
+ __skb_checksum_complete(skb) :
+ __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov));
}
static inline int udp_lib_checksum_complete(struct sk_buff *skb)
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 6b1c04ca1d50..488dd1a825c0 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -739,22 +739,38 @@ __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
__sum16 sum;
sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
- if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && !sum &&
- !skb->csum_complete_sw)
- netdev_rx_csum_fault(skb->dev);
-
- /* Save checksum complete for later use */
- skb->csum = sum;
- skb->ip_summed = CHECKSUM_COMPLETE;
- skb->csum_complete_sw = 1;
-
+ if (likely(!sum)) {
+ if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
+ !skb->csum_complete_sw)
+ netdev_rx_csum_fault(skb->dev);
+ }
+ skb->csum_valid = !sum;
return sum;
}
EXPORT_SYMBOL(__skb_checksum_complete_head);
__sum16 __skb_checksum_complete(struct sk_buff *skb)
{
- return __skb_checksum_complete_head(skb, skb->len);
+ __wsum csum;
+ __sum16 sum;
+
+ csum = skb_checksum(skb, 0, skb->len, 0);
+
+ /* skb->csum holds pseudo checksum */
+ sum = csum_fold(csum_add(skb->csum, csum));
+ if (likely(!sum)) {
+ if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
+ !skb->csum_complete_sw)
+ netdev_rx_csum_fault(skb->dev);
+ }
+
+ /* Save full packet checksum */
+ skb->csum = csum;
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum_complete_sw = 1;
+ skb->csum_valid = !sum;
+
+ return sum;
}
EXPORT_SYMBOL(__skb_checksum_complete);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index bf92824af3f7..9cd5344fad73 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -689,6 +689,9 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->ooo_okay = old->ooo_okay;
new->no_fcs = old->no_fcs;
new->encapsulation = old->encapsulation;
+ new->encap_hdr_csum = old->encap_hdr_csum;
+ new->csum_valid = old->csum_valid;
+ new->csum_complete_sw = old->csum_complete_sw;
#ifdef CONFIG_XFRM
new->sp = secpath_get(old->sp);
#endif