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