blob: 9a20a3b1998e722e81e4a386319ef2b73e9d7598 [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>
38#include <linux/coda_proc.h>
39
Jan Harkesa1b0aa82007-07-19 01:48:50 -070040static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 union inputArgs *buffer);
42
43static void *alloc_upcall(int opcode, int size)
44{
45 union inputArgs *inp;
46
47 CODA_ALLOC(inp, union inputArgs *, size);
48 if (!inp)
49 return ERR_PTR(-ENOMEM);
50
51 inp->ih.opcode = opcode;
52 inp->ih.pid = current->pid;
53 inp->ih.pgid = process_group(current);
54#ifdef CONFIG_CODA_FS_OLD_API
55 memset(&inp->ih.cred, 0, sizeof(struct coda_cred));
56 inp->ih.cred.cr_fsuid = current->fsuid;
57#else
58 inp->ih.uid = current->fsuid;
59#endif
60 return (void*)inp;
61}
62
63#define UPARG(op)\
64do {\
65 inp = (union inputArgs *)alloc_upcall(op, insize); \
66 if (IS_ERR(inp)) { return PTR_ERR(inp); }\
67 outp = (union outputArgs *)(inp); \
68 outsize = insize; \
69} while (0)
70
71#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
72#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
73#define SIZE(tag) max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
74
75
76/* the upcalls */
77int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
78{
79 union inputArgs *inp;
80 union outputArgs *outp;
81 int insize, outsize, error;
82
83 insize = SIZE(root);
84 UPARG(CODA_ROOT);
85
Jan Harkesa1b0aa82007-07-19 01:48:50 -070086 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -070087 if (!error)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 *fidp = outp->coda_root.VFid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089
90 CODA_FREE(inp, insize);
91 return error;
92}
93
94int venus_getattr(struct super_block *sb, struct CodaFid *fid,
95 struct coda_vattr *attr)
96{
97 union inputArgs *inp;
98 union outputArgs *outp;
99 int insize, outsize, error;
100
101 insize = SIZE(getattr);
102 UPARG(CODA_GETATTR);
103 inp->coda_getattr.VFid = *fid;
104
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700105 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700106 if (!error)
107 *attr = outp->coda_getattr.attr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
109 CODA_FREE(inp, insize);
110 return error;
111}
112
113int venus_setattr(struct super_block *sb, struct CodaFid *fid,
114 struct coda_vattr *vattr)
115{
116 union inputArgs *inp;
117 union outputArgs *outp;
118 int insize, outsize, error;
119
120 insize = SIZE(setattr);
121 UPARG(CODA_SETATTR);
122
123 inp->coda_setattr.VFid = *fid;
124 inp->coda_setattr.attr = *vattr;
125
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700126 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 CODA_FREE(inp, insize);
129 return error;
130}
131
132int venus_lookup(struct super_block *sb, struct CodaFid *fid,
133 const char *name, int length, int * type,
134 struct CodaFid *resfid)
135{
136 union inputArgs *inp;
137 union outputArgs *outp;
138 int insize, outsize, error;
139 int offset;
140
141 offset = INSIZE(lookup);
142 insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
143 UPARG(CODA_LOOKUP);
144
145 inp->coda_lookup.VFid = *fid;
146 inp->coda_lookup.name = offset;
147 inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
148 /* send Venus a null terminated string */
149 memcpy((char *)(inp) + offset, name, length);
150 *((char *)inp + offset + length) = '\0';
151
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700152 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700153 if (!error) {
154 *resfid = outp->coda_lookup.VFid;
155 *type = outp->coda_lookup.vtype;
156 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
158 CODA_FREE(inp, insize);
159 return error;
160}
161
162int venus_store(struct super_block *sb, struct CodaFid *fid, int flags,
163 vuid_t uid)
164{
165 union inputArgs *inp;
166 union outputArgs *outp;
167 int insize, outsize, error;
168#ifdef CONFIG_CODA_FS_OLD_API
169 struct coda_cred cred = { 0, };
170 cred.cr_fsuid = uid;
171#endif
172
173 insize = SIZE(store);
174 UPARG(CODA_STORE);
175
176#ifdef CONFIG_CODA_FS_OLD_API
177 memcpy(&(inp->ih.cred), &cred, sizeof(cred));
178#else
179 inp->ih.uid = uid;
180#endif
181
182 inp->coda_store.VFid = *fid;
183 inp->coda_store.flags = flags;
184
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700185 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
187 CODA_FREE(inp, insize);
188 return error;
189}
190
191int venus_release(struct super_block *sb, struct CodaFid *fid, int flags)
192{
193 union inputArgs *inp;
194 union outputArgs *outp;
195 int insize, outsize, error;
196
197 insize = SIZE(release);
198 UPARG(CODA_RELEASE);
199
200 inp->coda_release.VFid = *fid;
201 inp->coda_release.flags = flags;
202
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700203 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
205 CODA_FREE(inp, insize);
206 return error;
207}
208
209int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
210 vuid_t uid)
211{
212 union inputArgs *inp;
213 union outputArgs *outp;
214 int insize, outsize, error;
215#ifdef CONFIG_CODA_FS_OLD_API
216 struct coda_cred cred = { 0, };
217 cred.cr_fsuid = uid;
218#endif
219
220 insize = SIZE(release);
221 UPARG(CODA_CLOSE);
222
223#ifdef CONFIG_CODA_FS_OLD_API
224 memcpy(&(inp->ih.cred), &cred, sizeof(cred));
225#else
226 inp->ih.uid = uid;
227#endif
228
229 inp->coda_close.VFid = *fid;
230 inp->coda_close.flags = flags;
231
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700232 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
234 CODA_FREE(inp, insize);
235 return error;
236}
237
238int venus_open(struct super_block *sb, struct CodaFid *fid,
239 int flags, struct file **fh)
240{
241 union inputArgs *inp;
242 union outputArgs *outp;
243 int insize, outsize, error;
244
245 insize = SIZE(open_by_fd);
246 UPARG(CODA_OPEN_BY_FD);
247
Jan Harkes38c2e432007-07-19 01:48:41 -0700248 inp->coda_open_by_fd.VFid = *fid;
249 inp->coda_open_by_fd.flags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700251 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes38c2e432007-07-19 01:48:41 -0700252 if (!error)
253 *fh = outp->coda_open_by_fd.fh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
255 CODA_FREE(inp, insize);
256 return error;
257}
258
259int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid,
260 const char *name, int length,
261 struct CodaFid *newfid, struct coda_vattr *attrs)
262{
263 union inputArgs *inp;
264 union outputArgs *outp;
265 int insize, outsize, error;
266 int offset;
267
268 offset = INSIZE(mkdir);
269 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
270 UPARG(CODA_MKDIR);
271
272 inp->coda_mkdir.VFid = *dirfid;
273 inp->coda_mkdir.attr = *attrs;
274 inp->coda_mkdir.name = offset;
275 /* Venus must get null terminated string */
276 memcpy((char *)(inp) + offset, name, length);
277 *((char *)inp + offset + length) = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700279 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700280 if (!error) {
281 *attrs = outp->coda_mkdir.attr;
282 *newfid = outp->coda_mkdir.VFid;
283 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
285 CODA_FREE(inp, insize);
286 return error;
287}
288
289
290int venus_rename(struct super_block *sb, struct CodaFid *old_fid,
291 struct CodaFid *new_fid, size_t old_length,
292 size_t new_length, const char *old_name,
293 const char *new_name)
294{
295 union inputArgs *inp;
296 union outputArgs *outp;
297 int insize, outsize, error;
298 int offset, s;
299
300 offset = INSIZE(rename);
301 insize = max_t(unsigned int, offset + new_length + old_length + 8,
302 OUTSIZE(rename));
303 UPARG(CODA_RENAME);
304
305 inp->coda_rename.sourceFid = *old_fid;
306 inp->coda_rename.destFid = *new_fid;
307 inp->coda_rename.srcname = offset;
308
309 /* Venus must receive an null terminated string */
310 s = ( old_length & ~0x3) +4; /* round up to word boundary */
311 memcpy((char *)(inp) + offset, old_name, old_length);
312 *((char *)inp + offset + old_length) = '\0';
313
314 /* another null terminated string for Venus */
315 offset += s;
316 inp->coda_rename.destname = offset;
317 s = ( new_length & ~0x3) +4; /* round up to word boundary */
318 memcpy((char *)(inp) + offset, new_name, new_length);
319 *((char *)inp + offset + new_length) = '\0';
320
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700321 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323 CODA_FREE(inp, insize);
324 return error;
325}
326
327int venus_create(struct super_block *sb, struct CodaFid *dirfid,
328 const char *name, int length, int excl, int mode,
329 struct CodaFid *newfid, struct coda_vattr *attrs)
330{
331 union inputArgs *inp;
332 union outputArgs *outp;
333 int insize, outsize, error;
334 int offset;
335
336 offset = INSIZE(create);
337 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
338 UPARG(CODA_CREATE);
339
340 inp->coda_create.VFid = *dirfid;
341 inp->coda_create.attr.va_mode = mode;
342 inp->coda_create.excl = excl;
343 inp->coda_create.mode = mode;
344 inp->coda_create.name = offset;
345
346 /* Venus must get null terminated string */
347 memcpy((char *)(inp) + offset, name, length);
348 *((char *)inp + offset + length) = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700350 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700351 if (!error) {
352 *attrs = outp->coda_create.attr;
353 *newfid = outp->coda_create.VFid;
354 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
356 CODA_FREE(inp, insize);
357 return error;
358}
359
360int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid,
361 const char *name, int length)
362{
363 union inputArgs *inp;
364 union outputArgs *outp;
365 int insize, outsize, error;
366 int offset;
367
368 offset = INSIZE(rmdir);
369 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
370 UPARG(CODA_RMDIR);
371
372 inp->coda_rmdir.VFid = *dirfid;
373 inp->coda_rmdir.name = offset;
374 memcpy((char *)(inp) + offset, name, length);
375 *((char *)inp + offset + length) = '\0';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700376
377 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
379 CODA_FREE(inp, insize);
380 return error;
381}
382
383int venus_remove(struct super_block *sb, struct CodaFid *dirfid,
384 const char *name, int length)
385{
386 union inputArgs *inp;
387 union outputArgs *outp;
388 int error=0, insize, outsize, offset;
389
390 offset = INSIZE(remove);
391 insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
392 UPARG(CODA_REMOVE);
393
394 inp->coda_remove.VFid = *dirfid;
395 inp->coda_remove.name = offset;
396 memcpy((char *)(inp) + offset, name, length);
397 *((char *)inp + offset + length) = '\0';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700398
399 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
401 CODA_FREE(inp, insize);
402 return error;
403}
404
405int venus_readlink(struct super_block *sb, struct CodaFid *fid,
406 char *buffer, int *length)
407{
408 union inputArgs *inp;
409 union outputArgs *outp;
410 int insize, outsize, error;
411 int retlen;
412 char *result;
413
414 insize = max_t(unsigned int,
415 INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
416 UPARG(CODA_READLINK);
417
418 inp->coda_readlink.VFid = *fid;
Jan Harkes970648e2007-07-19 01:48:48 -0700419
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700420 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700421 if (!error) {
422 retlen = outp->coda_readlink.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 if ( retlen > *length )
Jan Harkes970648e2007-07-19 01:48:48 -0700424 retlen = *length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 *length = retlen;
426 result = (char *)outp + (long)outp->coda_readlink.data;
427 memcpy(buffer, result, retlen);
428 *(buffer + retlen) = '\0';
429 }
Jan Harkes970648e2007-07-19 01:48:48 -0700430
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 CODA_FREE(inp, insize);
432 return error;
433}
434
435
436
437int venus_link(struct super_block *sb, struct CodaFid *fid,
438 struct CodaFid *dirfid, const char *name, int len )
439{
440 union inputArgs *inp;
441 union outputArgs *outp;
442 int insize, outsize, error;
443 int offset;
444
445 offset = INSIZE(link);
446 insize = max_t(unsigned int, offset + len + 1, OUTSIZE(link));
447 UPARG(CODA_LINK);
448
449 inp->coda_link.sourceFid = *fid;
450 inp->coda_link.destFid = *dirfid;
451 inp->coda_link.tname = offset;
452
453 /* make sure strings are null terminated */
454 memcpy((char *)(inp) + offset, name, len);
455 *((char *)inp + offset + len) = '\0';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700456
457 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
459 CODA_FREE(inp, insize);
460 return error;
461}
462
463int venus_symlink(struct super_block *sb, struct CodaFid *fid,
464 const char *name, int len,
465 const char *symname, int symlen)
466{
467 union inputArgs *inp;
468 union outputArgs *outp;
469 int insize, outsize, error;
470 int offset, s;
471
472 offset = INSIZE(symlink);
473 insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
474 UPARG(CODA_SYMLINK);
475
476 /* inp->coda_symlink.attr = *tva; XXXXXX */
477 inp->coda_symlink.VFid = *fid;
478
479 /* Round up to word boundary and null terminate */
480 inp->coda_symlink.srcname = offset;
481 s = ( symlen & ~0x3 ) + 4;
482 memcpy((char *)(inp) + offset, symname, symlen);
483 *((char *)inp + offset + symlen) = '\0';
484
485 /* Round up to word boundary and null terminate */
486 offset += s;
487 inp->coda_symlink.tname = offset;
488 s = (len & ~0x3) + 4;
489 memcpy((char *)(inp) + offset, name, len);
490 *((char *)inp + offset + len) = '\0';
491
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700492 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
494 CODA_FREE(inp, insize);
495 return error;
496}
497
498int venus_fsync(struct super_block *sb, struct CodaFid *fid)
499{
500 union inputArgs *inp;
501 union outputArgs *outp;
502 int insize, outsize, error;
503
504 insize=SIZE(fsync);
505 UPARG(CODA_FSYNC);
506
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700507 inp->coda_fsync.VFid = *fid;
508 error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs),
509 &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
511 CODA_FREE(inp, insize);
512 return error;
513}
514
515int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
516{
517 union inputArgs *inp;
518 union outputArgs *outp;
519 int insize, outsize, error;
520
521 insize = SIZE(access);
522 UPARG(CODA_ACCESS);
523
524 inp->coda_access.VFid = *fid;
525 inp->coda_access.flags = mask;
526
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700527 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
529 CODA_FREE(inp, insize);
530 return error;
531}
532
533
534int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
535 unsigned int cmd, struct PioctlData *data)
536{
537 union inputArgs *inp;
538 union outputArgs *outp;
539 int insize, outsize, error;
540 int iocsize;
541
542 insize = VC_MAXMSGSIZE;
543 UPARG(CODA_IOCTL);
544
545 /* build packet for Venus */
546 if (data->vi.in_size > VC_MAXDATASIZE) {
547 error = -EINVAL;
548 goto exit;
549 }
550
551 if (data->vi.out_size > VC_MAXDATASIZE) {
552 error = -EINVAL;
553 goto exit;
554 }
555
556 inp->coda_ioctl.VFid = *fid;
557
558 /* the cmd field was mutated by increasing its size field to
559 * reflect the path and follow args. We need to subtract that
560 * out before sending the command to Venus. */
561 inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
562 iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
563 inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
564
565 /* in->coda_ioctl.rwflag = flag; */
566 inp->coda_ioctl.len = data->vi.in_size;
567 inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
568
569 /* get the data out of user space */
570 if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data,
571 data->vi.in, data->vi.in_size) ) {
572 error = -EINVAL;
573 goto exit;
574 }
575
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700576 error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
577 &outsize, inp);
578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 if (error) {
580 printk("coda_pioctl: Venus returns: %d for %s\n",
581 error, coda_f2s(fid));
582 goto exit;
583 }
584
585 if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
586 error = -EINVAL;
587 goto exit;
588 }
589
590 /* Copy out the OUT buffer. */
591 if (outp->coda_ioctl.len > data->vi.out_size) {
592 error = -EINVAL;
593 goto exit;
594 }
595
596 /* Copy out the OUT buffer. */
597 if (copy_to_user(data->vi.out,
598 (char *)outp + (long)outp->coda_ioctl.data,
599 outp->coda_ioctl.len)) {
600 error = -EFAULT;
601 goto exit;
602 }
603
604 exit:
605 CODA_FREE(inp, insize);
606 return error;
607}
608
David Howells726c3342006-06-23 02:02:58 -0700609int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610{
611 union inputArgs *inp;
612 union outputArgs *outp;
613 int insize, outsize, error;
614
615 insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
616 UPARG(CODA_STATFS);
617
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700618 error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700619 if (!error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
621 sfs->f_bfree = outp->coda_statfs.stat.f_bfree;
622 sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
623 sfs->f_files = outp->coda_statfs.stat.f_files;
624 sfs->f_ffree = outp->coda_statfs.stat.f_ffree;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 }
626
627 CODA_FREE(inp, insize);
628 return error;
629}
630
631/*
632 * coda_upcall and coda_downcall routines.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 */
Jan Harkesd9664c92007-07-19 01:48:46 -0700634static void block_signals(sigset_t *old)
635{
636 spin_lock_irq(&current->sighand->siglock);
637 *old = current->blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Jan Harkesd9664c92007-07-19 01:48:46 -0700639 sigfillset(&current->blocked);
640 sigdelset(&current->blocked, SIGKILL);
641 sigdelset(&current->blocked, SIGSTOP);
642 sigdelset(&current->blocked, SIGINT);
643
644 recalc_sigpending();
645 spin_unlock_irq(&current->sighand->siglock);
646}
647
648static void unblock_signals(sigset_t *old)
649{
650 spin_lock_irq(&current->sighand->siglock);
651 current->blocked = *old;
652 recalc_sigpending();
653 spin_unlock_irq(&current->sighand->siglock);
654}
655
656/* Don't allow signals to interrupt the following upcalls before venus
657 * has seen them,
658 * - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems)
659 * - CODA_STORE (to avoid data loss)
660 */
661#define CODA_INTERRUPTIBLE(r) (!coda_hard && \
662 (((r)->uc_opcode != CODA_CLOSE && \
663 (r)->uc_opcode != CODA_STORE && \
664 (r)->uc_opcode != CODA_RELEASE) || \
665 (r)->uc_flags & REQ_READ))
666
667static inline void coda_waitfor_upcall(struct upc_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668{
669 DECLARE_WAITQUEUE(wait, current);
Jan Harkesd9664c92007-07-19 01:48:46 -0700670 unsigned long timeout = jiffies + coda_timeout * HZ;
671 sigset_t old;
672 int blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Jan Harkesd9664c92007-07-19 01:48:46 -0700674 block_signals(&old);
675 blocked = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
Jan Harkesd9664c92007-07-19 01:48:46 -0700677 add_wait_queue(&req->uc_sleep, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 for (;;) {
Jan Harkesd9664c92007-07-19 01:48:46 -0700679 if (CODA_INTERRUPTIBLE(req))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 set_current_state(TASK_INTERRUPTIBLE);
681 else
682 set_current_state(TASK_UNINTERRUPTIBLE);
683
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 /* got a reply */
Jan Harkesd9664c92007-07-19 01:48:46 -0700685 if (req->uc_flags & (REQ_WRITE | REQ_ABORT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 break;
687
Jan Harkesd9664c92007-07-19 01:48:46 -0700688 if (blocked && time_after(jiffies, timeout) &&
689 CODA_INTERRUPTIBLE(req))
690 {
691 unblock_signals(&old);
692 blocked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Jan Harkesd9664c92007-07-19 01:48:46 -0700695 if (signal_pending(current)) {
696 list_del(&req->uc_chain);
697 break;
698 }
699
700 if (blocked)
701 schedule_timeout(HZ);
702 else
703 schedule();
704 }
705 if (blocked)
706 unblock_signals(&old);
707
708 remove_wait_queue(&req->uc_sleep, &wait);
709 set_current_state(TASK_RUNNING);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710}
711
712
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700713/*
714 * coda_upcall will return an error in the case of
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 * failed communication with Venus _or_ will peek at Venus
716 * reply and return Venus' error.
717 *
718 * As venus has 2 types of errors, normal errors (positive) and internal
719 * errors (negative), normal errors are negated, while internal errors
720 * are all mapped to -EINTR, while showing a nice warning message. (jh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700722static int coda_upcall(struct venus_comm *vcp,
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700723 int inSize, int *outSize,
724 union inputArgs *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 union outputArgs *out;
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700727 union inputArgs *sig_inputArgs;
728 struct upc_req *req, *sig_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 int error = 0;
730
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700731 if (!vcp->vc_inuse) {
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700732 printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n");
733 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 }
735
736 /* Format the request message. */
Jan Harkes37461e12007-07-19 01:48:48 -0700737 req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700738 if (!req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 return -ENOMEM;
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 req->uc_data = (void *)buffer;
742 req->uc_flags = 0;
743 req->uc_inSize = inSize;
744 req->uc_outSize = *outSize ? *outSize : inSize;
745 req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700746 req->uc_unique = ++vcp->vc_seq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 init_waitqueue_head(&req->uc_sleep);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700748
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 /* Fill in the common input args. */
750 ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
751
752 /* Append msg to pending queue and poke Venus. */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700753 list_add_tail(&req->uc_chain, &vcp->vc_pending);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700754
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700755 wake_up_interruptible(&vcp->vc_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 /* We can be interrupted while we wait for Venus to process
757 * our request. If the interrupt occurs before Venus has read
758 * the request, we dequeue and return. If it occurs after the
759 * read but before the reply, we dequeue, send a signal
760 * message, and return. If it occurs after the reply we ignore
761 * it. In no case do we want to restart the syscall. If it
762 * was interrupted by a venus shutdown (psdev_close), return
763 * ENODEV. */
764
765 /* Go to sleep. Wake up on signals only after the timeout. */
Jan Harkes87065512007-07-19 01:48:45 -0700766 coda_waitfor_upcall(req);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700768 /* Op went through, interrupt or not... */
769 if (req->uc_flags & REQ_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 out = (union outputArgs *)req->uc_data;
771 /* here we map positive Venus errors to kernel errors */
772 error = -out->oh.result;
773 *outSize = req->uc_outSize;
774 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 }
776
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700777 error = -EINTR;
778 if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) {
779 printk(KERN_WARNING "coda: Unexpected interruption.\n");
780 goto exit;
781 }
782
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700783 /* Interrupted before venus read it. */
784 if (!(req->uc_flags & REQ_READ))
785 goto exit;
786
787 /* Venus saw the upcall, make sure we can send interrupt signal */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700788 if (!vcp->vc_inuse) {
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700789 printk(KERN_INFO "coda: Venus dead, not sending signal.\n");
790 goto exit;
791 }
792
793 error = -ENOMEM;
Jan Harkes37461e12007-07-19 01:48:48 -0700794 sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700795 if (!sig_req) goto exit;
796
797 CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
798 if (!sig_req->uc_data) {
Jan Harkes37461e12007-07-19 01:48:48 -0700799 kfree(sig_req);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700800 goto exit;
801 }
802
803 error = -EINTR;
804 sig_inputArgs = (union inputArgs *)sig_req->uc_data;
805 sig_inputArgs->ih.opcode = CODA_SIGNAL;
806 sig_inputArgs->ih.unique = req->uc_unique;
807
808 sig_req->uc_flags = REQ_ASYNC;
809 sig_req->uc_opcode = sig_inputArgs->ih.opcode;
810 sig_req->uc_unique = sig_inputArgs->ih.unique;
811 sig_req->uc_inSize = sizeof(struct coda_in_hdr);
812 sig_req->uc_outSize = sizeof(struct coda_in_hdr);
813
814 /* insert at head of queue! */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700815 list_add(&(sig_req->uc_chain), &vcp->vc_pending);
816 wake_up_interruptible(&vcp->vc_waitq);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700817
818exit:
Jan Harkes37461e12007-07-19 01:48:48 -0700819 kfree(req);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 return error;
821}
822
823/*
824 The statements below are part of the Coda opportunistic
825 programming -- taken from the Mach/BSD kernel code for Coda.
826 You don't get correct semantics by stating what needs to be
827 done without guaranteeing the invariants needed for it to happen.
828 When will be have time to find out what exactly is going on? (pjb)
829*/
830
831
832/*
833 * There are 7 cases where cache invalidations occur. The semantics
834 * of each is listed here:
835 *
836 * CODA_FLUSH -- flush all entries from the name cache and the cnode cache.
837 * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
838 * This call is a result of token expiration.
839 *
840 * The next arise as the result of callbacks on a file or directory.
841 * CODA_ZAPFILE -- flush the cached attributes for a file.
842
843 * CODA_ZAPDIR -- flush the attributes for the dir and
844 * force a new lookup for all the children
845 of this dir.
846
847 *
848 * The next is a result of Venus detecting an inconsistent file.
849 * CODA_PURGEFID -- flush the attribute for the file
850 * purge it and its children from the dcache
851 *
852 * The last allows Venus to replace local fids with global ones
853 * during reintegration.
854 *
855 * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
856
857int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
858{
Jan Harkes5fd31e92007-07-19 01:48:49 -0700859 struct inode *inode = NULL;
860 struct CodaFid *fid, *newfid;
861
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 /* Handle invalidation requests. */
Jan Harkes5fd31e92007-07-19 01:48:49 -0700863 if ( !sb || !sb->s_root)
864 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
Jan Harkes5fd31e92007-07-19 01:48:49 -0700866 switch (opcode) {
867 case CODA_FLUSH:
868 coda_cache_clear_all(sb);
869 shrink_dcache_sb(sb);
870 if (sb->s_root->d_inode)
871 coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
872 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
Jan Harkes5fd31e92007-07-19 01:48:49 -0700874 case CODA_PURGEUSER:
875 coda_cache_clear_all(sb);
876 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
Jan Harkes5fd31e92007-07-19 01:48:49 -0700878 case CODA_ZAPDIR:
879 fid = &out->coda_zapdir.CodaFid;
880 inode = coda_fid_to_inode(fid, sb);
881 if (inode) {
882 coda_flag_inode_children(inode, C_PURGE);
883 coda_flag_inode(inode, C_VATTR);
884 }
885 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
Jan Harkes5fd31e92007-07-19 01:48:49 -0700887 case CODA_ZAPFILE:
888 fid = &out->coda_zapfile.CodaFid;
889 inode = coda_fid_to_inode(fid, sb);
890 if (inode)
891 coda_flag_inode(inode, C_VATTR);
892 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893
Jan Harkes5fd31e92007-07-19 01:48:49 -0700894 case CODA_PURGEFID:
895 fid = &out->coda_purgefid.CodaFid;
896 inode = coda_fid_to_inode(fid, sb);
897 if (inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 coda_flag_inode_children(inode, C_PURGE);
899
900 /* catch the dentries later if some are still busy */
901 coda_flag_inode(inode, C_PURGE);
902 d_prune_aliases(inode);
903
Jan Harkes5fd31e92007-07-19 01:48:49 -0700904 }
905 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
Jan Harkes5fd31e92007-07-19 01:48:49 -0700907 case CODA_REPLACE:
908 fid = &out->coda_replace.OldFid;
909 newfid = &out->coda_replace.NewFid;
910 inode = coda_fid_to_inode(fid, sb);
911 if (inode)
912 coda_replace_fid(inode, fid, newfid);
913 break;
914 }
915
916 if (inode)
917 iput(inode);
918
919 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920}
921