datapath: Use openvswitch_handle_frame hook in >=RHEL6.4 to live side by side with...
authorThomas Graf <tgraf@redhat.com>
Fri, 26 Apr 2013 10:03:10 +0000 (12:03 +0200)
committerJesse Gross <jesse@nicira.com>
Mon, 29 Apr 2013 17:05:21 +0000 (10:05 -0700)
Due to the missing register rx_handler API in the kernel RHEL6 is
based on, the datapath currently falls back to using the bridging
hook with the consequence that bridging and OVS cannot be used in
parallel on any RHEL6 release.

For this purpose, >=RHEL6.4 releases provide a special rx frame hook
to be used by OVS. It captures frames at the same location in the
stack as the rx_handler would do in more recent kernel releases. In
order to store the vport pointer, the net_device's ax25_ptr field is
utilized under the assumption that an AX25 device will never be
attached to an OVS bridge.

Signed-off-by: Thomas Graf <tgraf@redhat.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
(cherry picked from commit f285d3e715512571c4b2f92a4d1c65022bbcc9d5)

Conflicts:
datapath/vport-netdev.c

acinclude.m4
datapath/vport-netdev.c

index f0610c9..ec0ac7e 100644 (file)
@@ -273,6 +273,9 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [ADD_ALL_VLANS_CMD],
                   [OVS_DEFINE([HAVE_VLAN_BUG_WORKAROUND])])
 
+  OVS_GREP_IFELSE([$KSRC/include/linux/openvswitch.h], [openvswitch_handle_frame_hook],
+                  [OVS_DEFINE([HAVE_RHEL_OVS_HOOK])])
+
   OVS_CHECK_LOG2_H
 
   if cmp -s datapath/linux/kcompat.h.new \
index 8440796..0f117b2 100644 (file)
@@ -45,6 +45,12 @@ 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)
@@ -63,7 +69,8 @@ static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb)
 
        return RX_HANDLER_CONSUMED;
 }
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) || \
+      defined HAVE_RHEL_OVS_HOOK
 /* Called with rcu_read_lock and bottom-halves disabled. */
 static struct sk_buff *netdev_frame_hook(struct sk_buff *skb)
 {
@@ -105,7 +112,8 @@ static int netdev_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
 #error
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) || \
+    defined HAVE_RHEL_OVS_HOOK
 static int netdev_init(void) { return 0; }
 static void netdev_exit(void) { }
 #else
@@ -152,10 +160,16 @@ static struct vport *netdev_create(const struct vport_parms *parms)
                goto error_put;
        }
 
+#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_rx_handler_register(netdev_vport->dev, netdev_frame_hook,
                                         vport);
        if (err)
                goto error_put;
+#endif
 
        dev_set_promiscuity(netdev_vport->dev, 1);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
@@ -178,6 +192,12 @@ 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));
 }
@@ -392,13 +412,18 @@ error:
 /* Returns null if this device is not attached to a datapath. */
 struct vport *ovs_netdev_get_vport(struct net_device *dev)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) || \
+    defined HAVE_RHEL_OVS_HOOK
 #if IFF_OVS_DATAPATH != 0
        if (likely(dev->priv_flags & IFF_OVS_DATAPATH))
 #else
        if (likely(rcu_access_pointer(dev->rx_handler) == netdev_frame_hook))
 #endif
+#ifdef HAVE_RHEL_OVS_HOOK
+               return (struct vport *)rcu_dereference_rtnl(dev->ax25_ptr);
+#else
                return (struct vport *)rcu_dereference_rtnl(dev->rx_handler_data);
+#endif
        else
                return NULL;
 #else
@@ -425,7 +450,8 @@ const struct vport_ops ovs_netdev_vport_ops = {
        .send           = netdev_send,
 };
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) && \
+    !defined HAVE_RHEL_OVS_HOOK
 /*
  * In kernels earlier than 2.6.36, Open vSwitch cannot safely coexist with the
  * Linux bridge module, because there is only a single bridge hook function and