shithub: riscv

Download patch

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;
 }