Deleted Added
full compact
coda_psdev.c (130585) coda_psdev.c (134542)
1/*
2 * Coda: an Experimental Distributed File System
3 * Release 3.1
4 *
5 * Copyright (c) 1987-1998 Carnegie Mellon University
6 * All Rights Reserved
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation, and
13 * that credit is given to Carnegie Mellon University in all documents
14 * and publicity pertaining to direct or indirect use of this code or its
15 * derivatives.
16 *
17 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS,
18 * SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS
19 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON
20 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
21 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF
22 * ANY DERIVATIVE WORK.
23 *
24 * Carnegie Mellon encourages users of this software to return any
25 * improvements or extensions that they make, and to grant Carnegie
26 * Mellon the rights to redistribute these changes without encumbrance.
27 *
28 * @(#) src/sys/coda/coda_psdev.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
29 */
30/*
31 * Mach Operating System
32 * Copyright (c) 1989 Carnegie-Mellon University
33 * All rights reserved. The CMU software License Agreement specifies
34 * the terms and conditions for use and redistribution.
35 */
36
37/*
38 * This code was written for the Coda filesystem at Carnegie Mellon
39 * University. Contributers include David Steere, James Kistler, and
40 * M. Satyanarayanan. */
41
42/*
43 * These routines define the psuedo device for communication between
44 * Coda's Venus and Minicache in Mach 2.6. They used to be in cfs_subr.c,
45 * but I moved them to make it easier to port the Minicache without
46 * porting coda. -- DCS 10/12/94
47 */
48
49/* These routines are the device entry points for Venus. */
50
51#include <sys/cdefs.h>
1/*
2 * Coda: an Experimental Distributed File System
3 * Release 3.1
4 *
5 * Copyright (c) 1987-1998 Carnegie Mellon University
6 * All Rights Reserved
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation, and
13 * that credit is given to Carnegie Mellon University in all documents
14 * and publicity pertaining to direct or indirect use of this code or its
15 * derivatives.
16 *
17 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS,
18 * SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS
19 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON
20 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
21 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF
22 * ANY DERIVATIVE WORK.
23 *
24 * Carnegie Mellon encourages users of this software to return any
25 * improvements or extensions that they make, and to grant Carnegie
26 * Mellon the rights to redistribute these changes without encumbrance.
27 *
28 * @(#) src/sys/coda/coda_psdev.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
29 */
30/*
31 * Mach Operating System
32 * Copyright (c) 1989 Carnegie-Mellon University
33 * All rights reserved. The CMU software License Agreement specifies
34 * the terms and conditions for use and redistribution.
35 */
36
37/*
38 * This code was written for the Coda filesystem at Carnegie Mellon
39 * University. Contributers include David Steere, James Kistler, and
40 * M. Satyanarayanan. */
41
42/*
43 * These routines define the psuedo device for communication between
44 * Coda's Venus and Minicache in Mach 2.6. They used to be in cfs_subr.c,
45 * but I moved them to make it easier to port the Minicache without
46 * porting coda. -- DCS 10/12/94
47 */
48
49/* These routines are the device entry points for Venus. */
50
51#include <sys/cdefs.h>
52__FBSDID("$FreeBSD: head/sys/fs/coda/coda_psdev.c 130585 2004-06-16 09:47:26Z phk $");
52__FBSDID("$FreeBSD: head/sys/fs/coda/coda_psdev.c 134542 2004-08-30 23:03:58Z peter $");
53
54
55extern int coda_nc_initialized; /* Set if cache has been initialized */
56
53
54
55extern int coda_nc_initialized; /* Set if cache has been initialized */
56
57#include <vcoda.h>
57#include "opt_coda.h"
58
59#include <sys/param.h>
60#include <sys/systm.h>
61#include <sys/ioccom.h>
62#include <sys/kernel.h>
63#include <sys/lock.h>
64#include <sys/malloc.h>
65#include <sys/file.h> /* must come after sys/malloc.h */
66#include <sys/mount.h>
67#include <sys/mutex.h>
68#include <sys/poll.h>
69#include <sys/proc.h>
70
71#include <coda/coda.h>
72#include <coda/cnode.h>
73#include <coda/coda_namecache.h>
74#include <coda/coda_io.h>
75#include <coda/coda_psdev.h>
76
77#define CTL_C
78
79#ifdef CTL_C
80#include <sys/signalvar.h>
81#endif
82
83int coda_psdev_print_entry = 0;
84static
85int outstanding_upcalls = 0;
86int coda_call_sleep = PZERO - 1;
87#ifdef CTL_C
88int coda_pcatch = PCATCH;
89#else
90#endif
91
92#define ENTRY if(coda_psdev_print_entry) myprintf(("Entered %s\n",__func__))
93
94void vcodaattach(int n);
95
96struct vmsg {
97 struct queue vm_chain;
98 caddr_t vm_data;
99 u_short vm_flags;
100 u_short vm_inSize; /* Size is at most 5000 bytes */
101 u_short vm_outSize;
102 u_short vm_opcode; /* copied from data to save ptr lookup */
103 int vm_unique;
104 caddr_t vm_sleep; /* Not used by Mach. */
105};
106
107#define VM_READ 1
108#define VM_WRITE 2
109#define VM_INTR 4
110
111/* vcodaattach: do nothing */
112void
113vcodaattach(n)
114 int n;
115{
116}
117
118int
119vc_nb_open(dev, flag, mode, td)
120 struct cdev *dev;
121 int flag;
122 int mode;
123 struct thread *td; /* NetBSD only */
124{
125 register struct vcomm *vcp;
126
127 ENTRY;
128
129 if (minor(dev) >= NVCODA || minor(dev) < 0)
130 return(ENXIO);
131
132 if (!coda_nc_initialized)
133 coda_nc_init();
134
135 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
136 if (VC_OPEN(vcp))
137 return(EBUSY);
138
139 bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
140 INIT_QUEUE(vcp->vc_requests);
141 INIT_QUEUE(vcp->vc_replys);
142 MARK_VC_OPEN(vcp);
143
144 coda_mnttbl[minor(dev)].mi_vfsp = NULL;
145 coda_mnttbl[minor(dev)].mi_rootvp = NULL;
146
147 return(0);
148}
149
150int
151vc_nb_close (dev, flag, mode, td)
152 struct cdev *dev;
153 int flag;
154 int mode;
155 struct thread *td;
156{
157 register struct vcomm *vcp;
158 register struct vmsg *vmp, *nvmp = NULL;
159 struct coda_mntinfo *mi;
160 int err;
161
162 ENTRY;
163
164 if (minor(dev) >= NVCODA || minor(dev) < 0)
165 return(ENXIO);
166
167 mi = &coda_mnttbl[minor(dev)];
168 vcp = &(mi->mi_vcomm);
169
170 if (!VC_OPEN(vcp))
171 panic("vcclose: not open");
172
173 /* prevent future operations on this vfs from succeeding by auto-
174 * unmounting any vfs mounted via this device. This frees user or
175 * sysadm from having to remember where all mount points are located.
176 * Put this before WAKEUPs to avoid queuing new messages between
177 * the WAKEUP and the unmount (which can happen if we're unlucky)
178 */
179 if (!mi->mi_rootvp) {
180 /* just a simple open/close w no mount */
181 MARK_VC_CLOSED(vcp);
182 return 0;
183 }
184
185 /* Let unmount know this is for real */
186 VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
187 coda_unmounting(mi->mi_vfsp);
188
189 outstanding_upcalls = 0;
190 /* Wakeup clients so they can return. */
191 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
192 !EOQ(vmp, vcp->vc_requests);
193 vmp = nvmp)
194 {
195 nvmp = (struct vmsg *)GETNEXT(vmp->vm_chain);
196 /* Free signal request messages and don't wakeup cause
197 no one is waiting. */
198 if (vmp->vm_opcode == CODA_SIGNAL) {
199 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
200 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
201 continue;
202 }
203 outstanding_upcalls++;
204 wakeup(&vmp->vm_sleep);
205 }
206
207 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
208 !EOQ(vmp, vcp->vc_replys);
209 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
210 {
211 outstanding_upcalls++;
212 wakeup(&vmp->vm_sleep);
213 }
214
215 MARK_VC_CLOSED(vcp);
216
217 if (outstanding_upcalls) {
218#ifdef CODA_VERBOSE
219 printf("presleep: outstanding_upcalls = %d\n", outstanding_upcalls);
220 (void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
221 printf("postsleep: outstanding_upcalls = %d\n", outstanding_upcalls);
222#else
223 (void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
224#endif
225 }
226
227 err = dounmount(mi->mi_vfsp, flag, td);
228 if (err)
229 myprintf(("Error %d unmounting vfs in vcclose(%d)\n",
230 err, minor(dev)));
231 return 0;
232}
233
234int
235vc_nb_read(dev, uiop, flag)
236 struct cdev *dev;
237 struct uio *uiop;
238 int flag;
239{
240 register struct vcomm * vcp;
241 register struct vmsg *vmp;
242 int error = 0;
243
244 ENTRY;
245
246 if (minor(dev) >= NVCODA || minor(dev) < 0)
247 return(ENXIO);
248
249 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
250 /* Get message at head of request queue. */
251 if (EMPTY(vcp->vc_requests))
252 return(0); /* Nothing to read */
253
254 vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
255
256 /* Move the input args into userspace */
257 uiop->uio_rw = UIO_READ;
258 error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
259 if (error) {
260 myprintf(("vcread: error (%d) on uiomove\n", error));
261 error = EINVAL;
262 }
263
264#ifdef OLD_DIAGNOSTIC
265 if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
266 panic("vc_nb_read: bad chain");
267#endif
268
269 REMQUE(vmp->vm_chain);
270
271 /* If request was a signal, free up the message and don't
272 enqueue it in the reply queue. */
273 if (vmp->vm_opcode == CODA_SIGNAL) {
274 if (codadebug)
275 myprintf(("vcread: signal msg (%d, %d)\n",
276 vmp->vm_opcode, vmp->vm_unique));
277 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
278 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
279 return(error);
280 }
281
282 vmp->vm_flags |= VM_READ;
283 INSQUE(vmp->vm_chain, vcp->vc_replys);
284
285 return(error);
286}
287
288int
289vc_nb_write(dev, uiop, flag)
290 struct cdev *dev;
291 struct uio *uiop;
292 int flag;
293{
294 register struct vcomm * vcp;
295 register struct vmsg *vmp;
296 struct coda_out_hdr *out;
297 u_long seq;
298 u_long opcode;
299 int buf[2];
300 int error = 0;
301
302 ENTRY;
303
304 if (minor(dev) >= NVCODA || minor(dev) < 0)
305 return(ENXIO);
306
307 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
308
309 /* Peek at the opcode, unique without transfering the data. */
310 uiop->uio_rw = UIO_WRITE;
311 error = uiomove((caddr_t)buf, sizeof(int) * 2, uiop);
312 if (error) {
313 myprintf(("vcwrite: error (%d) on uiomove\n", error));
314 return(EINVAL);
315 }
316
317 opcode = buf[0];
318 seq = buf[1];
319
320 if (codadebug)
321 myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
322
323 if (DOWNCALL(opcode)) {
324 union outputArgs pbuf;
325
326 /* get the rest of the data. */
327 uiop->uio_rw = UIO_WRITE;
328 error = uiomove((caddr_t)&pbuf.coda_purgeuser.oh.result, sizeof(pbuf) - (sizeof(int)*2), uiop);
329 if (error) {
330 myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
331 error, opcode, seq));
332 return(EINVAL);
333 }
334
335 return handleDownCall(opcode, &pbuf);
336 }
337
338 /* Look for the message on the (waiting for) reply queue. */
339 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
340 !EOQ(vmp, vcp->vc_replys);
341 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
342 {
343 if (vmp->vm_unique == seq) break;
344 }
345
346 if (EOQ(vmp, vcp->vc_replys)) {
347 if (codadebug)
348 myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
349
350 return(ESRCH);
351 }
352
353 /* Remove the message from the reply queue */
354 REMQUE(vmp->vm_chain);
355
356 /* move data into response buffer. */
357 out = (struct coda_out_hdr *)vmp->vm_data;
358 /* Don't need to copy opcode and uniquifier. */
359
360 /* get the rest of the data. */
361 if (vmp->vm_outSize < uiop->uio_resid) {
362 myprintf(("vcwrite: more data than asked for (%d < %d)\n",
363 vmp->vm_outSize, uiop->uio_resid));
364 wakeup(&vmp->vm_sleep); /* Notify caller of the error. */
365 return(EINVAL);
366 }
367
368 buf[0] = uiop->uio_resid; /* Save this value. */
369 uiop->uio_rw = UIO_WRITE;
370 error = uiomove((caddr_t) &out->result, vmp->vm_outSize - (sizeof(int) * 2), uiop);
371 if (error) {
372 myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
373 error, opcode, seq));
374 return(EINVAL);
375 }
376
377 /* I don't think these are used, but just in case. */
378 /* XXX - aren't these two already correct? -bnoble */
379 out->opcode = opcode;
380 out->unique = seq;
381 vmp->vm_outSize = buf[0]; /* Amount of data transferred? */
382 vmp->vm_flags |= VM_WRITE;
383 wakeup(&vmp->vm_sleep);
384
385 return(0);
386}
387
388int
389vc_nb_ioctl(dev, cmd, addr, flag, td)
390 struct cdev *dev;
391 u_long cmd;
392 caddr_t addr;
393 int flag;
394 struct thread *td;
395{
396 ENTRY;
397
398 switch(cmd) {
399 case CODARESIZE: {
400 struct coda_resize *data = (struct coda_resize *)addr;
401 return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
402 break;
403 }
404 case CODASTATS:
405 if (coda_nc_use) {
406 coda_nc_gather_stats();
407 return(0);
408 } else {
409 return(ENODEV);
410 }
411 break;
412 case CODAPRINT:
413 if (coda_nc_use) {
414 print_coda_nc();
415 return(0);
416 } else {
417 return(ENODEV);
418 }
419 break;
420 case CIOC_KERNEL_VERSION:
421 switch (*(u_int *)addr) {
422 case 0:
423 *(u_int *)addr = coda_kernel_version;
424 return 0;
425 break;
426 case 1:
427 case 2:
428 if (coda_kernel_version != *(u_int *)addr)
429 return ENOENT;
430 else
431 return 0;
432 default:
433 return ENOENT;
434 }
435 break;
436 default :
437 return(EINVAL);
438 break;
439 }
440}
441
442int
443vc_nb_poll(dev, events, td)
444 struct cdev *dev;
445 int events;
446 struct thread *td;
447{
448 register struct vcomm *vcp;
449 int event_msk = 0;
450
451 ENTRY;
452
453 if (minor(dev) >= NVCODA || minor(dev) < 0)
454 return(ENXIO);
455
456 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
457
458 event_msk = events & (POLLIN|POLLRDNORM);
459 if (!event_msk)
460 return(0);
461
462 if (!EMPTY(vcp->vc_requests))
463 return(events & (POLLIN|POLLRDNORM));
464
465 selrecord(td, &(vcp->vc_selproc));
466
467 return(0);
468}
469
470/*
471 * Statistics
472 */
473struct coda_clstat coda_clstat;
474
475/*
476 * Key question: whether to sleep interuptably or uninteruptably when
477 * waiting for Venus. The former seems better (cause you can ^C a
478 * job), but then GNU-EMACS completion breaks. Use tsleep with no
479 * timeout, and no longjmp happens. But, when sleeping
480 * "uninterruptibly", we don't get told if it returns abnormally
481 * (e.g. kill -9).
482 */
483
484int
485coda_call(mntinfo, inSize, outSize, buffer)
486 struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
487{
488 struct vcomm *vcp;
489 struct vmsg *vmp;
490 int error;
491#ifdef CTL_C
492 struct thread *td = curthread;
493 struct proc *p = td->td_proc;
494 sigset_t psig_omask;
495 sigset_t tempset;
496 int i;
497#endif
498 if (mntinfo == NULL) {
499 /* Unlikely, but could be a race condition with a dying warden */
500 return ENODEV;
501 }
502
503 vcp = &(mntinfo->mi_vcomm);
504
505 coda_clstat.ncalls++;
506 coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
507
508 if (!VC_OPEN(vcp))
509 return(ENODEV);
510
511 CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
512 /* Format the request message. */
513 vmp->vm_data = buffer;
514 vmp->vm_flags = 0;
515 vmp->vm_inSize = inSize;
516 vmp->vm_outSize
517 = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
518 vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
519 vmp->vm_unique = ++vcp->vc_seq;
520 if (codadebug)
521 myprintf(("Doing a call for %d.%d\n",
522 vmp->vm_opcode, vmp->vm_unique));
523
524 /* Fill in the common input args. */
525 ((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
526
527 /* Append msg to request queue and poke Venus. */
528 INSQUE(vmp->vm_chain, vcp->vc_requests);
529 selwakeuppri(&(vcp->vc_selproc), coda_call_sleep);
530
531 /* We can be interrupted while we wait for Venus to process
532 * our request. If the interrupt occurs before Venus has read
533 * the request, we dequeue and return. If it occurs after the
534 * read but before the reply, we dequeue, send a signal
535 * message, and return. If it occurs after the reply we ignore
536 * it. In no case do we want to restart the syscall. If it
537 * was interrupted by a venus shutdown (vcclose), return
538 * ENODEV. */
539
540 /* Ignore return, We have to check anyway */
541#ifdef CTL_C
542 /* This is work in progress. Setting coda_pcatch lets tsleep reawaken
543 on a ^c or ^z. The problem is that emacs sets certain interrupts
544 as SA_RESTART. This means that we should exit sleep handle the
545 "signal" and then go to sleep again. Mostly this is done by letting
546 the syscall complete and be restarted. We are not idempotent and
547 can not do this. A better solution is necessary.
548 */
549 i = 0;
550 PROC_LOCK(p);
551 psig_omask = td->td_sigmask;
552 do {
553 error = msleep(&vmp->vm_sleep, &p->p_mtx,
554 (coda_call_sleep|coda_pcatch), "coda_call",
555 hz*2);
556 if (error == 0)
557 break;
558 else if (error == EWOULDBLOCK) {
559#ifdef CODA_VERBOSE
560 printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
561#endif
562 }
563 else {
564 SIGEMPTYSET(tempset);
565 SIGADDSET(tempset, SIGIO);
566 if (SIGSETEQ(td->td_siglist, tempset)) {
567 SIGADDSET(td->td_sigmask, SIGIO);
568#ifdef CODA_VERBOSE
569 printf("coda_call: tsleep returns %d SIGIO, cnt %d\n",
570 error, i);
571#endif
572 } else {
573 SIGDELSET(tempset, SIGIO);
574 SIGADDSET(tempset, SIGALRM);
575 if (SIGSETEQ(td->td_siglist, tempset)) {
576 SIGADDSET(td->td_sigmask, SIGALRM);
577#ifdef CODA_VERBOSE
578 printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n",
579 error, i);
580#endif
581 }
582 else {
583#ifdef CODA_VERBOSE
584 printf("coda_call: tsleep returns %d, cnt %d\n",
585 error, i);
586#endif
587
588#if notyet
589 tempset = td->td_siglist;
590 SIGSETNAND(tempset, td->td_sigmask);
591 printf("coda_call: siglist = %p, sigmask = %p, mask %p\n",
592 td->td_siglist, td->td_sigmask,
593 tempset);
594 break;
595 SIGSETOR(td->td_sigmask, td->td_siglist);
596 tempset = td->td_siglist;
597 SIGSETNAND(tempset, td->td_sigmask);
598 printf("coda_call: new mask, siglist = %p, sigmask = %p, mask %p\n",
599 td->td_siglist, td->td_sigmask,
600 tempset);
601#endif
602 }
603 }
604 }
605 } while (error && i++ < 128 && VC_OPEN(vcp));
606 td->td_sigmask = psig_omask;
607 signotify(td);
608 PROC_UNLOCK(p);
609#else
610 (void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
611#endif
612 if (VC_OPEN(vcp)) { /* Venus is still alive */
613 /* Op went through, interrupt or not... */
614 if (vmp->vm_flags & VM_WRITE) {
615 error = 0;
616 *outSize = vmp->vm_outSize;
617 }
618
619 else if (!(vmp->vm_flags & VM_READ)) {
620 /* Interrupted before venus read it. */
621#ifdef CODA_VERBOSE
622 if (1)
623#else
624 if (codadebug)
625#endif
626 myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
627 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
628 REMQUE(vmp->vm_chain);
629 error = EINTR;
630 }
631
632 else {
633 /* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
634 upcall started */
635 /* Interrupted after start of upcall, send venus a signal */
636 struct coda_in_hdr *dog;
637 struct vmsg *svmp;
638
639#ifdef CODA_VERBOSE
640 if (1)
641#else
642 if (codadebug)
643#endif
644 myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
645 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
646
647 REMQUE(vmp->vm_chain);
648 error = EINTR;
649
650 CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
651
652 CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
653 dog = (struct coda_in_hdr *)svmp->vm_data;
654
655 svmp->vm_flags = 0;
656 dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
657 dog->unique = svmp->vm_unique = vmp->vm_unique;
658 svmp->vm_inSize = sizeof (struct coda_in_hdr);
659/*??? rvb */ svmp->vm_outSize = sizeof (struct coda_in_hdr);
660
661 if (codadebug)
662 myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
663 svmp->vm_opcode, svmp->vm_unique));
664
665 /* insert at head of queue! */
666 INSQUE(svmp->vm_chain, vcp->vc_requests);
667 selwakeuppri(&(vcp->vc_selproc), coda_call_sleep);
668 }
669 }
670
671 else { /* If venus died (!VC_OPEN(vcp)) */
672 if (codadebug)
673 myprintf(("vcclose woke op %d.%d flags %d\n",
674 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
675
676 error = ENODEV;
677 }
678
679 CODA_FREE(vmp, sizeof(struct vmsg));
680
681 if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
682 wakeup(&outstanding_upcalls);
683
684 if (!error)
685 error = ((struct coda_out_hdr *)buffer)->result;
686 return(error);
687}
58
59#include <sys/param.h>
60#include <sys/systm.h>
61#include <sys/ioccom.h>
62#include <sys/kernel.h>
63#include <sys/lock.h>
64#include <sys/malloc.h>
65#include <sys/file.h> /* must come after sys/malloc.h */
66#include <sys/mount.h>
67#include <sys/mutex.h>
68#include <sys/poll.h>
69#include <sys/proc.h>
70
71#include <coda/coda.h>
72#include <coda/cnode.h>
73#include <coda/coda_namecache.h>
74#include <coda/coda_io.h>
75#include <coda/coda_psdev.h>
76
77#define CTL_C
78
79#ifdef CTL_C
80#include <sys/signalvar.h>
81#endif
82
83int coda_psdev_print_entry = 0;
84static
85int outstanding_upcalls = 0;
86int coda_call_sleep = PZERO - 1;
87#ifdef CTL_C
88int coda_pcatch = PCATCH;
89#else
90#endif
91
92#define ENTRY if(coda_psdev_print_entry) myprintf(("Entered %s\n",__func__))
93
94void vcodaattach(int n);
95
96struct vmsg {
97 struct queue vm_chain;
98 caddr_t vm_data;
99 u_short vm_flags;
100 u_short vm_inSize; /* Size is at most 5000 bytes */
101 u_short vm_outSize;
102 u_short vm_opcode; /* copied from data to save ptr lookup */
103 int vm_unique;
104 caddr_t vm_sleep; /* Not used by Mach. */
105};
106
107#define VM_READ 1
108#define VM_WRITE 2
109#define VM_INTR 4
110
111/* vcodaattach: do nothing */
112void
113vcodaattach(n)
114 int n;
115{
116}
117
118int
119vc_nb_open(dev, flag, mode, td)
120 struct cdev *dev;
121 int flag;
122 int mode;
123 struct thread *td; /* NetBSD only */
124{
125 register struct vcomm *vcp;
126
127 ENTRY;
128
129 if (minor(dev) >= NVCODA || minor(dev) < 0)
130 return(ENXIO);
131
132 if (!coda_nc_initialized)
133 coda_nc_init();
134
135 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
136 if (VC_OPEN(vcp))
137 return(EBUSY);
138
139 bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
140 INIT_QUEUE(vcp->vc_requests);
141 INIT_QUEUE(vcp->vc_replys);
142 MARK_VC_OPEN(vcp);
143
144 coda_mnttbl[minor(dev)].mi_vfsp = NULL;
145 coda_mnttbl[minor(dev)].mi_rootvp = NULL;
146
147 return(0);
148}
149
150int
151vc_nb_close (dev, flag, mode, td)
152 struct cdev *dev;
153 int flag;
154 int mode;
155 struct thread *td;
156{
157 register struct vcomm *vcp;
158 register struct vmsg *vmp, *nvmp = NULL;
159 struct coda_mntinfo *mi;
160 int err;
161
162 ENTRY;
163
164 if (minor(dev) >= NVCODA || minor(dev) < 0)
165 return(ENXIO);
166
167 mi = &coda_mnttbl[minor(dev)];
168 vcp = &(mi->mi_vcomm);
169
170 if (!VC_OPEN(vcp))
171 panic("vcclose: not open");
172
173 /* prevent future operations on this vfs from succeeding by auto-
174 * unmounting any vfs mounted via this device. This frees user or
175 * sysadm from having to remember where all mount points are located.
176 * Put this before WAKEUPs to avoid queuing new messages between
177 * the WAKEUP and the unmount (which can happen if we're unlucky)
178 */
179 if (!mi->mi_rootvp) {
180 /* just a simple open/close w no mount */
181 MARK_VC_CLOSED(vcp);
182 return 0;
183 }
184
185 /* Let unmount know this is for real */
186 VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
187 coda_unmounting(mi->mi_vfsp);
188
189 outstanding_upcalls = 0;
190 /* Wakeup clients so they can return. */
191 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
192 !EOQ(vmp, vcp->vc_requests);
193 vmp = nvmp)
194 {
195 nvmp = (struct vmsg *)GETNEXT(vmp->vm_chain);
196 /* Free signal request messages and don't wakeup cause
197 no one is waiting. */
198 if (vmp->vm_opcode == CODA_SIGNAL) {
199 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
200 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
201 continue;
202 }
203 outstanding_upcalls++;
204 wakeup(&vmp->vm_sleep);
205 }
206
207 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
208 !EOQ(vmp, vcp->vc_replys);
209 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
210 {
211 outstanding_upcalls++;
212 wakeup(&vmp->vm_sleep);
213 }
214
215 MARK_VC_CLOSED(vcp);
216
217 if (outstanding_upcalls) {
218#ifdef CODA_VERBOSE
219 printf("presleep: outstanding_upcalls = %d\n", outstanding_upcalls);
220 (void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
221 printf("postsleep: outstanding_upcalls = %d\n", outstanding_upcalls);
222#else
223 (void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
224#endif
225 }
226
227 err = dounmount(mi->mi_vfsp, flag, td);
228 if (err)
229 myprintf(("Error %d unmounting vfs in vcclose(%d)\n",
230 err, minor(dev)));
231 return 0;
232}
233
234int
235vc_nb_read(dev, uiop, flag)
236 struct cdev *dev;
237 struct uio *uiop;
238 int flag;
239{
240 register struct vcomm * vcp;
241 register struct vmsg *vmp;
242 int error = 0;
243
244 ENTRY;
245
246 if (minor(dev) >= NVCODA || minor(dev) < 0)
247 return(ENXIO);
248
249 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
250 /* Get message at head of request queue. */
251 if (EMPTY(vcp->vc_requests))
252 return(0); /* Nothing to read */
253
254 vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
255
256 /* Move the input args into userspace */
257 uiop->uio_rw = UIO_READ;
258 error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
259 if (error) {
260 myprintf(("vcread: error (%d) on uiomove\n", error));
261 error = EINVAL;
262 }
263
264#ifdef OLD_DIAGNOSTIC
265 if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
266 panic("vc_nb_read: bad chain");
267#endif
268
269 REMQUE(vmp->vm_chain);
270
271 /* If request was a signal, free up the message and don't
272 enqueue it in the reply queue. */
273 if (vmp->vm_opcode == CODA_SIGNAL) {
274 if (codadebug)
275 myprintf(("vcread: signal msg (%d, %d)\n",
276 vmp->vm_opcode, vmp->vm_unique));
277 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
278 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
279 return(error);
280 }
281
282 vmp->vm_flags |= VM_READ;
283 INSQUE(vmp->vm_chain, vcp->vc_replys);
284
285 return(error);
286}
287
288int
289vc_nb_write(dev, uiop, flag)
290 struct cdev *dev;
291 struct uio *uiop;
292 int flag;
293{
294 register struct vcomm * vcp;
295 register struct vmsg *vmp;
296 struct coda_out_hdr *out;
297 u_long seq;
298 u_long opcode;
299 int buf[2];
300 int error = 0;
301
302 ENTRY;
303
304 if (minor(dev) >= NVCODA || minor(dev) < 0)
305 return(ENXIO);
306
307 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
308
309 /* Peek at the opcode, unique without transfering the data. */
310 uiop->uio_rw = UIO_WRITE;
311 error = uiomove((caddr_t)buf, sizeof(int) * 2, uiop);
312 if (error) {
313 myprintf(("vcwrite: error (%d) on uiomove\n", error));
314 return(EINVAL);
315 }
316
317 opcode = buf[0];
318 seq = buf[1];
319
320 if (codadebug)
321 myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
322
323 if (DOWNCALL(opcode)) {
324 union outputArgs pbuf;
325
326 /* get the rest of the data. */
327 uiop->uio_rw = UIO_WRITE;
328 error = uiomove((caddr_t)&pbuf.coda_purgeuser.oh.result, sizeof(pbuf) - (sizeof(int)*2), uiop);
329 if (error) {
330 myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
331 error, opcode, seq));
332 return(EINVAL);
333 }
334
335 return handleDownCall(opcode, &pbuf);
336 }
337
338 /* Look for the message on the (waiting for) reply queue. */
339 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
340 !EOQ(vmp, vcp->vc_replys);
341 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
342 {
343 if (vmp->vm_unique == seq) break;
344 }
345
346 if (EOQ(vmp, vcp->vc_replys)) {
347 if (codadebug)
348 myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
349
350 return(ESRCH);
351 }
352
353 /* Remove the message from the reply queue */
354 REMQUE(vmp->vm_chain);
355
356 /* move data into response buffer. */
357 out = (struct coda_out_hdr *)vmp->vm_data;
358 /* Don't need to copy opcode and uniquifier. */
359
360 /* get the rest of the data. */
361 if (vmp->vm_outSize < uiop->uio_resid) {
362 myprintf(("vcwrite: more data than asked for (%d < %d)\n",
363 vmp->vm_outSize, uiop->uio_resid));
364 wakeup(&vmp->vm_sleep); /* Notify caller of the error. */
365 return(EINVAL);
366 }
367
368 buf[0] = uiop->uio_resid; /* Save this value. */
369 uiop->uio_rw = UIO_WRITE;
370 error = uiomove((caddr_t) &out->result, vmp->vm_outSize - (sizeof(int) * 2), uiop);
371 if (error) {
372 myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
373 error, opcode, seq));
374 return(EINVAL);
375 }
376
377 /* I don't think these are used, but just in case. */
378 /* XXX - aren't these two already correct? -bnoble */
379 out->opcode = opcode;
380 out->unique = seq;
381 vmp->vm_outSize = buf[0]; /* Amount of data transferred? */
382 vmp->vm_flags |= VM_WRITE;
383 wakeup(&vmp->vm_sleep);
384
385 return(0);
386}
387
388int
389vc_nb_ioctl(dev, cmd, addr, flag, td)
390 struct cdev *dev;
391 u_long cmd;
392 caddr_t addr;
393 int flag;
394 struct thread *td;
395{
396 ENTRY;
397
398 switch(cmd) {
399 case CODARESIZE: {
400 struct coda_resize *data = (struct coda_resize *)addr;
401 return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
402 break;
403 }
404 case CODASTATS:
405 if (coda_nc_use) {
406 coda_nc_gather_stats();
407 return(0);
408 } else {
409 return(ENODEV);
410 }
411 break;
412 case CODAPRINT:
413 if (coda_nc_use) {
414 print_coda_nc();
415 return(0);
416 } else {
417 return(ENODEV);
418 }
419 break;
420 case CIOC_KERNEL_VERSION:
421 switch (*(u_int *)addr) {
422 case 0:
423 *(u_int *)addr = coda_kernel_version;
424 return 0;
425 break;
426 case 1:
427 case 2:
428 if (coda_kernel_version != *(u_int *)addr)
429 return ENOENT;
430 else
431 return 0;
432 default:
433 return ENOENT;
434 }
435 break;
436 default :
437 return(EINVAL);
438 break;
439 }
440}
441
442int
443vc_nb_poll(dev, events, td)
444 struct cdev *dev;
445 int events;
446 struct thread *td;
447{
448 register struct vcomm *vcp;
449 int event_msk = 0;
450
451 ENTRY;
452
453 if (minor(dev) >= NVCODA || minor(dev) < 0)
454 return(ENXIO);
455
456 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
457
458 event_msk = events & (POLLIN|POLLRDNORM);
459 if (!event_msk)
460 return(0);
461
462 if (!EMPTY(vcp->vc_requests))
463 return(events & (POLLIN|POLLRDNORM));
464
465 selrecord(td, &(vcp->vc_selproc));
466
467 return(0);
468}
469
470/*
471 * Statistics
472 */
473struct coda_clstat coda_clstat;
474
475/*
476 * Key question: whether to sleep interuptably or uninteruptably when
477 * waiting for Venus. The former seems better (cause you can ^C a
478 * job), but then GNU-EMACS completion breaks. Use tsleep with no
479 * timeout, and no longjmp happens. But, when sleeping
480 * "uninterruptibly", we don't get told if it returns abnormally
481 * (e.g. kill -9).
482 */
483
484int
485coda_call(mntinfo, inSize, outSize, buffer)
486 struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
487{
488 struct vcomm *vcp;
489 struct vmsg *vmp;
490 int error;
491#ifdef CTL_C
492 struct thread *td = curthread;
493 struct proc *p = td->td_proc;
494 sigset_t psig_omask;
495 sigset_t tempset;
496 int i;
497#endif
498 if (mntinfo == NULL) {
499 /* Unlikely, but could be a race condition with a dying warden */
500 return ENODEV;
501 }
502
503 vcp = &(mntinfo->mi_vcomm);
504
505 coda_clstat.ncalls++;
506 coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
507
508 if (!VC_OPEN(vcp))
509 return(ENODEV);
510
511 CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
512 /* Format the request message. */
513 vmp->vm_data = buffer;
514 vmp->vm_flags = 0;
515 vmp->vm_inSize = inSize;
516 vmp->vm_outSize
517 = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
518 vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
519 vmp->vm_unique = ++vcp->vc_seq;
520 if (codadebug)
521 myprintf(("Doing a call for %d.%d\n",
522 vmp->vm_opcode, vmp->vm_unique));
523
524 /* Fill in the common input args. */
525 ((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
526
527 /* Append msg to request queue and poke Venus. */
528 INSQUE(vmp->vm_chain, vcp->vc_requests);
529 selwakeuppri(&(vcp->vc_selproc), coda_call_sleep);
530
531 /* We can be interrupted while we wait for Venus to process
532 * our request. If the interrupt occurs before Venus has read
533 * the request, we dequeue and return. If it occurs after the
534 * read but before the reply, we dequeue, send a signal
535 * message, and return. If it occurs after the reply we ignore
536 * it. In no case do we want to restart the syscall. If it
537 * was interrupted by a venus shutdown (vcclose), return
538 * ENODEV. */
539
540 /* Ignore return, We have to check anyway */
541#ifdef CTL_C
542 /* This is work in progress. Setting coda_pcatch lets tsleep reawaken
543 on a ^c or ^z. The problem is that emacs sets certain interrupts
544 as SA_RESTART. This means that we should exit sleep handle the
545 "signal" and then go to sleep again. Mostly this is done by letting
546 the syscall complete and be restarted. We are not idempotent and
547 can not do this. A better solution is necessary.
548 */
549 i = 0;
550 PROC_LOCK(p);
551 psig_omask = td->td_sigmask;
552 do {
553 error = msleep(&vmp->vm_sleep, &p->p_mtx,
554 (coda_call_sleep|coda_pcatch), "coda_call",
555 hz*2);
556 if (error == 0)
557 break;
558 else if (error == EWOULDBLOCK) {
559#ifdef CODA_VERBOSE
560 printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
561#endif
562 }
563 else {
564 SIGEMPTYSET(tempset);
565 SIGADDSET(tempset, SIGIO);
566 if (SIGSETEQ(td->td_siglist, tempset)) {
567 SIGADDSET(td->td_sigmask, SIGIO);
568#ifdef CODA_VERBOSE
569 printf("coda_call: tsleep returns %d SIGIO, cnt %d\n",
570 error, i);
571#endif
572 } else {
573 SIGDELSET(tempset, SIGIO);
574 SIGADDSET(tempset, SIGALRM);
575 if (SIGSETEQ(td->td_siglist, tempset)) {
576 SIGADDSET(td->td_sigmask, SIGALRM);
577#ifdef CODA_VERBOSE
578 printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n",
579 error, i);
580#endif
581 }
582 else {
583#ifdef CODA_VERBOSE
584 printf("coda_call: tsleep returns %d, cnt %d\n",
585 error, i);
586#endif
587
588#if notyet
589 tempset = td->td_siglist;
590 SIGSETNAND(tempset, td->td_sigmask);
591 printf("coda_call: siglist = %p, sigmask = %p, mask %p\n",
592 td->td_siglist, td->td_sigmask,
593 tempset);
594 break;
595 SIGSETOR(td->td_sigmask, td->td_siglist);
596 tempset = td->td_siglist;
597 SIGSETNAND(tempset, td->td_sigmask);
598 printf("coda_call: new mask, siglist = %p, sigmask = %p, mask %p\n",
599 td->td_siglist, td->td_sigmask,
600 tempset);
601#endif
602 }
603 }
604 }
605 } while (error && i++ < 128 && VC_OPEN(vcp));
606 td->td_sigmask = psig_omask;
607 signotify(td);
608 PROC_UNLOCK(p);
609#else
610 (void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
611#endif
612 if (VC_OPEN(vcp)) { /* Venus is still alive */
613 /* Op went through, interrupt or not... */
614 if (vmp->vm_flags & VM_WRITE) {
615 error = 0;
616 *outSize = vmp->vm_outSize;
617 }
618
619 else if (!(vmp->vm_flags & VM_READ)) {
620 /* Interrupted before venus read it. */
621#ifdef CODA_VERBOSE
622 if (1)
623#else
624 if (codadebug)
625#endif
626 myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
627 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
628 REMQUE(vmp->vm_chain);
629 error = EINTR;
630 }
631
632 else {
633 /* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
634 upcall started */
635 /* Interrupted after start of upcall, send venus a signal */
636 struct coda_in_hdr *dog;
637 struct vmsg *svmp;
638
639#ifdef CODA_VERBOSE
640 if (1)
641#else
642 if (codadebug)
643#endif
644 myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
645 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
646
647 REMQUE(vmp->vm_chain);
648 error = EINTR;
649
650 CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
651
652 CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
653 dog = (struct coda_in_hdr *)svmp->vm_data;
654
655 svmp->vm_flags = 0;
656 dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
657 dog->unique = svmp->vm_unique = vmp->vm_unique;
658 svmp->vm_inSize = sizeof (struct coda_in_hdr);
659/*??? rvb */ svmp->vm_outSize = sizeof (struct coda_in_hdr);
660
661 if (codadebug)
662 myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
663 svmp->vm_opcode, svmp->vm_unique));
664
665 /* insert at head of queue! */
666 INSQUE(svmp->vm_chain, vcp->vc_requests);
667 selwakeuppri(&(vcp->vc_selproc), coda_call_sleep);
668 }
669 }
670
671 else { /* If venus died (!VC_OPEN(vcp)) */
672 if (codadebug)
673 myprintf(("vcclose woke op %d.%d flags %d\n",
674 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
675
676 error = ENODEV;
677 }
678
679 CODA_FREE(vmp, sizeof(struct vmsg));
680
681 if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
682 wakeup(&outstanding_upcalls);
683
684 if (!error)
685 error = ((struct coda_out_hdr *)buffer)->result;
686 return(error);
687}