18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2005-2008 Red Hat, Inc. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/fs.h> 78c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/poll.h> 98c2ecf20Sopenharmony_ci#include <linux/dlm.h> 108c2ecf20Sopenharmony_ci#include <linux/dlm_plock.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "dlm_internal.h" 148c2ecf20Sopenharmony_ci#include "lockspace.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic spinlock_t ops_lock; 178c2ecf20Sopenharmony_cistatic struct list_head send_list; 188c2ecf20Sopenharmony_cistatic struct list_head recv_list; 198c2ecf20Sopenharmony_cistatic wait_queue_head_t send_wq; 208c2ecf20Sopenharmony_cistatic wait_queue_head_t recv_wq; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct plock_async_data { 238c2ecf20Sopenharmony_ci void *fl; 248c2ecf20Sopenharmony_ci void *file; 258c2ecf20Sopenharmony_ci struct file_lock flc; 268c2ecf20Sopenharmony_ci int (*callback)(struct file_lock *fl, int result); 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct plock_op { 308c2ecf20Sopenharmony_ci struct list_head list; 318c2ecf20Sopenharmony_ci int done; 328c2ecf20Sopenharmony_ci struct dlm_plock_info info; 338c2ecf20Sopenharmony_ci /* if set indicates async handling */ 348c2ecf20Sopenharmony_ci struct plock_async_data *data; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline void set_version(struct dlm_plock_info *info) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci info->version[0] = DLM_PLOCK_VERSION_MAJOR; 408c2ecf20Sopenharmony_ci info->version[1] = DLM_PLOCK_VERSION_MINOR; 418c2ecf20Sopenharmony_ci info->version[2] = DLM_PLOCK_VERSION_PATCH; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int check_version(struct dlm_plock_info *info) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci if ((DLM_PLOCK_VERSION_MAJOR != info->version[0]) || 478c2ecf20Sopenharmony_ci (DLM_PLOCK_VERSION_MINOR < info->version[1])) { 488c2ecf20Sopenharmony_ci log_print("plock device version mismatch: " 498c2ecf20Sopenharmony_ci "kernel (%u.%u.%u), user (%u.%u.%u)", 508c2ecf20Sopenharmony_ci DLM_PLOCK_VERSION_MAJOR, 518c2ecf20Sopenharmony_ci DLM_PLOCK_VERSION_MINOR, 528c2ecf20Sopenharmony_ci DLM_PLOCK_VERSION_PATCH, 538c2ecf20Sopenharmony_ci info->version[0], 548c2ecf20Sopenharmony_ci info->version[1], 558c2ecf20Sopenharmony_ci info->version[2]); 568c2ecf20Sopenharmony_ci return -EINVAL; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void dlm_release_plock_op(struct plock_op *op) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci kfree(op->data); 648c2ecf20Sopenharmony_ci kfree(op); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void send_op(struct plock_op *op) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci set_version(&op->info); 708c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&op->list); 718c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 728c2ecf20Sopenharmony_ci list_add_tail(&op->list, &send_list); 738c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 748c2ecf20Sopenharmony_ci wake_up(&send_wq); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* If a process was killed while waiting for the only plock on a file, 788c2ecf20Sopenharmony_ci locks_remove_posix will not see any lock on the file so it won't 798c2ecf20Sopenharmony_ci send an unlock-close to us to pass on to userspace to clean up the 808c2ecf20Sopenharmony_ci abandoned waiter. So, we have to insert the unlock-close when the 818c2ecf20Sopenharmony_ci lock call is interrupted. */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void do_unlock_close(const struct dlm_plock_info *info) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct plock_op *op; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci op = kzalloc(sizeof(*op), GFP_NOFS); 888c2ecf20Sopenharmony_ci if (!op) 898c2ecf20Sopenharmony_ci return; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci op->info.optype = DLM_PLOCK_OP_UNLOCK; 928c2ecf20Sopenharmony_ci op->info.pid = info->pid; 938c2ecf20Sopenharmony_ci op->info.fsid = info->fsid; 948c2ecf20Sopenharmony_ci op->info.number = info->number; 958c2ecf20Sopenharmony_ci op->info.start = 0; 968c2ecf20Sopenharmony_ci op->info.end = OFFSET_MAX; 978c2ecf20Sopenharmony_ci op->info.owner = info->owner; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci op->info.flags |= DLM_PLOCK_FL_CLOSE; 1008c2ecf20Sopenharmony_ci send_op(op); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ciint dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file, 1048c2ecf20Sopenharmony_ci int cmd, struct file_lock *fl) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct plock_async_data *op_data; 1078c2ecf20Sopenharmony_ci struct dlm_ls *ls; 1088c2ecf20Sopenharmony_ci struct plock_op *op; 1098c2ecf20Sopenharmony_ci int rv; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ls = dlm_find_lockspace_local(lockspace); 1128c2ecf20Sopenharmony_ci if (!ls) 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci op = kzalloc(sizeof(*op), GFP_NOFS); 1168c2ecf20Sopenharmony_ci if (!op) { 1178c2ecf20Sopenharmony_ci rv = -ENOMEM; 1188c2ecf20Sopenharmony_ci goto out; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci op->info.optype = DLM_PLOCK_OP_LOCK; 1228c2ecf20Sopenharmony_ci op->info.pid = fl->fl_pid; 1238c2ecf20Sopenharmony_ci op->info.ex = (fl->fl_type == F_WRLCK); 1248c2ecf20Sopenharmony_ci op->info.wait = IS_SETLKW(cmd); 1258c2ecf20Sopenharmony_ci op->info.fsid = ls->ls_global_id; 1268c2ecf20Sopenharmony_ci op->info.number = number; 1278c2ecf20Sopenharmony_ci op->info.start = fl->fl_start; 1288c2ecf20Sopenharmony_ci op->info.end = fl->fl_end; 1298c2ecf20Sopenharmony_ci /* async handling */ 1308c2ecf20Sopenharmony_ci if (fl->fl_lmops && fl->fl_lmops->lm_grant) { 1318c2ecf20Sopenharmony_ci op_data = kzalloc(sizeof(*op_data), GFP_NOFS); 1328c2ecf20Sopenharmony_ci if (!op_data) { 1338c2ecf20Sopenharmony_ci dlm_release_plock_op(op); 1348c2ecf20Sopenharmony_ci rv = -ENOMEM; 1358c2ecf20Sopenharmony_ci goto out; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* fl_owner is lockd which doesn't distinguish 1398c2ecf20Sopenharmony_ci processes on the nfs client */ 1408c2ecf20Sopenharmony_ci op->info.owner = (__u64) fl->fl_pid; 1418c2ecf20Sopenharmony_ci op_data->callback = fl->fl_lmops->lm_grant; 1428c2ecf20Sopenharmony_ci locks_init_lock(&op_data->flc); 1438c2ecf20Sopenharmony_ci locks_copy_lock(&op_data->flc, fl); 1448c2ecf20Sopenharmony_ci op_data->fl = fl; 1458c2ecf20Sopenharmony_ci op_data->file = file; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci op->data = op_data; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci send_op(op); 1508c2ecf20Sopenharmony_ci rv = FILE_LOCK_DEFERRED; 1518c2ecf20Sopenharmony_ci goto out; 1528c2ecf20Sopenharmony_ci } else { 1538c2ecf20Sopenharmony_ci op->info.owner = (__u64)(long) fl->fl_owner; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci send_op(op); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci rv = wait_event_killable(recv_wq, (op->done != 0)); 1598c2ecf20Sopenharmony_ci if (rv == -ERESTARTSYS) { 1608c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 1618c2ecf20Sopenharmony_ci list_del(&op->list); 1628c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 1638c2ecf20Sopenharmony_ci log_debug(ls, "%s: wait interrupted %x %llx pid %d", 1648c2ecf20Sopenharmony_ci __func__, ls->ls_global_id, 1658c2ecf20Sopenharmony_ci (unsigned long long)number, op->info.pid); 1668c2ecf20Sopenharmony_ci dlm_release_plock_op(op); 1678c2ecf20Sopenharmony_ci do_unlock_close(&op->info); 1688c2ecf20Sopenharmony_ci goto out; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 1728c2ecf20Sopenharmony_ci if (!list_empty(&op->list)) { 1738c2ecf20Sopenharmony_ci log_error(ls, "dlm_posix_lock: op on list %llx", 1748c2ecf20Sopenharmony_ci (unsigned long long)number); 1758c2ecf20Sopenharmony_ci list_del(&op->list); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci rv = op->info.rv; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (!rv) { 1828c2ecf20Sopenharmony_ci if (locks_lock_file_wait(file, fl) < 0) 1838c2ecf20Sopenharmony_ci log_error(ls, "dlm_posix_lock: vfs lock error %llx", 1848c2ecf20Sopenharmony_ci (unsigned long long)number); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci dlm_release_plock_op(op); 1888c2ecf20Sopenharmony_ciout: 1898c2ecf20Sopenharmony_ci dlm_put_lockspace(ls); 1908c2ecf20Sopenharmony_ci return rv; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dlm_posix_lock); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* Returns failure iff a successful lock operation should be canceled */ 1958c2ecf20Sopenharmony_cistatic int dlm_plock_callback(struct plock_op *op) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct plock_async_data *op_data = op->data; 1988c2ecf20Sopenharmony_ci struct file *file; 1998c2ecf20Sopenharmony_ci struct file_lock *fl; 2008c2ecf20Sopenharmony_ci struct file_lock *flc; 2018c2ecf20Sopenharmony_ci int (*notify)(struct file_lock *fl, int result) = NULL; 2028c2ecf20Sopenharmony_ci int rv = 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 2058c2ecf20Sopenharmony_ci if (!list_empty(&op->list)) { 2068c2ecf20Sopenharmony_ci log_print("dlm_plock_callback: op on list %llx", 2078c2ecf20Sopenharmony_ci (unsigned long long)op->info.number); 2088c2ecf20Sopenharmony_ci list_del(&op->list); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* check if the following 2 are still valid or make a copy */ 2138c2ecf20Sopenharmony_ci file = op_data->file; 2148c2ecf20Sopenharmony_ci flc = &op_data->flc; 2158c2ecf20Sopenharmony_ci fl = op_data->fl; 2168c2ecf20Sopenharmony_ci notify = op_data->callback; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (op->info.rv) { 2198c2ecf20Sopenharmony_ci notify(fl, op->info.rv); 2208c2ecf20Sopenharmony_ci goto out; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* got fs lock; bookkeep locally as well: */ 2248c2ecf20Sopenharmony_ci flc->fl_flags &= ~FL_SLEEP; 2258c2ecf20Sopenharmony_ci if (posix_lock_file(file, flc, NULL)) { 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * This can only happen in the case of kmalloc() failure. 2288c2ecf20Sopenharmony_ci * The filesystem's own lock is the authoritative lock, 2298c2ecf20Sopenharmony_ci * so a failure to get the lock locally is not a disaster. 2308c2ecf20Sopenharmony_ci * As long as the fs cannot reliably cancel locks (especially 2318c2ecf20Sopenharmony_ci * in a low-memory situation), we're better off ignoring 2328c2ecf20Sopenharmony_ci * this failure than trying to recover. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci log_print("dlm_plock_callback: vfs lock error %llx file %p fl %p", 2358c2ecf20Sopenharmony_ci (unsigned long long)op->info.number, file, fl); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci rv = notify(fl, 0); 2398c2ecf20Sopenharmony_ci if (rv) { 2408c2ecf20Sopenharmony_ci /* XXX: We need to cancel the fs lock here: */ 2418c2ecf20Sopenharmony_ci log_print("dlm_plock_callback: lock granted after lock request " 2428c2ecf20Sopenharmony_ci "failed; dangling lock!\n"); 2438c2ecf20Sopenharmony_ci goto out; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciout: 2478c2ecf20Sopenharmony_ci dlm_release_plock_op(op); 2488c2ecf20Sopenharmony_ci return rv; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ciint dlm_posix_unlock(dlm_lockspace_t *lockspace, u64 number, struct file *file, 2528c2ecf20Sopenharmony_ci struct file_lock *fl) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct dlm_ls *ls; 2558c2ecf20Sopenharmony_ci struct plock_op *op; 2568c2ecf20Sopenharmony_ci int rv; 2578c2ecf20Sopenharmony_ci unsigned char fl_flags = fl->fl_flags; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ls = dlm_find_lockspace_local(lockspace); 2608c2ecf20Sopenharmony_ci if (!ls) 2618c2ecf20Sopenharmony_ci return -EINVAL; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci op = kzalloc(sizeof(*op), GFP_NOFS); 2648c2ecf20Sopenharmony_ci if (!op) { 2658c2ecf20Sopenharmony_ci rv = -ENOMEM; 2668c2ecf20Sopenharmony_ci goto out; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* cause the vfs unlock to return ENOENT if lock is not found */ 2708c2ecf20Sopenharmony_ci fl->fl_flags |= FL_EXISTS; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci rv = locks_lock_file_wait(file, fl); 2738c2ecf20Sopenharmony_ci if (rv == -ENOENT) { 2748c2ecf20Sopenharmony_ci rv = 0; 2758c2ecf20Sopenharmony_ci goto out_free; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci if (rv < 0) { 2788c2ecf20Sopenharmony_ci log_error(ls, "dlm_posix_unlock: vfs unlock error %d %llx", 2798c2ecf20Sopenharmony_ci rv, (unsigned long long)number); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci op->info.optype = DLM_PLOCK_OP_UNLOCK; 2838c2ecf20Sopenharmony_ci op->info.pid = fl->fl_pid; 2848c2ecf20Sopenharmony_ci op->info.fsid = ls->ls_global_id; 2858c2ecf20Sopenharmony_ci op->info.number = number; 2868c2ecf20Sopenharmony_ci op->info.start = fl->fl_start; 2878c2ecf20Sopenharmony_ci op->info.end = fl->fl_end; 2888c2ecf20Sopenharmony_ci if (fl->fl_lmops && fl->fl_lmops->lm_grant) 2898c2ecf20Sopenharmony_ci op->info.owner = (__u64) fl->fl_pid; 2908c2ecf20Sopenharmony_ci else 2918c2ecf20Sopenharmony_ci op->info.owner = (__u64)(long) fl->fl_owner; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (fl->fl_flags & FL_CLOSE) { 2948c2ecf20Sopenharmony_ci op->info.flags |= DLM_PLOCK_FL_CLOSE; 2958c2ecf20Sopenharmony_ci send_op(op); 2968c2ecf20Sopenharmony_ci rv = 0; 2978c2ecf20Sopenharmony_ci goto out; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci send_op(op); 3018c2ecf20Sopenharmony_ci wait_event(recv_wq, (op->done != 0)); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 3048c2ecf20Sopenharmony_ci if (!list_empty(&op->list)) { 3058c2ecf20Sopenharmony_ci log_error(ls, "dlm_posix_unlock: op on list %llx", 3068c2ecf20Sopenharmony_ci (unsigned long long)number); 3078c2ecf20Sopenharmony_ci list_del(&op->list); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci rv = op->info.rv; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (rv == -ENOENT) 3148c2ecf20Sopenharmony_ci rv = 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ciout_free: 3178c2ecf20Sopenharmony_ci dlm_release_plock_op(op); 3188c2ecf20Sopenharmony_ciout: 3198c2ecf20Sopenharmony_ci dlm_put_lockspace(ls); 3208c2ecf20Sopenharmony_ci fl->fl_flags = fl_flags; 3218c2ecf20Sopenharmony_ci return rv; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dlm_posix_unlock); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ciint dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file, 3268c2ecf20Sopenharmony_ci struct file_lock *fl) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct dlm_ls *ls; 3298c2ecf20Sopenharmony_ci struct plock_op *op; 3308c2ecf20Sopenharmony_ci int rv; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ls = dlm_find_lockspace_local(lockspace); 3338c2ecf20Sopenharmony_ci if (!ls) 3348c2ecf20Sopenharmony_ci return -EINVAL; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci op = kzalloc(sizeof(*op), GFP_NOFS); 3378c2ecf20Sopenharmony_ci if (!op) { 3388c2ecf20Sopenharmony_ci rv = -ENOMEM; 3398c2ecf20Sopenharmony_ci goto out; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci op->info.optype = DLM_PLOCK_OP_GET; 3438c2ecf20Sopenharmony_ci op->info.pid = fl->fl_pid; 3448c2ecf20Sopenharmony_ci op->info.ex = (fl->fl_type == F_WRLCK); 3458c2ecf20Sopenharmony_ci op->info.fsid = ls->ls_global_id; 3468c2ecf20Sopenharmony_ci op->info.number = number; 3478c2ecf20Sopenharmony_ci op->info.start = fl->fl_start; 3488c2ecf20Sopenharmony_ci op->info.end = fl->fl_end; 3498c2ecf20Sopenharmony_ci if (fl->fl_lmops && fl->fl_lmops->lm_grant) 3508c2ecf20Sopenharmony_ci op->info.owner = (__u64) fl->fl_pid; 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci op->info.owner = (__u64)(long) fl->fl_owner; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci send_op(op); 3558c2ecf20Sopenharmony_ci wait_event(recv_wq, (op->done != 0)); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 3588c2ecf20Sopenharmony_ci if (!list_empty(&op->list)) { 3598c2ecf20Sopenharmony_ci log_error(ls, "dlm_posix_get: op on list %llx", 3608c2ecf20Sopenharmony_ci (unsigned long long)number); 3618c2ecf20Sopenharmony_ci list_del(&op->list); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* info.rv from userspace is 1 for conflict, 0 for no-conflict, 3668c2ecf20Sopenharmony_ci -ENOENT if there are no locks on the file */ 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci rv = op->info.rv; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci fl->fl_type = F_UNLCK; 3718c2ecf20Sopenharmony_ci if (rv == -ENOENT) 3728c2ecf20Sopenharmony_ci rv = 0; 3738c2ecf20Sopenharmony_ci else if (rv > 0) { 3748c2ecf20Sopenharmony_ci locks_init_lock(fl); 3758c2ecf20Sopenharmony_ci fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK; 3768c2ecf20Sopenharmony_ci fl->fl_flags = FL_POSIX; 3778c2ecf20Sopenharmony_ci fl->fl_pid = op->info.pid; 3788c2ecf20Sopenharmony_ci if (op->info.nodeid != dlm_our_nodeid()) 3798c2ecf20Sopenharmony_ci fl->fl_pid = -fl->fl_pid; 3808c2ecf20Sopenharmony_ci fl->fl_start = op->info.start; 3818c2ecf20Sopenharmony_ci fl->fl_end = op->info.end; 3828c2ecf20Sopenharmony_ci rv = 0; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci dlm_release_plock_op(op); 3868c2ecf20Sopenharmony_ciout: 3878c2ecf20Sopenharmony_ci dlm_put_lockspace(ls); 3888c2ecf20Sopenharmony_ci return rv; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dlm_posix_get); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/* a read copies out one plock request from the send list */ 3938c2ecf20Sopenharmony_cistatic ssize_t dev_read(struct file *file, char __user *u, size_t count, 3948c2ecf20Sopenharmony_ci loff_t *ppos) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct dlm_plock_info info; 3978c2ecf20Sopenharmony_ci struct plock_op *op = NULL; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (count < sizeof(info)) 4008c2ecf20Sopenharmony_ci return -EINVAL; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 4038c2ecf20Sopenharmony_ci if (!list_empty(&send_list)) { 4048c2ecf20Sopenharmony_ci op = list_entry(send_list.next, struct plock_op, list); 4058c2ecf20Sopenharmony_ci if (op->info.flags & DLM_PLOCK_FL_CLOSE) 4068c2ecf20Sopenharmony_ci list_del(&op->list); 4078c2ecf20Sopenharmony_ci else 4088c2ecf20Sopenharmony_ci list_move_tail(&op->list, &recv_list); 4098c2ecf20Sopenharmony_ci memcpy(&info, &op->info, sizeof(info)); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (!op) 4148c2ecf20Sopenharmony_ci return -EAGAIN; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* there is no need to get a reply from userspace for unlocks 4178c2ecf20Sopenharmony_ci that were generated by the vfs cleaning up for a close 4188c2ecf20Sopenharmony_ci (the process did not make an unlock call). */ 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (op->info.flags & DLM_PLOCK_FL_CLOSE) 4218c2ecf20Sopenharmony_ci dlm_release_plock_op(op); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (copy_to_user(u, &info, sizeof(info))) 4248c2ecf20Sopenharmony_ci return -EFAULT; 4258c2ecf20Sopenharmony_ci return sizeof(info); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci/* a write copies in one plock result that should match a plock_op 4298c2ecf20Sopenharmony_ci on the recv list */ 4308c2ecf20Sopenharmony_cistatic ssize_t dev_write(struct file *file, const char __user *u, size_t count, 4318c2ecf20Sopenharmony_ci loff_t *ppos) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct plock_op *op = NULL, *iter; 4348c2ecf20Sopenharmony_ci struct dlm_plock_info info; 4358c2ecf20Sopenharmony_ci int do_callback = 0; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (count != sizeof(info)) 4388c2ecf20Sopenharmony_ci return -EINVAL; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (copy_from_user(&info, u, sizeof(info))) 4418c2ecf20Sopenharmony_ci return -EFAULT; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (check_version(&info)) 4448c2ecf20Sopenharmony_ci return -EINVAL; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* 4478c2ecf20Sopenharmony_ci * The results for waiting ops (SETLKW) can be returned in any 4488c2ecf20Sopenharmony_ci * order, so match all fields to find the op. The results for 4498c2ecf20Sopenharmony_ci * non-waiting ops are returned in the order that they were sent 4508c2ecf20Sopenharmony_ci * to userspace, so match the result with the first non-waiting op. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 4538c2ecf20Sopenharmony_ci if (info.wait) { 4548c2ecf20Sopenharmony_ci list_for_each_entry(iter, &recv_list, list) { 4558c2ecf20Sopenharmony_ci if (iter->info.fsid == info.fsid && 4568c2ecf20Sopenharmony_ci iter->info.number == info.number && 4578c2ecf20Sopenharmony_ci iter->info.owner == info.owner && 4588c2ecf20Sopenharmony_ci iter->info.pid == info.pid && 4598c2ecf20Sopenharmony_ci iter->info.start == info.start && 4608c2ecf20Sopenharmony_ci iter->info.end == info.end && 4618c2ecf20Sopenharmony_ci iter->info.ex == info.ex && 4628c2ecf20Sopenharmony_ci iter->info.wait) { 4638c2ecf20Sopenharmony_ci op = iter; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci } else { 4688c2ecf20Sopenharmony_ci list_for_each_entry(iter, &recv_list, list) { 4698c2ecf20Sopenharmony_ci if (!iter->info.wait && 4708c2ecf20Sopenharmony_ci iter->info.fsid == info.fsid) { 4718c2ecf20Sopenharmony_ci op = iter; 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (op) { 4788c2ecf20Sopenharmony_ci /* Sanity check that op and info match. */ 4798c2ecf20Sopenharmony_ci if (info.wait) 4808c2ecf20Sopenharmony_ci WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK); 4818c2ecf20Sopenharmony_ci else 4828c2ecf20Sopenharmony_ci WARN_ON(op->info.number != info.number || 4838c2ecf20Sopenharmony_ci op->info.owner != info.owner || 4848c2ecf20Sopenharmony_ci op->info.optype != info.optype); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci list_del_init(&op->list); 4878c2ecf20Sopenharmony_ci memcpy(&op->info, &info, sizeof(info)); 4888c2ecf20Sopenharmony_ci if (op->data) 4898c2ecf20Sopenharmony_ci do_callback = 1; 4908c2ecf20Sopenharmony_ci else 4918c2ecf20Sopenharmony_ci op->done = 1; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (op) { 4968c2ecf20Sopenharmony_ci if (do_callback) 4978c2ecf20Sopenharmony_ci dlm_plock_callback(op); 4988c2ecf20Sopenharmony_ci else 4998c2ecf20Sopenharmony_ci wake_up(&recv_wq); 5008c2ecf20Sopenharmony_ci } else 5018c2ecf20Sopenharmony_ci log_print("%s: no op %x %llx", __func__, 5028c2ecf20Sopenharmony_ci info.fsid, (unsigned long long)info.number); 5038c2ecf20Sopenharmony_ci return count; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic __poll_t dev_poll(struct file *file, poll_table *wait) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci __poll_t mask = 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci poll_wait(file, &send_wq, wait); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci spin_lock(&ops_lock); 5138c2ecf20Sopenharmony_ci if (!list_empty(&send_list)) 5148c2ecf20Sopenharmony_ci mask = EPOLLIN | EPOLLRDNORM; 5158c2ecf20Sopenharmony_ci spin_unlock(&ops_lock); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return mask; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic const struct file_operations dev_fops = { 5218c2ecf20Sopenharmony_ci .read = dev_read, 5228c2ecf20Sopenharmony_ci .write = dev_write, 5238c2ecf20Sopenharmony_ci .poll = dev_poll, 5248c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5258c2ecf20Sopenharmony_ci .llseek = noop_llseek, 5268c2ecf20Sopenharmony_ci}; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic struct miscdevice plock_dev_misc = { 5298c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 5308c2ecf20Sopenharmony_ci .name = DLM_PLOCK_MISC_NAME, 5318c2ecf20Sopenharmony_ci .fops = &dev_fops 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ciint dlm_plock_init(void) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci int rv; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci spin_lock_init(&ops_lock); 5398c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&send_list); 5408c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&recv_list); 5418c2ecf20Sopenharmony_ci init_waitqueue_head(&send_wq); 5428c2ecf20Sopenharmony_ci init_waitqueue_head(&recv_wq); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci rv = misc_register(&plock_dev_misc); 5458c2ecf20Sopenharmony_ci if (rv) 5468c2ecf20Sopenharmony_ci log_print("dlm_plock_init: misc_register failed %d", rv); 5478c2ecf20Sopenharmony_ci return rv; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_civoid dlm_plock_exit(void) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci misc_deregister(&plock_dev_misc); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 555