mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-27 11:52:03 +00:00
6fc7da3c52
and one per-package memory leak (in writethread).
311 lines
5.4 KiB
C
311 lines
5.4 KiB
C
#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;
|
|
}
|