xref: /plan9-contrib/sys/src/9/port/devmnt.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 #include	"devtab.h"
8 
9 typedef struct Mntrpc Mntrpc;
10 struct Mntrpc
11 {
12 	Mntrpc	*list;		/* Free/pending list */
13 	Fcall	request;	/* Outgoing file system protocol message */
14 	Fcall	reply;		/* Incoming reply */
15 	Mnt	*m;		/* Mount device during rpc */
16 	Rendez	r;		/* Place to hang out */
17 	char	*rpc;		/* I/O Data buffer */
18 	char	done;		/* Rpc completed */
19 	char	flushed;	/* Flush was sent */
20 	ushort	flushtag;	/* Tag flush sent on */
21 	char	flush[MAXMSG];	/* Somewhere to build flush */
22 };
23 
24 struct Mnt
25 {
26 	int reads;		/* counters for debugging */
27 	int writes;
28 	int readerrs;
29 	int badlen;
30 	int goodconv;
31 	int noone;
32 
33 	Ref;			/* Count of attached channels */
34 	Chan	*c;		/* Channel to file service */
35 	Proc	*rip;		/* Reader in progress */
36 	Mntrpc	*queue;		/* Queue of pending requests on this channel */
37 	ulong	id;		/* Multiplexor id for channel check */
38 	Mnt	*list;		/* Free list */
39 	char	mux;		/* Set if the device does the multiplexing */
40 	int	blocksize;	/* read/write block size */
41 	ushort	flushtag;	/* Tag to send flush on */
42 	ushort	flushbase;	/* Base tag of flush window for this buffer */
43 };
44 
45 struct Mntalloc
46 {
47 	Lock;
48 	Mnt	*list;		/* Mount devices in used */
49 	Mnt	*mntfree;	/* Free list */
50 	Mntrpc	*rpcfree;
51 	int	id;
52 	int	rpctag;
53 }mntalloc;
54 
55 #define MAXRPC		(MAXFDATA+MAXMSG)
56 #define limit(n, max)	(n > max ? max : n)
57 
58 Chan*	mattach(Mnt*, char*);
59 Mnt*	mntchk(Chan*);
60 void	mntdirfix(uchar*, Chan*);
61 void	mntdoclunk(Mnt *, Mntrpc *);
62 int	mntflush(Mnt*, Mntrpc*);
63 void	mntfree(Mntrpc*);
64 void	mntgate(Mnt*);
65 void	mntpntfree(Mnt*);
66 void	mntqrm(Mnt*, Mntrpc*);
67 Mntrpc*	mntralloc(void);
68 long	mntrdwr(int , Chan*, void*,long , ulong);
69 void	mntrpcread(Mnt*, Mntrpc*);
70 void	mountio(Mnt*, Mntrpc*);
71 void	mountmux(Mnt*, Mntrpc*);
72 void	mountrpc(Mnt*, Mntrpc*);
73 int	rpcattn(Mntrpc*);
74 void	mclose(Mnt*);
75 
76 static int	defblocksize = MAXFDATA;
77 
78 enum
79 {
80 	Tagspace = 1,
81 	Tagfls = 0x8000,
82 	Tagend = 0xfffe,
83 
84 	ALIGN = 256,		/* Vme block mode alignment */
85 };
86 
87 void
88 mntblocksize(int s)
89 {
90 	if(s < 256 || s > MAXFDATA)
91 		return;
92 	defblocksize = s;
93 }
94 
95 void
96 mntreset(void)
97 {
98 	mntalloc.id = 1;
99 	mntalloc.rpctag = Tagspace;
100 }
101 
102 void
103 mntinit(void)
104 {
105 }
106 
107 Chan*
108 mntattach(char *muxattach)
109 {
110 	Mnt *m;
111 	Chan *c, *mc;
112 	struct bogus{
113 		Chan	*chan;
114 		char	*spec;
115 	}bogus;
116 
117 	bogus = *((struct bogus *)muxattach);
118 	c = bogus.chan;
119 
120 	lock(&mntalloc);
121 	for(m = mntalloc.list; m; m = m->list) {
122 		if(m->c == c && m->id) {
123 			lock(m);
124 			if(m->id && m->ref > 0 && m->c == c) {
125 				unlock(&mntalloc);
126 				m->ref++;
127 				unlock(m);
128 				return mattach(m, bogus.spec);
129 			}
130 			unlock(m);
131 		}
132 	}
133 
134 	m = mntalloc.mntfree;
135 	if(m != 0)
136 		mntalloc.mntfree = m->list;
137 	else {
138 		m = malloc(sizeof(Mnt));
139 		if(m == 0) {
140 			unlock(&mntalloc);
141 			exhausted("mount devices");
142 		}
143 		m->flushbase = Tagfls;
144 		m->flushtag = Tagfls;
145 	}
146 	m->list = mntalloc.list;
147 	mntalloc.list = m;
148 	m->id = mntalloc.id++;
149 	lock(m);
150 	unlock(&mntalloc);
151 
152 	m->ref = 1;
153 	m->queue = 0;
154 	m->rip = 0;
155 	m->c = c;
156 	m->c->flag |= CMSG;
157 	m->blocksize = defblocksize;
158 
159 	switch(devchar[m->c->type]) {
160 	default:
161 		m->mux = 0;
162 		break;
163 	case 'C':			/* Cyclone */
164 		m->mux = 1;
165 		break;
166 	}
167 	incref(m->c);
168 	unlock(m);
169 
170 	if(waserror()) {
171 		mclose(m);
172 		nexterror();
173 	}
174 
175 	c = mattach(m, bogus.spec);
176 
177 	/*
178 	 *  If exportfs mounts on behalf of a local devmnt, the mount
179 	 *  point is folded onto the original channel to preserve a single
180 	 *  fid/tag space.  CHDIR is cleared by exportfs to indicate it
181 	 *  is supplying the mount.
182 	 */
183 	mc = m->c;
184 	if(mc->type == devno('M', 0) && (c->qid.path&CHDIR) == 0) {
185 		c->qid.path |= CHDIR;
186 		c->mntptr = mc->mntptr;
187 		c->mchan = mc->mntptr->c;
188 		c->mqid = c->qid;
189 		incref(c->mntptr);
190 		mclose(m);
191 	}
192 
193 	poperror();
194 	return c;
195 }
196 
197 Chan *
198 mattach(Mnt *m, char *spec)
199 {
200 	Chan *c;
201 	Mntrpc *r;
202 	ulong id;
203 
204 	r = mntralloc();
205 	c = devattach('M', spec);
206 	lock(&mntalloc);
207 	c->dev = mntalloc.id++;
208 	unlock(&mntalloc);
209 	c->mntptr = m;
210 
211 	if(waserror()){
212 		mntfree(r);
213 		/* Close must not be called since
214 		 * it will call mnt recursively
215 		 */
216 		chanfree(c);
217 		nexterror();
218 	}
219 
220 	r->request.type = Tattach;
221 	r->request.fid = c->fid;
222 	memmove(r->request.uname, u->p->user, NAMELEN);
223 	strncpy(r->request.aname, spec, NAMELEN);
224 	id = authrequest(m->c->session, &r->request);
225 	mountrpc(m, r);
226 	authreply(m->c->session, id, &r->reply);
227 
228 	c->qid = r->reply.qid;
229 	c->mchan = m->c;
230 	c->mqid = c->qid;
231 	poperror();
232 	mntfree(r);
233 	return c;
234 }
235 
236 Chan*
237 mntclone(Chan *c, Chan *nc)
238 {
239 	Mnt *m;
240 	Mntrpc *r;
241 	int alloc = 0;
242 
243 	m = mntchk(c);
244 	r = mntralloc();
245 	if(nc == 0) {
246 		nc = newchan();
247 		alloc = 1;
248 	}
249 	if(waserror()) {
250 		mntfree(r);
251 		if(alloc)
252 			close(nc);
253 		nexterror();
254 	}
255 
256 	r->request.type = Tclone;
257 	r->request.fid = c->fid;
258 	r->request.newfid = nc->fid;
259 	mountrpc(m, r);
260 
261 	devclone(c, nc);
262 	nc->mqid = c->qid;
263 	incref(m);
264 
265 	USED(alloc);
266 	poperror();
267 	mntfree(r);
268 	return nc;
269 }
270 
271 int
272 mntwalk(Chan *c, char *name)
273 {
274 	Mnt *m;
275 	Mntrpc *r;
276 
277 	m = mntchk(c);
278 	r = mntralloc();
279 	if(waserror()) {
280 		mntfree(r);
281 		return 0;
282 	}
283 	r->request.type = Twalk;
284 	r->request.fid = c->fid;
285 	strncpy(r->request.name, name, NAMELEN);
286 	mountrpc(m, r);
287 
288 	c->qid = r->reply.qid;
289 
290 	poperror();
291 	mntfree(r);
292 	return 1;
293 }
294 
295 void
296 mntstat(Chan *c, char *dp)
297 {
298 	Mnt *m;
299 	Mntrpc *r;
300 
301 	m = mntchk(c);
302 	r = mntralloc();
303 	if(waserror()) {
304 		mntfree(r);
305 		nexterror();
306 	}
307 	r->request.type = Tstat;
308 	r->request.fid = c->fid;
309 	mountrpc(m, r);
310 
311 	memmove(dp, r->reply.stat, DIRLEN);
312 	mntdirfix((uchar*)dp, c);
313 	poperror();
314 	mntfree(r);
315 }
316 
317 Chan*
318 mntopen(Chan *c, int omode)
319 {
320 	Mnt *m;
321 	Mntrpc *r;
322 
323 	m = mntchk(c);
324 	r = mntralloc();
325 	if(waserror()) {
326 		mntfree(r);
327 		nexterror();
328 	}
329 	r->request.type = Topen;
330 	r->request.fid = c->fid;
331 	r->request.mode = omode;
332 	mountrpc(m, r);
333 
334 	c->qid = r->reply.qid;
335 	c->offset = 0;
336 	c->mode = openmode(omode);
337 	c->flag |= COPEN;
338 	poperror();
339 	mntfree(r);
340 	return c;
341 }
342 
343 void
344 mntcreate(Chan *c, char *name, int omode, ulong perm)
345 {
346 	Mnt *m;
347 	Mntrpc *r;
348 
349 	m = mntchk(c);
350 	r = mntralloc();
351 	if(waserror()) {
352 		mntfree(r);
353 		nexterror();
354 	}
355 	r->request.type = Tcreate;
356 	r->request.fid = c->fid;
357 	r->request.mode = omode;
358 	r->request.perm = perm;
359 	strncpy(r->request.name, name, NAMELEN);
360 	mountrpc(m, r);
361 
362 	c->qid = r->reply.qid;
363 	c->flag |= COPEN;
364 	c->mode = openmode(omode);
365 	poperror();
366 	mntfree(r);
367 }
368 
369 void
370 mntclunk(Chan *c, int t)
371 {
372 	Mnt *m;
373 	Mntrpc *r;
374 
375 	m = mntchk(c);
376 	r = mntralloc();
377 	if(waserror()){
378 		mntdoclunk(m, r);
379 		nexterror();
380 	}
381 
382 	r->request.type = t;
383 	r->request.fid = c->fid;
384 	mountrpc(m, r);
385 	mntdoclunk(m, r);
386 	poperror();
387 }
388 
389 void
390 mclose(Mnt *m)
391 {
392 	Mntrpc *q, *r;
393 
394 	if(decref(m) != 0)
395 		return;
396 
397 	for(q = m->queue; q; q = r) {
398 		r = q->list;
399 		q->flushed = 0;
400 		mntfree(q);
401 	}
402 	m->id = 0;
403 	close(m->c);
404 	mntpntfree(m);
405 }
406 
407 void
408 mntdoclunk(Mnt *m, Mntrpc *r)
409 {
410 	mntfree(r);
411 	mclose(m);
412 }
413 
414 void
415 mntpntfree(Mnt *m)
416 {
417 	Mnt *f, **l;
418 
419 	lock(&mntalloc);
420 	l = &mntalloc.list;
421 	for(f = *l; f; f = f->list) {
422 		if(f == m) {
423 			*l = m->list;
424 			break;
425 		}
426 		l = &f->list;
427 	}
428 
429 	m->list = mntalloc.mntfree;
430 	mntalloc.mntfree = m;
431 	unlock(&mntalloc);
432 }
433 
434 void
435 mntclose(Chan *c)
436 {
437 	mntclunk(c, Tclunk);
438 }
439 
440 void
441 mntremove(Chan *c)
442 {
443 	mntclunk(c, Tremove);
444 }
445 
446 void
447 mntwstat(Chan *c, char *dp)
448 {
449 	Mnt *m;
450 	Mntrpc *r;
451 
452 	m = mntchk(c);
453 	r = mntralloc();
454 	if(waserror()) {
455 		mntfree(r);
456 		nexterror();
457 	}
458 	r->request.type = Twstat;
459 	r->request.fid = c->fid;
460 	memmove(r->request.stat, dp, DIRLEN);
461 	mountrpc(m, r);
462 	poperror();
463 	mntfree(r);
464 }
465 
466 long
467 mntread(Chan *c, void *buf, long n, ulong offset)
468 {
469 	uchar *p, *e;
470 
471 	n = mntrdwr(Tread, c, buf, n, offset);
472 	if(c->qid.path & CHDIR)
473 		for(p = (uchar*)buf, e = &p[n]; p < e; p += DIRLEN)
474 			mntdirfix(p, c);
475 
476 	return n;
477 }
478 
479 long
480 mntwrite(Chan *c, void *buf, long n, ulong offset)
481 {
482 	return mntrdwr(Twrite, c, buf, n, offset);
483 }
484 
485 long
486 mntrdwr(int type, Chan *c, void *buf, long n, ulong offset)
487 {
488 	Mnt *m;
489 	Mntrpc *r;
490 	char *uba;
491 	ulong cnt, nr;
492 
493 	m = mntchk(c);
494 	uba = buf;
495 	cnt = 0;
496 	for(;;) {
497 		r = mntralloc();
498 		if(waserror()) {
499 			mntfree(r);
500 			nexterror();
501 		}
502 		r->request.type = type;
503 		r->request.fid = c->fid;
504 		r->request.offset = offset;
505 		r->request.data = uba;
506 		r->request.count = limit(n, m->blocksize);
507 		mountrpc(m, r);
508 		nr = r->reply.count;
509 		if(nr > r->request.count)
510 			nr = r->request.count;
511 		if(type == Tread)
512 			memmove(uba, r->reply.data, nr);
513 		poperror();
514 		mntfree(r);
515 		offset += nr;
516 		uba += nr;
517 		cnt += nr;
518 		n -= nr;
519 		if(nr != r->request.count || n == 0 || u->nnote)
520 			break;
521 	}
522 	return cnt;
523 }
524 
525 void
526 mountrpc(Mnt *m, Mntrpc *r)
527 {
528 	r->reply.tag = 0;		/* poison the old values */
529 	r->reply.type = 4;
530 
531 	mountio(m, r);
532 	if(r->reply.type == Rerror)
533 		error(r->reply.ename);
534 
535 	if(r->reply.type == Rflush)
536 		error(Eintr);
537 
538 	if(r->reply.type != r->request.type+1) {
539 		print("mnt: mismatched reply 0x%lux T%d R%d tags req %d fls %d rep %d\n",
540 				r, r->request.type, r->reply.type, r->request.tag,
541 				r->flushtag, r->reply.tag);
542 
543 		error(Emountrpc);
544 	}
545 }
546 
547 void
548 mountio(Mnt *m, Mntrpc *r)
549 {
550 	int n;
551 
552 	lock(m);
553 	r->flushed = 0;
554 	r->m = m;
555 	r->list = m->queue;
556 	m->queue = r;
557 	unlock(m);
558 
559 	/* Transmit a file system rpc */
560 	n = convS2M(&r->request, r->rpc);
561 	if(waserror()) {
562 		if(mntflush(m, r) == 0)
563 			nexterror();
564 	}
565 	else {
566 		if((*devtab[m->c->type].write)(m->c, r->rpc, n, 0) != n)
567 			error(Emountrpc);
568 		m->writes++;
569 		poperror();
570 	}
571 	if(m->mux) {
572 		mntqrm(m, r);
573 		mntrpcread(m, r);
574 		return;
575 	}
576 
577 	/* Gate readers onto the mount point one at a time */
578 	for(;;) {
579 		lock(m);
580 		if(m->rip == 0)
581 			break;
582 		unlock(m);
583 		if(waserror()) {
584 			if(mntflush(m, r) == 0)
585 				nexterror();
586 			continue;
587 		}
588 		sleep(&r->r, rpcattn, r);
589 		poperror();
590 		if(r->done)
591 			return;
592 	}
593 	m->rip = u->p;
594 	unlock(m);
595 	while(r->done == 0) {
596 		mntrpcread(m, r);
597 		mountmux(m, r);
598 	}
599 	mntgate(m);
600 }
601 
602 void
603 mntrpcread(Mnt *m, Mntrpc *r)
604 {
605 	int n;
606 
607 	for(;;) {
608 		if(waserror()) {
609 			m->readerrs++;
610 			if(mntflush(m, r) == 0) {
611 				if(m->mux == 0)
612 					mntgate(m);
613 				nexterror();
614 			}
615 			continue;
616 		}
617 		r->reply.type = 0;
618 		r->reply.tag = 0;
619 		n = (*devtab[m->c->type].read)(m->c, r->rpc, MAXRPC, 0);
620 		poperror();
621 		m->reads++;
622 		if(n == 0){
623 			m->badlen++;
624 			continue;
625 		}
626 
627 		if(convM2S(r->rpc, &r->reply, n) != 0){
628 			m->goodconv++;
629 			return;
630 		}
631 	}
632 }
633 
634 void
635 mntgate(Mnt *m)
636 {
637 	Mntrpc *q;
638 
639 	lock(m);
640 	m->rip = 0;
641 	for(q = m->queue; q; q = q->list)
642 		if(q->done == 0) {
643 			lock(&q->r);
644 			if(q->r.p) {
645 				unlock(&q->r);
646 				unlock(m);
647 				wakeup(&q->r);
648 				return;
649 			}
650 			unlock(&q->r);
651 		}
652 	unlock(m);
653 }
654 
655 void
656 mountmux(Mnt *m, Mntrpc *r)
657 {
658 	char *dp;
659 	Mntrpc **l, *q;
660 
661 	lock(m);
662 	l = &m->queue;
663 	for(q = *l; q; q = q->list) {
664 		if(q->request.tag == r->reply.tag
665 		|| q->flushed && q->flushtag == r->reply.tag) {
666 			*l = q->list;
667 			unlock(m);
668 			if(q != r) {		/* Completed someone else */
669 				dp = q->rpc;
670 				q->rpc = r->rpc;
671 				r->rpc = dp;
672 				memmove(&q->reply, &r->reply, sizeof(Fcall));
673 				q->done = 1;
674 				wakeup(&q->r);
675 			}else
676 				q->done = 1;
677 			return;
678 		}
679 		l = &q->list;
680 	}
681 	m->noone++;
682 	unlock(m);
683 }
684 
685 int
686 mntflush(Mnt *m, Mntrpc *r)
687 {
688 	int n;
689 	Fcall flush;
690 
691 	lock(m);
692 	r->flushtag = m->flushtag++;
693 	if(m->flushtag == Tagend)
694 		m->flushtag = m->flushbase;
695 	r->flushed = 1;
696 	unlock(m);
697 
698 	flush.type = Tflush;
699 	flush.tag = r->flushtag;
700 	flush.oldtag = r->request.tag;
701 	n = convS2M(&flush, r->flush);
702 
703 	if(waserror()) {
704 		if(strcmp(u->error, Eintr) == 0)
705 			return 1;
706 		mntqrm(m, r);
707 		return 0;
708 	}
709 	(*devtab[m->c->type].write)(m->c, r->flush, n, 0);
710 	poperror();
711 	return 1;
712 }
713 
714 Mntrpc *
715 mntralloc(void)
716 {
717 	Mntrpc *new;
718 
719 	lock(&mntalloc);
720 	new = mntalloc.rpcfree;
721 	if(new != 0)
722 		mntalloc.rpcfree = new->list;
723 	else {
724 		new = xalloc(sizeof(Mntrpc)+MAXRPC);
725 		if(new == 0) {
726 			unlock(&mntalloc);
727 			exhausted("mount rpc buffer");
728 		}
729 		new->rpc = (char*)new+sizeof(Mntrpc);
730 		new->request.tag = mntalloc.rpctag++;
731 	}
732 	unlock(&mntalloc);
733 	new->done = 0;
734 	new->flushed = 0;
735 	return new;
736 }
737 
738 void
739 mntfree(Mntrpc *r)
740 {
741 	lock(&mntalloc);
742 	r->list = mntalloc.rpcfree;
743 	mntalloc.rpcfree = r;
744 	unlock(&mntalloc);
745 }
746 
747 void
748 mntqrm(Mnt *m, Mntrpc *r)
749 {
750 	Mntrpc **l, *f;
751 
752 	lock(m);
753 	r->done = 1;
754 	r->flushed = 0;
755 
756 	l = &m->queue;
757 	for(f = *l; f; f = f->list) {
758 		if(f == r) {
759 			*l = r->list;
760 			break;
761 		}
762 		l = &f->list;
763 	}
764 	unlock(m);
765 }
766 
767 Mnt *
768 mntchk(Chan *c)
769 {
770 	Mnt *m;
771 
772 	m = c->mntptr;
773 	/* Was it closed and reused ? */
774 	if(m->id == 0 || m->id >= c->dev)	/* Sanity check */
775 		error(Eshutdown);
776 	return m;
777 }
778 
779 void
780 mntdirfix(uchar *dirbuf, Chan *c)
781 {
782 	dirbuf[DIRLEN-4] = devchar[c->type]>>0;
783 	dirbuf[DIRLEN-3] = devchar[c->type]>>8;
784 	dirbuf[DIRLEN-2] = c->dev;
785 	dirbuf[DIRLEN-1] = c->dev>>8;
786 }
787 
788 int
789 rpcattn(Mntrpc *r)
790 {
791 	return r->done || r->m->rip == 0;
792 }
793