summaryrefslogtreecommitdiff
path: root/datapath
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2011-03-16 14:39:17 -0700
committerBen Pfaff <blp@nicira.com>2011-03-16 14:39:17 -0700
commit431488e6638d3fbb53d215fa10d2ff2d8f1c2824 (patch)
tree0bf2a0c44cd5fbd2fa3718acaab4b5d8e91644ef /datapath
parent21dcb94fd5fbfda3b281cfcf124ab57802d77c23 (diff)
downloadopenvswitch-431488e6638d3fbb53d215fa10d2ff2d8f1c2824.tar.gz
Support vlan_group workaround implemented in XenServer kernels.
Some Linux network drivers support a feature called "VLAN acceleration", associated with a data structure called a "vlan_group". A vlan_group is, abstractly, a dictionary that maps from a VLAN ID (in the range 0...4095) to a VLAN device, that is, a Linux network device associated with a particular VLAN, e.g. "eth0.9" for VLAN 9 on eth0. Some drivers that support VLAN acceleration have bugs that fall roughly into the following categories: * Some NICs strip VLAN tags on receive if no vlan_group is registered, so that the tag is completely lost. * Some drivers size their receive buffers based on whether a vlan_group is enabled, meaning that a maximum size packet with a VLAN tag will not fit if a vlan_group is not configured. * On transmit some drivers expect that VLAN acceleration will be used if it is available (which can only be done if a vlan_group is configured). In these cases, the driver may fail to parse the packet and correctly setup checksum offloading and/or TSO. The correct long term solution is to fix these driver bugs. To cope until then, we have prepared a patch to the Linux kernel network stack that works around these problems. This commit adds support for the workaround implemented by that patch. Signed-off-by: Ben Pfaff <blp@nicira.com> Acked-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'datapath')
-rw-r--r--datapath/vport-netdev.c22
1 files changed, 18 insertions, 4 deletions
diff --git a/datapath/vport-netdev.c b/datapath/vport-netdev.c
index 11faf8ad1..2583566b1 100644
--- a/datapath/vport-netdev.c
+++ b/datapath/vport-netdev.c
@@ -22,12 +22,15 @@
#include "vport-internal_dev.h"
#include "vport-netdev.h"
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \
+ !defined(HAVE_VLAN_BUG_WORKAROUND)
#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");
+#else
+#define vlan_tso true
#endif
/* If the native device stats aren't 64 bit use the vport stats tracking instead. */
@@ -266,6 +269,19 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
vport_receive(vport, skb);
}
+static bool dev_supports_vlan_tx(struct net_device *dev)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
+ /* Software fallback means every device supports vlan_tci on TX. */
+ return true;
+#elif defined(HAVE_VLAN_BUG_WORKAROUND)
+ return dev->features & NETIF_F_HW_VLAN_TX;
+#else
+ /* Assume that the driver is buggy. */
+ return false;
+#endif
+}
+
static int netdev_send(struct vport *vport, struct sk_buff *skb)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
@@ -274,8 +290,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
skb->dev = netdev_vport->dev;
forward_ip_summed(skb);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
- if (vlan_tx_tag_present(skb)) {
+ if (vlan_tx_tag_present(skb) && !dev_supports_vlan_tx(skb->dev)) {
int err;
int features = 0;
@@ -339,7 +354,6 @@ tag:
return 0;
vlan_set_tci(skb, 0);
}
-#endif
len = skb->len;
dev_queue_xmit(skb);