File system access library.
This commit is contained in:
parent
be7cbb4ef2
commit
0c98da8bf8
16 changed files with 2967 additions and 0 deletions
311
src/libdiskfs/cache.c
Normal file
311
src/libdiskfs/cache.c
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <diskfs.h>
|
||||
|
||||
/*
|
||||
* Disk cache. Caches by offset, so higher levels have
|
||||
* to deal with alignment issues (if we get asked for the
|
||||
* blocks at offsets 0 and 1, we'll do two reads.
|
||||
*/
|
||||
|
||||
typedef struct DiskCache DiskCache;
|
||||
typedef struct DiskCacheBlock DiskCacheBlock;
|
||||
|
||||
struct DiskCache
|
||||
{
|
||||
Disk disk;
|
||||
Disk *subdisk;
|
||||
DiskCacheBlock **h;
|
||||
DiskCacheBlock *lruhead;
|
||||
DiskCacheBlock *lrutail;
|
||||
int nhash;
|
||||
int blocksize;
|
||||
Lock lk;
|
||||
};
|
||||
|
||||
struct DiskCacheBlock
|
||||
{
|
||||
Block block;
|
||||
Block *subblock;
|
||||
Lock lk;
|
||||
int ref;
|
||||
DiskCache *dc;
|
||||
DiskCacheBlock *next;
|
||||
DiskCacheBlock *lrunext;
|
||||
DiskCacheBlock *lruprev;
|
||||
u64int offset;
|
||||
};
|
||||
|
||||
static void
|
||||
addtohash(DiskCache *d, DiskCacheBlock *b, u64int offset)
|
||||
{
|
||||
int h;
|
||||
|
||||
if(b->offset != ~(u64int)0){
|
||||
fprint(2, "bad offset in addtohash\n");
|
||||
return;
|
||||
}
|
||||
b->offset = offset;
|
||||
h = offset % d->nhash;
|
||||
b->next = d->h[h];
|
||||
d->h[h] = b;
|
||||
}
|
||||
|
||||
static void
|
||||
delfromhash(DiskCache *d, DiskCacheBlock *b)
|
||||
{
|
||||
int h;
|
||||
DiskCacheBlock **l;
|
||||
|
||||
if(b->offset == ~(u64int)0)
|
||||
return;
|
||||
|
||||
h = b->offset % d->nhash;
|
||||
for(l=&d->h[h]; *l; l=&(*l)->next)
|
||||
if(*l == b){
|
||||
*l = b->next;
|
||||
b->offset = ~(u64int)0;
|
||||
return;
|
||||
}
|
||||
fprint(2, "delfromhash: didn't find in hash table\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
putmru(DiskCache *d, DiskCacheBlock *b)
|
||||
{
|
||||
b->lruprev = nil;
|
||||
b->lrunext = d->lruhead;
|
||||
d->lruhead = b;
|
||||
if(b->lrunext == nil)
|
||||
d->lrutail = b;
|
||||
else
|
||||
b->lrunext->lruprev = b;
|
||||
}
|
||||
|
||||
static void
|
||||
putlru(DiskCache *d, DiskCacheBlock *b)
|
||||
{
|
||||
b->lruprev = d->lrutail;
|
||||
b->lrunext = nil;
|
||||
d->lrutail = b;
|
||||
if(b->lruprev == nil)
|
||||
d->lruhead = b;
|
||||
else
|
||||
b->lruprev->lrunext = b;
|
||||
}
|
||||
|
||||
static void
|
||||
delfromlru(DiskCache *d, DiskCacheBlock *b)
|
||||
{
|
||||
if(b->lruprev)
|
||||
b->lruprev->lrunext = b->lrunext;
|
||||
else
|
||||
d->lruhead = b->lrunext;
|
||||
if(b->lrunext)
|
||||
b->lrunext->lruprev = b->lruprev;
|
||||
else
|
||||
d->lrutail = b->lruprev;
|
||||
}
|
||||
|
||||
static DiskCacheBlock*
|
||||
getlru(DiskCache *d)
|
||||
{
|
||||
DiskCacheBlock *b;
|
||||
|
||||
b = d->lrutail;
|
||||
if(b){
|
||||
delfromlru(d, b);
|
||||
delfromhash(d, b);
|
||||
blockput(b->subblock);
|
||||
b->subblock = nil;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
static DiskCacheBlock*
|
||||
findblock(DiskCache *d, u64int offset)
|
||||
{
|
||||
int h;
|
||||
DiskCacheBlock *b;
|
||||
|
||||
h = offset % d->nhash;
|
||||
for(b=d->h[h]; b; b=b->next)
|
||||
if(b->offset == offset)
|
||||
return b;
|
||||
return nil;
|
||||
}
|
||||
|
||||
static DiskCacheBlock*
|
||||
diskcachereadbig(DiskCache *d, u64int offset)
|
||||
{
|
||||
Block *b;
|
||||
DiskCacheBlock *dcb;
|
||||
|
||||
lock(&d->lk);
|
||||
dcb = findblock(d, offset);
|
||||
if(dcb){
|
||||
//fprint(2, "found %llud in cache %p\n", (uvlong)offset, dcb);
|
||||
if(dcb->ref++ == 0)
|
||||
delfromlru(d, dcb);
|
||||
unlock(&d->lk);
|
||||
return dcb;
|
||||
}
|
||||
|
||||
dcb = getlru(d);
|
||||
unlock(&d->lk);
|
||||
if(dcb == nil){
|
||||
fprint(2, "diskcacheread: all blocks in use\n");
|
||||
return nil;
|
||||
}
|
||||
|
||||
b = diskread(d->subdisk, d->blocksize, offset);
|
||||
lock(&d->lk);
|
||||
if(b == nil){
|
||||
putlru(d, dcb);
|
||||
dcb = nil;
|
||||
}else{
|
||||
//fprint(2, "read %llud from disk %p\n", (uvlong)offset, dcb);
|
||||
dcb->subblock = b;
|
||||
dcb->ref++;
|
||||
addtohash(d, dcb, offset);
|
||||
}
|
||||
unlock(&d->lk);
|
||||
return dcb;
|
||||
}
|
||||
|
||||
static void
|
||||
diskcacheblockclose(Block *bb)
|
||||
{
|
||||
DiskCacheBlock *b = bb->priv;
|
||||
|
||||
lock(&b->dc->lk);
|
||||
if(--b->ref == 0)
|
||||
putmru(b->dc, b);
|
||||
unlock(&b->dc->lk);
|
||||
free(bb);
|
||||
}
|
||||
|
||||
static Block*
|
||||
diskcacheread(Disk *dd, u32int len, u64int offset)
|
||||
{
|
||||
int frag, dlen;
|
||||
DiskCache *d = (DiskCache*)dd;
|
||||
DiskCacheBlock *dcb;
|
||||
Block *b;
|
||||
|
||||
if(offset/d->blocksize != (offset+len-1)/d->blocksize){
|
||||
fprint(2, "diskBigRead: request for block crossing big block boundary\n");
|
||||
return nil;
|
||||
}
|
||||
|
||||
b = mallocz(sizeof(Block), 1);
|
||||
if(b == nil)
|
||||
return nil;
|
||||
|
||||
frag = offset%d->blocksize;
|
||||
|
||||
dcb = diskcachereadbig(d, offset-frag);
|
||||
if(dcb == nil){
|
||||
free(b);
|
||||
return nil;
|
||||
}
|
||||
b->priv = dcb;
|
||||
b->_close = diskcacheblockclose;
|
||||
b->data = dcb->subblock->data+frag;
|
||||
|
||||
dlen = dcb->subblock->len;
|
||||
if(frag+len >= dlen){
|
||||
if(frag >= dlen){
|
||||
blockput(b);
|
||||
return nil;
|
||||
}
|
||||
len = dlen-frag;
|
||||
}
|
||||
b->len = len;
|
||||
//fprint(2, "offset %llud at pointer %p %lux\n", (uvlong)offset, b->data, *(ulong*)(b->data+4));
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
* It's okay to remove these from the hash table.
|
||||
* Either the block is in use by someone or it is on
|
||||
* the lru list. If it's in use it will get put on the lru
|
||||
* list once the refs go away.
|
||||
*/
|
||||
static int
|
||||
diskcachesync(Disk *dd)
|
||||
{
|
||||
DiskCache *d = (DiskCache*)dd;
|
||||
DiskCacheBlock *b, *nextb;
|
||||
int i;
|
||||
|
||||
lock(&d->lk);
|
||||
for(i=0; i<d->nhash; i++){
|
||||
for(b=d->h[i]; b; b=nextb){
|
||||
nextb = b->next;
|
||||
b->next = nil;
|
||||
b->offset = ~(u64int)0;
|
||||
}
|
||||
d->h[i] = nil;
|
||||
}
|
||||
unlock(&d->lk);
|
||||
return disksync(d->subdisk);
|
||||
}
|
||||
|
||||
static void
|
||||
diskcacheclose(Disk *dd)
|
||||
{
|
||||
DiskCacheBlock *b;
|
||||
DiskCache *d = (DiskCache*)dd;
|
||||
|
||||
diskclose(d->subdisk);
|
||||
for(b=d->lruhead; b; b=b->lrunext)
|
||||
blockput(b->subblock);
|
||||
free(d);
|
||||
}
|
||||
|
||||
/* needn't be fast */
|
||||
static int
|
||||
isprime(int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=2; i*i<=n; i++)
|
||||
if(n%i == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Disk*
|
||||
diskcache(Disk *subdisk, uint blocksize, uint ncache)
|
||||
{
|
||||
int nhash, i;
|
||||
DiskCache *d;
|
||||
DiskCacheBlock *b;
|
||||
|
||||
nhash = ncache;
|
||||
while(nhash > 1 && !isprime(nhash))
|
||||
nhash--;
|
||||
d = mallocz(sizeof(DiskCache)+ncache*sizeof(DiskCacheBlock)+nhash*sizeof(DiskCacheBlock*), 1);
|
||||
if(d == nil)
|
||||
return nil;
|
||||
|
||||
b = (DiskCacheBlock*)&d[1];
|
||||
d->h = (DiskCacheBlock**)&b[ncache];
|
||||
d->nhash = nhash;
|
||||
d->blocksize = blocksize;
|
||||
d->subdisk = subdisk;
|
||||
d->disk._read = diskcacheread;
|
||||
d->disk._sync = diskcachesync;
|
||||
d->disk._close = diskcacheclose;
|
||||
|
||||
for(i=0; i<ncache; i++){
|
||||
b[i].block._close = diskcacheblockclose;
|
||||
b[i].offset = ~(u64int)0;
|
||||
b[i].dc = d;
|
||||
putlru(d, &b[i]);
|
||||
}
|
||||
|
||||
return &d->disk;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue