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 $
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/kernel.h>
63 #include <sys/malloc.h>
64 #include <sys/mount.h>
66 #include <sys/ioccom.h>
70 #include <coda/coda.h>
71 #include <coda/cnode.h>
72 #include <coda/coda_namecache.h>
73 #include <coda/coda_io.h>
74 #include <coda/coda_psdev.h>
79 #include <sys/signalvar.h>
82 int coda_psdev_print_entry = 0;
84 int outstanding_upcalls = 0;
85 int coda_call_sleep = PZERO - 1;
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, flag, mode, p)
122 struct proc *p; /* NetBSD only */
124 register struct vcomm *vcp;
128 if (minor(dev) >= NVCODA || minor(dev) < 0)
131 if (!coda_nc_initialized)
134 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
138 bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
139 INIT_QUEUE(vcp->vc_requests);
140 INIT_QUEUE(vcp->vc_replys);
143 coda_mnttbl[minor(dev)].mi_vfsp = NULL;
144 coda_mnttbl[minor(dev)].mi_rootvp = NULL;
150 vc_nb_close (dev, flag, mode, p)
156 register struct vcomm *vcp;
157 register struct vmsg *vmp, *nvmp = NULL;
158 struct coda_mntinfo *mi;
163 if (minor(dev) >= NVCODA || minor(dev) < 0)
166 mi = &coda_mnttbl[minor(dev)];
167 vcp = &(mi->mi_vcomm);
170 panic("vcclose: not open");
172 /* prevent future operations on this vfs from succeeding by auto-
173 * unmounting any vfs mounted via this device. This frees user or
174 * sysadm from having to remember where all mount points are located.
175 * Put this before WAKEUPs to avoid queuing new messages between
176 * the WAKEUP and the unmount (which can happen if we're unlucky)
178 if (!mi->mi_rootvp) {
179 /* just a simple open/close w no mount */
184 /* Let unmount know this is for real */
185 VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
186 coda_unmounting(mi->mi_vfsp);
188 outstanding_upcalls = 0;
189 /* Wakeup clients so they can return. */
190 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
191 !EOQ(vmp, vcp->vc_requests);
194 nvmp = (struct vmsg *)GETNEXT(vmp->vm_chain);
195 /* Free signal request messages and don't wakeup cause
196 no one is waiting. */
197 if (vmp->vm_opcode == CODA_SIGNAL) {
198 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
199 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
202 outstanding_upcalls++;
203 wakeup(&vmp->vm_sleep);
206 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
207 !EOQ(vmp, vcp->vc_replys);
208 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
210 outstanding_upcalls++;
211 wakeup(&vmp->vm_sleep);
216 if (outstanding_upcalls) {
218 printf("presleep: outstanding_upcalls = %d\n", outstanding_upcalls);
219 (void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
220 printf("postsleep: outstanding_upcalls = %d\n", outstanding_upcalls);
222 (void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
226 err = dounmount(mi->mi_vfsp, flag, p);
228 myprintf(("Error %d unmounting vfs in vcclose(%d)\n",
234 vc_nb_read(dev, uiop, flag)
239 register struct vcomm * vcp;
240 register struct vmsg *vmp;
245 if (minor(dev) >= NVCODA || minor(dev) < 0)
248 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
249 /* Get message at head of request queue. */
250 if (EMPTY(vcp->vc_requests))
251 return(0); /* Nothing to read */
253 vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
255 /* Move the input args into userspace */
256 uiop->uio_rw = UIO_READ;
257 error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
259 myprintf(("vcread: error (%d) on uiomove\n", error));
263 #ifdef OLD_DIAGNOSTIC
264 if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
265 panic("vc_nb_read: bad chain");
268 REMQUE(vmp->vm_chain);
270 /* If request was a signal, free up the message and don't
271 enqueue it in the reply queue. */
272 if (vmp->vm_opcode == CODA_SIGNAL) {
274 myprintf(("vcread: signal msg (%d, %d)\n",
275 vmp->vm_opcode, vmp->vm_unique));
276 CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
277 CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
281 vmp->vm_flags |= VM_READ;
282 INSQUE(vmp->vm_chain, vcp->vc_replys);
288 vc_nb_write(dev, uiop, flag)
293 register struct vcomm * vcp;
294 register struct vmsg *vmp;
295 struct coda_out_hdr *out;
303 if (minor(dev) >= NVCODA || minor(dev) < 0)
306 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
308 /* Peek at the opcode, unique without transfering the data. */
309 uiop->uio_rw = UIO_WRITE;
310 error = uiomove((caddr_t)buf, sizeof(int) * 2, uiop);
312 myprintf(("vcwrite: error (%d) on uiomove\n", error));
320 myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
322 if (DOWNCALL(opcode)) {
323 union outputArgs pbuf;
325 /* get the rest of the data. */
326 uiop->uio_rw = UIO_WRITE;
327 error = uiomove((caddr_t)&pbuf.coda_purgeuser.oh.result, sizeof(pbuf) - (sizeof(int)*2), uiop);
329 myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
330 error, opcode, seq));
334 return handleDownCall(opcode, &pbuf);
337 /* Look for the message on the (waiting for) reply queue. */
338 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
339 !EOQ(vmp, vcp->vc_replys);
340 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
342 if (vmp->vm_unique == seq) break;
345 if (EOQ(vmp, vcp->vc_replys)) {
347 myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
352 /* Remove the message from the reply queue */
353 REMQUE(vmp->vm_chain);
355 /* move data into response buffer. */
356 out = (struct coda_out_hdr *)vmp->vm_data;
357 /* Don't need to copy opcode and uniquifier. */
359 /* get the rest of the data. */
360 if (vmp->vm_outSize < uiop->uio_resid) {
361 myprintf(("vcwrite: more data than asked for (%d < %d)\n",
362 vmp->vm_outSize, uiop->uio_resid));
363 wakeup(&vmp->vm_sleep); /* Notify caller of the error. */
367 buf[0] = uiop->uio_resid; /* Save this value. */
368 uiop->uio_rw = UIO_WRITE;
369 error = uiomove((caddr_t) &out->result, vmp->vm_outSize - (sizeof(int) * 2), uiop);
371 myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
372 error, opcode, seq));
376 /* I don't think these are used, but just in case. */
377 /* XXX - aren't these two already correct? -bnoble */
378 out->opcode = opcode;
380 vmp->vm_outSize = buf[0]; /* Amount of data transferred? */
381 vmp->vm_flags |= VM_WRITE;
382 wakeup(&vmp->vm_sleep);
388 vc_nb_ioctl(dev, cmd, addr, flag, p)
399 struct coda_resize *data = (struct coda_resize *)addr;
400 return(coda_nc_resize(data->hashsize, data->heapsize, IS_DOWNCALL));
405 coda_nc_gather_stats();
419 case CIOC_KERNEL_VERSION:
420 switch (*(u_int *)addr) {
422 *(u_int *)addr = coda_kernel_version;
427 if (coda_kernel_version != *(u_int *)addr)
442 vc_nb_poll(dev, events, p)
447 register struct vcomm *vcp;
452 if (minor(dev) >= NVCODA || minor(dev) < 0)
455 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
457 event_msk = events & (POLLIN|POLLRDNORM);
461 if (!EMPTY(vcp->vc_requests))
462 return(events & (POLLIN|POLLRDNORM));
464 selrecord(p, &(vcp->vc_selproc));
472 struct coda_clstat coda_clstat;
475 * Key question: whether to sleep interuptably or uninteruptably when
476 * waiting for Venus. The former seems better (cause you can ^C a
477 * job), but then GNU-EMACS completion breaks. Use tsleep with no
478 * timeout, and no longjmp happens. But, when sleeping
479 * "uninterruptibly", we don't get told if it returns abnormally
484 coda_call(mntinfo, inSize, outSize, buffer)
485 struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
491 struct proc *p = curproc;
492 sigset_t psig_omask = p->p_sigmask;
496 if (mntinfo == NULL) {
497 /* Unlikely, but could be a race condition with a dying warden */
501 vcp = &(mntinfo->mi_vcomm);
503 coda_clstat.ncalls++;
504 coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
509 CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
510 /* Format the request message. */
511 vmp->vm_data = buffer;
513 vmp->vm_inSize = inSize;
515 = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
516 vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
517 vmp->vm_unique = ++vcp->vc_seq;
519 myprintf(("Doing a call for %d.%d\n",
520 vmp->vm_opcode, vmp->vm_unique));
522 /* Fill in the common input args. */
523 ((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
525 /* Append msg to request queue and poke Venus. */
526 INSQUE(vmp->vm_chain, vcp->vc_requests);
527 selwakeup(&(vcp->vc_selproc));
529 /* We can be interrupted while we wait for Venus to process
530 * our request. If the interrupt occurs before Venus has read
531 * the request, we dequeue and return. If it occurs after the
532 * read but before the reply, we dequeue, send a signal
533 * message, and return. If it occurs after the reply we ignore
534 * it. In no case do we want to restart the syscall. If it
535 * was interrupted by a venus shutdown (vcclose), return
538 /* Ignore return, We have to check anyway */
540 /* This is work in progress. Setting coda_pcatch lets tsleep reawaken
541 on a ^c or ^z. The problem is that emacs sets certain interrupts
542 as SA_RESTART. This means that we should exit sleep handle the
543 "signal" and then go to sleep again. Mostly this is done by letting
544 the syscall complete and be restarted. We are not idempotent and
545 can not do this. A better solution is necessary.
549 error = tsleep(&vmp->vm_sleep,
550 (coda_call_sleep|coda_pcatch), "coda_call",
554 else if (error == EWOULDBLOCK) {
556 printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
560 SIGEMPTYSET(tempset);
561 SIGADDSET(tempset, SIGIO);
562 if (SIGSETEQ(p->p_siglist, tempset)) {
563 SIGADDSET(p->p_sigmask, SIGIO);
565 printf("coda_call: tsleep returns %d SIGIO, cnt %d\n",
569 SIGDELSET(tempset, SIGIO);
570 SIGADDSET(tempset, SIGALRM);
571 if (SIGSETEQ(p->p_siglist, tempset)) {
572 SIGADDSET(p->p_sigmask, SIGALRM);
574 printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n",
579 printf("coda_call: tsleep returns %d, cnt %d\n",
583 tempset = p->p_siglist;
584 SIGSETNAND(tempset, p->p_sigmask);
585 printf("coda_call: siglist = %p, sigmask = %p, mask %p\n",
586 p->p_siglist, p->p_sigmask,
589 SIGSETOR(p->p_sigmask, p->p_siglist);
590 tempset = p->p_siglist;
591 SIGSETNAND(tempset, p->p_sigmask);
592 printf("coda_call: new mask, siglist = %p, sigmask = %p, mask %p\n",
593 p->p_siglist, p->p_sigmask,
599 } while (error && i++ < 128 && VC_OPEN(vcp));
600 p->p_sigmask = psig_omask;
602 (void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
604 if (VC_OPEN(vcp)) { /* Venus is still alive */
605 /* Op went through, interrupt or not... */
606 if (vmp->vm_flags & VM_WRITE) {
608 *outSize = vmp->vm_outSize;
611 else if (!(vmp->vm_flags & VM_READ)) {
612 /* Interrupted before venus read it. */
618 myprintf(("interrupted before read: op = %d.%d, flags = %x\n",
619 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
620 REMQUE(vmp->vm_chain);
625 /* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
627 /* Interrupted after start of upcall, send venus a signal */
628 struct coda_in_hdr *dog;
636 myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
637 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
639 REMQUE(vmp->vm_chain);
642 CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
644 CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
645 dog = (struct coda_in_hdr *)svmp->vm_data;
648 dog->opcode = svmp->vm_opcode = CODA_SIGNAL;
649 dog->unique = svmp->vm_unique = vmp->vm_unique;
650 svmp->vm_inSize = sizeof (struct coda_in_hdr);
651 /*??? rvb */ svmp->vm_outSize = sizeof (struct coda_in_hdr);
654 myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
655 svmp->vm_opcode, svmp->vm_unique));
657 /* insert at head of queue! */
658 INSQUE(svmp->vm_chain, vcp->vc_requests);
659 selwakeup(&(vcp->vc_selproc));
663 else { /* If venus died (!VC_OPEN(vcp)) */
665 myprintf(("vcclose woke op %d.%d flags %d\n",
666 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
671 CODA_FREE(vmp, sizeof(struct vmsg));
673 if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
674 wakeup(&outstanding_upcalls);
677 error = ((struct coda_out_hdr *)buffer)->result;