blob: cdb4c07a78700a93e34739a4aadddda96bfce8cc [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Alexey Dobriyane8edc6e2007-05-21 01:22:52 +040019#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#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>
Jan Harkes3cf01f22007-07-19 01:48:51 -070038
39#include "coda_int.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
Jan Harkesa1b0aa82007-07-19 01:48:50 -070041static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 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
Jan Harkesa1b0aa82007-07-19 01:48:50 -070087 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -070088 if (!error)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 *fidp = outp->coda_root.VFid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700106 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700107 if (!error)
108 *attr = outp->coda_getattr.attr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700127 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700153 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700154 if (!error) {
155 *resfid = outp->coda_lookup.VFid;
156 *type = outp->coda_lookup.vtype;
157 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159 CODA_FREE(inp, insize);
160 return error;
161}
162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
Jan Harkesd3fec422007-07-21 04:37:26 -0700164 vuid_t uid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
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(release);
175 UPARG(CODA_CLOSE);
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_close.VFid = *fid;
184 inp->coda_close.flags = flags;
185
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700186 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
188 CODA_FREE(inp, insize);
189 return error;
190}
191
192int venus_open(struct super_block *sb, struct CodaFid *fid,
193 int flags, struct file **fh)
194{
195 union inputArgs *inp;
196 union outputArgs *outp;
197 int insize, outsize, error;
198
199 insize = SIZE(open_by_fd);
200 UPARG(CODA_OPEN_BY_FD);
201
Jan Harkes38c2e432007-07-19 01:48:41 -0700202 inp->coda_open_by_fd.VFid = *fid;
203 inp->coda_open_by_fd.flags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700205 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes38c2e432007-07-19 01:48:41 -0700206 if (!error)
207 *fh = outp->coda_open_by_fd.fh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208
209 CODA_FREE(inp, insize);
210 return error;
211}
212
213int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,
214 const char *name, int length,
215 struct CodaFid *newfid, struct coda_vattr *attrs)
216{
217 union inputArgs *inp;
218 union outputArgs *outp;
219 int insize, outsize, error;
220 int offset;
221
222 offset = INSIZE(mkdir);
223 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
224 UPARG(CODA_MKDIR);
225
226 inp->coda_mkdir.VFid = *dirfid;
227 inp->coda_mkdir.attr = *attrs;
228 inp->coda_mkdir.name = offset;
229 /* Venus must get null terminated string */
230 memcpy((char *)(inp) + offset, name, length);
231 *((char *)inp + offset + length) = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700233 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700234 if (!error) {
235 *attrs = outp->coda_mkdir.attr;
236 *newfid = outp->coda_mkdir.VFid;
237 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
239 CODA_FREE(inp, insize);
240 return error;
241}
242
243
244int venus_rename(struct super_block *sb, struct CodaFid *old_fid,
245 struct CodaFid *new_fid, size_t old_length,
246 size_t new_length, const char *old_name,
247 const char *new_name)
248{
249 union inputArgs *inp;
250 union outputArgs *outp;
251 int insize, outsize, error;
252 int offset, s;
253
254 offset = INSIZE(rename);
255 insize = max_t(unsigned int, offset + new_length + old_length + 8,
256 OUTSIZE(rename));
257 UPARG(CODA_RENAME);
258
259 inp->coda_rename.sourceFid = *old_fid;
260 inp->coda_rename.destFid = *new_fid;
261 inp->coda_rename.srcname = offset;
262
263 /* Venus must receive an null terminated string */
264 s = ( old_length & ~0x3) +4; /* round up to word boundary */
265 memcpy((char *)(inp) + offset, old_name, old_length);
266 *((char *)inp + offset + old_length) = '\0';
267
268 /* another null terminated string for Venus */
269 offset += s;
270 inp->coda_rename.destname = offset;
271 s = ( new_length & ~0x3) +4; /* round up to word boundary */
272 memcpy((char *)(inp) + offset, new_name, new_length);
273 *((char *)inp + offset + new_length) = '\0';
274
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700275 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
277 CODA_FREE(inp, insize);
278 return error;
279}
280
281int venus_create(struct super_block *sb, struct CodaFid *dirfid,
282 const char *name, int length, int excl, int mode,
283 struct CodaFid *newfid, struct coda_vattr *attrs)
284{
285 union inputArgs *inp;
286 union outputArgs *outp;
287 int insize, outsize, error;
288 int offset;
289
290 offset = INSIZE(create);
291 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
292 UPARG(CODA_CREATE);
293
294 inp->coda_create.VFid = *dirfid;
295 inp->coda_create.attr.va_mode = mode;
296 inp->coda_create.excl = excl;
297 inp->coda_create.mode = mode;
298 inp->coda_create.name = offset;
299
300 /* Venus must get null terminated string */
301 memcpy((char *)(inp) + offset, name, length);
302 *((char *)inp + offset + length) = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700304 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700305 if (!error) {
306 *attrs = outp->coda_create.attr;
307 *newfid = outp->coda_create.VFid;
308 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 CODA_FREE(inp, insize);
311 return error;
312}
313
314int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,
315 const char *name, int length)
316{
317 union inputArgs *inp;
318 union outputArgs *outp;
319 int insize, outsize, error;
320 int offset;
321
322 offset = INSIZE(rmdir);
323 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
324 UPARG(CODA_RMDIR);
325
326 inp->coda_rmdir.VFid = *dirfid;
327 inp->coda_rmdir.name = offset;
328 memcpy((char *)(inp) + offset, name, length);
329 *((char *)inp + offset + length) = '\0';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700330
331 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333 CODA_FREE(inp, insize);
334 return error;
335}
336
337int venus_remove(struct super_block *sb, struct CodaFid *dirfid,
338 const char *name, int length)
339{
340 union inputArgs *inp;
341 union outputArgs *outp;
342 int error=0, insize, outsize, offset;
343
344 offset = INSIZE(remove);
345 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
346 UPARG(CODA_REMOVE);
347
348 inp->coda_remove.VFid = *dirfid;
349 inp->coda_remove.name = offset;
350 memcpy((char *)(inp) + offset, name, length);
351 *((char *)inp + offset + length) = '\0';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700352
353 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
355 CODA_FREE(inp, insize);
356 return error;
357}
358
359int venus_readlink(struct super_block *sb, struct CodaFid *fid,
360 char *buffer, int *length)
361{
362 union inputArgs *inp;
363 union outputArgs *outp;
364 int insize, outsize, error;
365 int retlen;
366 char *result;
367
368 insize = max_t(unsigned int,
369 INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
370 UPARG(CODA_READLINK);
371
372 inp->coda_readlink.VFid = *fid;
Jan Harkes970648e2007-07-19 01:48:48 -0700373
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700374 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700375 if (!error) {
376 retlen = outp->coda_readlink.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 if ( retlen > *length )
Jan Harkes970648e2007-07-19 01:48:48 -0700378 retlen = *length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 *length = retlen;
380 result = (char *)outp + (long)outp->coda_readlink.data;
381 memcpy(buffer, result, retlen);
382 *(buffer + retlen) = '\0';
383 }
Jan Harkes970648e2007-07-19 01:48:48 -0700384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 CODA_FREE(inp, insize);
386 return error;
387}
388
389
390
391int venus_link(struct super_block *sb, struct CodaFid *fid,
392 struct CodaFid *dirfid, const char *name, int len )
393{
394 union inputArgs *inp;
395 union outputArgs *outp;
396 int insize, outsize, error;
397 int offset;
398
399 offset = INSIZE(link);
400 insize = max_t(unsigned int, offset + len + 1, OUTSIZE(link));
401 UPARG(CODA_LINK);
402
403 inp->coda_link.sourceFid = *fid;
404 inp->coda_link.destFid = *dirfid;
405 inp->coda_link.tname = offset;
406
407 /* make sure strings are null terminated */
408 memcpy((char *)(inp) + offset, name, len);
409 *((char *)inp + offset + len) = '\0';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700410
411 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412
413 CODA_FREE(inp, insize);
414 return error;
415}
416
417int venus_symlink(struct super_block *sb, struct CodaFid *fid,
418 const char *name, int len,
419 const char *symname, int symlen)
420{
421 union inputArgs *inp;
422 union outputArgs *outp;
423 int insize, outsize, error;
424 int offset, s;
425
426 offset = INSIZE(symlink);
427 insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
428 UPARG(CODA_SYMLINK);
429
430 /* inp->coda_symlink.attr = *tva; XXXXXX */
431 inp->coda_symlink.VFid = *fid;
432
433 /* Round up to word boundary and null terminate */
434 inp->coda_symlink.srcname = offset;
435 s = ( symlen & ~0x3 ) + 4;
436 memcpy((char *)(inp) + offset, symname, symlen);
437 *((char *)inp + offset + symlen) = '\0';
438
439 /* Round up to word boundary and null terminate */
440 offset += s;
441 inp->coda_symlink.tname = offset;
442 s = (len & ~0x3) + 4;
443 memcpy((char *)(inp) + offset, name, len);
444 *((char *)inp + offset + len) = '\0';
445
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700446 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
448 CODA_FREE(inp, insize);
449 return error;
450}
451
452int venus_fsync(struct super_block *sb, struct CodaFid *fid)
453{
454 union inputArgs *inp;
455 union outputArgs *outp;
456 int insize, outsize, error;
457
458 insize=SIZE(fsync);
459 UPARG(CODA_FSYNC);
460
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700461 inp->coda_fsync.VFid = *fid;
462 error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs),
463 &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464
465 CODA_FREE(inp, insize);
466 return error;
467}
468
469int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
470{
471 union inputArgs *inp;
472 union outputArgs *outp;
473 int insize, outsize, error;
474
475 insize = SIZE(access);
476 UPARG(CODA_ACCESS);
477
478 inp->coda_access.VFid = *fid;
479 inp->coda_access.flags = mask;
480
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700481 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
483 CODA_FREE(inp, insize);
484 return error;
485}
486
487
488int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
489 unsigned int cmd, struct PioctlData *data)
490{
491 union inputArgs *inp;
492 union outputArgs *outp;
493 int insize, outsize, error;
494 int iocsize;
495
496 insize = VC_MAXMSGSIZE;
497 UPARG(CODA_IOCTL);
498
499 /* build packet for Venus */
500 if (data->vi.in_size > VC_MAXDATASIZE) {
501 error = -EINVAL;
502 goto exit;
503 }
504
505 if (data->vi.out_size > VC_MAXDATASIZE) {
506 error = -EINVAL;
507 goto exit;
508 }
509
510 inp->coda_ioctl.VFid = *fid;
511
512 /* the cmd field was mutated by increasing its size field to
513 * reflect the path and follow args. We need to subtract that
514 * out before sending the command to Venus. */
515 inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
516 iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
517 inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
518
519 /* in->coda_ioctl.rwflag = flag; */
520 inp->coda_ioctl.len = data->vi.in_size;
521 inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
522
523 /* get the data out of user space */
524 if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data,
525 data->vi.in, data->vi.in_size) ) {
526 error = -EINVAL;
527 goto exit;
528 }
529
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700530 error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
531 &outsize, inp);
532
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 if (error) {
534 printk("coda_pioctl: Venus returns: %d for %s\n",
535 error, coda_f2s(fid));
536 goto exit;
537 }
538
539 if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
540 error = -EINVAL;
541 goto exit;
542 }
543
544 /* Copy out the OUT buffer. */
545 if (outp->coda_ioctl.len > data->vi.out_size) {
546 error = -EINVAL;
547 goto exit;
548 }
549
550 /* Copy out the OUT buffer. */
551 if (copy_to_user(data->vi.out,
552 (char *)outp + (long)outp->coda_ioctl.data,
553 outp->coda_ioctl.len)) {
554 error = -EFAULT;
555 goto exit;
556 }
557
558 exit:
559 CODA_FREE(inp, insize);
560 return error;
561}
562
David Howells726c3342006-06-23 02:02:58 -0700563int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
565 union inputArgs *inp;
566 union outputArgs *outp;
567 int insize, outsize, error;
568
569 insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
570 UPARG(CODA_STATFS);
571
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700572 error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700573 if (!error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
575 sfs->f_bfree = outp->coda_statfs.stat.f_bfree;
576 sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
577 sfs->f_files = outp->coda_statfs.stat.f_files;
578 sfs->f_ffree = outp->coda_statfs.stat.f_ffree;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 }
580
581 CODA_FREE(inp, insize);
582 return error;
583}
584
585/*
586 * coda_upcall and coda_downcall routines.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 */
Al Viro5f47c7e2007-07-20 00:23:31 +0100588static void coda_block_signals(sigset_t *old)
Jan Harkesd9664c92007-07-19 01:48:46 -0700589{
590 spin_lock_irq(&current->sighand->siglock);
591 *old = current->blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
Jan Harkesd9664c92007-07-19 01:48:46 -0700593 sigfillset(&current->blocked);
594 sigdelset(&current->blocked, SIGKILL);
595 sigdelset(&current->blocked, SIGSTOP);
596 sigdelset(&current->blocked, SIGINT);
597
598 recalc_sigpending();
599 spin_unlock_irq(&current->sighand->siglock);
600}
601
Al Viro5f47c7e2007-07-20 00:23:31 +0100602static void coda_unblock_signals(sigset_t *old)
Jan Harkesd9664c92007-07-19 01:48:46 -0700603{
604 spin_lock_irq(&current->sighand->siglock);
605 current->blocked = *old;
606 recalc_sigpending();
607 spin_unlock_irq(&current->sighand->siglock);
608}
609
610/* Don't allow signals to interrupt the following upcalls before venus
611 * has seen them,
612 * - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems)
613 * - CODA_STORE (to avoid data loss)
614 */
615#define CODA_INTERRUPTIBLE(r) (!coda_hard && \
616 (((r)->uc_opcode != CODA_CLOSE && \
617 (r)->uc_opcode != CODA_STORE && \
618 (r)->uc_opcode != CODA_RELEASE) || \
619 (r)->uc_flags & REQ_READ))
620
621static inline void coda_waitfor_upcall(struct upc_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622{
623 DECLARE_WAITQUEUE(wait, current);
Jan Harkesd9664c92007-07-19 01:48:46 -0700624 unsigned long timeout = jiffies + coda_timeout * HZ;
625 sigset_t old;
626 int blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Al Viro5f47c7e2007-07-20 00:23:31 +0100628 coda_block_signals(&old);
Jan Harkesd9664c92007-07-19 01:48:46 -0700629 blocked = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
Jan Harkesd9664c92007-07-19 01:48:46 -0700631 add_wait_queue(&req->uc_sleep, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 for (;;) {
Jan Harkesd9664c92007-07-19 01:48:46 -0700633 if (CODA_INTERRUPTIBLE(req))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 set_current_state(TASK_INTERRUPTIBLE);
635 else
636 set_current_state(TASK_UNINTERRUPTIBLE);
637
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 /* got a reply */
Jan Harkesd9664c92007-07-19 01:48:46 -0700639 if (req->uc_flags & (REQ_WRITE | REQ_ABORT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 break;
641
Jan Harkesd9664c92007-07-19 01:48:46 -0700642 if (blocked && time_after(jiffies, timeout) &&
643 CODA_INTERRUPTIBLE(req))
644 {
Al Viro5f47c7e2007-07-20 00:23:31 +0100645 coda_unblock_signals(&old);
Jan Harkesd9664c92007-07-19 01:48:46 -0700646 blocked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
Jan Harkesd9664c92007-07-19 01:48:46 -0700649 if (signal_pending(current)) {
650 list_del(&req->uc_chain);
651 break;
652 }
653
654 if (blocked)
655 schedule_timeout(HZ);
656 else
657 schedule();
658 }
659 if (blocked)
Al Viro5f47c7e2007-07-20 00:23:31 +0100660 coda_unblock_signals(&old);
Jan Harkesd9664c92007-07-19 01:48:46 -0700661
662 remove_wait_queue(&req->uc_sleep, &wait);
663 set_current_state(TASK_RUNNING);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664}
665
666
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700667/*
668 * coda_upcall will return an error in the case of
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 * failed communication with Venus _or_ will peek at Venus
670 * reply and return Venus' error.
671 *
672 * As venus has 2 types of errors, normal errors (positive) and internal
673 * errors (negative), normal errors are negated, while internal errors
674 * are all mapped to -EINTR, while showing a nice warning message. (jh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700676static int coda_upcall(struct venus_comm *vcp,
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700677 int inSize, int *outSize,
678 union inputArgs *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 union outputArgs *out;
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700681 union inputArgs *sig_inputArgs;
682 struct upc_req *req, *sig_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 int error = 0;
684
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700685 if (!vcp->vc_inuse) {
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700686 printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n");
687 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 }
689
690 /* Format the request message. */
Jan Harkes37461e12007-07-19 01:48:48 -0700691 req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700692 if (!req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 return -ENOMEM;
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700694
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 req->uc_data = (void *)buffer;
696 req->uc_flags = 0;
697 req->uc_inSize = inSize;
698 req->uc_outSize = *outSize ? *outSize : inSize;
699 req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700700 req->uc_unique = ++vcp->vc_seq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 init_waitqueue_head(&req->uc_sleep);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700702
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 /* Fill in the common input args. */
704 ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
705
706 /* Append msg to pending queue and poke Venus. */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700707 list_add_tail(&req->uc_chain, &vcp->vc_pending);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700708
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700709 wake_up_interruptible(&vcp->vc_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 /* We can be interrupted while we wait for Venus to process
711 * our request. If the interrupt occurs before Venus has read
712 * the request, we dequeue and return. If it occurs after the
713 * read but before the reply, we dequeue, send a signal
714 * message, and return. If it occurs after the reply we ignore
715 * it. In no case do we want to restart the syscall. If it
716 * was interrupted by a venus shutdown (psdev_close), return
717 * ENODEV. */
718
719 /* Go to sleep. Wake up on signals only after the timeout. */
Jan Harkes87065512007-07-19 01:48:45 -0700720 coda_waitfor_upcall(req);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700722 /* Op went through, interrupt or not... */
723 if (req->uc_flags & REQ_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 out = (union outputArgs *)req->uc_data;
725 /* here we map positive Venus errors to kernel errors */
726 error = -out->oh.result;
727 *outSize = req->uc_outSize;
728 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 }
730
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700731 error = -EINTR;
732 if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) {
733 printk(KERN_WARNING "coda: Unexpected interruption.\n");
734 goto exit;
735 }
736
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700737 /* Interrupted before venus read it. */
738 if (!(req->uc_flags & REQ_READ))
739 goto exit;
740
741 /* Venus saw the upcall, make sure we can send interrupt signal */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700742 if (!vcp->vc_inuse) {
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700743 printk(KERN_INFO "coda: Venus dead, not sending signal.\n");
744 goto exit;
745 }
746
747 error = -ENOMEM;
Jan Harkes37461e12007-07-19 01:48:48 -0700748 sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700749 if (!sig_req) goto exit;
750
751 CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
752 if (!sig_req->uc_data) {
Jan Harkes37461e12007-07-19 01:48:48 -0700753 kfree(sig_req);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700754 goto exit;
755 }
756
757 error = -EINTR;
758 sig_inputArgs = (union inputArgs *)sig_req->uc_data;
759 sig_inputArgs->ih.opcode = CODA_SIGNAL;
760 sig_inputArgs->ih.unique = req->uc_unique;
761
762 sig_req->uc_flags = REQ_ASYNC;
763 sig_req->uc_opcode = sig_inputArgs->ih.opcode;
764 sig_req->uc_unique = sig_inputArgs->ih.unique;
765 sig_req->uc_inSize = sizeof(struct coda_in_hdr);
766 sig_req->uc_outSize = sizeof(struct coda_in_hdr);
767
768 /* insert at head of queue! */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700769 list_add(&(sig_req->uc_chain), &vcp->vc_pending);
770 wake_up_interruptible(&vcp->vc_waitq);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700771
772exit:
Jan Harkes37461e12007-07-19 01:48:48 -0700773 kfree(req);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 return error;
775}
776
777/*
778 The statements below are part of the Coda opportunistic
779 programming -- taken from the Mach/BSD kernel code for Coda.
780 You don't get correct semantics by stating what needs to be
781 done without guaranteeing the invariants needed for it to happen.
782 When will be have time to find out what exactly is going on? (pjb)
783*/
784
785
786/*
787 * There are 7 cases where cache invalidations occur. The semantics
788 * of each is listed here:
789 *
790 * CODA_FLUSH -- flush all entries from the name cache and the cnode cache.
791 * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
792 * This call is a result of token expiration.
793 *
794 * The next arise as the result of callbacks on a file or directory.
795 * CODA_ZAPFILE -- flush the cached attributes for a file.
796
797 * CODA_ZAPDIR -- flush the attributes for the dir and
798 * force a new lookup for all the children
799 of this dir.
800
801 *
802 * The next is a result of Venus detecting an inconsistent file.
803 * CODA_PURGEFID -- flush the attribute for the file
804 * purge it and its children from the dcache
805 *
806 * The last allows Venus to replace local fids with global ones
807 * during reintegration.
808 *
809 * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
810
811int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
812{
Jan Harkes5fd31e92007-07-19 01:48:49 -0700813 struct inode *inode = NULL;
814 struct CodaFid *fid, *newfid;
815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 /* Handle invalidation requests. */
Jan Harkes5fd31e92007-07-19 01:48:49 -0700817 if ( !sb || !sb->s_root)
818 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Jan Harkes5fd31e92007-07-19 01:48:49 -0700820 switch (opcode) {
821 case CODA_FLUSH:
822 coda_cache_clear_all(sb);
823 shrink_dcache_sb(sb);
824 if (sb->s_root->d_inode)
825 coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
826 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Jan Harkes5fd31e92007-07-19 01:48:49 -0700828 case CODA_PURGEUSER:
829 coda_cache_clear_all(sb);
830 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Jan Harkes5fd31e92007-07-19 01:48:49 -0700832 case CODA_ZAPDIR:
833 fid = &out->coda_zapdir.CodaFid;
834 inode = coda_fid_to_inode(fid, sb);
835 if (inode) {
836 coda_flag_inode_children(inode, C_PURGE);
837 coda_flag_inode(inode, C_VATTR);
838 }
839 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Jan Harkes5fd31e92007-07-19 01:48:49 -0700841 case CODA_ZAPFILE:
842 fid = &out->coda_zapfile.CodaFid;
843 inode = coda_fid_to_inode(fid, sb);
844 if (inode)
845 coda_flag_inode(inode, C_VATTR);
846 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Jan Harkes5fd31e92007-07-19 01:48:49 -0700848 case CODA_PURGEFID:
849 fid = &out->coda_purgefid.CodaFid;
850 inode = coda_fid_to_inode(fid, sb);
851 if (inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 coda_flag_inode_children(inode, C_PURGE);
853
854 /* catch the dentries later if some are still busy */
855 coda_flag_inode(inode, C_PURGE);
856 d_prune_aliases(inode);
857
Jan Harkes5fd31e92007-07-19 01:48:49 -0700858 }
859 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Jan Harkes5fd31e92007-07-19 01:48:49 -0700861 case CODA_REPLACE:
862 fid = &out->coda_replace.OldFid;
863 newfid = &out->coda_replace.NewFid;
864 inode = coda_fid_to_inode(fid, sb);
865 if (inode)
866 coda_replace_fid(inode, fid, newfid);
867 break;
868 }
869
870 if (inode)
871 iput(inode);
872
873 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874}
875