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.
This commit is contained in:
parent
aff0dc5e67
commit
6b5d69391c
|
@ -11,10 +11,6 @@
|
||||||
/*
|
/*
|
||||||
* virtio ethernet driver
|
* virtio ethernet driver
|
||||||
* http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
|
* http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
|
||||||
*
|
|
||||||
* TODO
|
|
||||||
*
|
|
||||||
* implement control queue
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct Vring Vring;
|
typedef struct Vring Vring;
|
||||||
|
@ -78,6 +74,16 @@ enum {
|
||||||
Vrxq = 0,
|
Vrxq = 0,
|
||||||
Vtxq = 1,
|
Vtxq = 1,
|
||||||
Vctlq = 2,
|
Vctlq = 2,
|
||||||
|
|
||||||
|
/* class/cmd for Vctlq */
|
||||||
|
CtrlRx = 0x00,
|
||||||
|
CmdPromisc = 0x00,
|
||||||
|
CmdAllmulti = 0x01,
|
||||||
|
CtrlMac = 0x01,
|
||||||
|
CmdMacTableSet = 0x00,
|
||||||
|
CtrlVlan= 0x02,
|
||||||
|
CmdVlanAdd = 0x00,
|
||||||
|
CmdVlanDel = 0x01,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Vring
|
struct Vring
|
||||||
|
@ -128,24 +134,23 @@ struct Vqueue
|
||||||
Vused *usedent;
|
Vused *usedent;
|
||||||
u16int *usedevent;
|
u16int *usedevent;
|
||||||
u16int lastused;
|
u16int lastused;
|
||||||
|
|
||||||
Vheader *header;
|
|
||||||
Block **block;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Ctlr {
|
struct Ctlr {
|
||||||
Lock;
|
Lock;
|
||||||
|
|
||||||
int attached;
|
QLock ctllock;
|
||||||
|
|
||||||
int port;
|
int attached;
|
||||||
Pcidev* pcidev;
|
|
||||||
Ctlr* next;
|
int port;
|
||||||
int active;
|
Pcidev *pcidev;
|
||||||
int id;
|
Ctlr *next;
|
||||||
int typ;
|
int active;
|
||||||
|
int id;
|
||||||
|
int typ;
|
||||||
ulong feat;
|
ulong feat;
|
||||||
int nqueue;
|
int nqueue;
|
||||||
|
|
||||||
/* virtioether has 3 queues: rx, tx and ctl */
|
/* virtioether has 3 queues: rx, tx and ctl */
|
||||||
Vqueue *queue[3];
|
Vqueue *queue[3];
|
||||||
|
@ -166,6 +171,8 @@ vhasroom(void *v)
|
||||||
static void
|
static void
|
||||||
txproc(void *v)
|
txproc(void *v)
|
||||||
{
|
{
|
||||||
|
Vheader *header;
|
||||||
|
Block **blocks;
|
||||||
Ether *edev;
|
Ether *edev;
|
||||||
Ctlr *ctlr;
|
Ctlr *ctlr;
|
||||||
Vqueue *q;
|
Vqueue *q;
|
||||||
|
@ -177,12 +184,12 @@ txproc(void *v)
|
||||||
ctlr = edev->ctlr;
|
ctlr = edev->ctlr;
|
||||||
q = ctlr->queue[Vtxq];
|
q = ctlr->queue[Vtxq];
|
||||||
|
|
||||||
while(waserror())
|
header = smalloc(VheaderSize);
|
||||||
;
|
blocks = smalloc(sizeof(Block*) * (q->qsize/2));
|
||||||
|
|
||||||
for(i = 0; i < q->qsize/2; i++){
|
for(i = 0; i < q->qsize/2; i++){
|
||||||
j = i << 1;
|
j = i << 1;
|
||||||
q->desc[j].addr = PADDR(q->header);
|
q->desc[j].addr = PADDR(header);
|
||||||
q->desc[j].len = VheaderSize;
|
q->desc[j].len = VheaderSize;
|
||||||
q->desc[j].next = j | 1;
|
q->desc[j].next = j | 1;
|
||||||
q->desc[j].flags = Dnext;
|
q->desc[j].flags = Dnext;
|
||||||
|
@ -196,22 +203,25 @@ txproc(void *v)
|
||||||
|
|
||||||
q->used->flags &= ~Rnointerrupt;
|
q->used->flags &= ~Rnointerrupt;
|
||||||
|
|
||||||
|
while(waserror())
|
||||||
|
;
|
||||||
|
|
||||||
while((b = qbread(edev->oq, 1000000)) != nil){
|
while((b = qbread(edev->oq, 1000000)) != nil){
|
||||||
for(;;){
|
for(;;){
|
||||||
/* retire completed packets */
|
/* retire completed packets */
|
||||||
while((i = q->lastused) != q->used->idx){
|
while((i = q->lastused) != q->used->idx){
|
||||||
u = &q->usedent[i & q->qmask];
|
u = &q->usedent[i & q->qmask];
|
||||||
i = (u->id & q->qmask) >> 1;
|
i = (u->id & q->qmask) >> 1;
|
||||||
if(q->block[i] == nil)
|
if(blocks[i] == nil)
|
||||||
break;
|
break;
|
||||||
freeb(q->block[i]);
|
freeb(blocks[i]);
|
||||||
q->block[i] = nil;
|
blocks[i] = nil;
|
||||||
q->lastused++;
|
q->lastused++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* have free slot? */
|
/* have free slot? */
|
||||||
i = q->avail->idx & (q->qmask >> 1);
|
i = q->avail->idx & (q->qmask >> 1);
|
||||||
if(q->block[i] == nil)
|
if(blocks[i] == nil)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* ring full, wait and retry */
|
/* ring full, wait and retry */
|
||||||
|
@ -220,7 +230,7 @@ txproc(void *v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* slot is free, fill in descriptor */
|
/* slot is free, fill in descriptor */
|
||||||
q->block[i] = b;
|
blocks[i] = b;
|
||||||
j = (i << 1) | 1;
|
j = (i << 1) | 1;
|
||||||
q->desc[j].addr = PADDR(b->rp);
|
q->desc[j].addr = PADDR(b->rp);
|
||||||
q->desc[j].len = BLEN(b);
|
q->desc[j].len = BLEN(b);
|
||||||
|
@ -235,6 +245,8 @@ txproc(void *v)
|
||||||
static void
|
static void
|
||||||
rxproc(void *v)
|
rxproc(void *v)
|
||||||
{
|
{
|
||||||
|
Vheader *header;
|
||||||
|
Block **blocks;
|
||||||
Ether *edev;
|
Ether *edev;
|
||||||
Ctlr *ctlr;
|
Ctlr *ctlr;
|
||||||
Vqueue *q;
|
Vqueue *q;
|
||||||
|
@ -246,12 +258,12 @@ rxproc(void *v)
|
||||||
ctlr = edev->ctlr;
|
ctlr = edev->ctlr;
|
||||||
q = ctlr->queue[Vrxq];
|
q = ctlr->queue[Vrxq];
|
||||||
|
|
||||||
while(waserror())
|
header = smalloc(VheaderSize);
|
||||||
;
|
blocks = smalloc(sizeof(Block*) * (q->qsize/2));
|
||||||
|
|
||||||
for(i = 0; i < q->qsize/2; i++){
|
for(i = 0; i < q->qsize/2; i++){
|
||||||
j = i << 1;
|
j = i << 1;
|
||||||
q->desc[j].addr = PADDR(q->header);
|
q->desc[j].addr = PADDR(header);
|
||||||
q->desc[j].len = VheaderSize;
|
q->desc[j].len = VheaderSize;
|
||||||
q->desc[j].next = j | 1;
|
q->desc[j].next = j | 1;
|
||||||
q->desc[j].flags = Dwrite|Dnext;
|
q->desc[j].flags = Dwrite|Dnext;
|
||||||
|
@ -265,15 +277,18 @@ rxproc(void *v)
|
||||||
|
|
||||||
q->used->flags &= ~Rnointerrupt;
|
q->used->flags &= ~Rnointerrupt;
|
||||||
|
|
||||||
|
while(waserror())
|
||||||
|
;
|
||||||
|
|
||||||
for(;;){
|
for(;;){
|
||||||
/* replenish receive ring */
|
/* replenish receive ring */
|
||||||
do {
|
do {
|
||||||
i = q->avail->idx & (q->qmask >> 1);
|
i = q->avail->idx & (q->qmask >> 1);
|
||||||
if(q->block[i] != nil)
|
if(blocks[i] != nil)
|
||||||
break;
|
break;
|
||||||
if((b = iallocb(ETHERMAXTU)) == nil)
|
if((b = iallocb(ETHERMAXTU)) == nil)
|
||||||
break;
|
break;
|
||||||
q->block[i] = b;
|
blocks[i] = b;
|
||||||
j = (i << 1) | 1;
|
j = (i << 1) | 1;
|
||||||
q->desc[j].addr = PADDR(b->rp);
|
q->desc[j].addr = PADDR(b->rp);
|
||||||
q->desc[j].len = BALLOC(b);
|
q->desc[j].len = BALLOC(b);
|
||||||
|
@ -290,10 +305,10 @@ rxproc(void *v)
|
||||||
while((i = q->lastused) != q->used->idx) {
|
while((i = q->lastused) != q->used->idx) {
|
||||||
u = &q->usedent[i & q->qmask];
|
u = &q->usedent[i & q->qmask];
|
||||||
i = (u->id & q->qmask) >> 1;
|
i = (u->id & q->qmask) >> 1;
|
||||||
if((b = q->block[i]) == nil)
|
if((b = blocks[i]) == nil)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
q->block[i] = nil;
|
blocks[i] = nil;
|
||||||
|
|
||||||
b->wp = b->rp + u->len;
|
b->wp = b->rp + u->len;
|
||||||
etheriq(edev, b, 1);
|
etheriq(edev, b, 1);
|
||||||
|
@ -302,21 +317,81 @@ rxproc(void *v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
interrupt(Ureg*, void* arg)
|
interrupt(Ureg*, void* arg)
|
||||||
{
|
{
|
||||||
Ether *edev;
|
Ether *edev;
|
||||||
Ctlr* ctlr;
|
Ctlr *ctlr;
|
||||||
Vqueue *q;
|
Vqueue *q;
|
||||||
|
int i;
|
||||||
|
|
||||||
edev = arg;
|
edev = arg;
|
||||||
ctlr = edev->ctlr;
|
ctlr = edev->ctlr;
|
||||||
|
|
||||||
if(inb(ctlr->port+Qisr) & 1){
|
if(inb(ctlr->port+Qisr) & 1){
|
||||||
if(vhasroom(q = ctlr->queue[Vtxq]))
|
for(i = 0; i < ctlr->nqueue; i++){
|
||||||
wakeup(q);
|
q = ctlr->queue[i];
|
||||||
if(vhasroom(q = ctlr->queue[Vrxq]))
|
if(vhasroom(q))
|
||||||
wakeup(q);
|
wakeup(q);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,21 +402,19 @@ attach(Ether* edev)
|
||||||
Ctlr* ctlr;
|
Ctlr* ctlr;
|
||||||
|
|
||||||
ctlr = edev->ctlr;
|
ctlr = edev->ctlr;
|
||||||
|
|
||||||
lock(ctlr);
|
lock(ctlr);
|
||||||
if(!ctlr->attached){
|
if(!ctlr->attached){
|
||||||
ctlr->attached = 1;
|
ctlr->attached = 1;
|
||||||
|
|
||||||
|
/* ready to go */
|
||||||
|
outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok);
|
||||||
|
|
||||||
/* start kprocs */
|
/* start kprocs */
|
||||||
snprint(name, sizeof name, "#l%drx", edev->ctlrno);
|
snprint(name, sizeof name, "#l%drx", edev->ctlrno);
|
||||||
kproc(name, rxproc, edev);
|
kproc(name, rxproc, edev);
|
||||||
snprint(name, sizeof name, "#l%dtx", edev->ctlrno);
|
snprint(name, sizeof name, "#l%dtx", edev->ctlrno);
|
||||||
kproc(name, txproc, edev);
|
kproc(name, txproc, edev);
|
||||||
|
|
||||||
/* ready to go */
|
|
||||||
outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock(ctlr);
|
unlock(ctlr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,48 +448,31 @@ ifstat(Ether *edev, void *a, long n, ulong offset)
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: not done */
|
|
||||||
static long
|
|
||||||
ctl(Ether *, void *, long)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX: not done */
|
|
||||||
static void
|
static void
|
||||||
promiscuous(void *v, int on)
|
shutdown(Ether* edev)
|
||||||
{
|
{
|
||||||
Ether *edev;
|
Ctlr *ctlr = edev->ctlr;
|
||||||
Ctlr *ctlr;
|
|
||||||
|
|
||||||
edev = v;
|
|
||||||
ctlr = edev->ctlr;
|
|
||||||
|
|
||||||
USED(ctlr, on);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX: not done */
|
|
||||||
static void
|
|
||||||
shutdown(Ether* ether)
|
|
||||||
{
|
|
||||||
Ctlr *ctlr;
|
|
||||||
|
|
||||||
ctlr = (Ctlr*) ether;
|
|
||||||
|
|
||||||
outb(ctlr->port+Qstatus, 0);
|
outb(ctlr->port+Qstatus, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: not done */
|
static void
|
||||||
|
promiscuous(void *arg, int on)
|
||||||
|
{
|
||||||
|
Ether *edev = arg;
|
||||||
|
uchar b[1];
|
||||||
|
|
||||||
|
b[0] = on != 0;
|
||||||
|
vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b));
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
multicast(void *arg, uchar*, int)
|
multicast(void *arg, uchar*, int)
|
||||||
{
|
{
|
||||||
Ether *edev;
|
Ether *edev = arg;
|
||||||
Ctlr *ctlr;
|
uchar b[1];
|
||||||
|
|
||||||
edev = arg;
|
b[0] = edev->nmaddr > 0;
|
||||||
ctlr = edev->ctlr;
|
vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b));
|
||||||
|
|
||||||
USED(ctlr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */
|
/* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */
|
||||||
|
@ -466,9 +522,6 @@ mkqueue(int size)
|
||||||
|
|
||||||
q->lastused = q->avail->idx = q->used->idx = 0;
|
q->lastused = q->avail->idx = q->used->idx = 0;
|
||||||
|
|
||||||
q->block = mallocz(sizeof(Block*) * size, 1);
|
|
||||||
q->header = mallocz(VheaderSize, 1);
|
|
||||||
|
|
||||||
/* disable interrupts
|
/* disable interrupts
|
||||||
* virtio spec says we still get interrupts if
|
* virtio spec says we still get interrupts if
|
||||||
* VnotifyEmpty is set in Drvfeat */
|
* VnotifyEmpty is set in Drvfeat */
|
||||||
|
@ -614,9 +667,8 @@ reset(Ether* edev)
|
||||||
edev->interrupt = interrupt;
|
edev->interrupt = interrupt;
|
||||||
|
|
||||||
edev->ifstat = ifstat;
|
edev->ifstat = ifstat;
|
||||||
edev->ctl = ctl;
|
|
||||||
edev->promiscuous = promiscuous;
|
|
||||||
edev->multicast = multicast;
|
edev->multicast = multicast;
|
||||||
|
edev->promiscuous = promiscuous;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue