*/
 
 #include <linux/crc32.h>
-#include <linux/kmod.h>
+#include <linux/module.h>
 
 /* Needed for AOP_TRUNCATED_PAGE in mlog_errno() */
 #include <linux/fs.h>
        struct dlm_eviction_cb op_eviction_cb;
 };
 
+static struct ocfs2_stack_plugin o2cb_stack;
+
 /* These should be identical */
 #if (DLM_LOCK_IV != LKM_IVMODE)
 # error Lock modes do not match
 
 static void o2dlm_lock_ast_wrapper(void *astarg)
 {
-       BUG_ON(stack_glue_lproto == NULL);
+       BUG_ON(o2cb_stack.sp_proto == NULL);
 
-       stack_glue_lproto->lp_lock_ast(astarg);
+       o2cb_stack.sp_proto->lp_lock_ast(astarg);
 }
 
 static void o2dlm_blocking_ast_wrapper(void *astarg, int level)
 {
-       BUG_ON(stack_glue_lproto == NULL);
+       BUG_ON(o2cb_stack.sp_proto == NULL);
 
-       stack_glue_lproto->lp_blocking_ast(astarg, level);
+       o2cb_stack.sp_proto->lp_blocking_ast(astarg, level);
 }
 
 static void o2dlm_unlock_ast_wrapper(void *astarg, enum dlm_status status)
 {
        int error = dlm_status_to_errno(status);
 
-       BUG_ON(stack_glue_lproto == NULL);
+       BUG_ON(o2cb_stack.sp_proto == NULL);
 
        /*
         * In o2dlm, you can get both the lock_ast() for the lock being
        if (status == DLM_CANCELGRANT)
                return;
 
-       stack_glue_lproto->lp_unlock_ast(astarg, error);
+       o2cb_stack.sp_proto->lp_unlock_ast(astarg, error);
 }
 
 static int o2cb_dlm_lock(struct ocfs2_cluster_connection *conn,
        struct dlm_protocol_version dlm_version;
 
        BUG_ON(conn == NULL);
+       BUG_ON(o2cb_stack.sp_proto == NULL);
 
        /* for now we only have one cluster/node, make sure we see it
         * in the heartbeat universe */
        return rc;
 }
 
-static int o2cb_cluster_disconnect(struct ocfs2_cluster_connection *conn)
+static int o2cb_cluster_disconnect(struct ocfs2_cluster_connection *conn,
+                                  int hangup_pending)
 {
        struct dlm_ctxt *dlm = conn->cc_lockspace;
        struct o2dlm_private *priv = conn->cc_private;
        .dump_lksb      = o2cb_dump_lksb,
 };
 
+static struct ocfs2_stack_plugin o2cb_stack = {
+       .sp_name        = "o2cb",
+       .sp_ops         = &o2cb_stack_ops,
+       .sp_owner       = THIS_MODULE,
+};
+
+static int __init o2cb_stack_init(void)
+{
+       return ocfs2_stack_glue_register(&o2cb_stack);
+}
+
+static void __exit o2cb_stack_exit(void)
+{
+       ocfs2_stack_glue_unregister(&o2cb_stack);
+}
+
+MODULE_AUTHOR("Oracle");
+MODULE_DESCRIPTION("ocfs2 driver for the classic o2cb stack");
+MODULE_LICENSE("GPL");
+module_init(o2cb_stack_init);
+module_exit(o2cb_stack_exit);
 
  * General Public License for more details.
  */
 
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/kmod.h>
 
-/* Needed for AOP_TRUNCATED_PAGE in mlog_errno() */
-#include <linux/fs.h>
+#include "stackglue.h"
 
-#include "cluster/masklog.h"
+static struct ocfs2_locking_protocol *lproto;
+static DEFINE_SPINLOCK(ocfs2_stack_lock);
+static LIST_HEAD(ocfs2_stack_list);
 
