Whenever a port is added to the datapath, LRO is automatically disabled. In the future, we may want to enable LRO in some circumstances, so have userspace disable LRO through the ethtool ioctls.
As part of this change, the MTU and LRO checks are moved to netdev-vport's send(), which is where they're actually needed. Feature #6810 Signed-off-by: Justin Pettit <jpet...@nicira.com> --- datapath/linux/.gitignore | 1 - datapath/linux/Modules.mk | 1 - datapath/linux/compat/dev-openvswitch.c | 32 ----------------------- datapath/linux/compat/include/linux/netdevice.h | 9 +++++- datapath/vport-netdev.c | 32 +++++++++++++++++++++- datapath/vport.c | 30 +-------------------- lib/dpif-linux.c | 8 +++++ 7 files changed, 47 insertions(+), 66 deletions(-) delete mode 100644 datapath/linux/compat/dev-openvswitch.c diff --git a/datapath/linux/.gitignore b/datapath/linux/.gitignore index 0aee746..5b772e0 100644 --- a/datapath/linux/.gitignore +++ b/datapath/linux/.gitignore @@ -8,7 +8,6 @@ /brc_sysfs_if.c /brcompat.c /checksum.c -/dev-openvswitch.c /dp_sysfs_dp.c /dp_sysfs_if.c /datapath.c diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk index cb68010..2531118 100644 --- a/datapath/linux/Modules.mk +++ b/datapath/linux/Modules.mk @@ -1,6 +1,5 @@ openvswitch_sources += \ linux/compat/addrconf_core-openvswitch.c \ - linux/compat/dev-openvswitch.c \ linux/compat/flex_array.c \ linux/compat/genetlink-openvswitch.c \ linux/compat/ip_output-openvswitch.c \ diff --git a/datapath/linux/compat/dev-openvswitch.c b/datapath/linux/compat/dev-openvswitch.c deleted file mode 100644 index 5b7444b..0000000 --- a/datapath/linux/compat/dev-openvswitch.c +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef HAVE_DEV_DISABLE_LRO - -#include <linux/netdevice.h> - -#ifdef NETIF_F_LRO -#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); -} -#else -void dev_disable_lro(struct net_device *dev) { } -#endif /* NETIF_F_LRO */ - -#endif /* HAVE_DEV_DISABLE_LRO */ diff --git a/datapath/linux/compat/include/linux/netdevice.h b/datapath/linux/compat/include/linux/netdevice.h index 04ebd89..664ff2e 100644 --- a/datapath/linux/compat/include/linux/netdevice.h +++ b/datapath/linux/compat/include/linux/netdevice.h @@ -74,7 +74,14 @@ extern void unregister_netdevice_many(struct list_head *head); #endif #ifndef HAVE_DEV_DISABLE_LRO -extern void dev_disable_lro(struct net_device *dev); +/* Some distributions (e.g., RHEL5) backported LRO support, but not the + * userspace interface to adjust them, so it is necessary to call + * dev_disable_lro() from the kernel. If this is an older kernel with + * LRO support, then we assume that they either backported + * dev_disable_lro() or provided the userspace interface. In either + * case, it's a no-op. + */ +static inline void dev_disable_lro(struct net_device *dev) { } #endif /* Linux 2.6.28 introduced dev_get_stats(): diff --git a/datapath/vport-netdev.c b/datapath/vport-netdev.c index f1e9b09..d1e61bb 100644 --- a/datapath/vport-netdev.c +++ b/datapath/vport-netdev.c @@ -6,6 +6,8 @@ * kernel, by Linus Torvalds and others. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/if_arp.h> #include <linux/if_bridge.h> #include <linux/if_vlan.h> @@ -158,7 +160,9 @@ static struct vport *netdev_create(const struct vport_parms *parms) goto error_put; dev_set_promiscuity(netdev_vport->dev, 1); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) dev_disable_lro(netdev_vport->dev); +#endif netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH; return vport; @@ -281,8 +285,6 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) if (unlikely(!skb)) return; - skb_warn_if_lro(skb); - skb_push(skb, ETH_HLEN); if (unlikely(compute_ip_summed(skb, false))) { @@ -294,6 +296,16 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) vport_receive(vport, skb); } +static inline unsigned packet_length(const struct sk_buff *skb) +{ + unsigned length = skb->len - ETH_HLEN; + + if (skb->protocol == htons(ETH_P_8021Q)) + length -= VLAN_HLEN; + + return length; +} + static bool dev_supports_vlan_tx(struct net_device *dev) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) @@ -310,8 +322,19 @@ static bool dev_supports_vlan_tx(struct net_device *dev) static int netdev_send(struct vport *vport, struct sk_buff *skb) { struct netdev_vport *netdev_vport = netdev_vport_priv(vport); + int mtu = netdev_vport->dev->mtu; int len; + if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) { + if (net_ratelimit()) + pr_warn("%s: dropped over-mtu packet: %d > %d\n", + dp_name(vport->dp), packet_length(skb), mtu); + goto error; + } + + if (unlikely(skb_warn_if_lro(skb))) + goto error; + skb->dev = netdev_vport->dev; forward_ip_summed(skb, true); @@ -379,6 +402,11 @@ tag: dev_queue_xmit(skb); return len; + +error: + kfree_skb(skb); + vport_record_error(vport, VPORT_E_TX_DROPPED); + return 0; } /* Returns null if this device is not attached to a datapath. */ diff --git a/datapath/vport.c b/datapath/vport.c index 487d752..2b5a0b4 100644 --- a/datapath/vport.c +++ b/datapath/vport.c @@ -6,8 +6,6 @@ * kernel, by Linus Torvalds and others. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/dcache.h> #include <linux/etherdevice.h> #include <linux/if.h> @@ -687,16 +685,6 @@ void vport_receive(struct vport *vport, struct sk_buff *skb) dp_process_received_packet(vport, skb); } -static inline unsigned packet_length(const struct sk_buff *skb) -{ - unsigned length = skb->len - ETH_HLEN; - - if (skb->protocol == htons(ETH_P_8021Q)) - length -= VLAN_HLEN; - - return length; -} - /** * vport_send - send a packet on a device * @@ -708,18 +696,7 @@ static inline unsigned packet_length(const struct sk_buff *skb) */ int vport_send(struct vport *vport, struct sk_buff *skb) { - int mtu; - int sent; - - mtu = vport_get_mtu(vport); - if (mtu && unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) { - if (net_ratelimit()) - pr_warn("%s: dropped over-mtu packet: %d > %d\n", - dp_name(vport->dp), packet_length(skb), mtu); - goto error; - } - - sent = vport->ops->send(vport, skb); + int sent = vport->ops->send(vport, skb); if (vport->ops->flags & VPORT_F_GEN_STATS && sent > 0) { struct vport_percpu_stats *stats; @@ -736,11 +713,6 @@ int vport_send(struct vport *vport, struct sk_buff *skb) } return sent; - -error: - kfree_skb(skb); - vport_record_error(vport, VPORT_E_TX_DROPPED); - return 0; } /** diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c index 55d22b4..da48c68 100644 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@ -60,6 +60,10 @@ enum { LRU_MAX_PORTS = 1024 }; enum { LRU_MASK = LRU_MAX_PORTS - 1}; BUILD_ASSERT_DECL(IS_POW2(LRU_MAX_PORTS)); +/* This ethtool flag was introduced in Linux 2.6.24, so it might be + * missing if we have old headers. */ +#define ETH_FLAG_LRO (1 << 15) /* LRO is enabled */ + struct dpif_linux_dp { /* Generic Netlink header. */ uint8_t cmd; @@ -393,6 +397,10 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev, request.options_len = options->size; } + if (request.type == OVS_VPORT_TYPE_NETDEV) { + netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false); + } + /* Loop until we find a port that isn't used. */ do { request.port_no = dpif_linux_pop_port(dpif); -- 1.7.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev