162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005-2008 Red Hat, Inc. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/fs.h> 762306a36Sopenharmony_ci#include <linux/filelock.h> 862306a36Sopenharmony_ci#include <linux/miscdevice.h> 962306a36Sopenharmony_ci#include <linux/poll.h> 1062306a36Sopenharmony_ci#include <linux/dlm.h> 1162306a36Sopenharmony_ci#include <linux/dlm_plock.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <trace/events/dlm.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "dlm_internal.h" 1762306a36Sopenharmony_ci#include "lockspace.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ops_lock); 2062306a36Sopenharmony_cistatic LIST_HEAD(send_list); 2162306a36Sopenharmony_cistatic LIST_HEAD(recv_list); 2262306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(send_wq); 2362306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(recv_wq); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct plock_async_data { 2662306a36Sopenharmony_ci void *fl; 2762306a36Sopenharmony_ci void *file; 2862306a36Sopenharmony_ci struct file_lock flc; 2962306a36Sopenharmony_ci int (*callback)(struct file_lock *fl, int result); 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct plock_op { 3362306a36Sopenharmony_ci struct list_head list; 3462306a36Sopenharmony_ci int done; 3562306a36Sopenharmony_ci struct dlm_plock_info info; 3662306a36Sopenharmony_ci /* if set indicates async handling */ 3762306a36Sopenharmony_ci struct plock_async_data *data; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic inline void set_version(struct dlm_plock_info *info) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci info->version[0] = DLM_PLOCK_VERSION_MAJOR; 4362306a36Sopenharmony_ci info->version[1] = DLM_PLOCK_VERSION_MINOR; 4462306a36Sopenharmony_ci info->version[2] = DLM_PLOCK_VERSION_PATCH; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct plock_op *plock_lookup_waiter(const struct dlm_plock_info *info) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct plock_op *op = NULL, *iter; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci list_for_each_entry(iter, &recv_list, list) { 5262306a36Sopenharmony_ci if (iter->info.fsid == info->fsid && 5362306a36Sopenharmony_ci iter->info.number == info->number && 5462306a36Sopenharmony_ci iter->info.owner == info->owner && 5562306a36Sopenharmony_ci iter->info.pid == info->pid && 5662306a36Sopenharmony_ci iter->info.start == info->start && 5762306a36Sopenharmony_ci iter->info.end == info->end && 5862306a36Sopenharmony_ci iter->info.ex == info->ex && 5962306a36Sopenharmony_ci iter->info.wait) { 6062306a36Sopenharmony_ci op = iter; 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return op; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int check_version(struct dlm_plock_info *info) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci if ((DLM_PLOCK_VERSION_MAJOR != info->version[0]) || 7162306a36Sopenharmony_ci (DLM_PLOCK_VERSION_MINOR < info->version[1])) { 7262306a36Sopenharmony_ci log_print("plock device version mismatch: " 7362306a36Sopenharmony_ci "kernel (%u.%u.%u), user (%u.%u.%u)", 7462306a36Sopenharmony_ci DLM_PLOCK_VERSION_MAJOR, 7562306a36Sopenharmony_ci DLM_PLOCK_VERSION_MINOR, 7662306a36Sopenharmony_ci DLM_PLOCK_VERSION_PATCH, 7762306a36Sopenharmony_ci info->version[0], 7862306a36Sopenharmony_ci info->version[1], 7962306a36Sopenharmony_ci info->version[2]); 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic void dlm_release_plock_op(struct plock_op *op) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci kfree(op->data); 8862306a36Sopenharmony_ci kfree(op); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void send_op(struct plock_op *op) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci set_version(&op->info); 9462306a36Sopenharmony_ci spin_lock(&ops_lock); 9562306a36Sopenharmony_ci list_add_tail(&op->list, &send_list); 9662306a36Sopenharmony_ci spin_unlock(&ops_lock); 9762306a36Sopenharmony_ci wake_up(&send_wq); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int do_lock_cancel(const struct dlm_plock_info *orig_info) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct plock_op *op; 10362306a36Sopenharmony_ci int rv; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci op = kzalloc(sizeof(*op), GFP_NOFS); 10662306a36Sopenharmony_ci if (!op) 10762306a36Sopenharmony_ci return -ENOMEM; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci op->info = *orig_info; 11062306a36Sopenharmony_ci op->info.optype = DLM_PLOCK_OP_CANCEL; 11162306a36Sopenharmony_ci op->info.wait = 0; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci send_op(op); 11462306a36Sopenharmony_ci wait_event(recv_wq, (op->done != 0)); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci rv = op->info.rv; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci dlm_release_plock_op(op); 11962306a36Sopenharmony_ci return rv; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciint dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file, 12362306a36Sopenharmony_ci int cmd, struct file_lock *fl) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct plock_async_data *op_data; 12662306a36Sopenharmony_ci struct dlm_ls *ls; 12762306a36Sopenharmony_ci struct plock_op *op; 12862306a36Sopenharmony_ci int rv; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ls = dlm_find_lockspace_local(lockspace); 13162306a36Sopenharmony_ci if (!ls) 13262306a36Sopenharmony_ci return -EINVAL; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci op = kzalloc(sizeof(*op), GFP_NOFS); 13562306a36Sopenharmony_ci if (!op) { 13662306a36Sopenharmony_ci rv = -ENOMEM; 13762306a36Sopenharmony_ci goto out; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci op->info.optype = DLM_PLOCK_OP_LOCK; 14162306a36Sopenharmony_ci op->info.pid = fl->fl_pid; 14262306a36Sopenharmony_ci op->info.ex = (fl->fl_type == F_WRLCK); 14362306a36Sopenharmony_ci op->info.wait = IS_SETLKW(cmd); 14462306a36Sopenharmony_ci op->info.fsid = ls->ls_global_id; 14562306a36Sopenharmony_ci op->info.number = number; 14662306a36Sopenharmony_ci op->info.start = fl->fl_start; 14762306a36Sopenharmony_ci op->info.end = fl->fl_end; 14862306a36Sopenharmony_ci /* async handling */ 14962306a36Sopenharmony_ci if (fl->fl_lmops && fl->fl_lmops->lm_grant) { 15062306a36Sopenharmony_ci op_data = kzalloc(sizeof(*op_data), GFP_NOFS); 15162306a36Sopenharmony_ci if (!op_data) { 15262306a36Sopenharmony_ci dlm_release_plock_op(op); 15362306a36Sopenharmony_ci rv = -ENOMEM; 15462306a36Sopenharmony_ci goto out; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* fl_owner is lockd which doesn't distinguish 15862306a36Sopenharmony_ci processes on the nfs client */ 15962306a36Sopenharmony_ci op->info.owner = (__u64) fl->fl_pid; 16062306a36Sopenharmony_ci op_data->callback = fl->fl_lmops->lm_grant; 16162306a36Sopenharmony_ci locks_init_lock(&op_data->flc); 16262306a36Sopenharmony_ci locks_copy_lock(&op_data->flc, fl); 16362306a36Sopenharmony_ci op_data->fl = fl; 16462306a36Sopenharmony_ci op_data->file = file; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci op->data = op_data; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci send_op(op); 16962306a36Sopenharmony_ci rv = FILE_LOCK_DEFERRED; 17062306a36Sopenharmony_ci goto out; 17162306a36Sopenharmony_ci } else { 17262306a36Sopenharmony_ci op->info.owner = (__u64)(long) fl->fl_owner; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci send_op(op); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (op->info.wait) { 17862306a36Sopenharmony_ci rv = wait_event_interruptible(recv_wq, (op->done != 0)); 17962306a36Sopenharmony_ci if (rv == -ERESTARTSYS) { 18062306a36Sopenharmony_ci spin_lock(&ops_lock); 18162306a36Sopenharmony_ci /* recheck under ops_lock if we got a done != 0, 18262306a36Sopenharmony_ci * if so this interrupt case should be ignored 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci if (op->done != 0) { 18562306a36Sopenharmony_ci spin_unlock(&ops_lock); 18662306a36Sopenharmony_ci goto do_lock_wait; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci spin_unlock(&ops_lock); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci rv = do_lock_cancel(&op->info); 19162306a36Sopenharmony_ci switch (rv) { 19262306a36Sopenharmony_ci case 0: 19362306a36Sopenharmony_ci /* waiter was deleted in user space, answer will never come 19462306a36Sopenharmony_ci * remove original request. The original request must be 19562306a36Sopenharmony_ci * on recv_list because the answer of do_lock_cancel() 19662306a36Sopenharmony_ci * synchronized it. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci spin_lock(&ops_lock); 19962306a36Sopenharmony_ci list_del(&op->list); 20062306a36Sopenharmony_ci spin_unlock(&ops_lock); 20162306a36Sopenharmony_ci rv = -EINTR; 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci case -ENOENT: 20462306a36Sopenharmony_ci /* cancellation wasn't successful but op should be done */ 20562306a36Sopenharmony_ci fallthrough; 20662306a36Sopenharmony_ci default: 20762306a36Sopenharmony_ci /* internal error doing cancel we need to wait */ 20862306a36Sopenharmony_ci goto wait; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci log_debug(ls, "%s: wait interrupted %x %llx pid %d", 21262306a36Sopenharmony_ci __func__, ls->ls_global_id, 21362306a36Sopenharmony_ci (unsigned long long)number, op->info.pid); 21462306a36Sopenharmony_ci dlm_release_plock_op(op); 21562306a36Sopenharmony_ci goto out; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci } else { 21862306a36Sopenharmony_ciwait: 21962306a36Sopenharmony_ci wait_event(recv_wq, (op->done != 0)); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cido_lock_wait: 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci WARN_ON(!list_empty(&op->list)); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci rv = op->info.rv; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!rv) { 22962306a36Sopenharmony_ci if (locks_lock_file_wait(file, fl) < 0) 23062306a36Sopenharmony_ci log_error(ls, "dlm_posix_lock: vfs lock error %llx", 23162306a36Sopenharmony_ci (unsigned long long)number); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci dlm_release_plock_op(op); 23562306a36Sopenharmony_ciout: 23662306a36Sopenharmony_ci dlm_put_lockspace(ls); 23762306a36Sopenharmony_ci return rv; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dlm_posix_lock); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* Returns failure iff a successful lock operation should be canceled */ 24262306a36Sopenharmony_cistatic int dlm_plock_callback(struct plock_op *op) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct plock_async_data *op_data = op->data; 24562306a36Sopenharmony_ci struct file *file; 24662306a36Sopenharmony_ci struct file_lock *fl; 24762306a36Sopenharmony_ci struct file_lock *flc; 24862306a36Sopenharmony_ci int (*notify)(struct file_lock *fl, int result) = NULL; 24962306a36Sopenharmony_ci int rv = 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci WARN_ON(!list_empty(&op->list)); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* check if the following 2 are still valid or make a copy */ 25462306a36Sopenharmony_ci file = op_data->file; 25562306a36Sopenharmony_ci flc = &op_data->flc; 25662306a36Sopenharmony_ci fl = op_data->fl; 25762306a36Sopenharmony_ci notify = op_data->callback; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (op->info.rv) { 26062306a36Sopenharmony_ci notify(fl, op->info.rv); 26162306a36Sopenharmony_ci goto out; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* got fs lock; bookkeep locally as well: */ 26562306a36Sopenharmony_ci flc->fl_flags &= ~FL_SLEEP; 26662306a36Sopenharmony_ci if (posix_lock_file(file, flc, NULL)) { 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * This can only happen in the case of kmalloc() failure. 26962306a36Sopenharmony_ci * The filesystem's own lock is the authoritative lock, 27062306a36Sopenharmony_ci * so a failure to get the lock locally is not a disaster. 27162306a36Sopenharmony_ci * As long as the fs cannot reliably cancel locks (especially 27262306a36Sopenharmony_ci * in a low-memory situation), we're better off ignoring 27362306a36Sopenharmony_ci * this failure than trying to recover. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci log_print("dlm_plock_callback: vfs lock error %llx file %p fl %p", 27662306a36Sopenharmony_ci (unsigned long long)op->info.number, file, fl); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci rv = notify(fl, 0); 28062306a36Sopenharmony_ci if (rv) { 28162306a36Sopenharmony_ci /* XXX: We need to cancel the fs lock here: */ 28262306a36Sopenharmony_ci log_print("%s: lock granted after lock request failed; dangling lock!", 28362306a36Sopenharmony_ci __func__); 28462306a36Sopenharmony_ci goto out; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ciout: 28862306a36Sopenharmony_ci dlm_release_plock_op(op); 28962306a36Sopenharmony_ci return rv; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciint dlm_posix_unlock(dlm_lockspace_t *lockspace, u64 number, struct file *file, 29362306a36Sopenharmony_ci struct file_lock *fl) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct dlm_ls *ls; 29662306a36Sopenharmony_ci struct plock_op *op; 29762306a36Sopenharmony_ci int rv; 29862306a36Sopenharmony_ci unsigned char fl_flags = fl->fl_flags; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ls = dlm_find_lockspace_local(lockspace); 30162306a36Sopenharmony_ci if (!ls) 30262306a36Sopenharmony_ci return -EINVAL; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci op = kzalloc(sizeof(*op), GFP_NOFS); 30562306a36Sopenharmony_ci if (!op) { 30662306a36Sopenharmony_ci rv = -ENOMEM; 30762306a36Sopenharmony_ci goto out; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* cause the vfs unlock to return ENOENT if lock is not found */ 31162306a36Sopenharmony_ci fl->fl_flags |= FL_EXISTS; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci rv = locks_lock_file_wait(file, fl); 31462306a36Sopenharmony_ci if (rv == -ENOENT) { 31562306a36Sopenharmony_ci rv = 0; 31662306a36Sopenharmony_ci goto out_free; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci if (rv < 0) { 31962306a36Sopenharmony_ci log_error(ls, "dlm_posix_unlock: vfs unlock error %d %llx", 32062306a36Sopenharmony_ci rv, (unsigned long long)number); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci op->info.optype = DLM_PLOCK_OP_UNLOCK; 32462306a36Sopenharmony_ci op->info.pid = fl->fl_pid; 32562306a36Sopenharmony_ci op->info.fsid = ls->ls_global_id; 32662306a36Sopenharmony_ci op->info.number = number; 32762306a36Sopenharmony_ci op->info.start = fl->fl_start; 32862306a36Sopenharmony_ci op->info.end = fl->fl_end; 32962306a36Sopenharmony_ci if (fl->fl_lmops && fl->fl_lmops->lm_grant) 33062306a36Sopenharmony_ci op->info.owner = (__u64) fl->fl_pid; 33162306a36Sopenharmony_ci else 33262306a36Sopenharmony_ci op->info.owner = (__u64)(long) fl->fl_owner; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (fl->fl_flags & FL_CLOSE) { 33562306a36Sopenharmony_ci op->info.flags |= DLM_PLOCK_FL_CLOSE; 33662306a36Sopenharmony_ci send_op(op); 33762306a36Sopenharmony_ci rv = 0; 33862306a36Sopenharmony_ci goto out; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci send_op(op); 34262306a36Sopenharmony_ci wait_event(recv_wq, (op->done != 0)); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci WARN_ON(!list_empty(&op->list)); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci rv = op->info.rv; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (rv == -ENOENT) 34962306a36Sopenharmony_ci rv = 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ciout_free: 35262306a36Sopenharmony_ci dlm_release_plock_op(op); 35362306a36Sopenharmony_ciout: 35462306a36Sopenharmony_ci dlm_put_lockspace(ls); 35562306a36Sopenharmony_ci fl->fl_flags = fl_flags; 35662306a36Sopenharmony_ci return rv; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dlm_posix_unlock); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* 36162306a36Sopenharmony_ci * NOTE: This implementation can only handle async lock requests as nfs 36262306a36Sopenharmony_ci * do it. It cannot handle cancellation of a pending lock request sitting 36362306a36Sopenharmony_ci * in wait_event(), but for now only nfs is the only user local kernel 36462306a36Sopenharmony_ci * user. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ciint dlm_posix_cancel(dlm_lockspace_t *lockspace, u64 number, struct file *file, 36762306a36Sopenharmony_ci struct file_lock *fl) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct dlm_plock_info info; 37062306a36Sopenharmony_ci struct plock_op *op; 37162306a36Sopenharmony_ci struct dlm_ls *ls; 37262306a36Sopenharmony_ci int rv; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* this only works for async request for now and nfs is the only 37562306a36Sopenharmony_ci * kernel user right now. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci if (WARN_ON_ONCE(!fl->fl_lmops || !fl->fl_lmops->lm_grant)) 37862306a36Sopenharmony_ci return -EOPNOTSUPP; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ls = dlm_find_lockspace_local(lockspace); 38162306a36Sopenharmony_ci if (!ls) 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 38562306a36Sopenharmony_ci info.pid = fl->fl_pid; 38662306a36Sopenharmony_ci info.ex = (fl->fl_type == F_WRLCK); 38762306a36Sopenharmony_ci info.fsid = ls->ls_global_id; 38862306a36Sopenharmony_ci dlm_put_lockspace(ls); 38962306a36Sopenharmony_ci info.number = number; 39062306a36Sopenharmony_ci info.start = fl->fl_start; 39162306a36Sopenharmony_ci info.end = fl->fl_end; 39262306a36Sopenharmony_ci info.owner = (__u64)fl->fl_pid; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci rv = do_lock_cancel(&info); 39562306a36Sopenharmony_ci switch (rv) { 39662306a36Sopenharmony_ci case 0: 39762306a36Sopenharmony_ci spin_lock(&ops_lock); 39862306a36Sopenharmony_ci /* lock request to cancel must be on recv_list because 39962306a36Sopenharmony_ci * do_lock_cancel() synchronizes it. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci op = plock_lookup_waiter(&info); 40262306a36Sopenharmony_ci if (WARN_ON_ONCE(!op)) { 40362306a36Sopenharmony_ci spin_unlock(&ops_lock); 40462306a36Sopenharmony_ci rv = -ENOLCK; 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci list_del(&op->list); 40962306a36Sopenharmony_ci spin_unlock(&ops_lock); 41062306a36Sopenharmony_ci WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK); 41162306a36Sopenharmony_ci op->data->callback(op->data->fl, -EINTR); 41262306a36Sopenharmony_ci dlm_release_plock_op(op); 41362306a36Sopenharmony_ci rv = -EINTR; 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci case -ENOENT: 41662306a36Sopenharmony_ci /* if cancel wasn't successful we probably were to late 41762306a36Sopenharmony_ci * or it was a non-blocking lock request, so just unlock it. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci rv = dlm_posix_unlock(lockspace, number, file, fl); 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci default: 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return rv; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dlm_posix_cancel); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ciint dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file, 43062306a36Sopenharmony_ci struct file_lock *fl) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct dlm_ls *ls; 43362306a36Sopenharmony_ci struct plock_op *op; 43462306a36Sopenharmony_ci int rv; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci ls = dlm_find_lockspace_local(lockspace); 43762306a36Sopenharmony_ci if (!ls) 43862306a36Sopenharmony_ci return -EINVAL; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci op = kzalloc(sizeof(*op), GFP_NOFS); 44162306a36Sopenharmony_ci if (!op) { 44262306a36Sopenharmony_ci rv = -ENOMEM; 44362306a36Sopenharmony_ci goto out; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci op->info.optype = DLM_PLOCK_OP_GET; 44762306a36Sopenharmony_ci op->info.pid = fl->fl_pid; 44862306a36Sopenharmony_ci op->info.ex = (fl->fl_type == F_WRLCK); 44962306a36Sopenharmony_ci op->info.fsid = ls->ls_global_id; 45062306a36Sopenharmony_ci op->info.number = number; 45162306a36Sopenharmony_ci op->info.start = fl->fl_start; 45262306a36Sopenharmony_ci op->info.end = fl->fl_end; 45362306a36Sopenharmony_ci if (fl->fl_lmops && fl->fl_lmops->lm_grant) 45462306a36Sopenharmony_ci op->info.owner = (__u64) fl->fl_pid; 45562306a36Sopenharmony_ci else 45662306a36Sopenharmony_ci op->info.owner = (__u64)(long) fl->fl_owner; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci send_op(op); 45962306a36Sopenharmony_ci wait_event(recv_wq, (op->done != 0)); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci WARN_ON(!list_empty(&op->list)); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* info.rv from userspace is 1 for conflict, 0 for no-conflict, 46462306a36Sopenharmony_ci -ENOENT if there are no locks on the file */ 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci rv = op->info.rv; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci fl->fl_type = F_UNLCK; 46962306a36Sopenharmony_ci if (rv == -ENOENT) 47062306a36Sopenharmony_ci rv = 0; 47162306a36Sopenharmony_ci else if (rv > 0) { 47262306a36Sopenharmony_ci locks_init_lock(fl); 47362306a36Sopenharmony_ci fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK; 47462306a36Sopenharmony_ci fl->fl_flags = FL_POSIX; 47562306a36Sopenharmony_ci fl->fl_pid = op->info.pid; 47662306a36Sopenharmony_ci if (op->info.nodeid != dlm_our_nodeid()) 47762306a36Sopenharmony_ci fl->fl_pid = -fl->fl_pid; 47862306a36Sopenharmony_ci fl->fl_start = op->info.start; 47962306a36Sopenharmony_ci fl->fl_end = op->info.end; 48062306a36Sopenharmony_ci rv = 0; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci dlm_release_plock_op(op); 48462306a36Sopenharmony_ciout: 48562306a36Sopenharmony_ci dlm_put_lockspace(ls); 48662306a36Sopenharmony_ci return rv; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dlm_posix_get); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/* a read copies out one plock request from the send list */ 49162306a36Sopenharmony_cistatic ssize_t dev_read(struct file *file, char __user *u, size_t count, 49262306a36Sopenharmony_ci loff_t *ppos) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct dlm_plock_info info; 49562306a36Sopenharmony_ci struct plock_op *op = NULL; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (count < sizeof(info)) 49862306a36Sopenharmony_ci return -EINVAL; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci spin_lock(&ops_lock); 50162306a36Sopenharmony_ci if (!list_empty(&send_list)) { 50262306a36Sopenharmony_ci op = list_first_entry(&send_list, struct plock_op, list); 50362306a36Sopenharmony_ci if (op->info.flags & DLM_PLOCK_FL_CLOSE) 50462306a36Sopenharmony_ci list_del(&op->list); 50562306a36Sopenharmony_ci else 50662306a36Sopenharmony_ci list_move_tail(&op->list, &recv_list); 50762306a36Sopenharmony_ci memcpy(&info, &op->info, sizeof(info)); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci spin_unlock(&ops_lock); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (!op) 51262306a36Sopenharmony_ci return -EAGAIN; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci trace_dlm_plock_read(&info); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* there is no need to get a reply from userspace for unlocks 51762306a36Sopenharmony_ci that were generated by the vfs cleaning up for a close 51862306a36Sopenharmony_ci (the process did not make an unlock call). */ 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (op->info.flags & DLM_PLOCK_FL_CLOSE) 52162306a36Sopenharmony_ci dlm_release_plock_op(op); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (copy_to_user(u, &info, sizeof(info))) 52462306a36Sopenharmony_ci return -EFAULT; 52562306a36Sopenharmony_ci return sizeof(info); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/* a write copies in one plock result that should match a plock_op 52962306a36Sopenharmony_ci on the recv list */ 53062306a36Sopenharmony_cistatic ssize_t dev_write(struct file *file, const char __user *u, size_t count, 53162306a36Sopenharmony_ci loff_t *ppos) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct plock_op *op = NULL, *iter; 53462306a36Sopenharmony_ci struct dlm_plock_info info; 53562306a36Sopenharmony_ci int do_callback = 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (count != sizeof(info)) 53862306a36Sopenharmony_ci return -EINVAL; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (copy_from_user(&info, u, sizeof(info))) 54162306a36Sopenharmony_ci return -EFAULT; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci trace_dlm_plock_write(&info); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (check_version(&info)) 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 54962306a36Sopenharmony_ci * The results for waiting ops (SETLKW) can be returned in any 55062306a36Sopenharmony_ci * order, so match all fields to find the op. The results for 55162306a36Sopenharmony_ci * non-waiting ops are returned in the order that they were sent 55262306a36Sopenharmony_ci * to userspace, so match the result with the first non-waiting op. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci spin_lock(&ops_lock); 55562306a36Sopenharmony_ci if (info.wait) { 55662306a36Sopenharmony_ci op = plock_lookup_waiter(&info); 55762306a36Sopenharmony_ci } else { 55862306a36Sopenharmony_ci list_for_each_entry(iter, &recv_list, list) { 55962306a36Sopenharmony_ci if (!iter->info.wait && 56062306a36Sopenharmony_ci iter->info.fsid == info.fsid) { 56162306a36Sopenharmony_ci op = iter; 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (op) { 56862306a36Sopenharmony_ci /* Sanity check that op and info match. */ 56962306a36Sopenharmony_ci if (info.wait) 57062306a36Sopenharmony_ci WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK); 57162306a36Sopenharmony_ci else 57262306a36Sopenharmony_ci WARN_ON(op->info.number != info.number || 57362306a36Sopenharmony_ci op->info.owner != info.owner || 57462306a36Sopenharmony_ci op->info.optype != info.optype); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci list_del_init(&op->list); 57762306a36Sopenharmony_ci memcpy(&op->info, &info, sizeof(info)); 57862306a36Sopenharmony_ci if (op->data) 57962306a36Sopenharmony_ci do_callback = 1; 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci op->done = 1; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci spin_unlock(&ops_lock); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (op) { 58662306a36Sopenharmony_ci if (do_callback) 58762306a36Sopenharmony_ci dlm_plock_callback(op); 58862306a36Sopenharmony_ci else 58962306a36Sopenharmony_ci wake_up(&recv_wq); 59062306a36Sopenharmony_ci } else 59162306a36Sopenharmony_ci pr_debug("%s: no op %x %llx", __func__, 59262306a36Sopenharmony_ci info.fsid, (unsigned long long)info.number); 59362306a36Sopenharmony_ci return count; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic __poll_t dev_poll(struct file *file, poll_table *wait) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci __poll_t mask = 0; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci poll_wait(file, &send_wq, wait); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci spin_lock(&ops_lock); 60362306a36Sopenharmony_ci if (!list_empty(&send_list)) 60462306a36Sopenharmony_ci mask = EPOLLIN | EPOLLRDNORM; 60562306a36Sopenharmony_ci spin_unlock(&ops_lock); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return mask; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic const struct file_operations dev_fops = { 61162306a36Sopenharmony_ci .read = dev_read, 61262306a36Sopenharmony_ci .write = dev_write, 61362306a36Sopenharmony_ci .poll = dev_poll, 61462306a36Sopenharmony_ci .owner = THIS_MODULE, 61562306a36Sopenharmony_ci .llseek = noop_llseek, 61662306a36Sopenharmony_ci}; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic struct miscdevice plock_dev_misc = { 61962306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 62062306a36Sopenharmony_ci .name = DLM_PLOCK_MISC_NAME, 62162306a36Sopenharmony_ci .fops = &dev_fops 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ciint dlm_plock_init(void) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci int rv; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci rv = misc_register(&plock_dev_misc); 62962306a36Sopenharmony_ci if (rv) 63062306a36Sopenharmony_ci log_print("dlm_plock_init: misc_register failed %d", rv); 63162306a36Sopenharmony_ci return rv; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_civoid dlm_plock_exit(void) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci misc_deregister(&plock_dev_misc); 63762306a36Sopenharmony_ci WARN_ON(!list_empty(&send_list)); 63862306a36Sopenharmony_ci WARN_ON(!list_empty(&recv_list)); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 641