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