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