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