162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2006-2010 Red Hat, Inc. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/miscdevice.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/wait.h> 962306a36Sopenharmony_ci#include <linux/file.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/poll.h> 1262306a36Sopenharmony_ci#include <linux/signal.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <linux/dlm.h> 1562306a36Sopenharmony_ci#include <linux/dlm_device.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/sched/signal.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <trace/events/dlm.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "dlm_internal.h" 2262306a36Sopenharmony_ci#include "lockspace.h" 2362306a36Sopenharmony_ci#include "lock.h" 2462306a36Sopenharmony_ci#include "lvb_table.h" 2562306a36Sopenharmony_ci#include "user.h" 2662306a36Sopenharmony_ci#include "ast.h" 2762306a36Sopenharmony_ci#include "config.h" 2862306a36Sopenharmony_ci#include "memory.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const char name_prefix[] = "dlm"; 3162306a36Sopenharmony_cistatic const struct file_operations device_fops; 3262306a36Sopenharmony_cistatic atomic_t dlm_monitor_opened; 3362306a36Sopenharmony_cistatic int dlm_monitor_unused = 1; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct dlm_lock_params32 { 3862306a36Sopenharmony_ci __u8 mode; 3962306a36Sopenharmony_ci __u8 namelen; 4062306a36Sopenharmony_ci __u16 unused; 4162306a36Sopenharmony_ci __u32 flags; 4262306a36Sopenharmony_ci __u32 lkid; 4362306a36Sopenharmony_ci __u32 parent; 4462306a36Sopenharmony_ci __u64 xid; 4562306a36Sopenharmony_ci __u64 timeout; 4662306a36Sopenharmony_ci __u32 castparam; 4762306a36Sopenharmony_ci __u32 castaddr; 4862306a36Sopenharmony_ci __u32 bastparam; 4962306a36Sopenharmony_ci __u32 bastaddr; 5062306a36Sopenharmony_ci __u32 lksb; 5162306a36Sopenharmony_ci char lvb[DLM_USER_LVB_LEN]; 5262306a36Sopenharmony_ci char name[]; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct dlm_write_request32 { 5662306a36Sopenharmony_ci __u32 version[3]; 5762306a36Sopenharmony_ci __u8 cmd; 5862306a36Sopenharmony_ci __u8 is64bit; 5962306a36Sopenharmony_ci __u8 unused[2]; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci union { 6262306a36Sopenharmony_ci struct dlm_lock_params32 lock; 6362306a36Sopenharmony_ci struct dlm_lspace_params lspace; 6462306a36Sopenharmony_ci struct dlm_purge_params purge; 6562306a36Sopenharmony_ci } i; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct dlm_lksb32 { 6962306a36Sopenharmony_ci __u32 sb_status; 7062306a36Sopenharmony_ci __u32 sb_lkid; 7162306a36Sopenharmony_ci __u8 sb_flags; 7262306a36Sopenharmony_ci __u32 sb_lvbptr; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct dlm_lock_result32 { 7662306a36Sopenharmony_ci __u32 version[3]; 7762306a36Sopenharmony_ci __u32 length; 7862306a36Sopenharmony_ci __u32 user_astaddr; 7962306a36Sopenharmony_ci __u32 user_astparam; 8062306a36Sopenharmony_ci __u32 user_lksb; 8162306a36Sopenharmony_ci struct dlm_lksb32 lksb; 8262306a36Sopenharmony_ci __u8 bast_mode; 8362306a36Sopenharmony_ci __u8 unused[3]; 8462306a36Sopenharmony_ci /* Offsets may be zero if no data is present */ 8562306a36Sopenharmony_ci __u32 lvb_offset; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void compat_input(struct dlm_write_request *kb, 8962306a36Sopenharmony_ci struct dlm_write_request32 *kb32, 9062306a36Sopenharmony_ci int namelen) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci kb->version[0] = kb32->version[0]; 9362306a36Sopenharmony_ci kb->version[1] = kb32->version[1]; 9462306a36Sopenharmony_ci kb->version[2] = kb32->version[2]; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci kb->cmd = kb32->cmd; 9762306a36Sopenharmony_ci kb->is64bit = kb32->is64bit; 9862306a36Sopenharmony_ci if (kb->cmd == DLM_USER_CREATE_LOCKSPACE || 9962306a36Sopenharmony_ci kb->cmd == DLM_USER_REMOVE_LOCKSPACE) { 10062306a36Sopenharmony_ci kb->i.lspace.flags = kb32->i.lspace.flags; 10162306a36Sopenharmony_ci kb->i.lspace.minor = kb32->i.lspace.minor; 10262306a36Sopenharmony_ci memcpy(kb->i.lspace.name, kb32->i.lspace.name, namelen); 10362306a36Sopenharmony_ci } else if (kb->cmd == DLM_USER_PURGE) { 10462306a36Sopenharmony_ci kb->i.purge.nodeid = kb32->i.purge.nodeid; 10562306a36Sopenharmony_ci kb->i.purge.pid = kb32->i.purge.pid; 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci kb->i.lock.mode = kb32->i.lock.mode; 10862306a36Sopenharmony_ci kb->i.lock.namelen = kb32->i.lock.namelen; 10962306a36Sopenharmony_ci kb->i.lock.flags = kb32->i.lock.flags; 11062306a36Sopenharmony_ci kb->i.lock.lkid = kb32->i.lock.lkid; 11162306a36Sopenharmony_ci kb->i.lock.parent = kb32->i.lock.parent; 11262306a36Sopenharmony_ci kb->i.lock.xid = kb32->i.lock.xid; 11362306a36Sopenharmony_ci kb->i.lock.timeout = kb32->i.lock.timeout; 11462306a36Sopenharmony_ci kb->i.lock.castparam = (__user void *)(long)kb32->i.lock.castparam; 11562306a36Sopenharmony_ci kb->i.lock.castaddr = (__user void *)(long)kb32->i.lock.castaddr; 11662306a36Sopenharmony_ci kb->i.lock.bastparam = (__user void *)(long)kb32->i.lock.bastparam; 11762306a36Sopenharmony_ci kb->i.lock.bastaddr = (__user void *)(long)kb32->i.lock.bastaddr; 11862306a36Sopenharmony_ci kb->i.lock.lksb = (__user void *)(long)kb32->i.lock.lksb; 11962306a36Sopenharmony_ci memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN); 12062306a36Sopenharmony_ci memcpy(kb->i.lock.name, kb32->i.lock.name, namelen); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void compat_output(struct dlm_lock_result *res, 12562306a36Sopenharmony_ci struct dlm_lock_result32 *res32) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci memset(res32, 0, sizeof(*res32)); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci res32->version[0] = res->version[0]; 13062306a36Sopenharmony_ci res32->version[1] = res->version[1]; 13162306a36Sopenharmony_ci res32->version[2] = res->version[2]; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci res32->user_astaddr = (__u32)(__force long)res->user_astaddr; 13462306a36Sopenharmony_ci res32->user_astparam = (__u32)(__force long)res->user_astparam; 13562306a36Sopenharmony_ci res32->user_lksb = (__u32)(__force long)res->user_lksb; 13662306a36Sopenharmony_ci res32->bast_mode = res->bast_mode; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci res32->lvb_offset = res->lvb_offset; 13962306a36Sopenharmony_ci res32->length = res->length; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci res32->lksb.sb_status = res->lksb.sb_status; 14262306a36Sopenharmony_ci res32->lksb.sb_flags = res->lksb.sb_flags; 14362306a36Sopenharmony_ci res32->lksb.sb_lkid = res->lksb.sb_lkid; 14462306a36Sopenharmony_ci res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci#endif 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* should held proc->asts_spin lock */ 14962306a36Sopenharmony_civoid dlm_purge_lkb_callbacks(struct dlm_lkb *lkb) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct dlm_callback *cb, *safe; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci list_for_each_entry_safe(cb, safe, &lkb->lkb_callbacks, list) { 15462306a36Sopenharmony_ci list_del(&cb->list); 15562306a36Sopenharmony_ci kref_put(&cb->ref, dlm_release_callback); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci clear_bit(DLM_IFL_CB_PENDING_BIT, &lkb->lkb_iflags); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* invalidate */ 16162306a36Sopenharmony_ci dlm_callback_set_last_ptr(&lkb->lkb_last_cast, NULL); 16262306a36Sopenharmony_ci dlm_callback_set_last_ptr(&lkb->lkb_last_cb, NULL); 16362306a36Sopenharmony_ci lkb->lkb_last_bast_mode = -1; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* Figure out if this lock is at the end of its life and no longer 16762306a36Sopenharmony_ci available for the application to use. The lkb still exists until 16862306a36Sopenharmony_ci the final ast is read. A lock becomes EOL in three situations: 16962306a36Sopenharmony_ci 1. a noqueue request fails with EAGAIN 17062306a36Sopenharmony_ci 2. an unlock completes with EUNLOCK 17162306a36Sopenharmony_ci 3. a cancel of a waiting request completes with ECANCEL/EDEADLK 17262306a36Sopenharmony_ci An EOL lock needs to be removed from the process's list of locks. 17362306a36Sopenharmony_ci And we can't allow any new operation on an EOL lock. This is 17462306a36Sopenharmony_ci not related to the lifetime of the lkb struct which is managed 17562306a36Sopenharmony_ci entirely by refcount. */ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int lkb_is_endoflife(int mode, int status) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci switch (status) { 18062306a36Sopenharmony_ci case -DLM_EUNLOCK: 18162306a36Sopenharmony_ci return 1; 18262306a36Sopenharmony_ci case -DLM_ECANCEL: 18362306a36Sopenharmony_ci case -ETIMEDOUT: 18462306a36Sopenharmony_ci case -EDEADLK: 18562306a36Sopenharmony_ci case -EAGAIN: 18662306a36Sopenharmony_ci if (mode == DLM_LOCK_IV) 18762306a36Sopenharmony_ci return 1; 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* we could possibly check if the cancel of an orphan has resulted in the lkb 19462306a36Sopenharmony_ci being removed and then remove that lkb from the orphans list and free it */ 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_civoid dlm_user_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode, 19762306a36Sopenharmony_ci int status, uint32_t sbflags) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct dlm_ls *ls; 20062306a36Sopenharmony_ci struct dlm_user_args *ua; 20162306a36Sopenharmony_ci struct dlm_user_proc *proc; 20262306a36Sopenharmony_ci int rv; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (test_bit(DLM_DFL_ORPHAN_BIT, &lkb->lkb_dflags) || 20562306a36Sopenharmony_ci test_bit(DLM_IFL_DEAD_BIT, &lkb->lkb_iflags)) 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ls = lkb->lkb_resource->res_ls; 20962306a36Sopenharmony_ci spin_lock(&ls->ls_clear_proc_locks); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* If ORPHAN/DEAD flag is set, it means the process is dead so an ast 21262306a36Sopenharmony_ci can't be delivered. For ORPHAN's, dlm_clear_proc_locks() freed 21362306a36Sopenharmony_ci lkb->ua so we can't try to use it. This second check is necessary 21462306a36Sopenharmony_ci for cases where a completion ast is received for an operation that 21562306a36Sopenharmony_ci began before clear_proc_locks did its cancel/unlock. */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (test_bit(DLM_DFL_ORPHAN_BIT, &lkb->lkb_dflags) || 21862306a36Sopenharmony_ci test_bit(DLM_IFL_DEAD_BIT, &lkb->lkb_iflags)) 21962306a36Sopenharmony_ci goto out; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci DLM_ASSERT(lkb->lkb_ua, dlm_print_lkb(lkb);); 22262306a36Sopenharmony_ci ua = lkb->lkb_ua; 22362306a36Sopenharmony_ci proc = ua->proc; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if ((flags & DLM_CB_BAST) && ua->bastaddr == NULL) 22662306a36Sopenharmony_ci goto out; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if ((flags & DLM_CB_CAST) && lkb_is_endoflife(mode, status)) 22962306a36Sopenharmony_ci set_bit(DLM_IFL_ENDOFLIFE_BIT, &lkb->lkb_iflags); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci spin_lock(&proc->asts_spin); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci rv = dlm_enqueue_lkb_callback(lkb, flags, mode, status, sbflags); 23462306a36Sopenharmony_ci switch (rv) { 23562306a36Sopenharmony_ci case DLM_ENQUEUE_CALLBACK_FAILURE: 23662306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 23762306a36Sopenharmony_ci WARN_ON_ONCE(1); 23862306a36Sopenharmony_ci goto out; 23962306a36Sopenharmony_ci case DLM_ENQUEUE_CALLBACK_NEED_SCHED: 24062306a36Sopenharmony_ci kref_get(&lkb->lkb_ref); 24162306a36Sopenharmony_ci list_add_tail(&lkb->lkb_cb_list, &proc->asts); 24262306a36Sopenharmony_ci wake_up_interruptible(&proc->wait); 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case DLM_ENQUEUE_CALLBACK_SUCCESS: 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci default: 24762306a36Sopenharmony_ci WARN_ON_ONCE(1); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (test_bit(DLM_IFL_ENDOFLIFE_BIT, &lkb->lkb_iflags)) { 25362306a36Sopenharmony_ci /* N.B. spin_lock locks_spin, not asts_spin */ 25462306a36Sopenharmony_ci spin_lock(&proc->locks_spin); 25562306a36Sopenharmony_ci if (!list_empty(&lkb->lkb_ownqueue)) { 25662306a36Sopenharmony_ci list_del_init(&lkb->lkb_ownqueue); 25762306a36Sopenharmony_ci dlm_put_lkb(lkb); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci spin_unlock(&proc->locks_spin); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci out: 26262306a36Sopenharmony_ci spin_unlock(&ls->ls_clear_proc_locks); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int device_user_lock(struct dlm_user_proc *proc, 26662306a36Sopenharmony_ci struct dlm_lock_params *params) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct dlm_ls *ls; 26962306a36Sopenharmony_ci struct dlm_user_args *ua; 27062306a36Sopenharmony_ci uint32_t lkid; 27162306a36Sopenharmony_ci int error = -ENOMEM; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ls = dlm_find_lockspace_local(proc->lockspace); 27462306a36Sopenharmony_ci if (!ls) 27562306a36Sopenharmony_ci return -ENOENT; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!params->castaddr || !params->lksb) { 27862306a36Sopenharmony_ci error = -EINVAL; 27962306a36Sopenharmony_ci goto out; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS); 28362306a36Sopenharmony_ci if (!ua) 28462306a36Sopenharmony_ci goto out; 28562306a36Sopenharmony_ci ua->proc = proc; 28662306a36Sopenharmony_ci ua->user_lksb = params->lksb; 28762306a36Sopenharmony_ci ua->castparam = params->castparam; 28862306a36Sopenharmony_ci ua->castaddr = params->castaddr; 28962306a36Sopenharmony_ci ua->bastparam = params->bastparam; 29062306a36Sopenharmony_ci ua->bastaddr = params->bastaddr; 29162306a36Sopenharmony_ci ua->xid = params->xid; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (params->flags & DLM_LKF_CONVERT) { 29462306a36Sopenharmony_ci error = dlm_user_convert(ls, ua, 29562306a36Sopenharmony_ci params->mode, params->flags, 29662306a36Sopenharmony_ci params->lkid, params->lvb); 29762306a36Sopenharmony_ci } else if (params->flags & DLM_LKF_ORPHAN) { 29862306a36Sopenharmony_ci error = dlm_user_adopt_orphan(ls, ua, 29962306a36Sopenharmony_ci params->mode, params->flags, 30062306a36Sopenharmony_ci params->name, params->namelen, 30162306a36Sopenharmony_ci &lkid); 30262306a36Sopenharmony_ci if (!error) 30362306a36Sopenharmony_ci error = lkid; 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci error = dlm_user_request(ls, ua, 30662306a36Sopenharmony_ci params->mode, params->flags, 30762306a36Sopenharmony_ci params->name, params->namelen); 30862306a36Sopenharmony_ci if (!error) 30962306a36Sopenharmony_ci error = ua->lksb.sb_lkid; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci out: 31262306a36Sopenharmony_ci dlm_put_lockspace(ls); 31362306a36Sopenharmony_ci return error; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int device_user_unlock(struct dlm_user_proc *proc, 31762306a36Sopenharmony_ci struct dlm_lock_params *params) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct dlm_ls *ls; 32062306a36Sopenharmony_ci struct dlm_user_args *ua; 32162306a36Sopenharmony_ci int error = -ENOMEM; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ls = dlm_find_lockspace_local(proc->lockspace); 32462306a36Sopenharmony_ci if (!ls) 32562306a36Sopenharmony_ci return -ENOENT; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS); 32862306a36Sopenharmony_ci if (!ua) 32962306a36Sopenharmony_ci goto out; 33062306a36Sopenharmony_ci ua->proc = proc; 33162306a36Sopenharmony_ci ua->user_lksb = params->lksb; 33262306a36Sopenharmony_ci ua->castparam = params->castparam; 33362306a36Sopenharmony_ci ua->castaddr = params->castaddr; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (params->flags & DLM_LKF_CANCEL) 33662306a36Sopenharmony_ci error = dlm_user_cancel(ls, ua, params->flags, params->lkid); 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci error = dlm_user_unlock(ls, ua, params->flags, params->lkid, 33962306a36Sopenharmony_ci params->lvb); 34062306a36Sopenharmony_ci out: 34162306a36Sopenharmony_ci dlm_put_lockspace(ls); 34262306a36Sopenharmony_ci return error; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int device_user_deadlock(struct dlm_user_proc *proc, 34662306a36Sopenharmony_ci struct dlm_lock_params *params) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct dlm_ls *ls; 34962306a36Sopenharmony_ci int error; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ls = dlm_find_lockspace_local(proc->lockspace); 35262306a36Sopenharmony_ci if (!ls) 35362306a36Sopenharmony_ci return -ENOENT; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci error = dlm_user_deadlock(ls, params->flags, params->lkid); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci dlm_put_lockspace(ls); 35862306a36Sopenharmony_ci return error; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int dlm_device_register(struct dlm_ls *ls, char *name) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci int error, len; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* The device is already registered. This happens when the 36662306a36Sopenharmony_ci lockspace is created multiple times from userspace. */ 36762306a36Sopenharmony_ci if (ls->ls_device.name) 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci error = -ENOMEM; 37162306a36Sopenharmony_ci len = strlen(name) + strlen(name_prefix) + 2; 37262306a36Sopenharmony_ci ls->ls_device.name = kzalloc(len, GFP_NOFS); 37362306a36Sopenharmony_ci if (!ls->ls_device.name) 37462306a36Sopenharmony_ci goto fail; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix, 37762306a36Sopenharmony_ci name); 37862306a36Sopenharmony_ci ls->ls_device.fops = &device_fops; 37962306a36Sopenharmony_ci ls->ls_device.minor = MISC_DYNAMIC_MINOR; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci error = misc_register(&ls->ls_device); 38262306a36Sopenharmony_ci if (error) { 38362306a36Sopenharmony_ci kfree(ls->ls_device.name); 38462306a36Sopenharmony_ci /* this has to be set to NULL 38562306a36Sopenharmony_ci * to avoid a double-free in dlm_device_deregister 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci ls->ls_device.name = NULL; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_cifail: 39062306a36Sopenharmony_ci return error; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ciint dlm_device_deregister(struct dlm_ls *ls) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci /* The device is not registered. This happens when the lockspace 39662306a36Sopenharmony_ci was never used from userspace, or when device_create_lockspace() 39762306a36Sopenharmony_ci calls dlm_release_lockspace() after the register fails. */ 39862306a36Sopenharmony_ci if (!ls->ls_device.name) 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci misc_deregister(&ls->ls_device); 40262306a36Sopenharmony_ci kfree(ls->ls_device.name); 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int device_user_purge(struct dlm_user_proc *proc, 40762306a36Sopenharmony_ci struct dlm_purge_params *params) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct dlm_ls *ls; 41062306a36Sopenharmony_ci int error; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci ls = dlm_find_lockspace_local(proc->lockspace); 41362306a36Sopenharmony_ci if (!ls) 41462306a36Sopenharmony_ci return -ENOENT; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci error = dlm_user_purge(ls, proc, params->nodeid, params->pid); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci dlm_put_lockspace(ls); 41962306a36Sopenharmony_ci return error; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int device_create_lockspace(struct dlm_lspace_params *params) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci dlm_lockspace_t *lockspace; 42562306a36Sopenharmony_ci struct dlm_ls *ls; 42662306a36Sopenharmony_ci int error; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 42962306a36Sopenharmony_ci return -EPERM; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci error = dlm_new_user_lockspace(params->name, dlm_config.ci_cluster_name, 43262306a36Sopenharmony_ci params->flags, DLM_USER_LVB_LEN, NULL, 43362306a36Sopenharmony_ci NULL, NULL, &lockspace); 43462306a36Sopenharmony_ci if (error) 43562306a36Sopenharmony_ci return error; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ls = dlm_find_lockspace_local(lockspace); 43862306a36Sopenharmony_ci if (!ls) 43962306a36Sopenharmony_ci return -ENOENT; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci error = dlm_device_register(ls, params->name); 44262306a36Sopenharmony_ci dlm_put_lockspace(ls); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (error) 44562306a36Sopenharmony_ci dlm_release_lockspace(lockspace, 0); 44662306a36Sopenharmony_ci else 44762306a36Sopenharmony_ci error = ls->ls_device.minor; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return error; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int device_remove_lockspace(struct dlm_lspace_params *params) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci dlm_lockspace_t *lockspace; 45562306a36Sopenharmony_ci struct dlm_ls *ls; 45662306a36Sopenharmony_ci int error, force = 0; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 45962306a36Sopenharmony_ci return -EPERM; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ls = dlm_find_lockspace_device(params->minor); 46262306a36Sopenharmony_ci if (!ls) 46362306a36Sopenharmony_ci return -ENOENT; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (params->flags & DLM_USER_LSFLG_FORCEFREE) 46662306a36Sopenharmony_ci force = 2; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci lockspace = ls->ls_local_handle; 46962306a36Sopenharmony_ci dlm_put_lockspace(ls); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* The final dlm_release_lockspace waits for references to go to 47262306a36Sopenharmony_ci zero, so all processes will need to close their device for the 47362306a36Sopenharmony_ci ls before the release will proceed. release also calls the 47462306a36Sopenharmony_ci device_deregister above. Converting a positive return value 47562306a36Sopenharmony_ci from release to zero means that userspace won't know when its 47662306a36Sopenharmony_ci release was the final one, but it shouldn't need to know. */ 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci error = dlm_release_lockspace(lockspace, force); 47962306a36Sopenharmony_ci if (error > 0) 48062306a36Sopenharmony_ci error = 0; 48162306a36Sopenharmony_ci return error; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/* Check the user's version matches ours */ 48562306a36Sopenharmony_cistatic int check_version(struct dlm_write_request *req) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci if (req->version[0] != DLM_DEVICE_VERSION_MAJOR || 48862306a36Sopenharmony_ci (req->version[0] == DLM_DEVICE_VERSION_MAJOR && 48962306a36Sopenharmony_ci req->version[1] > DLM_DEVICE_VERSION_MINOR)) { 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci printk(KERN_DEBUG "dlm: process %s (%d) version mismatch " 49262306a36Sopenharmony_ci "user (%d.%d.%d) kernel (%d.%d.%d)\n", 49362306a36Sopenharmony_ci current->comm, 49462306a36Sopenharmony_ci task_pid_nr(current), 49562306a36Sopenharmony_ci req->version[0], 49662306a36Sopenharmony_ci req->version[1], 49762306a36Sopenharmony_ci req->version[2], 49862306a36Sopenharmony_ci DLM_DEVICE_VERSION_MAJOR, 49962306a36Sopenharmony_ci DLM_DEVICE_VERSION_MINOR, 50062306a36Sopenharmony_ci DLM_DEVICE_VERSION_PATCH); 50162306a36Sopenharmony_ci return -EINVAL; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci return 0; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* 50762306a36Sopenharmony_ci * device_write 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * device_user_lock 51062306a36Sopenharmony_ci * dlm_user_request -> request_lock 51162306a36Sopenharmony_ci * dlm_user_convert -> convert_lock 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * device_user_unlock 51462306a36Sopenharmony_ci * dlm_user_unlock -> unlock_lock 51562306a36Sopenharmony_ci * dlm_user_cancel -> cancel_lock 51662306a36Sopenharmony_ci * 51762306a36Sopenharmony_ci * device_create_lockspace 51862306a36Sopenharmony_ci * dlm_new_lockspace 51962306a36Sopenharmony_ci * 52062306a36Sopenharmony_ci * device_remove_lockspace 52162306a36Sopenharmony_ci * dlm_release_lockspace 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/* a write to a lockspace device is a lock or unlock request, a write 52562306a36Sopenharmony_ci to the control device is to create/remove a lockspace */ 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic ssize_t device_write(struct file *file, const char __user *buf, 52862306a36Sopenharmony_ci size_t count, loff_t *ppos) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct dlm_user_proc *proc = file->private_data; 53162306a36Sopenharmony_ci struct dlm_write_request *kbuf; 53262306a36Sopenharmony_ci int error; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 53562306a36Sopenharmony_ci if (count < sizeof(struct dlm_write_request32)) 53662306a36Sopenharmony_ci#else 53762306a36Sopenharmony_ci if (count < sizeof(struct dlm_write_request)) 53862306a36Sopenharmony_ci#endif 53962306a36Sopenharmony_ci return -EINVAL; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * can't compare against COMPAT/dlm_write_request32 because 54362306a36Sopenharmony_ci * we don't yet know if is64bit is zero 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN) 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci kbuf = memdup_user_nul(buf, count); 54962306a36Sopenharmony_ci if (IS_ERR(kbuf)) 55062306a36Sopenharmony_ci return PTR_ERR(kbuf); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (check_version(kbuf)) { 55362306a36Sopenharmony_ci error = -EBADE; 55462306a36Sopenharmony_ci goto out_free; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 55862306a36Sopenharmony_ci if (!kbuf->is64bit) { 55962306a36Sopenharmony_ci struct dlm_write_request32 *k32buf; 56062306a36Sopenharmony_ci int namelen = 0; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (count > sizeof(struct dlm_write_request32)) 56362306a36Sopenharmony_ci namelen = count - sizeof(struct dlm_write_request32); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci k32buf = (struct dlm_write_request32 *)kbuf; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* add 1 after namelen so that the name string is terminated */ 56862306a36Sopenharmony_ci kbuf = kzalloc(sizeof(struct dlm_write_request) + namelen + 1, 56962306a36Sopenharmony_ci GFP_NOFS); 57062306a36Sopenharmony_ci if (!kbuf) { 57162306a36Sopenharmony_ci kfree(k32buf); 57262306a36Sopenharmony_ci return -ENOMEM; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (proc) 57662306a36Sopenharmony_ci set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci compat_input(kbuf, k32buf, namelen); 57962306a36Sopenharmony_ci kfree(k32buf); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci#endif 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* do we really need this? can a write happen after a close? */ 58462306a36Sopenharmony_ci if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) && 58562306a36Sopenharmony_ci (proc && test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))) { 58662306a36Sopenharmony_ci error = -EINVAL; 58762306a36Sopenharmony_ci goto out_free; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci error = -EINVAL; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci switch (kbuf->cmd) 59362306a36Sopenharmony_ci { 59462306a36Sopenharmony_ci case DLM_USER_LOCK: 59562306a36Sopenharmony_ci if (!proc) { 59662306a36Sopenharmony_ci log_print("no locking on control device"); 59762306a36Sopenharmony_ci goto out_free; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci error = device_user_lock(proc, &kbuf->i.lock); 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci case DLM_USER_UNLOCK: 60362306a36Sopenharmony_ci if (!proc) { 60462306a36Sopenharmony_ci log_print("no locking on control device"); 60562306a36Sopenharmony_ci goto out_free; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci error = device_user_unlock(proc, &kbuf->i.lock); 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci case DLM_USER_DEADLOCK: 61162306a36Sopenharmony_ci if (!proc) { 61262306a36Sopenharmony_ci log_print("no locking on control device"); 61362306a36Sopenharmony_ci goto out_free; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci error = device_user_deadlock(proc, &kbuf->i.lock); 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci case DLM_USER_CREATE_LOCKSPACE: 61962306a36Sopenharmony_ci if (proc) { 62062306a36Sopenharmony_ci log_print("create/remove only on control device"); 62162306a36Sopenharmony_ci goto out_free; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci error = device_create_lockspace(&kbuf->i.lspace); 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci case DLM_USER_REMOVE_LOCKSPACE: 62762306a36Sopenharmony_ci if (proc) { 62862306a36Sopenharmony_ci log_print("create/remove only on control device"); 62962306a36Sopenharmony_ci goto out_free; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci error = device_remove_lockspace(&kbuf->i.lspace); 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci case DLM_USER_PURGE: 63562306a36Sopenharmony_ci if (!proc) { 63662306a36Sopenharmony_ci log_print("no locking on control device"); 63762306a36Sopenharmony_ci goto out_free; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci error = device_user_purge(proc, &kbuf->i.purge); 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci default: 64362306a36Sopenharmony_ci log_print("Unknown command passed to DLM device : %d\n", 64462306a36Sopenharmony_ci kbuf->cmd); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci out_free: 64862306a36Sopenharmony_ci kfree(kbuf); 64962306a36Sopenharmony_ci return error; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/* Every process that opens the lockspace device has its own "proc" structure 65362306a36Sopenharmony_ci hanging off the open file that's used to keep track of locks owned by the 65462306a36Sopenharmony_ci process and asts that need to be delivered to the process. */ 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int device_open(struct inode *inode, struct file *file) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct dlm_user_proc *proc; 65962306a36Sopenharmony_ci struct dlm_ls *ls; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ls = dlm_find_lockspace_device(iminor(inode)); 66262306a36Sopenharmony_ci if (!ls) 66362306a36Sopenharmony_ci return -ENOENT; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci proc = kzalloc(sizeof(struct dlm_user_proc), GFP_NOFS); 66662306a36Sopenharmony_ci if (!proc) { 66762306a36Sopenharmony_ci dlm_put_lockspace(ls); 66862306a36Sopenharmony_ci return -ENOMEM; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci proc->lockspace = ls->ls_local_handle; 67262306a36Sopenharmony_ci INIT_LIST_HEAD(&proc->asts); 67362306a36Sopenharmony_ci INIT_LIST_HEAD(&proc->locks); 67462306a36Sopenharmony_ci INIT_LIST_HEAD(&proc->unlocking); 67562306a36Sopenharmony_ci spin_lock_init(&proc->asts_spin); 67662306a36Sopenharmony_ci spin_lock_init(&proc->locks_spin); 67762306a36Sopenharmony_ci init_waitqueue_head(&proc->wait); 67862306a36Sopenharmony_ci file->private_data = proc; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int device_close(struct inode *inode, struct file *file) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct dlm_user_proc *proc = file->private_data; 68662306a36Sopenharmony_ci struct dlm_ls *ls; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci ls = dlm_find_lockspace_local(proc->lockspace); 68962306a36Sopenharmony_ci if (!ls) 69062306a36Sopenharmony_ci return -ENOENT; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci dlm_clear_proc_locks(ls, proc); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* at this point no more lkb's should exist for this lockspace, 69762306a36Sopenharmony_ci so there's no chance of dlm_user_add_ast() being called and 69862306a36Sopenharmony_ci looking for lkb->ua->proc */ 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci kfree(proc); 70162306a36Sopenharmony_ci file->private_data = NULL; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci dlm_put_lockspace(ls); 70462306a36Sopenharmony_ci dlm_put_lockspace(ls); /* for the find in device_open() */ 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* FIXME: AUTOFREE: if this ls is no longer used do 70762306a36Sopenharmony_ci device_remove_lockspace() */ 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int copy_result_to_user(struct dlm_user_args *ua, int compat, 71362306a36Sopenharmony_ci uint32_t flags, int mode, int copy_lvb, 71462306a36Sopenharmony_ci char __user *buf, size_t count) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 71762306a36Sopenharmony_ci struct dlm_lock_result32 result32; 71862306a36Sopenharmony_ci#endif 71962306a36Sopenharmony_ci struct dlm_lock_result result; 72062306a36Sopenharmony_ci void *resultptr; 72162306a36Sopenharmony_ci int error=0; 72262306a36Sopenharmony_ci int len; 72362306a36Sopenharmony_ci int struct_len; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci memset(&result, 0, sizeof(struct dlm_lock_result)); 72662306a36Sopenharmony_ci result.version[0] = DLM_DEVICE_VERSION_MAJOR; 72762306a36Sopenharmony_ci result.version[1] = DLM_DEVICE_VERSION_MINOR; 72862306a36Sopenharmony_ci result.version[2] = DLM_DEVICE_VERSION_PATCH; 72962306a36Sopenharmony_ci memcpy(&result.lksb, &ua->lksb, offsetof(struct dlm_lksb, sb_lvbptr)); 73062306a36Sopenharmony_ci result.user_lksb = ua->user_lksb; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* FIXME: dlm1 provides for the user's bastparam/addr to not be updated 73362306a36Sopenharmony_ci in a conversion unless the conversion is successful. See code 73462306a36Sopenharmony_ci in dlm_user_convert() for updating ua from ua_tmp. OpenVMS, though, 73562306a36Sopenharmony_ci notes that a new blocking AST address and parameter are set even if 73662306a36Sopenharmony_ci the conversion fails, so maybe we should just do that. */ 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (flags & DLM_CB_BAST) { 73962306a36Sopenharmony_ci result.user_astaddr = ua->bastaddr; 74062306a36Sopenharmony_ci result.user_astparam = ua->bastparam; 74162306a36Sopenharmony_ci result.bast_mode = mode; 74262306a36Sopenharmony_ci } else { 74362306a36Sopenharmony_ci result.user_astaddr = ua->castaddr; 74462306a36Sopenharmony_ci result.user_astparam = ua->castparam; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 74862306a36Sopenharmony_ci if (compat) 74962306a36Sopenharmony_ci len = sizeof(struct dlm_lock_result32); 75062306a36Sopenharmony_ci else 75162306a36Sopenharmony_ci#endif 75262306a36Sopenharmony_ci len = sizeof(struct dlm_lock_result); 75362306a36Sopenharmony_ci struct_len = len; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* copy lvb to userspace if there is one, it's been updated, and 75662306a36Sopenharmony_ci the user buffer has space for it */ 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (copy_lvb && ua->lksb.sb_lvbptr && count >= len + DLM_USER_LVB_LEN) { 75962306a36Sopenharmony_ci if (copy_to_user(buf+len, ua->lksb.sb_lvbptr, 76062306a36Sopenharmony_ci DLM_USER_LVB_LEN)) { 76162306a36Sopenharmony_ci error = -EFAULT; 76262306a36Sopenharmony_ci goto out; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci result.lvb_offset = len; 76662306a36Sopenharmony_ci len += DLM_USER_LVB_LEN; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci result.length = len; 77062306a36Sopenharmony_ci resultptr = &result; 77162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 77262306a36Sopenharmony_ci if (compat) { 77362306a36Sopenharmony_ci compat_output(&result, &result32); 77462306a36Sopenharmony_ci resultptr = &result32; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci#endif 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (copy_to_user(buf, resultptr, struct_len)) 77962306a36Sopenharmony_ci error = -EFAULT; 78062306a36Sopenharmony_ci else 78162306a36Sopenharmony_ci error = len; 78262306a36Sopenharmony_ci out: 78362306a36Sopenharmony_ci return error; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic int copy_version_to_user(char __user *buf, size_t count) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct dlm_device_version ver; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci memset(&ver, 0, sizeof(struct dlm_device_version)); 79162306a36Sopenharmony_ci ver.version[0] = DLM_DEVICE_VERSION_MAJOR; 79262306a36Sopenharmony_ci ver.version[1] = DLM_DEVICE_VERSION_MINOR; 79362306a36Sopenharmony_ci ver.version[2] = DLM_DEVICE_VERSION_PATCH; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (copy_to_user(buf, &ver, sizeof(struct dlm_device_version))) 79662306a36Sopenharmony_ci return -EFAULT; 79762306a36Sopenharmony_ci return sizeof(struct dlm_device_version); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/* a read returns a single ast described in a struct dlm_lock_result */ 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic ssize_t device_read(struct file *file, char __user *buf, size_t count, 80362306a36Sopenharmony_ci loff_t *ppos) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct dlm_user_proc *proc = file->private_data; 80662306a36Sopenharmony_ci struct dlm_lkb *lkb; 80762306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 80862306a36Sopenharmony_ci struct dlm_callback *cb; 80962306a36Sopenharmony_ci int rv, copy_lvb = 0; 81062306a36Sopenharmony_ci int old_mode, new_mode; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (count == sizeof(struct dlm_device_version)) { 81362306a36Sopenharmony_ci rv = copy_version_to_user(buf, count); 81462306a36Sopenharmony_ci return rv; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (!proc) { 81862306a36Sopenharmony_ci log_print("non-version read from control device %zu", count); 81962306a36Sopenharmony_ci return -EINVAL; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 82362306a36Sopenharmony_ci if (count < sizeof(struct dlm_lock_result32)) 82462306a36Sopenharmony_ci#else 82562306a36Sopenharmony_ci if (count < sizeof(struct dlm_lock_result)) 82662306a36Sopenharmony_ci#endif 82762306a36Sopenharmony_ci return -EINVAL; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci try_another: 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* do we really need this? can a read happen after a close? */ 83262306a36Sopenharmony_ci if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)) 83362306a36Sopenharmony_ci return -EINVAL; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci spin_lock(&proc->asts_spin); 83662306a36Sopenharmony_ci if (list_empty(&proc->asts)) { 83762306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 83862306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 83962306a36Sopenharmony_ci return -EAGAIN; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci add_wait_queue(&proc->wait, &wait); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci repeat: 84562306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 84662306a36Sopenharmony_ci if (list_empty(&proc->asts) && !signal_pending(current)) { 84762306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 84862306a36Sopenharmony_ci schedule(); 84962306a36Sopenharmony_ci spin_lock(&proc->asts_spin); 85062306a36Sopenharmony_ci goto repeat; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 85362306a36Sopenharmony_ci remove_wait_queue(&proc->wait, &wait); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (signal_pending(current)) { 85662306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 85762306a36Sopenharmony_ci return -ERESTARTSYS; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* if we empty lkb_callbacks, we don't want to unlock the spinlock 86262306a36Sopenharmony_ci without removing lkb_cb_list; so empty lkb_cb_list is always 86362306a36Sopenharmony_ci consistent with empty lkb_callbacks */ 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci lkb = list_first_entry(&proc->asts, struct dlm_lkb, lkb_cb_list); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci /* rem_lkb_callback sets a new lkb_last_cast */ 86862306a36Sopenharmony_ci old_mode = lkb->lkb_last_cast->mode; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci rv = dlm_dequeue_lkb_callback(lkb, &cb); 87162306a36Sopenharmony_ci switch (rv) { 87262306a36Sopenharmony_ci case DLM_DEQUEUE_CALLBACK_EMPTY: 87362306a36Sopenharmony_ci /* this shouldn't happen; lkb should have been removed from 87462306a36Sopenharmony_ci * list when last item was dequeued 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ci log_print("dlm_rem_lkb_callback empty %x", lkb->lkb_id); 87762306a36Sopenharmony_ci list_del_init(&lkb->lkb_cb_list); 87862306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 87962306a36Sopenharmony_ci /* removes ref for proc->asts, may cause lkb to be freed */ 88062306a36Sopenharmony_ci dlm_put_lkb(lkb); 88162306a36Sopenharmony_ci WARN_ON_ONCE(1); 88262306a36Sopenharmony_ci goto try_another; 88362306a36Sopenharmony_ci case DLM_DEQUEUE_CALLBACK_LAST: 88462306a36Sopenharmony_ci list_del_init(&lkb->lkb_cb_list); 88562306a36Sopenharmony_ci clear_bit(DLM_IFL_CB_PENDING_BIT, &lkb->lkb_iflags); 88662306a36Sopenharmony_ci break; 88762306a36Sopenharmony_ci case DLM_DEQUEUE_CALLBACK_SUCCESS: 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci default: 89062306a36Sopenharmony_ci WARN_ON_ONCE(1); 89162306a36Sopenharmony_ci break; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (cb->flags & DLM_CB_BAST) { 89662306a36Sopenharmony_ci trace_dlm_bast(lkb->lkb_resource->res_ls, lkb, cb->mode); 89762306a36Sopenharmony_ci } else if (cb->flags & DLM_CB_CAST) { 89862306a36Sopenharmony_ci new_mode = cb->mode; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (!cb->sb_status && lkb->lkb_lksb->sb_lvbptr && 90162306a36Sopenharmony_ci dlm_lvb_operations[old_mode + 1][new_mode + 1]) 90262306a36Sopenharmony_ci copy_lvb = 1; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci lkb->lkb_lksb->sb_status = cb->sb_status; 90562306a36Sopenharmony_ci lkb->lkb_lksb->sb_flags = cb->sb_flags; 90662306a36Sopenharmony_ci trace_dlm_ast(lkb->lkb_resource->res_ls, lkb); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci rv = copy_result_to_user(lkb->lkb_ua, 91062306a36Sopenharmony_ci test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), 91162306a36Sopenharmony_ci cb->flags, cb->mode, copy_lvb, buf, count); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci kref_put(&cb->ref, dlm_release_callback); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* removes ref for proc->asts, may cause lkb to be freed */ 91662306a36Sopenharmony_ci if (rv == DLM_DEQUEUE_CALLBACK_LAST) 91762306a36Sopenharmony_ci dlm_put_lkb(lkb); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return rv; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic __poll_t device_poll(struct file *file, poll_table *wait) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct dlm_user_proc *proc = file->private_data; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci poll_wait(file, &proc->wait, wait); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci spin_lock(&proc->asts_spin); 92962306a36Sopenharmony_ci if (!list_empty(&proc->asts)) { 93062306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 93162306a36Sopenharmony_ci return EPOLLIN | EPOLLRDNORM; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci spin_unlock(&proc->asts_spin); 93462306a36Sopenharmony_ci return 0; 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ciint dlm_user_daemon_available(void) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci /* dlm_controld hasn't started (or, has started, but not 94062306a36Sopenharmony_ci properly populated configfs) */ 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (!dlm_our_nodeid()) 94362306a36Sopenharmony_ci return 0; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* This is to deal with versions of dlm_controld that don't 94662306a36Sopenharmony_ci know about the monitor device. We assume that if the 94762306a36Sopenharmony_ci dlm_controld was started (above), but the monitor device 94862306a36Sopenharmony_ci was never opened, that it's an old version. dlm_controld 94962306a36Sopenharmony_ci should open the monitor device before populating configfs. */ 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (dlm_monitor_unused) 95262306a36Sopenharmony_ci return 1; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return atomic_read(&dlm_monitor_opened) ? 1 : 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int ctl_device_open(struct inode *inode, struct file *file) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci file->private_data = NULL; 96062306a36Sopenharmony_ci return 0; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic int ctl_device_close(struct inode *inode, struct file *file) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci return 0; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic int monitor_device_open(struct inode *inode, struct file *file) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci atomic_inc(&dlm_monitor_opened); 97162306a36Sopenharmony_ci dlm_monitor_unused = 0; 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int monitor_device_close(struct inode *inode, struct file *file) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci if (atomic_dec_and_test(&dlm_monitor_opened)) 97862306a36Sopenharmony_ci dlm_stop_lockspaces(); 97962306a36Sopenharmony_ci return 0; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic const struct file_operations device_fops = { 98362306a36Sopenharmony_ci .open = device_open, 98462306a36Sopenharmony_ci .release = device_close, 98562306a36Sopenharmony_ci .read = device_read, 98662306a36Sopenharmony_ci .write = device_write, 98762306a36Sopenharmony_ci .poll = device_poll, 98862306a36Sopenharmony_ci .owner = THIS_MODULE, 98962306a36Sopenharmony_ci .llseek = noop_llseek, 99062306a36Sopenharmony_ci}; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic const struct file_operations ctl_device_fops = { 99362306a36Sopenharmony_ci .open = ctl_device_open, 99462306a36Sopenharmony_ci .release = ctl_device_close, 99562306a36Sopenharmony_ci .read = device_read, 99662306a36Sopenharmony_ci .write = device_write, 99762306a36Sopenharmony_ci .owner = THIS_MODULE, 99862306a36Sopenharmony_ci .llseek = noop_llseek, 99962306a36Sopenharmony_ci}; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic struct miscdevice ctl_device = { 100262306a36Sopenharmony_ci .name = "dlm-control", 100362306a36Sopenharmony_ci .fops = &ctl_device_fops, 100462306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 100562306a36Sopenharmony_ci}; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic const struct file_operations monitor_device_fops = { 100862306a36Sopenharmony_ci .open = monitor_device_open, 100962306a36Sopenharmony_ci .release = monitor_device_close, 101062306a36Sopenharmony_ci .owner = THIS_MODULE, 101162306a36Sopenharmony_ci .llseek = noop_llseek, 101262306a36Sopenharmony_ci}; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic struct miscdevice monitor_device = { 101562306a36Sopenharmony_ci .name = "dlm-monitor", 101662306a36Sopenharmony_ci .fops = &monitor_device_fops, 101762306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 101862306a36Sopenharmony_ci}; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ciint __init dlm_user_init(void) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci int error; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci atomic_set(&dlm_monitor_opened, 0); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci error = misc_register(&ctl_device); 102762306a36Sopenharmony_ci if (error) { 102862306a36Sopenharmony_ci log_print("misc_register failed for control device"); 102962306a36Sopenharmony_ci goto out; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci error = misc_register(&monitor_device); 103362306a36Sopenharmony_ci if (error) { 103462306a36Sopenharmony_ci log_print("misc_register failed for monitor device"); 103562306a36Sopenharmony_ci misc_deregister(&ctl_device); 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci out: 103862306a36Sopenharmony_ci return error; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_civoid dlm_user_exit(void) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci misc_deregister(&ctl_device); 104462306a36Sopenharmony_ci misc_deregister(&monitor_device); 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 1047