blob: e4e766e5557c6f85bfbf1735f367be6731056bf6 [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
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
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_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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700204 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700233 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
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
Jan Harkes38c2e432007-07-19 01:48:41 -0700249 inp->coda_open_by_fd.VFid = *fid;
250 inp->coda_open_by_fd.flags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700252 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes38c2e432007-07-19 01:48:41 -0700253 if (!error)
254 *fh = outp->coda_open_by_fd.fh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
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';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700280 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700281 if (!error) {
282 *attrs = outp->coda_mkdir.attr;
283 *newfid = outp->coda_mkdir.VFid;
284 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700322 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
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';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700351 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700352 if (!error) {
353 *attrs = outp->coda_create.attr;
354 *newfid = outp->coda_create.VFid;
355 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
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';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700377
378 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
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';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700399
400 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
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;
Jan Harkes970648e2007-07-19 01:48:48 -0700420
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700421 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700422 if (!error) {
423 retlen = outp->coda_readlink.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 if ( retlen > *length )
Jan Harkes970648e2007-07-19 01:48:48 -0700425 retlen = *length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 *length = retlen;
427 result = (char *)outp + (long)outp->coda_readlink.data;
428 memcpy(buffer, result, retlen);
429 *(buffer + retlen) = '\0';
430 }
Jan Harkes970648e2007-07-19 01:48:48 -0700431
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 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';
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700457
458 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700493 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700508 inp->coda_fsync.VFid = *fid;
509 error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs),
510 &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700528 error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700577 error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
578 &outsize, inp);
579
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 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
David Howells726c3342006-06-23 02:02:58 -0700610int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611{
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
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700619 error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
Jan Harkes970648e2007-07-19 01:48:48 -0700620 if (!error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 }
627
628 CODA_FREE(inp, insize);
629 return error;
630}
631
632/*
633 * coda_upcall and coda_downcall routines.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 */
Jan Harkesd9664c92007-07-19 01:48:46 -0700635static void block_signals(sigset_t *old)
636{
637 spin_lock_irq(&current->sighand->siglock);
638 *old = current->blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Jan Harkesd9664c92007-07-19 01:48:46 -0700640 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))
667
668static inline void coda_waitfor_upcall(struct upc_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669{
670 DECLARE_WAITQUEUE(wait, current);
Jan Harkesd9664c92007-07-19 01:48:46 -0700671 unsigned long timeout = jiffies + coda_timeout * HZ;
672 sigset_t old;
673 int blocked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Jan Harkesd9664c92007-07-19 01:48:46 -0700675 block_signals(&old);
676 blocked = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
Jan Harkesd9664c92007-07-19 01:48:46 -0700678 add_wait_queue(&req->uc_sleep, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 for (;;) {
Jan Harkesd9664c92007-07-19 01:48:46 -0700680 if (CODA_INTERRUPTIBLE(req))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 set_current_state(TASK_INTERRUPTIBLE);
682 else
683 set_current_state(TASK_UNINTERRUPTIBLE);
684
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 /* got a reply */
Jan Harkesd9664c92007-07-19 01:48:46 -0700686 if (req->uc_flags & (REQ_WRITE | REQ_ABORT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 break;
688
Jan Harkesd9664c92007-07-19 01:48:46 -0700689 if (blocked && time_after(jiffies, timeout) &&
690 CODA_INTERRUPTIBLE(req))
691 {
692 unblock_signals(&old);
693 blocked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Jan Harkesd9664c92007-07-19 01:48:46 -0700696 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();
705 }
706 if (blocked)
707 unblock_signals(&old);
708
709 remove_wait_queue(&req->uc_sleep, &wait);
710 set_current_state(TASK_RUNNING);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711}
712
713
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700714/*
715 * coda_upcall will return an error in the case of
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 * 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)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700723static int coda_upcall(struct venus_comm *vcp,
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700724 int inSize, int *outSize,
725 union inputArgs *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 union outputArgs *out;
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700728 union inputArgs *sig_inputArgs;
729 struct upc_req *req, *sig_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 int error = 0;
731
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700732 if (!vcp->vc_inuse) {
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700733 printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n");
734 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 }
736
737 /* Format the request message. */
Jan Harkes37461e12007-07-19 01:48:48 -0700738 req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700739 if (!req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 return -ENOMEM;
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700741
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 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;
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700747 req->uc_unique = ++vcp->vc_seq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 init_waitqueue_head(&req->uc_sleep);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700749
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 /* 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. */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700754 list_add_tail(&req->uc_chain, &vcp->vc_pending);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700755
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700756 wake_up_interruptible(&vcp->vc_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 /* 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. */
Jan Harkes87065512007-07-19 01:48:45 -0700767 coda_waitfor_upcall(req);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700769 /* Op went through, interrupt or not... */
770 if (req->uc_flags & REQ_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 }
777
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700778 error = -EINTR;
779 if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) {
780 printk(KERN_WARNING "coda: Unexpected interruption.\n");
781 goto exit;
782 }
783
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700784 /* 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 */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700789 if (!vcp->vc_inuse) {
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700790 printk(KERN_INFO "coda: Venus dead, not sending signal.\n");
791 goto exit;
792 }
793
794 error = -ENOMEM;
Jan Harkes37461e12007-07-19 01:48:48 -0700795 sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700796 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) {
Jan Harkes37461e12007-07-19 01:48:48 -0700800 kfree(sig_req);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700801 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! */
Jan Harkesa1b0aa82007-07-19 01:48:50 -0700816 list_add(&(sig_req->uc_chain), &vcp->vc_pending);
817 wake_up_interruptible(&vcp->vc_waitq);
Jan Harkesfe71b5f2007-07-19 01:48:46 -0700818
819exit:
Jan Harkes37461e12007-07-19 01:48:48 -0700820 kfree(req);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 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{
Jan Harkes5fd31e92007-07-19 01:48:49 -0700860 struct inode *inode = NULL;
861 struct CodaFid *fid, *newfid;
862
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 /* Handle invalidation requests. */
Jan Harkes5fd31e92007-07-19 01:48:49 -0700864 if ( !sb || !sb->s_root)
865 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Jan Harkes5fd31e92007-07-19 01:48:49 -0700867 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
Jan Harkes5fd31e92007-07-19 01:48:49 -0700875 case CODA_PURGEUSER:
876 coda_cache_clear_all(sb);
877 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
Jan Harkes5fd31e92007-07-19 01:48:49 -0700879 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Jan Harkes5fd31e92007-07-19 01:48:49 -0700888 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Jan Harkes5fd31e92007-07-19 01:48:49 -0700895 case CODA_PURGEFID:
896 fid = &out->coda_purgefid.CodaFid;
897 inode = coda_fid_to_inode(fid, sb);
898 if (inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 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
Jan Harkes5fd31e92007-07-19 01:48:49 -0700905 }
906 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Jan Harkes5fd31e92007-07-19 01:48:49 -0700908 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921}
922