1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237 |
- /******************************************************************************
- *******************************************************************************
- **
- ** 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;
- /* Lock infos are stored in here indexed by lock ID */
- static DEFINE_IDR(lockinfo_idr);
- static rwlock_t lockinfo_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;
- /* 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 lock_info *li)
- {
- put_file_info(li->li_file);
- write_lock(&lockinfo_lock);
- idr_remove(&lockinfo_idr, li->li_lksb.sb_lkid);
- write_unlock(&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(uint32_t lockid)
- {
- struct lock_info *li;
- read_lock(&lockinfo_lock);
- li = idr_find(&lockinfo_idr, lockid);
- read_unlock(&lockinfo_lock);
- return li;
- }
- static int add_lockinfo(struct lock_info *li)
- {
- int n;
- int r;
- int ret = -EINVAL;
- write_lock(&lockinfo_lock);
- if (idr_find(&lockinfo_idr, li->li_lksb.sb_lkid))
- goto out_up;
- ret = -ENOMEM;
- r = idr_pre_get(&lockinfo_idr, GFP_KERNEL);
- if (!r)
- goto out_up;
- r = idr_get_new_above(&lockinfo_idr, li, li->li_lksb.sb_lkid, &n);
- if (r)
- goto out_up;
- if (n != li->li_lksb.sb_lkid) {
- idr_remove(&lockinfo_idr, n);
- goto out_up;
- }
- ret = 0;
- out_up:
- write_unlock(&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;
- }
- 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);
- return;
- }
- /* Free unlocks & queries */
- if (li->li_lksb.sb_status == -DLM_EUNLOCK ||
- li->li_cmd == DLM_USER_QUERY) {
- release_lockinfo(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);
- 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);
- }
- 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(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(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(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(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(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);
- rwlock_init(&lockinfo_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);
|