xref: /openbmc/linux/fs/coda/upcall.c (revision 901181b7)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Mostly platform independent upcall operations to Venus:
4  *  -- upcalls
5  *  -- upcall routines
6  *
7  * Linux 2.0 version
8  * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>,
9  * Michael Callahan <callahan@maths.ox.ac.uk>
10  *
11  * Redone for Linux 2.1
12  * Copyright (C) 1997 Carnegie Mellon University
13  *
14  * Carnegie Mellon University encourages users of this code to contribute
15  * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
16  */
17 
18 #include <linux/signal.h>
19 #include <linux/sched/signal.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 <linux/uaccess.h>
32 #include <linux/vmalloc.h>
33 #include <linux/vfs.h>
34 
35 #include <linux/coda.h>
36 #include "coda_psdev.h"
37 #include "coda_linux.h"
38 #include "coda_cache.h"
39 
40 #include "coda_int.h"
41 
42 static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
43 		       union inputArgs *buffer);
44 
45 static void *alloc_upcall(int opcode, int size)
46 {
47 	union inputArgs *inp;
48 
49 	inp = kvzalloc(size, GFP_KERNEL);
50         if (!inp)
51 		return ERR_PTR(-ENOMEM);
52 
53         inp->ih.opcode = opcode;
54 	inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns);
55 	inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns);
56 	inp->ih.uid = from_kuid(&init_user_ns, current_fsuid());
57 
58 	return (void*)inp;
59 }
60 
61 #define UPARG(op)\
62 do {\
63 	inp = (union inputArgs *)alloc_upcall(op, insize); \
64         if (IS_ERR(inp)) { return PTR_ERR(inp); }\
65         outp = (union outputArgs *)(inp); \
66         outsize = insize; \
67 } while (0)
68 
69 #define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
70 #define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
71 #define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
72 
73 
74 /* the upcalls */
75 int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
76 {
77         union inputArgs *inp;
78         union outputArgs *outp;
79         int insize, outsize, error;
80 
81         insize = SIZE(root);
82         UPARG(CODA_ROOT);
83 
84 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
85 	if (!error)
86 		*fidp = outp->coda_root.VFid;
87 
88 	kvfree(inp);
89 	return error;
90 }
91 
92 int venus_getattr(struct super_block *sb, struct CodaFid *fid,
93 		     struct coda_vattr *attr)
94 {
95         union inputArgs *inp;
96         union outputArgs *outp;
97         int insize, outsize, error;
98 
99         insize = SIZE(getattr);
100 	UPARG(CODA_GETATTR);
101         inp->coda_getattr.VFid = *fid;
102 
103 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
104 	if (!error)
105 		*attr = outp->coda_getattr.attr;
106 
107 	kvfree(inp);
108         return error;
109 }
110 
111 int venus_setattr(struct super_block *sb, struct CodaFid *fid,
112 		  struct coda_vattr *vattr)
113 {
114         union inputArgs *inp;
115         union outputArgs *outp;
116         int insize, outsize, error;
117 
118 	insize = SIZE(setattr);
119 	UPARG(CODA_SETATTR);
120 
121         inp->coda_setattr.VFid = *fid;
122 	inp->coda_setattr.attr = *vattr;
123 
124 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
125 
126 	kvfree(inp);
127         return error;
128 }
129 
130 int venus_lookup(struct super_block *sb, struct CodaFid *fid,
131 		    const char *name, int length, int * type,
132 		    struct CodaFid *resfid)
133 {
134         union inputArgs *inp;
135         union outputArgs *outp;
136         int insize, outsize, error;
137 	int offset;
138 
139 	offset = INSIZE(lookup);
140         insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
141 	UPARG(CODA_LOOKUP);
142 
143         inp->coda_lookup.VFid = *fid;
144 	inp->coda_lookup.name = offset;
145 	inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
146         /* send Venus a null terminated string */
147         memcpy((char *)(inp) + offset, name, length);
148         *((char *)inp + offset + length) = '\0';
149 
150 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
151 	if (!error) {
152 		*resfid = outp->coda_lookup.VFid;
153 		*type = outp->coda_lookup.vtype;
154 	}
155 
156 	kvfree(inp);
157 	return error;
158 }
159 
160 int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
161 		kuid_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 = from_kuid(&init_user_ns, 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 	kvfree(inp);
177         return error;
178 }
179 
180 int venus_open(struct super_block *sb, struct CodaFid *fid,
181 		  int flags, struct file **fh)
182 {
183         union inputArgs *inp;
184         union outputArgs *outp;
185         int insize, outsize, error;
186 
187 	insize = SIZE(open_by_fd);
188 	UPARG(CODA_OPEN_BY_FD);
189 
190 	inp->coda_open_by_fd.VFid = *fid;
191 	inp->coda_open_by_fd.flags = flags;
192 
193 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
194 	if (!error)
195 		*fh = outp->coda_open_by_fd.fh;
196 
197 	kvfree(inp);
198 	return error;
199 }
200 
201 int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,
202 		   const char *name, int length,
203 		   struct CodaFid *newfid, struct coda_vattr *attrs)
204 {
205         union inputArgs *inp;
206         union outputArgs *outp;
207         int insize, outsize, error;
208         int offset;
209 
210 	offset = INSIZE(mkdir);
211 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
212 	UPARG(CODA_MKDIR);
213 
214         inp->coda_mkdir.VFid = *dirfid;
215         inp->coda_mkdir.attr = *attrs;
216 	inp->coda_mkdir.name = offset;
217         /* Venus must get null terminated string */
218         memcpy((char *)(inp) + offset, name, length);
219         *((char *)inp + offset + length) = '\0';
220 
221 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
222 	if (!error) {
223 		*attrs = outp->coda_mkdir.attr;
224 		*newfid = outp->coda_mkdir.VFid;
225 	}
226 
227 	kvfree(inp);
228 	return error;
229 }
230 
231 
232 int venus_rename(struct super_block *sb, struct CodaFid *old_fid,
233 		 struct CodaFid *new_fid, size_t old_length,
234 		 size_t new_length, const char *old_name,
235 		 const char *new_name)
236 {
237 	union inputArgs *inp;
238         union outputArgs *outp;
239         int insize, outsize, error;
240 	int offset, s;
241 
242 	offset = INSIZE(rename);
243 	insize = max_t(unsigned int, offset + new_length + old_length + 8,
244 		     OUTSIZE(rename));
245  	UPARG(CODA_RENAME);
246 
247         inp->coda_rename.sourceFid = *old_fid;
248         inp->coda_rename.destFid =  *new_fid;
249         inp->coda_rename.srcname = offset;
250 
251         /* Venus must receive an null terminated string */
252         s = ( old_length & ~0x3) +4; /* round up to word boundary */
253         memcpy((char *)(inp) + offset, old_name, old_length);
254         *((char *)inp + offset + old_length) = '\0';
255 
256         /* another null terminated string for Venus */
257         offset += s;
258         inp->coda_rename.destname = offset;
259         s = ( new_length & ~0x3) +4; /* round up to word boundary */
260         memcpy((char *)(inp) + offset, new_name, new_length);
261         *((char *)inp + offset + new_length) = '\0';
262 
263 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
264 
265 	kvfree(inp);
266 	return error;
267 }
268 
269 int venus_create(struct super_block *sb, struct CodaFid *dirfid,
270 		 const char *name, int length, int excl, int mode,
271 		 struct CodaFid *newfid, struct coda_vattr *attrs)
272 {
273         union inputArgs *inp;
274         union outputArgs *outp;
275         int insize, outsize, error;
276         int offset;
277 
278         offset = INSIZE(create);
279 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
280 	UPARG(CODA_CREATE);
281 
282         inp->coda_create.VFid = *dirfid;
283         inp->coda_create.attr.va_mode = mode;
284 	inp->coda_create.excl = excl;
285         inp->coda_create.mode = mode;
286         inp->coda_create.name = offset;
287 
288         /* Venus must get null terminated string */
289         memcpy((char *)(inp) + offset, name, length);
290         *((char *)inp + offset + length) = '\0';
291 
292 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
293 	if (!error) {
294 		*attrs = outp->coda_create.attr;
295 		*newfid = outp->coda_create.VFid;
296 	}
297 
298 	kvfree(inp);
299 	return error;
300 }
301 
302 int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,
303 		    const char *name, int length)
304 {
305         union inputArgs *inp;
306         union outputArgs *outp;
307         int insize, outsize, error;
308         int offset;
309 
310         offset = INSIZE(rmdir);
311 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
312 	UPARG(CODA_RMDIR);
313 
314         inp->coda_rmdir.VFid = *dirfid;
315         inp->coda_rmdir.name = offset;
316         memcpy((char *)(inp) + offset, name, length);
317 	*((char *)inp + offset + length) = '\0';
318 
319 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
320 
321 	kvfree(inp);
322 	return error;
323 }
324 
325 int venus_remove(struct super_block *sb, struct CodaFid *dirfid,
326 		    const char *name, int length)
327 {
328         union inputArgs *inp;
329         union outputArgs *outp;
330         int error=0, insize, outsize, offset;
331 
332         offset = INSIZE(remove);
333 	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
334 	UPARG(CODA_REMOVE);
335 
336         inp->coda_remove.VFid = *dirfid;
337         inp->coda_remove.name = offset;
338         memcpy((char *)(inp) + offset, name, length);
339 	*((char *)inp + offset + length) = '\0';
340 
341 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
342 
343 	kvfree(inp);
344 	return error;
345 }
346 
347 int venus_readlink(struct super_block *sb, struct CodaFid *fid,
348 		      char *buffer, int *length)
349 {
350         union inputArgs *inp;
351         union outputArgs *outp;
352         int insize, outsize, error;
353         int retlen;
354         char *result;
355 
356 	insize = max_t(unsigned int,
357 		     INSIZE(readlink), OUTSIZE(readlink)+ *length);
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 - 1;
367 		*length = retlen;
368 		result =  (char *)outp + (long)outp->coda_readlink.data;
369 		memcpy(buffer, result, retlen);
370 		*(buffer + retlen) = '\0';
371 	}
372 
373 	kvfree(inp);
374         return error;
375 }
376 
377 
378 
379 int venus_link(struct super_block *sb, struct CodaFid *fid,
380 		  struct CodaFid *dirfid, const char *name, int len )
381 {
382         union inputArgs *inp;
383         union outputArgs *outp;
384         int insize, outsize, error;
385         int offset;
386 
387 	offset = INSIZE(link);
388 	insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));
389         UPARG(CODA_LINK);
390 
391         inp->coda_link.sourceFid = *fid;
392         inp->coda_link.destFid = *dirfid;
393         inp->coda_link.tname = offset;
394 
395         /* make sure strings are null terminated */
396         memcpy((char *)(inp) + offset, name, len);
397         *((char *)inp + offset + len) = '\0';
398 
399 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
400 
401 	kvfree(inp);
402         return error;
403 }
404 
405 int venus_symlink(struct super_block *sb, struct CodaFid *fid,
406 		     const char *name, int len,
407 		     const char *symname, int symlen)
408 {
409         union inputArgs *inp;
410         union outputArgs *outp;
411         int insize, outsize, error;
412         int offset, s;
413 
414         offset = INSIZE(symlink);
415 	insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
416 	UPARG(CODA_SYMLINK);
417 
418         /*        inp->coda_symlink.attr = *tva; XXXXXX */
419         inp->coda_symlink.VFid = *fid;
420 
421 	/* Round up to word boundary and null terminate */
422         inp->coda_symlink.srcname = offset;
423         s = ( symlen  & ~0x3 ) + 4;
424         memcpy((char *)(inp) + offset, symname, symlen);
425         *((char *)inp + offset + symlen) = '\0';
426 
427 	/* Round up to word boundary and null terminate */
428         offset += s;
429         inp->coda_symlink.tname = offset;
430         s = (len & ~0x3) + 4;
431         memcpy((char *)(inp) + offset, name, len);
432         *((char *)inp + offset + len) = '\0';
433 
434 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
435 
436 	kvfree(inp);
437         return error;
438 }
439 
440 int venus_fsync(struct super_block *sb, struct CodaFid *fid)
441 {
442         union inputArgs *inp;
443         union outputArgs *outp;
444 	int insize, outsize, error;
445 
446 	insize=SIZE(fsync);
447 	UPARG(CODA_FSYNC);
448 
449 	inp->coda_fsync.VFid = *fid;
450 	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
451 
452 	kvfree(inp);
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 	kvfree(inp);
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 		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 	kvfree(inp);
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 = SIZE(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 	kvfree(inp);
569         return error;
570 }
571 
572 int venus_access_intent(struct super_block *sb, struct CodaFid *fid,
573 			bool *access_intent_supported,
574 			size_t count, loff_t ppos, int type)
575 {
576 	union inputArgs *inp;
577 	union outputArgs *outp;
578 	int insize, outsize, error;
579 	bool finalizer =
580 		type == CODA_ACCESS_TYPE_READ_FINISH ||
581 		type == CODA_ACCESS_TYPE_WRITE_FINISH;
582 
583 	if (!*access_intent_supported && !finalizer)
584 		return 0;
585 
586 	insize = SIZE(access_intent);
587 	UPARG(CODA_ACCESS_INTENT);
588 
589 	inp->coda_access_intent.VFid = *fid;
590 	inp->coda_access_intent.count = count;
591 	inp->coda_access_intent.pos = ppos;
592 	inp->coda_access_intent.type = type;
593 
594 	error = coda_upcall(coda_vcp(sb), insize,
595 			    finalizer ? NULL : &outsize, inp);
596 
597 	/*
598 	 * we have to free the request buffer for synchronous upcalls
599 	 * or when asynchronous upcalls fail, but not when asynchronous
600 	 * upcalls succeed
601 	 */
602 	if (!finalizer || error)
603 		kvfree(inp);
604 
605 	/* Chunked access is not supported or an old Coda client */
606 	if (error == -EOPNOTSUPP) {
607 		*access_intent_supported = false;
608 		error = 0;
609 	}
610 	return error;
611 }
612 
613 /*
614  * coda_upcall and coda_downcall routines.
615  */
616 static void coda_block_signals(sigset_t *old)
617 {
618 	spin_lock_irq(&current->sighand->siglock);
619 	*old = current->blocked;
620 
621 	sigfillset(&current->blocked);
622 	sigdelset(&current->blocked, SIGKILL);
623 	sigdelset(&current->blocked, SIGSTOP);
624 	sigdelset(&current->blocked, SIGINT);
625 
626 	recalc_sigpending();
627 	spin_unlock_irq(&current->sighand->siglock);
628 }
629 
630 static void coda_unblock_signals(sigset_t *old)
631 {
632 	spin_lock_irq(&current->sighand->siglock);
633 	current->blocked = *old;
634 	recalc_sigpending();
635 	spin_unlock_irq(&current->sighand->siglock);
636 }
637 
638 /* Don't allow signals to interrupt the following upcalls before venus
639  * has seen them,
640  * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
641  * - CODA_STORE				(to avoid data loss)
642  * - CODA_ACCESS_INTENT                 (to avoid reference count problems)
643  */
644 #define CODA_INTERRUPTIBLE(r) (!coda_hard && \
645 			       (((r)->uc_opcode != CODA_CLOSE && \
646 				 (r)->uc_opcode != CODA_STORE && \
647 				 (r)->uc_opcode != CODA_ACCESS_INTENT && \
648 				 (r)->uc_opcode != CODA_RELEASE) || \
649 				(r)->uc_flags & CODA_REQ_READ))
650 
651 static inline void coda_waitfor_upcall(struct venus_comm *vcp,
652 				       struct upc_req *req)
653 {
654 	DECLARE_WAITQUEUE(wait, current);
655 	unsigned long timeout = jiffies + coda_timeout * HZ;
656 	sigset_t old;
657 	int blocked;
658 
659 	coda_block_signals(&old);
660 	blocked = 1;
661 
662 	add_wait_queue(&req->uc_sleep, &wait);
663 	for (;;) {
664 		if (CODA_INTERRUPTIBLE(req))
665 			set_current_state(TASK_INTERRUPTIBLE);
666 		else
667 			set_current_state(TASK_UNINTERRUPTIBLE);
668 
669 		/* got a reply */
670 		if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
671 			break;
672 
673 		if (blocked && time_after(jiffies, timeout) &&
674 		    CODA_INTERRUPTIBLE(req))
675 		{
676 			coda_unblock_signals(&old);
677 			blocked = 0;
678 		}
679 
680 		if (signal_pending(current)) {
681 			list_del(&req->uc_chain);
682 			break;
683 		}
684 
685 		mutex_unlock(&vcp->vc_mutex);
686 		if (blocked)
687 			schedule_timeout(HZ);
688 		else
689 			schedule();
690 		mutex_lock(&vcp->vc_mutex);
691 	}
692 	if (blocked)
693 		coda_unblock_signals(&old);
694 
695 	remove_wait_queue(&req->uc_sleep, &wait);
696 	set_current_state(TASK_RUNNING);
697 }
698 
699 
700 /*
701  * coda_upcall will return an error in the case of
702  * failed communication with Venus _or_ will peek at Venus
703  * reply and return Venus' error.
704  *
705  * As venus has 2 types of errors, normal errors (positive) and internal
706  * errors (negative), normal errors are negated, while internal errors
707  * are all mapped to -EINTR, while showing a nice warning message. (jh)
708  */
709 static int coda_upcall(struct venus_comm *vcp,
710 		       int inSize, int *outSize,
711 		       union inputArgs *buffer)
712 {
713 	union outputArgs *out;
714 	union inputArgs *sig_inputArgs;
715 	struct upc_req *req = NULL, *sig_req;
716 	int error;
717 
718 	mutex_lock(&vcp->vc_mutex);
719 
720 	if (!vcp->vc_inuse) {
721 		pr_notice("Venus dead, not sending upcall\n");
722 		error = -ENXIO;
723 		goto exit;
724 	}
725 
726 	/* Format the request message. */
727 	req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
728 	if (!req) {
729 		error = -ENOMEM;
730 		goto exit;
731 	}
732 
733 	buffer->ih.unique = ++vcp->vc_seq;
734 
735 	req->uc_data = (void *)buffer;
736 	req->uc_flags = outSize ? 0 : CODA_REQ_ASYNC;
737 	req->uc_inSize = inSize;
738 	req->uc_outSize = (outSize && *outSize) ? *outSize : inSize;
739 	req->uc_opcode = buffer->ih.opcode;
740 	req->uc_unique = buffer->ih.unique;
741 	init_waitqueue_head(&req->uc_sleep);
742 
743 	/* Append msg to pending queue and poke Venus. */
744 	list_add_tail(&req->uc_chain, &vcp->vc_pending);
745 	wake_up_interruptible(&vcp->vc_waitq);
746 
747 	/* We can return early on asynchronous requests */
748 	if (outSize == NULL) {
749 		mutex_unlock(&vcp->vc_mutex);
750 		return 0;
751 	}
752 
753 	/* We can be interrupted while we wait for Venus to process
754 	 * our request.  If the interrupt occurs before Venus has read
755 	 * the request, we dequeue and return. If it occurs after the
756 	 * read but before the reply, we dequeue, send a signal
757 	 * message, and return. If it occurs after the reply we ignore
758 	 * it. In no case do we want to restart the syscall.  If it
759 	 * was interrupted by a venus shutdown (psdev_close), return
760 	 * ENODEV.  */
761 
762 	/* Go to sleep.  Wake up on signals only after the timeout. */
763 	coda_waitfor_upcall(vcp, req);
764 
765 	/* Op went through, interrupt or not... */
766 	if (req->uc_flags & CODA_REQ_WRITE) {
767 		out = (union outputArgs *)req->uc_data;
768 		/* here we map positive Venus errors to kernel errors */
769 		error = -out->oh.result;
770 		*outSize = req->uc_outSize;
771 		goto exit;
772 	}
773 
774 	error = -EINTR;
775 	if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
776 		pr_warn("Unexpected interruption.\n");
777 		goto exit;
778 	}
779 
780 	/* Interrupted before venus read it. */
781 	if (!(req->uc_flags & CODA_REQ_READ))
782 		goto exit;
783 
784 	/* Venus saw the upcall, make sure we can send interrupt signal */
785 	if (!vcp->vc_inuse) {
786 		pr_info("Venus dead, not sending signal.\n");
787 		goto exit;
788 	}
789 
790 	error = -ENOMEM;
791 	sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
792 	if (!sig_req) goto exit;
793 
794 	sig_inputArgs = kvzalloc(sizeof(struct coda_in_hdr), GFP_KERNEL);
795 	if (!sig_inputArgs) {
796 		kfree(sig_req);
797 		goto exit;
798 	}
799 
800 	error = -EINTR;
801 	sig_inputArgs->ih.opcode = CODA_SIGNAL;
802 	sig_inputArgs->ih.unique = req->uc_unique;
803 
804 	sig_req->uc_flags = CODA_REQ_ASYNC;
805 	sig_req->uc_opcode = sig_inputArgs->ih.opcode;
806 	sig_req->uc_unique = sig_inputArgs->ih.unique;
807 	sig_req->uc_data = (void *)sig_inputArgs;
808 	sig_req->uc_inSize = sizeof(struct coda_in_hdr);
809 	sig_req->uc_outSize = sizeof(struct coda_in_hdr);
810 
811 	/* insert at head of queue! */
812 	list_add(&(sig_req->uc_chain), &vcp->vc_pending);
813 	wake_up_interruptible(&vcp->vc_waitq);
814 
815 exit:
816 	kfree(req);
817 	mutex_unlock(&vcp->vc_mutex);
818 	return error;
819 }
820 
821 /*
822     The statements below are part of the Coda opportunistic
823     programming -- taken from the Mach/BSD kernel code for Coda.
824     You don't get correct semantics by stating what needs to be
825     done without guaranteeing the invariants needed for it to happen.
826     When will be have time to find out what exactly is going on?  (pjb)
827 */
828 
829 
830 /*
831  * There are 7 cases where cache invalidations occur.  The semantics
832  *  of each is listed here:
833  *
834  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
835  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
836  *                  This call is a result of token expiration.
837  *
838  * The next arise as the result of callbacks on a file or directory.
839  * CODA_ZAPFILE   -- flush the cached attributes for a file.
840 
841  * CODA_ZAPDIR    -- flush the attributes for the dir and
842  *                  force a new lookup for all the children
843                     of this dir.
844 
845  *
846  * The next is a result of Venus detecting an inconsistent file.
847  * CODA_PURGEFID  -- flush the attribute for the file
848  *                  purge it and its children from the dcache
849  *
850  * The last  allows Venus to replace local fids with global ones
851  * during reintegration.
852  *
853  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
854 
855 int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out,
856 		  size_t nbytes)
857 {
858 	struct inode *inode = NULL;
859 	struct CodaFid *fid = NULL, *newfid;
860 	struct super_block *sb;
861 
862 	/*
863 	 * Make sure we have received enough data from the cache
864 	 * manager to populate the necessary fields in the buffer
865 	 */
866 	switch (opcode) {
867 	case CODA_PURGEUSER:
868 		if (nbytes < sizeof(struct coda_purgeuser_out))
869 			return -EINVAL;
870 		break;
871 
872 	case CODA_ZAPDIR:
873 		if (nbytes < sizeof(struct coda_zapdir_out))
874 			return -EINVAL;
875 		break;
876 
877 	case CODA_ZAPFILE:
878 		if (nbytes < sizeof(struct coda_zapfile_out))
879 			return -EINVAL;
880 		break;
881 
882 	case CODA_PURGEFID:
883 		if (nbytes < sizeof(struct coda_purgefid_out))
884 			return -EINVAL;
885 		break;
886 
887 	case CODA_REPLACE:
888 		if (nbytes < sizeof(struct coda_replace_out))
889 			return -EINVAL;
890 		break;
891 	}
892 
893 	/* Handle invalidation requests. */
894 	mutex_lock(&vcp->vc_mutex);
895 	sb = vcp->vc_sb;
896 	if (!sb || !sb->s_root)
897 		goto unlock_out;
898 
899 	switch (opcode) {
900 	case CODA_FLUSH:
901 		coda_cache_clear_all(sb);
902 		shrink_dcache_sb(sb);
903 		if (d_really_is_positive(sb->s_root))
904 			coda_flag_inode(d_inode(sb->s_root), C_FLUSH);
905 		break;
906 
907 	case CODA_PURGEUSER:
908 		coda_cache_clear_all(sb);
909 		break;
910 
911 	case CODA_ZAPDIR:
912 		fid = &out->coda_zapdir.CodaFid;
913 		break;
914 
915 	case CODA_ZAPFILE:
916 		fid = &out->coda_zapfile.CodaFid;
917 		break;
918 
919 	case CODA_PURGEFID:
920 		fid = &out->coda_purgefid.CodaFid;
921 		break;
922 
923 	case CODA_REPLACE:
924 		fid = &out->coda_replace.OldFid;
925 		break;
926 	}
927 	if (fid)
928 		inode = coda_fid_to_inode(fid, sb);
929 
930 unlock_out:
931 	mutex_unlock(&vcp->vc_mutex);
932 
933 	if (!inode)
934 		return 0;
935 
936 	switch (opcode) {
937 	case CODA_ZAPDIR:
938 		coda_flag_inode_children(inode, C_PURGE);
939 		coda_flag_inode(inode, C_VATTR);
940 		break;
941 
942 	case CODA_ZAPFILE:
943 		coda_flag_inode(inode, C_VATTR);
944 		break;
945 
946 	case CODA_PURGEFID:
947 		coda_flag_inode_children(inode, C_PURGE);
948 
949 		/* catch the dentries later if some are still busy */
950 		coda_flag_inode(inode, C_PURGE);
951 		d_prune_aliases(inode);
952 		break;
953 
954 	case CODA_REPLACE:
955 		newfid = &out->coda_replace.NewFid;
956 		coda_replace_fid(inode, fid, newfid);
957 		break;
958 	}
959 	iput(inode);
960 	return 0;
961 }
962