FreeBSD/Linux Kernel Cross Reference
sys/coda/coda_psdev.c
1 /*
2 *
3 * Coda: an Experimental Distributed File System
4 * Release 3.1
5 *
6 * Copyright (c) 1987-1998 Carnegie Mellon University
7 * All Rights Reserved
8 *
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
16 * derivatives.
17 *
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.
24 *
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.
28 *
29 * @(#) src/sys/coda/coda_psdev.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
30 * $FreeBSD$
31 *
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 /*
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
51 */
52
53 /* These routines are the device entry points for Venus. */
54
55 extern int coda_nc_initialized; /* Set if cache has been initialized */
56
57 #include <vcoda.h>
58
59 #include <sys/param.h>
60 #include <sys/systm.h>
61 #include <sys/kernel.h>
62 #include <sys/proc.h>
63 #include <sys/malloc.h>
64 #include <sys/mount.h>
65 #include <sys/file.h>
66 #include <sys/ioccom.h>
67 #include <sys/poll.h>
68 #include <sys/conf.h>
69
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>
75
76 #define CTL_C
77
78 #ifdef CTL_C
79 #include <sys/signalvar.h>
80 #endif
81
82 int coda_psdev_print_entry = 0;
83 static
84 int outstanding_upcalls = 0;
85 int coda_call_sleep = PZERO - 1;
86 #ifdef CTL_C
87 int coda_pcatch = PCATCH;
88 #else
89 #endif
90
91 #define ENTRY if(coda_psdev_print_entry) myprintf(("Entered %s\n",__FUNCTION__))
92
93 void vcodaattach(int n);
94
95 struct vmsg {
96 struct queue vm_chain;
97 caddr_t vm_data;
98 u_short vm_flags;
99 u_short vm_inSize; /* Size is at most 5000 bytes */
100 u_short vm_outSize;
101 u_short vm_opcode; /* copied from data to save ptr lookup */
102 int vm_unique;
103 caddr_t vm_sleep; /* Not used by Mach. */
104 };
105
106 #define VM_READ 1
107 #define VM_WRITE 2
108 #define VM_INTR 4
109
110 /* vcodaattach: do nothing */
111 void
112 vcodaattach(n)
113 int n;
114 {
115 }
116
117 int
118 vc_nb_open(dev, flag, mode, p)
119 dev_t dev;
120 int flag;
121 int mode;
122 struct proc *p; /* NetBSD only */
123 {
124 register struct vcomm *vcp;
125
126 ENTRY;
127
128 if (minor(dev) >= NVCODA || minor(dev) < 0)
129 return(ENXIO);
130
131 if (!coda_nc_initialized)
132 coda_nc_init();
133
134 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
135 if (VC_OPEN(vcp))
136 return(EBUSY);
137
138 bzero(&(vcp->vc_selproc), sizeof (struct selinfo));
139 INIT_QUEUE(vcp->vc_requests);
140 INIT_QUEUE(vcp->vc_replys);
141 MARK_VC_OPEN(vcp);
142
143 coda_mnttbl[minor(dev)].mi_vfsp = NULL;
144 coda_mnttbl[minor(dev)].mi_rootvp = NULL;
145
146 return(0);
147 }
148
149 int
150 vc_nb_close (dev, flag, mode, p)
151 dev_t dev;
152 int flag;
153 int mode;
154 struct proc *p;
155 {
156 register struct vcomm *vcp;
157 register struct vmsg *vmp, *nvmp = NULL;
158 struct coda_mntinfo *mi;
159 int err;
160
161 ENTRY;
162
163 if (minor(dev) >= NVCODA || minor(dev) < 0)
164 return(ENXIO);
165
166 mi = &coda_mnttbl[minor(dev)];
167 vcp = &(mi->mi_vcomm);
168
169 if (!VC_OPEN(vcp))
170 panic("vcclose: not open");
171
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)
177 */
178 if (!mi->mi_rootvp) {
179 /* just a simple open/close w no mount */
180 MARK_VC_CLOSED(vcp);
181 return 0;
182 }
183
184 /* Let unmount know this is for real */
185 VTOC(mi->mi_rootvp)->c_flags |= C_UNMOUNTING;
186 coda_unmounting(mi->mi_vfsp);
187
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);
192 vmp = nvmp)
193 {
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));
200 continue;
201 }
202 outstanding_upcalls++;
203 wakeup(&vmp->vm_sleep);
204 }
205
206 for (vmp = (struct vmsg *)GETNEXT(vcp->vc_replys);
207 !EOQ(vmp, vcp->vc_replys);
208 vmp = (struct vmsg *)GETNEXT(vmp->vm_chain))
209 {
210 outstanding_upcalls++;
211 wakeup(&vmp->vm_sleep);
212 }
213
214 MARK_VC_CLOSED(vcp);
215
216 if (outstanding_upcalls) {
217 #ifdef CODA_VERBOSE
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);
221 #else
222 (void) tsleep(&outstanding_upcalls, coda_call_sleep, "coda_umount", 0);
223 #endif
224 }
225
226 err = dounmount(mi->mi_vfsp, flag, p);
227 if (err)
228 myprintf(("Error %d unmounting vfs in vcclose(%d)\n",
229 err, minor(dev)));
230 return 0;
231 }
232
233 int
234 vc_nb_read(dev, uiop, flag)
235 dev_t dev;
236 struct uio *uiop;
237 int flag;
238 {
239 register struct vcomm * vcp;
240 register struct vmsg *vmp;
241 int error = 0;
242
243 ENTRY;
244
245 if (minor(dev) >= NVCODA || minor(dev) < 0)
246 return(ENXIO);
247
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 */
252
253 vmp = (struct vmsg *)GETNEXT(vcp->vc_requests);
254
255 /* Move the input args into userspace */
256 uiop->uio_rw = UIO_READ;
257 error = uiomove(vmp->vm_data, vmp->vm_inSize, uiop);
258 if (error) {
259 myprintf(("vcread: error (%d) on uiomove\n", error));
260 error = EINVAL;
261 }
262
263 #ifdef OLD_DIAGNOSTIC
264 if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
265 panic("vc_nb_read: bad chain");
266 #endif
267
268 REMQUE(vmp->vm_chain);
269
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) {
273 if (codadebug)
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));
278 return(error);
279 }
280
281 vmp->vm_flags |= VM_READ;
282 INSQUE(vmp->vm_chain, vcp->vc_replys);
283
284 return(error);
285 }
286
287 int
288 vc_nb_write(dev, uiop, flag)
289 dev_t dev;
290 struct uio *uiop;
291 int flag;
292 {
293 register struct vcomm * vcp;
294 register struct vmsg *vmp;
295 struct coda_out_hdr *out;
296 u_long seq;
297 u_long opcode;
298 int buf[2];
299 int error = 0;
300
301 ENTRY;
302
303 if (minor(dev) >= NVCODA || minor(dev) < 0)
304 return(ENXIO);
305
306 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
307
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);
311 if (error) {
312 myprintf(("vcwrite: error (%d) on uiomove\n", error));
313 return(EINVAL);
314 }
315
316 opcode = buf[0];
317 seq = buf[1];
318
319 if (codadebug)
320 myprintf(("vcwrite got a call for %ld.%ld\n", opcode, seq));
321
322 if (DOWNCALL(opcode)) {
323 union outputArgs pbuf;
324
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);
328 if (error) {
329 myprintf(("vcwrite: error (%d) on uiomove (Op %ld seq %ld)\n",
330 error, opcode, seq));
331 return(EINVAL);
332 }
333
334 return handleDownCall(opcode, &pbuf);
335 }
336
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))
341 {
342 if (vmp->vm_unique == seq) break;
343 }
344
345 if (EOQ(vmp, vcp->vc_replys)) {
346 if (codadebug)
347 myprintf(("vcwrite: msg (%ld, %ld) not found\n", opcode, seq));
348
349 return(ESRCH);
350 }
351
352 /* Remove the message from the reply queue */
353 REMQUE(vmp->vm_chain);
354
355 /* move data into response buffer. */
356 out = (struct coda_out_hdr *)vmp->vm_data;
357 /* Don't need to copy opcode and uniquifier. */
358
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. */
364 return(EINVAL);
365 }
366
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);
370 if (error) {
371 myprintf(("vcwrite: error (%d) on uiomove (op %ld seq %ld)\n",
372 error, opcode, seq));
373 return(EINVAL);
374 }
375
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;
379 out->unique = seq;
380 vmp->vm_outSize = buf[0]; /* Amount of data transferred? */
381 vmp->vm_flags |= VM_WRITE;
382 wakeup(&vmp->vm_sleep);
383
384 return(0);
385 }
386
387 int
388 vc_nb_ioctl(dev, cmd, addr, flag, p)
389 dev_t dev;
390 u_long cmd;
391 caddr_t addr;
392 int flag;
393 struct proc *p;
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
442 vc_nb_poll(dev, events, p)
443 dev_t dev;
444 int events;
445 struct proc *p;
446 {
447 register struct vcomm *vcp;
448 int event_msk = 0;
449
450 ENTRY;
451
452 if (minor(dev) >= NVCODA || minor(dev) < 0)
453 return(ENXIO);
454
455 vcp = &coda_mnttbl[minor(dev)].mi_vcomm;
456
457 event_msk = events & (POLLIN|POLLRDNORM);
458 if (!event_msk)
459 return(0);
460
461 if (!EMPTY(vcp->vc_requests))
462 return(events & (POLLIN|POLLRDNORM));
463
464 selrecord(p, &(vcp->vc_selproc));
465
466 return(0);
467 }
468
469 /*
470 * Statistics
471 */
472 struct coda_clstat coda_clstat;
473
474 /*
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
480 * (e.g. kill -9).
481 */
482
483 int
484 coda_call(mntinfo, inSize, outSize, buffer)
485 struct coda_mntinfo *mntinfo; int inSize; int *outSize; caddr_t buffer;
486 {
487 struct vcomm *vcp;
488 struct vmsg *vmp;
489 int error;
490 #ifdef CTL_C
491 struct proc *p = curproc;
492 sigset_t psig_omask = p->p_sigmask;
493 sigset_t tempset;
494 int i;
495 #endif
496 if (mntinfo == NULL) {
497 /* Unlikely, but could be a race condition with a dying warden */
498 return ENODEV;
499 }
500
501 vcp = &(mntinfo->mi_vcomm);
502
503 coda_clstat.ncalls++;
504 coda_clstat.reqs[((struct coda_in_hdr *)buffer)->opcode]++;
505
506 if (!VC_OPEN(vcp))
507 return(ENODEV);
508
509 CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
510 /* Format the request message. */
511 vmp->vm_data = buffer;
512 vmp->vm_flags = 0;
513 vmp->vm_inSize = inSize;
514 vmp->vm_outSize
515 = *outSize ? *outSize : inSize; /* |buffer| >= inSize */
516 vmp->vm_opcode = ((struct coda_in_hdr *)buffer)->opcode;
517 vmp->vm_unique = ++vcp->vc_seq;
518 if (codadebug)
519 myprintf(("Doing a call for %d.%d\n",
520 vmp->vm_opcode, vmp->vm_unique));
521
522 /* Fill in the common input args. */
523 ((struct coda_in_hdr *)buffer)->unique = vmp->vm_unique;
524
525 /* Append msg to request queue and poke Venus. */
526 INSQUE(vmp->vm_chain, vcp->vc_requests);
527 selwakeup(&(vcp->vc_selproc));
528
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
536 * ENODEV. */
537
538 /* Ignore return, We have to check anyway */
539 #ifdef CTL_C
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.
546 */
547 i = 0;
548 do {
549 error = tsleep(&vmp->vm_sleep,
550 (coda_call_sleep|coda_pcatch), "coda_call",
551 hz*2);
552 if (error == 0)
553 break;
554 else if (error == EWOULDBLOCK) {
555 #ifdef CODA_VERBOSE
556 printf("coda_call: tsleep TIMEOUT %d sec\n", 2+2*i);
557 #endif
558 }
559 else {
560 SIGEMPTYSET(tempset);
561 SIGADDSET(tempset, SIGIO);
562 if (SIGSETEQ(p->p_siglist, tempset)) {
563 SIGADDSET(p->p_sigmask, SIGIO);
564 #ifdef CODA_VERBOSE
565 printf("coda_call: tsleep returns %d SIGIO, cnt %d\n",
566 error, i);
567 #endif
568 } else {
569 SIGDELSET(tempset, SIGIO);
570 SIGADDSET(tempset, SIGALRM);
571 if (SIGSETEQ(p->p_siglist, tempset)) {
572 SIGADDSET(p->p_sigmask, SIGALRM);
573 #ifdef CODA_VERBOSE
574 printf("coda_call: tsleep returns %d SIGALRM, cnt %d\n",
575 error, i);
576 #endif
577 }
578 else {
579 printf("coda_call: tsleep returns %d, cnt %d\n",
580 error, i);
581
582 #if notyet
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,
587 tempset);
588 break;
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,
594 tempset);
595 #endif
596 }
597 }
598 }
599 } while (error && i++ < 128 && VC_OPEN(vcp));
600 p->p_sigmask = psig_omask;
601 #else
602 (void) tsleep(&vmp->vm_sleep, coda_call_sleep, "coda_call", 0);
603 #endif
604 if (VC_OPEN(vcp)) { /* Venus is still alive */
605 /* Op went through, interrupt or not... */
606 if (vmp->vm_flags & VM_WRITE) {
607 error = 0;
608 *outSize = vmp->vm_outSize;
609 }
610
611 else if (!(vmp->vm_flags & VM_READ)) {
612 /* Interrupted before venus read it. */
613 #ifdef CODA_VERBOSE
614 if (1)
615 #else
616 if (codadebug)
617 #endif
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);
621 error = EINTR;
622 }
623
624 else {
625 /* (!(vmp->vm_flags & VM_WRITE)) means interrupted after
626 upcall started */
627 /* Interrupted after start of upcall, send venus a signal */
628 struct coda_in_hdr *dog;
629 struct vmsg *svmp;
630
631 #ifdef CODA_VERBOSE
632 if (1)
633 #else
634 if (codadebug)
635 #endif
636 myprintf(("Sending Venus a signal: op = %d.%d, flags = %x\n",
637 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
638
639 REMQUE(vmp->vm_chain);
640 error = EINTR;
641
642 CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
643
644 CODA_ALLOC((svmp->vm_data), char *, sizeof (struct coda_in_hdr));
645 dog = (struct coda_in_hdr *)svmp->vm_data;
646
647 svmp->vm_flags = 0;
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);
652
653 if (codadebug)
654 myprintf(("coda_call: enqueing signal msg (%d, %d)\n",
655 svmp->vm_opcode, svmp->vm_unique));
656
657 /* insert at head of queue! */
658 INSQUE(svmp->vm_chain, vcp->vc_requests);
659 selwakeup(&(vcp->vc_selproc));
660 }
661 }
662
663 else { /* If venus died (!VC_OPEN(vcp)) */
664 if (codadebug)
665 myprintf(("vcclose woke op %d.%d flags %d\n",
666 vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags));
667
668 error = ENODEV;
669 }
670
671 CODA_FREE(vmp, sizeof(struct vmsg));
672
673 if (outstanding_upcalls > 0 && (--outstanding_upcalls == 0))
674 wakeup(&outstanding_upcalls);
675
676 if (!error)
677 error = ((struct coda_out_hdr *)buffer)->result;
678 return(error);
679 }
Cache object: 670f84549d3e92cc191125c883929ed6
|