jehanne/sys/src/kern/port/cache.c

544 lines
7.9 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
enum
{
NHASH = 128,
MAXCACHE = 1024*1024,
NFILE = 4096,
NEXTENT = 200, /* extent allocation size */
};
typedef struct Extent Extent;
struct Extent
{
uint32_t start;
int len;
char *cache;
Extent *next;
};
typedef struct Mntcache Mntcache;
struct Mntcache
{
Qid qid;
uint32_t devno;
Dev* dev;
QLock;
Extent *list;
Mntcache *hash;
Mntcache *prev;
Mntcache *next;
};
typedef struct Cache Cache;
struct Cache
{
Lock l;
int pgno;
Mntcache *head;
Mntcache *tail;
Mntcache *hash[NHASH];
};
typedef struct Ecache Ecache;
struct Ecache
{
Lock l;
int total;
int free;
Extent* head;
};
//static Image fscache; // not used
static Cache cache;
static Ecache ecache;
static int maxcache = MAXCACHE;
static void
extentfree(Extent* e)
{
jehanne_free(e->cache);
e->cache = nil;
lock(&ecache.l);
e->next = ecache.head;
ecache.head = e;
ecache.free++;
unlock(&ecache.l);
}
static Extent*
extentalloc(void)
{
Extent *e;
int i;
lock(&ecache.l);
if(ecache.head == nil){
e = jehanne_malloc(NEXTENT*sizeof(Extent));
if(e == nil){
unlock(&ecache.l);
return nil;
}
for(i = 0; i < NEXTENT; i++){
e->next = ecache.head;
ecache.head = e;
e++;
}
ecache.free += NEXTENT;
ecache.total += NEXTENT;
}
e = ecache.head;
ecache.head = e->next;
jehanne_memset(e, 0, sizeof(Extent));
ecache.free--;
unlock(&ecache.l);
return e;
}
void
cinit(void)
{
int i;
Mntcache *mc;
if((cache.head = jehanne_malloc(sizeof(Mntcache)*NFILE)) == nil)
panic("cinit: no memory");
mc = cache.head;
/* a good algorithm to set maxcache would be nice */
for(i = 0; i < NFILE-1; i++) {
mc->next = mc+1;
mc->prev = mc-1;
mc++;
}
cache.tail = mc;
cache.tail->next = 0;
cache.head->prev = 0;
}
void
cnodata(Mntcache *mc)
{
Extent *e, *n;
/*
* Invalidate all extent data
* Image lru will waste the pages
*/
for(e = mc->list; e; e = n) {
n = e->next;
extentfree(e);
}
mc->list = 0;
}
void
ctail(Mntcache *mc)
{
/* Unlink and send to the tail */
if(mc->prev)
mc->prev->next = mc->next;
else
cache.head = mc->next;
if(mc->next)
mc->next->prev = mc->prev;
else
cache.tail = mc->prev;
if(cache.tail) {
mc->prev = cache.tail;
cache.tail->next = mc;
mc->next = 0;
cache.tail = mc;
}
else {
cache.head = mc;
cache.tail = mc;
mc->prev = 0;
mc->next = 0;
}
}
void
copen(Chan *c)
{
int h;
Extent *e, *next;
Mntcache *mc, *f, **l;
/* directories aren't cacheable and append-only files confuse us */
if(c->qid.type&(QTDIR|QTAPPEND))
return;
h = c->qid.path%NHASH;
lock(&cache.l);
for(mc = cache.hash[h]; mc != nil; mc = mc->hash) {
if(mc->qid.path == c->qid.path)
if(mc->qid.type == c->qid.type)
if(mc->devno == c->devno && mc->dev == c->dev) {
c->mc = mc;
ctail(mc);
unlock(&cache.l);
/* File was updated, invalidate cache */
if(mc->qid.vers != c->qid.vers) {
mc->qid.vers = c->qid.vers;
qlock(mc);
cnodata(mc);
qunlock(mc);
}
return;
}
}
/* LRU the cache headers */
mc = cache.head;
l = &cache.hash[mc->qid.path%NHASH];
for(f = *l; f; f = f->hash) {
if(f == mc) {
*l = mc->hash;
break;
}
l = &f->hash;
}
mc->qid = c->qid;
mc->devno = c->devno;
mc->dev = c->dev;
l = &cache.hash[h];
mc->hash = *l;
*l = mc;
ctail(mc);
qlock(mc);
c->mc = mc;
e = mc->list;
mc->list = 0;
unlock(&cache.l);
while(e) {
next = e->next;
extentfree(e);
e = next;
}
qunlock(mc);
}
static int
cdev(Mntcache *mc, Chan *c)
{
if(mc->qid.path != c->qid.path)
return 0;
if(mc->qid.type != c->qid.type)
return 0;
if(mc->devno != c->devno)
return 0;
if(mc->dev != c->dev)
return 0;
if(mc->qid.vers != c->qid.vers)
return 0;
return 1;
}
int
cread(Chan *c, uint8_t *buf, int len, int64_t off)
{
char *p;
Mntcache *mc;
Extent *e, **t;
int o, l, total;
uint32_t offset;
if(off+len > maxcache)
return 0;
mc = c->mc;
if(mc == nil)
return 0;
qlock(mc);
if(cdev(mc, c) == 0) {
qunlock(mc);
return 0;
}
offset = off;
t = &mc->list;
for(e = *t; e; e = e->next) {
if(offset >= e->start && offset < e->start+e->len)
break;
t = &e->next;
}
if(e == 0) {
qunlock(mc);
return 0;
}
total = 0;
while(len) {
p = e->cache;
if(p == nil){
*t = e->next;
extentfree(e);
qunlock(mc);
return total;
}
o = offset - e->start;
l = len;
if(l > e->len-o)
l = e->len-o;
if(waserror()) {
qunlock(mc);
nexterror();
}
jehanne_memmove(buf, p+o, l);
poperror();
buf += l;
len -= l;
offset += l;
total += l;
t = &e->next;
e = e->next;
if(e == 0 || e->start != offset)
break;
}
qunlock(mc);
return total;
}
Extent*
cchain(uint8_t *buf, uint32_t offset, int len, Extent **tail)
{
int l;
char *p;
Extent *e, *start, **t;
start = 0;
*tail = 0;
t = &start;
while(len) {
e = extentalloc();
if(e == 0)
break;
p = jehanne_mallocz(PGSZ, 0);
if(p == nil){
extentfree(e);
break;
}
l = len;
if(l > PGSZ)
l = PGSZ;
e->cache = p;
e->start = offset;
e->len = l;
jehanne_memmove(p, buf, l);
buf += l;
offset += l;
len -= l;
*t = e;
*tail = e;
t = &e->next;
}
return start;
}
int
cpgmove(Extent *e, uint8_t *buf, int boff, int len)
{
if(e->cache == nil){
/* shouldn't happen */
jehanne_print("CACHE: cpgmove %#p %d %d nil\n", e, boff, len);
return 0;
}
jehanne_memmove(e->cache+boff, buf, len);
return 1;
}
void
cupdate(Chan *c, uint8_t *buf, int len, int64_t off)
{
Mntcache *mc;
Extent *tail;
Extent *e, *f, *p;
int o, ee, eblock;
uint32_t offset;
if(off > maxcache || len == 0)
return;
mc = c->mc;
if(mc == nil)
return;
qlock(mc);
if(cdev(mc, c) == 0) {
qunlock(mc);
return;
}
/*
* Find the insertion point
*/
offset = off;
p = 0;
for(f = mc->list; f; f = f->next) {
if(f->start > offset)
break;
p = f;
}
/* trim if there is a successor */
eblock = offset+len;
if(f != 0 && eblock > f->start) {
len -= (eblock - f->start);
if(len <= 0) {
qunlock(mc);
return;
}
}
if(p == 0) { /* at the head */
e = cchain(buf, offset, len, &tail);
if(e != 0) {
mc->list = e;
tail->next = f;
}
qunlock(mc);
return;
}
/* trim to the predecessor */
ee = p->start+p->len;
if(offset < ee) {
o = ee - offset;
len -= o;
if(len <= 0) {
qunlock(mc);
return;
}
buf += o;
offset += o;
}
/* try and pack data into the predecessor */
if(offset == ee && p->len < PGSZ) {
o = len;
if(o > PGSZ - p->len)
o = PGSZ - p->len;
if(cpgmove(p, buf, p->len, o)) {
p->len += o;
buf += o;
len -= o;
offset += o;
if(len <= 0) {
if(f && p->start + p->len > f->start) jehanne_print("CACHE: p->start=%uld p->len=%d f->start=%uld\n", p->start, p->len, f->start);
qunlock(mc);
return;
}
}
}
e = cchain(buf, offset, len, &tail);
if(e != 0) {
p->next = e;
tail->next = f;
}
qunlock(mc);
}
void
cwrite(Chan* c, uint8_t *buf, int len, int64_t off)
{
int o, eo;
Mntcache *mc;
uint32_t eblock, ee;
Extent *p, *f, *e, *tail;
uint32_t offset;
if(off > maxcache || len == 0)
return;
mc = c->mc;
if(mc == nil)
return;
qlock(mc);
if(cdev(mc, c) == 0) {
qunlock(mc);
return;
}
offset = off;
mc->qid.vers++;
c->qid.vers++;
p = 0;
for(f = mc->list; f; f = f->next) {
if(f->start >= offset)
break;
p = f;
}
if(p != 0) {
ee = p->start+p->len;
eo = offset - p->start;
/* pack in predecessor if there is space */
if(offset <= ee && eo < PGSZ) {
o = len;
if(o > PGSZ - eo)
o = PGSZ - 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 && 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 != 0) {
tail->next = f;
f = e;
}
if(p == 0)
mc->list = f;
else
p->next = f;
qunlock(mc);
}