/* * 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 #include #include #include #include #include #include #include #include #include struct lmh_wrapper { struct list_head lw_list; const struct lm_lockops *lw_ops; }; struct nolock_lockspace { unsigned int nl_lvb_size; }; /** * nolock_get_lock - get a lm_lock_t given a descripton of the lock * @lockspace: the lockspace the lock lives in * @name: the name of the lock * @lockp: return the lm_lock_t here * * Returns: 0 on success, -EXXX on failure */ static int nolock_get_lock(void *lockspace, struct lm_lockname *name, void **lockp) { *lockp = lockspace; return 0; } /** * nolock_put_lock - get rid of a lock structure * @lock: the lock to throw away * */ static void nolock_put_lock(void *lock) { } /** * nolock_hold_lvb - hold on to a lock value block * @lock: the lock the LVB is associated with * @lvbp: return the lm_lvb_t here * * Returns: 0 on success, -EXXX on failure */ static int nolock_hold_lvb(void *lock, char **lvbp) { struct nolock_lockspace *nl = lock; int error = 0; *lvbp = kzalloc(nl->nl_lvb_size, GFP_KERNEL); if (!*lvbp) error = -ENOMEM; return error; } /** * nolock_unhold_lvb - release a LVB * @lock: the lock the LVB is associated with * @lvb: the lock value block * */ static void nolock_unhold_lvb(void *lock, char *lvb) { kfree(lvb); } static int nolock_mount(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); static void nolock_unmount(void *lockspace); /* 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 const struct lm_lockops nolock_ops = { .lm_proto_name = "lock_nolock", .lm_mount = nolock_mount, .lm_unmount = nolock_unmount, .lm_get_lock = nolock_get_lock, .lm_put_lock = nolock_put_lock, .lm_hold_lvb = nolock_hold_lvb, .lm_unhold_lvb = nolock_unhold_lvb, }; static struct lmh_wrapper nolock_proto = { .lw_list = LIST_HEAD_INIT(nolock_proto.lw_list), .lw_ops = &nolock_ops, }; static LIST_HEAD(lmh_list); static DEFINE_MUTEX(lmh_lock); static int nolock_mount(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) { char *c; unsigned int jid; struct nolock_lockspace *nl; c = strstr(host_data, "jid="); if (!c) jid = 0; else { c += 4; sscanf(c, "%u", &jid); } nl = kzalloc(sizeof(struct nolock_lockspace), GFP_KERNEL); if (!nl) return -ENOMEM; nl->nl_lvb_size = min_lvb_size; lockstruct->ls_jid = jid; lockstruct->ls_first = 1; lockstruct->ls_lvb_size = min_lvb_size; lockstruct->ls_lockspace = nl; lockstruct->ls_ops = &nolock_ops; lockstruct->ls_flags = LM_LSFLAG_LOCAL; return 0; } static void nolock_unmount(void *lockspace) { struct nolock_lockspace *nl = lockspace; kfree(nl); } /** * 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); if (list_empty(&nolock_proto.lw_list)) list_add(&nolock_proto.lw_list, &lmh_list); 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 (lw->lw_ops->lm_owner && !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); if (lockstruct->ls_ops->lm_unmount) 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);