2013-09-23 23:00:39 +02:00
|
|
|
#include "stdinc.h"
|
|
|
|
|
#include "dat.h"
|
|
|
|
|
#include "fns.h"
|
|
|
|
|
#include "error.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Lock watcher. Check that locking of blocks is always down.
|
|
|
|
|
*
|
|
|
|
|
* This is REALLY slow, and it won't work when the blocks aren't
|
|
|
|
|
* arranged in a tree (e.g., after the first snapshot). But it's great
|
|
|
|
|
* for debugging.
|
|
|
|
|
*/
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
MaxLock = 16,
|
|
|
|
|
HashSize = 1009,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Thread-specific watch state.
|
|
|
|
|
*/
|
|
|
|
|
typedef struct WThread WThread;
|
|
|
|
|
struct WThread
|
|
|
|
|
{
|
|
|
|
|
Block *b[MaxLock]; /* blocks currently held */
|
|
|
|
|
uint nb;
|
|
|
|
|
uint pid;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct WMap WMap;
|
|
|
|
|
typedef struct WEntry WEntry;
|
|
|
|
|
|
|
|
|
|
struct WEntry
|
|
|
|
|
{
|
|
|
|
|
uchar c[VtScoreSize];
|
|
|
|
|
uchar p[VtScoreSize];
|
|
|
|
|
int off;
|
|
|
|
|
|
|
|
|
|
WEntry *cprev;
|
|
|
|
|
WEntry *cnext;
|
|
|
|
|
WEntry *pprev;
|
|
|
|
|
WEntry *pnext;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct WMap
|
|
|
|
|
{
|
2013-09-23 23:16:25 +02:00
|
|
|
QLock lk;
|
2013-09-23 23:00:39 +02:00
|
|
|
|
|
|
|
|
WEntry *hchild[HashSize];
|
|
|
|
|
WEntry *hparent[HashSize];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static WMap map;
|
|
|
|
|
static void **wp;
|
|
|
|
|
static uint blockSize;
|
|
|
|
|
static WEntry *pool;
|
|
|
|
|
uint bwatchDisabled;
|
|
|
|
|
|
|
|
|
|
static uint
|
|
|
|
|
hash(uchar score[VtScoreSize])
|
|
|
|
|
{
|
|
|
|
|
uint i, h;
|
|
|
|
|
|
|
|
|
|
h = 0;
|
|
|
|
|
for(i=0; i<VtScoreSize; i++)
|
|
|
|
|
h = h*37 + score[i];
|
|
|
|
|
return h%HashSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#include <pool.h>
|
|
|
|
|
static void
|
|
|
|
|
freeWEntry(WEntry *e)
|
|
|
|
|
{
|
|
|
|
|
memset(e, 0, sizeof(WEntry));
|
|
|
|
|
e->pnext = pool;
|
|
|
|
|
pool = e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static WEntry*
|
|
|
|
|
allocWEntry(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
WEntry *w;
|
|
|
|
|
|
|
|
|
|
w = pool;
|
|
|
|
|
if(w == nil){
|
2013-09-23 23:16:25 +02:00
|
|
|
w = vtmallocz(1024*sizeof(WEntry));
|
2013-09-23 23:00:39 +02:00
|
|
|
for(i=0; i<1024; i++)
|
|
|
|
|
freeWEntry(&w[i]);
|
|
|
|
|
w = pool;
|
|
|
|
|
}
|
|
|
|
|
pool = w->pnext;
|
|
|
|
|
memset(w, 0, sizeof(WEntry));
|
|
|
|
|
return w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* remove all dependencies with score as a parent
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
_bwatchResetParent(uchar *score)
|
|
|
|
|
{
|
|
|
|
|
WEntry *w, *next;
|
|
|
|
|
uint h;
|
|
|
|
|
|
|
|
|
|
h = hash(score);
|
|
|
|
|
for(w=map.hparent[h]; w; w=next){
|
|
|
|
|
next = w->pnext;
|
|
|
|
|
if(memcmp(w->p, score, VtScoreSize) == 0){
|
|
|
|
|
if(w->pnext)
|
|
|
|
|
w->pnext->pprev = w->pprev;
|
|
|
|
|
if(w->pprev)
|
|
|
|
|
w->pprev->pnext = w->pnext;
|
|
|
|
|
else
|
|
|
|
|
map.hparent[h] = w->pnext;
|
|
|
|
|
if(w->cnext)
|
|
|
|
|
w->cnext->cprev = w->cprev;
|
|
|
|
|
if(w->cprev)
|
|
|
|
|
w->cprev->cnext = w->cnext;
|
|
|
|
|
else
|
|
|
|
|
map.hchild[hash(w->c)] = w->cnext;
|
|
|
|
|
freeWEntry(w);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*
|
2020-01-10 14:44:21 +00:00
|
|
|
* and child
|
2013-09-23 23:00:39 +02:00
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
_bwatchResetChild(uchar *score)
|
|
|
|
|
{
|
|
|
|
|
WEntry *w, *next;
|
|
|
|
|
uint h;
|
|
|
|
|
|
|
|
|
|
h = hash(score);
|
|
|
|
|
for(w=map.hchild[h]; w; w=next){
|
|
|
|
|
next = w->cnext;
|
|
|
|
|
if(memcmp(w->c, score, VtScoreSize) == 0){
|
|
|
|
|
if(w->pnext)
|
|
|
|
|
w->pnext->pprev = w->pprev;
|
|
|
|
|
if(w->pprev)
|
|
|
|
|
w->pprev->pnext = w->pnext;
|
|
|
|
|
else
|
|
|
|
|
map.hparent[hash(w->p)] = w->pnext;
|
|
|
|
|
if(w->cnext)
|
|
|
|
|
w->cnext->cprev = w->cprev;
|
|
|
|
|
if(w->cprev)
|
|
|
|
|
w->cprev->cnext = w->cnext;
|
|
|
|
|
else
|
|
|
|
|
map.hchild[h] = w->cnext;
|
|
|
|
|
freeWEntry(w);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uchar*
|
|
|
|
|
parent(uchar c[VtScoreSize], int *off)
|
|
|
|
|
{
|
|
|
|
|
WEntry *w;
|
|
|
|
|
uint h;
|
|
|
|
|
|
|
|
|
|
h = hash(c);
|
|
|
|
|
for(w=map.hchild[h]; w; w=w->cnext)
|
|
|
|
|
if(memcmp(w->c, c, VtScoreSize) == 0){
|
|
|
|
|
*off = w->off;
|
|
|
|
|
return w->p;
|
|
|
|
|
}
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
addChild(uchar p[VtEntrySize], uchar c[VtEntrySize], int off)
|
|
|
|
|
{
|
|
|
|
|
uint h;
|
|
|
|
|
WEntry *w;
|
|
|
|
|
|
|
|
|
|
w = allocWEntry();
|
|
|
|
|
memmove(w->p, p, VtScoreSize);
|
|
|
|
|
memmove(w->c, c, VtScoreSize);
|
|
|
|
|
w->off = off;
|
|
|
|
|
|
|
|
|
|
h = hash(p);
|
|
|
|
|
w->pnext = map.hparent[h];
|
|
|
|
|
if(w->pnext)
|
|
|
|
|
w->pnext->pprev = w;
|
|
|
|
|
map.hparent[h] = w;
|
|
|
|
|
|
|
|
|
|
h = hash(c);
|
|
|
|
|
w->cnext = map.hchild[h];
|
|
|
|
|
if(w->cnext)
|
|
|
|
|
w->cnext->cprev = w;
|
|
|
|
|
map.hchild[h] = w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
bwatchReset(uchar score[VtScoreSize])
|
|
|
|
|
{
|
2013-09-23 23:16:25 +02:00
|
|
|
qlock(&map.lk);
|
2013-09-23 23:00:39 +02:00
|
|
|
_bwatchResetParent(score);
|
|
|
|
|
_bwatchResetChild(score);
|
2013-09-23 23:16:25 +02:00
|
|
|
qunlock(&map.lk);
|
2013-09-23 23:00:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
bwatchInit(void)
|
|
|
|
|
{
|
|
|
|
|
wp = privalloc();
|
|
|
|
|
*wp = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
bwatchSetBlockSize(uint bs)
|
|
|
|
|
{
|
|
|
|
|
blockSize = bs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static WThread*
|
|
|
|
|
getWThread(void)
|
|
|
|
|
{
|
|
|
|
|
WThread *w;
|
|
|
|
|
|
|
|
|
|
w = *wp;
|
|
|
|
|
if(w == nil || w->pid != getpid()){
|
2013-09-23 23:16:25 +02:00
|
|
|
w = vtmallocz(sizeof(WThread));
|
2013-09-23 23:00:39 +02:00
|
|
|
*wp = w;
|
|
|
|
|
w->pid = getpid();
|
|
|
|
|
}
|
|
|
|
|
return w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Derive dependencies from the contents of b.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
bwatchDependency(Block *b)
|
|
|
|
|
{
|
|
|
|
|
int i, epb, ppb;
|
|
|
|
|
Entry e;
|
|
|
|
|
|
|
|
|
|
if(bwatchDisabled)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-09-23 23:16:25 +02:00
|
|
|
qlock(&map.lk);
|
2013-09-23 23:00:39 +02:00
|
|
|
_bwatchResetParent(b->score);
|
|
|
|
|
|
|
|
|
|
switch(b->l.type){
|
|
|
|
|
case BtData:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BtDir:
|
|
|
|
|
epb = blockSize / VtEntrySize;
|
|
|
|
|
for(i=0; i<epb; i++){
|
|
|
|
|
entryUnpack(&e, b->data, i);
|
|
|
|
|
if(!(e.flags & VtEntryActive))
|
|
|
|
|
continue;
|
|
|
|
|
addChild(b->score, e.score, i);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ppb = blockSize / VtScoreSize;
|
|
|
|
|
for(i=0; i<ppb; i++)
|
|
|
|
|
addChild(b->score, b->data+i*VtScoreSize, i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2013-09-23 23:16:25 +02:00
|
|
|
qunlock(&map.lk);
|
2013-09-23 23:00:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
depth(uchar *s)
|
|
|
|
|
{
|
|
|
|
|
int d, x;
|
|
|
|
|
|
|
|
|
|
d = -1;
|
|
|
|
|
while(s){
|
|
|
|
|
d++;
|
|
|
|
|
s = parent(s, &x);
|
|
|
|
|
}
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
lockConflicts(uchar xhave[VtScoreSize], uchar xwant[VtScoreSize])
|
|
|
|
|
{
|
|
|
|
|
uchar *have, *want;
|
|
|
|
|
int havedepth, wantdepth, havepos, wantpos;
|
|
|
|
|
|
|
|
|
|
have = xhave;
|
|
|
|
|
want = xwant;
|
|
|
|
|
|
|
|
|
|
havedepth = depth(have);
|
|
|
|
|
wantdepth = depth(want);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* walk one or the other up until they're both
|
|
|
|
|
* at the same level.
|
|
|
|
|
*/
|
|
|
|
|
havepos = -1;
|
|
|
|
|
wantpos = -1;
|
|
|
|
|
have = xhave;
|
|
|
|
|
want = xwant;
|
|
|
|
|
while(wantdepth > havedepth){
|
|
|
|
|
wantdepth--;
|
|
|
|
|
want = parent(want, &wantpos);
|
|
|
|
|
}
|
|
|
|
|
while(havedepth > wantdepth){
|
|
|
|
|
havedepth--;
|
|
|
|
|
have = parent(have, &havepos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* walk them up simultaneously until we reach
|
|
|
|
|
* a common ancestor.
|
|
|
|
|
*/
|
|
|
|
|
while(have && want && memcmp(have, want, VtScoreSize) != 0){
|
|
|
|
|
have = parent(have, &havepos);
|
|
|
|
|
want = parent(want, &wantpos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* not part of same tree. happens mainly with
|
|
|
|
|
* newly allocated blocks.
|
|
|
|
|
*/
|
|
|
|
|
if(!have || !want)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* never walked want: means we want to lock
|
|
|
|
|
* an ancestor of have. no no.
|
|
|
|
|
*/
|
|
|
|
|
if(wantpos == -1)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* never walked have: means we want to lock a
|
|
|
|
|
* child of have. that's okay.
|
|
|
|
|
*/
|
|
|
|
|
if(havepos == -1)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* walked both: they're from different places in the tree.
|
|
|
|
|
* require that the left one be locked before the right one.
|
|
|
|
|
* (this is questionable, but it puts a total order on the block tree).
|
|
|
|
|
*/
|
|
|
|
|
return havepos < wantpos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
stop(void)
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
|
|
snprint(buf, sizeof buf, "#p/%d/ctl", getpid());
|
|
|
|
|
fd = open(buf, OWRITE);
|
|
|
|
|
write(fd, "stop", 4);
|
|
|
|
|
close(fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check whether the calling thread can validly lock b.
|
|
|
|
|
* That is, check that the calling thread doesn't hold
|
|
|
|
|
* locks for any of b's children.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
bwatchLock(Block *b)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
WThread *w;
|
|
|
|
|
|
|
|
|
|
if(bwatchDisabled)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if(b->part != PartData)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-09-23 23:16:25 +02:00
|
|
|
qlock(&map.lk);
|
2013-09-23 23:00:39 +02:00
|
|
|
w = getWThread();
|
|
|
|
|
for(i=0; i<w->nb; i++){
|
|
|
|
|
if(lockConflicts(w->b[i]->score, b->score)){
|
|
|
|
|
fprint(2, "%d: have block %V; shouldn't lock %V\n",
|
|
|
|
|
w->pid, w->b[i]->score, b->score);
|
|
|
|
|
stop();
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-09-23 23:16:25 +02:00
|
|
|
qunlock(&map.lk);
|
2013-09-23 23:00:39 +02:00
|
|
|
if(w->nb >= MaxLock){
|
|
|
|
|
fprint(2, "%d: too many blocks held\n", w->pid);
|
|
|
|
|
stop();
|
|
|
|
|
}else
|
|
|
|
|
w->b[w->nb++] = b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Note that the calling thread is about to unlock b.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
bwatchUnlock(Block *b)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
WThread *w;
|
|
|
|
|
|
|
|
|
|
if(bwatchDisabled)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if(b->part != PartData)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
w = getWThread();
|
|
|
|
|
for(i=0; i<w->nb; i++)
|
|
|
|
|
if(w->b[i] == b)
|
|
|
|
|
break;
|
|
|
|
|
if(i>=w->nb){
|
|
|
|
|
fprint(2, "%d: unlock of unlocked block %V\n", w->pid, b->score);
|
|
|
|
|
stop();
|
|
|
|
|
}else
|
|
|
|
|
w->b[i] = w->b[--w->nb];
|
|
|
|
|
}
|