PageRenderTime 42ms CodeModel.GetById 9ms app.highlight 27ms RepoModel.GetById 0ms app.codeStats 0ms

/fs/coda/upcall.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 883 lines | 614 code | 168 blank | 101 comment | 50 complexity | 83578e7d67f4e0381a794784d4b27c8b MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  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
 42static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
 43		       union inputArgs *buffer);
 44
 45static 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)\
 62do {\
 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 */
 75int 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
 92int 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
111int 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
130int 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
160int 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
180int 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
201int 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
232int 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
269int 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
302int 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
325int 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
347int 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
379int 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
405int 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
440int 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
457int 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
476int 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
551int 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 */
576static 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
590static 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
609static 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 */
667static 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
768exit:
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
808int 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
851unlock_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