diff options
Diffstat (limited to 'fs/dlm/device.c')
-rw-r--r-- | fs/dlm/device.c | 1239 |
1 files changed, 0 insertions, 1239 deletions
diff --git a/fs/dlm/device.c b/fs/dlm/device.c deleted file mode 100644 index 825bbc0a09c0..000000000000 --- a/fs/dlm/device.c +++ /dev/null @@ -1,1239 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** 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 v.2. -** -******************************************************************************* -******************************************************************************/ - -/* - * device.c - * - * This is the userland interface to the DLM. - * - * The locking is done via a misc char device (find the - * registered minor number in /proc/misc). - * - * User code should not use this interface directly but - * call the library routines in libdlm.a instead. - * - */ - -#include <linux/miscdevice.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/module.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/signal.h> -#include <linux/spinlock.h> -#include <linux/idr.h> - -#include <linux/dlm.h> -#include <linux/dlm_device.h> - -#include "lvb_table.h" - -static struct file_operations _dlm_fops; -static const char *name_prefix="dlm"; -static struct list_head user_ls_list; -static struct mutex user_ls_lock; - -/* Flags in li_flags */ -#define LI_FLAG_COMPLETE 1 -#define LI_FLAG_FIRSTLOCK 2 -#define LI_FLAG_PERSISTENT 3 -#define LI_FLAG_ONLIST 4 - -/* flags in ls_flags*/ -#define LS_FLAG_DELETED 1 -#define LS_FLAG_AUTOFREE 2 - -/* flags in ls_flags*/ -#define FI_FLAG_OPEN 1 -#define FI_FLAG_COMPAT 2 - -#define LOCKINFO_MAGIC 0x53595324 - -struct lock_info { - uint32_t li_magic; - uint8_t li_cmd; - int8_t li_grmode; - int8_t li_rqmode; - struct dlm_lksb li_lksb; - wait_queue_head_t li_waitq; - unsigned long li_flags; - void __user *li_castparam; - void __user *li_castaddr; - void __user *li_bastparam; - void __user *li_bastaddr; - void __user *li_pend_bastparam; - void __user *li_pend_bastaddr; - struct list_head li_ownerqueue; - struct file_info *li_file; - struct dlm_lksb __user *li_user_lksb; - struct completion li_firstcomp; -}; - -/* A queued AST no less */ -struct ast_info { - struct dlm_lock_result result; - struct list_head list; - uint32_t lvb_updated; - uint32_t progress; /* How much has been read */ -}; - -/* One of these per userland lockspace */ -struct user_ls { - void *ls_lockspace; - atomic_t ls_refcnt; - long ls_flags; - - /* Lock infos are stored in here indexed by lock ID */ - struct idr lockinfo_idr; - rwlock_t lockinfo_lock; - - /* Passed into misc_register() */ - struct miscdevice ls_miscinfo; - struct list_head ls_list; -}; - -/* misc_device info for the control device */ -static struct miscdevice ctl_device; - -/* - * Stuff we hang off the file struct. - * The first two are to cope with unlocking all the - * locks help by a process when it dies. - */ -struct file_info { - struct list_head fi_li_list; /* List of active lock_infos */ - spinlock_t fi_li_lock; - struct list_head fi_ast_list; /* Queue of ASTs to be delivered */ - spinlock_t fi_ast_lock; - wait_queue_head_t fi_wait; - struct user_ls *fi_ls; - atomic_t fi_refcnt; /* Number of users */ - unsigned long fi_flags; -}; - -#ifdef CONFIG_COMPAT - -struct dlm_lock_params32 { - __u8 mode; - __u8 namelen; - __u16 flags; - __u32 lkid; - __u32 parent; - - __u32 castparam; - __u32 castaddr; - __u32 bastparam; - __u32 bastaddr; - __u32 lksb; - - char lvb[DLM_USER_LVB_LEN]; - char name[0]; -}; - -struct dlm_write_request32 { - __u32 version[3]; - __u8 cmd; - __u8 is64bit; - __u8 unused[2]; - - union { - struct dlm_lock_params32 lock; - struct dlm_lspace_params lspace; - } i; -}; - -struct dlm_lksb32 { - __u32 sb_status; - __u32 sb_lkid; - __u8 sb_flags; - __u32 sb_lvbptr; -}; - -struct dlm_lock_result32 { - __u32 length; - __u32 user_astaddr; - __u32 user_astparam; - __u32 user_lksb; - struct dlm_lksb32 lksb; - __u8 bast_mode; - __u8 unused[3]; - /* Offsets may be zero if no data is present */ - __u32 lvb_offset; -}; - - -static void compat_input(struct dlm_write_request *kparams, struct dlm_write_request32 *k32params) -{ - - kparams->version[0] = k32params->version[0]; - kparams->version[1] = k32params->version[1]; - kparams->version[2] = k32params->version[2]; - - kparams->cmd = k32params->cmd; - kparams->is64bit = k32params->is64bit; - if (kparams->cmd == DLM_USER_CREATE_LOCKSPACE || - kparams->cmd == DLM_USER_REMOVE_LOCKSPACE) { - - kparams->i.lspace.flags = k32params->i.lspace.flags; - kparams->i.lspace.minor = k32params->i.lspace.minor; - strcpy(kparams->i.lspace.name, k32params->i.lspace.name); - } - else { - kparams->i.lock.mode = k32params->i.lock.mode; - kparams->i.lock.namelen = k32params->i.lock.namelen; - kparams->i.lock.flags = k32params->i.lock.flags; - kparams->i.lock.lkid = k32params->i.lock.lkid; - kparams->i.lock.parent = k32params->i.lock.parent; - kparams->i.lock.castparam = (void *)(long)k32params->i.lock.castparam; - kparams->i.lock.castaddr = (void *)(long)k32params->i.lock.castaddr; - kparams->i.lock.bastparam = (void *)(long)k32params->i.lock.bastparam; - kparams->i.lock.bastaddr = (void *)(long)k32params->i.lock.bastaddr; - kparams->i.lock.lksb = (void *)(long)k32params->i.lock.lksb; - memcpy(kparams->i.lock.lvb, k32params->i.lock.lvb, DLM_USER_LVB_LEN); - memcpy(kparams->i.lock.name, k32params->i.lock.name, kparams->i.lock.namelen); - } -} - -void compat_output(struct dlm_lock_result *res, struct dlm_lock_result32 *res32) -{ - res32->length = res->length - (sizeof(struct dlm_lock_result) - sizeof(struct dlm_lock_result32)); - res32->user_astaddr = (__u32)(long)res->user_astaddr; - res32->user_astparam = (__u32)(long)res->user_astparam; - res32->user_lksb = (__u32)(long)res->user_lksb; - res32->bast_mode = res->bast_mode; - - res32->lvb_offset = res->lvb_offset; - res32->length = res->length; - - res32->lksb.sb_status = res->lksb.sb_status; - res32->lksb.sb_flags = res->lksb.sb_flags; - res32->lksb.sb_lkid = res->lksb.sb_lkid; - res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr; -} -#endif - - -/* get and put ops for file_info. - Actually I don't really like "get" and "put", but everyone - else seems to use them and I can't think of anything - nicer at the moment */ -static void get_file_info(struct file_info *f) -{ - atomic_inc(&f->fi_refcnt); -} - -static void put_file_info(struct file_info *f) -{ - if (atomic_dec_and_test(&f->fi_refcnt)) - kfree(f); -} - -static void release_lockinfo(struct user_ls *ls, struct lock_info *li) -{ - put_file_info(li->li_file); - - write_lock(&ls->lockinfo_lock); - idr_remove(&ls->lockinfo_idr, li->li_lksb.sb_lkid); - write_unlock(&ls->lockinfo_lock); - - if (li->li_lksb.sb_lvbptr) - kfree(li->li_lksb.sb_lvbptr); - kfree(li); - - module_put(THIS_MODULE); -} - -static struct lock_info *get_lockinfo(struct user_ls *ls, uint32_t lockid) -{ - struct lock_info *li; - - read_lock(&ls->lockinfo_lock); - li = idr_find(&ls->lockinfo_idr, lockid); - read_unlock(&ls->lockinfo_lock); - - return li; -} - -static int add_lockinfo(struct user_ls *ls, struct lock_info *li) -{ - int n; - int r; - int ret = -EINVAL; - - write_lock(&ls->lockinfo_lock); - - if (idr_find(&ls->lockinfo_idr, li->li_lksb.sb_lkid)) - goto out_up; - - ret = -ENOMEM; - r = idr_pre_get(&ls->lockinfo_idr, GFP_KERNEL); - if (!r) - goto out_up; - - r = idr_get_new_above(&ls->lockinfo_idr, li, li->li_lksb.sb_lkid, &n); - if (r) - goto out_up; - - if (n != li->li_lksb.sb_lkid) { - idr_remove(&ls->lockinfo_idr, n); - goto out_up; - } - - ret = 0; - - out_up: - write_unlock(&ls->lockinfo_lock); - - return ret; -} - - -static struct user_ls *__find_lockspace(int minor) -{ - struct user_ls *lsinfo; - - list_for_each_entry(lsinfo, &user_ls_list, ls_list) { - if (lsinfo->ls_miscinfo.minor == minor) - return lsinfo; - } - return NULL; -} - -/* Find a lockspace struct given the device minor number */ -static struct user_ls *find_lockspace(int minor) -{ - struct user_ls *lsinfo; - - mutex_lock(&user_ls_lock); - lsinfo = __find_lockspace(minor); - mutex_unlock(&user_ls_lock); - - return lsinfo; -} - -static void add_lockspace_to_list(struct user_ls *lsinfo) -{ - mutex_lock(&user_ls_lock); - list_add(&lsinfo->ls_list, &user_ls_list); - mutex_unlock(&user_ls_lock); -} - -/* Register a lockspace with the DLM and create a misc - device for userland to access it */ -static int register_lockspace(char *name, struct user_ls **ls, int flags) -{ - struct user_ls *newls; - int status; - int namelen; - - namelen = strlen(name)+strlen(name_prefix)+2; - - newls = kzalloc(sizeof(struct user_ls), GFP_KERNEL); - if (!newls) - return -ENOMEM; - - newls->ls_miscinfo.name = kzalloc(namelen, GFP_KERNEL); - if (!newls->ls_miscinfo.name) { - kfree(newls); - return -ENOMEM; - } - - status = dlm_new_lockspace(name, strlen(name), &newls->ls_lockspace, 0, - DLM_USER_LVB_LEN); - if (status != 0) { - kfree(newls->ls_miscinfo.name); - kfree(newls); - return status; - } - - idr_init(&newls->lockinfo_idr); - rwlock_init(&newls->lockinfo_lock); - - snprintf((char*)newls->ls_miscinfo.name, namelen, "%s_%s", - name_prefix, name); - - newls->ls_miscinfo.fops = &_dlm_fops; - newls->ls_miscinfo.minor = MISC_DYNAMIC_MINOR; - - status = misc_register(&newls->ls_miscinfo); - if (status) { - printk(KERN_ERR "dlm: misc register failed for %s\n", name); - dlm_release_lockspace(newls->ls_lockspace, 0); - kfree(newls->ls_miscinfo.name); - kfree(newls); - return status; - } - - if (flags & DLM_USER_LSFLG_AUTOFREE) - set_bit(LS_FLAG_AUTOFREE, &newls->ls_flags); - - add_lockspace_to_list(newls); - *ls = newls; - return 0; -} - -/* Called with the user_ls_lock mutex held */ -static int unregister_lockspace(struct user_ls *lsinfo, int force) -{ - int status; - - status = dlm_release_lockspace(lsinfo->ls_lockspace, force); - if (status) - return status; - - status = misc_deregister(&lsinfo->ls_miscinfo); - if (status) - return status; - - list_del(&lsinfo->ls_list); - set_bit(LS_FLAG_DELETED, &lsinfo->ls_flags); - lsinfo->ls_lockspace = NULL; - if (atomic_read(&lsinfo->ls_refcnt) == 0) { - kfree(lsinfo->ls_miscinfo.name); - kfree(lsinfo); - } - - return 0; -} - -/* Add it to userland's AST queue */ -static void add_to_astqueue(struct lock_info *li, void *astaddr, void *astparam, - int lvb_updated) -{ - struct ast_info *ast = kzalloc(sizeof(struct ast_info), GFP_KERNEL); - if (!ast) - return; - - ast->result.user_astparam = astparam; - ast->result.user_astaddr = astaddr; - ast->result.user_lksb = li->li_user_lksb; - memcpy(&ast->result.lksb, &li->li_lksb, sizeof(struct dlm_lksb)); - ast->lvb_updated = lvb_updated; - - spin_lock(&li->li_file->fi_ast_lock); - list_add_tail(&ast->list, &li->li_file->fi_ast_list); - spin_unlock(&li->li_file->fi_ast_lock); - wake_up_interruptible(&li->li_file->fi_wait); -} - -static void bast_routine(void *param, int mode) -{ - struct lock_info *li = param; - - if (li && li->li_bastaddr) - add_to_astqueue(li, li->li_bastaddr, li->li_bastparam, 0); -} - -/* - * This is the kernel's AST routine. - * All lock, unlock & query operations complete here. - * The only syncronous ops are those done during device close. - */ -static void ast_routine(void *param) -{ - struct lock_info *li = param; - - /* Param may be NULL if a persistent lock is unlocked by someone else */ - if (!li) - return; - - /* If this is a succesful conversion then activate the blocking ast - * args from the conversion request */ - if (!test_bit(LI_FLAG_FIRSTLOCK, &li->li_flags) && - li->li_lksb.sb_status == 0) { - - li->li_bastparam = li->li_pend_bastparam; - li->li_bastaddr = li->li_pend_bastaddr; - li->li_pend_bastaddr = NULL; - } - - /* If it's an async request then post data to the user's AST queue. */ - if (li->li_castaddr) { - int lvb_updated = 0; - - /* See if the lvb has been updated */ - if (dlm_lvb_operations[li->li_grmode+1][li->li_rqmode+1] == 1) - lvb_updated = 1; - - if (li->li_lksb.sb_status == 0) - li->li_grmode = li->li_rqmode; - - /* Only queue AST if the device is still open */ - if (test_bit(FI_FLAG_OPEN, &li->li_file->fi_flags)) - add_to_astqueue(li, li->li_castaddr, li->li_castparam, - lvb_updated); - - /* If it's a new lock operation that failed, then - * remove it from the owner queue and free the - * lock_info. - */ - if (test_and_clear_bit(LI_FLAG_FIRSTLOCK, &li->li_flags) && - li->li_lksb.sb_status != 0) { - - /* Wait till dlm_lock() has finished */ - wait_for_completion(&li->li_firstcomp); - - spin_lock(&li->li_file->fi_li_lock); - list_del(&li->li_ownerqueue); - clear_bit(LI_FLAG_ONLIST, &li->li_flags); - spin_unlock(&li->li_file->fi_li_lock); - release_lockinfo(li->li_file->fi_ls, li); - return; - } - /* Free unlocks & queries */ - if (li->li_lksb.sb_status == -DLM_EUNLOCK || - li->li_cmd == DLM_USER_QUERY) { - release_lockinfo(li->li_file->fi_ls, li); - } - } else { - /* Synchronous request, just wake up the caller */ - set_bit(LI_FLAG_COMPLETE, &li->li_flags); - wake_up_interruptible(&li->li_waitq); - } -} - -/* - * Wait for the lock op to complete and return the status. - */ -static int wait_for_ast(struct lock_info *li) -{ - /* Wait for the AST routine to complete */ - set_task_state(current, TASK_INTERRUPTIBLE); - while (!test_bit(LI_FLAG_COMPLETE, &li->li_flags)) - schedule(); - - set_task_state(current, TASK_RUNNING); - - return li->li_lksb.sb_status; -} - - -/* Open on control device */ -static int dlm_ctl_open(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return 0; -} - -/* Close on control device */ -static int dlm_ctl_close(struct inode *inode, struct file *file) -{ - return 0; -} - -/* Open on lockspace device */ -static int dlm_open(struct inode *inode, struct file *file) -{ - struct file_info *f; - struct user_ls *lsinfo; - - lsinfo = find_lockspace(iminor(inode)); - if (!lsinfo) - return -ENOENT; - - f = kzalloc(sizeof(struct file_info), GFP_KERNEL); - if (!f) - return -ENOMEM; - - atomic_inc(&lsinfo->ls_refcnt); - INIT_LIST_HEAD(&f->fi_li_list); - INIT_LIST_HEAD(&f->fi_ast_list); - spin_lock_init(&f->fi_li_lock); - spin_lock_init(&f->fi_ast_lock); - init_waitqueue_head(&f->fi_wait); - f->fi_ls = lsinfo; - f->fi_flags = 0; - get_file_info(f); - set_bit(FI_FLAG_OPEN, &f->fi_flags); - - file->private_data = f; - - return 0; -} - -/* Check the user's version matches ours */ -static int check_version(struct dlm_write_request *req) -{ - if (req->version[0] != DLM_DEVICE_VERSION_MAJOR || - (req->version[0] == DLM_DEVICE_VERSION_MAJOR && - req->version[1] > DLM_DEVICE_VERSION_MINOR)) { - - printk(KERN_DEBUG "dlm: process %s (%d) version mismatch " - "user (%d.%d.%d) kernel (%d.%d.%d)\n", - current->comm, - current->pid, - req->version[0], - req->version[1], - req->version[2], - DLM_DEVICE_VERSION_MAJOR, - DLM_DEVICE_VERSION_MINOR, - DLM_DEVICE_VERSION_PATCH); - return -EINVAL; - } - return 0; -} - -/* Close on lockspace device */ -static int dlm_close(struct inode *inode, struct file *file) -{ - struct file_info *f = file->private_data; - struct lock_info li; - struct lock_info *old_li, *safe; - sigset_t tmpsig; - sigset_t allsigs; - struct user_ls *lsinfo; - DECLARE_WAITQUEUE(wq, current); - - lsinfo = find_lockspace(iminor(inode)); - if (!lsinfo) - return -ENOENT; - - /* Mark this closed so that ASTs will not be delivered any more */ - clear_bit(FI_FLAG_OPEN, &f->fi_flags); - - /* Block signals while we are doing this */ - sigfillset(&allsigs); - sigprocmask(SIG_BLOCK, &allsigs, &tmpsig); - - /* We use our own lock_info struct here, so that any - * outstanding "real" ASTs will be delivered with the - * corresponding "real" params, thus freeing the lock_info - * that belongs the lock. This catches the corner case where - * a lock is BUSY when we try to unlock it here - */ - memset(&li, 0, sizeof(li)); - clear_bit(LI_FLAG_COMPLETE, &li.li_flags); - init_waitqueue_head(&li.li_waitq); - add_wait_queue(&li.li_waitq, &wq); - - /* - * Free any outstanding locks, they are on the - * list in LIFO order so there should be no problems - * about unlocking parents before children. - */ - list_for_each_entry_safe(old_li, safe, &f->fi_li_list, li_ownerqueue) { - int status; - int flags = 0; - - /* Don't unlock persistent locks, just mark them orphaned */ - if (test_bit(LI_FLAG_PERSISTENT, &old_li->li_flags)) { - list_del(&old_li->li_ownerqueue); - - /* Update master copy */ - /* TODO: Check locking core updates the local and - remote ORPHAN flags */ - li.li_lksb.sb_lkid = old_li->li_lksb.sb_lkid; - status = dlm_lock(f->fi_ls->ls_lockspace, - old_li->li_grmode, &li.li_lksb, - DLM_LKF_CONVERT|DLM_LKF_ORPHAN, - NULL, 0, 0, ast_routine, NULL, NULL); - if (status != 0) - printk("dlm: Error orphaning lock %x: %d\n", - old_li->li_lksb.sb_lkid, status); - - /* But tidy our references in it */ - release_lockinfo(old_li->li_file->fi_ls, old_li); - continue; - } - - clear_bit(LI_FLAG_COMPLETE, &li.li_flags); - - flags = DLM_LKF_FORCEUNLOCK; - if (old_li->li_grmode >= DLM_LOCK_PW) - flags |= DLM_LKF_IVVALBLK; - - status = dlm_unlock(f->fi_ls->ls_lockspace, - old_li->li_lksb.sb_lkid, flags, - &li.li_lksb, &li); - - /* Must wait for it to complete as the next lock could be its - * parent */ - if (status == 0) - wait_for_ast(&li); - - /* Unlock suceeded, free the lock_info struct. */ - if (status == 0) - release_lockinfo(old_li->li_file->fi_ls, old_li); - } - - remove_wait_queue(&li.li_waitq, &wq); - - /* - * If this is the last reference to the lockspace - * then free the struct. If it's an AUTOFREE lockspace - * then free the whole thing. - */ - mutex_lock(&user_ls_lock); - if (atomic_dec_and_test(&lsinfo->ls_refcnt)) { - - if (lsinfo->ls_lockspace) { - if (test_bit(LS_FLAG_AUTOFREE, &lsinfo->ls_flags)) { - unregister_lockspace(lsinfo, 1); - } - } else { - kfree(lsinfo->ls_miscinfo.name); - kfree(lsinfo); - } - } - mutex_unlock(&user_ls_lock); - put_file_info(f); - - /* Restore signals */ - sigprocmask(SIG_SETMASK, &tmpsig, NULL); - recalc_sigpending(); - - return 0; -} - -static int do_user_create_lockspace(struct file_info *fi, uint8_t cmd, - struct dlm_lspace_params *kparams) -{ - int status; - struct user_ls *lsinfo; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - status = register_lockspace(kparams->name, &lsinfo, kparams->flags); - - /* If it succeeded then return the minor number */ - if (status == 0) - status = lsinfo->ls_miscinfo.minor; - - return status; -} - -static int do_user_remove_lockspace(struct file_info *fi, uint8_t cmd, - struct dlm_lspace_params *kparams) -{ - int status; - int force = 1; - struct user_ls *lsinfo; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - mutex_lock(&user_ls_lock); - lsinfo = __find_lockspace(kparams->minor); - if (!lsinfo) { - mutex_unlock(&user_ls_lock); - return -EINVAL; - } - - if (kparams->flags & DLM_USER_LSFLG_FORCEFREE) - force = 3; - - status = unregister_lockspace(lsinfo, force); - mutex_unlock(&user_ls_lock); - - return status; -} - -/* Read call, might block if no ASTs are waiting. - * It will only ever return one message at a time, regardless - * of how many are pending. - */ -static ssize_t dlm_read(struct file *file, char __user *buffer, size_t count, - loff_t *ppos) -{ - struct file_info *fi = file->private_data; - struct ast_info *ast; - void *data; - int data_size; - int struct_size; - int offset; - DECLARE_WAITQUEUE(wait, current); -#ifdef CONFIG_COMPAT - struct dlm_lock_result32 result32; - - if (count < sizeof(struct dlm_lock_result32)) -#else - if (count < sizeof(struct dlm_lock_result)) -#endif - return -EINVAL; - - spin_lock(&fi->fi_ast_lock); - if (list_empty(&fi->fi_ast_list)) { - - /* No waiting ASTs. - * Return EOF if the lockspace been deleted. - */ - if (test_bit(LS_FLAG_DELETED, &fi->fi_ls->ls_flags)) - return 0; - - if (file->f_flags & O_NONBLOCK) { - spin_unlock(&fi->fi_ast_lock); - return -EAGAIN; - } - - add_wait_queue(&fi->fi_wait, &wait); - - repeat: - set_current_state(TASK_INTERRUPTIBLE); - if (list_empty(&fi->fi_ast_list) && - !signal_pending(current)) { - - spin_unlock(&fi->fi_ast_lock); - schedule(); - spin_lock(&fi->fi_ast_lock); - goto repeat; - } - - current->state = TASK_RUNNING; - remove_wait_queue(&fi->fi_wait, &wait); - - if (signal_pending(current)) { - spin_unlock(&fi->fi_ast_lock); - return -ERESTARTSYS; - } - } - - ast = list_entry(fi->fi_ast_list.next, struct ast_info, list); - list_del(&ast->list); - spin_unlock(&fi->fi_ast_lock); - - /* Work out the size of the returned data */ -#ifdef CONFIG_COMPAT - if (test_bit(FI_FLAG_COMPAT, &fi->fi_flags)) { - data_size = struct_size = sizeof(struct dlm_lock_result32); - data = &result32; - } - else -#endif - { - data_size = struct_size = sizeof(struct dlm_lock_result); - data = &ast->result; - } - if (ast->lvb_updated && ast->result.lksb.sb_lvbptr) - data_size += DLM_USER_LVB_LEN; - - offset = struct_size; - - /* Room for the extended data ? */ - if (count >= data_size) { - - if (ast->lvb_updated && ast->result.lksb.sb_lvbptr) { - if (copy_to_user(buffer+offset, - ast->result.lksb.sb_lvbptr, - DLM_USER_LVB_LEN)) - return -EFAULT; - ast->result.lvb_offset = offset; - offset += DLM_USER_LVB_LEN; - } - } - - ast->result.length = data_size; - -#ifdef CONFIG_COMPAT - compat_output(&ast->result, &result32); -#endif - - /* Copy the header now it has all the offsets in it */ - if (copy_to_user(buffer, data, struct_size)) - offset = -EFAULT; - - /* If we only returned a header and there's more to come then put it - back on the list */ - if (count < data_size) { - spin_lock(&fi->fi_ast_lock); - list_add(&ast->list, &fi->fi_ast_list); - spin_unlock(&fi->fi_ast_lock); - } else - kfree(ast); - return offset; -} - -static unsigned int dlm_poll(struct file *file, poll_table *wait) -{ - struct file_info *fi = file->private_data; - - poll_wait(file, &fi->fi_wait, wait); - - spin_lock(&fi->fi_ast_lock); - if (!list_empty(&fi->fi_ast_list)) { - spin_unlock(&fi->fi_ast_lock); - return POLLIN | POLLRDNORM; - } - - spin_unlock(&fi->fi_ast_lock); - return 0; -} - -static struct lock_info *allocate_lockinfo(struct file_info *fi, uint8_t cmd, - struct dlm_lock_params *kparams) -{ - struct lock_info *li; - - if (!try_module_get(THIS_MODULE)) - return NULL; - - li = kzalloc(sizeof(struct lock_info), GFP_KERNEL); - if (li) { - li->li_magic = LOCKINFO_MAGIC; - li->li_file = fi; - li->li_cmd = cmd; - li->li_flags = 0; - li->li_grmode = -1; - li->li_rqmode = -1; - li->li_pend_bastparam = NULL; - li->li_pend_bastaddr = NULL; - li->li_castaddr = NULL; - li->li_castparam = NULL; - li->li_lksb.sb_lvbptr = NULL; - li->li_bastaddr = kparams->bastaddr; - li->li_bastparam = kparams->bastparam; - - get_file_info(fi); - } - return li; -} - -static int do_user_lock(struct file_info *fi, uint8_t cmd, - struct dlm_lock_params *kparams) -{ - struct lock_info *li; - int status; - - /* - * Validate things that we need to have correct. - */ - if (!kparams->castaddr) - return -EINVAL; - - if (!kparams->lksb) - return -EINVAL; - - /* Persistent child locks are not available yet */ - if ((kparams->flags & DLM_LKF_PERSISTENT) && kparams->parent) - return -EINVAL; - - /* For conversions, there should already be a lockinfo struct, - unless we are adopting an orphaned persistent lock */ - if (kparams->flags & DLM_LKF_CONVERT) { - - li = get_lockinfo(fi->fi_ls, kparams->lkid); - - /* If this is a persistent lock we will have to create a - lockinfo again */ - if (!li && (kparams->flags & DLM_LKF_PERSISTENT)) { - li = allocate_lockinfo(fi, cmd, kparams); - if (!li) - return -ENOMEM; - - li->li_lksb.sb_lkid = kparams->lkid; - li->li_castaddr = kparams->castaddr; - li->li_castparam = kparams->castparam; - - /* OK, this isn't exactly a FIRSTLOCK but it is the - first time we've used this lockinfo, and if things - fail we want rid of it */ - init_completion(&li->li_firstcomp); - set_bit(LI_FLAG_FIRSTLOCK, &li->li_flags); - add_lockinfo(fi->fi_ls, li); - - /* TODO: do a query to get the current state ?? */ - } - if (!li) - return -EINVAL; - - if (li->li_magic != LOCKINFO_MAGIC) - return -EINVAL; - - /* For conversions don't overwrite the current blocking AST - info so that: - a) if a blocking AST fires before the conversion is queued - it runs the current handler - b) if the conversion is cancelled, the original blocking AST - declaration is active - The pend_ info is made active when the conversion - completes. - */ - li->li_pend_bastaddr = kparams->bastaddr; - li->li_pend_bastparam = kparams->bastparam; - } else { - li = allocate_lockinfo(fi, cmd, kparams); - if (!li) - return -ENOMEM; - - /* Allow us to complete our work before - the AST routine runs. In fact we only need (and use) this - when the initial lock fails */ - init_completion(&li->li_firstcomp); - set_bit(LI_FLAG_FIRSTLOCK, &li->li_flags); - } - - li->li_user_lksb = kparams->lksb; - li->li_castaddr = kparams->castaddr; - li->li_castparam = kparams->castparam; - li->li_lksb.sb_lkid = kparams->lkid; - li->li_rqmode = kparams->mode; - if (kparams->flags & DLM_LKF_PERSISTENT) - set_bit(LI_FLAG_PERSISTENT, &li->li_flags); - - /* Copy in the value block */ - if (kparams->flags & DLM_LKF_VALBLK) { - if (!li->li_lksb.sb_lvbptr) { - li->li_lksb.sb_lvbptr = kmalloc(DLM_USER_LVB_LEN, - GFP_KERNEL); - if (!li->li_lksb.sb_lvbptr) { - status = -ENOMEM; - goto out_err; - } - } - - memcpy(li->li_lksb.sb_lvbptr, kparams->lvb, DLM_USER_LVB_LEN); - } - - /* Lock it ... */ - status = dlm_lock(fi->fi_ls->ls_lockspace, - kparams->mode, &li->li_lksb, - kparams->flags, - kparams->name, kparams->namelen, - kparams->parent, - ast_routine, - li, - (li->li_pend_bastaddr || li->li_bastaddr) ? - bast_routine : NULL); - if (status) - goto out_err; - - /* If it succeeded (this far) with a new lock then keep track of - it on the file's lockinfo list */ - if (!status && test_bit(LI_FLAG_FIRSTLOCK, &li->li_flags)) { - - spin_lock(&fi->fi_li_lock); - list_add(&li->li_ownerqueue, &fi->fi_li_list); - set_bit(LI_FLAG_ONLIST, &li->li_flags); - spin_unlock(&fi->fi_li_lock); - if (add_lockinfo(fi->fi_ls, li)) - printk(KERN_WARNING "Add lockinfo failed\n"); - - complete(&li->li_firstcomp); - } - - /* Return the lockid as the user needs it /now/ */ - return li->li_lksb.sb_lkid; - - out_err: - if (test_bit(LI_FLAG_FIRSTLOCK, &li->li_flags)) - release_lockinfo(fi->fi_ls, li); - return status; - -} - -static int do_user_unlock(struct file_info *fi, uint8_t cmd, - struct dlm_lock_params *kparams) -{ - struct lock_info *li; - int status; - int convert_cancel = 0; - - li = get_lockinfo(fi->fi_ls, kparams->lkid); - if (!li) { - li = allocate_lockinfo(fi, cmd, kparams); - if (!li) - return -ENOMEM; - spin_lock(&fi->fi_li_lock); - list_add(&li->li_ownerqueue, &fi->fi_li_list); - set_bit(LI_FLAG_ONLIST, &li->li_flags); - spin_unlock(&fi->fi_li_lock); - } - - if (li->li_magic != LOCKINFO_MAGIC) - return -EINVAL; - - li->li_user_lksb = kparams->lksb; - li->li_castparam = kparams->castparam; - li->li_cmd = cmd; - - /* Cancelling a conversion doesn't remove the lock...*/ - if (kparams->flags & DLM_LKF_CANCEL && li->li_grmode != -1) - convert_cancel = 1; - - /* Wait until dlm_lock() has completed */ - if (!test_bit(LI_FLAG_ONLIST, &li->li_flags)) { - wait_for_completion(&li->li_firstcomp); - } - - /* dlm_unlock() passes a 0 for castaddr which means don't overwrite - the existing li_castaddr as that's the completion routine for - unlocks. dlm_unlock_wait() specifies a new AST routine to be - executed when the unlock completes. */ - if (kparams->castaddr) - li->li_castaddr = kparams->castaddr; - - /* Use existing lksb & astparams */ - status = dlm_unlock(fi->fi_ls->ls_lockspace, - kparams->lkid, - kparams->flags, &li->li_lksb, li); - - if (!status && !convert_cancel) { - spin_lock(&fi->fi_li_lock); - list_del(&li->li_ownerqueue); - clear_bit(LI_FLAG_ONLIST, &li->li_flags); - spin_unlock(&fi->fi_li_lock); - } - - return status; -} - -/* Write call, submit a locking request */ -static ssize_t dlm_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - struct file_info *fi = file->private_data; - struct dlm_write_request *kparams; - sigset_t tmpsig; - sigset_t allsigs; - int status; - -#ifdef CONFIG_COMPAT - if (count < sizeof(struct dlm_write_request32)) -#else - if (count < sizeof(struct dlm_write_request)) -#endif - return -EINVAL; - - if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN) - return -EINVAL; - - /* Has the lockspace been deleted */ - if (fi && test_bit(LS_FLAG_DELETED, &fi->fi_ls->ls_flags)) - return -ENOENT; - - kparams = kmalloc(count, GFP_KERNEL); - if (!kparams) - return -ENOMEM; - - status = -EFAULT; - /* Get the command info */ - if (copy_from_user(kparams, buffer, count)) - goto out_free; - - status = -EBADE; - if (check_version(kparams)) - goto out_free; - -#ifdef CONFIG_COMPAT - if (!kparams->is64bit) { - struct dlm_write_request32 *k32params = (struct dlm_write_request32 *)kparams; - kparams = kmalloc(count + (sizeof(struct dlm_write_request) - sizeof(struct dlm_write_request32)), GFP_KERNEL); - if (!kparams) - return -ENOMEM; - - if (fi) - set_bit(FI_FLAG_COMPAT, &fi->fi_flags); - compat_input(kparams, k32params); - kfree(k32params); - } -#endif - - /* Block signals while we are doing this */ - sigfillset(&allsigs); - sigprocmask(SIG_BLOCK, &allsigs, &tmpsig); - - status = -EINVAL; - switch (kparams->cmd) - { - case DLM_USER_LOCK: - if (!fi) goto out_sig; - status = do_user_lock(fi, kparams->cmd, &kparams->i.lock); - break; - - case DLM_USER_UNLOCK: - if (!fi) goto out_sig; - status = do_user_unlock(fi, kparams->cmd, &kparams->i.lock); - break; - - case DLM_USER_CREATE_LOCKSPACE: - if (fi) goto out_sig; - status = do_user_create_lockspace(fi, kparams->cmd, - &kparams->i.lspace); - break; - - case DLM_USER_REMOVE_LOCKSPACE: - if (fi) goto out_sig; - status = do_user_remove_lockspace(fi, kparams->cmd, - &kparams->i.lspace); - break; - default: - printk("Unknown command passed to DLM device : %d\n", - kparams->cmd); - break; - } - - out_sig: - /* Restore signals */ - sigprocmask(SIG_SETMASK, &tmpsig, NULL); - recalc_sigpending(); - - out_free: - kfree(kparams); - if (status == 0) - return count; - else - return status; -} - -static struct file_operations _dlm_fops = { - .open = dlm_open, - .release = dlm_close, - .read = dlm_read, - .write = dlm_write, - .poll = dlm_poll, - .owner = THIS_MODULE, -}; - -static struct file_operations _dlm_ctl_fops = { - .open = dlm_ctl_open, - .release = dlm_ctl_close, - .write = dlm_write, - .owner = THIS_MODULE, -}; - -/* - * Create control device - */ -static int __init dlm_device_init(void) -{ - int r; - - INIT_LIST_HEAD(&user_ls_list); - mutex_init(&user_ls_lock); - - ctl_device.name = "dlm-control"; - ctl_device.fops = &_dlm_ctl_fops; - ctl_device.minor = MISC_DYNAMIC_MINOR; - - r = misc_register(&ctl_device); - if (r) { - printk(KERN_ERR "dlm: misc_register failed for control dev\n"); - return r; - } - - return 0; -} - -static void __exit dlm_device_exit(void) -{ - misc_deregister(&ctl_device); -} - -MODULE_DESCRIPTION("Distributed Lock Manager device interface"); -MODULE_AUTHOR("Red Hat, Inc."); -MODULE_LICENSE("GPL"); - -module_init(dlm_device_init); -module_exit(dlm_device_exit); |