kernel: new mount cache
this is a new more simple version of the mount cache that does not require dynamic allocations for extends. the Mntcache structure now contains a page bitmap that is used for quick page invalidation. the size of the bitmap is proportional to MAXCACHE. instead of keeping track of cached range in the Extend data structure, we keep all the information in the Page itself. the offset from the page where the cache range starts is in the low PGSHIT bits and the end in the top bits of Page.va. we choose Page.daddr to map 1:1 the Mountcache number and page number (pn) in the Mountcache. to find a page, we first check the bitmap if the page is there and then do a pagelookup() with the daddr key.
This commit is contained in:
parent
bc97fa79b1
commit
8309f15c36
|
@ -8,19 +8,11 @@
|
|||
enum
|
||||
{
|
||||
NHASH = 128,
|
||||
MAXCACHE = 1024*1024,
|
||||
NFILE = 4096,
|
||||
NEXTENT = 200, /* extent allocation size */
|
||||
};
|
||||
NFILE = 4093, /* should be prime */
|
||||
MAXCACHE = 8*1024*1024,
|
||||
|
||||
typedef struct Extent Extent;
|
||||
struct Extent
|
||||
{
|
||||
uintptr bid;
|
||||
ulong start;
|
||||
int len;
|
||||
Page *cache;
|
||||
Extent *next;
|
||||
MAPBITS = 8*sizeof(ulong),
|
||||
NBITMAP = (PGROUND(MAXCACHE)/BY2PG + MAPBITS-1) / MAPBITS,
|
||||
};
|
||||
|
||||
typedef struct Mntcache Mntcache;
|
||||
|
@ -29,79 +21,29 @@ struct Mntcache
|
|||
Qid qid;
|
||||
int dev;
|
||||
int type;
|
||||
|
||||
QLock;
|
||||
Extent *list;
|
||||
Mntcache *hash;
|
||||
Mntcache *prev;
|
||||
Mntcache *next;
|
||||
Mntcache *hash;
|
||||
Mntcache *prev;
|
||||
Mntcache *next;
|
||||
|
||||
/* page bitmap of valid pages */
|
||||
ulong bitmap[NBITMAP];
|
||||
};
|
||||
|
||||
typedef struct Cache Cache;
|
||||
struct Cache
|
||||
{
|
||||
Lock;
|
||||
uintptr pgno;
|
||||
Mntcache *alloc;
|
||||
Mntcache *head;
|
||||
Mntcache *tail;
|
||||
Mntcache *hash[NHASH];
|
||||
};
|
||||
|
||||
typedef struct Ecache Ecache;
|
||||
struct Ecache
|
||||
{
|
||||
Lock;
|
||||
int total;
|
||||
int free;
|
||||
Extent* head;
|
||||
};
|
||||
|
||||
Image fscache;
|
||||
|
||||
static Cache cache;
|
||||
static Ecache ecache;
|
||||
static ulong maxcache = MAXCACHE;
|
||||
|
||||
static void
|
||||
extentfree(Extent* e)
|
||||
{
|
||||
lock(&ecache);
|
||||
e->next = ecache.head;
|
||||
ecache.head = e;
|
||||
ecache.free++;
|
||||
unlock(&ecache);
|
||||
}
|
||||
|
||||
static Extent*
|
||||
extentalloc(void)
|
||||
{
|
||||
Extent *e;
|
||||
int i;
|
||||
|
||||
lock(&ecache);
|
||||
if(ecache.head == nil){
|
||||
e = xalloc(NEXTENT*sizeof(Extent));
|
||||
if(e == nil){
|
||||
unlock(&ecache);
|
||||
return nil;
|
||||
}
|
||||
for(i = 0; i < NEXTENT; i++){
|
||||
e->next = ecache.head;
|
||||
ecache.head = e;
|
||||
e++;
|
||||
}
|
||||
ecache.total += NEXTENT;
|
||||
ecache.free += NEXTENT;
|
||||
}
|
||||
|
||||
e = ecache.head;
|
||||
ecache.head = e->next;
|
||||
ecache.free--;
|
||||
unlock(&ecache);
|
||||
|
||||
memset(e, 0, sizeof(Extent));
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void
|
||||
cinit(void)
|
||||
|
@ -109,14 +51,12 @@ cinit(void)
|
|||
int i;
|
||||
Mntcache *m;
|
||||
|
||||
cache.head = xalloc(sizeof(Mntcache)*NFILE);
|
||||
m = cache.head;
|
||||
cache.alloc = xalloc(sizeof(Mntcache)*NFILE);
|
||||
m = cache.alloc;
|
||||
if (m == nil)
|
||||
panic("cinit: no memory");
|
||||
|
||||
/* a better algorithm would be nice */
|
||||
if(conf.npage*BY2PG > 200*MB)
|
||||
maxcache = 10*MAXCACHE;
|
||||
cache.head = m;
|
||||
|
||||
for(i = 0; i < NFILE-1; i++) {
|
||||
m->next = m+1;
|
||||
|
@ -131,29 +71,17 @@ cinit(void)
|
|||
fscache.notext = 1;
|
||||
}
|
||||
|
||||
static Page*
|
||||
cpage(Extent *e)
|
||||
static uintptr
|
||||
cacheaddr(Mntcache *m, ulong pn)
|
||||
{
|
||||
/* Easy consistency check */
|
||||
if(e->cache->daddr != e->bid)
|
||||
return nil;
|
||||
|
||||
return lookpage(&fscache, e->bid);
|
||||
uintptr da = pn * NFILE + (m - cache.alloc);
|
||||
return (da << PGSHIFT) | (da >> (sizeof(da)*8 - PGSHIFT));
|
||||
}
|
||||
|
||||
static void
|
||||
cnodata(Mntcache *m)
|
||||
{
|
||||
Extent *e;
|
||||
|
||||
/*
|
||||
* Invalidate all extent data
|
||||
* pagereclaim() will waste the pages
|
||||
*/
|
||||
while((e = m->list) != nil){
|
||||
m->list = e->next;
|
||||
extentfree(e);
|
||||
}
|
||||
memset(m->bitmap, 0, sizeof(m->bitmap));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -277,50 +205,62 @@ ccache(Chan *c)
|
|||
return nil;
|
||||
}
|
||||
|
||||
static Page*
|
||||
cpage(Mntcache *m, ulong pn, ulong *po, ulong *pe)
|
||||
{
|
||||
ulong b;
|
||||
Page *p;
|
||||
|
||||
b = 1 << (pn%MAPBITS);
|
||||
if((m->bitmap[pn/MAPBITS] & b) == 0)
|
||||
return nil;
|
||||
p = lookpage(&fscache, cacheaddr(m, pn));
|
||||
if(p == nil){
|
||||
m->bitmap[pn/MAPBITS] &= ~b;
|
||||
return nil;
|
||||
}
|
||||
/* see cachedata() below */
|
||||
*po = (ulong)p->va & (BY2PG-1);
|
||||
*pe = (ulong)p->va >> PGSHIFT;
|
||||
return p;
|
||||
}
|
||||
|
||||
int
|
||||
cread(Chan *c, uchar *buf, int len, vlong off)
|
||||
{
|
||||
KMap *k;
|
||||
Page *p;
|
||||
Mntcache *m;
|
||||
Extent *e, **t;
|
||||
int o, l, total;
|
||||
ulong offset;
|
||||
int l, total;
|
||||
ulong offset, pn, po, pe;
|
||||
|
||||
if(off >= maxcache || len <= 0)
|
||||
if(off >= MAXCACHE || len <= 0)
|
||||
return 0;
|
||||
|
||||
m = ccache(c);
|
||||
if(m == nil)
|
||||
return 0;
|
||||
|
||||
offset = off;
|
||||
t = &m->list;
|
||||
for(e = *t; e != nil; e = e->next) {
|
||||
if(offset >= e->start && offset < e->start+e->len)
|
||||
break;
|
||||
t = &e->next;
|
||||
}
|
||||
|
||||
if(e == nil) {
|
||||
qunlock(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
total = 0;
|
||||
while(len > 0) {
|
||||
p = cpage(e);
|
||||
if(p == nil) {
|
||||
*t = e->next;
|
||||
extentfree(e);
|
||||
|
||||
offset = off;
|
||||
if(offset+len > MAXCACHE)
|
||||
len = MAXCACHE - offset;
|
||||
pn = offset / BY2PG;
|
||||
offset &= (BY2PG-1);
|
||||
|
||||
while(len > 0){
|
||||
p = cpage(m, pn, &po, &pe);
|
||||
if(p == nil)
|
||||
break;
|
||||
if(po >= pe || offset < po || offset >= pe){
|
||||
putpage(p);
|
||||
break;
|
||||
}
|
||||
|
||||
o = offset - e->start;
|
||||
l = len;
|
||||
if(l > e->len-o)
|
||||
l = e->len-o;
|
||||
|
||||
l = pe - offset;
|
||||
if(l > len)
|
||||
l = len;
|
||||
|
||||
k = kmap(p);
|
||||
if(waserror()) {
|
||||
kunmap(k);
|
||||
|
@ -328,265 +268,136 @@ cread(Chan *c, uchar *buf, int len, vlong off)
|
|||
qunlock(m);
|
||||
nexterror();
|
||||
}
|
||||
|
||||
memmove(buf, (uchar*)VA(k) + o, l);
|
||||
|
||||
memmove(buf, (uchar*)VA(k) + offset, l);
|
||||
poperror();
|
||||
kunmap(k);
|
||||
|
||||
putpage(p);
|
||||
|
||||
total += l;
|
||||
|
||||
offset += l;
|
||||
offset &= (BY2PG-1);
|
||||
if(offset != 0)
|
||||
break;
|
||||
|
||||
pn++;
|
||||
buf += l;
|
||||
len -= l;
|
||||
offset += l;
|
||||
total += l;
|
||||
t = &e->next;
|
||||
e = e->next;
|
||||
if(e == nil || e->start != offset)
|
||||
break;
|
||||
}
|
||||
|
||||
qunlock(m);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static Extent*
|
||||
cchain(uchar *buf, ulong offset, int len, Extent **tail)
|
||||
/* invalidate pages in page bitmap */
|
||||
static void
|
||||
invalidate(Mntcache *m, ulong offset, int len)
|
||||
{
|
||||
ulong pn;
|
||||
|
||||
for(pn = offset/BY2PG; len > 0; pn++, len -= BY2PG)
|
||||
m->bitmap[pn/MAPBITS] &= ~(1 << (pn%MAPBITS));
|
||||
}
|
||||
|
||||
/* replace buf data from [off, off+len) in the cache or invalidate */
|
||||
static void
|
||||
cachedata(Mntcache *m, uchar *buf, int len, vlong off)
|
||||
{
|
||||
int l;
|
||||
Page *p;
|
||||
KMap *k;
|
||||
Extent *e, *start, **t;
|
||||
ulong offset, pn, po, pe;
|
||||
|
||||
start = nil;
|
||||
*tail = nil;
|
||||
t = &start;
|
||||
while(len > 0) {
|
||||
e = extentalloc();
|
||||
if(e == nil)
|
||||
break;
|
||||
if(off >= MAXCACHE || len <= 0){
|
||||
qunlock(m);
|
||||
return;
|
||||
}
|
||||
|
||||
p = auxpage();
|
||||
if(p == nil) {
|
||||
extentfree(e);
|
||||
break;
|
||||
offset = off;
|
||||
if(offset+len > MAXCACHE)
|
||||
len = MAXCACHE - offset;
|
||||
pn = offset / BY2PG;
|
||||
offset &= (BY2PG-1);
|
||||
|
||||
while(len > 0){
|
||||
l = BY2PG - offset;
|
||||
if(l > len)
|
||||
l = len;
|
||||
p = cpage(m, pn, &po, &pe);
|
||||
if(p != nil){
|
||||
if(po >= pe || offset > pe || (offset+l) < po){
|
||||
/* cached range empty or not extendable, set new cached range */
|
||||
po = offset;
|
||||
pe = offset+l;
|
||||
} else {
|
||||
/* extend cached range */
|
||||
if(offset < po)
|
||||
po = offset;
|
||||
if((offset+l) > pe)
|
||||
pe = offset+l;
|
||||
}
|
||||
} else {
|
||||
p = auxpage();
|
||||
if(p == nil){
|
||||
invalidate(m, offset + pn*BY2PG, len);
|
||||
break;
|
||||
}
|
||||
|
||||
p->va = 0;
|
||||
p->daddr = cacheaddr(m, pn);
|
||||
cachedel(&fscache, p->daddr);
|
||||
cachepage(p, &fscache);
|
||||
m->bitmap[pn/MAPBITS] |= 1 << (pn%MAPBITS);
|
||||
|
||||
po = offset;
|
||||
pe = offset+l;
|
||||
}
|
||||
l = len;
|
||||
if(l > BY2PG)
|
||||
l = BY2PG;
|
||||
|
||||
e->cache = p;
|
||||
e->start = offset;
|
||||
e->len = l;
|
||||
|
||||
lock(&cache);
|
||||
e->bid = cache.pgno;
|
||||
cache.pgno += BY2PG;
|
||||
/* wrap the counter; low bits are unused by pghash but checked by lookpage */
|
||||
if((cache.pgno & ~(BY2PG-1)) == 0){
|
||||
if(cache.pgno == BY2PG-1){
|
||||
print("cache wrapped\n");
|
||||
cache.pgno = 0;
|
||||
}else
|
||||
cache.pgno++;
|
||||
}
|
||||
unlock(&cache);
|
||||
|
||||
p->daddr = e->bid;
|
||||
k = kmap(p);
|
||||
if(waserror()) { /* buf may be virtual */
|
||||
if(waserror()) {
|
||||
kunmap(k);
|
||||
putpage(p);
|
||||
invalidate(m, offset + pn*BY2PG, len);
|
||||
qunlock(m);
|
||||
nexterror();
|
||||
}
|
||||
memmove((void*)VA(k), buf, l);
|
||||
memmove((uchar*)VA(k) + offset, buf, l);
|
||||
poperror();
|
||||
kunmap(k);
|
||||
|
||||
cachepage(p, &fscache);
|
||||
/* update cached range */
|
||||
p->va = po | (pe << PGSHIFT);
|
||||
putpage(p);
|
||||
|
||||
offset = 0;
|
||||
pn++;
|
||||
buf += l;
|
||||
offset += l;
|
||||
len -= l;
|
||||
|
||||
*t = e;
|
||||
*tail = e;
|
||||
t = &e->next;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static int
|
||||
cpgmove(Extent *e, uchar *buf, int boff, int len)
|
||||
{
|
||||
Page *p;
|
||||
KMap *k;
|
||||
|
||||
p = cpage(e);
|
||||
if(p == nil)
|
||||
return 0;
|
||||
|
||||
k = kmap(p);
|
||||
if(waserror()) { /* Since buf may be virtual */
|
||||
kunmap(k);
|
||||
nexterror();
|
||||
}
|
||||
|
||||
memmove((uchar*)VA(k)+boff, buf, len);
|
||||
|
||||
poperror();
|
||||
kunmap(k);
|
||||
putpage(p);
|
||||
|
||||
return 1;
|
||||
qunlock(m);
|
||||
}
|
||||
|
||||
void
|
||||
cupdate(Chan *c, uchar *buf, int len, vlong off)
|
||||
{
|
||||
int o;
|
||||
Mntcache *m;
|
||||
Extent *tail;
|
||||
Extent *e, *f, *p;
|
||||
ulong offset, eblock, ee;
|
||||
|
||||
if(off >= maxcache || len <= 0)
|
||||
return;
|
||||
|
||||
m = ccache(c);
|
||||
if(m == nil)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Find the insertion point
|
||||
*/
|
||||
offset = off;
|
||||
p = nil;
|
||||
for(f = m->list; f != nil; f = f->next) {
|
||||
if(f->start > offset)
|
||||
break;
|
||||
p = f;
|
||||
}
|
||||
|
||||
/* trim if there is a successor */
|
||||
eblock = offset+len;
|
||||
if(f != nil && eblock > f->start) {
|
||||
len -= (eblock - f->start);
|
||||
if(len <= 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(p == nil) { /* at the head */
|
||||
e = cchain(buf, offset, len, &tail);
|
||||
if(e != nil) {
|
||||
m->list = e;
|
||||
tail->next = f;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* trim to the predecessor */
|
||||
ee = p->start+p->len;
|
||||
if(offset < ee) {
|
||||
o = ee - offset;
|
||||
len -= o;
|
||||
if(len <= 0)
|
||||
goto out;
|
||||
buf += o;
|
||||
offset += o;
|
||||
}
|
||||
|
||||
/* try and pack data into the predecessor */
|
||||
if(offset == ee && p->len < BY2PG) {
|
||||
o = len;
|
||||
if(o > BY2PG - p->len)
|
||||
o = BY2PG - p->len;
|
||||
if(cpgmove(p, buf, p->len, o)) {
|
||||
p->len += o;
|
||||
buf += o;
|
||||
len -= o;
|
||||
offset += o;
|
||||
if(len <= 0) {
|
||||
if(f != nil && p->start + p->len > f->start)
|
||||
print("CACHE: p->start=%uld p->len=%d f->start=%uld\n",
|
||||
p->start, p->len, f->start);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e = cchain(buf, offset, len, &tail);
|
||||
if(e != nil) {
|
||||
p->next = e;
|
||||
tail->next = f;
|
||||
}
|
||||
out:
|
||||
qunlock(m);
|
||||
cachedata(m, buf, len, off);
|
||||
}
|
||||
|
||||
void
|
||||
cwrite(Chan* c, uchar *buf, int len, vlong off)
|
||||
{
|
||||
int o, eo;
|
||||
Mntcache *m;
|
||||
Extent *p, *f, *e, *tail;
|
||||
ulong offset, eblock, ee;
|
||||
|
||||
if(off >= maxcache || len <= 0)
|
||||
return;
|
||||
|
||||
m = ccache(c);
|
||||
if(m == nil)
|
||||
return;
|
||||
|
||||
offset = off;
|
||||
m->qid.vers++;
|
||||
c->qid.vers++;
|
||||
|
||||
p = nil;
|
||||
for(f = m->list; f != nil; f = f->next) {
|
||||
if(f->start >= offset)
|
||||
break;
|
||||
p = f;
|
||||
}
|
||||
|
||||
if(p != nil) {
|
||||
ee = p->start+p->len;
|
||||
eo = offset - p->start;
|
||||
/* pack in predecessor if there is space */
|
||||
if(offset <= ee && eo < BY2PG) {
|
||||
o = len;
|
||||
if(o > BY2PG - eo)
|
||||
o = BY2PG - eo;
|
||||
if(cpgmove(p, buf, eo, o)) {
|
||||
if(eo+o > p->len)
|
||||
p->len = eo+o;
|
||||
buf += o;
|
||||
len -= o;
|
||||
offset += o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* free the overlap -- it's a rare case */
|
||||
eblock = offset+len;
|
||||
while(f != nil && f->start < eblock) {
|
||||
e = f->next;
|
||||
extentfree(f);
|
||||
f = e;
|
||||
}
|
||||
|
||||
/* link the block (if any) into the middle */
|
||||
e = cchain(buf, offset, len, &tail);
|
||||
if(e != nil) {
|
||||
tail->next = f;
|
||||
f = e;
|
||||
}
|
||||
|
||||
if(p == nil)
|
||||
m->list = f;
|
||||
else
|
||||
p->next = f;
|
||||
qunlock(m);
|
||||
cachedata(m, buf, len, off);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue