1597d0caeSDavid Teigland /* 27fe2b319SDavid Teigland * Copyright (C) 2006-2010 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> 205a0e3ad6STejun Heo #include <linux/slab.h> 21597d0caeSDavid Teigland 22597d0caeSDavid Teigland #include "dlm_internal.h" 23597d0caeSDavid Teigland #include "lockspace.h" 24597d0caeSDavid Teigland #include "lock.h" 25597d0caeSDavid Teigland #include "lvb_table.h" 2684c6e8cdSAdrian Bunk #include "user.h" 278304d6f2SDavid Teigland #include "ast.h" 28597d0caeSDavid Teigland 290fe410d3SDenis Cheng static const char name_prefix[] = "dlm"; 3000977a59SArjan van de Ven static const struct file_operations device_fops; 31dc68c7edSDavid Teigland static atomic_t dlm_monitor_opened; 32dc68c7edSDavid Teigland static int dlm_monitor_unused = 1; 33597d0caeSDavid Teigland 34597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 35597d0caeSDavid Teigland 36597d0caeSDavid Teigland struct dlm_lock_params32 { 37597d0caeSDavid Teigland __u8 mode; 38597d0caeSDavid Teigland __u8 namelen; 39d7db923eSDavid Teigland __u16 unused; 40d7db923eSDavid Teigland __u32 flags; 41597d0caeSDavid Teigland __u32 lkid; 42597d0caeSDavid Teigland __u32 parent; 43d7db923eSDavid Teigland __u64 xid; 44d7db923eSDavid Teigland __u64 timeout; 45597d0caeSDavid Teigland __u32 castparam; 46597d0caeSDavid Teigland __u32 castaddr; 47597d0caeSDavid Teigland __u32 bastparam; 48597d0caeSDavid Teigland __u32 bastaddr; 49597d0caeSDavid Teigland __u32 lksb; 50597d0caeSDavid Teigland char lvb[DLM_USER_LVB_LEN]; 51597d0caeSDavid Teigland char name[0]; 52597d0caeSDavid Teigland }; 53597d0caeSDavid Teigland 54597d0caeSDavid Teigland struct dlm_write_request32 { 55597d0caeSDavid Teigland __u32 version[3]; 56597d0caeSDavid Teigland __u8 cmd; 57597d0caeSDavid Teigland __u8 is64bit; 58597d0caeSDavid Teigland __u8 unused[2]; 59597d0caeSDavid Teigland 60597d0caeSDavid Teigland union { 61597d0caeSDavid Teigland struct dlm_lock_params32 lock; 62597d0caeSDavid Teigland struct dlm_lspace_params lspace; 6372c2be77SDavid Teigland struct dlm_purge_params purge; 64597d0caeSDavid Teigland } i; 65597d0caeSDavid Teigland }; 66597d0caeSDavid Teigland 67597d0caeSDavid Teigland struct dlm_lksb32 { 68597d0caeSDavid Teigland __u32 sb_status; 69597d0caeSDavid Teigland __u32 sb_lkid; 70597d0caeSDavid Teigland __u8 sb_flags; 71597d0caeSDavid Teigland __u32 sb_lvbptr; 72597d0caeSDavid Teigland }; 73597d0caeSDavid Teigland 74597d0caeSDavid Teigland struct dlm_lock_result32 { 75d7db923eSDavid Teigland __u32 version[3]; 76597d0caeSDavid Teigland __u32 length; 77597d0caeSDavid Teigland __u32 user_astaddr; 78597d0caeSDavid Teigland __u32 user_astparam; 79597d0caeSDavid Teigland __u32 user_lksb; 80597d0caeSDavid Teigland struct dlm_lksb32 lksb; 81597d0caeSDavid Teigland __u8 bast_mode; 82597d0caeSDavid Teigland __u8 unused[3]; 83597d0caeSDavid Teigland /* Offsets may be zero if no data is present */ 84597d0caeSDavid Teigland __u32 lvb_offset; 85597d0caeSDavid Teigland }; 86597d0caeSDavid Teigland 87597d0caeSDavid Teigland static void compat_input(struct dlm_write_request *kb, 882a79289eSPatrick Caulfeld struct dlm_write_request32 *kb32, 891fecb1c4SDavid Teigland int namelen) 90597d0caeSDavid Teigland { 91597d0caeSDavid Teigland kb->version[0] = kb32->version[0]; 92597d0caeSDavid Teigland kb->version[1] = kb32->version[1]; 93597d0caeSDavid Teigland kb->version[2] = kb32->version[2]; 94597d0caeSDavid Teigland 95597d0caeSDavid Teigland kb->cmd = kb32->cmd; 96597d0caeSDavid Teigland kb->is64bit = kb32->is64bit; 97597d0caeSDavid Teigland if (kb->cmd == DLM_USER_CREATE_LOCKSPACE || 98597d0caeSDavid Teigland kb->cmd == DLM_USER_REMOVE_LOCKSPACE) { 99597d0caeSDavid Teigland kb->i.lspace.flags = kb32->i.lspace.flags; 100597d0caeSDavid Teigland kb->i.lspace.minor = kb32->i.lspace.minor; 1011fecb1c4SDavid Teigland memcpy(kb->i.lspace.name, kb32->i.lspace.name, namelen); 10272c2be77SDavid Teigland } else if (kb->cmd == DLM_USER_PURGE) { 10372c2be77SDavid Teigland kb->i.purge.nodeid = kb32->i.purge.nodeid; 10472c2be77SDavid Teigland kb->i.purge.pid = kb32->i.purge.pid; 105597d0caeSDavid Teigland } else { 106597d0caeSDavid Teigland kb->i.lock.mode = kb32->i.lock.mode; 107597d0caeSDavid Teigland kb->i.lock.namelen = kb32->i.lock.namelen; 108597d0caeSDavid Teigland kb->i.lock.flags = kb32->i.lock.flags; 109597d0caeSDavid Teigland kb->i.lock.lkid = kb32->i.lock.lkid; 110597d0caeSDavid Teigland kb->i.lock.parent = kb32->i.lock.parent; 111d7db923eSDavid Teigland kb->i.lock.xid = kb32->i.lock.xid; 112d7db923eSDavid Teigland kb->i.lock.timeout = kb32->i.lock.timeout; 113597d0caeSDavid Teigland kb->i.lock.castparam = (void *)(long)kb32->i.lock.castparam; 114597d0caeSDavid Teigland kb->i.lock.castaddr = (void *)(long)kb32->i.lock.castaddr; 115597d0caeSDavid Teigland kb->i.lock.bastparam = (void *)(long)kb32->i.lock.bastparam; 116597d0caeSDavid Teigland kb->i.lock.bastaddr = (void *)(long)kb32->i.lock.bastaddr; 117597d0caeSDavid Teigland kb->i.lock.lksb = (void *)(long)kb32->i.lock.lksb; 118597d0caeSDavid Teigland memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN); 1191fecb1c4SDavid Teigland memcpy(kb->i.lock.name, kb32->i.lock.name, namelen); 120597d0caeSDavid Teigland } 121597d0caeSDavid Teigland } 122597d0caeSDavid Teigland 123597d0caeSDavid Teigland static void compat_output(struct dlm_lock_result *res, 124597d0caeSDavid Teigland struct dlm_lock_result32 *res32) 125597d0caeSDavid Teigland { 126d7db923eSDavid Teigland res32->version[0] = res->version[0]; 127d7db923eSDavid Teigland res32->version[1] = res->version[1]; 128d7db923eSDavid Teigland res32->version[2] = res->version[2]; 129d7db923eSDavid Teigland 130597d0caeSDavid Teigland res32->user_astaddr = (__u32)(long)res->user_astaddr; 131597d0caeSDavid Teigland res32->user_astparam = (__u32)(long)res->user_astparam; 132597d0caeSDavid Teigland res32->user_lksb = (__u32)(long)res->user_lksb; 133597d0caeSDavid Teigland res32->bast_mode = res->bast_mode; 134597d0caeSDavid Teigland 135597d0caeSDavid Teigland res32->lvb_offset = res->lvb_offset; 136597d0caeSDavid Teigland res32->length = res->length; 137597d0caeSDavid Teigland 138597d0caeSDavid Teigland res32->lksb.sb_status = res->lksb.sb_status; 139597d0caeSDavid Teigland res32->lksb.sb_flags = res->lksb.sb_flags; 140597d0caeSDavid Teigland res32->lksb.sb_lkid = res->lksb.sb_lkid; 141597d0caeSDavid Teigland res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr; 142597d0caeSDavid Teigland } 143597d0caeSDavid Teigland #endif 144597d0caeSDavid Teigland 14584d8cd69SDavid Teigland /* Figure out if this lock is at the end of its life and no longer 14684d8cd69SDavid Teigland available for the application to use. The lkb still exists until 14784d8cd69SDavid Teigland the final ast is read. A lock becomes EOL in three situations: 14884d8cd69SDavid Teigland 1. a noqueue request fails with EAGAIN 14984d8cd69SDavid Teigland 2. an unlock completes with EUNLOCK 15084d8cd69SDavid Teigland 3. a cancel of a waiting request completes with ECANCEL/EDEADLK 15184d8cd69SDavid Teigland An EOL lock needs to be removed from the process's list of locks. 15284d8cd69SDavid Teigland And we can't allow any new operation on an EOL lock. This is 15384d8cd69SDavid Teigland not related to the lifetime of the lkb struct which is managed 15484d8cd69SDavid Teigland entirely by refcount. */ 15584d8cd69SDavid Teigland 1568304d6f2SDavid Teigland static int lkb_is_endoflife(int mode, int status) 15784d8cd69SDavid Teigland { 1588304d6f2SDavid Teigland switch (status) { 15984d8cd69SDavid Teigland case -DLM_EUNLOCK: 16084d8cd69SDavid Teigland return 1; 16184d8cd69SDavid Teigland case -DLM_ECANCEL: 16284d8cd69SDavid Teigland case -ETIMEDOUT: 1638b4021faSDavid Teigland case -EDEADLK: 16484d8cd69SDavid Teigland case -EAGAIN: 1658304d6f2SDavid Teigland if (mode == DLM_LOCK_IV) 16684d8cd69SDavid Teigland return 1; 16784d8cd69SDavid Teigland break; 16884d8cd69SDavid Teigland } 16984d8cd69SDavid Teigland return 0; 17084d8cd69SDavid Teigland } 17184d8cd69SDavid Teigland 172ef0c2bb0SDavid Teigland /* we could possibly check if the cancel of an orphan has resulted in the lkb 173ef0c2bb0SDavid Teigland being removed and then remove that lkb from the orphans list and free it */ 174597d0caeSDavid Teigland 1758304d6f2SDavid Teigland void dlm_user_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode, 1768304d6f2SDavid Teigland int status, uint32_t sbflags, uint64_t seq) 177597d0caeSDavid Teigland { 178597d0caeSDavid Teigland struct dlm_ls *ls; 179597d0caeSDavid Teigland struct dlm_user_args *ua; 180597d0caeSDavid Teigland struct dlm_user_proc *proc; 1818304d6f2SDavid Teigland int rv; 182597d0caeSDavid Teigland 183ef0c2bb0SDavid Teigland if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) 184597d0caeSDavid Teigland return; 185597d0caeSDavid Teigland 186597d0caeSDavid Teigland ls = lkb->lkb_resource->res_ls; 187597d0caeSDavid Teigland mutex_lock(&ls->ls_clear_proc_locks); 188597d0caeSDavid Teigland 189597d0caeSDavid Teigland /* If ORPHAN/DEAD flag is set, it means the process is dead so an ast 190597d0caeSDavid Teigland can't be delivered. For ORPHAN's, dlm_clear_proc_locks() freed 191ef0c2bb0SDavid Teigland lkb->ua so we can't try to use it. This second check is necessary 192ef0c2bb0SDavid Teigland for cases where a completion ast is received for an operation that 193ef0c2bb0SDavid Teigland began before clear_proc_locks did its cancel/unlock. */ 194597d0caeSDavid Teigland 195ef0c2bb0SDavid Teigland if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) 196597d0caeSDavid Teigland goto out; 197597d0caeSDavid Teigland 198d292c0ccSDavid Teigland DLM_ASSERT(lkb->lkb_ua, dlm_print_lkb(lkb);); 199d292c0ccSDavid Teigland ua = lkb->lkb_ua; 200597d0caeSDavid Teigland proc = ua->proc; 201597d0caeSDavid Teigland 2028304d6f2SDavid Teigland if ((flags & DLM_CB_BAST) && ua->bastaddr == NULL) 203597d0caeSDavid Teigland goto out; 204597d0caeSDavid Teigland 2058304d6f2SDavid Teigland if ((flags & DLM_CB_CAST) && lkb_is_endoflife(mode, status)) 2068304d6f2SDavid Teigland lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; 2078304d6f2SDavid Teigland 208597d0caeSDavid Teigland spin_lock(&proc->asts_spin); 209ef0c2bb0SDavid Teigland 2108304d6f2SDavid Teigland rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, seq); 2118304d6f2SDavid Teigland if (rv < 0) { 2128304d6f2SDavid Teigland spin_unlock(&proc->asts_spin); 2138304d6f2SDavid Teigland goto out; 2148304d6f2SDavid Teigland } 215ef0c2bb0SDavid Teigland 21623e8e1aaSDavid Teigland if (list_empty(&lkb->lkb_cb_list)) { 217597d0caeSDavid Teigland kref_get(&lkb->lkb_ref); 21823e8e1aaSDavid Teigland list_add_tail(&lkb->lkb_cb_list, &proc->asts); 219597d0caeSDavid Teigland wake_up_interruptible(&proc->wait); 220597d0caeSDavid Teigland } 221597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 22234e22bedSDavid Teigland 2238304d6f2SDavid Teigland if (lkb->lkb_flags & DLM_IFL_ENDOFLIFE) { 2248304d6f2SDavid Teigland /* N.B. spin_lock locks_spin, not asts_spin */ 225ce5246b9SDavid Teigland spin_lock(&proc->locks_spin); 226ef0c2bb0SDavid Teigland if (!list_empty(&lkb->lkb_ownqueue)) { 22734e22bedSDavid Teigland list_del_init(&lkb->lkb_ownqueue); 22834e22bedSDavid Teigland dlm_put_lkb(lkb); 22934e22bedSDavid Teigland } 230ce5246b9SDavid Teigland spin_unlock(&proc->locks_spin); 231ef0c2bb0SDavid Teigland } 232597d0caeSDavid Teigland out: 233597d0caeSDavid Teigland mutex_unlock(&ls->ls_clear_proc_locks); 234597d0caeSDavid Teigland } 235597d0caeSDavid Teigland 236597d0caeSDavid Teigland static int device_user_lock(struct dlm_user_proc *proc, 237597d0caeSDavid Teigland struct dlm_lock_params *params) 238597d0caeSDavid Teigland { 239597d0caeSDavid Teigland struct dlm_ls *ls; 240597d0caeSDavid Teigland struct dlm_user_args *ua; 2412ab4bd8eSDavid Teigland uint32_t lkid; 242597d0caeSDavid Teigland int error = -ENOMEM; 243597d0caeSDavid Teigland 244597d0caeSDavid Teigland ls = dlm_find_lockspace_local(proc->lockspace); 245597d0caeSDavid Teigland if (!ls) 246597d0caeSDavid Teigland return -ENOENT; 247597d0caeSDavid Teigland 248597d0caeSDavid Teigland if (!params->castaddr || !params->lksb) { 249597d0caeSDavid Teigland error = -EINVAL; 250597d0caeSDavid Teigland goto out; 251597d0caeSDavid Teigland } 252597d0caeSDavid Teigland 253573c24c4SDavid Teigland ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS); 254597d0caeSDavid Teigland if (!ua) 255597d0caeSDavid Teigland goto out; 256597d0caeSDavid Teigland ua->proc = proc; 257597d0caeSDavid Teigland ua->user_lksb = params->lksb; 258597d0caeSDavid Teigland ua->castparam = params->castparam; 259597d0caeSDavid Teigland ua->castaddr = params->castaddr; 260597d0caeSDavid Teigland ua->bastparam = params->bastparam; 261597d0caeSDavid Teigland ua->bastaddr = params->bastaddr; 262d7db923eSDavid Teigland ua->xid = params->xid; 263597d0caeSDavid Teigland 2642ab4bd8eSDavid Teigland if (params->flags & DLM_LKF_CONVERT) { 265597d0caeSDavid Teigland error = dlm_user_convert(ls, ua, 266597d0caeSDavid Teigland params->mode, params->flags, 267d7db923eSDavid Teigland params->lkid, params->lvb, 268d7db923eSDavid Teigland (unsigned long) params->timeout); 2692ab4bd8eSDavid Teigland } else if (params->flags & DLM_LKF_ORPHAN) { 2702ab4bd8eSDavid Teigland error = dlm_user_adopt_orphan(ls, ua, 2712ab4bd8eSDavid Teigland params->mode, params->flags, 2722ab4bd8eSDavid Teigland params->name, params->namelen, 2732ab4bd8eSDavid Teigland (unsigned long) params->timeout, 2742ab4bd8eSDavid Teigland &lkid); 2752ab4bd8eSDavid Teigland if (!error) 2762ab4bd8eSDavid Teigland error = lkid; 2772ab4bd8eSDavid Teigland } else { 278597d0caeSDavid Teigland error = dlm_user_request(ls, ua, 279597d0caeSDavid Teigland params->mode, params->flags, 280597d0caeSDavid Teigland params->name, params->namelen, 281d7db923eSDavid Teigland (unsigned long) params->timeout); 282597d0caeSDavid Teigland if (!error) 283597d0caeSDavid Teigland error = ua->lksb.sb_lkid; 284597d0caeSDavid Teigland } 285597d0caeSDavid Teigland out: 286597d0caeSDavid Teigland dlm_put_lockspace(ls); 287597d0caeSDavid Teigland return error; 288597d0caeSDavid Teigland } 289597d0caeSDavid Teigland 290597d0caeSDavid Teigland static int device_user_unlock(struct dlm_user_proc *proc, 291597d0caeSDavid Teigland struct dlm_lock_params *params) 292597d0caeSDavid Teigland { 293597d0caeSDavid Teigland struct dlm_ls *ls; 294597d0caeSDavid Teigland struct dlm_user_args *ua; 295597d0caeSDavid Teigland int error = -ENOMEM; 296597d0caeSDavid Teigland 297597d0caeSDavid Teigland ls = dlm_find_lockspace_local(proc->lockspace); 298597d0caeSDavid Teigland if (!ls) 299597d0caeSDavid Teigland return -ENOENT; 300597d0caeSDavid Teigland 301573c24c4SDavid Teigland ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS); 302597d0caeSDavid Teigland if (!ua) 303597d0caeSDavid Teigland goto out; 304597d0caeSDavid Teigland ua->proc = proc; 305597d0caeSDavid Teigland ua->user_lksb = params->lksb; 306597d0caeSDavid Teigland ua->castparam = params->castparam; 307597d0caeSDavid Teigland ua->castaddr = params->castaddr; 308597d0caeSDavid Teigland 309597d0caeSDavid Teigland if (params->flags & DLM_LKF_CANCEL) 310597d0caeSDavid Teigland error = dlm_user_cancel(ls, ua, params->flags, params->lkid); 311597d0caeSDavid Teigland else 312597d0caeSDavid Teigland error = dlm_user_unlock(ls, ua, params->flags, params->lkid, 313597d0caeSDavid Teigland params->lvb); 314597d0caeSDavid Teigland out: 315597d0caeSDavid Teigland dlm_put_lockspace(ls); 316597d0caeSDavid Teigland return error; 317597d0caeSDavid Teigland } 318597d0caeSDavid Teigland 3198b4021faSDavid Teigland static int device_user_deadlock(struct dlm_user_proc *proc, 3208b4021faSDavid Teigland struct dlm_lock_params *params) 3218b4021faSDavid Teigland { 3228b4021faSDavid Teigland struct dlm_ls *ls; 3238b4021faSDavid Teigland int error; 3248b4021faSDavid Teigland 3258b4021faSDavid Teigland ls = dlm_find_lockspace_local(proc->lockspace); 3268b4021faSDavid Teigland if (!ls) 3278b4021faSDavid Teigland return -ENOENT; 3288b4021faSDavid Teigland 3298b4021faSDavid Teigland error = dlm_user_deadlock(ls, params->flags, params->lkid); 3308b4021faSDavid Teigland 3318b4021faSDavid Teigland dlm_put_lockspace(ls); 3328b4021faSDavid Teigland return error; 3338b4021faSDavid Teigland } 3348b4021faSDavid Teigland 3350f8e0d9aSDavid Teigland static int dlm_device_register(struct dlm_ls *ls, char *name) 336254da030SPatrick Caulfield { 337254da030SPatrick Caulfield int error, len; 338254da030SPatrick Caulfield 3390f8e0d9aSDavid Teigland /* The device is already registered. This happens when the 3400f8e0d9aSDavid Teigland lockspace is created multiple times from userspace. */ 3410f8e0d9aSDavid Teigland if (ls->ls_device.name) 3420f8e0d9aSDavid Teigland return 0; 3430f8e0d9aSDavid Teigland 344254da030SPatrick Caulfield error = -ENOMEM; 345254da030SPatrick Caulfield len = strlen(name) + strlen(name_prefix) + 2; 346573c24c4SDavid Teigland ls->ls_device.name = kzalloc(len, GFP_NOFS); 347254da030SPatrick Caulfield if (!ls->ls_device.name) 348254da030SPatrick Caulfield goto fail; 349254da030SPatrick Caulfield 350254da030SPatrick Caulfield snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix, 351254da030SPatrick Caulfield name); 352254da030SPatrick Caulfield ls->ls_device.fops = &device_fops; 353254da030SPatrick Caulfield ls->ls_device.minor = MISC_DYNAMIC_MINOR; 354254da030SPatrick Caulfield 355254da030SPatrick Caulfield error = misc_register(&ls->ls_device); 356254da030SPatrick Caulfield if (error) { 357254da030SPatrick Caulfield kfree(ls->ls_device.name); 358254da030SPatrick Caulfield } 359254da030SPatrick Caulfield fail: 360254da030SPatrick Caulfield return error; 361254da030SPatrick Caulfield } 362254da030SPatrick Caulfield 3630f8e0d9aSDavid Teigland int dlm_device_deregister(struct dlm_ls *ls) 3640f8e0d9aSDavid Teigland { 3650f8e0d9aSDavid Teigland /* The device is not registered. This happens when the lockspace 3660f8e0d9aSDavid Teigland was never used from userspace, or when device_create_lockspace() 3670f8e0d9aSDavid Teigland calls dlm_release_lockspace() after the register fails. */ 3680f8e0d9aSDavid Teigland if (!ls->ls_device.name) 3690f8e0d9aSDavid Teigland return 0; 3700f8e0d9aSDavid Teigland 371f368ed60SGreg Kroah-Hartman misc_deregister(&ls->ls_device); 3720f8e0d9aSDavid Teigland kfree(ls->ls_device.name); 373f368ed60SGreg Kroah-Hartman return 0; 3740f8e0d9aSDavid Teigland } 3750f8e0d9aSDavid Teigland 37672c2be77SDavid Teigland static int device_user_purge(struct dlm_user_proc *proc, 37772c2be77SDavid Teigland struct dlm_purge_params *params) 37872c2be77SDavid Teigland { 37972c2be77SDavid Teigland struct dlm_ls *ls; 38072c2be77SDavid Teigland int error; 38172c2be77SDavid Teigland 38272c2be77SDavid Teigland ls = dlm_find_lockspace_local(proc->lockspace); 38372c2be77SDavid Teigland if (!ls) 38472c2be77SDavid Teigland return -ENOENT; 38572c2be77SDavid Teigland 38672c2be77SDavid Teigland error = dlm_user_purge(ls, proc, params->nodeid, params->pid); 38772c2be77SDavid Teigland 38872c2be77SDavid Teigland dlm_put_lockspace(ls); 38972c2be77SDavid Teigland return error; 39072c2be77SDavid Teigland } 39172c2be77SDavid Teigland 392597d0caeSDavid Teigland static int device_create_lockspace(struct dlm_lspace_params *params) 393597d0caeSDavid Teigland { 394597d0caeSDavid Teigland dlm_lockspace_t *lockspace; 395597d0caeSDavid Teigland struct dlm_ls *ls; 396254da030SPatrick Caulfield int error; 397597d0caeSDavid Teigland 398597d0caeSDavid Teigland if (!capable(CAP_SYS_ADMIN)) 399597d0caeSDavid Teigland return -EPERM; 400597d0caeSDavid Teigland 40160f98d18SDavid Teigland error = dlm_new_lockspace(params->name, NULL, params->flags, 40260f98d18SDavid Teigland DLM_USER_LVB_LEN, NULL, NULL, NULL, 40360f98d18SDavid Teigland &lockspace); 404597d0caeSDavid Teigland if (error) 405597d0caeSDavid Teigland return error; 406597d0caeSDavid Teigland 407597d0caeSDavid Teigland ls = dlm_find_lockspace_local(lockspace); 408597d0caeSDavid Teigland if (!ls) 409597d0caeSDavid Teigland return -ENOENT; 410597d0caeSDavid Teigland 4110f8e0d9aSDavid Teigland error = dlm_device_register(ls, params->name); 412597d0caeSDavid Teigland dlm_put_lockspace(ls); 413597d0caeSDavid Teigland 414254da030SPatrick Caulfield if (error) 415597d0caeSDavid Teigland dlm_release_lockspace(lockspace, 0); 416254da030SPatrick Caulfield else 417254da030SPatrick Caulfield error = ls->ls_device.minor; 418254da030SPatrick Caulfield 419597d0caeSDavid Teigland return error; 420597d0caeSDavid Teigland } 421597d0caeSDavid Teigland 422597d0caeSDavid Teigland static int device_remove_lockspace(struct dlm_lspace_params *params) 423597d0caeSDavid Teigland { 424597d0caeSDavid Teigland dlm_lockspace_t *lockspace; 425597d0caeSDavid Teigland struct dlm_ls *ls; 426c6e6f0baSDavid Teigland int error, force = 0; 427597d0caeSDavid Teigland 428597d0caeSDavid Teigland if (!capable(CAP_SYS_ADMIN)) 429597d0caeSDavid Teigland return -EPERM; 430597d0caeSDavid Teigland 431597d0caeSDavid Teigland ls = dlm_find_lockspace_device(params->minor); 432597d0caeSDavid Teigland if (!ls) 433597d0caeSDavid Teigland return -ENOENT; 434597d0caeSDavid Teigland 435c6e6f0baSDavid Teigland if (params->flags & DLM_USER_LSFLG_FORCEFREE) 436c6e6f0baSDavid Teigland force = 2; 437c6e6f0baSDavid Teigland 438597d0caeSDavid Teigland lockspace = ls->ls_local_handle; 439597d0caeSDavid Teigland dlm_put_lockspace(ls); 4400f8e0d9aSDavid Teigland 4410f8e0d9aSDavid Teigland /* The final dlm_release_lockspace waits for references to go to 4420f8e0d9aSDavid Teigland zero, so all processes will need to close their device for the 4430f8e0d9aSDavid Teigland ls before the release will proceed. release also calls the 4440f8e0d9aSDavid Teigland device_deregister above. Converting a positive return value 4450f8e0d9aSDavid Teigland from release to zero means that userspace won't know when its 4460f8e0d9aSDavid Teigland release was the final one, but it shouldn't need to know. */ 4470f8e0d9aSDavid Teigland 448c6e6f0baSDavid Teigland error = dlm_release_lockspace(lockspace, force); 4490f8e0d9aSDavid Teigland if (error > 0) 4500f8e0d9aSDavid Teigland error = 0; 451597d0caeSDavid Teigland return error; 452597d0caeSDavid Teigland } 453597d0caeSDavid Teigland 454597d0caeSDavid Teigland /* Check the user's version matches ours */ 455597d0caeSDavid Teigland static int check_version(struct dlm_write_request *req) 456597d0caeSDavid Teigland { 457597d0caeSDavid Teigland if (req->version[0] != DLM_DEVICE_VERSION_MAJOR || 458597d0caeSDavid Teigland (req->version[0] == DLM_DEVICE_VERSION_MAJOR && 459597d0caeSDavid Teigland req->version[1] > DLM_DEVICE_VERSION_MINOR)) { 460597d0caeSDavid Teigland 461597d0caeSDavid Teigland printk(KERN_DEBUG "dlm: process %s (%d) version mismatch " 462597d0caeSDavid Teigland "user (%d.%d.%d) kernel (%d.%d.%d)\n", 463597d0caeSDavid Teigland current->comm, 464ba25f9dcSPavel Emelyanov task_pid_nr(current), 465597d0caeSDavid Teigland req->version[0], 466597d0caeSDavid Teigland req->version[1], 467597d0caeSDavid Teigland req->version[2], 468597d0caeSDavid Teigland DLM_DEVICE_VERSION_MAJOR, 469597d0caeSDavid Teigland DLM_DEVICE_VERSION_MINOR, 470597d0caeSDavid Teigland DLM_DEVICE_VERSION_PATCH); 471597d0caeSDavid Teigland return -EINVAL; 472597d0caeSDavid Teigland } 473597d0caeSDavid Teigland return 0; 474597d0caeSDavid Teigland } 475597d0caeSDavid Teigland 476597d0caeSDavid Teigland /* 477597d0caeSDavid Teigland * device_write 478597d0caeSDavid Teigland * 479597d0caeSDavid Teigland * device_user_lock 480597d0caeSDavid Teigland * dlm_user_request -> request_lock 481597d0caeSDavid Teigland * dlm_user_convert -> convert_lock 482597d0caeSDavid Teigland * 483597d0caeSDavid Teigland * device_user_unlock 484597d0caeSDavid Teigland * dlm_user_unlock -> unlock_lock 485597d0caeSDavid Teigland * dlm_user_cancel -> cancel_lock 486597d0caeSDavid Teigland * 487597d0caeSDavid Teigland * device_create_lockspace 488597d0caeSDavid Teigland * dlm_new_lockspace 489597d0caeSDavid Teigland * 490597d0caeSDavid Teigland * device_remove_lockspace 491597d0caeSDavid Teigland * dlm_release_lockspace 492597d0caeSDavid Teigland */ 493597d0caeSDavid Teigland 494597d0caeSDavid Teigland /* a write to a lockspace device is a lock or unlock request, a write 495597d0caeSDavid Teigland to the control device is to create/remove a lockspace */ 496597d0caeSDavid Teigland 497597d0caeSDavid Teigland static ssize_t device_write(struct file *file, const char __user *buf, 498597d0caeSDavid Teigland size_t count, loff_t *ppos) 499597d0caeSDavid Teigland { 500597d0caeSDavid Teigland struct dlm_user_proc *proc = file->private_data; 501597d0caeSDavid Teigland struct dlm_write_request *kbuf; 502597d0caeSDavid Teigland int error; 503597d0caeSDavid Teigland 504597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 505597d0caeSDavid Teigland if (count < sizeof(struct dlm_write_request32)) 506597d0caeSDavid Teigland #else 507597d0caeSDavid Teigland if (count < sizeof(struct dlm_write_request)) 508597d0caeSDavid Teigland #endif 509597d0caeSDavid Teigland return -EINVAL; 510597d0caeSDavid Teigland 511d4b0bcf3SDavid Teigland /* 512d4b0bcf3SDavid Teigland * can't compare against COMPAT/dlm_write_request32 because 513d4b0bcf3SDavid Teigland * we don't yet know if is64bit is zero 514d4b0bcf3SDavid Teigland */ 5152b75bc91SSasha Levin if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN) 5162b75bc91SSasha Levin return -EINVAL; 5172b75bc91SSasha Levin 518*16e5c1fcSAl Viro kbuf = memdup_user_nul(buf, count); 519*16e5c1fcSAl Viro if (!IS_ERR(kbuf)) 520*16e5c1fcSAl Viro return PTR_ERR(kbuf); 521597d0caeSDavid Teigland 522597d0caeSDavid Teigland if (check_version(kbuf)) { 523597d0caeSDavid Teigland error = -EBADE; 524597d0caeSDavid Teigland goto out_free; 525597d0caeSDavid Teigland } 526597d0caeSDavid Teigland 527597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 528597d0caeSDavid Teigland if (!kbuf->is64bit) { 529597d0caeSDavid Teigland struct dlm_write_request32 *k32buf; 5301fecb1c4SDavid Teigland int namelen = 0; 5311fecb1c4SDavid Teigland 5321fecb1c4SDavid Teigland if (count > sizeof(struct dlm_write_request32)) 5331fecb1c4SDavid Teigland namelen = count - sizeof(struct dlm_write_request32); 5341fecb1c4SDavid Teigland 535597d0caeSDavid Teigland k32buf = (struct dlm_write_request32 *)kbuf; 5361fecb1c4SDavid Teigland 5371fecb1c4SDavid Teigland /* add 1 after namelen so that the name string is terminated */ 5381fecb1c4SDavid Teigland kbuf = kzalloc(sizeof(struct dlm_write_request) + namelen + 1, 539573c24c4SDavid Teigland GFP_NOFS); 540cb980d9aSDavid Teigland if (!kbuf) { 541cb980d9aSDavid Teigland kfree(k32buf); 542597d0caeSDavid Teigland return -ENOMEM; 543cb980d9aSDavid Teigland } 544597d0caeSDavid Teigland 545597d0caeSDavid Teigland if (proc) 546597d0caeSDavid Teigland set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags); 5471fecb1c4SDavid Teigland 5481fecb1c4SDavid Teigland compat_input(kbuf, k32buf, namelen); 549597d0caeSDavid Teigland kfree(k32buf); 550597d0caeSDavid Teigland } 551597d0caeSDavid Teigland #endif 552597d0caeSDavid Teigland 553597d0caeSDavid Teigland /* do we really need this? can a write happen after a close? */ 554597d0caeSDavid Teigland if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) && 555cb980d9aSDavid Teigland (proc && test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))) { 556cb980d9aSDavid Teigland error = -EINVAL; 557cb980d9aSDavid Teigland goto out_free; 558cb980d9aSDavid Teigland } 559597d0caeSDavid Teigland 560597d0caeSDavid Teigland error = -EINVAL; 561597d0caeSDavid Teigland 562597d0caeSDavid Teigland switch (kbuf->cmd) 563597d0caeSDavid Teigland { 564597d0caeSDavid Teigland case DLM_USER_LOCK: 565597d0caeSDavid Teigland if (!proc) { 566597d0caeSDavid Teigland log_print("no locking on control device"); 567c6ca7bc9SDavid Teigland goto out_free; 568597d0caeSDavid Teigland } 569597d0caeSDavid Teigland error = device_user_lock(proc, &kbuf->i.lock); 570597d0caeSDavid Teigland break; 571597d0caeSDavid Teigland 572597d0caeSDavid Teigland case DLM_USER_UNLOCK: 573597d0caeSDavid Teigland if (!proc) { 574597d0caeSDavid Teigland log_print("no locking on control device"); 575c6ca7bc9SDavid Teigland goto out_free; 576597d0caeSDavid Teigland } 577597d0caeSDavid Teigland error = device_user_unlock(proc, &kbuf->i.lock); 578597d0caeSDavid Teigland break; 579597d0caeSDavid Teigland 5808b4021faSDavid Teigland case DLM_USER_DEADLOCK: 5818b4021faSDavid Teigland if (!proc) { 5828b4021faSDavid Teigland log_print("no locking on control device"); 583c6ca7bc9SDavid Teigland goto out_free; 5848b4021faSDavid Teigland } 5858b4021faSDavid Teigland error = device_user_deadlock(proc, &kbuf->i.lock); 5868b4021faSDavid Teigland break; 5878b4021faSDavid Teigland 588597d0caeSDavid Teigland case DLM_USER_CREATE_LOCKSPACE: 589597d0caeSDavid Teigland if (proc) { 590597d0caeSDavid Teigland log_print("create/remove only on control device"); 591c6ca7bc9SDavid Teigland goto out_free; 592597d0caeSDavid Teigland } 593597d0caeSDavid Teigland error = device_create_lockspace(&kbuf->i.lspace); 594597d0caeSDavid Teigland break; 595597d0caeSDavid Teigland 596597d0caeSDavid Teigland case DLM_USER_REMOVE_LOCKSPACE: 597597d0caeSDavid Teigland if (proc) { 598597d0caeSDavid Teigland log_print("create/remove only on control device"); 599c6ca7bc9SDavid Teigland goto out_free; 600597d0caeSDavid Teigland } 601597d0caeSDavid Teigland error = device_remove_lockspace(&kbuf->i.lspace); 602597d0caeSDavid Teigland break; 603597d0caeSDavid Teigland 60472c2be77SDavid Teigland case DLM_USER_PURGE: 60572c2be77SDavid Teigland if (!proc) { 60672c2be77SDavid Teigland log_print("no locking on control device"); 607c6ca7bc9SDavid Teigland goto out_free; 60872c2be77SDavid Teigland } 60972c2be77SDavid Teigland error = device_user_purge(proc, &kbuf->i.purge); 61072c2be77SDavid Teigland break; 61172c2be77SDavid Teigland 612597d0caeSDavid Teigland default: 613597d0caeSDavid Teigland log_print("Unknown command passed to DLM device : %d\n", 614597d0caeSDavid Teigland kbuf->cmd); 615597d0caeSDavid Teigland } 616597d0caeSDavid Teigland 617597d0caeSDavid Teigland out_free: 618597d0caeSDavid Teigland kfree(kbuf); 619597d0caeSDavid Teigland return error; 620597d0caeSDavid Teigland } 621597d0caeSDavid Teigland 622597d0caeSDavid Teigland /* Every process that opens the lockspace device has its own "proc" structure 623597d0caeSDavid Teigland hanging off the open file that's used to keep track of locks owned by the 624597d0caeSDavid Teigland process and asts that need to be delivered to the process. */ 625597d0caeSDavid Teigland 626597d0caeSDavid Teigland static int device_open(struct inode *inode, struct file *file) 627597d0caeSDavid Teigland { 628597d0caeSDavid Teigland struct dlm_user_proc *proc; 629597d0caeSDavid Teigland struct dlm_ls *ls; 630597d0caeSDavid Teigland 631597d0caeSDavid Teigland ls = dlm_find_lockspace_device(iminor(inode)); 632f9f2ed48SDavid Teigland if (!ls) 633597d0caeSDavid Teigland return -ENOENT; 634597d0caeSDavid Teigland 635573c24c4SDavid Teigland proc = kzalloc(sizeof(struct dlm_user_proc), GFP_NOFS); 636597d0caeSDavid Teigland if (!proc) { 637597d0caeSDavid Teigland dlm_put_lockspace(ls); 638597d0caeSDavid Teigland return -ENOMEM; 639597d0caeSDavid Teigland } 640597d0caeSDavid Teigland 641597d0caeSDavid Teigland proc->lockspace = ls->ls_local_handle; 642597d0caeSDavid Teigland INIT_LIST_HEAD(&proc->asts); 643597d0caeSDavid Teigland INIT_LIST_HEAD(&proc->locks); 644a1bc86e6SDavid Teigland INIT_LIST_HEAD(&proc->unlocking); 645597d0caeSDavid Teigland spin_lock_init(&proc->asts_spin); 646597d0caeSDavid Teigland spin_lock_init(&proc->locks_spin); 647597d0caeSDavid Teigland init_waitqueue_head(&proc->wait); 648597d0caeSDavid Teigland file->private_data = proc; 649597d0caeSDavid Teigland 650597d0caeSDavid Teigland return 0; 651597d0caeSDavid Teigland } 652597d0caeSDavid Teigland 653597d0caeSDavid Teigland static int device_close(struct inode *inode, struct file *file) 654597d0caeSDavid Teigland { 655597d0caeSDavid Teigland struct dlm_user_proc *proc = file->private_data; 656597d0caeSDavid Teigland struct dlm_ls *ls; 657597d0caeSDavid Teigland 658597d0caeSDavid Teigland ls = dlm_find_lockspace_local(proc->lockspace); 659597d0caeSDavid Teigland if (!ls) 660597d0caeSDavid Teigland return -ENOENT; 661597d0caeSDavid Teigland 662597d0caeSDavid Teigland set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags); 663597d0caeSDavid Teigland 664597d0caeSDavid Teigland dlm_clear_proc_locks(ls, proc); 665597d0caeSDavid Teigland 666597d0caeSDavid Teigland /* at this point no more lkb's should exist for this lockspace, 667597d0caeSDavid Teigland so there's no chance of dlm_user_add_ast() being called and 668597d0caeSDavid Teigland looking for lkb->ua->proc */ 669597d0caeSDavid Teigland 670597d0caeSDavid Teigland kfree(proc); 671597d0caeSDavid Teigland file->private_data = NULL; 672597d0caeSDavid Teigland 673597d0caeSDavid Teigland dlm_put_lockspace(ls); 674597d0caeSDavid Teigland dlm_put_lockspace(ls); /* for the find in device_open() */ 675597d0caeSDavid Teigland 676597d0caeSDavid Teigland /* FIXME: AUTOFREE: if this ls is no longer used do 677597d0caeSDavid Teigland device_remove_lockspace() */ 678597d0caeSDavid Teigland 679597d0caeSDavid Teigland return 0; 680597d0caeSDavid Teigland } 681597d0caeSDavid Teigland 6828304d6f2SDavid Teigland static int copy_result_to_user(struct dlm_user_args *ua, int compat, 6838304d6f2SDavid Teigland uint32_t flags, int mode, int copy_lvb, 6848304d6f2SDavid Teigland char __user *buf, size_t count) 685597d0caeSDavid Teigland { 686597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 687597d0caeSDavid Teigland struct dlm_lock_result32 result32; 688597d0caeSDavid Teigland #endif 689597d0caeSDavid Teigland struct dlm_lock_result result; 690597d0caeSDavid Teigland void *resultptr; 691597d0caeSDavid Teigland int error=0; 692597d0caeSDavid Teigland int len; 693597d0caeSDavid Teigland int struct_len; 694597d0caeSDavid Teigland 695597d0caeSDavid Teigland memset(&result, 0, sizeof(struct dlm_lock_result)); 696d7db923eSDavid Teigland result.version[0] = DLM_DEVICE_VERSION_MAJOR; 697d7db923eSDavid Teigland result.version[1] = DLM_DEVICE_VERSION_MINOR; 698d7db923eSDavid Teigland result.version[2] = DLM_DEVICE_VERSION_PATCH; 699597d0caeSDavid Teigland memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb)); 700597d0caeSDavid Teigland result.user_lksb = ua->user_lksb; 701597d0caeSDavid Teigland 702597d0caeSDavid Teigland /* FIXME: dlm1 provides for the user's bastparam/addr to not be updated 703597d0caeSDavid Teigland in a conversion unless the conversion is successful. See code 704597d0caeSDavid Teigland in dlm_user_convert() for updating ua from ua_tmp. OpenVMS, though, 705597d0caeSDavid Teigland notes that a new blocking AST address and parameter are set even if 706597d0caeSDavid Teigland the conversion fails, so maybe we should just do that. */ 707597d0caeSDavid Teigland 7088304d6f2SDavid Teigland if (flags & DLM_CB_BAST) { 709597d0caeSDavid Teigland result.user_astaddr = ua->bastaddr; 710597d0caeSDavid Teigland result.user_astparam = ua->bastparam; 71189d799d0SDavid Teigland result.bast_mode = mode; 712597d0caeSDavid Teigland } else { 713597d0caeSDavid Teigland result.user_astaddr = ua->castaddr; 714597d0caeSDavid Teigland result.user_astparam = ua->castparam; 715597d0caeSDavid Teigland } 716597d0caeSDavid Teigland 717597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 718597d0caeSDavid Teigland if (compat) 719597d0caeSDavid Teigland len = sizeof(struct dlm_lock_result32); 720597d0caeSDavid Teigland else 721597d0caeSDavid Teigland #endif 722597d0caeSDavid Teigland len = sizeof(struct dlm_lock_result); 723597d0caeSDavid Teigland struct_len = len; 724597d0caeSDavid Teigland 725597d0caeSDavid Teigland /* copy lvb to userspace if there is one, it's been updated, and 726597d0caeSDavid Teigland the user buffer has space for it */ 727597d0caeSDavid Teigland 7288304d6f2SDavid Teigland if (copy_lvb && ua->lksb.sb_lvbptr && count >= len + DLM_USER_LVB_LEN) { 729597d0caeSDavid Teigland if (copy_to_user(buf+len, ua->lksb.sb_lvbptr, 730597d0caeSDavid Teigland DLM_USER_LVB_LEN)) { 731597d0caeSDavid Teigland error = -EFAULT; 732597d0caeSDavid Teigland goto out; 733597d0caeSDavid Teigland } 734597d0caeSDavid Teigland 735597d0caeSDavid Teigland result.lvb_offset = len; 736597d0caeSDavid Teigland len += DLM_USER_LVB_LEN; 737597d0caeSDavid Teigland } 738597d0caeSDavid Teigland 739597d0caeSDavid Teigland result.length = len; 740597d0caeSDavid Teigland resultptr = &result; 741597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 742597d0caeSDavid Teigland if (compat) { 743597d0caeSDavid Teigland compat_output(&result, &result32); 744597d0caeSDavid Teigland resultptr = &result32; 745597d0caeSDavid Teigland } 746597d0caeSDavid Teigland #endif 747597d0caeSDavid Teigland 748597d0caeSDavid Teigland if (copy_to_user(buf, resultptr, struct_len)) 749597d0caeSDavid Teigland error = -EFAULT; 750597d0caeSDavid Teigland else 751597d0caeSDavid Teigland error = len; 752597d0caeSDavid Teigland out: 753597d0caeSDavid Teigland return error; 754597d0caeSDavid Teigland } 755597d0caeSDavid Teigland 756d7db923eSDavid Teigland static int copy_version_to_user(char __user *buf, size_t count) 757d7db923eSDavid Teigland { 758d7db923eSDavid Teigland struct dlm_device_version ver; 759d7db923eSDavid Teigland 760d7db923eSDavid Teigland memset(&ver, 0, sizeof(struct dlm_device_version)); 761d7db923eSDavid Teigland ver.version[0] = DLM_DEVICE_VERSION_MAJOR; 762d7db923eSDavid Teigland ver.version[1] = DLM_DEVICE_VERSION_MINOR; 763d7db923eSDavid Teigland ver.version[2] = DLM_DEVICE_VERSION_PATCH; 764d7db923eSDavid Teigland 765d7db923eSDavid Teigland if (copy_to_user(buf, &ver, sizeof(struct dlm_device_version))) 766d7db923eSDavid Teigland return -EFAULT; 767d7db923eSDavid Teigland return sizeof(struct dlm_device_version); 768d7db923eSDavid Teigland } 769d7db923eSDavid Teigland 770597d0caeSDavid Teigland /* a read returns a single ast described in a struct dlm_lock_result */ 771597d0caeSDavid Teigland 772597d0caeSDavid Teigland static ssize_t device_read(struct file *file, char __user *buf, size_t count, 773597d0caeSDavid Teigland loff_t *ppos) 774597d0caeSDavid Teigland { 775597d0caeSDavid Teigland struct dlm_user_proc *proc = file->private_data; 776597d0caeSDavid Teigland struct dlm_lkb *lkb; 777597d0caeSDavid Teigland DECLARE_WAITQUEUE(wait, current); 7788304d6f2SDavid Teigland struct dlm_callback cb; 7798304d6f2SDavid Teigland int rv, resid, copy_lvb = 0; 780b96f4650SDavid Teigland int old_mode, new_mode; 781597d0caeSDavid Teigland 782d7db923eSDavid Teigland if (count == sizeof(struct dlm_device_version)) { 7838304d6f2SDavid Teigland rv = copy_version_to_user(buf, count); 7848304d6f2SDavid Teigland return rv; 785d7db923eSDavid Teigland } 786d7db923eSDavid Teigland 787d7db923eSDavid Teigland if (!proc) { 788d7db923eSDavid Teigland log_print("non-version read from control device %zu", count); 789d7db923eSDavid Teigland return -EINVAL; 790d7db923eSDavid Teigland } 791d7db923eSDavid Teigland 792597d0caeSDavid Teigland #ifdef CONFIG_COMPAT 793597d0caeSDavid Teigland if (count < sizeof(struct dlm_lock_result32)) 794597d0caeSDavid Teigland #else 795597d0caeSDavid Teigland if (count < sizeof(struct dlm_lock_result)) 796597d0caeSDavid Teigland #endif 797597d0caeSDavid Teigland return -EINVAL; 798597d0caeSDavid Teigland 79989d799d0SDavid Teigland try_another: 80089d799d0SDavid Teigland 801597d0caeSDavid Teigland /* do we really need this? can a read happen after a close? */ 802597d0caeSDavid Teigland if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)) 803597d0caeSDavid Teigland return -EINVAL; 804597d0caeSDavid Teigland 805597d0caeSDavid Teigland spin_lock(&proc->asts_spin); 806597d0caeSDavid Teigland if (list_empty(&proc->asts)) { 807597d0caeSDavid Teigland if (file->f_flags & O_NONBLOCK) { 808597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 809597d0caeSDavid Teigland return -EAGAIN; 810597d0caeSDavid Teigland } 811597d0caeSDavid Teigland 812597d0caeSDavid Teigland add_wait_queue(&proc->wait, &wait); 813597d0caeSDavid Teigland 814597d0caeSDavid Teigland repeat: 815597d0caeSDavid Teigland set_current_state(TASK_INTERRUPTIBLE); 816597d0caeSDavid Teigland if (list_empty(&proc->asts) && !signal_pending(current)) { 817597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 818597d0caeSDavid Teigland schedule(); 819597d0caeSDavid Teigland spin_lock(&proc->asts_spin); 820597d0caeSDavid Teigland goto repeat; 821597d0caeSDavid Teigland } 822597d0caeSDavid Teigland set_current_state(TASK_RUNNING); 823597d0caeSDavid Teigland remove_wait_queue(&proc->wait, &wait); 824597d0caeSDavid Teigland 825597d0caeSDavid Teigland if (signal_pending(current)) { 826597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 827597d0caeSDavid Teigland return -ERESTARTSYS; 828597d0caeSDavid Teigland } 829597d0caeSDavid Teigland } 830597d0caeSDavid Teigland 8318304d6f2SDavid Teigland /* if we empty lkb_callbacks, we don't want to unlock the spinlock 83223e8e1aaSDavid Teigland without removing lkb_cb_list; so empty lkb_cb_list is always 8338304d6f2SDavid Teigland consistent with empty lkb_callbacks */ 834597d0caeSDavid Teigland 83523e8e1aaSDavid Teigland lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_cb_list); 836597d0caeSDavid Teigland 837b96f4650SDavid Teigland /* rem_lkb_callback sets a new lkb_last_cast */ 838b96f4650SDavid Teigland old_mode = lkb->lkb_last_cast.mode; 839b96f4650SDavid Teigland 8408304d6f2SDavid Teigland rv = dlm_rem_lkb_callback(lkb->lkb_resource->res_ls, lkb, &cb, &resid); 8418304d6f2SDavid Teigland if (rv < 0) { 8428304d6f2SDavid Teigland /* this shouldn't happen; lkb should have been removed from 8438304d6f2SDavid Teigland list when resid was zero */ 8448304d6f2SDavid Teigland log_print("dlm_rem_lkb_callback empty %x", lkb->lkb_id); 84523e8e1aaSDavid Teigland list_del_init(&lkb->lkb_cb_list); 8468304d6f2SDavid Teigland spin_unlock(&proc->asts_spin); 8478304d6f2SDavid Teigland /* removes ref for proc->asts, may cause lkb to be freed */ 8488304d6f2SDavid Teigland dlm_put_lkb(lkb); 8498304d6f2SDavid Teigland goto try_another; 85089d799d0SDavid Teigland } 8518304d6f2SDavid Teigland if (!resid) 85223e8e1aaSDavid Teigland list_del_init(&lkb->lkb_cb_list); 853597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 854597d0caeSDavid Teigland 8558304d6f2SDavid Teigland if (cb.flags & DLM_CB_SKIP) { 8568304d6f2SDavid Teigland /* removes ref for proc->asts, may cause lkb to be freed */ 8578304d6f2SDavid Teigland if (!resid) 8588304d6f2SDavid Teigland dlm_put_lkb(lkb); 8598304d6f2SDavid Teigland goto try_another; 86089d799d0SDavid Teigland } 861597d0caeSDavid Teigland 8628304d6f2SDavid Teigland if (cb.flags & DLM_CB_CAST) { 8638304d6f2SDavid Teigland new_mode = cb.mode; 8648304d6f2SDavid Teigland 8658304d6f2SDavid Teigland if (!cb.sb_status && lkb->lkb_lksb->sb_lvbptr && 8668304d6f2SDavid Teigland dlm_lvb_operations[old_mode + 1][new_mode + 1]) 8678304d6f2SDavid Teigland copy_lvb = 1; 8688304d6f2SDavid Teigland 8698304d6f2SDavid Teigland lkb->lkb_lksb->sb_status = cb.sb_status; 8708304d6f2SDavid Teigland lkb->lkb_lksb->sb_flags = cb.sb_flags; 8718304d6f2SDavid Teigland } 8728304d6f2SDavid Teigland 8738304d6f2SDavid Teigland rv = copy_result_to_user(lkb->lkb_ua, 8748304d6f2SDavid Teigland test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), 8758304d6f2SDavid Teigland cb.flags, cb.mode, copy_lvb, buf, count); 8768304d6f2SDavid Teigland 8778304d6f2SDavid Teigland /* removes ref for proc->asts, may cause lkb to be freed */ 8788304d6f2SDavid Teigland if (!resid) 879597d0caeSDavid Teigland dlm_put_lkb(lkb); 880597d0caeSDavid Teigland 8818304d6f2SDavid Teigland return rv; 882597d0caeSDavid Teigland } 883597d0caeSDavid Teigland 884597d0caeSDavid Teigland static unsigned int device_poll(struct file *file, poll_table *wait) 885597d0caeSDavid Teigland { 886597d0caeSDavid Teigland struct dlm_user_proc *proc = file->private_data; 887597d0caeSDavid Teigland 888597d0caeSDavid Teigland poll_wait(file, &proc->wait, wait); 889597d0caeSDavid Teigland 890597d0caeSDavid Teigland spin_lock(&proc->asts_spin); 891597d0caeSDavid Teigland if (!list_empty(&proc->asts)) { 892597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 893597d0caeSDavid Teigland return POLLIN | POLLRDNORM; 894597d0caeSDavid Teigland } 895597d0caeSDavid Teigland spin_unlock(&proc->asts_spin); 896597d0caeSDavid Teigland return 0; 897597d0caeSDavid Teigland } 898597d0caeSDavid Teigland 899dc68c7edSDavid Teigland int dlm_user_daemon_available(void) 900dc68c7edSDavid Teigland { 901dc68c7edSDavid Teigland /* dlm_controld hasn't started (or, has started, but not 902dc68c7edSDavid Teigland properly populated configfs) */ 903dc68c7edSDavid Teigland 904dc68c7edSDavid Teigland if (!dlm_our_nodeid()) 905dc68c7edSDavid Teigland return 0; 906dc68c7edSDavid Teigland 907dc68c7edSDavid Teigland /* This is to deal with versions of dlm_controld that don't 908dc68c7edSDavid Teigland know about the monitor device. We assume that if the 909dc68c7edSDavid Teigland dlm_controld was started (above), but the monitor device 910dc68c7edSDavid Teigland was never opened, that it's an old version. dlm_controld 911dc68c7edSDavid Teigland should open the monitor device before populating configfs. */ 912dc68c7edSDavid Teigland 913dc68c7edSDavid Teigland if (dlm_monitor_unused) 914dc68c7edSDavid Teigland return 1; 915dc68c7edSDavid Teigland 916dc68c7edSDavid Teigland return atomic_read(&dlm_monitor_opened) ? 1 : 0; 917dc68c7edSDavid Teigland } 918dc68c7edSDavid Teigland 919597d0caeSDavid Teigland static int ctl_device_open(struct inode *inode, struct file *file) 920597d0caeSDavid Teigland { 921597d0caeSDavid Teigland file->private_data = NULL; 922597d0caeSDavid Teigland return 0; 923597d0caeSDavid Teigland } 924597d0caeSDavid Teigland 925597d0caeSDavid Teigland static int ctl_device_close(struct inode *inode, struct file *file) 926597d0caeSDavid Teigland { 927597d0caeSDavid Teigland return 0; 928597d0caeSDavid Teigland } 929597d0caeSDavid Teigland 930dc68c7edSDavid Teigland static int monitor_device_open(struct inode *inode, struct file *file) 931dc68c7edSDavid Teigland { 932dc68c7edSDavid Teigland atomic_inc(&dlm_monitor_opened); 933dc68c7edSDavid Teigland dlm_monitor_unused = 0; 934dc68c7edSDavid Teigland return 0; 935dc68c7edSDavid Teigland } 936dc68c7edSDavid Teigland 937dc68c7edSDavid Teigland static int monitor_device_close(struct inode *inode, struct file *file) 938dc68c7edSDavid Teigland { 939dc68c7edSDavid Teigland if (atomic_dec_and_test(&dlm_monitor_opened)) 940dc68c7edSDavid Teigland dlm_stop_lockspaces(); 941dc68c7edSDavid Teigland return 0; 942dc68c7edSDavid Teigland } 943dc68c7edSDavid Teigland 94400977a59SArjan van de Ven static const struct file_operations device_fops = { 945597d0caeSDavid Teigland .open = device_open, 946597d0caeSDavid Teigland .release = device_close, 947597d0caeSDavid Teigland .read = device_read, 948597d0caeSDavid Teigland .write = device_write, 949597d0caeSDavid Teigland .poll = device_poll, 950597d0caeSDavid Teigland .owner = THIS_MODULE, 9516038f373SArnd Bergmann .llseek = noop_llseek, 952597d0caeSDavid Teigland }; 953597d0caeSDavid Teigland 95400977a59SArjan van de Ven static const struct file_operations ctl_device_fops = { 955597d0caeSDavid Teigland .open = ctl_device_open, 956597d0caeSDavid Teigland .release = ctl_device_close, 957d7db923eSDavid Teigland .read = device_read, 958597d0caeSDavid Teigland .write = device_write, 959597d0caeSDavid Teigland .owner = THIS_MODULE, 9606038f373SArnd Bergmann .llseek = noop_llseek, 961597d0caeSDavid Teigland }; 962597d0caeSDavid Teigland 9630fe410d3SDenis Cheng static struct miscdevice ctl_device = { 9640fe410d3SDenis Cheng .name = "dlm-control", 9650fe410d3SDenis Cheng .fops = &ctl_device_fops, 9660fe410d3SDenis Cheng .minor = MISC_DYNAMIC_MINOR, 9670fe410d3SDenis Cheng }; 9680fe410d3SDenis Cheng 969dc68c7edSDavid Teigland static const struct file_operations monitor_device_fops = { 970dc68c7edSDavid Teigland .open = monitor_device_open, 971dc68c7edSDavid Teigland .release = monitor_device_close, 972dc68c7edSDavid Teigland .owner = THIS_MODULE, 9736038f373SArnd Bergmann .llseek = noop_llseek, 974dc68c7edSDavid Teigland }; 975dc68c7edSDavid Teigland 976dc68c7edSDavid Teigland static struct miscdevice monitor_device = { 977dc68c7edSDavid Teigland .name = "dlm-monitor", 978dc68c7edSDavid Teigland .fops = &monitor_device_fops, 979dc68c7edSDavid Teigland .minor = MISC_DYNAMIC_MINOR, 980dc68c7edSDavid Teigland }; 981dc68c7edSDavid Teigland 98230727174SDenis Cheng int __init dlm_user_init(void) 983597d0caeSDavid Teigland { 984597d0caeSDavid Teigland int error; 985597d0caeSDavid Teigland 986dc68c7edSDavid Teigland atomic_set(&dlm_monitor_opened, 0); 987597d0caeSDavid Teigland 988dc68c7edSDavid Teigland error = misc_register(&ctl_device); 989dc68c7edSDavid Teigland if (error) { 990dc68c7edSDavid Teigland log_print("misc_register failed for control device"); 991dc68c7edSDavid Teigland goto out; 992dc68c7edSDavid Teigland } 993dc68c7edSDavid Teigland 994dc68c7edSDavid Teigland error = misc_register(&monitor_device); 995dc68c7edSDavid Teigland if (error) { 996dc68c7edSDavid Teigland log_print("misc_register failed for monitor device"); 997dc68c7edSDavid Teigland misc_deregister(&ctl_device); 998dc68c7edSDavid Teigland } 999dc68c7edSDavid Teigland out: 1000597d0caeSDavid Teigland return error; 1001597d0caeSDavid Teigland } 1002597d0caeSDavid Teigland 1003597d0caeSDavid Teigland void dlm_user_exit(void) 1004597d0caeSDavid Teigland { 1005597d0caeSDavid Teigland misc_deregister(&ctl_device); 1006dc68c7edSDavid Teigland misc_deregister(&monitor_device); 1007597d0caeSDavid Teigland } 1008597d0caeSDavid Teigland 1009