Merge branch 'mainstream'
[sliver-openvswitch.git] / datapath / vport-netdev.c
index 4558079..50373b1 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/llc.h>
 #include <linux/rtnetlink.h>
 #include <linux/skbuff.h>
+#include <linux/openvswitch.h>
 
 #include <net/llc.h>
 
@@ -45,12 +46,6 @@ MODULE_PARM_DESC(vlan_tso, "Enable TSO for VLAN packets");
 #define vlan_tso true
 #endif
 
-#ifdef HAVE_RHEL_OVS_HOOK
-static atomic_t nr_bridges = ATOMIC_INIT(0);
-
-extern struct sk_buff *(*openvswitch_handle_frame_hook)(struct sk_buff *skb);
-#endif
-
 static void netdev_port_receive(struct vport *vport, struct sk_buff *skb);
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
@@ -117,21 +112,40 @@ static int netdev_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
 static int netdev_init(void) { return 0; }
 static void netdev_exit(void) { }
 #else
-static int netdev_init(void)
+static int port_count;
+
+static void netdev_init(void)
 {
+       port_count++;
+       if (port_count > 1)
+               return;
+
        /* Hook into callback used by the bridge to intercept packets.
         * Parasites we are. */
        br_handle_frame_hook = netdev_frame_hook;
 
-       return 0;
+       return;
 }
 
 static void netdev_exit(void)
 {
+       port_count--;
+       if (port_count > 0)
+               return;
+
        br_handle_frame_hook = NULL;
 }
 #endif
 
+static struct net_device *get_dpdev(struct datapath *dp)
+{
+       struct vport *local;
+
+       local = ovs_vport_ovsl(dp, OVSP_LOCAL);
+       BUG_ON(!local);
+       return netdev_vport_priv(local)->dev;
+}
+
 static struct vport *netdev_create(const struct vport_parms *parms)
 {
        struct vport *vport;
@@ -161,16 +175,15 @@ static struct vport *netdev_create(const struct vport_parms *parms)
        }
 
        rtnl_lock();
-#ifdef HAVE_RHEL_OVS_HOOK
-       rcu_assign_pointer(netdev_vport->dev->ax25_ptr, vport);
-       atomic_inc(&nr_bridges);
-       rcu_assign_pointer(openvswitch_handle_frame_hook, netdev_frame_hook);
-#else
+       err = netdev_master_upper_dev_link(netdev_vport->dev,
+                                          get_dpdev(vport->dp));
+       if (err)
+               goto error_unlock;
+
        err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook,
                                         vport);
        if (err)
-               goto error_unlock;
-#endif
+               goto error_master_upper_dev_unlink;
 
        dev_set_promiscuity(netdev_vport->dev, 1);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
@@ -179,11 +192,12 @@ static struct vport *netdev_create(const struct vport_parms *parms)
        netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;
        rtnl_unlock();
 
+       netdev_init();
        return vport;
 
-#ifndef HAVE_RHEL_OVS_HOOK
+error_master_upper_dev_unlink:
+       netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp));
 error_unlock:
-#endif
        rtnl_unlock();
 error_put:
        dev_put(netdev_vport->dev);
@@ -198,12 +212,6 @@ static void free_port_rcu(struct rcu_head *rcu)
        struct netdev_vport *netdev_vport = container_of(rcu,
                                        struct netdev_vport, rcu);
 
-#ifdef HAVE_RHEL_OVS_HOOK
-       rcu_assign_pointer(netdev_vport->dev->ax25_ptr, NULL);
-
-       if (atomic_dec_and_test(&nr_bridges))
-               rcu_assign_pointer(openvswitch_handle_frame_hook, NULL);
-#endif
        dev_put(netdev_vport->dev);
        ovs_vport_free(vport_from_priv(netdev_vport));
 }
@@ -212,9 +220,11 @@ static void netdev_destroy(struct vport *vport)
 {
        struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
 
+       netdev_exit();
        rtnl_lock();
        netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
        netdev_rx_handler_unregister(netdev_vport->dev);
+       netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp));
        dev_set_promiscuity(netdev_vport->dev, -1);
        rtnl_unlock();
 
@@ -244,14 +254,15 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
        if (unlikely(!skb))
                return;
 
-       skb_push(skb, ETH_HLEN);
-
        if (unlikely(compute_ip_summed(skb, false)))
                goto error;
 
+       skb_push(skb, ETH_HLEN);
+       ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
+
        vlan_copy_skb_tci(skb);
 
-       ovs_vport_receive(vport, skb);
+       ovs_vport_receive(vport, skb, NULL);
        return;
 
 error:
@@ -291,7 +302,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
                net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
                                     netdev_vport->dev->name,
                                     packet_length(skb), mtu);
-               goto error;
+               goto drop;
        }
 
        skb->dev = netdev_vport->dev;
@@ -312,19 +323,15 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
                        nskb = skb_gso_segment(skb, features);
                        if (!nskb) {
                                if (unlikely(skb_cloned(skb) &&
-                                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) {
-                                       kfree_skb(skb);
-                                       return 0;
-                               }
+                                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+                                       goto drop;
 
                                skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY;
                                goto tag;
                        }
 
-                       if (IS_ERR(nskb)) {
-                               kfree_skb(skb);
-                               return 0;
-                       }
+                       if (IS_ERR(nskb))
+                               goto drop;
                        consume_skb(skb);
                        skb = nskb;
 
@@ -333,7 +340,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
                                nskb = skb->next;
                                skb->next = NULL;
 
-                               skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+                               skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb));
                                if (likely(skb)) {
                                        len += skb->len;
                                        vlan_set_tci(skb, 0);
@@ -347,7 +354,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
                }
 
 tag:
-               skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+               skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb));
                if (unlikely(!skb))
                        return 0;
                vlan_set_tci(skb, 0);
@@ -358,9 +365,8 @@ tag:
 
        return len;
 
-error:
+drop:
        kfree_skb(skb);
-       ovs_vport_record_error(vport, VPORT_E_TX_DROPPED);
        return 0;
 }
 
@@ -388,9 +394,6 @@ struct vport *ovs_netdev_get_vport(struct net_device *dev)
 
 const struct vport_ops ovs_netdev_vport_ops = {
        .type           = OVS_VPORT_TYPE_NETDEV,
-       .flags          = VPORT_F_REQUIRED,
-       .init           = netdev_init,
-       .exit           = netdev_exit,
        .create         = netdev_create,
        .destroy        = netdev_destroy,
        .get_name       = ovs_netdev_get_name,