198 lines
2.9 KiB
C
198 lines
2.9 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include "cformat.h"
|
|
#include "lru.h"
|
|
#include "bcache.h"
|
|
|
|
int
|
|
bcinit(Bcache *bc, int f, int bsize)
|
|
{
|
|
Bbuf *b;
|
|
|
|
/*
|
|
* allocate space for all buffers
|
|
* point all buffers into outer space
|
|
*/
|
|
bc->dfirst = 0;
|
|
bc->bsize = bsize;
|
|
bc->f = f;
|
|
lruinit(bc);
|
|
for(b = bc->bb; b < &bc->bb[Nbcache]; b++){
|
|
b->inuse = 0;
|
|
b->next = 0;
|
|
b->dirty = 0;
|
|
if(b->data == 0)
|
|
b->data = (char *)malloc(bc->bsize);
|
|
if(b->data == 0)
|
|
return -1;
|
|
lruadd(bc, b);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find a buffer for block b. If it's dirty, write it out.
|
|
*/
|
|
Bbuf *
|
|
bcfind(Bcache *bc, ulong bno)
|
|
{
|
|
Bbuf *b;
|
|
|
|
if(bno == Notabno)
|
|
error("bcfind: Notabno");
|
|
bno &= ~Indbno;
|
|
|
|
/*
|
|
* if we already have a buffer for this bno, use it
|
|
*/
|
|
for(b = bc->bb; b < &bc->bb[Nbcache]; b++)
|
|
if(b->inuse && b->bno==bno)
|
|
goto out;
|
|
|
|
/*
|
|
* get least recently used block
|
|
*/
|
|
b = (Bbuf*)bc->lnext;
|
|
out:
|
|
/*
|
|
* if dirty, write it out
|
|
*/
|
|
if(b->dirty)
|
|
if(bcwrite(bc, b) < 0)
|
|
warning("writing dirty page");
|
|
lruref(bc, b);
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* allocate a buffer block for a block. it's guaranteed to be there till
|
|
* the next Nbcache bcread's.
|
|
*/
|
|
Bbuf *
|
|
bcalloc(Bcache *bc, ulong bno)
|
|
{
|
|
Bbuf *b;
|
|
|
|
b = bcfind(bc, bno);
|
|
bno &= ~Indbno;
|
|
b->bno = bno;
|
|
b->inuse = 1;
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* read a block into a buffer cache. it's guaranteed to be there till
|
|
* the next Nbcache bcread's.
|
|
*/
|
|
Bbuf *
|
|
bcread(Bcache *bc, ulong bno)
|
|
{
|
|
Bbuf *b;
|
|
|
|
b = bcfind(bc, bno);
|
|
bno &= ~Indbno;
|
|
if(b->bno!=bno || !b->inuse)
|
|
/*
|
|
* read in the one we really want
|
|
*/
|
|
if(bread(bc, bno, b->data) < 0){
|
|
b->inuse = 0;
|
|
return 0;
|
|
}
|
|
b->bno = bno;
|
|
b->inuse = 1;
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* mark a page dirty, if it's already dirty force a write
|
|
*
|
|
* N.B: ordering is important.
|
|
*/
|
|
void
|
|
bcmark(Bcache *bc, Bbuf *b)
|
|
{
|
|
lruref(bc, b);
|
|
|
|
if(b->dirty){
|
|
bcwrite(bc, b);
|
|
return;
|
|
}
|
|
|
|
b->dirty = 1;
|
|
if(bc->dfirst)
|
|
bc->dlast->next = b;
|
|
else
|
|
bc->dfirst = b;
|
|
bc->dlast = b;
|
|
}
|
|
|
|
/*
|
|
* write out a page (and all preceding dirty ones)
|
|
*/
|
|
int
|
|
bcwrite(Bcache *bc, Bbuf *b)
|
|
{
|
|
Bbuf *nb;
|
|
|
|
/*
|
|
* write out all preceding pages
|
|
*/
|
|
while(nb = bc->dfirst){
|
|
if(bwrite(bc, nb->bno, nb->data) < 0)
|
|
return -1;
|
|
nb->dirty = 0;
|
|
bc->dfirst = nb->next;
|
|
nb->next = 0;
|
|
if(nb == b)
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* write out this page
|
|
*/
|
|
if(bwrite(bc, b->bno, b->data) < 0)
|
|
return -1;
|
|
b->dirty = 0;
|
|
b->next = 0;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* write out all dirty pages (in order)
|
|
*/
|
|
int
|
|
bcsync(Bcache *bc)
|
|
{
|
|
if(bc->dfirst)
|
|
return bcwrite(bc, bc->dlast);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* read a block from disk
|
|
*/
|
|
int
|
|
bread(Bcache *bc, ulong bno, void *buf)
|
|
{
|
|
uvlong x = (uvlong)bno * bc->bsize;
|
|
|
|
if(pread(bc->f, buf, bc->bsize, x) != bc->bsize)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* write a block to disk
|
|
*/
|
|
int
|
|
bwrite(Bcache *bc, ulong bno, void *buf)
|
|
{
|
|
uvlong x = (uvlong)bno * bc->bsize;
|
|
|
|
if(pwrite(bc->f, buf, bc->bsize, x) != bc->bsize)
|
|
return -1;
|
|
return 0;
|
|
}
|