[ovs-dev] [PATCH 1/8] datapath: Disable large receive offload.

Jesse Gross jesse at nicira.com
Thu Mar 4 18:22:06 UTC 2010


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                                |    6 +++
 datapath/linux-2.6/Modules.mk                      |    4 ++-
 datapath/linux-2.6/compat-2.6/dev-openvswitch.c    |   35 ++++++++++++++++++++
 .../linux-2.6/compat-2.6/include/linux/netdevice.h |    4 ++
 .../linux-2.6/compat-2.6/include/linux/skbuff.h    |   24 +++++++++++++
 datapath/linux-2.6/compat-2.6/skbuff-openvswitch.c |   13 +++++++
 6 files changed, 85 insertions(+), 1 deletions(-)
 create mode 100644 datapath/linux-2.6/compat-2.6/dev-openvswitch.c
 create mode 100644 datapath/linux-2.6/compat-2.6/skbuff-openvswitch.c

diff --git a/datapath/datapath.c b/datapath/datapath.c
index b6aefe8..112188a 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -359,6 +359,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++;
@@ -506,6 +507,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
diff --git a/datapath/linux-2.6/Modules.mk b/datapath/linux-2.6/Modules.mk
index c15b735..820d09b 100644
--- a/datapath/linux-2.6/Modules.mk
+++ b/datapath/linux-2.6/Modules.mk
@@ -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
index 0000000..2bfd947
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/dev-openvswitch.c
@@ -0,0 +1,35 @@
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+#include <linux/netdevice.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+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 /* kernel < 2.6.24 */
+
+#endif /* kernel < 2.6.27 */
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h
index 0cd91b9..924dc0d 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/netdevice.h
@@ -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
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/skbuff.h b/datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
index d9f043a..6c16bba 100644
--- a/datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
+++ b/datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
@@ -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);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+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 /* kernel < 2.6.24 */
+#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
index 0000000..a9743ad
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/skbuff-openvswitch.c
@@ -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 */
-- 
1.6.3.3





More information about the dev mailing list