]> rtime.felk.cvut.cz Git - mcf548x/linux.git/blob - fs/coda/upcall.c
Initial 2.6.37
[mcf548x/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/mutex.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 venus_comm *vcp,
611                                        struct upc_req *req)
612 {
613         DECLARE_WAITQUEUE(wait, current);
614         unsigned long timeout = jiffies + coda_timeout * HZ;
615         sigset_t old;
616         int blocked;
617
618         coda_block_signals(&old);
619         blocked = 1;
620
621         add_wait_queue(&req->uc_sleep, &wait);
622         for (;;) {
623                 if (CODA_INTERRUPTIBLE(req))
624                         set_current_state(TASK_INTERRUPTIBLE);
625                 else
626                         set_current_state(TASK_UNINTERRUPTIBLE);
627
628                 /* got a reply */
629                 if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
630                         break;
631
632                 if (blocked && time_after(jiffies, timeout) &&
633                     CODA_INTERRUPTIBLE(req))
634                 {
635                         coda_unblock_signals(&old);
636                         blocked = 0;
637                 }
638
639                 if (signal_pending(current)) {
640                         list_del(&req->uc_chain);
641                         break;
642                 }
643
644                 mutex_unlock(&vcp->vc_mutex);
645                 if (blocked)
646                         schedule_timeout(HZ);
647                 else
648                         schedule();
649                 mutex_lock(&vcp->vc_mutex);
650         }
651         if (blocked)
652                 coda_unblock_signals(&old);
653
654         remove_wait_queue(&req->uc_sleep, &wait);
655         set_current_state(TASK_RUNNING);
656 }
657
658
659 /*
660  * coda_upcall will return an error in the case of
661  * failed communication with Venus _or_ will peek at Venus
662  * reply and return Venus' error.
663  *
664  * As venus has 2 types of errors, normal errors (positive) and internal
665  * errors (negative), normal errors are negated, while internal errors
666  * are all mapped to -EINTR, while showing a nice warning message. (jh)
667  */
668 static int coda_upcall(struct venus_comm *vcp,
669                        int inSize, int *outSize,
670                        union inputArgs *buffer)
671 {
672         union outputArgs *out;
673         union inputArgs *sig_inputArgs;
674         struct upc_req *req = NULL, *sig_req;
675         int error;
676
677         mutex_lock(&vcp->vc_mutex);
678
679         if (!vcp->vc_inuse) {
680                 printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n");
681                 error = -ENXIO;
682                 goto exit;
683         }
684
685         /* Format the request message. */
686         req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
687         if (!req) {
688                 error = -ENOMEM;
689                 goto exit;
690         }
691
692         req->uc_data = (void *)buffer;
693         req->uc_flags = 0;
694         req->uc_inSize = inSize;
695         req->uc_outSize = *outSize ? *outSize : inSize;
696         req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
697         req->uc_unique = ++vcp->vc_seq;
698         init_waitqueue_head(&req->uc_sleep);
699
700         /* Fill in the common input args. */
701         ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
702
703         /* Append msg to pending queue and poke Venus. */
704         list_add_tail(&req->uc_chain, &vcp->vc_pending);
705
706         wake_up_interruptible(&vcp->vc_waitq);
707         /* We can be interrupted while we wait for Venus to process
708          * our request.  If the interrupt occurs before Venus has read
709          * the request, we dequeue and return. If it occurs after the
710          * read but before the reply, we dequeue, send a signal
711          * message, and return. If it occurs after the reply we ignore
712          * it. In no case do we want to restart the syscall.  If it
713          * was interrupted by a venus shutdown (psdev_close), return
714          * ENODEV.  */
715
716         /* Go to sleep.  Wake up on signals only after the timeout. */
717         coda_waitfor_upcall(vcp, req);
718
719         /* Op went through, interrupt or not... */
720         if (req->uc_flags & CODA_REQ_WRITE) {
721                 out = (union outputArgs *)req->uc_data;
722                 /* here we map positive Venus errors to kernel errors */
723                 error = -out->oh.result;
724                 *outSize = req->uc_outSize;
725                 goto exit;
726         }
727
728         error = -EINTR;
729         if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
730                 printk(KERN_WARNING "coda: Unexpected interruption.\n");
731                 goto exit;
732         }
733
734         /* Interrupted before venus read it. */
735         if (!(req->uc_flags & CODA_REQ_READ))
736                 goto exit;
737
738         /* Venus saw the upcall, make sure we can send interrupt signal */
739         if (!vcp->vc_inuse) {
740                 printk(KERN_INFO "coda: Venus dead, not sending signal.\n");
741                 goto exit;
742         }
743
744         error = -ENOMEM;
745         sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
746         if (!sig_req) goto exit;
747
748         CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
749         if (!sig_req->uc_data) {
750                 kfree(sig_req);
751                 goto exit;
752         }
753
754         error = -EINTR;
755         sig_inputArgs = (union inputArgs *)sig_req->uc_data;
756         sig_inputArgs->ih.opcode = CODA_SIGNAL;
757         sig_inputArgs->ih.unique = req->uc_unique;
758
759         sig_req->uc_flags = CODA_REQ_ASYNC;
760         sig_req->uc_opcode = sig_inputArgs->ih.opcode;
761         sig_req->uc_unique = sig_inputArgs->ih.unique;
762         sig_req->uc_inSize = sizeof(struct coda_in_hdr);
763         sig_req->uc_outSize = sizeof(struct coda_in_hdr);
764
765         /* insert at head of queue! */
766         list_add(&(sig_req->uc_chain), &vcp->vc_pending);
767         wake_up_interruptible(&vcp->vc_waitq);
768
769 exit:
770         kfree(req);
771         mutex_unlock(&vcp->vc_mutex);
772         return error;
773 }
774
775 /*  
776     The statements below are part of the Coda opportunistic
777     programming -- taken from the Mach/BSD kernel code for Coda. 
778     You don't get correct semantics by stating what needs to be
779     done without guaranteeing the invariants needed for it to happen.
780     When will be have time to find out what exactly is going on?  (pjb)
781 */
782
783
784 /* 
785  * There are 7 cases where cache invalidations occur.  The semantics
786  *  of each is listed here:
787  *
788  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
789  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
790  *                  This call is a result of token expiration.
791  *
792  * The next arise as the result of callbacks on a file or directory.
793  * CODA_ZAPFILE   -- flush the cached attributes for a file.
794
795  * CODA_ZAPDIR    -- flush the attributes for the dir and
796  *                  force a new lookup for all the children
797                     of this dir.
798
799  *
800  * The next is a result of Venus detecting an inconsistent file.
801  * CODA_PURGEFID  -- flush the attribute for the file
802  *                  purge it and its children from the dcache
803  *
804  * The last  allows Venus to replace local fids with global ones
805  * during reintegration.
806  *
807  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
808
809 int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out)
810 {
811         struct inode *inode = NULL;
812         struct CodaFid *fid = NULL, *newfid;
813         struct super_block *sb;
814
815         /* Handle invalidation requests. */
816         mutex_lock(&vcp->vc_mutex);
817         sb = vcp->vc_sb;
818         if (!sb || !sb->s_root)
819                 goto unlock_out;
820
821         switch (opcode) {
822         case CODA_FLUSH:
823                 coda_cache_clear_all(sb);
824                 shrink_dcache_sb(sb);
825                 if (sb->s_root->d_inode)
826                         coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
827                 break;
828
829         case CODA_PURGEUSER:
830                 coda_cache_clear_all(sb);
831                 break;
832
833         case CODA_ZAPDIR:
834                 fid = &out->coda_zapdir.CodaFid;
835                 break;
836
837         case CODA_ZAPFILE:
838                 fid = &out->coda_zapfile.CodaFid;
839                 break;
840
841         case CODA_PURGEFID:
842                 fid = &out->coda_purgefid.CodaFid;
843                 break;
844
845         case CODA_REPLACE:
846                 fid = &out->coda_replace.OldFid;
847                 break;
848         }
849         if (fid)
850                 inode = coda_fid_to_inode(fid, sb);
851
852 unlock_out:
853         mutex_unlock(&vcp->vc_mutex);
854
855         if (!inode)
856                 return 0;
857
858         switch (opcode) {
859         case CODA_ZAPDIR:
860                 coda_flag_inode_children(inode, C_PURGE);
861                 coda_flag_inode(inode, C_VATTR);
862                 break;
863
864         case CODA_ZAPFILE:
865                 coda_flag_inode(inode, C_VATTR);
866                 break;
867
868         case CODA_PURGEFID:
869                 coda_flag_inode_children(inode, C_PURGE);
870
871                 /* catch the dentries later if some are still busy */
872                 coda_flag_inode(inode, C_PURGE);
873                 d_prune_aliases(inode);
874                 break;
875
876         case CODA_REPLACE:
877                 newfid = &out->coda_replace.NewFid;
878                 coda_replace_fid(inode, fid, newfid);
879                 break;
880         }
881         iput(inode);
882         return 0;
883 }
884