Import 2.4.0-test2pre7
[davej-history.git] / fs / lockd / clntproc.c
bloba5ec6c774962ecdade296aab906209af4e2957a6
1 /*
2 * linux/fs/lockd/clntproc.c
4 * RPC procedures for the client side NLM implementation
6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
7 */
9 #include <linux/types.h>
10 #include <linux/errno.h>
11 #include <linux/fs.h>
12 #include <linux/nfs_fs.h>
13 #include <linux/utsname.h>
14 #include <linux/sunrpc/clnt.h>
15 #include <linux/sunrpc/svc.h>
16 #include <linux/lockd/lockd.h>
17 #include <linux/lockd/sm_inter.h>
19 #define NLMDBG_FACILITY NLMDBG_CLIENT
21 static int nlmclnt_test(struct nlm_rqst *, struct file_lock *);
22 static int nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
23 static int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *);
24 static void nlmclnt_unlock_callback(struct rpc_task *);
25 static void nlmclnt_cancel_callback(struct rpc_task *);
26 static int nlm_stat_to_errno(u32 stat);
29 * Cookie counter for NLM requests
31 static u32 nlm_cookie = 0x1234;
33 static inline void nlmclnt_next_cookie(struct nlm_cookie *c)
35 memcpy(c->data, &nlm_cookie, 4);
36 memset(c->data+4, 0, 4);
37 c->len=4;
38 nlm_cookie++;
42 * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls
44 static inline void
45 nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
47 struct nlm_args *argp = &req->a_args;
48 struct nlm_lock *lock = &argp->lock;
50 memset(argp, 0, sizeof(*argp));
51 nlmclnt_next_cookie(&argp->cookie);
52 argp->state = nsm_local_state;
53 memcpy(&lock->fh, NFS_FH(fl->fl_file->f_dentry), sizeof(struct nfs_fh));
54 lock->caller = system_utsname.nodename;
55 lock->oh.data = req->a_owner;
56 lock->oh.len = sprintf(req->a_owner, "%d@%s",
57 current->pid, system_utsname.nodename);
58 lock->fl = *fl;
62 * Initialize arguments for GRANTED call. The nlm_rqst structure
63 * has been cleared already.
65 int
66 nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
68 nlmclnt_next_cookie(&call->a_args.cookie);
69 call->a_args.lock = *lock;
70 call->a_args.lock.caller = system_utsname.nodename;
72 init_waitqueue_head(&call->a_args.lock.fl.fl_wait);
73 /* set default data area */
74 call->a_args.lock.oh.data = call->a_owner;
76 if (lock->oh.len > NLMCLNT_OHSIZE) {
77 void *data = kmalloc(lock->oh.len, GFP_KERNEL);
78 if (!data)
79 return 0;
80 call->a_args.lock.oh.data = (u8 *) data;
83 memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len);
84 return 1;
87 void
88 nlmclnt_freegrantargs(struct nlm_rqst *call)
91 * Check whether we allocated memory for the owner.
93 if (call->a_args.lock.oh.data != (u8 *) call->a_owner) {
94 kfree(call->a_args.lock.oh.data);
99 * This is the main entry point for the NLM client.
102 nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
104 struct nfs_server *nfssrv = NFS_SERVER(inode);
105 struct nlm_host *host;
106 struct nlm_rqst reqst, *call = &reqst;
107 sigset_t oldset;
108 unsigned long flags;
109 int status, proto, vers;
111 vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1;
112 if (NFS_PROTO(inode)->version > 3) {
113 printk(KERN_NOTICE "NFSv4 file locking not implemented!\n");
114 return -ENOLCK;
117 /* Retrieve transport protocol from NFS client */
118 proto = NFS_CLIENT(inode)->cl_xprt->prot;
120 if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers)))
121 return -ENOLCK;
123 /* Create RPC client handle if not there, and copy soft
124 * and intr flags from NFS client. */
125 if (host->h_rpcclnt == NULL) {
126 struct rpc_clnt *clnt;
128 /* Bind an rpc client to this host handle (does not
129 * perform a portmapper lookup) */
130 if (!(clnt = nlm_bind_host(host))) {
131 status = -ENOLCK;
132 goto done;
134 clnt->cl_softrtry = nfssrv->client->cl_softrtry;
135 clnt->cl_intr = nfssrv->client->cl_intr;
136 clnt->cl_chatty = nfssrv->client->cl_chatty;
139 /* Keep the old signal mask */
140 spin_lock_irqsave(&current->sigmask_lock, flags);
141 oldset = current->blocked;
143 /* If we're cleaning up locks because the process is exiting,
144 * perform the RPC call asynchronously. */
145 if ((cmd == F_SETLK || cmd == F_SETLKW)
146 && fl->fl_type == F_UNLCK
147 && (current->flags & PF_EXITING)) {
148 sigfillset(&current->blocked); /* Mask all signals */
149 recalc_sigpending(current);
150 spin_unlock_irqrestore(&current->sigmask_lock, flags);
152 call = nlmclnt_alloc_call();
153 if (!call) {
154 status = -ENOMEM;
155 goto out_restore;
157 call->a_flags = RPC_TASK_ASYNC;
158 } else {
159 spin_unlock_irqrestore(&current->sigmask_lock, flags);
160 call->a_flags = 0;
162 call->a_host = host;
164 /* Set up the argument struct */
165 nlmclnt_setlockargs(call, fl);
167 if (cmd == F_GETLK) {
168 status = nlmclnt_test(call, fl);
169 } else if ((cmd == F_SETLK || cmd == F_SETLKW)
170 && fl->fl_type == F_UNLCK) {
171 status = nlmclnt_unlock(call, fl);
172 } else if (cmd == F_SETLK || cmd == F_SETLKW) {
173 call->a_args.block = (cmd == F_SETLKW)? 1 : 0;
174 status = nlmclnt_lock(call, fl);
175 } else {
176 status = -EINVAL;
179 if (status < 0 && (call->a_flags & RPC_TASK_ASYNC))
180 kfree(call);
182 out_restore:
183 spin_lock_irqsave(&current->sigmask_lock, flags);
184 current->blocked = oldset;
185 recalc_sigpending(current);
186 spin_unlock_irqrestore(&current->sigmask_lock, flags);
188 done:
189 dprintk("lockd: clnt proc returns %d\n", status);
190 nlm_release_host(host);
191 return status;
195 * Wait while server is in grace period
197 static inline int
198 nlmclnt_grace_wait(struct nlm_host *host)
200 if (!host->h_reclaiming)
201 interruptible_sleep_on_timeout(&host->h_gracewait, 10*HZ);
202 else
203 interruptible_sleep_on(&host->h_gracewait);
204 return signalled()? -ERESTARTSYS : 0;
208 * Allocate an NLM RPC call struct
210 struct nlm_rqst *
211 nlmclnt_alloc_call(void)
213 struct nlm_rqst *call;
215 while (!signalled()) {
216 call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL);
217 if (call)
218 return call;
219 printk("nlmclnt_alloc_call: failed, waiting for memory\n");
220 current->state = TASK_INTERRUPTIBLE;
221 schedule_timeout(5*HZ);
223 return NULL;
227 * Generic NLM call
230 nlmclnt_call(struct nlm_rqst *req, u32 proc)
232 struct nlm_host *host = req->a_host;
233 struct rpc_clnt *clnt;
234 struct nlm_args *argp = &req->a_args;
235 struct nlm_res *resp = &req->a_res;
236 struct file *filp = argp->lock.fl.fl_file;
237 struct rpc_message msg;
238 int status;
240 dprintk("lockd: call procedure %s on %s\n",
241 nlm_procname(proc), host->h_name);
243 msg.rpc_proc = proc;
244 msg.rpc_argp = argp;
245 msg.rpc_resp = resp;
246 if (filp)
247 msg.rpc_cred = nfs_file_cred(filp);
248 else
249 msg.rpc_cred = NULL;
251 do {
252 if (host->h_reclaiming && !argp->reclaim) {
253 interruptible_sleep_on(&host->h_gracewait);
254 continue;
257 /* If we have no RPC client yet, create one. */
258 if ((clnt = nlm_bind_host(host)) == NULL)
259 return -ENOLCK;
261 /* Perform the RPC call. If an error occurs, try again */
262 if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) {
263 dprintk("lockd: rpc_call returned error %d\n", -status);
264 switch (status) {
265 case -EPROTONOSUPPORT:
266 status = -EINVAL;
267 break;
268 case -ECONNREFUSED:
269 case -ETIMEDOUT:
270 case -ENOTCONN:
271 nlm_rebind_host(host);
272 status = -EAGAIN;
273 break;
274 case -ERESTARTSYS:
275 return signalled () ? -EINTR : status;
276 default:
277 break;
279 break;
280 } else
281 if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) {
282 dprintk("lockd: server in grace period\n");
283 if (argp->reclaim) {
284 printk(KERN_WARNING
285 "lockd: spurious grace period reject?!\n");
286 return -ENOLCK;
288 } else {
289 dprintk("lockd: server returns status %d\n", resp->status);
290 return 0; /* Okay, call complete */
293 /* Back off a little and try again */
294 interruptible_sleep_on_timeout(&host->h_gracewait, 15*HZ);
296 /* When the lock requested by F_SETLKW isn't available,
297 we will wait until the request can be satisfied. If
298 a signal is received during wait, we should return
299 -EINTR. */
300 if (signalled ()) {
301 status = -EINTR;
302 break;
304 } while (1);
306 return status;
310 * Generic NLM call, async version.
313 nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
315 struct nlm_host *host = req->a_host;
316 struct rpc_clnt *clnt;
317 struct nlm_args *argp = &req->a_args;
318 struct nlm_res *resp = &req->a_res;
319 struct rpc_message msg;
320 int status;
322 dprintk("lockd: call procedure %s on %s (async)\n",
323 nlm_procname(proc), host->h_name);
325 /* If we have no RPC client yet, create one. */
326 if ((clnt = nlm_bind_host(host)) == NULL)
327 return -ENOLCK;
329 /* bootstrap and kick off the async RPC call */
330 msg.rpc_proc = proc;
331 msg.rpc_argp = argp;
332 msg.rpc_resp =resp;
333 msg.rpc_cred = NULL;
334 status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
336 return status;
340 nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
342 struct nlm_host *host = req->a_host;
343 struct rpc_clnt *clnt;
344 struct nlm_args *argp = &req->a_args;
345 struct nlm_res *resp = &req->a_res;
346 struct file *file = argp->lock.fl.fl_file;
347 struct rpc_message msg;
348 int status;
350 dprintk("lockd: call procedure %s on %s (async)\n",
351 nlm_procname(proc), host->h_name);
353 /* If we have no RPC client yet, create one. */
354 if ((clnt = nlm_bind_host(host)) == NULL)
355 return -ENOLCK;
357 /* bootstrap and kick off the async RPC call */
358 msg.rpc_proc = proc;
359 msg.rpc_argp = argp;
360 msg.rpc_resp =resp;
361 if (file)
362 msg.rpc_cred = nfs_file_cred(file);
363 else
364 msg.rpc_cred = NULL;
365 /* Increment host refcount */
366 nlm_get_host(host);
367 status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
368 if (status < 0)
369 nlm_release_host(host);
370 return status;
374 * TEST for the presence of a conflicting lock
376 static int
377 nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
379 int status;
381 if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0)
382 return status;
384 status = req->a_res.status;
385 if (status == NLM_LCK_GRANTED) {
386 fl->fl_type = F_UNLCK;
387 } if (status == NLM_LCK_DENIED) {
389 * Report the conflicting lock back to the application.
390 * FIXME: Is it OK to report the pid back as well?
392 memcpy(fl, &req->a_res.lock.fl, sizeof(*fl));
393 /* fl->fl_pid = 0; */
394 } else {
395 return nlm_stat_to_errno(req->a_res.status);
398 return 0;
401 static
402 void nlmclnt_insert_lock_callback(struct file_lock *fl)
404 nlm_get_host(fl->fl_u.nfs_fl.host);
406 static
407 void nlmclnt_remove_lock_callback(struct file_lock *fl)
409 if (fl->fl_u.nfs_fl.host) {
410 nlm_release_host(fl->fl_u.nfs_fl.host);
411 fl->fl_u.nfs_fl.host = NULL;
416 * LOCK: Try to create a lock
418 * Programmer Harassment Alert
420 * When given a blocking lock request in a sync RPC call, the HPUX lockd
421 * will faithfully return LCK_BLOCKED but never cares to notify us when
422 * the lock could be granted. This way, our local process could hang
423 * around forever waiting for the callback.
425 * Solution A: Implement busy-waiting
426 * Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES})
428 * For now I am implementing solution A, because I hate the idea of
429 * re-implementing lockd for a third time in two months. The async
430 * calls shouldn't be too hard to do, however.
432 * This is one of the lovely things about standards in the NFS area:
433 * they're so soft and squishy you can't really blame HP for doing this.
435 static int
436 nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
438 struct nlm_host *host = req->a_host;
439 struct nlm_res *resp = &req->a_res;
440 int status;
442 if (!host->h_monitored && nsm_monitor(host) < 0) {
443 printk(KERN_NOTICE "lockd: failed to monitor %s\n",
444 host->h_name);
445 return -ENOLCK;
448 do {
449 if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
450 if (resp->status != NLM_LCK_BLOCKED)
451 break;
452 status = nlmclnt_block(host, fl, &resp->status);
454 if (status < 0)
455 return status;
456 } while (resp->status == NLM_LCK_BLOCKED);
458 if (resp->status == NLM_LCK_GRANTED) {
459 fl->fl_u.nfs_fl.state = host->h_state;
460 fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
461 fl->fl_u.nfs_fl.host = host;
462 fl->fl_insert = nlmclnt_insert_lock_callback;
463 fl->fl_remove = nlmclnt_remove_lock_callback;
466 return nlm_stat_to_errno(resp->status);
470 * RECLAIM: Try to reclaim a lock
473 nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
475 struct nlm_rqst reqst, *req;
476 int status;
478 req = &reqst;
479 req->a_host = host;
480 req->a_flags = 0;
482 /* Set up the argument struct */
483 nlmclnt_setlockargs(req, fl);
484 req->a_args.reclaim = 1;
486 if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
487 && req->a_res.status == NLM_LCK_GRANTED)
488 return 0;
490 printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
491 "(errno %d, status %d)\n", fl->fl_pid,
492 status, req->a_res.status);
495 * FIXME: This is a serious failure. We can
497 * a. Ignore the problem
498 * b. Send the owning process some signal (Linux doesn't have
499 * SIGLOST, though...)
500 * c. Retry the operation
502 * Until someone comes up with a simple implementation
503 * for b or c, I'll choose option a.
506 return -ENOLCK;
510 * UNLOCK: remove an existing lock
512 static int
513 nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
515 struct nlm_res *resp = &req->a_res;
516 int status;
518 /* Clean the GRANTED flag now so the lock doesn't get
519 * reclaimed while we're stuck in the unlock call. */
520 fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
522 if (req->a_flags & RPC_TASK_ASYNC) {
523 return nlmclnt_async_call(req, NLMPROC_UNLOCK,
524 nlmclnt_unlock_callback);
527 if ((status = nlmclnt_call(req, NLMPROC_UNLOCK)) < 0)
528 return status;
530 if (resp->status == NLM_LCK_GRANTED)
531 return 0;
533 if (resp->status != NLM_LCK_DENIED_NOLOCKS)
534 printk("lockd: unexpected unlock status: %d\n", resp->status);
536 /* What to do now? I'm out of my depth... */
538 return -ENOLCK;
541 static void
542 nlmclnt_unlock_callback(struct rpc_task *task)
544 struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
545 int status = req->a_res.status;
547 if (RPC_ASSASSINATED(task))
548 goto die;
550 if (task->tk_status < 0) {
551 dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
552 goto retry_unlock;
554 if (status != NLM_LCK_GRANTED
555 && status != NLM_LCK_DENIED_GRACE_PERIOD) {
556 printk("lockd: unexpected unlock status: %d\n", status);
559 die:
560 nlm_release_host(req->a_host);
561 kfree(req);
562 return;
563 retry_unlock:
564 nlm_rebind_host(req->a_host);
565 rpc_restart_call(task);
569 * Cancel a blocked lock request.
570 * We always use an async RPC call for this in order not to hang a
571 * process that has been Ctrl-C'ed.
574 nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl)
576 struct nlm_rqst *req;
577 unsigned long flags;
578 sigset_t oldset;
579 int status;
581 /* Block all signals while setting up call */
582 spin_lock_irqsave(&current->sigmask_lock, flags);
583 oldset = current->blocked;
584 sigfillset(&current->blocked);
585 recalc_sigpending(current);
586 spin_unlock_irqrestore(&current->sigmask_lock, flags);
588 req = nlmclnt_alloc_call();
589 if (!req)
590 return -ENOMEM;
591 req->a_host = host;
592 req->a_flags = RPC_TASK_ASYNC;
594 nlmclnt_setlockargs(req, fl);
596 status = nlmclnt_async_call(req, NLMPROC_CANCEL,
597 nlmclnt_cancel_callback);
598 if (status < 0)
599 kfree(req);
601 spin_lock_irqsave(&current->sigmask_lock, flags);
602 current->blocked = oldset;
603 recalc_sigpending(current);
604 spin_unlock_irqrestore(&current->sigmask_lock, flags);
606 return status;
609 static void
610 nlmclnt_cancel_callback(struct rpc_task *task)
612 struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
614 if (RPC_ASSASSINATED(task))
615 goto die;
617 if (task->tk_status < 0) {
618 dprintk("lockd: CANCEL call error %d, retrying.\n",
619 task->tk_status);
620 goto retry_cancel;
623 dprintk("lockd: cancel status %d (task %d)\n",
624 req->a_res.status, task->tk_pid);
626 switch (req->a_res.status) {
627 case NLM_LCK_GRANTED:
628 case NLM_LCK_DENIED_GRACE_PERIOD:
629 /* Everything's good */
630 break;
631 case NLM_LCK_DENIED_NOLOCKS:
632 dprintk("lockd: CANCEL failed (server has no locks)\n");
633 goto retry_cancel;
634 default:
635 printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
636 req->a_res.status);
639 die:
640 nlm_release_host(req->a_host);
641 kfree(req);
642 return;
644 retry_cancel:
645 nlm_rebind_host(req->a_host);
646 rpc_restart_call(task);
647 rpc_delay(task, 30 * HZ);
651 * Convert an NLM status code to a generic kernel errno
653 static int
654 nlm_stat_to_errno(u32 status)
656 switch(status) {
657 case NLM_LCK_GRANTED:
658 return 0;
659 case NLM_LCK_DENIED:
660 return -EAGAIN;
661 case NLM_LCK_DENIED_NOLOCKS:
662 case NLM_LCK_DENIED_GRACE_PERIOD:
663 return -ENOLCK;
664 case NLM_LCK_BLOCKED:
665 printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n");
666 return -ENOLCK;
668 printk(KERN_NOTICE "lockd: unexpected server status %d\n", status);
669 return -ENOLCK;