2011-03-30 12:46:40 +00:00
|
|
|
#include "u.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "../port/error.h"
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
NHASH = 128,
|
|
|
|
MAXCACHE = 1024*1024,
|
|
|
|
NFILE = 4096,
|
|
|
|
NEXTENT = 200, /* extent allocation size */
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Extent Extent;
|
|
|
|
struct Extent
|
|
|
|
{
|
|
|
|
int bid;
|
|
|
|
ulong start;
|
|
|
|
int len;
|
|
|
|
Page *cache;
|
|
|
|
Extent *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Mntcache Mntcache;
|
|
|
|
struct Mntcache
|
|
|
|
{
|
|
|
|
Qid qid;
|
|
|
|
int dev;
|
|
|
|
int type;
|
|
|
|
QLock;
|
|
|
|
Extent *list;
|
|
|
|
Mntcache *hash;
|
|
|
|
Mntcache *prev;
|
|
|
|
Mntcache *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Cache Cache;
|
|
|
|
struct Cache
|
|
|
|
{
|
|
|
|
Lock;
|
|
|
|
int pgno;
|
|
|
|
Mntcache *head;
|
|
|
|
Mntcache *tail;
|
|
|
|
Mntcache *hash[NHASH];
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Ecache Ecache;
|
|
|
|
struct Ecache
|
|
|
|
{
|
|
|
|
Lock;
|
|
|
|
int total;
|
|
|
|
int free;
|
|
|
|
Extent* head;
|
|
|
|
};
|
|
|
|
|
|
|
|
static Image fscache;
|
|
|
|
static Cache cache;
|
|
|
|
static Ecache ecache;
|
|
|
|
static int 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.free += NEXTENT;
|
|
|
|
ecache.total += NEXTENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
e = ecache.head;
|
|
|
|
ecache.head = e->next;
|
|
|
|
memset(e, 0, sizeof(Extent));
|
|
|
|
ecache.free--;
|
|
|
|
unlock(&ecache);
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cinit(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Mntcache *m;
|
|
|
|
|
|
|
|
cache.head = xalloc(sizeof(Mntcache)*NFILE);
|
|
|
|
m = cache.head;
|
|
|
|
if (m == nil)
|
|
|
|
panic("cinit: no memory");
|
|
|
|
|
|
|
|
/* a better algorithm would be nice */
|
2011-12-22 01:17:29 +00:00
|
|
|
if(conf.npage*BY2PG > 200*MB)
|
|
|
|
maxcache = 10*MAXCACHE;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
for(i = 0; i < NFILE-1; i++) {
|
|
|
|
m->next = m+1;
|
|
|
|
m->prev = m-1;
|
|
|
|
m++;
|
|
|
|
}
|
|
|
|
|
|
|
|
cache.tail = m;
|
|
|
|
cache.tail->next = 0;
|
|
|
|
cache.head->prev = 0;
|
|
|
|
|
|
|
|
fscache.notext = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Page*
|
|
|
|
cpage(Extent *e)
|
|
|
|
{
|
|
|
|
/* Easy consistency check */
|
|
|
|
if(e->cache->daddr != e->bid)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return lookpage(&fscache, e->bid);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cnodata(Mntcache *m)
|
|
|
|
{
|
2011-12-22 01:17:29 +00:00
|
|
|
Extent *e;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Invalidate all extent data
|
|
|
|
* Image lru will waste the pages
|
|
|
|
*/
|
2011-12-22 01:17:29 +00:00
|
|
|
while(e = m->list){
|
|
|
|
m->list = e->next;
|
2011-03-30 12:46:40 +00:00
|
|
|
extentfree(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ctail(Mntcache *m)
|
|
|
|
{
|
|
|
|
/* Unlink and send to the tail */
|
|
|
|
if(m->prev)
|
|
|
|
m->prev->next = m->next;
|
|
|
|
else
|
|
|
|
cache.head = m->next;
|
|
|
|
if(m->next)
|
|
|
|
m->next->prev = m->prev;
|
|
|
|
else
|
|
|
|
cache.tail = m->prev;
|
|
|
|
|
|
|
|
if(cache.tail) {
|
|
|
|
m->prev = cache.tail;
|
|
|
|
cache.tail->next = m;
|
|
|
|
m->next = 0;
|
|
|
|
cache.tail = m;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
cache.head = m;
|
|
|
|
cache.tail = m;
|
|
|
|
m->prev = 0;
|
|
|
|
m->next = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-17 13:48:30 +00:00
|
|
|
/* called with cache locked */
|
|
|
|
static Mntcache*
|
|
|
|
clookup(Chan *c, int skipvers)
|
|
|
|
{
|
|
|
|
Mntcache *m;
|
|
|
|
|
|
|
|
for(m = cache.hash[c->qid.path%NHASH]; m; m = m->hash)
|
|
|
|
if(eqchantdqid(c, m->type, m->dev, m->qid, skipvers) && c->qid.type == m->qid.type)
|
|
|
|
return m;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
void
|
|
|
|
copen(Chan *c)
|
|
|
|
{
|
|
|
|
Mntcache *m, *f, **l;
|
|
|
|
|
|
|
|
/* directories aren't cacheable and append-only files confuse us */
|
2012-10-17 13:48:30 +00:00
|
|
|
if(c->qid.type&(QTDIR|QTAPPEND)){
|
|
|
|
c->mcp = 0;
|
2011-03-30 12:46:40 +00:00
|
|
|
return;
|
2012-10-17 13:48:30 +00:00
|
|
|
}
|
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
lock(&cache);
|
2012-10-17 13:48:30 +00:00
|
|
|
m = clookup(c, 1);
|
|
|
|
if(m == 0)
|
|
|
|
m = cache.head;
|
|
|
|
else if(m->qid.vers == c->qid.vers) {
|
|
|
|
ctail(m);
|
|
|
|
unlock(&cache);
|
|
|
|
c->mcp = m;
|
|
|
|
return;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-10-17 13:48:30 +00:00
|
|
|
ctail(m);
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
l = &cache.hash[m->qid.path%NHASH];
|
|
|
|
for(f = *l; f; f = f->hash) {
|
|
|
|
if(f == m) {
|
|
|
|
*l = m->hash;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
l = &f->hash;
|
|
|
|
}
|
|
|
|
|
2012-10-17 13:48:30 +00:00
|
|
|
if(!canqlock(m)){
|
|
|
|
unlock(&cache);
|
|
|
|
qlock(m);
|
|
|
|
lock(&cache);
|
|
|
|
f = clookup(c, 0);
|
|
|
|
if(f != 0) {
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
qunlock(m);
|
|
|
|
c->mcp = f;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
m->qid = c->qid;
|
|
|
|
m->dev = c->dev;
|
|
|
|
m->type = c->type;
|
|
|
|
|
2012-10-17 13:48:30 +00:00
|
|
|
l = &cache.hash[c->qid.path%NHASH];
|
2011-03-30 12:46:40 +00:00
|
|
|
m->hash = *l;
|
|
|
|
*l = m;
|
|
|
|
unlock(&cache);
|
2011-12-22 01:17:29 +00:00
|
|
|
cnodata(m);
|
2011-03-30 12:46:40 +00:00
|
|
|
qunlock(m);
|
2012-10-17 13:48:30 +00:00
|
|
|
c->mcp = m;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2012-10-17 13:48:30 +00:00
|
|
|
/* return locked Mntcache if still valid else reset mcp */
|
|
|
|
static Mntcache*
|
|
|
|
ccache(Chan *c)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-10-17 13:48:30 +00:00
|
|
|
Mntcache *m;
|
|
|
|
|
|
|
|
m = c->mcp;
|
|
|
|
if(m) {
|
|
|
|
qlock(m);
|
|
|
|
if(eqchantdqid(c, m->type, m->dev, m->qid, 0) && c->qid.type == m->qid.type)
|
|
|
|
return m;
|
|
|
|
c->mcp = 0;
|
|
|
|
qunlock(m);
|
|
|
|
}
|
|
|
|
return 0;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if(off+len > maxcache)
|
|
|
|
return 0;
|
|
|
|
|
2012-10-17 13:48:30 +00:00
|
|
|
m = ccache(c);
|
2011-03-30 12:46:40 +00:00
|
|
|
if(m == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
offset = off;
|
|
|
|
t = &m->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(m);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
total = 0;
|
|
|
|
while(len) {
|
|
|
|
p = cpage(e);
|
|
|
|
if(p == 0) {
|
|
|
|
*t = e->next;
|
|
|
|
extentfree(e);
|
|
|
|
qunlock(m);
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
o = offset - e->start;
|
|
|
|
l = len;
|
|
|
|
if(l > e->len-o)
|
|
|
|
l = e->len-o;
|
|
|
|
|
|
|
|
k = kmap(p);
|
|
|
|
if(waserror()) {
|
|
|
|
kunmap(k);
|
|
|
|
putpage(p);
|
|
|
|
qunlock(m);
|
|
|
|
nexterror();
|
|
|
|
}
|
|
|
|
|
|
|
|
memmove(buf, (uchar*)VA(k) + o, l);
|
|
|
|
|
|
|
|
poperror();
|
|
|
|
kunmap(k);
|
|
|
|
|
|
|
|
putpage(p);
|
|
|
|
|
|
|
|
buf += l;
|
|
|
|
len -= l;
|
|
|
|
offset += l;
|
|
|
|
total += l;
|
|
|
|
t = &e->next;
|
|
|
|
e = e->next;
|
|
|
|
if(e == 0 || e->start != offset)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
qunlock(m);
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
Extent*
|
|
|
|
cchain(uchar *buf, ulong offset, int len, Extent **tail)
|
|
|
|
{
|
|
|
|
int l;
|
|
|
|
Page *p;
|
|
|
|
KMap *k;
|
|
|
|
Extent *e, *start, **t;
|
|
|
|
|
|
|
|
start = 0;
|
|
|
|
*tail = 0;
|
|
|
|
t = &start;
|
|
|
|
while(len) {
|
|
|
|
e = extentalloc();
|
|
|
|
if(e == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
p = auxpage();
|
|
|
|
if(p == 0) {
|
|
|
|
extentfree(e);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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 */
|
|
|
|
kunmap(k);
|
|
|
|
nexterror();
|
|
|
|
}
|
|
|
|
memmove((void*)VA(k), buf, l);
|
|
|
|
poperror();
|
|
|
|
kunmap(k);
|
|
|
|
|
|
|
|
cachepage(p, &fscache);
|
|
|
|
putpage(p);
|
|
|
|
|
|
|
|
buf += l;
|
|
|
|
offset += l;
|
|
|
|
len -= l;
|
|
|
|
|
|
|
|
*t = e;
|
|
|
|
*tail = e;
|
|
|
|
t = &e->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
cpgmove(Extent *e, uchar *buf, int boff, int len)
|
|
|
|
{
|
|
|
|
Page *p;
|
|
|
|
KMap *k;
|
|
|
|
|
|
|
|
p = cpage(e);
|
|
|
|
if(p == 0)
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cupdate(Chan *c, uchar *buf, int len, vlong off)
|
|
|
|
{
|
|
|
|
Mntcache *m;
|
|
|
|
Extent *tail;
|
|
|
|
Extent *e, *f, *p;
|
|
|
|
int o, ee, eblock;
|
|
|
|
ulong offset;
|
|
|
|
|
|
|
|
if(off > maxcache || len == 0)
|
|
|
|
return;
|
|
|
|
|
2012-10-17 13:48:30 +00:00
|
|
|
m = ccache(c);
|
2011-03-30 12:46:40 +00:00
|
|
|
if(m == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the insertion point
|
|
|
|
*/
|
|
|
|
offset = off;
|
|
|
|
p = 0;
|
|
|
|
for(f = m->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(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(p == 0) { /* at the head */
|
|
|
|
e = cchain(buf, offset, len, &tail);
|
|
|
|
if(e != 0) {
|
|
|
|
m->list = e;
|
|
|
|
tail->next = f;
|
|
|
|
}
|
|
|
|
qunlock(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* trim to the predecessor */
|
|
|
|
ee = p->start+p->len;
|
|
|
|
if(offset < ee) {
|
|
|
|
o = ee - offset;
|
|
|
|
len -= o;
|
|
|
|
if(len <= 0) {
|
|
|
|
qunlock(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
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) {
|
2011-12-22 01:17:29 +00:00
|
|
|
if(f && p->start + p->len > f->start)
|
|
|
|
print("CACHE: p->start=%uld p->len=%d f->start=%uld\n",
|
|
|
|
p->start, p->len, f->start);
|
2011-03-30 12:46:40 +00:00
|
|
|
qunlock(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
e = cchain(buf, offset, len, &tail);
|
|
|
|
if(e != 0) {
|
|
|
|
p->next = e;
|
|
|
|
tail->next = f;
|
|
|
|
}
|
|
|
|
qunlock(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cwrite(Chan* c, uchar *buf, int len, vlong off)
|
|
|
|
{
|
|
|
|
int o, eo;
|
|
|
|
Mntcache *m;
|
|
|
|
ulong eblock, ee;
|
|
|
|
Extent *p, *f, *e, *tail;
|
|
|
|
ulong offset;
|
|
|
|
|
|
|
|
if(off > maxcache || len == 0)
|
|
|
|
return;
|
|
|
|
|
2012-10-17 13:48:30 +00:00
|
|
|
m = ccache(c);
|
2011-03-30 12:46:40 +00:00
|
|
|
if(m == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
offset = off;
|
|
|
|
m->qid.vers++;
|
|
|
|
c->qid.vers++;
|
|
|
|
|
|
|
|
p = 0;
|
|
|
|
for(f = m->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 < 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 && 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)
|
|
|
|
m->list = f;
|
|
|
|
else
|
|
|
|
p->next = f;
|
|
|
|
qunlock(m);
|
|
|
|
}
|