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