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