18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/lockd/clntlock.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Lock handling for the client side NLM implementation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/time.h> 148c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h> 158c2ecf20Sopenharmony_ci#include <linux/sunrpc/addr.h> 168c2ecf20Sopenharmony_ci#include <linux/sunrpc/svc.h> 178c2ecf20Sopenharmony_ci#include <linux/lockd/lockd.h> 188c2ecf20Sopenharmony_ci#include <linux/kthread.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define NLMDBG_FACILITY NLMDBG_CLIENT 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Local function prototypes 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_cistatic int reclaimer(void *ptr); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * The following functions handle blocking and granting from the 298c2ecf20Sopenharmony_ci * client perspective. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * This is the representation of a blocked client lock. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistruct nlm_wait { 368c2ecf20Sopenharmony_ci struct list_head b_list; /* linked list */ 378c2ecf20Sopenharmony_ci wait_queue_head_t b_wait; /* where to wait on */ 388c2ecf20Sopenharmony_ci struct nlm_host * b_host; 398c2ecf20Sopenharmony_ci struct file_lock * b_lock; /* local file lock */ 408c2ecf20Sopenharmony_ci unsigned short b_reclaim; /* got to reclaim lock */ 418c2ecf20Sopenharmony_ci __be32 b_status; /* grant callback status */ 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic LIST_HEAD(nlm_blocked); 458c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(nlm_blocked_lock); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/** 488c2ecf20Sopenharmony_ci * nlmclnt_init - Set up per-NFS mount point lockd data structures 498c2ecf20Sopenharmony_ci * @nlm_init: pointer to arguments structure 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * Returns pointer to an appropriate nlm_host struct, 528c2ecf20Sopenharmony_ci * or an ERR_PTR value. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistruct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct nlm_host *host; 578c2ecf20Sopenharmony_ci u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4; 588c2ecf20Sopenharmony_ci int status; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci status = lockd_up(nlm_init->net, nlm_init->cred); 618c2ecf20Sopenharmony_ci if (status < 0) 628c2ecf20Sopenharmony_ci return ERR_PTR(status); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen, 658c2ecf20Sopenharmony_ci nlm_init->protocol, nlm_version, 668c2ecf20Sopenharmony_ci nlm_init->hostname, nlm_init->noresvport, 678c2ecf20Sopenharmony_ci nlm_init->net, nlm_init->cred); 688c2ecf20Sopenharmony_ci if (host == NULL) 698c2ecf20Sopenharmony_ci goto out_nohost; 708c2ecf20Sopenharmony_ci if (host->h_rpcclnt == NULL && nlm_bind_host(host) == NULL) 718c2ecf20Sopenharmony_ci goto out_nobind; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci host->h_nlmclnt_ops = nlm_init->nlmclnt_ops; 748c2ecf20Sopenharmony_ci return host; 758c2ecf20Sopenharmony_ciout_nobind: 768c2ecf20Sopenharmony_ci nlmclnt_release_host(host); 778c2ecf20Sopenharmony_ciout_nohost: 788c2ecf20Sopenharmony_ci lockd_down(nlm_init->net); 798c2ecf20Sopenharmony_ci return ERR_PTR(-ENOLCK); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_init); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/** 848c2ecf20Sopenharmony_ci * nlmclnt_done - Release resources allocated by nlmclnt_init() 858c2ecf20Sopenharmony_ci * @host: nlm_host structure reserved by nlmclnt_init() 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_civoid nlmclnt_done(struct nlm_host *host) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct net *net = host->net; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci nlmclnt_release_host(host); 938c2ecf20Sopenharmony_ci lockd_down(net); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_done); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Queue up a lock for blocking so that the GRANTED request can see it 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistruct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct nlm_wait *block; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci block = kmalloc(sizeof(*block), GFP_KERNEL); 1058c2ecf20Sopenharmony_ci if (block != NULL) { 1068c2ecf20Sopenharmony_ci block->b_host = host; 1078c2ecf20Sopenharmony_ci block->b_lock = fl; 1088c2ecf20Sopenharmony_ci init_waitqueue_head(&block->b_wait); 1098c2ecf20Sopenharmony_ci block->b_status = nlm_lck_blocked; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci spin_lock(&nlm_blocked_lock); 1128c2ecf20Sopenharmony_ci list_add(&block->b_list, &nlm_blocked); 1138c2ecf20Sopenharmony_ci spin_unlock(&nlm_blocked_lock); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci return block; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_civoid nlmclnt_finish_block(struct nlm_wait *block) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci if (block == NULL) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci spin_lock(&nlm_blocked_lock); 1238c2ecf20Sopenharmony_ci list_del(&block->b_list); 1248c2ecf20Sopenharmony_ci spin_unlock(&nlm_blocked_lock); 1258c2ecf20Sopenharmony_ci kfree(block); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * Block on a lock 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ciint nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci long ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* A borken server might ask us to block even if we didn't 1368c2ecf20Sopenharmony_ci * request it. Just say no! 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci if (block == NULL) 1398c2ecf20Sopenharmony_ci return -EAGAIN; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Go to sleep waiting for GRANT callback. Some servers seem 1428c2ecf20Sopenharmony_ci * to lose callbacks, however, so we're going to poll from 1438c2ecf20Sopenharmony_ci * time to time just to make sure. 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * For now, the retry frequency is pretty high; normally 1468c2ecf20Sopenharmony_ci * a 1 minute timeout would do. See the comment before 1478c2ecf20Sopenharmony_ci * nlmclnt_lock for an explanation. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(block->b_wait, 1508c2ecf20Sopenharmony_ci block->b_status != nlm_lck_blocked, 1518c2ecf20Sopenharmony_ci timeout); 1528c2ecf20Sopenharmony_ci if (ret < 0) 1538c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1548c2ecf20Sopenharmony_ci /* Reset the lock status after a server reboot so we resend */ 1558c2ecf20Sopenharmony_ci if (block->b_status == nlm_lck_denied_grace_period) 1568c2ecf20Sopenharmony_ci block->b_status = nlm_lck_blocked; 1578c2ecf20Sopenharmony_ci req->a_res.status = block->b_status; 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* 1628c2ecf20Sopenharmony_ci * The server lockd has called us back to tell us the lock was granted 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci const struct file_lock *fl = &lock->fl; 1678c2ecf20Sopenharmony_ci const struct nfs_fh *fh = &lock->fh; 1688c2ecf20Sopenharmony_ci struct nlm_wait *block; 1698c2ecf20Sopenharmony_ci __be32 res = nlm_lck_denied; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * Look up blocked request based on arguments. 1738c2ecf20Sopenharmony_ci * Warning: must not use cookie to match it! 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci spin_lock(&nlm_blocked_lock); 1768c2ecf20Sopenharmony_ci list_for_each_entry(block, &nlm_blocked, b_list) { 1778c2ecf20Sopenharmony_ci struct file_lock *fl_blocked = block->b_lock; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (fl_blocked->fl_start != fl->fl_start) 1808c2ecf20Sopenharmony_ci continue; 1818c2ecf20Sopenharmony_ci if (fl_blocked->fl_end != fl->fl_end) 1828c2ecf20Sopenharmony_ci continue; 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * Careful! The NLM server will return the 32-bit "pid" that 1858c2ecf20Sopenharmony_ci * we put on the wire: in this case the lockowner "pid". 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid) 1888c2ecf20Sopenharmony_ci continue; 1898c2ecf20Sopenharmony_ci if (!rpc_cmp_addr(nlm_addr(block->b_host), addr)) 1908c2ecf20Sopenharmony_ci continue; 1918c2ecf20Sopenharmony_ci if (nfs_compare_fh(NFS_FH(locks_inode(fl_blocked->fl_file)), fh) != 0) 1928c2ecf20Sopenharmony_ci continue; 1938c2ecf20Sopenharmony_ci /* Alright, we found a lock. Set the return status 1948c2ecf20Sopenharmony_ci * and wake up the caller 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci block->b_status = nlm_granted; 1978c2ecf20Sopenharmony_ci wake_up(&block->b_wait); 1988c2ecf20Sopenharmony_ci res = nlm_granted; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci spin_unlock(&nlm_blocked_lock); 2018c2ecf20Sopenharmony_ci return res; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* 2058c2ecf20Sopenharmony_ci * The following procedures deal with the recovery of locks after a 2068c2ecf20Sopenharmony_ci * server crash. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * Reclaim all locks on server host. We do this by spawning a separate 2118c2ecf20Sopenharmony_ci * reclaimer thread. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_civoid 2148c2ecf20Sopenharmony_cinlmclnt_recovery(struct nlm_host *host) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct task_struct *task; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (!host->h_reclaiming++) { 2198c2ecf20Sopenharmony_ci nlm_get_host(host); 2208c2ecf20Sopenharmony_ci task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name); 2218c2ecf20Sopenharmony_ci if (IS_ERR(task)) 2228c2ecf20Sopenharmony_ci printk(KERN_ERR "lockd: unable to spawn reclaimer " 2238c2ecf20Sopenharmony_ci "thread. Locks for %s won't be reclaimed! " 2248c2ecf20Sopenharmony_ci "(%ld)\n", host->h_name, PTR_ERR(task)); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int 2298c2ecf20Sopenharmony_cireclaimer(void *ptr) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct nlm_host *host = (struct nlm_host *) ptr; 2328c2ecf20Sopenharmony_ci struct nlm_wait *block; 2338c2ecf20Sopenharmony_ci struct nlm_rqst *req; 2348c2ecf20Sopenharmony_ci struct file_lock *fl, *next; 2358c2ecf20Sopenharmony_ci u32 nsmstate; 2368c2ecf20Sopenharmony_ci struct net *net = host->net; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci req = kmalloc(sizeof(*req), GFP_KERNEL); 2398c2ecf20Sopenharmony_ci if (!req) 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci allow_signal(SIGKILL); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci down_write(&host->h_rwsem); 2458c2ecf20Sopenharmony_ci lockd_up(net, NULL); /* note: this cannot fail as lockd is already running */ 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci dprintk("lockd: reclaiming locks for host %s\n", host->h_name); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cirestart: 2508c2ecf20Sopenharmony_ci nsmstate = host->h_nsmstate; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Force a portmap getport - the peer's lockd will 2538c2ecf20Sopenharmony_ci * most likely end up on a different port. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci host->h_nextrebind = jiffies; 2568c2ecf20Sopenharmony_ci nlm_rebind_host(host); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* First, reclaim all locks that have been granted. */ 2598c2ecf20Sopenharmony_ci list_splice_init(&host->h_granted, &host->h_reclaim); 2608c2ecf20Sopenharmony_ci list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { 2618c2ecf20Sopenharmony_ci list_del_init(&fl->fl_u.nfs_fl.list); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * sending this thread a SIGKILL will result in any unreclaimed 2658c2ecf20Sopenharmony_ci * locks being removed from the h_granted list. This means that 2668c2ecf20Sopenharmony_ci * the kernel will not attempt to reclaim them again if a new 2678c2ecf20Sopenharmony_ci * reclaimer thread is spawned for this host. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci if (signalled()) 2708c2ecf20Sopenharmony_ci continue; 2718c2ecf20Sopenharmony_ci if (nlmclnt_reclaim(host, fl, req) != 0) 2728c2ecf20Sopenharmony_ci continue; 2738c2ecf20Sopenharmony_ci list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); 2748c2ecf20Sopenharmony_ci if (host->h_nsmstate != nsmstate) { 2758c2ecf20Sopenharmony_ci /* Argh! The server rebooted again! */ 2768c2ecf20Sopenharmony_ci goto restart; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci host->h_reclaiming = 0; 2818c2ecf20Sopenharmony_ci up_write(&host->h_rwsem); 2828c2ecf20Sopenharmony_ci dprintk("NLM: done reclaiming locks for host %s\n", host->h_name); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Now, wake up all processes that sleep on a blocked lock */ 2858c2ecf20Sopenharmony_ci spin_lock(&nlm_blocked_lock); 2868c2ecf20Sopenharmony_ci list_for_each_entry(block, &nlm_blocked, b_list) { 2878c2ecf20Sopenharmony_ci if (block->b_host == host) { 2888c2ecf20Sopenharmony_ci block->b_status = nlm_lck_denied_grace_period; 2898c2ecf20Sopenharmony_ci wake_up(&block->b_wait); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci spin_unlock(&nlm_blocked_lock); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Release host handle after use */ 2958c2ecf20Sopenharmony_ci nlmclnt_release_host(host); 2968c2ecf20Sopenharmony_ci lockd_down(net); 2978c2ecf20Sopenharmony_ci kfree(req); 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 300