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