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