3 * Coda: an Experimental Distributed File System
6 * Copyright (c) 1987-1998 Carnegie Mellon University
9 * Permission to use, copy, modify and distribute this software and its
10 * documentation is hereby granted, provided that both the copyright
11 * notice and this permission notice appear in all copies of the
12 * software, derivative works or modified versions, and any portions
13 * thereof, and that both notices appear in supporting documentation, and
14 * that credit is given to Carnegie Mellon University in all documents
15 * and publicity pertaining to direct or indirect use of this code or its
18 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS,
19 * SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS
20 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON
21 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
22 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF
23 * ANY DERIVATIVE WORK.
25 * Carnegie Mellon encourages users of this software to return any
26 * improvements or extensions that they make, and to grant Carnegie
27 * Mellon the rights to redistribute these changes without encumbrance.
29 * @(#) src/sys/coda/coda_psdev.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
35 * Mach Operating System
36 * Copyright (c) 1989 Carnegie-Mellon University
37 * All rights reserved. The CMU software License Agreement specifies
38 * the terms and conditions for use and redistribution.
42 * This code was written for the Coda file system at Carnegie Mellon
43 * University. Contributers include David Steere, James Kistler, and
44 * M. Satyanarayanan. */
47 * These routines define the psuedo device for communication between
48 * Coda's Venus and Minicache in Mach 2.6. They used to be in cfs_subr.c,
49 * but I moved them to make it easier to port the Minicache without
50 * porting coda. -- DCS 10/12/94
53 /* These routines are the device entry points for Venus. */
55 extern int coda_nc_initialized; /* Set if cache has been initialized */
59 #include <sys/param.h>
60 #include <sys/systm.h>
61 #include <sys/ioccom.h>
62 #include <sys/kernel.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>
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>
80 #include <sys/signalvar.h>
83 int coda_psdev_print_entry = 0;
85 int outstanding_upcalls = 0;
86 int coda_call_sleep = PZERO - 1;
88 int coda_pcatch = PCATCH;
92 #define ENTRY if(coda_psdev_print_entry) myprintf(("Entered %s\n",__func__))
94 void vcodaattach(int n);
97 struct queue vm_chain;
100 u_short vm_inSize; /* Size is at most 5000 bytes */
102 u_short vm_opcode; /* copied from data to save ptr lookup */
104 caddr_t vm_sleep; /* Not used by Mach. */
111 /* vcodaattach: do nothing */
119 vc_nb_open(dev, flag, mode, td)
123 struct thread *td; /* NetBSD only */
125 register struct vcomm *vcp;
129 if (minor(dev) >= NVCODA || minor(dev) < 0)
132 if (!coda_nc_initialized)
135 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
139 bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
140 INIT_QUEUE(vcp->vc_requests);
141 INIT_QUEUE(vcp->vc_replys);
144 coda_mnttbl[minor(dev)].mi_vfsp = NULL;
145 coda_mnttbl[minor(dev)].mi_rootvp = NULL;
151 vc_nb_close (dev, flag, mode, td)
157 register struct vcomm *vcp;
158 register struct vmsg *vmp, *nvmp = NULL;
159 struct coda_mntinfo *mi;
164 if (minor(dev) >= NVCODA || minor(dev) < 0)
167 mi = &coda_mnttbl[minor(dev)];
168 vcp = &(mi->mi_vcomm);
171 panic("vcclose: not open");
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)
179 if (!mi->mi_rootvp) {
180 /* just a simple open/close w no mount */
185 /* Let unmount know this is for real */
186 VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
187 coda_unmounting(mi->mi_vfsp);
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);
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));
203 outstanding_upcalls++;
204 wakeup(&vmp->vm_sleep);
207 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
208 !EOQ(vmp, vcp->vc_replys);
209 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
211 outstanding_upcalls++;
212 wakeup(&vmp->vm_sleep);
217 if (outstanding_upcalls) {
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);
223 (void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
227 err = dounmount(mi->mi_vfsp, flag, td);
229 myprintf(("Error %d unmounting vfs in vcclose(%d)\n",
235 vc_nb_read(dev, uiop, flag)
240 register struct vcomm * vcp;
241 register struct vmsg *vmp;
246 if (minor(dev) >= NVCODA || minor(dev) < 0)
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 */
254 vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
256 /* Move the input args into userspace */
257 uiop->uio_rw = UIO_READ;
258 error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
260 myprintf(("vcread: error (%d) on uiomove\n", error));
264 #ifdef OLD_DIAGNOSTIC
265 if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
266 panic("vc_nb_read: bad chain");
269 REMQUE(vmp->vm_chain);
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) {
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));
282 vmp->vm_flags |= VM_READ;
283 INSQUE(vmp->vm_chain, vcp->vc_replys);
289 vc_nb_write(dev, uiop, flag)
294 register struct vcomm * vcp;
295 register struct vmsg *vmp;
296 struct coda_out_hdr *out;
304 if (minor(dev) >= NVCODA || minor(dev) < 0)
307 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
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);
313 myprintf(("vcwrite: error (%d) on uiomove\n", error));
321 myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
323 if (DOWNCALL(opcode)) {
324 union outputArgs pbuf;
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);
330 myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
331 error, opcode, seq));
335 return handleDownCall(opcode, &pbuf);
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))
343 if (vmp->vm_unique == seq) break;
346 if (EOQ(vmp, vcp->vc_replys)) {
348 myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
353 /* Remove the message from the reply queue */
354 REMQUE(vmp->vm_chain);
356 /* move data into response buffer. */
357 out = (struct coda_out_hdr *)vmp->vm_data;
358 /* Don't need to copy opcode and uniquifier. */
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. */
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);
372 myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
373 error, opcode, seq));
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;
381 vmp->vm_outSize = buf[0]; /* Amount of data transferred? */
382 vmp->vm_flags |= VM_WRITE;
383 wakeup(&vmp->vm_sleep);
389 vc_nb_ioctl(dev, cmd, addr, flag, td)
400 struct coda_resize *data = (struct coda_resize *)addr;
401 return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
406 coda_nc_gather_stats();
420 case CIOC_KERNEL_VERSION:
421 switch (*(u_int *)addr) {
423 *(u_int *)addr = coda_kernel_version;
428 if (coda_kernel_version != *(u_int *)addr)
443 vc_nb_poll(dev, events, td)
448 register struct vcomm *vcp;
453 if (minor(dev) >= NVCODA || minor(dev) < 0)
456 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
458 event_msk = events & (POLLIN|POLLRDNORM);
462 if (!EMPTY(vcp->vc_requests))
463 return(events & (POLLIN|POLLRDNORM));
465 selrecord(td, &(vcp->vc_selproc));
473 struct coda_clstat coda_clstat;
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
485 coda_call(mntinfo, inSize, outSize, buffer)
486 struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
492 struct thread *td = curthread;
493 struct proc *p = td->td_proc;
498 if (mntinfo == NULL) {
499 /* Unlikely, but could be a race condition with a dying warden */
503 vcp = &(mntinfo->mi_vcomm);
505 coda_clstat.ncalls++;
506 coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
511 CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
512 /* Format the request message. */
513 vmp->vm_data = buffer;
515 vmp->vm_inSize = inSize;
517 = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
518 vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
519 vmp->vm_unique = ++vcp->vc_seq;
521 myprintf(("Doing a call for %d.%d\n",
522 vmp->vm_opcode, vmp->vm_unique));
524 /* Fill in the common input args. */
525 ((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
527 /* Append msg to request queue and poke Venus. */
528 INSQUE(vmp->vm_chain, vcp->vc_requests);
529 selwakeup(&(vcp->vc_selproc));
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
540 /* Ignore return, We have to check anyway */
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.
551 psig_omask = p->p_sigmask;
553 error = msleep(&vmp->vm_sleep, &p->p_mtx,
554 (coda_call_sleep|coda_pcatch), "coda_call",
558 else if (error == EWOULDBLOCK) {
560 printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
564 SIGEMPTYSET(tempset);
565 SIGADDSET(tempset, SIGIO);
566 if (SIGSETEQ(p->p_siglist, tempset)) {
567 SIGADDSET(p->p_sigmask, SIGIO);
569 printf("coda_call: tsleep returns %d SIGIO, cnt %d\n",
573 SIGDELSET(tempset, SIGIO);
574 SIGADDSET(tempset, SIGALRM);
575 if (SIGSETEQ(p->p_siglist, tempset)) {
576 SIGADDSET(p->p_sigmask, SIGALRM);
578 printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n",
583 printf("coda_call: tsleep returns %d, cnt %d\n",
587 tempset = p->p_siglist;
588 SIGSETNAND(tempset, p->p_sigmask);
589 printf("coda_call: siglist = %p, sigmask = %p, mask %p\n",
590 p->p_siglist, p->p_sigmask,
593 SIGSETOR(p->p_sigmask, p->p_siglist);
594 tempset = p->p_siglist;
595 SIGSETNAND(tempset, p->p_sigmask);
596 printf("coda_call: new mask, siglist = %p, sigmask = %p, mask %p\n",
597 p->p_siglist, p->p_sigmask,
603 } while (error && i++ < 128 && VC_OPEN(vcp));
604 p->p_sigmask = psig_omask;
608 (void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
610 if (VC_OPEN(vcp)) { /* Venus is still alive */
611 /* Op went through, interrupt or not... */
612 if (vmp->vm_flags & VM_WRITE) {
614 *outSize = vmp->vm_outSize;
617 else if (!(vmp->vm_flags & VM_READ)) {
618 /* Interrupted before venus read it. */
624 myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
625 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
626 REMQUE(vmp->vm_chain);
631 /* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
633 /* Interrupted after start of upcall, send venus a signal */
634 struct coda_in_hdr *dog;
642 myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
643 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
645 REMQUE(vmp->vm_chain);
648 CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
650 CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
651 dog = (struct coda_in_hdr *)svmp->vm_data;
654 dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
655 dog->unique = svmp->vm_unique = vmp->vm_unique;
656 svmp->vm_inSize = sizeof (struct coda_in_hdr);
657 /*??? rvb */ svmp->vm_outSize = sizeof (struct coda_in_hdr);
660 myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
661 svmp->vm_opcode, svmp->vm_unique));
663 /* insert at head of queue! */
664 INSQUE(svmp->vm_chain, vcp->vc_requests);
665 selwakeup(&(vcp->vc_selproc));
669 else { /* If venus died (!VC_OPEN(vcp)) */
671 myprintf(("vcclose woke op %d.%d flags %d\n",
672 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
677 CODA_FREE(vmp, sizeof(struct vmsg));
679 if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
680 wakeup(&outstanding_upcalls);
683 error = ((struct coda_out_hdr *)buffer)->result;