GNU Linux-libre 4.19.211-gnu1
[releases.git] / fs / coda / upcall.c
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 <linux/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         CODA_ALLOC(inp, union inputArgs *, size);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
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         CODA_FREE(inp, insize);
453         return error;
454 }
455
456 int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
457 {
458         union inputArgs *inp;
459         union outputArgs *outp; 
460         int insize, outsize, error;
461
462         insize = SIZE(access);
463         UPARG(CODA_ACCESS);
464
465         inp->coda_access.VFid = *fid;
466         inp->coda_access.flags = mask;
467
468         error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
469
470         CODA_FREE(inp, insize);
471         return error;
472 }
473
474
475 int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
476                  unsigned int cmd, struct PioctlData *data)
477 {
478         union inputArgs *inp;
479         union outputArgs *outp;  
480         int insize, outsize, error;
481         int iocsize;
482
483         insize = VC_MAXMSGSIZE;
484         UPARG(CODA_IOCTL);
485
486         /* build packet for Venus */
487         if (data->vi.in_size > VC_MAXDATASIZE) {
488                 error = -EINVAL;
489                 goto exit;
490         }
491
492         if (data->vi.out_size > VC_MAXDATASIZE) {
493                 error = -EINVAL;
494                 goto exit;
495         }
496
497         inp->coda_ioctl.VFid = *fid;
498     
499         /* the cmd field was mutated by increasing its size field to
500          * reflect the path and follow args. We need to subtract that
501          * out before sending the command to Venus.  */
502         inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));   
503         iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
504         inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<     16;     
505     
506         /* in->coda_ioctl.rwflag = flag; */
507         inp->coda_ioctl.len = data->vi.in_size;
508         inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
509      
510         /* get the data out of user space */
511         if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data,
512                            data->vi.in, data->vi.in_size)) {
513                 error = -EINVAL;
514                 goto exit;
515         }
516
517         error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
518                             &outsize, inp);
519
520         if (error) {
521                 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
550 int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
551
552         union inputArgs *inp;
553         union outputArgs *outp;
554         int insize, outsize, error;
555         
556         insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
557         UPARG(CODA_STATFS);
558
559         error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
560         if (!error) {
561                 sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
562                 sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;
563                 sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
564                 sfs->f_files  = outp->coda_statfs.stat.f_files;
565                 sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;
566         }
567
568         CODA_FREE(inp, insize);
569         return error;
570 }
571
572 /*
573  * coda_upcall and coda_downcall routines.
574  */
575 static void coda_block_signals(sigset_t *old)
576 {
577         spin_lock_irq(&current->sighand->siglock);
578         *old = current->blocked;
579
580         sigfillset(&current->blocked);
581         sigdelset(&current->blocked, SIGKILL);
582         sigdelset(&current->blocked, SIGSTOP);
583         sigdelset(&current->blocked, SIGINT);
584
585         recalc_sigpending();
586         spin_unlock_irq(&current->sighand->siglock);
587 }
588
589 static void coda_unblock_signals(sigset_t *old)
590 {
591         spin_lock_irq(&current->sighand->siglock);
592         current->blocked = *old;
593         recalc_sigpending();
594         spin_unlock_irq(&current->sighand->siglock);
595 }
596
597 /* Don't allow signals to interrupt the following upcalls before venus
598  * has seen them,
599  * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
600  * - CODA_STORE                         (to avoid data loss)
601  */
602 #define CODA_INTERRUPTIBLE(r) (!coda_hard && \
603                                (((r)->uc_opcode != CODA_CLOSE && \
604                                  (r)->uc_opcode != CODA_STORE && \
605                                  (r)->uc_opcode != CODA_RELEASE) || \
606                                 (r)->uc_flags & CODA_REQ_READ))
607
608 static 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  */
666 static 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
767 exit:
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
807 int 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
850 unlock_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