qio: remove qproduce()
[akaros.git] / kern / drivers / dev / mnt.c
index 55cccec..b9036cc 100644 (file)
@@ -1,4 +1,31 @@
-// INFERNO
+/* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+ * Portions Copyright © 1997-1999 Vita Nuova Limited
+ * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
+ *                                (www.vitanuova.com)
+ * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+ *
+ * Modified for the Akaros operating system:
+ * Copyright (c) 2013-2014 The Regents of the University of California
+ * Copyright (c) 2013-2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
 #include <vfs.h>
 #include <kfs.h>
 #include <slab.h>
 #include <pmap.h>
 #include <smp.h>
 #include <ip.h>
+#include <smallidpool.h>
+
+struct dev mntdevtab;
+
+static char *devname(void)
+{
+       return mntdevtab.name;
+}
 
 /*
  * References are managed as follows:
@@ -25,6 +60,7 @@
  */
 
 #define MAXRPC (IOHDRSZ+8192)
+#define MAXTAG MAX_U16_POOL_SZ
 
 static __inline int isxdigit(int c)
 {
@@ -54,12 +90,6 @@ struct mntrpc {
        struct mntrpc *flushed;         /* message this one flushes */
 };
 
-enum {
-       TAGSHIFT = 5,                           /* uint32_t has to be 32 bits */
-       TAGMASK = (1 << TAGSHIFT) - 1,
-       NMASK = (64 * 1024) >> TAGSHIFT,
-};
-
 /* Our TRUNC and remove on close differ from 9ps, so we'll need to translate.
  * I got these flags from http://man.cat-v.org/plan_9/5/open */
 #define MNT_9P_OPEN_OTRUNC             0x10
@@ -73,7 +103,7 @@ struct Mntalloc {
        int nrpcfree;
        int nrpcused;
        uint32_t id;
-       uint32_t tagmask[NMASK];
+       struct u16_pool *tags;
 } mntalloc;
 
 void mattach(struct mnt *, struct chan *, char *unused_char_p_t);
@@ -94,16 +124,13 @@ void mountrpc(struct mnt *, struct mntrpc *);
 int rpcattn(void *);
 struct chan *mntchan(void);
 
-char Esbadstat[] = "invalid directory entry received from server";
-char Enoversion[] = "version not established for mount channel";
-
 void (*mntstats) (int unused_int, struct chan *, uint64_t, uint32_t);
 
 static void mntinit(void)
 {
        mntalloc.id = 1;
-       mntalloc.tagmask[0] = 1;        /* don't allow 0 as a tag */
-       mntalloc.tagmask[NMASK - 1] = 0x80000000UL;     /* don't allow NOTAG */
+       mntalloc.tags = create_u16_pool(MAXTAG);
+       (void) get_u16(mntalloc.tags);  /* don't allow 0 as a tag */
        //fmtinstall('F', fcallfmt);
 /*     fmtinstall('D', dirfmt); */
 /*     fmtinstall('M', dirmodefmt);  */
@@ -142,9 +169,9 @@ long mntversion(struct chan *c, char *version, int msize, int returnlen)
 
        /* validity */
        if (msize < 0)
-               error("bad iounit in version call");
+               error(EFAIL, "bad iounit in version call");
        if (strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
-               error("bad 9P version specification");
+               error(EFAIL, "bad 9P version specification");
 
        m = c->mux;
 
@@ -152,16 +179,16 @@ long mntversion(struct chan *c, char *version, int msize, int returnlen)
                qunlock(&c->umqlock);
                poperror();
 
-               strncpy(buf, m->version, sizeof buf);
+               strlcpy(buf, m->version, sizeof(buf));
                k = strlen(buf);
                if (strncmp(buf, v, k) != 0) {
                        snprintf(buf, sizeof buf, "incompatible 9P versions %s %s",
                                         m->version, v);
-                       error(buf);
+                       error(EFAIL, buf);
                }
                if (returnlen > 0) {
                        if (returnlen < k)
-                               error(Eshort);
+                               error(ENAMETOOLONG, ERROR_FIXME);
                        memmove(version, buf, k);
                }
                return k;
@@ -180,7 +207,7 @@ long mntversion(struct chan *c, char *version, int msize, int returnlen)
        }
        k = convS2M(&f, msg, 8192 + IOHDRSZ);
        if (k == 0)
-               error("bad fversion conversion on send");
+               error(EFAIL, "bad fversion conversion on send");
 
        spin_lock(&c->lock);
        oo = c->offset;
@@ -193,13 +220,13 @@ long mntversion(struct chan *c, char *version, int msize, int returnlen)
                spin_lock(&c->lock);
                c->offset -= k - l;
                spin_unlock(&c->lock);
-               error("short write in fversion");
+               error(EFAIL, "short write in fversion");
        }
 
        /* message sent; receive and decode reply */
        k = devtab[c->type].read(c, msg, 8192 + IOHDRSZ, c->offset);
        if (k <= 0)
-               error("EOF receiving fversion reply");
+               error(EFAIL, "EOF receiving fversion reply");
 
        spin_lock(&c->lock);
        c->offset += k;
@@ -207,18 +234,18 @@ long mntversion(struct chan *c, char *version, int msize, int returnlen)
 
        l = convM2S(msg, k, &f);
        if (l != k)
-               error("bad fversion conversion on reply");
+               error(EFAIL, "bad fversion conversion on reply");
        if (f.type != Rversion) {
                if (f.type == Rerror)
-                       error(f.ename);
-               error("unexpected reply type in fversion");
+                       error(EFAIL, f.ename);
+               error(EFAIL, "unexpected reply type in fversion");
        }
        if (f.msize > msize)
-               error("server tries to increase msize in fversion");
+               error(EFAIL, "server tries to increase msize in fversion");
        if (f.msize < 256 || f.msize > 1024 * 1024)
-               error("nonsense value of msize in fversion");
+               error(EFAIL, "nonsense value of msize in fversion");
        if (strncmp(f.version, v, strlen(f.version)) != 0)
-               error("bad 9P version returned from server");
+               error(EFAIL, "bad 9P version returned from server");
 
        /* now build Mnt associated with this connection */
        spin_lock(&mntalloc.l);
@@ -258,7 +285,7 @@ long mntversion(struct chan *c, char *version, int msize, int returnlen)
        k = strlen(f.version);
        if (returnlen > 0) {
                if (returnlen < k)
-                       error(Eshort);
+                       error(ENAMETOOLONG, ERROR_FIXME);
                memmove(version, f.version, k);
        }
 
@@ -277,7 +304,7 @@ struct chan *mntauth(struct chan *c, char *spec)
                mntversion(c, VERSION9P, MAXRPC, 0);
                m = c->mux;
                if (m == NULL)
-                       error(Enoversion);
+                       error(EINVAL, ERROR_FIXME);
        }
 
        c = mntchan();
