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:
cinap_lenrek 2014-12-16 05:41:20 +01:00
parent bc97fa79b1
commit 8309f15c36

View file

@ -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);
}