net: Fix test for VLAN TX checksum offload capability
authorBen Hutchings <bhutchings@solarflare.com>
Tue, 17 Jun 2008 00:02:28 +0000 (17:02 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 17 Jun 2008 00:02:28 +0000 (17:02 -0700)
Selected device feature bits can be propagated to VLAN devices, so we
can make use of TX checksum offload and TSO on VLAN-tagged packets.
However, if the physical device does not do VLAN tag insertion or
generic checksum offload then the test for TX checksum offload in
dev_queue_xmit() will see a protocol of htons(ETH_P_8021Q) and yield
false.

This splits the checksum offload test into two functions:

- can_checksum_protocol() tests a given protocol against a feature bitmask

- dev_can_checksum() first tests the skb protocol against the device
  features; if that fails and the protocol is htons(ETH_P_8021Q) then
  it tests the encapsulated protocol against the effective device
  features for VLANs

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Acked-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/dev.c

index 58296307787746e99da8bdcfe006b9b39af32de1..68d8df0992abfd425c517fc8b9df3d6a40fe71ff 100644 (file)
 #include <linux/err.h>
 #include <linux/ctype.h>
 #include <linux/if_arp.h>
+#include <linux/if_vlan.h>
 
 #include "net-sysfs.h"
 
@@ -1362,6 +1363,29 @@ void netif_device_attach(struct net_device *dev)
 }
 EXPORT_SYMBOL(netif_device_attach);
 
+static bool can_checksum_protocol(unsigned long features, __be16 protocol)
+{
+       return ((features & NETIF_F_GEN_CSUM) ||
+               ((features & NETIF_F_IP_CSUM) &&
+                protocol == htons(ETH_P_IP)) ||
+               ((features & NETIF_F_IPV6_CSUM) &&
+                protocol == htons(ETH_P_IPV6)));
+}
+
+static bool dev_can_checksum(struct net_device *dev, struct sk_buff *skb)
+{
+       if (can_checksum_protocol(dev->features, skb->protocol))
+               return true;
+
+       if (skb->protocol == htons(ETH_P_8021Q)) {
+               struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
+               if (can_checksum_protocol(dev->features & dev->vlan_features,
+                                         veh->h_vlan_encapsulated_proto))
+                       return true;
+       }
+
+       return false;
+}
 
 /*
  * Invalidate hardware checksum when packet is to be mangled, and
@@ -1640,14 +1664,8 @@ int dev_queue_xmit(struct sk_buff *skb)
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
                skb_set_transport_header(skb, skb->csum_start -
                                              skb_headroom(skb));
-
-               if (!(dev->features & NETIF_F_GEN_CSUM) &&
-                   !((dev->features & NETIF_F_IP_CSUM) &&
-                     skb->protocol == htons(ETH_P_IP)) &&
-                   !((dev->features & NETIF_F_IPV6_CSUM) &&
-                     skb->protocol == htons(ETH_P_IPV6)))
-                       if (skb_checksum_help(skb))
-                               goto out_kfree_skb;
+               if (!dev_can_checksum(dev, skb) && skb_checksum_help(skb))
+                       goto out_kfree_skb;
        }
 
 gso: