datapath: Don't call genlmsg_reply() under rcu_read_lock().
[openvswitch] / datapath / vport-netdev.c
index 11faf8ad11b8157c7ad2940b2064e1b3d450a8cc..3bab666c888132a9936812f4a331e9ed90ff42ac 100644 (file)
 #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. */
@@ -35,7 +38,23 @@ MODULE_PARM_DESC(vlan_tso, "Enable TSO for VLAN packets");
 
 static void netdev_port_receive(struct vport *vport, struct sk_buff *skb);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
+/* Called with rcu_read_lock and bottom-halves disabled. */
+static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb)
+{
+       struct sk_buff *skb = *pskb;
+       struct vport *vport;
+
+       if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
+               return RX_HANDLER_PASS;
+
+       vport = netdev_get_vport(skb->dev);
+
+       netdev_port_receive(vport, skb);
+
+       return RX_HANDLER_CONSUMED;
+}
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
 /* Called with rcu_read_lock and bottom-halves disabled. */
 static struct sk_buff *netdev_frame_hook(struct sk_buff *skb)
 {
@@ -249,6 +268,11 @@ int netdev_get_mtu(const struct vport *vport)
 /* Must be called with rcu_read_lock. */
 static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
 {
+       if (unlikely(!vport)) {
+               kfree_skb(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
@@ -266,6 +290,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 +311,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 +375,6 @@ tag:
                        return 0;
                vlan_set_tci(skb, 0);
        }
-#endif
 
        len = skb->len;
        dev_queue_xmit(skb);