datapath: Add module parameter to allow TSO with vlans.
authorJesse Gross <jesse@nicira.com>
Thu, 30 Dec 2010 20:28:10 +0000 (12:28 -0800)
committerJesse Gross <jesse@nicira.com>
Mon, 7 Feb 2011 21:49:01 +0000 (13:49 -0800)
We currently perform GSO on packets before adding a vlan tag,
which is reliable but hurts performance.  Even NICs that support
TSO on vlan tagged packets typically expect vlan acceleration to
be used.  Before 2.6.37 we can't use vlan acceleration and must
place the tag in the packet itself, which is risky when used with
TSO.  However, if the driver is known to work with internally
tagged packets and TSO this exposes a module parameter to enable it.

Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
INSTALL.Linux
datapath/vport-netdev.c

index e950c05..f80e1c6 100644 (file)
@@ -227,6 +227,17 @@ Prerequisites section, follow the procedure below to build.
         module loading, please include the output from the "dmesg" and
         "modinfo" commands mentioned above.
 
+   There is an optional module parameter to openvswitch_mod.ko called
+   vlan_tso that enables TCP segmentation offload over VLANs on NICs
+   that support it. Many drivers do not expose support for TSO on VLANs
+   in a way that Open vSwitch can use but there is no way to detect
+   whether this is the case. If you know that your particular driver can
+   handle it (for example by testing sending large TCP packets over VLANs)
+   then passing in a value of 1 may improve performance. Modules built for
+   Linux kernels 2.6.37 and later do not need this and do not have this
+   parameter. If you do not understand what this means or do not know if
+   your driver will work, do not set this.
+
 7. Initialize the configuration database using ovsdb-tool, e.g.:
 
       % ovsdb-tool create /usr/local/etc/ovs-vswitchd.conf.db vswitchd/vswitch.ovsschema
index 3693004..21fa6a0 100644 (file)
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+#include <linux/module.h>
+
+static int vlan_tso __read_mostly = 0;
+module_param(vlan_tso, int, 0644);
+MODULE_PARM_DESC(vlan_tso, "Enable TSO for VLAN packets");
+#endif
+
 /* If the native device stats aren't 64 bit use the vport stats tracking instead. */
 #define USE_VPORT_STATS (sizeof(((struct net_device_stats *)0)->rx_bytes) < sizeof(u64))
 
@@ -269,6 +277,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
        if (vlan_tx_tag_present(skb)) {
                int err;
+               int features = skb->dev->features & skb->dev->vlan_features;
 
                err = vswitch_skb_checksum_setup(skb);
                if (unlikely(err)) {
@@ -276,10 +285,27 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
                        return 0;
                }
 
-               if (skb_is_gso(skb)) {
+               if (!vlan_tso)
+                       features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
+                                     NETIF_F_UFO | NETIF_F_FSO);
+
+               if (skb_is_gso(skb) &&
+                   (!skb_gso_ok(skb, features) ||
+                    unlikely(skb->ip_summed != CHECKSUM_PARTIAL))) {
                        struct sk_buff *nskb;
 
-                       nskb = skb_gso_segment(skb, 0);
+                       nskb = skb_gso_segment(skb, features);
+                       if (!nskb) {
+                               if (unlikely(skb_cloned(skb) &&
+                                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) {
+                                       kfree_skb(skb);
+                                       return 0;
+                               }
+
+                               skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY;
+                               goto tag;
+                       }
+
                        kfree_skb(skb);
                        skb = nskb;
                        if (IS_ERR(skb))
@@ -301,12 +327,13 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
                        } while (skb);
 
                        return len;
-               } else {
-                       skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
-                       if (unlikely(!skb))
-                               return 0;
-                       vlan_set_tci(skb, 0);
                }
+
+tag:
+               skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+               if (unlikely(!skb))
+                       return 0;
+               vlan_set_tci(skb, 0);
        }
 #endif