ref: c217da0b5b1e92d0af62f5f373514d1befdb206e
parent: 501e69d0108812d41f9772dc21cb075af9c65490
author: cinap_lenrek <[email protected]>
date: Mon Dec 8 14:19:53 EST 2014
ethervirtio: feature negotiation, allow setting mac address, dont read isr from ifstatus, avoid indirection - properly negotiate Fctrlrx feature bit for promisc and multicast. - allow setting mac address with ea= option from plan9.ini - dont read the isr register from ifstats() as it has the side effect of reseting isr status - embedd the Vqueue array in the Ctlr structure avoiding indirection - add a interrupt counter Vqueue.nintr for statistical purposes - only read network status register if the feature has been negotiated - change name to "virtio" as "ethervirtio" is kind of redundant
--- a/sys/src/9/pc/ethervirtio.c
+++ b/sys/src/9/pc/ethervirtio.c
@@ -48,6 +48,7 @@
Fmac = (1<<5),
Fstatus = (1<<16),
Fctrlvq = (1<<17),
+ Fctrlrx = (1<<18),
/* vring used flags */
Unonotify = 1,
@@ -134,6 +135,8 @@
Vused *usedent;
u16int *usedevent;
u16int lastused;
+
+ uint nintr;
};
struct Ctlr {
@@ -153,10 +156,7 @@
int nqueue;
/* virtioether has 3 queues: rx, tx and ctl */
- Vqueue *queue[3];
-
- /* MAC address */
- uchar ea[Eaddrlen];
+ Vqueue queue[3];
};
static Ctlr *ctlrhead;
@@ -182,7 +182,7 @@
edev = v;
ctlr = edev->ctlr;
- q = ctlr->queue[Vtxq];
+ q = &ctlr->queue[Vtxq];
header = smalloc(VheaderSize);
blocks = smalloc(sizeof(Block*) * (q->qsize/2));
@@ -256,7 +256,7 @@
edev = v;
ctlr = edev->ctlr;
- q = ctlr->queue[Vrxq];
+ q = &ctlr->queue[Vrxq];
header = smalloc(VheaderSize);
blocks = smalloc(sizeof(Block*) * (q->qsize/2));
@@ -327,8 +327,8 @@
int i;
ctlr = edev->ctlr;
- q = ctlr->queue[Vctlq];
- if(q == nil || q->qsize < 3)
+ q = &ctlr->queue[Vctlq];
+ if(q->qsize < 3)
return -1;
qlock(&ctlr->ctllock);
@@ -388,9 +388,11 @@
ctlr = edev->ctlr;
if(inb(ctlr->port+Qisr) & 1){
for(i = 0; i < ctlr->nqueue; i++){
- q = ctlr->queue[i];
- if(vhasroom(q))
+ q = &ctlr->queue[i];
+ if(vhasroom(q)){
+ q->nintr++;
wakeup(q);
+ }
}
}
}
@@ -433,13 +435,13 @@
l = snprint(p, READSTR, "devfeat %32.32lub\n", ctlr->feat);
l += snprint(p+l, READSTR-l, "drvfeat %32.32lub\n", inl(ctlr->port+Qdrvfeat));
l += snprint(p+l, READSTR-l, "devstatus %8.8ub\n", inb(ctlr->port+Qstatus));
- l += snprint(p+l, READSTR-l, "isr %8.8ub\n", inb(ctlr->port+Qisr));
- l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus));
+ if(ctlr->feat & Fstatus)
+ l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus));
for(i = 0; i < ctlr->nqueue; i++){
- q = ctlr->queue[i];
- l += snprint(p+l, READSTR-l, "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud\n",
- i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused);
+ q = &ctlr->queue[i];
+ l += snprint(p+l, READSTR-l, "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud nintr %ud\n",
+ i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused, q->nintr);
}
n = readstr(offset, a, n, p);
@@ -483,22 +485,19 @@
+ VPGROUND(sizeof(u16int)*3 + VusedSize*size);
}
-static Vqueue*
-mkqueue(int size)
+static int
+initqueue(Vqueue *q, int size)
{
- Vqueue *q;
uchar *p;
/* §2.4: Queue Size value is always a power of 2 and <= 32768 */
assert(!(size & (size - 1)) && size <= 32768);
- q = mallocz(sizeof(Vqueue), 1);
p = mallocalign(queuesize(size), VBY2PG, 0, 0);
- if(p == nil || q == nil){
+ if(p == nil){
print("ethervirtio: no memory for Vqueue\n");
free(p);
- free(q);
- return nil;
+ return -1;
}
q->desc = (void*)p;
@@ -521,13 +520,9 @@
q->qmask = q->qsize - 1;
q->lastused = q->avail->idx = q->used->idx = 0;
-
- /* disable interrupts
- * virtio spec says we still get interrupts if
- * VnotifyEmpty is set in Drvfeat */
q->used->flags |= Rnointerrupt;
- return q;
+ return 0;
}
static Ctlr*
@@ -552,13 +547,12 @@
/* non-transitional device will have typ+0x40 */
if(pcicfgr16(p, 0x2E) != typ)
continue;
- if((c = malloc(sizeof(Ctlr))) == nil){
+ if((c = mallocz(sizeof(Ctlr), 1)) == nil){
print("ethervirtio: no memory for Ctlr\n");
break;
}
c->port = p->mem[0].bar & ~0x1;
-
if(ioalloc(c->port, p->mem[0].size, 0, "ethervirtio") < 0){
print("ethervirtio: port %ux in use\n", c->port);
free(c);
@@ -571,53 +565,25 @@
/* §3.1.2 Legacy Device Initialization */
outb(c->port+Qstatus, 0);
-
outb(c->port+Qstatus, Sacknowledge|Sdriver);
+ /* negotiate feature bits */
c->feat = inl(c->port+Qdevfeat);
+ outl(c->port+Qdrvfeat, c->feat & (Fmac|Fstatus|Fctrlvq|Fctrlrx));
- if((c->feat & (Fmac|Fstatus|Fctrlvq)) != (Fmac|Fstatus|Fctrlvq)){
- print("ethervirtio: feature mismatch %32.32lub\n", c->feat);
- outb(c->port+Qstatus, Sfailed);
- iofree(c->port);
- free(c);
- continue;
- }
-
- outl(c->port+Qdrvfeat, Fmac|Fstatus|Fctrlvq);
-
- /* part of the 1.0 spec, not used in legacy */
- /*
- outb(vd->port+Status, inb(vd->port+Status) | FeatureOk);
- i = inb(vd->port+Status);
- if(!(i & FeatureOk)){
- print("ethervirtio: feature mismatch %32.32lub\n", vd->feat);
- outb(vd->port+Status, Failed);
- iofree(vd->port);
- free(vd);
- continue;
- }
- */
-
/* §4.1.5.1.4 Virtqueue Configuration */
for(i=0; i<nelem(c->queue); i++){
outs(c->port+Qselect, i);
n = ins(c->port+Qsize);
- if(n == 0 || (n & (n-1)) != 0){
- c->queue[i] = nil;
+ if(n == 0 || (n & (n-1)) != 0)
break;
- }
- if((c->queue[i] = mkqueue(n)) == nil)
+ if(initqueue(&c->queue[i], n) < 0)
break;
coherence();
- outl(c->port+Qaddr, PADDR(c->queue[i]->desc)/VBY2PG);
+ outl(c->port+Qaddr, PADDR(c->queue[i].desc)/VBY2PG);
}
- c->nqueue = i;
+ c->nqueue = i;
- /* read virtio mac */
- for(i = 0; i < Eaddrlen; i++)
- c->ea[i] = inb(c->port+Qmac+i);
-
if(h == nil)
h = c;
else
@@ -632,7 +598,9 @@
static int
reset(Ether* edev)
{
+ static uchar zeros[Eaddrlen];
Ctlr *ctlr;
+ int i;
if(ctlrhead == nil) {
ctlrhead = pciprobe(1);
@@ -657,19 +625,26 @@
edev->mbps = 1000;
edev->link = 1;
- memmove(edev->ea, ctlr->ea, Eaddrlen);
+ if((ctlr->feat & Fmac) != 0 && memcmp(edev->ea, zeros, Eaddrlen) == 0){
+ for(i = 0; i < Eaddrlen; i++)
+ edev->ea[i] = inb(ctlr->port+Qmac+i);
+ } else {
+ for(i = 0; i < Eaddrlen; i++)
+ outb(ctlr->port+Qmac+i, edev->ea[i]);
+ }
edev->arg = edev;
edev->attach = attach;
edev->shutdown = shutdown;
-
edev->interrupt = interrupt;
-
edev->ifstat = ifstat;
- edev->multicast = multicast;
- edev->promiscuous = promiscuous;
+ if((ctlr->feat & (Fctrlvq|Fctrlrx)) == (Fctrlvq|Fctrlrx)){
+ edev->multicast = multicast;
+ edev->promiscuous = promiscuous;
+ }
+
return 0;
}
@@ -676,6 +651,6 @@
void
ethervirtiolink(void)
{
- addethercard("ethervirtio", reset);
+ addethercard("virtio", reset);
}