18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/lockd/clntproc.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * RPC procedures for the client side NLM implementation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/fs.h> 158c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h> 168c2ecf20Sopenharmony_ci#include <linux/utsname.h> 178c2ecf20Sopenharmony_ci#include <linux/freezer.h> 188c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h> 198c2ecf20Sopenharmony_ci#include <linux/sunrpc/svc.h> 208c2ecf20Sopenharmony_ci#include <linux/lockd/lockd.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define NLMDBG_FACILITY NLMDBG_CLIENT 238c2ecf20Sopenharmony_ci#define NLMCLNT_GRACE_WAIT (5*HZ) 248c2ecf20Sopenharmony_ci#define NLMCLNT_POLL_TIMEOUT (30*HZ) 258c2ecf20Sopenharmony_ci#define NLMCLNT_MAX_RETRIES 3 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int nlmclnt_test(struct nlm_rqst *, struct file_lock *); 288c2ecf20Sopenharmony_cistatic int nlmclnt_lock(struct nlm_rqst *, struct file_lock *); 298c2ecf20Sopenharmony_cistatic int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *); 308c2ecf20Sopenharmony_cistatic int nlm_stat_to_errno(__be32 stat); 318c2ecf20Sopenharmony_cistatic void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host); 328c2ecf20Sopenharmony_cistatic int nlmclnt_cancel(struct nlm_host *, int , struct file_lock *); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_unlock_ops; 358c2ecf20Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_cancel_ops; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Cookie counter for NLM requests 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic atomic_t nlm_cookie = ATOMIC_INIT(0x1234); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_civoid nlmclnt_next_cookie(struct nlm_cookie *c) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci u32 cookie = atomic_inc_return(&nlm_cookie); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci memcpy(c->data, &cookie, 4); 478c2ecf20Sopenharmony_ci c->len=4; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct nlm_lockowner * 518c2ecf20Sopenharmony_cinlmclnt_get_lockowner(struct nlm_lockowner *lockowner) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci refcount_inc(&lockowner->count); 548c2ecf20Sopenharmony_ci return lockowner; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void nlmclnt_put_lockowner(struct nlm_lockowner *lockowner) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock)) 608c2ecf20Sopenharmony_ci return; 618c2ecf20Sopenharmony_ci list_del(&lockowner->list); 628c2ecf20Sopenharmony_ci spin_unlock(&lockowner->host->h_lock); 638c2ecf20Sopenharmony_ci nlmclnt_release_host(lockowner->host); 648c2ecf20Sopenharmony_ci kfree(lockowner); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline int nlm_pidbusy(struct nlm_host *host, uint32_t pid) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct nlm_lockowner *lockowner; 708c2ecf20Sopenharmony_ci list_for_each_entry(lockowner, &host->h_lockowners, list) { 718c2ecf20Sopenharmony_ci if (lockowner->pid == pid) 728c2ecf20Sopenharmony_ci return -EBUSY; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline uint32_t __nlm_alloc_pid(struct nlm_host *host) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci uint32_t res; 808c2ecf20Sopenharmony_ci do { 818c2ecf20Sopenharmony_ci res = host->h_pidcount++; 828c2ecf20Sopenharmony_ci } while (nlm_pidbusy(host, res) < 0); 838c2ecf20Sopenharmony_ci return res; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic struct nlm_lockowner *__nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct nlm_lockowner *lockowner; 898c2ecf20Sopenharmony_ci list_for_each_entry(lockowner, &host->h_lockowners, list) { 908c2ecf20Sopenharmony_ci if (lockowner->owner != owner) 918c2ecf20Sopenharmony_ci continue; 928c2ecf20Sopenharmony_ci return nlmclnt_get_lockowner(lockowner); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci return NULL; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct nlm_lockowner *nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct nlm_lockowner *res, *new = NULL; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci spin_lock(&host->h_lock); 1028c2ecf20Sopenharmony_ci res = __nlmclnt_find_lockowner(host, owner); 1038c2ecf20Sopenharmony_ci if (res == NULL) { 1048c2ecf20Sopenharmony_ci spin_unlock(&host->h_lock); 1058c2ecf20Sopenharmony_ci new = kmalloc(sizeof(*new), GFP_KERNEL); 1068c2ecf20Sopenharmony_ci spin_lock(&host->h_lock); 1078c2ecf20Sopenharmony_ci res = __nlmclnt_find_lockowner(host, owner); 1088c2ecf20Sopenharmony_ci if (res == NULL && new != NULL) { 1098c2ecf20Sopenharmony_ci res = new; 1108c2ecf20Sopenharmony_ci refcount_set(&new->count, 1); 1118c2ecf20Sopenharmony_ci new->owner = owner; 1128c2ecf20Sopenharmony_ci new->pid = __nlm_alloc_pid(host); 1138c2ecf20Sopenharmony_ci new->host = nlm_get_host(host); 1148c2ecf20Sopenharmony_ci list_add(&new->list, &host->h_lockowners); 1158c2ecf20Sopenharmony_ci new = NULL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci spin_unlock(&host->h_lock); 1198c2ecf20Sopenharmony_ci kfree(new); 1208c2ecf20Sopenharmony_ci return res; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* 1248c2ecf20Sopenharmony_ci * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_cistatic void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct nlm_args *argp = &req->a_args; 1298c2ecf20Sopenharmony_ci struct nlm_lock *lock = &argp->lock; 1308c2ecf20Sopenharmony_ci char *nodename = req->a_host->h_rpcclnt->cl_nodename; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci nlmclnt_next_cookie(&argp->cookie); 1338c2ecf20Sopenharmony_ci memcpy(&lock->fh, NFS_FH(locks_inode(fl->fl_file)), sizeof(struct nfs_fh)); 1348c2ecf20Sopenharmony_ci lock->caller = nodename; 1358c2ecf20Sopenharmony_ci lock->oh.data = req->a_owner; 1368c2ecf20Sopenharmony_ci lock->oh.len = snprintf(req->a_owner, sizeof(req->a_owner), "%u@%s", 1378c2ecf20Sopenharmony_ci (unsigned int)fl->fl_u.nfs_fl.owner->pid, 1388c2ecf20Sopenharmony_ci nodename); 1398c2ecf20Sopenharmony_ci lock->svid = fl->fl_u.nfs_fl.owner->pid; 1408c2ecf20Sopenharmony_ci lock->fl.fl_start = fl->fl_start; 1418c2ecf20Sopenharmony_ci lock->fl.fl_end = fl->fl_end; 1428c2ecf20Sopenharmony_ci lock->fl.fl_type = fl->fl_type; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void nlmclnt_release_lockargs(struct nlm_rqst *req) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci WARN_ON_ONCE(req->a_args.lock.fl.fl_ops != NULL); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * nlmclnt_proc - Perform a single client-side lock request 1528c2ecf20Sopenharmony_ci * @host: address of a valid nlm_host context representing the NLM server 1538c2ecf20Sopenharmony_ci * @cmd: fcntl-style file lock operation to perform 1548c2ecf20Sopenharmony_ci * @fl: address of arguments for the lock operation 1558c2ecf20Sopenharmony_ci * @data: address of data to be sent to callback operations 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ciint nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct nlm_rqst *call; 1618c2ecf20Sopenharmony_ci int status; 1628c2ecf20Sopenharmony_ci const struct nlmclnt_operations *nlmclnt_ops = host->h_nlmclnt_ops; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci call = nlm_alloc_call(host); 1658c2ecf20Sopenharmony_ci if (call == NULL) 1668c2ecf20Sopenharmony_ci return -ENOMEM; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call) 1698c2ecf20Sopenharmony_ci nlmclnt_ops->nlmclnt_alloc_call(data); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci nlmclnt_locks_init_private(fl, host); 1728c2ecf20Sopenharmony_ci if (!fl->fl_u.nfs_fl.owner) { 1738c2ecf20Sopenharmony_ci /* lockowner allocation has failed */ 1748c2ecf20Sopenharmony_ci nlmclnt_release_call(call); 1758c2ecf20Sopenharmony_ci return -ENOMEM; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci /* Set up the argument struct */ 1788c2ecf20Sopenharmony_ci nlmclnt_setlockargs(call, fl); 1798c2ecf20Sopenharmony_ci call->a_callback_data = data; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { 1828c2ecf20Sopenharmony_ci if (fl->fl_type != F_UNLCK) { 1838c2ecf20Sopenharmony_ci call->a_args.block = IS_SETLKW(cmd) ? 1 : 0; 1848c2ecf20Sopenharmony_ci status = nlmclnt_lock(call, fl); 1858c2ecf20Sopenharmony_ci } else 1868c2ecf20Sopenharmony_ci status = nlmclnt_unlock(call, fl); 1878c2ecf20Sopenharmony_ci } else if (IS_GETLK(cmd)) 1888c2ecf20Sopenharmony_ci status = nlmclnt_test(call, fl); 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci status = -EINVAL; 1918c2ecf20Sopenharmony_ci fl->fl_ops->fl_release_private(fl); 1928c2ecf20Sopenharmony_ci fl->fl_ops = NULL; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dprintk("lockd: clnt proc returns %d\n", status); 1958c2ecf20Sopenharmony_ci return status; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_proc); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * Allocate an NLM RPC call struct 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistruct nlm_rqst *nlm_alloc_call(struct nlm_host *host) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct nlm_rqst *call; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for(;;) { 2078c2ecf20Sopenharmony_ci call = kzalloc(sizeof(*call), GFP_KERNEL); 2088c2ecf20Sopenharmony_ci if (call != NULL) { 2098c2ecf20Sopenharmony_ci refcount_set(&call->a_count, 1); 2108c2ecf20Sopenharmony_ci locks_init_lock(&call->a_args.lock.fl); 2118c2ecf20Sopenharmony_ci locks_init_lock(&call->a_res.lock.fl); 2128c2ecf20Sopenharmony_ci call->a_host = nlm_get_host(host); 2138c2ecf20Sopenharmony_ci return call; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci if (signalled()) 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci printk("nlm_alloc_call: failed, waiting for memory\n"); 2188c2ecf20Sopenharmony_ci schedule_timeout_interruptible(5*HZ); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci return NULL; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid nlmclnt_release_call(struct nlm_rqst *call) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci const struct nlmclnt_operations *nlmclnt_ops = call->a_host->h_nlmclnt_ops; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (!refcount_dec_and_test(&call->a_count)) 2288c2ecf20Sopenharmony_ci return; 2298c2ecf20Sopenharmony_ci if (nlmclnt_ops && nlmclnt_ops->nlmclnt_release_call) 2308c2ecf20Sopenharmony_ci nlmclnt_ops->nlmclnt_release_call(call->a_callback_data); 2318c2ecf20Sopenharmony_ci nlmclnt_release_host(call->a_host); 2328c2ecf20Sopenharmony_ci nlmclnt_release_lockargs(call); 2338c2ecf20Sopenharmony_ci kfree(call); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void nlmclnt_rpc_release(void *data) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci nlmclnt_release_call(data); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int nlm_wait_on_grace(wait_queue_head_t *queue) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 2448c2ecf20Sopenharmony_ci int status = -EINTR; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci prepare_to_wait(queue, &wait, TASK_INTERRUPTIBLE); 2478c2ecf20Sopenharmony_ci if (!signalled ()) { 2488c2ecf20Sopenharmony_ci schedule_timeout(NLMCLNT_GRACE_WAIT); 2498c2ecf20Sopenharmony_ci try_to_freeze(); 2508c2ecf20Sopenharmony_ci if (!signalled ()) 2518c2ecf20Sopenharmony_ci status = 0; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci finish_wait(queue, &wait); 2548c2ecf20Sopenharmony_ci return status; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* 2588c2ecf20Sopenharmony_ci * Generic NLM call 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_cistatic int 2618c2ecf20Sopenharmony_cinlmclnt_call(const struct cred *cred, struct nlm_rqst *req, u32 proc) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct nlm_host *host = req->a_host; 2648c2ecf20Sopenharmony_ci struct rpc_clnt *clnt; 2658c2ecf20Sopenharmony_ci struct nlm_args *argp = &req->a_args; 2668c2ecf20Sopenharmony_ci struct nlm_res *resp = &req->a_res; 2678c2ecf20Sopenharmony_ci struct rpc_message msg = { 2688c2ecf20Sopenharmony_ci .rpc_argp = argp, 2698c2ecf20Sopenharmony_ci .rpc_resp = resp, 2708c2ecf20Sopenharmony_ci .rpc_cred = cred, 2718c2ecf20Sopenharmony_ci }; 2728c2ecf20Sopenharmony_ci int status; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci dprintk("lockd: call procedure %d on %s\n", 2758c2ecf20Sopenharmony_ci (int)proc, host->h_name); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci do { 2788c2ecf20Sopenharmony_ci if (host->h_reclaiming && !argp->reclaim) 2798c2ecf20Sopenharmony_ci goto in_grace_period; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* If we have no RPC client yet, create one. */ 2828c2ecf20Sopenharmony_ci if ((clnt = nlm_bind_host(host)) == NULL) 2838c2ecf20Sopenharmony_ci return -ENOLCK; 2848c2ecf20Sopenharmony_ci msg.rpc_proc = &clnt->cl_procinfo[proc]; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Perform the RPC call. If an error occurs, try again */ 2878c2ecf20Sopenharmony_ci if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) { 2888c2ecf20Sopenharmony_ci dprintk("lockd: rpc_call returned error %d\n", -status); 2898c2ecf20Sopenharmony_ci switch (status) { 2908c2ecf20Sopenharmony_ci case -EPROTONOSUPPORT: 2918c2ecf20Sopenharmony_ci status = -EINVAL; 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci case -ECONNREFUSED: 2948c2ecf20Sopenharmony_ci case -ETIMEDOUT: 2958c2ecf20Sopenharmony_ci case -ENOTCONN: 2968c2ecf20Sopenharmony_ci nlm_rebind_host(host); 2978c2ecf20Sopenharmony_ci status = -EAGAIN; 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case -ERESTARTSYS: 3008c2ecf20Sopenharmony_ci return signalled () ? -EINTR : status; 3018c2ecf20Sopenharmony_ci default: 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci } else 3068c2ecf20Sopenharmony_ci if (resp->status == nlm_lck_denied_grace_period) { 3078c2ecf20Sopenharmony_ci dprintk("lockd: server in grace period\n"); 3088c2ecf20Sopenharmony_ci if (argp->reclaim) { 3098c2ecf20Sopenharmony_ci printk(KERN_WARNING 3108c2ecf20Sopenharmony_ci "lockd: spurious grace period reject?!\n"); 3118c2ecf20Sopenharmony_ci return -ENOLCK; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci } else { 3148c2ecf20Sopenharmony_ci if (!argp->reclaim) { 3158c2ecf20Sopenharmony_ci /* We appear to be out of the grace period */ 3168c2ecf20Sopenharmony_ci wake_up_all(&host->h_gracewait); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci dprintk("lockd: server returns status %d\n", 3198c2ecf20Sopenharmony_ci ntohl(resp->status)); 3208c2ecf20Sopenharmony_ci return 0; /* Okay, call complete */ 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ciin_grace_period: 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * The server has rebooted and appears to be in the grace 3268c2ecf20Sopenharmony_ci * period during which locks are only allowed to be 3278c2ecf20Sopenharmony_ci * reclaimed. 3288c2ecf20Sopenharmony_ci * We can only back off and try again later. 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_ci status = nlm_wait_on_grace(&host->h_gracewait); 3318c2ecf20Sopenharmony_ci } while (status == 0); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return status; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* 3378c2ecf20Sopenharmony_ci * Generic NLM call, async version. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_cistatic struct rpc_task *__nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct nlm_host *host = req->a_host; 3428c2ecf20Sopenharmony_ci struct rpc_clnt *clnt; 3438c2ecf20Sopenharmony_ci struct rpc_task_setup task_setup_data = { 3448c2ecf20Sopenharmony_ci .rpc_message = msg, 3458c2ecf20Sopenharmony_ci .callback_ops = tk_ops, 3468c2ecf20Sopenharmony_ci .callback_data = req, 3478c2ecf20Sopenharmony_ci .flags = RPC_TASK_ASYNC, 3488c2ecf20Sopenharmony_ci }; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci dprintk("lockd: call procedure %d on %s (async)\n", 3518c2ecf20Sopenharmony_ci (int)proc, host->h_name); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* If we have no RPC client yet, create one. */ 3548c2ecf20Sopenharmony_ci clnt = nlm_bind_host(host); 3558c2ecf20Sopenharmony_ci if (clnt == NULL) 3568c2ecf20Sopenharmony_ci goto out_err; 3578c2ecf20Sopenharmony_ci msg->rpc_proc = &clnt->cl_procinfo[proc]; 3588c2ecf20Sopenharmony_ci task_setup_data.rpc_client = clnt; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* bootstrap and kick off the async RPC call */ 3618c2ecf20Sopenharmony_ci return rpc_run_task(&task_setup_data); 3628c2ecf20Sopenharmony_ciout_err: 3638c2ecf20Sopenharmony_ci tk_ops->rpc_release(req); 3648c2ecf20Sopenharmony_ci return ERR_PTR(-ENOLCK); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int nlm_do_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct rpc_task *task; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci task = __nlm_async_call(req, proc, msg, tk_ops); 3728c2ecf20Sopenharmony_ci if (IS_ERR(task)) 3738c2ecf20Sopenharmony_ci return PTR_ERR(task); 3748c2ecf20Sopenharmony_ci rpc_put_task(task); 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/* 3798c2ecf20Sopenharmony_ci * NLM asynchronous call. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ciint nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct rpc_message msg = { 3848c2ecf20Sopenharmony_ci .rpc_argp = &req->a_args, 3858c2ecf20Sopenharmony_ci .rpc_resp = &req->a_res, 3868c2ecf20Sopenharmony_ci }; 3878c2ecf20Sopenharmony_ci return nlm_do_async_call(req, proc, &msg, tk_ops); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ciint nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct rpc_message msg = { 3938c2ecf20Sopenharmony_ci .rpc_argp = &req->a_res, 3948c2ecf20Sopenharmony_ci }; 3958c2ecf20Sopenharmony_ci return nlm_do_async_call(req, proc, &msg, tk_ops); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* 3998c2ecf20Sopenharmony_ci * NLM client asynchronous call. 4008c2ecf20Sopenharmony_ci * 4018c2ecf20Sopenharmony_ci * Note that although the calls are asynchronous, and are therefore 4028c2ecf20Sopenharmony_ci * guaranteed to complete, we still always attempt to wait for 4038c2ecf20Sopenharmony_ci * completion in order to be able to correctly track the lock 4048c2ecf20Sopenharmony_ci * state. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_cistatic int nlmclnt_async_call(const struct cred *cred, struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct rpc_message msg = { 4098c2ecf20Sopenharmony_ci .rpc_argp = &req->a_args, 4108c2ecf20Sopenharmony_ci .rpc_resp = &req->a_res, 4118c2ecf20Sopenharmony_ci .rpc_cred = cred, 4128c2ecf20Sopenharmony_ci }; 4138c2ecf20Sopenharmony_ci struct rpc_task *task; 4148c2ecf20Sopenharmony_ci int err; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci task = __nlm_async_call(req, proc, &msg, tk_ops); 4178c2ecf20Sopenharmony_ci if (IS_ERR(task)) 4188c2ecf20Sopenharmony_ci return PTR_ERR(task); 4198c2ecf20Sopenharmony_ci err = rpc_wait_for_completion_task(task); 4208c2ecf20Sopenharmony_ci rpc_put_task(task); 4218c2ecf20Sopenharmony_ci return err; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/* 4258c2ecf20Sopenharmony_ci * TEST for the presence of a conflicting lock 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_cistatic int 4288c2ecf20Sopenharmony_cinlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci int status; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_TEST); 4338c2ecf20Sopenharmony_ci if (status < 0) 4348c2ecf20Sopenharmony_ci goto out; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci switch (req->a_res.status) { 4378c2ecf20Sopenharmony_ci case nlm_granted: 4388c2ecf20Sopenharmony_ci fl->fl_type = F_UNLCK; 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci case nlm_lck_denied: 4418c2ecf20Sopenharmony_ci /* 4428c2ecf20Sopenharmony_ci * Report the conflicting lock back to the application. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ci fl->fl_start = req->a_res.lock.fl.fl_start; 4458c2ecf20Sopenharmony_ci fl->fl_end = req->a_res.lock.fl.fl_end; 4468c2ecf20Sopenharmony_ci fl->fl_type = req->a_res.lock.fl.fl_type; 4478c2ecf20Sopenharmony_ci fl->fl_pid = -req->a_res.lock.fl.fl_pid; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci default: 4508c2ecf20Sopenharmony_ci status = nlm_stat_to_errno(req->a_res.status); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ciout: 4538c2ecf20Sopenharmony_ci nlmclnt_release_call(req); 4548c2ecf20Sopenharmony_ci return status; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock); 4608c2ecf20Sopenharmony_ci new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state; 4618c2ecf20Sopenharmony_ci new->fl_u.nfs_fl.owner = nlmclnt_get_lockowner(fl->fl_u.nfs_fl.owner); 4628c2ecf20Sopenharmony_ci list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted); 4638c2ecf20Sopenharmony_ci spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic void nlmclnt_locks_release_private(struct file_lock *fl) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock); 4698c2ecf20Sopenharmony_ci list_del(&fl->fl_u.nfs_fl.list); 4708c2ecf20Sopenharmony_ci spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock); 4718c2ecf20Sopenharmony_ci nlmclnt_put_lockowner(fl->fl_u.nfs_fl.owner); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct file_lock_operations nlmclnt_lock_ops = { 4758c2ecf20Sopenharmony_ci .fl_copy_lock = nlmclnt_locks_copy_lock, 4768c2ecf20Sopenharmony_ci .fl_release_private = nlmclnt_locks_release_private, 4778c2ecf20Sopenharmony_ci}; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci fl->fl_u.nfs_fl.state = 0; 4828c2ecf20Sopenharmony_ci fl->fl_u.nfs_fl.owner = nlmclnt_find_lockowner(host, fl->fl_owner); 4838c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list); 4848c2ecf20Sopenharmony_ci fl->fl_ops = &nlmclnt_lock_ops; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int do_vfs_lock(struct file_lock *fl) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci return locks_lock_file_wait(fl->fl_file, fl); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/* 4938c2ecf20Sopenharmony_ci * LOCK: Try to create a lock 4948c2ecf20Sopenharmony_ci * 4958c2ecf20Sopenharmony_ci * Programmer Harassment Alert 4968c2ecf20Sopenharmony_ci * 4978c2ecf20Sopenharmony_ci * When given a blocking lock request in a sync RPC call, the HPUX lockd 4988c2ecf20Sopenharmony_ci * will faithfully return LCK_BLOCKED but never cares to notify us when 4998c2ecf20Sopenharmony_ci * the lock could be granted. This way, our local process could hang 5008c2ecf20Sopenharmony_ci * around forever waiting for the callback. 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * Solution A: Implement busy-waiting 5038c2ecf20Sopenharmony_ci * Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES}) 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci * For now I am implementing solution A, because I hate the idea of 5068c2ecf20Sopenharmony_ci * re-implementing lockd for a third time in two months. The async 5078c2ecf20Sopenharmony_ci * calls shouldn't be too hard to do, however. 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * This is one of the lovely things about standards in the NFS area: 5108c2ecf20Sopenharmony_ci * they're so soft and squishy you can't really blame HP for doing this. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_cistatic int 5138c2ecf20Sopenharmony_cinlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci const struct cred *cred = nfs_file_cred(fl->fl_file); 5168c2ecf20Sopenharmony_ci struct nlm_host *host = req->a_host; 5178c2ecf20Sopenharmony_ci struct nlm_res *resp = &req->a_res; 5188c2ecf20Sopenharmony_ci struct nlm_wait *block = NULL; 5198c2ecf20Sopenharmony_ci unsigned char fl_flags = fl->fl_flags; 5208c2ecf20Sopenharmony_ci unsigned char fl_type; 5218c2ecf20Sopenharmony_ci int status = -ENOLCK; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (nsm_monitor(host) < 0) 5248c2ecf20Sopenharmony_ci goto out; 5258c2ecf20Sopenharmony_ci req->a_args.state = nsm_local_state; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci fl->fl_flags |= FL_ACCESS; 5288c2ecf20Sopenharmony_ci status = do_vfs_lock(fl); 5298c2ecf20Sopenharmony_ci fl->fl_flags = fl_flags; 5308c2ecf20Sopenharmony_ci if (status < 0) 5318c2ecf20Sopenharmony_ci goto out; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci block = nlmclnt_prepare_block(host, fl); 5348c2ecf20Sopenharmony_ciagain: 5358c2ecf20Sopenharmony_ci /* 5368c2ecf20Sopenharmony_ci * Initialise resp->status to a valid non-zero value, 5378c2ecf20Sopenharmony_ci * since 0 == nlm_lck_granted 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci resp->status = nlm_lck_blocked; 5408c2ecf20Sopenharmony_ci for(;;) { 5418c2ecf20Sopenharmony_ci /* Reboot protection */ 5428c2ecf20Sopenharmony_ci fl->fl_u.nfs_fl.state = host->h_state; 5438c2ecf20Sopenharmony_ci status = nlmclnt_call(cred, req, NLMPROC_LOCK); 5448c2ecf20Sopenharmony_ci if (status < 0) 5458c2ecf20Sopenharmony_ci break; 5468c2ecf20Sopenharmony_ci /* Did a reclaimer thread notify us of a server reboot? */ 5478c2ecf20Sopenharmony_ci if (resp->status == nlm_lck_denied_grace_period) 5488c2ecf20Sopenharmony_ci continue; 5498c2ecf20Sopenharmony_ci if (resp->status != nlm_lck_blocked) 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci /* Wait on an NLM blocking lock */ 5528c2ecf20Sopenharmony_ci status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT); 5538c2ecf20Sopenharmony_ci if (status < 0) 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci if (resp->status != nlm_lck_blocked) 5568c2ecf20Sopenharmony_ci break; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* if we were interrupted while blocking, then cancel the lock request 5608c2ecf20Sopenharmony_ci * and exit 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci if (resp->status == nlm_lck_blocked) { 5638c2ecf20Sopenharmony_ci if (!req->a_args.block) 5648c2ecf20Sopenharmony_ci goto out_unlock; 5658c2ecf20Sopenharmony_ci if (nlmclnt_cancel(host, req->a_args.block, fl) == 0) 5668c2ecf20Sopenharmony_ci goto out_unblock; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (resp->status == nlm_granted) { 5708c2ecf20Sopenharmony_ci down_read(&host->h_rwsem); 5718c2ecf20Sopenharmony_ci /* Check whether or not the server has rebooted */ 5728c2ecf20Sopenharmony_ci if (fl->fl_u.nfs_fl.state != host->h_state) { 5738c2ecf20Sopenharmony_ci up_read(&host->h_rwsem); 5748c2ecf20Sopenharmony_ci goto again; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci /* Ensure the resulting lock will get added to granted list */ 5778c2ecf20Sopenharmony_ci fl->fl_flags |= FL_SLEEP; 5788c2ecf20Sopenharmony_ci if (do_vfs_lock(fl) < 0) 5798c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__); 5808c2ecf20Sopenharmony_ci up_read(&host->h_rwsem); 5818c2ecf20Sopenharmony_ci fl->fl_flags = fl_flags; 5828c2ecf20Sopenharmony_ci status = 0; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci if (status < 0) 5858c2ecf20Sopenharmony_ci goto out_unlock; 5868c2ecf20Sopenharmony_ci /* 5878c2ecf20Sopenharmony_ci * EAGAIN doesn't make sense for sleeping locks, and in some 5888c2ecf20Sopenharmony_ci * cases NLM_LCK_DENIED is returned for a permanent error. So 5898c2ecf20Sopenharmony_ci * turn it into an ENOLCK. 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ci if (resp->status == nlm_lck_denied && (fl_flags & FL_SLEEP)) 5928c2ecf20Sopenharmony_ci status = -ENOLCK; 5938c2ecf20Sopenharmony_ci else 5948c2ecf20Sopenharmony_ci status = nlm_stat_to_errno(resp->status); 5958c2ecf20Sopenharmony_ciout_unblock: 5968c2ecf20Sopenharmony_ci nlmclnt_finish_block(block); 5978c2ecf20Sopenharmony_ciout: 5988c2ecf20Sopenharmony_ci nlmclnt_release_call(req); 5998c2ecf20Sopenharmony_ci return status; 6008c2ecf20Sopenharmony_ciout_unlock: 6018c2ecf20Sopenharmony_ci /* Fatal error: ensure that we remove the lock altogether */ 6028c2ecf20Sopenharmony_ci dprintk("lockd: lock attempt ended in fatal error.\n" 6038c2ecf20Sopenharmony_ci " Attempting to unlock.\n"); 6048c2ecf20Sopenharmony_ci nlmclnt_finish_block(block); 6058c2ecf20Sopenharmony_ci fl_type = fl->fl_type; 6068c2ecf20Sopenharmony_ci fl->fl_type = F_UNLCK; 6078c2ecf20Sopenharmony_ci down_read(&host->h_rwsem); 6088c2ecf20Sopenharmony_ci do_vfs_lock(fl); 6098c2ecf20Sopenharmony_ci up_read(&host->h_rwsem); 6108c2ecf20Sopenharmony_ci fl->fl_type = fl_type; 6118c2ecf20Sopenharmony_ci fl->fl_flags = fl_flags; 6128c2ecf20Sopenharmony_ci nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); 6138c2ecf20Sopenharmony_ci return status; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci/* 6178c2ecf20Sopenharmony_ci * RECLAIM: Try to reclaim a lock 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ciint 6208c2ecf20Sopenharmony_cinlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl, 6218c2ecf20Sopenharmony_ci struct nlm_rqst *req) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci int status; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci memset(req, 0, sizeof(*req)); 6268c2ecf20Sopenharmony_ci locks_init_lock(&req->a_args.lock.fl); 6278c2ecf20Sopenharmony_ci locks_init_lock(&req->a_res.lock.fl); 6288c2ecf20Sopenharmony_ci req->a_host = host; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* Set up the argument struct */ 6318c2ecf20Sopenharmony_ci nlmclnt_setlockargs(req, fl); 6328c2ecf20Sopenharmony_ci req->a_args.reclaim = 1; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_LOCK); 6358c2ecf20Sopenharmony_ci if (status >= 0 && req->a_res.status == nlm_granted) 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d " 6398c2ecf20Sopenharmony_ci "(errno %d, status %d)\n", fl->fl_pid, 6408c2ecf20Sopenharmony_ci status, ntohl(req->a_res.status)); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* 6438c2ecf20Sopenharmony_ci * FIXME: This is a serious failure. We can 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * a. Ignore the problem 6468c2ecf20Sopenharmony_ci * b. Send the owning process some signal (Linux doesn't have 6478c2ecf20Sopenharmony_ci * SIGLOST, though...) 6488c2ecf20Sopenharmony_ci * c. Retry the operation 6498c2ecf20Sopenharmony_ci * 6508c2ecf20Sopenharmony_ci * Until someone comes up with a simple implementation 6518c2ecf20Sopenharmony_ci * for b or c, I'll choose option a. 6528c2ecf20Sopenharmony_ci */ 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return -ENOLCK; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci/* 6588c2ecf20Sopenharmony_ci * UNLOCK: remove an existing lock 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_cistatic int 6618c2ecf20Sopenharmony_cinlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct nlm_host *host = req->a_host; 6648c2ecf20Sopenharmony_ci struct nlm_res *resp = &req->a_res; 6658c2ecf20Sopenharmony_ci int status; 6668c2ecf20Sopenharmony_ci unsigned char fl_flags = fl->fl_flags; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* 6698c2ecf20Sopenharmony_ci * Note: the server is supposed to either grant us the unlock 6708c2ecf20Sopenharmony_ci * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either 6718c2ecf20Sopenharmony_ci * case, we want to unlock. 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_ci fl->fl_flags |= FL_EXISTS; 6748c2ecf20Sopenharmony_ci down_read(&host->h_rwsem); 6758c2ecf20Sopenharmony_ci status = do_vfs_lock(fl); 6768c2ecf20Sopenharmony_ci up_read(&host->h_rwsem); 6778c2ecf20Sopenharmony_ci fl->fl_flags = fl_flags; 6788c2ecf20Sopenharmony_ci if (status == -ENOENT) { 6798c2ecf20Sopenharmony_ci status = 0; 6808c2ecf20Sopenharmony_ci goto out; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci refcount_inc(&req->a_count); 6848c2ecf20Sopenharmony_ci status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req, 6858c2ecf20Sopenharmony_ci NLMPROC_UNLOCK, &nlmclnt_unlock_ops); 6868c2ecf20Sopenharmony_ci if (status < 0) 6878c2ecf20Sopenharmony_ci goto out; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (resp->status == nlm_granted) 6908c2ecf20Sopenharmony_ci goto out; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (resp->status != nlm_lck_denied_nolocks) 6938c2ecf20Sopenharmony_ci printk("lockd: unexpected unlock status: %d\n", 6948c2ecf20Sopenharmony_ci ntohl(resp->status)); 6958c2ecf20Sopenharmony_ci /* What to do now? I'm out of my depth... */ 6968c2ecf20Sopenharmony_ci status = -ENOLCK; 6978c2ecf20Sopenharmony_ciout: 6988c2ecf20Sopenharmony_ci nlmclnt_release_call(req); 6998c2ecf20Sopenharmony_ci return status; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic void nlmclnt_unlock_prepare(struct rpc_task *task, void *data) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct nlm_rqst *req = data; 7058c2ecf20Sopenharmony_ci const struct nlmclnt_operations *nlmclnt_ops = req->a_host->h_nlmclnt_ops; 7068c2ecf20Sopenharmony_ci bool defer_call = false; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (nlmclnt_ops && nlmclnt_ops->nlmclnt_unlock_prepare) 7098c2ecf20Sopenharmony_ci defer_call = nlmclnt_ops->nlmclnt_unlock_prepare(task, req->a_callback_data); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (!defer_call) 7128c2ecf20Sopenharmony_ci rpc_call_start(task); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic void nlmclnt_unlock_callback(struct rpc_task *task, void *data) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct nlm_rqst *req = data; 7188c2ecf20Sopenharmony_ci u32 status = ntohl(req->a_res.status); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (RPC_SIGNALLED(task)) 7218c2ecf20Sopenharmony_ci goto die; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (task->tk_status < 0) { 7248c2ecf20Sopenharmony_ci dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status); 7258c2ecf20Sopenharmony_ci switch (task->tk_status) { 7268c2ecf20Sopenharmony_ci case -EACCES: 7278c2ecf20Sopenharmony_ci case -EIO: 7288c2ecf20Sopenharmony_ci goto die; 7298c2ecf20Sopenharmony_ci default: 7308c2ecf20Sopenharmony_ci goto retry_rebind; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci if (status == NLM_LCK_DENIED_GRACE_PERIOD) { 7348c2ecf20Sopenharmony_ci rpc_delay(task, NLMCLNT_GRACE_WAIT); 7358c2ecf20Sopenharmony_ci goto retry_unlock; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci if (status != NLM_LCK_GRANTED) 7388c2ecf20Sopenharmony_ci printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status); 7398c2ecf20Sopenharmony_cidie: 7408c2ecf20Sopenharmony_ci return; 7418c2ecf20Sopenharmony_ci retry_rebind: 7428c2ecf20Sopenharmony_ci nlm_rebind_host(req->a_host); 7438c2ecf20Sopenharmony_ci retry_unlock: 7448c2ecf20Sopenharmony_ci rpc_restart_call(task); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_unlock_ops = { 7488c2ecf20Sopenharmony_ci .rpc_call_prepare = nlmclnt_unlock_prepare, 7498c2ecf20Sopenharmony_ci .rpc_call_done = nlmclnt_unlock_callback, 7508c2ecf20Sopenharmony_ci .rpc_release = nlmclnt_rpc_release, 7518c2ecf20Sopenharmony_ci}; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/* 7548c2ecf20Sopenharmony_ci * Cancel a blocked lock request. 7558c2ecf20Sopenharmony_ci * We always use an async RPC call for this in order not to hang a 7568c2ecf20Sopenharmony_ci * process that has been Ctrl-C'ed. 7578c2ecf20Sopenharmony_ci */ 7588c2ecf20Sopenharmony_cistatic int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct nlm_rqst *req; 7618c2ecf20Sopenharmony_ci int status; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci dprintk("lockd: blocking lock attempt was interrupted by a signal.\n" 7648c2ecf20Sopenharmony_ci " Attempting to cancel lock.\n"); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci req = nlm_alloc_call(host); 7678c2ecf20Sopenharmony_ci if (!req) 7688c2ecf20Sopenharmony_ci return -ENOMEM; 7698c2ecf20Sopenharmony_ci req->a_flags = RPC_TASK_ASYNC; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci nlmclnt_setlockargs(req, fl); 7728c2ecf20Sopenharmony_ci req->a_args.block = block; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci refcount_inc(&req->a_count); 7758c2ecf20Sopenharmony_ci status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req, 7768c2ecf20Sopenharmony_ci NLMPROC_CANCEL, &nlmclnt_cancel_ops); 7778c2ecf20Sopenharmony_ci if (status == 0 && req->a_res.status == nlm_lck_denied) 7788c2ecf20Sopenharmony_ci status = -ENOLCK; 7798c2ecf20Sopenharmony_ci nlmclnt_release_call(req); 7808c2ecf20Sopenharmony_ci return status; 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic void nlmclnt_cancel_callback(struct rpc_task *task, void *data) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct nlm_rqst *req = data; 7868c2ecf20Sopenharmony_ci u32 status = ntohl(req->a_res.status); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (RPC_SIGNALLED(task)) 7898c2ecf20Sopenharmony_ci goto die; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (task->tk_status < 0) { 7928c2ecf20Sopenharmony_ci dprintk("lockd: CANCEL call error %d, retrying.\n", 7938c2ecf20Sopenharmony_ci task->tk_status); 7948c2ecf20Sopenharmony_ci goto retry_cancel; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci dprintk("lockd: cancel status %u (task %u)\n", 7988c2ecf20Sopenharmony_ci status, task->tk_pid); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci switch (status) { 8018c2ecf20Sopenharmony_ci case NLM_LCK_GRANTED: 8028c2ecf20Sopenharmony_ci case NLM_LCK_DENIED_GRACE_PERIOD: 8038c2ecf20Sopenharmony_ci case NLM_LCK_DENIED: 8048c2ecf20Sopenharmony_ci /* Everything's good */ 8058c2ecf20Sopenharmony_ci break; 8068c2ecf20Sopenharmony_ci case NLM_LCK_DENIED_NOLOCKS: 8078c2ecf20Sopenharmony_ci dprintk("lockd: CANCEL failed (server has no locks)\n"); 8088c2ecf20Sopenharmony_ci goto retry_cancel; 8098c2ecf20Sopenharmony_ci default: 8108c2ecf20Sopenharmony_ci printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n", 8118c2ecf20Sopenharmony_ci status); 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cidie: 8158c2ecf20Sopenharmony_ci return; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ciretry_cancel: 8188c2ecf20Sopenharmony_ci /* Don't ever retry more than 3 times */ 8198c2ecf20Sopenharmony_ci if (req->a_retries++ >= NLMCLNT_MAX_RETRIES) 8208c2ecf20Sopenharmony_ci goto die; 8218c2ecf20Sopenharmony_ci nlm_rebind_host(req->a_host); 8228c2ecf20Sopenharmony_ci rpc_restart_call(task); 8238c2ecf20Sopenharmony_ci rpc_delay(task, 30 * HZ); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_cancel_ops = { 8278c2ecf20Sopenharmony_ci .rpc_call_done = nlmclnt_cancel_callback, 8288c2ecf20Sopenharmony_ci .rpc_release = nlmclnt_rpc_release, 8298c2ecf20Sopenharmony_ci}; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci/* 8328c2ecf20Sopenharmony_ci * Convert an NLM status code to a generic kernel errno 8338c2ecf20Sopenharmony_ci */ 8348c2ecf20Sopenharmony_cistatic int 8358c2ecf20Sopenharmony_cinlm_stat_to_errno(__be32 status) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci switch(ntohl(status)) { 8388c2ecf20Sopenharmony_ci case NLM_LCK_GRANTED: 8398c2ecf20Sopenharmony_ci return 0; 8408c2ecf20Sopenharmony_ci case NLM_LCK_DENIED: 8418c2ecf20Sopenharmony_ci return -EAGAIN; 8428c2ecf20Sopenharmony_ci case NLM_LCK_DENIED_NOLOCKS: 8438c2ecf20Sopenharmony_ci case NLM_LCK_DENIED_GRACE_PERIOD: 8448c2ecf20Sopenharmony_ci return -ENOLCK; 8458c2ecf20Sopenharmony_ci case NLM_LCK_BLOCKED: 8468c2ecf20Sopenharmony_ci printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n"); 8478c2ecf20Sopenharmony_ci return -ENOLCK; 8488c2ecf20Sopenharmony_ci#ifdef CONFIG_LOCKD_V4 8498c2ecf20Sopenharmony_ci case NLM_DEADLCK: 8508c2ecf20Sopenharmony_ci return -EDEADLK; 8518c2ecf20Sopenharmony_ci case NLM_ROFS: 8528c2ecf20Sopenharmony_ci return -EROFS; 8538c2ecf20Sopenharmony_ci case NLM_STALE_FH: 8548c2ecf20Sopenharmony_ci return -ESTALE; 8558c2ecf20Sopenharmony_ci case NLM_FBIG: 8568c2ecf20Sopenharmony_ci return -EOVERFLOW; 8578c2ecf20Sopenharmony_ci case NLM_FAILED: 8588c2ecf20Sopenharmony_ci return -ENOLCK; 8598c2ecf20Sopenharmony_ci#endif 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci printk(KERN_NOTICE "lockd: unexpected server status %d\n", 8628c2ecf20Sopenharmony_ci ntohl(status)); 8638c2ecf20Sopenharmony_ci return -ENOLCK; 8648c2ecf20Sopenharmony_ci} 865