1597d0caeSDavid Teigland /* 2597d0caeSDavid Teigland * Copyright (C) 2006 Red Hat, Inc. All rights reserved. 3597d0caeSDavid Teigland * 4597d0caeSDavid Teigland * This copyrighted material is made available to anyone wishing to use, 5597d0caeSDavid Teigland * modify, copy, or redistribute it subject to the terms and conditions 6597d0caeSDavid Teigland * of the GNU General Public License v.2. 7597d0caeSDavid Teigland */ 8597d0caeSDavid Teigland 9597d0caeSDavid Teigland #include <linux/miscdevice.h> 10597d0caeSDavid Teigland #include <linux/init.h> 11597d0caeSDavid Teigland #include <linux/wait.h> 12597d0caeSDavid Teigland #include <linux/module.h> 13597d0caeSDavid Teigland #include <linux/file.h> 14597d0caeSDavid Teigland #include <linux/fs.h> 15597d0caeSDavid Teigland #include <linux/poll.h> 16597d0caeSDavid Teigland #include <linux/signal.h> 17597d0caeSDavid Teigland #include <linux/spinlock.h> 18597d0caeSDavid Teigland #include <linux/dlm.h> 19597d0caeSDavid Teigland #include <linux/dlm_device.h> 20597d0caeSDavid Teigland 21597d0caeSDavid Teigland #include "dlm_internal.h" 22597d0caeSDavid Teigland #include "lockspace.h" 23597d0caeSDavid Teigland #include "lock.h" 24597d0caeSDavid Teigland #include "lvb_table.h" 25597d0caeSDavid Teigland 26597d0caeSDavid Teigland static const char *name_prefix="dlm"; 27597d0caeSDavid Teigland static struct miscdevice ctl_device; 28597d0caeSDavid Teigland static struct file_operations device_fops; 29597d0caeSDavid Teigland 30597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 31597d0caeSDavid Teigland 32597d0caeSDavid Teigland struct dlm_lock_params32 { 33597d0caeSDavid Teigland __u8 mode; 34597d0caeSDavid Teigland __u8 namelen; 35597d0caeSDavid Teigland __u16 flags; 36597d0caeSDavid Teigland __u32 lkid; 37597d0caeSDavid Teigland __u32 parent; 38597d0caeSDavid Teigland 39597d0caeSDavid Teigland __u32 castparam; 40597d0caeSDavid Teigland __u32 castaddr; 41597d0caeSDavid Teigland __u32 bastparam; 42597d0caeSDavid Teigland __u32 bastaddr; 43597d0caeSDavid Teigland __u32 lksb; 44597d0caeSDavid Teigland 45597d0caeSDavid Teigland char lvb[DLM_USER_LVB_LEN]; 46597d0caeSDavid Teigland char name[0]; 47597d0caeSDavid Teigland }; 48597d0caeSDavid Teigland 49597d0caeSDavid Teigland struct dlm_write_request32 { 50597d0caeSDavid Teigland __u32 version[3]; 51597d0caeSDavid Teigland __u8 cmd; 52597d0caeSDavid Teigland __u8 is64bit; 53597d0caeSDavid Teigland __u8 unused[2]; 54597d0caeSDavid Teigland 55597d0caeSDavid Teigland union { 56597d0caeSDavid Teigland struct dlm_lock_params32 lock; 57597d0caeSDavid Teigland struct dlm_lspace_params lspace; 58597d0caeSDavid Teigland } i; 59597d0caeSDavid Teigland }; 60597d0caeSDavid Teigland 61597d0caeSDavid Teigland struct dlm_lksb32 { 62597d0caeSDavid Teigland __u32 sb_status; 63597d0caeSDavid Teigland __u32 sb_lkid; 64597d0caeSDavid Teigland __u8 sb_flags; 65597d0caeSDavid Teigland __u32 sb_lvbptr; 66597d0caeSDavid Teigland }; 67597d0caeSDavid Teigland 68597d0caeSDavid Teigland struct dlm_lock_result32 { 69597d0caeSDavid Teigland __u32 length; 70597d0caeSDavid Teigland __u32 user_astaddr; 71597d0caeSDavid Teigland __u32 user_astparam; 72597d0caeSDavid Teigland __u32 user_lksb; 73597d0caeSDavid Teigland struct dlm_lksb32 lksb; 74597d0caeSDavid Teigland __u8 bast_mode; 75597d0caeSDavid Teigland __u8 unused[3]; 76597d0caeSDavid Teigland /* Offsets may be zero if no data is present */ 77597d0caeSDavid Teigland __u32 lvb_offset; 78597d0caeSDavid Teigland }; 79597d0caeSDavid Teigland 80597d0caeSDavid Teigland static void compat_input(struct dlm_write_request *kb, 81597d0caeSDavid Teigland struct dlm_write_request32 *kb32) 82597d0caeSDavid Teigland { 83597d0caeSDavid Teigland kb->version[0] = kb32->version[0]; 84597d0caeSDavid Teigland kb->version[1] = kb32->version[1]; 85597d0caeSDavid Teigland kb->version[2] = kb32->version[2]; 86597d0caeSDavid Teigland 87597d0caeSDavid Teigland kb->cmd = kb32->cmd; 88597d0caeSDavid Teigland kb->is64bit = kb32->is64bit; 89597d0caeSDavid Teigland if (kb->cmd == DLM_USER_CREATE_LOCKSPACE || 90597d0caeSDavid Teigland kb->cmd == DLM_USER_REMOVE_LOCKSPACE) { 91597d0caeSDavid Teigland kb->i.lspace.flags = kb32->i.lspace.flags; 92597d0caeSDavid Teigland kb->i.lspace.minor = kb32->i.lspace.minor; 93597d0caeSDavid Teigland strcpy(kb->i.lspace.name, kb32->i.lspace.name); 94597d0caeSDavid Teigland } else { 95597d0caeSDavid Teigland kb->i.lock.mode = kb32->i.lock.mode; 96597d0caeSDavid Teigland kb->i.lock.namelen = kb32->i.lock.namelen; 97597d0caeSDavid Teigland kb->i.lock.flags = kb32->i.lock.flags; 98597d0caeSDavid Teigland kb->i.lock.lkid = kb32->i.lock.lkid; 99597d0caeSDavid Teigland kb->i.lock.parent = kb32->i.lock.parent; 100597d0caeSDavid Teigland kb->i.lock.castparam = (void *)(long)kb32->i.lock.castparam; 101597d0caeSDavid Teigland kb->i.lock.castaddr = (void *)(long)kb32->i.lock.castaddr; 102597d0caeSDavid Teigland kb->i.lock.bastparam = (void *)(long)kb32->i.lock.bastparam; 103597d0caeSDavid Teigland kb->i.lock.bastaddr = (void *)(long)kb32->i.lock.bastaddr; 104597d0caeSDavid Teigland kb->i.lock.lksb = (void *)(long)kb32->i.lock.lksb; 105597d0caeSDavid Teigland memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN); 106597d0caeSDavid Teigland memcpy(kb->i.lock.name, kb32->i.lock.name, kb->i.lock.namelen); 107597d0caeSDavid Teigland } 108597d0caeSDavid Teigland } 109597d0caeSDavid Teigland 110597d0caeSDavid Teigland static void compat_output(struct dlm_lock_result *res, 111597d0caeSDavid Teigland struct dlm_lock_result32 *res32) 112597d0caeSDavid Teigland { 113597d0caeSDavid Teigland res32->length = res->length - (sizeof(struct dlm_lock_result) - 114597d0caeSDavid Teigland sizeof(struct dlm_lock_result32)); 115597d0caeSDavid Teigland res32->user_astaddr = (__u32)(long)res->user_astaddr; 116597d0caeSDavid Teigland res32->user_astparam = (__u32)(long)res->user_astparam; 117597d0caeSDavid Teigland res32->user_lksb = (__u32)(long)res->user_lksb; 118597d0caeSDavid Teigland res32->bast_mode = res->bast_mode; 119597d0caeSDavid Teigland 120597d0caeSDavid Teigland res32->lvb_offset = res->lvb_offset; 121597d0caeSDavid Teigland res32->length = res->length; 122597d0caeSDavid Teigland 123597d0caeSDavid Teigland res32->lksb.sb_status = res->lksb.sb_status; 124597d0caeSDavid Teigland res32->lksb.sb_flags = res->lksb.sb_flags; 125597d0caeSDavid Teigland res32->lksb.sb_lkid = res->lksb.sb_lkid; 126597d0caeSDavid Teigland res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr; 127597d0caeSDavid Teigland } 128597d0caeSDavid Teigland #endif 129597d0caeSDavid Teigland 130597d0caeSDavid Teigland 131597d0caeSDavid Teigland void dlm_user_add_ast(struct dlm_lkb *lkb, int type) 132597d0caeSDavid Teigland { 133597d0caeSDavid Teigland struct dlm_ls *ls; 134597d0caeSDavid Teigland struct dlm_user_args *ua; 135597d0caeSDavid Teigland struct dlm_user_proc *proc; 13634e22bedSDavid Teigland int remove_ownqueue = 0; 137597d0caeSDavid Teigland 138597d0caeSDavid Teigland /* dlm_clear_proc_locks() sets ORPHAN/DEAD flag on each 139597d0caeSDavid Teigland lkb before dealing with it. We need to check this 140597d0caeSDavid Teigland flag before taking ls_clear_proc_locks mutex because if 141597d0caeSDavid Teigland it's set, dlm_clear_proc_locks() holds the mutex. */ 142597d0caeSDavid Teigland 143597d0caeSDavid Teigland if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) { 144597d0caeSDavid Teigland /* log_print("user_add_ast skip1 %x", lkb->lkb_flags); */ 145597d0caeSDavid Teigland return; 146597d0caeSDavid Teigland } 147597d0caeSDavid Teigland 148597d0caeSDavid Teigland ls = lkb->lkb_resource->res_ls; 149597d0caeSDavid Teigland mutex_lock(&ls->ls_clear_proc_locks); 150597d0caeSDavid Teigland 151597d0caeSDavid Teigland /* If ORPHAN/DEAD flag is set, it means the process is dead so an ast 152597d0caeSDavid Teigland can't be delivered. For ORPHAN's, dlm_clear_proc_locks() freed 153597d0caeSDavid Teigland lkb->ua so we can't try to use it. */ 154597d0caeSDavid Teigland 155597d0caeSDavid Teigland if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) { 156597d0caeSDavid Teigland /* log_print("user_add_ast skip2 %x", lkb->lkb_flags); */ 157597d0caeSDavid Teigland goto out; 158597d0caeSDavid Teigland } 159597d0caeSDavid Teigland 160597d0caeSDavid Teigland DLM_ASSERT(lkb->lkb_astparam, dlm_print_lkb(lkb);); 161597d0caeSDavid Teigland ua = (struct dlm_user_args *)lkb->lkb_astparam; 162597d0caeSDavid Teigland proc = ua->proc; 163597d0caeSDavid Teigland 164597d0caeSDavid Teigland if (type == AST_BAST && ua->bastaddr == NULL) 165597d0caeSDavid Teigland goto out; 166597d0caeSDavid Teigland 167597d0caeSDavid Teigland spin_lock(&proc->asts_spin); 168597d0caeSDavid Teigland if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) { 169597d0caeSDavid Teigland kref_get(&lkb->lkb_ref); 170597d0caeSDavid Teigland list_add_tail(&lkb->lkb_astqueue, &proc->asts); 171597d0caeSDavid Teigland lkb->lkb_ast_type |= type; 172597d0caeSDavid Teigland wake_up_interruptible(&proc->wait); 173597d0caeSDavid Teigland } 174597d0caeSDavid Teigland 17534e22bedSDavid Teigland /* noqueue requests that fail may need to be removed from the 17634e22bedSDavid Teigland proc's locks list, there should be a better way of detecting 17734e22bedSDavid Teigland this situation than checking all these things... */ 17834e22bedSDavid Teigland 17934e22bedSDavid Teigland if (type == AST_COMP && lkb->lkb_grmode == DLM_LOCK_IV && 18034e22bedSDavid Teigland ua->lksb.sb_status == -EAGAIN && !list_empty(&lkb->lkb_ownqueue)) 18134e22bedSDavid Teigland remove_ownqueue = 1; 18234e22bedSDavid Teigland 183597d0caeSDavid Teigland /* We want to copy the lvb to userspace when the completion 184597d0caeSDavid Teigland ast is read if the status is 0, the lock has an lvb and 185597d0caeSDavid Teigland lvb_ops says we should. We could probably have set_lvb_lock() 186597d0caeSDavid Teigland set update_user_lvb instead and not need old_mode */ 187597d0caeSDavid Teigland 188597d0caeSDavid Teigland if ((lkb->lkb_ast_type & AST_COMP) && 189597d0caeSDavid Teigland (lkb->lkb_lksb->sb_status == 0) && 190597d0caeSDavid Teigland lkb->lkb_lksb->sb_lvbptr && 191597d0caeSDavid Teigland dlm_lvb_operations[ua->old_mode + 1][lkb->lkb_grmode + 1]) 192597d0caeSDavid Teigland ua->update_user_lvb = 1; 193597d0caeSDavid Teigland else 194597d0caeSDavid Teigland ua->update_user_lvb = 0; 195597d0caeSDavid Teigland 196597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 19734e22bedSDavid Teigland 19834e22bedSDavid Teigland if (remove_ownqueue) { 19934e22bedSDavid Teigland spin_lock(&ua->proc->locks_spin); 20034e22bedSDavid Teigland list_del_init(&lkb->lkb_ownqueue); 20134e22bedSDavid Teigland spin_unlock(&ua->proc->locks_spin); 20234e22bedSDavid Teigland dlm_put_lkb(lkb); 20334e22bedSDavid Teigland } 204597d0caeSDavid Teigland out: 205597d0caeSDavid Teigland mutex_unlock(&ls->ls_clear_proc_locks); 206597d0caeSDavid Teigland } 207597d0caeSDavid Teigland 208597d0caeSDavid Teigland static int device_user_lock(struct dlm_user_proc *proc, 209597d0caeSDavid Teigland struct dlm_lock_params *params) 210597d0caeSDavid Teigland { 211597d0caeSDavid Teigland struct dlm_ls *ls; 212597d0caeSDavid Teigland struct dlm_user_args *ua; 213597d0caeSDavid Teigland int error = -ENOMEM; 214597d0caeSDavid Teigland 215597d0caeSDavid Teigland ls = dlm_find_lockspace_local(proc->lockspace); 216597d0caeSDavid Teigland if (!ls) 217597d0caeSDavid Teigland return -ENOENT; 218597d0caeSDavid Teigland 219597d0caeSDavid Teigland if (!params->castaddr || !params->lksb) { 220597d0caeSDavid Teigland error = -EINVAL; 221597d0caeSDavid Teigland goto out; 222597d0caeSDavid Teigland } 223597d0caeSDavid Teigland 224597d0caeSDavid Teigland ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL); 225597d0caeSDavid Teigland if (!ua) 226597d0caeSDavid Teigland goto out; 227597d0caeSDavid Teigland ua->proc = proc; 228597d0caeSDavid Teigland ua->user_lksb = params->lksb; 229597d0caeSDavid Teigland ua->castparam = params->castparam; 230597d0caeSDavid Teigland ua->castaddr = params->castaddr; 231597d0caeSDavid Teigland ua->bastparam = params->bastparam; 232597d0caeSDavid Teigland ua->bastaddr = params->bastaddr; 233597d0caeSDavid Teigland 234597d0caeSDavid Teigland if (params->flags & DLM_LKF_CONVERT) 235597d0caeSDavid Teigland error = dlm_user_convert(ls, ua, 236597d0caeSDavid Teigland params->mode, params->flags, 237597d0caeSDavid Teigland params->lkid, params->lvb); 238597d0caeSDavid Teigland else { 239597d0caeSDavid Teigland error = dlm_user_request(ls, ua, 240597d0caeSDavid Teigland params->mode, params->flags, 241597d0caeSDavid Teigland params->name, params->namelen, 242597d0caeSDavid Teigland params->parent); 243597d0caeSDavid Teigland if (!error) 244597d0caeSDavid Teigland error = ua->lksb.sb_lkid; 245597d0caeSDavid Teigland } 246597d0caeSDavid Teigland out: 247597d0caeSDavid Teigland dlm_put_lockspace(ls); 248597d0caeSDavid Teigland return error; 249597d0caeSDavid Teigland } 250597d0caeSDavid Teigland 251597d0caeSDavid Teigland static int device_user_unlock(struct dlm_user_proc *proc, 252597d0caeSDavid Teigland struct dlm_lock_params *params) 253597d0caeSDavid Teigland { 254597d0caeSDavid Teigland struct dlm_ls *ls; 255597d0caeSDavid Teigland struct dlm_user_args *ua; 256597d0caeSDavid Teigland int error = -ENOMEM; 257597d0caeSDavid Teigland 258597d0caeSDavid Teigland ls = dlm_find_lockspace_local(proc->lockspace); 259597d0caeSDavid Teigland if (!ls) 260597d0caeSDavid Teigland return -ENOENT; 261597d0caeSDavid Teigland 262597d0caeSDavid Teigland ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL); 263597d0caeSDavid Teigland if (!ua) 264597d0caeSDavid Teigland goto out; 265597d0caeSDavid Teigland ua->proc = proc; 266597d0caeSDavid Teigland ua->user_lksb = params->lksb; 267597d0caeSDavid Teigland ua->castparam = params->castparam; 268597d0caeSDavid Teigland ua->castaddr = params->castaddr; 269597d0caeSDavid Teigland 270597d0caeSDavid Teigland if (params->flags & DLM_LKF_CANCEL) 271597d0caeSDavid Teigland error = dlm_user_cancel(ls, ua, params->flags, params->lkid); 272597d0caeSDavid Teigland else 273597d0caeSDavid Teigland error = dlm_user_unlock(ls, ua, params->flags, params->lkid, 274597d0caeSDavid Teigland params->lvb); 275597d0caeSDavid Teigland out: 276597d0caeSDavid Teigland dlm_put_lockspace(ls); 277597d0caeSDavid Teigland return error; 278597d0caeSDavid Teigland } 279597d0caeSDavid Teigland 280597d0caeSDavid Teigland static int device_create_lockspace(struct dlm_lspace_params *params) 281597d0caeSDavid Teigland { 282597d0caeSDavid Teigland dlm_lockspace_t *lockspace; 283597d0caeSDavid Teigland struct dlm_ls *ls; 284597d0caeSDavid Teigland int error, len; 285597d0caeSDavid Teigland 286597d0caeSDavid Teigland if (!capable(CAP_SYS_ADMIN)) 287597d0caeSDavid Teigland return -EPERM; 288597d0caeSDavid Teigland 289597d0caeSDavid Teigland error = dlm_new_lockspace(params->name, strlen(params->name), 290597d0caeSDavid Teigland &lockspace, 0, DLM_USER_LVB_LEN); 291597d0caeSDavid Teigland if (error) 292597d0caeSDavid Teigland return error; 293597d0caeSDavid Teigland 294597d0caeSDavid Teigland ls = dlm_find_lockspace_local(lockspace); 295597d0caeSDavid Teigland if (!ls) 296597d0caeSDavid Teigland return -ENOENT; 297597d0caeSDavid Teigland 298597d0caeSDavid Teigland error = -ENOMEM; 299597d0caeSDavid Teigland len = strlen(params->name) + strlen(name_prefix) + 2; 300597d0caeSDavid Teigland ls->ls_device.name = kzalloc(len, GFP_KERNEL); 301597d0caeSDavid Teigland if (!ls->ls_device.name) 302597d0caeSDavid Teigland goto fail; 303597d0caeSDavid Teigland snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix, 304597d0caeSDavid Teigland params->name); 305597d0caeSDavid Teigland ls->ls_device.fops = &device_fops; 306597d0caeSDavid Teigland ls->ls_device.minor = MISC_DYNAMIC_MINOR; 307597d0caeSDavid Teigland 308597d0caeSDavid Teigland error = misc_register(&ls->ls_device); 309597d0caeSDavid Teigland if (error) { 310597d0caeSDavid Teigland kfree(ls->ls_device.name); 311597d0caeSDavid Teigland goto fail; 312597d0caeSDavid Teigland } 313597d0caeSDavid Teigland 314597d0caeSDavid Teigland error = ls->ls_device.minor; 315597d0caeSDavid Teigland dlm_put_lockspace(ls); 316597d0caeSDavid Teigland return error; 317597d0caeSDavid Teigland 318597d0caeSDavid Teigland fail: 319597d0caeSDavid Teigland dlm_put_lockspace(ls); 320597d0caeSDavid Teigland dlm_release_lockspace(lockspace, 0); 321597d0caeSDavid Teigland return error; 322597d0caeSDavid Teigland } 323597d0caeSDavid Teigland 324597d0caeSDavid Teigland static int device_remove_lockspace(struct dlm_lspace_params *params) 325597d0caeSDavid Teigland { 326597d0caeSDavid Teigland dlm_lockspace_t *lockspace; 327597d0caeSDavid Teigland struct dlm_ls *ls; 328*c6e6f0baSDavid Teigland int error, force = 0; 329597d0caeSDavid Teigland 330597d0caeSDavid Teigland if (!capable(CAP_SYS_ADMIN)) 331597d0caeSDavid Teigland return -EPERM; 332597d0caeSDavid Teigland 333597d0caeSDavid Teigland ls = dlm_find_lockspace_device(params->minor); 334597d0caeSDavid Teigland if (!ls) 335597d0caeSDavid Teigland return -ENOENT; 336597d0caeSDavid Teigland 337597d0caeSDavid Teigland error = misc_deregister(&ls->ls_device); 338597d0caeSDavid Teigland if (error) { 339597d0caeSDavid Teigland dlm_put_lockspace(ls); 340597d0caeSDavid Teigland goto out; 341597d0caeSDavid Teigland } 342597d0caeSDavid Teigland kfree(ls->ls_device.name); 343597d0caeSDavid Teigland 344*c6e6f0baSDavid Teigland if (params->flags & DLM_USER_LSFLG_FORCEFREE) 345*c6e6f0baSDavid Teigland force = 2; 346*c6e6f0baSDavid Teigland 347597d0caeSDavid Teigland lockspace = ls->ls_local_handle; 348597d0caeSDavid Teigland 349597d0caeSDavid Teigland /* dlm_release_lockspace waits for references to go to zero, 350597d0caeSDavid Teigland so all processes will need to close their device for the ls 351597d0caeSDavid Teigland before the release will procede */ 352597d0caeSDavid Teigland 353597d0caeSDavid Teigland dlm_put_lockspace(ls); 354*c6e6f0baSDavid Teigland error = dlm_release_lockspace(lockspace, force); 355597d0caeSDavid Teigland out: 356597d0caeSDavid Teigland return error; 357597d0caeSDavid Teigland } 358597d0caeSDavid Teigland 359597d0caeSDavid Teigland /* Check the user's version matches ours */ 360597d0caeSDavid Teigland static int check_version(struct dlm_write_request *req) 361597d0caeSDavid Teigland { 362597d0caeSDavid Teigland if (req->version[0] != DLM_DEVICE_VERSION_MAJOR || 363597d0caeSDavid Teigland (req->version[0] == DLM_DEVICE_VERSION_MAJOR && 364597d0caeSDavid Teigland req->version[1] > DLM_DEVICE_VERSION_MINOR)) { 365597d0caeSDavid Teigland 366597d0caeSDavid Teigland printk(KERN_DEBUG "dlm: process %s (%d) version mismatch " 367597d0caeSDavid Teigland "user (%d.%d.%d) kernel (%d.%d.%d)\n", 368597d0caeSDavid Teigland current->comm, 369597d0caeSDavid Teigland current->pid, 370597d0caeSDavid Teigland req->version[0], 371597d0caeSDavid Teigland req->version[1], 372597d0caeSDavid Teigland req->version[2], 373597d0caeSDavid Teigland DLM_DEVICE_VERSION_MAJOR, 374597d0caeSDavid Teigland DLM_DEVICE_VERSION_MINOR, 375597d0caeSDavid Teigland DLM_DEVICE_VERSION_PATCH); 376597d0caeSDavid Teigland return -EINVAL; 377597d0caeSDavid Teigland } 378597d0caeSDavid Teigland return 0; 379597d0caeSDavid Teigland } 380597d0caeSDavid Teigland 381597d0caeSDavid Teigland /* 382597d0caeSDavid Teigland * device_write 383597d0caeSDavid Teigland * 384597d0caeSDavid Teigland * device_user_lock 385597d0caeSDavid Teigland * dlm_user_request -> request_lock 386597d0caeSDavid Teigland * dlm_user_convert -> convert_lock 387597d0caeSDavid Teigland * 388597d0caeSDavid Teigland * device_user_unlock 389597d0caeSDavid Teigland * dlm_user_unlock -> unlock_lock 390597d0caeSDavid Teigland * dlm_user_cancel -> cancel_lock 391597d0caeSDavid Teigland * 392597d0caeSDavid Teigland * device_create_lockspace 393597d0caeSDavid Teigland * dlm_new_lockspace 394597d0caeSDavid Teigland * 395597d0caeSDavid Teigland * device_remove_lockspace 396597d0caeSDavid Teigland * dlm_release_lockspace 397597d0caeSDavid Teigland */ 398597d0caeSDavid Teigland 399597d0caeSDavid Teigland /* a write to a lockspace device is a lock or unlock request, a write 400597d0caeSDavid Teigland to the control device is to create/remove a lockspace */ 401597d0caeSDavid Teigland 402597d0caeSDavid Teigland static ssize_t device_write(struct file *file, const char __user *buf, 403597d0caeSDavid Teigland size_t count, loff_t *ppos) 404597d0caeSDavid Teigland { 405597d0caeSDavid Teigland struct dlm_user_proc *proc = file->private_data; 406597d0caeSDavid Teigland struct dlm_write_request *kbuf; 407597d0caeSDavid Teigland sigset_t tmpsig, allsigs; 408597d0caeSDavid Teigland int error; 409597d0caeSDavid Teigland 410597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 411597d0caeSDavid Teigland if (count < sizeof(struct dlm_write_request32)) 412597d0caeSDavid Teigland #else 413597d0caeSDavid Teigland if (count < sizeof(struct dlm_write_request)) 414597d0caeSDavid Teigland #endif 415597d0caeSDavid Teigland return -EINVAL; 416597d0caeSDavid Teigland 417597d0caeSDavid Teigland kbuf = kmalloc(count, GFP_KERNEL); 418597d0caeSDavid Teigland if (!kbuf) 419597d0caeSDavid Teigland return -ENOMEM; 420597d0caeSDavid Teigland 421597d0caeSDavid Teigland if (copy_from_user(kbuf, buf, count)) { 422597d0caeSDavid Teigland error = -EFAULT; 423597d0caeSDavid Teigland goto out_free; 424597d0caeSDavid Teigland } 425597d0caeSDavid Teigland 426597d0caeSDavid Teigland if (check_version(kbuf)) { 427597d0caeSDavid Teigland error = -EBADE; 428597d0caeSDavid Teigland goto out_free; 429597d0caeSDavid Teigland } 430597d0caeSDavid Teigland 431597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 432597d0caeSDavid Teigland if (!kbuf->is64bit) { 433597d0caeSDavid Teigland struct dlm_write_request32 *k32buf; 434597d0caeSDavid Teigland k32buf = (struct dlm_write_request32 *)kbuf; 435597d0caeSDavid Teigland kbuf = kmalloc(count + (sizeof(struct dlm_write_request) - 436597d0caeSDavid Teigland sizeof(struct dlm_write_request32)), GFP_KERNEL); 437597d0caeSDavid Teigland if (!kbuf) 438597d0caeSDavid Teigland return -ENOMEM; 439597d0caeSDavid Teigland 440597d0caeSDavid Teigland if (proc) 441597d0caeSDavid Teigland set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags); 442597d0caeSDavid Teigland compat_input(kbuf, k32buf); 443597d0caeSDavid Teigland kfree(k32buf); 444597d0caeSDavid Teigland } 445597d0caeSDavid Teigland #endif 446597d0caeSDavid Teigland 447597d0caeSDavid Teigland /* do we really need this? can a write happen after a close? */ 448597d0caeSDavid Teigland if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) && 449597d0caeSDavid Teigland test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)) 450597d0caeSDavid Teigland return -EINVAL; 451597d0caeSDavid Teigland 452597d0caeSDavid Teigland sigfillset(&allsigs); 453597d0caeSDavid Teigland sigprocmask(SIG_BLOCK, &allsigs, &tmpsig); 454597d0caeSDavid Teigland 455597d0caeSDavid Teigland error = -EINVAL; 456597d0caeSDavid Teigland 457597d0caeSDavid Teigland switch (kbuf->cmd) 458597d0caeSDavid Teigland { 459597d0caeSDavid Teigland case DLM_USER_LOCK: 460597d0caeSDavid Teigland if (!proc) { 461597d0caeSDavid Teigland log_print("no locking on control device"); 462597d0caeSDavid Teigland goto out_sig; 463597d0caeSDavid Teigland } 464597d0caeSDavid Teigland error = device_user_lock(proc, &kbuf->i.lock); 465597d0caeSDavid Teigland break; 466597d0caeSDavid Teigland 467597d0caeSDavid Teigland case DLM_USER_UNLOCK: 468597d0caeSDavid Teigland if (!proc) { 469597d0caeSDavid Teigland log_print("no locking on control device"); 470597d0caeSDavid Teigland goto out_sig; 471597d0caeSDavid Teigland } 472597d0caeSDavid Teigland error = device_user_unlock(proc, &kbuf->i.lock); 473597d0caeSDavid Teigland break; 474597d0caeSDavid Teigland 475597d0caeSDavid Teigland case DLM_USER_CREATE_LOCKSPACE: 476597d0caeSDavid Teigland if (proc) { 477597d0caeSDavid Teigland log_print("create/remove only on control device"); 478597d0caeSDavid Teigland goto out_sig; 479597d0caeSDavid Teigland } 480597d0caeSDavid Teigland error = device_create_lockspace(&kbuf->i.lspace); 481597d0caeSDavid Teigland break; 482597d0caeSDavid Teigland 483597d0caeSDavid Teigland case DLM_USER_REMOVE_LOCKSPACE: 484597d0caeSDavid Teigland if (proc) { 485597d0caeSDavid Teigland log_print("create/remove only on control device"); 486597d0caeSDavid Teigland goto out_sig; 487597d0caeSDavid Teigland } 488597d0caeSDavid Teigland error = device_remove_lockspace(&kbuf->i.lspace); 489597d0caeSDavid Teigland break; 490597d0caeSDavid Teigland 491597d0caeSDavid Teigland default: 492597d0caeSDavid Teigland log_print("Unknown command passed to DLM device : %d\n", 493597d0caeSDavid Teigland kbuf->cmd); 494597d0caeSDavid Teigland } 495597d0caeSDavid Teigland 496597d0caeSDavid Teigland out_sig: 497597d0caeSDavid Teigland sigprocmask(SIG_SETMASK, &tmpsig, NULL); 498597d0caeSDavid Teigland recalc_sigpending(); 499597d0caeSDavid Teigland out_free: 500597d0caeSDavid Teigland kfree(kbuf); 501597d0caeSDavid Teigland return error; 502597d0caeSDavid Teigland } 503597d0caeSDavid Teigland 504597d0caeSDavid Teigland /* Every process that opens the lockspace device has its own "proc" structure 505597d0caeSDavid Teigland hanging off the open file that's used to keep track of locks owned by the 506597d0caeSDavid Teigland process and asts that need to be delivered to the process. */ 507597d0caeSDavid Teigland 508597d0caeSDavid Teigland static int device_open(struct inode *inode, struct file *file) 509597d0caeSDavid Teigland { 510597d0caeSDavid Teigland struct dlm_user_proc *proc; 511597d0caeSDavid Teigland struct dlm_ls *ls; 512597d0caeSDavid Teigland 513597d0caeSDavid Teigland ls = dlm_find_lockspace_device(iminor(inode)); 514597d0caeSDavid Teigland if (!ls) 515597d0caeSDavid Teigland return -ENOENT; 516597d0caeSDavid Teigland 517597d0caeSDavid Teigland proc = kzalloc(sizeof(struct dlm_user_proc), GFP_KERNEL); 518597d0caeSDavid Teigland if (!proc) { 519597d0caeSDavid Teigland dlm_put_lockspace(ls); 520597d0caeSDavid Teigland return -ENOMEM; 521597d0caeSDavid Teigland } 522597d0caeSDavid Teigland 523597d0caeSDavid Teigland proc->lockspace = ls->ls_local_handle; 524597d0caeSDavid Teigland INIT_LIST_HEAD(&proc->asts); 525597d0caeSDavid Teigland INIT_LIST_HEAD(&proc->locks); 526597d0caeSDavid Teigland spin_lock_init(&proc->asts_spin); 527597d0caeSDavid Teigland spin_lock_init(&proc->locks_spin); 528597d0caeSDavid Teigland init_waitqueue_head(&proc->wait); 529597d0caeSDavid Teigland file->private_data = proc; 530597d0caeSDavid Teigland 531597d0caeSDavid Teigland return 0; 532597d0caeSDavid Teigland } 533597d0caeSDavid Teigland 534597d0caeSDavid Teigland static int device_close(struct inode *inode, struct file *file) 535597d0caeSDavid Teigland { 536597d0caeSDavid Teigland struct dlm_user_proc *proc = file->private_data; 537597d0caeSDavid Teigland struct dlm_ls *ls; 538597d0caeSDavid Teigland sigset_t tmpsig, allsigs; 539597d0caeSDavid Teigland 540597d0caeSDavid Teigland ls = dlm_find_lockspace_local(proc->lockspace); 541597d0caeSDavid Teigland if (!ls) 542597d0caeSDavid Teigland return -ENOENT; 543597d0caeSDavid Teigland 544597d0caeSDavid Teigland sigfillset(&allsigs); 545597d0caeSDavid Teigland sigprocmask(SIG_BLOCK, &allsigs, &tmpsig); 546597d0caeSDavid Teigland 547597d0caeSDavid Teigland set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags); 548597d0caeSDavid Teigland 549597d0caeSDavid Teigland dlm_clear_proc_locks(ls, proc); 550597d0caeSDavid Teigland 551597d0caeSDavid Teigland /* at this point no more lkb's should exist for this lockspace, 552597d0caeSDavid Teigland so there's no chance of dlm_user_add_ast() being called and 553597d0caeSDavid Teigland looking for lkb->ua->proc */ 554597d0caeSDavid Teigland 555597d0caeSDavid Teigland kfree(proc); 556597d0caeSDavid Teigland file->private_data = NULL; 557597d0caeSDavid Teigland 558597d0caeSDavid Teigland dlm_put_lockspace(ls); 559597d0caeSDavid Teigland dlm_put_lockspace(ls); /* for the find in device_open() */ 560597d0caeSDavid Teigland 561597d0caeSDavid Teigland /* FIXME: AUTOFREE: if this ls is no longer used do 562597d0caeSDavid Teigland device_remove_lockspace() */ 563597d0caeSDavid Teigland 564597d0caeSDavid Teigland sigprocmask(SIG_SETMASK, &tmpsig, NULL); 565597d0caeSDavid Teigland recalc_sigpending(); 566597d0caeSDavid Teigland 567597d0caeSDavid Teigland return 0; 568597d0caeSDavid Teigland } 569597d0caeSDavid Teigland 570597d0caeSDavid Teigland static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, 571597d0caeSDavid Teigland int bmode, char __user *buf, size_t count) 572597d0caeSDavid Teigland { 573597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 574597d0caeSDavid Teigland struct dlm_lock_result32 result32; 575597d0caeSDavid Teigland #endif 576597d0caeSDavid Teigland struct dlm_lock_result result; 577597d0caeSDavid Teigland void *resultptr; 578597d0caeSDavid Teigland int error=0; 579597d0caeSDavid Teigland int len; 580597d0caeSDavid Teigland int struct_len; 581597d0caeSDavid Teigland 582597d0caeSDavid Teigland memset(&result, 0, sizeof(struct dlm_lock_result)); 583597d0caeSDavid Teigland memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb)); 584597d0caeSDavid Teigland result.user_lksb = ua->user_lksb; 585597d0caeSDavid Teigland 586597d0caeSDavid Teigland /* FIXME: dlm1 provides for the user's bastparam/addr to not be updated 587597d0caeSDavid Teigland in a conversion unless the conversion is successful. See code 588597d0caeSDavid Teigland in dlm_user_convert() for updating ua from ua_tmp. OpenVMS, though, 589597d0caeSDavid Teigland notes that a new blocking AST address and parameter are set even if 590597d0caeSDavid Teigland the conversion fails, so maybe we should just do that. */ 591597d0caeSDavid Teigland 592597d0caeSDavid Teigland if (type == AST_BAST) { 593597d0caeSDavid Teigland result.user_astaddr = ua->bastaddr; 594597d0caeSDavid Teigland result.user_astparam = ua->bastparam; 595597d0caeSDavid Teigland result.bast_mode = bmode; 596597d0caeSDavid Teigland } else { 597597d0caeSDavid Teigland result.user_astaddr = ua->castaddr; 598597d0caeSDavid Teigland result.user_astparam = ua->castparam; 599597d0caeSDavid Teigland } 600597d0caeSDavid Teigland 601597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 602597d0caeSDavid Teigland if (compat) 603597d0caeSDavid Teigland len = sizeof(struct dlm_lock_result32); 604597d0caeSDavid Teigland else 605597d0caeSDavid Teigland #endif 606597d0caeSDavid Teigland len = sizeof(struct dlm_lock_result); 607597d0caeSDavid Teigland struct_len = len; 608597d0caeSDavid Teigland 609597d0caeSDavid Teigland /* copy lvb to userspace if there is one, it's been updated, and 610597d0caeSDavid Teigland the user buffer has space for it */ 611597d0caeSDavid Teigland 612597d0caeSDavid Teigland if (ua->update_user_lvb && ua->lksb.sb_lvbptr && 613597d0caeSDavid Teigland count >= len + DLM_USER_LVB_LEN) { 614597d0caeSDavid Teigland if (copy_to_user(buf+len, ua->lksb.sb_lvbptr, 615597d0caeSDavid Teigland DLM_USER_LVB_LEN)) { 616597d0caeSDavid Teigland error = -EFAULT; 617597d0caeSDavid Teigland goto out; 618597d0caeSDavid Teigland } 619597d0caeSDavid Teigland 620597d0caeSDavid Teigland result.lvb_offset = len; 621597d0caeSDavid Teigland len += DLM_USER_LVB_LEN; 622597d0caeSDavid Teigland } 623597d0caeSDavid Teigland 624597d0caeSDavid Teigland result.length = len; 625597d0caeSDavid Teigland resultptr = &result; 626597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 627597d0caeSDavid Teigland if (compat) { 628597d0caeSDavid Teigland compat_output(&result, &result32); 629597d0caeSDavid Teigland resultptr = &result32; 630597d0caeSDavid Teigland } 631597d0caeSDavid Teigland #endif 632597d0caeSDavid Teigland 633597d0caeSDavid Teigland if (copy_to_user(buf, resultptr, struct_len)) 634597d0caeSDavid Teigland error = -EFAULT; 635597d0caeSDavid Teigland else 636597d0caeSDavid Teigland error = len; 637597d0caeSDavid Teigland out: 638597d0caeSDavid Teigland return error; 639597d0caeSDavid Teigland } 640597d0caeSDavid Teigland 641597d0caeSDavid Teigland /* a read returns a single ast described in a struct dlm_lock_result */ 642597d0caeSDavid Teigland 643597d0caeSDavid Teigland static ssize_t device_read(struct file *file, char __user *buf, size_t count, 644597d0caeSDavid Teigland loff_t *ppos) 645597d0caeSDavid Teigland { 646597d0caeSDavid Teigland struct dlm_user_proc *proc = file->private_data; 647597d0caeSDavid Teigland struct dlm_lkb *lkb; 648597d0caeSDavid Teigland struct dlm_user_args *ua; 649597d0caeSDavid Teigland DECLARE_WAITQUEUE(wait, current); 650597d0caeSDavid Teigland int error, type=0, bmode=0, removed = 0; 651597d0caeSDavid Teigland 652597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 653597d0caeSDavid Teigland if (count < sizeof(struct dlm_lock_result32)) 654597d0caeSDavid Teigland #else 655597d0caeSDavid Teigland if (count < sizeof(struct dlm_lock_result)) 656597d0caeSDavid Teigland #endif 657597d0caeSDavid Teigland return -EINVAL; 658597d0caeSDavid Teigland 659597d0caeSDavid Teigland /* do we really need this? can a read happen after a close? */ 660597d0caeSDavid Teigland if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)) 661597d0caeSDavid Teigland return -EINVAL; 662597d0caeSDavid Teigland 663597d0caeSDavid Teigland spin_lock(&proc->asts_spin); 664597d0caeSDavid Teigland if (list_empty(&proc->asts)) { 665597d0caeSDavid Teigland if (file->f_flags & O_NONBLOCK) { 666597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 667597d0caeSDavid Teigland return -EAGAIN; 668597d0caeSDavid Teigland } 669597d0caeSDavid Teigland 670597d0caeSDavid Teigland add_wait_queue(&proc->wait, &wait); 671597d0caeSDavid Teigland 672597d0caeSDavid Teigland repeat: 673597d0caeSDavid Teigland set_current_state(TASK_INTERRUPTIBLE); 674597d0caeSDavid Teigland if (list_empty(&proc->asts) && !signal_pending(current)) { 675597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 676597d0caeSDavid Teigland schedule(); 677597d0caeSDavid Teigland spin_lock(&proc->asts_spin); 678597d0caeSDavid Teigland goto repeat; 679597d0caeSDavid Teigland } 680597d0caeSDavid Teigland set_current_state(TASK_RUNNING); 681597d0caeSDavid Teigland remove_wait_queue(&proc->wait, &wait); 682597d0caeSDavid Teigland 683597d0caeSDavid Teigland if (signal_pending(current)) { 684597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 685597d0caeSDavid Teigland return -ERESTARTSYS; 686597d0caeSDavid Teigland } 687597d0caeSDavid Teigland } 688597d0caeSDavid Teigland 689597d0caeSDavid Teigland if (list_empty(&proc->asts)) { 690597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 691597d0caeSDavid Teigland return -EAGAIN; 692597d0caeSDavid Teigland } 693597d0caeSDavid Teigland 694597d0caeSDavid Teigland /* there may be both completion and blocking asts to return for 695597d0caeSDavid Teigland the lkb, don't remove lkb from asts list unless no asts remain */ 696597d0caeSDavid Teigland 697597d0caeSDavid Teigland lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue); 698597d0caeSDavid Teigland 699597d0caeSDavid Teigland if (lkb->lkb_ast_type & AST_COMP) { 700597d0caeSDavid Teigland lkb->lkb_ast_type &= ~AST_COMP; 701597d0caeSDavid Teigland type = AST_COMP; 702597d0caeSDavid Teigland } else if (lkb->lkb_ast_type & AST_BAST) { 703597d0caeSDavid Teigland lkb->lkb_ast_type &= ~AST_BAST; 704597d0caeSDavid Teigland type = AST_BAST; 705597d0caeSDavid Teigland bmode = lkb->lkb_bastmode; 706597d0caeSDavid Teigland } 707597d0caeSDavid Teigland 708597d0caeSDavid Teigland if (!lkb->lkb_ast_type) { 709597d0caeSDavid Teigland list_del(&lkb->lkb_astqueue); 710597d0caeSDavid Teigland removed = 1; 711597d0caeSDavid Teigland } 712597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 713597d0caeSDavid Teigland 714597d0caeSDavid Teigland ua = (struct dlm_user_args *)lkb->lkb_astparam; 715597d0caeSDavid Teigland error = copy_result_to_user(ua, 716597d0caeSDavid Teigland test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), 717597d0caeSDavid Teigland type, bmode, buf, count); 718597d0caeSDavid Teigland 719597d0caeSDavid Teigland /* removes reference for the proc->asts lists added by 720597d0caeSDavid Teigland dlm_user_add_ast() and may result in the lkb being freed */ 721597d0caeSDavid Teigland if (removed) 722597d0caeSDavid Teigland dlm_put_lkb(lkb); 723597d0caeSDavid Teigland 724597d0caeSDavid Teigland return error; 725597d0caeSDavid Teigland } 726597d0caeSDavid Teigland 727597d0caeSDavid Teigland static unsigned int device_poll(struct file *file, poll_table *wait) 728597d0caeSDavid Teigland { 729597d0caeSDavid Teigland struct dlm_user_proc *proc = file->private_data; 730597d0caeSDavid Teigland 731597d0caeSDavid Teigland poll_wait(file, &proc->wait, wait); 732597d0caeSDavid Teigland 733597d0caeSDavid Teigland spin_lock(&proc->asts_spin); 734597d0caeSDavid Teigland if (!list_empty(&proc->asts)) { 735597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 736597d0caeSDavid Teigland return POLLIN | POLLRDNORM; 737597d0caeSDavid Teigland } 738597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 739597d0caeSDavid Teigland return 0; 740597d0caeSDavid Teigland } 741597d0caeSDavid Teigland 742597d0caeSDavid Teigland static int ctl_device_open(struct inode *inode, struct file *file) 743597d0caeSDavid Teigland { 744597d0caeSDavid Teigland file->private_data = NULL; 745597d0caeSDavid Teigland return 0; 746597d0caeSDavid Teigland } 747597d0caeSDavid Teigland 748597d0caeSDavid Teigland static int ctl_device_close(struct inode *inode, struct file *file) 749597d0caeSDavid Teigland { 750597d0caeSDavid Teigland return 0; 751597d0caeSDavid Teigland } 752597d0caeSDavid Teigland 753597d0caeSDavid Teigland static struct file_operations device_fops = { 754597d0caeSDavid Teigland .open = device_open, 755597d0caeSDavid Teigland .release = device_close, 756597d0caeSDavid Teigland .read = device_read, 757597d0caeSDavid Teigland .write = device_write, 758597d0caeSDavid Teigland .poll = device_poll, 759597d0caeSDavid Teigland .owner = THIS_MODULE, 760597d0caeSDavid Teigland }; 761597d0caeSDavid Teigland 762597d0caeSDavid Teigland static struct file_operations ctl_device_fops = { 763597d0caeSDavid Teigland .open = ctl_device_open, 764597d0caeSDavid Teigland .release = ctl_device_close, 765597d0caeSDavid Teigland .write = device_write, 766597d0caeSDavid Teigland .owner = THIS_MODULE, 767597d0caeSDavid Teigland }; 768597d0caeSDavid Teigland 769597d0caeSDavid Teigland int dlm_user_init(void) 770597d0caeSDavid Teigland { 771597d0caeSDavid Teigland int error; 772597d0caeSDavid Teigland 773597d0caeSDavid Teigland ctl_device.name = "dlm-control"; 774597d0caeSDavid Teigland ctl_device.fops = &ctl_device_fops; 775597d0caeSDavid Teigland ctl_device.minor = MISC_DYNAMIC_MINOR; 776597d0caeSDavid Teigland 777597d0caeSDavid Teigland error = misc_register(&ctl_device); 778597d0caeSDavid Teigland if (error) 779597d0caeSDavid Teigland log_print("misc_register failed for control device"); 780597d0caeSDavid Teigland 781597d0caeSDavid Teigland return error; 782597d0caeSDavid Teigland } 783597d0caeSDavid Teigland 784597d0caeSDavid Teigland void dlm_user_exit(void) 785597d0caeSDavid Teigland { 786597d0caeSDavid Teigland misc_deregister(&ctl_device); 787597d0caeSDavid Teigland } 788597d0caeSDavid Teigland 789