datapath: Prevent bridge module from loading along with openvswitch.
authorBen Pfaff <blp@nicira.com>
Fri, 29 May 2009 22:32:59 +0000 (15:32 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 29 May 2009 22:33:06 +0000 (15:33 -0700)
For some time, the openvswitch kernel module has checked whether the
bridge module is loaded and refused to start if so.  However, the bridge
module doesn't check whether the openvswitch module is loaded, so it can
end up loaded at the same time if something goes wrong, and then generally
the system OOPSes sooner or later.

This commit adds proper mutual exclusion, by registering an LLC SAP for
the protocol used by spanning tree.  This is the first thing that the
bridge module does at initialization time also, and since there can only
be one registration for a given protocol this prevents both modules from
being loaded at the same time.

This fixes an actual OOPS observed at boot time on a XenServer running the
debug kernel that we built based on Citrix's 5.5.0-beta1 kernel.

datapath/datapath.c

index e6484ac73cf9cd99fa17009dbe49b718176994ce..66f9202af1cbd97963cee4a9d0489a30a925752c 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/etherdevice.h>
 #include <linux/kernel.h>
 #include <linux/kthread.h>
+#include <linux/llc.h>
 #include <linux/mutex.h>
 #include <linux/percpu.h>
 #include <linux/rcupdate.h>
@@ -38,6 +39,7 @@
 #include <linux/rculist.h>
 #include <linux/workqueue.h>
 #include <linux/dmi.h>
+#include <net/llc.h>
 
 #include "openvswitch/datapath-protocol.h"
 #include "datapath.h"
@@ -1538,6 +1540,17 @@ struct file_operations openvswitch_fops = {
 };
 
 static int major;
+static struct llc_sap *dp_stp_sap;
+
+static int dp_stp_rcv(struct sk_buff *skb, struct net_device *dev,
+                     struct packet_type *pt, struct net_device *orig_dev)
+{
+       /* We don't really care about STP packets, we just listen for them for
+        * mutual exclusion with the bridge module, so this just discards
+        * them. */
+       kfree_skb(skb);
+       return 0;
+}
 
 static int __init dp_init(void)
 {
@@ -1545,6 +1558,17 @@ static int __init dp_init(void)
 
        printk("Open vSwitch %s, built "__DATE__" "__TIME__"\n", VERSION BUILDNR);
 
+       /* Register to receive STP packets because the bridge module also
+        * attempts to do so.  Since there can only be a single listener for a
+        * given protocol, this provides mutual exclusion against the bridge
+        * module, preventing both of them from being loaded at the same
+        * time. */
+       dp_stp_sap = llc_sap_open(LLC_SAP_BSPAN, dp_stp_rcv);
+       if (!dp_stp_sap) {
+               printk(KERN_ERR "openvswitch: can't register sap for STP (probably the bridge module is loaded)\n");
+               return -EADDRINUSE;
+       }
+
        err = flow_init();
        if (err)
                goto error;
@@ -1559,23 +1583,7 @@ static int __init dp_init(void)
 
        /* Hook into callback used by the bridge to intercept packets.
         * Parasites we are. */
-       rtnl_lock();
-       if (br_handle_frame_hook) {
-               struct net_device *dev;
-               for_each_netdev (&init_net, dev) {
-                       if (!dev->br_port)
-                               continue;
-
-                       rtnl_unlock();
-                       printk("openvswitch: must delete bridges "
-                              "before loading\n");
-                       err = -EBUSY;
-                       goto error_unreg_notifier;
-               }
-               printk("openvswitch: hijacking bridge hook\n");
-       }
        br_handle_frame_hook = dp_frame_hook;
-       rtnl_unlock();
 
        return 0;
 
@@ -1594,6 +1602,7 @@ static void dp_cleanup(void)
        unregister_netdevice_notifier(&dp_device_notifier);
        flow_exit();
        br_handle_frame_hook = NULL;
+       llc_sap_put(dp_stp_sap);
 }
 
 module_init(dp_init);