Added explicit errno reporting from error() API.
[akaros.git] / kern / drivers / dev / ether.c
index 2dfa58d..b45ee98 100644 (file)
 #include <smp.h>
 #include <ip.h>
 
-#include <vfs.h>
-#include <kfs.h>
-#include <slab.h>
-#include <kmalloc.h>
-#include <kref.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#include <error.h>
-#include <cpio.h>
-#include <pmap.h>
-#include <smp.h>
-#include <ip.h>
+struct dev etherdevtab;
+
+static char *devname(void)
+{
+       return etherdevtab.name;
+}
 
 enum {
        Type8021Q = 0x8100,                     /* value of type field for 802.1[pQ] tags */
@@ -51,15 +44,15 @@ struct chan *etherattach(char *spec)
                /* somebody interpret this for me. */
                if (((ctlrno == 0) && (p == spec)) ||
                        (ctlrno >= MaxEther) || ((*p) && (*p != '.')))
-                       error(Ebadarg);
+                       error(EINVAL, NULL);
                if (*p == '.') {        /* vlan */
                        vlanid = strtoul(p + 1, &p, 0);
                        if (vlanid <= 0 || vlanid > 0xFFF || *p)
-                               error(Ebadarg);
+                               error(EINVAL, NULL);
                }
        }
        if ((ether = etherxx[ctlrno]) == 0)
-               error(Enodev);
+               error(ENODEV, NULL);
        rlock(&ether->rwlock);
        if (waserror()) {
                runlock(&ether->rwlock);
@@ -67,16 +60,16 @@ struct chan *etherattach(char *spec)
        }
        if (vlanid) {
                if (ether->maxmtu < ETHERMAXTU + 4)
-                       error("interface cannot support 802.1 tags");
+                       error(EFAIL, "interface cannot support 802.1 tags");
                vlan = vlanalloc(ether, vlanid);
-               chan = devattach('l', spec);
+               chan = devattach(devname(), spec);
                chan->dev = ctlrno + (vlanid << 8);
                chan->aux = vlan;
                poperror();
                runlock(&ether->rwlock);
                return chan;
        }
-       chan = devattach('l', spec);
+       chan = devattach(devname(), spec);
        chan->dev = ctlrno;
        chan->aux = ether;
        if (ether->attach)
@@ -111,7 +104,7 @@ static struct walkqid *etherwalk(struct chan *chan, struct chan *nchan,
                runlock(&ether->rwlock);
                nexterror();
        }
-       wq = netifwalk(&ether->netif, chan, nchan, name, nname);
+       wq = netifwalk(ether, chan, nchan, name, nname);
        if (wq && wq->clone != NULL && wq->clone != chan)
                wq->clone->aux = ether;
        poperror();
@@ -131,7 +124,7 @@ static int etherstat(struct chan *chan, uint8_t * dp, int n)
                runlock(&ether->rwlock);
                nexterror();
        }
-       s = netifstat(&ether->netif, chan, dp, n);
+       s = netifstat(ether, chan, dp, n);
        poperror();
        runlock(&ether->rwlock);
        return s;
@@ -149,7 +142,7 @@ static struct chan *etheropen(struct chan *chan, int omode)
                runlock(&ether->rwlock);
                nexterror();
        }
-       c = netifopen(&ether->netif, chan, omode);
+       c = netifopen(ether, chan, omode);
        poperror();
        runlock(&ether->rwlock);
        return c;
@@ -166,7 +159,7 @@ static void etherclose(struct chan *chan)
                runlock(&ether->rwlock);
                nexterror();
        }
-       netifclose(&ether->netif, chan);
+       netifclose(ether, chan);
        poperror();
        runlock(&ether->rwlock);
 }
@@ -196,7 +189,7 @@ static long etherread(struct chan *chan, void *buf, long n, int64_t off)
                if (NETTYPE(chan->qid.path) == Nstatqid)
                        ether->ifstat(ether, buf, 0, offset);
        }
-       r = netifread(&ether->netif, chan, buf, n, offset);
+       r = netifread(ether, chan, buf, n, offset);
 out:
        poperror();
        runlock(&ether->rwlock);
@@ -215,7 +208,7 @@ static struct block *etherbread(struct chan *chan, long n, uint32_t offset)
                runlock(&ether->rwlock);
                nexterror();
        }
