code: mafs

Download patch

ref: 09233644202cb9d4b5623424db913767e42b29fc
parent: 8db68ca3d6ed8120aef097e365b88e545c4cce6c
author: 9ferno <gophone2015@gmail.com>
date: Wed Oct 26 06:36:51 EDT 2022

removed the wlock() in dowrite() to avoid deadlocks between putwrite() and dowrite()

--- a/all.h
+++ b/all.h
@@ -76,8 +76,19 @@
 		u8	*xiobuf;	/* "real" buffer pointer */
 		Content *io;	/* cast'able to contents */
 	};
-	u64	dirties;	/* number of dirty blocks yet to be written by the writer */
-	u8 tofree;		/* free this Iobuf after the dirty blocks are written to the disk */
+
+	/*
+		Using Ref to avoid using a wlock() in dowrite.
+		wlock() in dowrite() causes a deadlock with putwrite()
+
+		dirties is decremented without a wlock() on the buffer in dowrite().
+		Using a wlock() in dowrite() deadlocks with putwrite().
+		getbuf() guarantees that even a free'ed block cannot be
+		stolen until the dirties == 0. This avoids dirty blocks
+		being stolen by other block numbers.
+		incref(dirties) only happens with a wlock() in putwrite().
+	 */
+	Ref	dirties;	/* number of dirty blocks yet to be written by the writer */
 };
 
 extern	u64	nbuckets;		/* n hash buckets for i/o */
--- a/iobuf.c
+++ b/iobuf.c
@@ -153,13 +153,20 @@
 		but, setting this stolen buffer as the lru. I figure it should not matter
 		much either way. If it does, there is a changelru() function to do so in the
 		git history that can be reused.
+
+		dirties is decremented without a wlock() on the buffer.
+		Using a wlock() in dowrite() deadlocks with putwrite().
+		getbuf() guarantees that even a free'ed block cannot be
+		stolen until the dirties == 0. This avoids dirty blocks
+		being stolen by other block numbers.
+		incref(dirties) only happens with a wlock() in putwrite().
 	 */
 	if(ncollisions >= Ncollisions){
 Another:
 		do{
 			p = s->back;
-			if(p->ref == 0 && p->dirties == 0 && canwlock(p)){
-				if(p->dirties > 0 || p->ref > 0){
+			if(p->ref == 0 && p->dirties.ref == 0 && canwlock(p)){
+				if(p->dirties.ref > 0 || p->ref > 0){
 					wunlock(p);
 					goto Another;
 				}
--- a/sub.c
+++ b/sub.c
@@ -122,11 +122,8 @@
 		panic("freeblockbuf without Bwritable");
 
 	/* clear the buf to avoid leaks on reuse */
-	memset(buf->io, 0, Rawblocksize);
-	if(buf->dirties)
-		buf->tofree = 1;
-	else
-		bfree(&frees, buf->blkno, 1);
+	memset(buf->xiobuf, 0, Rawblocksize);
+	bfree(&frees, buf->blkno, 1);
 	putbuffree(buf);
 }
 
--- a/writer.c
+++ b/writer.c
@@ -110,18 +110,11 @@
 {
 	Wbuf *w;
 
-	/*
-		copying contents to w before.
-		This avoids rsleep'ing while holding a wlock() on the Iobuf.
-		rsleep'ing while holding the wlock() causes a deadlock()
-		with dowrite() when it tries to decrement dirties by holding
-		a wlock() on the Iobuf.
-	*/
 	w = emalloc9p(sizeof(Wbuf));
 	w->blkno = b->blkno;
 	w->payload = allocmemunit();
 	memcpy(w->payload, b->xiobuf, Rawblocksize);
-	b->dirties++;
+	incref(&b->dirties);
 	w->iobuf = b;
 	if(chkwunlock(b) == 0){
 		showbuf(b);
@@ -152,6 +145,14 @@
 	qunlock(&drts.lck);
 }
 
+/*
+	dirties is decremented without a wlock() on the buffer in dowrite().
+	Using a wlock() in dowrite() deadlocks with putwrite().
+	getbuf() guarantees that even a free'ed block cannot be
+	stolen until the dirties == 0. This avoids dirty blocks
+	being stolen by other block numbers.
+	incref(dirties) only happens with a wlock() in putwrite().
+ */
 void
 dowrite(Wbuf *p)
 {
@@ -174,13 +175,7 @@
 	n = p->blkno;
 	if(chatty9p > 4)
 	dprint("dowrite %llud wunlock()'ed\n", n);
-	wlock(p->iobuf);
-	p->iobuf->dirties--;
-	if(p->iobuf->tofree && p->iobuf->dirties == 0){
-		p->iobuf->tofree = 0;
-		bfree(&frees, p->iobuf->blkno, 1);
-	}
-	wunlock(p->iobuf);
+	decref(&p->iobuf->dirties);
 	freememunit(p->payload);
 	free(p);
 }