ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / lockd / clntproc.c
1 /*
2  * linux/fs/lockd/clntproc.c
3  *
4  * RPC procedures for the client side NLM implementation
5  *
6  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #include <linux/config.h>
10 #include <linux/types.h>
11 #include <linux/errno.h>
12 #include <linux/fs.h>
13 #include <linux/nfs_fs.h>
14 #include <linux/utsname.h>
15 #include <linux/smp_lock.h>
16 #include <linux/sunrpc/clnt.h>
17 #include <linux/sunrpc/svc.h>
18 #include <linux/lockd/lockd.h>
19 #include <linux/lockd/sm_inter.h>
20
21 #define NLMDBG_FACILITY         NLMDBG_CLIENT
22 #define NLMCLNT_GRACE_WAIT      (5*HZ)
23
24 static int      nlmclnt_test(struct nlm_rqst *, struct file_lock *);
25 static int      nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
26 static int      nlmclnt_unlock(struct nlm_rqst *, struct file_lock *);
27 static void     nlmclnt_unlock_callback(struct rpc_task *);
28 static void     nlmclnt_cancel_callback(struct rpc_task *);
29 static int      nlm_stat_to_errno(u32 stat);
30
31 /*
32  * Cookie counter for NLM requests
33  */
34 static u32      nlm_cookie = 0x1234;
35
36 static inline void nlmclnt_next_cookie(struct nlm_cookie *c)
37 {
38         memcpy(c->data, &nlm_cookie, 4);
39         memset(c->data+4, 0, 4);
40         c->len=4;
41         nlm_cookie++;
42 }
43
44 /*
45  * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls
46  */
47 static inline void
48 nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
49 {
50         struct nlm_args *argp = &req->a_args;
51         struct nlm_lock *lock = &argp->lock;
52
53         nlmclnt_next_cookie(&argp->cookie);
54         argp->state   = nsm_local_state;
55         memcpy(&lock->fh, NFS_FH(fl->fl_file->f_dentry->d_inode), sizeof(struct nfs_fh));
56         lock->caller  = system_utsname.nodename;
57         lock->oh.data = req->a_owner;
58         lock->oh.len  = sprintf(req->a_owner, "%d@%s",
59                                 current->pid, system_utsname.nodename);
60         locks_copy_lock(&lock->fl, fl);
61 }
62
63 /*
64  * Initialize arguments for GRANTED call. The nlm_rqst structure
65  * has been cleared already.
66  */
67 int
68 nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
69 {
70         locks_copy_lock(&call->a_args.lock.fl, &lock->fl);
71         memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh));
72         call->a_args.lock.caller = system_utsname.nodename;
73         call->a_args.lock.oh.len = lock->oh.len;
74
75         /* set default data area */
76         call->a_args.lock.oh.data = call->a_owner;
77
78         if (lock->oh.len > NLMCLNT_OHSIZE) {
79                 void *data = kmalloc(lock->oh.len, GFP_KERNEL);
80                 if (!data)
81                         return 0;
82                 call->a_args.lock.oh.data = (u8 *) data;
83         }
84
85         memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len);
86         return 1;
87 }
88
89 void
90 nlmclnt_freegrantargs(struct nlm_rqst *call)
91 {
92         /*
93          * Check whether we allocated memory for the owner.
94          */
95         if (call->a_args.lock.oh.data != (u8 *) call->a_owner) {
96                 kfree(call->a_args.lock.oh.data);
97         }
98 }
99
100 /*
101  * This is the main entry point for the NLM client.
102  */
103 int
104 nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
105 {
106         struct nfs_server       *nfssrv = NFS_SERVER(inode);
107         struct nlm_host         *host;
108         struct nlm_rqst         reqst, *call = &reqst;
109         sigset_t                oldset;
110         unsigned long           flags;
111         int                     status, proto, vers;
112
113         vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1;
114         if (NFS_PROTO(inode)->version > 3) {
115                 printk(KERN_NOTICE "NFSv4 file locking not implemented!\n");
116                 return -ENOLCK;
117         }
118
119         /* Retrieve transport protocol from NFS client */
120         proto = NFS_CLIENT(inode)->cl_xprt->prot;
121
122         if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers)))
123                 return -ENOLCK;
124
125         /* Create RPC client handle if not there, and copy soft
126          * and intr flags from NFS client. */
127         if (host->h_rpcclnt == NULL) {
128                 struct rpc_clnt *clnt;
129
130                 /* Bind an rpc client to this host handle (does not
131                  * perform a portmapper lookup) */
132                 if (!(clnt = nlm_bind_host(host))) {
133                         status = -ENOLCK;
134                         goto done;
135                 }
136                 clnt->cl_softrtry = nfssrv->client->cl_softrtry;
137                 clnt->cl_intr     = nfssrv->client->cl_intr;
138                 clnt->cl_chatty   = nfssrv->client->cl_chatty;
139         }
140
141         /* Keep the old signal mask */
142         spin_lock_irqsave(&current->sighand->siglock, flags);
143         oldset = current->blocked;
144
145         /* If we're cleaning up locks because the process is exiting,
146          * perform the RPC call asynchronously. */
147         if ((IS_SETLK(cmd) || IS_SETLKW(cmd))
148             && fl->fl_type == F_UNLCK
149             && (current->flags & PF_EXITING)) {
150                 sigfillset(&current->blocked);  /* Mask all signals */
151                 recalc_sigpending();
152                 spin_unlock_irqrestore(&current->sighand->siglock, flags);
153
154                 call = nlmclnt_alloc_call();
155                 if (!call) {
156                         status = -ENOMEM;
157                         goto out_restore;
158                 }
159                 call->a_flags = RPC_TASK_ASYNC;
160         } else {
161                 spin_unlock_irqrestore(&current->sighand->siglock, flags);
162                 memset(call, 0, sizeof(*call));
163                 locks_init_lock(&call->a_args.lock.fl);
164                 locks_init_lock(&call->a_res.lock.fl);
165         }
166         call->a_host = host;
167
168         /* Set up the argument struct */
169         nlmclnt_setlockargs(call, fl);
170
171         if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
172                 if (fl->fl_type != F_UNLCK) {
173                         call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
174                         status = nlmclnt_lock(call, fl);
175                 } else
176                         status = nlmclnt_unlock(call, fl);
177         } else if (IS_GETLK(cmd))
178                 status = nlmclnt_test(call, fl);
179         else
180                 status = -EINVAL;
181
182         if (status < 0 && (call->a_flags & RPC_TASK_ASYNC))
183                 kfree(call);
184
185  out_restore:
186         spin_lock_irqsave(&current->sighand->siglock, flags);
187         current->blocked = oldset;
188         recalc_sigpending();
189         spin_unlock_irqrestore(&current->sighand->siglock, flags);
190
191 done:
192         dprintk("lockd: clnt proc returns %d\n", status);
193         nlm_release_host(host);
194         return status;
195 }
196
197 /*
198  * Allocate an NLM RPC call struct
199  */
200 struct nlm_rqst *
201 nlmclnt_alloc_call(void)
202 {
203         struct nlm_rqst *call;
204
205         while (!signalled()) {
206                 call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL);
207                 if (call) {
208                         memset(call, 0, sizeof(*call));
209                         locks_init_lock(&call->a_args.lock.fl);
210                         locks_init_lock(&call->a_res.lock.fl);
211                         return call;
212                 }
213                 printk("nlmclnt_alloc_call: failed, waiting for memory\n");
214                 current->state = TASK_INTERRUPTIBLE;
215                 schedule_timeout(5*HZ);
216         }
217         return NULL;
218 }
219
220 static int nlm_wait_on_grace(wait_queue_head_t *queue)
221 {
222         DEFINE_WAIT(wait);
223         int status = -EINTR;
224
225         prepare_to_wait(queue, &wait, TASK_INTERRUPTIBLE);
226         if (!signalled ()) {
227                 schedule_timeout(NLMCLNT_GRACE_WAIT);
228                 if (!signalled ())
229                         status = 0;
230         }
231         finish_wait(queue, &wait);
232         return status;
233 }
234
235 /*
236  * Generic NLM call
237  */
238 int
239 nlmclnt_call(struct nlm_rqst *req, u32 proc)
240 {
241         struct nlm_host *host = req->a_host;
242         struct rpc_clnt *clnt;
243         struct nlm_args *argp = &req->a_args;
244         struct nlm_res  *resp = &req->a_res;
245         struct file     *filp = argp->lock.fl.fl_file;
246         struct rpc_message msg = {
247                 .rpc_argp       = argp,
248                 .rpc_resp       = resp,
249         };
250         int             status;
251
252         dprintk("lockd: call procedure %d on %s\n",
253                         (int)proc, host->h_name);
254
255         if (filp)
256                 msg.rpc_cred = nfs_file_cred(filp);
257
258         do {
259                 if (host->h_reclaiming && !argp->reclaim)
260                         goto in_grace_period;
261
262                 /* If we have no RPC client yet, create one. */
263                 if ((clnt = nlm_bind_host(host)) == NULL)
264                         return -ENOLCK;
265                 msg.rpc_proc = &clnt->cl_procinfo[proc];
266
267                 /* Perform the RPC call. If an error occurs, try again */
268                 if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) {
269                         dprintk("lockd: rpc_call returned error %d\n", -status);
270                         switch (status) {
271                         case -EPROTONOSUPPORT:
272                                 status = -EINVAL;
273                                 break;
274                         case -ECONNREFUSED:
275                         case -ETIMEDOUT:
276                         case -ENOTCONN:
277                                 nlm_rebind_host(host);
278                                 status = -EAGAIN;
279                                 break;
280                         case -ERESTARTSYS:
281                                 return signalled () ? -EINTR : status;
282                         default:
283                                 break;
284                         }
285                         break;
286                 } else
287                 if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) {
288                         dprintk("lockd: server in grace period\n");
289                         if (argp->reclaim) {
290                                 printk(KERN_WARNING
291                                      "lockd: spurious grace period reject?!\n");
292                                 return -ENOLCK;
293                         }
294                 } else {
295                         if (!argp->reclaim) {
296                                 /* We appear to be out of the grace period */
297                                 wake_up_all(&host->h_gracewait);
298                         }
299                         dprintk("lockd: server returns status %d\n", resp->status);
300                         return 0;       /* Okay, call complete */
301                 }
302
303 in_grace_period:
304                 /*
305                  * The server has rebooted and appears to be in the grace
306                  * period during which locks are only allowed to be
307                  * reclaimed.
308                  * We can only back off and try again later.
309                  */
310                 status = nlm_wait_on_grace(&host->h_gracewait);
311         } while (status == 0);
312
313         return status;
314 }
315
316 /*
317  * Generic NLM call, async version.
318  */
319 int
320 nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
321 {
322         struct nlm_host *host = req->a_host;
323         struct rpc_clnt *clnt;
324         struct rpc_message msg = {
325                 .rpc_argp       = &req->a_args,
326                 .rpc_resp       = &req->a_res,
327         };
328         int             status;
329
330         dprintk("lockd: call procedure %d on %s (async)\n",
331                         (int)proc, host->h_name);
332
333         /* If we have no RPC client yet, create one. */
334         if ((clnt = nlm_bind_host(host)) == NULL)
335                 return -ENOLCK;
336         msg.rpc_proc = &clnt->cl_procinfo[proc];
337
338         /* bootstrap and kick off the async RPC call */
339         status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
340
341         return status;
342 }
343
344 int
345 nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
346 {
347         struct nlm_host *host = req->a_host;
348         struct rpc_clnt *clnt;
349         struct nlm_args *argp = &req->a_args;
350         struct nlm_res  *resp = &req->a_res;
351         struct file     *file = argp->lock.fl.fl_file;
352         struct rpc_message msg = {
353                 .rpc_argp       = argp,
354                 .rpc_resp       = resp,
355         };
356         int             status;
357
358         dprintk("lockd: call procedure %d on %s (async)\n",
359                         (int)proc, host->h_name);
360
361         /* If we have no RPC client yet, create one. */
362         if ((clnt = nlm_bind_host(host)) == NULL)
363                 return -ENOLCK;
364         msg.rpc_proc = &clnt->cl_procinfo[proc];
365
366         /* bootstrap and kick off the async RPC call */
367         if (file)
368                 msg.rpc_cred = nfs_file_cred(file);
369         /* Increment host refcount */
370         nlm_get_host(host);
371         status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
372         if (status < 0)
373                 nlm_release_host(host);
374         return status;
375 }
376
377 /*
378  * TEST for the presence of a conflicting lock
379  */
380 static int
381 nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
382 {
383         int     status;
384
385         if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0)
386                 return status;
387
388         status = req->a_res.status;
389         if (status == NLM_LCK_GRANTED) {
390                 fl->fl_type = F_UNLCK;
391         } if (status == NLM_LCK_DENIED) {
392                 /*
393                  * Report the conflicting lock back to the application.
394                  * FIXME: Is it OK to report the pid back as well?
395                  */
396                 locks_copy_lock(fl, &req->a_res.lock.fl);
397                 /* fl->fl_pid = 0; */
398         } else {
399                 return nlm_stat_to_errno(req->a_res.status);
400         }
401
402         return 0;
403 }
404
405 static
406 void nlmclnt_insert_lock_callback(struct file_lock *fl)
407 {
408         nlm_get_host(fl->fl_u.nfs_fl.host);
409 }
410 static
411 void nlmclnt_remove_lock_callback(struct file_lock *fl)
412 {
413         if (fl->fl_u.nfs_fl.host) {
414                 nlm_release_host(fl->fl_u.nfs_fl.host);
415                 fl->fl_u.nfs_fl.host = NULL;
416         }
417 }
418
419 /*
420  * LOCK: Try to create a lock
421  *
422  *                      Programmer Harassment Alert
423  *
424  * When given a blocking lock request in a sync RPC call, the HPUX lockd
425  * will faithfully return LCK_BLOCKED but never cares to notify us when
426  * the lock could be granted. This way, our local process could hang
427  * around forever waiting for the callback.
428  *
429  *  Solution A: Implement busy-waiting
430  *  Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES})
431  *
432  * For now I am implementing solution A, because I hate the idea of
433  * re-implementing lockd for a third time in two months. The async
434  * calls shouldn't be too hard to do, however.
435  *
436  * This is one of the lovely things about standards in the NFS area:
437  * they're so soft and squishy you can't really blame HP for doing this.
438  */
439 static int
440 nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
441 {
442         struct nlm_host *host = req->a_host;
443         struct nlm_res  *resp = &req->a_res;
444         int             status;
445
446         if (!host->h_monitored && nsm_monitor(host) < 0) {
447                 printk(KERN_NOTICE "lockd: failed to monitor %s\n",
448                                         host->h_name);
449                 return -ENOLCK;
450         }
451
452         do {
453                 if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
454                         if (resp->status != NLM_LCK_BLOCKED)
455                                 break;
456                         status = nlmclnt_block(host, fl, &resp->status);
457                 }
458                 if (status < 0)
459                         return status;
460         } while (resp->status == NLM_LCK_BLOCKED && req->a_args.block);
461
462         if (resp->status == NLM_LCK_GRANTED) {
463                 fl->fl_u.nfs_fl.state = host->h_state;
464                 fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
465                 fl->fl_u.nfs_fl.host = host;
466                 fl->fl_insert = nlmclnt_insert_lock_callback;
467                 fl->fl_remove = nlmclnt_remove_lock_callback;
468         }
469
470         return nlm_stat_to_errno(resp->status);
471 }
472
473 /*
474  * RECLAIM: Try to reclaim a lock
475  */
476 int
477 nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
478 {
479         struct nlm_rqst reqst, *req;
480         int             status;
481
482         req = &reqst;
483         memset(req, 0, sizeof(*req));
484         locks_init_lock(&req->a_args.lock.fl);
485         locks_init_lock(&req->a_res.lock.fl);
486         req->a_host  = host;
487         req->a_flags = 0;
488
489         /* Set up the argument struct */
490         nlmclnt_setlockargs(req, fl);
491         req->a_args.reclaim = 1;
492
493         if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
494          && req->a_res.status == NLM_LCK_GRANTED)
495                 return 0;
496
497         printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
498                                 "(errno %d, status %d)\n", fl->fl_pid,
499                                 status, req->a_res.status);
500
501         /*
502          * FIXME: This is a serious failure. We can
503          *
504          *  a.  Ignore the problem
505          *  b.  Send the owning process some signal (Linux doesn't have
506          *      SIGLOST, though...)
507          *  c.  Retry the operation
508          *
509          * Until someone comes up with a simple implementation
510          * for b or c, I'll choose option a.
511          */
512
513         return -ENOLCK;
514 }
515
516 /*
517  * UNLOCK: remove an existing lock
518  */
519 static int
520 nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
521 {
522         struct nlm_res  *resp = &req->a_res;
523         int             status;
524
525         /* Clean the GRANTED flag now so the lock doesn't get
526          * reclaimed while we're stuck in the unlock call. */
527         fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
528
529         if (req->a_flags & RPC_TASK_ASYNC) {
530                 return nlmclnt_async_call(req, NLMPROC_UNLOCK,
531                                         nlmclnt_unlock_callback);
532         }
533
534         if ((status = nlmclnt_call(req, NLMPROC_UNLOCK)) < 0)
535                 return status;
536
537         if (resp->status == NLM_LCK_GRANTED)
538                 return 0;
539
540         if (resp->status != NLM_LCK_DENIED_NOLOCKS)
541                 printk("lockd: unexpected unlock status: %d\n", resp->status);
542
543         /* What to do now? I'm out of my depth... */
544
545         return -ENOLCK;
546 }
547
548 static void
549 nlmclnt_unlock_callback(struct rpc_task *task)
550 {
551         struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
552         int             status = req->a_res.status;
553
554         if (RPC_ASSASSINATED(task))
555                 goto die;
556
557         if (task->tk_status < 0) {
558                 dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
559                 goto retry_rebind;
560         }
561         if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
562                 rpc_delay(task, NLMCLNT_GRACE_WAIT);
563                 goto retry_unlock;
564         }
565         if (status != NLM_LCK_GRANTED)
566                 printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status);
567
568 die:
569         nlm_release_host(req->a_host);
570         kfree(req);
571         return;
572  retry_rebind:
573         nlm_rebind_host(req->a_host);
574  retry_unlock:
575         rpc_restart_call(task);
576 }
577
578 /*
579  * Cancel a blocked lock request.
580  * We always use an async RPC call for this in order not to hang a
581  * process that has been Ctrl-C'ed.
582  */
583 int
584 nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl)
585 {
586         struct nlm_rqst *req;
587         unsigned long   flags;
588         sigset_t        oldset;
589         int             status;
590
591         /* Block all signals while setting up call */
592         spin_lock_irqsave(&current->sighand->siglock, flags);
593         oldset = current->blocked;
594         sigfillset(&current->blocked);
595         recalc_sigpending();
596         spin_unlock_irqrestore(&current->sighand->siglock, flags);
597
598         req = nlmclnt_alloc_call();
599         if (!req)
600                 return -ENOMEM;
601         req->a_host  = host;
602         req->a_flags = RPC_TASK_ASYNC;
603
604         nlmclnt_setlockargs(req, fl);
605
606         status = nlmclnt_async_call(req, NLMPROC_CANCEL,
607                                         nlmclnt_cancel_callback);
608         if (status < 0)
609                 kfree(req);
610
611         spin_lock_irqsave(&current->sighand->siglock, flags);
612         current->blocked = oldset;
613         recalc_sigpending();
614         spin_unlock_irqrestore(&current->sighand->siglock, flags);
615
616         return status;
617 }
618
619 static void
620 nlmclnt_cancel_callback(struct rpc_task *task)
621 {
622         struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
623
624         if (RPC_ASSASSINATED(task))
625                 goto die;
626
627         if (task->tk_status < 0) {
628                 dprintk("lockd: CANCEL call error %d, retrying.\n",
629                                         task->tk_status);
630                 goto retry_cancel;
631         }
632
633         dprintk("lockd: cancel status %d (task %d)\n",
634                         req->a_res.status, task->tk_pid);
635
636         switch (req->a_res.status) {
637         case NLM_LCK_GRANTED:
638         case NLM_LCK_DENIED_GRACE_PERIOD:
639                 /* Everything's good */
640                 break;
641         case NLM_LCK_DENIED_NOLOCKS:
642                 dprintk("lockd: CANCEL failed (server has no locks)\n");
643                 goto retry_cancel;
644         default:
645                 printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
646                         req->a_res.status);
647         }
648
649 die:
650         nlm_release_host(req->a_host);
651         kfree(req);
652         return;
653
654 retry_cancel:
655         nlm_rebind_host(req->a_host);
656         rpc_restart_call(task);
657         rpc_delay(task, 30 * HZ);
658 }
659
660 /*
661  * Convert an NLM status code to a generic kernel errno
662  */
663 static int
664 nlm_stat_to_errno(u32 status)
665 {
666         switch(status) {
667         case NLM_LCK_GRANTED:
668                 return 0;
669         case NLM_LCK_DENIED:
670                 return -EAGAIN;
671         case NLM_LCK_DENIED_NOLOCKS:
672         case NLM_LCK_DENIED_GRACE_PERIOD:
673                 return -ENOLCK;
674         case NLM_LCK_BLOCKED:
675                 printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n");
676                 return -ENOLCK;
677 #ifdef CONFIG_LOCKD_V4
678         case NLM_DEADLCK:
679                 return -EDEADLK;
680         case NLM_ROFS:
681                 return -EROFS;
682         case NLM_STALE_FH:
683                 return -ESTALE;
684         case NLM_FBIG:
685                 return -EOVERFLOW;
686         case NLM_FAILED:
687                 return -ENOLCK;
688 #endif
689         }
690         printk(KERN_NOTICE "lockd: unexpected server status %d\n", status);
691         return -ENOLCK;
692 }