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 $
30 * $FreeBSD: src/sys/coda/coda_psdev.c,v 1.13 1999/09/29 15:03:46 marcel Exp $
31 * $DragonFly: src/sys/vfs/coda/Attic/coda_psdev.c,v 1.7 2003/08/07 21:17:40 dillon Exp $
36 * Mach Operating System
37 * Copyright (c) 1989 Carnegie-Mellon University
38 * All rights reserved. The CMU software License Agreement specifies
39 * the terms and conditions for use and redistribution.
43 * This code was written for the Coda file system at Carnegie Mellon
44 * University. Contributers include David Steere, James Kistler, and
45 * M. Satyanarayanan. */
48 * These routines define the psuedo device for communication between
49 * Coda's Venus and Minicache in Mach 2.6. They used to be in cfs_subr.c,
50 * but I moved them to make it easier to port the Minicache without
51 * porting coda. -- DCS 10/12/94
54 /* These routines are the device entry points for Venus. */
56 extern int coda_nc_initialized; /* Set if cache has been initialized */
58 #include "use_vcoda.h"
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/kernel.h>
64 #include <sys/malloc.h>
65 #include <sys/mount.h>
67 #include <sys/ioccom.h>
73 #include "coda_namecache.h"
75 #include "coda_psdev.h"
80 #include <sys/signalvar.h>
83 int coda_psdev_print_entry = 0;
85 int outstanding_upcalls = 0;
87 int coda_pcatch = PCATCH;
91 #define ENTRY if(coda_psdev_print_entry) myprintf(("Entered %s\n",__FUNCTION__))
93 void vcodaattach(int n);
96 struct queue vm_chain;
99 u_short vm_inSize; /* Size is at most 5000 bytes */
101 u_short vm_opcode; /* copied from data to save ptr lookup */
103 caddr_t vm_sleep; /* Not used by Mach. */
110 /* vcodaattach: do nothing */
118 vc_nb_open(dev_t dev, int flag, int mode, d_thread_t *td)
124 if (minor(dev) >= NVCODA || minor(dev) < 0)
127 if (!coda_nc_initialized)
130 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
134 bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
135 INIT_QUEUE(vcp->vc_requests);
136 INIT_QUEUE(vcp->vc_replys);
139 coda_mnttbl[minor(dev)].mi_vfsp = NULL;
140 coda_mnttbl[minor(dev)].mi_rootvp = NULL;
146 vc_nb_close (dev_t dev, int flag, int mode, d_thread_t *td)
149 struct vmsg *vmp, *nvmp = NULL;
150 struct coda_mntinfo *mi;
155 if (minor(dev) >= NVCODA || minor(dev) < 0)
158 mi = &coda_mnttbl[minor(dev)];
159 vcp = &(mi->mi_vcomm);
162 panic("vcclose: not open");
164 /* prevent future operations on this vfs from succeeding by auto-
165 * unmounting any vfs mounted via this device. This frees user or
166 * sysadm from having to remember where all mount points are located.
167 * Put this before WAKEUPs to avoid queuing new messages between
168 * the WAKEUP and the unmount (which can happen if we're unlucky)
170 if (!mi->mi_rootvp) {
171 /* just a simple open/close w no mount */
176 /* Let unmount know this is for real */
177 VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
178 coda_unmounting(mi->mi_vfsp);
180 outstanding_upcalls = 0;
181 /* Wakeup clients so they can return. */
182 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
183 !EOQ(vmp, vcp->vc_requests);
186 nvmp = (struct vmsg *)GETNEXT(vmp->vm_chain);
187 /* Free signal request messages and don't wakeup cause
188 no one is waiting. */
189 if (vmp->vm_opcode == CODA_SIGNAL) {
190 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
191 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
194 outstanding_upcalls++;
195 wakeup(&vmp->vm_sleep);
198 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
199 !EOQ(vmp, vcp->vc_replys);
200 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
202 outstanding_upcalls++;
203 wakeup(&vmp->vm_sleep);
208 if (outstanding_upcalls) {
210 printf("presleep: outstanding_upcalls = %d\n", outstanding_upcalls);
211 (void) tsleep(&outstanding_upcalls, 0, "coda_umount", 0);
212 printf("postsleep: outstanding_upcalls = %d\n", outstanding_upcalls);
214 (void) tsleep(&outstanding_upcalls, 0, "coda_umount", 0);
218 err = dounmount(mi->mi_vfsp, flag, td);
220 myprintf(("Error %d unmounting vfs in vcclose(%d)\n",
226 vc_nb_read(dev, uiop, flag)
237 if (minor(dev) >= NVCODA || minor(dev) < 0)
240 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
241 /* Get message at head of request queue. */
242 if (EMPTY(vcp->vc_requests))
243 return(0); /* Nothing to read */
245 vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
247 /* Move the input args into userspace */
248 uiop->uio_rw = UIO_READ;
249 error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
251 myprintf(("vcread: error (%d) on uiomove\n", error));
255 #ifdef OLD_DIAGNOSTIC
256 if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
257 panic("vc_nb_read: bad chain");
260 REMQUE(vmp->vm_chain);
262 /* If request was a signal, free up the message and don't
263 enqueue it in the reply queue. */
264 if (vmp->vm_opcode == CODA_SIGNAL) {
266 myprintf(("vcread: signal msg (%d, %d)\n",
267 vmp->vm_opcode, vmp->vm_unique));
268 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
269 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
273 vmp->vm_flags |= VM_READ;
274 INSQUE(vmp->vm_chain, vcp->vc_replys);
280 vc_nb_write(dev, uiop, flag)
287 struct coda_out_hdr *out;
295 if (minor(dev) >= NVCODA || minor(dev) < 0)
298 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
300 /* Peek at the opcode, unique without transfering the data. */
301 uiop->uio_rw = UIO_WRITE;
302 error = uiomove((caddr_t)buf, sizeof(int) * 2, uiop);
304 myprintf(("vcwrite: error (%d) on uiomove\n", error));
312 myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
314 if (DOWNCALL(opcode)) {
315 union outputArgs pbuf;
317 /* get the rest of the data. */
318 uiop->uio_rw = UIO_WRITE;
319 error = uiomove((caddr_t)&pbuf.coda_purgeuser.oh.result, sizeof(pbuf) - (sizeof(int)*2), uiop);
321 myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
322 error, opcode, seq));
326 return handleDownCall(opcode, &pbuf);
329 /* Look for the message on the (waiting for) reply queue. */
330 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
331 !EOQ(vmp, vcp->vc_replys);
332 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
334 if (vmp->vm_unique == seq) break;
337 if (EOQ(vmp, vcp->vc_replys)) {
339 myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
344 /* Remove the message from the reply queue */
345 REMQUE(vmp->vm_chain);
347 /* move data into response buffer. */
348 out = (struct coda_out_hdr *)vmp->vm_data;
349 /* Don't need to copy opcode and uniquifier. */
351 /* get the rest of the data. */
352 if (vmp->vm_outSize < uiop->uio_resid) {
353 myprintf(("vcwrite: more data than asked for (%d < %d)\n",
354 vmp->vm_outSize, uiop->uio_resid));
355 wakeup(&vmp->vm_sleep); /* Notify caller of the error. */
359 buf[0] = uiop->uio_resid; /* Save this value. */
360 uiop->uio_rw = UIO_WRITE;
361 error = uiomove((caddr_t) &out->result, vmp->vm_outSize - (sizeof(int) * 2), uiop);
363 myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
364 error, opcode, seq));
368 /* I don't think these are used, but just in case. */
369 /* XXX - aren't these two already correct? -bnoble */
370 out->opcode = opcode;
372 vmp->vm_outSize = buf[0]; /* Amount of data transferred? */
373 vmp->vm_flags |= VM_WRITE;
374 wakeup(&vmp->vm_sleep);
380 vc_nb_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, d_thread_t *td)
386 struct coda_resize *data = (struct coda_resize *)addr;
387 return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
392 coda_nc_gather_stats();
406 case CIOC_KERNEL_VERSION:
407 switch (*(u_int *)addr) {
409 *(u_int *)addr = coda_kernel_version;
414 if (coda_kernel_version != *(u_int *)addr)
429 vc_nb_poll(dev_t dev, int events, d_thread_t *td)
436 if (minor(dev) >= NVCODA || minor(dev) < 0)
439 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
441 event_msk = events & (POLLIN|POLLRDNORM);
445 if (!EMPTY(vcp->vc_requests))
446 return(events & (POLLIN|POLLRDNORM));
448 selrecord(td, &(vcp->vc_selproc));
456 struct coda_clstat coda_clstat;
459 * Key question: whether to sleep interuptably or uninteruptably when
460 * waiting for Venus. The former seems better (cause you can ^C a
461 * job), but then GNU-EMACS completion breaks. Use tsleep with no
462 * timeout, and no longjmp happens. But, when sleeping
463 * "uninterruptibly", we don't get told if it returns abnormally
468 coda_call(mntinfo, inSize, outSize, buffer)
469 struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
475 struct proc *p = curproc;
476 sigset_t psig_omask = p->p_sigmask;
480 if (mntinfo == NULL) {
481 /* Unlikely, but could be a race condition with a dying warden */
485 vcp = &(mntinfo->mi_vcomm);
487 coda_clstat.ncalls++;
488 coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
493 CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
494 /* Format the request message. */
495 vmp->vm_data = buffer;
497 vmp->vm_inSize = inSize;
499 = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
500 vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
501 vmp->vm_unique = ++vcp->vc_seq;
503 myprintf(("Doing a call for %d.%d\n",
504 vmp->vm_opcode, vmp->vm_unique));
506 /* Fill in the common input args. */
507 ((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
509 /* Append msg to request queue and poke Venus. */
510 INSQUE(vmp->vm_chain, vcp->vc_requests);
511 selwakeup(&(vcp->vc_selproc));
513 /* We can be interrupted while we wait for Venus to process
514 * our request. If the interrupt occurs before Venus has read
515 * the request, we dequeue and return. If it occurs after the
516 * read but before the reply, we dequeue, send a signal
517 * message, and return. If it occurs after the reply we ignore
518 * it. In no case do we want to restart the syscall. If it
519 * was interrupted by a venus shutdown (vcclose), return
522 /* Ignore return, We have to check anyway */
524 /* This is work in progress. Setting coda_pcatch lets tsleep reawaken
525 on a ^c or ^z. The problem is that emacs sets certain interrupts
526 as SA_RESTART. This means that we should exit sleep handle the
527 "signal" and then go to sleep again. Mostly this is done by letting
528 the syscall complete and be restarted. We are not idempotent and
529 can not do this. A better solution is necessary.
533 error = tsleep(&vmp->vm_sleep, coda_pcatch, "coda_call", hz*2);
536 else if (error == EWOULDBLOCK) {
538 printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
542 SIGEMPTYSET(tempset);
543 SIGADDSET(tempset, SIGIO);
544 if (SIGSETEQ(p->p_siglist, tempset)) {
545 SIGADDSET(p->p_sigmask, SIGIO);
547 printf("coda_call: tsleep returns %d SIGIO, cnt %d\n",
551 SIGDELSET(tempset, SIGIO);
552 SIGADDSET(tempset, SIGALRM);
553 if (SIGSETEQ(p->p_siglist, tempset)) {
554 SIGADDSET(p->p_sigmask, SIGALRM);
556 printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n",
561 printf("coda_call: tsleep returns %d, cnt %d\n",
565 tempset = p->p_siglist;
566 SIGSETNAND(tempset, p->p_sigmask);
567 printf("coda_call: siglist = %p, sigmask = %p, mask %p\n",
568 p->p_siglist, p->p_sigmask,
571 SIGSETOR(p->p_sigmask, p->p_siglist);
572 tempset = p->p_siglist;
573 SIGSETNAND(tempset, p->p_sigmask);
574 printf("coda_call: new mask, siglist = %p, sigmask = %p, mask %p\n",
575 p->p_siglist, p->p_sigmask,
581 } while (error && i++ < 128 && VC_OPEN(vcp));
582 p->p_sigmask = psig_omask;
584 (void) tsleep(&vmp->vm_sleep, 0, "coda_call", 0);
586 if (VC_OPEN(vcp)) { /* Venus is still alive */
587 /* Op went through, interrupt or not... */
588 if (vmp->vm_flags & VM_WRITE) {
590 *outSize = vmp->vm_outSize;
593 else if (!(vmp->vm_flags & VM_READ)) {
594 /* Interrupted before venus read it. */
600 myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
601 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
602 REMQUE(vmp->vm_chain);
607 /* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
609 /* Interrupted after start of upcall, send venus a signal */
610 struct coda_in_hdr *dog;
618 myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
619 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
621 REMQUE(vmp->vm_chain);
624 CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
626 CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
627 dog = (struct coda_in_hdr *)svmp->vm_data;
630 dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
631 dog->unique = svmp->vm_unique = vmp->vm_unique;
632 svmp->vm_inSize = sizeof (struct coda_in_hdr);
633 /*??? rvb */ svmp->vm_outSize = sizeof (struct coda_in_hdr);
636 myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
637 svmp->vm_opcode, svmp->vm_unique));
639 /* insert at head of queue! */
640 INSQUE(svmp->vm_chain, vcp->vc_requests);
641 selwakeup(&(vcp->vc_selproc));
645 else { /* If venus died (!VC_OPEN(vcp)) */
647 myprintf(("vcclose woke op %d.%d flags %d\n",
648 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
653 CODA_FREE(vmp, sizeof(struct vmsg));
655 if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
656 wakeup(&outstanding_upcalls);
659 error = ((struct coda_out_hdr *)buffer)->result;