-       b = netifbread(&ether->netif, chan, n, offset);
+       b = netifbread(ether, chan, n, offset);
        poperror();
        runlock(&ether->rwlock);
        return b;
@@ -233,7 +226,7 @@ static int etherwstat(struct chan *chan, uint8_t * dp, int n)
                runlock(&ether->rwlock);
                nexterror();
        }
-       r = netifwstat(&ether->netif, chan, dp, n);
+       r = netifwstat(ether, chan, dp, n);
        poperror();
        runlock(&ether->rwlock);
        return r;
@@ -241,7 +234,7 @@ static int etherwstat(struct chan *chan, uint8_t * dp, int n)
 
 static void etherrtrace(struct netfile *f, struct etherpkt *pkt, int len)
 {
-       int i, n;
+       uint64_t i, n;
        struct block *bp;
 
        if (qwindow(f->in) <= 0)
@@ -250,21 +243,38 @@ static void etherrtrace(struct netfile *f, struct etherpkt *pkt, int len)
                n = 58;
        else
                n = len;
-       bp = iallocb(64);
+       bp = iallocb(68);
        if (bp == NULL)
                return;
        memmove(bp->wp, pkt->d, n);
+       /* we're storing 8 bytes here (64 bit); old 9ns was 32 bit for msec */
        i = milliseconds();
        bp->wp[58] = len >> 8;
        bp->wp[59] = len;
-       bp->wp[60] = i >> 24;
-       bp->wp[61] = i >> 16;
-       bp->wp[62] = i >> 8;
-       bp->wp[63] = i;
-       bp->wp += 64;
+       bp->wp[60] = i >> 56;
+       bp->wp[61] = i >> 48;
+       bp->wp[62] = i >> 40;
+       bp->wp[63] = i >> 32;
+       bp->wp[64] = i >> 24;
+       bp->wp[65] = i >> 16;
+       bp->wp[66] = i >> 8;
+       bp->wp[67] = i;
+       bp->wp += 68;
        qpass(f->in, bp);
 }
 
+#ifdef CONFIG_RISCV
+#warning "Potentially unaligned ethernet addrs!"
+#endif
+
+static inline int eaddrcmp(uint8_t *x, uint8_t *y)
+{
+       uint16_t *a = (uint16_t *)x;
+       uint16_t *b = (uint16_t *)y;
+
+       return (a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]);
+}
+
 struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
 {
        struct etherpkt *pkt;
@@ -274,7 +284,7 @@ struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
        struct block *xbp;
        struct ether *vlan;
 
-       ether->netif.inpackets++;
+       ether->inpackets++;
 
        pkt = (struct etherpkt *)bp->rp;
        len = BLEN(bp);
@@ -295,13 +305,13 @@ struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
        }
 
        fx = 0;
-       ep = &ether->netif.f[Ntypes];
+       ep = &ether->f[Ntypes];
 
        multi = pkt->d[0] & 1;
        /* check for valid multcast addresses */
