xref: /openbmc/linux/fs/coda/upcall.c (revision 643d1f7f)
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 
876