162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/lockd/clntlock.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Lock handling 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/types.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/time.h> 1462306a36Sopenharmony_ci#include <linux/nfs_fs.h> 1562306a36Sopenharmony_ci#include <linux/sunrpc/addr.h> 1662306a36Sopenharmony_ci#include <linux/sunrpc/svc.h> 1762306a36Sopenharmony_ci#include <linux/sunrpc/svc_xprt.h> 1862306a36Sopenharmony_ci#include <linux/lockd/lockd.h> 1962306a36Sopenharmony_ci#include <linux/kthread.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "trace.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define NLMDBG_FACILITY NLMDBG_CLIENT 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Local function prototypes 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic int reclaimer(void *ptr); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * The following functions handle blocking and granting from the 3262306a36Sopenharmony_ci * client perspective. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic LIST_HEAD(nlm_blocked); 3662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nlm_blocked_lock); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/** 3962306a36Sopenharmony_ci * nlmclnt_init - Set up per-NFS mount point lockd data structures 4062306a36Sopenharmony_ci * @nlm_init: pointer to arguments structure 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Returns pointer to an appropriate nlm_host struct, 4362306a36Sopenharmony_ci * or an ERR_PTR value. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistruct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct nlm_host *host; 4862306a36Sopenharmony_ci u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4; 4962306a36Sopenharmony_ci int status; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci status = lockd_up(nlm_init->net, nlm_init->cred); 5262306a36Sopenharmony_ci if (status < 0) 5362306a36Sopenharmony_ci return ERR_PTR(status); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen, 5662306a36Sopenharmony_ci nlm_init->protocol, nlm_version, 5762306a36Sopenharmony_ci nlm_init->hostname, nlm_init->noresvport, 5862306a36Sopenharmony_ci nlm_init->net, nlm_init->cred); 5962306a36Sopenharmony_ci if (host == NULL) 6062306a36Sopenharmony_ci goto out_nohost; 6162306a36Sopenharmony_ci if (host->h_rpcclnt == NULL && nlm_bind_host(host) == NULL) 6262306a36Sopenharmony_ci goto out_nobind; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci host->h_nlmclnt_ops = nlm_init->nlmclnt_ops; 6562306a36Sopenharmony_ci return host; 6662306a36Sopenharmony_ciout_nobind: 6762306a36Sopenharmony_ci nlmclnt_release_host(host); 6862306a36Sopenharmony_ciout_nohost: 6962306a36Sopenharmony_ci lockd_down(nlm_init->net); 7062306a36Sopenharmony_ci return ERR_PTR(-ENOLCK); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_init); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/** 7562306a36Sopenharmony_ci * nlmclnt_done - Release resources allocated by nlmclnt_init() 7662306a36Sopenharmony_ci * @host: nlm_host structure reserved by nlmclnt_init() 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_civoid nlmclnt_done(struct nlm_host *host) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct net *net = host->net; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci nlmclnt_release_host(host); 8462306a36Sopenharmony_ci lockd_down(net); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_done); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_civoid nlmclnt_prepare_block(struct nlm_wait *block, struct nlm_host *host, struct file_lock *fl) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci block->b_host = host; 9162306a36Sopenharmony_ci block->b_lock = fl; 9262306a36Sopenharmony_ci init_waitqueue_head(&block->b_wait); 9362306a36Sopenharmony_ci block->b_status = nlm_lck_blocked; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct rpc_clnt *nlmclnt_rpc_clnt(struct nlm_host *host) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci return host->h_rpcclnt; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_rpc_clnt); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Queue up a lock for blocking so that the GRANTED request can see it 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_civoid nlmclnt_queue_block(struct nlm_wait *block) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci spin_lock(&nlm_blocked_lock); 10862306a36Sopenharmony_ci list_add(&block->b_list, &nlm_blocked); 10962306a36Sopenharmony_ci spin_unlock(&nlm_blocked_lock); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Dequeue the block and return its final status 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci__be32 nlmclnt_dequeue_block(struct nlm_wait *block) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci __be32 status; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci spin_lock(&nlm_blocked_lock); 12062306a36Sopenharmony_ci list_del(&block->b_list); 12162306a36Sopenharmony_ci status = block->b_status; 12262306a36Sopenharmony_ci spin_unlock(&nlm_blocked_lock); 12362306a36Sopenharmony_ci return status; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * Block on a lock 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ciint nlmclnt_wait(struct nlm_wait *block, struct nlm_rqst *req, long timeout) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci long ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* A borken server might ask us to block even if we didn't 13462306a36Sopenharmony_ci * request it. Just say no! 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci if (block == NULL) 13762306a36Sopenharmony_ci return -EAGAIN; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Go to sleep waiting for GRANT callback. Some servers seem 14062306a36Sopenharmony_ci * to lose callbacks, however, so we're going to poll from 14162306a36Sopenharmony_ci * time to time just to make sure. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * For now, the retry frequency is pretty high; normally 14462306a36Sopenharmony_ci * a 1 minute timeout would do. See the comment before 14562306a36Sopenharmony_ci * nlmclnt_lock for an explanation. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(block->b_wait, 14862306a36Sopenharmony_ci block->b_status != nlm_lck_blocked, 14962306a36Sopenharmony_ci timeout); 15062306a36Sopenharmony_ci if (ret < 0) 15162306a36Sopenharmony_ci return -ERESTARTSYS; 15262306a36Sopenharmony_ci /* Reset the lock status after a server reboot so we resend */ 15362306a36Sopenharmony_ci if (block->b_status == nlm_lck_denied_grace_period) 15462306a36Sopenharmony_ci block->b_status = nlm_lck_blocked; 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* 15962306a36Sopenharmony_ci * The server lockd has called us back to tell us the lock was granted 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci const struct file_lock *fl = &lock->fl; 16462306a36Sopenharmony_ci const struct nfs_fh *fh = &lock->fh; 16562306a36Sopenharmony_ci struct nlm_wait *block; 16662306a36Sopenharmony_ci __be32 res = nlm_lck_denied; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Look up blocked request based on arguments. 17062306a36Sopenharmony_ci * Warning: must not use cookie to match it! 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci spin_lock(&nlm_blocked_lock); 17362306a36Sopenharmony_ci list_for_each_entry(block, &nlm_blocked, b_list) { 17462306a36Sopenharmony_ci struct file_lock *fl_blocked = block->b_lock; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (fl_blocked->fl_start != fl->fl_start) 17762306a36Sopenharmony_ci continue; 17862306a36Sopenharmony_ci if (fl_blocked->fl_end != fl->fl_end) 17962306a36Sopenharmony_ci continue; 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * Careful! The NLM server will return the 32-bit "pid" that 18262306a36Sopenharmony_ci * we put on the wire: in this case the lockowner "pid". 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid) 18562306a36Sopenharmony_ci continue; 18662306a36Sopenharmony_ci if (!rpc_cmp_addr(nlm_addr(block->b_host), addr)) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci if (nfs_compare_fh(NFS_FH(file_inode(fl_blocked->fl_file)), fh) != 0) 18962306a36Sopenharmony_ci continue; 19062306a36Sopenharmony_ci /* Alright, we found a lock. Set the return status 19162306a36Sopenharmony_ci * and wake up the caller 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci block->b_status = nlm_granted; 19462306a36Sopenharmony_ci wake_up(&block->b_wait); 19562306a36Sopenharmony_ci res = nlm_granted; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci spin_unlock(&nlm_blocked_lock); 19862306a36Sopenharmony_ci trace_nlmclnt_grant(lock, addr, svc_addr_len(addr), res); 19962306a36Sopenharmony_ci return res; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * The following procedures deal with the recovery of locks after a 20462306a36Sopenharmony_ci * server crash. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * Reclaim all locks on server host. We do this by spawning a separate 20962306a36Sopenharmony_ci * reclaimer thread. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_civoid 21262306a36Sopenharmony_cinlmclnt_recovery(struct nlm_host *host) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct task_struct *task; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (!host->h_reclaiming++) { 21762306a36Sopenharmony_ci nlm_get_host(host); 21862306a36Sopenharmony_ci task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name); 21962306a36Sopenharmony_ci if (IS_ERR(task)) 22062306a36Sopenharmony_ci printk(KERN_ERR "lockd: unable to spawn reclaimer " 22162306a36Sopenharmony_ci "thread. Locks for %s won't be reclaimed! " 22262306a36Sopenharmony_ci "(%ld)\n", host->h_name, PTR_ERR(task)); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int 22762306a36Sopenharmony_cireclaimer(void *ptr) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct nlm_host *host = (struct nlm_host *) ptr; 23062306a36Sopenharmony_ci struct nlm_wait *block; 23162306a36Sopenharmony_ci struct nlm_rqst *req; 23262306a36Sopenharmony_ci struct file_lock *fl, *next; 23362306a36Sopenharmony_ci u32 nsmstate; 23462306a36Sopenharmony_ci struct net *net = host->net; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci req = kmalloc(sizeof(*req), GFP_KERNEL); 23762306a36Sopenharmony_ci if (!req) 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci allow_signal(SIGKILL); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci down_write(&host->h_rwsem); 24362306a36Sopenharmony_ci lockd_up(net, NULL); /* note: this cannot fail as lockd is already running */ 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci dprintk("lockd: reclaiming locks for host %s\n", host->h_name); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cirestart: 24862306a36Sopenharmony_ci nsmstate = host->h_nsmstate; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Force a portmap getport - the peer's lockd will 25162306a36Sopenharmony_ci * most likely end up on a different port. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci host->h_nextrebind = jiffies; 25462306a36Sopenharmony_ci nlm_rebind_host(host); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* First, reclaim all locks that have been granted. */ 25762306a36Sopenharmony_ci list_splice_init(&host->h_granted, &host->h_reclaim); 25862306a36Sopenharmony_ci list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { 25962306a36Sopenharmony_ci list_del_init(&fl->fl_u.nfs_fl.list); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * sending this thread a SIGKILL will result in any unreclaimed 26362306a36Sopenharmony_ci * locks being removed from the h_granted list. This means that 26462306a36Sopenharmony_ci * the kernel will not attempt to reclaim them again if a new 26562306a36Sopenharmony_ci * reclaimer thread is spawned for this host. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci if (signalled()) 26862306a36Sopenharmony_ci continue; 26962306a36Sopenharmony_ci if (nlmclnt_reclaim(host, fl, req) != 0) 27062306a36Sopenharmony_ci continue; 27162306a36Sopenharmony_ci list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); 27262306a36Sopenharmony_ci if (host->h_nsmstate != nsmstate) { 27362306a36Sopenharmony_ci /* Argh! The server rebooted again! */ 27462306a36Sopenharmony_ci goto restart; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci host->h_reclaiming = 0; 27962306a36Sopenharmony_ci up_write(&host->h_rwsem); 28062306a36Sopenharmony_ci dprintk("NLM: done reclaiming locks for host %s\n", host->h_name); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Now, wake up all processes that sleep on a blocked lock */ 28362306a36Sopenharmony_ci spin_lock(&nlm_blocked_lock); 28462306a36Sopenharmony_ci list_for_each_entry(block, &nlm_blocked, b_list) { 28562306a36Sopenharmony_ci if (block->b_host == host) { 28662306a36Sopenharmony_ci block->b_status = nlm_lck_denied_grace_period; 28762306a36Sopenharmony_ci wake_up(&block->b_wait); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci spin_unlock(&nlm_blocked_lock); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Release host handle after use */ 29362306a36Sopenharmony_ci nlmclnt_release_host(host); 29462306a36Sopenharmony_ci lockd_down(net); 29562306a36Sopenharmony_ci kfree(req); 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci} 298