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