-       if (multi && memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) != 0
-               && ether->netif.prom == 0) {
-               if (!activemulti(&ether->netif, pkt->d, sizeof(pkt->d))) {
+       if (multi && eaddrcmp(pkt->d, ether->bcast) != 0
+               && ether->prom == 0) {
+               if (!activemulti(ether, pkt->d, sizeof(pkt->d))) {
                        if (fromwire) {
                                freeb(bp);
                                bp = 0;
@@ -311,8 +321,8 @@ struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
        }
 
        /* is it for me? */
-       tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
-       fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+       tome = eaddrcmp(pkt->d, ether->ea) == 0;
+       fromme = eaddrcmp(pkt->s, ether->ea) == 0;
 
        /*
         * Multiplex the packet to all the connections which want it.
@@ -320,30 +330,35 @@ struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
         * attempt to simply pass it into one of the connections, thereby
         * saving a copy of the data (usual case hopefully).
         */
-       for (fp = ether->netif.f; fp < ep; fp++) {
+       for (fp = ether->f; fp < ep; fp++) {
                if ((f = *fp) && (f->type == type || f->type < 0))
                        if (tome || multi || f->prom) {
                                /* Don't want to hear bridged packets */
                                if (f->bridge && !fromwire && !fromme)
                                        continue;
-                               if (!f->headersonly) {
-                                       if (fromwire && fx == 0)
-                                               fx = f;
-                                       else if ((xbp = iallocb(len))) {
-                                               memmove(xbp->wp, pkt, len);
-                                               xbp->wp += len;
-                                               if (qpass(f->in, xbp) < 0)
-                                                       ether->netif.soverflows++;
-                                       } else
-                                               ether->netif.soverflows++;
-                               } else
+                               if (f->headersonly) {
                                        etherrtrace(f, pkt, len);
+                                       continue;
+                               }
+                               if (fromwire && fx == 0) {
+                                       fx = f;
+                                       continue;
+                               }
+                               xbp = iallocb(len);
+                               if (xbp == 0) {
+                                       ether->soverflows++;
+                                       continue;
+                               }
+                               memmove(xbp->wp, pkt, len);
+                               xbp->wp += len;
+                               if (qpass(f->in, xbp) < 0)
+                                       ether->soverflows++;
                        }
        }
 
        if (fx) {
                if (qpass(fx->in, bp) < 0)
-                       ether->netif.soverflows++;
+                       ether->soverflows++;
                return 0;
        }
        if (fromwire) {
@@ -360,8 +375,11 @@ static int etheroq(struct ether *ether, struct block *bp)
        struct etherpkt *pkt;
        int8_t irq_state = 0;
 
-       ether->netif.outpackets++;
+       ether->outpackets++;
 
+       if (!(ether->feat & NETF_SG))
+               bp = linearizeblock(bp);
+       ptclcsum_finalize(bp, ether->feat);
        /*
         * Check if the packet has to be placed back onto the input queue,
         * i.e. if it's a loopback or broadcast packet or the interface is
@@ -373,28 +391,32 @@ static int etheroq(struct ether *ether, struct block *bp)
         */
        pkt = (struct etherpkt *)bp->rp;
        len = BLEN(bp);
-       loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
-       if (loopback || memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) == 0
-               || ether->netif.prom) {
+       loopback = eaddrcmp(pkt->d, ether->ea) == 0;
+       if (loopback || eaddrcmp(pkt->d, ether->bcast) == 0 || ether->prom) {
                disable_irqsave(&irq_state);
                etheriq(ether, bp, 0);
                enable_irqsave(&irq_state);
+               if (loopback) {
+                       freeb(bp);
+                       return len;
+               }
        }
 
-       if (!loopback) {
-               if (ether->vlanid) {
-                       /* add tag */
-                       bp = padblock(bp, 2 + 2);
-                       memmove(bp->rp, bp->rp + 4, 2 * Eaddrlen);
-                       hnputs(bp->rp + 2 * Eaddrlen, Type8021Q);
-                       hnputs(bp->rp + 2 * Eaddrlen + 2, ether->vlanid & 0xFFF);       /* prio:3 0:1 vid:12 */
-                       ether = ether->ctlr;
-               }
-               qbwrite(ether->oq, bp);
-               if (ether->transmit != NULL)
-                       ether->transmit(ether);
-       } else
-               freeb(bp);
+       if (ether->vlanid) {
+               /* add tag */
+               bp = padblock(bp, 2 + 2);
+               memmove(bp->rp, bp->rp + 4, 2 * Eaddrlen);
+               hnputs(bp->rp + 2 * Eaddrlen, Type8021Q);
+               hnputs(bp->rp + 2 * Eaddrlen + 2, ether->vlanid & 0xFFF);       /* prio:3 0:1 vid:12 */
+               ether = ether->ctlr;
+       }
+
+       if ((ether->feat & NETF_PADMIN) == 0 && BLEN(bp) < ether->minmtu)
+               bp = adjustblock(bp, ether->minmtu);
+
+       qbwrite(ether->oq, bp);
+       if (ether->transmit != NULL)
+               ether->transmit(ether);
 
        return len;
 }
@@ -415,17 +437,21 @@ static long etherwrite(struct chan *chan, void *buf, long n, int64_t unused)
                nexterror();
        }
        if (NETTYPE(chan->qid.path) != Ndataqid) {
-               l = netifwrite(&ether->netif, chan, buf, n);
+               l = netifwrite(ether, chan, buf, n);
                if (l >= 0)
                        goto out;
                cb = parsecmd(buf, n);
+               if (cb->nf < 1) {
+                       kfree(cb);
+                       error(EFAIL, "short control request");
+               }
                if (strcmp(cb->f[0], "nonblocking") == 0) {
                        if (cb->nf <= 1)
                                onoff = 1;
                        else
                                onoff = atoi(cb->f[1]);
                        if (ether->oq != NULL)
-                               qnoblock(ether->oq, onoff);
+                               qdropoverflow(ether->oq, onoff);
                        kfree(cb);
                        goto out;
                }
@@ -434,13 +460,11 @@ static long etherwrite(struct chan *chan, void *buf, long n, int64_t unused)
                        l = ether->ctl(ether, buf, n);
                        goto out;
                }
-               error(Ebadctl);
+               error(EINVAL, NULL);
        }
 
-       if (n > ether->maxmtu)
-               error(Etoobig);
-       if (n < ether->minmtu)
-               error(Etoosmall);
+       if (n > ether->maxmtu + ETHERHDRSIZE)
+               error(E2BIG, NULL);
        bp = allocb(n);
        if (waserror()) {
                freeb(bp);
@@ -481,13 +505,9 @@ static long etherbwrite(struct chan *chan, struct block *bp, uint32_t unused)
                runlock(&ether->rwlock);
                nexterror();
        }
-       if (n > ether->maxmtu) {
+       if (n > ether->maxmtu + ETHERHDRSIZE && (bp->flag & Btso) == 0) {
                freeb(bp);
-               error(Etoobig);
-       }
-       if (n < ether->minmtu) {
-               freeb(bp);
-               error(Etoosmall);
+               error(E2BIG, NULL);
        }
        n = etheroq(ether, bp);
        poperror();
@@ -511,7 +531,7 @@ static long vlanctl(struct ether *ether, void *buf, long n)
                && strcmp(cb->f[0], "ea") == 0 && parseether(ea, cb->f[1]) == 0) {
                kfree(cb);
                memmove(ether->ea, ea, Eaddrlen);
-               memmove(ether->netif.addr, ether->ea, Eaddrlen);
+               memmove(ether->addr, ether->ea, Eaddrlen);
                return 0;
        }
        if (cb->nf == 1 && strcmp(cb->f[0], "disable") == 0) {
@@ -528,7 +548,7 @@ static long vlanctl(struct ether *ether, void *buf, long n)
                return 0;
        }
        kfree(cb);
-       error(Ebadctl);
+       error(EINVAL, NULL);
        return -1;      /* not reached */
 }
 
@@ -556,37 +576,37 @@ static struct ether *vlanalloc(struct ether *ether, int id)
                        fid = i;
        }
        if (fid < 0)
-               error(Enoifc);
+               error(ENOENT, NULL);
        snprintf(name, sizeof(name), "ether%d.%d", ether->ctlrno, id);
        vlan = ether->vlans[fid];
        if (vlan == NULL) {
                vlan = kzmalloc(sizeof(struct ether), 1);
                if (vlan == NULL)
-                       error(Enovmem);
+                       error(ENOMEM, NULL);
                rwinit(&vlan->rwlock);
                qlock_init(&vlan->vlq);
-               netifinit(&vlan->netif, name, Ntypes, ether->netif.limit);
+               netifinit(vlan, name, Ntypes, ether->limit);
                ether->vlans[fid] = vlan;       /* id is still zero, can't be matched */
                ether->nvlan++;
        } else
-               memmove(vlan->netif.name, name, KNAMELEN - 1);
+               memmove(vlan->name, name, KNAMELEN - 1);
        vlan->attach = nop;
        vlan->transmit = NULL;
        vlan->ctl = vlanctl;
        vlan->irq = -1;
-       vlan->netif.promiscuous = ether->netif.promiscuous;
-       vlan->netif.multicast = ether->netif.multicast;
-       vlan->netif.arg = vlan;
-       vlan->netif.mbps = ether->netif.mbps;
+       vlan->promiscuous = ether->promiscuous;
+       vlan->multicast = ether->multicast;
+       vlan->arg = vlan;
+       vlan->mbps = ether->mbps;
        vlan->fullduplex = ether->fullduplex;
        vlan->encry = ether->encry;
        vlan->minmtu = ether->minmtu;
        vlan->maxmtu = ether->maxmtu;
        vlan->ctlrno = ether->ctlrno;
        vlan->vlanid = id;
-       vlan->netif.alen = Eaddrlen;
-       memmove(vlan->netif.addr, ether->netif.addr, sizeof(vlan->netif.addr));
-       memmove(vlan->netif.bcast, ether->netif.bcast, sizeof(ether->netif.bcast));
+       vlan->alen = Eaddrlen;
+       memmove(vlan->addr, ether->addr, sizeof(vlan->addr));
+       memmove(vlan->bcast, ether->bcast, sizeof(ether->bcast));
        vlan->oq = NULL;
        vlan->ctlr = ether;
        vlan->vlanid = id;
@@ -636,7 +656,7 @@ int parseether(uint8_t * to, char *from)
 static void etherreset(void)
 {
        struct ether *ether;
-       int i, n, ctlrno;
+       int i, n, ctlrno, qsize;
        char name[KNAMELEN], buf[128];
 
        for (ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++) {
@@ -646,7 +666,7 @@ static void etherreset(void)
                rwinit(&ether->rwlock);
                qlock_init(&ether->vlq);
                ether->ctlrno = ctlrno;
-               ether->netif.mbps = 10;
+               ether->mbps = 10;
                ether->minmtu = ETHERMINTU;
                ether->maxmtu = ETHERMAXTU;
                /* looked like irq type, we don't have these yet */
@@ -681,12 +701,10 @@ static void etherreset(void)
                        ether->type = cards[n].type;
                        snprintf(name, sizeof(name), "ether%d", ctlrno);
 
-                       if (ether->interrupt != NULL)
-                               register_dev_irq(ether->irq, ether->interrupt, ether);
-
                        i = snprintf(buf, sizeof(buf),
                                                 "#l%d: %s: %dMbps port 0x%x irq %u", ctlrno,
-                                                ether->type, ether->netif.mbps, ether->port,
+                                                ether->type, ether->mbps,
+                                    ether->port,
                                                 ether->irq);
                        /* Looks like this is for printing MMIO addrs */
 #if 0
@@ -704,20 +722,28 @@ static void etherreset(void)
                        snprintf(buf + i, sizeof(buf) - i, "\n");
                        printk(buf);
 
-                       if (ether->netif.mbps == 100) {
-                               netifinit(&ether->netif, name, Ntypes, 256 * 1024);
-                               if (ether->oq == 0)
-                                       ether->oq = qopen(256 * 1024, Qmsg, 0, 0);
-                       } else {
-                               netifinit(&ether->netif, name, Ntypes, 64 * 1024);
-                               if (ether->oq == 0)
-                                       ether->oq = qopen(64 * 1024, Qmsg, 0, 0);
+                       switch (ether->mbps) {
+
+                       case 1 ... 99:
+                               qsize = 64 * 1024;
+                               break;
+                       case 100 ... 999:
+                               qsize = 256 * 1024;
+                               break;
+                       case 1000 ... 9999:
+                               qsize = 1024 * 1024;
+                               break;
+                       default:
+                               qsize = 8 * 1024 * 1024;
                        }
+                       netifinit(ether, name, Ntypes, qsize);
+                       if (ether->oq == 0)
+                               ether->oq = qopen(qsize, Qmsg, 0, 0);
                        if (ether->oq == 0)
                                panic("etherreset %s", name);
-                       ether->netif.alen = Eaddrlen;
-                       memmove(ether->netif.addr, ether->ea, Eaddrlen);
-                       memset(ether->netif.bcast, 0xFF, Eaddrlen);
+                       ether->alen = Eaddrlen;
+                       memmove(ether->addr, ether->ea, Eaddrlen);
+                       memset(ether->bcast, 0xFF, Eaddrlen);
 
                        etherxx[ctlrno] = ether;
                        ether = 0;
@@ -784,7 +810,6 @@ uint32_t ethercrc(uint8_t * p, int len)
 }
 
 struct dev etherdevtab __devtab = {
-       'l',
        "ether",
 
        etherreset,