ref: 09233644202cb9d4b5623424db913767e42b29fc
parent: 8db68ca3d6ed8120aef097e365b88e545c4cce6c
author: 9ferno <[email protected]>
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);
}