From da7ee71a72ab667cd86cde902a7d72b0650aa9af Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Wed, 21 Aug 2002 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2002-08-21 --- alphapc/dat.h | 2 + bitsy/dat.h | 5 +- ip/tcp.c | 11 +- mtx/dat.h | 2 + pc/dat.h | 2 + pc/devtv.c | 1 - pc/ether82543gc.c | 817 ++++++++++++++++++++++++++++++++-------------- pc/etherga620.c | 6 +- pc/main.c | 5 + pc/trap.c | 4 +- port/devcons.c | 25 +- port/portdat.h | 2 +- port/proc.c | 16 +- 13 files changed, 631 insertions(+), 267 deletions(-) diff --git a/alphapc/dat.h b/alphapc/dat.h index 9601a269389410431d17774d4776e2c9111c26d7..82dc71affdd9e70d7f3d3e075111f82f839da064 100644 --- a/alphapc/dat.h +++ b/alphapc/dat.h @@ -147,6 +147,8 @@ struct Mach ulong pcclast; uvlong fastclock; vlong intrts; /* time stamp of last interrupt */ + ulong inidle; /* fastticks in idlehands() since last slowtick */ + ulong avginidle; /* avg fastticks in idlehands() per slowtick */ int tlbfault; /* only used by devproc; no access to tlb */ int tlbpurge; /* ... */ diff --git a/bitsy/dat.h b/bitsy/dat.h index 80a1ace180b95c9e4a64574213a404e7a813203c..75c20e96737902145396fab177d5b6cc931a91b1 100644 --- a/bitsy/dat.h +++ b/bitsy/dat.h @@ -134,9 +134,12 @@ struct Mach int intr; vlong fastclock; /* last sampled value */ vlong intrts; /* time stamp of last interrupt */ + uvlong inidle; /* time spent in idlehands() */ ulong spuriousintr; int lastintr; - int ilockdepth; + int ilockdepth; + ulong inidle; /* fastticks in idlehands() since last slowtick */ + ulong avginidle; /* avg fastticks in idlehands() per slowtick */ int flushmmu; /* make current proc flush it's mmu state */ Proc *pid2proc[31]; /* what proc holds what pid */ diff --git a/ip/tcp.c b/ip/tcp.c index 7604292df38a0c090b98d583185ffd218cf4cc0b..2343d42537926c5fabf9fcba94cb34bdd0ec30a7 100644 --- a/ip/tcp.c +++ b/ip/tcp.c @@ -354,7 +354,7 @@ struct Tcppriv */ int tcpporthogdefense = 0; -int addreseq(Tcpctl*, Tcp*, Block*, ushort); +int addreseq(Tcpctl*, Tcppriv*, Tcp*, Block*, ushort); void getreseq(Tcpctl*, Tcp*, Block**, ushort*); void localclose(Conv*, char*); void procsyn(Conv*, Tcp*); @@ -2064,8 +2064,7 @@ reset: if(seg.seq != tcb->rcv.nxt) if(length != 0 || (seg.flags & (SYN|FIN))) { update(s, &seg); - tpriv->stats[OutOfOrder]++; - if(addreseq(tcb, &seg, bp, length) < 0) + if(addreseq(tcb, tpriv, &seg, bp, length) < 0) print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport, s->laddr, s->lport); tcb->flags |= FORCE; goto output; @@ -2711,7 +2710,7 @@ procsyn(Conv *s, Tcp *seg) } int -addreseq(Tcpctl *tcb, Tcp *seg, Block *bp, ushort length) +addreseq(Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length) { Reseq *rp, *rp1; int i; @@ -2732,6 +2731,8 @@ addreseq(Tcpctl *tcb, Tcp *seg, Block *bp, ushort length) if(rp1 == nil || seq_lt(seg->seq, rp1->seg.seq)) { rp->next = rp1; tcb->reseq = rp; + if(rp->next != nil) + tpriv->stats[OutOfOrder]++; return 0; } @@ -2741,6 +2742,8 @@ addreseq(Tcpctl *tcb, Tcp *seg, Block *bp, ushort length) if(rp1->next == nil || seq_lt(seg->seq, rp1->next->seg.seq)) { rp->next = rp1->next; rp1->next = rp; + if(rp->next != nil) + tpriv->stats[OutOfOrder]++; break; } rp1 = rp1->next; diff --git a/mtx/dat.h b/mtx/dat.h index 34f661a1544bd362657834a63627792c5f69c516..97277598234550035c94d37ea08e1f955ef95dea 100644 --- a/mtx/dat.h +++ b/mtx/dat.h @@ -142,6 +142,8 @@ struct Mach ulong pcclast; uvlong fastclock; vlong intrts; /* time stamp of last interrupt */ + ulong inidle; /* fastticks in idlehands() since last slowtick */ + ulong avginidle; /* avg fastticks in idlehands() per slowtick */ int tlbfault; /* only used by devproc; no access to tlb */ int tlbpurge; /* ... */ diff --git a/pc/dat.h b/pc/dat.h index e40618a1933f0ca329867c1151046112f50484d6..427dc57c8abcbd85bbb69d8084828f0c1561683b 100644 --- a/pc/dat.h +++ b/pc/dat.h @@ -178,6 +178,8 @@ struct Mach uvlong intrts; /* time stamp of last interrupt */ int flushmmu; /* make current proc flush it's mmu state */ int ilockdepth; + ulong inidle; /* fastticks in idlehands() since last slowtick */ + ulong avginidle; /* avg fastticks in idlehands() per slowtick */ ulong spuriousintr; int lastintr; diff --git a/pc/devtv.c b/pc/devtv.c index 23252cea988f5757b9450cfd7c49abba993973d4..61090b557f47c550fd5e2156458987ba833484c1 100644 --- a/pc/devtv.c +++ b/pc/devtv.c @@ -1517,7 +1517,6 @@ astart(Tv *tv, char *input, uint rate, uint nab, uint nasz) break; print("astart: sampleshift %d, decimation %d\n", s, d); - s = d = 0; tv->narblocks = 0; bt878->gpiodmactl = gpiodmactl_fifoenable | diff --git a/pc/ether82543gc.c b/pc/ether82543gc.c index 998b11f817d71492b940389741ab6260f95adf33..b18b0c7d89f772beb885851dcd4716dbd9925b8f 100644 --- a/pc/ether82543gc.c +++ b/pc/ether82543gc.c @@ -6,14 +6,9 @@ * The datasheet is not very clear about running on a big-endian system * and this driver assumes little-endian throughout. * To do: - * auto-negotiation and flow control setup and enable * GMII/MII * receive tuning - * check recovery from receive no buffers condition * transmit tuning - * automatic ett adjustment - * chip-specific offload... - * check PCI MWI and CLS initialisation */ #include "u.h" #include "../port/lib.h" @@ -41,12 +36,19 @@ enum { Imc = 0x000000D8, /* Interrupt mask Clear */ Rctl = 0x00000100, /* Receive Control */ Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit configuration word reg. */ + Rxcw = 0x00000180, /* Receive configuration word reg. */ Tctl = 0x00000400, /* Transmit Control */ Tipg = 0x00000410, /* Transmit IPG */ Tbt = 0x00000448, /* Transmit Burst Timer */ Ait = 0x00000458, /* Adaptive IFS Throttle */ Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Rdfh = 0x00002410, /* Receive data fifo head */ + Rdft = 0x00002418, /* Receive data fifo tail */ + Rdfhs = 0x00002420, /* Receive data fifo head saved */ + Rdfts = 0x00002428, /* Receive data fifo tail saved */ + Rdfpc = 0x00002430, /* Receive data fifo packet count */ Rdbal = 0x00002800, /* Rdesc Base Address Low */ Rdbah = 0x00002804, /* Rdesc Base Address High */ Rdlen = 0x00002808, /* Receive Descriptor Length */ @@ -56,6 +58,11 @@ enum { Rxdctl = 0x00002828, /* Receive Descriptor Control */ Txdmac = 0x00003000, /* Transfer DMA Control */ Ett = 0x00003008, /* Early Transmit Control */ + Tdfh = 0x00003410, /* Transmit data fifo head */ + Tdft = 0x00003418, /* Transmit data fifo tail */ + Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */ + Tdfts = 0x00003428, /* Transmit data fifo tail saved */ + Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */ Tdbal = 0x00003800, /* Tdesc Base Address Low */ Tdbah = 0x00003804, /* Tdesc Base Address High */ Tdlen = 0x00003808, /* Transmit Descriptor Length */ @@ -87,7 +94,15 @@ enum { /* Ctrl */ Frcspd = 0x00000800, /* Force Speed */ Frcdplx = 0x00001000, /* Force Duplex */ Swdpinslo = 0x003C0000, /* Software Defined Pins - lo nibble */ - Swdpiolo = 0x03C00000, /* Software Defined Pins - I or O */ + Swdpin0 = 0x00040000, + Swdpin1 = 0x00080000, + Swdpin2 = 0x00100000, + Swdpin3 = 0x00200000, + Swdpiolo = 0x03C00000, /* Software Defined I/O Pins */ + Swdpio0 = 0x00400000, + Swdpio1 = 0x00800000, + Swdpio2 = 0x01000000, + Swdpio3 = 0x02000000, Devrst = 0x04000000, /* Device Reset */ Rfce = 0x08000000, /* Receive Flow Control Enable */ Tfce = 0x10000000, /* Transmit Flow Control Enable */ @@ -116,7 +131,7 @@ enum { /* Ctrl and Status */ Asdv100 = 0x00000100, /* 100Mb/s */ Asdv1000 = 0x00000200, /* 1000Mb/s */ }; - + enum { /* Eecd */ Sk = 0x00000001, /* Clock input to the EEPROM */ Cs = 0x00000002, /* Chip Select */ @@ -134,6 +149,18 @@ enum { /* Ctrlext */ Spdbyps = 0x00008000, /* Speed Select Bypass */ }; +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + Pba = 0x08, /* Printed Board Assembly number */ + Icw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + enum { /* Mdic */ MDIdMASK = 0x0000FFFF, /* Data */ MDIdSHIFT = 0, @@ -164,6 +191,25 @@ enum { /* Icr, Ics, Ims, Imc */ Gpi3 = 0x00004000, }; +enum { /* Txcw */ + Ane = 0x80000000, /* Autonegotiate enable */ + Np = 0x00008000, /* Next Page */ + As = 0x00000100, /* Asymmetric Flow control desired */ + Ps = 0x00000080, /* Pause supported */ + Hd = 0x00000040, /* Half duplex supported */ + TxcwFd = 0x00000020, /* Full Duplex supported */ +}; + +enum { /* Rxcw */ + Rxword = 0x0000FFFF, /* Data from auto-negotiation process */ + Rxnocarrier = 0x04000000, /* Carrier Sense indication */ + Rxinvalid = 0x08000000, /* Invalid Symbol during configuration */ + Rxchange = 0x10000000, /* Change to the Rxword indication */ + Rxconfig = 0x20000000, /* /C/ order set reception indication */ + Rxsync = 0x40000000, /* Lost bit synchronization indication */ + Anc = 0x80000000, /* Auto Negotiation Complete */ +}; + enum { /* Rctl */ Rrst = 0x00000001, /* Receiver Software Reset */ Ren = 0x00000002, /* Receiver Enable */ @@ -183,10 +229,11 @@ enum { /* Rctl */ MoMASK = 0x00003000, /* Multicast Offset */ Bam = 0x00008000, /* Broadcast Accept Mode */ BsizeMASK = 0x00030000, /* Receive Buffer Size */ - Bsize2048 = 0x00000000, - Bsize1024 = 0x00010000, - Bsize512 = 0x00020000, - Bsize256 = 0x00030000, + Bsize2048 = 0x00000000, /* Bsex = 0 */ + Bsize1024 = 0x00010000, /* Bsex = 0 */ + Bsize512 = 0x00020000, /* Bsex = 0 */ + Bsize256 = 0x00030000, /* Bsex = 0 */ + Bsize16384 = 0x00010000, /* Bsex = 1 */ Vfe = 0x00040000, /* VLAN Filter Enable */ Cfien = 0x00080000, /* Canonical Form Indicator Enable */ Cfi = 0x00100000, /* Canonical Form Indicator value */ @@ -215,9 +262,10 @@ enum { /* [RT]xdctl */ PthreshSHIFT = 0, HthreshMASK = 0x00003F00, /* Host Threshold */ HthreshSHIFT = 8, - WthreshMASK = 0x003F0000, /* Writebacj Threshold */ + WthreshMASK = 0x003F0000, /* Writeback Threshold */ WthreshSHIFT = 16, - Gran = 0x01000000, /* Granularity */ + Gran = 0x00000000, /* Granularity */ + RxGran = 0x01000000, /* Granularity */ }; enum { /* Rxcsum */ @@ -227,6 +275,10 @@ enum { /* Rxcsum */ Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */ }; +enum { /* Receive Delay Timer Ring */ + Fpd = 0x80000000, /* Flush partial Descriptor Block */ +}; + typedef struct Rdesc { /* Receive Descriptor */ uint addr[2]; ushort length; @@ -286,8 +338,15 @@ enum { /* Tdesc status */ }; enum { - Nrdesc = 128, /* multiple of 8 */ - Ntdesc = 128, /* multiple of 8 */ + Nrdesc = 256, /* multiple of 8 */ + Ntdesc = 256, /* multiple of 8 */ + Nblocks = 4098, /* total number of blocks to use */ + + SBLOCKSIZE = 2048, + JBLOCKSIZE = 16384, + + NORMAL = 1, + JUMBO = 2, }; typedef struct Ctlr Ctlr; @@ -296,6 +355,7 @@ typedef struct Ctlr { Pcidev* pcidev; Ctlr* next; int active; + int started; int id; ushort eeprom[0x40]; @@ -305,47 +365,69 @@ typedef struct Ctlr { Lock slock; uint statistics[Nstatistics]; + Lock rdlock; Rdesc* rdba; /* receive descriptor base address */ - Block** rb; /* receive buffers */ + Block* rb[Nrdesc]; /* receive buffers */ int rdh; /* receive descriptor head */ int rdt; /* receive descriptor tail */ + Block** freehead; /* points to long or short head */ - Tdesc* tdba; /* transmit descriptor base address */ Lock tdlock; - Block** tb; /* transmit buffers */ + Tdesc* tdba; /* transmit descriptor base address */ + Block* tb[Ntdesc]; /* transmit buffers */ int tdh; /* transmit descriptor head */ int tdt; /* transmit descriptor tail */ - int ett; /* early transmit threshold */ + Rendez timer; /* watchdog timer */ + int txstalled; /* count of times unable to send */ + + int txcw; + int fcrtl; + int fcrth; ulong multimask[128]; /* bit mask for multicast addresses */ } Ctlr; -static Ctlr* ctlrhead; -static Ctlr* ctlrtail; +static Ctlr* gc82543ctlrhead; +static Ctlr* gc82543ctlrtail; + +static Lock freelistlock; +static Block* freeShortHead; +static Block* freeJumboHead; #define csr32r(c, r) (*((c)->nic+((r)/4))) #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) +static void gc82543watchdog(void* arg); + static void gc82543attach(Ether* edev) { int ctl; Ctlr *ctlr; + char name[KNAMELEN]; /* * To do here: * one-time stuff; - * start off a kproc for link status change: * adjust queue length depending on speed; * flow control. * more needed here... */ ctlr = edev->ctlr; - csr32w(ctlr, Ims, ctlr->im); + lock(&ctlr->slock); + if(ctlr->started == 0){ + ctlr->started = 1; + snprint(name, KNAMELEN, "#l%d82543", edev->ctlrno); + kproc(name, gc82543watchdog, edev); + } + unlock(&ctlr->slock); + ctl = csr32r(ctlr, Rctl)|Ren; csr32w(ctlr, Rctl, ctl); ctl = csr32r(ctlr, Tctl)|Ten; csr32w(ctlr, Tctl, ctl); + + csr32w(ctlr, Ims, ctlr->im); } static char* statistics[Nstatistics] = { @@ -424,10 +506,9 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset) uvlong tuvl, ruvl; ctlr = edev->ctlr; - + lock(&ctlr->slock); p = malloc(2*READSTR); l = 0; - for(i = 0; i < Nstatistics; i++){ r = csr32r(ctlr, Statistics+i*4); if((s = statistics[i]) == nil) @@ -450,7 +531,7 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset) s, tuvl, ruvl); i++; break; - + default: ctlr->statistics[i] += r; if(ctlr->statistics[i] == 0) @@ -461,7 +542,6 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset) } } - l += snprint(p+l, 2*READSTR-l, "ett: %d\n", ctlr->ett); l += snprint(p+l, 2*READSTR-l, "eeprom:"); for(i = 0; i < 0x40; i++){ if(i && ((i & 0x07) == 0)) @@ -469,74 +549,220 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset) l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]); } - snprint(p+l, 2*READSTR-l, "\n"); + snprint(p+l, 2*READSTR-l, "\ntxstalled %d\n", ctlr->txstalled); n = readstr(offset, a, n, p); free(p); + unlock(&ctlr->slock); return n; } static void -gc82543transmit(Ether* edev) +gc82543promiscuous(void* arg, int on) { - Block *bp; + int rctl; Ctlr *ctlr; - Tdesc *tdesc; - int tdh, tdt; + Ether *edev; - /* - * For now there are no smarts here. Tuning comes later. - */ + edev = arg; ctlr = edev->ctlr; - ilock(&ctlr->tdlock); + + rctl = csr32r(ctlr, Rctl); + rctl &= ~MoMASK; /* make sure we're using bits 47:36 */ + if(on) + rctl |= Upe|Mpe; + else + rctl &= ~(Upe|Mpe); + csr32w(ctlr, Rctl, rctl); +} + +static void +gc82543multicast(void* arg, uchar* addr, int on) +{ + int bit, x; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + x = addr[5]>>1; + bit = ((addr[5] & 1)<<4)|(addr[4]>>4); + if(on) + ctlr->multimask[x] |= 1<multimask[x] &= ~(1<multimask[x]); +} + +static long +gc82543ctl(Ether* edev, void* buf, long n) +{ + Cmdbuf *cb; + Ctlr *ctlr; + int ctrl, i, r; + + ctlr = edev->ctlr; + if(ctlr == nil) + error(Enonexist); + + lock(&ctlr->slock); + r = 0; + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "auto") == 0){ + ctrl = csr32r(ctlr, Ctrl); + if(cistrcmp(cb->f[1], "off") == 0){ + csr32w(ctlr, Txcw, ctlr->txcw & ~Ane); + ctrl |= (Slu|Fd); + if(ctlr->txcw & As) + ctrl |= Rfce; + if(ctlr->txcw & Ps) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + } + else if(cistrcmp(cb->f[1], "on") == 0){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd); + csr32w(ctlr, Ctrl, ctrl); + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "clear") == 0){ + if(cistrcmp(cb->f[1], "stats") == 0){ + for(i = 0; i < Nstatistics; i++) + ctlr->statistics[i] = 0; + } + else + r = -1; + } + else + r = -1; + unlock(&ctlr->slock); + + free(cb); + return (r == 0) ? n : r; +} + +static void +gc82543txinit(Ctlr* ctlr) +{ + int i; + int tdsize; + Block *bp, **bpp; + + tdsize = ROUND(Ntdesc*sizeof(Tdesc), 4096); + + if(ctlr->tdba == nil) + ctlr->tdba = xspanalloc(tdsize, 32, 0); + + for(i = 0; i < Ntdesc; i++){ + bpp = &ctlr->tb[i]; + bp = *bpp; + if(bp != nil){ + *bpp = nil; + freeb(bp); + } + memset(&ctlr->tdba[i], 0, sizeof(Tdesc)); + } + + csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); + csr32w(ctlr, Tdbah, 0); + csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc)); /* - * Free any completed packets - * - try to get the soft tdh to catch the tdt; - * - if the packet had an underrun bump the threshold - * - the Tu bit doesn't seem to ever be set, perhaps - * because Rs mode is used? + * set the ring head and tail pointers. */ + ctlr->tdh = 0; + csr32w(ctlr, Tdh, ctlr->tdh); + ctlr->tdt = 0; + csr32w(ctlr, Tdt, ctlr->tdt); + + csr32w(ctlr, Tipg, (6<<20)|(8<<10)|6); + csr32w(ctlr, Tidv, 128); + csr32w(ctlr, Ait, 0); + csr32w(ctlr, Txdmac, 0); + csr32w(ctlr, Txdctl, Gran|(4<im |= Txdw; +} + +static void +gc82543transmit(Ether* edev) +{ + Block *bp, **bpp; + Ctlr *ctlr; + Tdesc *tdesc; + int tdh, tdt, s; + + ctlr = edev->ctlr; + + ilock(&ctlr->tdlock); tdh = ctlr->tdh; for(;;){ + /* + * Free any completed packets + */ tdesc = &ctlr->tdba[tdh]; if(!(tdesc->status & Tdd)) break; - if(tdesc->status & Tu){ - ctlr->ett++; - csr32w(ctlr, Ett, ctlr->ett); - } - tdesc->status = 0; - if(ctlr->tb[tdh] != nil){ - freeb(ctlr->tb[tdh]); - ctlr->tb[tdh] = nil; + memset(tdesc, 0, sizeof(Tdesc)); + bpp = &ctlr->tb[tdh]; + bp = *bpp; + if(bp != nil){ + *bpp = nil; + freeb(bp); } tdh = NEXT(tdh, Ntdesc); } ctlr->tdh = tdh; + s = csr32r(ctlr, Status); /* - * Try to fill the ring back up. + * Try to fill the ring back up + * but only if link is up and transmission isn't paused. */ - tdh = PREV(ctlr->tdh, Ntdesc); - for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){ - if((bp = qget(edev->oq)) == nil) - break; + if((s & (Txoff|Lu)) == Lu){ + tdt = ctlr->tdt; + while(NEXT(tdt, Ntdesc) != tdh){ + if((bp = qget(edev->oq)) == nil) + break; - tdesc = &ctlr->tdba[tdt]; - tdesc->addr[0] = PCIWADDR(bp->rp); - tdesc->addr[1] = 0; - tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp); - tdesc->status = 0; + tdesc = &ctlr->tdba[tdt]; + tdesc->addr[0] = PCIWADDR(bp->rp); + tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp); + ctlr->tb[tdt] = bp; + tdt = NEXT(tdt, Ntdesc); + } - ctlr->tb[tdt] = bp; + if(tdt != ctlr->tdt){ + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + } } - ctlr->tdt = tdt; - csr32w(ctlr, Tdt, tdt); + else + ctlr->txstalled++; iunlock(&ctlr->tdlock); } +static Block * +gc82543allocb(Ctlr* ctlr) +{ + Block *bp; + + ilock(&freelistlock); + if((bp = *(ctlr->freehead)) != nil){ + *(ctlr->freehead) = bp->next; + bp->next = nil; + } + iunlock(&freelistlock); + return bp; +} + static void gc82543replenish(Ctlr* ctlr) { @@ -544,72 +770,171 @@ gc82543replenish(Ctlr* ctlr) Block *bp; Rdesc *rdesc; + ilock(&ctlr->rdlock); rdt = ctlr->rdt; while(NEXT(rdt, Nrdesc) != ctlr->rdh){ rdesc = &ctlr->rdba[rdt]; - if(ctlr->rb[rdt] != nil){ - /* nothing to do */ - } - else if((bp = iallocb(2048)) != nil){ + if(ctlr->rb[rdt] == nil){ + bp = gc82543allocb(ctlr); + if(bp == nil){ + iprint("no available buffers\n"); + break; + } ctlr->rb[rdt] = bp; rdesc->addr[0] = PCIWADDR(bp->rp); rdesc->addr[1] = 0; } - else - break; + coherence(); rdesc->status = 0; - rdt = NEXT(rdt, Nrdesc); } ctlr->rdt = rdt; csr32w(ctlr, Rdt, rdt); + iunlock(&ctlr->rdlock); } static void -gc82543interrupt(Ureg*, void* arg) +gc82543rxinit(Ctlr* ctlr) +{ + int rdsize, i; + + csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF); + + /* + * Allocate the descriptor ring and load its + * address and length into the NIC. + */ + rdsize = ROUND(Nrdesc*sizeof(Rdesc), 4096); + if(ctlr->rdba == nil) + ctlr->rdba = xspanalloc(rdsize, 32, 0); + memset(ctlr->rdba, 0, rdsize); + + ctlr->rdh = 0; + ctlr->rdt = 0; + + csr32w(ctlr, Rdtr, Fpd|64); + csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); + csr32w(ctlr, Rdbah, 0); + csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc)); + csr32w(ctlr, Rdh, 0); + csr32w(ctlr, Rdt, 0); + for(i = 0; i < Nrdesc; i++){ + if(ctlr->rb[i] != nil){ + freeb(ctlr->rb[i]); + ctlr->rb[i] = nil; + } + } + gc82543replenish(ctlr); + + csr32w(ctlr, Rxdctl, RxGran|(8<im |= Rxt0|Rxo|Rxdmt0|Rxseq; +} + +static void +gc82543recv(Ether* edev, int icr) { Block *bp; Ctlr *ctlr; - Ether *edev; Rdesc *rdesc; - int icr, rdh; + int rdh; + + ctlr = edev->ctlr; + + rdh = ctlr->rdh; + for(;;){ + rdesc = &ctlr->rdba[rdh]; + + if(!(rdesc->status & Rdd)) + break; + + if((rdesc->status & Reop) && rdesc->errors == 0){ + bp = ctlr->rb[rdh]; + ctlr->rb[rdh] = nil; + bp->wp += rdesc->length; + bp->next = nil; + etheriq(edev, bp, 1); + } + + if(ctlr->rb[rdh] != nil){ + /* either non eop packet, or error */ + freeb(ctlr->rb[rdh]); + ctlr->rb[rdh] = nil; + } + memset(rdesc, 0, sizeof(Rdesc)); + coherence(); + rdh = NEXT(rdh, Nrdesc); + } + ctlr->rdh = rdh; + + if(icr & Rxdmt0) + gc82543replenish(ctlr); +} + +static void +freegc82543short(Block *bp) +{ + ilock(&freelistlock); + /* reset read/write pointer to proper positions */ + bp->rp = bp->lim - ROUND(SBLOCKSIZE, BLOCKALIGN); + bp->wp = bp->rp; + bp->next = freeShortHead; + freeShortHead = bp; + iunlock(&freelistlock); +} + +static void +freegc82532jumbo(Block *bp) +{ + ilock(&freelistlock); + /* reset read/write pointer to proper positions */ + bp->rp = bp->lim - ROUND(JBLOCKSIZE, BLOCKALIGN); + bp->wp = bp->rp; + bp->next = freeJumboHead; + freeJumboHead = bp; + iunlock(&freelistlock); +} + +static void +linkintr(Ctlr* ctlr) +{ + int ctrl; + + ctrl = csr32r(ctlr, Ctrl); + + if((ctrl & Swdpin1) || + ((csr32r(ctlr, Rxcw) & Rxconfig) && !(csr32r(ctlr, Txcw) & Ane))){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd|Frcdplx); + csr32w(ctlr, Ctrl, ctrl); + } +} + +static void +gc82543interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + int icr; edev = arg; ctlr = edev->ctlr; - for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){ + while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){ /* * Link status changed. */ - if(icr & (Rxseq|Lsc)){ - /* - * More here... - */ - } + if(icr & (Lsc|Rxseq)) + linkintr(ctlr); /* - * Process any received packets. + * Process recv buffers. */ - rdh = ctlr->rdh; - for(;;){ - rdesc = &ctlr->rdba[rdh]; - if(!(rdesc->status & Rdd)) - break; - if((rdesc->status & Reop) && rdesc->errors == 0){ - bp = ctlr->rb[rdh]; - ctlr->rb[rdh] = nil; - bp->wp += rdesc->length-4; - etheriq(edev, bp, 1); - } - rdesc->status = 0; - rdh = NEXT(rdh, Nrdesc); - } - ctlr->rdh = rdh; + gc82543recv(edev, icr); + /* + * Refill transmit ring and free packets. + */ gc82543transmit(edev); - - if(icr & Rxdmt0) - gc82543replenish(ctlr); } } @@ -617,10 +942,32 @@ static int gc82543init(Ether* edev) { int csr, i; - + Block *bp; Ctlr *ctlr; + ctlr = edev->ctlr; + /* + * Allocate private buffer pool to use for receiving packets. + */ + ilock(&freelistlock); + if (ctlr->freehead == nil){ + for(i = 0; i < Nblocks; i++){ + bp = iallocb(SBLOCKSIZE); + if(bp != nil){ + bp->next = freeShortHead; + bp->free = freegc82543short; + freeShortHead = bp; + } + else{ + print("82543gc: no memory\n"); + break; + } + } + ctlr->freehead = &freeShortHead; + } + iunlock(&freelistlock); + /* * Set up the receive addresses. * There are 16 addresses. The first should be the MAC address. @@ -642,102 +989,8 @@ gc82543init(Ether* edev) for(i = 0; i < 128; i++) csr32w(ctlr, Mta+i*4, 0); - /* - * Receive initialisation. - * Mostly defaults from the datasheet, will - * need some tuning for performance: - * Rctl descriptor mimimum threshold size - * discard pause frames - * strip CRC - * Rdtr interrupt delay - * Rxdctl all the thresholds - */ - csr32w(ctlr, Rctl, 0); - - /* - * Allocate the descriptor ring and load its - * address and length into the NIC. - */ - ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 16, 0); - csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); - csr32w(ctlr, Rdbah, 0); - csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc)); - - /* - * Initialise the ring head and tail pointers and - * populate the ring with Blocks. - * The datasheet says the tail pointer is set to beyond the last - * descriptor hardware can process, which implies the initial - * condition is Rdh == Rdt. However, experience shows Rdt must - * always be 'behind' Rdh; the replenish routine ensures this. - */ - ctlr->rdh = 0; - csr32w(ctlr, Rdh, ctlr->rdh); - ctlr->rdt = 0; - csr32w(ctlr, Rdt, ctlr->rdt); - ctlr->rb = malloc(sizeof(Block*)*Nrdesc); - gc82543replenish(ctlr); - - /* - * Set up Rctl but don't enable receiver (yet). - */ - csr32w(ctlr, Rdtr, 0); - csr32w(ctlr, Rxdctl, Gran|(1<im |= Rxt0|Rxo|Rxdmt0|Rxseq; - - /* - * Transmit initialisation. - * Mostly defaults from the datasheet, will - * need some tuning for performance. The normal mode will - * be full-duplex and things to tune for half-duplex are - * Tctl re-transmit on late collision - * Tipg all IPG times - * Tbt burst timer - * Ait adaptive IFS throttle - * and in general - * Txdmac packet prefetching - * Ett transmit early threshold - * Tidv interrupt delay value - * Txdctl all the thresholds - */ - csr = (0x0F<ett = 120/8; - else - ctlr->ett = 0; - csr32w(ctlr, Ett, ctlr->ett); - csr32w(ctlr, Tidv, 128); - csr32w(ctlr, Txdctl, Gran|(16<tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 16, 0); - csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); - csr32w(ctlr, Tdbah, 0); - csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc)); - - /* - * Initialise the ring head and tail pointers. - */ - ctlr->tdh = 0; - csr32w(ctlr, Tdh, ctlr->tdh); - ctlr->tdt = 0; - csr32w(ctlr, Tdt, ctlr->tdt); - ctlr->tb = malloc(sizeof(Block*)*Ntdesc); - ctlr->im |= Txqe|Txdw; + gc82543txinit(ctlr); + gc82543rxinit(ctlr); return 0; } @@ -854,6 +1107,12 @@ gc82543detach(Ctlr* ctlr) * power-on state, followed by an EEPROM reset to read * the defaults for some internal registers. */ + csr32w(ctlr, Imc, ~0); + csr32w(ctlr, Rctl, 0); + csr32w(ctlr, Tctl, 0); + + delay(10); + csr32w(ctlr, Ctrl, Devrst); while(csr32r(ctlr, Ctrl) & Devrst) ; @@ -861,26 +1120,89 @@ gc82543detach(Ctlr* ctlr) csr32w(ctlr, Ctrlext, Eerst); while(csr32r(ctlr, Ctrlext) & Eerst) ; + + csr32w(ctlr, Imc, ~0); + while(csr32r(ctlr, Icr)) + ; +} + +static void +gc82543checklink(Ctlr* ctlr) +{ + int ctrl, status, rxcw; + + ctrl = csr32r(ctlr, Ctrl); + status = csr32r(ctlr, Status); + rxcw = csr32r(ctlr, Rxcw); + + if(!(status & Lu)){ + if(!(ctrl & (Swdpin1|Slu)) && !(rxcw & Rxconfig)){ + csr32w(ctlr, Txcw, ctlr->txcw & ~Ane); + ctrl |= (Slu|Fd); + if(ctlr->txcw & As) + ctrl |= Rfce; + if(ctlr->txcw & Ps) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + } + } + else if((ctrl & Slu) && (rxcw & Rxconfig)){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd); + csr32w(ctlr, Ctrl, ctrl); + } } static void gc82543shutdown(Ether* ether) { -print("gc82543shutdown\n"); gc82543detach(ether->ctlr); } static int gc82543reset(Ctlr* ctlr) { + int ctl; + int te; + /* * Read the EEPROM, validate the checksum * then get the device back to a power-on state. */ if(at93c46r(ctlr) != 0xBABA) return -1; + gc82543detach(ctlr); + te = ctlr->eeprom[Icw2]; + if((te & 0x3000) == 0){ + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + ctlr->txcw = Ane|TxcwFd; + } + else if((te & 0x3000) == 0x2000){ + ctlr->fcrtl = 0; + ctlr->fcrth = 0; + ctlr->txcw = Ane|TxcwFd|As; + } + else{ + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + ctlr->txcw = Ane|TxcwFd|As|Ps; + } + + csr32w(ctlr, Txcw, ctlr->txcw); + + csr32w(ctlr, Ctrlext, (te & 0x00f0)<<4); + + csr32w(ctlr, Tctl, csr32r(ctlr, Tctl)|(64<eeprom[Icw1]; + ctl = ((te & 0x01E0)<<17)|(te & 0x0010)<<3; + csr32w(ctlr, Ctrl, ctl); + + delay(10); + /* * Flow control - values from the datasheet. */ @@ -889,23 +1211,41 @@ gc82543reset(Ctlr* ctlr) csr32w(ctlr, Fct, 0x00008808); csr32w(ctlr, Fcttv, 0x00000100); - /* - * More here... - * Need to take deal with making sure hardware auto-negotiation is - * set up, how Rxcw and Txcw are configured, Fcrtl, Fcrth, etc. - */ + csr32w(ctlr, Fcrtl, ctlr->fcrtl); + csr32w(ctlr, Fcrth, ctlr->fcrth); - csr32w(ctlr, Imc, ~0); ctlr->im = Lsc; - csr32w(ctlr, Ims, ctlr->im); + gc82543checklink(ctlr); return 0; } +static void +gc82543watchdog(void* arg) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + for(;;){ + ctlr = edev->ctlr; + tsleep(&ctlr->timer, return0, 0, 1000); + + ctlr = edev->ctlr; + if(ctlr == nil){ + print("%s: exiting\n", up->text); + pexit("disabled", 0); + } + + gc82543checklink(ctlr); + gc82543replenish(ctlr); + } +} + static void gc82543pci(void) { - int port; + int port, cls; Pcidev *p; Ctlr *ctlr; @@ -931,64 +1271,38 @@ gc82543pci(void) print("gc82543: can't map %8.8luX\n", p->mem[0].bar); continue; } - + cls = pcicfgr8(p, PciCLS); + switch(cls){ + case 0x00: + case 0xFF: + print("82543gc: unusable cache line size\n"); + continue; + case 0x08: + break; + default: + print("82543gc: cache line size %d, expected 32\n", + cls*4); + } ctlr = malloc(sizeof(Ctlr)); ctlr->port = port; ctlr->pcidev = p; ctlr->id = (p->did<<16)|p->vid; ctlr->nic = KADDR(ctlr->port); -print("status0 %8.8uX\n", csr32r(ctlr, Status)); + if(gc82543reset(ctlr)){ free(ctlr); continue; } -print("status1 %8.8uX\n", csr32r(ctlr, Status)); - if(ctlrhead != nil) - ctlrtail->next = ctlr; + if(gc82543ctlrhead != nil) + gc82543ctlrtail->next = ctlr; else - ctlrhead = ctlr; - ctlrtail = ctlr; + gc82543ctlrhead = ctlr; + gc82543ctlrtail = ctlr; } } -static void -gc82543promiscuous(void* arg, int on) -{ - Ether *edev=arg; - Ctlr *ctlr; - ulong ctl; - - ctlr = edev->ctlr; - ctl = csr32r(ctlr, Rctl); - ctl &= ~MoMASK; /* make sure we're using bits 47:36 */ - if(on) - ctl |= Upe|Mpe; - else - ctl &= ~(Upe|Mpe); - csr32w(ctlr, Rctl, ctl); -} - -static void -gc82543multicast(void* arg, uchar *addr, int on) -{ - Ether *edev=arg; - Ctlr *ctlr; - int x; - int bit; - - ctlr = edev->ctlr; - x = addr[5]>>1; - bit = ((addr[5]&1)<<4)|(addr[4]>>4); - if(on) - ctlr->multimask[x] |= 1<multimask[x] &= ~(1<multimask[x]); -} - static int gc82543pnp(Ether* edev) { @@ -996,14 +1310,14 @@ gc82543pnp(Ether* edev) Ctlr *ctlr; uchar ea[Eaddrlen]; - if(ctlrhead == nil) + if(gc82543ctlrhead == nil) gc82543pci(); /* * Any adapter matches if no edev->port is supplied, * otherwise the ports must match. */ - for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + for(ctlr = gc82543ctlrhead; ctlr != nil; ctlr = ctlr->next){ if(ctlr->active) continue; if(edev->port == 0 || edev->port == ctlr->port){ @@ -1027,7 +1341,7 @@ gc82543pnp(Ether* edev) */ memset(ea, 0, Eaddrlen); if(memcmp(ea, edev->ea, Eaddrlen) == 0){ - for(i = 0; i < Eaddrlen/2; i++){ + for(i = Ea; i < Eaddrlen/2; i++){ edev->ea[2*i] = ctlr->eeprom[i]; edev->ea[2*i+1] = ctlr->eeprom[i]>>8; } @@ -1042,12 +1356,11 @@ gc82543pnp(Ether* edev) edev->interrupt = gc82543interrupt; edev->ifstat = gc82543ifstat; edev->shutdown = gc82543shutdown; + edev->ctl = gc82543ctl; + edev->arg = edev; edev->promiscuous = gc82543promiscuous; edev->multicast = gc82543multicast; - edev->arg = edev; - edev->promiscuous = nil; - return 0; } diff --git a/pc/etherga620.c b/pc/etherga620.c index 0f861811a7a13f54a55a66f4aaebe54a9b6a6112..f351a302072926f47a1198af38a22dc7973c5944 100644 --- a/pc/etherga620.c +++ b/pc/etherga620.c @@ -627,7 +627,8 @@ ga620interrupt(Ureg*, void* arg) if(!(csr32r(ctlr, Mhc) & Is)) return; - rdtsc(&tsc0); + if(m->havetsc) + rdtsc(&tsc0); ctlr->interrupts++; csr32w(ctlr, Hi, 1); @@ -659,7 +660,8 @@ ga620interrupt(Ureg*, void* arg) work = 0; } - rdtsc(&tsc1); + if(m->havetsc) + rdtsc(&tsc1); ctlr->ticks += tsc1-tsc0; } diff --git a/pc/main.c b/pc/main.c index 1d9642965a83f8d167af5c401c412163c818fd73..0ab5399efdbbbb12f5e63a8067a63b603e061b18 100644 --- a/pc/main.c +++ b/pc/main.c @@ -104,6 +104,11 @@ main(void) initseg(); links(); conf.monitor = 1; +{int x; uvlong start, end; +rdtsc(&start); +for(x = 0; x < 10000; x++) rdtsc(&end); +print("%lld\n", end-start); +} chandevreset(); i8253link(); pageinit(); diff --git a/pc/trap.c b/pc/trap.c index a3eea2f037d727480c3854638526004a05c3c6e1..aa586d8e9b980fa657d7307e3fdf4fbc30b065c4 100644 --- a/pc/trap.c +++ b/pc/trap.c @@ -58,6 +58,8 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi); v->next = vctl[vno]; } + if(intrtimes[vno] == nil) + intrtimes[vno] = xalloc(Ntimevec*sizeof(ulong)); vctl[vno] = v; iunlock(&vctllock); } @@ -260,8 +262,6 @@ intrtime(Mach *m, int vno) diff /= m->cpumhz; if(diff >= 1000) diff = 999; - if(intrtimes[vno] == nil) - intrtimes[vno] = xalloc(Ntimevec*sizeof(ulong)); intrtimes[vno][diff]++; } diff --git a/port/devcons.c b/port/devcons.c index 87b7d7277f5bd30851ffae3f3c44b6af9b8bdf0e..fa1015e70480ddc2b5d3f5e86c1315fa5767e03b 100644 --- a/port/devcons.c +++ b/port/devcons.c @@ -641,6 +641,27 @@ consclose(Chan *c) } } +/* + * calculate duty cycle for a processor (100-(%time in idlehands())) + */ +static int +dutycycle(Mach *mp) +{ + static ulong ticks; + uvlong hz; + int x; + + if(ticks == 0){ + fastticks(&hz); + ticks = hz/HZ; + } + x = ticks - mp->avginidle; + if(x < 0) + return 0; + x *= 100; + return x/ticks; +} + static long consread(Chan *c, void *buf, long n, vlong off) { @@ -757,7 +778,7 @@ consread(Chan *c, void *buf, long n, vlong off) return 0; case Qsysstat: - b = smalloc(conf.nmach*(NUMSIZE*8+1) + 1); /* +1 for NUL */ + b = smalloc(conf.nmach*(NUMSIZE*9+1) + 1); /* +1 for NUL */ bp = b; for(id = 0; id < 32; id++) { if(active.machs & (1<load, NUMSIZE); bp += NUMSIZE; + readnum(0, bp, NUMSIZE, dutycycle(mp), NUMSIZE); + bp += NUMSIZE; *bp++ = '\n'; } } diff --git a/port/portdat.h b/port/portdat.h index 04477e0423129b6973098f8d4b7f757e9b93e642..0b2bf7e4e0bcc793088be8e318f574f0f20b3bae 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -135,7 +135,7 @@ struct Block uchar* rp; /* first unconsumed byte */ uchar* wp; /* first empty byte */ uchar* lim; /* 1 past the end of the buffer */ - uchar* base; /* start of the buffer */ + uchar* base; /* start of the buffer */ void (*free)(Block*); ulong flag; }; diff --git a/port/proc.c b/port/proc.c index 08ec1d7013f096015d580a2ba9354c2845fb77af..ee1eb1cc140d52afe5ec0c91e2fb5086027c261b 100644 --- a/port/proc.c +++ b/port/proc.c @@ -211,6 +211,9 @@ runproc(void) Schedq *rq, *xrq; Proc *p, *l; ulong rt; + uvlong start; + + start = 0; if ((p = edf->edfrunproc()) != nil) return p; @@ -262,6 +265,8 @@ loop: } } } + if(start == 0) + start = fastticks(nil); idlehands(); } @@ -301,6 +306,8 @@ found: p->movetime = MACHP(0)->ticks + HZ/10; p->mp = MACHP(m->machno); + if(start) + m->inidle += fastticks(nil)-start; return p; } @@ -1228,8 +1235,8 @@ void accounttime(void) { Proc *p; - int n; - static int nrun; + ulong n; + static ulong nrun; p = m->proc; if(p) { @@ -1244,9 +1251,12 @@ accounttime(void) /* calculate decaying load average */ n = nrun; nrun = 0; - n = (nrdy+n)*1000; m->load = (m->load*19+n)/20; + + /* calculate decaying duty cycle average */ + m->avginidle = (m->avginidle*(HZ-1)+m->inidle)/HZ; + m->inidle = 0; } static void