Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : /* 3 : : * linux/fs/lockd/clntlock.c 4 : : * 5 : : * Lock handling for the client side NLM implementation 6 : : * 7 : : * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> 8 : : */ 9 : : 10 : : #include <linux/module.h> 11 : : #include <linux/types.h> 12 : : #include <linux/slab.h> 13 : : #include <linux/time.h> 14 : : #include <linux/nfs_fs.h> 15 : : #include <linux/sunrpc/addr.h> 16 : : #include <linux/sunrpc/svc.h> 17 : : #include <linux/lockd/lockd.h> 18 : : #include <linux/kthread.h> 19 : : 20 : : #define NLMDBG_FACILITY NLMDBG_CLIENT 21 : : 22 : : /* 23 : : * Local function prototypes 24 : : */ 25 : : static int reclaimer(void *ptr); 26 : : 27 : : /* 28 : : * The following functions handle blocking and granting from the 29 : : * client perspective. 30 : : */ 31 : : 32 : : /* 33 : : * This is the representation of a blocked client lock. 34 : : */ 35 : : struct nlm_wait { 36 : : struct list_head b_list; /* linked list */ 37 : : wait_queue_head_t b_wait; /* where to wait on */ 38 : : struct nlm_host * b_host; 39 : : struct file_lock * b_lock; /* local file lock */ 40 : : unsigned short b_reclaim; /* got to reclaim lock */ 41 : : __be32 b_status; /* grant callback status */ 42 : : }; 43 : : 44 : : static LIST_HEAD(nlm_blocked); 45 : : static DEFINE_SPINLOCK(nlm_blocked_lock); 46 : : 47 : : /** 48 : : * nlmclnt_init - Set up per-NFS mount point lockd data structures 49 : : * @nlm_init: pointer to arguments structure 50 : : * 51 : : * Returns pointer to an appropriate nlm_host struct, 52 : : * or an ERR_PTR value. 53 : : */ 54 : 0 : struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) 55 : : { 56 : : struct nlm_host *host; 57 : 0 : u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4; 58 : : int status; 59 : : 60 : 0 : status = lockd_up(nlm_init->net, nlm_init->cred); 61 : 0 : if (status < 0) 62 : 0 : return ERR_PTR(status); 63 : : 64 : 0 : host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen, 65 : : nlm_init->protocol, nlm_version, 66 : : nlm_init->hostname, nlm_init->noresvport, 67 : : nlm_init->net, nlm_init->cred); 68 : 0 : if (host == NULL) 69 : : goto out_nohost; 70 : 0 : if (host->h_rpcclnt == NULL && nlm_bind_host(host) == NULL) 71 : : goto out_nobind; 72 : : 73 : 0 : host->h_nlmclnt_ops = nlm_init->nlmclnt_ops; 74 : 0 : return host; 75 : : out_nobind: 76 : 0 : nlmclnt_release_host(host); 77 : : out_nohost: 78 : 0 : lockd_down(nlm_init->net); 79 : 0 : return ERR_PTR(-ENOLCK); 80 : : } 81 : : EXPORT_SYMBOL_GPL(nlmclnt_init); 82 : : 83 : : /** 84 : : * nlmclnt_done - Release resources allocated by nlmclnt_init() 85 : : * @host: nlm_host structure reserved by nlmclnt_init() 86 : : * 87 : : */ 88 : 0 : void nlmclnt_done(struct nlm_host *host) 89 : : { 90 : 0 : struct net *net = host->net; 91 : : 92 : 0 : nlmclnt_release_host(host); 93 : 0 : lockd_down(net); 94 : 0 : } 95 : : EXPORT_SYMBOL_GPL(nlmclnt_done); 96 : : 97 : : /* 98 : : * Queue up a lock for blocking so that the GRANTED request can see it 99 : : */ 100 : 0 : struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl) 101 : : { 102 : : struct nlm_wait *block; 103 : : 104 : : block = kmalloc(sizeof(*block), GFP_KERNEL); 105 : 0 : if (block != NULL) { 106 : 0 : block->b_host = host; 107 : 0 : block->b_lock = fl; 108 : 0 : init_waitqueue_head(&block->b_wait); 109 : 0 : block->b_status = nlm_lck_blocked; 110 : : 111 : : spin_lock(&nlm_blocked_lock); 112 : 0 : list_add(&block->b_list, &nlm_blocked); 113 : : spin_unlock(&nlm_blocked_lock); 114 : : } 115 : 0 : return block; 116 : : } 117 : : 118 : 0 : void nlmclnt_finish_block(struct nlm_wait *block) 119 : : { 120 : 0 : if (block == NULL) 121 : 0 : return; 122 : : spin_lock(&nlm_blocked_lock); 123 : : list_del(&block->b_list); 124 : : spin_unlock(&nlm_blocked_lock); 125 : 0 : kfree(block); 126 : : } 127 : : 128 : : /* 129 : : * Block on a lock 130 : : */ 131 : 0 : int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout) 132 : : { 133 : : long ret; 134 : : 135 : : /* A borken server might ask us to block even if we didn't 136 : : * request it. Just say no! 137 : : */ 138 : 0 : if (block == NULL) 139 : : return -EAGAIN; 140 : : 141 : : /* Go to sleep waiting for GRANT callback. Some servers seem 142 : : * to lose callbacks, however, so we're going to poll from 143 : : * time to time just to make sure. 144 : : * 145 : : * For now, the retry frequency is pretty high; normally 146 : : * a 1 minute timeout would do. See the comment before 147 : : * nlmclnt_lock for an explanation. 148 : : */ 149 : 0 : ret = wait_event_interruptible_timeout(block->b_wait, 150 : : block->b_status != nlm_lck_blocked, 151 : : timeout); 152 : 0 : if (ret < 0) 153 : : return -ERESTARTSYS; 154 : : /* Reset the lock status after a server reboot so we resend */ 155 : 0 : if (block->b_status == nlm_lck_denied_grace_period) 156 : 0 : block->b_status = nlm_lck_blocked; 157 : 0 : req->a_res.status = block->b_status; 158 : 0 : return 0; 159 : : } 160 : : 161 : : /* 162 : : * The server lockd has called us back to tell us the lock was granted 163 : : */ 164 : 0 : __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) 165 : : { 166 : : const struct file_lock *fl = &lock->fl; 167 : 0 : const struct nfs_fh *fh = &lock->fh; 168 : : struct nlm_wait *block; 169 : : __be32 res = nlm_lck_denied; 170 : : 171 : : /* 172 : : * Look up blocked request based on arguments. 173 : : * Warning: must not use cookie to match it! 174 : : */ 175 : : spin_lock(&nlm_blocked_lock); 176 : 0 : list_for_each_entry(block, &nlm_blocked, b_list) { 177 : 0 : struct file_lock *fl_blocked = block->b_lock; 178 : : 179 : 0 : if (fl_blocked->fl_start != fl->fl_start) 180 : 0 : continue; 181 : 0 : if (fl_blocked->fl_end != fl->fl_end) 182 : 0 : continue; 183 : : /* 184 : : * Careful! The NLM server will return the 32-bit "pid" that 185 : : * we put on the wire: in this case the lockowner "pid". 186 : : */ 187 : 0 : if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid) 188 : 0 : continue; 189 : 0 : if (!rpc_cmp_addr(nlm_addr(block->b_host), addr)) 190 : 0 : continue; 191 : 0 : if (nfs_compare_fh(NFS_FH(locks_inode(fl_blocked->fl_file)), fh) != 0) 192 : 0 : continue; 193 : : /* Alright, we found a lock. Set the return status 194 : : * and wake up the caller 195 : : */ 196 : 0 : block->b_status = nlm_granted; 197 : 0 : wake_up(&block->b_wait); 198 : : res = nlm_granted; 199 : : } 200 : : spin_unlock(&nlm_blocked_lock); 201 : 0 : return res; 202 : : } 203 : : 204 : : /* 205 : : * The following procedures deal with the recovery of locks after a 206 : : * server crash. 207 : : */ 208 : : 209 : : /* 210 : : * Reclaim all locks on server host. We do this by spawning a separate 211 : : * reclaimer thread. 212 : : */ 213 : : void 214 : 0 : nlmclnt_recovery(struct nlm_host *host) 215 : : { 216 : : struct task_struct *task; 217 : : 218 : 0 : if (!host->h_reclaiming++) { 219 : 0 : nlm_get_host(host); 220 : 0 : task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name); 221 : 0 : if (IS_ERR(task)) 222 : 0 : printk(KERN_ERR "lockd: unable to spawn reclaimer " 223 : : "thread. Locks for %s won't be reclaimed! " 224 : : "(%ld)\n", host->h_name, PTR_ERR(task)); 225 : : } 226 : 0 : } 227 : : 228 : : static int 229 : 0 : reclaimer(void *ptr) 230 : : { 231 : : struct nlm_host *host = (struct nlm_host *) ptr; 232 : : struct nlm_wait *block; 233 : : struct nlm_rqst *req; 234 : : struct file_lock *fl, *next; 235 : : u32 nsmstate; 236 : 0 : struct net *net = host->net; 237 : : 238 : : req = kmalloc(sizeof(*req), GFP_KERNEL); 239 : 0 : if (!req) 240 : : return 0; 241 : : 242 : : allow_signal(SIGKILL); 243 : : 244 : 0 : down_write(&host->h_rwsem); 245 : 0 : lockd_up(net, NULL); /* note: this cannot fail as lockd is already running */ 246 : : 247 : : dprintk("lockd: reclaiming locks for host %s\n", host->h_name); 248 : : 249 : : restart: 250 : 0 : nsmstate = host->h_nsmstate; 251 : : 252 : : /* Force a portmap getport - the peer's lockd will 253 : : * most likely end up on a different port. 254 : : */ 255 : 0 : host->h_nextrebind = jiffies; 256 : 0 : nlm_rebind_host(host); 257 : : 258 : : /* First, reclaim all locks that have been granted. */ 259 : 0 : list_splice_init(&host->h_granted, &host->h_reclaim); 260 : 0 : list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { 261 : : list_del_init(&fl->fl_u.nfs_fl.list); 262 : : 263 : : /* 264 : : * sending this thread a SIGKILL will result in any unreclaimed 265 : : * locks being removed from the h_granted list. This means that 266 : : * the kernel will not attempt to reclaim them again if a new 267 : : * reclaimer thread is spawned for this host. 268 : : */ 269 : 0 : if (signalled()) 270 : 0 : continue; 271 : 0 : if (nlmclnt_reclaim(host, fl, req) != 0) 272 : 0 : continue; 273 : : list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); 274 : 0 : if (host->h_nsmstate != nsmstate) { 275 : : /* Argh! The server rebooted again! */ 276 : : goto restart; 277 : : } 278 : : } 279 : : 280 : 0 : host->h_reclaiming = 0; 281 : 0 : up_write(&host->h_rwsem); 282 : : dprintk("NLM: done reclaiming locks for host %s\n", host->h_name); 283 : : 284 : : /* Now, wake up all processes that sleep on a blocked lock */ 285 : : spin_lock(&nlm_blocked_lock); 286 : 0 : list_for_each_entry(block, &nlm_blocked, b_list) { 287 : 0 : if (block->b_host == host) { 288 : 0 : block->b_status = nlm_lck_denied_grace_period; 289 : 0 : wake_up(&block->b_wait); 290 : : } 291 : : } 292 : : spin_unlock(&nlm_blocked_lock); 293 : : 294 : : /* Release host handle after use */ 295 : 0 : nlmclnt_release_host(host); 296 : 0 : lockd_down(net); 297 : 0 : kfree(req); 298 : 0 : return 0; 299 : : }