datapath: Disable large receive offload.
authorJesse Gross <jesse@nicira.com>
Fri, 19 Feb 2010 21:54:19 +0000 (16:54 -0500)
committerJesse Gross <jesse@nicira.com>
Fri, 5 Mar 2010 21:31:26 +0000 (16:31 -0500)
LRO can play fast and loose with the packets that it merges, which
isn't very polite when you are bridging packets for other operating
systems.  This disables LRO on any underlying devices that are added
to the datapath, which is the same as what the bridge does.

Note that this does not disable GRO, which has a more strict set of
rules about what is merged and is therefore safe for bridging.  Both
are typically done in software anyways.

datapath/datapath.c
datapath/linux-2.6/Modules.mk
datapath/linux-2.6/compat-2.6/dev-openvswitch.c [new file with mode: 0644]
datapath/linux-2.6/compat-2.6/include/linux/netdevice.h
datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
datapath/linux-2.6/compat-2.6/skbuff-openvswitch.c [new file with mode: 0644]

index 76569f504072d039dd0f36cc7871e61c5490930b..f8a2aa3c7e624d561d147867865c88f564483a69 100644 (file)
@@ -358,6 +358,7 @@ static int new_nbp(struct datapath *dp, struct net_device *dev, int port_no)
                 * in dp_frame_hook().  In turn dp_frame_hook() can reject them
                 * back to network stack, but that's a waste of time. */
        }
+       dev_disable_lro(dev);
        rcu_assign_pointer(dp->ports[port_no], p);
        list_add_rcu(&p->node, &dp->port_list);
        dp->n_ports++;
@@ -505,6 +506,11 @@ out:
 static void
 do_port_input(struct net_bridge_port *p, struct sk_buff *skb) 
 {
+       /* LRO isn't suitable for bridging.  We turn it off but make sure
+        * that it wasn't reactivated. */
+       if (skb_warn_if_lro(skb))
+               return;
+
        /* Make our own copy of the packet.  Otherwise we will mangle the
         * packet for anyone who came before us (e.g. tcpdump via AF_PACKET).
         * (No one comes after us, since we tell handle_bridge() that we took
index c15b735f599fd3b4c460fc886a535902427057db..820d09b65b53e7cd8e55a19e904e86002d5ee0ce 100644 (file)
@@ -1,6 +1,8 @@
 openvswitch_sources += \
+       linux-2.6/compat-2.6/dev-openvswitch.c \
        linux-2.6/compat-2.6/genetlink-openvswitch.c \
-       linux-2.6/compat-2.6/random32.c
+       linux-2.6/compat-2.6/random32.c \
+       linux-2.6/compat-2.6/skbuff-openvswitch.c
 openvswitch_headers += \
        linux-2.6/compat-2.6/compat26.h \
        linux-2.6/compat-2.6/include/asm-generic/bug.h \
diff --git a/datapath/linux-2.6/compat-2.6/dev-openvswitch.c b/datapath/linux-2.6/compat-2.6/dev-openvswitch.c
new file mode 100644 (file)
index 0000000..7be33f6
--- /dev/null
@@ -0,0 +1,35 @@
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+#include <linux/netdevice.h>
+
+#ifndef NETIF_F_LRO
+void dev_disable_lro(struct net_device *dev) { }
+#else
+
+#include <linux/ethtool.h>
+
+/**
+ *     dev_disable_lro - disable Large Receive Offload on a device
+ *     @dev: device
+ *
+ *     Disable Large Receive Offload (LRO) on a net device.  Must be
+ *     called under RTNL.  This is needed if received packets may be
+ *     forwarded to another interface.
+ */
+void dev_disable_lro(struct net_device *dev)
+{
+       if (dev->ethtool_ops && dev->ethtool_ops->get_flags &&
+           dev->ethtool_ops->set_flags) {
+               u32 flags = dev->ethtool_ops->get_flags(dev);
+               if (flags & ETH_FLAG_LRO) {
+                       flags &= ~ETH_FLAG_LRO;
+                       dev->ethtool_ops->set_flags(dev, flags);
+               }
+       }
+       WARN_ON(dev->features & NETIF_F_LRO);
+}
+
+#endif /* NETIF_F_LRO */
+
+#endif /* kernel < 2.6.27 */
index 0cd91b9f4bc18fa3437065fd7d599ecd30919ff0..924dc0d3eb8e518776b20d1d4d36f6707bfbf948 100644 (file)
@@ -73,4 +73,8 @@ extern void unregister_netdevice_queue(struct net_device *dev,
 extern void unregister_netdevice_many(struct list_head *head);
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+extern void dev_disable_lro(struct net_device *dev);
+#endif
+
 #endif
index d9f043ac6442960e8f5841b06dd070b1881a86da..349e504ce932cc199a6d8788146f0808271f25a3 100644 (file)
@@ -205,4 +205,28 @@ static inline struct sk_buff *skb_gso_segment(struct sk_buff *skb,
 }
 #endif /* before 2.6.18 */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+extern void __skb_warn_lro_forwarding(const struct sk_buff *skb);
+
+#ifndef NETIF_F_LRO
+static inline bool skb_warn_if_lro(const struct sk_buff *skb)
+{
+       return false;
+}
+#else
+static inline bool skb_warn_if_lro(const struct sk_buff *skb)
+{
+       /* LRO sets gso_size but not gso_type, whereas if GSO is really
+        * wanted then gso_type will be set. */
+       struct skb_shared_info *shinfo = skb_shinfo(skb);
+       if (shinfo->gso_size != 0 && unlikely(shinfo->gso_type == 0)) {
+               __skb_warn_lro_forwarding(skb);
+               return true;
+       }
+       return false;
+}
+#endif /* NETIF_F_LRO */
+#endif /* kernel < 2.6.27 */
+
 #endif
diff --git a/datapath/linux-2.6/compat-2.6/skbuff-openvswitch.c b/datapath/linux-2.6/compat-2.6/skbuff-openvswitch.c
new file mode 100644 (file)
index 0000000..a9743ad
--- /dev/null
@@ -0,0 +1,13 @@
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+#include <linux/netdevice.h>
+
+void __skb_warn_lro_forwarding(const struct sk_buff *skb)
+{
+       if (net_ratelimit())
+               printk(KERN_WARNING "%s: received packets cannot be forwarded"
+                                   " while LRO is enabled\n", skb->dev->name);
+}
+
+#endif /* kernel < 2.6.27 */