ref: 6b5d69391cb9351b49c480a3cc40f67e184a8d39
parent: aff0dc5e673bcf905ebbde0094b2611dd57b5bed
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Dec 7 12:58:51 EST 2014
ethervirtio: implement promisc and multicast mode, cleanup add vctlcmd() function to setup and comlete control commands. handle Vctlq and implement promiscuous and multicast mode commands. remove Vqueue.block[] and Vqueue.header. these are not properties of the queue (Vctlq as no block array). the block[] array only needs to be half the queue size as we use two descriptors per packet. fix broken shutdown() and remove useless ctl() function.
--- a/sys/src/9/pc/ethervirtio.c
+++ b/sys/src/9/pc/ethervirtio.c
@@ -11,10 +11,6 @@
/*
* virtio ethernet driver
* http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
- *
- * TODO
- *
- * implement control queue
*/
typedef struct Vring Vring;
@@ -78,6 +74,16 @@
Vrxq = 0,
Vtxq = 1,
Vctlq = 2,
+
+ /* class/cmd for Vctlq */
+ CtrlRx = 0x00,
+ CmdPromisc = 0x00,
+ CmdAllmulti = 0x01,
+ CtrlMac = 0x01,
+ CmdMacTableSet = 0x00,
+ CtrlVlan= 0x02,
+ CmdVlanAdd = 0x00,
+ CmdVlanDel = 0x01,
};
struct Vring
@@ -128,24 +134,23 @@
Vused *usedent;
u16int *usedevent;
u16int lastused;
-
- Vheader *header;
- Block **block;
};
struct Ctlr {
Lock;
- int attached;
+ QLock ctllock;
- int port;
- Pcidev* pcidev;
- Ctlr* next;
- int active;
- int id;
- int typ;
+ int attached;
+
+ int port;
+ Pcidev *pcidev;
+ Ctlr *next;
+ int active;
+ int id;
+ int typ;
ulong feat;
- int nqueue;
+ int nqueue;
/* virtioether has 3 queues: rx, tx and ctl */
Vqueue *queue[3];
@@ -166,6 +171,8 @@
static void
txproc(void *v)
{
+ Vheader *header;
+ Block **blocks;
Ether *edev;
Ctlr *ctlr;
Vqueue *q;
@@ -177,12 +184,12 @@
ctlr = edev->ctlr;
q = ctlr->queue[Vtxq];
- while(waserror())
- ;
+ header = smalloc(VheaderSize);
+ blocks = smalloc(sizeof(Block*) * (q->qsize/2));
for(i = 0; i < q->qsize/2; i++){
j = i << 1;
- q->desc[j].addr = PADDR(q->header);
+ q->desc[j].addr = PADDR(header);
q->desc[j].len = VheaderSize;
q->desc[j].next = j | 1;
q->desc[j].flags = Dnext;
@@ -196,6 +203,9 @@
q->used->flags &= ~Rnointerrupt;
+ while(waserror())
+ ;
+
while((b = qbread(edev->oq, 1000000)) != nil){
for(;;){
/* retire completed packets */
@@ -202,16 +212,16 @@
while((i = q->lastused) != q->used->idx){
u = &q->usedent[i & q->qmask];
i = (u->id & q->qmask) >> 1;
- if(q->block[i] == nil)
+ if(blocks[i] == nil)
break;
- freeb(q->block[i]);
- q->block[i] = nil;
+ freeb(blocks[i]);
+ blocks[i] = nil;
q->lastused++;
}
/* have free slot? */
i = q->avail->idx & (q->qmask >> 1);
- if(q->block[i] == nil)
+ if(blocks[i] == nil)
break;
/* ring full, wait and retry */
@@ -220,7 +230,7 @@
}
/* slot is free, fill in descriptor */
- q->block[i] = b;
+ blocks[i] = b;
j = (i << 1) | 1;
q->desc[j].addr = PADDR(b->rp);
q->desc[j].len = BLEN(b);
@@ -235,6 +245,8 @@
static void
rxproc(void *v)
{
+ Vheader *header;
+ Block **blocks;
Ether *edev;
Ctlr *ctlr;
Vqueue *q;
@@ -246,12 +258,12 @@
ctlr = edev->ctlr;
q = ctlr->queue[Vrxq];
- while(waserror())
- ;
+ header = smalloc(VheaderSize);
+ blocks = smalloc(sizeof(Block*) * (q->qsize/2));
for(i = 0; i < q->qsize/2; i++){
j = i << 1;
- q->desc[j].addr = PADDR(q->header);
+ q->desc[j].addr = PADDR(header);
q->desc[j].len = VheaderSize;
q->desc[j].next = j | 1;
q->desc[j].flags = Dwrite|Dnext;
@@ -265,15 +277,18 @@
q->used->flags &= ~Rnointerrupt;
+ while(waserror())
+ ;
+
for(;;){
/* replenish receive ring */
do {
i = q->avail->idx & (q->qmask >> 1);
- if(q->block[i] != nil)
+ if(blocks[i] != nil)
break;
if((b = iallocb(ETHERMAXTU)) == nil)
break;
- q->block[i] = b;
+ blocks[i] = b;
j = (i << 1) | 1;
q->desc[j].addr = PADDR(b->rp);
q->desc[j].len = BALLOC(b);
@@ -290,10 +305,10 @@
while((i = q->lastused) != q->used->idx) {
u = &q->usedent[i & q->qmask];
i = (u->id & q->qmask) >> 1;
- if((b = q->block[i]) == nil)
+ if((b = blocks[i]) == nil)
break;
- q->block[i] = nil;
+ blocks[i] = nil;
b->wp = b->rp + u->len;
etheriq(edev, b, 1);
@@ -302,21 +317,81 @@
}
}
+static int
+vctlcmd(Ether *edev, uchar class, uchar cmd, uchar *data, int ndata)
+{
+ uchar hdr[2], ack[1];
+ Ctlr *ctlr;
+ Vqueue *q;
+ Vdesc *d;
+ int i;
+
+ ctlr = edev->ctlr;
+ q = ctlr->queue[Vctlq];
+ if(q == nil || q->qsize < 3)
+ return -1;
+
+ qlock(&ctlr->ctllock);
+ while(waserror())
+ ;
+
+ ack[0] = 0x55;
+ hdr[0] = class;
+ hdr[1] = cmd;
+
+ d = &q->desc[0];
+ d->addr = PADDR(hdr);
+ d->len = sizeof(hdr);
+ d->next = 1;
+ d->flags = Dnext;
+ d++;
+ d->addr = PADDR(data);
+ d->len = ndata;
+ d->next = 2;
+ d->flags = Dnext;
+ d++;
+ d->addr = PADDR(ack);
+ d->len = sizeof(ack);
+ d->next = 0;
+ d->flags = Dwrite;
+
+ i = q->avail->idx & q->qmask;
+ q->availent[i] = 0;
+ coherence();
+
+ q->used->flags &= ~Rnointerrupt;
+ q->avail->idx++;
+ outs(ctlr->port+Qnotify, Vctlq);
+ while(!vhasroom(q))
+ sleep(q, vhasroom, q);
+ q->lastused = q->used->idx;
+ q->used->flags |= Rnointerrupt;
+
+ qunlock(&ctlr->ctllock);
+ poperror();
+
+ if(ack[0] != 0)
+ print("#l%d: vctlcmd: %ux.%ux -> %ux\n", edev->ctlrno, class, cmd, ack[0]);
+
+ return ack[0];
+}
+
static void
interrupt(Ureg*, void* arg)
{
Ether *edev;
- Ctlr* ctlr;
+ Ctlr *ctlr;
Vqueue *q;
+ int i;
edev = arg;
ctlr = edev->ctlr;
-
if(inb(ctlr->port+Qisr) & 1){
- if(vhasroom(q = ctlr->queue[Vtxq]))
- wakeup(q);
- if(vhasroom(q = ctlr->queue[Vrxq]))
- wakeup(q);
+ for(i = 0; i < ctlr->nqueue; i++){
+ q = ctlr->queue[i];
+ if(vhasroom(q))
+ wakeup(q);
+ }
}
}
@@ -327,21 +402,19 @@
Ctlr* ctlr;
ctlr = edev->ctlr;
-
lock(ctlr);
if(!ctlr->attached){
ctlr->attached = 1;
+ /* ready to go */
+ outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok);
+
/* start kprocs */
snprint(name, sizeof name, "#l%drx", edev->ctlrno);
kproc(name, rxproc, edev);
snprint(name, sizeof name, "#l%dtx", edev->ctlrno);
kproc(name, txproc, edev);
-
- /* ready to go */
- outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok);
}
-
unlock(ctlr);
}
@@ -375,48 +448,31 @@
return n;
}
-/* XXX: not done */
-static long
-ctl(Ether *, void *, long)
-{
- return 0;
-}
-
-/* XXX: not done */
static void
-promiscuous(void *v, int on)
+shutdown(Ether* edev)
{
- Ether *edev;
- Ctlr *ctlr;
-
- edev = v;
- ctlr = edev->ctlr;
-
- USED(ctlr, on);
+ Ctlr *ctlr = edev->ctlr;
+ outb(ctlr->port+Qstatus, 0);
}
-/* XXX: not done */
static void
-shutdown(Ether* ether)
+promiscuous(void *arg, int on)
{
- Ctlr *ctlr;
+ Ether *edev = arg;
+ uchar b[1];
- ctlr = (Ctlr*) ether;
-
- outb(ctlr->port+Qstatus, 0);
+ b[0] = on != 0;
+ vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b));
}
-/* XXX: not done */
static void
multicast(void *arg, uchar*, int)
{
- Ether *edev;
- Ctlr *ctlr;
+ Ether *edev = arg;
+ uchar b[1];
- edev = arg;
- ctlr = edev->ctlr;
-
- USED(ctlr);
+ b[0] = edev->nmaddr > 0;
+ vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b));
}
/* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */
@@ -466,9 +522,6 @@
q->lastused = q->avail->idx = q->used->idx = 0;
- q->block = mallocz(sizeof(Block*) * size, 1);
- q->header = mallocz(VheaderSize, 1);
-
/* disable interrupts
* virtio spec says we still get interrupts if
* VnotifyEmpty is set in Drvfeat */
@@ -614,9 +667,8 @@
edev->interrupt = interrupt;
edev->ifstat = ifstat;
- edev->ctl = ctl;
- edev->promiscuous = promiscuous;
edev->multicast = multicast;
+ edev->promiscuous = promiscuous;
return 0;
}