Pull ia64-clocksource into release branch
[GitHub/MotorolaMobilityLLC/kernel-slsi.git] / fs / coda / upcall.c
CommitLineData
1da177e4
LT
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>
e8edc6e0 19#include <linux/sched.h>
1da177e4
LT
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 <asm/uaccess.h>
30#include <linux/vmalloc.h>
31#include <linux/vfs.h>
32
33#include <linux/coda.h>
34#include <linux/coda_linux.h>
35#include <linux/coda_psdev.h>
36#include <linux/coda_fs_i.h>
37#include <linux/coda_cache.h>
3cf01f28
JH
38
39#include "coda_int.h"
1da177e4 40
a1b0aa87 41static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
1da177e4
LT
42 union inputArgs *buffer);
43
44static void *alloc_upcall(int opcode, int size)
45{
46 union inputArgs *inp;
47
48 CODA_ALLOC(inp, union inputArgs *, size);
49 if (!inp)
50 return ERR_PTR(-ENOMEM);
51
52 inp->ih.opcode = opcode;
53 inp->ih.pid = current->pid;
54 inp->ih.pgid = process_group(current);
55#ifdef CONFIG_CODA_FS_OLD_API
56 memset(&inp->ih.cred, 0, sizeof(struct coda_cred));
57 inp->ih.cred.cr_fsuid = current->fsuid;
58#else
59 inp->ih.uid = current->fsuid;
60#endif
61 return (void*)inp;
62}
63
64#define UPARG(op)\
65do {\
66 inp = (union inputArgs *)alloc_upcall(op, insize); \
67 if (IS_ERR(inp)) { return PTR_ERR(inp); }\
68 outp = (union outputArgs *)(inp); \
69 outsize = insize; \
70} while (0)
71
72#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
73#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
74#define SIZE(tag) max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
75
76
77/* the upcalls */
78int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
79{
80 union inputArgs *inp;
81 union outputArgs *outp;
82 int insize, outsize, error;
83
84 insize = SIZE(root);
85 UPARG(CODA_ROOT);
86
a1b0aa87 87 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
970648eb 88 if (!error)
1da177e4 89 *fidp = outp->coda_root.VFid;
1da177e4
LT
90
91 CODA_FREE(inp, insize);
92 return error;
93}
94
95int venus_getattr(struct super_block *sb, struct CodaFid *fid,
96 struct coda_vattr *attr)
97{
98 union inputArgs *inp;
99 union outputArgs *outp;
100 int insize, outsize, error;
101
102 insize = SIZE(getattr);
103 UPARG(CODA_GETATTR);
104 inp->coda_getattr.VFid = *fid;
105
a1b0aa87 106 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
970648eb
JH
107 if (!error)
108 *attr = outp->coda_getattr.attr;
1da177e4
LT
109
110 CODA_FREE(inp, insize);
111 return error;
112}
113
114int venus_setattr(struct super_block *sb, struct CodaFid *fid,
115 struct coda_vattr *vattr)
116{
117 union inputArgs *inp;
118 union outputArgs *outp;
119 int insize, outsize, error;
120
121 insize = SIZE(setattr);
122 UPARG(CODA_SETATTR);
123
124 inp->coda_setattr.VFid = *fid;
125 inp->coda_setattr.attr = *vattr;
126
a1b0aa87 127 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
128
129 CODA_FREE(inp, insize);
130 return error;
131}
132
133int venus_lookup(struct super_block *sb, struct CodaFid *fid,
134 const char *name, int length, int * type,
135 struct CodaFid *resfid)
136{
137 union inputArgs *inp;
138 union outputArgs *outp;
139 int insize, outsize, error;
140 int offset;
141
142 offset = INSIZE(lookup);
143 insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
144 UPARG(CODA_LOOKUP);
145
146 inp->coda_lookup.VFid = *fid;
147 inp->coda_lookup.name = offset;
148 inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
149 /* send Venus a null terminated string */
150 memcpy((char *)(inp) + offset, name, length);
151 *((char *)inp + offset + length) = '\0';
152
a1b0aa87 153 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
970648eb
JH
154 if (!error) {
155 *resfid = outp->coda_lookup.VFid;
156 *type = outp->coda_lookup.vtype;
157 }
1da177e4
LT
158
159 CODA_FREE(inp, insize);
160 return error;
161}
162
163int venus_store(struct super_block *sb, struct CodaFid *fid, int flags,
164 vuid_t uid)
165{
166 union inputArgs *inp;
167 union outputArgs *outp;
168 int insize, outsize, error;
169#ifdef CONFIG_CODA_FS_OLD_API
170 struct coda_cred cred = { 0, };
171 cred.cr_fsuid = uid;
172#endif
173
174 insize = SIZE(store);
175 UPARG(CODA_STORE);
176
177#ifdef CONFIG_CODA_FS_OLD_API
178 memcpy(&(inp->ih.cred), &cred, sizeof(cred));
179#else
180 inp->ih.uid = uid;
181#endif
182
183 inp->coda_store.VFid = *fid;
184 inp->coda_store.flags = flags;
185
a1b0aa87 186 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
187
188 CODA_FREE(inp, insize);
189 return error;
190}
191
192int venus_release(struct super_block *sb, struct CodaFid *fid, int flags)
193{
194 union inputArgs *inp;
195 union outputArgs *outp;
196 int insize, outsize, error;
197
198 insize = SIZE(release);
199 UPARG(CODA_RELEASE);
200
201 inp->coda_release.VFid = *fid;
202 inp->coda_release.flags = flags;
203
a1b0aa87 204 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
205
206 CODA_FREE(inp, insize);
207 return error;
208}
209
210int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
211 vuid_t uid)
212{
213 union inputArgs *inp;
214 union outputArgs *outp;
215 int insize, outsize, error;
216#ifdef CONFIG_CODA_FS_OLD_API
217 struct coda_cred cred = { 0, };
218 cred.cr_fsuid = uid;
219#endif
220
221 insize = SIZE(release);
222 UPARG(CODA_CLOSE);
223
224#ifdef CONFIG_CODA_FS_OLD_API
225 memcpy(&(inp->ih.cred), &cred, sizeof(cred));
226#else
227 inp->ih.uid = uid;
228#endif
229
230 inp->coda_close.VFid = *fid;
231 inp->coda_close.flags = flags;
232
a1b0aa87 233 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
234
235 CODA_FREE(inp, insize);
236 return error;
237}
238
239int venus_open(struct super_block *sb, struct CodaFid *fid,
240 int flags, struct file **fh)
241{
242 union inputArgs *inp;
243 union outputArgs *outp;
244 int insize, outsize, error;
245
246 insize = SIZE(open_by_fd);
247 UPARG(CODA_OPEN_BY_FD);
248
38c2e437
JH
249 inp->coda_open_by_fd.VFid = *fid;
250 inp->coda_open_by_fd.flags = flags;
1da177e4 251
a1b0aa87 252 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
38c2e437
JH
253 if (!error)
254 *fh = outp->coda_open_by_fd.fh;
1da177e4
LT
255
256 CODA_FREE(inp, insize);
257 return error;
258}
259
260int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,
261 const char *name, int length,
262 struct CodaFid *newfid, struct coda_vattr *attrs)
263{
264 union inputArgs *inp;
265 union outputArgs *outp;
266 int insize, outsize, error;
267 int offset;
268
269 offset = INSIZE(mkdir);
270 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
271 UPARG(CODA_MKDIR);
272
273 inp->coda_mkdir.VFid = *dirfid;
274 inp->coda_mkdir.attr = *attrs;
275 inp->coda_mkdir.name = offset;
276 /* Venus must get null terminated string */
277 memcpy((char *)(inp) + offset, name, length);
278 *((char *)inp + offset + length) = '\0';
1da177e4 279
a1b0aa87 280 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
970648eb
JH
281 if (!error) {
282 *attrs = outp->coda_mkdir.attr;
283 *newfid = outp->coda_mkdir.VFid;
284 }
1da177e4
LT
285
286 CODA_FREE(inp, insize);
287 return error;
288}
289
290
291int venus_rename(struct super_block *sb, struct CodaFid *old_fid,
292 struct CodaFid *new_fid, size_t old_length,
293 size_t new_length, const char *old_name,
294 const char *new_name)
295{
296 union inputArgs *inp;
297 union outputArgs *outp;
298 int insize, outsize, error;
299 int offset, s;
300
301 offset = INSIZE(rename);
302 insize = max_t(unsigned int, offset + new_length + old_length + 8,
303 OUTSIZE(rename));
304 UPARG(CODA_RENAME);
305
306 inp->coda_rename.sourceFid = *old_fid;
307 inp->coda_rename.destFid = *new_fid;
308 inp->coda_rename.srcname = offset;
309
310 /* Venus must receive an null terminated string */
311 s = ( old_length & ~0x3) +4; /* round up to word boundary */
312 memcpy((char *)(inp) + offset, old_name, old_length);
313 *((char *)inp + offset + old_length) = '\0';
314
315 /* another null terminated string for Venus */
316 offset += s;
317 inp->coda_rename.destname = offset;
318 s = ( new_length & ~0x3) +4; /* round up to word boundary */
319 memcpy((char *)(inp) + offset, new_name, new_length);
320 *((char *)inp + offset + new_length) = '\0';
321
a1b0aa87 322 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
323
324 CODA_FREE(inp, insize);
325 return error;
326}
327
328int venus_create(struct super_block *sb, struct CodaFid *dirfid,
329 const char *name, int length, int excl, int mode,
330 struct CodaFid *newfid, struct coda_vattr *attrs)
331{
332 union inputArgs *inp;
333 union outputArgs *outp;
334 int insize, outsize, error;
335 int offset;
336
337 offset = INSIZE(create);
338 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
339 UPARG(CODA_CREATE);
340
341 inp->coda_create.VFid = *dirfid;
342 inp->coda_create.attr.va_mode = mode;
343 inp->coda_create.excl = excl;
344 inp->coda_create.mode = mode;
345 inp->coda_create.name = offset;
346
347 /* Venus must get null terminated string */
348 memcpy((char *)(inp) + offset, name, length);
349 *((char *)inp + offset + length) = '\0';
1da177e4 350
a1b0aa87 351 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
970648eb
JH
352 if (!error) {
353 *attrs = outp->coda_create.attr;
354 *newfid = outp->coda_create.VFid;
355 }
1da177e4
LT
356
357 CODA_FREE(inp, insize);
358 return error;
359}
360
361int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,
362 const char *name, int length)
363{
364 union inputArgs *inp;
365 union outputArgs *outp;
366 int insize, outsize, error;
367 int offset;
368
369 offset = INSIZE(rmdir);
370 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
371 UPARG(CODA_RMDIR);
372
373 inp->coda_rmdir.VFid = *dirfid;
374 inp->coda_rmdir.name = offset;
375 memcpy((char *)(inp) + offset, name, length);
376 *((char *)inp + offset + length) = '\0';
a1b0aa87
JH
377
378 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
379
380 CODA_FREE(inp, insize);
381 return error;
382}
383
384int venus_remove(struct super_block *sb, struct CodaFid *dirfid,
385 const char *name, int length)
386{
387 union inputArgs *inp;
388 union outputArgs *outp;
389 int error=0, insize, outsize, offset;
390
391 offset = INSIZE(remove);
392 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
393 UPARG(CODA_REMOVE);
394
395 inp->coda_remove.VFid = *dirfid;
396 inp->coda_remove.name = offset;
397 memcpy((char *)(inp) + offset, name, length);
398 *((char *)inp + offset + length) = '\0';
a1b0aa87
JH
399
400 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
401
402 CODA_FREE(inp, insize);
403 return error;
404}
405
406int venus_readlink(struct super_block *sb, struct CodaFid *fid,
407 char *buffer, int *length)
408{
409 union inputArgs *inp;
410 union outputArgs *outp;
411 int insize, outsize, error;
412 int retlen;
413 char *result;
414
415 insize = max_t(unsigned int,
416 INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
417 UPARG(CODA_READLINK);
418
419 inp->coda_readlink.VFid = *fid;
970648eb 420
a1b0aa87 421 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
970648eb
JH
422 if (!error) {
423 retlen = outp->coda_readlink.count;
1da177e4 424 if ( retlen > *length )
970648eb 425 retlen = *length;
1da177e4
LT
426 *length = retlen;
427 result = (char *)outp + (long)outp->coda_readlink.data;
428 memcpy(buffer, result, retlen);
429 *(buffer + retlen) = '\0';
430 }
970648eb 431
1da177e4
LT
432 CODA_FREE(inp, insize);
433 return error;
434}
435
436
437
438int venus_link(struct super_block *sb, struct CodaFid *fid,
439 struct CodaFid *dirfid, const char *name, int len )
440{
441 union inputArgs *inp;
442 union outputArgs *outp;
443 int insize, outsize, error;
444 int offset;
445
446 offset = INSIZE(link);
447 insize = max_t(unsigned int, offset + len + 1, OUTSIZE(link));
448 UPARG(CODA_LINK);
449
450 inp->coda_link.sourceFid = *fid;
451 inp->coda_link.destFid = *dirfid;
452 inp->coda_link.tname = offset;
453
454 /* make sure strings are null terminated */
455 memcpy((char *)(inp) + offset, name, len);
456 *((char *)inp + offset + len) = '\0';
a1b0aa87
JH
457
458 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
459
460 CODA_FREE(inp, insize);
461 return error;
462}
463
464int venus_symlink(struct super_block *sb, struct CodaFid *fid,
465 const char *name, int len,
466 const char *symname, int symlen)
467{
468 union inputArgs *inp;
469 union outputArgs *outp;
470 int insize, outsize, error;
471 int offset, s;
472
473 offset = INSIZE(symlink);
474 insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
475 UPARG(CODA_SYMLINK);
476
477 /* inp->coda_symlink.attr = *tva; XXXXXX */
478 inp->coda_symlink.VFid = *fid;
479
480 /* Round up to word boundary and null terminate */
481 inp->coda_symlink.srcname = offset;
482 s = ( symlen & ~0x3 ) + 4;
483 memcpy((char *)(inp) + offset, symname, symlen);
484 *((char *)inp + offset + symlen) = '\0';
485
486 /* Round up to word boundary and null terminate */
487 offset += s;
488 inp->coda_symlink.tname = offset;
489 s = (len & ~0x3) + 4;
490 memcpy((char *)(inp) + offset, name, len);
491 *((char *)inp + offset + len) = '\0';
492
a1b0aa87 493 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
494
495 CODA_FREE(inp, insize);
496 return error;
497}
498
499int venus_fsync(struct super_block *sb, struct CodaFid *fid)
500{
501 union inputArgs *inp;
502 union outputArgs *outp;
503 int insize, outsize, error;
504
505 insize=SIZE(fsync);
506 UPARG(CODA_FSYNC);
507
a1b0aa87
JH
508 inp->coda_fsync.VFid = *fid;
509 error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs),
510 &outsize, inp);
1da177e4
LT
511
512 CODA_FREE(inp, insize);
513 return error;
514}
515
516int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
517{
518 union inputArgs *inp;
519 union outputArgs *outp;
520 int insize, outsize, error;
521
522 insize = SIZE(access);
523 UPARG(CODA_ACCESS);
524
525 inp->coda_access.VFid = *fid;
526 inp->coda_access.flags = mask;
527
a1b0aa87 528 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
1da177e4
LT
529
530 CODA_FREE(inp, insize);
531 return error;
532}
533
534
535int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
536 unsigned int cmd, struct PioctlData *data)
537{
538 union inputArgs *inp;
539 union outputArgs *outp;
540 int insize, outsize, error;
541 int iocsize;
542
543 insize = VC_MAXMSGSIZE;
544 UPARG(CODA_IOCTL);
545
546 /* build packet for Venus */
547 if (data->vi.in_size > VC_MAXDATASIZE) {
548 error = -EINVAL;
549 goto exit;
550 }
551
552 if (data->vi.out_size > VC_MAXDATASIZE) {
553 error = -EINVAL;
554 goto exit;
555 }
556
557 inp->coda_ioctl.VFid = *fid;
558
559 /* the cmd field was mutated by increasing its size field to
560 * reflect the path and follow args. We need to subtract that
561 * out before sending the command to Venus. */
562 inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
563 iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
564 inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
565
566 /* in->coda_ioctl.rwflag = flag; */
567 inp->coda_ioctl.len = data->vi.in_size;
568 inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
569
570 /* get the data out of user space */
571 if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data,
572 data->vi.in, data->vi.in_size) ) {
573 error = -EINVAL;
574 goto exit;
575 }
576
a1b0aa87
JH
577 error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
578 &outsize, inp);
579
1da177e4
LT
580 if (error) {
581 printk("coda_pioctl: Venus returns: %d for %s\n",
582 error, coda_f2s(fid));
583 goto exit;
584 }
585
586 if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
587 error = -EINVAL;
588 goto exit;
589 }
590
591 /* Copy out the OUT buffer. */
592 if (outp->coda_ioctl.len > data->vi.out_size) {
593 error = -EINVAL;
594 goto exit;
595 }
596
597 /* Copy out the OUT buffer. */
598 if (copy_to_user(data->vi.out,
599 (char *)outp + (long)outp->coda_ioctl.data,
600 outp->coda_ioctl.len)) {
601 error = -EFAULT;
602 goto exit;
603 }
604
605 exit:
606 CODA_FREE(inp, insize);
607 return error;
608}
609
726c3342 610int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
1da177e4
LT
611{
612 union inputArgs *inp;
613 union outputArgs *outp;
614 int insize, outsize, error;
615
616 insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
617 UPARG(CODA_STATFS);
618
a1b0aa87 619 error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
970648eb 620 if (!error) {
1da177e4
LT
621 sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
622 sfs->f_bfree = outp->coda_statfs.stat.f_bfree;
623 sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
624 sfs->f_files = outp->coda_statfs.stat.f_files;
625 sfs->f_ffree = outp->coda_statfs.stat.f_ffree;
1da177e4
LT
626 }
627
628 CODA_FREE(inp, insize);
629 return error;
630}
631
632/*
633 * coda_upcall and coda_downcall routines.
1da177e4 634 */
d9664c95
JH
635static void block_signals(sigset_t *old)
636{
637 spin_lock_irq(&current->sighand->siglock);
638 *old = current->blocked;
639
640 sigfillset(&current->blocked);
641 sigdelset(&current->blocked, SIGKILL);
642 sigdelset(&current->blocked, SIGSTOP);
643 sigdelset(&current->blocked, SIGINT);
644
645 recalc_sigpending();
646 spin_unlock_irq(&current->sighand->siglock);
647}
648
649static void unblock_signals(sigset_t *old)
650{
651 spin_lock_irq(&current->sighand->siglock);
652 current->blocked = *old;
653 recalc_sigpending();
654 spin_unlock_irq(&current->sighand->siglock);
655}
656
657/* Don't allow signals to interrupt the following upcalls before venus
658 * has seen them,
659 * - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems)
660 * - CODA_STORE (to avoid data loss)
661 */
662#define CODA_INTERRUPTIBLE(r) (!coda_hard && \
663 (((r)->uc_opcode != CODA_CLOSE && \
664 (r)->uc_opcode != CODA_STORE && \
665 (r)->uc_opcode != CODA_RELEASE) || \
666 (r)->uc_flags & REQ_READ))
1da177e4 667
d9664c95 668static inline void coda_waitfor_upcall(struct upc_req *req)
1da177e4
LT
669{
670 DECLARE_WAITQUEUE(wait, current);
d9664c95
JH
671 unsigned long timeout = jiffies + coda_timeout * HZ;
672 sigset_t old;
673 int blocked;
1da177e4 674
d9664c95
JH
675 block_signals(&old);
676 blocked = 1;
1da177e4 677
d9664c95 678 add_wait_queue(&req->uc_sleep, &wait);
1da177e4 679 for (;;) {
d9664c95 680 if (CODA_INTERRUPTIBLE(req))
1da177e4
LT
681 set_current_state(TASK_INTERRUPTIBLE);
682 else
683 set_current_state(TASK_UNINTERRUPTIBLE);
684
1da177e4 685 /* got a reply */
d9664c95 686 if (req->uc_flags & (REQ_WRITE | REQ_ABORT))
1da177e4
LT
687 break;
688
d9664c95
JH
689 if (blocked && time_after(jiffies, timeout) &&
690 CODA_INTERRUPTIBLE(req))
691 {
692 unblock_signals(&old);
693 blocked = 0;
1da177e4 694 }
d9664c95
JH
695
696 if (signal_pending(current)) {
697 list_del(&req->uc_chain);
698 break;
699 }
700
701 if (blocked)
702 schedule_timeout(HZ);
703 else
704 schedule();
1da177e4 705 }
d9664c95
JH
706 if (blocked)
707 unblock_signals(&old);
1da177e4 708
d9664c95
JH
709 remove_wait_queue(&req->uc_sleep, &wait);
710 set_current_state(TASK_RUNNING);
1da177e4
LT
711}
712
713
a1b0aa87
JH
714/*
715 * coda_upcall will return an error in the case of
1da177e4
LT
716 * failed communication with Venus _or_ will peek at Venus
717 * reply and return Venus' error.
718 *
719 * As venus has 2 types of errors, normal errors (positive) and internal
720 * errors (negative), normal errors are negated, while internal errors
721 * are all mapped to -EINTR, while showing a nice warning message. (jh)
1da177e4 722 */
a1b0aa87 723static int coda_upcall(struct venus_comm *vcp,
fe71b5f3
JH
724 int inSize, int *outSize,
725 union inputArgs *buffer)
1da177e4 726{
1da177e4 727 union outputArgs *out;
fe71b5f3
JH
728 union inputArgs *sig_inputArgs;
729 struct upc_req *req, *sig_req;
1da177e4
LT
730 int error = 0;
731
a1b0aa87 732 if (!vcp->vc_inuse) {
fe71b5f3
JH
733 printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n");
734 return -ENXIO;
1da177e4
LT
735 }
736
737 /* Format the request message. */
37461e19 738 req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
fe71b5f3 739 if (!req)
1da177e4 740 return -ENOMEM;
fe71b5f3 741
1da177e4
LT
742 req->uc_data = (void *)buffer;
743 req->uc_flags = 0;
744 req->uc_inSize = inSize;
745 req->uc_outSize = *outSize ? *outSize : inSize;
746 req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
a1b0aa87 747 req->uc_unique = ++vcp->vc_seq;
1da177e4 748 init_waitqueue_head(&req->uc_sleep);
fe71b5f3 749
1da177e4
LT
750 /* Fill in the common input args. */
751 ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
752
753 /* Append msg to pending queue and poke Venus. */
a1b0aa87 754 list_add_tail(&req->uc_chain, &vcp->vc_pending);
fe71b5f3 755
a1b0aa87 756 wake_up_interruptible(&vcp->vc_waitq);
1da177e4
LT
757 /* We can be interrupted while we wait for Venus to process
758 * our request. If the interrupt occurs before Venus has read
759 * the request, we dequeue and return. If it occurs after the
760 * read but before the reply, we dequeue, send a signal
761 * message, and return. If it occurs after the reply we ignore
762 * it. In no case do we want to restart the syscall. If it
763 * was interrupted by a venus shutdown (psdev_close), return
764 * ENODEV. */
765
766 /* Go to sleep. Wake up on signals only after the timeout. */
87065519 767 coda_waitfor_upcall(req);
1da177e4 768
fe71b5f3
JH
769 /* Op went through, interrupt or not... */
770 if (req->uc_flags & REQ_WRITE) {
1da177e4
LT
771 out = (union outputArgs *)req->uc_data;
772 /* here we map positive Venus errors to kernel errors */
773 error = -out->oh.result;
774 *outSize = req->uc_outSize;
775 goto exit;
fe71b5f3
JH
776 }
777
778 error = -EINTR;
779 if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) {
780 printk(KERN_WARNING "coda: Unexpected interruption.\n");
1da177e4 781 goto exit;
1da177e4
LT
782 }
783
fe71b5f3
JH
784 /* Interrupted before venus read it. */
785 if (!(req->uc_flags & REQ_READ))
786 goto exit;
787
788 /* Venus saw the upcall, make sure we can send interrupt signal */
a1b0aa87 789 if (!vcp->vc_inuse) {
fe71b5f3
JH
790 printk(KERN_INFO "coda: Venus dead, not sending signal.\n");
791 goto exit;
792 }
793
794 error = -ENOMEM;
37461e19 795 sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
fe71b5f3
JH
796 if (!sig_req) goto exit;
797
798 CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
799 if (!sig_req->uc_data) {
37461e19 800 kfree(sig_req);
fe71b5f3
JH
801 goto exit;
802 }
803
804 error = -EINTR;
805 sig_inputArgs = (union inputArgs *)sig_req->uc_data;
806 sig_inputArgs->ih.opcode = CODA_SIGNAL;
807 sig_inputArgs->ih.unique = req->uc_unique;
808
809 sig_req->uc_flags = REQ_ASYNC;
810 sig_req->uc_opcode = sig_inputArgs->ih.opcode;
811 sig_req->uc_unique = sig_inputArgs->ih.unique;
812 sig_req->uc_inSize = sizeof(struct coda_in_hdr);
813 sig_req->uc_outSize = sizeof(struct coda_in_hdr);
814
815 /* insert at head of queue! */
a1b0aa87
JH
816 list_add(&(sig_req->uc_chain), &vcp->vc_pending);
817 wake_up_interruptible(&vcp->vc_waitq);
fe71b5f3
JH
818
819exit:
37461e19 820 kfree(req);
1da177e4
LT
821 return error;
822}
823
824/*
825 The statements below are part of the Coda opportunistic
826 programming -- taken from the Mach/BSD kernel code for Coda.
827 You don't get correct semantics by stating what needs to be
828 done without guaranteeing the invariants needed for it to happen.
829 When will be have time to find out what exactly is going on? (pjb)
830*/
831
832
833/*
834 * There are 7 cases where cache invalidations occur. The semantics
835 * of each is listed here:
836 *
837 * CODA_FLUSH -- flush all entries from the name cache and the cnode cache.
838 * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
839 * This call is a result of token expiration.
840 *
841 * The next arise as the result of callbacks on a file or directory.
842 * CODA_ZAPFILE -- flush the cached attributes for a file.
843
844 * CODA_ZAPDIR -- flush the attributes for the dir and
845 * force a new lookup for all the children
846 of this dir.
847
848 *
849 * The next is a result of Venus detecting an inconsistent file.
850 * CODA_PURGEFID -- flush the attribute for the file
851 * purge it and its children from the dcache
852 *
853 * The last allows Venus to replace local fids with global ones
854 * during reintegration.
855 *
856 * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
857
858int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
859{
5fd31e9a
JH
860 struct inode *inode = NULL;
861 struct CodaFid *fid, *newfid;
862
1da177e4 863 /* Handle invalidation requests. */
5fd31e9a
JH
864 if ( !sb || !sb->s_root)
865 return 0;
866
867 switch (opcode) {
868 case CODA_FLUSH:
869 coda_cache_clear_all(sb);
870 shrink_dcache_sb(sb);
871 if (sb->s_root->d_inode)
872 coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
873 break;
874
875 case CODA_PURGEUSER:
876 coda_cache_clear_all(sb);
877 break;
878
879 case CODA_ZAPDIR:
880 fid = &out->coda_zapdir.CodaFid;
881 inode = coda_fid_to_inode(fid, sb);
882 if (inode) {
883 coda_flag_inode_children(inode, C_PURGE);
884 coda_flag_inode(inode, C_VATTR);
885 }
886 break;
887
888 case CODA_ZAPFILE:
889 fid = &out->coda_zapfile.CodaFid;
890 inode = coda_fid_to_inode(fid, sb);
891 if (inode)
892 coda_flag_inode(inode, C_VATTR);
893 break;
894
895 case CODA_PURGEFID:
896 fid = &out->coda_purgefid.CodaFid;
897 inode = coda_fid_to_inode(fid, sb);
898 if (inode) {
1da177e4
LT
899 coda_flag_inode_children(inode, C_PURGE);
900
901 /* catch the dentries later if some are still busy */
902 coda_flag_inode(inode, C_PURGE);
903 d_prune_aliases(inode);
904
5fd31e9a
JH
905 }
906 break;
907
908 case CODA_REPLACE:
909 fid = &out->coda_replace.OldFid;
910 newfid = &out->coda_replace.NewFid;
911 inode = coda_fid_to_inode(fid, sb);
912 if (inode)
913 coda_replace_fid(inode, fid, newfid);
914 break;
915 }
916
917 if (inode)
918 iput(inode);
919
920 return 0;
1da177e4
LT
921}
922