]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devmnt.c
reduce software cursor flickering
[plan9front.git] / sys / src / 9 / 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* z;              /* Place to hang out */
29         uchar*  rpc;            /* I/O Data buffer */
30         uint    rpclen;         /* len of buffer */
31         Block*  b;              /* reply blocks */
32         uvlong  stime;          /* start time for mnt statistics */
33         ulong   reqlen;         /* request length for mnt statistics */
34         ulong   replen;         /* reply length for mnt statistics */
35         Mntrpc* flushed;        /* message this one flushes */
36         char    done;           /* Rpc completed */
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 void    (*mntstats)(int, Chan*, uvlong, ulong);
79
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         eqlock(&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                 nc->flag |= (c->flag & CCACHE);
413                 alloc = 1;
414         }
415         wq->clone = nc;
416
417         if(waserror()) {
418                 mntfree(r);
419                 nexterror();
420         }
421         r->request.type = Twalk;
422         r->request.fid = c->fid;
423         r->request.newfid = nc->fid;
424         r->request.nwname = nname;
425         memmove(r->request.wname, name, nname*sizeof(char*));
426
427         mountrpc(m, r);
428
429         if(r->reply.nwqid > nname)
430                 error("too many QIDs returned by walk");
431         if(r->reply.nwqid < nname){
432                 if(alloc)
433                         cclose(nc);
434                 wq->clone = nil;
435                 if(r->reply.nwqid == 0){
436                         free(wq);
437                         wq = nil;
438                         goto Return;
439                 }
440         }
441
442         /* move new fid onto mnt device and update its qid */
443         if(wq->clone != nil){
444                 if(wq->clone != c){
445                         wq->clone->type = c->type;
446                         wq->clone->mchan = c->mchan;
447                         incref(c->mchan);
448                 }
449                 if(r->reply.nwqid > 0)
450                         wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
451         }
452         wq->nqid = r->reply.nwqid;
453         for(i=0; i<wq->nqid; i++)
454                 wq->qid[i] = r->reply.wqid[i];
455
456     Return:
457         poperror();
458         mntfree(r);
459         poperror();
460         return wq;
461 }
462
463 static int
464 mntstat(Chan *c, uchar *dp, int n)
465 {
466         Mnt *m;
467         Mntrpc *r;
468
469         if(n < BIT16SZ)
470                 error(Eshortstat);
471         m = mntchk(c);
472         r = mntralloc(c, m->msize);
473         if(waserror()) {
474                 mntfree(r);
475                 nexterror();
476         }
477         r->request.type = Tstat;
478         r->request.fid = c->fid;
479         mountrpc(m, r);
480
481         if(r->reply.nstat > n){
482                 n = BIT16SZ;
483                 PBIT16((uchar*)dp, r->reply.nstat-2);
484         }else{
485                 n = r->reply.nstat;
486                 memmove(dp, r->reply.stat, n);
487                 validstat(dp, n);
488                 mntdirfix(dp, c);
489         }
490         poperror();
491         mntfree(r);
492         return n;
493 }
494
495 static Chan*
496 mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
497 {
498         Mnt *m;
499         Mntrpc *r;
500
501         m = mntchk(c);
502         r = mntralloc(c, m->msize);
503         if(waserror()) {
504                 mntfree(r);
505                 nexterror();
506         }
507         r->request.type = type;
508         r->request.fid = c->fid;
509         r->request.mode = omode;
510         if(type == Tcreate){
511                 r->request.perm = perm;
512                 r->request.name = name;
513         }
514         mountrpc(m, r);
515
516         c->qid = r->reply.qid;
517         c->offset = 0;
518         c->mode = openmode(omode);
519         c->iounit = r->reply.iounit;
520         if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
521                 c->iounit = m->msize-IOHDRSZ;
522         c->flag |= COPEN;
523         poperror();
524         mntfree(r);
525
526         if(c->flag & CCACHE)
527                 copen(c);
528
529         return c;
530 }
531
532 static Chan*
533 mntopen(Chan *c, int omode)
534 {
535         return mntopencreate(Topen, c, nil, omode, 0);
536 }
537
538 static Chan*
539 mntcreate(Chan *c, char *name, int omode, ulong perm)
540 {
541         return mntopencreate(Tcreate, c, name, omode, perm);
542 }
543
544 static void
545 mntclunk(Chan *c, int t)
546 {
547         Mnt *m;
548         Mntrpc *r;
549
550         m = mntchk(c);
551         r = mntralloc(c, m->msize);
552         if(waserror()){
553                 mntfree(r);
554                 nexterror();
555         }
556
557         r->request.type = t;
558         r->request.fid = c->fid;
559         mountrpc(m, r);
560         mntfree(r);
561         poperror();
562 }
563
564 void
565 muxclose(Mnt *m)
566 {
567         Mntrpc *q, *r;
568
569         for(q = m->queue; q; q = r) {
570                 r = q->list;
571                 mntfree(q);
572         }
573         m->id = 0;
574         free(m->version);
575         m->version = nil;
576         mntpntfree(m);
577 }
578
579 void
580 mntpntfree(Mnt *m)
581 {
582         Mnt *f, **l;
583         Queue *q;
584
585         lock(&mntalloc);
586         l = &mntalloc.list;
587         for(f = *l; f; f = f->list) {
588                 if(f == m) {
589                         *l = m->list;
590                         break;
591                 }
592                 l = &f->list;
593         }
594         m->list = mntalloc.mntfree;
595         mntalloc.mntfree = m;
596         q = m->q;
597         unlock(&mntalloc);
598
599         qfree(q);
600 }
601
602 static void
603 mntclose(Chan *c)
604 {
605         mntclunk(c, Tclunk);
606 }
607
608 static void
609 mntremove(Chan *c)
610 {
611         mntclunk(c, Tremove);
612 }
613
614 static int
615 mntwstat(Chan *c, uchar *dp, int n)
616 {
617         Mnt *m;
618         Mntrpc *r;
619
620         m = mntchk(c);
621         r = mntralloc(c, m->msize);
622         if(waserror()) {
623                 mntfree(r);
624                 nexterror();
625         }
626         r->request.type = Twstat;
627         r->request.fid = c->fid;
628         r->request.nstat = n;
629         r->request.stat = dp;
630         mountrpc(m, r);
631         poperror();
632         mntfree(r);
633         return n;
634 }
635
636 static long
637 mntread(Chan *c, void *buf, long n, vlong off)
638 {
639         uchar *p, *e;
640         int nc, cache, isdir, dirlen;
641
642         isdir = 0;
643         cache = c->flag & CCACHE;
644         if(c->qid.type & QTDIR) {
645                 cache = 0;
646                 isdir = 1;
647         }
648
649         p = buf;
650         if(cache) {
651                 nc = cread(c, buf, n, off);
652                 if(nc > 0) {
653                         n -= nc;
654                         if(n == 0)
655                                 return nc;
656                         p += nc;
657                         off += nc;
658                 }
659                 n = mntrdwr(Tread, c, p, n, off);
660                 cupdate(c, p, n, off);
661                 return n + nc;
662         }
663
664         n = mntrdwr(Tread, c, buf, n, off);
665         if(isdir) {
666                 for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
667                         dirlen = BIT16SZ+GBIT16(p);
668                         if(p+dirlen > e)
669                                 break;
670                         validstat(p, dirlen);
671                         mntdirfix(p, c);
672                 }
673                 if(p != e)
674                         error(Esbadstat);
675         }
676         return n;
677 }
678
679 static long
680 mntwrite(Chan *c, void *buf, long n, vlong off)
681 {
682         return mntrdwr(Twrite, c, buf, n, off);
683 }
684
685 long
686 mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
687 {
688         Mnt *m;
689         Mntrpc *r;
690         char *uba;
691         int cache;
692         ulong cnt, nr, nreq;
693
694         m = mntchk(c);
695         uba = buf;
696         cnt = 0;
697         cache = c->flag & CCACHE;
698         if(c->qid.type & QTDIR)
699                 cache = 0;
700         for(;;) {
701                 r = mntralloc(c, m->msize);
702                 if(waserror()) {
703                         mntfree(r);
704                         nexterror();
705                 }
706                 r->request.type = type;
707                 r->request.fid = c->fid;
708                 r->request.offset = off;
709                 r->request.data = uba;
710                 nr = n;
711                 if(nr > m->msize-IOHDRSZ)
712                         nr = m->msize-IOHDRSZ;
713                 r->request.count = nr;
714                 mountrpc(m, r);
715                 nreq = r->request.count;
716                 nr = r->reply.count;
717                 if(nr > nreq)
718                         nr = nreq;
719
720                 if(type == Tread)
721                         r->b = bl2mem((uchar*)uba, r->b, nr);
722                 else if(cache)
723                         cwrite(c, (uchar*)uba, nr, off);
724
725                 poperror();
726                 mntfree(r);
727                 off += nr;
728                 uba += nr;
729                 cnt += nr;
730                 n -= nr;
731                 if(nr != nreq || n == 0 || up->nnote)
732                         break;
733         }
734         return cnt;
735 }
736
737 void
738 mountrpc(Mnt *m, Mntrpc *r)
739 {
740         char *sn, *cn;
741         int t;
742
743         r->reply.tag = 0;
744         r->reply.type = Tmax;   /* can't ever be a valid message type */
745
746         mountio(m, r);
747
748         t = r->reply.type;
749         switch(t) {
750         case Rerror:
751                 error(r->reply.ename);
752         case Rflush:
753                 error(Eintr);
754         default:
755                 if(t == r->request.type+1)
756                         break;
757                 sn = "?";
758                 if(m->c->path != nil)
759                         sn = m->c->path->s;
760                 cn = "?";
761                 if(r->c != nil && r->c->path != nil)
762                         cn = r->c->path->s;
763                 print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
764                         up->text, up->pid, sn, cn,
765                         r, r->request.tag, r->request.fid, r->request.type,
766                         r->reply.type, r->reply.tag);
767                 error(Emountrpc);
768         }
769 }
770
771 void
772 mountio(Mnt *m, Mntrpc *r)
773 {
774         int n;
775
776         while(waserror()) {
777                 if(m->rip == up)
778                         mntgate(m);
779                 if(strcmp(up->errstr, Eintr) != 0){
780                         mntflushfree(m, r);
781                         nexterror();
782                 }
783                 r = mntflushalloc(r, m->msize);
784         }
785
786         lock(m);
787         r->z = &up->sleep;
788         r->m = m;
789         r->list = m->queue;
790         m->queue = r;
791         unlock(m);
792
793         /* Transmit a file system rpc */
794         if(m->msize == 0)
795                 panic("msize");
796         n = convS2M(&r->request, r->rpc, m->msize);
797         if(n < 0)
798                 panic("bad message type in mountio");
799         if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
800                 error(Emountrpc);
801         r->stime = fastticks(nil);
802         r->reqlen = n;
803
804         /* Gate readers onto the mount point one at a time */
805         for(;;) {
806                 lock(m);
807                 if(m->rip == 0)
808                         break;
809                 unlock(m);
810                 sleep(r->z, rpcattn, r);
811                 if(r->done){
812                         poperror();
813                         mntflushfree(m, r);
814                         return;
815                 }
816         }
817         m->rip = up;
818         unlock(m);
819         while(r->done == 0) {
820                 if(mntrpcread(m, r) < 0)
821                         error(Emountrpc);
822                 mountmux(m, r);
823         }
824         mntgate(m);
825         poperror();
826         mntflushfree(m, r);
827 }
828
829 static int
830 doread(Mnt *m, int len)
831 {
832         Block *b;
833
834         while(qlen(m->q) < len){
835                 b = devtab[m->c->type]->bread(m->c, m->msize, 0);
836                 if(b == nil)
837                         return -1;
838                 if(blocklen(b) == 0){
839                         freeblist(b);
840                         return -1;
841                 }
842                 qaddlist(m->q, b);
843         }
844         return 0;
845 }
846
847 int
848 mntrpcread(Mnt *m, Mntrpc *r)
849 {
850         int i, t, len, hlen;
851         Block *b, **l, *nb;
852
853         r->reply.type = 0;
854         r->reply.tag = 0;
855
856         /* read at least length, type, and tag and pullup to a single block */
857         if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
858                 return -1;
859         nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
860
861         /* read in the rest of the message, avoid ridiculous (for now) message sizes */
862         len = GBIT32(nb->rp);
863         if(len > m->msize){
864                 qdiscard(m->q, qlen(m->q));
865                 return -1;
866         }
867         if(doread(m, len) < 0)
868                 return -1;
869
870         /* pullup the header (i.e. everything except data) */
871         t = nb->rp[BIT32SZ];
872         switch(t){
873         case Rread:
874                 hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
875                 break;
876         default:
877                 hlen = len;
878                 break;
879         }
880         nb = pullupqueue(m->q, hlen);
881
882         if(convM2S(nb->rp, len, &r->reply) <= 0){
883                 /* bad message, dump it */
884                 print("mntrpcread: convM2S failed\n");
885                 qdiscard(m->q, len);
886                 return -1;
887         }
888
889         /* hang the data off of the fcall struct */
890         l = &r->b;
891         *l = nil;
892         do {
893                 b = qremove(m->q);
894                 if(hlen > 0){
895                         b->rp += hlen;
896                         len -= hlen;
897                         hlen = 0;
898                 }
899                 i = BLEN(b);
900                 if(i <= len){
901                         len -= i;
902                         *l = b;
903                         l = &(b->next);
904                 } else {
905                         /* split block and put unused bit back */
906                         nb = allocb(i-len);
907                         memmove(nb->wp, b->rp+len, i-len);
908                         b->wp = b->rp+len;
909                         nb->wp += i-len;
910                         qputback(m->q, nb);
911                         *l = b;
912                         return 0;
913                 }
914         }while(len > 0);
915
916         return 0;
917 }
918
919 void
920 mntgate(Mnt *m)
921 {
922         Mntrpc *q;
923
924         lock(m);
925         m->rip = 0;
926         for(q = m->queue; q; q = q->list) {
927                 if(q->done == 0)
928                 if(wakeup(q->z))
929                         break;
930         }
931         unlock(m);
932 }
933
934 void
935 mountmux(Mnt *m, Mntrpc *r)
936 {
937         Mntrpc **l, *q;
938         Rendez *z;
939
940         lock(m);
941         l = &m->queue;
942         for(q = *l; q; q = q->list) {
943                 /* look for a reply to a message */
944                 if(q->request.tag == r->reply.tag) {
945                         *l = q->list;
946                         if(mntstats != nil)
947                                 (*mntstats)(q->request.type,
948                                         m->c, q->stime,
949                                         q->reqlen + r->replen);
950                         if(q != r) {
951                                 /*
952                                  * Completed someone else.
953                                  * Trade pointers to receive buffer.
954                                  */
955                                 q->reply = r->reply;
956                                 q->b = r->b;
957                                 r->b = nil;
958                                 z = q->z;
959                         } else
960                                 z = nil;
961                         q->done = 1;    /* hands off */
962                         if(z != nil)
963                                 wakeup(z);
964                         unlock(m);
965                         return;
966                 }
967                 l = &q->list;
968         }
969         unlock(m);
970         print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
971 }
972
973 /*
974  * Create a new flush request and chain the previous
975  * requests from it
976  */
977 Mntrpc*
978 mntflushalloc(Mntrpc *r, ulong iounit)
979 {
980         Mntrpc *fr;
981
982         fr = mntralloc(0, iounit);
983
984         fr->request.type = Tflush;
985         if(r->request.type == Tflush)
986                 fr->request.oldtag = r->request.oldtag;
987         else
988                 fr->request.oldtag = r->request.tag;
989         fr->flushed = r;
990
991         return fr;
992 }
993
994 /*
995  *  Free a chain of flushes.  Remove each unanswered
996  *  flush and the original message from the unanswered
997  *  request queue.  Mark the original message as done
998  *  and if it hasn't been answered set the reply to to
999  *  Rflush.
1000  */
1001 void
1002 mntflushfree(Mnt *m, Mntrpc *r)
1003 {
1004         Mntrpc *fr;
1005
1006         while(r){
1007                 fr = r->flushed;
1008                 if(!r->done){
1009                         r->reply.type = Rflush;
1010                         mntqrm(m, r);
1011                 }
1012                 if(fr)
1013                         mntfree(r);
1014                 r = fr;
1015         }
1016 }
1017
1018 int
1019 alloctag(void)
1020 {
1021         int i, j;
1022         ulong v;
1023
1024         for(i = 0; i < NMASK; i++){
1025                 v = mntalloc.tagmask[i];
1026                 if(v == ~0UL)
1027                         continue;
1028                 for(j = 0; j < 1<<TAGSHIFT; j++)
1029                         if((v & (1<<j)) == 0){
1030                                 mntalloc.tagmask[i] |= 1<<j;
1031                                 return (i<<TAGSHIFT) + j;
1032                         }
1033         }
1034         panic("no friggin tags left");
1035         return NOTAG;
1036 }
1037
1038 void
1039 freetag(int t)
1040 {
1041         mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1042 }
1043
1044 Mntrpc*
1045 mntralloc(Chan *c, ulong msize)
1046 {
1047         Mntrpc *new;
1048
1049         lock(&mntalloc);
1050         new = mntalloc.rpcfree;
1051         if(new == nil){
1052                 new = malloc(sizeof(Mntrpc));
1053                 if(new == nil) {
1054                         unlock(&mntalloc);
1055                         exhausted("mount rpc header");
1056                 }
1057                 /*
1058                  * The header is split from the data buffer as
1059                  * mountmux may swap the buffer with another header.
1060                  */
1061                 new->rpc = mallocz(msize, 0);
1062                 if(new->rpc == nil){
1063                         free(new);
1064                         unlock(&mntalloc);
1065                         exhausted("mount rpc buffer");
1066                 }
1067                 new->rpclen = msize;
1068                 new->request.tag = alloctag();
1069         }
1070         else {
1071                 mntalloc.rpcfree = new->list;
1072                 mntalloc.nrpcfree--;
1073                 if(new->rpclen < msize){
1074                         free(new->rpc);
1075                         new->rpc = mallocz(msize, 0);
1076                         if(new->rpc == nil){
1077                                 free(new);
1078                                 mntalloc.nrpcused--;
1079                                 unlock(&mntalloc);
1080                                 exhausted("mount rpc buffer");
1081                         }
1082                         new->rpclen = msize;
1083                 }
1084         }
1085         mntalloc.nrpcused++;
1086         unlock(&mntalloc);
1087         new->c = c;
1088         new->done = 0;
1089         new->flushed = nil;
1090         new->b = nil;
1091         return new;
1092 }
1093
1094 void
1095 mntfree(Mntrpc *r)
1096 {
1097         if(r->b != nil)
1098                 freeblist(r->b);
1099         lock(&mntalloc);
1100         if(mntalloc.nrpcfree >= 10){
1101                 free(r->rpc);
1102                 freetag(r->request.tag);
1103                 free(r);
1104         }
1105         else{
1106                 r->list = mntalloc.rpcfree;
1107                 mntalloc.rpcfree = r;
1108                 mntalloc.nrpcfree++;
1109         }
1110         mntalloc.nrpcused--;
1111         unlock(&mntalloc);
1112 }
1113
1114 void
1115 mntqrm(Mnt *m, Mntrpc *r)
1116 {
1117         Mntrpc **l, *f;
1118
1119         lock(m);
1120         r->done = 1;
1121
1122         l = &m->queue;
1123         for(f = *l; f; f = f->list) {
1124                 if(f == r) {
1125                         *l = r->list;
1126                         break;
1127                 }
1128                 l = &f->list;
1129         }
1130         unlock(m);
1131 }
1132
1133 Mnt*
1134 mntchk(Chan *c)
1135 {
1136         Mnt *m;
1137
1138         /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1139
1140         if(c->mchan == nil)
1141                 panic("mntchk 1: nil mchan c %s", chanpath(c));
1142
1143         m = c->mchan->mux;
1144
1145         if(m == nil)
1146                 print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
1147
1148         /*
1149          * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
1150          */
1151         if(m->id == 0 || m->id >= c->dev)
1152                 panic("mntchk 3: can't happen");
1153
1154         return m;
1155 }
1156
1157 /*
1158  * Rewrite channel type and dev for in-flight data to
1159  * reflect local values.  These entries are known to be
1160  * the first two in the Dir encoding after the count.
1161  */
1162 void
1163 mntdirfix(uchar *dirbuf, Chan *c)
1164 {
1165         uint r;
1166
1167         r = devtab[c->type]->dc;
1168         dirbuf += BIT16SZ;      /* skip count */
1169         PBIT16(dirbuf, r);
1170         dirbuf += BIT16SZ;
1171         PBIT32(dirbuf, c->dev);
1172 }
1173
1174 int
1175 rpcattn(void *v)
1176 {
1177         Mntrpc *r;
1178
1179         r = v;
1180         return r->done || r->m->rip == 0;
1181 }
1182
1183 Dev mntdevtab = {
1184         'M',
1185         "mnt",
1186
1187         mntreset,
1188         devinit,
1189         devshutdown,
1190         mntattach,
1191         mntwalk,
1192         mntstat,
1193         mntopen,
1194         mntcreate,
1195         mntclose,
1196         mntread,
1197         devbread,
1198         mntwrite,
1199         devbwrite,
1200         mntremove,
1201         mntwstat,
1202 };