4c258cb5266dd169fac9b34f3b7c2e4f8af0eb62
[cascardo/linux.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 <linux/slab.h>
30 #include <linux/smp_lock.h>
31 #include <asm/uaccess.h>
32 #include <linux/vmalloc.h>
33 #include <linux/vfs.h>
34
35 #include <linux/coda.h>
36 #include <linux/coda_linux.h>
37 #include <linux/coda_psdev.h>
38 #include <linux/coda_fs_i.h>
39 #include <linux/coda_cache.h>
40
41 #include "coda_int.h"
42
43 static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
44                        union inputArgs *buffer);
45
46 static void *alloc_upcall(int opcode, int size)
47 {
48         union inputArgs *inp;
49
50         CODA_ALLOC(inp, union inputArgs *, size);
51         if (!inp)
52                 return ERR_PTR(-ENOMEM);
53
54         inp->ih.opcode = opcode;
55         inp->ih.pid = current->pid;
56         inp->ih.pgid = task_pgrp_nr(current);
57         inp->ih.uid = current_fsuid();
58
59         return (void*)inp;
60 }
61
62 #define UPARG(op)\
63 do {\
64         inp = (union inputArgs *)alloc_upcall(op, insize); \
65         if (IS_ERR(inp)) { return PTR_ERR(inp); }\
66         outp = (union outputArgs *)(inp); \
67         outsize = insize; \
68 } while (0)
69
70 #define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
71 #define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
72 #define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
73
74
75 /* the upcalls */
76 int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
77 {
78         union inputArgs *inp;
79         union outputArgs *outp;
80         int insize, outsize, error;
81
82         insize = SIZE(root);
83         UPARG(CODA_ROOT);
84
85         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
86         if (!error)
87                 *fidp = outp->coda_root.VFid;
88
89         CODA_FREE(inp, insize);
90         return error;
91 }
92
93 int venus_getattr(struct super_block *sb, struct CodaFid *fid, 
94                      struct coda_vattr *attr) 
95 {
96         union inputArgs *inp;
97         union outputArgs *outp;
98         int insize, outsize, error;
99
100         insize = SIZE(getattr); 
101         UPARG(CODA_GETATTR);
102         inp->coda_getattr.VFid = *fid;
103
104         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
105         if (!error)
106                 *attr = outp->coda_getattr.attr;
107
108         CODA_FREE(inp, insize);
109         return error;
110 }
111
112 int venus_setattr(struct super_block *sb, struct CodaFid *fid, 
113                   struct coda_vattr *vattr)
114 {
115         union inputArgs *inp;
116         union outputArgs *outp;
117         int insize, outsize, error;
118         
119         insize = SIZE(setattr);
120         UPARG(CODA_SETATTR);
121
122         inp->coda_setattr.VFid = *fid;
123         inp->coda_setattr.attr = *vattr;
124
125         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
126
127         CODA_FREE(inp, insize);
128         return error;
129 }
130
131 int venus_lookup(struct super_block *sb, struct CodaFid *fid, 
132                     const char *name, int length, int * type, 
133                     struct CodaFid *resfid)
134 {
135         union inputArgs *inp;
136         union outputArgs *outp;
137         int insize, outsize, error;
138         int offset;
139
140         offset = INSIZE(lookup);
141         insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
142         UPARG(CODA_LOOKUP);
143
144         inp->coda_lookup.VFid = *fid;
145         inp->coda_lookup.name = offset;
146         inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
147         /* send Venus a null terminated string */
148         memcpy((char *)(inp) + offset, name, length);
149         *((char *)inp + offset + length) = '\0';
150
151         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
152         if (!error) {
153                 *resfid = outp->coda_lookup.VFid;
154                 *type = outp->coda_lookup.vtype;
155         }
156
157         CODA_FREE(inp, insize);
158         return error;
159 }
160
161 int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
162                 vuid_t uid)
163 {
164         union inputArgs *inp;
165         union outputArgs *outp;
166         int insize, outsize, error;
167         
168         insize = SIZE(release);
169         UPARG(CODA_CLOSE);
170         
171         inp->ih.uid = uid;
172         inp->coda_close.VFid = *fid;
173         inp->coda_close.flags = flags;
174
175         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
176
177         CODA_FREE(inp, insize);
178         return error;
179 }
180
181 int venus_open(struct super_block *sb, struct CodaFid *fid,
182                   int flags, struct file **fh)
183 {
184         union inputArgs *inp;
185         union outputArgs *outp;
186         int insize, outsize, error;
187        
188         insize = SIZE(open_by_fd);
189         UPARG(CODA_OPEN_BY_FD);
190
191         inp->coda_open_by_fd.VFid = *fid;
192         inp->coda_open_by_fd.flags = flags;
193
194         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
195         if (!error)
196                 *fh = outp->coda_open_by_fd.fh;
197
198         CODA_FREE(inp, insize);
199         return error;
200 }       
201
202 int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, 
203                    const char *name, int length, 
204                    struct CodaFid *newfid, struct coda_vattr *attrs)
205 {
206         union inputArgs *inp;
207         union outputArgs *outp;
208         int insize, outsize, error;
209         int offset;
210
211         offset = INSIZE(mkdir);
212         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
213         UPARG(CODA_MKDIR);
214
215         inp->coda_mkdir.VFid = *dirfid;
216         inp->coda_mkdir.attr = *attrs;
217         inp->coda_mkdir.name = offset;
218         /* Venus must get null terminated string */
219         memcpy((char *)(inp) + offset, name, length);
220         *((char *)inp + offset + length) = '\0';
221
222         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
223         if (!error) {
224                 *attrs = outp->coda_mkdir.attr;
225                 *newfid = outp->coda_mkdir.VFid;
226         }
227
228         CODA_FREE(inp, insize);
229         return error;        
230 }
231
232
233 int venus_rename(struct super_block *sb, struct CodaFid *old_fid, 
234                  struct CodaFid *new_fid, size_t old_length, 
235                  size_t new_length, const char *old_name, 
236                  const char *new_name)
237 {
238         union inputArgs *inp;
239         union outputArgs *outp;
240         int insize, outsize, error; 
241         int offset, s;
242         
243         offset = INSIZE(rename);
244         insize = max_t(unsigned int, offset + new_length + old_length + 8,
245                      OUTSIZE(rename)); 
246         UPARG(CODA_RENAME);
247
248         inp->coda_rename.sourceFid = *old_fid;
249         inp->coda_rename.destFid =  *new_fid;
250         inp->coda_rename.srcname = offset;
251
252         /* Venus must receive an null terminated string */
253         s = ( old_length & ~0x3) +4; /* round up to word boundary */
254         memcpy((char *)(inp) + offset, old_name, old_length);
255         *((char *)inp + offset + old_length) = '\0';
256
257         /* another null terminated string for Venus */
258         offset += s;
259         inp->coda_rename.destname = offset;
260         s = ( new_length & ~0x3) +4; /* round up to word boundary */
261         memcpy((char *)(inp) + offset, new_name, new_length);
262         *((char *)inp + offset + new_length) = '\0';
263
264         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
265
266         CODA_FREE(inp, insize);
267         return error;
268 }
269
270 int venus_create(struct super_block *sb, struct CodaFid *dirfid, 
271                  const char *name, int length, int excl, int mode,
272                  struct CodaFid *newfid, struct coda_vattr *attrs) 
273 {
274         union inputArgs *inp;
275         union outputArgs *outp;
276         int insize, outsize, error;
277         int offset;
278
279         offset = INSIZE(create);
280         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
281         UPARG(CODA_CREATE);
282
283         inp->coda_create.VFid = *dirfid;
284         inp->coda_create.attr.va_mode = mode;
285         inp->coda_create.excl = excl;
286         inp->coda_create.mode = mode;
287         inp->coda_create.name = offset;
288
289         /* Venus must get null terminated string */
290         memcpy((char *)(inp) + offset, name, length);
291         *((char *)inp + offset + length) = '\0';
292
293         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
294         if (!error) {
295                 *attrs = outp->coda_create.attr;
296                 *newfid = outp->coda_create.VFid;
297         }
298
299         CODA_FREE(inp, insize);
300         return error;        
301 }
302
303 int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, 
304                     const char *name, int length)
305 {
306         union inputArgs *inp;
307         union outputArgs *outp;
308         int insize, outsize, error;
309         int offset;
310
311         offset = INSIZE(rmdir);
312         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
313         UPARG(CODA_RMDIR);
314
315         inp->coda_rmdir.VFid = *dirfid;
316         inp->coda_rmdir.name = offset;
317         memcpy((char *)(inp) + offset, name, length);
318         *((char *)inp + offset + length) = '\0';
319
320         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
321
322         CODA_FREE(inp, insize);
323         return error;
324 }
325
326 int venus_remove(struct super_block *sb, struct CodaFid *dirfid, 
327                     const char *name, int length)
328 {
329         union inputArgs *inp;
330         union outputArgs *outp;
331         int error=0, insize, outsize, offset;
332
333         offset = INSIZE(remove);
334         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
335         UPARG(CODA_REMOVE);
336
337         inp->coda_remove.VFid = *dirfid;
338         inp->coda_remove.name = offset;
339         memcpy((char *)(inp) + offset, name, length);
340         *((char *)inp + offset + length) = '\0';
341
342         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
343
344         CODA_FREE(inp, insize);
345         return error;
346 }
347
348 int venus_readlink(struct super_block *sb, struct CodaFid *fid, 
349                       char *buffer, int *length)
350
351         union inputArgs *inp;
352         union outputArgs *outp;
353         int insize, outsize, error;
354         int retlen;
355         char *result;
356         
357         insize = max_t(unsigned int,
358                      INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
359         UPARG(CODA_READLINK);
360
361         inp->coda_readlink.VFid = *fid;
362
363         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
364         if (!error) {
365                 retlen = outp->coda_readlink.count;
366                 if ( retlen > *length )
367                         retlen = *length;
368                 *length = retlen;
369                 result =  (char *)outp + (long)outp->coda_readlink.data;
370                 memcpy(buffer, result, retlen);
371                 *(buffer + retlen) = '\0';
372         }
373
374         CODA_FREE(inp, insize);
375         return error;
376 }
377
378
379
380 int venus_link(struct super_block *sb, struct CodaFid *fid, 
381                   struct CodaFid *dirfid, const char *name, int len )
382 {
383         union inputArgs *inp;
384         union outputArgs *outp;
385         int insize, outsize, error;
386         int offset;
387
388         offset = INSIZE(link);
389         insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));
390         UPARG(CODA_LINK);
391
392         inp->coda_link.sourceFid = *fid;
393         inp->coda_link.destFid = *dirfid;
394         inp->coda_link.tname = offset;
395
396         /* make sure strings are null terminated */
397         memcpy((char *)(inp) + offset, name, len);
398         *((char *)inp + offset + len) = '\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_symlink(struct super_block *sb, struct CodaFid *fid,
407                      const char *name, int len,
408                      const char *symname, int symlen)
409 {
410         union inputArgs *inp;
411         union outputArgs *outp;
412         int insize, outsize, error;
413         int offset, s;
414
415         offset = INSIZE(symlink);
416         insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
417         UPARG(CODA_SYMLINK);
418         
419         /*        inp->coda_symlink.attr = *tva; XXXXXX */ 
420         inp->coda_symlink.VFid = *fid;
421
422         /* Round up to word boundary and null terminate */
423         inp->coda_symlink.srcname = offset;
424         s = ( symlen  & ~0x3 ) + 4; 
425         memcpy((char *)(inp) + offset, symname, symlen);
426         *((char *)inp + offset + symlen) = '\0';
427         
428         /* Round up to word boundary and null terminate */
429         offset += s;
430         inp->coda_symlink.tname = offset;
431         s = (len & ~0x3) + 4;
432         memcpy((char *)(inp) + offset, name, len);
433         *((char *)inp + offset + len) = '\0';
434
435         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
436
437         CODA_FREE(inp, insize);
438         return error;
439 }
440
441 int venus_fsync(struct super_block *sb, struct CodaFid *fid)
442 {
443         union inputArgs *inp;
444         union outputArgs *outp; 
445         int insize, outsize, error;
446         
447         insize=SIZE(fsync);
448         UPARG(CODA_FSYNC);
449
450         inp->coda_fsync.VFid = *fid;
451         error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs),
452                             &outsize, inp);
453
454         CODA_FREE(inp, insize);
455         return error;
456 }
457
458 int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
459 {
460         union inputArgs *inp;
461         union outputArgs *outp; 
462         int insize, outsize, error;
463
464         insize = SIZE(access);
465         UPARG(CODA_ACCESS);
466
467         inp->coda_access.VFid = *fid;
468         inp->coda_access.flags = mask;
469
470         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
471
472         CODA_FREE(inp, insize);
473         return error;
474 }
475
476
477 int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
478                  unsigned int cmd, struct PioctlData *data)
479 {
480         union inputArgs *inp;
481         union outputArgs *outp;  
482         int insize, outsize, error;
483         int iocsize;
484
485         insize = VC_MAXMSGSIZE;
486         UPARG(CODA_IOCTL);
487
488         /* build packet for Venus */
489         if (data->vi.in_size > VC_MAXDATASIZE) {
490                 error = -EINVAL;
491                 goto exit;
492         }
493
494         if (data->vi.out_size > VC_MAXDATASIZE) {
495                 error = -EINVAL;
496                 goto exit;
497         }
498
499         inp->coda_ioctl.VFid = *fid;
500     
501         /* the cmd field was mutated by increasing its size field to
502          * reflect the path and follow args. We need to subtract that
503          * out before sending the command to Venus.  */
504         inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));   
505         iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
506         inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<     16;     
507     
508         /* in->coda_ioctl.rwflag = flag; */
509         inp->coda_ioctl.len = data->vi.in_size;
510         inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
511      
512         /* get the data out of user space */
513         if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data,
514                             data->vi.in, data->vi.in_size) ) {
515                 error = -EINVAL;
516                 goto exit;
517         }
518
519         error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
520                             &outsize, inp);
521
522         if (error) {
523                 printk("coda_pioctl: Venus returns: %d for %s\n", 
524                        error, coda_f2s(fid));
525                 goto exit; 
526         }
527
528         if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
529                 error = -EINVAL;
530                 goto exit;
531         }
532         
533         /* Copy out the OUT buffer. */
534         if (outp->coda_ioctl.len > data->vi.out_size) {
535                 error = -EINVAL;
536                 goto exit;
537         }
538
539         /* Copy out the OUT buffer. */
540         if (copy_to_user(data->vi.out,
541                          (char *)outp + (long)outp->coda_ioctl.data,
542                          outp->coda_ioctl.len)) {
543                 error = -EFAULT;
544                 goto exit;
545         }
546
547  exit:
548         CODA_FREE(inp, insize);
549         return error;
550 }
551
552 int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
553
554         union inputArgs *inp;
555         union outputArgs *outp;
556         int insize, outsize, error;
557         
558         insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
559         UPARG(CODA_STATFS);
560
561         error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
562         if (!error) {
563                 sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
564                 sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;
565                 sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
566                 sfs->f_files  = outp->coda_statfs.stat.f_files;
567                 sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;
568         }
569
570         CODA_FREE(inp, insize);
571         return error;
572 }
573
574 /*
575  * coda_upcall and coda_downcall routines.
576  */
577 static void coda_block_signals(sigset_t *old)
578 {
579         spin_lock_irq(&current->sighand->siglock);
580         *old = current->blocked;
581
582         sigfillset(&current->blocked);
583         sigdelset(&current->blocked, SIGKILL);
584         sigdelset(&current->blocked, SIGSTOP);
585         sigdelset(&current->blocked, SIGINT);
586
587         recalc_sigpending();
588         spin_unlock_irq(&current->sighand->siglock);
589 }
590
591 static void coda_unblock_signals(sigset_t *old)
592 {
593         spin_lock_irq(&current->sighand->siglock);
594         current->blocked = *old;
595         recalc_sigpending();
596         spin_unlock_irq(&current->sighand->siglock);
597 }
598
599 /* Don't allow signals to interrupt the following upcalls before venus
600  * has seen them,
601  * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
602  * - CODA_STORE                         (to avoid data loss)
603  */
604 #define CODA_INTERRUPTIBLE(r) (!coda_hard && \
605                                (((r)->uc_opcode != CODA_CLOSE && \
606                                  (r)->uc_opcode != CODA_STORE && \
607                                  (r)->uc_opcode != CODA_RELEASE) || \
608                                 (r)->uc_flags & CODA_REQ_READ))
609
610 static inline void coda_waitfor_upcall(struct upc_req *req)
611 {
612         DECLARE_WAITQUEUE(wait, current);
613         unsigned long timeout = jiffies + coda_timeout * HZ;
614         sigset_t old;
615         int blocked;
616
617         coda_block_signals(&old);
618         blocked = 1;
619
620         add_wait_queue(&req->uc_sleep, &wait);
621         for (;;) {
622                 if (CODA_INTERRUPTIBLE(req))
623                         set_current_state(TASK_INTERRUPTIBLE);
624                 else
625                         set_current_state(TASK_UNINTERRUPTIBLE);
626
627                 /* got a reply */
628                 if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
629                         break;
630
631                 if (blocked && time_after(jiffies, timeout) &&
632                     CODA_INTERRUPTIBLE(req))
633                 {
634                         coda_unblock_signals(&old);
635                         blocked = 0;
636                 }
637
638                 if (signal_pending(current)) {
639                         list_del(&req->uc_chain);
640                         break;
641                 }
642
643                 if (blocked)
644                         schedule_timeout(HZ);
645                 else
646                         schedule();
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         lock_kernel();
675
676         if (!vcp->vc_inuse) {
677                 printk(KERN_NOTICE "coda: 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(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                 printk(KERN_WARNING "coda: 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                 printk(KERN_INFO "coda: 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         unlock_kernel();
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, *newfid;
810         struct super_block *sb;
811
812         /* Handle invalidation requests. */
813         lock_kernel();
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 (sb->s_root->d_inode)
823                         coda_flag_inode(sb->s_root->d_inode, 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                 inode = coda_fid_to_inode(fid, sb);
833                 if (inode) {
834                         coda_flag_inode_children(inode, C_PURGE);
835                         coda_flag_inode(inode, C_VATTR);
836                 }
837                 break;
838
839         case CODA_ZAPFILE:
840                 fid = &out->coda_zapfile.CodaFid;
841                 inode = coda_fid_to_inode(fid, sb);
842                 if (inode)
843                         coda_flag_inode(inode, C_VATTR);
844                 break;
845
846         case CODA_PURGEFID:
847                 fid = &out->coda_purgefid.CodaFid;
848                 inode = coda_fid_to_inode(fid, sb);
849                 if (inode) {
850                         coda_flag_inode_children(inode, C_PURGE);
851
852                         /* catch the dentries later if some are still busy */
853                         coda_flag_inode(inode, C_PURGE);
854                         d_prune_aliases(inode);
855
856                 }
857                 break;
858
859         case CODA_REPLACE:
860                 fid = &out->coda_replace.OldFid;
861                 newfid = &out->coda_replace.NewFid;
862                 inode = coda_fid_to_inode(fid, sb);
863                 if (inode)
864                         coda_replace_fid(inode, fid, newfid);
865                 break;
866         }
867
868 unlock_out:
869         unlock_kernel();
870
871         if (inode)
872                 iput(inode);
873         return 0;
874 }
875