shithub: riscv

Download patch

ref: b7b734bd5987b34bada728e103b0732153cbb784
parent: 80381d93d9d162b819055eedfdcc6d249def340b
author: cinap_lenrek <[email protected]>
date: Mon Apr 23 07:42:05 EDT 2012

sdvirtio: reset device, better block completion handling

--- a/sys/src/9/pc/sdvirtio.c
+++ b/sys/src/9/pc/sdvirtio.c
@@ -63,6 +63,7 @@
 
 struct Vqueue
 {
+	Lock;
 	int	size;
 
 	int	free;
@@ -77,12 +78,9 @@
 	Vring	*used;
 	Vused	*usedent;
 	u16int	*usedevent;
-
 	u16int	lastused;
 
-	Rendez;
-	QLock;
-	Lock;
+	void	*rock[];
 };
 
 struct Vdev
@@ -92,7 +90,7 @@
 	Pcidev	*pci;
 
 	ulong	port;
-	ulong	features;
+	ulong	feat;
 
 	int	nqueue;
 	Vqueue	*queue[16];
@@ -107,7 +105,7 @@
 	uchar *p;
 	int i;
 
-	q = malloc(sizeof(*q));
+	q = malloc(sizeof(*q) + sizeof(void*)*size);
 	p = mallocalign(
 		PGROUND(sizeof(Vdesc)*size + 
 			sizeof(Vring) + 
@@ -175,8 +173,12 @@
 		vd->port = p->mem[0].bar & ~0x1;
 		vd->typ = typ;
 		vd->pci = p;
-		vd->features = inl(vd->port+Devfeat);
-		outb(vd->port+Status, inb(vd->port+Status)|Acknowledge|Driver);
+
+		/* reset */
+		outb(vd->port+Status, 0);
+
+		vd->feat = inl(vd->port+Devfeat);
+		outb(vd->port+Status, Acknowledge|Driver);
 		for(i=0; i<nelem(vd->queue); i++){
 			outs(vd->port+Qselect, i);
 			if((n = ins(vd->port+Qsize)) == 0)
@@ -199,62 +201,54 @@
 }
 
 struct Rock {
-	Vqueue *q;
-	int id;
 	int done;
+	Rendez *sleep;
 };
 
 static void
 viointerrupt(Ureg *, void *arg)
 {
+	int id, free, m;
+	struct Rock *r;
+	Vqueue *q;
 	Vdev *vd;
 
 	vd = arg;
-	if(inb(vd->port+Isr) & 1)
-		wakeup(vd->queue[0]);
+	if(inb(vd->port+Isr) & 1){
+		q = vd->queue[0];
+		m = q->size-1;
+
+		ilock(q);
+		while((q->lastused ^ q->used->idx) & m){
+			id = q->usedent[q->lastused++ & m].id;
+			if(r = q->rock[id]){
+				q->rock[id] = nil;
+				r->done = 1;
+				wakeup(r->sleep);
+			}
+			do {
+				free = id;
+				id = q->desc[free].next;
+				q->desc[free].next = q->free;
+				q->free = free;
+				q->nfree++;
+			} while(q->desc[free].flags & Next);
+		}
+		iunlock(q);
+	}
 }
 
 static int
 viodone(void *arg)
 {
-	struct Rock *r;
-	Vqueue *q;
-	u16int i;
-
-	r = arg;
-	q = r->q;
-	for(i = q->lastused; i != q->used->idx; i++)
-		if(q->usedent[i % q->size].id == r->id){
-			if(i == q->lastused)
-				q->lastused++;
-			r->done = 1;
-			break;
-		}
-	return r->done;
+	return ((struct Rock*)arg)->done;
 }
 
-static void
-viowait(Vqueue *q, int id)
-{
-	struct Rock r;
-
-	r.q = q;
-	r.id = id;
-	r.done = 0;
-	do {
-		qlock(q);
-		while(waserror())
-			;
-		sleep(q, viodone, &r);
-		poperror();
-		qunlock(q);
-	} while(!r.done);
-}
-
 static int
-vioreq(Vdev *vd, int typ, uchar *a, long count, long secsize, uvlong lba)
+vioreq(Vdev *vd, int typ, void *a, long count, long secsize, uvlong lba)
 {
-	int i, free, head;
+	struct Rock rock;
+	int free, head;
 	Vqueue *q;
 	Vdesc *d;
 
@@ -270,15 +264,19 @@
 	req.prio = 0;
 	req.lba = lba;
 
+	rock.done = 0;
+	rock.sleep = &up->sleep;
+
 	q = vd->queue[0];
-	for(;;){
-		lock(q);
-		if(q->nfree >= count+2)
-			break;
-		unlock(q);
+	ilock(q);
+	while(q->nfree < 3){
+		iunlock(q);
+
 		if(!waserror())
 			tsleep(&up->sleep, return0, 0, 500);
 		poperror();
+
+		ilock(q);
 	}
 
 	head = free = q->free;
@@ -288,37 +286,36 @@
 	d->len = sizeof(req);
 	d->flags = Next;
 
-	for(i = 0; i<count; i++){
-		d = &q->desc[free]; free = d->next;
-		d->addr = PADDR(a);
-		d->len = secsize;
-		d->flags = typ ? Next : (Write|Next);
-		a += secsize;
-	}
+	d = &q->desc[free]; free = d->next;
+	d->addr = PADDR(a);
+	d->len = secsize*count;
+	d->flags = typ ? Next : (Write|Next);
 
 	d = &q->desc[free]; free = d->next;
 	d->addr = PADDR(&status);
 	d->len = sizeof(status);
 	d->flags = Write;
-	d->next = -1;
 
 	q->free = free;
-	q->nfree -= 2+count;
+	q->nfree -= 3;
 
-	coherence();
-	q->availent[q->avail->idx++ % q->size] = head;
-	unlock(q);
+	q->rock[head] = &rock;
 
 	coherence();
+	q->availent[q->avail->idx++ & (q->size-1)] = head;
+	coherence();
 	outs(vd->port+Qnotify, 0);
+	iunlock(q);
 
-	viowait(q, head);
+	while(!rock.done){
+		while(waserror())
+			;
+		tsleep(rock.sleep, viodone, &rock, 1000);
+		poperror();
 
-	lock(q);
-	d->next = q->free;
-	q->free = head;
-	q->nfree += 2+count;
-	unlock(q);
+		if(!rock.done)
+			viointerrupt(nil, vd);
+	}
 
 	return status;
 }
@@ -327,13 +324,11 @@
 viobio(SDunit *u, int, int write, void *a, long count, uvlong lba)
 {
 	long ss, cc, max, ret;
-	Vqueue *q;
 	Vdev *vd;
 
+	max = 32;
 	ss = u->secsize;
 	vd = u->dev->ctlr;
-	q = vd->queue[0];
-	max = q->size-2;
 
 	ret = 0;
 	while(count > 0){