2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-17 14:28:02 +00:00

datapath: Disable large receive offload.

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.
This commit is contained in:
Jesse Gross
2010-02-19 16:54:19 -05:00
parent 635c9298b9
commit 2de320799d
6 changed files with 85 additions and 1 deletions

View 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

View 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 \

View File

@@ -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 */

View 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

View 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

View File

@@ -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 */