GNU Linux-libre 4.9.317-gnu1
[releases.git] / fs / coda / upcall.c
1 /*
2  * Mostly platform independent upcall operations to Venus:
3  *  -- upcalls
4  *  -- upcall routines
5  *
6  * Linux 2.0 version
7  * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>, 
8  * Michael Callahan <callahan@maths.ox.ac.uk> 
9  * 
10  * Redone for Linux 2.1
11  * Copyright (C) 1997 Carnegie Mellon University
12  *
13  * Carnegie Mellon University encourages users of this code to contribute
14  * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
15  */
16
17 #include <linux/signal.h>
18 #include <linux/sched.h>
19 #include <linux/types.h>
20 #include <linux/kernel.h>
21 #include <linux/mm.h>
22 #include <linux/time.h>
23 #include <linux/fs.h>
24 #include <linux/file.h>
25 #include <linux/stat.h>
26 #include <linux/errno.h>
27 #include <linux/string.h>
28 #include <linux/slab.h>
29 #include <linux/mutex.h>
30 #include <linux/uaccess.h>
31 #include <linux/vmalloc.h>
32 #include <linux/vfs.h>
33
34 #include <linux/coda.h>
35 #include <linux/coda_psdev.h>
36 #include "coda_linux.h"
37 #include "coda_cache.h"
38
39 #include "coda_int.h"
40
41 static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
42                        union inputArgs *buffer);
43
44 static void *alloc_upcall(int opcode, int size)
45 {
46         union inputArgs *inp;
47
48         CODA_ALLOC(inp, union inputArgs *, size);
49         if (!inp)
50                 return ERR_PTR(-ENOMEM);
51
52         inp->ih.opcode = opcode;
53         inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns);
54         inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns);
55         inp->ih.uid = from_kuid(&init_user_ns, current_fsuid());
56
57         return (void*)inp;
58 }
59
60 #define UPARG(op)\
61 do {\
62         inp = (union inputArgs *)alloc_upcall(op, insize); \
63         if (IS_ERR(inp)) { return PTR_ERR(inp); }\
64         outp = (union outputArgs *)(inp); \
65         outsize = insize; \
66 } while (0)
67
68 #define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
69 #define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
70 #define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
71
72
73 /* the upcalls */
74 int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
75 {
76         union inputArgs *inp;
77         union outputArgs *outp;
78         int insize, outsize, error;
79
80         insize = SIZE(root);
81         UPARG(CODA_ROOT);
82
83         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
84         if (!error)
85                 *fidp = outp->coda_root.VFid;
86
87         CODA_FREE(inp, insize);
88         return error;
89 }
90
91 int venus_getattr(struct super_block *sb, struct CodaFid *fid, 
92                      struct coda_vattr *attr) 
93 {
94         union inputArgs *inp;
95         union outputArgs *outp;
96         int insize, outsize, error;
97
98         insize = SIZE(getattr); 
99         UPARG(CODA_GETATTR);
100         inp->coda_getattr.VFid = *fid;
101
102         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
103         if (!error)
104                 *attr = outp->coda_getattr.attr;
105
106         CODA_FREE(inp, insize);
107         return error;
108 }
109
110 int venus_setattr(struct super_block *sb, struct CodaFid *fid, 
111                   struct coda_vattr *vattr)
112 {
113         union inputArgs *inp;
114         union outputArgs *outp;
115         int insize, outsize, error;
116         
117         insize = SIZE(setattr);
118         UPARG(CODA_SETATTR);
119
120         inp->coda_setattr.VFid = *fid;
121         inp->coda_setattr.attr = *vattr;
122
123         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
124
125         CODA_FREE(inp, insize);
126         return error;
127 }
128
129 int venus_lookup(struct super_block *sb, struct CodaFid *fid, 
130                     const char *name, int length, int * type, 
131                     struct CodaFid *resfid)
132 {
133         union inputArgs *inp;
134         union outputArgs *outp;
135         int insize, outsize, error;
136         int offset;
137
138         offset = INSIZE(lookup);
139         insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
140         UPARG(CODA_LOOKUP);
141
142         inp->coda_lookup.VFid = *fid;
143         inp->coda_lookup.name = offset;
144         inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
145         /* send Venus a null terminated string */
146         memcpy((char *)(inp) + offset, name, length);
147         *((char *)inp + offset + length) = '\0';
148
149         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
150         if (!error) {
151                 *resfid = outp->coda_lookup.VFid;
152                 *type = outp->coda_lookup.vtype;
153         }
154
155         CODA_FREE(inp, insize);
156         return error;
157 }
158
159 int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
160                 kuid_t uid)
161 {
162         union inputArgs *inp;
163         union outputArgs *outp;
164         int insize, outsize, error;
165         
166         insize = SIZE(release);
167         UPARG(CODA_CLOSE);
168         
169         inp->ih.uid = from_kuid(&init_user_ns, uid);
170         inp->coda_close.VFid = *fid;
171         inp->coda_close.flags = flags;
172
173         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
174
175         CODA_FREE(inp, insize);
176         return error;
177 }
178
179 int venus_open(struct super_block *sb, struct CodaFid *fid,
180                   int flags, struct file **fh)
181 {
182         union inputArgs *inp;
183         union outputArgs *outp;
184         int insize, outsize, error;
185        
186         insize = SIZE(open_by_fd);
187         UPARG(CODA_OPEN_BY_FD);
188
189         inp->coda_open_by_fd.VFid = *fid;
190         inp->coda_open_by_fd.flags = flags;
191
192         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
193         if (!error)
194                 *fh = outp->coda_open_by_fd.fh;
195
196         CODA_FREE(inp, insize);
197         return error;
198 }       
199
200 int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, 
201                    const char *name, int length, 
202                    struct CodaFid *newfid, struct coda_vattr *attrs)
203 {
204         union inputArgs *inp;
205         union outputArgs *outp;
206         int insize, outsize, error;
207         int offset;
208
209         offset = INSIZE(mkdir);
210         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
211         UPARG(CODA_MKDIR);
212
213         inp->coda_mkdir.VFid = *dirfid;
214         inp->coda_mkdir.attr = *attrs;
215         inp->coda_mkdir.name = offset;
216         /* Venus must get null terminated string */
217         memcpy((char *)(inp) + offset, name, length);
218         *((char *)inp + offset + length) = '\0';
219
220         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
221         if (!error) {
222                 *attrs = outp->coda_mkdir.attr;
223                 *newfid = outp->coda_mkdir.VFid;
224         }
225
226         CODA_FREE(inp, insize);
227         return error;        
228 }
229
230
231 int venus_rename(struct super_block *sb, struct CodaFid *old_fid, 
232                  struct CodaFid *new_fid, size_t old_length, 
233                  size_t new_length, const char *old_name, 
234                  const char *new_name)
235 {
236         union inputArgs *inp;
237         union outputArgs *outp;
238         int insize, outsize, error; 
239         int offset, s;
240         
241         offset = INSIZE(rename);
242         insize = max_t(unsigned int, offset + new_length + old_length + 8,
243                      OUTSIZE(rename)); 
244         UPARG(CODA_RENAME);
245
246         inp->coda_rename.sourceFid = *old_fid;
247         inp->coda_rename.destFid =  *new_fid;
248         inp->coda_rename.srcname = offset;
249
250         /* Venus must receive an null terminated string */
251         s = ( old_length & ~0x3) +4; /* round up to word boundary */
252         memcpy((char *)(inp) + offset, old_name, old_length);
253         *((char *)inp + offset + old_length) = '\0';
254
255         /* another null terminated string for Venus */
256         offset += s;
257         inp->coda_rename.destname = offset;
258         s = ( new_length & ~0x3) +4; /* round up to word boundary */
259         memcpy((char *)(inp) + offset, new_name, new_length);
260         *((char *)inp + offset + new_length) = '\0';
261
262         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
263
264         CODA_FREE(inp, insize);
265         return error;
266 }
267
268 int venus_create(struct super_block *sb, struct CodaFid *dirfid, 
269                  const char *name, int length, int excl, int mode,
270                  struct CodaFid *newfid, struct coda_vattr *attrs) 
271 {
272         union inputArgs *inp;
273         union outputArgs *outp;
274         int insize, outsize, error;
275         int offset;
276
277         offset = INSIZE(create);
278         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
279         UPARG(CODA_CREATE);
280
281         inp->coda_create.VFid = *dirfid;
282         inp->coda_create.attr.va_mode = mode;
283         inp->coda_create.excl = excl;
284         inp->coda_create.mode = mode;
285         inp->coda_create.name = offset;
286
287         /* Venus must get null terminated string */
288         memcpy((char *)(inp) + offset, name, length);
289         *((char *)inp + offset + length) = '\0';
290
291         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
292         if (!error) {
293                 *attrs = outp->coda_create.attr;
294                 *newfid = outp->coda_create.VFid;
295         }
296
297         CODA_FREE(inp, insize);
298         return error;        
299 }
300
301 int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, 
302                     const char *name, int length)
303 {
304         union inputArgs *inp;
305         union outputArgs *outp;
306         int insize, outsize, error;
307         int offset;
308
309         offset = INSIZE(rmdir);
310         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
311         UPARG(CODA_RMDIR);
312
313         inp->coda_rmdir.VFid = *dirfid;
314         inp->coda_rmdir.name = offset;
315         memcpy((char *)(inp) + offset, name, length);
316         *((char *)inp + offset + length) = '\0';
317
318         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
319
320         CODA_FREE(inp, insize);
321         return error;
322 }
323
324 int venus_remove(struct super_block *sb, struct CodaFid *dirfid, 
325                     const char *name, int length)
326 {
327         union inputArgs *inp;
328         union outputArgs *outp;
329         int error=0, insize, outsize, offset;
330
331         offset = INSIZE(remove);
332         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
333         UPARG(CODA_REMOVE);
334
335         inp->coda_remove.VFid = *dirfid;
336         inp->coda_remove.name = offset;
337         memcpy((char *)(inp) + offset, name, length);
338         *((char *)inp + offset + length) = '\0';
339
340         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
341
342         CODA_FREE(inp, insize);
343         return error;
344 }
345
346 int venus_readlink(struct super_block *sb, struct CodaFid *fid, 
347                       char *buffer, int *length)
348
349         union inputArgs *inp;
350         union outputArgs *outp;
351         int insize, outsize, error;
352         int retlen;
353         char *result;
354         
355         insize = max_t(unsigned int,
356                      INSIZE(readlink), OUTSIZE(readlink)+ *length);
357         UPARG(CODA_READLINK);
358
359         inp->coda_readlink.VFid = *fid;
360
361         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
362         if (!error) {
363                 retlen = outp->coda_readlink.count;
364                 if (retlen >= *length)
365                         retlen = *length - 1;
366                 *length = retlen;
367                 result =  (char *)outp + (long)outp->coda_readlink.data;
368                 memcpy(buffer, result, retlen);
369                 *(buffer + retlen) = '\0';
370         }
371
372         CODA_FREE(inp, insize);
373         return error;
374 }
375
376
377
378 int venus_link(struct super_block *sb, struct CodaFid *fid, 
379                   struct CodaFid *dirfid, const char *name, int len )
380 {
381         union inputArgs *inp;
382         union outputArgs *outp;
383         int insize, outsize, error;
384         int offset;
385
386         offset = INSIZE(link);
387         insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));
388         UPARG(CODA_LINK);
389
390         inp->coda_link.sourceFid = *fid;
391         inp->coda_link.destFid = *dirfid;
392         inp->coda_link.tname = offset;
393
394         /* make sure strings are null terminated */
395         memcpy((char *)(inp) + offset, name, len);
396         *((char *)inp + offset + len) = '\0';
397
398         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
399
400         CODA_FREE(inp, insize);
401         return error;
402 }
403
404 int venus_symlink(struct super_block *sb, struct CodaFid *fid,
405                      const char *name, int len,
406                      const char *symname, int symlen)
407 {
408         union inputArgs *inp;
409         union outputArgs *outp;
410         int insize, outsize, error;
411         int offset, s;
412
413         offset = INSIZE(symlink);
414         insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
415         UPARG(CODA_SYMLINK);
416         
417         /*        inp->coda_symlink.attr = *tva; XXXXXX */ 
418         inp->coda_symlink.VFid = *fid;
419
420         /* Round up to word boundary and null terminate */
421         inp->coda_symlink.srcname = offset;
422         s = ( symlen  & ~0x3 ) + 4; 
423         memcpy((char *)(inp) + offset, symname, symlen);
424         *((char *)inp + offset + symlen) = '\0';
425         
426         /* Round up to word boundary and null terminate */
427         offset += s;
428         inp->coda_symlink.tname = offset;
429         s = (len & ~0x3) + 4;
430         memcpy((char *)(inp) + offset, name, len);
431         *((char *)inp + offset + len) = '\0';
432
433         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
434
435         CODA_FREE(inp, insize);
436         return error;
437 }
438
439 int venus_fsync(struct super_block *sb, struct CodaFid *fid)
440 {
441         union inputArgs *inp;
442         union outputArgs *outp; 
443         int insize, outsize, error;
444         
445         insize=SIZE(fsync);
446         UPARG(CODA_FSYNC);
447
448         inp->coda_fsync.VFid = *fid;
449         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
450
451         CODA_FREE(inp, insize);
452         return error;
453 }
454
455 int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
456 {
457         union inputArgs *inp;
458         union outputArgs *outp; 
459         int insize, outsize, error;
460
461         insize = SIZE(access);
462         UPARG(CODA_ACCESS);
463
464         inp->coda_access.VFid = *fid;
465         inp->coda_access.flags = mask;
466
467         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
468
469         CODA_FREE(inp, insize);
470         return error;
471 }
472
473
474 int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
475                  unsigned int cmd, struct PioctlData *data)
476 {
477         union inputArgs *inp;
478         union outputArgs *outp;  
479         int insize, outsize, error;
480         int iocsize;
481
482         insize = VC_MAXMSGSIZE;
483         UPARG(CODA_IOCTL);
484
485         /* build packet for Venus */
486         if (data->vi.in_size > VC_MAXDATASIZE) {
487                 error = -EINVAL;
488                 goto exit;
489         }
490
491         if (data->vi.out_size > VC_MAXDATASIZE) {
492                 error = -EINVAL;
493                 goto exit;
494         }
495
496         inp->coda_ioctl.VFid = *fid;
497     
498         /* the cmd field was mutated by increasing its size field to
499          * reflect the path and follow args. We need to subtract that
500          * out before sending the command to Venus.  */
501         inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));   
502         iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
503         inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<     16;     
504     
505         /* in->coda_ioctl.rwflag = flag; */
506         inp->coda_ioctl.len = data->vi.in_size;
507         inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
508      
509         /* get the data out of user space */
510         if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data,
511                            data->vi.in, data->vi.in_size)) {
512                 error = -EINVAL;
513                 goto exit;
514         }
515
516         error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
517                             &outsize, inp);
518
519         if (error) {
520                 pr_warn("%s: Venus returns: %d for %s\n",
521                         __func__, error, coda_f2s(fid));
522                 goto exit; 
523         }
524
525         if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
526                 error = -EINVAL;
527                 goto exit;
528         }
529         
530         /* Copy out the OUT buffer. */
531         if (outp->coda_ioctl.len > data->vi.out_size) {
532                 error = -EINVAL;
533                 goto exit;
534         }
535
536         /* Copy out the OUT buffer. */
537         if (copy_to_user(data->vi.out,
538                          (char *)outp + (long)outp->coda_ioctl.data,
539                          outp->coda_ioctl.len)) {
540                 error = -EFAULT;
541                 goto exit;
542         }
543
544  exit:
545         CODA_FREE(inp, insize);
546         return error;
547 }
548
549 int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
550
551         union inputArgs *inp;
552         union outputArgs *outp;
553         int insize, outsize, error;
554         
555         insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
556         UPARG(CODA_STATFS);
557
558         error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
559         if (!error) {
560                 sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
561                 sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;
562                 sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
563                 sfs->f_files  = outp->coda_statfs.stat.f_files;
564                 sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;
565         }
566
567         CODA_FREE(inp, insize);
568         return error;
569 }
570
571 /*
572  * coda_upcall and coda_downcall routines.
573  */
574 static void coda_block_signals(sigset_t *old)
575 {
576         spin_lock_irq(&current->sighand->siglock);
577         *old = current->blocked;
578
579         sigfillset(&current->blocked);
580         sigdelset(&current->blocked, SIGKILL);
581         sigdelset(&current->blocked, SIGSTOP);
582         sigdelset(&current->blocked, SIGINT);
583
584         recalc_sigpending();
585         spin_unlock_irq(&current->sighand->siglock);
586 }
587
588 static void coda_unblock_signals(sigset_t *old)
589 {
590         spin_lock_irq(&current->sighand->siglock);
591         current->blocked = *old;
592         recalc_sigpending();
593         spin_unlock_irq(&current->sighand->siglock);
594 }
595
596 /* Don't allow signals to interrupt the following upcalls before venus
597  * has seen them,
598  * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
599  * - CODA_STORE                         (to avoid data loss)
600  */
601 #define CODA_INTERRUPTIBLE(r) (!coda_hard && \
602                                (((r)->uc_opcode != CODA_CLOSE && \
603                                  (r)->uc_opcode != CODA_STORE && \
604                                  (r)->uc_opcode != CODA_RELEASE) || \
605                                 (r)->uc_flags & CODA_REQ_READ))
606
607 static inline void coda_waitfor_upcall(struct venus_comm *vcp,
608                                        struct upc_req *req)
609 {
610         DECLARE_WAITQUEUE(wait, current);
611         unsigned long timeout = jiffies + coda_timeout * HZ;
612         sigset_t old;
613         int blocked;
614
615         coda_block_signals(&old);
616         blocked = 1;
617
618         add_wait_queue(&req->uc_sleep, &wait);
619         for (;;) {
620                 if (CODA_INTERRUPTIBLE(req))
621                         set_current_state(TASK_INTERRUPTIBLE);
622                 else
623                         set_current_state(TASK_UNINTERRUPTIBLE);
624
625                 /* got a reply */
626                 if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
627                         break;
628
629                 if (blocked && time_after(jiffies, timeout) &&
630                     CODA_INTERRUPTIBLE(req))
631                 {
632                         coda_unblock_signals(&old);
633                         blocked = 0;
634                 }
635
636                 if (signal_pending(current)) {
637                         list_del(&req->uc_chain);
638                         break;
639                 }
640
641                 mutex_unlock(&vcp->vc_mutex);
642                 if (blocked)
643                         schedule_timeout(HZ);
644                 else
645                         schedule();
646                 mutex_lock(&vcp->vc_mutex);
647         }
648         if (blocked)
649                 coda_unblock_signals(&old);
650
651         remove_wait_queue(&req->uc_sleep, &wait);
652         set_current_state(TASK_RUNNING);
653 }
654
655
656 /*
657  * coda_upcall will return an error in the case of
658  * failed communication with Venus _or_ will peek at Venus
659  * reply and return Venus' error.
660  *
661  * As venus has 2 types of errors, normal errors (positive) and internal
662  * errors (negative), normal errors are negated, while internal errors
663  * are all mapped to -EINTR, while showing a nice warning message. (jh)
664  */
665 static int coda_upcall(struct venus_comm *vcp,
666                        int inSize, int *outSize,
667                        union inputArgs *buffer)
668 {
669         union outputArgs *out;
670         union inputArgs *sig_inputArgs;
671         struct upc_req *req = NULL, *sig_req;
672         int error;
673
674         mutex_lock(&vcp->vc_mutex);
675
676         if (!vcp->vc_inuse) {
677                 pr_notice("Venus dead, not sending upcall\n");
678                 error = -ENXIO;
679                 goto exit;
680         }
681
682         /* Format the request message. */
683         req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
684         if (!req) {
685                 error = -ENOMEM;
686                 goto exit;
687         }
688
689         req->uc_data = (void *)buffer;
690         req->uc_flags = 0;
691         req->uc_inSize = inSize;
692         req->uc_outSize = *outSize ? *outSize : inSize;
693         req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
694         req->uc_unique = ++vcp->vc_seq;
695         init_waitqueue_head(&req->uc_sleep);
696
697         /* Fill in the common input args. */
698         ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
699
700         /* Append msg to pending queue and poke Venus. */
701         list_add_tail(&req->uc_chain, &vcp->vc_pending);
702
703         wake_up_interruptible(&vcp->vc_waitq);
704         /* We can be interrupted while we wait for Venus to process
705          * our request.  If the interrupt occurs before Venus has read
706          * the request, we dequeue and return. If it occurs after the
707          * read but before the reply, we dequeue, send a signal
708          * message, and return. If it occurs after the reply we ignore
709          * it. In no case do we want to restart the syscall.  If it
710          * was interrupted by a venus shutdown (psdev_close), return
711          * ENODEV.  */
712
713         /* Go to sleep.  Wake up on signals only after the timeout. */
714         coda_waitfor_upcall(vcp, req);
715
716         /* Op went through, interrupt or not... */
717         if (req->uc_flags & CODA_REQ_WRITE) {
718                 out = (union outputArgs *)req->uc_data;
719                 /* here we map positive Venus errors to kernel errors */
720                 error = -out->oh.result;
721                 *outSize = req->uc_outSize;
722                 goto exit;
723         }
724
725         error = -EINTR;
726         if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
727                 pr_warn("Unexpected interruption.\n");
728                 goto exit;
729         }
730
731         /* Interrupted before venus read it. */
732         if (!(req->uc_flags & CODA_REQ_READ))
733                 goto exit;
734
735         /* Venus saw the upcall, make sure we can send interrupt signal */
736         if (!vcp->vc_inuse) {
737                 pr_info("Venus dead, not sending signal.\n");
738                 goto exit;
739         }
740
741         error = -ENOMEM;
742         sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
743         if (!sig_req) goto exit;
744
745         CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
746         if (!sig_req->uc_data) {
747                 kfree(sig_req);
748                 goto exit;
749         }
750
751         error = -EINTR;
752         sig_inputArgs = (union inputArgs *)sig_req->uc_data;
753         sig_inputArgs->ih.opcode = CODA_SIGNAL;
754         sig_inputArgs->ih.unique = req->uc_unique;
755
756         sig_req->uc_flags = CODA_REQ_ASYNC;
757         sig_req->uc_opcode = sig_inputArgs->ih.opcode;
758         sig_req->uc_unique = sig_inputArgs->ih.unique;
759         sig_req->uc_inSize = sizeof(struct coda_in_hdr);
760         sig_req->uc_outSize = sizeof(struct coda_in_hdr);
761
762         /* insert at head of queue! */
763         list_add(&(sig_req->uc_chain), &vcp->vc_pending);
764         wake_up_interruptible(&vcp->vc_waitq);
765
766 exit:
767         kfree(req);
768         mutex_unlock(&vcp->vc_mutex);
769         return error;
770 }
771
772 /*  
773     The statements below are part of the Coda opportunistic
774     programming -- taken from the Mach/BSD kernel code for Coda. 
775     You don't get correct semantics by stating what needs to be
776     done without guaranteeing the invariants needed for it to happen.
777     When will be have time to find out what exactly is going on?  (pjb)
778 */
779
780
781 /* 
782  * There are 7 cases where cache invalidations occur.  The semantics
783  *  of each is listed here:
784  *
785  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
786  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
787  *                  This call is a result of token expiration.
788  *
789  * The next arise as the result of callbacks on a file or directory.
790  * CODA_ZAPFILE   -- flush the cached attributes for a file.
791
792  * CODA_ZAPDIR    -- flush the attributes for the dir and
793  *                  force a new lookup for all the children
794                     of this dir.
795
796  *
797  * The next is a result of Venus detecting an inconsistent file.
798  * CODA_PURGEFID  -- flush the attribute for the file
799  *                  purge it and its children from the dcache
800  *
801  * The last  allows Venus to replace local fids with global ones
802  * during reintegration.
803  *
804  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
805
806 int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out)
807 {
808         struct inode *inode = NULL;
809         struct CodaFid *fid = NULL, *newfid;
810         struct super_block *sb;
811
812         /* Handle invalidation requests. */
813         mutex_lock(&vcp->vc_mutex);
814         sb = vcp->vc_sb;
815         if (!sb || !sb->s_root)
816                 goto unlock_out;
817
818         switch (opcode) {
819         case CODA_FLUSH:
820                 coda_cache_clear_all(sb);
821                 shrink_dcache_sb(sb);
822                 if (d_really_is_positive(sb->s_root))
823                         coda_flag_inode(d_inode(sb->s_root), C_FLUSH);
824                 break;
825
826         case CODA_PURGEUSER:
827                 coda_cache_clear_all(sb);
828                 break;
829
830         case CODA_ZAPDIR:
831                 fid = &out->coda_zapdir.CodaFid;
832                 break;
833
834         case CODA_ZAPFILE:
835                 fid = &out->coda_zapfile.CodaFid;
836                 break;
837
838         case CODA_PURGEFID:
839                 fid = &out->coda_purgefid.CodaFid;
840                 break;
841
842         case CODA_REPLACE:
843                 fid = &out->coda_replace.OldFid;
844                 break;
845         }
846         if (fid)
847                 inode = coda_fid_to_inode(fid, sb);
848
849 unlock_out:
850         mutex_unlock(&vcp->vc_mutex);
851
852         if (!inode)
853                 return 0;
854
855         switch (opcode) {
856         case CODA_ZAPDIR:
857                 coda_flag_inode_children(inode, C_PURGE);
858                 coda_flag_inode(inode, C_VATTR);
859                 break;
860
861         case CODA_ZAPFILE:
862                 coda_flag_inode(inode, C_VATTR);
863                 break;
864
865         case CODA_PURGEFID:
866                 coda_flag_inode_children(inode, C_PURGE);
867
868                 /* catch the dentries later if some are still busy */
869                 coda_flag_inode(inode, C_PURGE);
870                 d_prune_aliases(inode);
871                 break;
872
873         case CODA_REPLACE:
874                 newfid = &out->coda_replace.NewFid;
875                 coda_replace_fid(inode, fid, newfid);
876                 break;
877         }
878         iput(inode);
879         return 0;
880 }
881