162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/lockd/clntproc.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * RPC procedures for the client side NLM implementation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/fs.h> 1562306a36Sopenharmony_ci#include <linux/filelock.h> 1662306a36Sopenharmony_ci#include <linux/nfs_fs.h> 1762306a36Sopenharmony_ci#include <linux/utsname.h> 1862306a36Sopenharmony_ci#include <linux/freezer.h> 1962306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h> 2062306a36Sopenharmony_ci#include <linux/sunrpc/svc.h> 2162306a36Sopenharmony_ci#include <linux/lockd/lockd.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "trace.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define NLMDBG_FACILITY NLMDBG_CLIENT 2662306a36Sopenharmony_ci#define NLMCLNT_GRACE_WAIT (5*HZ) 2762306a36Sopenharmony_ci#define NLMCLNT_POLL_TIMEOUT (30*HZ) 2862306a36Sopenharmony_ci#define NLMCLNT_MAX_RETRIES 3 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int nlmclnt_test(struct nlm_rqst *, struct file_lock *); 3162306a36Sopenharmony_cistatic int nlmclnt_lock(struct nlm_rqst *, struct file_lock *); 3262306a36Sopenharmony_cistatic int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *); 3362306a36Sopenharmony_cistatic int nlm_stat_to_errno(__be32 stat); 3462306a36Sopenharmony_cistatic void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host); 3562306a36Sopenharmony_cistatic int nlmclnt_cancel(struct nlm_host *, int , struct file_lock *); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_unlock_ops; 3862306a36Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_cancel_ops; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Cookie counter for NLM requests 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistatic atomic_t nlm_cookie = ATOMIC_INIT(0x1234); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_civoid nlmclnt_next_cookie(struct nlm_cookie *c) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci u32 cookie = atomic_inc_return(&nlm_cookie); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci memcpy(c->data, &cookie, 4); 5062306a36Sopenharmony_ci c->len=4; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic struct nlm_lockowner * 5462306a36Sopenharmony_cinlmclnt_get_lockowner(struct nlm_lockowner *lockowner) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci refcount_inc(&lockowner->count); 5762306a36Sopenharmony_ci return lockowner; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void nlmclnt_put_lockowner(struct nlm_lockowner *lockowner) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock)) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci list_del(&lockowner->list); 6562306a36Sopenharmony_ci spin_unlock(&lockowner->host->h_lock); 6662306a36Sopenharmony_ci nlmclnt_release_host(lockowner->host); 6762306a36Sopenharmony_ci kfree(lockowner); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic inline int nlm_pidbusy(struct nlm_host *host, uint32_t pid) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct nlm_lockowner *lockowner; 7362306a36Sopenharmony_ci list_for_each_entry(lockowner, &host->h_lockowners, list) { 7462306a36Sopenharmony_ci if (lockowner->pid == pid) 7562306a36Sopenharmony_ci return -EBUSY; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic inline uint32_t __nlm_alloc_pid(struct nlm_host *host) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci uint32_t res; 8362306a36Sopenharmony_ci do { 8462306a36Sopenharmony_ci res = host->h_pidcount++; 8562306a36Sopenharmony_ci } while (nlm_pidbusy(host, res) < 0); 8662306a36Sopenharmony_ci return res; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct nlm_lockowner *__nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct nlm_lockowner *lockowner; 9262306a36Sopenharmony_ci list_for_each_entry(lockowner, &host->h_lockowners, list) { 9362306a36Sopenharmony_ci if (lockowner->owner != owner) 9462306a36Sopenharmony_ci continue; 9562306a36Sopenharmony_ci return nlmclnt_get_lockowner(lockowner); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci return NULL; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct nlm_lockowner *nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct nlm_lockowner *res, *new = NULL; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci spin_lock(&host->h_lock); 10562306a36Sopenharmony_ci res = __nlmclnt_find_lockowner(host, owner); 10662306a36Sopenharmony_ci if (res == NULL) { 10762306a36Sopenharmony_ci spin_unlock(&host->h_lock); 10862306a36Sopenharmony_ci new = kmalloc(sizeof(*new), GFP_KERNEL); 10962306a36Sopenharmony_ci spin_lock(&host->h_lock); 11062306a36Sopenharmony_ci res = __nlmclnt_find_lockowner(host, owner); 11162306a36Sopenharmony_ci if (res == NULL && new != NULL) { 11262306a36Sopenharmony_ci res = new; 11362306a36Sopenharmony_ci refcount_set(&new->count, 1); 11462306a36Sopenharmony_ci new->owner = owner; 11562306a36Sopenharmony_ci new->pid = __nlm_alloc_pid(host); 11662306a36Sopenharmony_ci new->host = nlm_get_host(host); 11762306a36Sopenharmony_ci list_add(&new->list, &host->h_lockowners); 11862306a36Sopenharmony_ci new = NULL; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci spin_unlock(&host->h_lock); 12262306a36Sopenharmony_ci kfree(new); 12362306a36Sopenharmony_ci return res; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct nlm_args *argp = &req->a_args; 13262306a36Sopenharmony_ci struct nlm_lock *lock = &argp->lock; 13362306a36Sopenharmony_ci char *nodename = req->a_host->h_rpcclnt->cl_nodename; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci nlmclnt_next_cookie(&argp->cookie); 13662306a36Sopenharmony_ci memcpy(&lock->fh, NFS_FH(file_inode(fl->fl_file)), sizeof(struct nfs_fh)); 13762306a36Sopenharmony_ci lock->caller = nodename; 13862306a36Sopenharmony_ci lock->oh.data = req->a_owner; 13962306a36Sopenharmony_ci lock->oh.len = snprintf(req->a_owner, sizeof(req->a_owner), "%u@%s", 14062306a36Sopenharmony_ci (unsigned int)fl->fl_u.nfs_fl.owner->pid, 14162306a36Sopenharmony_ci nodename); 14262306a36Sopenharmony_ci lock->svid = fl->fl_u.nfs_fl.owner->pid; 14362306a36Sopenharmony_ci lock->fl.fl_start = fl->fl_start; 14462306a36Sopenharmony_ci lock->fl.fl_end = fl->fl_end; 14562306a36Sopenharmony_ci lock->fl.fl_type = fl->fl_type; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void nlmclnt_release_lockargs(struct nlm_rqst *req) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci WARN_ON_ONCE(req->a_args.lock.fl.fl_ops != NULL); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * nlmclnt_proc - Perform a single client-side lock request 15562306a36Sopenharmony_ci * @host: address of a valid nlm_host context representing the NLM server 15662306a36Sopenharmony_ci * @cmd: fcntl-style file lock operation to perform 15762306a36Sopenharmony_ci * @fl: address of arguments for the lock operation 15862306a36Sopenharmony_ci * @data: address of data to be sent to callback operations 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ciint nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct nlm_rqst *call; 16462306a36Sopenharmony_ci int status; 16562306a36Sopenharmony_ci const struct nlmclnt_operations *nlmclnt_ops = host->h_nlmclnt_ops; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci call = nlm_alloc_call(host); 16862306a36Sopenharmony_ci if (call == NULL) 16962306a36Sopenharmony_ci return -ENOMEM; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call) 17262306a36Sopenharmony_ci nlmclnt_ops->nlmclnt_alloc_call(data); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci nlmclnt_locks_init_private(fl, host); 17562306a36Sopenharmony_ci if (!fl->fl_u.nfs_fl.owner) { 17662306a36Sopenharmony_ci /* lockowner allocation has failed */ 17762306a36Sopenharmony_ci nlmclnt_release_call(call); 17862306a36Sopenharmony_ci return -ENOMEM; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci /* Set up the argument struct */ 18162306a36Sopenharmony_ci nlmclnt_setlockargs(call, fl); 18262306a36Sopenharmony_ci call->a_callback_data = data; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { 18562306a36Sopenharmony_ci if (fl->fl_type != F_UNLCK) { 18662306a36Sopenharmony_ci call->a_args.block = IS_SETLKW(cmd) ? 1 : 0; 18762306a36Sopenharmony_ci status = nlmclnt_lock(call, fl); 18862306a36Sopenharmony_ci } else 18962306a36Sopenharmony_ci status = nlmclnt_unlock(call, fl); 19062306a36Sopenharmony_ci } else if (IS_GETLK(cmd)) 19162306a36Sopenharmony_ci status = nlmclnt_test(call, fl); 19262306a36Sopenharmony_ci else 19362306a36Sopenharmony_ci status = -EINVAL; 19462306a36Sopenharmony_ci fl->fl_ops->fl_release_private(fl); 19562306a36Sopenharmony_ci fl->fl_ops = NULL; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci dprintk("lockd: clnt proc returns %d\n", status); 19862306a36Sopenharmony_ci return status; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_proc); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * Allocate an NLM RPC call struct 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistruct nlm_rqst *nlm_alloc_call(struct nlm_host *host) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct nlm_rqst *call; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for(;;) { 21062306a36Sopenharmony_ci call = kzalloc(sizeof(*call), GFP_KERNEL); 21162306a36Sopenharmony_ci if (call != NULL) { 21262306a36Sopenharmony_ci refcount_set(&call->a_count, 1); 21362306a36Sopenharmony_ci locks_init_lock(&call->a_args.lock.fl); 21462306a36Sopenharmony_ci locks_init_lock(&call->a_res.lock.fl); 21562306a36Sopenharmony_ci call->a_host = nlm_get_host(host); 21662306a36Sopenharmony_ci return call; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci if (signalled()) 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci printk("nlm_alloc_call: failed, waiting for memory\n"); 22162306a36Sopenharmony_ci schedule_timeout_interruptible(5*HZ); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci return NULL; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_civoid nlmclnt_release_call(struct nlm_rqst *call) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci const struct nlmclnt_operations *nlmclnt_ops = call->a_host->h_nlmclnt_ops; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!refcount_dec_and_test(&call->a_count)) 23162306a36Sopenharmony_ci return; 23262306a36Sopenharmony_ci if (nlmclnt_ops && nlmclnt_ops->nlmclnt_release_call) 23362306a36Sopenharmony_ci nlmclnt_ops->nlmclnt_release_call(call->a_callback_data); 23462306a36Sopenharmony_ci nlmclnt_release_host(call->a_host); 23562306a36Sopenharmony_ci nlmclnt_release_lockargs(call); 23662306a36Sopenharmony_ci kfree(call); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void nlmclnt_rpc_release(void *data) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci nlmclnt_release_call(data); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int nlm_wait_on_grace(wait_queue_head_t *queue) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci DEFINE_WAIT(wait); 24762306a36Sopenharmony_ci int status = -EINTR; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci prepare_to_wait(queue, &wait, TASK_INTERRUPTIBLE); 25062306a36Sopenharmony_ci if (!signalled ()) { 25162306a36Sopenharmony_ci schedule_timeout(NLMCLNT_GRACE_WAIT); 25262306a36Sopenharmony_ci try_to_freeze(); 25362306a36Sopenharmony_ci if (!signalled ()) 25462306a36Sopenharmony_ci status = 0; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci finish_wait(queue, &wait); 25762306a36Sopenharmony_ci return status; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * Generic NLM call 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic int 26462306a36Sopenharmony_cinlmclnt_call(const struct cred *cred, struct nlm_rqst *req, u32 proc) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct nlm_host *host = req->a_host; 26762306a36Sopenharmony_ci struct rpc_clnt *clnt; 26862306a36Sopenharmony_ci struct nlm_args *argp = &req->a_args; 26962306a36Sopenharmony_ci struct nlm_res *resp = &req->a_res; 27062306a36Sopenharmony_ci struct rpc_message msg = { 27162306a36Sopenharmony_ci .rpc_argp = argp, 27262306a36Sopenharmony_ci .rpc_resp = resp, 27362306a36Sopenharmony_ci .rpc_cred = cred, 27462306a36Sopenharmony_ci }; 27562306a36Sopenharmony_ci int status; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci dprintk("lockd: call procedure %d on %s\n", 27862306a36Sopenharmony_ci (int)proc, host->h_name); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci do { 28162306a36Sopenharmony_ci if (host->h_reclaiming && !argp->reclaim) 28262306a36Sopenharmony_ci goto in_grace_period; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* If we have no RPC client yet, create one. */ 28562306a36Sopenharmony_ci if ((clnt = nlm_bind_host(host)) == NULL) 28662306a36Sopenharmony_ci return -ENOLCK; 28762306a36Sopenharmony_ci msg.rpc_proc = &clnt->cl_procinfo[proc]; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Perform the RPC call. If an error occurs, try again */ 29062306a36Sopenharmony_ci if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) { 29162306a36Sopenharmony_ci dprintk("lockd: rpc_call returned error %d\n", -status); 29262306a36Sopenharmony_ci switch (status) { 29362306a36Sopenharmony_ci case -EPROTONOSUPPORT: 29462306a36Sopenharmony_ci status = -EINVAL; 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci case -ECONNREFUSED: 29762306a36Sopenharmony_ci case -ETIMEDOUT: 29862306a36Sopenharmony_ci case -ENOTCONN: 29962306a36Sopenharmony_ci nlm_rebind_host(host); 30062306a36Sopenharmony_ci status = -EAGAIN; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case -ERESTARTSYS: 30362306a36Sopenharmony_ci return signalled () ? -EINTR : status; 30462306a36Sopenharmony_ci default: 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci } else 30962306a36Sopenharmony_ci if (resp->status == nlm_lck_denied_grace_period) { 31062306a36Sopenharmony_ci dprintk("lockd: server in grace period\n"); 31162306a36Sopenharmony_ci if (argp->reclaim) { 31262306a36Sopenharmony_ci printk(KERN_WARNING 31362306a36Sopenharmony_ci "lockd: spurious grace period reject?!\n"); 31462306a36Sopenharmony_ci return -ENOLCK; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci if (!argp->reclaim) { 31862306a36Sopenharmony_ci /* We appear to be out of the grace period */ 31962306a36Sopenharmony_ci wake_up_all(&host->h_gracewait); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci dprintk("lockd: server returns status %d\n", 32262306a36Sopenharmony_ci ntohl(resp->status)); 32362306a36Sopenharmony_ci return 0; /* Okay, call complete */ 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ciin_grace_period: 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * The server has rebooted and appears to be in the grace 32962306a36Sopenharmony_ci * period during which locks are only allowed to be 33062306a36Sopenharmony_ci * reclaimed. 33162306a36Sopenharmony_ci * We can only back off and try again later. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci status = nlm_wait_on_grace(&host->h_gracewait); 33462306a36Sopenharmony_ci } while (status == 0); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return status; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* 34062306a36Sopenharmony_ci * Generic NLM call, async version. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_cistatic struct rpc_task *__nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct nlm_host *host = req->a_host; 34562306a36Sopenharmony_ci struct rpc_clnt *clnt; 34662306a36Sopenharmony_ci struct rpc_task_setup task_setup_data = { 34762306a36Sopenharmony_ci .rpc_message = msg, 34862306a36Sopenharmony_ci .callback_ops = tk_ops, 34962306a36Sopenharmony_ci .callback_data = req, 35062306a36Sopenharmony_ci .flags = RPC_TASK_ASYNC, 35162306a36Sopenharmony_ci }; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci dprintk("lockd: call procedure %d on %s (async)\n", 35462306a36Sopenharmony_ci (int)proc, host->h_name); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* If we have no RPC client yet, create one. */ 35762306a36Sopenharmony_ci clnt = nlm_bind_host(host); 35862306a36Sopenharmony_ci if (clnt == NULL) 35962306a36Sopenharmony_ci goto out_err; 36062306a36Sopenharmony_ci msg->rpc_proc = &clnt->cl_procinfo[proc]; 36162306a36Sopenharmony_ci task_setup_data.rpc_client = clnt; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* bootstrap and kick off the async RPC call */ 36462306a36Sopenharmony_ci return rpc_run_task(&task_setup_data); 36562306a36Sopenharmony_ciout_err: 36662306a36Sopenharmony_ci tk_ops->rpc_release(req); 36762306a36Sopenharmony_ci return ERR_PTR(-ENOLCK); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int nlm_do_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct rpc_task *task; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci task = __nlm_async_call(req, proc, msg, tk_ops); 37562306a36Sopenharmony_ci if (IS_ERR(task)) 37662306a36Sopenharmony_ci return PTR_ERR(task); 37762306a36Sopenharmony_ci rpc_put_task(task); 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* 38262306a36Sopenharmony_ci * NLM asynchronous call. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ciint nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct rpc_message msg = { 38762306a36Sopenharmony_ci .rpc_argp = &req->a_args, 38862306a36Sopenharmony_ci .rpc_resp = &req->a_res, 38962306a36Sopenharmony_ci }; 39062306a36Sopenharmony_ci return nlm_do_async_call(req, proc, &msg, tk_ops); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ciint nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct rpc_message msg = { 39662306a36Sopenharmony_ci .rpc_argp = &req->a_res, 39762306a36Sopenharmony_ci }; 39862306a36Sopenharmony_ci return nlm_do_async_call(req, proc, &msg, tk_ops); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* 40262306a36Sopenharmony_ci * NLM client asynchronous call. 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci * Note that although the calls are asynchronous, and are therefore 40562306a36Sopenharmony_ci * guaranteed to complete, we still always attempt to wait for 40662306a36Sopenharmony_ci * completion in order to be able to correctly track the lock 40762306a36Sopenharmony_ci * state. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_cistatic int nlmclnt_async_call(const struct cred *cred, struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct rpc_message msg = { 41262306a36Sopenharmony_ci .rpc_argp = &req->a_args, 41362306a36Sopenharmony_ci .rpc_resp = &req->a_res, 41462306a36Sopenharmony_ci .rpc_cred = cred, 41562306a36Sopenharmony_ci }; 41662306a36Sopenharmony_ci struct rpc_task *task; 41762306a36Sopenharmony_ci int err; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci task = __nlm_async_call(req, proc, &msg, tk_ops); 42062306a36Sopenharmony_ci if (IS_ERR(task)) 42162306a36Sopenharmony_ci return PTR_ERR(task); 42262306a36Sopenharmony_ci err = rpc_wait_for_completion_task(task); 42362306a36Sopenharmony_ci rpc_put_task(task); 42462306a36Sopenharmony_ci return err; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* 42862306a36Sopenharmony_ci * TEST for the presence of a conflicting lock 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic int 43162306a36Sopenharmony_cinlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci int status; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_TEST); 43662306a36Sopenharmony_ci if (status < 0) 43762306a36Sopenharmony_ci goto out; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci switch (req->a_res.status) { 44062306a36Sopenharmony_ci case nlm_granted: 44162306a36Sopenharmony_ci fl->fl_type = F_UNLCK; 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci case nlm_lck_denied: 44462306a36Sopenharmony_ci /* 44562306a36Sopenharmony_ci * Report the conflicting lock back to the application. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci fl->fl_start = req->a_res.lock.fl.fl_start; 44862306a36Sopenharmony_ci fl->fl_end = req->a_res.lock.fl.fl_end; 44962306a36Sopenharmony_ci fl->fl_type = req->a_res.lock.fl.fl_type; 45062306a36Sopenharmony_ci fl->fl_pid = -req->a_res.lock.fl.fl_pid; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci default: 45362306a36Sopenharmony_ci status = nlm_stat_to_errno(req->a_res.status); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ciout: 45662306a36Sopenharmony_ci trace_nlmclnt_test(&req->a_args.lock, 45762306a36Sopenharmony_ci (const struct sockaddr *)&req->a_host->h_addr, 45862306a36Sopenharmony_ci req->a_host->h_addrlen, req->a_res.status); 45962306a36Sopenharmony_ci nlmclnt_release_call(req); 46062306a36Sopenharmony_ci return status; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock); 46662306a36Sopenharmony_ci new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state; 46762306a36Sopenharmony_ci new->fl_u.nfs_fl.owner = nlmclnt_get_lockowner(fl->fl_u.nfs_fl.owner); 46862306a36Sopenharmony_ci list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted); 46962306a36Sopenharmony_ci spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void nlmclnt_locks_release_private(struct file_lock *fl) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock); 47562306a36Sopenharmony_ci list_del(&fl->fl_u.nfs_fl.list); 47662306a36Sopenharmony_ci spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock); 47762306a36Sopenharmony_ci nlmclnt_put_lockowner(fl->fl_u.nfs_fl.owner); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic const struct file_lock_operations nlmclnt_lock_ops = { 48162306a36Sopenharmony_ci .fl_copy_lock = nlmclnt_locks_copy_lock, 48262306a36Sopenharmony_ci .fl_release_private = nlmclnt_locks_release_private, 48362306a36Sopenharmony_ci}; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci fl->fl_u.nfs_fl.state = 0; 48862306a36Sopenharmony_ci fl->fl_u.nfs_fl.owner = nlmclnt_find_lockowner(host, fl->fl_owner); 48962306a36Sopenharmony_ci INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list); 49062306a36Sopenharmony_ci fl->fl_ops = &nlmclnt_lock_ops; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic int do_vfs_lock(struct file_lock *fl) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci return locks_lock_file_wait(fl->fl_file, fl); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* 49962306a36Sopenharmony_ci * LOCK: Try to create a lock 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * Programmer Harassment Alert 50262306a36Sopenharmony_ci * 50362306a36Sopenharmony_ci * When given a blocking lock request in a sync RPC call, the HPUX lockd 50462306a36Sopenharmony_ci * will faithfully return LCK_BLOCKED but never cares to notify us when 50562306a36Sopenharmony_ci * the lock could be granted. This way, our local process could hang 50662306a36Sopenharmony_ci * around forever waiting for the callback. 50762306a36Sopenharmony_ci * 50862306a36Sopenharmony_ci * Solution A: Implement busy-waiting 50962306a36Sopenharmony_ci * Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES}) 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * For now I am implementing solution A, because I hate the idea of 51262306a36Sopenharmony_ci * re-implementing lockd for a third time in two months. The async 51362306a36Sopenharmony_ci * calls shouldn't be too hard to do, however. 51462306a36Sopenharmony_ci * 51562306a36Sopenharmony_ci * This is one of the lovely things about standards in the NFS area: 51662306a36Sopenharmony_ci * they're so soft and squishy you can't really blame HP for doing this. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_cistatic int 51962306a36Sopenharmony_cinlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci const struct cred *cred = nfs_file_cred(fl->fl_file); 52262306a36Sopenharmony_ci struct nlm_host *host = req->a_host; 52362306a36Sopenharmony_ci struct nlm_res *resp = &req->a_res; 52462306a36Sopenharmony_ci struct nlm_wait block; 52562306a36Sopenharmony_ci unsigned char fl_flags = fl->fl_flags; 52662306a36Sopenharmony_ci unsigned char fl_type; 52762306a36Sopenharmony_ci __be32 b_status; 52862306a36Sopenharmony_ci int status = -ENOLCK; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (nsm_monitor(host) < 0) 53162306a36Sopenharmony_ci goto out; 53262306a36Sopenharmony_ci req->a_args.state = nsm_local_state; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci fl->fl_flags |= FL_ACCESS; 53562306a36Sopenharmony_ci status = do_vfs_lock(fl); 53662306a36Sopenharmony_ci fl->fl_flags = fl_flags; 53762306a36Sopenharmony_ci if (status < 0) 53862306a36Sopenharmony_ci goto out; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci nlmclnt_prepare_block(&block, host, fl); 54162306a36Sopenharmony_ciagain: 54262306a36Sopenharmony_ci /* 54362306a36Sopenharmony_ci * Initialise resp->status to a valid non-zero value, 54462306a36Sopenharmony_ci * since 0 == nlm_lck_granted 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_ci resp->status = nlm_lck_blocked; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 54962306a36Sopenharmony_ci * A GRANTED callback can come at any time -- even before the reply 55062306a36Sopenharmony_ci * to the LOCK request arrives, so we queue the wait before 55162306a36Sopenharmony_ci * requesting the lock. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci nlmclnt_queue_block(&block); 55462306a36Sopenharmony_ci for (;;) { 55562306a36Sopenharmony_ci /* Reboot protection */ 55662306a36Sopenharmony_ci fl->fl_u.nfs_fl.state = host->h_state; 55762306a36Sopenharmony_ci status = nlmclnt_call(cred, req, NLMPROC_LOCK); 55862306a36Sopenharmony_ci if (status < 0) 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci /* Did a reclaimer thread notify us of a server reboot? */ 56162306a36Sopenharmony_ci if (resp->status == nlm_lck_denied_grace_period) 56262306a36Sopenharmony_ci continue; 56362306a36Sopenharmony_ci if (resp->status != nlm_lck_blocked) 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci /* Wait on an NLM blocking lock */ 56662306a36Sopenharmony_ci status = nlmclnt_wait(&block, req, NLMCLNT_POLL_TIMEOUT); 56762306a36Sopenharmony_ci if (status < 0) 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci if (block.b_status != nlm_lck_blocked) 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci b_status = nlmclnt_dequeue_block(&block); 57362306a36Sopenharmony_ci if (resp->status == nlm_lck_blocked) 57462306a36Sopenharmony_ci resp->status = b_status; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* if we were interrupted while blocking, then cancel the lock request 57762306a36Sopenharmony_ci * and exit 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci if (resp->status == nlm_lck_blocked) { 58062306a36Sopenharmony_ci if (!req->a_args.block) 58162306a36Sopenharmony_ci goto out_unlock; 58262306a36Sopenharmony_ci if (nlmclnt_cancel(host, req->a_args.block, fl) == 0) 58362306a36Sopenharmony_ci goto out; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (resp->status == nlm_granted) { 58762306a36Sopenharmony_ci down_read(&host->h_rwsem); 58862306a36Sopenharmony_ci /* Check whether or not the server has rebooted */ 58962306a36Sopenharmony_ci if (fl->fl_u.nfs_fl.state != host->h_state) { 59062306a36Sopenharmony_ci up_read(&host->h_rwsem); 59162306a36Sopenharmony_ci goto again; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci /* Ensure the resulting lock will get added to granted list */ 59462306a36Sopenharmony_ci fl->fl_flags |= FL_SLEEP; 59562306a36Sopenharmony_ci if (do_vfs_lock(fl) < 0) 59662306a36Sopenharmony_ci printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__); 59762306a36Sopenharmony_ci up_read(&host->h_rwsem); 59862306a36Sopenharmony_ci fl->fl_flags = fl_flags; 59962306a36Sopenharmony_ci status = 0; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci if (status < 0) 60262306a36Sopenharmony_ci goto out_unlock; 60362306a36Sopenharmony_ci /* 60462306a36Sopenharmony_ci * EAGAIN doesn't make sense for sleeping locks, and in some 60562306a36Sopenharmony_ci * cases NLM_LCK_DENIED is returned for a permanent error. So 60662306a36Sopenharmony_ci * turn it into an ENOLCK. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ci if (resp->status == nlm_lck_denied && (fl_flags & FL_SLEEP)) 60962306a36Sopenharmony_ci status = -ENOLCK; 61062306a36Sopenharmony_ci else 61162306a36Sopenharmony_ci status = nlm_stat_to_errno(resp->status); 61262306a36Sopenharmony_ciout: 61362306a36Sopenharmony_ci trace_nlmclnt_lock(&req->a_args.lock, 61462306a36Sopenharmony_ci (const struct sockaddr *)&req->a_host->h_addr, 61562306a36Sopenharmony_ci req->a_host->h_addrlen, req->a_res.status); 61662306a36Sopenharmony_ci nlmclnt_release_call(req); 61762306a36Sopenharmony_ci return status; 61862306a36Sopenharmony_ciout_unlock: 61962306a36Sopenharmony_ci /* Fatal error: ensure that we remove the lock altogether */ 62062306a36Sopenharmony_ci trace_nlmclnt_lock(&req->a_args.lock, 62162306a36Sopenharmony_ci (const struct sockaddr *)&req->a_host->h_addr, 62262306a36Sopenharmony_ci req->a_host->h_addrlen, req->a_res.status); 62362306a36Sopenharmony_ci dprintk("lockd: lock attempt ended in fatal error.\n" 62462306a36Sopenharmony_ci " Attempting to unlock.\n"); 62562306a36Sopenharmony_ci fl_type = fl->fl_type; 62662306a36Sopenharmony_ci fl->fl_type = F_UNLCK; 62762306a36Sopenharmony_ci down_read(&host->h_rwsem); 62862306a36Sopenharmony_ci do_vfs_lock(fl); 62962306a36Sopenharmony_ci up_read(&host->h_rwsem); 63062306a36Sopenharmony_ci fl->fl_type = fl_type; 63162306a36Sopenharmony_ci fl->fl_flags = fl_flags; 63262306a36Sopenharmony_ci nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); 63362306a36Sopenharmony_ci return status; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci/* 63762306a36Sopenharmony_ci * RECLAIM: Try to reclaim a lock 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ciint 64062306a36Sopenharmony_cinlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl, 64162306a36Sopenharmony_ci struct nlm_rqst *req) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci int status; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 64662306a36Sopenharmony_ci locks_init_lock(&req->a_args.lock.fl); 64762306a36Sopenharmony_ci locks_init_lock(&req->a_res.lock.fl); 64862306a36Sopenharmony_ci req->a_host = host; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* Set up the argument struct */ 65162306a36Sopenharmony_ci nlmclnt_setlockargs(req, fl); 65262306a36Sopenharmony_ci req->a_args.reclaim = 1; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_LOCK); 65562306a36Sopenharmony_ci if (status >= 0 && req->a_res.status == nlm_granted) 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d " 65962306a36Sopenharmony_ci "(errno %d, status %d)\n", fl->fl_pid, 66062306a36Sopenharmony_ci status, ntohl(req->a_res.status)); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* 66362306a36Sopenharmony_ci * FIXME: This is a serious failure. We can 66462306a36Sopenharmony_ci * 66562306a36Sopenharmony_ci * a. Ignore the problem 66662306a36Sopenharmony_ci * b. Send the owning process some signal (Linux doesn't have 66762306a36Sopenharmony_ci * SIGLOST, though...) 66862306a36Sopenharmony_ci * c. Retry the operation 66962306a36Sopenharmony_ci * 67062306a36Sopenharmony_ci * Until someone comes up with a simple implementation 67162306a36Sopenharmony_ci * for b or c, I'll choose option a. 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return -ENOLCK; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/* 67862306a36Sopenharmony_ci * UNLOCK: remove an existing lock 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_cistatic int 68162306a36Sopenharmony_cinlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct nlm_host *host = req->a_host; 68462306a36Sopenharmony_ci struct nlm_res *resp = &req->a_res; 68562306a36Sopenharmony_ci int status; 68662306a36Sopenharmony_ci unsigned char fl_flags = fl->fl_flags; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* 68962306a36Sopenharmony_ci * Note: the server is supposed to either grant us the unlock 69062306a36Sopenharmony_ci * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either 69162306a36Sopenharmony_ci * case, we want to unlock. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci fl->fl_flags |= FL_EXISTS; 69462306a36Sopenharmony_ci down_read(&host->h_rwsem); 69562306a36Sopenharmony_ci status = do_vfs_lock(fl); 69662306a36Sopenharmony_ci up_read(&host->h_rwsem); 69762306a36Sopenharmony_ci fl->fl_flags = fl_flags; 69862306a36Sopenharmony_ci if (status == -ENOENT) { 69962306a36Sopenharmony_ci status = 0; 70062306a36Sopenharmony_ci goto out; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci refcount_inc(&req->a_count); 70462306a36Sopenharmony_ci status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req, 70562306a36Sopenharmony_ci NLMPROC_UNLOCK, &nlmclnt_unlock_ops); 70662306a36Sopenharmony_ci if (status < 0) 70762306a36Sopenharmony_ci goto out; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (resp->status == nlm_granted) 71062306a36Sopenharmony_ci goto out; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (resp->status != nlm_lck_denied_nolocks) 71362306a36Sopenharmony_ci printk("lockd: unexpected unlock status: %d\n", 71462306a36Sopenharmony_ci ntohl(resp->status)); 71562306a36Sopenharmony_ci /* What to do now? I'm out of my depth... */ 71662306a36Sopenharmony_ci status = -ENOLCK; 71762306a36Sopenharmony_ciout: 71862306a36Sopenharmony_ci trace_nlmclnt_unlock(&req->a_args.lock, 71962306a36Sopenharmony_ci (const struct sockaddr *)&req->a_host->h_addr, 72062306a36Sopenharmony_ci req->a_host->h_addrlen, req->a_res.status); 72162306a36Sopenharmony_ci nlmclnt_release_call(req); 72262306a36Sopenharmony_ci return status; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void nlmclnt_unlock_prepare(struct rpc_task *task, void *data) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct nlm_rqst *req = data; 72862306a36Sopenharmony_ci const struct nlmclnt_operations *nlmclnt_ops = req->a_host->h_nlmclnt_ops; 72962306a36Sopenharmony_ci bool defer_call = false; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (nlmclnt_ops && nlmclnt_ops->nlmclnt_unlock_prepare) 73262306a36Sopenharmony_ci defer_call = nlmclnt_ops->nlmclnt_unlock_prepare(task, req->a_callback_data); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!defer_call) 73562306a36Sopenharmony_ci rpc_call_start(task); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic void nlmclnt_unlock_callback(struct rpc_task *task, void *data) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct nlm_rqst *req = data; 74162306a36Sopenharmony_ci u32 status = ntohl(req->a_res.status); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (RPC_SIGNALLED(task)) 74462306a36Sopenharmony_ci goto die; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (task->tk_status < 0) { 74762306a36Sopenharmony_ci dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status); 74862306a36Sopenharmony_ci switch (task->tk_status) { 74962306a36Sopenharmony_ci case -EACCES: 75062306a36Sopenharmony_ci case -EIO: 75162306a36Sopenharmony_ci goto die; 75262306a36Sopenharmony_ci default: 75362306a36Sopenharmony_ci goto retry_rebind; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci if (status == NLM_LCK_DENIED_GRACE_PERIOD) { 75762306a36Sopenharmony_ci rpc_delay(task, NLMCLNT_GRACE_WAIT); 75862306a36Sopenharmony_ci goto retry_unlock; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci if (status != NLM_LCK_GRANTED) 76162306a36Sopenharmony_ci printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status); 76262306a36Sopenharmony_cidie: 76362306a36Sopenharmony_ci return; 76462306a36Sopenharmony_ci retry_rebind: 76562306a36Sopenharmony_ci nlm_rebind_host(req->a_host); 76662306a36Sopenharmony_ci retry_unlock: 76762306a36Sopenharmony_ci rpc_restart_call(task); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_unlock_ops = { 77162306a36Sopenharmony_ci .rpc_call_prepare = nlmclnt_unlock_prepare, 77262306a36Sopenharmony_ci .rpc_call_done = nlmclnt_unlock_callback, 77362306a36Sopenharmony_ci .rpc_release = nlmclnt_rpc_release, 77462306a36Sopenharmony_ci}; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci/* 77762306a36Sopenharmony_ci * Cancel a blocked lock request. 77862306a36Sopenharmony_ci * We always use an async RPC call for this in order not to hang a 77962306a36Sopenharmony_ci * process that has been Ctrl-C'ed. 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_cistatic int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct nlm_rqst *req; 78462306a36Sopenharmony_ci int status; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci dprintk("lockd: blocking lock attempt was interrupted by a signal.\n" 78762306a36Sopenharmony_ci " Attempting to cancel lock.\n"); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci req = nlm_alloc_call(host); 79062306a36Sopenharmony_ci if (!req) 79162306a36Sopenharmony_ci return -ENOMEM; 79262306a36Sopenharmony_ci req->a_flags = RPC_TASK_ASYNC; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci nlmclnt_setlockargs(req, fl); 79562306a36Sopenharmony_ci req->a_args.block = block; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci refcount_inc(&req->a_count); 79862306a36Sopenharmony_ci status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req, 79962306a36Sopenharmony_ci NLMPROC_CANCEL, &nlmclnt_cancel_ops); 80062306a36Sopenharmony_ci if (status == 0 && req->a_res.status == nlm_lck_denied) 80162306a36Sopenharmony_ci status = -ENOLCK; 80262306a36Sopenharmony_ci nlmclnt_release_call(req); 80362306a36Sopenharmony_ci return status; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic void nlmclnt_cancel_callback(struct rpc_task *task, void *data) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct nlm_rqst *req = data; 80962306a36Sopenharmony_ci u32 status = ntohl(req->a_res.status); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (RPC_SIGNALLED(task)) 81262306a36Sopenharmony_ci goto die; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (task->tk_status < 0) { 81562306a36Sopenharmony_ci dprintk("lockd: CANCEL call error %d, retrying.\n", 81662306a36Sopenharmony_ci task->tk_status); 81762306a36Sopenharmony_ci goto retry_cancel; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci switch (status) { 82162306a36Sopenharmony_ci case NLM_LCK_GRANTED: 82262306a36Sopenharmony_ci case NLM_LCK_DENIED_GRACE_PERIOD: 82362306a36Sopenharmony_ci case NLM_LCK_DENIED: 82462306a36Sopenharmony_ci /* Everything's good */ 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci case NLM_LCK_DENIED_NOLOCKS: 82762306a36Sopenharmony_ci dprintk("lockd: CANCEL failed (server has no locks)\n"); 82862306a36Sopenharmony_ci goto retry_cancel; 82962306a36Sopenharmony_ci default: 83062306a36Sopenharmony_ci printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n", 83162306a36Sopenharmony_ci status); 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cidie: 83562306a36Sopenharmony_ci return; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ciretry_cancel: 83862306a36Sopenharmony_ci /* Don't ever retry more than 3 times */ 83962306a36Sopenharmony_ci if (req->a_retries++ >= NLMCLNT_MAX_RETRIES) 84062306a36Sopenharmony_ci goto die; 84162306a36Sopenharmony_ci nlm_rebind_host(req->a_host); 84262306a36Sopenharmony_ci rpc_restart_call(task); 84362306a36Sopenharmony_ci rpc_delay(task, 30 * HZ); 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_cancel_ops = { 84762306a36Sopenharmony_ci .rpc_call_done = nlmclnt_cancel_callback, 84862306a36Sopenharmony_ci .rpc_release = nlmclnt_rpc_release, 84962306a36Sopenharmony_ci}; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci/* 85262306a36Sopenharmony_ci * Convert an NLM status code to a generic kernel errno 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_cistatic int 85562306a36Sopenharmony_cinlm_stat_to_errno(__be32 status) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci switch(ntohl(status)) { 85862306a36Sopenharmony_ci case NLM_LCK_GRANTED: 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci case NLM_LCK_DENIED: 86162306a36Sopenharmony_ci return -EAGAIN; 86262306a36Sopenharmony_ci case NLM_LCK_DENIED_NOLOCKS: 86362306a36Sopenharmony_ci case NLM_LCK_DENIED_GRACE_PERIOD: 86462306a36Sopenharmony_ci return -ENOLCK; 86562306a36Sopenharmony_ci case NLM_LCK_BLOCKED: 86662306a36Sopenharmony_ci printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n"); 86762306a36Sopenharmony_ci return -ENOLCK; 86862306a36Sopenharmony_ci#ifdef CONFIG_LOCKD_V4 86962306a36Sopenharmony_ci case NLM_DEADLCK: 87062306a36Sopenharmony_ci return -EDEADLK; 87162306a36Sopenharmony_ci case NLM_ROFS: 87262306a36Sopenharmony_ci return -EROFS; 87362306a36Sopenharmony_ci case NLM_STALE_FH: 87462306a36Sopenharmony_ci return -ESTALE; 87562306a36Sopenharmony_ci case NLM_FBIG: 87662306a36Sopenharmony_ci return -EOVERFLOW; 87762306a36Sopenharmony_ci case NLM_FAILED: 87862306a36Sopenharmony_ci return -ENOLCK; 87962306a36Sopenharmony_ci#endif 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci printk(KERN_NOTICE "lockd: unexpected server status %d\n", 88262306a36Sopenharmony_ci ntohl(status)); 88362306a36Sopenharmony_ci return -ENOLCK; 88462306a36Sopenharmony_ci} 885