-#include "stackglue.h"
+/*
+ * The stack currently in use.  If not null, active_stack->sp_count > 0,
+ * the module is pinned, and the locking protocol cannot be changed.
+ */
+static struct ocfs2_stack_plugin *active_stack;
+
+static struct ocfs2_stack_plugin *ocfs2_stack_lookup(const char *name)
+{
+       struct ocfs2_stack_plugin *p;
+
+       assert_spin_locked(&ocfs2_stack_lock);
+
+       list_for_each_entry(p, &ocfs2_stack_list, sp_list) {
+               if (!strcmp(p->sp_name, name))
+                       return p;
+       }
+
+       return NULL;
+}
+
+static int ocfs2_stack_driver_request(const char *name)
+{
+       int rc;
+       struct ocfs2_stack_plugin *p;
+
+       spin_lock(&ocfs2_stack_lock);
+
+       if (active_stack) {
+               /*
+                * If the active stack isn't the one we want, it cannot
+                * be selected right now.
+                */
+               if (!strcmp(active_stack->sp_name, name))
+                       rc = 0;
+               else
+                       rc = -EBUSY;
+               goto out;
+       }
+
+       p = ocfs2_stack_lookup(name);
+       if (!p || !try_module_get(p->sp_owner)) {
+               rc = -ENOENT;
+               goto out;
+       }
+
+       /* Ok, the stack is pinned */
+       p->sp_count++;
+       active_stack = p;
+
+       rc = 0;
+
+out:
+       spin_unlock(&ocfs2_stack_lock);
+       return rc;
+}
+
+/*
+ * This function looks up the appropriate stack and makes it active.  If
+ * there is no stack, it tries to load it.  It will fail if the stack still
+ * cannot be found.  It will also fail if a different stack is in use.
+ */
+static int ocfs2_stack_driver_get(const char *name)
+{
+       int rc;
+
+       rc = ocfs2_stack_driver_request(name);
+       if (rc == -ENOENT) {
+               request_module("ocfs2_stack_%s", name);
+               rc = ocfs2_stack_driver_request(name);
+       }
+
+       if (rc == -ENOENT) {
+               printk(KERN_ERR
+                      "ocfs2: Cluster stack driver \"%s\" cannot be found\n",
+                      name);
+       } else if (rc == -EBUSY) {
+               printk(KERN_ERR
+                      "ocfs2: A different cluster stack driver is in use\n");
+       }
+
+       return rc;
+}
 
-struct ocfs2_locking_protocol *stack_glue_lproto;
+static void ocfs2_stack_driver_put(void)
+{
+       spin_lock(&ocfs2_stack_lock);
+       BUG_ON(active_stack == NULL);
+       BUG_ON(active_stack->sp_count == 0);
+
+       active_stack->sp_count--;
+       if (!active_stack->sp_count) {
+               module_put(active_stack->sp_owner);
+               active_stack = NULL;
+       }
+       spin_unlock(&ocfs2_stack_lock);
+}
+
+int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin)
+{
+       int rc;
+
+       spin_lock(&ocfs2_stack_lock);
+       if (!ocfs2_stack_lookup(plugin->sp_name)) {
+               plugin->sp_count = 0;
+               plugin->sp_proto = lproto;
+               list_add(&plugin->sp_list, &ocfs2_stack_list);
+               printk(KERN_INFO "ocfs2: Registered cluster interface %s\n",
+                      plugin->sp_name);
+               rc = 0;
+       } else {
+               printk(KERN_ERR "ocfs2: Stack \"%s\" already registered\n",
+                      plugin->sp_name);
+               rc = -EEXIST;
+       }
+       spin_unlock(&ocfs2_stack_lock);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(ocfs2_stack_glue_register);
+
+void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin)
+{
+       struct ocfs2_stack_plugin *p;
+
+       spin_lock(&ocfs2_stack_lock);
+       p = ocfs2_stack_lookup(plugin->sp_name);
+       if (p) {
+               BUG_ON(p != plugin);
+               BUG_ON(plugin == active_stack);
+               BUG_ON(plugin->sp_count != 0);
+               list_del_init(&plugin->sp_list);
+               printk(KERN_INFO "ocfs2: Unregistered cluster interface %s\n",
+                      plugin->sp_name);
+       } else {
+               printk(KERN_ERR "Stack \"%s\" is not registered\n",
+                      plugin->sp_name);
+       }
+       spin_unlock(&ocfs2_stack_lock);
+}
+EXPORT_SYMBOL_GPL(ocfs2_stack_glue_unregister);
+
+void ocfs2_stack_glue_set_locking_protocol(struct ocfs2_locking_protocol *proto)
+{
+       struct ocfs2_stack_plugin *p;
+
+       BUG_ON(proto == NULL);
+
+       spin_lock(&ocfs2_stack_lock);
+       BUG_ON(active_stack != NULL);
+
+       lproto = proto;
+       list_for_each_entry(p, &ocfs2_stack_list, sp_list) {
+               p->sp_proto = lproto;
+       }
+
+       spin_unlock(&ocfs2_stack_lock);
+}
+EXPORT_SYMBOL_GPL(ocfs2_stack_glue_set_locking_protocol);
 
 
 int ocfs2_dlm_lock(struct ocfs2_cluster_connection *conn,
                   unsigned int namelen,
                   void *astarg)
 {
-       BUG_ON(stack_glue_lproto == NULL);
+       BUG_ON(lproto == NULL);
 
-       return o2cb_stack_ops.dlm_lock(conn, mode, lksb, flags,
-                                      name, namelen, astarg);
+       return active_stack->sp_ops->dlm_lock(conn, mode, lksb, flags,
+                                             name, namelen, astarg);
 }
