/* * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License version 2. */ #include <linux/module.h> #include <linux/init.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/kmod.h> #include <linux/fs.h> #include <linux/delay.h> #include <linux/lm_interface.h> struct lmh_wrapper { struct list_head lw_list; const struct lm_lockops *lw_ops; }; /* List of registered low-level locking protocols. A file system selects one of them by name at mount time, e.g. lock_nolock, lock_dlm. */ static LIST_HEAD(lmh_list); static DEFINE_MUTEX(lmh_lock); /** * gfs2_register_lockproto - Register a low-level locking protocol * @proto: the protocol definition * * Returns: 0 on success, -EXXX on failure */ int gfs2_register_lockproto(const struct lm_lockops *proto) { struct lmh_wrapper *lw; mutex_lock(&lmh_lock); list_for_each_entry(lw, &lmh_list, lw_list) { if (!strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name)) { mutex_unlock(&lmh_lock); printk(KERN_INFO "GFS2: protocol %s already exists\n", proto->lm_proto_name); return -EEXIST; } } lw = kzalloc(sizeof(struct lmh_wrapper), GFP_KERNEL); if (!lw) { mutex_unlock(&lmh_lock); return -ENOMEM; } lw->lw_ops = proto; list_add(&lw->lw_list, &lmh_list); mutex_unlock(&lmh_lock); return 0; } /** * gfs2_unregister_lockproto - Unregister a low-level locking protocol * @proto: the protocol definition * */ void gfs2_unregister_lockproto(const struct lm_lockops *proto) { struct lmh_wrapper *lw; mutex_lock(&lmh_lock); list_for_each_entry(lw, &lmh_list, lw_list) { if (!strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name)) { list_del(&lw->lw_list); mutex_unlock(&lmh_lock); kfree(lw); return; } } mutex_unlock(&lmh_lock); printk(KERN_WARNING "GFS2: can't unregister lock protocol %s\n", proto->lm_proto_name); } /** * gfs2_mount_lockproto - Mount a lock protocol * @proto_name - the name of the protocol * @table_name - the name of the lock space * @host_data - data specific to this host * @cb - the callback to the code using the lock module * @sdp - The GFS2 superblock * @min_lvb_size - the mininum LVB size that the caller can deal with * @flags - LM_MFLAG_* * @lockstruct - a structure returned describing the mount * * Returns: 0 on success, -EXXX on failure */ int gfs2_mount_lockproto(char *proto_name, char *table_name, char *host_data, lm_callback_t cb, void *cb_data, unsigned int min_lvb_size, int flags, struct lm_lockstruct *lockstruct, struct kobject *fskobj) { struct lmh_wrapper *lw = NULL; int try = 0; int error, found; retry: mutex_lock(&lmh_lock); found = 0; list_for_each_entry(lw, &lmh_list, lw_list) { if (!strcmp(lw->lw_ops->lm_proto_name, proto_name)) { found = 1; break; } } if (!found) { if (!try && capable(CAP_SYS_MODULE)) { try = 1; mutex_unlock(&lmh_lock); request_module(proto_name); goto retry; } printk(KERN_INFO "GFS2: can't find protocol %s\n", proto_name); error = -ENOENT; goto out; } if (!try_module_get(lw->lw_ops->lm_owner)) { try = 0; mutex_unlock(&lmh_lock); msleep(1000); goto retry; } error = lw->lw_ops->lm_mount(table_name, host_data, cb, cb_data, min_lvb_size, flags, lockstruct, fskobj); if (error) module_put(lw->lw_ops->lm_owner); out: mutex_unlock(&lmh_lock); return error; } void gfs2_unmount_lockproto(struct lm_lockstruct *lockstruct) { mutex_lock(&lmh_lock); lockstruct->ls_ops->lm_unmount(lockstruct->ls_lockspace); if (lockstruct->ls_ops->lm_owner) module_put(lockstruct->ls_ops->lm_owner); mutex_unlock(&lmh_lock); } /** * gfs2_withdraw_lockproto - abnormally unmount a lock module * @lockstruct: the lockstruct passed into mount * */ void gfs2_withdraw_lockproto(struct lm_lockstruct *lockstruct) { mutex_lock(&lmh_lock); lockstruct->ls_ops->lm_withdraw(lockstruct->ls_lockspace); if (lockstruct->ls_ops->lm_owner) module_put(lockstruct->ls_ops->lm_owner); mutex_unlock(&lmh_lock); } EXPORT_SYMBOL_GPL(gfs2_register_lockproto); EXPORT_SYMBOL_GPL(gfs2_unregister_lockproto);