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 { 24*d7aba6c3SDavid 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 */ 31*d7aba6c3SDavid 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; 41*d7aba6c3SDavid 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 51*d7aba6c3SDavid du Colombier Rendez die; /* daemon threads should die when QLock != nil */ 525e96a66cSDavid du Colombier 53*d7aba6c3SDavid du Colombier Rendez flush; 54*d7aba6c3SDavid du Colombier Rendez flushwait; 55*d7aba6c3SDavid 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; 65*d7aba6c3SDavid 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 { 98*d7aba6c3SDavid 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 */ 126*d7aba6c3SDavid du Colombier VtDataType+1, /* BtData | 1 */ 127*d7aba6c3SDavid du Colombier VtDataType+2, /* BtData | 2 */ 128*d7aba6c3SDavid du Colombier VtDataType+3, /* BtData | 3 */ 129*d7aba6c3SDavid du Colombier VtDataType+4, /* BtData | 4 */ 130*d7aba6c3SDavid du Colombier VtDataType+5, /* BtData | 5 */ 131*d7aba6c3SDavid du Colombier VtDataType+6, /* BtData | 6 */ 132*d7aba6c3SDavid du Colombier VtDataType+7, /* BtData | 7 */ 1335e96a66cSDavid du Colombier VtDirType, /* BtDir | 0 */ 134*d7aba6c3SDavid du Colombier VtDirType+1, /* BtDir | 1 */ 135*d7aba6c3SDavid du Colombier VtDirType+2, /* BtDir | 2 */ 136*d7aba6c3SDavid du Colombier VtDirType+3, /* BtDir | 3 */ 137*d7aba6c3SDavid du Colombier VtDirType+4, /* BtDir | 4 */ 138*d7aba6c3SDavid du Colombier VtDirType+5, /* BtDir | 5 */ 139*d7aba6c3SDavid du Colombier VtDirType+6, /* BtDir | 6 */ 140*d7aba6c3SDavid 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 * 147*d7aba6c3SDavid 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 156*d7aba6c3SDavid 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; 171*d7aba6c3SDavid du Colombier c->heads = vtmallocz(c->hashSize*sizeof(Block*)); 172*d7aba6c3SDavid du Colombier c->heap = vtmallocz(nblocks*sizeof(Block*)); 173*d7aba6c3SDavid du Colombier c->blocks = vtmallocz(nblocks*sizeof(Block)); 174*d7aba6c3SDavid du Colombier c->mem = vtmallocz(nblocks * (c->size + c->ndmap) + nbl * sizeof(BList)); 175*d7aba6c3SDavid 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; 184*d7aba6c3SDavid 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 202*d7aba6c3SDavid 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 208*d7aba6c3SDavid du Colombier c->unlink.l = &c->lk; 209*d7aba6c3SDavid du Colombier c->flush.l = &c->lk; 210*d7aba6c3SDavid du Colombier c->flushwait.l = &c->lk; 211*d7aba6c3SDavid 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; 216*d7aba6c3SDavid du Colombier proccreate(unlinkThread, c, STACK); 217*d7aba6c3SDavid 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 2285e96a66cSDavid du Colombier cacheFree(Cache *c) 2295e96a66cSDavid du Colombier { 2305e96a66cSDavid du Colombier int i; 2315e96a66cSDavid du Colombier 2325e96a66cSDavid du Colombier /* kill off daemon threads */ 233*d7aba6c3SDavid du Colombier qlock(&c->lk); 234*d7aba6c3SDavid du Colombier c->die.l = &c->lk; 23561201b97SDavid du Colombier periodicKill(c->sync); 236*d7aba6c3SDavid du Colombier rwakeup(&c->flush); 237*d7aba6c3SDavid du Colombier rwakeup(&c->unlink); 2385e96a66cSDavid du Colombier while(c->ref > 1) 239*d7aba6c3SDavid du Colombier rsleep(&c->die); 2405e96a66cSDavid du Colombier 2415e96a66cSDavid du Colombier /* flush everything out */ 2425e96a66cSDavid du Colombier do { 2435e96a66cSDavid du Colombier unlinkBody(c); 244*d7aba6c3SDavid du Colombier qunlock(&c->lk); 2455e96a66cSDavid du Colombier while(cacheFlushBlock(c)) 2465e96a66cSDavid du Colombier ; 2475e96a66cSDavid du Colombier diskFlush(c->disk); 248*d7aba6c3SDavid du Colombier qlock(&c->lk); 2495e96a66cSDavid du Colombier } while(c->uhead || c->ndirty); 250*d7aba6c3SDavid 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); 258*d7aba6c3SDavid du Colombier vtfree(c->baddr); 259*d7aba6c3SDavid du Colombier vtfree(c->heads); 260*d7aba6c3SDavid du Colombier vtfree(c->blocks); 261*d7aba6c3SDavid du Colombier vtfree(c->mem); 2625e96a66cSDavid du Colombier diskFree(c->disk); 2635e96a66cSDavid du Colombier /* don't close vtSession */ 264*d7aba6c3SDavid du Colombier vtfree(c); 2655e96a66cSDavid du Colombier } 2665e96a66cSDavid du Colombier 2675e96a66cSDavid du Colombier static void 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 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) 294*d7aba6c3SDavid 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) 296*d7aba6c3SDavid 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) 299*d7aba6c3SDavid 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) 302*d7aba6c3SDavid 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]) 309*d7aba6c3SDavid 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 * 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){ 349*d7aba6c3SDavid du Colombier rwakeup(&c->flush); 350*d7aba6c3SDavid 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 * 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 */ 411*d7aba6c3SDavid 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){ 417*d7aba6c3SDavid du Colombier qunlock(&c->lk); 4185e96a66cSDavid du Colombier return nil; 4195e96a66cSDavid du Colombier } 420*d7aba6c3SDavid du Colombier if(!waitlock && !canqlock(&b->lk)){ 4215e96a66cSDavid du Colombier *lockfailure = 1; 422*d7aba6c3SDavid du Colombier qunlock(&c->lk); 4235e96a66cSDavid du Colombier return nil; 4245e96a66cSDavid du Colombier } 4255e96a66cSDavid du Colombier heapDel(b); 4265e96a66cSDavid du Colombier b->ref++; 427*d7aba6c3SDavid du Colombier qunlock(&c->lk); 4285e96a66cSDavid du Colombier 4295e96a66cSDavid du Colombier bwatchLock(b); 4305e96a66cSDavid du Colombier if(waitlock) 431*d7aba6c3SDavid 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: 449*d7aba6c3SDavid du Colombier rsleep(&b->ioready); 4505e96a66cSDavid du Colombier break; 4515e96a66cSDavid du Colombier case BioVentiError: 45211e1fb05SDavid du Colombier blockPut(b); 453*d7aba6c3SDavid 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); 457*d7aba6c3SDavid 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* 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 * 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 */ 487*d7aba6c3SDavid 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); 493*d7aba6c3SDavid du Colombier qunlock(&c->lk); 494*d7aba6c3SDavid 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 517*d7aba6c3SDavid 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); 531*d7aba6c3SDavid 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); 544*d7aba6c3SDavid 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); 563*d7aba6c3SDavid 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: 570*d7aba6c3SDavid 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); 575*d7aba6c3SDavid 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 * 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 * 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); 604*d7aba6c3SDavid 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 * 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 */ 638*d7aba6c3SDavid 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 } 664*d7aba6c3SDavid du Colombier qunlock(&c->lk); 6655e96a66cSDavid du Colombier 6665e96a66cSDavid du Colombier bwatchLock(b); 667*d7aba6c3SDavid 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: 675*d7aba6c3SDavid du Colombier n = vtread(c->z, score, vtType[type], b->data, c->size); 676*d7aba6c3SDavid du Colombier if(n < 0 || vtsha1check(score, b->data, n) < 0){ 6775e96a66cSDavid du Colombier blockSetIOState(b, BioVentiError); 6785e96a66cSDavid du Colombier blockPut(b); 679*d7aba6c3SDavid 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 } 684*d7aba6c3SDavid 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); 691*d7aba6c3SDavid 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); 695*d7aba6c3SDavid 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 * 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 719*d7aba6c3SDavid du Colombier qlock(&fl->lk); 7205e96a66cSDavid du Colombier addr = fl->last; 7215e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly); 7225e96a66cSDavid du Colombier if(b == nil){ 723*d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx %r\n", argv0); 724*d7aba6c3SDavid du Colombier qunlock(&fl->lk); 7255e96a66cSDavid du Colombier return nil; 7265e96a66cSDavid du Colombier } 7275e96a66cSDavid du Colombier nwrap = 0; 7285e96a66cSDavid du Colombier for(;;){ 7295e96a66cSDavid du Colombier if(++addr >= fl->end){ 7305e96a66cSDavid du Colombier addr = 0; 7315e96a66cSDavid du Colombier if(++nwrap >= 2){ 7325e96a66cSDavid du Colombier blockPut(b); 733*d7aba6c3SDavid du Colombier werrstr("disk is full"); 734b3b810bfSDavid du Colombier /* 735b3b810bfSDavid du Colombier * try to avoid a continuous spew of console 736b3b810bfSDavid du Colombier * messages. 737b3b810bfSDavid du Colombier */ 738b3b810bfSDavid du Colombier if (fl->last != 0) 739*d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx1 %r\n", 740b3b810bfSDavid du Colombier argv0); 741b3b810bfSDavid du Colombier fl->last = 0; 742*d7aba6c3SDavid du Colombier qunlock(&fl->lk); 7435e96a66cSDavid du Colombier return nil; 7445e96a66cSDavid du Colombier } 7455e96a66cSDavid du Colombier } 7465e96a66cSDavid du Colombier if(addr%n == 0){ 7475e96a66cSDavid du Colombier blockPut(b); 7485e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly); 7495e96a66cSDavid du Colombier if(b == nil){ 7505e96a66cSDavid du Colombier fl->last = addr; 751*d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx2 %r\n", argv0); 752*d7aba6c3SDavid du Colombier qunlock(&fl->lk); 7535e96a66cSDavid du Colombier return nil; 7545e96a66cSDavid du Colombier } 7555e96a66cSDavid du Colombier } 7565e96a66cSDavid du Colombier if(!labelUnpack(&lab, b->data, addr%n)) 7575e96a66cSDavid du Colombier continue; 7585e96a66cSDavid du Colombier if(lab.state == BsFree) 7595e96a66cSDavid du Colombier goto Found; 760e569ccb5SDavid du Colombier if(lab.state&BsClosed) 761e569ccb5SDavid du Colombier if(lab.epochClose <= epochLow || lab.epoch==lab.epochClose) 7625e96a66cSDavid du Colombier goto Found; 7635e96a66cSDavid du Colombier } 7645e96a66cSDavid du Colombier Found: 7655e96a66cSDavid du Colombier blockPut(b); 7665e96a66cSDavid du Colombier b = cacheLocal(c, PartData, addr, OOverWrite); 7675e96a66cSDavid du Colombier if(b == nil){ 768*d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx3 %r\n", argv0); 7695e96a66cSDavid du Colombier return nil; 7705e96a66cSDavid du Colombier } 7715e96a66cSDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean); 7725e96a66cSDavid du Colombier fl->last = addr; 7735e96a66cSDavid du Colombier lab.type = type; 7745e96a66cSDavid du Colombier lab.tag = tag; 7755e96a66cSDavid du Colombier lab.state = BsAlloc; 7765e96a66cSDavid du Colombier lab.epoch = epoch; 7775e96a66cSDavid du Colombier lab.epochClose = ~(u32int)0; 778e569ccb5SDavid du Colombier if(!blockSetLabel(b, &lab, 1)){ 779*d7aba6c3SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx4 %r\n", argv0); 7805e96a66cSDavid du Colombier blockPut(b); 7815e96a66cSDavid du Colombier return nil; 7825e96a66cSDavid du Colombier } 783*d7aba6c3SDavid du Colombier vtzeroextend(vtType[type], b->data, 0, c->size); 7845e96a66cSDavid du Colombier if(0)diskWrite(c->disk, b); 7855e96a66cSDavid du Colombier 7863b86f2f8SDavid du Colombier if(0)fprint(2, "%s: fsAlloc %ud type=%d tag = %ux\n", argv0, addr, type, tag); 7875e96a66cSDavid du Colombier lastAlloc = addr; 7887abd426fSDavid du Colombier fl->nused++; 789*d7aba6c3SDavid du Colombier qunlock(&fl->lk); 7905e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 7915e96a66cSDavid du Colombier return b; 7925e96a66cSDavid du Colombier } 7935e96a66cSDavid du Colombier 7940b9a5132SDavid du Colombier int 7950b9a5132SDavid du Colombier cacheDirty(Cache *c) 7960b9a5132SDavid du Colombier { 7970b9a5132SDavid du Colombier return c->ndirty; 7980b9a5132SDavid du Colombier } 7990b9a5132SDavid du Colombier 8007abd426fSDavid du Colombier void 8017abd426fSDavid du Colombier cacheCountUsed(Cache *c, u32int epochLow, u32int *used, u32int *total, u32int *bsize) 8027abd426fSDavid du Colombier { 8037abd426fSDavid du Colombier int n; 8047abd426fSDavid du Colombier u32int addr, nused; 8057abd426fSDavid du Colombier Block *b; 8067abd426fSDavid du Colombier Label lab; 8077abd426fSDavid du Colombier FreeList *fl; 8087abd426fSDavid du Colombier 8097abd426fSDavid du Colombier fl = c->fl; 8107abd426fSDavid du Colombier n = c->size / LabelSize; 8117abd426fSDavid du Colombier *bsize = c->size; 812*d7aba6c3SDavid du Colombier qlock(&fl->lk); 8137abd426fSDavid du Colombier if(fl->epochLow == epochLow){ 8147abd426fSDavid du Colombier *used = fl->nused; 8157abd426fSDavid du Colombier *total = fl->end; 816*d7aba6c3SDavid du Colombier qunlock(&fl->lk); 8177abd426fSDavid du Colombier return; 8187abd426fSDavid du Colombier } 8197abd426fSDavid du Colombier b = nil; 8207abd426fSDavid du Colombier nused = 0; 8217abd426fSDavid du Colombier for(addr=0; addr<fl->end; addr++){ 8227abd426fSDavid du Colombier if(addr%n == 0){ 8237abd426fSDavid du Colombier blockPut(b); 8247abd426fSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly); 8257abd426fSDavid du Colombier if(b == nil){ 826*d7aba6c3SDavid du Colombier fprint(2, "%s: flCountUsed: loading %ux: %r\n", 8273b86f2f8SDavid du Colombier argv0, addr/n); 8287abd426fSDavid du Colombier break; 8297abd426fSDavid du Colombier } 8307abd426fSDavid du Colombier } 8317abd426fSDavid du Colombier if(!labelUnpack(&lab, b->data, addr%n)) 8327abd426fSDavid du Colombier continue; 8337abd426fSDavid du Colombier if(lab.state == BsFree) 8347abd426fSDavid du Colombier continue; 835e569ccb5SDavid du Colombier if(lab.state&BsClosed) 836e569ccb5SDavid du Colombier if(lab.epochClose <= epochLow || lab.epoch==lab.epochClose) 8377abd426fSDavid du Colombier continue; 8387abd426fSDavid du Colombier nused++; 8397abd426fSDavid du Colombier } 8407abd426fSDavid du Colombier blockPut(b); 8417abd426fSDavid du Colombier if(addr == fl->end){ 8427abd426fSDavid du Colombier fl->nused = nused; 8437abd426fSDavid du Colombier fl->epochLow = epochLow; 8447abd426fSDavid du Colombier } 8457abd426fSDavid du Colombier *used = nused; 8467abd426fSDavid du Colombier *total = fl->end; 847*d7aba6c3SDavid du Colombier qunlock(&fl->lk); 8487abd426fSDavid du Colombier return; 8497abd426fSDavid du Colombier } 8507abd426fSDavid du Colombier 8515e96a66cSDavid du Colombier static FreeList * 8525e96a66cSDavid du Colombier flAlloc(u32int end) 8535e96a66cSDavid du Colombier { 8545e96a66cSDavid du Colombier FreeList *fl; 8555e96a66cSDavid du Colombier 856*d7aba6c3SDavid du Colombier fl = vtmallocz(sizeof(*fl)); 857b5e190f4SDavid du Colombier fl->last = 0; 8585e96a66cSDavid du Colombier fl->end = end; 8595e96a66cSDavid du Colombier return fl; 8605e96a66cSDavid du Colombier } 8615e96a66cSDavid du Colombier 8625e96a66cSDavid du Colombier static void 8635e96a66cSDavid du Colombier flFree(FreeList *fl) 8645e96a66cSDavid du Colombier { 865*d7aba6c3SDavid du Colombier vtfree(fl); 8665e96a66cSDavid du Colombier } 8675e96a66cSDavid du Colombier 8685e96a66cSDavid du Colombier u32int 8695e96a66cSDavid du Colombier cacheLocalSize(Cache *c, int part) 8705e96a66cSDavid du Colombier { 8715e96a66cSDavid du Colombier return diskSize(c->disk, part); 8725e96a66cSDavid du Colombier } 8735e96a66cSDavid du Colombier 8745e96a66cSDavid du Colombier /* 8755e96a66cSDavid du Colombier * The thread that has locked b may refer to it by 8765e96a66cSDavid du Colombier * multiple names. Nlock counts the number of 8775e96a66cSDavid du Colombier * references the locking thread holds. It will call 8785e96a66cSDavid du Colombier * blockPut once per reference. 8795e96a66cSDavid du Colombier */ 8805e96a66cSDavid du Colombier void 8815e96a66cSDavid du Colombier blockDupLock(Block *b) 8825e96a66cSDavid du Colombier { 8835e96a66cSDavid du Colombier assert(b->nlock > 0); 8845e96a66cSDavid du Colombier b->nlock++; 8855e96a66cSDavid du Colombier } 8865e96a66cSDavid du Colombier 8875e96a66cSDavid du Colombier /* 8885e96a66cSDavid du Colombier * we're done with the block. 8895e96a66cSDavid du Colombier * unlock it. can't use it after calling this. 8905e96a66cSDavid du Colombier */ 8915e96a66cSDavid du Colombier void 8925e96a66cSDavid du Colombier blockPut(Block* b) 8935e96a66cSDavid du Colombier { 8945e96a66cSDavid du Colombier Cache *c; 8955e96a66cSDavid du Colombier 8965e96a66cSDavid du Colombier if(b == nil) 8975e96a66cSDavid du Colombier return; 8985e96a66cSDavid du Colombier 8993b86f2f8SDavid 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)); 9005e96a66cSDavid du Colombier 9015e96a66cSDavid du Colombier if(b->iostate == BioDirty) 9025e96a66cSDavid du Colombier bwatchDependency(b); 9035e96a66cSDavid du Colombier 9045e96a66cSDavid du Colombier if(--b->nlock > 0) 9055e96a66cSDavid du Colombier return; 9065e96a66cSDavid du Colombier 9075e96a66cSDavid du Colombier /* 9085e96a66cSDavid du Colombier * b->nlock should probably stay at zero while 909*d7aba6c3SDavid du Colombier * the block is unlocked, but diskThread and rsleep 910*d7aba6c3SDavid du Colombier * conspire to assume that they can just qlock(&b->lk); blockPut(b), 9115e96a66cSDavid du Colombier * so we have to keep b->nlock set to 1 even 9125e96a66cSDavid du Colombier * when the block is unlocked. 9135e96a66cSDavid du Colombier */ 9145e96a66cSDavid du Colombier assert(b->nlock == 0); 9155e96a66cSDavid du Colombier b->nlock = 1; 9165e96a66cSDavid du Colombier // b->pc = 0; 9175e96a66cSDavid du Colombier 9185e96a66cSDavid du Colombier bwatchUnlock(b); 919*d7aba6c3SDavid du Colombier qunlock(&b->lk); 9205e96a66cSDavid du Colombier c = b->c; 921*d7aba6c3SDavid du Colombier qlock(&c->lk); 9225e96a66cSDavid du Colombier 9235e96a66cSDavid du Colombier if(--b->ref > 0){ 924*d7aba6c3SDavid du Colombier qunlock(&c->lk); 9255e96a66cSDavid du Colombier return; 9265e96a66cSDavid du Colombier } 9275e96a66cSDavid du Colombier 9285e96a66cSDavid du Colombier assert(b->ref == 0); 9295e96a66cSDavid du Colombier switch(b->iostate){ 9305e96a66cSDavid du Colombier default: 9315e96a66cSDavid du Colombier b->used = c->now++; 9325e96a66cSDavid du Colombier heapIns(b); 9335e96a66cSDavid du Colombier break; 9345e96a66cSDavid du Colombier case BioEmpty: 9355e96a66cSDavid du Colombier case BioLabel: 9365e96a66cSDavid du Colombier if(c->nheap == 0) 9375e96a66cSDavid du Colombier b->used = c->now++; 9385e96a66cSDavid du Colombier else 9395e96a66cSDavid du Colombier b->used = c->heap[0]->used; 9405e96a66cSDavid du Colombier heapIns(b); 9415e96a66cSDavid du Colombier break; 9425e96a66cSDavid du Colombier case BioDirty: 9435e96a66cSDavid du Colombier break; 9445e96a66cSDavid du Colombier } 945*d7aba6c3SDavid du Colombier qunlock(&c->lk); 9465e96a66cSDavid du Colombier } 9475e96a66cSDavid du Colombier 9485e96a66cSDavid du Colombier /* 949e569ccb5SDavid du Colombier * set the label associated with a block. 9505e96a66cSDavid du Colombier */ 951e569ccb5SDavid du Colombier Block* 952e569ccb5SDavid du Colombier _blockSetLabel(Block *b, Label *l) 9535e96a66cSDavid du Colombier { 954e569ccb5SDavid du Colombier int lpb; 9555e96a66cSDavid du Colombier Block *bb; 9565e96a66cSDavid du Colombier u32int a; 9575e96a66cSDavid du Colombier Cache *c; 9585e96a66cSDavid du Colombier 9595e96a66cSDavid du Colombier c = b->c; 9605e96a66cSDavid du Colombier 961e569ccb5SDavid du Colombier assert(b->part == PartData); 962e569ccb5SDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean || b->iostate == BioDirty); 963e569ccb5SDavid du Colombier lpb = c->size / LabelSize; 964e569ccb5SDavid du Colombier a = b->addr / lpb; 965e569ccb5SDavid du Colombier bb = cacheLocal(c, PartLabel, a, OReadWrite); 966e569ccb5SDavid du Colombier if(bb == nil){ 967e569ccb5SDavid du Colombier blockPut(b); 9685e96a66cSDavid du Colombier return nil; 9695e96a66cSDavid du Colombier } 970e569ccb5SDavid du Colombier b->l = *l; 971e569ccb5SDavid du Colombier labelPack(l, bb->data, b->addr%lpb); 972e569ccb5SDavid du Colombier blockDirty(bb); 973e569ccb5SDavid du Colombier return bb; 974e569ccb5SDavid du Colombier } 975e569ccb5SDavid du Colombier 976e569ccb5SDavid du Colombier int 977e569ccb5SDavid du Colombier blockSetLabel(Block *b, Label *l, int allocating) 978e569ccb5SDavid du Colombier { 979e569ccb5SDavid du Colombier Block *lb; 980e569ccb5SDavid du Colombier Label oldl; 981e569ccb5SDavid du Colombier 982e569ccb5SDavid du Colombier oldl = b->l; 983e569ccb5SDavid du Colombier lb = _blockSetLabel(b, l); 984e569ccb5SDavid du Colombier if(lb == nil) 985e569ccb5SDavid du Colombier return 0; 9865e96a66cSDavid du Colombier 9875e96a66cSDavid du Colombier /* 988e569ccb5SDavid du Colombier * If we're allocating the block, make sure the label (bl) 989e569ccb5SDavid du Colombier * goes to disk before the data block (b) itself. This is to help 990e569ccb5SDavid du Colombier * the blocks that in turn depend on b. 991e569ccb5SDavid du Colombier * 992e569ccb5SDavid du Colombier * Suppose bx depends on (must be written out after) b. 993e569ccb5SDavid du Colombier * Once we write b we'll think it's safe to write bx. 994e569ccb5SDavid du Colombier * Bx can't get at b unless it has a valid label, though. 995e569ccb5SDavid du Colombier * 996e569ccb5SDavid du Colombier * Allocation is the only case in which having a current label 997e569ccb5SDavid du Colombier * is vital because: 998e569ccb5SDavid du Colombier * 999e569ccb5SDavid du Colombier * - l.type is set at allocation and never changes. 1000e569ccb5SDavid du Colombier * - l.tag is set at allocation and never changes. 1001e569ccb5SDavid du Colombier * - l.state is not checked when we load blocks. 1002e569ccb5SDavid du Colombier * - the archiver cares deeply about l.state being 1003e569ccb5SDavid du Colombier * BaActive vs. BaCopied, but that's handled 1004e569ccb5SDavid du Colombier * by direct calls to _blockSetLabel. 10055e96a66cSDavid du Colombier */ 10065e96a66cSDavid du Colombier 1007e569ccb5SDavid du Colombier if(allocating) 1008e569ccb5SDavid du Colombier blockDependency(b, lb, -1, nil, nil); 1009e569ccb5SDavid du Colombier blockPut(lb); 1010e569ccb5SDavid du Colombier return 1; 10115e96a66cSDavid du Colombier } 10125e96a66cSDavid du Colombier 10135e96a66cSDavid du Colombier /* 101461201b97SDavid du Colombier * Record that bb must be written out before b. 101561201b97SDavid du Colombier * If index is given, we're about to overwrite the score/e 101661201b97SDavid du Colombier * at that index in the block. Save the old value so we 10175e96a66cSDavid du Colombier * can write a safer ``old'' version of the block if pressed. 10185e96a66cSDavid du Colombier */ 10195e96a66cSDavid du Colombier void 102061201b97SDavid du Colombier blockDependency(Block *b, Block *bb, int index, uchar *score, Entry *e) 10215e96a66cSDavid du Colombier { 10225e96a66cSDavid du Colombier BList *p; 10235e96a66cSDavid du Colombier 10245e96a66cSDavid du Colombier if(bb->iostate == BioClean) 10255e96a66cSDavid du Colombier return; 10265e96a66cSDavid du Colombier 102761201b97SDavid du Colombier /* 102861201b97SDavid du Colombier * Dependencies for blocks containing Entry structures 102961201b97SDavid du Colombier * or scores must always be explained. The problem with 103061201b97SDavid du Colombier * only explaining some of them is this. Suppose we have two 103161201b97SDavid du Colombier * dependencies for the same field, the first explained 103261201b97SDavid du Colombier * and the second not. We try to write the block when the first 103361201b97SDavid du Colombier * dependency is not written but the second is. We will roll back 103461201b97SDavid du Colombier * the first change even though the second trumps it. 103561201b97SDavid du Colombier */ 103661201b97SDavid du Colombier if(index == -1 && bb->part == PartData) 103761201b97SDavid du Colombier assert(b->l.type == BtData); 103861201b97SDavid du Colombier 10397abd426fSDavid du Colombier if(bb->iostate != BioDirty){ 10403b86f2f8SDavid du Colombier fprint(2, "%s: %d:%x:%d iostate is %d in blockDependency\n", 10413b86f2f8SDavid du Colombier argv0, bb->part, bb->addr, bb->l.type, bb->iostate); 10427abd426fSDavid du Colombier abort(); 10437abd426fSDavid du Colombier } 10445e96a66cSDavid du Colombier 10455e96a66cSDavid du Colombier p = blistAlloc(bb); 10465e96a66cSDavid du Colombier if(p == nil) 10475e96a66cSDavid du Colombier return; 10485e96a66cSDavid du Colombier 10497f1bc48aSDavid du Colombier assert(bb->iostate == BioDirty); 10503b86f2f8SDavid 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); 10515e96a66cSDavid du Colombier 10525e96a66cSDavid du Colombier p->part = bb->part; 10535e96a66cSDavid du Colombier p->addr = bb->addr; 10545e96a66cSDavid du Colombier p->type = bb->l.type; 10555e96a66cSDavid du Colombier p->vers = bb->vers; 10565e96a66cSDavid du Colombier p->index = index; 105761201b97SDavid du Colombier if(p->index >= 0){ 105861201b97SDavid du Colombier /* 105961201b97SDavid du Colombier * This test would just be b->l.type==BtDir except 106061201b97SDavid du Colombier * we need to exclude the super block. 106161201b97SDavid du Colombier */ 106261201b97SDavid du Colombier if(b->l.type == BtDir && b->part == PartData) 106361201b97SDavid du Colombier entryPack(e, p->old.entry, 0); 106461201b97SDavid du Colombier else 106561201b97SDavid du Colombier memmove(p->old.score, score, VtScoreSize); 106661201b97SDavid du Colombier } 10675e96a66cSDavid du Colombier p->next = b->prior; 10685e96a66cSDavid du Colombier b->prior = p; 10695e96a66cSDavid du Colombier } 10705e96a66cSDavid du Colombier 10715e96a66cSDavid du Colombier /* 10725e96a66cSDavid du Colombier * Mark an in-memory block as dirty. If there are too many 1073fe853e23SDavid du Colombier * dirty blocks, start writing some out to disk. 10745e96a66cSDavid du Colombier * 1075fe853e23SDavid du Colombier * If there were way too many dirty blocks, we used to 1076fe853e23SDavid du Colombier * try to do some flushing ourselves, but it's just too dangerous -- 1077fe853e23SDavid du Colombier * it implies that the callers cannot have any of our priors locked, 1078fe853e23SDavid du Colombier * but this is hard to avoid in some cases. 10795e96a66cSDavid du Colombier */ 10805e96a66cSDavid du Colombier int 10815e96a66cSDavid du Colombier blockDirty(Block *b) 10825e96a66cSDavid du Colombier { 10835e96a66cSDavid du Colombier Cache *c; 10845e96a66cSDavid du Colombier 10855e96a66cSDavid du Colombier c = b->c; 10865e96a66cSDavid du Colombier 10875e96a66cSDavid du Colombier assert(b->part != PartVenti); 10885e96a66cSDavid du Colombier 10895e96a66cSDavid du Colombier if(b->iostate == BioDirty) 10905e96a66cSDavid du Colombier return 1; 1091e12a9870SDavid du Colombier assert(b->iostate == BioClean || b->iostate == BioLabel); 10925e96a66cSDavid du Colombier 1093*d7aba6c3SDavid du Colombier qlock(&c->lk); 109461201b97SDavid du Colombier b->iostate = BioDirty; 10955e96a66cSDavid du Colombier c->ndirty++; 10965e96a66cSDavid du Colombier if(c->ndirty > (c->maxdirty>>1)) 1097*d7aba6c3SDavid du Colombier rwakeup(&c->flush); 1098*d7aba6c3SDavid du Colombier qunlock(&c->lk); 10995e96a66cSDavid du Colombier 11005e96a66cSDavid du Colombier return 1; 11015e96a66cSDavid du Colombier } 11025e96a66cSDavid du Colombier 11035e96a66cSDavid du Colombier /* 110461201b97SDavid du Colombier * We've decided to write out b. Maybe b has some pointers to blocks 110561201b97SDavid du Colombier * that haven't yet been written to disk. If so, construct a slightly out-of-date 110661201b97SDavid du Colombier * copy of b that is safe to write out. (diskThread will make sure the block 11075e96a66cSDavid du Colombier * remains marked as dirty.) 11085e96a66cSDavid du Colombier */ 11095e96a66cSDavid du Colombier uchar * 11105e96a66cSDavid du Colombier blockRollback(Block *b, uchar *buf) 11115e96a66cSDavid du Colombier { 11125e96a66cSDavid du Colombier u32int addr; 11135e96a66cSDavid du Colombier BList *p; 11145e96a66cSDavid du Colombier Super super; 11155e96a66cSDavid du Colombier 11165e96a66cSDavid du Colombier /* easy case */ 11175e96a66cSDavid du Colombier if(b->prior == nil) 11185e96a66cSDavid du Colombier return b->data; 11195e96a66cSDavid du Colombier 11205e96a66cSDavid du Colombier memmove(buf, b->data, b->c->size); 11215e96a66cSDavid du Colombier for(p=b->prior; p; p=p->next){ 11225e96a66cSDavid du Colombier /* 11235e96a66cSDavid du Colombier * we know p->index >= 0 because blockWrite has vetted this block for us. 11245e96a66cSDavid du Colombier */ 11255e96a66cSDavid du Colombier assert(p->index >= 0); 11265e96a66cSDavid du Colombier assert(b->part == PartSuper || (b->part == PartData && b->l.type != BtData)); 11275e96a66cSDavid du Colombier if(b->part == PartSuper){ 11285e96a66cSDavid du Colombier assert(p->index == 0); 11295e96a66cSDavid du Colombier superUnpack(&super, buf); 113061201b97SDavid du Colombier addr = globalToLocal(p->old.score); 11315e96a66cSDavid du Colombier if(addr == NilBlock){ 11323b86f2f8SDavid du Colombier fprint(2, "%s: rolling back super block: " 11333b86f2f8SDavid du Colombier "bad replacement addr %V\n", 11343b86f2f8SDavid du Colombier argv0, p->old.score); 11355e96a66cSDavid du Colombier abort(); 11365e96a66cSDavid du Colombier } 11375e96a66cSDavid du Colombier super.active = addr; 11385e96a66cSDavid du Colombier superPack(&super, buf); 11395e96a66cSDavid du Colombier continue; 11405e96a66cSDavid du Colombier } 114161201b97SDavid du Colombier if(b->l.type == BtDir) 114261201b97SDavid du Colombier memmove(buf+p->index*VtEntrySize, p->old.entry, VtEntrySize); 114361201b97SDavid du Colombier else 114461201b97SDavid du Colombier memmove(buf+p->index*VtScoreSize, p->old.score, VtScoreSize); 11455e96a66cSDavid du Colombier } 11465e96a66cSDavid du Colombier return buf; 11475e96a66cSDavid du Colombier } 11485e96a66cSDavid du Colombier 11495e96a66cSDavid du Colombier /* 11505e96a66cSDavid du Colombier * Try to write block b. 11515e96a66cSDavid du Colombier * If b depends on other blocks: 11525e96a66cSDavid du Colombier * 11535e96a66cSDavid du Colombier * If the block has been written out, remove the dependency. 11547abd426fSDavid du Colombier * If the dependency is replaced by a more recent dependency, 11557abd426fSDavid du Colombier * throw it out. 11565e96a66cSDavid du Colombier * If we know how to write out an old version of b that doesn't 11575e96a66cSDavid du Colombier * depend on it, do that. 11585e96a66cSDavid du Colombier * 11595e96a66cSDavid du Colombier * Otherwise, bail. 11605e96a66cSDavid du Colombier */ 11615e96a66cSDavid du Colombier int 1162e12a9870SDavid du Colombier blockWrite(Block *b, int waitlock) 11635e96a66cSDavid du Colombier { 116461201b97SDavid du Colombier uchar *dmap; 11655e96a66cSDavid du Colombier Cache *c; 11665e96a66cSDavid du Colombier BList *p, **pp; 11675e96a66cSDavid du Colombier Block *bb; 11685e96a66cSDavid du Colombier int lockfail; 11695e96a66cSDavid du Colombier 11705e96a66cSDavid du Colombier c = b->c; 11715e96a66cSDavid du Colombier 11725e96a66cSDavid du Colombier if(b->iostate != BioDirty) 11735e96a66cSDavid du Colombier return 1; 11745e96a66cSDavid du Colombier 117561201b97SDavid du Colombier dmap = b->dmap; 117661201b97SDavid du Colombier memset(dmap, 0, c->ndmap); 11775e96a66cSDavid du Colombier pp = &b->prior; 11785e96a66cSDavid du Colombier for(p=*pp; p; p=*pp){ 117961201b97SDavid du Colombier if(p->index >= 0){ 118061201b97SDavid du Colombier /* more recent dependency has succeeded; this one can go */ 118161201b97SDavid du Colombier if(dmap[p->index/8] & (1<<(p->index%8))) 118261201b97SDavid du Colombier goto ignblock; 118361201b97SDavid du Colombier } 118461201b97SDavid du Colombier 118561201b97SDavid du Colombier lockfail = 0; 1186e12a9870SDavid du Colombier bb = _cacheLocalLookup(c, p->part, p->addr, p->vers, waitlock, 11872651f6bbSDavid du Colombier &lockfail); 11885e96a66cSDavid du Colombier if(bb == nil){ 11895e96a66cSDavid du Colombier if(lockfail) 11905e96a66cSDavid du Colombier return 0; 119161201b97SDavid du Colombier /* block not in cache => was written already */ 119261201b97SDavid du Colombier dmap[p->index/8] |= 1<<(p->index%8); 119361201b97SDavid du Colombier goto ignblock; 11945e96a66cSDavid du Colombier } 11955e96a66cSDavid du Colombier 11965e96a66cSDavid du Colombier /* 11975e96a66cSDavid du Colombier * same version of block is still in cache. 11985e96a66cSDavid du Colombier * 11995e96a66cSDavid du Colombier * the assertion is true because the block still has version p->vers, 12005e96a66cSDavid du Colombier * which means it hasn't been written out since we last saw it. 12015e96a66cSDavid du Colombier */ 12027abd426fSDavid du Colombier if(bb->iostate != BioDirty){ 12033b86f2f8SDavid du Colombier fprint(2, "%s: %d:%x:%d iostate is %d in blockWrite\n", 12043b86f2f8SDavid du Colombier argv0, bb->part, bb->addr, bb->l.type, bb->iostate); 12057abd426fSDavid du Colombier /* probably BioWriting if it happens? */ 12067f1bc48aSDavid du Colombier if(bb->iostate == BioClean) 12077f1bc48aSDavid du Colombier goto ignblock; 12087abd426fSDavid du Colombier } 12097abd426fSDavid du Colombier 12105e96a66cSDavid du Colombier blockPut(bb); 12115e96a66cSDavid du Colombier 12125e96a66cSDavid du Colombier if(p->index < 0){ 12135e96a66cSDavid du Colombier /* 12145e96a66cSDavid du Colombier * We don't know how to temporarily undo 12155e96a66cSDavid du Colombier * b's dependency on bb, so just don't write b yet. 12165e96a66cSDavid du Colombier */ 12173b86f2f8SDavid du Colombier if(0) fprint(2, "%s: blockWrite skipping %d %x %d %d; need to write %d %x %d\n", 12183b86f2f8SDavid du Colombier argv0, b->part, b->addr, b->vers, b->l.type, p->part, p->addr, bb->vers); 12195e96a66cSDavid du Colombier return 0; 12205e96a66cSDavid du Colombier } 12215e96a66cSDavid du Colombier /* keep walking down the list */ 12225e96a66cSDavid du Colombier pp = &p->next; 122361201b97SDavid du Colombier continue; 122461201b97SDavid du Colombier 122561201b97SDavid du Colombier ignblock: 122661201b97SDavid du Colombier *pp = p->next; 122761201b97SDavid du Colombier blistFree(c, p); 122861201b97SDavid du Colombier continue; 12295e96a66cSDavid du Colombier } 12305e96a66cSDavid du Colombier 1231867bfcc6SDavid du Colombier /* 1232867bfcc6SDavid du Colombier * DiskWrite must never be called with a double-locked block. 1233867bfcc6SDavid du Colombier * This call to diskWrite is okay because blockWrite is only called 1234867bfcc6SDavid du Colombier * from the cache flush thread, which never double-locks a block. 1235867bfcc6SDavid du Colombier */ 12365e96a66cSDavid du Colombier diskWrite(c->disk, b); 12375e96a66cSDavid du Colombier return 1; 12385e96a66cSDavid du Colombier } 12395e96a66cSDavid du Colombier 12405e96a66cSDavid du Colombier /* 12415e96a66cSDavid du Colombier * Change the I/O state of block b. 12425e96a66cSDavid du Colombier * Just an assignment except for magic in 12435e96a66cSDavid du Colombier * switch statement (read comments there). 12445e96a66cSDavid du Colombier */ 12455e96a66cSDavid du Colombier void 12465e96a66cSDavid du Colombier blockSetIOState(Block *b, int iostate) 12475e96a66cSDavid du Colombier { 12485e96a66cSDavid du Colombier int dowakeup; 12495e96a66cSDavid du Colombier Cache *c; 12505e96a66cSDavid du Colombier BList *p, *q; 12515e96a66cSDavid du Colombier 12523b86f2f8SDavid 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)); 12535e96a66cSDavid du Colombier 12545e96a66cSDavid du Colombier c = b->c; 12555e96a66cSDavid du Colombier 12565e96a66cSDavid du Colombier dowakeup = 0; 12575e96a66cSDavid du Colombier switch(iostate){ 12585e96a66cSDavid du Colombier default: 12595e96a66cSDavid du Colombier abort(); 12605e96a66cSDavid du Colombier case BioEmpty: 12615e96a66cSDavid du Colombier assert(!b->uhead); 12625e96a66cSDavid du Colombier break; 12635e96a66cSDavid du Colombier case BioLabel: 12645e96a66cSDavid du Colombier assert(!b->uhead); 12655e96a66cSDavid du Colombier break; 12665e96a66cSDavid du Colombier case BioClean: 12675e96a66cSDavid du Colombier bwatchDependency(b); 12685e96a66cSDavid du Colombier /* 12695e96a66cSDavid du Colombier * If b->prior is set, it means a write just finished. 12705e96a66cSDavid du Colombier * The prior list isn't needed anymore. 12715e96a66cSDavid du Colombier */ 12725e96a66cSDavid du Colombier for(p=b->prior; p; p=q){ 12735e96a66cSDavid du Colombier q = p->next; 12745e96a66cSDavid du Colombier blistFree(c, p); 12755e96a66cSDavid du Colombier } 12765e96a66cSDavid du Colombier b->prior = nil; 12775e96a66cSDavid du Colombier /* 12785e96a66cSDavid du Colombier * Freeing a block or just finished a write. 12795e96a66cSDavid du Colombier * Move the blocks from the per-block unlink 12805e96a66cSDavid du Colombier * queue to the cache unlink queue. 12815e96a66cSDavid du Colombier */ 12825e96a66cSDavid du Colombier if(b->iostate == BioDirty || b->iostate == BioWriting){ 1283*d7aba6c3SDavid du Colombier qlock(&c->lk); 12845e96a66cSDavid du Colombier c->ndirty--; 128561201b97SDavid du Colombier b->iostate = iostate; /* change here to keep in sync with ndirty */ 12865e96a66cSDavid du Colombier b->vers = c->vers++; 12875e96a66cSDavid du Colombier if(b->uhead){ 12885e96a66cSDavid du Colombier /* add unlink blocks to unlink queue */ 12895e96a66cSDavid du Colombier if(c->uhead == nil){ 12905e96a66cSDavid du Colombier c->uhead = b->uhead; 1291*d7aba6c3SDavid du Colombier rwakeup(&c->unlink); 12925e96a66cSDavid du Colombier }else 12935e96a66cSDavid du Colombier c->utail->next = b->uhead; 12945e96a66cSDavid du Colombier c->utail = b->utail; 12955e96a66cSDavid du Colombier b->uhead = nil; 12965e96a66cSDavid du Colombier } 1297*d7aba6c3SDavid du Colombier qunlock(&c->lk); 12985e96a66cSDavid du Colombier } 12995e96a66cSDavid du Colombier assert(!b->uhead); 13005e96a66cSDavid du Colombier dowakeup = 1; 13015e96a66cSDavid du Colombier break; 13025e96a66cSDavid du Colombier case BioDirty: 13035e96a66cSDavid du Colombier /* 13045e96a66cSDavid du Colombier * Wrote out an old version of the block (see blockRollback). 13055e96a66cSDavid du Colombier * Bump a version count, leave it dirty. 13065e96a66cSDavid du Colombier */ 13075e96a66cSDavid du Colombier if(b->iostate == BioWriting){ 1308*d7aba6c3SDavid du Colombier qlock(&c->lk); 13095e96a66cSDavid du Colombier b->vers = c->vers++; 1310*d7aba6c3SDavid du Colombier qunlock(&c->lk); 13115e96a66cSDavid du Colombier dowakeup = 1; 13125e96a66cSDavid du Colombier } 13135e96a66cSDavid du Colombier break; 13145e96a66cSDavid du Colombier case BioReading: 13155e96a66cSDavid du Colombier case BioWriting: 13165e96a66cSDavid du Colombier /* 13175e96a66cSDavid du Colombier * Adding block to disk queue. Bump reference count. 13185e96a66cSDavid du Colombier * diskThread decs the count later by calling blockPut. 13195e96a66cSDavid du Colombier * This is here because we need to lock c->lk to 13205e96a66cSDavid du Colombier * manipulate the ref count. 13215e96a66cSDavid du Colombier */ 1322*d7aba6c3SDavid du Colombier qlock(&c->lk); 13235e96a66cSDavid du Colombier b->ref++; 1324*d7aba6c3SDavid du Colombier qunlock(&c->lk); 13255e96a66cSDavid du Colombier break; 13265e96a66cSDavid du Colombier case BioReadError: 13275e96a66cSDavid du Colombier case BioVentiError: 13285e96a66cSDavid du Colombier /* 13295e96a66cSDavid du Colombier * Oops. 13305e96a66cSDavid du Colombier */ 13315e96a66cSDavid du Colombier dowakeup = 1; 13325e96a66cSDavid du Colombier break; 13335e96a66cSDavid du Colombier } 13345e96a66cSDavid du Colombier b->iostate = iostate; 13355e96a66cSDavid du Colombier /* 13365e96a66cSDavid du Colombier * Now that the state has changed, we can wake the waiters. 13375e96a66cSDavid du Colombier */ 13385e96a66cSDavid du Colombier if(dowakeup) 1339*d7aba6c3SDavid du Colombier rwakeupall(&b->ioready); 13405e96a66cSDavid du Colombier } 13415e96a66cSDavid du Colombier 1342e569ccb5SDavid du Colombier /* 1343e569ccb5SDavid du Colombier * The active file system is a tree of blocks. 1344e569ccb5SDavid du Colombier * When we add snapshots to the mix, the entire file system 1345e569ccb5SDavid du Colombier * becomes a dag and thus requires a bit more care. 1346e569ccb5SDavid du Colombier * 1347e569ccb5SDavid du Colombier * The life of the file system is divided into epochs. A snapshot 1348e569ccb5SDavid du Colombier * ends one epoch and begins the next. Each file system block 1349e569ccb5SDavid du Colombier * is marked with the epoch in which it was created (b.epoch). 1350e569ccb5SDavid du Colombier * When the block is unlinked from the file system (closed), it is marked 1351e569ccb5SDavid du Colombier * with the epoch in which it was removed (b.epochClose). 1352e569ccb5SDavid du Colombier * Once we have discarded or archived all snapshots up to 1353e569ccb5SDavid du Colombier * b.epochClose, we can reclaim the block. 1354e569ccb5SDavid du Colombier * 1355e569ccb5SDavid du Colombier * If a block was created in a past epoch but is not yet closed, 1356e569ccb5SDavid du Colombier * it is treated as copy-on-write. Of course, in order to insert the 1357e569ccb5SDavid du Colombier * new pointer into the tree, the parent must be made writable, 1358e569ccb5SDavid du Colombier * and so on up the tree. The recursion stops because the root 1359e569ccb5SDavid du Colombier * block is always writable. 1360e569ccb5SDavid du Colombier * 1361e569ccb5SDavid du Colombier * If blocks are never closed, they will never be reused, and 1362e569ccb5SDavid du Colombier * we will run out of disk space. But marking a block as closed 1363e569ccb5SDavid du Colombier * requires some care about dependencies and write orderings. 1364e569ccb5SDavid du Colombier * 1365e569ccb5SDavid du Colombier * (1) If a block p points at a copy-on-write block b and we 1366e569ccb5SDavid du Colombier * copy b to create bb, then p must be written out after bb and 1367e569ccb5SDavid du Colombier * lbb (bb's label block). 1368e569ccb5SDavid du Colombier * 1369e569ccb5SDavid du Colombier * (2) We have to mark b as closed, but only after we switch 1370e569ccb5SDavid du Colombier * the pointer, so lb must be written out after p. In fact, we 1371e569ccb5SDavid du Colombier * can't even update the in-memory copy, or the cache might 1372e569ccb5SDavid du Colombier * mistakenly give out b for reuse before p gets written. 1373e569ccb5SDavid du Colombier * 1374e569ccb5SDavid du Colombier * CacheAllocBlock's call to blockSetLabel records a "bb after lbb" dependency. 1375e569ccb5SDavid du Colombier * The caller is expected to record a "p after bb" dependency 1376e569ccb5SDavid du Colombier * to finish (1), and also expected to call blockRemoveLink 1377e569ccb5SDavid du Colombier * to arrange for (2) to happen once p is written. 1378e569ccb5SDavid du Colombier * 1379e569ccb5SDavid du Colombier * Until (2) happens, some pieces of the code (e.g., the archiver) 1380e569ccb5SDavid du Colombier * still need to know whether a block has been copied, so we 1381e569ccb5SDavid du Colombier * set the BsCopied bit in the label and force that to disk *before* 1382e569ccb5SDavid du Colombier * the copy gets written out. 1383e569ccb5SDavid du Colombier */ 1384e569ccb5SDavid du Colombier Block* 1385e569ccb5SDavid du Colombier blockCopy(Block *b, u32int tag, u32int ehi, u32int elo) 1386e569ccb5SDavid du Colombier { 1387e569ccb5SDavid du Colombier Block *bb, *lb; 1388e569ccb5SDavid du Colombier Label l; 1389e569ccb5SDavid du Colombier 1390e569ccb5SDavid du Colombier if((b->l.state&BsClosed) || b->l.epoch >= ehi) 13913b86f2f8SDavid du Colombier fprint(2, "%s: blockCopy %#ux %L but fs is [%ud,%ud]\n", 13923b86f2f8SDavid du Colombier argv0, b->addr, &b->l, elo, ehi); 1393e569ccb5SDavid du Colombier 1394e569ccb5SDavid du Colombier bb = cacheAllocBlock(b->c, b->l.type, tag, ehi, elo); 1395e569ccb5SDavid du Colombier if(bb == nil){ 1396e569ccb5SDavid du Colombier blockPut(b); 1397e569ccb5SDavid du Colombier return nil; 1398e569ccb5SDavid du Colombier } 1399e569ccb5SDavid du Colombier 1400e569ccb5SDavid du Colombier /* 1401e569ccb5SDavid du Colombier * Update label so we know the block has been copied. 1402e569ccb5SDavid du Colombier * (It will be marked closed once it has been unlinked from 1403e569ccb5SDavid du Colombier * the tree.) This must follow cacheAllocBlock since we 1404e569ccb5SDavid du Colombier * can't be holding onto lb when we call cacheAllocBlock. 1405e569ccb5SDavid du Colombier */ 1406e569ccb5SDavid du Colombier if((b->l.state&BsCopied)==0) 1407e569ccb5SDavid du Colombier if(b->part == PartData){ /* not the superblock */ 1408e569ccb5SDavid du Colombier l = b->l; 1409e569ccb5SDavid du Colombier l.state |= BsCopied; 1410e569ccb5SDavid du Colombier lb = _blockSetLabel(b, &l); 1411e569ccb5SDavid du Colombier if(lb == nil){ 1412e569ccb5SDavid du Colombier /* can't set label => can't copy block */ 1413e569ccb5SDavid du Colombier blockPut(b); 1414e569ccb5SDavid du Colombier l.type = BtMax; 1415e569ccb5SDavid du Colombier l.state = BsFree; 1416e569ccb5SDavid du Colombier l.epoch = 0; 1417e569ccb5SDavid du Colombier l.epochClose = 0; 1418e569ccb5SDavid du Colombier l.tag = 0; 1419e569ccb5SDavid du Colombier blockSetLabel(bb, &l, 0); 1420e569ccb5SDavid du Colombier blockPut(bb); 1421e569ccb5SDavid du Colombier return nil; 1422e569ccb5SDavid du Colombier } 1423e569ccb5SDavid du Colombier blockDependency(bb, lb, -1, nil, nil); 1424e569ccb5SDavid du Colombier blockPut(lb); 1425e569ccb5SDavid du Colombier } 1426e569ccb5SDavid du Colombier 1427e569ccb5SDavid du Colombier memmove(bb->data, b->data, b->c->size); 1428e569ccb5SDavid du Colombier blockDirty(bb); 1429e569ccb5SDavid du Colombier blockPut(b); 1430e569ccb5SDavid du Colombier return bb; 1431e569ccb5SDavid du Colombier } 1432e569ccb5SDavid du Colombier 1433e569ccb5SDavid du Colombier /* 1434e569ccb5SDavid du Colombier * Block b once pointed at the block bb at addr/type/tag, but no longer does. 1435e569ccb5SDavid du Colombier * If recurse is set, we are unlinking all of bb's children as well. 1436e569ccb5SDavid du Colombier * 1437e569ccb5SDavid du Colombier * We can't reclaim bb (or its kids) until the block b gets written to disk. We add 1438e569ccb5SDavid du Colombier * the relevant information to b's list of unlinked blocks. Once b is written, 1439e569ccb5SDavid du Colombier * the list will be queued for processing. 1440e569ccb5SDavid du Colombier * 1441e569ccb5SDavid du Colombier * If b depends on bb, it doesn't anymore, so we remove bb from the prior list. 1442e569ccb5SDavid du Colombier */ 1443e569ccb5SDavid du Colombier void 1444e569ccb5SDavid du Colombier blockRemoveLink(Block *b, u32int addr, int type, u32int tag, int recurse) 1445e569ccb5SDavid du Colombier { 1446e569ccb5SDavid du Colombier BList *p, **pp, bl; 1447e569ccb5SDavid du Colombier 1448e569ccb5SDavid du Colombier /* remove bb from prior list */ 1449e569ccb5SDavid du Colombier for(pp=&b->prior; (p=*pp)!=nil; ){ 1450e569ccb5SDavid du Colombier if(p->part == PartData && p->addr == addr){ 1451e569ccb5SDavid du Colombier *pp = p->next; 1452e569ccb5SDavid du Colombier blistFree(b->c, p); 1453e569ccb5SDavid du Colombier }else 1454e569ccb5SDavid du Colombier pp = &p->next; 1455e569ccb5SDavid du Colombier } 1456e569ccb5SDavid du Colombier 1457e569ccb5SDavid du Colombier bl.part = PartData; 1458e569ccb5SDavid du Colombier bl.addr = addr; 1459e569ccb5SDavid du Colombier bl.type = type; 1460e569ccb5SDavid du Colombier bl.tag = tag; 1461e569ccb5SDavid du Colombier if(b->l.epoch == 0) 1462e569ccb5SDavid du Colombier assert(b->part == PartSuper); 1463e569ccb5SDavid du Colombier bl.epoch = b->l.epoch; 1464e569ccb5SDavid du Colombier bl.next = nil; 1465e569ccb5SDavid du Colombier bl.recurse = recurse; 1466e569ccb5SDavid du Colombier 1467e12a9870SDavid du Colombier if(b->part == PartSuper && b->iostate == BioClean) 1468e12a9870SDavid du Colombier p = nil; 1469e12a9870SDavid du Colombier else 1470e569ccb5SDavid du Colombier p = blistAlloc(b); 1471e569ccb5SDavid du Colombier if(p == nil){ 1472e569ccb5SDavid du Colombier /* 1473e12a9870SDavid du Colombier * b has already been written to disk. 1474e569ccb5SDavid du Colombier */ 1475e569ccb5SDavid du Colombier doRemoveLink(b->c, &bl); 1476e569ccb5SDavid du Colombier return; 1477e569ccb5SDavid du Colombier } 1478e569ccb5SDavid du Colombier 1479e569ccb5SDavid du Colombier /* Uhead is only processed when the block goes from Dirty -> Clean */ 1480e569ccb5SDavid du Colombier assert(b->iostate == BioDirty); 1481e569ccb5SDavid du Colombier 1482e569ccb5SDavid du Colombier *p = bl; 1483e569ccb5SDavid du Colombier if(b->uhead == nil) 1484e569ccb5SDavid du Colombier b->uhead = p; 1485e569ccb5SDavid du Colombier else 1486e569ccb5SDavid du Colombier b->utail->next = p; 1487e569ccb5SDavid du Colombier b->utail = p; 1488e569ccb5SDavid du Colombier } 1489e569ccb5SDavid du Colombier 1490e569ccb5SDavid du Colombier /* 1491e569ccb5SDavid du Colombier * Process removal of a single block and perhaps its children. 1492e569ccb5SDavid du Colombier */ 1493e569ccb5SDavid du Colombier static void 1494e569ccb5SDavid du Colombier doRemoveLink(Cache *c, BList *p) 1495e569ccb5SDavid du Colombier { 14960b9a5132SDavid du Colombier int i, n, recurse; 1497e569ccb5SDavid du Colombier u32int a; 1498e569ccb5SDavid du Colombier Block *b; 1499e569ccb5SDavid du Colombier Label l; 1500e569ccb5SDavid du Colombier BList bl; 1501e569ccb5SDavid du Colombier 15020b9a5132SDavid du Colombier recurse = (p->recurse && p->type != BtData && p->type != BtDir); 15030b9a5132SDavid du Colombier 15040b9a5132SDavid du Colombier /* 15050b9a5132SDavid du Colombier * We're not really going to overwrite b, but if we're not 15060b9a5132SDavid du Colombier * going to look at its contents, there is no point in reading 15070b9a5132SDavid du Colombier * them from the disk. 15080b9a5132SDavid du Colombier */ 15090b9a5132SDavid du Colombier b = cacheLocalData(c, p->addr, p->type, p->tag, recurse ? OReadOnly : OOverWrite, 0); 1510e569ccb5SDavid du Colombier if(b == nil) 1511e569ccb5SDavid du Colombier return; 1512e569ccb5SDavid du Colombier 1513e569ccb5SDavid du Colombier /* 1514e569ccb5SDavid du Colombier * When we're unlinking from the superblock, close with the next epoch. 1515e569ccb5SDavid du Colombier */ 1516e569ccb5SDavid du Colombier if(p->epoch == 0) 1517e569ccb5SDavid du Colombier p->epoch = b->l.epoch+1; 1518e569ccb5SDavid du Colombier 1519e569ccb5SDavid du Colombier /* sanity check */ 1520e569ccb5SDavid du Colombier if(b->l.epoch > p->epoch){ 15213b86f2f8SDavid du Colombier fprint(2, "%s: doRemoveLink: strange epoch %ud > %ud\n", 15223b86f2f8SDavid du Colombier argv0, b->l.epoch, p->epoch); 1523e569ccb5SDavid du Colombier blockPut(b); 1524e569ccb5SDavid du Colombier return; 1525e569ccb5SDavid du Colombier } 1526e569ccb5SDavid du Colombier 15270b9a5132SDavid du Colombier if(recurse){ 1528e569ccb5SDavid du Colombier n = c->size / VtScoreSize; 1529e569ccb5SDavid du Colombier for(i=0; i<n; i++){ 1530e569ccb5SDavid du Colombier a = globalToLocal(b->data + i*VtScoreSize); 1531e569ccb5SDavid du Colombier if(a == NilBlock || !readLabel(c, &l, a)) 1532e569ccb5SDavid du Colombier continue; 1533e569ccb5SDavid du Colombier if(l.state&BsClosed) 1534e569ccb5SDavid du Colombier continue; 1535e569ccb5SDavid du Colombier /* 1536e569ccb5SDavid du Colombier * If stack space becomes an issue... 1537e569ccb5SDavid du Colombier p->addr = a; 1538e569ccb5SDavid du Colombier p->type = l.type; 1539e569ccb5SDavid du Colombier p->tag = l.tag; 1540e569ccb5SDavid du Colombier doRemoveLink(c, p); 1541e569ccb5SDavid du Colombier */ 1542e569ccb5SDavid du Colombier 1543e569ccb5SDavid du Colombier bl.part = PartData; 1544e569ccb5SDavid du Colombier bl.addr = a; 1545e569ccb5SDavid du Colombier bl.type = l.type; 1546e569ccb5SDavid du Colombier bl.tag = l.tag; 1547e569ccb5SDavid du Colombier bl.epoch = p->epoch; 1548e569ccb5SDavid du Colombier bl.next = nil; 1549e569ccb5SDavid du Colombier bl.recurse = 1; 15500b9a5132SDavid du Colombier /* give up the block lock - share with others */ 15510b9a5132SDavid du Colombier blockPut(b); 1552e569ccb5SDavid du Colombier doRemoveLink(c, &bl); 15530b9a5132SDavid du Colombier b = cacheLocalData(c, p->addr, p->type, p->tag, OReadOnly, 0); 15540b9a5132SDavid du Colombier if(b == nil){ 15553b86f2f8SDavid du Colombier fprint(2, "%s: warning: lost block in doRemoveLink\n", 15563b86f2f8SDavid du Colombier argv0); 15570b9a5132SDavid du Colombier return; 15580b9a5132SDavid du Colombier } 1559e569ccb5SDavid du Colombier } 1560e569ccb5SDavid du Colombier } 1561e569ccb5SDavid du Colombier 1562e569ccb5SDavid du Colombier l = b->l; 1563e569ccb5SDavid du Colombier l.state |= BsClosed; 1564e569ccb5SDavid du Colombier l.epochClose = p->epoch; 1565225077b0SDavid du Colombier if(l.epochClose == l.epoch){ 1566*d7aba6c3SDavid du Colombier qlock(&c->fl->lk); 1567225077b0SDavid du Colombier if(l.epoch == c->fl->epochLow) 1568225077b0SDavid du Colombier c->fl->nused--; 1569225077b0SDavid du Colombier blockSetLabel(b, &l, 0); 1570*d7aba6c3SDavid du Colombier qunlock(&c->fl->lk); 1571225077b0SDavid du Colombier }else 1572e569ccb5SDavid du Colombier blockSetLabel(b, &l, 0); 1573e569ccb5SDavid du Colombier blockPut(b); 1574e569ccb5SDavid du Colombier } 1575e569ccb5SDavid du Colombier 1576e569ccb5SDavid du Colombier /* 1577e569ccb5SDavid du Colombier * Allocate a BList so that we can record a dependency 1578e569ccb5SDavid du Colombier * or queue a removal related to block b. 1579e569ccb5SDavid du Colombier * If we can't find a BList, we write out b and return nil. 1580e569ccb5SDavid du Colombier */ 1581e569ccb5SDavid du Colombier static BList * 1582e569ccb5SDavid du Colombier blistAlloc(Block *b) 1583e569ccb5SDavid du Colombier { 1584e569ccb5SDavid du Colombier Cache *c; 1585e569ccb5SDavid du Colombier BList *p; 1586e569ccb5SDavid du Colombier 1587e569ccb5SDavid du Colombier if(b->iostate != BioDirty){ 1588e569ccb5SDavid du Colombier /* 1589e569ccb5SDavid du Colombier * should not happen anymore - 1590e569ccb5SDavid du Colombier * blockDirty used to flush but no longer does. 1591e569ccb5SDavid du Colombier */ 1592e569ccb5SDavid du Colombier assert(b->iostate == BioClean); 15933b86f2f8SDavid du Colombier fprint(2, "%s: blistAlloc: called on clean block\n", argv0); 1594e569ccb5SDavid du Colombier return nil; 1595e569ccb5SDavid du Colombier } 1596e569ccb5SDavid du Colombier 1597e569ccb5SDavid du Colombier c = b->c; 1598*d7aba6c3SDavid du Colombier qlock(&c->lk); 1599e569ccb5SDavid du Colombier if(c->blfree == nil){ 1600e569ccb5SDavid du Colombier /* 1601e569ccb5SDavid du Colombier * No free BLists. What are our options? 1602e569ccb5SDavid du Colombier */ 1603e569ccb5SDavid du Colombier 1604e569ccb5SDavid du Colombier /* Block has no priors? Just write it. */ 1605e569ccb5SDavid du Colombier if(b->prior == nil){ 1606*d7aba6c3SDavid du Colombier qunlock(&c->lk); 1607e569ccb5SDavid du Colombier diskWriteAndWait(c->disk, b); 1608e569ccb5SDavid du Colombier return nil; 1609e569ccb5SDavid du Colombier } 1610e569ccb5SDavid du Colombier 1611e569ccb5SDavid du Colombier /* 1612e569ccb5SDavid du Colombier * Wake the flush thread, which will hopefully free up 1613e569ccb5SDavid du Colombier * some BLists for us. We used to flush a block from 1614e569ccb5SDavid du Colombier * our own prior list and reclaim that BList, but this is 1615e569ccb5SDavid du Colombier * a no-no: some of the blocks on our prior list may 1616e569ccb5SDavid du Colombier * be locked by our caller. Or maybe their label blocks 1617e569ccb5SDavid du Colombier * are locked by our caller. In any event, it's too hard 1618e569ccb5SDavid du Colombier * to make sure we can do I/O for ourselves. Instead, 1619e569ccb5SDavid du Colombier * we assume the flush thread will find something. 1620e569ccb5SDavid du Colombier * (The flush thread never blocks waiting for a block, 1621e569ccb5SDavid du Colombier * so it can't deadlock like we can.) 1622e569ccb5SDavid du Colombier */ 1623e569ccb5SDavid du Colombier while(c->blfree == nil){ 1624*d7aba6c3SDavid du Colombier rwakeup(&c->flush); 1625*d7aba6c3SDavid du Colombier rsleep(&c->blrend); 16260b9a5132SDavid du Colombier if(c->blfree == nil) 16273b86f2f8SDavid du Colombier fprint(2, "%s: flushing for blists\n", argv0); 1628e569ccb5SDavid du Colombier } 1629e569ccb5SDavid du Colombier } 1630e569ccb5SDavid du Colombier 1631e569ccb5SDavid du Colombier p = c->blfree; 1632e569ccb5SDavid du Colombier c->blfree = p->next; 1633*d7aba6c3SDavid du Colombier qunlock(&c->lk); 1634e569ccb5SDavid du Colombier return p; 1635e569ccb5SDavid du Colombier } 1636e569ccb5SDavid du Colombier 1637e569ccb5SDavid du Colombier static void 1638e569ccb5SDavid du Colombier blistFree(Cache *c, BList *bl) 1639e569ccb5SDavid du Colombier { 1640*d7aba6c3SDavid du Colombier qlock(&c->lk); 1641e569ccb5SDavid du Colombier bl->next = c->blfree; 1642e569ccb5SDavid du Colombier c->blfree = bl; 1643*d7aba6c3SDavid du Colombier rwakeup(&c->blrend); 1644*d7aba6c3SDavid du Colombier qunlock(&c->lk); 1645e569ccb5SDavid du Colombier } 1646e569ccb5SDavid du Colombier 16475e96a66cSDavid du Colombier char* 16485e96a66cSDavid du Colombier bsStr(int state) 16495e96a66cSDavid du Colombier { 16505e96a66cSDavid du Colombier static char s[100]; 16515e96a66cSDavid du Colombier 16525e96a66cSDavid du Colombier if(state == BsFree) 16535e96a66cSDavid du Colombier return "Free"; 16545e96a66cSDavid du Colombier if(state == BsBad) 16555e96a66cSDavid du Colombier return "Bad"; 16565e96a66cSDavid du Colombier 16575e96a66cSDavid du Colombier sprint(s, "%x", state); 16585e96a66cSDavid du Colombier if(!(state&BsAlloc)) 16595e96a66cSDavid du Colombier strcat(s, ",Free"); /* should not happen */ 16605e96a66cSDavid du Colombier if(state&BsCopied) 16615e96a66cSDavid du Colombier strcat(s, ",Copied"); 16625e96a66cSDavid du Colombier if(state&BsVenti) 16635e96a66cSDavid du Colombier strcat(s, ",Venti"); 16645e96a66cSDavid du Colombier if(state&BsClosed) 16655e96a66cSDavid du Colombier strcat(s, ",Closed"); 16665e96a66cSDavid du Colombier return s; 16675e96a66cSDavid du Colombier } 16685e96a66cSDavid du Colombier 16695e96a66cSDavid du Colombier char * 16705e96a66cSDavid du Colombier bioStr(int iostate) 16715e96a66cSDavid du Colombier { 16725e96a66cSDavid du Colombier switch(iostate){ 16735e96a66cSDavid du Colombier default: 16745e96a66cSDavid du Colombier return "Unknown!!"; 16755e96a66cSDavid du Colombier case BioEmpty: 16765e96a66cSDavid du Colombier return "Empty"; 16775e96a66cSDavid du Colombier case BioLabel: 16785e96a66cSDavid du Colombier return "Label"; 16795e96a66cSDavid du Colombier case BioClean: 16805e96a66cSDavid du Colombier return "Clean"; 16815e96a66cSDavid du Colombier case BioDirty: 16825e96a66cSDavid du Colombier return "Dirty"; 16835e96a66cSDavid du Colombier case BioReading: 16845e96a66cSDavid du Colombier return "Reading"; 16855e96a66cSDavid du Colombier case BioWriting: 16865e96a66cSDavid du Colombier return "Writing"; 16875e96a66cSDavid du Colombier case BioReadError: 16885e96a66cSDavid du Colombier return "ReadError"; 16895e96a66cSDavid du Colombier case BioVentiError: 16905e96a66cSDavid du Colombier return "VentiError"; 16915e96a66cSDavid du Colombier case BioMax: 16925e96a66cSDavid du Colombier return "Max"; 16935e96a66cSDavid du Colombier } 16945e96a66cSDavid du Colombier } 16955e96a66cSDavid du Colombier 16965e96a66cSDavid du Colombier static char *bttab[] = { 16975e96a66cSDavid du Colombier "BtData", 16985e96a66cSDavid du Colombier "BtData+1", 16995e96a66cSDavid du Colombier "BtData+2", 17005e96a66cSDavid du Colombier "BtData+3", 17015e96a66cSDavid du Colombier "BtData+4", 17025e96a66cSDavid du Colombier "BtData+5", 17035e96a66cSDavid du Colombier "BtData+6", 17045e96a66cSDavid du Colombier "BtData+7", 17055e96a66cSDavid du Colombier "BtDir", 17065e96a66cSDavid du Colombier "BtDir+1", 17075e96a66cSDavid du Colombier "BtDir+2", 17085e96a66cSDavid du Colombier "BtDir+3", 17095e96a66cSDavid du Colombier "BtDir+4", 17105e96a66cSDavid du Colombier "BtDir+5", 17115e96a66cSDavid du Colombier "BtDir+6", 17125e96a66cSDavid du Colombier "BtDir+7", 17135e96a66cSDavid du Colombier }; 17145e96a66cSDavid du Colombier 17155e96a66cSDavid du Colombier char* 17165e96a66cSDavid du Colombier btStr(int type) 17175e96a66cSDavid du Colombier { 17185e96a66cSDavid du Colombier if(type < nelem(bttab)) 17195e96a66cSDavid du Colombier return bttab[type]; 17205e96a66cSDavid du Colombier return "unknown"; 17215e96a66cSDavid du Colombier } 17225e96a66cSDavid du Colombier 17235e96a66cSDavid du Colombier int 17245e96a66cSDavid du Colombier labelFmt(Fmt *f) 17255e96a66cSDavid du Colombier { 17265e96a66cSDavid du Colombier Label *l; 17275e96a66cSDavid du Colombier 17285e96a66cSDavid du Colombier l = va_arg(f->args, Label*); 17295e96a66cSDavid du Colombier return fmtprint(f, "%s,%s,e=%ud,%d,tag=%#ux", 17305e96a66cSDavid du Colombier btStr(l->type), bsStr(l->state), l->epoch, (int)l->epochClose, l->tag); 17315e96a66cSDavid du Colombier } 17325e96a66cSDavid du Colombier 17335e96a66cSDavid du Colombier int 17345e96a66cSDavid du Colombier scoreFmt(Fmt *f) 17355e96a66cSDavid du Colombier { 17365e96a66cSDavid du Colombier uchar *v; 17375e96a66cSDavid du Colombier int i; 17385e96a66cSDavid du Colombier u32int addr; 17395e96a66cSDavid du Colombier 17405e96a66cSDavid du Colombier v = va_arg(f->args, uchar*); 17415e96a66cSDavid du Colombier if(v == nil){ 17425e96a66cSDavid du Colombier fmtprint(f, "*"); 17435e96a66cSDavid du Colombier }else if((addr = globalToLocal(v)) != NilBlock) 17445e96a66cSDavid du Colombier fmtprint(f, "0x%.8ux", addr); 17455e96a66cSDavid du Colombier else{ 17465e96a66cSDavid du Colombier for(i = 0; i < VtScoreSize; i++) 17475e96a66cSDavid du Colombier fmtprint(f, "%2.2ux", v[i]); 17485e96a66cSDavid du Colombier } 17495e96a66cSDavid du Colombier 17505e96a66cSDavid du Colombier return 0; 17515e96a66cSDavid du Colombier } 17525e96a66cSDavid du Colombier 17535e96a66cSDavid du Colombier static int 17545e96a66cSDavid du Colombier upHeap(int i, Block *b) 17555e96a66cSDavid du Colombier { 17565e96a66cSDavid du Colombier Block *bb; 17575e96a66cSDavid du Colombier u32int now; 17585e96a66cSDavid du Colombier int p; 17595e96a66cSDavid du Colombier Cache *c; 17605e96a66cSDavid du Colombier 17615e96a66cSDavid du Colombier c = b->c; 17625e96a66cSDavid du Colombier now = c->now; 17635e96a66cSDavid du Colombier for(; i != 0; i = p){ 17645e96a66cSDavid du Colombier p = (i - 1) >> 1; 17655e96a66cSDavid du Colombier bb = c->heap[p]; 17665e96a66cSDavid du Colombier if(b->used - now >= bb->used - now) 17675e96a66cSDavid du Colombier break; 17685e96a66cSDavid du Colombier c->heap[i] = bb; 17695e96a66cSDavid du Colombier bb->heap = i; 17705e96a66cSDavid du Colombier } 17715e96a66cSDavid du Colombier c->heap[i] = b; 17725e96a66cSDavid du Colombier b->heap = i; 17735e96a66cSDavid du Colombier 17745e96a66cSDavid du Colombier return i; 17755e96a66cSDavid du Colombier } 17765e96a66cSDavid du Colombier 17775e96a66cSDavid du Colombier static int 17785e96a66cSDavid du Colombier downHeap(int i, Block *b) 17795e96a66cSDavid du Colombier { 17805e96a66cSDavid du Colombier Block *bb; 17815e96a66cSDavid du Colombier u32int now; 17825e96a66cSDavid du Colombier int k; 17835e96a66cSDavid du Colombier Cache *c; 17845e96a66cSDavid du Colombier 17855e96a66cSDavid du Colombier c = b->c; 17865e96a66cSDavid du Colombier now = c->now; 17875e96a66cSDavid du Colombier for(; ; i = k){ 17885e96a66cSDavid du Colombier k = (i << 1) + 1; 17895e96a66cSDavid du Colombier if(k >= c->nheap) 17905e96a66cSDavid du Colombier break; 17915e96a66cSDavid du Colombier if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now) 17925e96a66cSDavid du Colombier k++; 17935e96a66cSDavid du Colombier bb = c->heap[k]; 17945e96a66cSDavid du Colombier if(b->used - now <= bb->used - now) 17955e96a66cSDavid du Colombier break; 17965e96a66cSDavid du Colombier c->heap[i] = bb; 17975e96a66cSDavid du Colombier bb->heap = i; 17985e96a66cSDavid du Colombier } 17995e96a66cSDavid du Colombier c->heap[i] = b; 18005e96a66cSDavid du Colombier b->heap = i; 18015e96a66cSDavid du Colombier return i; 18025e96a66cSDavid du Colombier } 18035e96a66cSDavid du Colombier 18045e96a66cSDavid du Colombier /* 18055e96a66cSDavid du Colombier * Delete a block from the heap. 18065e96a66cSDavid du Colombier * Called with c->lk held. 18075e96a66cSDavid du Colombier */ 18085e96a66cSDavid du Colombier static void 18095e96a66cSDavid du Colombier heapDel(Block *b) 18105e96a66cSDavid du Colombier { 18115e96a66cSDavid du Colombier int i, si; 18125e96a66cSDavid du Colombier Cache *c; 18135e96a66cSDavid du Colombier 18145e96a66cSDavid du Colombier c = b->c; 18155e96a66cSDavid du Colombier 18165e96a66cSDavid du Colombier si = b->heap; 18175e96a66cSDavid du Colombier if(si == BadHeap) 18185e96a66cSDavid du Colombier return; 18195e96a66cSDavid du Colombier b->heap = BadHeap; 18205e96a66cSDavid du Colombier c->nheap--; 18215e96a66cSDavid du Colombier if(si == c->nheap) 18225e96a66cSDavid du Colombier return; 18235e96a66cSDavid du Colombier b = c->heap[c->nheap]; 18245e96a66cSDavid du Colombier i = upHeap(si, b); 18255e96a66cSDavid du Colombier if(i == si) 18265e96a66cSDavid du Colombier downHeap(i, b); 18275e96a66cSDavid du Colombier } 18285e96a66cSDavid du Colombier 18295e96a66cSDavid du Colombier /* 18305e96a66cSDavid du Colombier * Insert a block into the heap. 18315e96a66cSDavid du Colombier * Called with c->lk held. 18325e96a66cSDavid du Colombier */ 18335e96a66cSDavid du Colombier static void 18345e96a66cSDavid du Colombier heapIns(Block *b) 18355e96a66cSDavid du Colombier { 18365e96a66cSDavid du Colombier assert(b->heap == BadHeap); 18375e96a66cSDavid du Colombier upHeap(b->c->nheap++, b); 1838*d7aba6c3SDavid du Colombier rwakeup(&b->c->heapwait); 18395e96a66cSDavid du Colombier } 18405e96a66cSDavid du Colombier 18415e96a66cSDavid du Colombier /* 18425e96a66cSDavid du Colombier * Get just the label for a block. 18435e96a66cSDavid du Colombier */ 1844e569ccb5SDavid du Colombier int 18455e96a66cSDavid du Colombier readLabel(Cache *c, Label *l, u32int addr) 18465e96a66cSDavid du Colombier { 18475e96a66cSDavid du Colombier int lpb; 18485e96a66cSDavid du Colombier Block *b; 18495e96a66cSDavid du Colombier u32int a; 18505e96a66cSDavid du Colombier 18515e96a66cSDavid du Colombier lpb = c->size / LabelSize; 18525e96a66cSDavid du Colombier a = addr / lpb; 18535e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, a, OReadOnly); 18545e96a66cSDavid du Colombier if(b == nil){ 18555e96a66cSDavid du Colombier blockPut(b); 18565e96a66cSDavid du Colombier return 0; 18575e96a66cSDavid du Colombier } 18585e96a66cSDavid du Colombier 18595e96a66cSDavid du Colombier if(!labelUnpack(l, b->data, addr%lpb)){ 18605e96a66cSDavid du Colombier blockPut(b); 18615e96a66cSDavid du Colombier return 0; 18625e96a66cSDavid du Colombier } 18635e96a66cSDavid du Colombier blockPut(b); 18645e96a66cSDavid du Colombier return 1; 18655e96a66cSDavid du Colombier } 18665e96a66cSDavid du Colombier 18675e96a66cSDavid du Colombier /* 18685e96a66cSDavid du Colombier * Process unlink queue. 18695e96a66cSDavid du Colombier * Called with c->lk held. 18705e96a66cSDavid du Colombier */ 18715e96a66cSDavid du Colombier static void 18725e96a66cSDavid du Colombier unlinkBody(Cache *c) 18735e96a66cSDavid du Colombier { 18745e96a66cSDavid du Colombier BList *p; 18755e96a66cSDavid du Colombier 18765e96a66cSDavid du Colombier while(c->uhead != nil){ 18775e96a66cSDavid du Colombier p = c->uhead; 18785e96a66cSDavid du Colombier c->uhead = p->next; 1879*d7aba6c3SDavid du Colombier qunlock(&c->lk); 1880e569ccb5SDavid du Colombier doRemoveLink(c, p); 1881*d7aba6c3SDavid du Colombier qlock(&c->lk); 18825e96a66cSDavid du Colombier p->next = c->blfree; 18835e96a66cSDavid du Colombier c->blfree = p; 18845e96a66cSDavid du Colombier } 18855e96a66cSDavid du Colombier } 18865e96a66cSDavid du Colombier 18875e96a66cSDavid du Colombier /* 18885e96a66cSDavid du Colombier * Occasionally unlink the blocks on the cache unlink queue. 18895e96a66cSDavid du Colombier */ 18905e96a66cSDavid du Colombier static void 18915e96a66cSDavid du Colombier unlinkThread(void *a) 18925e96a66cSDavid du Colombier { 18935e96a66cSDavid du Colombier Cache *c = a; 18945e96a66cSDavid du Colombier 1895*d7aba6c3SDavid du Colombier threadsetname("unlink"); 18965e96a66cSDavid du Colombier 1897*d7aba6c3SDavid du Colombier qlock(&c->lk); 18985e96a66cSDavid du Colombier for(;;){ 1899*d7aba6c3SDavid du Colombier while(c->uhead == nil && c->die.l == nil) 1900*d7aba6c3SDavid du Colombier rsleep(&c->unlink); 1901*d7aba6c3SDavid du Colombier if(c->die.l != nil) 19025e96a66cSDavid du Colombier break; 19035e96a66cSDavid du Colombier unlinkBody(c); 19045e96a66cSDavid du Colombier } 19055e96a66cSDavid du Colombier c->ref--; 1906*d7aba6c3SDavid du Colombier rwakeup(&c->die); 1907*d7aba6c3SDavid du Colombier qunlock(&c->lk); 19085e96a66cSDavid du Colombier } 19095e96a66cSDavid du Colombier 19105e96a66cSDavid du Colombier static int 19115e96a66cSDavid du Colombier baddrCmp(void *a0, void *a1) 19125e96a66cSDavid du Colombier { 19135e96a66cSDavid du Colombier BAddr *b0, *b1; 19145e96a66cSDavid du Colombier b0 = a0; 19155e96a66cSDavid du Colombier b1 = a1; 19165e96a66cSDavid du Colombier 19175e96a66cSDavid du Colombier if(b0->part < b1->part) 19185e96a66cSDavid du Colombier return -1; 19195e96a66cSDavid du Colombier if(b0->part > b1->part) 19205e96a66cSDavid du Colombier return 1; 19215e96a66cSDavid du Colombier if(b0->addr < b1->addr) 19225e96a66cSDavid du Colombier return -1; 19235e96a66cSDavid du Colombier if(b0->addr > b1->addr) 19245e96a66cSDavid du Colombier return 1; 19255e96a66cSDavid du Colombier return 0; 19265e96a66cSDavid du Colombier } 19275e96a66cSDavid du Colombier 19285e96a66cSDavid du Colombier /* 19295e96a66cSDavid du Colombier * Scan the block list for dirty blocks; add them to the list c->baddr. 19305e96a66cSDavid du Colombier */ 19315e96a66cSDavid du Colombier static void 19325e96a66cSDavid du Colombier flushFill(Cache *c) 19335e96a66cSDavid du Colombier { 193461201b97SDavid du Colombier int i, ndirty; 19355e96a66cSDavid du Colombier BAddr *p; 19365e96a66cSDavid du Colombier Block *b; 19375e96a66cSDavid du Colombier 1938*d7aba6c3SDavid du Colombier qlock(&c->lk); 193939734e7eSDavid du Colombier if(c->ndirty == 0){ 1940*d7aba6c3SDavid du Colombier qunlock(&c->lk); 194139734e7eSDavid du Colombier return; 194239734e7eSDavid du Colombier } 19435e96a66cSDavid du Colombier 19445e96a66cSDavid du Colombier p = c->baddr; 194561201b97SDavid du Colombier ndirty = 0; 19465e96a66cSDavid du Colombier for(i=0; i<c->nblocks; i++){ 19475e96a66cSDavid du Colombier b = c->blocks + i; 194861201b97SDavid du Colombier if(b->part == PartError) 194961201b97SDavid du Colombier continue; 195061201b97SDavid du Colombier if(b->iostate == BioDirty || b->iostate == BioWriting) 195161201b97SDavid du Colombier ndirty++; 195261201b97SDavid du Colombier if(b->iostate != BioDirty) 19535e96a66cSDavid du Colombier continue; 19545e96a66cSDavid du Colombier p->part = b->part; 19555e96a66cSDavid du Colombier p->addr = b->addr; 19565e96a66cSDavid du Colombier p->vers = b->vers; 19575e96a66cSDavid du Colombier p++; 19585e96a66cSDavid du Colombier } 195961201b97SDavid du Colombier if(ndirty != c->ndirty){ 19603b86f2f8SDavid du Colombier fprint(2, "%s: ndirty mismatch expected %d found %d\n", 19613b86f2f8SDavid du Colombier argv0, c->ndirty, ndirty); 196261201b97SDavid du Colombier c->ndirty = ndirty; 196361201b97SDavid du Colombier } 1964*d7aba6c3SDavid du Colombier qunlock(&c->lk); 19655e96a66cSDavid du Colombier 19665e96a66cSDavid du Colombier c->bw = p - c->baddr; 19675e96a66cSDavid du Colombier qsort(c->baddr, c->bw, sizeof(BAddr), baddrCmp); 19685e96a66cSDavid du Colombier } 19695e96a66cSDavid du Colombier 19705e96a66cSDavid du Colombier /* 19715e96a66cSDavid du Colombier * This is not thread safe, i.e. it can't be called from multiple threads. 19725e96a66cSDavid du Colombier * 19735e96a66cSDavid du Colombier * It's okay how we use it, because it only gets called in 19745e96a66cSDavid du Colombier * the flushThread. And cacheFree, but only after 19755e96a66cSDavid du Colombier * cacheFree has killed off the flushThread. 19765e96a66cSDavid du Colombier */ 19775e96a66cSDavid du Colombier static int 19785e96a66cSDavid du Colombier cacheFlushBlock(Cache *c) 19795e96a66cSDavid du Colombier { 19805e96a66cSDavid du Colombier Block *b; 19815e96a66cSDavid du Colombier BAddr *p; 19825e96a66cSDavid du Colombier int lockfail, nfail; 19835e96a66cSDavid du Colombier 19845e96a66cSDavid du Colombier nfail = 0; 19855e96a66cSDavid du Colombier for(;;){ 19865e96a66cSDavid du Colombier if(c->br == c->be){ 19875e96a66cSDavid du Colombier if(c->bw == 0 || c->bw == c->be) 19885e96a66cSDavid du Colombier flushFill(c); 19895e96a66cSDavid du Colombier c->br = 0; 19905e96a66cSDavid du Colombier c->be = c->bw; 19915e96a66cSDavid du Colombier c->bw = 0; 19925e96a66cSDavid du Colombier c->nflush = 0; 19935e96a66cSDavid du Colombier } 19945e96a66cSDavid du Colombier 19955e96a66cSDavid du Colombier if(c->br == c->be) 19965e96a66cSDavid du Colombier return 0; 19975e96a66cSDavid du Colombier p = c->baddr + c->br; 19985e96a66cSDavid du Colombier c->br++; 19992651f6bbSDavid du Colombier b = _cacheLocalLookup(c, p->part, p->addr, p->vers, Nowaitlock, 20002651f6bbSDavid du Colombier &lockfail); 20015e96a66cSDavid du Colombier 2002e12a9870SDavid du Colombier if(b && blockWrite(b, Nowaitlock)){ 20035e96a66cSDavid du Colombier c->nflush++; 20045e96a66cSDavid du Colombier blockPut(b); 20055e96a66cSDavid du Colombier return 1; 20065e96a66cSDavid du Colombier } 20075e96a66cSDavid du Colombier if(b) 20085e96a66cSDavid du Colombier blockPut(b); 20095e96a66cSDavid du Colombier 20105e96a66cSDavid du Colombier /* 20115e96a66cSDavid du Colombier * Why didn't we write the block? 20125e96a66cSDavid du Colombier */ 20135e96a66cSDavid du Colombier 20145e96a66cSDavid du Colombier /* Block already written out */ 20155e96a66cSDavid du Colombier if(b == nil && !lockfail) 20165e96a66cSDavid du Colombier continue; 20175e96a66cSDavid du Colombier 20185e96a66cSDavid du Colombier /* Failed to acquire lock; sleep if happens a lot. */ 2019fe853e23SDavid du Colombier if(lockfail && ++nfail > 100){ 20205e96a66cSDavid du Colombier sleep(500); 2021fe853e23SDavid du Colombier nfail = 0; 2022fe853e23SDavid du Colombier } 20235e96a66cSDavid du Colombier /* Requeue block. */ 20245e96a66cSDavid du Colombier if(c->bw < c->be) 20255e96a66cSDavid du Colombier c->baddr[c->bw++] = *p; 20265e96a66cSDavid du Colombier } 20275e96a66cSDavid du Colombier } 20285e96a66cSDavid du Colombier 20295e96a66cSDavid du Colombier /* 20305e96a66cSDavid du Colombier * Occasionally flush dirty blocks from memory to the disk. 20315e96a66cSDavid du Colombier */ 20325e96a66cSDavid du Colombier static void 20335e96a66cSDavid du Colombier flushThread(void *a) 20345e96a66cSDavid du Colombier { 20355e96a66cSDavid du Colombier Cache *c = a; 20365e96a66cSDavid du Colombier int i; 20375e96a66cSDavid du Colombier 2038*d7aba6c3SDavid du Colombier threadsetname("flush"); 2039*d7aba6c3SDavid du Colombier qlock(&c->lk); 2040*d7aba6c3SDavid du Colombier while(c->die.l == nil){ 2041*d7aba6c3SDavid du Colombier rsleep(&c->flush); 2042*d7aba6c3SDavid du Colombier qunlock(&c->lk); 20435e96a66cSDavid du Colombier for(i=0; i<FlushSize; i++) 204467031067SDavid du Colombier if(!cacheFlushBlock(c)){ 204567031067SDavid du Colombier /* 204667031067SDavid du Colombier * If i==0, could be someone is waking us repeatedly 204767031067SDavid du Colombier * to flush the cache but there's no work to do. 204867031067SDavid du Colombier * Pause a little. 204967031067SDavid du Colombier */ 20500b9a5132SDavid du Colombier if(i==0){ 20513b86f2f8SDavid du Colombier // fprint(2, "%s: flushthread found " 20523b86f2f8SDavid du Colombier // "nothing to flush - %d dirty\n", 20533b86f2f8SDavid du Colombier // argv0, c->ndirty); 205467031067SDavid du Colombier sleep(250); 20550b9a5132SDavid du Colombier } 20565e96a66cSDavid du Colombier break; 205767031067SDavid du Colombier } 205839734e7eSDavid du Colombier if(i==0 && c->ndirty){ 205939734e7eSDavid du Colombier /* 206039734e7eSDavid du Colombier * All the blocks are being written right now -- there's nothing to do. 206139734e7eSDavid du Colombier * We might be spinning with cacheFlush though -- he'll just keep 206239734e7eSDavid du Colombier * kicking us until c->ndirty goes down. Probably we should sleep 206339734e7eSDavid du Colombier * on something that the diskThread can kick, but for now we'll 206439734e7eSDavid du Colombier * just pause for a little while waiting for disks to finish. 206539734e7eSDavid du Colombier */ 206639734e7eSDavid du Colombier sleep(100); 206739734e7eSDavid du Colombier } 2068*d7aba6c3SDavid du Colombier qlock(&c->lk); 2069*d7aba6c3SDavid du Colombier rwakeupall(&c->flushwait); 20705e96a66cSDavid du Colombier } 20715e96a66cSDavid du Colombier c->ref--; 2072*d7aba6c3SDavid du Colombier rwakeup(&c->die); 2073*d7aba6c3SDavid du Colombier qunlock(&c->lk); 20745e96a66cSDavid du Colombier } 20755e96a66cSDavid du Colombier 20765e96a66cSDavid du Colombier /* 20770b9a5132SDavid du Colombier * Flush the cache. 20785e96a66cSDavid du Colombier */ 20795e96a66cSDavid du Colombier void 20805e96a66cSDavid du Colombier cacheFlush(Cache *c, int wait) 20815e96a66cSDavid du Colombier { 2082*d7aba6c3SDavid du Colombier qlock(&c->lk); 20835e96a66cSDavid du Colombier if(wait){ 20845e96a66cSDavid du Colombier while(c->ndirty){ 2085dc5a79c1SDavid du Colombier // consPrint("cacheFlush: %d dirty blocks, uhead %p\n", 2086dc5a79c1SDavid du Colombier // c->ndirty, c->uhead); 2087*d7aba6c3SDavid du Colombier rwakeup(&c->flush); 2088*d7aba6c3SDavid du Colombier rsleep(&c->flushwait); 20895e96a66cSDavid du Colombier } 2090dc5a79c1SDavid du Colombier // consPrint("cacheFlush: done (uhead %p)\n", c->ndirty, c->uhead); 20910b9a5132SDavid du Colombier }else if(c->ndirty) 2092*d7aba6c3SDavid du Colombier rwakeup(&c->flush); 2093*d7aba6c3SDavid du Colombier qunlock(&c->lk); 20945e96a66cSDavid du Colombier } 209561201b97SDavid du Colombier 209661201b97SDavid du Colombier /* 209761201b97SDavid du Colombier * Kick the flushThread every 30 seconds. 209861201b97SDavid du Colombier */ 209961201b97SDavid du Colombier static void 210061201b97SDavid du Colombier cacheSync(void *v) 210161201b97SDavid du Colombier { 210261201b97SDavid du Colombier Cache *c; 210361201b97SDavid du Colombier 210461201b97SDavid du Colombier c = v; 210561201b97SDavid du Colombier cacheFlush(c, 0); 210661201b97SDavid du Colombier } 2107