+EXPORT_SYMBOL_GPL(ocfs2_dlm_lock);
 
 int ocfs2_dlm_unlock(struct ocfs2_cluster_connection *conn,
                     union ocfs2_dlm_lksb *lksb,
                     u32 flags,
                     void *astarg)
 {
-       BUG_ON(stack_glue_lproto == NULL);
+       BUG_ON(lproto == NULL);
 
-       return o2cb_stack_ops.dlm_unlock(conn, lksb, flags, astarg);
+       return active_stack->sp_ops->dlm_unlock(conn, lksb, flags, astarg);
 }
+EXPORT_SYMBOL_GPL(ocfs2_dlm_unlock);
 
 int ocfs2_dlm_lock_status(union ocfs2_dlm_lksb *lksb)
 {
-       return o2cb_stack_ops.lock_status(lksb);
+       return active_stack->sp_ops->lock_status(lksb);
 }
+EXPORT_SYMBOL_GPL(ocfs2_dlm_lock_status);
 
 /*
  * Why don't we cast to ocfs2_meta_lvb?  The "clean" answer is that we
  */
 void *ocfs2_dlm_lvb(union ocfs2_dlm_lksb *lksb)
 {
-       return o2cb_stack_ops.lock_lvb(lksb);
+       return active_stack->sp_ops->lock_lvb(lksb);
 }
+EXPORT_SYMBOL_GPL(ocfs2_dlm_lvb);
 
 void ocfs2_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb)
 {
-       o2cb_stack_ops.dump_lksb(lksb);
+       active_stack->sp_ops->dump_lksb(lksb);
 }
+EXPORT_SYMBOL_GPL(ocfs2_dlm_dump_lksb);
 
 int ocfs2_cluster_connect(const char *group,
                          int grouplen,
        new_conn->cc_recovery_data = recovery_data;
 
        /* Start the new connection at our maximum compatibility level */
-       new_conn->cc_version = stack_glue_lproto->lp_max_version;
+       new_conn->cc_version = lproto->lp_max_version;
+
+       /* This will pin the stack driver if successful */
+       rc = ocfs2_stack_driver_get("o2cb");
+       if (rc)
+               goto out_free;
 
-       rc = o2cb_stack_ops.connect(new_conn);
+       rc = active_stack->sp_ops->connect(new_conn);
        if (rc) {
-               mlog_errno(rc);
+               ocfs2_stack_driver_put();
                goto out_free;
        }
 
 out:
        return rc;
 }
+EXPORT_SYMBOL_GPL(ocfs2_cluster_connect);
 
