FreeBSD/Linux Kernel Cross Reference
sys/port/devmnt.c
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
8 /*
9 * References are managed as follows:
10 * The channel to the server - a network connection or pipe - has one
11 * reference for every Chan open on the server. The server channel has
12 * c->mux set to the Mnt used for muxing control to that server. Mnts
13 * have no reference count; they go away when c goes away.
14 * Each channel derived from the mount point has mchan set to c,
15 * and increfs/decrefs mchan to manage references on the server
16 * connection.
17 */
18
19 #define MAXRPC (IOHDRSZ+8192)
20
21 struct Mntrpc
22 {
23 Chan* c; /* Channel for whom we are working */
24 Mntrpc* list; /* Free/pending list */
25 Fcall request; /* Outgoing file system protocol message */
26 Fcall reply; /* Incoming reply */
27 Mnt* m; /* Mount device during rpc */
28 Rendez r; /* Place to hang out */
29 uchar* rpc; /* I/O Data buffer */
30 uint rpclen; /* len of buffer */
31 Block *b; /* reply blocks */
32 char done; /* Rpc completed */
33 uvlong stime; /* start time for mnt statistics */
34 ulong reqlen; /* request length for mnt statistics */
35 ulong replen; /* reply length for mnt statistics */
36 Mntrpc* flushed; /* message this one flushes */
37 };
38
39 enum
40 {
41 TAGSHIFT = 5, /* ulong has to be 32 bits */
42 TAGMASK = (1<<TAGSHIFT)-1,
43 NMASK = (64*1024)>>TAGSHIFT,
44 };
45
46 struct Mntalloc
47 {
48 Lock;
49 Mnt* list; /* Mount devices in use */
50 Mnt* mntfree; /* Free list */
51 Mntrpc* rpcfree;
52 int nrpcfree;
53 int nrpcused;
54 ulong id;
55 ulong tagmask[NMASK];
56 }mntalloc;
57
58 Mnt* mntchk(Chan*);
59 void mntdirfix(uchar*, Chan*);
60 Mntrpc* mntflushalloc(Mntrpc*, ulong);
61 void mntflushfree(Mnt*, Mntrpc*);
62 void mntfree(Mntrpc*);
63 void mntgate(Mnt*);
64 void mntpntfree(Mnt*);
65 void mntqrm(Mnt*, Mntrpc*);
66 Mntrpc* mntralloc(Chan*, ulong);
67 long mntrdwr(int, Chan*, void*, long, vlong);
68 int mntrpcread(Mnt*, Mntrpc*);
69 void mountio(Mnt*, Mntrpc*);
70 void mountmux(Mnt*, Mntrpc*);
71 void mountrpc(Mnt*, Mntrpc*);
72 int rpcattn(void*);
73 Chan* mntchan(void);
74
75 char Esbadstat[] = "invalid directory entry received from server";
76 char Enoversion[] = "version not established for mount channel";
77
78
79 void (*mntstats)(int, Chan*, uvlong, ulong);
80
81 static void
82 mntreset(void)
83 {
84 mntalloc.id = 1;
85 mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */
86 mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */
87 fmtinstall('F', fcallfmt);
88 fmtinstall('D', dirfmt);
89 /* We can't install %M since eipfmt does and is used in the kernel [sape] */
90
91 cinit();
92 }
93
94 /*
95 * Version is not multiplexed: message sent only once per connection.
96 */
97 long
98 mntversion(Chan *c, char *version, int msize, int returnlen)
99 {
100 Fcall f;
101 uchar *msg;
102 Mnt *m;
103 char *v;
104 long k, l;
105 uvlong oo;
106 char buf[128];
107
108 qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */
109 if(waserror()){
110 qunlock(&c->umqlock);
111 nexterror();
112 }
113
114 /* defaults */
115 if(msize == 0)
116 msize = MAXRPC;
117 if(msize > c->iounit && c->iounit != 0)
118 msize = c->iounit;
119 v = version;
120 if(v == nil || v[0] == '\0')
121 v = VERSION9P;
122
123 /* validity */
124 if(msize < 0)
125 error("bad iounit in version call");
126 if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
127 error("bad 9P version specification");
128
129 m = c->mux;
130
131 if(m != nil){
132 qunlock(&c->umqlock);
133 poperror();
134
135 strecpy(buf, buf+sizeof buf, m->version);
136 k = strlen(buf);
137 if(strncmp(buf, v, k) != 0){
138 snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v);
139 error(buf);
140 }
141 if(returnlen > 0){
142 if(returnlen < k)
143 error(Eshort);
144 memmove(version, buf, k);
145 }
146 return k;
147 }
148
149 f.type = Tversion;
150 f.tag = NOTAG;
151 f.msize = msize;
152 f.version = v;
153 msg = malloc(8192+IOHDRSZ);
154 if(msg == nil)
155 exhausted("version memory");
156 if(waserror()){
157 free(msg);
158 nexterror();
159 }
160 k = convS2M(&f, msg, 8192+IOHDRSZ);
161 if(k == 0)
162 error("bad fversion conversion on send");
163
164 lock(c);
165 oo = c->offset;
166 c->offset += k;
167 unlock(c);
168
169 l = devtab[c->type]->write(c, msg, k, oo);
170
171 if(l < k){
172 lock(c);
173 c->offset -= k - l;
174 unlock(c);
175 error("short write in fversion");
176 }
177
178 /* message sent; receive and decode reply */
179 k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
180 if(k <= 0)
181 error("EOF receiving fversion reply");
182
183 lock(c);
184 c->offset += k;
185 unlock(c);
186
187 l = convM2S(msg, k, &f);
188 if(l != k)
189 error("bad fversion conversion on reply");
190 if(f.type != Rversion){
191 if(f.type == Rerror)
192 error(f.ename);
193 error("unexpected reply type in fversion");
194 }
195 if(f.msize > msize)
196 error("server tries to increase msize in fversion");
197 if(f.msize<256 || f.msize>1024*1024)
198 error("nonsense value of msize in fversion");
199 k = strlen(f.version);
200 if(strncmp(f.version, v, k) != 0)
201 error("bad 9P version returned from server");
202
203 /* now build Mnt associated with this connection */
204 lock(&mntalloc);
205 m = mntalloc.mntfree;
206 if(m != 0)
207 mntalloc.mntfree = m->list;
208 else {
209 m = malloc(sizeof(Mnt));
210 if(m == 0) {
211 unlock(&mntalloc);
212 exhausted("mount devices");
213 }
214 }
215 m->list = mntalloc.list;
216 mntalloc.list = m;
217 m->version = nil;
218 kstrdup(&m->version, f.version);
219 m->id = mntalloc.id++;
220 m->q = qopen(10*MAXRPC, 0, nil, nil);
221 m->msize = f.msize;
222 unlock(&mntalloc);
223
224 if(returnlen > 0){
225 if(returnlen < k)
226 error(Eshort);
227 memmove(version, f.version, k);
228 }
229
230 poperror(); /* msg */
231 free(msg);
232
233 lock(m);
234 m->queue = 0;
235 m->rip = 0;
236
237 c->flag |= CMSG;
238 c->mux = m;
239 m->c = c;
240 unlock(m);
241
242 poperror(); /* c */
243 qunlock(&c->umqlock);
244
245 return k;
246 }
247
248 Chan*
249 mntauth(Chan *c, char *spec)
250 {
251 Mnt *m;
252 Mntrpc *r;
253
254 m = c->mux;
255
256 if(m == nil){
257 mntversion(c, VERSION9P, MAXRPC, 0);
258 m = c->mux;
259 if(m == nil)
260 error(Enoversion);
261 }
262
263 c = mntchan();
264 if(waserror()) {
265 /* Close must not be called since it will
266 * call mnt recursively
267 */
268 chanfree(c);
269 nexterror();
270 }
271
272 r = mntralloc(0, m->msize);
273
274 if(waserror()) {
275 mntfree(r);
276 nexterror();
277 }
278
279 r->request.type = Tauth;
280 r->request.afid = c->fid;
281 r->request.uname = up->user;
282 r->request.aname = spec;
283 mountrpc(m, r);
284
285 c->qid = r->reply.aqid;
286 c->mchan = m->c;
287 incref(m->c);
288 c->mqid = c->qid;
289 c->mode = ORDWR;
290
291 poperror(); /* r */
292 mntfree(r);
293
294 poperror(); /* c */
295
296 return c;
297
298 }
299
300 static Chan*
301 mntattach(char *muxattach)
302 {
303 Mnt *m;
304 Chan *c;
305 Mntrpc *r;
306 struct bogus{
307 Chan *chan;
308 Chan *authchan;
309 char *spec;
310 int flags;
311 }bogus;
312
313 bogus = *((struct bogus *)muxattach);
314 c = bogus.chan;
315
316 m = c->mux;
317
318 if(m == nil){
319 mntversion(c, nil, 0, 0);
320 m = c->mux;
321 if(m == nil)
322 error(Enoversion);
323 }
324
325 c = mntchan();
326 if(waserror()) {
327 /* Close must not be called since it will
328 * call mnt recursively
329 */
330 chanfree(c);
331 nexterror();
332 }
333
334 r = mntralloc(0, m->msize);
335
336 if(waserror()) {
337 mntfree(r);
338 nexterror();
339 }
340
341 r->request.type = Tattach;
342 r->request.fid = c->fid;
343 if(bogus.authchan == nil)
344 r->request.afid = NOFID;
345 else
346 r->request.afid = bogus.authchan->fid;
347 r->request.uname = up->user;
348 r->request.aname = bogus.spec;
349 mountrpc(m, r);
350
351 c->qid = r->reply.qid;
352 c->mchan = m->c;
353 incref(m->c);
354 c->mqid = c->qid;
355
356 poperror(); /* r */
357 mntfree(r);
358
359 poperror(); /* c */
360
361 if(bogus.flags&MCACHE)
362 c->flag |= CCACHE;
363 return c;
364 }
365
366 Chan*
367 mntchan(void)
368 {
369 Chan *c;
370
371 c = devattach('M', 0);
372 lock(&mntalloc);
373 c->dev = mntalloc.id++;
374 unlock(&mntalloc);
375
376 if(c->mchan)
377 panic("mntchan non-zero %p", c->mchan);
378 return c;
379 }
380
381 static Walkqid*
382 mntwalk(Chan *c, Chan *nc, char **name, int nname)
383 {
384 int i, alloc;
385 Mnt *m;
386 Mntrpc *r;
387 Walkqid *wq;
388
389 if(nc != nil)
390 print("mntwalk: nc != nil\n");
391 if(nname > MAXWELEM)
392 error("devmnt: too many name elements");
393 alloc = 0;
394 wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
395 if(waserror()){
396 if(alloc && wq->clone!=nil)
397 cclose(wq->clone);
398 free(wq);
399 return nil;
400 }
401
402 alloc = 0;
403 m = mntchk(c);
404 r = mntralloc(c, m->msize);
405 if(nc == nil){
406 nc = devclone(c);
407 /*
408 * Until the other side accepts this fid, we can't mntclose it.
409 * Therefore set type to 0 for now; rootclose is known to be safe.
410 */
411 nc->type = 0;
412 alloc = 1;
413 }
414 wq->clone = nc;
415
416 if(waserror()) {
417 mntfree(r);
418 nexterror();
419 }
420 r->request.type = Twalk;
421 r->request.fid = c->fid;
422 r->request.newfid = nc->fid;
423 r->request.nwname = nname;
424 memmove(r->request.wname, name, nname*sizeof(char*));
425
426 mountrpc(m, r);
427
428 if(r->reply.nwqid > nname)
429 error("too many QIDs returned by walk");
430 if(r->reply.nwqid < nname){
431 if(alloc)
432 cclose(nc);
433 wq->clone = nil;
434 if(r->reply.nwqid == 0){
435 free(wq);
436 wq = nil;
437 goto Return;
438 }
439 }
440
441 /* move new fid onto mnt device and update its qid */
442 if(wq->clone != nil){
443 if(wq->clone != c){
444 wq->clone->type = c->type;
445 wq->clone->mchan = c->mchan;
446 incref(c->mchan);
447 }
448 if(r->reply.nwqid > 0)
449 wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
450 }
451 wq->nqid = r->reply.nwqid;
452 for(i=0; i<wq->nqid; i++)
453 wq->qid[i] = r->reply.wqid[i];
454
455 Return:
456 poperror();
457 mntfree(r);
458 poperror();
459 return wq;
460 }
461
462 static int
463 mntstat(Chan *c, uchar *dp, int n)
464 {
465 Mnt *m;
466 Mntrpc *r;
467
468 if(n < BIT16SZ)
469 error(Eshortstat);
470 m = mntchk(c);
471 r = mntralloc(c, m->msize);
472 if(waserror()) {
473 mntfree(r);
474 nexterror();
475 }
476 r->request.type = Tstat;
477 r->request.fid = c->fid;
478 mountrpc(m, r);
479
480 if(r->reply.nstat > n){
481 n = BIT16SZ;
482 PBIT16((uchar*)dp, r->reply.nstat-2);
483 }else{
484 n = r->reply.nstat;
485 memmove(dp, r->reply.stat, n);
486 validstat(dp, n);
487 mntdirfix(dp, c);
488 }
489 poperror();
490 mntfree(r);
491 return n;
492 }
493
494 static Chan*
495 mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
496 {
497 Mnt *m;
498 Mntrpc *r;
499
500 m = mntchk(c);
501 r = mntralloc(c, m->msize);
502 if(waserror()) {
503 mntfree(r);
504 nexterror();
505 }
506 r->request.type = type;
507 r->request.fid = c->fid;
508 r->request.mode = omode;
509 if(type == Tcreate){
510 r->request.perm = perm;
511 r->request.name = name;
512 }
513 mountrpc(m, r);
514
515 c->qid = r->reply.qid;
516 c->offset = 0;
517 c->mode = openmode(omode);
518 c->iounit = r->reply.iounit;
519 if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
520 c->iounit = m->msize-IOHDRSZ;
521 c->flag |= COPEN;
522 poperror();
523 mntfree(r);
524
525 if(c->flag & CCACHE)
526 copen(c);
527
528 return c;
529 }
530
531 static Chan*
532 mntopen(Chan *c, int omode)
533 {
534 return mntopencreate(Topen, c, nil, omode, 0);
535 }
536
537 static void
538 mntcreate(Chan *c, char *name, int omode, ulong perm)
539 {
540 mntopencreate(Tcreate, c, name, omode, perm);
541 }
542
543 static void
544 mntclunk(Chan *c, int t)
545 {
546 Mnt *m;
547 Mntrpc *r;
548
549 m = mntchk(c);
550 r = mntralloc(c, m->msize);
551 if(waserror()){
552 mntfree(r);
553 nexterror();
554 }
555
556 r->request.type = t;
557 r->request.fid = c->fid;
558 mountrpc(m, r);
559 mntfree(r);
560 poperror();
561 }
562
563 void
564 muxclose(Mnt *m)
565 {
566 Mntrpc *q, *r;
567
568 for(q = m->queue; q; q = r) {
569 r = q->list;
570 mntfree(q);
571 }
572 m->id = 0;
573 free(m->version);
574 m->version = nil;
575 mntpntfree(m);
576 }
577
578 void
579 mntpntfree(Mnt *m)
580 {
581 Mnt *f, **l;
582 Queue *q;
583
584 lock(&mntalloc);
585 l = &mntalloc.list;
586 for(f = *l; f; f = f->list) {
587 if(f == m) {
588 *l = m->list;
589 break;
590 }
591 l = &f->list;
592 }
593 m->list = mntalloc.mntfree;
594 mntalloc.mntfree = m;
595 q = m->q;
596 unlock(&mntalloc);
597
598 qfree(q);
599 }
600
601 static void
602 mntclose(Chan *c)
603 {
604 mntclunk(c, Tclunk);
605 }
606
607 static void
608 mntremove(Chan *c)
609 {
610 mntclunk(c, Tremove);
611 }
612
613 static int
614 mntwstat(Chan *c, uchar *dp, int n)
615 {
616 Mnt *m;
617 Mntrpc *r;
618
619 m = mntchk(c);
620 r = mntralloc(c, m->msize);
621 if(waserror()) {
622 mntfree(r);
623 nexterror();
624 }
625 r->request.type = Twstat;
626 r->request.fid = c->fid;
627 r->request.nstat = n;
628 r->request.stat = dp;
629 mountrpc(m, r);
630 poperror();
631 mntfree(r);
632 return n;
633 }
634
635 static long
636 mntread(Chan *c, void *buf, long n, vlong off)
637 {
638 uchar *p, *e;
639 int nc, cache, isdir, dirlen;
640
641 isdir = 0;
642 cache = c->flag & CCACHE;
643 if(c->qid.type & QTDIR) {
644 cache = 0;
645 isdir = 1;
646 }
647
648 p = buf;
649 if(cache) {
650 nc = cread(c, buf, n, off);
651 if(nc > 0) {
652 n -= nc;
653 if(n == 0)
654 return nc;
655 p += nc;
656 off += nc;
657 }
658 n = mntrdwr(Tread, c, p, n, off);
659 cupdate(c, p, n, off);
660 return n + nc;
661 }
662
663 n = mntrdwr(Tread, c, buf, n, off);
664 if(isdir) {
665 for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
666 dirlen = BIT16SZ+GBIT16(p);
667 if(p+dirlen > e)
668 break;
669 validstat(p, dirlen);
670 mntdirfix(p, c);
671 }
672 if(p != e)
673 error(Esbadstat);
674 }
675 return n;
676 }
677
678 static long
679 mntwrite(Chan *c, void *buf, long n, vlong off)
680 {
681 return mntrdwr(Twrite, c, buf, n, off);
682 }
683
684 long
685 mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
686 {
687 Mnt *m;
688 Mntrpc *r;
689 char *uba;
690 int cache;
691 ulong cnt, nr, nreq;
692
693 m = mntchk(c);
694 uba = buf;
695 cnt = 0;
696 cache = c->flag & CCACHE;
697 if(c->qid.type & QTDIR)
698 cache = 0;
699 for(;;) {
700 r = mntralloc(c, m->msize);
701 if(waserror()) {
702 mntfree(r);
703 nexterror();
704 }
705 r->request.type = type;
706 r->request.fid = c->fid;
707 r->request.offset = off;
708 r->request.data = uba;
709 nr = n;
710 if(nr > m->msize-IOHDRSZ)
711 nr = m->msize-IOHDRSZ;
712 r->request.count = nr;
713 mountrpc(m, r);
714 nreq = r->request.count;
715 nr = r->reply.count;
716 if(nr > nreq)
717 nr = nreq;
718
719 if(type == Tread)
720 r->b = bl2mem((uchar*)uba, r->b, nr);
721 else if(cache)
722 cwrite(c, (uchar*)uba, nr, off);
723
724 poperror();
725 mntfree(r);
726 off += nr;
727 uba += nr;
728 cnt += nr;
729 n -= nr;
730 if(nr != nreq || n == 0 || up->nnote)
731 break;
732 }
733 return cnt;
734 }
735
736 void
737 mountrpc(Mnt *m, Mntrpc *r)
738 {
739 char *sn, *cn;
740 int t;
741
742 r->reply.tag = 0;
743 r->reply.type = Tmax; /* can't ever be a valid message type */
744
745 mountio(m, r);
746
747 t = r->reply.type;
748 switch(t) {
749 case Rerror:
750 error(r->reply.ename);
751 case Rflush:
752 error(Eintr);
753 default:
754 if(t == r->request.type+1)
755 break;
756 sn = "?";
757 if(m->c->path != nil)
758 sn = m->c->path->s;
759 cn = "?";
760 if(r->c != nil && r->c->path != nil)
761 cn = r->c->path->s;
762 print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
763 up->text, up->pid, sn, cn,
764 r, r->request.tag, r->request.fid, r->request.type,
765 r->reply.type, r->reply.tag);
766 error(Emountrpc);
767 }
768 }
769
770 void
771 mountio(Mnt *m, Mntrpc *r)
772 {
773 int n;
774
775 while(waserror()) {
776 if(m->rip == up)
777 mntgate(m);
778 if(strcmp(up->errstr, Eintr) != 0){
779 mntflushfree(m, r);
780 nexterror();
781 }
782 r = mntflushalloc(r, m->msize);
783 }
784
785 lock(m);
786 r->m = m;
787 r->list = m->queue;
788 m->queue = r;
789 unlock(m);
790
791 /* Transmit a file system rpc */
792 if(m->msize == 0)
793 panic("msize");
794 n = convS2M(&r->request, r->rpc, m->msize);
795 if(n < 0)
796 panic("bad message type in mountio");
797 if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
798 error(Emountrpc);
799 r->stime = fastticks(nil);
800 r->reqlen = n;
801
802 /* Gate readers onto the mount point one at a time */
803 for(;;) {
804 lock(m);
805 if(m->rip == 0)
806 break;
807 unlock(m);
808 sleep(&r->r, rpcattn, r);
809 if(r->done){
810 poperror();
811 mntflushfree(m, r);
812 return;
813 }
814 }
815 m->rip = up;
816 unlock(m);
817 while(r->done == 0) {
818 if(mntrpcread(m, r) < 0)
819 error(Emountrpc);
820 mountmux(m, r);
821 }
822 mntgate(m);
823 poperror();
824 mntflushfree(m, r);
825 }
826
827 static int
828 doread(Mnt *m, int len)
829 {
830 Block *b;
831
832 while(qlen(m->q) < len){
833 b = devtab[m->c->type]->bread(m->c, m->msize, 0);
834 if(b == nil)
835 return -1;
836 if(blocklen(b) == 0){
837 freeblist(b);
838 return -1;
839 }
840 qaddlist(m->q, b);
841 }
842 return 0;
843 }
844
845 int
846 mntrpcread(Mnt *m, Mntrpc *r)
847 {
848 int i, t, len, hlen;
849 Block *b, **l, *nb;
850
851 r->reply.type = 0;
852 r->reply.tag = 0;
853
854 /* read at least length, type, and tag and pullup to a single block */
855 if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
856 return -1;
857 nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
858
859 /* read in the rest of the message, avoid ridiculous (for now) message sizes */
860 len = GBIT32(nb->rp);
861 if(len > m->msize){
862 qdiscard(m->q, qlen(m->q));
863 return -1;
864 }
865 if(doread(m, len) < 0)
866 return -1;
867
868 /* pullup the header (i.e. everything except data) */
869 t = nb->rp[BIT32SZ];
870 switch(t){
871 case Rread:
872 hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
873 break;
874 default:
875 hlen = len;
876 break;
877 }
878 nb = pullupqueue(m->q, hlen);
879
880 if(convM2S(nb->rp, len, &r->reply) <= 0){
881 /* bad message, dump it */
882 print("mntrpcread: convM2S failed\n");
883 qdiscard(m->q, len);
884 return -1;
885 }
886
887 /* hang the data off of the fcall struct */
888 l = &r->b;
889 *l = nil;
890 do {
891 b = qremove(m->q);
892 if(hlen > 0){
893 b->rp += hlen;
894 len -= hlen;
895 hlen = 0;
896 }
897 i = BLEN(b);
898 if(i <= len){
899 len -= i;
900 *l = b;
901 l = &(b->next);
902 } else {
903 /* split block and put unused bit back */
904 nb = allocb(i-len);
905 memmove(nb->wp, b->rp+len, i-len);
906 b->wp = b->rp+len;
907 nb->wp += i-len;
908 qputback(m->q, nb);
909 *l = b;
910 return 0;
911 }
912 }while(len > 0);
913
914 return 0;
915 }
916
917 void
918 mntgate(Mnt *m)
919 {
920 Mntrpc *q;
921
922 lock(m);
923 m->rip = 0;
924 for(q = m->queue; q; q = q->list) {
925 if(q->done == 0)
926 if(wakeup(&q->r))
927 break;
928 }
929 unlock(m);
930 }
931
932 void
933 mountmux(Mnt *m, Mntrpc *r)
934 {
935 Mntrpc **l, *q;
936
937 lock(m);
938 l = &m->queue;
939 for(q = *l; q; q = q->list) {
940 /* look for a reply to a message */
941 if(q->request.tag == r->reply.tag) {
942 *l = q->list;
943 if(q != r) {
944 /*
945 * Completed someone else.
946 * Trade pointers to receive buffer.
947 */
948 q->reply = r->reply;
949 q->b = r->b;
950 r->b = nil;
951 }
952 q->done = 1;
953 unlock(m);
954 if(mntstats != nil)
955 (*mntstats)(q->request.type,
956 m->c, q->stime,
957 q->reqlen + r->replen);
958 if(q != r)
959 wakeup(&q->r);
960 return;
961 }
962 l = &q->list;
963 }
964 unlock(m);
965 print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
966 }
967
968 /*
969 * Create a new flush request and chain the previous
970 * requests from it
971 */
972 Mntrpc*
973 mntflushalloc(Mntrpc *r, ulong iounit)
974 {
975 Mntrpc *fr;
976
977 fr = mntralloc(0, iounit);
978
979 fr->request.type = Tflush;
980 if(r->request.type == Tflush)
981 fr->request.oldtag = r->request.oldtag;
982 else
983 fr->request.oldtag = r->request.tag;
984 fr->flushed = r;
985
986 return fr;
987 }
988
989 /*
990 * Free a chain of flushes. Remove each unanswered
991 * flush and the original message from the unanswered
992 * request queue. Mark the original message as done
993 * and if it hasn't been answered set the reply to to
994 * Rflush.
995 */
996 void
997 mntflushfree(Mnt *m, Mntrpc *r)
998 {
999 Mntrpc *fr;
1000
1001 while(r){
1002 fr = r->flushed;
1003 if(!r->done){
1004 r->reply.type = Rflush;
1005 mntqrm(m, r);
1006 }
1007 if(fr)
1008 mntfree(r);
1009 r = fr;
1010 }
1011 }
1012
1013 int
1014 alloctag(void)
1015 {
1016 int i, j;
1017 ulong v;
1018
1019 for(i = 0; i < NMASK; i++){
1020 v = mntalloc.tagmask[i];
1021 if(v == ~0UL)
1022 continue;
1023 for(j = 0; j < 1<<TAGSHIFT; j++)
1024 if((v & (1<<j)) == 0){
1025 mntalloc.tagmask[i] |= 1<<j;
1026 return (i<<TAGSHIFT) + j;
1027 }
1028 }
1029 panic("no friggin tags left");
1030 return NOTAG;
1031 }
1032
1033 void
1034 freetag(int t)
1035 {
1036 mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1037 }
1038
1039 Mntrpc*
1040 mntralloc(Chan *c, ulong msize)
1041 {
1042 Mntrpc *new;
1043
1044 lock(&mntalloc);
1045 new = mntalloc.rpcfree;
1046 if(new == nil){
1047 new = malloc(sizeof(Mntrpc));
1048 if(new == nil) {
1049 unlock(&mntalloc);
1050 exhausted("mount rpc header");
1051 }
1052 /*
1053 * The header is split from the data buffer as
1054 * mountmux may swap the buffer with another header.
1055 */
1056 new->rpc = mallocz(msize, 0);
1057 if(new->rpc == nil){
1058 free(new);
1059 unlock(&mntalloc);
1060 exhausted("mount rpc buffer");
1061 }
1062 new->rpclen = msize;
1063 new->request.tag = alloctag();
1064 }
1065 else {
1066 mntalloc.rpcfree = new->list;
1067 mntalloc.nrpcfree--;
1068 if(new->rpclen < msize){
1069 free(new->rpc);
1070 new->rpc = mallocz(msize, 0);
1071 if(new->rpc == nil){
1072 free(new);
1073 mntalloc.nrpcused--;
1074 unlock(&mntalloc);
1075 exhausted("mount rpc buffer");
1076 }
1077 new->rpclen = msize;
1078 }
1079 }
1080 mntalloc.nrpcused++;
1081 unlock(&mntalloc);
1082 new->c = c;
1083 new->done = 0;
1084 new->flushed = nil;
1085 new->b = nil;
1086 return new;
1087 }
1088
1089 void
1090 mntfree(Mntrpc *r)
1091 {
1092 if(r->b != nil)
1093 freeblist(r->b);
1094 lock(&mntalloc);
1095 if(mntalloc.nrpcfree >= 10){
1096 free(r->rpc);
1097 free(r);
1098 freetag(r->request.tag);
1099 }
1100 else{
1101 r->list = mntalloc.rpcfree;
1102 mntalloc.rpcfree = r;
1103 mntalloc.nrpcfree++;
1104 }
1105 mntalloc.nrpcused--;
1106 unlock(&mntalloc);
1107 }
1108
1109 void
1110 mntqrm(Mnt *m, Mntrpc *r)
1111 {
1112 Mntrpc **l, *f;
1113
1114 lock(m);
1115 r->done = 1;
1116
1117 l = &m->queue;
1118 for(f = *l; f; f = f->list) {
1119 if(f == r) {
1120 *l = r->list;
1121 break;
1122 }
1123 l = &f->list;
1124 }
1125 unlock(m);
1126 }
1127
1128 Mnt*
1129 mntchk(Chan *c)
1130 {
1131 Mnt *m;
1132
1133 /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1134
1135 if(c->mchan == nil)
1136 panic("mntchk 1: nil mchan c %s\n", chanpath(c));
1137
1138 m = c->mchan->mux;
1139
1140 if(m == nil)
1141 print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
1142
1143 /*
1144 * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
1145 */
1146 if(m->id == 0 || m->id >= c->dev)
1147 panic("mntchk 3: can't happen");
1148
1149 return m;
1150 }
1151
1152 /*
1153 * Rewrite channel type and dev for in-flight data to
1154 * reflect local values. These entries are known to be
1155 * the first two in the Dir encoding after the count.
1156 */
1157 void
1158 mntdirfix(uchar *dirbuf, Chan *c)
1159 {
1160 uint r;
1161
1162 r = devtab[c->type]->dc;
1163 dirbuf += BIT16SZ; /* skip count */
1164 PBIT16(dirbuf, r);
1165 dirbuf += BIT16SZ;
1166 PBIT32(dirbuf, c->dev);
1167 }
1168
1169 int
1170 rpcattn(void *v)
1171 {
1172 Mntrpc *r;
1173
1174 r = v;
1175 return r->done || r->m->rip == 0;
1176 }
1177
1178 Dev mntdevtab = {
1179 'M',
1180 "mnt",
1181
1182 mntreset,
1183 devinit,
1184 devshutdown,
1185 mntattach,
1186 mntwalk,
1187 mntstat,
1188 mntopen,
1189 mntcreate,
1190 mntclose,
1191 mntread,
1192 devbread,
1193 mntwrite,
1194 devbwrite,
1195 mntremove,
1196 mntwstat,
1197 };
Cache object: 94a5340ca147f55e33f53437570f77f4
|