plan9fox/sys/src/9/port/cache.c

522 lines
8 KiB
C
Raw Normal View History

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
enum
{
NHASH = 128,
NFILE = 4093, /* should be prime */
MAXCACHE = 8*1024*1024,
MAPBITS = 8*sizeof(ulong),
NBITMAP = (PGROUND(MAXCACHE)/BY2PG + MAPBITS-1) / MAPBITS,
};
/* devmnt.c: parallel read ahread implementation */
extern void mntrahinit(Mntrah *rah);
extern long mntrahread(Mntrah *rah, Chan *c, uchar *buf, long len, vlong off);
typedef struct Mntcache Mntcache;
struct Mntcache
{
Qid qid;
int dev;
int type;
QLock;
Proc *locked;
ulong nlocked;
Mntcache *hash;
Mntcache *prev;
Mntcache *next;
/* page bitmap of valid pages */
ulong bitmap[NBITMAP];
/* read ahead state */
Mntrah rah;
};
typedef struct Cache Cache;
struct Cache
{
Lock;
Mntcache *alloc;
Mntcache *head;
Mntcache *tail;
Mntcache *hash[NHASH];
};
Image fscache;
static Cache cache;
void
cinit(void)
{
int i;
Mntcache *m;
m = xalloc(sizeof(Mntcache)*NFILE);
if (m == nil)
panic("cinit: no memory");
cache.alloc = m;
cache.head = m;
for(i = 0; i < NFILE-1; i++) {
m->next = m+1;
m->prev = m-1;
m++;
}
cache.tail = m;
cache.tail->next = nil;
cache.head->prev = nil;
fscache.notext = 1;
}
static uintptr
cacheaddr(Mntcache *m, ulong pn)
{
uintptr da = pn * NFILE + (m - cache.alloc);
return (da << PGSHIFT) | (da >> (sizeof(da)*8 - PGSHIFT));
}
static void
cnodata(Mntcache *m)
{
memset(m->bitmap, 0, sizeof(m->bitmap));
}
static void
ctail(Mntcache *m)
{
/* Unlink and send to the tail */
if(m->prev != nil)
m->prev->next = m->next;
else
cache.head = m->next;
if(m->next != nil)
m->next->prev = m->prev;
else
cache.tail = m->prev;
if(cache.tail != nil) {
m->prev = cache.tail;
cache.tail->next = m;
m->next = nil;
cache.tail = m;
}
else {
cache.head = m;
cache.tail = m;
m->prev = nil;
m->next = nil;
}
}
/* called with cache locked */
static Mntcache*
clookup(Chan *c, int skipvers)
{
Mntcache *m;
for(m = cache.hash[c->qid.path%NHASH]; m != nil; m = m->hash)
if(eqchantdqid(c, m->type, m->dev, m->qid, skipvers) && c->qid.type == m->qid.type)
return m;
return nil;
}
/*
* resursive Mntcache locking. Mntcache.rah is protected by the
* same lock and we want to call cupdate() from mntrahread()
* while holding the lock.
*/
static int
cancachelock(Mntcache *m)
{
if(m->locked == up || canqlock(m)){
m->locked = up;
m->nlocked++;
return 1;
}
return 0;
}
static void
cachelock(Mntcache *m)
{
if(m->locked != up){
qlock(m);
assert(m->nlocked == 0);
m->locked = up;
}
m->nlocked++;
}
static void
cacheunlock(Mntcache *m)
{
assert(m->locked == up);
if(--m->nlocked == 0){
m->locked = nil;
qunlock(m);
}
}
/* return locked Mntcache if still valid else reset mcp */
static Mntcache*
ccache(Chan *c)
{
Mntcache *m;
m = c->mcp;
if(m != nil) {
cachelock(m);
if(eqchantdqid(c, m->type, m->dev, m->qid, 0) && c->qid.type == m->qid.type)
return m;
c->mcp = nil;
cacheunlock(m);
}
return nil;
}
int
copen(Chan *c)
{
Mntcache *m, *f, **l;
/* directories aren't cacheable */
if(c->qid.type&QTDIR){
c->mcp = nil;
return 0;
}
lock(&cache);
m = clookup(c, 0);
if(m != nil){
ctail(m);
unlock(&cache);
c->mcp = m;
return 1;
}
m = clookup(c, 1);
if(m == nil)
m = cache.head;
ctail(m);
l = &cache.hash[m->qid.path%NHASH];
for(f = *l; f != nil; f = f->hash) {
if(f == m) {
*l = m->hash;
break;
}
l = &f->hash;
}
if(!cancachelock(m)){
unlock(&cache);
cachelock(m);
lock(&cache);
f = clookup(c, 0);
if(f != nil) {
/*
* someone got there first while cache lock
* was released and added a updated Mntcache
* for us. update LRU and use it.
*/
ctail(f);
unlock(&cache);
cacheunlock(m);
c->mcp = f;
return 1;
}
}
m->qid = c->qid;
m->dev = c->dev;
m->type = c->type;
l = &cache.hash[c->qid.path%NHASH];
m->hash = *l;
*l = m;
unlock(&cache);
m->rah.vers = m->qid.vers;
mntrahinit(&m->rah);
cnodata(m);
cacheunlock(m);
c->mcp = m;
return 0;
}
enum {
VABITS = 8*sizeof(uintptr) - 2*PGSHIFT,
VAMASK = (((uintptr)1 << VABITS)-1) << PGSHIFT,
};
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;
}
*po = p->va & (BY2PG-1);
*pe = 1 + (p->va >> (PGSHIFT+VABITS));
assert(*po < *pe);
return p;
}
static void
cpageset(Page *p, ulong po, ulong pe)
{
assert(po < pe);
p->va = po | (p->va & VAMASK) | ((uintptr)pe - 1) << (PGSHIFT+VABITS);
}
int
cread(Chan *c, uchar *buf, int len, vlong off)
{
KMap *k;
Page *p;
Mntcache *m;
int l, tot;
ulong offset, pn, po, pe;
if(len <= 0)
return 0;
m = ccache(c);
if(m == nil)
return 0;
if(waserror()){
cacheunlock(m);
nexterror();
}
tot = 0;
if(off >= MAXCACHE)
goto Prefetch;
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(offset < po || offset >= pe){
putpage(p);
break;
}
l = pe - offset;
if(l > len)
l = len;
k = kmap(p);
if(waserror()) {
kunmap(k);
putpage(p);
nexterror();
}
memmove(buf, (uchar*)VA(k) + offset, l);
kunmap(k);
putpage(p);
poperror();
tot += l;
buf += l;
len -= l;
offset += l;
offset &= (BY2PG-1);
if(offset != 0)
break;
pn++;
}
Prefetch:
if(len > 0){
if(m->rah.vers != m->qid.vers){
mntrahinit(&m->rah);
m->rah.vers = m->qid.vers;
}
off += tot;
tot += mntrahread(&m->rah, c, buf, len, off);
}
cacheunlock(m);
poperror();
return tot;
}
/* 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;
ulong offset, pn, po, pe;
if(off >= MAXCACHE || len <= 0){
cacheunlock(m);
return;
}
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(offset > pe || (offset+l) < po){
/* cached range 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 {
if(needpages(nil)){
invalidate(m, offset + pn*BY2PG, len);
break;
}
p = newpage(0, nil, pn*BY2PG);
p->daddr = cacheaddr(m, pn);
cachedel(&fscache, p->daddr);
cachepage(p, &fscache);
m->bitmap[pn/MAPBITS] |= 1 << (pn%MAPBITS);
po = offset;
pe = offset+l;
}
cpageset(p, po, pe);
k = kmap(p);
if(waserror()) {
kunmap(k);
putpage(p);
invalidate(m, offset + pn*BY2PG, len);
cacheunlock(m);
nexterror();
}
memmove((uchar*)VA(k) + offset, buf, l);
poperror();
kunmap(k);
putpage(p);
offset = 0;
pn++;
buf += l;
len -= l;
}
cacheunlock(m);
}
void
cupdate(Chan *c, uchar *buf, int len, vlong off)
{
Mntcache *m;
m = ccache(c);
if(m == nil)
return;
cachedata(m, buf, len, off);
}
void
cwrite(Chan* c, uchar *buf, int len, vlong off)
{
Mntcache *m;
m = ccache(c);
if(m == nil)
return;
m->qid.vers++;
c->qid.vers++;
if(c->qid.type&QTAPPEND){
cacheunlock(m);
return;
}
cachedata(m, buf, len, off);
}
void
ctrunc(Chan *c)
{
Mntcache *m;
if(c->qid.type&QTDIR)
return;
if((c->flag&COPEN) == 0){
lock(&cache);
c->mcp = clookup(c, 0);
unlock(&cache);
}
m = ccache(c);
if(m == nil)
return;
mntrahinit(&m->rah);
cnodata(m);
cacheunlock(m);
if((c->flag&COPEN) == 0)
c->mcp = nil;
}
void
cclunk(Chan *c)
{
Mntcache *m;
m = ccache(c);
if(m == nil)
return;
mntrahinit(&m->rah);
cacheunlock(m);
c->mcp = nil;
}