-int ocfs2_cluster_disconnect(struct ocfs2_cluster_connection *conn)
+/* If hangup_pending is 0, the stack driver will be dropped */
+int ocfs2_cluster_disconnect(struct ocfs2_cluster_connection *conn,
+                            int hangup_pending)
 {
        int ret;
 
        BUG_ON(conn == NULL);
 
-       ret = o2cb_stack_ops.disconnect(conn);
+       ret = active_stack->sp_ops->disconnect(conn, hangup_pending);
 
        /* XXX Should we free it anyway? */
-       if (!ret)
+       if (!ret) {
                kfree(conn);
+               if (!hangup_pending)
+                       ocfs2_stack_driver_put();
+       }
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(ocfs2_cluster_disconnect);
 
 void ocfs2_cluster_hangup(const char *group, int grouplen)
 {
        BUG_ON(group == NULL);
        BUG_ON(group[grouplen] != '\0');
 
-       o2cb_stack_ops.hangup(group, grouplen);
+       active_stack->sp_ops->hangup(group, grouplen);
+
+       /* cluster_disconnect() was called with hangup_pending==1 */
+       ocfs2_stack_driver_put();
 }
+EXPORT_SYMBOL_GPL(ocfs2_cluster_hangup);
 
 int ocfs2_cluster_this_node(unsigned int *node)
 {
-       return o2cb_stack_ops.this_node(node);
+       return active_stack->sp_ops->this_node(node);
 }
+EXPORT_SYMBOL_GPL(ocfs2_cluster_this_node);
 
-void ocfs2_stack_glue_set_locking_protocol(struct ocfs2_locking_protocol *proto)
+
+static int __init ocfs2_stack_glue_init(void)
 {
-       BUG_ON(proto != NULL);
+       return 0;
+}
 
-       stack_glue_lproto = proto;
+static void __exit ocfs2_stack_glue_exit(void)
+{
+       lproto = NULL;
 }
 
+MODULE_AUTHOR("Oracle");
+MODULE_DESCRIPTION("ocfs2 cluter stack glue layer");
+MODULE_LICENSE("GPL");
+module_init(ocfs2_stack_glue_init);
+module_exit(ocfs2_stack_glue_exit);
 
         * Once ->disconnect() has returned, the connection structure will
         * be freed.  Thus, a stack must not return from ->disconnect()
         * until it will no longer reference the conn pointer.
+        *
+        * If hangup_pending is zero, ocfs2_cluster_disconnect() will also
+        * be dropping the reference on the module.
         */
-       int (*disconnect)(struct ocfs2_cluster_connection *conn);
+       int (*disconnect)(struct ocfs2_cluster_connection *conn,
+                         int hangup_pending);
 
        /*
         * ocfs2_cluster_hangup() exists for compatibility with older
         * ocfs2 tools.  Only the classic stack really needs it.  As such
         * ->hangup() is not required of all stacks.  See the comment by
         * ocfs2_cluster_hangup() for more details.
+        *
+        * Note that ocfs2_cluster_hangup() can only be called if
+        * hangup_pending was passed to ocfs2_cluster_disconnect().
         */
        void (*hangup)(const char *group, int grouplen);
 
        void (*dump_lksb)(union ocfs2_dlm_lksb *lksb);
 };
 
+/*
+ * Each stack plugin must describe itself by registering a
+ * ocfs2_stack_plugin structure.  This is only seen by stackglue and the
+ * stack driver.
+ */
+struct ocfs2_stack_plugin {
+       char *sp_name;
+       struct ocfs2_stack_operations *sp_ops;
+       struct module *sp_owner;
+
+       /* These are managed by the stackglue code. */
+       struct list_head sp_list;
+       unsigned int sp_count;
+       struct ocfs2_locking_protocol *sp_proto;
+};
+
+
+/* Used by the filesystem */
 int ocfs2_cluster_connect(const char *group,
                          int grouplen,
                          void (*recovery_handler)(int node_num,
                                                   void *recovery_data),
                          void *recovery_data,
                          struct ocfs2_cluster_connection **conn);
-int ocfs2_cluster_disconnect(struct ocfs2_cluster_connection *conn);
+int ocfs2_cluster_disconnect(struct ocfs2_cluster_connection *conn,
+                            int hangup_pending);
 void ocfs2_cluster_hangup(const char *group, int grouplen);
 int ocfs2_cluster_this_node(unsigned int *node);
 
 
 void ocfs2_stack_glue_set_locking_protocol(struct ocfs2_locking_protocol *proto);
 
-extern struct ocfs2_locking_protocol *stack_glue_lproto;
-extern struct ocfs2_stack_operations o2cb_stack_ops;
+
+/* Used by stack plugins */
+int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin);
+void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin);
 #endif  /* STACKGLUE_H */