1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * linux/fs/lockd/clntproc.c 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * RPC procedures for the client side NLM implementation 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds #include <linux/module.h> 115a0e3ad6STejun Heo #include <linux/slab.h> 121da177e4SLinus Torvalds #include <linux/types.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/fs.h> 155970e15dSJeff Layton #include <linux/filelock.h> 161da177e4SLinus Torvalds #include <linux/nfs_fs.h> 171da177e4SLinus Torvalds #include <linux/utsname.h> 187dfb7103SNigel Cunningham #include <linux/freezer.h> 191da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h> 201da177e4SLinus Torvalds #include <linux/sunrpc/svc.h> 211da177e4SLinus Torvalds #include <linux/lockd/lockd.h> 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #define NLMDBG_FACILITY NLMDBG_CLIENT 241da177e4SLinus Torvalds #define NLMCLNT_GRACE_WAIT (5*HZ) 25ecdbf769STrond Myklebust #define NLMCLNT_POLL_TIMEOUT (30*HZ) 26aaaa9942STrond Myklebust #define NLMCLNT_MAX_RETRIES 3 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds static int nlmclnt_test(struct nlm_rqst *, struct file_lock *); 291da177e4SLinus Torvalds static int nlmclnt_lock(struct nlm_rqst *, struct file_lock *); 301da177e4SLinus Torvalds static int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *); 31e8c5c045SAl Viro static int nlm_stat_to_errno(__be32 stat); 321da177e4SLinus Torvalds static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host); 3316fb2425STrond Myklebust static int nlmclnt_cancel(struct nlm_host *, int , struct file_lock *); 341da177e4SLinus Torvalds 35963d8fe5STrond Myklebust static const struct rpc_call_ops nlmclnt_unlock_ops; 36963d8fe5STrond Myklebust static const struct rpc_call_ops nlmclnt_cancel_ops; 37963d8fe5STrond Myklebust 381da177e4SLinus Torvalds /* 391da177e4SLinus Torvalds * Cookie counter for NLM requests 401da177e4SLinus Torvalds */ 41031d869dSOlaf Kirch static atomic_t nlm_cookie = ATOMIC_INIT(0x1234); 421da177e4SLinus Torvalds 43031d869dSOlaf Kirch void nlmclnt_next_cookie(struct nlm_cookie *c) 441da177e4SLinus Torvalds { 45031d869dSOlaf Kirch u32 cookie = atomic_inc_return(&nlm_cookie); 46031d869dSOlaf Kirch 47031d869dSOlaf Kirch memcpy(c->data, &cookie, 4); 481da177e4SLinus Torvalds c->len=4; 491da177e4SLinus Torvalds } 501da177e4SLinus Torvalds 519de3ec1dSBenjamin Coddington static struct nlm_lockowner * 529de3ec1dSBenjamin Coddington nlmclnt_get_lockowner(struct nlm_lockowner *lockowner) 531da177e4SLinus Torvalds { 54431f125bSElena Reshetova refcount_inc(&lockowner->count); 551da177e4SLinus Torvalds return lockowner; 561da177e4SLinus Torvalds } 571da177e4SLinus Torvalds 58291adeb2SYueHaibing static void nlmclnt_put_lockowner(struct nlm_lockowner *lockowner) 591da177e4SLinus Torvalds { 60431f125bSElena Reshetova if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock)) 611da177e4SLinus Torvalds return; 621da177e4SLinus Torvalds list_del(&lockowner->list); 631da177e4SLinus Torvalds spin_unlock(&lockowner->host->h_lock); 648ea6ecc8SChuck Lever nlmclnt_release_host(lockowner->host); 651da177e4SLinus Torvalds kfree(lockowner); 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds static inline int nlm_pidbusy(struct nlm_host *host, uint32_t pid) 691da177e4SLinus Torvalds { 701da177e4SLinus Torvalds struct nlm_lockowner *lockowner; 711da177e4SLinus Torvalds list_for_each_entry(lockowner, &host->h_lockowners, list) { 721da177e4SLinus Torvalds if (lockowner->pid == pid) 731da177e4SLinus Torvalds return -EBUSY; 741da177e4SLinus Torvalds } 751da177e4SLinus Torvalds return 0; 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds static inline uint32_t __nlm_alloc_pid(struct nlm_host *host) 791da177e4SLinus Torvalds { 801da177e4SLinus Torvalds uint32_t res; 811da177e4SLinus Torvalds do { 821da177e4SLinus Torvalds res = host->h_pidcount++; 831da177e4SLinus Torvalds } while (nlm_pidbusy(host, res) < 0); 841da177e4SLinus Torvalds return res; 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 879de3ec1dSBenjamin Coddington static struct nlm_lockowner *__nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner) 881da177e4SLinus Torvalds { 891da177e4SLinus Torvalds struct nlm_lockowner *lockowner; 901da177e4SLinus Torvalds list_for_each_entry(lockowner, &host->h_lockowners, list) { 911da177e4SLinus Torvalds if (lockowner->owner != owner) 921da177e4SLinus Torvalds continue; 939de3ec1dSBenjamin Coddington return nlmclnt_get_lockowner(lockowner); 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds return NULL; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 989de3ec1dSBenjamin Coddington static struct nlm_lockowner *nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner) 991da177e4SLinus Torvalds { 1001da177e4SLinus Torvalds struct nlm_lockowner *res, *new = NULL; 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds spin_lock(&host->h_lock); 1039de3ec1dSBenjamin Coddington res = __nlmclnt_find_lockowner(host, owner); 1041da177e4SLinus Torvalds if (res == NULL) { 1051da177e4SLinus Torvalds spin_unlock(&host->h_lock); 106f52720caSPanagiotis Issaris new = kmalloc(sizeof(*new), GFP_KERNEL); 1071da177e4SLinus Torvalds spin_lock(&host->h_lock); 1089de3ec1dSBenjamin Coddington res = __nlmclnt_find_lockowner(host, owner); 1091da177e4SLinus Torvalds if (res == NULL && new != NULL) { 1101da177e4SLinus Torvalds res = new; 111431f125bSElena Reshetova refcount_set(&new->count, 1); 1121da177e4SLinus Torvalds new->owner = owner; 1131da177e4SLinus Torvalds new->pid = __nlm_alloc_pid(host); 1141da177e4SLinus Torvalds new->host = nlm_get_host(host); 1151da177e4SLinus Torvalds list_add(&new->list, &host->h_lockowners); 1161da177e4SLinus Torvalds new = NULL; 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds spin_unlock(&host->h_lock); 1201da177e4SLinus Torvalds kfree(new); 1211da177e4SLinus Torvalds return res; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds /* 1251da177e4SLinus Torvalds * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls 1261da177e4SLinus Torvalds */ 1271da177e4SLinus Torvalds static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) 1281da177e4SLinus Torvalds { 1291da177e4SLinus Torvalds struct nlm_args *argp = &req->a_args; 1301da177e4SLinus Torvalds struct nlm_lock *lock = &argp->lock; 1319a1b6bf8STrond Myklebust char *nodename = req->a_host->h_rpcclnt->cl_nodename; 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds nlmclnt_next_cookie(&argp->cookie); 134*c65454a9SJeff Layton memcpy(&lock->fh, NFS_FH(file_inode(fl->fl_file)), sizeof(struct nfs_fh)); 1359a1b6bf8STrond Myklebust lock->caller = nodename; 1361da177e4SLinus Torvalds lock->oh.data = req->a_owner; 1377bab377fSTrond Myklebust lock->oh.len = snprintf(req->a_owner, sizeof(req->a_owner), "%u@%s", 1387bab377fSTrond Myklebust (unsigned int)fl->fl_u.nfs_fl.owner->pid, 1399a1b6bf8STrond Myklebust nodename); 1407bab377fSTrond Myklebust lock->svid = fl->fl_u.nfs_fl.owner->pid; 1413a649b88STrond Myklebust lock->fl.fl_start = fl->fl_start; 1423a649b88STrond Myklebust lock->fl.fl_end = fl->fl_end; 1433a649b88STrond Myklebust lock->fl.fl_type = fl->fl_type; 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds static void nlmclnt_release_lockargs(struct nlm_rqst *req) 1471da177e4SLinus Torvalds { 14826269348STrond Myklebust WARN_ON_ONCE(req->a_args.lock.fl.fl_ops != NULL); 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds 1511093a60eSChuck Lever /** 1521093a60eSChuck Lever * nlmclnt_proc - Perform a single client-side lock request 1531093a60eSChuck Lever * @host: address of a valid nlm_host context representing the NLM server 1541093a60eSChuck Lever * @cmd: fcntl-style file lock operation to perform 1551093a60eSChuck Lever * @fl: address of arguments for the lock operation 156b1ece737SBenjamin Coddington * @data: address of data to be sent to callback operations 1571093a60eSChuck Lever * 1581da177e4SLinus Torvalds */ 159b1ece737SBenjamin Coddington int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data) 1601da177e4SLinus Torvalds { 16192737230STrond Myklebust struct nlm_rqst *call; 1621093a60eSChuck Lever int status; 163b1ece737SBenjamin Coddington const struct nlmclnt_operations *nlmclnt_ops = host->h_nlmclnt_ops; 1641da177e4SLinus Torvalds 16592737230STrond Myklebust call = nlm_alloc_call(host); 16692737230STrond Myklebust if (call == NULL) 16792737230STrond Myklebust return -ENOMEM; 1681da177e4SLinus Torvalds 169b1ece737SBenjamin Coddington if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call) 170b1ece737SBenjamin Coddington nlmclnt_ops->nlmclnt_alloc_call(data); 171b1ece737SBenjamin Coddington 17292737230STrond Myklebust nlmclnt_locks_init_private(fl, host); 173bf884891SAl Viro if (!fl->fl_u.nfs_fl.owner) { 174bf884891SAl Viro /* lockowner allocation has failed */ 175bf884891SAl Viro nlmclnt_release_call(call); 176bf884891SAl Viro return -ENOMEM; 177bf884891SAl Viro } 17892737230STrond Myklebust /* Set up the argument struct */ 17992737230STrond Myklebust nlmclnt_setlockargs(call, fl); 180b1ece737SBenjamin Coddington call->a_callback_data = data; 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { 1831da177e4SLinus Torvalds if (fl->fl_type != F_UNLCK) { 1841da177e4SLinus Torvalds call->a_args.block = IS_SETLKW(cmd) ? 1 : 0; 1851da177e4SLinus Torvalds status = nlmclnt_lock(call, fl); 1861da177e4SLinus Torvalds } else 1871da177e4SLinus Torvalds status = nlmclnt_unlock(call, fl); 1881da177e4SLinus Torvalds } else if (IS_GETLK(cmd)) 1891da177e4SLinus Torvalds status = nlmclnt_test(call, fl); 1901da177e4SLinus Torvalds else 1911da177e4SLinus Torvalds status = -EINVAL; 19292737230STrond Myklebust fl->fl_ops->fl_release_private(fl); 19392737230STrond Myklebust fl->fl_ops = NULL; 19492737230STrond Myklebust 1951da177e4SLinus Torvalds dprintk("lockd: clnt proc returns %d\n", status); 1961da177e4SLinus Torvalds return status; 1971da177e4SLinus Torvalds } 1981093a60eSChuck Lever EXPORT_SYMBOL_GPL(nlmclnt_proc); 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds /* 2011da177e4SLinus Torvalds * Allocate an NLM RPC call struct 2021da177e4SLinus Torvalds */ 20392737230STrond Myklebust struct nlm_rqst *nlm_alloc_call(struct nlm_host *host) 2041da177e4SLinus Torvalds { 2051da177e4SLinus Torvalds struct nlm_rqst *call; 2061da177e4SLinus Torvalds 20736943fa4STrond Myklebust for(;;) { 20836943fa4STrond Myklebust call = kzalloc(sizeof(*call), GFP_KERNEL); 20936943fa4STrond Myklebust if (call != NULL) { 210fbca30c5SElena Reshetova refcount_set(&call->a_count, 1); 2111da177e4SLinus Torvalds locks_init_lock(&call->a_args.lock.fl); 2121da177e4SLinus Torvalds locks_init_lock(&call->a_res.lock.fl); 213446945abSAl Viro call->a_host = nlm_get_host(host); 2141da177e4SLinus Torvalds return call; 2151da177e4SLinus Torvalds } 21636943fa4STrond Myklebust if (signalled()) 21736943fa4STrond Myklebust break; 21892737230STrond Myklebust printk("nlm_alloc_call: failed, waiting for memory\n"); 219041e0e3bSNishanth Aravamudan schedule_timeout_interruptible(5*HZ); 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds return NULL; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2247db836d4SChuck Lever void nlmclnt_release_call(struct nlm_rqst *call) 22592737230STrond Myklebust { 226b1ece737SBenjamin Coddington const struct nlmclnt_operations *nlmclnt_ops = call->a_host->h_nlmclnt_ops; 227b1ece737SBenjamin Coddington 228fbca30c5SElena Reshetova if (!refcount_dec_and_test(&call->a_count)) 2295e7f37a7STrond Myklebust return; 230b1ece737SBenjamin Coddington if (nlmclnt_ops && nlmclnt_ops->nlmclnt_release_call) 231b1ece737SBenjamin Coddington nlmclnt_ops->nlmclnt_release_call(call->a_callback_data); 2328ea6ecc8SChuck Lever nlmclnt_release_host(call->a_host); 23392737230STrond Myklebust nlmclnt_release_lockargs(call); 23492737230STrond Myklebust kfree(call); 23592737230STrond Myklebust } 23692737230STrond Myklebust 23792737230STrond Myklebust static void nlmclnt_rpc_release(void *data) 23892737230STrond Myklebust { 2397db836d4SChuck Lever nlmclnt_release_call(data); 24092737230STrond Myklebust } 24192737230STrond Myklebust 2421da177e4SLinus Torvalds static int nlm_wait_on_grace(wait_queue_head_t *queue) 2431da177e4SLinus Torvalds { 2441da177e4SLinus Torvalds DEFINE_WAIT(wait); 2451da177e4SLinus Torvalds int status = -EINTR; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds prepare_to_wait(queue, &wait, TASK_INTERRUPTIBLE); 2481da177e4SLinus Torvalds if (!signalled ()) { 2491da177e4SLinus Torvalds schedule_timeout(NLMCLNT_GRACE_WAIT); 2503e1d1d28SChristoph Lameter try_to_freeze(); 2511da177e4SLinus Torvalds if (!signalled ()) 2521da177e4SLinus Torvalds status = 0; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds finish_wait(queue, &wait); 2551da177e4SLinus Torvalds return status; 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds /* 2591da177e4SLinus Torvalds * Generic NLM call 2601da177e4SLinus Torvalds */ 2611da177e4SLinus Torvalds static int 262a52458b4SNeilBrown nlmclnt_call(const struct cred *cred, struct nlm_rqst *req, u32 proc) 2631da177e4SLinus Torvalds { 2641da177e4SLinus Torvalds struct nlm_host *host = req->a_host; 2651da177e4SLinus Torvalds struct rpc_clnt *clnt; 2661da177e4SLinus Torvalds struct nlm_args *argp = &req->a_args; 2671da177e4SLinus Torvalds struct nlm_res *resp = &req->a_res; 2681da177e4SLinus Torvalds struct rpc_message msg = { 2691da177e4SLinus Torvalds .rpc_argp = argp, 2701da177e4SLinus Torvalds .rpc_resp = resp, 271d11d10ccSTrond Myklebust .rpc_cred = cred, 2721da177e4SLinus Torvalds }; 2731da177e4SLinus Torvalds int status; 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds dprintk("lockd: call procedure %d on %s\n", 2761da177e4SLinus Torvalds (int)proc, host->h_name); 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds do { 2791da177e4SLinus Torvalds if (host->h_reclaiming && !argp->reclaim) 2801da177e4SLinus Torvalds goto in_grace_period; 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds /* If we have no RPC client yet, create one. */ 2831da177e4SLinus Torvalds if ((clnt = nlm_bind_host(host)) == NULL) 2841da177e4SLinus Torvalds return -ENOLCK; 2851da177e4SLinus Torvalds msg.rpc_proc = &clnt->cl_procinfo[proc]; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds /* Perform the RPC call. If an error occurs, try again */ 2881da177e4SLinus Torvalds if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) { 2891da177e4SLinus Torvalds dprintk("lockd: rpc_call returned error %d\n", -status); 2901da177e4SLinus Torvalds switch (status) { 2911da177e4SLinus Torvalds case -EPROTONOSUPPORT: 2921da177e4SLinus Torvalds status = -EINVAL; 2931da177e4SLinus Torvalds break; 2941da177e4SLinus Torvalds case -ECONNREFUSED: 2951da177e4SLinus Torvalds case -ETIMEDOUT: 2961da177e4SLinus Torvalds case -ENOTCONN: 2971da177e4SLinus Torvalds nlm_rebind_host(host); 2981da177e4SLinus Torvalds status = -EAGAIN; 2991da177e4SLinus Torvalds break; 3001da177e4SLinus Torvalds case -ERESTARTSYS: 3011da177e4SLinus Torvalds return signalled () ? -EINTR : status; 3021da177e4SLinus Torvalds default: 3031da177e4SLinus Torvalds break; 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds break; 3061da177e4SLinus Torvalds } else 307e8c5c045SAl Viro if (resp->status == nlm_lck_denied_grace_period) { 3081da177e4SLinus Torvalds dprintk("lockd: server in grace period\n"); 3091da177e4SLinus Torvalds if (argp->reclaim) { 3101da177e4SLinus Torvalds printk(KERN_WARNING 3111da177e4SLinus Torvalds "lockd: spurious grace period reject?!\n"); 3121da177e4SLinus Torvalds return -ENOLCK; 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds } else { 3151da177e4SLinus Torvalds if (!argp->reclaim) { 3161da177e4SLinus Torvalds /* We appear to be out of the grace period */ 3171da177e4SLinus Torvalds wake_up_all(&host->h_gracewait); 3181da177e4SLinus Torvalds } 31982c2c8b8SVasily Averin dprintk("lockd: server returns status %d\n", 32082c2c8b8SVasily Averin ntohl(resp->status)); 3211da177e4SLinus Torvalds return 0; /* Okay, call complete */ 3221da177e4SLinus Torvalds } 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds in_grace_period: 3251da177e4SLinus Torvalds /* 3261da177e4SLinus Torvalds * The server has rebooted and appears to be in the grace 3271da177e4SLinus Torvalds * period during which locks are only allowed to be 3281da177e4SLinus Torvalds * reclaimed. 3291da177e4SLinus Torvalds * We can only back off and try again later. 3301da177e4SLinus Torvalds */ 3311da177e4SLinus Torvalds status = nlm_wait_on_grace(&host->h_gracewait); 3321da177e4SLinus Torvalds } while (status == 0); 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds return status; 3351da177e4SLinus Torvalds } 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds /* 3381da177e4SLinus Torvalds * Generic NLM call, async version. 3391da177e4SLinus Torvalds */ 340dc9d8d04STrond Myklebust static struct rpc_task *__nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) 3411da177e4SLinus Torvalds { 3421da177e4SLinus Torvalds struct nlm_host *host = req->a_host; 3431da177e4SLinus Torvalds struct rpc_clnt *clnt; 344dc9d8d04STrond Myklebust struct rpc_task_setup task_setup_data = { 345dc9d8d04STrond Myklebust .rpc_message = msg, 346dc9d8d04STrond Myklebust .callback_ops = tk_ops, 347dc9d8d04STrond Myklebust .callback_data = req, 348dc9d8d04STrond Myklebust .flags = RPC_TASK_ASYNC, 349dc9d8d04STrond Myklebust }; 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds dprintk("lockd: call procedure %d on %s (async)\n", 3521da177e4SLinus Torvalds (int)proc, host->h_name); 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds /* If we have no RPC client yet, create one. */ 35592737230STrond Myklebust clnt = nlm_bind_host(host); 35692737230STrond Myklebust if (clnt == NULL) 35792737230STrond Myklebust goto out_err; 358d4716624STrond Myklebust msg->rpc_proc = &clnt->cl_procinfo[proc]; 359dc9d8d04STrond Myklebust task_setup_data.rpc_client = clnt; 3601da177e4SLinus Torvalds 3611da177e4SLinus Torvalds /* bootstrap and kick off the async RPC call */ 362dc9d8d04STrond Myklebust return rpc_run_task(&task_setup_data); 36392737230STrond Myklebust out_err: 364a995e9ebSTrond Myklebust tk_ops->rpc_release(req); 365dc9d8d04STrond Myklebust return ERR_PTR(-ENOLCK); 3661da177e4SLinus Torvalds } 3671da177e4SLinus Torvalds 368dc9d8d04STrond Myklebust static int nlm_do_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) 369dc9d8d04STrond Myklebust { 370dc9d8d04STrond Myklebust struct rpc_task *task; 371dc9d8d04STrond Myklebust 372dc9d8d04STrond Myklebust task = __nlm_async_call(req, proc, msg, tk_ops); 373dc9d8d04STrond Myklebust if (IS_ERR(task)) 374dc9d8d04STrond Myklebust return PTR_ERR(task); 375dc9d8d04STrond Myklebust rpc_put_task(task); 376dc9d8d04STrond Myklebust return 0; 377dc9d8d04STrond Myklebust } 378dc9d8d04STrond Myklebust 379dc9d8d04STrond Myklebust /* 380dc9d8d04STrond Myklebust * NLM asynchronous call. 381dc9d8d04STrond Myklebust */ 382d4716624STrond Myklebust int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 383d4716624STrond Myklebust { 384d4716624STrond Myklebust struct rpc_message msg = { 385d4716624STrond Myklebust .rpc_argp = &req->a_args, 386d4716624STrond Myklebust .rpc_resp = &req->a_res, 387d4716624STrond Myklebust }; 388dc9d8d04STrond Myklebust return nlm_do_async_call(req, proc, &msg, tk_ops); 389d4716624STrond Myklebust } 390d4716624STrond Myklebust 391d4716624STrond Myklebust int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 392d4716624STrond Myklebust { 393d4716624STrond Myklebust struct rpc_message msg = { 394d4716624STrond Myklebust .rpc_argp = &req->a_res, 395d4716624STrond Myklebust }; 396dc9d8d04STrond Myklebust return nlm_do_async_call(req, proc, &msg, tk_ops); 397dc9d8d04STrond Myklebust } 398dc9d8d04STrond Myklebust 399dc9d8d04STrond Myklebust /* 400dc9d8d04STrond Myklebust * NLM client asynchronous call. 401dc9d8d04STrond Myklebust * 402dc9d8d04STrond Myklebust * Note that although the calls are asynchronous, and are therefore 403dc9d8d04STrond Myklebust * guaranteed to complete, we still always attempt to wait for 404dc9d8d04STrond Myklebust * completion in order to be able to correctly track the lock 405dc9d8d04STrond Myklebust * state. 406dc9d8d04STrond Myklebust */ 407a52458b4SNeilBrown static int nlmclnt_async_call(const struct cred *cred, struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) 408dc9d8d04STrond Myklebust { 409dc9d8d04STrond Myklebust struct rpc_message msg = { 410dc9d8d04STrond Myklebust .rpc_argp = &req->a_args, 411dc9d8d04STrond Myklebust .rpc_resp = &req->a_res, 412d11d10ccSTrond Myklebust .rpc_cred = cred, 413dc9d8d04STrond Myklebust }; 414dc9d8d04STrond Myklebust struct rpc_task *task; 415dc9d8d04STrond Myklebust int err; 416dc9d8d04STrond Myklebust 417dc9d8d04STrond Myklebust task = __nlm_async_call(req, proc, &msg, tk_ops); 418dc9d8d04STrond Myklebust if (IS_ERR(task)) 419dc9d8d04STrond Myklebust return PTR_ERR(task); 420dc9d8d04STrond Myklebust err = rpc_wait_for_completion_task(task); 421dc9d8d04STrond Myklebust rpc_put_task(task); 422dc9d8d04STrond Myklebust return err; 423d4716624STrond Myklebust } 424d4716624STrond Myklebust 4251da177e4SLinus Torvalds /* 4261da177e4SLinus Torvalds * TEST for the presence of a conflicting lock 4271da177e4SLinus Torvalds */ 4281da177e4SLinus Torvalds static int 4291da177e4SLinus Torvalds nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) 4301da177e4SLinus Torvalds { 4311da177e4SLinus Torvalds int status; 4321da177e4SLinus Torvalds 433d11d10ccSTrond Myklebust status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_TEST); 4341da177e4SLinus Torvalds if (status < 0) 43592737230STrond Myklebust goto out; 4361da177e4SLinus Torvalds 43792737230STrond Myklebust switch (req->a_res.status) { 438e8c5c045SAl Viro case nlm_granted: 4391da177e4SLinus Torvalds fl->fl_type = F_UNLCK; 44092737230STrond Myklebust break; 441e8c5c045SAl Viro case nlm_lck_denied: 4421da177e4SLinus Torvalds /* 4431da177e4SLinus Torvalds * Report the conflicting lock back to the application. 4441da177e4SLinus Torvalds */ 445e4cd038aSTrond Myklebust fl->fl_start = req->a_res.lock.fl.fl_start; 446d67d1c7bSFelix Blyakher fl->fl_end = req->a_res.lock.fl.fl_end; 447e4cd038aSTrond Myklebust fl->fl_type = req->a_res.lock.fl.fl_type; 448b8eee0e9SBenjamin Coddington fl->fl_pid = -req->a_res.lock.fl.fl_pid; 44992737230STrond Myklebust break; 45092737230STrond Myklebust default: 45192737230STrond Myklebust status = nlm_stat_to_errno(req->a_res.status); 4521da177e4SLinus Torvalds } 45392737230STrond Myklebust out: 4547db836d4SChuck Lever nlmclnt_release_call(req); 45592737230STrond Myklebust return status; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl) 4591da177e4SLinus Torvalds { 46063185942SBryan Schumaker spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock); 4614c060b53STrond Myklebust new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state; 4629de3ec1dSBenjamin Coddington new->fl_u.nfs_fl.owner = nlmclnt_get_lockowner(fl->fl_u.nfs_fl.owner); 4634c060b53STrond Myklebust list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted); 46463185942SBryan Schumaker spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock); 4651da177e4SLinus Torvalds } 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds static void nlmclnt_locks_release_private(struct file_lock *fl) 4681da177e4SLinus Torvalds { 46963185942SBryan Schumaker spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock); 4704c060b53STrond Myklebust list_del(&fl->fl_u.nfs_fl.list); 47163185942SBryan Schumaker spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock); 4729de3ec1dSBenjamin Coddington nlmclnt_put_lockowner(fl->fl_u.nfs_fl.owner); 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds 4756aed6285SAlexey Dobriyan static const struct file_lock_operations nlmclnt_lock_ops = { 4761da177e4SLinus Torvalds .fl_copy_lock = nlmclnt_locks_copy_lock, 4771da177e4SLinus Torvalds .fl_release_private = nlmclnt_locks_release_private, 4781da177e4SLinus Torvalds }; 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host) 4811da177e4SLinus Torvalds { 4821da177e4SLinus Torvalds fl->fl_u.nfs_fl.state = 0; 4839de3ec1dSBenjamin Coddington fl->fl_u.nfs_fl.owner = nlmclnt_find_lockowner(host, fl->fl_owner); 4844c060b53STrond Myklebust INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list); 4851da177e4SLinus Torvalds fl->fl_ops = &nlmclnt_lock_ops; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 4889b073574STrond Myklebust static int do_vfs_lock(struct file_lock *fl) 4891da177e4SLinus Torvalds { 4904f656367SBenjamin Coddington return locks_lock_file_wait(fl->fl_file, fl); 4911da177e4SLinus Torvalds } 4921da177e4SLinus Torvalds 4931da177e4SLinus Torvalds /* 4941da177e4SLinus Torvalds * LOCK: Try to create a lock 4951da177e4SLinus Torvalds * 4961da177e4SLinus Torvalds * Programmer Harassment Alert 4971da177e4SLinus Torvalds * 4981da177e4SLinus Torvalds * When given a blocking lock request in a sync RPC call, the HPUX lockd 4991da177e4SLinus Torvalds * will faithfully return LCK_BLOCKED but never cares to notify us when 5001da177e4SLinus Torvalds * the lock could be granted. This way, our local process could hang 5011da177e4SLinus Torvalds * around forever waiting for the callback. 5021da177e4SLinus Torvalds * 5031da177e4SLinus Torvalds * Solution A: Implement busy-waiting 5041da177e4SLinus Torvalds * Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES}) 5051da177e4SLinus Torvalds * 5061da177e4SLinus Torvalds * For now I am implementing solution A, because I hate the idea of 5071da177e4SLinus Torvalds * re-implementing lockd for a third time in two months. The async 5081da177e4SLinus Torvalds * calls shouldn't be too hard to do, however. 5091da177e4SLinus Torvalds * 5101da177e4SLinus Torvalds * This is one of the lovely things about standards in the NFS area: 5111da177e4SLinus Torvalds * they're so soft and squishy you can't really blame HP for doing this. 5121da177e4SLinus Torvalds */ 5131da177e4SLinus Torvalds static int 5141da177e4SLinus Torvalds nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) 5151da177e4SLinus Torvalds { 516a52458b4SNeilBrown const struct cred *cred = nfs_file_cred(fl->fl_file); 5171da177e4SLinus Torvalds struct nlm_host *host = req->a_host; 5181da177e4SLinus Torvalds struct nlm_res *resp = &req->a_res; 5193a649b88STrond Myklebust struct nlm_wait *block = NULL; 52001c3b861STrond Myklebust unsigned char fl_flags = fl->fl_flags; 5215f50c0c6STrond Myklebust unsigned char fl_type; 5223a649b88STrond Myklebust int status = -ENOLCK; 5231da177e4SLinus Torvalds 524501c1ed3SChuck Lever if (nsm_monitor(host) < 0) 5251da177e4SLinus Torvalds goto out; 5266c9dc425SChuck Lever req->a_args.state = nsm_local_state; 527501c1ed3SChuck Lever 52801c3b861STrond Myklebust fl->fl_flags |= FL_ACCESS; 52901c3b861STrond Myklebust status = do_vfs_lock(fl); 5304a9af59fSTrond Myklebust fl->fl_flags = fl_flags; 53101c3b861STrond Myklebust if (status < 0) 53201c3b861STrond Myklebust goto out; 5331da177e4SLinus Torvalds 5343a649b88STrond Myklebust block = nlmclnt_prepare_block(host, fl); 53528df955aSTrond Myklebust again: 5365f50c0c6STrond Myklebust /* 5375f50c0c6STrond Myklebust * Initialise resp->status to a valid non-zero value, 5385f50c0c6STrond Myklebust * since 0 == nlm_lck_granted 5395f50c0c6STrond Myklebust */ 5405f50c0c6STrond Myklebust resp->status = nlm_lck_blocked; 541ecdbf769STrond Myklebust for(;;) { 54228df955aSTrond Myklebust /* Reboot protection */ 54328df955aSTrond Myklebust fl->fl_u.nfs_fl.state = host->h_state; 544d11d10ccSTrond Myklebust status = nlmclnt_call(cred, req, NLMPROC_LOCK); 545ecdbf769STrond Myklebust if (status < 0) 546ecdbf769STrond Myklebust break; 547ecdbf769STrond Myklebust /* Did a reclaimer thread notify us of a server reboot? */ 548e8c5c045SAl Viro if (resp->status == nlm_lck_denied_grace_period) 549ecdbf769STrond Myklebust continue; 550e8c5c045SAl Viro if (resp->status != nlm_lck_blocked) 551ecdbf769STrond Myklebust break; 5523a649b88STrond Myklebust /* Wait on an NLM blocking lock */ 5533a649b88STrond Myklebust status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT); 5543a649b88STrond Myklebust if (status < 0) 5555f50c0c6STrond Myklebust break; 556e8c5c045SAl Viro if (resp->status != nlm_lck_blocked) 5573a649b88STrond Myklebust break; 558ecdbf769STrond Myklebust } 5591da177e4SLinus Torvalds 5605f50c0c6STrond Myklebust /* if we were interrupted while blocking, then cancel the lock request 5615f50c0c6STrond Myklebust * and exit 5625f50c0c6STrond Myklebust */ 5635f50c0c6STrond Myklebust if (resp->status == nlm_lck_blocked) { 5645f50c0c6STrond Myklebust if (!req->a_args.block) 5655f50c0c6STrond Myklebust goto out_unlock; 5665f50c0c6STrond Myklebust if (nlmclnt_cancel(host, req->a_args.block, fl) == 0) 5675f50c0c6STrond Myklebust goto out_unblock; 5685f50c0c6STrond Myklebust } 5695f50c0c6STrond Myklebust 570e8c5c045SAl Viro if (resp->status == nlm_granted) { 57128df955aSTrond Myklebust down_read(&host->h_rwsem); 57228df955aSTrond Myklebust /* Check whether or not the server has rebooted */ 57328df955aSTrond Myklebust if (fl->fl_u.nfs_fl.state != host->h_state) { 57428df955aSTrond Myklebust up_read(&host->h_rwsem); 57528df955aSTrond Myklebust goto again; 57628df955aSTrond Myklebust } 5774c060b53STrond Myklebust /* Ensure the resulting lock will get added to granted list */ 5784a9af59fSTrond Myklebust fl->fl_flags |= FL_SLEEP; 5799b073574STrond Myklebust if (do_vfs_lock(fl) < 0) 5808e24eea7SHarvey Harrison printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__); 58128df955aSTrond Myklebust up_read(&host->h_rwsem); 5824a9af59fSTrond Myklebust fl->fl_flags = fl_flags; 5835f50c0c6STrond Myklebust status = 0; 5841da177e4SLinus Torvalds } 5855f50c0c6STrond Myklebust if (status < 0) 5865f50c0c6STrond Myklebust goto out_unlock; 587cc77b152SMiklos Szeredi /* 588cc77b152SMiklos Szeredi * EAGAIN doesn't make sense for sleeping locks, and in some 589cc77b152SMiklos Szeredi * cases NLM_LCK_DENIED is returned for a permanent error. So 590cc77b152SMiklos Szeredi * turn it into an ENOLCK. 591cc77b152SMiklos Szeredi */ 592cc77b152SMiklos Szeredi if (resp->status == nlm_lck_denied && (fl_flags & FL_SLEEP)) 593cc77b152SMiklos Szeredi status = -ENOLCK; 594cc77b152SMiklos Szeredi else 5951da177e4SLinus Torvalds status = nlm_stat_to_errno(resp->status); 596ecdbf769STrond Myklebust out_unblock: 5973a649b88STrond Myklebust nlmclnt_finish_block(block); 5981da177e4SLinus Torvalds out: 5997db836d4SChuck Lever nlmclnt_release_call(req); 6001da177e4SLinus Torvalds return status; 6015f50c0c6STrond Myklebust out_unlock: 6025f50c0c6STrond Myklebust /* Fatal error: ensure that we remove the lock altogether */ 6035f50c0c6STrond Myklebust dprintk("lockd: lock attempt ended in fatal error.\n" 6045f50c0c6STrond Myklebust " Attempting to unlock.\n"); 6055f50c0c6STrond Myklebust nlmclnt_finish_block(block); 6065f50c0c6STrond Myklebust fl_type = fl->fl_type; 6075f50c0c6STrond Myklebust fl->fl_type = F_UNLCK; 6085f50c0c6STrond Myklebust down_read(&host->h_rwsem); 6095f50c0c6STrond Myklebust do_vfs_lock(fl); 6105f50c0c6STrond Myklebust up_read(&host->h_rwsem); 6115f50c0c6STrond Myklebust fl->fl_type = fl_type; 6125f50c0c6STrond Myklebust fl->fl_flags = fl_flags; 613d11d10ccSTrond Myklebust nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); 6145f50c0c6STrond Myklebust return status; 6151da177e4SLinus Torvalds } 6161da177e4SLinus Torvalds 6171da177e4SLinus Torvalds /* 6181da177e4SLinus Torvalds * RECLAIM: Try to reclaim a lock 6191da177e4SLinus Torvalds */ 6201da177e4SLinus Torvalds int 621f25cc71eSTim Gardner nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl, 622f25cc71eSTim Gardner struct nlm_rqst *req) 6231da177e4SLinus Torvalds { 6241da177e4SLinus Torvalds int status; 6251da177e4SLinus Torvalds 6261da177e4SLinus Torvalds memset(req, 0, sizeof(*req)); 6271da177e4SLinus Torvalds locks_init_lock(&req->a_args.lock.fl); 6281da177e4SLinus Torvalds locks_init_lock(&req->a_res.lock.fl); 6291da177e4SLinus Torvalds req->a_host = host; 6301da177e4SLinus Torvalds 6311da177e4SLinus Torvalds /* Set up the argument struct */ 6321da177e4SLinus Torvalds nlmclnt_setlockargs(req, fl); 6331da177e4SLinus Torvalds req->a_args.reclaim = 1; 6341da177e4SLinus Torvalds 635d11d10ccSTrond Myklebust status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_LOCK); 636d11d10ccSTrond Myklebust if (status >= 0 && req->a_res.status == nlm_granted) 6371da177e4SLinus Torvalds return 0; 6381da177e4SLinus Torvalds 6391da177e4SLinus Torvalds printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d " 6401da177e4SLinus Torvalds "(errno %d, status %d)\n", fl->fl_pid, 641e8c5c045SAl Viro status, ntohl(req->a_res.status)); 6421da177e4SLinus Torvalds 6431da177e4SLinus Torvalds /* 6441da177e4SLinus Torvalds * FIXME: This is a serious failure. We can 6451da177e4SLinus Torvalds * 6461da177e4SLinus Torvalds * a. Ignore the problem 6471da177e4SLinus Torvalds * b. Send the owning process some signal (Linux doesn't have 6481da177e4SLinus Torvalds * SIGLOST, though...) 6491da177e4SLinus Torvalds * c. Retry the operation 6501da177e4SLinus Torvalds * 6511da177e4SLinus Torvalds * Until someone comes up with a simple implementation 6521da177e4SLinus Torvalds * for b or c, I'll choose option a. 6531da177e4SLinus Torvalds */ 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds return -ENOLCK; 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds 6581da177e4SLinus Torvalds /* 6591da177e4SLinus Torvalds * UNLOCK: remove an existing lock 6601da177e4SLinus Torvalds */ 6611da177e4SLinus Torvalds static int 6621da177e4SLinus Torvalds nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) 6631da177e4SLinus Torvalds { 66428df955aSTrond Myklebust struct nlm_host *host = req->a_host; 6651da177e4SLinus Torvalds struct nlm_res *resp = &req->a_res; 6664a9af59fSTrond Myklebust int status; 6674a9af59fSTrond Myklebust unsigned char fl_flags = fl->fl_flags; 6681da177e4SLinus Torvalds 66926bcbf96SChristoph Hellwig /* 67030f4e20aSTrond Myklebust * Note: the server is supposed to either grant us the unlock 67130f4e20aSTrond Myklebust * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either 67230f4e20aSTrond Myklebust * case, we want to unlock. 67330f4e20aSTrond Myklebust */ 6749b073574STrond Myklebust fl->fl_flags |= FL_EXISTS; 67528df955aSTrond Myklebust down_read(&host->h_rwsem); 6764a9af59fSTrond Myklebust status = do_vfs_lock(fl); 6779b073574STrond Myklebust up_read(&host->h_rwsem); 6784a9af59fSTrond Myklebust fl->fl_flags = fl_flags; 6794a9af59fSTrond Myklebust if (status == -ENOENT) { 6804a9af59fSTrond Myklebust status = 0; 6819b073574STrond Myklebust goto out; 6829b073574STrond Myklebust } 68330f4e20aSTrond Myklebust 684fbca30c5SElena Reshetova refcount_inc(&req->a_count); 685d11d10ccSTrond Myklebust status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req, 686d11d10ccSTrond Myklebust NLMPROC_UNLOCK, &nlmclnt_unlock_ops); 6871da177e4SLinus Torvalds if (status < 0) 68892737230STrond Myklebust goto out; 6891da177e4SLinus Torvalds 690e8c5c045SAl Viro if (resp->status == nlm_granted) 69192737230STrond Myklebust goto out; 6921da177e4SLinus Torvalds 693e8c5c045SAl Viro if (resp->status != nlm_lck_denied_nolocks) 69482c2c8b8SVasily Averin printk("lockd: unexpected unlock status: %d\n", 69582c2c8b8SVasily Averin ntohl(resp->status)); 6961da177e4SLinus Torvalds /* What to do now? I'm out of my depth... */ 69792737230STrond Myklebust status = -ENOLCK; 69892737230STrond Myklebust out: 6997db836d4SChuck Lever nlmclnt_release_call(req); 70092737230STrond Myklebust return status; 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds 703b1ece737SBenjamin Coddington static void nlmclnt_unlock_prepare(struct rpc_task *task, void *data) 704b1ece737SBenjamin Coddington { 705b1ece737SBenjamin Coddington struct nlm_rqst *req = data; 706b1ece737SBenjamin Coddington const struct nlmclnt_operations *nlmclnt_ops = req->a_host->h_nlmclnt_ops; 707b1ece737SBenjamin Coddington bool defer_call = false; 708b1ece737SBenjamin Coddington 709b1ece737SBenjamin Coddington if (nlmclnt_ops && nlmclnt_ops->nlmclnt_unlock_prepare) 710b1ece737SBenjamin Coddington defer_call = nlmclnt_ops->nlmclnt_unlock_prepare(task, req->a_callback_data); 711b1ece737SBenjamin Coddington 712b1ece737SBenjamin Coddington if (!defer_call) 713b1ece737SBenjamin Coddington rpc_call_start(task); 714b1ece737SBenjamin Coddington } 715b1ece737SBenjamin Coddington 716963d8fe5STrond Myklebust static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) 7171da177e4SLinus Torvalds { 718963d8fe5STrond Myklebust struct nlm_rqst *req = data; 719e8c5c045SAl Viro u32 status = ntohl(req->a_res.status); 7201da177e4SLinus Torvalds 721ae67bd38STrond Myklebust if (RPC_SIGNALLED(task)) 7221da177e4SLinus Torvalds goto die; 7231da177e4SLinus Torvalds 7241da177e4SLinus Torvalds if (task->tk_status < 0) { 7251da177e4SLinus Torvalds dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status); 7260b760113STrond Myklebust switch (task->tk_status) { 7270b760113STrond Myklebust case -EACCES: 7280b760113STrond Myklebust case -EIO: 7290b760113STrond Myklebust goto die; 7300b760113STrond Myklebust default: 7311da177e4SLinus Torvalds goto retry_rebind; 7321da177e4SLinus Torvalds } 7330b760113STrond Myklebust } 7341da177e4SLinus Torvalds if (status == NLM_LCK_DENIED_GRACE_PERIOD) { 7351da177e4SLinus Torvalds rpc_delay(task, NLMCLNT_GRACE_WAIT); 7361da177e4SLinus Torvalds goto retry_unlock; 7371da177e4SLinus Torvalds } 7381da177e4SLinus Torvalds if (status != NLM_LCK_GRANTED) 7391da177e4SLinus Torvalds printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status); 7401da177e4SLinus Torvalds die: 7411da177e4SLinus Torvalds return; 7421da177e4SLinus Torvalds retry_rebind: 7431da177e4SLinus Torvalds nlm_rebind_host(req->a_host); 7441da177e4SLinus Torvalds retry_unlock: 7451da177e4SLinus Torvalds rpc_restart_call(task); 7461da177e4SLinus Torvalds } 7471da177e4SLinus Torvalds 748963d8fe5STrond Myklebust static const struct rpc_call_ops nlmclnt_unlock_ops = { 749b1ece737SBenjamin Coddington .rpc_call_prepare = nlmclnt_unlock_prepare, 750963d8fe5STrond Myklebust .rpc_call_done = nlmclnt_unlock_callback, 75192737230STrond Myklebust .rpc_release = nlmclnt_rpc_release, 752963d8fe5STrond Myklebust }; 753963d8fe5STrond Myklebust 7541da177e4SLinus Torvalds /* 7551da177e4SLinus Torvalds * Cancel a blocked lock request. 7561da177e4SLinus Torvalds * We always use an async RPC call for this in order not to hang a 7571da177e4SLinus Torvalds * process that has been Ctrl-C'ed. 7581da177e4SLinus Torvalds */ 75916fb2425STrond Myklebust static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl) 7601da177e4SLinus Torvalds { 7611da177e4SLinus Torvalds struct nlm_rqst *req; 7626b4b3a75STrond Myklebust int status; 7636b4b3a75STrond Myklebust 7646b4b3a75STrond Myklebust dprintk("lockd: blocking lock attempt was interrupted by a signal.\n" 7656b4b3a75STrond Myklebust " Attempting to cancel lock.\n"); 7661da177e4SLinus Torvalds 767446945abSAl Viro req = nlm_alloc_call(host); 7681da177e4SLinus Torvalds if (!req) 7691da177e4SLinus Torvalds return -ENOMEM; 7701da177e4SLinus Torvalds req->a_flags = RPC_TASK_ASYNC; 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds nlmclnt_setlockargs(req, fl); 77316fb2425STrond Myklebust req->a_args.block = block; 7741da177e4SLinus Torvalds 775fbca30c5SElena Reshetova refcount_inc(&req->a_count); 776d11d10ccSTrond Myklebust status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req, 777d11d10ccSTrond Myklebust NLMPROC_CANCEL, &nlmclnt_cancel_ops); 7786b4b3a75STrond Myklebust if (status == 0 && req->a_res.status == nlm_lck_denied) 7796b4b3a75STrond Myklebust status = -ENOLCK; 7807db836d4SChuck Lever nlmclnt_release_call(req); 7816b4b3a75STrond Myklebust return status; 7821da177e4SLinus Torvalds } 7831da177e4SLinus Torvalds 784963d8fe5STrond Myklebust static void nlmclnt_cancel_callback(struct rpc_task *task, void *data) 7851da177e4SLinus Torvalds { 786963d8fe5STrond Myklebust struct nlm_rqst *req = data; 787e8c5c045SAl Viro u32 status = ntohl(req->a_res.status); 7881da177e4SLinus Torvalds 789ae67bd38STrond Myklebust if (RPC_SIGNALLED(task)) 7901da177e4SLinus Torvalds goto die; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds if (task->tk_status < 0) { 7931da177e4SLinus Torvalds dprintk("lockd: CANCEL call error %d, retrying.\n", 7941da177e4SLinus Torvalds task->tk_status); 7951da177e4SLinus Torvalds goto retry_cancel; 7961da177e4SLinus Torvalds } 7971da177e4SLinus Torvalds 798e8c5c045SAl Viro switch (status) { 7991da177e4SLinus Torvalds case NLM_LCK_GRANTED: 8001da177e4SLinus Torvalds case NLM_LCK_DENIED_GRACE_PERIOD: 80135576cbaSTrond Myklebust case NLM_LCK_DENIED: 8021da177e4SLinus Torvalds /* Everything's good */ 8031da177e4SLinus Torvalds break; 8041da177e4SLinus Torvalds case NLM_LCK_DENIED_NOLOCKS: 8051da177e4SLinus Torvalds dprintk("lockd: CANCEL failed (server has no locks)\n"); 8061da177e4SLinus Torvalds goto retry_cancel; 8071da177e4SLinus Torvalds default: 8081da177e4SLinus Torvalds printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n", 809e8c5c045SAl Viro status); 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds die: 8131da177e4SLinus Torvalds return; 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds retry_cancel: 816aaaa9942STrond Myklebust /* Don't ever retry more than 3 times */ 817aaaa9942STrond Myklebust if (req->a_retries++ >= NLMCLNT_MAX_RETRIES) 818aaaa9942STrond Myklebust goto die; 8191da177e4SLinus Torvalds nlm_rebind_host(req->a_host); 8201da177e4SLinus Torvalds rpc_restart_call(task); 8211da177e4SLinus Torvalds rpc_delay(task, 30 * HZ); 8221da177e4SLinus Torvalds } 8231da177e4SLinus Torvalds 824963d8fe5STrond Myklebust static const struct rpc_call_ops nlmclnt_cancel_ops = { 825963d8fe5STrond Myklebust .rpc_call_done = nlmclnt_cancel_callback, 82692737230STrond Myklebust .rpc_release = nlmclnt_rpc_release, 827963d8fe5STrond Myklebust }; 828963d8fe5STrond Myklebust 8291da177e4SLinus Torvalds /* 8301da177e4SLinus Torvalds * Convert an NLM status code to a generic kernel errno 8311da177e4SLinus Torvalds */ 8321da177e4SLinus Torvalds static int 833e8c5c045SAl Viro nlm_stat_to_errno(__be32 status) 8341da177e4SLinus Torvalds { 835e8c5c045SAl Viro switch(ntohl(status)) { 8361da177e4SLinus Torvalds case NLM_LCK_GRANTED: 8371da177e4SLinus Torvalds return 0; 8381da177e4SLinus Torvalds case NLM_LCK_DENIED: 8391da177e4SLinus Torvalds return -EAGAIN; 8401da177e4SLinus Torvalds case NLM_LCK_DENIED_NOLOCKS: 8411da177e4SLinus Torvalds case NLM_LCK_DENIED_GRACE_PERIOD: 8421da177e4SLinus Torvalds return -ENOLCK; 8431da177e4SLinus Torvalds case NLM_LCK_BLOCKED: 8441da177e4SLinus Torvalds printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n"); 8451da177e4SLinus Torvalds return -ENOLCK; 8461da177e4SLinus Torvalds #ifdef CONFIG_LOCKD_V4 8471da177e4SLinus Torvalds case NLM_DEADLCK: 8481da177e4SLinus Torvalds return -EDEADLK; 8491da177e4SLinus Torvalds case NLM_ROFS: 8501da177e4SLinus Torvalds return -EROFS; 8511da177e4SLinus Torvalds case NLM_STALE_FH: 8521da177e4SLinus Torvalds return -ESTALE; 8531da177e4SLinus Torvalds case NLM_FBIG: 8541da177e4SLinus Torvalds return -EOVERFLOW; 8551da177e4SLinus Torvalds case NLM_FAILED: 8561da177e4SLinus Torvalds return -ENOLCK; 8571da177e4SLinus Torvalds #endif 8581da177e4SLinus Torvalds } 85982c2c8b8SVasily Averin printk(KERN_NOTICE "lockd: unexpected server status %d\n", 86082c2c8b8SVasily Averin ntohl(status)); 8611da177e4SLinus Torvalds return -ENOLCK; 8621da177e4SLinus Torvalds } 863