• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
42 		       union inputArgs *buffer);
43 
alloc_upcall(int opcode,int size)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 = 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)\
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 */
venus_rootfid(struct super_block * sb,struct CodaFid * fidp)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 
venus_getattr(struct super_block * sb,struct CodaFid * fid,struct coda_vattr * attr)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 
venus_setattr(struct super_block * sb,struct CodaFid * fid,struct coda_vattr * vattr)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 
venus_lookup(struct super_block * sb,struct CodaFid * fid,const char * name,int length,int * type,struct CodaFid * resfid)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 
venus_close(struct super_block * sb,struct CodaFid * fid,int flags,kuid_t uid)159 int 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 
venus_open(struct super_block * sb,struct CodaFid * fid,int flags,struct file ** fh)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 
venus_mkdir(struct super_block * sb,struct CodaFid * dirfid,const char * name,int length,struct CodaFid * newfid,struct coda_vattr * attrs)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 
venus_rename(struct super_block * sb,struct CodaFid * old_fid,struct CodaFid * new_fid,size_t old_length,size_t new_length,const char * old_name,const char * new_name)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 
venus_create(struct super_block * sb,struct CodaFid * dirfid,const char * name,int length,int excl,int mode,struct CodaFid * newfid,struct coda_vattr * attrs)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 
venus_rmdir(struct super_block * sb,struct CodaFid * dirfid,const char * name,int length)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 
venus_remove(struct super_block * sb,struct CodaFid * dirfid,const char * name,int length)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 
venus_readlink(struct super_block * sb,struct CodaFid * fid,char * buffer,int * length)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);
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 - 1;
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 
venus_link(struct super_block * sb,struct CodaFid * fid,struct CodaFid * dirfid,const char * name,int len)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 
venus_symlink(struct super_block * sb,struct CodaFid * fid,const char * name,int len,const char * symname,int symlen)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 
venus_fsync(struct super_block * sb,struct CodaFid * fid)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), insize, &outsize, inp);
450 
451 	CODA_FREE(inp, insize);
452 	return error;
453 }
454 
venus_access(struct super_block * sb,struct CodaFid * fid,int mask)455 int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
456 {
457         union inputArgs *inp;
458         union outputArgs *outp;
459 	int insize, outsize, error;
460 
461 	insize = SIZE(access);
462 	UPARG(CODA_ACCESS);
463 
464         inp->coda_access.VFid = *fid;
465         inp->coda_access.flags = mask;
466 
467 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
468 
469 	CODA_FREE(inp, insize);
470 	return error;
471 }
472 
473 
venus_pioctl(struct super_block * sb,struct CodaFid * fid,unsigned int cmd,struct PioctlData * data)474 int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
475 		 unsigned int cmd, struct PioctlData *data)
476 {
477         union inputArgs *inp;
478         union outputArgs *outp;
479 	int insize, outsize, error;
480 	int iocsize;
481 
482 	insize = VC_MAXMSGSIZE;
483 	UPARG(CODA_IOCTL);
484 
485         /* build packet for Venus */
486         if (data->vi.in_size > VC_MAXDATASIZE) {
487 		error = -EINVAL;
488 		goto exit;
489         }
490 
491         if (data->vi.out_size > VC_MAXDATASIZE) {
492 		error = -EINVAL;
493 		goto exit;
494 	}
495 
496         inp->coda_ioctl.VFid = *fid;
497 
498         /* the cmd field was mutated by increasing its size field to
499          * reflect the path and follow args. We need to subtract that
500          * out before sending the command to Venus.  */
501         inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
502         iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
503         inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<	16;
504 
505         /* in->coda_ioctl.rwflag = flag; */
506         inp->coda_ioctl.len = data->vi.in_size;
507         inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
508 
509         /* get the data out of user space */
510 	if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data,
511 			   data->vi.in, data->vi.in_size)) {
512 		error = -EINVAL;
513 	        goto exit;
514 	}
515 
516 	error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
517 			    &outsize, inp);
518 
519         if (error) {
520 		pr_warn("%s: Venus returns: %d for %s\n",
521 			__func__, error, coda_f2s(fid));
522 		goto exit;
523 	}
524 
525 	if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
526 		error = -EINVAL;
527 		goto exit;
528 	}
529 
530 	/* Copy out the OUT buffer. */
531         if (outp->coda_ioctl.len > data->vi.out_size) {
532 		error = -EINVAL;
533 		goto exit;
534         }
535 
536 	/* Copy out the OUT buffer. */
537 	if (copy_to_user(data->vi.out,
538 			 (char *)outp + (long)outp->coda_ioctl.data,
539 			 outp->coda_ioctl.len)) {
540 		error = -EFAULT;
541 		goto exit;
542 	}
543 
544  exit:
545 	CODA_FREE(inp, insize);
546 	return error;
547 }
548 
venus_statfs(struct dentry * dentry,struct kstatfs * sfs)549 int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
550 {
551         union inputArgs *inp;
552         union outputArgs *outp;
553         int insize, outsize, error;
554 
555 	insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
556 	UPARG(CODA_STATFS);
557 
558 	error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
559 	if (!error) {
560 		sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
561 		sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;
562 		sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
563 		sfs->f_files  = outp->coda_statfs.stat.f_files;
564 		sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;
565 	}
566 
567         CODA_FREE(inp, insize);
568         return error;
569 }
570 
571 /*
572  * coda_upcall and coda_downcall routines.
573  */
coda_block_signals(sigset_t * old)574 static void coda_block_signals(sigset_t *old)
575 {
576 	spin_lock_irq(&current->sighand->siglock);
577 	*old = current->blocked;
578 
579 	sigfillset(&current->blocked);
580 	sigdelset(&current->blocked, SIGKILL);
581 	sigdelset(&current->blocked, SIGSTOP);
582 	sigdelset(&current->blocked, SIGINT);
583 
584 	recalc_sigpending();
585 	spin_unlock_irq(&current->sighand->siglock);
586 }
587 
coda_unblock_signals(sigset_t * old)588 static void coda_unblock_signals(sigset_t *old)
589 {
590 	spin_lock_irq(&current->sighand->siglock);
591 	current->blocked = *old;
592 	recalc_sigpending();
593 	spin_unlock_irq(&current->sighand->siglock);
594 }
595 
596 /* Don't allow signals to interrupt the following upcalls before venus
597  * has seen them,
598  * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
599  * - CODA_STORE				(to avoid data loss)
600  */
601 #define CODA_INTERRUPTIBLE(r) (!coda_hard && \
602 			       (((r)->uc_opcode != CODA_CLOSE && \
603 				 (r)->uc_opcode != CODA_STORE && \
604 				 (r)->uc_opcode != CODA_RELEASE) || \
605 				(r)->uc_flags & CODA_REQ_READ))
606 
coda_waitfor_upcall(struct venus_comm * vcp,struct upc_req * req)607 static inline void coda_waitfor_upcall(struct venus_comm *vcp,
608 				       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 & (CODA_REQ_WRITE | CODA_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 		mutex_unlock(&vcp->vc_mutex);
642 		if (blocked)
643 			schedule_timeout(HZ);
644 		else
645 			schedule();
646 		mutex_lock(&vcp->vc_mutex);
647 	}
648 	if (blocked)
649 		coda_unblock_signals(&old);
650 
651 	remove_wait_queue(&req->uc_sleep, &wait);
652 	set_current_state(TASK_RUNNING);
653 }
654 
655 
656 /*
657  * coda_upcall will return an error in the case of
658  * failed communication with Venus _or_ will peek at Venus
659  * reply and return Venus' error.
660  *
661  * As venus has 2 types of errors, normal errors (positive) and internal
662  * errors (negative), normal errors are negated, while internal errors
663  * are all mapped to -EINTR, while showing a nice warning message. (jh)
664  */
coda_upcall(struct venus_comm * vcp,int inSize,int * outSize,union inputArgs * buffer)665 static int coda_upcall(struct venus_comm *vcp,
666 		       int inSize, int *outSize,
667 		       union inputArgs *buffer)
668 {
669 	union outputArgs *out;
670 	union inputArgs *sig_inputArgs;
671 	struct upc_req *req = NULL, *sig_req;
672 	int error;
673 
674 	mutex_lock(&vcp->vc_mutex);
675 
676 	if (!vcp->vc_inuse) {
677 		pr_notice("Venus dead, not sending upcall\n");
678 		error = -ENXIO;
679 		goto exit;
680 	}
681 
682 	/* Format the request message. */
683 	req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
684 	if (!req) {
685 		error = -ENOMEM;
686 		goto exit;
687 	}
688 
689 	req->uc_data = (void *)buffer;
690 	req->uc_flags = 0;
691 	req->uc_inSize = inSize;
692 	req->uc_outSize = *outSize ? *outSize : inSize;
693 	req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
694 	req->uc_unique = ++vcp->vc_seq;
695 	init_waitqueue_head(&req->uc_sleep);
696 
697 	/* Fill in the common input args. */
698 	((union inputArgs *)buffer)->ih.unique = req->uc_unique;
699 
700 	/* Append msg to pending queue and poke Venus. */
701 	list_add_tail(&req->uc_chain, &vcp->vc_pending);
702 
703 	wake_up_interruptible(&vcp->vc_waitq);
704 	/* We can be interrupted while we wait for Venus to process
705 	 * our request.  If the interrupt occurs before Venus has read
706 	 * the request, we dequeue and return. If it occurs after the
707 	 * read but before the reply, we dequeue, send a signal
708 	 * message, and return. If it occurs after the reply we ignore
709 	 * it. In no case do we want to restart the syscall.  If it
710 	 * was interrupted by a venus shutdown (psdev_close), return
711 	 * ENODEV.  */
712 
713 	/* Go to sleep.  Wake up on signals only after the timeout. */
714 	coda_waitfor_upcall(vcp, req);
715 
716 	/* Op went through, interrupt or not... */
717 	if (req->uc_flags & CODA_REQ_WRITE) {
718 		out = (union outputArgs *)req->uc_data;
719 		/* here we map positive Venus errors to kernel errors */
720 		error = -out->oh.result;
721 		*outSize = req->uc_outSize;
722 		goto exit;
723 	}
724 
725 	error = -EINTR;
726 	if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
727 		pr_warn("Unexpected interruption.\n");
728 		goto exit;
729 	}
730 
731 	/* Interrupted before venus read it. */
732 	if (!(req->uc_flags & CODA_REQ_READ))
733 		goto exit;
734 
735 	/* Venus saw the upcall, make sure we can send interrupt signal */
736 	if (!vcp->vc_inuse) {
737 		pr_info("Venus dead, not sending signal.\n");
738 		goto exit;
739 	}
740 
741 	error = -ENOMEM;
742 	sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
743 	if (!sig_req) goto exit;
744 
745 	CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
746 	if (!sig_req->uc_data) {
747 		kfree(sig_req);
748 		goto exit;
749 	}
750 
751 	error = -EINTR;
752 	sig_inputArgs = (union inputArgs *)sig_req->uc_data;
753 	sig_inputArgs->ih.opcode = CODA_SIGNAL;
754 	sig_inputArgs->ih.unique = req->uc_unique;
755 
756 	sig_req->uc_flags = CODA_REQ_ASYNC;
757 	sig_req->uc_opcode = sig_inputArgs->ih.opcode;
758 	sig_req->uc_unique = sig_inputArgs->ih.unique;
759 	sig_req->uc_inSize = sizeof(struct coda_in_hdr);
760 	sig_req->uc_outSize = sizeof(struct coda_in_hdr);
761 
762 	/* insert at head of queue! */
763 	list_add(&(sig_req->uc_chain), &vcp->vc_pending);
764 	wake_up_interruptible(&vcp->vc_waitq);
765 
766 exit:
767 	kfree(req);
768 	mutex_unlock(&vcp->vc_mutex);
769 	return error;
770 }
771 
772 /*
773     The statements below are part of the Coda opportunistic
774     programming -- taken from the Mach/BSD kernel code for Coda.
775     You don't get correct semantics by stating what needs to be
776     done without guaranteeing the invariants needed for it to happen.
777     When will be have time to find out what exactly is going on?  (pjb)
778 */
779 
780 
781 /*
782  * There are 7 cases where cache invalidations occur.  The semantics
783  *  of each is listed here:
784  *
785  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
786  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
787  *                  This call is a result of token expiration.
788  *
789  * The next arise as the result of callbacks on a file or directory.
790  * CODA_ZAPFILE   -- flush the cached attributes for a file.
791 
792  * CODA_ZAPDIR    -- flush the attributes for the dir and
793  *                  force a new lookup for all the children
794                     of this dir.
795 
796  *
797  * The next is a result of Venus detecting an inconsistent file.
798  * CODA_PURGEFID  -- flush the attribute for the file
799  *                  purge it and its children from the dcache
800  *
801  * The last  allows Venus to replace local fids with global ones
802  * during reintegration.
803  *
804  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
805 
coda_downcall(struct venus_comm * vcp,int opcode,union outputArgs * out)806 int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out)
807 {
808 	struct inode *inode = NULL;
809 	struct CodaFid *fid = NULL, *newfid;
810 	struct super_block *sb;
811 
812 	/* Handle invalidation requests. */
813 	mutex_lock(&vcp->vc_mutex);
814 	sb = vcp->vc_sb;
815 	if (!sb || !sb->s_root)
816 		goto unlock_out;
817 
818 	switch (opcode) {
819 	case CODA_FLUSH:
820 		coda_cache_clear_all(sb);
821 		shrink_dcache_sb(sb);
822 		if (d_really_is_positive(sb->s_root))
823 			coda_flag_inode(d_inode(sb->s_root), C_FLUSH);
824 		break;
825 
826 	case CODA_PURGEUSER:
827 		coda_cache_clear_all(sb);
828 		break;
829 
830 	case CODA_ZAPDIR:
831 		fid = &out->coda_zapdir.CodaFid;
832 		break;
833 
834 	case CODA_ZAPFILE:
835 		fid = &out->coda_zapfile.CodaFid;
836 		break;
837 
838 	case CODA_PURGEFID:
839 		fid = &out->coda_purgefid.CodaFid;
840 		break;
841 
842 	case CODA_REPLACE:
843 		fid = &out->coda_replace.OldFid;
844 		break;
845 	}
846 	if (fid)
847 		inode = coda_fid_to_inode(fid, sb);
848 
849 unlock_out:
850 	mutex_unlock(&vcp->vc_mutex);
851 
852 	if (!inode)
853 		return 0;
854 
855 	switch (opcode) {
856 	case CODA_ZAPDIR:
857 		coda_flag_inode_children(inode, C_PURGE);
858 		coda_flag_inode(inode, C_VATTR);
859 		break;
860 
861 	case CODA_ZAPFILE:
862 		coda_flag_inode(inode, C_VATTR);
863 		break;
864 
865 	case CODA_PURGEFID:
866 		coda_flag_inode_children(inode, C_PURGE);
867 
868 		/* catch the dentries later if some are still busy */
869 		coda_flag_inode(inode, C_PURGE);
870 		d_prune_aliases(inode);
871 		break;
872 
873 	case CODA_REPLACE:
874 		newfid = &out->coda_replace.NewFid;
875 		coda_replace_fid(inode, fid, newfid);
876 		break;
877 	}
878 	iput(inode);
879 	return 0;
880 }
881 
882