@@ -304,9 +331,9 @@ struct chan *mntauth(struct chan *c, char *spec)
 
        c->qid = r->reply.aqid;
        c->mchan = m->c;
-       kref_get(&m->c->ref, 1);
+       chan_incref(m->c);
        c->mqid = c->qid;
-       c->mode = ORDWR;
+       c->mode = O_RDWR;
 
        poperror();     /* r */
        mntfree(r);
@@ -339,7 +366,7 @@ static struct chan *mntattach(char *muxattach)
                mntversion(c, NULL, 0, 0);
                m = c->mux;
                if (m == NULL)
-                       error(Enoversion);
+                       error(EINVAL, ERROR_FIXME);
        }
 
        c = mntchan();
@@ -370,7 +397,7 @@ static struct chan *mntattach(char *muxattach)
 
        c->qid = r->reply.qid;
        c->mchan = m->c;
-       kref_get(&m->c->ref, 1);
+       chan_incref(m->c);
        c->mqid = c->qid;
 
        poperror();     /* r */
@@ -387,7 +414,7 @@ struct chan *mntchan(void)
 {
        struct chan *c;
 
-       c = devattach('M', 0);
+       c = devattach(devname(), 0);
        spin_lock(&mntalloc.l);
        c->dev = mntalloc.id++;
        spin_unlock(&mntalloc.l);
@@ -410,10 +437,10 @@ static struct walkqid *mntwalk(struct chan *c, struct chan *nc, char **name,
        if (nc != NULL)
                printd("mntwalk: nc != NULL\n");
        if (nname > MAXWELEM)
-               error("devmnt: too many name elements");
+               error(EFAIL, "devmnt: too many name elements");
        alloc = 0;
        wq = kzmalloc(sizeof(struct walkqid) + nname * sizeof(struct qid),
-                                 KMALLOC_WAIT);
+                                 MEM_WAIT);
        if (waserror()) {
                if (alloc && wq->clone != NULL)
                        cclose(wq->clone);
@@ -448,7 +475,7 @@ static struct walkqid *mntwalk(struct chan *c, struct chan *nc, char **name,
        mountrpc(m, r);
 
        if (r->reply.nwqid > nname)
-               error("too many QIDs returned by walk");
+               error(EFAIL, "too many QIDs returned by walk");
        if (r->reply.nwqid < nname) {
                if (alloc)
                        cclose(nc);
@@ -465,7 +492,7 @@ static struct walkqid *mntwalk(struct chan *c, struct chan *nc, char **name,
                if (wq->clone != c) {
                        wq->clone->type = c->type;
                        wq->clone->mchan = c->mchan;
-                       kref_get(&c->mchan->ref, 1);
+                       chan_incref(c->mchan);
                }
                if (r->reply.nwqid > 0)
                        wq->clone->qid = r->reply.wqid[r->reply.nwqid - 1];
@@ -488,7 +515,7 @@ static int mntstat(struct chan *c, uint8_t * dp, int n)
        struct mntrpc *r;
 
        if (n < BIT16SZ)
-               error(Eshortstat);
+               error(EINVAL, ERROR_FIXME);
        m = mntchk(c);
        r = mntralloc(c, m->msize);
        if (waserror()) {
@@ -506,7 +533,7 @@ static int mntstat(struct chan *c, uint8_t * dp, int n)
        } else {
                n = r->reply.nstat;
                memmove(dp, r->reply.stat, n);
-               validstat(dp, n);
+               validstat(dp, n, 0);
                mntdirfix(dp, c);
        }
        poperror();
@@ -529,7 +556,7 @@ static struct chan *mntopencreate(int type, struct chan *c, char *name,
        }
        r->request.type = type;
        r->request.fid = c->fid;
-       r->request.mode = omode & O_ACCMODE;    /* not using openmode, want O_EXEC */
+       r->request.mode = omode_to_9p_accmode(omode);
        if (omode & O_TRUNC)
                r->request.mode |= MNT_9P_OPEN_OTRUNC;
        if (omode & O_REMCLO)
@@ -654,10 +681,17 @@ static int mntwstat(struct chan *c, uint8_t * dp, int n)
        return n;
 }
 
+/* the servers should either return units of whole directory entries
+ * OR support seeking to an arbitrary place. One or other.
+ * Both are fine, but at least one is a minimum.
+ * If the return a partial result, but more than one result,
+ * we'll return a shorter read and the next offset will be aligned
+ */
 static long mntread(struct chan *c, void *buf, long n, int64_t off)
 {
        uint8_t *p, *e;
        int nc, cache, isdir, dirlen;
+       int numdirent = 0;
 
        isdir = 0;
        cache = c->flag & CCACHE;
@@ -682,16 +716,24 @@ static long mntread(struct chan *c, void *buf, long n, int64_t off)
        }
 
        n = mntrdwr(Tread, c, buf, n, off);
+
        if (isdir) {
                for (e = &p[n]; p + BIT16SZ < e; p += dirlen) {
                        dirlen = BIT16SZ + GBIT16(p);
-                       if (p + dirlen > e)
+                       if (p + dirlen > e){
                                break;
-                       validstat(p, dirlen);
+                       }
+                       validstat(p, dirlen, 0);
                        mntdirfix(p, c);
+                       numdirent += dirlen;
+               }
+               if (p != e) {
+                       //error(Esbadstat);
+                       /* not really. Maybe the server supports
+                        * arbitrary seek like go9p now does.
+                        */
+                       n = numdirent;
                }
-               if (p != e)
-                       error(Esbadstat);
        }
        return n;
 }
@@ -776,13 +818,14 @@ void mountrpc(struct mnt *m, struct mntrpc *r)
                                isxdigit(e[1]) &&
                                isxdigit(e[2]) &&
                                isxdigit(e[3])) {
+
                                int errno = strtoul(e, NULL, 16);
-                               set_errno(errno);
-                               error(&r->reply.ename[5]);
+
+                               error(errno, &r->reply.ename[5]);
                        } else
-                               error(r->reply.ename);
+                               error(EFAIL, r->reply.ename);
                case Rflush:
-                       error(Eintr);
+                       error(EINTR, ERROR_FIXME);
                default:
                        if (t == r->request.type + 1)
                                break;
@@ -796,7 +839,7 @@ void mountrpc(struct mnt *m, struct mntrpc *r)
                                ("mnt: proc %s %lu: mismatch from %s %s rep 0x%p tag %d fid %d T%d R%d rp %d\n",
                                 "current->text", "current->pid", sn, cn, r, r->request.tag,
                                 r->request.fid, r->request.type, r->reply.type, r->reply.tag);
-                       error(Emountrpc);
+                       error(EPROTO, ERROR_FIXME);
        }
 }
 
