15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier #include "dat.h"
35e96a66cSDavid du Colombier #include "fns.h"
45e96a66cSDavid du Colombier #include "error.h"
55e96a66cSDavid du Colombier
65e96a66cSDavid du Colombier #include "9.h" /* for cacheFlush */
75e96a66cSDavid du Colombier
85e96a66cSDavid du Colombier typedef struct FreeList FreeList;
95e96a66cSDavid du Colombier typedef struct BAddr BAddr;
105e96a66cSDavid du Colombier
115e96a66cSDavid du Colombier enum {
125e96a66cSDavid du Colombier BadHeap = ~0,
135e96a66cSDavid du Colombier };
145e96a66cSDavid du Colombier
155e96a66cSDavid du Colombier /*
165e96a66cSDavid du Colombier * Store data to the memory cache in c->size blocks
175e96a66cSDavid du Colombier * with the block zero extended to fill it out. When writing to
185e96a66cSDavid du Colombier * Venti, the block will be zero truncated. The walker will also check
195e96a66cSDavid du Colombier * that the block fits within psize or dsize as the case may be.
205e96a66cSDavid du Colombier */
215e96a66cSDavid du Colombier
225e96a66cSDavid du Colombier struct Cache
235e96a66cSDavid du Colombier {
24d7aba6c3SDavid du Colombier QLock lk;
255e96a66cSDavid du Colombier int ref;
265e96a66cSDavid du Colombier int mode;
275e96a66cSDavid du Colombier
285e96a66cSDavid du Colombier Disk *disk;
295e96a66cSDavid du Colombier int size; /* block size */
3061201b97SDavid du Colombier int ndmap; /* size of per-block dirty pointer map used in blockWrite */
31d7aba6c3SDavid du Colombier VtConn *z;
325e96a66cSDavid du Colombier u32int now; /* ticks for usage timestamps */
335e96a66cSDavid du Colombier Block **heads; /* hash table for finding address */
345e96a66cSDavid du Colombier int nheap; /* number of available victims */
355e96a66cSDavid du Colombier Block **heap; /* heap for locating victims */
365e96a66cSDavid du Colombier long nblocks; /* number of blocks allocated */
375e96a66cSDavid du Colombier Block *blocks; /* array of block descriptors */
385e96a66cSDavid du Colombier u8int *mem; /* memory for all block data & blists */
395e96a66cSDavid du Colombier
405e96a66cSDavid du Colombier BList *blfree;
41d7aba6c3SDavid du Colombier Rendez blrend;
425e96a66cSDavid du Colombier
435e96a66cSDavid du Colombier int ndirty; /* number of dirty blocks in the cache */
445e96a66cSDavid du Colombier int maxdirty; /* max number of dirty blocks */
455e96a66cSDavid du Colombier u32int vers;
465e96a66cSDavid du Colombier
475e96a66cSDavid du Colombier long hashSize;
485e96a66cSDavid du Colombier
495e96a66cSDavid du Colombier FreeList *fl;
505e96a66cSDavid du Colombier
51d7aba6c3SDavid du Colombier Rendez die; /* daemon threads should die when QLock != nil */
525e96a66cSDavid du Colombier
53d7aba6c3SDavid du Colombier Rendez flush;
54d7aba6c3SDavid du Colombier Rendez flushwait;
55d7aba6c3SDavid du Colombier Rendez heapwait;
565e96a66cSDavid du Colombier BAddr *baddr;
575e96a66cSDavid du Colombier int bw, br, be;
585e96a66cSDavid du Colombier int nflush;
595e96a66cSDavid du Colombier
6061201b97SDavid du Colombier Periodic *sync;
6161201b97SDavid du Colombier
625e96a66cSDavid du Colombier /* unlink daemon */
635e96a66cSDavid du Colombier BList *uhead;
645e96a66cSDavid du Colombier BList *utail;
65d7aba6c3SDavid du Colombier Rendez unlink;
667abd426fSDavid du Colombier
677abd426fSDavid du Colombier /* block counts */
687abd426fSDavid du Colombier int nused;
697abd426fSDavid du Colombier int ndisk;
705e96a66cSDavid du Colombier };
715e96a66cSDavid du Colombier
725e96a66cSDavid du Colombier struct BList {
735e96a66cSDavid du Colombier int part;
745e96a66cSDavid du Colombier u32int addr;
755e96a66cSDavid du Colombier uchar type;
765e96a66cSDavid du Colombier u32int tag;
775e96a66cSDavid du Colombier u32int epoch;
785e96a66cSDavid du Colombier u32int vers;
795e96a66cSDavid du Colombier
80e569ccb5SDavid du Colombier int recurse; /* for block unlink */
81e569ccb5SDavid du Colombier
825e96a66cSDavid du Colombier /* for roll back */
835e96a66cSDavid du Colombier int index; /* -1 indicates not valid */
8461201b97SDavid du Colombier union {
855e96a66cSDavid du Colombier uchar score[VtScoreSize];
8661201b97SDavid du Colombier uchar entry[VtEntrySize];
8761201b97SDavid du Colombier } old;
885e96a66cSDavid du Colombier BList *next;
895e96a66cSDavid du Colombier };
905e96a66cSDavid du Colombier
915e96a66cSDavid du Colombier struct BAddr {
925e96a66cSDavid du Colombier int part;
935e96a66cSDavid du Colombier u32int addr;
945e96a66cSDavid du Colombier u32int vers;
955e96a66cSDavid du Colombier };
965e96a66cSDavid du Colombier
975e96a66cSDavid du Colombier struct FreeList {
98d7aba6c3SDavid du Colombier QLock lk;
995e96a66cSDavid du Colombier u32int last; /* last block allocated */
1005e96a66cSDavid du Colombier u32int end; /* end of data partition */
1017abd426fSDavid du Colombier u32int nused; /* number of used blocks */
102225077b0SDavid du Colombier u32int epochLow; /* low epoch when last updated nused */
1035e96a66cSDavid du Colombier };
1045e96a66cSDavid du Colombier
1055e96a66cSDavid du Colombier static FreeList *flAlloc(u32int end);
1065e96a66cSDavid du Colombier static void flFree(FreeList *fl);
1075e96a66cSDavid du Colombier
1085e96a66cSDavid du Colombier static Block *cacheBumpBlock(Cache *c);
1095e96a66cSDavid du Colombier static void heapDel(Block*);
1105e96a66cSDavid du Colombier static void heapIns(Block*);
1115e96a66cSDavid du Colombier static void cacheCheck(Cache*);
1125e96a66cSDavid du Colombier static void unlinkThread(void *a);
1135e96a66cSDavid du Colombier static void flushThread(void *a);
1145e96a66cSDavid du Colombier static void unlinkBody(Cache *c);
1155e96a66cSDavid du Colombier static int cacheFlushBlock(Cache *c);
11661201b97SDavid du Colombier static void cacheSync(void*);
117e569ccb5SDavid du Colombier static BList *blistAlloc(Block*);
118e569ccb5SDavid du Colombier static void blistFree(Cache*, BList*);
119e569ccb5SDavid du Colombier static void doRemoveLink(Cache*, BList*);
120e569ccb5SDavid du Colombier
1215e96a66cSDavid du Colombier /*
1225e96a66cSDavid du Colombier * Mapping from local block type to Venti type
1235e96a66cSDavid du Colombier */
1245e96a66cSDavid du Colombier int vtType[BtMax] = {
1255e96a66cSDavid du Colombier VtDataType, /* BtData | 0 */
126d7aba6c3SDavid du Colombier VtDataType+1, /* BtData | 1 */
127d7aba6c3SDavid du Colombier VtDataType+2, /* BtData | 2 */
128d7aba6c3SDavid du Colombier VtDataType+3, /* BtData | 3 */
129d7aba6c3SDavid du Colombier VtDataType+4, /* BtData | 4 */
130d7aba6c3SDavid du Colombier VtDataType+5, /* BtData | 5 */
131d7aba6c3SDavid du Colombier VtDataType+6, /* BtData | 6 */
132d7aba6c3SDavid du Colombier VtDataType+7, /* BtData | 7 */
1335e96a66cSDavid du Colombier VtDirType, /* BtDir | 0 */
134d7aba6c3SDavid du Colombier VtDirType+1, /* BtDir | 1 */
135d7aba6c3SDavid du Colombier VtDirType+2, /* BtDir | 2 */
136d7aba6c3SDavid du Colombier VtDirType+3, /* BtDir | 3 */
137d7aba6c3SDavid du Colombier VtDirType+4, /* BtDir | 4 */
138d7aba6c3SDavid du Colombier VtDirType+5, /* BtDir | 5 */
139d7aba6c3SDavid du Colombier VtDirType+6, /* BtDir | 6 */
140d7aba6c3SDavid du Colombier VtDirType+7, /* BtDir | 7 */
1415e96a66cSDavid du Colombier };
1425e96a66cSDavid du Colombier
1435e96a66cSDavid du Colombier /*
1445e96a66cSDavid du Colombier * Allocate the memory cache.
1455e96a66cSDavid du Colombier */
1465e96a66cSDavid du Colombier Cache *
cacheAlloc(Disk * disk,VtConn * z,ulong nblocks,int mode)147d7aba6c3SDavid du Colombier cacheAlloc(Disk *disk, VtConn *z, ulong nblocks, int mode)
1485e96a66cSDavid du Colombier {
1495e96a66cSDavid du Colombier int i;
1505e96a66cSDavid du Colombier Cache *c;
1515e96a66cSDavid du Colombier Block *b;
1525e96a66cSDavid du Colombier BList *bl;
1535e96a66cSDavid du Colombier u8int *p;
1545e96a66cSDavid du Colombier int nbl;
1555e96a66cSDavid du Colombier
156d7aba6c3SDavid du Colombier c = vtmallocz(sizeof(Cache));
1575e96a66cSDavid du Colombier
1585e96a66cSDavid du Colombier /* reasonable number of BList elements */
1595e96a66cSDavid du Colombier nbl = nblocks * 4;
1605e96a66cSDavid du Colombier
1615e96a66cSDavid du Colombier c->ref = 1;
1625e96a66cSDavid du Colombier c->disk = disk;
1635e96a66cSDavid du Colombier c->z = z;
1645e96a66cSDavid du Colombier c->size = diskBlockSize(disk);
1655e96a66cSDavid du Colombier bwatchSetBlockSize(c->size);
1665e96a66cSDavid du Colombier /* round c->size up to be a nice multiple */
1675e96a66cSDavid du Colombier c->size = (c->size + 127) & ~127;
16861201b97SDavid du Colombier c->ndmap = (c->size/20 + 7) / 8;
1695e96a66cSDavid du Colombier c->nblocks = nblocks;
1705e96a66cSDavid du Colombier c->hashSize = nblocks;
171d7aba6c3SDavid du Colombier c->heads = vtmallocz(c->hashSize*sizeof(Block*));
172d7aba6c3SDavid du Colombier c->heap = vtmallocz(nblocks*sizeof(Block*));
173d7aba6c3SDavid du Colombier c->blocks = vtmallocz(nblocks*sizeof(Block));
174d7aba6c3SDavid du Colombier c->mem = vtmallocz(nblocks * (c->size + c->ndmap) + nbl * sizeof(BList));
175d7aba6c3SDavid du Colombier c->baddr = vtmallocz(nblocks * sizeof(BAddr));
1765e96a66cSDavid du Colombier c->mode = mode;
1775e96a66cSDavid du Colombier c->vers++;
1785e96a66cSDavid du Colombier p = c->mem;
1795e96a66cSDavid du Colombier for(i = 0; i < nblocks; i++){
1805e96a66cSDavid du Colombier b = &c->blocks[i];
1815e96a66cSDavid du Colombier b->c = c;
1825e96a66cSDavid du Colombier b->data = p;
1835e96a66cSDavid du Colombier b->heap = i;
184d7aba6c3SDavid du Colombier b->ioready.l = &b->lk;
1855e96a66cSDavid du Colombier c->heap[i] = b;
1865e96a66cSDavid du Colombier p += c->size;
1875e96a66cSDavid du Colombier }
1885e96a66cSDavid du Colombier c->nheap = nblocks;
1895e96a66cSDavid du Colombier for(i = 0; i < nbl; i++){
1905e96a66cSDavid du Colombier bl = (BList*)p;
1915e96a66cSDavid du Colombier bl->next = c->blfree;
1925e96a66cSDavid du Colombier c->blfree = bl;
1935e96a66cSDavid du Colombier p += sizeof(BList);
1945e96a66cSDavid du Colombier }
19561201b97SDavid du Colombier /* separate loop to keep blocks and blists reasonably aligned */
19661201b97SDavid du Colombier for(i = 0; i < nblocks; i++){
19761201b97SDavid du Colombier b = &c->blocks[i];
19861201b97SDavid du Colombier b->dmap = p;
19961201b97SDavid du Colombier p += c->ndmap;
20061201b97SDavid du Colombier }
20161201b97SDavid du Colombier
202d7aba6c3SDavid du Colombier c->blrend.l = &c->lk;
2035e96a66cSDavid du Colombier
2045e96a66cSDavid du Colombier c->maxdirty = nblocks*(DirtyPercentage*0.01);
2055e96a66cSDavid du Colombier
2065e96a66cSDavid du Colombier c->fl = flAlloc(diskSize(disk, PartData));
2075e96a66cSDavid du Colombier
208d7aba6c3SDavid du Colombier c->unlink.l = &c->lk;
209d7aba6c3SDavid du Colombier c->flush.l = &c->lk;
210d7aba6c3SDavid du Colombier c->flushwait.l = &c->lk;
211d7aba6c3SDavid du Colombier c->heapwait.l = &c->lk;
21261201b97SDavid du Colombier c->sync = periodicAlloc(cacheSync, c, 30*1000);
2135e96a66cSDavid du Colombier
2145e96a66cSDavid du Colombier if(mode == OReadWrite){
2155e96a66cSDavid du Colombier c->ref += 2;
216d7aba6c3SDavid du Colombier proccreate(unlinkThread, c, STACK);
217d7aba6c3SDavid du Colombier proccreate(flushThread, c, STACK);
2185e96a66cSDavid du Colombier }
2195e96a66cSDavid du Colombier cacheCheck(c);
2205e96a66cSDavid du Colombier
2215e96a66cSDavid du Colombier return c;
2225e96a66cSDavid du Colombier }
2235e96a66cSDavid du Colombier
2245e96a66cSDavid du Colombier /*
2255e96a66cSDavid du Colombier * Free the whole memory cache, flushing all dirty blocks to the disk.
2265e96a66cSDavid du Colombier */
2275e96a66cSDavid du Colombier void
cacheFree(Cache * c)2285e96a66cSDavid du Colombier cacheFree(Cache *c)
2295e96a66cSDavid du Colombier {
2305e96a66cSDavid du Colombier int i;
2315e96a66cSDavid du Colombier
2325e96a66cSDavid du Colombier /* kill off daemon threads */
233d7aba6c3SDavid du Colombier qlock(&c->lk);
234d7aba6c3SDavid du Colombier c->die.l = &c->lk;
23561201b97SDavid du Colombier periodicKill(c->sync);
236d7aba6c3SDavid du Colombier rwakeup(&c->flush);
237d7aba6c3SDavid du Colombier rwakeup(&c->unlink);
2385e96a66cSDavid du Colombier while(c->ref > 1)
239d7aba6c3SDavid du Colombier rsleep(&c->die);
2405e96a66cSDavid du Colombier
2415e96a66cSDavid du Colombier /* flush everything out */
2425e96a66cSDavid du Colombier do {
2435e96a66cSDavid du Colombier unlinkBody(c);
244d7aba6c3SDavid du Colombier qunlock(&c->lk);
2455e96a66cSDavid du Colombier while(cacheFlushBlock(c))
2465e96a66cSDavid du Colombier ;
2475e96a66cSDavid du Colombier diskFlush(c->disk);
248d7aba6c3SDavid du Colombier qlock(&c->lk);
2495e96a66cSDavid du Colombier } while(c->uhead || c->ndirty);
250d7aba6c3SDavid du Colombier qunlock(&c->lk);
2515e96a66cSDavid du Colombier
2525e96a66cSDavid du Colombier cacheCheck(c);
2535e96a66cSDavid du Colombier
2545e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){
2555e96a66cSDavid du Colombier assert(c->blocks[i].ref == 0);
2565e96a66cSDavid du Colombier }
2575e96a66cSDavid du Colombier flFree(c->fl);
258d7aba6c3SDavid du Colombier vtfree(c->baddr);
259d7aba6c3SDavid du Colombier vtfree(c->heads);
260d7aba6c3SDavid du Colombier vtfree(c->blocks);
261d7aba6c3SDavid du Colombier vtfree(c->mem);
2625e96a66cSDavid du Colombier diskFree(c->disk);
2635e96a66cSDavid du Colombier /* don't close vtSession */
264d7aba6c3SDavid du Colombier vtfree(c);
2655e96a66cSDavid du Colombier }
2665e96a66cSDavid du Colombier
2675e96a66cSDavid du Colombier static void
cacheDump(Cache * c)2685e96a66cSDavid du Colombier cacheDump(Cache *c)
2695e96a66cSDavid du Colombier {
2705e96a66cSDavid du Colombier int i;
2715e96a66cSDavid du Colombier Block *b;
2725e96a66cSDavid du Colombier
2735e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){
2745e96a66cSDavid du Colombier b = &c->blocks[i];
27574f16c81SDavid du Colombier fprint(2, "%d. p=%d a=%ud %V t=%d ref=%d state=%s io=%s pc=%#p\n",
276fe853e23SDavid du Colombier i, b->part, b->addr, b->score, b->l.type, b->ref,
277fe853e23SDavid du Colombier bsStr(b->l.state), bioStr(b->iostate), b->pc);
2785e96a66cSDavid du Colombier }
2795e96a66cSDavid du Colombier }
2805e96a66cSDavid du Colombier
2815e96a66cSDavid du Colombier static void
cacheCheck(Cache * c)2825e96a66cSDavid du Colombier cacheCheck(Cache *c)
2835e96a66cSDavid du Colombier {
2845e96a66cSDavid du Colombier u32int size, now;
2855e96a66cSDavid du Colombier int i, k, refed;
2865e96a66cSDavid du Colombier static uchar zero[VtScoreSize];
2875e96a66cSDavid du Colombier Block *b;
2885e96a66cSDavid du Colombier
2895e96a66cSDavid du Colombier size = c->size;
2905e96a66cSDavid du Colombier now = c->now;
2915e96a66cSDavid du Colombier
2925e96a66cSDavid du Colombier for(i = 0; i < c->nheap; i++){
2935e96a66cSDavid du Colombier if(c->heap[i]->heap != i)
294d7aba6c3SDavid du Colombier sysfatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
2955e96a66cSDavid du Colombier if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now)
296d7aba6c3SDavid du Colombier sysfatal("bad heap ordering");
2975e96a66cSDavid du Colombier k = (i << 1) + 1;
2985e96a66cSDavid du Colombier if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
299d7aba6c3SDavid du Colombier sysfatal("bad heap ordering");
3005e96a66cSDavid du Colombier k++;
3015e96a66cSDavid du Colombier if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
302d7aba6c3SDavid du Colombier sysfatal("bad heap ordering");
3035e96a66cSDavid du Colombier }
3045e96a66cSDavid du Colombier
3055e96a66cSDavid du Colombier refed = 0;
3065e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){
3075e96a66cSDavid du Colombier b = &c->blocks[i];
3085e96a66cSDavid du Colombier if(b->data != &c->mem[i * size])
309d7aba6c3SDavid du Colombier sysfatal("mis-blocked at %d", i);
3105e96a66cSDavid du Colombier if(b->ref && b->heap == BadHeap){
3115e96a66cSDavid du Colombier refed++;
3125e96a66cSDavid du Colombier }
3135e96a66cSDavid du Colombier }
3145e96a66cSDavid du Colombier if(c->nheap + refed != c->nblocks){
3153b86f2f8SDavid du Colombier fprint(2, "%s: cacheCheck: nheap %d refed %d nblocks %ld\n", argv0, c->nheap, refed, c->nblocks);
3165e96a66cSDavid du Colombier cacheDump(c);
3175e96a66cSDavid du Colombier }
3185e96a66cSDavid du Colombier assert(c->nheap + refed == c->nblocks);
3195e96a66cSDavid du Colombier refed = 0;
3205e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){
3215e96a66cSDavid du Colombier b = &c->blocks[i];
3225e96a66cSDavid du Colombier if(b->ref){
3233b86f2f8SDavid du Colombier if(1)fprint(2, "%s: p=%d a=%ud %V ref=%d %L\n", argv0, b->part, b->addr, b->score, b->ref, &b->l);
3245e96a66cSDavid du Colombier refed++;
3255e96a66cSDavid du Colombier }
3265e96a66cSDavid du Colombier }
3273b86f2f8SDavid du Colombier if(refed > 0)fprint(2, "%s: cacheCheck: in used %d\n", argv0, refed);
3285e96a66cSDavid du Colombier }
3295e96a66cSDavid du Colombier
3305e96a66cSDavid du Colombier
3315e96a66cSDavid du Colombier /*
3325e96a66cSDavid du Colombier * locate the block with the oldest second to last use.
3335e96a66cSDavid du Colombier * remove it from the heap, and fix up the heap.
3345e96a66cSDavid du Colombier */
3355e96a66cSDavid du Colombier /* called with c->lk held */
3365e96a66cSDavid du Colombier static Block *
cacheBumpBlock(Cache * c)3375e96a66cSDavid du Colombier cacheBumpBlock(Cache *c)
3385e96a66cSDavid du Colombier {
3390b9a5132SDavid du Colombier int printed;
3405e96a66cSDavid du Colombier Block *b;
3415e96a66cSDavid du Colombier
3425e96a66cSDavid du Colombier /*
3435e96a66cSDavid du Colombier * locate the block with the oldest second to last use.
3445e96a66cSDavid du Colombier * remove it from the heap, and fix up the heap.
3455e96a66cSDavid du Colombier */
3460b9a5132SDavid du Colombier printed = 0;
347fe853e23SDavid du Colombier if(c->nheap == 0){
348fe853e23SDavid du Colombier while(c->nheap == 0){
349d7aba6c3SDavid du Colombier rwakeup(&c->flush);
350d7aba6c3SDavid du Colombier rsleep(&c->heapwait);
3510b9a5132SDavid du Colombier if(c->nheap == 0){
3520b9a5132SDavid du Colombier printed = 1;
3533b86f2f8SDavid du Colombier fprint(2, "%s: entire cache is busy, %d dirty "
3543b86f2f8SDavid du Colombier "-- waking flush thread\n",
3553b86f2f8SDavid du Colombier argv0, c->ndirty);
356fe853e23SDavid du Colombier }
3570b9a5132SDavid du Colombier }
3580b9a5132SDavid du Colombier if(printed)
3593b86f2f8SDavid du Colombier fprint(2, "%s: cache is okay again, %d dirty\n",
3603b86f2f8SDavid du Colombier argv0, c->ndirty);
361fe853e23SDavid du Colombier }
362fe853e23SDavid du Colombier
3635e96a66cSDavid du Colombier b = c->heap[0];
3645e96a66cSDavid du Colombier heapDel(b);
3655e96a66cSDavid du Colombier
3665e96a66cSDavid du Colombier assert(b->heap == BadHeap);
3675e96a66cSDavid du Colombier assert(b->ref == 0);
36861a5f0d0SDavid du Colombier assert(b->iostate != BioDirty && b->iostate != BioReading && b->iostate != BioWriting);
3695e96a66cSDavid du Colombier assert(b->prior == nil);
3705e96a66cSDavid du Colombier assert(b->uhead == nil);
3715e96a66cSDavid du Colombier
3725e96a66cSDavid du Colombier /*
3735e96a66cSDavid du Colombier * unchain the block from hash chain
3745e96a66cSDavid du Colombier */
3755e96a66cSDavid du Colombier if(b->prev){
3765e96a66cSDavid du Colombier *(b->prev) = b->next;
3775e96a66cSDavid du Colombier if(b->next)
3785e96a66cSDavid du Colombier b->next->prev = b->prev;
3795e96a66cSDavid du Colombier b->prev = nil;
3805e96a66cSDavid du Colombier }
3815e96a66cSDavid du Colombier
3825e96a66cSDavid du Colombier
3833b86f2f8SDavid du Colombier if(0)fprint(2, "%s: dropping %d:%x:%V\n", argv0, b->part, b->addr, b->score);
3845e96a66cSDavid du Colombier /* set block to a reasonable state */
3855e96a66cSDavid du Colombier b->ref = 1;
3865e96a66cSDavid du Colombier b->part = PartError;
3875e96a66cSDavid du Colombier memset(&b->l, 0, sizeof(b->l));
3885e96a66cSDavid du Colombier b->iostate = BioEmpty;
3895e96a66cSDavid du Colombier
3905e96a66cSDavid du Colombier return b;
3915e96a66cSDavid du Colombier }
3925e96a66cSDavid du Colombier
3935e96a66cSDavid du Colombier /*
3945e96a66cSDavid du Colombier * look for a particular version of the block in the memory cache.
3955e96a66cSDavid du Colombier */
3965e96a66cSDavid du Colombier static Block *
_cacheLocalLookup(Cache * c,int part,u32int addr,u32int vers,int waitlock,int * lockfailure)3975e96a66cSDavid du Colombier _cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers,
3985e96a66cSDavid du Colombier int waitlock, int *lockfailure)
3995e96a66cSDavid du Colombier {
4005e96a66cSDavid du Colombier Block *b;
4015e96a66cSDavid du Colombier ulong h;
4025e96a66cSDavid du Colombier
4035e96a66cSDavid du Colombier h = addr % c->hashSize;
4045e96a66cSDavid du Colombier
4055e96a66cSDavid du Colombier if(lockfailure)
4065e96a66cSDavid du Colombier *lockfailure = 0;
4075e96a66cSDavid du Colombier
4085e96a66cSDavid du Colombier /*
4095e96a66cSDavid du Colombier * look for the block in the cache
4105e96a66cSDavid du Colombier */
411d7aba6c3SDavid du Colombier qlock(&c->lk);
4125e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){
4135e96a66cSDavid du Colombier if(b->part == part && b->addr == addr)
4145e96a66cSDavid du Colombier break;
4155e96a66cSDavid du Colombier }
4165e96a66cSDavid du Colombier if(b == nil || b->vers != vers){
417d7aba6c3SDavid du Colombier qunlock(&c->lk);
4185e96a66cSDavid du Colombier return nil;
4195e96a66cSDavid du Colombier }
420d7aba6c3SDavid du Colombier if(!waitlock && !canqlock(&b->lk)){
4215e96a66cSDavid du Colombier *lockfailure = 1;
422d7aba6c3SDavid du Colombier qunlock(&c->lk);
4235e96a66cSDavid du Colombier return nil;
4245e96a66cSDavid du Colombier }
4255e96a66cSDavid du Colombier heapDel(b);
4265e96a66cSDavid du Colombier b->ref++;
427d7aba6c3SDavid du Colombier qunlock(&c->lk);
4285e96a66cSDavid du Colombier
4295e96a66cSDavid du Colombier bwatchLock(b);
4305e96a66cSDavid du Colombier if(waitlock)
431d7aba6c3SDavid du Colombier qlock(&b->lk);
4325e96a66cSDavid du Colombier b->nlock = 1;
4335e96a66cSDavid du Colombier
4345e96a66cSDavid du Colombier for(;;){
4355e96a66cSDavid du Colombier switch(b->iostate){
4365e96a66cSDavid du Colombier default:
4375e96a66cSDavid du Colombier abort();
4385e96a66cSDavid du Colombier case BioEmpty:
4395e96a66cSDavid du Colombier case BioLabel:
4405e96a66cSDavid du Colombier case BioClean:
4415e96a66cSDavid du Colombier case BioDirty:
4425e96a66cSDavid du Colombier if(b->vers != vers){
4435e96a66cSDavid du Colombier blockPut(b);
4445e96a66cSDavid du Colombier return nil;
4455e96a66cSDavid du Colombier }
4465e96a66cSDavid du Colombier return b;
4475e96a66cSDavid du Colombier case BioReading:
4485e96a66cSDavid du Colombier case BioWriting:
449d7aba6c3SDavid du Colombier rsleep(&b->ioready);
4505e96a66cSDavid du Colombier break;
4515e96a66cSDavid du Colombier case BioVentiError:
45211e1fb05SDavid du Colombier blockPut(b);
453d7aba6c3SDavid du Colombier werrstr("venti i/o error block 0x%.8ux", addr);
45411e1fb05SDavid du Colombier return nil;
4555e96a66cSDavid du Colombier case BioReadError:
4565e96a66cSDavid du Colombier blockPut(b);
457d7aba6c3SDavid du Colombier werrstr("error reading block 0x%.8ux", addr);
4585e96a66cSDavid du Colombier return nil;
4595e96a66cSDavid du Colombier }
4605e96a66cSDavid du Colombier }
4615e96a66cSDavid du Colombier /* NOT REACHED */
4625e96a66cSDavid du Colombier }
4635e96a66cSDavid du Colombier static Block*
cacheLocalLookup(Cache * c,int part,u32int addr,u32int vers)4645e96a66cSDavid du Colombier cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers)
4655e96a66cSDavid du Colombier {
4662651f6bbSDavid du Colombier return _cacheLocalLookup(c, part, addr, vers, Waitlock, 0);
4675e96a66cSDavid du Colombier }
4685e96a66cSDavid du Colombier
4695e96a66cSDavid du Colombier
4705e96a66cSDavid du Colombier /*
4715e96a66cSDavid du Colombier * fetch a local (on-disk) block from the memory cache.
4725e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block.
4735e96a66cSDavid du Colombier */
4745e96a66cSDavid du Colombier Block *
_cacheLocal(Cache * c,int part,u32int addr,int mode,u32int epoch)4755e96a66cSDavid du Colombier _cacheLocal(Cache *c, int part, u32int addr, int mode, u32int epoch)
4765e96a66cSDavid du Colombier {
4775e96a66cSDavid du Colombier Block *b;
4785e96a66cSDavid du Colombier ulong h;
4795e96a66cSDavid du Colombier
4805e96a66cSDavid du Colombier assert(part != PartVenti);
4815e96a66cSDavid du Colombier
4825e96a66cSDavid du Colombier h = addr % c->hashSize;
4835e96a66cSDavid du Colombier
4845e96a66cSDavid du Colombier /*
4855e96a66cSDavid du Colombier * look for the block in the cache
4865e96a66cSDavid du Colombier */
487d7aba6c3SDavid du Colombier qlock(&c->lk);
4885e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){
4895e96a66cSDavid du Colombier if(b->part != part || b->addr != addr)
4905e96a66cSDavid du Colombier continue;
4915e96a66cSDavid du Colombier if(epoch && b->l.epoch != epoch){
4923b86f2f8SDavid du Colombier fprint(2, "%s: _cacheLocal want epoch %ud got %ud\n", argv0, epoch, b->l.epoch);
493d7aba6c3SDavid du Colombier qunlock(&c->lk);
494d7aba6c3SDavid du Colombier werrstr(ELabelMismatch);
4955e96a66cSDavid du Colombier return nil;
4965e96a66cSDavid du Colombier }
4975e96a66cSDavid du Colombier heapDel(b);
4985e96a66cSDavid du Colombier b->ref++;
4995e96a66cSDavid du Colombier break;
5005e96a66cSDavid du Colombier }
5015e96a66cSDavid du Colombier
5025e96a66cSDavid du Colombier if(b == nil){
5035e96a66cSDavid du Colombier b = cacheBumpBlock(c);
5045e96a66cSDavid du Colombier
5055e96a66cSDavid du Colombier b->part = part;
5065e96a66cSDavid du Colombier b->addr = addr;
5075e96a66cSDavid du Colombier localToGlobal(addr, b->score);
5085e96a66cSDavid du Colombier
5095e96a66cSDavid du Colombier /* chain onto correct hash */
5105e96a66cSDavid du Colombier b->next = c->heads[h];
5115e96a66cSDavid du Colombier c->heads[h] = b;
5125e96a66cSDavid du Colombier if(b->next != nil)
5135e96a66cSDavid du Colombier b->next->prev = &b->next;
5145e96a66cSDavid du Colombier b->prev = &c->heads[h];
5155e96a66cSDavid du Colombier }
5165e96a66cSDavid du Colombier
517d7aba6c3SDavid du Colombier qunlock(&c->lk);
5185e96a66cSDavid du Colombier
5195e96a66cSDavid du Colombier /*
5205e96a66cSDavid du Colombier * BUG: what if the epoch changes right here?
5215e96a66cSDavid du Colombier * In the worst case, we could end up in some weird
5225e96a66cSDavid du Colombier * lock loop, because the block we want no longer exists,
5235e96a66cSDavid du Colombier * and instead we're trying to lock a block we have no
5245e96a66cSDavid du Colombier * business grabbing.
5255e96a66cSDavid du Colombier *
5265e96a66cSDavid du Colombier * For now, I'm not going to worry about it.
5275e96a66cSDavid du Colombier */
5285e96a66cSDavid du Colombier
5293b86f2f8SDavid du Colombier if(0)fprint(2, "%s: cacheLocal: %d: %d %x\n", argv0, getpid(), b->part, b->addr);
5305e96a66cSDavid du Colombier bwatchLock(b);
531d7aba6c3SDavid du Colombier qlock(&b->lk);
5325e96a66cSDavid du Colombier b->nlock = 1;
5335e96a66cSDavid du Colombier
5345e96a66cSDavid du Colombier if(part == PartData && b->iostate == BioEmpty){
5355e96a66cSDavid du Colombier if(!readLabel(c, &b->l, addr)){
5365e96a66cSDavid du Colombier blockPut(b);
5375e96a66cSDavid du Colombier return nil;
5385e96a66cSDavid du Colombier }
5395e96a66cSDavid du Colombier blockSetIOState(b, BioLabel);
5405e96a66cSDavid du Colombier }
5415e96a66cSDavid du Colombier if(epoch && b->l.epoch != epoch){
5425e96a66cSDavid du Colombier blockPut(b);
5433b86f2f8SDavid du Colombier fprint(2, "%s: _cacheLocal want epoch %ud got %ud\n", argv0, epoch, b->l.epoch);
544d7aba6c3SDavid du Colombier werrstr(ELabelMismatch);
5455e96a66cSDavid du Colombier return nil;
5465e96a66cSDavid du Colombier }
5475e96a66cSDavid du Colombier
5485e96a66cSDavid du Colombier b->pc = getcallerpc(&c);
5495e96a66cSDavid du Colombier for(;;){
5505e96a66cSDavid du Colombier switch(b->iostate){
5515e96a66cSDavid du Colombier default:
5525e96a66cSDavid du Colombier abort();
5535e96a66cSDavid du Colombier case BioLabel:
554e12a9870SDavid du Colombier if(mode == OOverWrite)
555e12a9870SDavid du Colombier /*
556e12a9870SDavid du Colombier * leave iostate as BioLabel because data
557e12a9870SDavid du Colombier * hasn't been read.
558e12a9870SDavid du Colombier */
5595e96a66cSDavid du Colombier return b;
560e12a9870SDavid du Colombier /* fall through */
561e12a9870SDavid du Colombier case BioEmpty:
5625e96a66cSDavid du Colombier diskRead(c->disk, b);
563d7aba6c3SDavid du Colombier rsleep(&b->ioready);
5645e96a66cSDavid du Colombier break;
5655e96a66cSDavid du Colombier case BioClean:
5665e96a66cSDavid du Colombier case BioDirty:
5675e96a66cSDavid du Colombier return b;
5685e96a66cSDavid du Colombier case BioReading:
5695e96a66cSDavid du Colombier case BioWriting:
570d7aba6c3SDavid du Colombier rsleep(&b->ioready);
5715e96a66cSDavid du Colombier break;
5725e96a66cSDavid du Colombier case BioReadError:
5735e96a66cSDavid du Colombier blockSetIOState(b, BioEmpty);
5745e96a66cSDavid du Colombier blockPut(b);
575d7aba6c3SDavid du Colombier werrstr("error reading block 0x%.8ux", addr);
5765e96a66cSDavid du Colombier return nil;
5775e96a66cSDavid du Colombier }
5785e96a66cSDavid du Colombier }
5795e96a66cSDavid du Colombier /* NOT REACHED */
5805e96a66cSDavid du Colombier }
5815e96a66cSDavid du Colombier
5825e96a66cSDavid du Colombier Block *
cacheLocal(Cache * c,int part,u32int addr,int mode)5835e96a66cSDavid du Colombier cacheLocal(Cache *c, int part, u32int addr, int mode)
5845e96a66cSDavid du Colombier {
5855e96a66cSDavid du Colombier return _cacheLocal(c, part, addr, mode, 0);
5865e96a66cSDavid du Colombier }
5875e96a66cSDavid du Colombier
5885e96a66cSDavid du Colombier /*
5895e96a66cSDavid du Colombier * fetch a local (on-disk) block from the memory cache.
5905e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block.
5915e96a66cSDavid du Colombier * check tag and type.
5925e96a66cSDavid du Colombier */
5935e96a66cSDavid du Colombier Block *
cacheLocalData(Cache * c,u32int addr,int type,u32int tag,int mode,u32int epoch)5945e96a66cSDavid du Colombier cacheLocalData(Cache *c, u32int addr, int type, u32int tag, int mode, u32int epoch)
5955e96a66cSDavid du Colombier {
5965e96a66cSDavid du Colombier Block *b;
5975e96a66cSDavid du Colombier
5985e96a66cSDavid du Colombier b = _cacheLocal(c, PartData, addr, mode, epoch);
5995e96a66cSDavid du Colombier if(b == nil)
6005e96a66cSDavid du Colombier return nil;
6015e96a66cSDavid du Colombier if(b->l.type != type || b->l.tag != tag){
6023b86f2f8SDavid du Colombier fprint(2, "%s: cacheLocalData: addr=%d type got %d exp %d: tag got %ux exp %ux\n",
6033b86f2f8SDavid du Colombier argv0, addr, b->l.type, type, b->l.tag, tag);
604d7aba6c3SDavid du Colombier werrstr(ELabelMismatch);
6055e96a66cSDavid du Colombier blockPut(b);
6065e96a66cSDavid du Colombier return nil;
6075e96a66cSDavid du Colombier }
6085e96a66cSDavid du Colombier b->pc = getcallerpc(&c);
6095e96a66cSDavid du Colombier return b;
6105e96a66cSDavid du Colombier }
6115e96a66cSDavid du Colombier
6125e96a66cSDavid du Colombier /*
6135e96a66cSDavid du Colombier * fetch a global (Venti) block from the memory cache.
6145e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block.
6155e96a66cSDavid du Colombier * check tag and type if it's really a local block in disguise.
6165e96a66cSDavid du Colombier */
6175e96a66cSDavid du Colombier Block *
cacheGlobal(Cache * c,uchar score[VtScoreSize],int type,u32int tag,int mode)6185e96a66cSDavid du Colombier cacheGlobal(Cache *c, uchar score[VtScoreSize], int type, u32int tag, int mode)
6195e96a66cSDavid du Colombier {
6205e96a66cSDavid du Colombier int n;
6215e96a66cSDavid du Colombier Block *b;
6225e96a66cSDavid du Colombier ulong h;
6235e96a66cSDavid du Colombier u32int addr;
6245e96a66cSDavid du Colombier
6255e96a66cSDavid du Colombier addr = globalToLocal(score);
6265e96a66cSDavid du Colombier if(addr != NilBlock){
6275e96a66cSDavid du Colombier b = cacheLocalData(c, addr, type, tag, mode, 0);
6285e96a66cSDavid du Colombier if(b)
6295e96a66cSDavid du Colombier b->pc = getcallerpc(&c);
6305e96a66cSDavid du Colombier return b;
6315e96a66cSDavid du Colombier }
6325e96a66cSDavid du Colombier
6335e96a66cSDavid du Colombier h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->hashSize;
6345e96a66cSDavid du Colombier
6355e96a66cSDavid du Colombier /*
6365e96a66cSDavid du Colombier * look for the block in the cache
6375e96a66cSDavid du Colombier */
638d7aba6c3SDavid du Colombier qlock(&c->lk);
6395e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){
6405e96a66cSDavid du Colombier if(b->part != PartVenti || memcmp(b->score, score, VtScoreSize) != 0 || b->l.type != type)
6415e96a66cSDavid du Colombier continue;
6425e96a66cSDavid du Colombier heapDel(b);
6435e96a66cSDavid du Colombier b->ref++;
6445e96a66cSDavid du Colombier break;
6455e96a66cSDavid du Colombier }
6465e96a66cSDavid du Colombier
6475e96a66cSDavid du Colombier if(b == nil){
6483b86f2f8SDavid du Colombier if(0)fprint(2, "%s: cacheGlobal %V %d\n", argv0, score, type);
6495e96a66cSDavid du Colombier
6505e96a66cSDavid du Colombier b = cacheBumpBlock(c);
6515e96a66cSDavid du Colombier
6525e96a66cSDavid du Colombier b->part = PartVenti;
6535e96a66cSDavid du Colombier b->addr = NilBlock;
6545e96a66cSDavid du Colombier b->l.type = type;
6555e96a66cSDavid du Colombier memmove(b->score, score, VtScoreSize);
6565e96a66cSDavid du Colombier
6575e96a66cSDavid du Colombier /* chain onto correct hash */
6585e96a66cSDavid du Colombier b->next = c->heads[h];
6595e96a66cSDavid du Colombier c->heads[h] = b;
6605e96a66cSDavid du Colombier if(b->next != nil)
6615e96a66cSDavid du Colombier b->next->prev = &b->next;
6625e96a66cSDavid du Colombier b->prev = &c->heads[h];
6635e96a66cSDavid du Colombier }
664d7aba6c3SDavid du Colombier qunlock(&c->lk);
6655e96a66cSDavid du Colombier
6665e96a66cSDavid du Colombier bwatchLock(b);
667d7aba6c3SDavid du Colombier qlock(&b->lk);
6685e96a66cSDavid du Colombier b->nlock = 1;
6695e96a66cSDavid du Colombier b->pc = getcallerpc(&c);
6705e96a66cSDavid du Colombier
6715e96a66cSDavid du Colombier switch(b->iostate){
6725e96a66cSDavid du Colombier default:
6735e96a66cSDavid du Colombier abort();
6745e96a66cSDavid du Colombier case BioEmpty:
675d7aba6c3SDavid du Colombier n = vtread(c->z, score, vtType[type], b->data, c->size);
676d7aba6c3SDavid du Colombier if(n < 0 || vtsha1check(score, b->data, n) < 0){
6775e96a66cSDavid du Colombier blockSetIOState(b, BioVentiError);
6785e96a66cSDavid du Colombier blockPut(b);
679d7aba6c3SDavid du Colombier werrstr(
680223a0358SDavid du Colombier "venti error reading block %V or wrong score: %r",
681223a0358SDavid du Colombier score);
6825e96a66cSDavid du Colombier return nil;
6835e96a66cSDavid du Colombier }
684d7aba6c3SDavid du Colombier vtzeroextend(vtType[type], b->data, n, c->size);
6855e96a66cSDavid du Colombier blockSetIOState(b, BioClean);
6865e96a66cSDavid du Colombier return b;
6875e96a66cSDavid du Colombier case BioClean:
6885e96a66cSDavid du Colombier return b;
6895e96a66cSDavid du Colombier case BioVentiError:
69011e1fb05SDavid du Colombier blockPut(b);
691d7aba6c3SDavid du Colombier werrstr("venti i/o error or wrong score, block %V", score);
69211e1fb05SDavid du Colombier return nil;
6935e96a66cSDavid du Colombier case BioReadError:
6945e96a66cSDavid du Colombier blockPut(b);
695d7aba6c3SDavid du Colombier werrstr("error reading block %V", b->score);
6965e96a66cSDavid du Colombier return nil;
6975e96a66cSDavid du Colombier }
6985e96a66cSDavid du Colombier /* NOT REACHED */
6995e96a66cSDavid du Colombier }
7005e96a66cSDavid du Colombier
7015e96a66cSDavid du Colombier /*
7025e96a66cSDavid du Colombier * allocate a new on-disk block and load it into the memory cache.
7035e96a66cSDavid du Colombier * BUG: if the disk is full, should we flush some of it to Venti?
7045e96a66cSDavid du Colombier */
7055e96a66cSDavid du Colombier static u32int lastAlloc;
7065e96a66cSDavid du Colombier
7075e96a66cSDavid du Colombier Block *
cacheAllocBlock(Cache * c,int type,u32int tag,u32int epoch,u32int epochLow)7085e96a66cSDavid du Colombier cacheAllocBlock(Cache *c, int type, u32int tag, u32int epoch, u32int epochLow)
7095e96a66cSDavid du Colombier {
7105e96a66cSDavid du Colombier FreeList *fl;
7115e96a66cSDavid du Colombier u32int addr;
7125e96a66cSDavid du Colombier Block *b;
7135e96a66cSDavid du Colombier int n, nwrap;
7145e96a66cSDavid du Colombier Label lab;
7155e96a66cSDavid du Colombier
7165e96a66cSDavid du Colombier n = c->size / LabelSize;
7175e96a66cSDavid du Colombier fl = c->fl;
7185e96a66cSDavid du Colombier
719d7aba6c3SDavid du Colombier qlock(&fl->lk);
7205e96a66cSDavid du Colombier addr = fl->last;
721*f078af31SDavid du Colombier nwrap = 0;
722*f078af31SDavid du Colombier NotFound:
7235e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly);
7245e96a66cSDavid du Colombier if(b == nil){
725d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx %r\n", argv0);
726d7aba6c3SDavid du Colombier qunlock(&fl->lk);
7275e96a66cSDavid du Colombier return nil;
7285e96a66cSDavid du Colombier }
7295e96a66cSDavid du Colombier for(;;){
7305e96a66cSDavid du Colombier if(++addr >= fl->end){
7315e96a66cSDavid du Colombier addr = 0;
7325e96a66cSDavid du Colombier if(++nwrap >= 2){
7335e96a66cSDavid du Colombier blockPut(b);
734d7aba6c3SDavid du Colombier werrstr("disk is full");
735b3b810bfSDavid du Colombier /*
736b3b810bfSDavid du Colombier * try to avoid a continuous spew of console
737b3b810bfSDavid du Colombier * messages.
738b3b810bfSDavid du Colombier */
739b3b810bfSDavid du Colombier if (fl->last != 0)
740d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx1 %r\n",
741b3b810bfSDavid du Colombier argv0);
742b3b810bfSDavid du Colombier fl->last = 0;
743d7aba6c3SDavid du Colombier qunlock(&fl->lk);
7445e96a66cSDavid du Colombier return nil;
7455e96a66cSDavid du Colombier }
7465e96a66cSDavid du Colombier }
7475e96a66cSDavid du Colombier if(addr%n == 0){
7485e96a66cSDavid du Colombier blockPut(b);
7495e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly);
7505e96a66cSDavid du Colombier if(b == nil){
7515e96a66cSDavid du Colombier fl->last = addr;
752d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx2 %r\n", argv0);
753d7aba6c3SDavid du Colombier qunlock(&fl->lk);
7545e96a66cSDavid du Colombier return nil;
7555e96a66cSDavid du Colombier }
7565e96a66cSDavid du Colombier }
7575e96a66cSDavid du Colombier if(!labelUnpack(&lab, b->data, addr%n))
7585e96a66cSDavid du Colombier continue;
7595e96a66cSDavid du Colombier if(lab.state == BsFree)
7605e96a66cSDavid du Colombier goto Found;
761e569ccb5SDavid du Colombier if(lab.state&BsClosed)
762e569ccb5SDavid du Colombier if(lab.epochClose <= epochLow || lab.epoch==lab.epochClose)
7635e96a66cSDavid du Colombier goto Found;
7645e96a66cSDavid du Colombier }
7655e96a66cSDavid du Colombier Found:
7665e96a66cSDavid du Colombier blockPut(b);
7675e96a66cSDavid du Colombier b = cacheLocal(c, PartData, addr, OOverWrite);
7685e96a66cSDavid du Colombier if(b == nil){
769d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx3 %r\n", argv0);
7705e96a66cSDavid du Colombier return nil;
7715e96a66cSDavid du Colombier }
772*f078af31SDavid du Colombier if(!(b->iostate == BioLabel || b->iostate == BioClean)){
773*f078af31SDavid du Colombier if(0)fprint(2, "%s: cacheAllocBlock addr %ud iostate %s label %L\n",
774*f078af31SDavid du Colombier argv0, addr, bioStr(b->iostate), &lab);
775*f078af31SDavid du Colombier blockPut(b);
776*f078af31SDavid du Colombier goto NotFound;
777*f078af31SDavid du Colombier }
7785e96a66cSDavid du Colombier fl->last = addr;
7795e96a66cSDavid du Colombier lab.type = type;
7805e96a66cSDavid du Colombier lab.tag = tag;
7815e96a66cSDavid du Colombier lab.state = BsAlloc;
7825e96a66cSDavid du Colombier lab.epoch = epoch;
7835e96a66cSDavid du Colombier lab.epochClose = ~(u32int)0;
784e569ccb5SDavid du Colombier if(!blockSetLabel(b, &lab, 1)){
785d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx4 %r\n", argv0);
7865e96a66cSDavid du Colombier blockPut(b);
7875e96a66cSDavid du Colombier return nil;
7885e96a66cSDavid du Colombier }
789d7aba6c3SDavid du Colombier vtzeroextend(vtType[type], b->data, 0, c->size);
7905e96a66cSDavid du Colombier if(0)diskWrite(c->disk, b);
7915e96a66cSDavid du Colombier
7923b86f2f8SDavid du Colombier if(0)fprint(2, "%s: fsAlloc %ud type=%d tag = %ux\n", argv0, addr, type, tag);
7935e96a66cSDavid du Colombier lastAlloc = addr;
7947abd426fSDavid du Colombier fl->nused++;
795d7aba6c3SDavid du Colombier qunlock(&fl->lk);
7965e96a66cSDavid du Colombier b->pc = getcallerpc(&c);
7975e96a66cSDavid du Colombier return b;
7985e96a66cSDavid du Colombier }
7995e96a66cSDavid du Colombier
8000b9a5132SDavid du Colombier int
cacheDirty(Cache * c)8010b9a5132SDavid du Colombier cacheDirty(Cache *c)
8020b9a5132SDavid du Colombier {
8030b9a5132SDavid du Colombier return c->ndirty;
8040b9a5132SDavid du Colombier }
8050b9a5132SDavid du Colombier
8067abd426fSDavid du Colombier void
cacheCountUsed(Cache * c,u32int epochLow,u32int * used,u32int * total,u32int * bsize)8077abd426fSDavid du Colombier cacheCountUsed(Cache *c, u32int epochLow, u32int *used, u32int *total, u32int *bsize)
8087abd426fSDavid du Colombier {
8097abd426fSDavid du Colombier int n;
8107abd426fSDavid du Colombier u32int addr, nused;
8117abd426fSDavid du Colombier Block *b;
8127abd426fSDavid du Colombier Label lab;
8137abd426fSDavid du Colombier FreeList *fl;
8147abd426fSDavid du Colombier
8157abd426fSDavid du Colombier fl = c->fl;
8167abd426fSDavid du Colombier n = c->size / LabelSize;
8177abd426fSDavid du Colombier *bsize = c->size;
818d7aba6c3SDavid du Colombier qlock(&fl->lk);
8197abd426fSDavid du Colombier if(fl->epochLow == epochLow){
8207abd426fSDavid du Colombier *used = fl->nused;
8217abd426fSDavid du Colombier *total = fl->end;
822d7aba6c3SDavid du Colombier qunlock(&fl->lk);
8237abd426fSDavid du Colombier return;
8247abd426fSDavid du Colombier }
8257abd426fSDavid du Colombier b = nil;
8267abd426fSDavid du Colombier nused = 0;
8277abd426fSDavid du Colombier for(addr=0; addr<fl->end; addr++){
8287abd426fSDavid du Colombier if(addr%n == 0){
8297abd426fSDavid du Colombier blockPut(b);
8307abd426fSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly);
8317abd426fSDavid du Colombier if(b == nil){
832d7aba6c3SDavid du Colombier fprint(2, "%s: flCountUsed: loading %ux: %r\n",
8333b86f2f8SDavid du Colombier argv0, addr/n);
8347abd426fSDavid du Colombier break;
8357abd426fSDavid du Colombier }
8367abd426fSDavid du Colombier }
8377abd426fSDavid du Colombier if(!labelUnpack(&lab, b->data, addr%n))
8387abd426fSDavid du Colombier continue;
8397abd426fSDavid du Colombier if(lab.state == BsFree)
8407abd426fSDavid du Colombier continue;
841e569ccb5SDavid du Colombier if(lab.state&BsClosed)
842e569ccb5SDavid du Colombier if(lab.epochClose <= epochLow || lab.epoch==lab.epochClose)
8437abd426fSDavid du Colombier continue;
8447abd426fSDavid du Colombier nused++;
8457abd426fSDavid du Colombier }
8467abd426fSDavid du Colombier blockPut(b);
8477abd426fSDavid du Colombier if(addr == fl->end){
8487abd426fSDavid du Colombier fl->nused = nused;
8497abd426fSDavid du Colombier fl->epochLow = epochLow;
8507abd426fSDavid du Colombier }
8517abd426fSDavid du Colombier *used = nused;
8527abd426fSDavid du Colombier *total = fl->end;
853d7aba6c3SDavid du Colombier qunlock(&fl->lk);
8547abd426fSDavid du Colombier return;
8557abd426fSDavid du Colombier }
8567abd426fSDavid du Colombier
8575e96a66cSDavid du Colombier static FreeList *
flAlloc(u32int end)8585e96a66cSDavid du Colombier flAlloc(u32int end)
8595e96a66cSDavid du Colombier {
8605e96a66cSDavid du Colombier FreeList *fl;
8615e96a66cSDavid du Colombier
862d7aba6c3SDavid du Colombier fl = vtmallocz(sizeof(*fl));
863b5e190f4SDavid du Colombier fl->last = 0;
8645e96a66cSDavid du Colombier fl->end = end;
8655e96a66cSDavid du Colombier return fl;
8665e96a66cSDavid du Colombier }
8675e96a66cSDavid du Colombier
8685e96a66cSDavid du Colombier static void
flFree(FreeList * fl)8695e96a66cSDavid du Colombier flFree(FreeList *fl)
8705e96a66cSDavid du Colombier {
871d7aba6c3SDavid du Colombier vtfree(fl);
8725e96a66cSDavid du Colombier }
8735e96a66cSDavid du Colombier
8745e96a66cSDavid du Colombier u32int
cacheLocalSize(Cache * c,int part)8755e96a66cSDavid du Colombier cacheLocalSize(Cache *c, int part)
8765e96a66cSDavid du Colombier {
8775e96a66cSDavid du Colombier return diskSize(c->disk, part);
8785e96a66cSDavid du Colombier }
8795e96a66cSDavid du Colombier
8805e96a66cSDavid du Colombier /*
8815e96a66cSDavid du Colombier * The thread that has locked b may refer to it by
8825e96a66cSDavid du Colombier * multiple names. Nlock counts the number of
8835e96a66cSDavid du Colombier * references the locking thread holds. It will call
8845e96a66cSDavid du Colombier * blockPut once per reference.
8855e96a66cSDavid du Colombier */
8865e96a66cSDavid du Colombier void
blockDupLock(Block * b)8875e96a66cSDavid du Colombier blockDupLock(Block *b)
8885e96a66cSDavid du Colombier {
8895e96a66cSDavid du Colombier assert(b->nlock > 0);
8905e96a66cSDavid du Colombier b->nlock++;
8915e96a66cSDavid du Colombier }
8925e96a66cSDavid du Colombier
8935e96a66cSDavid du Colombier /*
8945e96a66cSDavid du Colombier * we're done with the block.
8955e96a66cSDavid du Colombier * unlock it. can't use it after calling this.
8965e96a66cSDavid du Colombier */
8975e96a66cSDavid du Colombier void
blockPut(Block * b)8985e96a66cSDavid du Colombier blockPut(Block* b)
8995e96a66cSDavid du Colombier {
9005e96a66cSDavid du Colombier Cache *c;
9015e96a66cSDavid du Colombier
9025e96a66cSDavid du Colombier if(b == nil)
9035e96a66cSDavid du Colombier return;
9045e96a66cSDavid du Colombier
9053b86f2f8SDavid du Colombier if(0)fprint(2, "%s: blockPut: %d: %d %x %d %s\n", argv0, getpid(), b->part, b->addr, c->nheap, bioStr(b->iostate));
9065e96a66cSDavid du Colombier
9075e96a66cSDavid du Colombier if(b->iostate == BioDirty)
9085e96a66cSDavid du Colombier bwatchDependency(b);
9095e96a66cSDavid du Colombier
9105e96a66cSDavid du Colombier if(--b->nlock > 0)
9115e96a66cSDavid du Colombier return;
9125e96a66cSDavid du Colombier
9135e96a66cSDavid du Colombier /*
9145e96a66cSDavid du Colombier * b->nlock should probably stay at zero while
915d7aba6c3SDavid du Colombier * the block is unlocked, but diskThread and rsleep
916d7aba6c3SDavid du Colombier * conspire to assume that they can just qlock(&b->lk); blockPut(b),
9175e96a66cSDavid du Colombier * so we have to keep b->nlock set to 1 even
9185e96a66cSDavid du Colombier * when the block is unlocked.
9195e96a66cSDavid du Colombier */
9205e96a66cSDavid du Colombier assert(b->nlock == 0);
9215e96a66cSDavid du Colombier b->nlock = 1;
9225e96a66cSDavid du Colombier // b->pc = 0;
9235e96a66cSDavid du Colombier
9245e96a66cSDavid du Colombier bwatchUnlock(b);
925d7aba6c3SDavid du Colombier qunlock(&b->lk);
9265e96a66cSDavid du Colombier c = b->c;
927d7aba6c3SDavid du Colombier qlock(&c->lk);
9285e96a66cSDavid du Colombier
9295e96a66cSDavid du Colombier if(--b->ref > 0){
930d7aba6c3SDavid du Colombier qunlock(&c->lk);
9315e96a66cSDavid du Colombier return;
9325e96a66cSDavid du Colombier }
9335e96a66cSDavid du Colombier
9345e96a66cSDavid du Colombier assert(b->ref == 0);
9355e96a66cSDavid du Colombier switch(b->iostate){
9365e96a66cSDavid du Colombier default:
9375e96a66cSDavid du Colombier b->used = c->now++;
9385e96a66cSDavid du Colombier heapIns(b);
9395e96a66cSDavid du Colombier break;
9405e96a66cSDavid du Colombier case BioEmpty:
9415e96a66cSDavid du Colombier case BioLabel:
9425e96a66cSDavid du Colombier if(c->nheap == 0)
9435e96a66cSDavid du Colombier b->used = c->now++;
9445e96a66cSDavid du Colombier else
9455e96a66cSDavid du Colombier b->used = c->heap[0]->used;
9465e96a66cSDavid du Colombier heapIns(b);
9475e96a66cSDavid du Colombier break;
9485e96a66cSDavid du Colombier case BioDirty:
9495e96a66cSDavid du Colombier break;
9505e96a66cSDavid du Colombier }
951d7aba6c3SDavid du Colombier qunlock(&c->lk);
9525e96a66cSDavid du Colombier }
9535e96a66cSDavid du Colombier
9545e96a66cSDavid du Colombier /*
955e569ccb5SDavid du Colombier * set the label associated with a block.
9565e96a66cSDavid du Colombier */
957e569ccb5SDavid du Colombier Block*
_blockSetLabel(Block * b,Label * l)958e569ccb5SDavid du Colombier _blockSetLabel(Block *b, Label *l)
9595e96a66cSDavid du Colombier {
960e569ccb5SDavid du Colombier int lpb;
9615e96a66cSDavid du Colombier Block *bb;
9625e96a66cSDavid du Colombier u32int a;
9635e96a66cSDavid du Colombier Cache *c;
9645e96a66cSDavid du Colombier
9655e96a66cSDavid du Colombier c = b->c;
9665e96a66cSDavid du Colombier
967e569ccb5SDavid du Colombier assert(b->part == PartData);
968e569ccb5SDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean || b->iostate == BioDirty);
969e569ccb5SDavid du Colombier lpb = c->size / LabelSize;
970e569ccb5SDavid du Colombier a = b->addr / lpb;
971e569ccb5SDavid du Colombier bb = cacheLocal(c, PartLabel, a, OReadWrite);
972e569ccb5SDavid du Colombier if(bb == nil){
973e569ccb5SDavid du Colombier blockPut(b);
9745e96a66cSDavid du Colombier return nil;
9755e96a66cSDavid du Colombier }
976e569ccb5SDavid du Colombier b->l = *l;
977e569ccb5SDavid du Colombier labelPack(l, bb->data, b->addr%lpb);
978e569ccb5SDavid du Colombier blockDirty(bb);
979e569ccb5SDavid du Colombier return bb;
980e569ccb5SDavid du Colombier }
981e569ccb5SDavid du Colombier
982e569ccb5SDavid du Colombier int
blockSetLabel(Block * b,Label * l,int allocating)983e569ccb5SDavid du Colombier blockSetLabel(Block *b, Label *l, int allocating)
984e569ccb5SDavid du Colombier {
985e569ccb5SDavid du Colombier Block *lb;
986e569ccb5SDavid du Colombier Label oldl;
987e569ccb5SDavid du Colombier
988e569ccb5SDavid du Colombier oldl = b->l;
989e569ccb5SDavid du Colombier lb = _blockSetLabel(b, l);
990e569ccb5SDavid du Colombier if(lb == nil)
991e569ccb5SDavid du Colombier return 0;
9925e96a66cSDavid du Colombier
9935e96a66cSDavid du Colombier /*
994e569ccb5SDavid du Colombier * If we're allocating the block, make sure the label (bl)
995e569ccb5SDavid du Colombier * goes to disk before the data block (b) itself. This is to help
996e569ccb5SDavid du Colombier * the blocks that in turn depend on b.
997e569ccb5SDavid du Colombier *
998e569ccb5SDavid du Colombier * Suppose bx depends on (must be written out after) b.
999e569ccb5SDavid du Colombier * Once we write b we'll think it's safe to write bx.
1000e569ccb5SDavid du Colombier * Bx can't get at b unless it has a valid label, though.
1001e569ccb5SDavid du Colombier *
1002e569ccb5SDavid du Colombier * Allocation is the only case in which having a current label
1003e569ccb5SDavid du Colombier * is vital because:
1004e569ccb5SDavid du Colombier *
1005e569ccb5SDavid du Colombier * - l.type is set at allocation and never changes.
1006e569ccb5SDavid du Colombier * - l.tag is set at allocation and never changes.
1007e569ccb5SDavid du Colombier * - l.state is not checked when we load blocks.
1008e569ccb5SDavid du Colombier * - the archiver cares deeply about l.state being
1009e569ccb5SDavid du Colombier * BaActive vs. BaCopied, but that's handled
1010e569ccb5SDavid du Colombier * by direct calls to _blockSetLabel.
10115e96a66cSDavid du Colombier */
10125e96a66cSDavid du Colombier
1013e569ccb5SDavid du Colombier if(allocating)
1014e569ccb5SDavid du Colombier blockDependency(b, lb, -1, nil, nil);
1015e569ccb5SDavid du Colombier blockPut(lb);
1016e569ccb5SDavid du Colombier return 1;
10175e96a66cSDavid du Colombier }
10185e96a66cSDavid du Colombier
10195e96a66cSDavid du Colombier /*
102061201b97SDavid du Colombier * Record that bb must be written out before b.
102161201b97SDavid du Colombier * If index is given, we're about to overwrite the score/e
102261201b97SDavid du Colombier * at that index in the block. Save the old value so we
10235e96a66cSDavid du Colombier * can write a safer ``old'' version of the block if pressed.
10245e96a66cSDavid du Colombier */
10255e96a66cSDavid du Colombier void
blockDependency(Block * b,Block * bb,int index,uchar * score,Entry * e)102661201b97SDavid du Colombier blockDependency(Block *b, Block *bb, int index, uchar *score, Entry *e)
10275e96a66cSDavid du Colombier {
10285e96a66cSDavid du Colombier BList *p;
10295e96a66cSDavid du Colombier
10305e96a66cSDavid du Colombier if(bb->iostate == BioClean)
10315e96a66cSDavid du Colombier return;
10325e96a66cSDavid du Colombier
103361201b97SDavid du Colombier /*
103461201b97SDavid du Colombier * Dependencies for blocks containing Entry structures
103561201b97SDavid du Colombier * or scores must always be explained. The problem with
103661201b97SDavid du Colombier * only explaining some of them is this. Suppose we have two
103761201b97SDavid du Colombier * dependencies for the same field, the first explained
103861201b97SDavid du Colombier * and the second not. We try to write the block when the first
103961201b97SDavid du Colombier * dependency is not written but the second is. We will roll back
104061201b97SDavid du Colombier * the first change even though the second trumps it.
104161201b97SDavid du Colombier */
104261201b97SDavid du Colombier if(index == -1 && bb->part == PartData)
104361201b97SDavid du Colombier assert(b->l.type == BtData);
104461201b97SDavid du Colombier
10457abd426fSDavid du Colombier if(bb->iostate != BioDirty){
10463b86f2f8SDavid du Colombier fprint(2, "%s: %d:%x:%d iostate is %d in blockDependency\n",
10473b86f2f8SDavid du Colombier argv0, bb->part, bb->addr, bb->l.type, bb->iostate);
10487abd426fSDavid du Colombier abort();
10497abd426fSDavid du Colombier }
10505e96a66cSDavid du Colombier
10515e96a66cSDavid du Colombier p = blistAlloc(bb);
10525e96a66cSDavid du Colombier if(p == nil)
10535e96a66cSDavid du Colombier return;
10545e96a66cSDavid du Colombier
10557f1bc48aSDavid du Colombier assert(bb->iostate == BioDirty);
10563b86f2f8SDavid du Colombier if(0)fprint(2, "%s: %d:%x:%d depends on %d:%x:%d\n", argv0, b->part, b->addr, b->l.type, bb->part, bb->addr, bb->l.type);
10575e96a66cSDavid du Colombier
10585e96a66cSDavid du Colombier p->part = bb->part;
10595e96a66cSDavid du Colombier p->addr = bb->addr;
10605e96a66cSDavid du Colombier p->type = bb->l.type;
10615e96a66cSDavid du Colombier p->vers = bb->vers;
10625e96a66cSDavid du Colombier p->index = index;
106361201b97SDavid du Colombier if(p->index >= 0){
106461201b97SDavid du Colombier /*
106561201b97SDavid du Colombier * This test would just be b->l.type==BtDir except
106661201b97SDavid du Colombier * we need to exclude the super block.
106761201b97SDavid du Colombier */
106861201b97SDavid du Colombier if(b->l.type == BtDir && b->part == PartData)
106961201b97SDavid du Colombier entryPack(e, p->old.entry, 0);
107061201b97SDavid du Colombier else
107161201b97SDavid du Colombier memmove(p->old.score, score, VtScoreSize);
107261201b97SDavid du Colombier }
10735e96a66cSDavid du Colombier p->next = b->prior;
10745e96a66cSDavid du Colombier b->prior = p;
10755e96a66cSDavid du Colombier }
10765e96a66cSDavid du Colombier
10775e96a66cSDavid du Colombier /*
10785e96a66cSDavid du Colombier * Mark an in-memory block as dirty. If there are too many
1079fe853e23SDavid du Colombier * dirty blocks, start writing some out to disk.
10805e96a66cSDavid du Colombier *
1081fe853e23SDavid du Colombier * If there were way too many dirty blocks, we used to
1082fe853e23SDavid du Colombier * try to do some flushing ourselves, but it's just too dangerous --
1083fe853e23SDavid du Colombier * it implies that the callers cannot have any of our priors locked,
1084fe853e23SDavid du Colombier * but this is hard to avoid in some cases.
10855e96a66cSDavid du Colombier */
10865e96a66cSDavid du Colombier int
blockDirty(Block * b)10875e96a66cSDavid du Colombier blockDirty(Block *b)
10885e96a66cSDavid du Colombier {
10895e96a66cSDavid du Colombier Cache *c;
10905e96a66cSDavid du Colombier
10915e96a66cSDavid du Colombier c = b->c;
10925e96a66cSDavid du Colombier
10935e96a66cSDavid du Colombier assert(b->part != PartVenti);
10945e96a66cSDavid du Colombier
10955e96a66cSDavid du Colombier if(b->iostate == BioDirty)
10965e96a66cSDavid du Colombier return 1;
1097e12a9870SDavid du Colombier assert(b->iostate == BioClean || b->iostate == BioLabel);
10985e96a66cSDavid du Colombier
1099d7aba6c3SDavid du Colombier qlock(&c->lk);
110061201b97SDavid du Colombier b->iostate = BioDirty;
11015e96a66cSDavid du Colombier c->ndirty++;
11025e96a66cSDavid du Colombier if(c->ndirty > (c->maxdirty>>1))
1103d7aba6c3SDavid du Colombier rwakeup(&c->flush);
1104d7aba6c3SDavid du Colombier qunlock(&c->lk);
11055e96a66cSDavid du Colombier
11065e96a66cSDavid du Colombier return 1;
11075e96a66cSDavid du Colombier }
11085e96a66cSDavid du Colombier
11095e96a66cSDavid du Colombier /*
111061201b97SDavid du Colombier * We've decided to write out b. Maybe b has some pointers to blocks
111161201b97SDavid du Colombier * that haven't yet been written to disk. If so, construct a slightly out-of-date
111261201b97SDavid du Colombier * copy of b that is safe to write out. (diskThread will make sure the block
11135e96a66cSDavid du Colombier * remains marked as dirty.)
11145e96a66cSDavid du Colombier */
11155e96a66cSDavid du Colombier uchar *
blockRollback(Block * b,uchar * buf)11165e96a66cSDavid du Colombier blockRollback(Block *b, uchar *buf)
11175e96a66cSDavid du Colombier {
11185e96a66cSDavid du Colombier u32int addr;
11195e96a66cSDavid du Colombier BList *p;
11205e96a66cSDavid du Colombier Super super;
11215e96a66cSDavid du Colombier
11225e96a66cSDavid du Colombier /* easy case */
11235e96a66cSDavid du Colombier if(b->prior == nil)
11245e96a66cSDavid du Colombier return b->data;
11255e96a66cSDavid du Colombier
11265e96a66cSDavid du Colombier memmove(buf, b->data, b->c->size);
11275e96a66cSDavid du Colombier for(p=b->prior; p; p=p->next){
11285e96a66cSDavid du Colombier /*
11295e96a66cSDavid du Colombier * we know p->index >= 0 because blockWrite has vetted this block for us.
11305e96a66cSDavid du Colombier */
11315e96a66cSDavid du Colombier assert(p->index >= 0);
11325e96a66cSDavid du Colombier assert(b->part == PartSuper || (b->part == PartData && b->l.type != BtData));
11335e96a66cSDavid du Colombier if(b->part == PartSuper){
11345e96a66cSDavid du Colombier assert(p->index == 0);
11355e96a66cSDavid du Colombier superUnpack(&super, buf);
113661201b97SDavid du Colombier addr = globalToLocal(p->old.score);
11375e96a66cSDavid du Colombier if(addr == NilBlock){
11383b86f2f8SDavid du Colombier fprint(2, "%s: rolling back super block: "
11393b86f2f8SDavid du Colombier "bad replacement addr %V\n",
11403b86f2f8SDavid du Colombier argv0, p->old.score);
11415e96a66cSDavid du Colombier abort();
11425e96a66cSDavid du Colombier }
11435e96a66cSDavid du Colombier super.active = addr;
11445e96a66cSDavid du Colombier superPack(&super, buf);
11455e96a66cSDavid du Colombier continue;
11465e96a66cSDavid du Colombier }
114761201b97SDavid du Colombier if(b->l.type == BtDir)
114861201b97SDavid du Colombier memmove(buf+p->index*VtEntrySize, p->old.entry, VtEntrySize);
114961201b97SDavid du Colombier else
115061201b97SDavid du Colombier memmove(buf+p->index*VtScoreSize, p->old.score, VtScoreSize);
11515e96a66cSDavid du Colombier }
11525e96a66cSDavid du Colombier return buf;
11535e96a66cSDavid du Colombier }
11545e96a66cSDavid du Colombier
11555e96a66cSDavid du Colombier /*
11565e96a66cSDavid du Colombier * Try to write block b.
11575e96a66cSDavid du Colombier * If b depends on other blocks:
11585e96a66cSDavid du Colombier *
11595e96a66cSDavid du Colombier * If the block has been written out, remove the dependency.
11607abd426fSDavid du Colombier * If the dependency is replaced by a more recent dependency,
11617abd426fSDavid du Colombier * throw it out.
11625e96a66cSDavid du Colombier * If we know how to write out an old version of b that doesn't
11635e96a66cSDavid du Colombier * depend on it, do that.
11645e96a66cSDavid du Colombier *
11655e96a66cSDavid du Colombier * Otherwise, bail.
11665e96a66cSDavid du Colombier */
11675e96a66cSDavid du Colombier int
blockWrite(Block * b,int waitlock)1168e12a9870SDavid du Colombier blockWrite(Block *b, int waitlock)
11695e96a66cSDavid du Colombier {
117061201b97SDavid du Colombier uchar *dmap;
11715e96a66cSDavid du Colombier Cache *c;
11725e96a66cSDavid du Colombier BList *p, **pp;
11735e96a66cSDavid du Colombier Block *bb;
11745e96a66cSDavid du Colombier int lockfail;
11755e96a66cSDavid du Colombier
11765e96a66cSDavid du Colombier c = b->c;
11775e96a66cSDavid du Colombier
11785e96a66cSDavid du Colombier if(b->iostate != BioDirty)
11795e96a66cSDavid du Colombier return 1;
11805e96a66cSDavid du Colombier
118161201b97SDavid du Colombier dmap = b->dmap;
118261201b97SDavid du Colombier memset(dmap, 0, c->ndmap);
11835e96a66cSDavid du Colombier pp = &b->prior;
11845e96a66cSDavid du Colombier for(p=*pp; p; p=*pp){
118561201b97SDavid du Colombier if(p->index >= 0){
118661201b97SDavid du Colombier /* more recent dependency has succeeded; this one can go */
118761201b97SDavid du Colombier if(dmap[p->index/8] & (1<<(p->index%8)))
118861201b97SDavid du Colombier goto ignblock;
118961201b97SDavid du Colombier }
119061201b97SDavid du Colombier
119161201b97SDavid du Colombier lockfail = 0;
1192e12a9870SDavid du Colombier bb = _cacheLocalLookup(c, p->part, p->addr, p->vers, waitlock,
11932651f6bbSDavid du Colombier &lockfail);
11945e96a66cSDavid du Colombier if(bb == nil){
11955e96a66cSDavid du Colombier if(lockfail)
11965e96a66cSDavid du Colombier return 0;
119761201b97SDavid du Colombier /* block not in cache => was written already */
119861201b97SDavid du Colombier dmap[p->index/8] |= 1<<(p->index%8);
119961201b97SDavid du Colombier goto ignblock;
12005e96a66cSDavid du Colombier }
12015e96a66cSDavid du Colombier
12025e96a66cSDavid du Colombier /*
12035e96a66cSDavid du Colombier * same version of block is still in cache.
12045e96a66cSDavid du Colombier *
12055e96a66cSDavid du Colombier * the assertion is true because the block still has version p->vers,
12065e96a66cSDavid du Colombier * which means it hasn't been written out since we last saw it.
12075e96a66cSDavid du Colombier */
12087abd426fSDavid du Colombier if(bb->iostate != BioDirty){
12093b86f2f8SDavid du Colombier fprint(2, "%s: %d:%x:%d iostate is %d in blockWrite\n",
12103b86f2f8SDavid du Colombier argv0, bb->part, bb->addr, bb->l.type, bb->iostate);
12117abd426fSDavid du Colombier /* probably BioWriting if it happens? */
12127f1bc48aSDavid du Colombier if(bb->iostate == BioClean)
12137f1bc48aSDavid du Colombier goto ignblock;
12147abd426fSDavid du Colombier }
12157abd426fSDavid du Colombier
12165e96a66cSDavid du Colombier blockPut(bb);
12175e96a66cSDavid du Colombier
12185e96a66cSDavid du Colombier if(p->index < 0){
12195e96a66cSDavid du Colombier /*
12205e96a66cSDavid du Colombier * We don't know how to temporarily undo
12215e96a66cSDavid du Colombier * b's dependency on bb, so just don't write b yet.
12225e96a66cSDavid du Colombier */
12233b86f2f8SDavid du Colombier if(0) fprint(2, "%s: blockWrite skipping %d %x %d %d; need to write %d %x %d\n",
12243b86f2f8SDavid du Colombier argv0, b->part, b->addr, b->vers, b->l.type, p->part, p->addr, bb->vers);
12255e96a66cSDavid du Colombier return 0;
12265e96a66cSDavid du Colombier }
12275e96a66cSDavid du Colombier /* keep walking down the list */
12285e96a66cSDavid du Colombier pp = &p->next;
122961201b97SDavid du Colombier continue;
123061201b97SDavid du Colombier
123161201b97SDavid du Colombier ignblock:
123261201b97SDavid du Colombier *pp = p->next;
123361201b97SDavid du Colombier blistFree(c, p);
123461201b97SDavid du Colombier continue;
12355e96a66cSDavid du Colombier }
12365e96a66cSDavid du Colombier
1237867bfcc6SDavid du Colombier /*
1238867bfcc6SDavid du Colombier * DiskWrite must never be called with a double-locked block.
1239867bfcc6SDavid du Colombier * This call to diskWrite is okay because blockWrite is only called
1240867bfcc6SDavid du Colombier * from the cache flush thread, which never double-locks a block.
1241867bfcc6SDavid du Colombier */
12425e96a66cSDavid du Colombier diskWrite(c->disk, b);
12435e96a66cSDavid du Colombier return 1;
12445e96a66cSDavid du Colombier }
12455e96a66cSDavid du Colombier
12465e96a66cSDavid du Colombier /*
12475e96a66cSDavid du Colombier * Change the I/O state of block b.
12485e96a66cSDavid du Colombier * Just an assignment except for magic in
12495e96a66cSDavid du Colombier * switch statement (read comments there).
12505e96a66cSDavid du Colombier */
12515e96a66cSDavid du Colombier void
blockSetIOState(Block * b,int iostate)12525e96a66cSDavid du Colombier blockSetIOState(Block *b, int iostate)
12535e96a66cSDavid du Colombier {
12545e96a66cSDavid du Colombier int dowakeup;
12555e96a66cSDavid du Colombier Cache *c;
12565e96a66cSDavid du Colombier BList *p, *q;
12575e96a66cSDavid du Colombier
12583b86f2f8SDavid du Colombier if(0) fprint(2, "%s: iostate part=%d addr=%x %s->%s\n", argv0, b->part, b->addr, bioStr(b->iostate), bioStr(iostate));
12595e96a66cSDavid du Colombier
12605e96a66cSDavid du Colombier c = b->c;
12615e96a66cSDavid du Colombier
12625e96a66cSDavid du Colombier dowakeup = 0;
12635e96a66cSDavid du Colombier switch(iostate){
12645e96a66cSDavid du Colombier default:
12655e96a66cSDavid du Colombier abort();
12665e96a66cSDavid du Colombier case BioEmpty:
12675e96a66cSDavid du Colombier assert(!b->uhead);
12685e96a66cSDavid du Colombier break;
12695e96a66cSDavid du Colombier case BioLabel:
12705e96a66cSDavid du Colombier assert(!b->uhead);
12715e96a66cSDavid du Colombier break;
12725e96a66cSDavid du Colombier case BioClean:
12735e96a66cSDavid du Colombier bwatchDependency(b);
12745e96a66cSDavid du Colombier /*
12755e96a66cSDavid du Colombier * If b->prior is set, it means a write just finished.
12765e96a66cSDavid du Colombier * The prior list isn't needed anymore.
12775e96a66cSDavid du Colombier */
12785e96a66cSDavid du Colombier for(p=b->prior; p; p=q){
12795e96a66cSDavid du Colombier q = p->next;
12805e96a66cSDavid du Colombier blistFree(c, p);
12815e96a66cSDavid du Colombier }
12825e96a66cSDavid du Colombier b->prior = nil;
12835e96a66cSDavid du Colombier /*
12845e96a66cSDavid du Colombier * Freeing a block or just finished a write.
12855e96a66cSDavid du Colombier * Move the blocks from the per-block unlink
12865e96a66cSDavid du Colombier * queue to the cache unlink queue.
12875e96a66cSDavid du Colombier */
12885e96a66cSDavid du Colombier if(b->iostate == BioDirty || b->iostate == BioWriting){
1289d7aba6c3SDavid du Colombier qlock(&c->lk);
12905e96a66cSDavid du Colombier c->ndirty--;
129161201b97SDavid du Colombier b->iostate = iostate; /* change here to keep in sync with ndirty */
12925e96a66cSDavid du Colombier b->vers = c->vers++;
12935e96a66cSDavid du Colombier if(b->uhead){
12945e96a66cSDavid du Colombier /* add unlink blocks to unlink queue */
12955e96a66cSDavid du Colombier if(c->uhead == nil){
12965e96a66cSDavid du Colombier c->uhead = b->uhead;
1297d7aba6c3SDavid du Colombier rwakeup(&c->unlink);
12985e96a66cSDavid du Colombier }else
12995e96a66cSDavid du Colombier c->utail->next = b->uhead;
13005e96a66cSDavid du Colombier c->utail = b->utail;
13015e96a66cSDavid du Colombier b->uhead = nil;
13025e96a66cSDavid du Colombier }
1303d7aba6c3SDavid du Colombier qunlock(&c->lk);
13045e96a66cSDavid du Colombier }
13055e96a66cSDavid du Colombier assert(!b->uhead);
13065e96a66cSDavid du Colombier dowakeup = 1;
13075e96a66cSDavid du Colombier break;
13085e96a66cSDavid du Colombier case BioDirty:
13095e96a66cSDavid du Colombier /*
13105e96a66cSDavid du Colombier * Wrote out an old version of the block (see blockRollback).
13115e96a66cSDavid du Colombier * Bump a version count, leave it dirty.
13125e96a66cSDavid du Colombier */
13135e96a66cSDavid du Colombier if(b->iostate == BioWriting){
1314d7aba6c3SDavid du Colombier qlock(&c->lk);
13155e96a66cSDavid du Colombier b->vers = c->vers++;
1316d7aba6c3SDavid du Colombier qunlock(&c->lk);
13175e96a66cSDavid du Colombier dowakeup = 1;
13185e96a66cSDavid du Colombier }
13195e96a66cSDavid du Colombier break;
13205e96a66cSDavid du Colombier case BioReading:
13215e96a66cSDavid du Colombier case BioWriting:
13225e96a66cSDavid du Colombier /*
13235e96a66cSDavid du Colombier * Adding block to disk queue. Bump reference count.
13245e96a66cSDavid du Colombier * diskThread decs the count later by calling blockPut.
13255e96a66cSDavid du Colombier * This is here because we need to lock c->lk to
13265e96a66cSDavid du Colombier * manipulate the ref count.
13275e96a66cSDavid du Colombier */
1328d7aba6c3SDavid du Colombier qlock(&c->lk);
13295e96a66cSDavid du Colombier b->ref++;
1330d7aba6c3SDavid du Colombier qunlock(&c->lk);
13315e96a66cSDavid du Colombier break;
13325e96a66cSDavid du Colombier case BioReadError:
13335e96a66cSDavid du Colombier case BioVentiError:
13345e96a66cSDavid du Colombier /*
13355e96a66cSDavid du Colombier * Oops.
13365e96a66cSDavid du Colombier */
13375e96a66cSDavid du Colombier dowakeup = 1;
13385e96a66cSDavid du Colombier break;
13395e96a66cSDavid du Colombier }
13405e96a66cSDavid du Colombier b->iostate = iostate;
13415e96a66cSDavid du Colombier /*
13425e96a66cSDavid du Colombier * Now that the state has changed, we can wake the waiters.
13435e96a66cSDavid du Colombier */
13445e96a66cSDavid du Colombier if(dowakeup)
1345d7aba6c3SDavid du Colombier rwakeupall(&b->ioready);
13465e96a66cSDavid du Colombier }
13475e96a66cSDavid du Colombier
1348e569ccb5SDavid du Colombier /*
1349e569ccb5SDavid du Colombier * The active file system is a tree of blocks.
1350e569ccb5SDavid du Colombier * When we add snapshots to the mix, the entire file system
1351e569ccb5SDavid du Colombier * becomes a dag and thus requires a bit more care.
1352e569ccb5SDavid du Colombier *
1353e569ccb5SDavid du Colombier * The life of the file system is divided into epochs. A snapshot
1354e569ccb5SDavid du Colombier * ends one epoch and begins the next. Each file system block
1355e569ccb5SDavid du Colombier * is marked with the epoch in which it was created (b.epoch).
1356e569ccb5SDavid du Colombier * When the block is unlinked from the file system (closed), it is marked
1357e569ccb5SDavid du Colombier * with the epoch in which it was removed (b.epochClose).
1358e569ccb5SDavid du Colombier * Once we have discarded or archived all snapshots up to
1359e569ccb5SDavid du Colombier * b.epochClose, we can reclaim the block.
1360e569ccb5SDavid du Colombier *
1361e569ccb5SDavid du Colombier * If a block was created in a past epoch but is not yet closed,
1362e569ccb5SDavid du Colombier * it is treated as copy-on-write. Of course, in order to insert the
1363e569ccb5SDavid du Colombier * new pointer into the tree, the parent must be made writable,
1364e569ccb5SDavid du Colombier * and so on up the tree. The recursion stops because the root
1365e569ccb5SDavid du Colombier * block is always writable.
1366e569ccb5SDavid du Colombier *
1367e569ccb5SDavid du Colombier * If blocks are never closed, they will never be reused, and
1368e569ccb5SDavid du Colombier * we will run out of disk space. But marking a block as closed
1369e569ccb5SDavid du Colombier * requires some care about dependencies and write orderings.
1370e569ccb5SDavid du Colombier *
1371e569ccb5SDavid du Colombier * (1) If a block p points at a copy-on-write block b and we
1372e569ccb5SDavid du Colombier * copy b to create bb, then p must be written out after bb and
1373e569ccb5SDavid du Colombier * lbb (bb's label block).
1374e569ccb5SDavid du Colombier *
1375e569ccb5SDavid du Colombier * (2) We have to mark b as closed, but only after we switch
1376e569ccb5SDavid du Colombier * the pointer, so lb must be written out after p. In fact, we
1377e569ccb5SDavid du Colombier * can't even update the in-memory copy, or the cache might
1378e569ccb5SDavid du Colombier * mistakenly give out b for reuse before p gets written.
1379e569ccb5SDavid du Colombier *
1380e569ccb5SDavid du Colombier * CacheAllocBlock's call to blockSetLabel records a "bb after lbb" dependency.
1381e569ccb5SDavid du Colombier * The caller is expected to record a "p after bb" dependency
1382e569ccb5SDavid du Colombier * to finish (1), and also expected to call blockRemoveLink
1383e569ccb5SDavid du Colombier * to arrange for (2) to happen once p is written.
1384e569ccb5SDavid du Colombier *
1385e569ccb5SDavid du Colombier * Until (2) happens, some pieces of the code (e.g., the archiver)
1386e569ccb5SDavid du Colombier * still need to know whether a block has been copied, so we
1387e569ccb5SDavid du Colombier * set the BsCopied bit in the label and force that to disk *before*
1388e569ccb5SDavid du Colombier * the copy gets written out.
1389e569ccb5SDavid du Colombier */
1390e569ccb5SDavid du Colombier Block*
blockCopy(Block * b,u32int tag,u32int ehi,u32int elo)1391e569ccb5SDavid du Colombier blockCopy(Block *b, u32int tag, u32int ehi, u32int elo)
1392e569ccb5SDavid du Colombier {
1393e569ccb5SDavid du Colombier Block *bb, *lb;
1394e569ccb5SDavid du Colombier Label l;
1395e569ccb5SDavid du Colombier
1396e569ccb5SDavid du Colombier if((b->l.state&BsClosed) || b->l.epoch >= ehi)
13973b86f2f8SDavid du Colombier fprint(2, "%s: blockCopy %#ux %L but fs is [%ud,%ud]\n",
13983b86f2f8SDavid du Colombier argv0, b->addr, &b->l, elo, ehi);
1399e569ccb5SDavid du Colombier
1400e569ccb5SDavid du Colombier bb = cacheAllocBlock(b->c, b->l.type, tag, ehi, elo);
1401e569ccb5SDavid du Colombier if(bb == nil){
1402e569ccb5SDavid du Colombier blockPut(b);
1403e569ccb5SDavid du Colombier return nil;
1404e569ccb5SDavid du Colombier }
1405e569ccb5SDavid du Colombier
1406e569ccb5SDavid du Colombier /*
1407e569ccb5SDavid du Colombier * Update label so we know the block has been copied.
1408e569ccb5SDavid du Colombier * (It will be marked closed once it has been unlinked from
1409e569ccb5SDavid du Colombier * the tree.) This must follow cacheAllocBlock since we
1410e569ccb5SDavid du Colombier * can't be holding onto lb when we call cacheAllocBlock.
1411e569ccb5SDavid du Colombier */
1412e569ccb5SDavid du Colombier if((b->l.state&BsCopied)==0)
1413e569ccb5SDavid du Colombier if(b->part == PartData){ /* not the superblock */
1414e569ccb5SDavid du Colombier l = b->l;
1415e569ccb5SDavid du Colombier l.state |= BsCopied;
1416e569ccb5SDavid du Colombier lb = _blockSetLabel(b, &l);
1417e569ccb5SDavid du Colombier if(lb == nil){
1418e569ccb5SDavid du Colombier /* can't set label => can't copy block */
1419e569ccb5SDavid du Colombier blockPut(b);
1420e569ccb5SDavid du Colombier l.type = BtMax;
1421e569ccb5SDavid du Colombier l.state = BsFree;
1422e569ccb5SDavid du Colombier l.epoch = 0;
1423e569ccb5SDavid du Colombier l.epochClose = 0;
1424e569ccb5SDavid du Colombier l.tag = 0;
1425e569ccb5SDavid du Colombier blockSetLabel(bb, &l, 0);
1426e569ccb5SDavid du Colombier blockPut(bb);
1427e569ccb5SDavid du Colombier return nil;
1428e569ccb5SDavid du Colombier }
1429e569ccb5SDavid du Colombier blockDependency(bb, lb, -1, nil, nil);
1430e569ccb5SDavid du Colombier blockPut(lb);
1431e569ccb5SDavid du Colombier }
1432e569ccb5SDavid du Colombier
1433e569ccb5SDavid du Colombier memmove(bb->data, b->data, b->c->size);
1434e569ccb5SDavid du Colombier blockDirty(bb);
1435e569ccb5SDavid du Colombier blockPut(b);
1436e569ccb5SDavid du Colombier return bb;
1437e569ccb5SDavid du Colombier }
1438e569ccb5SDavid du Colombier
1439e569ccb5SDavid du Colombier /*
1440e569ccb5SDavid du Colombier * Block b once pointed at the block bb at addr/type/tag, but no longer does.
1441e569ccb5SDavid du Colombier * If recurse is set, we are unlinking all of bb's children as well.
1442e569ccb5SDavid du Colombier *
1443e569ccb5SDavid du Colombier * We can't reclaim bb (or its kids) until the block b gets written to disk. We add
1444e569ccb5SDavid du Colombier * the relevant information to b's list of unlinked blocks. Once b is written,
1445e569ccb5SDavid du Colombier * the list will be queued for processing.
1446e569ccb5SDavid du Colombier *
1447e569ccb5SDavid du Colombier * If b depends on bb, it doesn't anymore, so we remove bb from the prior list.
1448e569ccb5SDavid du Colombier */
1449e569ccb5SDavid du Colombier void
blockRemoveLink(Block * b,u32int addr,int type,u32int tag,int recurse)1450e569ccb5SDavid du Colombier blockRemoveLink(Block *b, u32int addr, int type, u32int tag, int recurse)
1451e569ccb5SDavid du Colombier {
1452e569ccb5SDavid du Colombier BList *p, **pp, bl;
1453e569ccb5SDavid du Colombier
1454e569ccb5SDavid du Colombier /* remove bb from prior list */
1455e569ccb5SDavid du Colombier for(pp=&b->prior; (p=*pp)!=nil; ){
1456e569ccb5SDavid du Colombier if(p->part == PartData && p->addr == addr){
1457e569ccb5SDavid du Colombier *pp = p->next;
1458e569ccb5SDavid du Colombier blistFree(b->c, p);
1459e569ccb5SDavid du Colombier }else
1460e569ccb5SDavid du Colombier pp = &p->next;
1461e569ccb5SDavid du Colombier }
1462e569ccb5SDavid du Colombier
1463e569ccb5SDavid du Colombier bl.part = PartData;
1464e569ccb5SDavid du Colombier bl.addr = addr;
1465e569ccb5SDavid du Colombier bl.type = type;
1466e569ccb5SDavid du Colombier bl.tag = tag;
1467e569ccb5SDavid du Colombier if(b->l.epoch == 0)
1468e569ccb5SDavid du Colombier assert(b->part == PartSuper);
1469e569ccb5SDavid du Colombier bl.epoch = b->l.epoch;
1470e569ccb5SDavid du Colombier bl.next = nil;
1471e569ccb5SDavid du Colombier bl.recurse = recurse;
1472e569ccb5SDavid du Colombier
1473e12a9870SDavid du Colombier if(b->part == PartSuper && b->iostate == BioClean)
1474e12a9870SDavid du Colombier p = nil;
1475e12a9870SDavid du Colombier else
1476e569ccb5SDavid du Colombier p = blistAlloc(b);
1477e569ccb5SDavid du Colombier if(p == nil){
1478e569ccb5SDavid du Colombier /*
1479e12a9870SDavid du Colombier * b has already been written to disk.
1480e569ccb5SDavid du Colombier */
1481e569ccb5SDavid du Colombier doRemoveLink(b->c, &bl);
1482e569ccb5SDavid du Colombier return;
1483e569ccb5SDavid du Colombier }
1484e569ccb5SDavid du Colombier
1485e569ccb5SDavid du Colombier /* Uhead is only processed when the block goes from Dirty -> Clean */
1486e569ccb5SDavid du Colombier assert(b->iostate == BioDirty);
1487e569ccb5SDavid du Colombier
1488e569ccb5SDavid du Colombier *p = bl;
1489e569ccb5SDavid du Colombier if(b->uhead == nil)
1490e569ccb5SDavid du Colombier b->uhead = p;
1491e569ccb5SDavid du Colombier else
1492e569ccb5SDavid du Colombier b->utail->next = p;
1493e569ccb5SDavid du Colombier b->utail = p;
1494e569ccb5SDavid du Colombier }
1495e569ccb5SDavid du Colombier
1496e569ccb5SDavid du Colombier /*
1497e569ccb5SDavid du Colombier * Process removal of a single block and perhaps its children.
1498e569ccb5SDavid du Colombier */
1499e569ccb5SDavid du Colombier static void
doRemoveLink(Cache * c,BList * p)1500e569ccb5SDavid du Colombier doRemoveLink(Cache *c, BList *p)
1501e569ccb5SDavid du Colombier {
15020b9a5132SDavid du Colombier int i, n, recurse;
1503e569ccb5SDavid du Colombier u32int a;
1504e569ccb5SDavid du Colombier Block *b;
1505e569ccb5SDavid du Colombier Label l;
1506e569ccb5SDavid du Colombier BList bl;
1507e569ccb5SDavid du Colombier
15080b9a5132SDavid du Colombier recurse = (p->recurse && p->type != BtData && p->type != BtDir);
15090b9a5132SDavid du Colombier
15100b9a5132SDavid du Colombier /*
15110b9a5132SDavid du Colombier * We're not really going to overwrite b, but if we're not
15120b9a5132SDavid du Colombier * going to look at its contents, there is no point in reading
15130b9a5132SDavid du Colombier * them from the disk.
15140b9a5132SDavid du Colombier */
15150b9a5132SDavid du Colombier b = cacheLocalData(c, p->addr, p->type, p->tag, recurse ? OReadOnly : OOverWrite, 0);
1516e569ccb5SDavid du Colombier if(b == nil)
1517e569ccb5SDavid du Colombier return;
1518e569ccb5SDavid du Colombier
1519e569ccb5SDavid du Colombier /*
1520e569ccb5SDavid du Colombier * When we're unlinking from the superblock, close with the next epoch.
1521e569ccb5SDavid du Colombier */
1522e569ccb5SDavid du Colombier if(p->epoch == 0)
1523e569ccb5SDavid du Colombier p->epoch = b->l.epoch+1;
1524e569ccb5SDavid du Colombier
1525e569ccb5SDavid du Colombier /* sanity check */
1526e569ccb5SDavid du Colombier if(b->l.epoch > p->epoch){
15273b86f2f8SDavid du Colombier fprint(2, "%s: doRemoveLink: strange epoch %ud > %ud\n",
15283b86f2f8SDavid du Colombier argv0, b->l.epoch, p->epoch);
1529e569ccb5SDavid du Colombier blockPut(b);
1530e569ccb5SDavid du Colombier return;
1531e569ccb5SDavid du Colombier }
1532e569ccb5SDavid du Colombier
15330b9a5132SDavid du Colombier if(recurse){
1534e569ccb5SDavid du Colombier n = c->size / VtScoreSize;
1535e569ccb5SDavid du Colombier for(i=0; i<n; i++){
1536e569ccb5SDavid du Colombier a = globalToLocal(b->data + i*VtScoreSize);
1537e569ccb5SDavid du Colombier if(a == NilBlock || !readLabel(c, &l, a))
1538e569ccb5SDavid du Colombier continue;
1539e569ccb5SDavid du Colombier if(l.state&BsClosed)
1540e569ccb5SDavid du Colombier continue;
1541e569ccb5SDavid du Colombier /*
1542e569ccb5SDavid du Colombier * If stack space becomes an issue...
1543e569ccb5SDavid du Colombier p->addr = a;
1544e569ccb5SDavid du Colombier p->type = l.type;
1545e569ccb5SDavid du Colombier p->tag = l.tag;
1546e569ccb5SDavid du Colombier doRemoveLink(c, p);
1547e569ccb5SDavid du Colombier */
1548e569ccb5SDavid du Colombier
1549e569ccb5SDavid du Colombier bl.part = PartData;
1550e569ccb5SDavid du Colombier bl.addr = a;
1551e569ccb5SDavid du Colombier bl.type = l.type;
1552e569ccb5SDavid du Colombier bl.tag = l.tag;
1553e569ccb5SDavid du Colombier bl.epoch = p->epoch;
1554e569ccb5SDavid du Colombier bl.next = nil;
1555e569ccb5SDavid du Colombier bl.recurse = 1;
15560b9a5132SDavid du Colombier /* give up the block lock - share with others */
15570b9a5132SDavid du Colombier blockPut(b);
1558e569ccb5SDavid du Colombier doRemoveLink(c, &bl);
15590b9a5132SDavid du Colombier b = cacheLocalData(c, p->addr, p->type, p->tag, OReadOnly, 0);
15600b9a5132SDavid du Colombier if(b == nil){
15613b86f2f8SDavid du Colombier fprint(2, "%s: warning: lost block in doRemoveLink\n",
15623b86f2f8SDavid du Colombier argv0);
15630b9a5132SDavid du Colombier return;
15640b9a5132SDavid du Colombier }
1565e569ccb5SDavid du Colombier }
1566e569ccb5SDavid du Colombier }
1567e569ccb5SDavid du Colombier
1568e569ccb5SDavid du Colombier l = b->l;
1569e569ccb5SDavid du Colombier l.state |= BsClosed;
1570e569ccb5SDavid du Colombier l.epochClose = p->epoch;
1571225077b0SDavid du Colombier if(l.epochClose == l.epoch){
1572*f078af31SDavid du Colombier /* lock ordering: trying for c->fl->lk while holding b->lk can deadlock */
1573*f078af31SDavid du Colombier if(!canqlock(&c->fl->lk)){
1574*f078af31SDavid du Colombier blockPut(b);
1575d7aba6c3SDavid du Colombier qlock(&c->fl->lk);
1576*f078af31SDavid du Colombier b = cacheLocalData(c, p->addr, p->type, p->tag, OOverWrite, 0);
1577*f078af31SDavid du Colombier if(b == nil){
1578*f078af31SDavid du Colombier fprint(2, "%s: warning: lost block at end of doRemoveLink\n",
1579*f078af31SDavid du Colombier argv0);
1580*f078af31SDavid du Colombier qunlock(&c->fl->lk);
1581*f078af31SDavid du Colombier return;
1582*f078af31SDavid du Colombier }
1583*f078af31SDavid du Colombier }
1584225077b0SDavid du Colombier if(l.epoch == c->fl->epochLow)
1585225077b0SDavid du Colombier c->fl->nused--;
1586225077b0SDavid du Colombier blockSetLabel(b, &l, 0);
1587d7aba6c3SDavid du Colombier qunlock(&c->fl->lk);
1588225077b0SDavid du Colombier }else
1589e569ccb5SDavid du Colombier blockSetLabel(b, &l, 0);
1590e569ccb5SDavid du Colombier blockPut(b);
1591e569ccb5SDavid du Colombier }
1592e569ccb5SDavid du Colombier
1593e569ccb5SDavid du Colombier /*
1594e569ccb5SDavid du Colombier * Allocate a BList so that we can record a dependency
1595e569ccb5SDavid du Colombier * or queue a removal related to block b.
1596e569ccb5SDavid du Colombier * If we can't find a BList, we write out b and return nil.
1597e569ccb5SDavid du Colombier */
1598e569ccb5SDavid du Colombier static BList *
blistAlloc(Block * b)1599e569ccb5SDavid du Colombier blistAlloc(Block *b)
1600e569ccb5SDavid du Colombier {
1601e569ccb5SDavid du Colombier Cache *c;
1602e569ccb5SDavid du Colombier BList *p;
1603e569ccb5SDavid du Colombier
1604e569ccb5SDavid du Colombier if(b->iostate != BioDirty){
1605e569ccb5SDavid du Colombier /*
1606e569ccb5SDavid du Colombier * should not happen anymore -
1607e569ccb5SDavid du Colombier * blockDirty used to flush but no longer does.
1608e569ccb5SDavid du Colombier */
1609e569ccb5SDavid du Colombier assert(b->iostate == BioClean);
16103b86f2f8SDavid du Colombier fprint(2, "%s: blistAlloc: called on clean block\n", argv0);
1611e569ccb5SDavid du Colombier return nil;
1612e569ccb5SDavid du Colombier }
1613e569ccb5SDavid du Colombier
1614e569ccb5SDavid du Colombier c = b->c;
1615d7aba6c3SDavid du Colombier qlock(&c->lk);
1616e569ccb5SDavid du Colombier if(c->blfree == nil){
1617e569ccb5SDavid du Colombier /*
1618e569ccb5SDavid du Colombier * No free BLists. What are our options?
1619e569ccb5SDavid du Colombier */
1620e569ccb5SDavid du Colombier
1621e569ccb5SDavid du Colombier /* Block has no priors? Just write it. */
1622e569ccb5SDavid du Colombier if(b->prior == nil){
1623d7aba6c3SDavid du Colombier qunlock(&c->lk);
1624e569ccb5SDavid du Colombier diskWriteAndWait(c->disk, b);
1625e569ccb5SDavid du Colombier return nil;
1626e569ccb5SDavid du Colombier }
1627e569ccb5SDavid du Colombier
1628e569ccb5SDavid du Colombier /*
1629e569ccb5SDavid du Colombier * Wake the flush thread, which will hopefully free up
1630e569ccb5SDavid du Colombier * some BLists for us. We used to flush a block from
1631e569ccb5SDavid du Colombier * our own prior list and reclaim that BList, but this is
1632e569ccb5SDavid du Colombier * a no-no: some of the blocks on our prior list may
1633e569ccb5SDavid du Colombier * be locked by our caller. Or maybe their label blocks
1634e569ccb5SDavid du Colombier * are locked by our caller. In any event, it's too hard
1635e569ccb5SDavid du Colombier * to make sure we can do I/O for ourselves. Instead,
1636e569ccb5SDavid du Colombier * we assume the flush thread will find something.
1637e569ccb5SDavid du Colombier * (The flush thread never blocks waiting for a block,
1638e569ccb5SDavid du Colombier * so it can't deadlock like we can.)
1639e569ccb5SDavid du Colombier */
1640e569ccb5SDavid du Colombier while(c->blfree == nil){
1641d7aba6c3SDavid du Colombier rwakeup(&c->flush);
1642d7aba6c3SDavid du Colombier rsleep(&c->blrend);
16430b9a5132SDavid du Colombier if(c->blfree == nil)
16443b86f2f8SDavid du Colombier fprint(2, "%s: flushing for blists\n", argv0);
1645e569ccb5SDavid du Colombier }
1646e569ccb5SDavid du Colombier }
1647e569ccb5SDavid du Colombier
1648e569ccb5SDavid du Colombier p = c->blfree;
1649e569ccb5SDavid du Colombier c->blfree = p->next;
1650d7aba6c3SDavid du Colombier qunlock(&c->lk);
1651e569ccb5SDavid du Colombier return p;
1652e569ccb5SDavid du Colombier }
1653e569ccb5SDavid du Colombier
1654e569ccb5SDavid du Colombier static void
blistFree(Cache * c,BList * bl)1655e569ccb5SDavid du Colombier blistFree(Cache *c, BList *bl)
1656e569ccb5SDavid du Colombier {
1657d7aba6c3SDavid du Colombier qlock(&c->lk);
1658e569ccb5SDavid du Colombier bl->next = c->blfree;
1659e569ccb5SDavid du Colombier c->blfree = bl;
1660d7aba6c3SDavid du Colombier rwakeup(&c->blrend);
1661d7aba6c3SDavid du Colombier qunlock(&c->lk);
1662e569ccb5SDavid du Colombier }
1663e569ccb5SDavid du Colombier
16645e96a66cSDavid du Colombier char*
bsStr(int state)16655e96a66cSDavid du Colombier bsStr(int state)
16665e96a66cSDavid du Colombier {
16675e96a66cSDavid du Colombier static char s[100];
16685e96a66cSDavid du Colombier
16695e96a66cSDavid du Colombier if(state == BsFree)
16705e96a66cSDavid du Colombier return "Free";
16715e96a66cSDavid du Colombier if(state == BsBad)
16725e96a66cSDavid du Colombier return "Bad";
16735e96a66cSDavid du Colombier
16745e96a66cSDavid du Colombier sprint(s, "%x", state);
16755e96a66cSDavid du Colombier if(!(state&BsAlloc))
16765e96a66cSDavid du Colombier strcat(s, ",Free"); /* should not happen */
16775e96a66cSDavid du Colombier if(state&BsCopied)
16785e96a66cSDavid du Colombier strcat(s, ",Copied");
16795e96a66cSDavid du Colombier if(state&BsVenti)
16805e96a66cSDavid du Colombier strcat(s, ",Venti");
16815e96a66cSDavid du Colombier if(state&BsClosed)
16825e96a66cSDavid du Colombier strcat(s, ",Closed");
16835e96a66cSDavid du Colombier return s;
16845e96a66cSDavid du Colombier }
16855e96a66cSDavid du Colombier
16865e96a66cSDavid du Colombier char *
bioStr(int iostate)16875e96a66cSDavid du Colombier bioStr(int iostate)
16885e96a66cSDavid du Colombier {
16895e96a66cSDavid du Colombier switch(iostate){
16905e96a66cSDavid du Colombier default:
16915e96a66cSDavid du Colombier return "Unknown!!";
16925e96a66cSDavid du Colombier case BioEmpty:
16935e96a66cSDavid du Colombier return "Empty";
16945e96a66cSDavid du Colombier case BioLabel:
16955e96a66cSDavid du Colombier return "Label";
16965e96a66cSDavid du Colombier case BioClean:
16975e96a66cSDavid du Colombier return "Clean";
16985e96a66cSDavid du Colombier case BioDirty:
16995e96a66cSDavid du Colombier return "Dirty";
17005e96a66cSDavid du Colombier case BioReading:
17015e96a66cSDavid du Colombier return "Reading";
17025e96a66cSDavid du Colombier case BioWriting:
17035e96a66cSDavid du Colombier return "Writing";
17045e96a66cSDavid du Colombier case BioReadError:
17055e96a66cSDavid du Colombier return "ReadError";
17065e96a66cSDavid du Colombier case BioVentiError:
17075e96a66cSDavid du Colombier return "VentiError";
17085e96a66cSDavid du Colombier case BioMax:
17095e96a66cSDavid du Colombier return "Max";
17105e96a66cSDavid du Colombier }
17115e96a66cSDavid du Colombier }
17125e96a66cSDavid du Colombier
17135e96a66cSDavid du Colombier static char *bttab[] = {
17145e96a66cSDavid du Colombier "BtData",
17155e96a66cSDavid du Colombier "BtData+1",
17165e96a66cSDavid du Colombier "BtData+2",
17175e96a66cSDavid du Colombier "BtData+3",
17185e96a66cSDavid du Colombier "BtData+4",
17195e96a66cSDavid du Colombier "BtData+5",
17205e96a66cSDavid du Colombier "BtData+6",
17215e96a66cSDavid du Colombier "BtData+7",
17225e96a66cSDavid du Colombier "BtDir",
17235e96a66cSDavid du Colombier "BtDir+1",
17245e96a66cSDavid du Colombier "BtDir+2",
17255e96a66cSDavid du Colombier "BtDir+3",
17265e96a66cSDavid du Colombier "BtDir+4",
17275e96a66cSDavid du Colombier "BtDir+5",
17285e96a66cSDavid du Colombier "BtDir+6",
17295e96a66cSDavid du Colombier "BtDir+7",
17305e96a66cSDavid du Colombier };
17315e96a66cSDavid du Colombier
17325e96a66cSDavid du Colombier char*
btStr(int type)17335e96a66cSDavid du Colombier btStr(int type)
17345e96a66cSDavid du Colombier {
17355e96a66cSDavid du Colombier if(type < nelem(bttab))
17365e96a66cSDavid du Colombier return bttab[type];
17375e96a66cSDavid du Colombier return "unknown";
17385e96a66cSDavid du Colombier }
17395e96a66cSDavid du Colombier
17405e96a66cSDavid du Colombier int
labelFmt(Fmt * f)17415e96a66cSDavid du Colombier labelFmt(Fmt *f)
17425e96a66cSDavid du Colombier {
17435e96a66cSDavid du Colombier Label *l;
17445e96a66cSDavid du Colombier
17455e96a66cSDavid du Colombier l = va_arg(f->args, Label*);
17465e96a66cSDavid du Colombier return fmtprint(f, "%s,%s,e=%ud,%d,tag=%#ux",
17475e96a66cSDavid du Colombier btStr(l->type), bsStr(l->state), l->epoch, (int)l->epochClose, l->tag);
17485e96a66cSDavid du Colombier }
17495e96a66cSDavid du Colombier
17505e96a66cSDavid du Colombier int
scoreFmt(Fmt * f)17515e96a66cSDavid du Colombier scoreFmt(Fmt *f)
17525e96a66cSDavid du Colombier {
17535e96a66cSDavid du Colombier uchar *v;
17545e96a66cSDavid du Colombier int i;
17555e96a66cSDavid du Colombier u32int addr;
17565e96a66cSDavid du Colombier
17575e96a66cSDavid du Colombier v = va_arg(f->args, uchar*);
17585e96a66cSDavid du Colombier if(v == nil){
17595e96a66cSDavid du Colombier fmtprint(f, "*");
17605e96a66cSDavid du Colombier }else if((addr = globalToLocal(v)) != NilBlock)
17615e96a66cSDavid du Colombier fmtprint(f, "0x%.8ux", addr);
17625e96a66cSDavid du Colombier else{
17635e96a66cSDavid du Colombier for(i = 0; i < VtScoreSize; i++)
17645e96a66cSDavid du Colombier fmtprint(f, "%2.2ux", v[i]);
17655e96a66cSDavid du Colombier }
17665e96a66cSDavid du Colombier
17675e96a66cSDavid du Colombier return 0;
17685e96a66cSDavid du Colombier }
17695e96a66cSDavid du Colombier
17705e96a66cSDavid du Colombier static int
upHeap(int i,Block * b)17715e96a66cSDavid du Colombier upHeap(int i, Block *b)
17725e96a66cSDavid du Colombier {
17735e96a66cSDavid du Colombier Block *bb;
17745e96a66cSDavid du Colombier u32int now;
17755e96a66cSDavid du Colombier int p;
17765e96a66cSDavid du Colombier Cache *c;
17775e96a66cSDavid du Colombier
17785e96a66cSDavid du Colombier c = b->c;
17795e96a66cSDavid du Colombier now = c->now;
17805e96a66cSDavid du Colombier for(; i != 0; i = p){
17815e96a66cSDavid du Colombier p = (i - 1) >> 1;
17825e96a66cSDavid du Colombier bb = c->heap[p];
17835e96a66cSDavid du Colombier if(b->used - now >= bb->used - now)
17845e96a66cSDavid du Colombier break;
17855e96a66cSDavid du Colombier c->heap[i] = bb;
17865e96a66cSDavid du Colombier bb->heap = i;
17875e96a66cSDavid du Colombier }
17885e96a66cSDavid du Colombier c->heap[i] = b;
17895e96a66cSDavid du Colombier b->heap = i;
17905e96a66cSDavid du Colombier
17915e96a66cSDavid du Colombier return i;
17925e96a66cSDavid du Colombier }
17935e96a66cSDavid du Colombier
17945e96a66cSDavid du Colombier static int
downHeap(int i,Block * b)17955e96a66cSDavid du Colombier downHeap(int i, Block *b)
17965e96a66cSDavid du Colombier {
17975e96a66cSDavid du Colombier Block *bb;
17985e96a66cSDavid du Colombier u32int now;
17995e96a66cSDavid du Colombier int k;
18005e96a66cSDavid du Colombier Cache *c;
18015e96a66cSDavid du Colombier
18025e96a66cSDavid du Colombier c = b->c;
18035e96a66cSDavid du Colombier now = c->now;
18045e96a66cSDavid du Colombier for(; ; i = k){
18055e96a66cSDavid du Colombier k = (i << 1) + 1;
18065e96a66cSDavid du Colombier if(k >= c->nheap)
18075e96a66cSDavid du Colombier break;
18085e96a66cSDavid du Colombier if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now)
18095e96a66cSDavid du Colombier k++;
18105e96a66cSDavid du Colombier bb = c->heap[k];
18115e96a66cSDavid du Colombier if(b->used - now <= bb->used - now)
18125e96a66cSDavid du Colombier break;
18135e96a66cSDavid du Colombier c->heap[i] = bb;
18145e96a66cSDavid du Colombier bb->heap = i;
18155e96a66cSDavid du Colombier }
18165e96a66cSDavid du Colombier c->heap[i] = b;
18175e96a66cSDavid du Colombier b->heap = i;
18185e96a66cSDavid du Colombier return i;
18195e96a66cSDavid du Colombier }
18205e96a66cSDavid du Colombier
18215e96a66cSDavid du Colombier /*
18225e96a66cSDavid du Colombier * Delete a block from the heap.
18235e96a66cSDavid du Colombier * Called with c->lk held.
18245e96a66cSDavid du Colombier */
18255e96a66cSDavid du Colombier static void
heapDel(Block * b)18265e96a66cSDavid du Colombier heapDel(Block *b)
18275e96a66cSDavid du Colombier {
18285e96a66cSDavid du Colombier int i, si;
18295e96a66cSDavid du Colombier Cache *c;
18305e96a66cSDavid du Colombier
18315e96a66cSDavid du Colombier c = b->c;
18325e96a66cSDavid du Colombier
18335e96a66cSDavid du Colombier si = b->heap;
18345e96a66cSDavid du Colombier if(si == BadHeap)
18355e96a66cSDavid du Colombier return;
18365e96a66cSDavid du Colombier b->heap = BadHeap;
18375e96a66cSDavid du Colombier c->nheap--;
18385e96a66cSDavid du Colombier if(si == c->nheap)
18395e96a66cSDavid du Colombier return;
18405e96a66cSDavid du Colombier b = c->heap[c->nheap];
18415e96a66cSDavid du Colombier i = upHeap(si, b);
18425e96a66cSDavid du Colombier if(i == si)
18435e96a66cSDavid du Colombier downHeap(i, b);
18445e96a66cSDavid du Colombier }
18455e96a66cSDavid du Colombier
18465e96a66cSDavid du Colombier /*
18475e96a66cSDavid du Colombier * Insert a block into the heap.
18485e96a66cSDavid du Colombier * Called with c->lk held.
18495e96a66cSDavid du Colombier */
18505e96a66cSDavid du Colombier static void
heapIns(Block * b)18515e96a66cSDavid du Colombier heapIns(Block *b)
18525e96a66cSDavid du Colombier {
18535e96a66cSDavid du Colombier assert(b->heap == BadHeap);
18545e96a66cSDavid du Colombier upHeap(b->c->nheap++, b);
1855d7aba6c3SDavid du Colombier rwakeup(&b->c->heapwait);
18565e96a66cSDavid du Colombier }
18575e96a66cSDavid du Colombier
18585e96a66cSDavid du Colombier /*
18595e96a66cSDavid du Colombier * Get just the label for a block.
18605e96a66cSDavid du Colombier */
1861e569ccb5SDavid du Colombier int
readLabel(Cache * c,Label * l,u32int addr)18625e96a66cSDavid du Colombier readLabel(Cache *c, Label *l, u32int addr)
18635e96a66cSDavid du Colombier {
18645e96a66cSDavid du Colombier int lpb;
18655e96a66cSDavid du Colombier Block *b;
18665e96a66cSDavid du Colombier u32int a;
18675e96a66cSDavid du Colombier
18685e96a66cSDavid du Colombier lpb = c->size / LabelSize;
18695e96a66cSDavid du Colombier a = addr / lpb;
18705e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, a, OReadOnly);
18715e96a66cSDavid du Colombier if(b == nil){
18725e96a66cSDavid du Colombier blockPut(b);
18735e96a66cSDavid du Colombier return 0;
18745e96a66cSDavid du Colombier }
18755e96a66cSDavid du Colombier
18765e96a66cSDavid du Colombier if(!labelUnpack(l, b->data, addr%lpb)){
18775e96a66cSDavid du Colombier blockPut(b);
18785e96a66cSDavid du Colombier return 0;
18795e96a66cSDavid du Colombier }
18805e96a66cSDavid du Colombier blockPut(b);
18815e96a66cSDavid du Colombier return 1;
18825e96a66cSDavid du Colombier }
18835e96a66cSDavid du Colombier
18845e96a66cSDavid du Colombier /*
18855e96a66cSDavid du Colombier * Process unlink queue.
18865e96a66cSDavid du Colombier * Called with c->lk held.
18875e96a66cSDavid du Colombier */
18885e96a66cSDavid du Colombier static void
unlinkBody(Cache * c)18895e96a66cSDavid du Colombier unlinkBody(Cache *c)
18905e96a66cSDavid du Colombier {
18915e96a66cSDavid du Colombier BList *p;
18925e96a66cSDavid du Colombier
18935e96a66cSDavid du Colombier while(c->uhead != nil){
18945e96a66cSDavid du Colombier p = c->uhead;
18955e96a66cSDavid du Colombier c->uhead = p->next;
1896d7aba6c3SDavid du Colombier qunlock(&c->lk);
1897e569ccb5SDavid du Colombier doRemoveLink(c, p);
1898d7aba6c3SDavid du Colombier qlock(&c->lk);
18995e96a66cSDavid du Colombier p->next = c->blfree;
19005e96a66cSDavid du Colombier c->blfree = p;
19015e96a66cSDavid du Colombier }
19025e96a66cSDavid du Colombier }
19035e96a66cSDavid du Colombier
19045e96a66cSDavid du Colombier /*
19055e96a66cSDavid du Colombier * Occasionally unlink the blocks on the cache unlink queue.
19065e96a66cSDavid du Colombier */
19075e96a66cSDavid du Colombier static void
unlinkThread(void * a)19085e96a66cSDavid du Colombier unlinkThread(void *a)
19095e96a66cSDavid du Colombier {
19105e96a66cSDavid du Colombier Cache *c = a;
19115e96a66cSDavid du Colombier
1912d7aba6c3SDavid du Colombier threadsetname("unlink");
19135e96a66cSDavid du Colombier
1914d7aba6c3SDavid du Colombier qlock(&c->lk);
19155e96a66cSDavid du Colombier for(;;){
1916d7aba6c3SDavid du Colombier while(c->uhead == nil && c->die.l == nil)
1917d7aba6c3SDavid du Colombier rsleep(&c->unlink);
1918d7aba6c3SDavid du Colombier if(c->die.l != nil)
19195e96a66cSDavid du Colombier break;
19205e96a66cSDavid du Colombier unlinkBody(c);
19215e96a66cSDavid du Colombier }
19225e96a66cSDavid du Colombier c->ref--;
1923d7aba6c3SDavid du Colombier rwakeup(&c->die);
1924d7aba6c3SDavid du Colombier qunlock(&c->lk);
19255e96a66cSDavid du Colombier }
19265e96a66cSDavid du Colombier
19275e96a66cSDavid du Colombier static int
baddrCmp(void * a0,void * a1)19285e96a66cSDavid du Colombier baddrCmp(void *a0, void *a1)
19295e96a66cSDavid du Colombier {
19305e96a66cSDavid du Colombier BAddr *b0, *b1;
19315e96a66cSDavid du Colombier b0 = a0;
19325e96a66cSDavid du Colombier b1 = a1;
19335e96a66cSDavid du Colombier
19345e96a66cSDavid du Colombier if(b0->part < b1->part)
19355e96a66cSDavid du Colombier return -1;
19365e96a66cSDavid du Colombier if(b0->part > b1->part)
19375e96a66cSDavid du Colombier return 1;
19385e96a66cSDavid du Colombier if(b0->addr < b1->addr)
19395e96a66cSDavid du Colombier return -1;
19405e96a66cSDavid du Colombier if(b0->addr > b1->addr)
19415e96a66cSDavid du Colombier return 1;
19425e96a66cSDavid du Colombier return 0;
19435e96a66cSDavid du Colombier }
19445e96a66cSDavid du Colombier
19455e96a66cSDavid du Colombier /*
19465e96a66cSDavid du Colombier * Scan the block list for dirty blocks; add them to the list c->baddr.
19475e96a66cSDavid du Colombier */
19485e96a66cSDavid du Colombier static void
flushFill(Cache * c)19495e96a66cSDavid du Colombier flushFill(Cache *c)
19505e96a66cSDavid du Colombier {
195161201b97SDavid du Colombier int i, ndirty;
19525e96a66cSDavid du Colombier BAddr *p;
19535e96a66cSDavid du Colombier Block *b;
19545e96a66cSDavid du Colombier
1955d7aba6c3SDavid du Colombier qlock(&c->lk);
195639734e7eSDavid du Colombier if(c->ndirty == 0){
1957d7aba6c3SDavid du Colombier qunlock(&c->lk);
195839734e7eSDavid du Colombier return;
195939734e7eSDavid du Colombier }
19605e96a66cSDavid du Colombier
19615e96a66cSDavid du Colombier p = c->baddr;
196261201b97SDavid du Colombier ndirty = 0;
19635e96a66cSDavid du Colombier for(i=0; i<c->nblocks; i++){
19645e96a66cSDavid du Colombier b = c->blocks + i;
196561201b97SDavid du Colombier if(b->part == PartError)
196661201b97SDavid du Colombier continue;
196761201b97SDavid du Colombier if(b->iostate == BioDirty || b->iostate == BioWriting)
196861201b97SDavid du Colombier ndirty++;
196961201b97SDavid du Colombier if(b->iostate != BioDirty)
19705e96a66cSDavid du Colombier continue;
19715e96a66cSDavid du Colombier p->part = b->part;
19725e96a66cSDavid du Colombier p->addr = b->addr;
19735e96a66cSDavid du Colombier p->vers = b->vers;
19745e96a66cSDavid du Colombier p++;
19755e96a66cSDavid du Colombier }
197661201b97SDavid du Colombier if(ndirty != c->ndirty){
19773b86f2f8SDavid du Colombier fprint(2, "%s: ndirty mismatch expected %d found %d\n",
19783b86f2f8SDavid du Colombier argv0, c->ndirty, ndirty);
197961201b97SDavid du Colombier c->ndirty = ndirty;
198061201b97SDavid du Colombier }
1981d7aba6c3SDavid du Colombier qunlock(&c->lk);
19825e96a66cSDavid du Colombier
19835e96a66cSDavid du Colombier c->bw = p - c->baddr;
19845e96a66cSDavid du Colombier qsort(c->baddr, c->bw, sizeof(BAddr), baddrCmp);
19855e96a66cSDavid du Colombier }
19865e96a66cSDavid du Colombier
19875e96a66cSDavid du Colombier /*
19885e96a66cSDavid du Colombier * This is not thread safe, i.e. it can't be called from multiple threads.
19895e96a66cSDavid du Colombier *
19905e96a66cSDavid du Colombier * It's okay how we use it, because it only gets called in
19915e96a66cSDavid du Colombier * the flushThread. And cacheFree, but only after
19925e96a66cSDavid du Colombier * cacheFree has killed off the flushThread.
19935e96a66cSDavid du Colombier */
19945e96a66cSDavid du Colombier static int
cacheFlushBlock(Cache * c)19955e96a66cSDavid du Colombier cacheFlushBlock(Cache *c)
19965e96a66cSDavid du Colombier {
19975e96a66cSDavid du Colombier Block *b;
19985e96a66cSDavid du Colombier BAddr *p;
19995e96a66cSDavid du Colombier int lockfail, nfail;
20005e96a66cSDavid du Colombier
20015e96a66cSDavid du Colombier nfail = 0;
20025e96a66cSDavid du Colombier for(;;){
20035e96a66cSDavid du Colombier if(c->br == c->be){
20045e96a66cSDavid du Colombier if(c->bw == 0 || c->bw == c->be)
20055e96a66cSDavid du Colombier flushFill(c);
20065e96a66cSDavid du Colombier c->br = 0;
20075e96a66cSDavid du Colombier c->be = c->bw;
20085e96a66cSDavid du Colombier c->bw = 0;
20095e96a66cSDavid du Colombier c->nflush = 0;
20105e96a66cSDavid du Colombier }
20115e96a66cSDavid du Colombier
20125e96a66cSDavid du Colombier if(c->br == c->be)
20135e96a66cSDavid du Colombier return 0;
20145e96a66cSDavid du Colombier p = c->baddr + c->br;
20155e96a66cSDavid du Colombier c->br++;
20162651f6bbSDavid du Colombier b = _cacheLocalLookup(c, p->part, p->addr, p->vers, Nowaitlock,
20172651f6bbSDavid du Colombier &lockfail);
20185e96a66cSDavid du Colombier
2019e12a9870SDavid du Colombier if(b && blockWrite(b, Nowaitlock)){
20205e96a66cSDavid du Colombier c->nflush++;
20215e96a66cSDavid du Colombier blockPut(b);
20225e96a66cSDavid du Colombier return 1;
20235e96a66cSDavid du Colombier }
20245e96a66cSDavid du Colombier if(b)
20255e96a66cSDavid du Colombier blockPut(b);
20265e96a66cSDavid du Colombier
20275e96a66cSDavid du Colombier /*
20285e96a66cSDavid du Colombier * Why didn't we write the block?
20295e96a66cSDavid du Colombier */
20305e96a66cSDavid du Colombier
20315e96a66cSDavid du Colombier /* Block already written out */
20325e96a66cSDavid du Colombier if(b == nil && !lockfail)
20335e96a66cSDavid du Colombier continue;
20345e96a66cSDavid du Colombier
20355e96a66cSDavid du Colombier /* Failed to acquire lock; sleep if happens a lot. */
2036fe853e23SDavid du Colombier if(lockfail && ++nfail > 100){
20375e96a66cSDavid du Colombier sleep(500);
2038fe853e23SDavid du Colombier nfail = 0;
2039fe853e23SDavid du Colombier }
20405e96a66cSDavid du Colombier /* Requeue block. */
20415e96a66cSDavid du Colombier if(c->bw < c->be)
20425e96a66cSDavid du Colombier c->baddr[c->bw++] = *p;
20435e96a66cSDavid du Colombier }
20445e96a66cSDavid du Colombier }
20455e96a66cSDavid du Colombier
20465e96a66cSDavid du Colombier /*
20475e96a66cSDavid du Colombier * Occasionally flush dirty blocks from memory to the disk.
20485e96a66cSDavid du Colombier */
20495e96a66cSDavid du Colombier static void
flushThread(void * a)20505e96a66cSDavid du Colombier flushThread(void *a)
20515e96a66cSDavid du Colombier {
20525e96a66cSDavid du Colombier Cache *c = a;
20535e96a66cSDavid du Colombier int i;
20545e96a66cSDavid du Colombier
2055d7aba6c3SDavid du Colombier threadsetname("flush");
2056d7aba6c3SDavid du Colombier qlock(&c->lk);
2057d7aba6c3SDavid du Colombier while(c->die.l == nil){
2058d7aba6c3SDavid du Colombier rsleep(&c->flush);
2059d7aba6c3SDavid du Colombier qunlock(&c->lk);
20605e96a66cSDavid du Colombier for(i=0; i<FlushSize; i++)
206167031067SDavid du Colombier if(!cacheFlushBlock(c)){
206267031067SDavid du Colombier /*
206367031067SDavid du Colombier * If i==0, could be someone is waking us repeatedly
206467031067SDavid du Colombier * to flush the cache but there's no work to do.
206567031067SDavid du Colombier * Pause a little.
206667031067SDavid du Colombier */
20670b9a5132SDavid du Colombier if(i==0){
20683b86f2f8SDavid du Colombier // fprint(2, "%s: flushthread found "
20693b86f2f8SDavid du Colombier // "nothing to flush - %d dirty\n",
20703b86f2f8SDavid du Colombier // argv0, c->ndirty);
207167031067SDavid du Colombier sleep(250);
20720b9a5132SDavid du Colombier }
20735e96a66cSDavid du Colombier break;
207467031067SDavid du Colombier }
207539734e7eSDavid du Colombier if(i==0 && c->ndirty){
207639734e7eSDavid du Colombier /*
207739734e7eSDavid du Colombier * All the blocks are being written right now -- there's nothing to do.
207839734e7eSDavid du Colombier * We might be spinning with cacheFlush though -- he'll just keep
207939734e7eSDavid du Colombier * kicking us until c->ndirty goes down. Probably we should sleep
208039734e7eSDavid du Colombier * on something that the diskThread can kick, but for now we'll
208139734e7eSDavid du Colombier * just pause for a little while waiting for disks to finish.
208239734e7eSDavid du Colombier */
208339734e7eSDavid du Colombier sleep(100);
208439734e7eSDavid du Colombier }
2085d7aba6c3SDavid du Colombier qlock(&c->lk);
2086d7aba6c3SDavid du Colombier rwakeupall(&c->flushwait);
20875e96a66cSDavid du Colombier }
20885e96a66cSDavid du Colombier c->ref--;
2089d7aba6c3SDavid du Colombier rwakeup(&c->die);
2090d7aba6c3SDavid du Colombier qunlock(&c->lk);
20915e96a66cSDavid du Colombier }
20925e96a66cSDavid du Colombier
20935e96a66cSDavid du Colombier /*
20940b9a5132SDavid du Colombier * Flush the cache.
20955e96a66cSDavid du Colombier */
20965e96a66cSDavid du Colombier void
cacheFlush(Cache * c,int wait)20975e96a66cSDavid du Colombier cacheFlush(Cache *c, int wait)
20985e96a66cSDavid du Colombier {
2099d7aba6c3SDavid du Colombier qlock(&c->lk);
21005e96a66cSDavid du Colombier if(wait){
21015e96a66cSDavid du Colombier while(c->ndirty){
2102dc5a79c1SDavid du Colombier // consPrint("cacheFlush: %d dirty blocks, uhead %p\n",
2103dc5a79c1SDavid du Colombier // c->ndirty, c->uhead);
2104d7aba6c3SDavid du Colombier rwakeup(&c->flush);
2105d7aba6c3SDavid du Colombier rsleep(&c->flushwait);
21065e96a66cSDavid du Colombier }
2107dc5a79c1SDavid du Colombier // consPrint("cacheFlush: done (uhead %p)\n", c->ndirty, c->uhead);
21080b9a5132SDavid du Colombier }else if(c->ndirty)
2109d7aba6c3SDavid du Colombier rwakeup(&c->flush);
2110d7aba6c3SDavid du Colombier qunlock(&c->lk);
21115e96a66cSDavid du Colombier }
211261201b97SDavid du Colombier
211361201b97SDavid du Colombier /*
211461201b97SDavid du Colombier * Kick the flushThread every 30 seconds.
211561201b97SDavid du Colombier */
211661201b97SDavid du Colombier static void
cacheSync(void * v)211761201b97SDavid du Colombier cacheSync(void *v)
211861201b97SDavid du Colombier {
211961201b97SDavid du Colombier Cache *c;
212061201b97SDavid du Colombier
212161201b97SDavid du Colombier c = v;
212261201b97SDavid du Colombier cacheFlush(c, 0);
212361201b97SDavid du Colombier }
2124