@@ -808,14 +851,12 @@ void mountio(struct mnt *m, struct mntrpc *r)
        while (waserror()) {
                if (m->rip == current)
                        mntgate(m);
-               if (!strcmp(current_errstr(), "syscall aborted")) {
-                       /* not sure what devmnt wants us to do here.  bail on aborted
-                        * syscall?  keep looping forever? (probably not) */
-                       printk("[kernel] mountio had aborted syscall");
-                       mntflushfree(m, r);
-                       nexterror();
-               }
-               if (strcmp(current_errstr(), Eintr) != 0) {
+               /* Syscall aborts are like Plan 9 Eintr.  For those, we need to change
+                * the old request to a flsh (mntflushalloc) and try again.  We'll
+                * always try to flush, and you can't get out until the flush either
+                * succeeds or errors out with a non-abort/Eintr error. */
+               if (get_errno() != EINTR) {
+                       /* all other errors (not abort or Eintr) */
                        mntflushfree(m, r);
                        nexterror();
                }
@@ -837,7 +878,7 @@ void mountio(struct mnt *m, struct mntrpc *r)
        if (n < 0)
                panic("bad message type in mountio");
        if (devtab[m->c->type].write(m->c, r->rpc, n, 0) != n)
-               error(Emountrpc);
+               error(EIO, ERROR_FIXME);
 /*     r->stime = fastticks(NULL); */
        r->reqlen = n;
 
@@ -858,7 +899,7 @@ void mountio(struct mnt *m, struct mntrpc *r)
        spin_unlock(&m->lock);
        while (r->done == 0) {
                if (mntrpcread(m, r) < 0)
-                       error(Emountrpc);
+                       error(EIO, ERROR_FIXME);
                mountmux(m, r);
        }
        mntgate(m);
@@ -929,6 +970,8 @@ int mntrpcread(struct mnt *m, struct mntrpc *r)
        *l = NULL;
        do {
                b = qremove(m->q);
+               /* TODO: have better block helpers for this and the memmove below */
+               b = linearizeblock(b);
                if (hlen > 0) {
                        b->rp += hlen;
                        len -= hlen;
@@ -941,7 +984,7 @@ int mntrpcread(struct mnt *m, struct mntrpc *r)
                        l = &(b->next);
                } else {
                        /* split block and put unused bit back */
-                       nb = allocb(i - len);
+                       nb = block_alloc(i - len, MEM_WAIT);
                        memmove(nb->wp, b->rp + len, i - len);
                        b->wp = b->rp + len;
                        nb->wp += i - len;
@@ -1053,26 +1096,12 @@ void mntflushfree(struct mnt *m, struct mntrpc *r)
 
 static int alloctag(void)
 {
-       int i, j;
-       uint32_t v;
-
-       for (i = 0; i < NMASK; i++) {
-               v = mntalloc.tagmask[i];
-               if (v == ~0UL)
-                       continue;
-               for (j = 0; j < 1 << TAGSHIFT; j++)
-                       if ((v & (1 << j)) == 0) {
-                               mntalloc.tagmask[i] |= 1 << j;
-                               return (i << TAGSHIFT) + j;
-                       }
-       }
-       /* panic("no devmnt tags left"); */
-       return NOTAG;
+       return get_u16(mntalloc.tags);
 }
 
 static void freetag(int t)
 {
-       mntalloc.tagmask[t >> TAGSHIFT] &= ~(1 << (t & TAGMASK));
+       put_u16(mntalloc.tags, t);
 }
 
 struct mntrpc *mntralloc(struct chan *c, uint32_t msize)
@@ -1092,7 +1121,7 @@ struct mntrpc *mntralloc(struct chan *c, uint32_t msize)
                 * The header is split from the data buffer as
                 * mountmux may swap the buffer with another header.
                 */
-               new->rpc = kzmalloc(msize, KMALLOC_WAIT);
+               new->rpc = kzmalloc(msize, MEM_WAIT);
                if (new->rpc == NULL) {
                        kfree(new);
                        spin_unlock(&mntalloc.l);
@@ -1110,7 +1139,7 @@ struct mntrpc *mntralloc(struct chan *c, uint32_t msize)
                mntalloc.nrpcfree--;
                if (new->rpclen < msize) {
                        kfree(new->rpc);
-                       new->rpc = kzmalloc(msize, KMALLOC_WAIT);
+                       new->rpc = kzmalloc(msize, MEM_WAIT);
                        if (new->rpc == NULL) {
                                kfree(new);
                                mntalloc.nrpcused--;
@@ -1196,11 +1225,13 @@ struct mnt *mntchk(struct chan *c)
  */
 void mntdirfix(uint8_t * dirbuf, struct chan *c)
 {
-       unsigned int r;
-
-       r = devtab[c->type].dc;
+       /* TODO: We used to use the device's char (dc), instead of the type.  not
+        * sure about the effects one way or the other.  This might be the type[2]
+        * and dev[4] in a D (struct dir, see 9p's stat
+        * (http://man.cat-v.org/plan_9/5/stat).  In which case, those should be for
+        * the kernel's use.  Hopefully our kernel. */
        dirbuf += BIT16SZ;      /* skip count */
-       PBIT16(dirbuf, r);
+       PBIT16(dirbuf, c->type);
        dirbuf += BIT16SZ;
        PBIT32(dirbuf, c->dev);
 }
@@ -1214,24 +1245,23 @@ int rpcattn(void *v)
 }
 
 struct dev mntdevtab __devtab = {
-       'M',
-       "mnt",
-
-       devreset,
-       mntinit,
-       devshutdown,
-       mntattach,
-       mntwalk,
-       mntstat,
-       mntopen,
-       mntcreate,
-       mntclose,
-       mntread,
-       devbread,
-       mntwrite,
-       devbwrite,
-       mntremove,
-       mntwstat,
-       devpower,
-       devchaninfo,
+       .name = "mnt",
+
+       .reset = devreset,
+       .init = mntinit,
+       .shutdown = devshutdown,
+       .attach = mntattach,
+       .walk = mntwalk,
+       .stat = mntstat,
+       .open = mntopen,
+       .create = mntcreate,
+       .close = mntclose,
+       .read = mntread,
+       .bread = devbread,
+       .write = mntwrite,
+       .bwrite = devbwrite,
+       .remove = mntremove,
+       .wstat = mntwstat,
+       .power = devpower,
+       .chaninfo = devchaninfo,
 };