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 { 245e96a66cSDavid du Colombier VtLock *lk; 250b9a5132SDavid du Colombier VtLock *dirtylk; 265e96a66cSDavid du Colombier int ref; 275e96a66cSDavid du Colombier int mode; 285e96a66cSDavid du Colombier 295e96a66cSDavid du Colombier Disk *disk; 305e96a66cSDavid du Colombier int size; /* block size */ 3161201b97SDavid du Colombier int ndmap; /* size of per-block dirty pointer map used in blockWrite */ 325e96a66cSDavid du Colombier VtSession *z; 335e96a66cSDavid du Colombier u32int now; /* ticks for usage timestamps */ 345e96a66cSDavid du Colombier Block **heads; /* hash table for finding address */ 355e96a66cSDavid du Colombier int nheap; /* number of available victims */ 365e96a66cSDavid du Colombier Block **heap; /* heap for locating victims */ 375e96a66cSDavid du Colombier long nblocks; /* number of blocks allocated */ 385e96a66cSDavid du Colombier Block *blocks; /* array of block descriptors */ 395e96a66cSDavid du Colombier u8int *mem; /* memory for all block data & blists */ 405e96a66cSDavid du Colombier 415e96a66cSDavid du Colombier BList *blfree; 425e96a66cSDavid du Colombier VtRendez *blrend; 435e96a66cSDavid du Colombier 445e96a66cSDavid du Colombier int ndirty; /* number of dirty blocks in the cache */ 455e96a66cSDavid du Colombier int maxdirty; /* max number of dirty blocks */ 465e96a66cSDavid du Colombier u32int vers; 475e96a66cSDavid du Colombier 485e96a66cSDavid du Colombier long hashSize; 495e96a66cSDavid du Colombier 505e96a66cSDavid du Colombier FreeList *fl; 515e96a66cSDavid du Colombier 525e96a66cSDavid du Colombier VtRendez *die; /* daemon threads should die when != nil */ 535e96a66cSDavid du Colombier 545e96a66cSDavid du Colombier VtRendez *flush; 555e96a66cSDavid du Colombier VtRendez *flushwait; 56fe853e23SDavid du Colombier VtRendez *heapwait; 575e96a66cSDavid du Colombier BAddr *baddr; 585e96a66cSDavid du Colombier int bw, br, be; 595e96a66cSDavid du Colombier int nflush; 605e96a66cSDavid du Colombier 6161201b97SDavid du Colombier Periodic *sync; 6261201b97SDavid du Colombier 635e96a66cSDavid du Colombier /* unlink daemon */ 645e96a66cSDavid du Colombier BList *uhead; 655e96a66cSDavid du Colombier BList *utail; 665e96a66cSDavid du Colombier VtRendez *unlink; 677abd426fSDavid du Colombier 687abd426fSDavid du Colombier /* block counts */ 697abd426fSDavid du Colombier int nused; 707abd426fSDavid du Colombier int ndisk; 715e96a66cSDavid du Colombier }; 725e96a66cSDavid du Colombier 735e96a66cSDavid du Colombier struct BList { 745e96a66cSDavid du Colombier int part; 755e96a66cSDavid du Colombier u32int addr; 765e96a66cSDavid du Colombier uchar type; 775e96a66cSDavid du Colombier u32int tag; 785e96a66cSDavid du Colombier u32int epoch; 795e96a66cSDavid du Colombier u32int vers; 805e96a66cSDavid du Colombier 81e569ccb5SDavid du Colombier int recurse; /* for block unlink */ 82e569ccb5SDavid du Colombier 835e96a66cSDavid du Colombier /* for roll back */ 845e96a66cSDavid du Colombier int index; /* -1 indicates not valid */ 8561201b97SDavid du Colombier union { 865e96a66cSDavid du Colombier uchar score[VtScoreSize]; 8761201b97SDavid du Colombier uchar entry[VtEntrySize]; 8861201b97SDavid du Colombier } old; 895e96a66cSDavid du Colombier BList *next; 905e96a66cSDavid du Colombier }; 915e96a66cSDavid du Colombier 925e96a66cSDavid du Colombier struct BAddr { 935e96a66cSDavid du Colombier int part; 945e96a66cSDavid du Colombier u32int addr; 955e96a66cSDavid du Colombier u32int vers; 965e96a66cSDavid du Colombier }; 975e96a66cSDavid du Colombier 985e96a66cSDavid du Colombier struct FreeList { 995e96a66cSDavid du Colombier VtLock *lk; 1005e96a66cSDavid du Colombier u32int last; /* last block allocated */ 1015e96a66cSDavid du Colombier u32int end; /* end of data partition */ 1027abd426fSDavid du Colombier u32int nused; /* number of used blocks */ 103225077b0SDavid du Colombier u32int epochLow; /* low epoch when last updated nused */ 1045e96a66cSDavid du Colombier }; 1055e96a66cSDavid du Colombier 1065e96a66cSDavid du Colombier static FreeList *flAlloc(u32int end); 1075e96a66cSDavid du Colombier static void flFree(FreeList *fl); 1085e96a66cSDavid du Colombier 1095e96a66cSDavid du Colombier static Block *cacheBumpBlock(Cache *c); 1105e96a66cSDavid du Colombier static void heapDel(Block*); 1115e96a66cSDavid du Colombier static void heapIns(Block*); 1125e96a66cSDavid du Colombier static void cacheCheck(Cache*); 1135e96a66cSDavid du Colombier static void unlinkThread(void *a); 1145e96a66cSDavid du Colombier static void flushThread(void *a); 1155e96a66cSDavid du Colombier static void flushBody(Cache *c); 1165e96a66cSDavid du Colombier static void unlinkBody(Cache *c); 1175e96a66cSDavid du Colombier static int cacheFlushBlock(Cache *c); 11861201b97SDavid du Colombier static void cacheSync(void*); 119e569ccb5SDavid du Colombier static BList *blistAlloc(Block*); 120e569ccb5SDavid du Colombier static void blistFree(Cache*, BList*); 121e569ccb5SDavid du Colombier static void doRemoveLink(Cache*, BList*); 122e569ccb5SDavid du Colombier static void doRemoveLinkList(Cache*, BList*); 123e569ccb5SDavid du Colombier 1245e96a66cSDavid du Colombier /* 1255e96a66cSDavid du Colombier * Mapping from local block type to Venti type 1265e96a66cSDavid du Colombier */ 1275e96a66cSDavid du Colombier int vtType[BtMax] = { 1285e96a66cSDavid du Colombier VtDataType, /* BtData | 0 */ 1295e96a66cSDavid du Colombier VtPointerType0, /* BtData | 1 */ 1305e96a66cSDavid du Colombier VtPointerType1, /* BtData | 2 */ 1315e96a66cSDavid du Colombier VtPointerType2, /* BtData | 3 */ 1325e96a66cSDavid du Colombier VtPointerType3, /* BtData | 4 */ 1335e96a66cSDavid du Colombier VtPointerType4, /* BtData | 5 */ 1345e96a66cSDavid du Colombier VtPointerType5, /* BtData | 6 */ 1355e96a66cSDavid du Colombier VtPointerType6, /* BtData | 7 */ 1365e96a66cSDavid du Colombier VtDirType, /* BtDir | 0 */ 1375e96a66cSDavid du Colombier VtPointerType0, /* BtDir | 1 */ 1385e96a66cSDavid du Colombier VtPointerType1, /* BtDir | 2 */ 1395e96a66cSDavid du Colombier VtPointerType2, /* BtDir | 3 */ 1405e96a66cSDavid du Colombier VtPointerType3, /* BtDir | 4 */ 1415e96a66cSDavid du Colombier VtPointerType4, /* BtDir | 5 */ 1425e96a66cSDavid du Colombier VtPointerType5, /* BtDir | 6 */ 1435e96a66cSDavid du Colombier VtPointerType6, /* BtDir | 7 */ 1445e96a66cSDavid du Colombier }; 1455e96a66cSDavid du Colombier 1465e96a66cSDavid du Colombier /* 1475e96a66cSDavid du Colombier * Allocate the memory cache. 1485e96a66cSDavid du Colombier */ 1495e96a66cSDavid du Colombier Cache * 1505e96a66cSDavid du Colombier cacheAlloc(Disk *disk, VtSession *z, ulong nblocks, int mode) 1515e96a66cSDavid du Colombier { 1525e96a66cSDavid du Colombier int i; 1535e96a66cSDavid du Colombier Cache *c; 1545e96a66cSDavid du Colombier Block *b; 1555e96a66cSDavid du Colombier BList *bl; 1565e96a66cSDavid du Colombier u8int *p; 1575e96a66cSDavid du Colombier int nbl; 1585e96a66cSDavid du Colombier 1595e96a66cSDavid du Colombier c = vtMemAllocZ(sizeof(Cache)); 1605e96a66cSDavid du Colombier 1615e96a66cSDavid du Colombier /* reasonable number of BList elements */ 1625e96a66cSDavid du Colombier nbl = nblocks * 4; 1635e96a66cSDavid du Colombier 1645e96a66cSDavid du Colombier c->lk = vtLockAlloc(); 1650b9a5132SDavid du Colombier c->dirtylk = vtLockAlloc(); /* allowed to dirty blocks */ 1665e96a66cSDavid du Colombier c->ref = 1; 1675e96a66cSDavid du Colombier c->disk = disk; 1685e96a66cSDavid du Colombier c->z = z; 1695e96a66cSDavid du Colombier c->size = diskBlockSize(disk); 1705e96a66cSDavid du Colombier bwatchSetBlockSize(c->size); 1715e96a66cSDavid du Colombier /* round c->size up to be a nice multiple */ 1725e96a66cSDavid du Colombier c->size = (c->size + 127) & ~127; 17361201b97SDavid du Colombier c->ndmap = (c->size/20 + 7) / 8; 1745e96a66cSDavid du Colombier c->nblocks = nblocks; 1755e96a66cSDavid du Colombier c->hashSize = nblocks; 1765e96a66cSDavid du Colombier c->heads = vtMemAllocZ(c->hashSize*sizeof(Block*)); 1775e96a66cSDavid du Colombier c->heap = vtMemAllocZ(nblocks*sizeof(Block*)); 1785e96a66cSDavid du Colombier c->blocks = vtMemAllocZ(nblocks*sizeof(Block)); 17961201b97SDavid du Colombier c->mem = vtMemAllocZ(nblocks * (c->size + c->ndmap) + nbl * sizeof(BList)); 1805e96a66cSDavid du Colombier c->baddr = vtMemAllocZ(nblocks * sizeof(BAddr)); 1815e96a66cSDavid du Colombier c->mode = mode; 1825e96a66cSDavid du Colombier c->vers++; 1835e96a66cSDavid du Colombier p = c->mem; 1845e96a66cSDavid du Colombier for(i = 0; i < nblocks; i++){ 1855e96a66cSDavid du Colombier b = &c->blocks[i]; 1865e96a66cSDavid du Colombier b->lk = vtLockAlloc(); 1875e96a66cSDavid du Colombier b->c = c; 1885e96a66cSDavid du Colombier b->data = p; 1895e96a66cSDavid du Colombier b->heap = i; 1905e96a66cSDavid du Colombier b->ioready = vtRendezAlloc(b->lk); 1915e96a66cSDavid du Colombier c->heap[i] = b; 1925e96a66cSDavid du Colombier p += c->size; 1935e96a66cSDavid du Colombier } 1945e96a66cSDavid du Colombier c->nheap = nblocks; 1955e96a66cSDavid du Colombier for(i = 0; i < nbl; i++){ 1965e96a66cSDavid du Colombier bl = (BList*)p; 1975e96a66cSDavid du Colombier bl->next = c->blfree; 1985e96a66cSDavid du Colombier c->blfree = bl; 1995e96a66cSDavid du Colombier p += sizeof(BList); 2005e96a66cSDavid du Colombier } 20161201b97SDavid du Colombier /* separate loop to keep blocks and blists reasonably aligned */ 20261201b97SDavid du Colombier for(i = 0; i < nblocks; i++){ 20361201b97SDavid du Colombier b = &c->blocks[i]; 20461201b97SDavid du Colombier b->dmap = p; 20561201b97SDavid du Colombier p += c->ndmap; 20661201b97SDavid du Colombier } 20761201b97SDavid du Colombier 2085e96a66cSDavid du Colombier c->blrend = vtRendezAlloc(c->lk); 2095e96a66cSDavid du Colombier 2105e96a66cSDavid du Colombier c->maxdirty = nblocks*(DirtyPercentage*0.01); 2115e96a66cSDavid du Colombier 2125e96a66cSDavid du Colombier c->fl = flAlloc(diskSize(disk, PartData)); 2135e96a66cSDavid du Colombier 2145e96a66cSDavid du Colombier c->unlink = vtRendezAlloc(c->lk); 2155e96a66cSDavid du Colombier c->flush = vtRendezAlloc(c->lk); 2165e96a66cSDavid du Colombier c->flushwait = vtRendezAlloc(c->lk); 217fe853e23SDavid du Colombier c->heapwait = vtRendezAlloc(c->lk); 21861201b97SDavid du Colombier c->sync = periodicAlloc(cacheSync, c, 30*1000); 2195e96a66cSDavid du Colombier 2205e96a66cSDavid du Colombier if(mode == OReadWrite){ 2215e96a66cSDavid du Colombier c->ref += 2; 2225e96a66cSDavid du Colombier vtThread(unlinkThread, c); 2235e96a66cSDavid du Colombier vtThread(flushThread, c); 2245e96a66cSDavid du Colombier } 2255e96a66cSDavid du Colombier cacheCheck(c); 2265e96a66cSDavid du Colombier 2275e96a66cSDavid du Colombier return c; 2285e96a66cSDavid du Colombier } 2295e96a66cSDavid du Colombier 2305e96a66cSDavid du Colombier /* 2315e96a66cSDavid du Colombier * Free the whole memory cache, flushing all dirty blocks to the disk. 2325e96a66cSDavid du Colombier */ 2335e96a66cSDavid du Colombier void 2345e96a66cSDavid du Colombier cacheFree(Cache *c) 2355e96a66cSDavid du Colombier { 2365e96a66cSDavid du Colombier int i; 2375e96a66cSDavid du Colombier 2385e96a66cSDavid du Colombier /* kill off daemon threads */ 2395e96a66cSDavid du Colombier vtLock(c->lk); 2405e96a66cSDavid du Colombier c->die = vtRendezAlloc(c->lk); 24161201b97SDavid du Colombier periodicKill(c->sync); 2425e96a66cSDavid du Colombier vtWakeup(c->flush); 2435e96a66cSDavid du Colombier vtWakeup(c->unlink); 2445e96a66cSDavid du Colombier while(c->ref > 1) 2455e96a66cSDavid du Colombier vtSleep(c->die); 2465e96a66cSDavid du Colombier 2475e96a66cSDavid du Colombier /* flush everything out */ 2485e96a66cSDavid du Colombier do { 2495e96a66cSDavid du Colombier unlinkBody(c); 2505e96a66cSDavid du Colombier vtUnlock(c->lk); 2515e96a66cSDavid du Colombier while(cacheFlushBlock(c)) 2525e96a66cSDavid du Colombier ; 2535e96a66cSDavid du Colombier diskFlush(c->disk); 2545e96a66cSDavid du Colombier vtLock(c->lk); 2555e96a66cSDavid du Colombier } while(c->uhead || c->ndirty); 2565e96a66cSDavid du Colombier vtUnlock(c->lk); 2575e96a66cSDavid du Colombier 2585e96a66cSDavid du Colombier cacheCheck(c); 2595e96a66cSDavid du Colombier 2605e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){ 2615e96a66cSDavid du Colombier assert(c->blocks[i].ref == 0); 2625e96a66cSDavid du Colombier vtRendezFree(c->blocks[i].ioready); 2635e96a66cSDavid du Colombier vtLockFree(c->blocks[i].lk); 2645e96a66cSDavid du Colombier } 2655e96a66cSDavid du Colombier flFree(c->fl); 2665e96a66cSDavid du Colombier vtMemFree(c->baddr); 2675e96a66cSDavid du Colombier vtMemFree(c->heads); 2685e96a66cSDavid du Colombier vtMemFree(c->blocks); 2695e96a66cSDavid du Colombier vtMemFree(c->mem); 2705e96a66cSDavid du Colombier vtLockFree(c->lk); 2715e96a66cSDavid du Colombier diskFree(c->disk); 2725e96a66cSDavid du Colombier vtRendezFree(c->blrend); 2735e96a66cSDavid du Colombier /* don't close vtSession */ 2745e96a66cSDavid du Colombier vtMemFree(c); 2755e96a66cSDavid du Colombier } 2765e96a66cSDavid du Colombier 2775e96a66cSDavid du Colombier static void 2785e96a66cSDavid du Colombier cacheDump(Cache *c) 2795e96a66cSDavid du Colombier { 2805e96a66cSDavid du Colombier int i; 2815e96a66cSDavid du Colombier Block *b; 2825e96a66cSDavid du Colombier 2835e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){ 2845e96a66cSDavid du Colombier b = &c->blocks[i]; 28574f16c81SDavid du Colombier fprint(2, "%d. p=%d a=%ud %V t=%d ref=%d state=%s io=%s pc=%#p\n", 286fe853e23SDavid du Colombier i, b->part, b->addr, b->score, b->l.type, b->ref, 287fe853e23SDavid du Colombier bsStr(b->l.state), bioStr(b->iostate), b->pc); 2885e96a66cSDavid du Colombier } 2895e96a66cSDavid du Colombier } 2905e96a66cSDavid du Colombier 2915e96a66cSDavid du Colombier static void 2925e96a66cSDavid du Colombier cacheCheck(Cache *c) 2935e96a66cSDavid du Colombier { 2945e96a66cSDavid du Colombier u32int size, now; 2955e96a66cSDavid du Colombier int i, k, refed; 2965e96a66cSDavid du Colombier static uchar zero[VtScoreSize]; 2975e96a66cSDavid du Colombier Block *b; 2985e96a66cSDavid du Colombier 2995e96a66cSDavid du Colombier size = c->size; 3005e96a66cSDavid du Colombier now = c->now; 3015e96a66cSDavid du Colombier 3025e96a66cSDavid du Colombier for(i = 0; i < c->nheap; i++){ 3035e96a66cSDavid du Colombier if(c->heap[i]->heap != i) 3045e96a66cSDavid du Colombier vtFatal("mis-heaped at %d: %d", i, c->heap[i]->heap); 3055e96a66cSDavid du Colombier if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now) 3065e96a66cSDavid du Colombier vtFatal("bad heap ordering"); 3075e96a66cSDavid du Colombier k = (i << 1) + 1; 3085e96a66cSDavid du Colombier if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now) 3095e96a66cSDavid du Colombier vtFatal("bad heap ordering"); 3105e96a66cSDavid du Colombier k++; 3115e96a66cSDavid du Colombier if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now) 3125e96a66cSDavid du Colombier vtFatal("bad heap ordering"); 3135e96a66cSDavid du Colombier } 3145e96a66cSDavid du Colombier 3155e96a66cSDavid du Colombier refed = 0; 3165e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){ 3175e96a66cSDavid du Colombier b = &c->blocks[i]; 3185e96a66cSDavid du Colombier if(b->data != &c->mem[i * size]) 3195e96a66cSDavid du Colombier vtFatal("mis-blocked at %d", i); 3205e96a66cSDavid du Colombier if(b->ref && b->heap == BadHeap){ 3215e96a66cSDavid du Colombier refed++; 3225e96a66cSDavid du Colombier } 3235e96a66cSDavid du Colombier } 3245e96a66cSDavid du Colombier if(c->nheap + refed != c->nblocks){ 3253b86f2f8SDavid du Colombier fprint(2, "%s: cacheCheck: nheap %d refed %d nblocks %ld\n", argv0, c->nheap, refed, c->nblocks); 3265e96a66cSDavid du Colombier cacheDump(c); 3275e96a66cSDavid du Colombier } 3285e96a66cSDavid du Colombier assert(c->nheap + refed == c->nblocks); 3295e96a66cSDavid du Colombier refed = 0; 3305e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){ 3315e96a66cSDavid du Colombier b = &c->blocks[i]; 3325e96a66cSDavid du Colombier if(b->ref){ 3333b86f2f8SDavid 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); 3345e96a66cSDavid du Colombier refed++; 3355e96a66cSDavid du Colombier } 3365e96a66cSDavid du Colombier } 3373b86f2f8SDavid du Colombier if(refed > 0)fprint(2, "%s: cacheCheck: in used %d\n", argv0, refed); 3385e96a66cSDavid du Colombier } 3395e96a66cSDavid du Colombier 3405e96a66cSDavid du Colombier 3415e96a66cSDavid du Colombier /* 3425e96a66cSDavid du Colombier * locate the block with the oldest second to last use. 3435e96a66cSDavid du Colombier * remove it from the heap, and fix up the heap. 3445e96a66cSDavid du Colombier */ 3455e96a66cSDavid du Colombier /* called with c->lk held */ 3465e96a66cSDavid du Colombier static Block * 3475e96a66cSDavid du Colombier cacheBumpBlock(Cache *c) 3485e96a66cSDavid du Colombier { 3490b9a5132SDavid du Colombier int printed; 3505e96a66cSDavid du Colombier Block *b; 3515e96a66cSDavid du Colombier 3525e96a66cSDavid du Colombier /* 3535e96a66cSDavid du Colombier * locate the block with the oldest second to last use. 3545e96a66cSDavid du Colombier * remove it from the heap, and fix up the heap. 3555e96a66cSDavid du Colombier */ 3560b9a5132SDavid du Colombier printed = 0; 357fe853e23SDavid du Colombier if(c->nheap == 0){ 358fe853e23SDavid du Colombier while(c->nheap == 0){ 359fe853e23SDavid du Colombier vtWakeup(c->flush); 360fe853e23SDavid du Colombier vtSleep(c->heapwait); 3610b9a5132SDavid du Colombier if(c->nheap == 0){ 3620b9a5132SDavid du Colombier printed = 1; 3633b86f2f8SDavid du Colombier fprint(2, "%s: entire cache is busy, %d dirty " 3643b86f2f8SDavid du Colombier "-- waking flush thread\n", 3653b86f2f8SDavid du Colombier argv0, c->ndirty); 366fe853e23SDavid du Colombier } 3670b9a5132SDavid du Colombier } 3680b9a5132SDavid du Colombier if(printed) 3693b86f2f8SDavid du Colombier fprint(2, "%s: cache is okay again, %d dirty\n", 3703b86f2f8SDavid du Colombier argv0, c->ndirty); 371fe853e23SDavid du Colombier } 372fe853e23SDavid du Colombier 3735e96a66cSDavid du Colombier b = c->heap[0]; 3745e96a66cSDavid du Colombier heapDel(b); 3755e96a66cSDavid du Colombier 3765e96a66cSDavid du Colombier assert(b->heap == BadHeap); 3775e96a66cSDavid du Colombier assert(b->ref == 0); 37861a5f0d0SDavid du Colombier assert(b->iostate != BioDirty && b->iostate != BioReading && b->iostate != BioWriting); 3795e96a66cSDavid du Colombier assert(b->prior == nil); 3805e96a66cSDavid du Colombier assert(b->uhead == nil); 3815e96a66cSDavid du Colombier 3825e96a66cSDavid du Colombier /* 3835e96a66cSDavid du Colombier * unchain the block from hash chain 3845e96a66cSDavid du Colombier */ 3855e96a66cSDavid du Colombier if(b->prev){ 3865e96a66cSDavid du Colombier *(b->prev) = b->next; 3875e96a66cSDavid du Colombier if(b->next) 3885e96a66cSDavid du Colombier b->next->prev = b->prev; 3895e96a66cSDavid du Colombier b->prev = nil; 3905e96a66cSDavid du Colombier } 3915e96a66cSDavid du Colombier 3925e96a66cSDavid du Colombier 3933b86f2f8SDavid du Colombier if(0)fprint(2, "%s: dropping %d:%x:%V\n", argv0, b->part, b->addr, b->score); 3945e96a66cSDavid du Colombier /* set block to a reasonable state */ 3955e96a66cSDavid du Colombier b->ref = 1; 3965e96a66cSDavid du Colombier b->part = PartError; 3975e96a66cSDavid du Colombier memset(&b->l, 0, sizeof(b->l)); 3985e96a66cSDavid du Colombier b->iostate = BioEmpty; 3995e96a66cSDavid du Colombier 4005e96a66cSDavid du Colombier return b; 4015e96a66cSDavid du Colombier } 4025e96a66cSDavid du Colombier 4035e96a66cSDavid du Colombier /* 4045e96a66cSDavid du Colombier * look for a particular version of the block in the memory cache. 4055e96a66cSDavid du Colombier */ 4065e96a66cSDavid du Colombier static Block * 4075e96a66cSDavid du Colombier _cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers, 4085e96a66cSDavid du Colombier int waitlock, int *lockfailure) 4095e96a66cSDavid du Colombier { 4105e96a66cSDavid du Colombier Block *b; 4115e96a66cSDavid du Colombier ulong h; 4125e96a66cSDavid du Colombier 4135e96a66cSDavid du Colombier h = addr % c->hashSize; 4145e96a66cSDavid du Colombier 4155e96a66cSDavid du Colombier if(lockfailure) 4165e96a66cSDavid du Colombier *lockfailure = 0; 4175e96a66cSDavid du Colombier 4185e96a66cSDavid du Colombier /* 4195e96a66cSDavid du Colombier * look for the block in the cache 4205e96a66cSDavid du Colombier */ 4215e96a66cSDavid du Colombier vtLock(c->lk); 4225e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){ 4235e96a66cSDavid du Colombier if(b->part == part && b->addr == addr) 4245e96a66cSDavid du Colombier break; 4255e96a66cSDavid du Colombier } 4265e96a66cSDavid du Colombier if(b == nil || b->vers != vers){ 4275e96a66cSDavid du Colombier vtUnlock(c->lk); 4285e96a66cSDavid du Colombier return nil; 4295e96a66cSDavid du Colombier } 4305e96a66cSDavid du Colombier if(!waitlock && !vtCanLock(b->lk)){ 4315e96a66cSDavid du Colombier *lockfailure = 1; 4325e96a66cSDavid du Colombier vtUnlock(c->lk); 4335e96a66cSDavid du Colombier return nil; 4345e96a66cSDavid du Colombier } 4355e96a66cSDavid du Colombier heapDel(b); 4365e96a66cSDavid du Colombier b->ref++; 4375e96a66cSDavid du Colombier vtUnlock(c->lk); 4385e96a66cSDavid du Colombier 4395e96a66cSDavid du Colombier bwatchLock(b); 4405e96a66cSDavid du Colombier if(waitlock) 4415e96a66cSDavid du Colombier vtLock(b->lk); 4425e96a66cSDavid du Colombier b->nlock = 1; 4435e96a66cSDavid du Colombier 4445e96a66cSDavid du Colombier for(;;){ 4455e96a66cSDavid du Colombier switch(b->iostate){ 4465e96a66cSDavid du Colombier default: 4475e96a66cSDavid du Colombier abort(); 4485e96a66cSDavid du Colombier case BioEmpty: 4495e96a66cSDavid du Colombier case BioLabel: 4505e96a66cSDavid du Colombier case BioClean: 4515e96a66cSDavid du Colombier case BioDirty: 4525e96a66cSDavid du Colombier if(b->vers != vers){ 4535e96a66cSDavid du Colombier blockPut(b); 4545e96a66cSDavid du Colombier return nil; 4555e96a66cSDavid du Colombier } 4565e96a66cSDavid du Colombier return b; 4575e96a66cSDavid du Colombier case BioReading: 4585e96a66cSDavid du Colombier case BioWriting: 4595e96a66cSDavid du Colombier vtSleep(b->ioready); 4605e96a66cSDavid du Colombier break; 4615e96a66cSDavid du Colombier case BioVentiError: 46211e1fb05SDavid du Colombier blockPut(b); 463e569ccb5SDavid du Colombier vtSetError("venti i/o error block 0x%.8ux", addr); 46411e1fb05SDavid du Colombier return nil; 4655e96a66cSDavid du Colombier case BioReadError: 4665e96a66cSDavid du Colombier blockPut(b); 467223a0358SDavid du Colombier vtSetError("error reading block 0x%.8ux", addr); 4685e96a66cSDavid du Colombier return nil; 4695e96a66cSDavid du Colombier } 4705e96a66cSDavid du Colombier } 4715e96a66cSDavid du Colombier /* NOT REACHED */ 4725e96a66cSDavid du Colombier } 4735e96a66cSDavid du Colombier static Block* 4745e96a66cSDavid du Colombier cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers) 4755e96a66cSDavid du Colombier { 4765e96a66cSDavid du Colombier return _cacheLocalLookup(c, part, addr, vers, 1, 0); 4775e96a66cSDavid du Colombier } 4785e96a66cSDavid du Colombier 4795e96a66cSDavid du Colombier 4805e96a66cSDavid du Colombier /* 4815e96a66cSDavid du Colombier * fetch a local (on-disk) block from the memory cache. 4825e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block. 4835e96a66cSDavid du Colombier */ 4845e96a66cSDavid du Colombier Block * 4855e96a66cSDavid du Colombier _cacheLocal(Cache *c, int part, u32int addr, int mode, u32int epoch) 4865e96a66cSDavid du Colombier { 4875e96a66cSDavid du Colombier Block *b; 4885e96a66cSDavid du Colombier ulong h; 4895e96a66cSDavid du Colombier 4905e96a66cSDavid du Colombier assert(part != PartVenti); 4915e96a66cSDavid du Colombier 4925e96a66cSDavid du Colombier h = addr % c->hashSize; 4935e96a66cSDavid du Colombier 4945e96a66cSDavid du Colombier /* 4955e96a66cSDavid du Colombier * look for the block in the cache 4965e96a66cSDavid du Colombier */ 4975e96a66cSDavid du Colombier vtLock(c->lk); 4985e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){ 4995e96a66cSDavid du Colombier if(b->part != part || b->addr != addr) 5005e96a66cSDavid du Colombier continue; 5015e96a66cSDavid du Colombier if(epoch && b->l.epoch != epoch){ 5023b86f2f8SDavid du Colombier fprint(2, "%s: _cacheLocal want epoch %ud got %ud\n", argv0, epoch, b->l.epoch); 5035e96a66cSDavid du Colombier vtUnlock(c->lk); 5045e96a66cSDavid du Colombier vtSetError(ELabelMismatch); 5055e96a66cSDavid du Colombier return nil; 5065e96a66cSDavid du Colombier } 5075e96a66cSDavid du Colombier heapDel(b); 5085e96a66cSDavid du Colombier b->ref++; 5095e96a66cSDavid du Colombier break; 5105e96a66cSDavid du Colombier } 5115e96a66cSDavid du Colombier 5125e96a66cSDavid du Colombier if(b == nil){ 5135e96a66cSDavid du Colombier b = cacheBumpBlock(c); 5145e96a66cSDavid du Colombier 5155e96a66cSDavid du Colombier b->part = part; 5165e96a66cSDavid du Colombier b->addr = addr; 5175e96a66cSDavid du Colombier localToGlobal(addr, b->score); 5185e96a66cSDavid du Colombier 5195e96a66cSDavid du Colombier /* chain onto correct hash */ 5205e96a66cSDavid du Colombier b->next = c->heads[h]; 5215e96a66cSDavid du Colombier c->heads[h] = b; 5225e96a66cSDavid du Colombier if(b->next != nil) 5235e96a66cSDavid du Colombier b->next->prev = &b->next; 5245e96a66cSDavid du Colombier b->prev = &c->heads[h]; 5255e96a66cSDavid du Colombier } 5265e96a66cSDavid du Colombier 5275e96a66cSDavid du Colombier vtUnlock(c->lk); 5285e96a66cSDavid du Colombier 5295e96a66cSDavid du Colombier /* 5305e96a66cSDavid du Colombier * BUG: what if the epoch changes right here? 5315e96a66cSDavid du Colombier * In the worst case, we could end up in some weird 5325e96a66cSDavid du Colombier * lock loop, because the block we want no longer exists, 5335e96a66cSDavid du Colombier * and instead we're trying to lock a block we have no 5345e96a66cSDavid du Colombier * business grabbing. 5355e96a66cSDavid du Colombier * 5365e96a66cSDavid du Colombier * For now, I'm not going to worry about it. 5375e96a66cSDavid du Colombier */ 5385e96a66cSDavid du Colombier 5393b86f2f8SDavid du Colombier if(0)fprint(2, "%s: cacheLocal: %d: %d %x\n", argv0, getpid(), b->part, b->addr); 5405e96a66cSDavid du Colombier bwatchLock(b); 5415e96a66cSDavid du Colombier vtLock(b->lk); 5425e96a66cSDavid du Colombier b->nlock = 1; 5435e96a66cSDavid du Colombier 5445e96a66cSDavid du Colombier if(part == PartData && b->iostate == BioEmpty){ 5455e96a66cSDavid du Colombier if(!readLabel(c, &b->l, addr)){ 5465e96a66cSDavid du Colombier blockPut(b); 5475e96a66cSDavid du Colombier return nil; 5485e96a66cSDavid du Colombier } 5495e96a66cSDavid du Colombier blockSetIOState(b, BioLabel); 5505e96a66cSDavid du Colombier } 5515e96a66cSDavid du Colombier if(epoch && b->l.epoch != epoch){ 5525e96a66cSDavid du Colombier blockPut(b); 5533b86f2f8SDavid du Colombier fprint(2, "%s: _cacheLocal want epoch %ud got %ud\n", argv0, epoch, b->l.epoch); 5545e96a66cSDavid du Colombier vtSetError(ELabelMismatch); 5555e96a66cSDavid du Colombier return nil; 5565e96a66cSDavid du Colombier } 5575e96a66cSDavid du Colombier 5585e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 5595e96a66cSDavid du Colombier for(;;){ 5605e96a66cSDavid du Colombier switch(b->iostate){ 5615e96a66cSDavid du Colombier default: 5625e96a66cSDavid du Colombier abort(); 5635e96a66cSDavid du Colombier case BioEmpty: 5645e96a66cSDavid du Colombier case BioLabel: 5655e96a66cSDavid du Colombier if(mode == OOverWrite){ 5665e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 5675e96a66cSDavid du Colombier return b; 5685e96a66cSDavid du Colombier } 5695e96a66cSDavid du Colombier diskRead(c->disk, b); 5705e96a66cSDavid du Colombier vtSleep(b->ioready); 5715e96a66cSDavid du Colombier break; 5725e96a66cSDavid du Colombier case BioClean: 5735e96a66cSDavid du Colombier case BioDirty: 5745e96a66cSDavid du Colombier return b; 5755e96a66cSDavid du Colombier case BioReading: 5765e96a66cSDavid du Colombier case BioWriting: 5775e96a66cSDavid du Colombier vtSleep(b->ioready); 5785e96a66cSDavid du Colombier break; 5795e96a66cSDavid du Colombier case BioReadError: 5805e96a66cSDavid du Colombier blockSetIOState(b, BioEmpty); 5815e96a66cSDavid du Colombier blockPut(b); 582223a0358SDavid du Colombier vtSetError("error reading block 0x%.8ux", addr); 5835e96a66cSDavid du Colombier return nil; 5845e96a66cSDavid du Colombier } 5855e96a66cSDavid du Colombier } 5865e96a66cSDavid du Colombier /* NOT REACHED */ 5875e96a66cSDavid du Colombier } 5885e96a66cSDavid du Colombier 5895e96a66cSDavid du Colombier Block * 5905e96a66cSDavid du Colombier cacheLocal(Cache *c, int part, u32int addr, int mode) 5915e96a66cSDavid du Colombier { 5925e96a66cSDavid du Colombier return _cacheLocal(c, part, addr, mode, 0); 5935e96a66cSDavid du Colombier } 5945e96a66cSDavid du Colombier 5955e96a66cSDavid du Colombier /* 5965e96a66cSDavid du Colombier * fetch a local (on-disk) block from the memory cache. 5975e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block. 5985e96a66cSDavid du Colombier * check tag and type. 5995e96a66cSDavid du Colombier */ 6005e96a66cSDavid du Colombier Block * 6015e96a66cSDavid du Colombier cacheLocalData(Cache *c, u32int addr, int type, u32int tag, int mode, u32int epoch) 6025e96a66cSDavid du Colombier { 6035e96a66cSDavid du Colombier Block *b; 6045e96a66cSDavid du Colombier 6055e96a66cSDavid du Colombier b = _cacheLocal(c, PartData, addr, mode, epoch); 6065e96a66cSDavid du Colombier if(b == nil) 6075e96a66cSDavid du Colombier return nil; 6085e96a66cSDavid du Colombier if(b->l.type != type || b->l.tag != tag){ 6093b86f2f8SDavid du Colombier fprint(2, "%s: cacheLocalData: addr=%d type got %d exp %d: tag got %ux exp %ux\n", 6103b86f2f8SDavid du Colombier argv0, addr, b->l.type, type, b->l.tag, tag); 6115e96a66cSDavid du Colombier vtSetError(ELabelMismatch); 6125e96a66cSDavid du Colombier blockPut(b); 6135e96a66cSDavid du Colombier return nil; 6145e96a66cSDavid du Colombier } 6155e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 6165e96a66cSDavid du Colombier return b; 6175e96a66cSDavid du Colombier } 6185e96a66cSDavid du Colombier 6195e96a66cSDavid du Colombier /* 6205e96a66cSDavid du Colombier * fetch a global (Venti) block from the memory cache. 6215e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block. 6225e96a66cSDavid du Colombier * check tag and type if it's really a local block in disguise. 6235e96a66cSDavid du Colombier */ 6245e96a66cSDavid du Colombier Block * 6255e96a66cSDavid du Colombier cacheGlobal(Cache *c, uchar score[VtScoreSize], int type, u32int tag, int mode) 6265e96a66cSDavid du Colombier { 6275e96a66cSDavid du Colombier int n; 6285e96a66cSDavid du Colombier Block *b; 6295e96a66cSDavid du Colombier ulong h; 6305e96a66cSDavid du Colombier u32int addr; 6315e96a66cSDavid du Colombier 6325e96a66cSDavid du Colombier addr = globalToLocal(score); 6335e96a66cSDavid du Colombier if(addr != NilBlock){ 6345e96a66cSDavid du Colombier b = cacheLocalData(c, addr, type, tag, mode, 0); 6355e96a66cSDavid du Colombier if(b) 6365e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 6375e96a66cSDavid du Colombier return b; 6385e96a66cSDavid du Colombier } 6395e96a66cSDavid du Colombier 6405e96a66cSDavid du Colombier h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->hashSize; 6415e96a66cSDavid du Colombier 6425e96a66cSDavid du Colombier /* 6435e96a66cSDavid du Colombier * look for the block in the cache 6445e96a66cSDavid du Colombier */ 6455e96a66cSDavid du Colombier vtLock(c->lk); 6465e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){ 6475e96a66cSDavid du Colombier if(b->part != PartVenti || memcmp(b->score, score, VtScoreSize) != 0 || b->l.type != type) 6485e96a66cSDavid du Colombier continue; 6495e96a66cSDavid du Colombier heapDel(b); 6505e96a66cSDavid du Colombier b->ref++; 6515e96a66cSDavid du Colombier break; 6525e96a66cSDavid du Colombier } 6535e96a66cSDavid du Colombier 6545e96a66cSDavid du Colombier if(b == nil){ 6553b86f2f8SDavid du Colombier if(0)fprint(2, "%s: cacheGlobal %V %d\n", argv0, score, type); 6565e96a66cSDavid du Colombier 6575e96a66cSDavid du Colombier b = cacheBumpBlock(c); 6585e96a66cSDavid du Colombier 6595e96a66cSDavid du Colombier b->part = PartVenti; 6605e96a66cSDavid du Colombier b->addr = NilBlock; 6615e96a66cSDavid du Colombier b->l.type = type; 6625e96a66cSDavid du Colombier memmove(b->score, score, VtScoreSize); 6635e96a66cSDavid du Colombier 6645e96a66cSDavid du Colombier /* chain onto correct hash */ 6655e96a66cSDavid du Colombier b->next = c->heads[h]; 6665e96a66cSDavid du Colombier c->heads[h] = b; 6675e96a66cSDavid du Colombier if(b->next != nil) 6685e96a66cSDavid du Colombier b->next->prev = &b->next; 6695e96a66cSDavid du Colombier b->prev = &c->heads[h]; 6705e96a66cSDavid du Colombier } 6715e96a66cSDavid du Colombier vtUnlock(c->lk); 6725e96a66cSDavid du Colombier 6735e96a66cSDavid du Colombier bwatchLock(b); 6745e96a66cSDavid du Colombier vtLock(b->lk); 6755e96a66cSDavid du Colombier b->nlock = 1; 6765e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 6775e96a66cSDavid du Colombier 6785e96a66cSDavid du Colombier switch(b->iostate){ 6795e96a66cSDavid du Colombier default: 6805e96a66cSDavid du Colombier abort(); 6815e96a66cSDavid du Colombier case BioEmpty: 6825e96a66cSDavid du Colombier n = vtRead(c->z, score, vtType[type], b->data, c->size); 6835e96a66cSDavid du Colombier if(n < 0 || !vtSha1Check(score, b->data, n)){ 6845e96a66cSDavid du Colombier blockSetIOState(b, BioVentiError); 6855e96a66cSDavid du Colombier blockPut(b); 686223a0358SDavid du Colombier vtSetError( 687223a0358SDavid du Colombier "venti error reading block %V or wrong score: %r", 688223a0358SDavid du Colombier score); 6895e96a66cSDavid du Colombier return nil; 6905e96a66cSDavid du Colombier } 6915e96a66cSDavid du Colombier vtZeroExtend(vtType[type], b->data, n, c->size); 6925e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 6935e96a66cSDavid du Colombier return b; 6945e96a66cSDavid du Colombier case BioClean: 6955e96a66cSDavid du Colombier return b; 6965e96a66cSDavid du Colombier case BioVentiError: 69711e1fb05SDavid du Colombier blockPut(b); 698223a0358SDavid du Colombier vtSetError("venti i/o error or wrong score, block %V", score); 69911e1fb05SDavid du Colombier return nil; 7005e96a66cSDavid du Colombier case BioReadError: 7015e96a66cSDavid du Colombier blockPut(b); 702223a0358SDavid du Colombier vtSetError("error reading block %V", b->score); 7035e96a66cSDavid du Colombier return nil; 7045e96a66cSDavid du Colombier } 7055e96a66cSDavid du Colombier /* NOT REACHED */ 7065e96a66cSDavid du Colombier } 7075e96a66cSDavid du Colombier 7085e96a66cSDavid du Colombier /* 7095e96a66cSDavid du Colombier * allocate a new on-disk block and load it into the memory cache. 7105e96a66cSDavid du Colombier * BUG: if the disk is full, should we flush some of it to Venti? 7115e96a66cSDavid du Colombier */ 7125e96a66cSDavid du Colombier static u32int lastAlloc; 7135e96a66cSDavid du Colombier 7145e96a66cSDavid du Colombier Block * 7155e96a66cSDavid du Colombier cacheAllocBlock(Cache *c, int type, u32int tag, u32int epoch, u32int epochLow) 7165e96a66cSDavid du Colombier { 7175e96a66cSDavid du Colombier FreeList *fl; 7185e96a66cSDavid du Colombier u32int addr; 7195e96a66cSDavid du Colombier Block *b; 7205e96a66cSDavid du Colombier int n, nwrap; 7215e96a66cSDavid du Colombier Label lab; 7225e96a66cSDavid du Colombier 7235e96a66cSDavid du Colombier n = c->size / LabelSize; 7245e96a66cSDavid du Colombier fl = c->fl; 7255e96a66cSDavid du Colombier 7265e96a66cSDavid du Colombier vtLock(fl->lk); 7275e96a66cSDavid du Colombier addr = fl->last; 7285e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly); 7295e96a66cSDavid du Colombier if(b == nil){ 7303b86f2f8SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx %R\n", argv0); 7315e96a66cSDavid du Colombier vtUnlock(fl->lk); 7325e96a66cSDavid du Colombier return nil; 7335e96a66cSDavid du Colombier } 7345e96a66cSDavid du Colombier nwrap = 0; 7355e96a66cSDavid du Colombier for(;;){ 7365e96a66cSDavid du Colombier if(++addr >= fl->end){ 7375e96a66cSDavid du Colombier addr = 0; 7385e96a66cSDavid du Colombier if(++nwrap >= 2){ 7395e96a66cSDavid du Colombier blockPut(b); 7405e96a66cSDavid du Colombier vtSetError("disk is full"); 741*b3b810bfSDavid du Colombier /* 742*b3b810bfSDavid du Colombier * try to avoid a continuous spew of console 743*b3b810bfSDavid du Colombier * messages. 744*b3b810bfSDavid du Colombier */ 745*b3b810bfSDavid du Colombier if (fl->last != 0) 746*b3b810bfSDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx1 %R\n", 747*b3b810bfSDavid du Colombier argv0); 748*b3b810bfSDavid du Colombier fl->last = 0; 7495e96a66cSDavid du Colombier vtUnlock(fl->lk); 7505e96a66cSDavid du Colombier return nil; 7515e96a66cSDavid du Colombier } 7525e96a66cSDavid du Colombier } 7535e96a66cSDavid du Colombier if(addr%n == 0){ 7545e96a66cSDavid du Colombier blockPut(b); 7555e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly); 7565e96a66cSDavid du Colombier if(b == nil){ 7575e96a66cSDavid du Colombier fl->last = addr; 7583b86f2f8SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx2 %R\n", argv0); 7595e96a66cSDavid du Colombier vtUnlock(fl->lk); 7605e96a66cSDavid du Colombier return nil; 7615e96a66cSDavid du Colombier } 7625e96a66cSDavid du Colombier } 7635e96a66cSDavid du Colombier if(!labelUnpack(&lab, b->data, addr%n)) 7645e96a66cSDavid du Colombier continue; 7655e96a66cSDavid du Colombier if(lab.state == BsFree) 7665e96a66cSDavid du Colombier goto Found; 767e569ccb5SDavid du Colombier if(lab.state&BsClosed) 768e569ccb5SDavid du Colombier if(lab.epochClose <= epochLow || lab.epoch==lab.epochClose) 7695e96a66cSDavid du Colombier goto Found; 7705e96a66cSDavid du Colombier } 7715e96a66cSDavid du Colombier Found: 7725e96a66cSDavid du Colombier blockPut(b); 7735e96a66cSDavid du Colombier b = cacheLocal(c, PartData, addr, OOverWrite); 7745e96a66cSDavid du Colombier if(b == nil){ 7753b86f2f8SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx3 %R\n", argv0); 7765e96a66cSDavid du Colombier return nil; 7775e96a66cSDavid du Colombier } 7785e96a66cSDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean); 7795e96a66cSDavid du Colombier fl->last = addr; 7805e96a66cSDavid du Colombier lab.type = type; 7815e96a66cSDavid du Colombier lab.tag = tag; 7825e96a66cSDavid du Colombier lab.state = BsAlloc; 7835e96a66cSDavid du Colombier lab.epoch = epoch; 7845e96a66cSDavid du Colombier lab.epochClose = ~(u32int)0; 785e569ccb5SDavid du Colombier if(!blockSetLabel(b, &lab, 1)){ 7863b86f2f8SDavid du Colombier fprint(2, "%s: cacheAllocBlock: xxx4 %R\n", argv0); 7875e96a66cSDavid du Colombier blockPut(b); 7885e96a66cSDavid du Colombier return nil; 7895e96a66cSDavid du Colombier } 7905e96a66cSDavid du Colombier vtZeroExtend(vtType[type], b->data, 0, c->size); 7915e96a66cSDavid du Colombier if(0)diskWrite(c->disk, b); 7925e96a66cSDavid du Colombier 7933b86f2f8SDavid du Colombier if(0)fprint(2, "%s: fsAlloc %ud type=%d tag = %ux\n", argv0, addr, type, tag); 7945e96a66cSDavid du Colombier lastAlloc = addr; 7957abd426fSDavid du Colombier fl->nused++; 7965e96a66cSDavid du Colombier vtUnlock(fl->lk); 7975e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 7985e96a66cSDavid du Colombier return b; 7995e96a66cSDavid du Colombier } 8005e96a66cSDavid du Colombier 8010b9a5132SDavid du Colombier int 8020b9a5132SDavid du Colombier cacheDirty(Cache *c) 8030b9a5132SDavid du Colombier { 8040b9a5132SDavid du Colombier return c->ndirty; 8050b9a5132SDavid du Colombier } 8060b9a5132SDavid du Colombier 8077abd426fSDavid du Colombier void 8087abd426fSDavid du Colombier cacheCountUsed(Cache *c, u32int epochLow, u32int *used, u32int *total, u32int *bsize) 8097abd426fSDavid du Colombier { 8107abd426fSDavid du Colombier int n; 8117abd426fSDavid du Colombier u32int addr, nused; 8127abd426fSDavid du Colombier Block *b; 8137abd426fSDavid du Colombier Label lab; 8147abd426fSDavid du Colombier FreeList *fl; 8157abd426fSDavid du Colombier 8167abd426fSDavid du Colombier fl = c->fl; 8177abd426fSDavid du Colombier n = c->size / LabelSize; 8187abd426fSDavid du Colombier *bsize = c->size; 8197abd426fSDavid du Colombier vtLock(fl->lk); 8207abd426fSDavid du Colombier if(fl->epochLow == epochLow){ 8217abd426fSDavid du Colombier *used = fl->nused; 8227abd426fSDavid du Colombier *total = fl->end; 8237abd426fSDavid du Colombier vtUnlock(fl->lk); 8247abd426fSDavid du Colombier return; 8257abd426fSDavid du Colombier } 8267abd426fSDavid du Colombier b = nil; 8277abd426fSDavid du Colombier nused = 0; 8287abd426fSDavid du Colombier for(addr=0; addr<fl->end; addr++){ 8297abd426fSDavid du Colombier if(addr%n == 0){ 8307abd426fSDavid du Colombier blockPut(b); 8317abd426fSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly); 8327abd426fSDavid du Colombier if(b == nil){ 8333b86f2f8SDavid du Colombier fprint(2, "%s: flCountUsed: loading %ux: %R\n", 8343b86f2f8SDavid du Colombier argv0, addr/n); 8357abd426fSDavid du Colombier break; 8367abd426fSDavid du Colombier } 8377abd426fSDavid du Colombier } 8387abd426fSDavid du Colombier if(!labelUnpack(&lab, b->data, addr%n)) 8397abd426fSDavid du Colombier continue; 8407abd426fSDavid du Colombier if(lab.state == BsFree) 8417abd426fSDavid du Colombier continue; 842e569ccb5SDavid du Colombier if(lab.state&BsClosed) 843e569ccb5SDavid du Colombier if(lab.epochClose <= epochLow || lab.epoch==lab.epochClose) 8447abd426fSDavid du Colombier continue; 8457abd426fSDavid du Colombier nused++; 8467abd426fSDavid du Colombier } 8477abd426fSDavid du Colombier blockPut(b); 8487abd426fSDavid du Colombier if(addr == fl->end){ 8497abd426fSDavid du Colombier fl->nused = nused; 8507abd426fSDavid du Colombier fl->epochLow = epochLow; 8517abd426fSDavid du Colombier } 8527abd426fSDavid du Colombier *used = nused; 8537abd426fSDavid du Colombier *total = fl->end; 8547abd426fSDavid du Colombier vtUnlock(fl->lk); 8557abd426fSDavid du Colombier return; 8567abd426fSDavid du Colombier } 8577abd426fSDavid du Colombier 8585e96a66cSDavid du Colombier static FreeList * 8595e96a66cSDavid du Colombier flAlloc(u32int end) 8605e96a66cSDavid du Colombier { 8615e96a66cSDavid du Colombier FreeList *fl; 8625e96a66cSDavid du Colombier 8635e96a66cSDavid du Colombier fl = vtMemAllocZ(sizeof(*fl)); 8645e96a66cSDavid du Colombier fl->lk = vtLockAlloc(); 865b5e190f4SDavid du Colombier fl->last = 0; 8665e96a66cSDavid du Colombier fl->end = end; 8675e96a66cSDavid du Colombier return fl; 8685e96a66cSDavid du Colombier } 8695e96a66cSDavid du Colombier 8705e96a66cSDavid du Colombier static void 8715e96a66cSDavid du Colombier flFree(FreeList *fl) 8725e96a66cSDavid du Colombier { 8735e96a66cSDavid du Colombier vtLockFree(fl->lk); 8745e96a66cSDavid du Colombier vtMemFree(fl); 8755e96a66cSDavid du Colombier } 8765e96a66cSDavid du Colombier 8775e96a66cSDavid du Colombier u32int 8785e96a66cSDavid du Colombier cacheLocalSize(Cache *c, int part) 8795e96a66cSDavid du Colombier { 8805e96a66cSDavid du Colombier return diskSize(c->disk, part); 8815e96a66cSDavid du Colombier } 8825e96a66cSDavid du Colombier 8835e96a66cSDavid du Colombier /* 8845e96a66cSDavid du Colombier * The thread that has locked b may refer to it by 8855e96a66cSDavid du Colombier * multiple names. Nlock counts the number of 8865e96a66cSDavid du Colombier * references the locking thread holds. It will call 8875e96a66cSDavid du Colombier * blockPut once per reference. 8885e96a66cSDavid du Colombier */ 8895e96a66cSDavid du Colombier void 8905e96a66cSDavid du Colombier blockDupLock(Block *b) 8915e96a66cSDavid du Colombier { 8925e96a66cSDavid du Colombier assert(b->nlock > 0); 8935e96a66cSDavid du Colombier b->nlock++; 8945e96a66cSDavid du Colombier } 8955e96a66cSDavid du Colombier 8965e96a66cSDavid du Colombier /* 8975e96a66cSDavid du Colombier * we're done with the block. 8985e96a66cSDavid du Colombier * unlock it. can't use it after calling this. 8995e96a66cSDavid du Colombier */ 9005e96a66cSDavid du Colombier void 9015e96a66cSDavid du Colombier blockPut(Block* b) 9025e96a66cSDavid du Colombier { 9035e96a66cSDavid du Colombier Cache *c; 9045e96a66cSDavid du Colombier 9055e96a66cSDavid du Colombier if(b == nil) 9065e96a66cSDavid du Colombier return; 9075e96a66cSDavid du Colombier 9083b86f2f8SDavid 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)); 9095e96a66cSDavid du Colombier 9105e96a66cSDavid du Colombier if(b->iostate == BioDirty) 9115e96a66cSDavid du Colombier bwatchDependency(b); 9125e96a66cSDavid du Colombier 9135e96a66cSDavid du Colombier if(--b->nlock > 0) 9145e96a66cSDavid du Colombier return; 9155e96a66cSDavid du Colombier 9165e96a66cSDavid du Colombier /* 9175e96a66cSDavid du Colombier * b->nlock should probably stay at zero while 9185e96a66cSDavid du Colombier * the block is unlocked, but diskThread and vtSleep 9195e96a66cSDavid du Colombier * conspire to assume that they can just vtLock(b->lk); blockPut(b), 9205e96a66cSDavid du Colombier * so we have to keep b->nlock set to 1 even 9215e96a66cSDavid du Colombier * when the block is unlocked. 9225e96a66cSDavid du Colombier */ 9235e96a66cSDavid du Colombier assert(b->nlock == 0); 9245e96a66cSDavid du Colombier b->nlock = 1; 9255e96a66cSDavid du Colombier // b->pc = 0; 9265e96a66cSDavid du Colombier 9275e96a66cSDavid du Colombier bwatchUnlock(b); 9285e96a66cSDavid du Colombier vtUnlock(b->lk); 9295e96a66cSDavid du Colombier c = b->c; 9305e96a66cSDavid du Colombier vtLock(c->lk); 9315e96a66cSDavid du Colombier 9325e96a66cSDavid du Colombier if(--b->ref > 0){ 9335e96a66cSDavid du Colombier vtUnlock(c->lk); 9345e96a66cSDavid du Colombier return; 9355e96a66cSDavid du Colombier } 9365e96a66cSDavid du Colombier 9375e96a66cSDavid du Colombier assert(b->ref == 0); 9385e96a66cSDavid du Colombier switch(b->iostate){ 9395e96a66cSDavid du Colombier default: 9405e96a66cSDavid du Colombier b->used = c->now++; 9415e96a66cSDavid du Colombier heapIns(b); 9425e96a66cSDavid du Colombier break; 9435e96a66cSDavid du Colombier case BioEmpty: 9445e96a66cSDavid du Colombier case BioLabel: 9455e96a66cSDavid du Colombier if(c->nheap == 0) 9465e96a66cSDavid du Colombier b->used = c->now++; 9475e96a66cSDavid du Colombier else 9485e96a66cSDavid du Colombier b->used = c->heap[0]->used; 9495e96a66cSDavid du Colombier heapIns(b); 9505e96a66cSDavid du Colombier break; 9515e96a66cSDavid du Colombier case BioDirty: 9525e96a66cSDavid du Colombier break; 9535e96a66cSDavid du Colombier } 9545e96a66cSDavid du Colombier vtUnlock(c->lk); 9555e96a66cSDavid du Colombier } 9565e96a66cSDavid du Colombier 9575e96a66cSDavid du Colombier /* 958e569ccb5SDavid du Colombier * set the label associated with a block. 9595e96a66cSDavid du Colombier */ 960e569ccb5SDavid du Colombier Block* 961e569ccb5SDavid du Colombier _blockSetLabel(Block *b, Label *l) 9625e96a66cSDavid du Colombier { 963e569ccb5SDavid du Colombier int lpb; 9645e96a66cSDavid du Colombier Block *bb; 9655e96a66cSDavid du Colombier u32int a; 9665e96a66cSDavid du Colombier Cache *c; 9675e96a66cSDavid du Colombier 9685e96a66cSDavid du Colombier c = b->c; 9695e96a66cSDavid du Colombier 970e569ccb5SDavid du Colombier assert(b->part == PartData); 971e569ccb5SDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean || b->iostate == BioDirty); 972e569ccb5SDavid du Colombier lpb = c->size / LabelSize; 973e569ccb5SDavid du Colombier a = b->addr / lpb; 974e569ccb5SDavid du Colombier bb = cacheLocal(c, PartLabel, a, OReadWrite); 975e569ccb5SDavid du Colombier if(bb == nil){ 976e569ccb5SDavid du Colombier blockPut(b); 9775e96a66cSDavid du Colombier return nil; 9785e96a66cSDavid du Colombier } 979e569ccb5SDavid du Colombier b->l = *l; 980e569ccb5SDavid du Colombier labelPack(l, bb->data, b->addr%lpb); 981e569ccb5SDavid du Colombier blockDirty(bb); 982e569ccb5SDavid du Colombier return bb; 983e569ccb5SDavid du Colombier } 984e569ccb5SDavid du Colombier 985e569ccb5SDavid du Colombier int 986e569ccb5SDavid du Colombier blockSetLabel(Block *b, Label *l, int allocating) 987e569ccb5SDavid du Colombier { 988e569ccb5SDavid du Colombier Block *lb; 989e569ccb5SDavid du Colombier Label oldl; 990e569ccb5SDavid du Colombier 991e569ccb5SDavid du Colombier oldl = b->l; 992e569ccb5SDavid du Colombier lb = _blockSetLabel(b, l); 993e569ccb5SDavid du Colombier if(lb == nil) 994e569ccb5SDavid du Colombier return 0; 9955e96a66cSDavid du Colombier 9965e96a66cSDavid du Colombier /* 997e569ccb5SDavid du Colombier * If we're allocating the block, make sure the label (bl) 998e569ccb5SDavid du Colombier * goes to disk before the data block (b) itself. This is to help 999e569ccb5SDavid du Colombier * the blocks that in turn depend on b. 1000e569ccb5SDavid du Colombier * 1001e569ccb5SDavid du Colombier * Suppose bx depends on (must be written out after) b. 1002e569ccb5SDavid du Colombier * Once we write b we'll think it's safe to write bx. 1003e569ccb5SDavid du Colombier * Bx can't get at b unless it has a valid label, though. 1004e569ccb5SDavid du Colombier * 1005e569ccb5SDavid du Colombier * Allocation is the only case in which having a current label 1006e569ccb5SDavid du Colombier * is vital because: 1007e569ccb5SDavid du Colombier * 1008e569ccb5SDavid du Colombier * - l.type is set at allocation and never changes. 1009e569ccb5SDavid du Colombier * - l.tag is set at allocation and never changes. 1010e569ccb5SDavid du Colombier * - l.state is not checked when we load blocks. 1011e569ccb5SDavid du Colombier * - the archiver cares deeply about l.state being 1012e569ccb5SDavid du Colombier * BaActive vs. BaCopied, but that's handled 1013e569ccb5SDavid du Colombier * by direct calls to _blockSetLabel. 10145e96a66cSDavid du Colombier */ 10155e96a66cSDavid du Colombier 1016e569ccb5SDavid du Colombier if(allocating) 1017e569ccb5SDavid du Colombier blockDependency(b, lb, -1, nil, nil); 1018e569ccb5SDavid du Colombier blockPut(lb); 1019e569ccb5SDavid du Colombier return 1; 10205e96a66cSDavid du Colombier } 10215e96a66cSDavid du Colombier 10225e96a66cSDavid du Colombier /* 102361201b97SDavid du Colombier * Record that bb must be written out before b. 102461201b97SDavid du Colombier * If index is given, we're about to overwrite the score/e 102561201b97SDavid du Colombier * at that index in the block. Save the old value so we 10265e96a66cSDavid du Colombier * can write a safer ``old'' version of the block if pressed. 10275e96a66cSDavid du Colombier */ 10285e96a66cSDavid du Colombier void 102961201b97SDavid du Colombier blockDependency(Block *b, Block *bb, int index, uchar *score, Entry *e) 10305e96a66cSDavid du Colombier { 10315e96a66cSDavid du Colombier BList *p; 10325e96a66cSDavid du Colombier 10335e96a66cSDavid du Colombier if(bb->iostate == BioClean) 10345e96a66cSDavid du Colombier return; 10355e96a66cSDavid du Colombier 103661201b97SDavid du Colombier /* 103761201b97SDavid du Colombier * Dependencies for blocks containing Entry structures 103861201b97SDavid du Colombier * or scores must always be explained. The problem with 103961201b97SDavid du Colombier * only explaining some of them is this. Suppose we have two 104061201b97SDavid du Colombier * dependencies for the same field, the first explained 104161201b97SDavid du Colombier * and the second not. We try to write the block when the first 104261201b97SDavid du Colombier * dependency is not written but the second is. We will roll back 104361201b97SDavid du Colombier * the first change even though the second trumps it. 104461201b97SDavid du Colombier */ 104561201b97SDavid du Colombier if(index == -1 && bb->part == PartData) 104661201b97SDavid du Colombier assert(b->l.type == BtData); 104761201b97SDavid du Colombier 10487abd426fSDavid du Colombier if(bb->iostate != BioDirty){ 10493b86f2f8SDavid du Colombier fprint(2, "%s: %d:%x:%d iostate is %d in blockDependency\n", 10503b86f2f8SDavid du Colombier argv0, bb->part, bb->addr, bb->l.type, bb->iostate); 10517abd426fSDavid du Colombier abort(); 10527abd426fSDavid du Colombier } 10535e96a66cSDavid du Colombier 10545e96a66cSDavid du Colombier p = blistAlloc(bb); 10555e96a66cSDavid du Colombier if(p == nil) 10565e96a66cSDavid du Colombier return; 10575e96a66cSDavid du Colombier 10587f1bc48aSDavid du Colombier assert(bb->iostate == BioDirty); 10593b86f2f8SDavid 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); 10605e96a66cSDavid du Colombier 10615e96a66cSDavid du Colombier p->part = bb->part; 10625e96a66cSDavid du Colombier p->addr = bb->addr; 10635e96a66cSDavid du Colombier p->type = bb->l.type; 10645e96a66cSDavid du Colombier p->vers = bb->vers; 10655e96a66cSDavid du Colombier p->index = index; 106661201b97SDavid du Colombier if(p->index >= 0){ 106761201b97SDavid du Colombier /* 106861201b97SDavid du Colombier * This test would just be b->l.type==BtDir except 106961201b97SDavid du Colombier * we need to exclude the super block. 107061201b97SDavid du Colombier */ 107161201b97SDavid du Colombier if(b->l.type == BtDir && b->part == PartData) 107261201b97SDavid du Colombier entryPack(e, p->old.entry, 0); 107361201b97SDavid du Colombier else 107461201b97SDavid du Colombier memmove(p->old.score, score, VtScoreSize); 107561201b97SDavid du Colombier } 10765e96a66cSDavid du Colombier p->next = b->prior; 10775e96a66cSDavid du Colombier b->prior = p; 10785e96a66cSDavid du Colombier } 10795e96a66cSDavid du Colombier 10805e96a66cSDavid du Colombier /* 10815e96a66cSDavid du Colombier * Mark an in-memory block as dirty. If there are too many 1082fe853e23SDavid du Colombier * dirty blocks, start writing some out to disk. 10835e96a66cSDavid du Colombier * 1084fe853e23SDavid du Colombier * If there were way too many dirty blocks, we used to 1085fe853e23SDavid du Colombier * try to do some flushing ourselves, but it's just too dangerous -- 1086fe853e23SDavid du Colombier * it implies that the callers cannot have any of our priors locked, 1087fe853e23SDavid du Colombier * but this is hard to avoid in some cases. 10885e96a66cSDavid du Colombier */ 10895e96a66cSDavid du Colombier int 10905e96a66cSDavid du Colombier blockDirty(Block *b) 10915e96a66cSDavid du Colombier { 10925e96a66cSDavid du Colombier Cache *c; 10935e96a66cSDavid du Colombier 10945e96a66cSDavid du Colombier c = b->c; 10955e96a66cSDavid du Colombier 10965e96a66cSDavid du Colombier assert(b->part != PartVenti); 10975e96a66cSDavid du Colombier 10985e96a66cSDavid du Colombier if(b->iostate == BioDirty) 10995e96a66cSDavid du Colombier return 1; 11005e96a66cSDavid du Colombier assert(b->iostate == BioClean); 11015e96a66cSDavid du Colombier 11020b9a5132SDavid du Colombier vtLock(c->dirtylk); 11035e96a66cSDavid du Colombier vtLock(c->lk); 110461201b97SDavid du Colombier b->iostate = BioDirty; 11055e96a66cSDavid du Colombier c->ndirty++; 11065e96a66cSDavid du Colombier if(c->ndirty > (c->maxdirty>>1)) 11075e96a66cSDavid du Colombier vtWakeup(c->flush); 11085e96a66cSDavid du Colombier vtUnlock(c->lk); 11090b9a5132SDavid du Colombier vtUnlock(c->dirtylk); 11105e96a66cSDavid du Colombier 11115e96a66cSDavid du Colombier return 1; 11125e96a66cSDavid du Colombier } 11135e96a66cSDavid du Colombier 11145e96a66cSDavid du Colombier /* 111561201b97SDavid du Colombier * We've decided to write out b. Maybe b has some pointers to blocks 111661201b97SDavid du Colombier * that haven't yet been written to disk. If so, construct a slightly out-of-date 111761201b97SDavid du Colombier * copy of b that is safe to write out. (diskThread will make sure the block 11185e96a66cSDavid du Colombier * remains marked as dirty.) 11195e96a66cSDavid du Colombier */ 11205e96a66cSDavid du Colombier uchar * 11215e96a66cSDavid du Colombier blockRollback(Block *b, uchar *buf) 11225e96a66cSDavid du Colombier { 11235e96a66cSDavid du Colombier u32int addr; 11245e96a66cSDavid du Colombier BList *p; 11255e96a66cSDavid du Colombier Super super; 11265e96a66cSDavid du Colombier 11275e96a66cSDavid du Colombier /* easy case */ 11285e96a66cSDavid du Colombier if(b->prior == nil) 11295e96a66cSDavid du Colombier return b->data; 11305e96a66cSDavid du Colombier 11315e96a66cSDavid du Colombier memmove(buf, b->data, b->c->size); 11325e96a66cSDavid du Colombier for(p=b->prior; p; p=p->next){ 11335e96a66cSDavid du Colombier /* 11345e96a66cSDavid du Colombier * we know p->index >= 0 because blockWrite has vetted this block for us. 11355e96a66cSDavid du Colombier */ 11365e96a66cSDavid du Colombier assert(p->index >= 0); 11375e96a66cSDavid du Colombier assert(b->part == PartSuper || (b->part == PartData && b->l.type != BtData)); 11385e96a66cSDavid du Colombier if(b->part == PartSuper){ 11395e96a66cSDavid du Colombier assert(p->index == 0); 11405e96a66cSDavid du Colombier superUnpack(&super, buf); 114161201b97SDavid du Colombier addr = globalToLocal(p->old.score); 11425e96a66cSDavid du Colombier if(addr == NilBlock){ 11433b86f2f8SDavid du Colombier fprint(2, "%s: rolling back super block: " 11443b86f2f8SDavid du Colombier "bad replacement addr %V\n", 11453b86f2f8SDavid du Colombier argv0, p->old.score); 11465e96a66cSDavid du Colombier abort(); 11475e96a66cSDavid du Colombier } 11485e96a66cSDavid du Colombier super.active = addr; 11495e96a66cSDavid du Colombier superPack(&super, buf); 11505e96a66cSDavid du Colombier continue; 11515e96a66cSDavid du Colombier } 115261201b97SDavid du Colombier if(b->l.type == BtDir) 115361201b97SDavid du Colombier memmove(buf+p->index*VtEntrySize, p->old.entry, VtEntrySize); 115461201b97SDavid du Colombier else 115561201b97SDavid du Colombier memmove(buf+p->index*VtScoreSize, p->old.score, VtScoreSize); 11565e96a66cSDavid du Colombier } 11575e96a66cSDavid du Colombier return buf; 11585e96a66cSDavid du Colombier } 11595e96a66cSDavid du Colombier 11605e96a66cSDavid du Colombier /* 11615e96a66cSDavid du Colombier * Try to write block b. 11625e96a66cSDavid du Colombier * If b depends on other blocks: 11635e96a66cSDavid du Colombier * 11645e96a66cSDavid du Colombier * If the block has been written out, remove the dependency. 11657abd426fSDavid du Colombier * If the dependency is replaced by a more recent dependency, 11667abd426fSDavid du Colombier * throw it out. 11675e96a66cSDavid du Colombier * If we know how to write out an old version of b that doesn't 11685e96a66cSDavid du Colombier * depend on it, do that. 11695e96a66cSDavid du Colombier * 11705e96a66cSDavid du Colombier * Otherwise, bail. 11715e96a66cSDavid du Colombier */ 11725e96a66cSDavid du Colombier int 11735e96a66cSDavid du Colombier blockWrite(Block *b) 11745e96a66cSDavid du Colombier { 117561201b97SDavid du Colombier uchar *dmap; 11765e96a66cSDavid du Colombier Cache *c; 11775e96a66cSDavid du Colombier BList *p, **pp; 11785e96a66cSDavid du Colombier Block *bb; 11795e96a66cSDavid du Colombier int lockfail; 11805e96a66cSDavid du Colombier 11815e96a66cSDavid du Colombier c = b->c; 11825e96a66cSDavid du Colombier 11835e96a66cSDavid du Colombier if(b->iostate != BioDirty) 11845e96a66cSDavid du Colombier return 1; 11855e96a66cSDavid du Colombier 118661201b97SDavid du Colombier dmap = b->dmap; 118761201b97SDavid du Colombier memset(dmap, 0, c->ndmap); 11885e96a66cSDavid du Colombier pp = &b->prior; 11895e96a66cSDavid du Colombier for(p=*pp; p; p=*pp){ 119061201b97SDavid du Colombier if(p->index >= 0){ 119161201b97SDavid du Colombier /* more recent dependency has succeeded; this one can go */ 119261201b97SDavid du Colombier if(dmap[p->index/8] & (1<<(p->index%8))) 119361201b97SDavid du Colombier goto ignblock; 119461201b97SDavid du Colombier } 119561201b97SDavid du Colombier 119661201b97SDavid du Colombier lockfail = 0; 11975e96a66cSDavid du Colombier bb = _cacheLocalLookup(c, p->part, p->addr, p->vers, 0, &lockfail); 11985e96a66cSDavid du Colombier if(bb == nil){ 11995e96a66cSDavid du Colombier if(lockfail) 12005e96a66cSDavid du Colombier return 0; 120161201b97SDavid du Colombier /* block not in cache => was written already */ 120261201b97SDavid du Colombier dmap[p->index/8] |= 1<<(p->index%8); 120361201b97SDavid du Colombier goto ignblock; 12045e96a66cSDavid du Colombier } 12055e96a66cSDavid du Colombier 12065e96a66cSDavid du Colombier /* 12075e96a66cSDavid du Colombier * same version of block is still in cache. 12085e96a66cSDavid du Colombier * 12095e96a66cSDavid du Colombier * the assertion is true because the block still has version p->vers, 12105e96a66cSDavid du Colombier * which means it hasn't been written out since we last saw it. 12115e96a66cSDavid du Colombier */ 12127abd426fSDavid du Colombier if(bb->iostate != BioDirty){ 12133b86f2f8SDavid du Colombier fprint(2, "%s: %d:%x:%d iostate is %d in blockWrite\n", 12143b86f2f8SDavid du Colombier argv0, bb->part, bb->addr, bb->l.type, bb->iostate); 12157abd426fSDavid du Colombier /* probably BioWriting if it happens? */ 12167f1bc48aSDavid du Colombier if(bb->iostate == BioClean) 12177f1bc48aSDavid du Colombier goto ignblock; 12187abd426fSDavid du Colombier } 12197abd426fSDavid du Colombier 12205e96a66cSDavid du Colombier blockPut(bb); 12215e96a66cSDavid du Colombier 12225e96a66cSDavid du Colombier if(p->index < 0){ 12235e96a66cSDavid du Colombier /* 12245e96a66cSDavid du Colombier * We don't know how to temporarily undo 12255e96a66cSDavid du Colombier * b's dependency on bb, so just don't write b yet. 12265e96a66cSDavid du Colombier */ 12273b86f2f8SDavid du Colombier if(0) fprint(2, "%s: blockWrite skipping %d %x %d %d; need to write %d %x %d\n", 12283b86f2f8SDavid du Colombier argv0, b->part, b->addr, b->vers, b->l.type, p->part, p->addr, bb->vers); 12295e96a66cSDavid du Colombier return 0; 12305e96a66cSDavid du Colombier } 12315e96a66cSDavid du Colombier /* keep walking down the list */ 12325e96a66cSDavid du Colombier pp = &p->next; 123361201b97SDavid du Colombier continue; 123461201b97SDavid du Colombier 123561201b97SDavid du Colombier ignblock: 123661201b97SDavid du Colombier *pp = p->next; 123761201b97SDavid du Colombier blistFree(c, p); 123861201b97SDavid du Colombier continue; 12395e96a66cSDavid du Colombier } 12405e96a66cSDavid du Colombier 1241867bfcc6SDavid du Colombier /* 1242867bfcc6SDavid du Colombier * DiskWrite must never be called with a double-locked block. 1243867bfcc6SDavid du Colombier * This call to diskWrite is okay because blockWrite is only called 1244867bfcc6SDavid du Colombier * from the cache flush thread, which never double-locks a block. 1245867bfcc6SDavid du Colombier */ 12465e96a66cSDavid du Colombier diskWrite(c->disk, b); 12475e96a66cSDavid du Colombier return 1; 12485e96a66cSDavid du Colombier } 12495e96a66cSDavid du Colombier 12505e96a66cSDavid du Colombier /* 12515e96a66cSDavid du Colombier * Change the I/O state of block b. 12525e96a66cSDavid du Colombier * Just an assignment except for magic in 12535e96a66cSDavid du Colombier * switch statement (read comments there). 12545e96a66cSDavid du Colombier */ 12555e96a66cSDavid du Colombier void 12565e96a66cSDavid du Colombier blockSetIOState(Block *b, int iostate) 12575e96a66cSDavid du Colombier { 12585e96a66cSDavid du Colombier int dowakeup; 12595e96a66cSDavid du Colombier Cache *c; 12605e96a66cSDavid du Colombier BList *p, *q; 12615e96a66cSDavid du Colombier 12623b86f2f8SDavid 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)); 12635e96a66cSDavid du Colombier 12645e96a66cSDavid du Colombier c = b->c; 12655e96a66cSDavid du Colombier 12665e96a66cSDavid du Colombier dowakeup = 0; 12675e96a66cSDavid du Colombier switch(iostate){ 12685e96a66cSDavid du Colombier default: 12695e96a66cSDavid du Colombier abort(); 12705e96a66cSDavid du Colombier case BioEmpty: 12715e96a66cSDavid du Colombier assert(!b->uhead); 12725e96a66cSDavid du Colombier break; 12735e96a66cSDavid du Colombier case BioLabel: 12745e96a66cSDavid du Colombier assert(!b->uhead); 12755e96a66cSDavid du Colombier break; 12765e96a66cSDavid du Colombier case BioClean: 12775e96a66cSDavid du Colombier bwatchDependency(b); 12785e96a66cSDavid du Colombier /* 12795e96a66cSDavid du Colombier * If b->prior is set, it means a write just finished. 12805e96a66cSDavid du Colombier * The prior list isn't needed anymore. 12815e96a66cSDavid du Colombier */ 12825e96a66cSDavid du Colombier for(p=b->prior; p; p=q){ 12835e96a66cSDavid du Colombier q = p->next; 12845e96a66cSDavid du Colombier blistFree(c, p); 12855e96a66cSDavid du Colombier } 12865e96a66cSDavid du Colombier b->prior = nil; 12875e96a66cSDavid du Colombier /* 12885e96a66cSDavid du Colombier * Freeing a block or just finished a write. 12895e96a66cSDavid du Colombier * Move the blocks from the per-block unlink 12905e96a66cSDavid du Colombier * queue to the cache unlink queue. 12915e96a66cSDavid du Colombier */ 12925e96a66cSDavid du Colombier if(b->iostate == BioDirty || b->iostate == BioWriting){ 12935e96a66cSDavid du Colombier vtLock(c->lk); 12945e96a66cSDavid du Colombier c->ndirty--; 129561201b97SDavid du Colombier b->iostate = iostate; /* change here to keep in sync with ndirty */ 12965e96a66cSDavid du Colombier b->vers = c->vers++; 12975e96a66cSDavid du Colombier if(b->uhead){ 12985e96a66cSDavid du Colombier /* add unlink blocks to unlink queue */ 12995e96a66cSDavid du Colombier if(c->uhead == nil){ 13005e96a66cSDavid du Colombier c->uhead = b->uhead; 13015e96a66cSDavid du Colombier vtWakeup(c->unlink); 13025e96a66cSDavid du Colombier }else 13035e96a66cSDavid du Colombier c->utail->next = b->uhead; 13045e96a66cSDavid du Colombier c->utail = b->utail; 13055e96a66cSDavid du Colombier b->uhead = nil; 13065e96a66cSDavid du Colombier } 13075e96a66cSDavid du Colombier vtUnlock(c->lk); 13085e96a66cSDavid du Colombier } 13095e96a66cSDavid du Colombier assert(!b->uhead); 13105e96a66cSDavid du Colombier dowakeup = 1; 13115e96a66cSDavid du Colombier break; 13125e96a66cSDavid du Colombier case BioDirty: 13135e96a66cSDavid du Colombier /* 13145e96a66cSDavid du Colombier * Wrote out an old version of the block (see blockRollback). 13155e96a66cSDavid du Colombier * Bump a version count, leave it dirty. 13165e96a66cSDavid du Colombier */ 13175e96a66cSDavid du Colombier if(b->iostate == BioWriting){ 13185e96a66cSDavid du Colombier vtLock(c->lk); 13195e96a66cSDavid du Colombier b->vers = c->vers++; 13205e96a66cSDavid du Colombier vtUnlock(c->lk); 13215e96a66cSDavid du Colombier dowakeup = 1; 13225e96a66cSDavid du Colombier } 13235e96a66cSDavid du Colombier break; 13245e96a66cSDavid du Colombier case BioReading: 13255e96a66cSDavid du Colombier case BioWriting: 13265e96a66cSDavid du Colombier /* 13275e96a66cSDavid du Colombier * Adding block to disk queue. Bump reference count. 13285e96a66cSDavid du Colombier * diskThread decs the count later by calling blockPut. 13295e96a66cSDavid du Colombier * This is here because we need to lock c->lk to 13305e96a66cSDavid du Colombier * manipulate the ref count. 13315e96a66cSDavid du Colombier */ 13325e96a66cSDavid du Colombier vtLock(c->lk); 13335e96a66cSDavid du Colombier b->ref++; 13345e96a66cSDavid du Colombier vtUnlock(c->lk); 13355e96a66cSDavid du Colombier break; 13365e96a66cSDavid du Colombier case BioReadError: 13375e96a66cSDavid du Colombier case BioVentiError: 13385e96a66cSDavid du Colombier /* 13395e96a66cSDavid du Colombier * Oops. 13405e96a66cSDavid du Colombier */ 13415e96a66cSDavid du Colombier dowakeup = 1; 13425e96a66cSDavid du Colombier break; 13435e96a66cSDavid du Colombier } 13445e96a66cSDavid du Colombier b->iostate = iostate; 13455e96a66cSDavid du Colombier /* 13465e96a66cSDavid du Colombier * Now that the state has changed, we can wake the waiters. 13475e96a66cSDavid du Colombier */ 13485e96a66cSDavid du Colombier if(dowakeup) 13495e96a66cSDavid du Colombier vtWakeupAll(b->ioready); 13505e96a66cSDavid du Colombier } 13515e96a66cSDavid du Colombier 1352e569ccb5SDavid du Colombier /* 1353e569ccb5SDavid du Colombier * The active file system is a tree of blocks. 1354e569ccb5SDavid du Colombier * When we add snapshots to the mix, the entire file system 1355e569ccb5SDavid du Colombier * becomes a dag and thus requires a bit more care. 1356e569ccb5SDavid du Colombier * 1357e569ccb5SDavid du Colombier * The life of the file system is divided into epochs. A snapshot 1358e569ccb5SDavid du Colombier * ends one epoch and begins the next. Each file system block 1359e569ccb5SDavid du Colombier * is marked with the epoch in which it was created (b.epoch). 1360e569ccb5SDavid du Colombier * When the block is unlinked from the file system (closed), it is marked 1361e569ccb5SDavid du Colombier * with the epoch in which it was removed (b.epochClose). 1362e569ccb5SDavid du Colombier * Once we have discarded or archived all snapshots up to 1363e569ccb5SDavid du Colombier * b.epochClose, we can reclaim the block. 1364e569ccb5SDavid du Colombier * 1365e569ccb5SDavid du Colombier * If a block was created in a past epoch but is not yet closed, 1366e569ccb5SDavid du Colombier * it is treated as copy-on-write. Of course, in order to insert the 1367e569ccb5SDavid du Colombier * new pointer into the tree, the parent must be made writable, 1368e569ccb5SDavid du Colombier * and so on up the tree. The recursion stops because the root 1369e569ccb5SDavid du Colombier * block is always writable. 1370e569ccb5SDavid du Colombier * 1371e569ccb5SDavid du Colombier * If blocks are never closed, they will never be reused, and 1372e569ccb5SDavid du Colombier * we will run out of disk space. But marking a block as closed 1373e569ccb5SDavid du Colombier * requires some care about dependencies and write orderings. 1374e569ccb5SDavid du Colombier * 1375e569ccb5SDavid du Colombier * (1) If a block p points at a copy-on-write block b and we 1376e569ccb5SDavid du Colombier * copy b to create bb, then p must be written out after bb and 1377e569ccb5SDavid du Colombier * lbb (bb's label block). 1378e569ccb5SDavid du Colombier * 1379e569ccb5SDavid du Colombier * (2) We have to mark b as closed, but only after we switch 1380e569ccb5SDavid du Colombier * the pointer, so lb must be written out after p. In fact, we 1381e569ccb5SDavid du Colombier * can't even update the in-memory copy, or the cache might 1382e569ccb5SDavid du Colombier * mistakenly give out b for reuse before p gets written. 1383e569ccb5SDavid du Colombier * 1384e569ccb5SDavid du Colombier * CacheAllocBlock's call to blockSetLabel records a "bb after lbb" dependency. 1385e569ccb5SDavid du Colombier * The caller is expected to record a "p after bb" dependency 1386e569ccb5SDavid du Colombier * to finish (1), and also expected to call blockRemoveLink 1387e569ccb5SDavid du Colombier * to arrange for (2) to happen once p is written. 1388e569ccb5SDavid du Colombier * 1389e569ccb5SDavid du Colombier * Until (2) happens, some pieces of the code (e.g., the archiver) 1390e569ccb5SDavid du Colombier * still need to know whether a block has been copied, so we 1391e569ccb5SDavid du Colombier * set the BsCopied bit in the label and force that to disk *before* 1392e569ccb5SDavid du Colombier * the copy gets written out. 1393e569ccb5SDavid du Colombier */ 1394e569ccb5SDavid du Colombier Block* 1395e569ccb5SDavid du Colombier blockCopy(Block *b, u32int tag, u32int ehi, u32int elo) 1396e569ccb5SDavid du Colombier { 1397e569ccb5SDavid du Colombier Block *bb, *lb; 1398e569ccb5SDavid du Colombier Label l; 1399e569ccb5SDavid du Colombier 1400e569ccb5SDavid du Colombier if((b->l.state&BsClosed) || b->l.epoch >= ehi) 14013b86f2f8SDavid du Colombier fprint(2, "%s: blockCopy %#ux %L but fs is [%ud,%ud]\n", 14023b86f2f8SDavid du Colombier argv0, b->addr, &b->l, elo, ehi); 1403e569ccb5SDavid du Colombier 1404e569ccb5SDavid du Colombier bb = cacheAllocBlock(b->c, b->l.type, tag, ehi, elo); 1405e569ccb5SDavid du Colombier if(bb == nil){ 1406e569ccb5SDavid du Colombier blockPut(b); 1407e569ccb5SDavid du Colombier return nil; 1408e569ccb5SDavid du Colombier } 1409e569ccb5SDavid du Colombier 1410e569ccb5SDavid du Colombier /* 1411e569ccb5SDavid du Colombier * Update label so we know the block has been copied. 1412e569ccb5SDavid du Colombier * (It will be marked closed once it has been unlinked from 1413e569ccb5SDavid du Colombier * the tree.) This must follow cacheAllocBlock since we 1414e569ccb5SDavid du Colombier * can't be holding onto lb when we call cacheAllocBlock. 1415e569ccb5SDavid du Colombier */ 1416e569ccb5SDavid du Colombier if((b->l.state&BsCopied)==0) 1417e569ccb5SDavid du Colombier if(b->part == PartData){ /* not the superblock */ 1418e569ccb5SDavid du Colombier l = b->l; 1419e569ccb5SDavid du Colombier l.state |= BsCopied; 1420e569ccb5SDavid du Colombier lb = _blockSetLabel(b, &l); 1421e569ccb5SDavid du Colombier if(lb == nil){ 1422e569ccb5SDavid du Colombier /* can't set label => can't copy block */ 1423e569ccb5SDavid du Colombier blockPut(b); 1424e569ccb5SDavid du Colombier l.type = BtMax; 1425e569ccb5SDavid du Colombier l.state = BsFree; 1426e569ccb5SDavid du Colombier l.epoch = 0; 1427e569ccb5SDavid du Colombier l.epochClose = 0; 1428e569ccb5SDavid du Colombier l.tag = 0; 1429e569ccb5SDavid du Colombier blockSetLabel(bb, &l, 0); 1430e569ccb5SDavid du Colombier blockPut(bb); 1431e569ccb5SDavid du Colombier return nil; 1432e569ccb5SDavid du Colombier } 1433e569ccb5SDavid du Colombier blockDependency(bb, lb, -1, nil, nil); 1434e569ccb5SDavid du Colombier blockPut(lb); 1435e569ccb5SDavid du Colombier } 1436e569ccb5SDavid du Colombier 1437e569ccb5SDavid du Colombier memmove(bb->data, b->data, b->c->size); 1438e569ccb5SDavid du Colombier blockDirty(bb); 1439e569ccb5SDavid du Colombier blockPut(b); 1440e569ccb5SDavid du Colombier return bb; 1441e569ccb5SDavid du Colombier } 1442e569ccb5SDavid du Colombier 1443e569ccb5SDavid du Colombier /* 1444e569ccb5SDavid du Colombier * Block b once pointed at the block bb at addr/type/tag, but no longer does. 1445e569ccb5SDavid du Colombier * If recurse is set, we are unlinking all of bb's children as well. 1446e569ccb5SDavid du Colombier * 1447e569ccb5SDavid du Colombier * We can't reclaim bb (or its kids) until the block b gets written to disk. We add 1448e569ccb5SDavid du Colombier * the relevant information to b's list of unlinked blocks. Once b is written, 1449e569ccb5SDavid du Colombier * the list will be queued for processing. 1450e569ccb5SDavid du Colombier * 1451e569ccb5SDavid du Colombier * If b depends on bb, it doesn't anymore, so we remove bb from the prior list. 1452e569ccb5SDavid du Colombier */ 1453e569ccb5SDavid du Colombier void 1454e569ccb5SDavid du Colombier blockRemoveLink(Block *b, u32int addr, int type, u32int tag, int recurse) 1455e569ccb5SDavid du Colombier { 1456e569ccb5SDavid du Colombier BList *p, **pp, bl; 1457e569ccb5SDavid du Colombier 1458e569ccb5SDavid du Colombier /* remove bb from prior list */ 1459e569ccb5SDavid du Colombier for(pp=&b->prior; (p=*pp)!=nil; ){ 1460e569ccb5SDavid du Colombier if(p->part == PartData && p->addr == addr){ 1461e569ccb5SDavid du Colombier *pp = p->next; 1462e569ccb5SDavid du Colombier blistFree(b->c, p); 1463e569ccb5SDavid du Colombier }else 1464e569ccb5SDavid du Colombier pp = &p->next; 1465e569ccb5SDavid du Colombier } 1466e569ccb5SDavid du Colombier 1467e569ccb5SDavid du Colombier bl.part = PartData; 1468e569ccb5SDavid du Colombier bl.addr = addr; 1469e569ccb5SDavid du Colombier bl.type = type; 1470e569ccb5SDavid du Colombier bl.tag = tag; 1471e569ccb5SDavid du Colombier if(b->l.epoch == 0) 1472e569ccb5SDavid du Colombier assert(b->part == PartSuper); 1473e569ccb5SDavid du Colombier bl.epoch = b->l.epoch; 1474e569ccb5SDavid du Colombier bl.next = nil; 1475e569ccb5SDavid du Colombier bl.recurse = recurse; 1476e569ccb5SDavid du Colombier 1477e569ccb5SDavid du Colombier p = blistAlloc(b); 1478e569ccb5SDavid du Colombier if(p == nil){ 1479e569ccb5SDavid du Colombier /* 1480e569ccb5SDavid du Colombier * We were out of blists so blistAlloc wrote b to disk. 1481e569ccb5SDavid du Colombier */ 1482e569ccb5SDavid du Colombier doRemoveLink(b->c, &bl); 1483e569ccb5SDavid du Colombier return; 1484e569ccb5SDavid du Colombier } 1485e569ccb5SDavid du Colombier 1486e569ccb5SDavid du Colombier /* Uhead is only processed when the block goes from Dirty -> Clean */ 1487e569ccb5SDavid du Colombier assert(b->iostate == BioDirty); 1488e569ccb5SDavid du Colombier 1489e569ccb5SDavid du Colombier *p = bl; 1490e569ccb5SDavid du Colombier if(b->uhead == nil) 1491e569ccb5SDavid du Colombier b->uhead = p; 1492e569ccb5SDavid du Colombier else 1493e569ccb5SDavid du Colombier b->utail->next = p; 1494e569ccb5SDavid du Colombier b->utail = p; 1495e569ccb5SDavid du Colombier } 1496e569ccb5SDavid du Colombier 1497e569ccb5SDavid du Colombier /* 1498e569ccb5SDavid du Colombier * Process removal of a single block and perhaps its children. 1499e569ccb5SDavid du Colombier */ 1500e569ccb5SDavid du Colombier static void 1501e569ccb5SDavid du Colombier doRemoveLink(Cache *c, BList *p) 1502e569ccb5SDavid du Colombier { 15030b9a5132SDavid du Colombier int i, n, recurse; 1504e569ccb5SDavid du Colombier u32int a; 1505e569ccb5SDavid du Colombier Block *b; 1506e569ccb5SDavid du Colombier Label l; 1507e569ccb5SDavid du Colombier BList bl; 1508e569ccb5SDavid du Colombier 15090b9a5132SDavid du Colombier recurse = (p->recurse && p->type != BtData && p->type != BtDir); 15100b9a5132SDavid du Colombier 15110b9a5132SDavid du Colombier /* 15120b9a5132SDavid du Colombier * We're not really going to overwrite b, but if we're not 15130b9a5132SDavid du Colombier * going to look at its contents, there is no point in reading 15140b9a5132SDavid du Colombier * them from the disk. 15150b9a5132SDavid du Colombier */ 15160b9a5132SDavid du Colombier b = cacheLocalData(c, p->addr, p->type, p->tag, recurse ? OReadOnly : OOverWrite, 0); 1517e569ccb5SDavid du Colombier if(b == nil) 1518e569ccb5SDavid du Colombier return; 1519e569ccb5SDavid du Colombier 1520e569ccb5SDavid du Colombier /* 1521e569ccb5SDavid du Colombier * When we're unlinking from the superblock, close with the next epoch. 1522e569ccb5SDavid du Colombier */ 1523e569ccb5SDavid du Colombier if(p->epoch == 0) 1524e569ccb5SDavid du Colombier p->epoch = b->l.epoch+1; 1525e569ccb5SDavid du Colombier 1526e569ccb5SDavid du Colombier /* sanity check */ 1527e569ccb5SDavid du Colombier if(b->l.epoch > p->epoch){ 15283b86f2f8SDavid du Colombier fprint(2, "%s: doRemoveLink: strange epoch %ud > %ud\n", 15293b86f2f8SDavid du Colombier argv0, b->l.epoch, p->epoch); 1530e569ccb5SDavid du Colombier blockPut(b); 1531e569ccb5SDavid du Colombier return; 1532e569ccb5SDavid du Colombier } 1533e569ccb5SDavid du Colombier 15340b9a5132SDavid du Colombier if(recurse){ 1535e569ccb5SDavid du Colombier n = c->size / VtScoreSize; 1536e569ccb5SDavid du Colombier for(i=0; i<n; i++){ 1537e569ccb5SDavid du Colombier a = globalToLocal(b->data + i*VtScoreSize); 1538e569ccb5SDavid du Colombier if(a == NilBlock || !readLabel(c, &l, a)) 1539e569ccb5SDavid du Colombier continue; 1540e569ccb5SDavid du Colombier if(l.state&BsClosed) 1541e569ccb5SDavid du Colombier continue; 1542e569ccb5SDavid du Colombier /* 1543e569ccb5SDavid du Colombier * If stack space becomes an issue... 1544e569ccb5SDavid du Colombier p->addr = a; 1545e569ccb5SDavid du Colombier p->type = l.type; 1546e569ccb5SDavid du Colombier p->tag = l.tag; 1547e569ccb5SDavid du Colombier doRemoveLink(c, p); 1548e569ccb5SDavid du Colombier */ 1549e569ccb5SDavid du Colombier 1550e569ccb5SDavid du Colombier bl.part = PartData; 1551e569ccb5SDavid du Colombier bl.addr = a; 1552e569ccb5SDavid du Colombier bl.type = l.type; 1553e569ccb5SDavid du Colombier bl.tag = l.tag; 1554e569ccb5SDavid du Colombier bl.epoch = p->epoch; 1555e569ccb5SDavid du Colombier bl.next = nil; 1556e569ccb5SDavid du Colombier bl.recurse = 1; 15570b9a5132SDavid du Colombier /* give up the block lock - share with others */ 15580b9a5132SDavid du Colombier blockPut(b); 1559e569ccb5SDavid du Colombier doRemoveLink(c, &bl); 15600b9a5132SDavid du Colombier b = cacheLocalData(c, p->addr, p->type, p->tag, OReadOnly, 0); 15610b9a5132SDavid du Colombier if(b == nil){ 15623b86f2f8SDavid du Colombier fprint(2, "%s: warning: lost block in doRemoveLink\n", 15633b86f2f8SDavid du Colombier argv0); 15640b9a5132SDavid du Colombier return; 15650b9a5132SDavid du Colombier } 1566e569ccb5SDavid du Colombier } 1567e569ccb5SDavid du Colombier } 1568e569ccb5SDavid du Colombier 1569e569ccb5SDavid du Colombier l = b->l; 1570e569ccb5SDavid du Colombier l.state |= BsClosed; 1571e569ccb5SDavid du Colombier l.epochClose = p->epoch; 1572225077b0SDavid du Colombier if(l.epochClose == l.epoch){ 1573225077b0SDavid du Colombier vtLock(c->fl->lk); 1574225077b0SDavid du Colombier if(l.epoch == c->fl->epochLow) 1575225077b0SDavid du Colombier c->fl->nused--; 1576225077b0SDavid du Colombier blockSetLabel(b, &l, 0); 1577225077b0SDavid du Colombier vtUnlock(c->fl->lk); 1578225077b0SDavid du Colombier }else 1579e569ccb5SDavid du Colombier blockSetLabel(b, &l, 0); 1580e569ccb5SDavid du Colombier blockPut(b); 1581e569ccb5SDavid du Colombier } 1582e569ccb5SDavid du Colombier 1583e569ccb5SDavid du Colombier /* 1584e569ccb5SDavid du Colombier * Allocate a BList so that we can record a dependency 1585e569ccb5SDavid du Colombier * or queue a removal related to block b. 1586e569ccb5SDavid du Colombier * If we can't find a BList, we write out b and return nil. 1587e569ccb5SDavid du Colombier */ 1588e569ccb5SDavid du Colombier static BList * 1589e569ccb5SDavid du Colombier blistAlloc(Block *b) 1590e569ccb5SDavid du Colombier { 1591e569ccb5SDavid du Colombier Cache *c; 1592e569ccb5SDavid du Colombier BList *p; 1593e569ccb5SDavid du Colombier 1594e569ccb5SDavid du Colombier if(b->iostate != BioDirty){ 1595e569ccb5SDavid du Colombier /* 1596e569ccb5SDavid du Colombier * should not happen anymore - 1597e569ccb5SDavid du Colombier * blockDirty used to flush but no longer does. 1598e569ccb5SDavid du Colombier */ 1599e569ccb5SDavid du Colombier assert(b->iostate == BioClean); 16003b86f2f8SDavid du Colombier fprint(2, "%s: blistAlloc: called on clean block\n", argv0); 1601e569ccb5SDavid du Colombier return nil; 1602e569ccb5SDavid du Colombier } 1603e569ccb5SDavid du Colombier 1604e569ccb5SDavid du Colombier c = b->c; 1605e569ccb5SDavid du Colombier vtLock(c->lk); 1606e569ccb5SDavid du Colombier if(c->blfree == nil){ 1607e569ccb5SDavid du Colombier /* 1608e569ccb5SDavid du Colombier * No free BLists. What are our options? 1609e569ccb5SDavid du Colombier */ 1610e569ccb5SDavid du Colombier 1611e569ccb5SDavid du Colombier /* Block has no priors? Just write it. */ 1612e569ccb5SDavid du Colombier if(b->prior == nil){ 1613e569ccb5SDavid du Colombier vtUnlock(c->lk); 1614e569ccb5SDavid du Colombier diskWriteAndWait(c->disk, b); 1615e569ccb5SDavid du Colombier return nil; 1616e569ccb5SDavid du Colombier } 1617e569ccb5SDavid du Colombier 1618e569ccb5SDavid du Colombier /* 1619e569ccb5SDavid du Colombier * Wake the flush thread, which will hopefully free up 1620e569ccb5SDavid du Colombier * some BLists for us. We used to flush a block from 1621e569ccb5SDavid du Colombier * our own prior list and reclaim that BList, but this is 1622e569ccb5SDavid du Colombier * a no-no: some of the blocks on our prior list may 1623e569ccb5SDavid du Colombier * be locked by our caller. Or maybe their label blocks 1624e569ccb5SDavid du Colombier * are locked by our caller. In any event, it's too hard 1625e569ccb5SDavid du Colombier * to make sure we can do I/O for ourselves. Instead, 1626e569ccb5SDavid du Colombier * we assume the flush thread will find something. 1627e569ccb5SDavid du Colombier * (The flush thread never blocks waiting for a block, 1628e569ccb5SDavid du Colombier * so it can't deadlock like we can.) 1629e569ccb5SDavid du Colombier */ 1630e569ccb5SDavid du Colombier while(c->blfree == nil){ 1631e569ccb5SDavid du Colombier vtWakeup(c->flush); 1632e569ccb5SDavid du Colombier vtSleep(c->blrend); 16330b9a5132SDavid du Colombier if(c->blfree == nil) 16343b86f2f8SDavid du Colombier fprint(2, "%s: flushing for blists\n", argv0); 1635e569ccb5SDavid du Colombier } 1636e569ccb5SDavid du Colombier } 1637e569ccb5SDavid du Colombier 1638e569ccb5SDavid du Colombier p = c->blfree; 1639e569ccb5SDavid du Colombier c->blfree = p->next; 1640e569ccb5SDavid du Colombier vtUnlock(c->lk); 1641e569ccb5SDavid du Colombier return p; 1642e569ccb5SDavid du Colombier } 1643e569ccb5SDavid du Colombier 1644e569ccb5SDavid du Colombier static void 1645e569ccb5SDavid du Colombier blistFree(Cache *c, BList *bl) 1646e569ccb5SDavid du Colombier { 1647e569ccb5SDavid du Colombier vtLock(c->lk); 1648e569ccb5SDavid du Colombier bl->next = c->blfree; 1649e569ccb5SDavid du Colombier c->blfree = bl; 1650e569ccb5SDavid du Colombier vtWakeup(c->blrend); 1651e569ccb5SDavid du Colombier vtUnlock(c->lk); 1652e569ccb5SDavid du Colombier } 1653e569ccb5SDavid du Colombier 16545e96a66cSDavid du Colombier char* 16555e96a66cSDavid du Colombier bsStr(int state) 16565e96a66cSDavid du Colombier { 16575e96a66cSDavid du Colombier static char s[100]; 16585e96a66cSDavid du Colombier 16595e96a66cSDavid du Colombier if(state == BsFree) 16605e96a66cSDavid du Colombier return "Free"; 16615e96a66cSDavid du Colombier if(state == BsBad) 16625e96a66cSDavid du Colombier return "Bad"; 16635e96a66cSDavid du Colombier 16645e96a66cSDavid du Colombier sprint(s, "%x", state); 16655e96a66cSDavid du Colombier if(!(state&BsAlloc)) 16665e96a66cSDavid du Colombier strcat(s, ",Free"); /* should not happen */ 16675e96a66cSDavid du Colombier if(state&BsCopied) 16685e96a66cSDavid du Colombier strcat(s, ",Copied"); 16695e96a66cSDavid du Colombier if(state&BsVenti) 16705e96a66cSDavid du Colombier strcat(s, ",Venti"); 16715e96a66cSDavid du Colombier if(state&BsClosed) 16725e96a66cSDavid du Colombier strcat(s, ",Closed"); 16735e96a66cSDavid du Colombier return s; 16745e96a66cSDavid du Colombier } 16755e96a66cSDavid du Colombier 16765e96a66cSDavid du Colombier char * 16775e96a66cSDavid du Colombier bioStr(int iostate) 16785e96a66cSDavid du Colombier { 16795e96a66cSDavid du Colombier switch(iostate){ 16805e96a66cSDavid du Colombier default: 16815e96a66cSDavid du Colombier return "Unknown!!"; 16825e96a66cSDavid du Colombier case BioEmpty: 16835e96a66cSDavid du Colombier return "Empty"; 16845e96a66cSDavid du Colombier case BioLabel: 16855e96a66cSDavid du Colombier return "Label"; 16865e96a66cSDavid du Colombier case BioClean: 16875e96a66cSDavid du Colombier return "Clean"; 16885e96a66cSDavid du Colombier case BioDirty: 16895e96a66cSDavid du Colombier return "Dirty"; 16905e96a66cSDavid du Colombier case BioReading: 16915e96a66cSDavid du Colombier return "Reading"; 16925e96a66cSDavid du Colombier case BioWriting: 16935e96a66cSDavid du Colombier return "Writing"; 16945e96a66cSDavid du Colombier case BioReadError: 16955e96a66cSDavid du Colombier return "ReadError"; 16965e96a66cSDavid du Colombier case BioVentiError: 16975e96a66cSDavid du Colombier return "VentiError"; 16985e96a66cSDavid du Colombier case BioMax: 16995e96a66cSDavid du Colombier return "Max"; 17005e96a66cSDavid du Colombier } 17015e96a66cSDavid du Colombier } 17025e96a66cSDavid du Colombier 17035e96a66cSDavid du Colombier static char *bttab[] = { 17045e96a66cSDavid du Colombier "BtData", 17055e96a66cSDavid du Colombier "BtData+1", 17065e96a66cSDavid du Colombier "BtData+2", 17075e96a66cSDavid du Colombier "BtData+3", 17085e96a66cSDavid du Colombier "BtData+4", 17095e96a66cSDavid du Colombier "BtData+5", 17105e96a66cSDavid du Colombier "BtData+6", 17115e96a66cSDavid du Colombier "BtData+7", 17125e96a66cSDavid du Colombier "BtDir", 17135e96a66cSDavid du Colombier "BtDir+1", 17145e96a66cSDavid du Colombier "BtDir+2", 17155e96a66cSDavid du Colombier "BtDir+3", 17165e96a66cSDavid du Colombier "BtDir+4", 17175e96a66cSDavid du Colombier "BtDir+5", 17185e96a66cSDavid du Colombier "BtDir+6", 17195e96a66cSDavid du Colombier "BtDir+7", 17205e96a66cSDavid du Colombier }; 17215e96a66cSDavid du Colombier 17225e96a66cSDavid du Colombier char* 17235e96a66cSDavid du Colombier btStr(int type) 17245e96a66cSDavid du Colombier { 17255e96a66cSDavid du Colombier if(type < nelem(bttab)) 17265e96a66cSDavid du Colombier return bttab[type]; 17275e96a66cSDavid du Colombier return "unknown"; 17285e96a66cSDavid du Colombier } 17295e96a66cSDavid du Colombier 17305e96a66cSDavid du Colombier int 17315e96a66cSDavid du Colombier labelFmt(Fmt *f) 17325e96a66cSDavid du Colombier { 17335e96a66cSDavid du Colombier Label *l; 17345e96a66cSDavid du Colombier 17355e96a66cSDavid du Colombier l = va_arg(f->args, Label*); 17365e96a66cSDavid du Colombier return fmtprint(f, "%s,%s,e=%ud,%d,tag=%#ux", 17375e96a66cSDavid du Colombier btStr(l->type), bsStr(l->state), l->epoch, (int)l->epochClose, l->tag); 17385e96a66cSDavid du Colombier } 17395e96a66cSDavid du Colombier 17405e96a66cSDavid du Colombier int 17415e96a66cSDavid du Colombier scoreFmt(Fmt *f) 17425e96a66cSDavid du Colombier { 17435e96a66cSDavid du Colombier uchar *v; 17445e96a66cSDavid du Colombier int i; 17455e96a66cSDavid du Colombier u32int addr; 17465e96a66cSDavid du Colombier 17475e96a66cSDavid du Colombier v = va_arg(f->args, uchar*); 17485e96a66cSDavid du Colombier if(v == nil){ 17495e96a66cSDavid du Colombier fmtprint(f, "*"); 17505e96a66cSDavid du Colombier }else if((addr = globalToLocal(v)) != NilBlock) 17515e96a66cSDavid du Colombier fmtprint(f, "0x%.8ux", addr); 17525e96a66cSDavid du Colombier else{ 17535e96a66cSDavid du Colombier for(i = 0; i < VtScoreSize; i++) 17545e96a66cSDavid du Colombier fmtprint(f, "%2.2ux", v[i]); 17555e96a66cSDavid du Colombier } 17565e96a66cSDavid du Colombier 17575e96a66cSDavid du Colombier return 0; 17585e96a66cSDavid du Colombier } 17595e96a66cSDavid du Colombier 17605e96a66cSDavid du Colombier static int 17615e96a66cSDavid du Colombier upHeap(int i, Block *b) 17625e96a66cSDavid du Colombier { 17635e96a66cSDavid du Colombier Block *bb; 17645e96a66cSDavid du Colombier u32int now; 17655e96a66cSDavid du Colombier int p; 17665e96a66cSDavid du Colombier Cache *c; 17675e96a66cSDavid du Colombier 17685e96a66cSDavid du Colombier c = b->c; 17695e96a66cSDavid du Colombier now = c->now; 17705e96a66cSDavid du Colombier for(; i != 0; i = p){ 17715e96a66cSDavid du Colombier p = (i - 1) >> 1; 17725e96a66cSDavid du Colombier bb = c->heap[p]; 17735e96a66cSDavid du Colombier if(b->used - now >= bb->used - now) 17745e96a66cSDavid du Colombier break; 17755e96a66cSDavid du Colombier c->heap[i] = bb; 17765e96a66cSDavid du Colombier bb->heap = i; 17775e96a66cSDavid du Colombier } 17785e96a66cSDavid du Colombier c->heap[i] = b; 17795e96a66cSDavid du Colombier b->heap = i; 17805e96a66cSDavid du Colombier 17815e96a66cSDavid du Colombier return i; 17825e96a66cSDavid du Colombier } 17835e96a66cSDavid du Colombier 17845e96a66cSDavid du Colombier static int 17855e96a66cSDavid du Colombier downHeap(int i, Block *b) 17865e96a66cSDavid du Colombier { 17875e96a66cSDavid du Colombier Block *bb; 17885e96a66cSDavid du Colombier u32int now; 17895e96a66cSDavid du Colombier int k; 17905e96a66cSDavid du Colombier Cache *c; 17915e96a66cSDavid du Colombier 17925e96a66cSDavid du Colombier c = b->c; 17935e96a66cSDavid du Colombier now = c->now; 17945e96a66cSDavid du Colombier for(; ; i = k){ 17955e96a66cSDavid du Colombier k = (i << 1) + 1; 17965e96a66cSDavid du Colombier if(k >= c->nheap) 17975e96a66cSDavid du Colombier break; 17985e96a66cSDavid du Colombier if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now) 17995e96a66cSDavid du Colombier k++; 18005e96a66cSDavid du Colombier bb = c->heap[k]; 18015e96a66cSDavid du Colombier if(b->used - now <= bb->used - now) 18025e96a66cSDavid du Colombier break; 18035e96a66cSDavid du Colombier c->heap[i] = bb; 18045e96a66cSDavid du Colombier bb->heap = i; 18055e96a66cSDavid du Colombier } 18065e96a66cSDavid du Colombier c->heap[i] = b; 18075e96a66cSDavid du Colombier b->heap = i; 18085e96a66cSDavid du Colombier return i; 18095e96a66cSDavid du Colombier } 18105e96a66cSDavid du Colombier 18115e96a66cSDavid du Colombier /* 18125e96a66cSDavid du Colombier * Delete a block from the heap. 18135e96a66cSDavid du Colombier * Called with c->lk held. 18145e96a66cSDavid du Colombier */ 18155e96a66cSDavid du Colombier static void 18165e96a66cSDavid du Colombier heapDel(Block *b) 18175e96a66cSDavid du Colombier { 18185e96a66cSDavid du Colombier int i, si; 18195e96a66cSDavid du Colombier Cache *c; 18205e96a66cSDavid du Colombier 18215e96a66cSDavid du Colombier c = b->c; 18225e96a66cSDavid du Colombier 18235e96a66cSDavid du Colombier si = b->heap; 18245e96a66cSDavid du Colombier if(si == BadHeap) 18255e96a66cSDavid du Colombier return; 18265e96a66cSDavid du Colombier b->heap = BadHeap; 18275e96a66cSDavid du Colombier c->nheap--; 18285e96a66cSDavid du Colombier if(si == c->nheap) 18295e96a66cSDavid du Colombier return; 18305e96a66cSDavid du Colombier b = c->heap[c->nheap]; 18315e96a66cSDavid du Colombier i = upHeap(si, b); 18325e96a66cSDavid du Colombier if(i == si) 18335e96a66cSDavid du Colombier downHeap(i, b); 18345e96a66cSDavid du Colombier } 18355e96a66cSDavid du Colombier 18365e96a66cSDavid du Colombier /* 18375e96a66cSDavid du Colombier * Insert a block into the heap. 18385e96a66cSDavid du Colombier * Called with c->lk held. 18395e96a66cSDavid du Colombier */ 18405e96a66cSDavid du Colombier static void 18415e96a66cSDavid du Colombier heapIns(Block *b) 18425e96a66cSDavid du Colombier { 18435e96a66cSDavid du Colombier assert(b->heap == BadHeap); 18445e96a66cSDavid du Colombier upHeap(b->c->nheap++, b); 1845fe853e23SDavid du Colombier vtWakeup(b->c->heapwait); 18465e96a66cSDavid du Colombier } 18475e96a66cSDavid du Colombier 18485e96a66cSDavid du Colombier /* 18495e96a66cSDavid du Colombier * Get just the label for a block. 18505e96a66cSDavid du Colombier */ 1851e569ccb5SDavid du Colombier int 18525e96a66cSDavid du Colombier readLabel(Cache *c, Label *l, u32int addr) 18535e96a66cSDavid du Colombier { 18545e96a66cSDavid du Colombier int lpb; 18555e96a66cSDavid du Colombier Block *b; 18565e96a66cSDavid du Colombier u32int a; 18575e96a66cSDavid du Colombier 18585e96a66cSDavid du Colombier lpb = c->size / LabelSize; 18595e96a66cSDavid du Colombier a = addr / lpb; 18605e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, a, OReadOnly); 18615e96a66cSDavid du Colombier if(b == nil){ 18625e96a66cSDavid du Colombier blockPut(b); 18635e96a66cSDavid du Colombier return 0; 18645e96a66cSDavid du Colombier } 18655e96a66cSDavid du Colombier 18665e96a66cSDavid du Colombier if(!labelUnpack(l, b->data, addr%lpb)){ 18675e96a66cSDavid du Colombier blockPut(b); 18685e96a66cSDavid du Colombier return 0; 18695e96a66cSDavid du Colombier } 18705e96a66cSDavid du Colombier blockPut(b); 18715e96a66cSDavid du Colombier return 1; 18725e96a66cSDavid du Colombier } 18735e96a66cSDavid du Colombier 18745e96a66cSDavid du Colombier /* 18755e96a66cSDavid du Colombier * Process unlink queue. 18765e96a66cSDavid du Colombier * Called with c->lk held. 18775e96a66cSDavid du Colombier */ 18785e96a66cSDavid du Colombier static void 18795e96a66cSDavid du Colombier unlinkBody(Cache *c) 18805e96a66cSDavid du Colombier { 18815e96a66cSDavid du Colombier BList *p; 18825e96a66cSDavid du Colombier 18835e96a66cSDavid du Colombier while(c->uhead != nil){ 18845e96a66cSDavid du Colombier p = c->uhead; 18855e96a66cSDavid du Colombier c->uhead = p->next; 18865e96a66cSDavid du Colombier vtUnlock(c->lk); 1887e569ccb5SDavid du Colombier doRemoveLink(c, p); 18885e96a66cSDavid du Colombier vtLock(c->lk); 18895e96a66cSDavid du Colombier p->next = c->blfree; 18905e96a66cSDavid du Colombier c->blfree = p; 18915e96a66cSDavid du Colombier } 18925e96a66cSDavid du Colombier } 18935e96a66cSDavid du Colombier 18945e96a66cSDavid du Colombier /* 18955e96a66cSDavid du Colombier * Occasionally unlink the blocks on the cache unlink queue. 18965e96a66cSDavid du Colombier */ 18975e96a66cSDavid du Colombier static void 18985e96a66cSDavid du Colombier unlinkThread(void *a) 18995e96a66cSDavid du Colombier { 19005e96a66cSDavid du Colombier Cache *c = a; 19015e96a66cSDavid du Colombier 19025e96a66cSDavid du Colombier vtThreadSetName("unlink"); 19035e96a66cSDavid du Colombier 19045e96a66cSDavid du Colombier vtLock(c->lk); 19055e96a66cSDavid du Colombier for(;;){ 19065e96a66cSDavid du Colombier while(c->uhead == nil && c->die == nil) 19075e96a66cSDavid du Colombier vtSleep(c->unlink); 19085e96a66cSDavid du Colombier if(c->die != nil) 19095e96a66cSDavid du Colombier break; 19105e96a66cSDavid du Colombier unlinkBody(c); 19115e96a66cSDavid du Colombier } 19125e96a66cSDavid du Colombier c->ref--; 19135e96a66cSDavid du Colombier vtWakeup(c->die); 19145e96a66cSDavid du Colombier vtUnlock(c->lk); 19155e96a66cSDavid du Colombier } 19165e96a66cSDavid du Colombier 19175e96a66cSDavid du Colombier static int 19185e96a66cSDavid du Colombier baddrCmp(void *a0, void *a1) 19195e96a66cSDavid du Colombier { 19205e96a66cSDavid du Colombier BAddr *b0, *b1; 19215e96a66cSDavid du Colombier b0 = a0; 19225e96a66cSDavid du Colombier b1 = a1; 19235e96a66cSDavid du Colombier 19245e96a66cSDavid du Colombier if(b0->part < b1->part) 19255e96a66cSDavid du Colombier return -1; 19265e96a66cSDavid du Colombier if(b0->part > b1->part) 19275e96a66cSDavid du Colombier return 1; 19285e96a66cSDavid du Colombier if(b0->addr < b1->addr) 19295e96a66cSDavid du Colombier return -1; 19305e96a66cSDavid du Colombier if(b0->addr > b1->addr) 19315e96a66cSDavid du Colombier return 1; 19325e96a66cSDavid du Colombier return 0; 19335e96a66cSDavid du Colombier } 19345e96a66cSDavid du Colombier 19355e96a66cSDavid du Colombier /* 19365e96a66cSDavid du Colombier * Scan the block list for dirty blocks; add them to the list c->baddr. 19375e96a66cSDavid du Colombier */ 19385e96a66cSDavid du Colombier static void 19395e96a66cSDavid du Colombier flushFill(Cache *c) 19405e96a66cSDavid du Colombier { 194161201b97SDavid du Colombier int i, ndirty; 19425e96a66cSDavid du Colombier BAddr *p; 19435e96a66cSDavid du Colombier Block *b; 19445e96a66cSDavid du Colombier 19455e96a66cSDavid du Colombier vtLock(c->lk); 194639734e7eSDavid du Colombier if(c->ndirty == 0){ 194739734e7eSDavid du Colombier vtUnlock(c->lk); 194839734e7eSDavid du Colombier return; 194939734e7eSDavid du Colombier } 19505e96a66cSDavid du Colombier 19515e96a66cSDavid du Colombier p = c->baddr; 195261201b97SDavid du Colombier ndirty = 0; 19535e96a66cSDavid du Colombier for(i=0; i<c->nblocks; i++){ 19545e96a66cSDavid du Colombier b = c->blocks + i; 195561201b97SDavid du Colombier if(b->part == PartError) 195661201b97SDavid du Colombier continue; 195761201b97SDavid du Colombier if(b->iostate == BioDirty || b->iostate == BioWriting) 195861201b97SDavid du Colombier ndirty++; 195961201b97SDavid du Colombier if(b->iostate != BioDirty) 19605e96a66cSDavid du Colombier continue; 19615e96a66cSDavid du Colombier p->part = b->part; 19625e96a66cSDavid du Colombier p->addr = b->addr; 19635e96a66cSDavid du Colombier p->vers = b->vers; 19645e96a66cSDavid du Colombier p++; 19655e96a66cSDavid du Colombier } 196661201b97SDavid du Colombier if(ndirty != c->ndirty){ 19673b86f2f8SDavid du Colombier fprint(2, "%s: ndirty mismatch expected %d found %d\n", 19683b86f2f8SDavid du Colombier argv0, c->ndirty, ndirty); 196961201b97SDavid du Colombier c->ndirty = ndirty; 197061201b97SDavid du Colombier } 19715e96a66cSDavid du Colombier vtUnlock(c->lk); 19725e96a66cSDavid du Colombier 19735e96a66cSDavid du Colombier c->bw = p - c->baddr; 19745e96a66cSDavid du Colombier qsort(c->baddr, c->bw, sizeof(BAddr), baddrCmp); 19755e96a66cSDavid du Colombier } 19765e96a66cSDavid du Colombier 19775e96a66cSDavid du Colombier /* 19785e96a66cSDavid du Colombier * This is not thread safe, i.e. it can't be called from multiple threads. 19795e96a66cSDavid du Colombier * 19805e96a66cSDavid du Colombier * It's okay how we use it, because it only gets called in 19815e96a66cSDavid du Colombier * the flushThread. And cacheFree, but only after 19825e96a66cSDavid du Colombier * cacheFree has killed off the flushThread. 19835e96a66cSDavid du Colombier */ 19845e96a66cSDavid du Colombier static int 19855e96a66cSDavid du Colombier cacheFlushBlock(Cache *c) 19865e96a66cSDavid du Colombier { 19875e96a66cSDavid du Colombier Block *b; 19885e96a66cSDavid du Colombier BAddr *p; 19895e96a66cSDavid du Colombier int lockfail, nfail; 19905e96a66cSDavid du Colombier 19915e96a66cSDavid du Colombier nfail = 0; 19925e96a66cSDavid du Colombier for(;;){ 19935e96a66cSDavid du Colombier if(c->br == c->be){ 19945e96a66cSDavid du Colombier if(c->bw == 0 || c->bw == c->be) 19955e96a66cSDavid du Colombier flushFill(c); 19965e96a66cSDavid du Colombier c->br = 0; 19975e96a66cSDavid du Colombier c->be = c->bw; 19985e96a66cSDavid du Colombier c->bw = 0; 19995e96a66cSDavid du Colombier c->nflush = 0; 20005e96a66cSDavid du Colombier } 20015e96a66cSDavid du Colombier 20025e96a66cSDavid du Colombier if(c->br == c->be) 20035e96a66cSDavid du Colombier return 0; 20045e96a66cSDavid du Colombier p = c->baddr + c->br; 20055e96a66cSDavid du Colombier c->br++; 20065e96a66cSDavid du Colombier b = _cacheLocalLookup(c, p->part, p->addr, p->vers, 0, &lockfail); 20075e96a66cSDavid du Colombier 20085e96a66cSDavid du Colombier if(b && blockWrite(b)){ 20095e96a66cSDavid du Colombier c->nflush++; 20105e96a66cSDavid du Colombier blockPut(b); 20115e96a66cSDavid du Colombier return 1; 20125e96a66cSDavid du Colombier } 20135e96a66cSDavid du Colombier if(b) 20145e96a66cSDavid du Colombier blockPut(b); 20155e96a66cSDavid du Colombier 20165e96a66cSDavid du Colombier /* 20175e96a66cSDavid du Colombier * Why didn't we write the block? 20185e96a66cSDavid du Colombier */ 20195e96a66cSDavid du Colombier 20205e96a66cSDavid du Colombier /* Block already written out */ 20215e96a66cSDavid du Colombier if(b == nil && !lockfail) 20225e96a66cSDavid du Colombier continue; 20235e96a66cSDavid du Colombier 20245e96a66cSDavid du Colombier /* Failed to acquire lock; sleep if happens a lot. */ 2025fe853e23SDavid du Colombier if(lockfail && ++nfail > 100){ 20265e96a66cSDavid du Colombier sleep(500); 2027fe853e23SDavid du Colombier nfail = 0; 2028fe853e23SDavid du Colombier } 20295e96a66cSDavid du Colombier /* Requeue block. */ 20305e96a66cSDavid du Colombier if(c->bw < c->be) 20315e96a66cSDavid du Colombier c->baddr[c->bw++] = *p; 20325e96a66cSDavid du Colombier } 20335e96a66cSDavid du Colombier } 20345e96a66cSDavid du Colombier 20355e96a66cSDavid du Colombier /* 20365e96a66cSDavid du Colombier * Occasionally flush dirty blocks from memory to the disk. 20375e96a66cSDavid du Colombier */ 20385e96a66cSDavid du Colombier static void 20395e96a66cSDavid du Colombier flushThread(void *a) 20405e96a66cSDavid du Colombier { 20415e96a66cSDavid du Colombier Cache *c = a; 20425e96a66cSDavid du Colombier int i; 20435e96a66cSDavid du Colombier 20445e96a66cSDavid du Colombier vtThreadSetName("flush"); 20455e96a66cSDavid du Colombier vtLock(c->lk); 20465e96a66cSDavid du Colombier while(c->die == nil){ 20475e96a66cSDavid du Colombier vtSleep(c->flush); 20485e96a66cSDavid du Colombier vtUnlock(c->lk); 20495e96a66cSDavid du Colombier for(i=0; i<FlushSize; i++) 205067031067SDavid du Colombier if(!cacheFlushBlock(c)){ 205167031067SDavid du Colombier /* 205267031067SDavid du Colombier * If i==0, could be someone is waking us repeatedly 205367031067SDavid du Colombier * to flush the cache but there's no work to do. 205467031067SDavid du Colombier * Pause a little. 205567031067SDavid du Colombier */ 20560b9a5132SDavid du Colombier if(i==0){ 20573b86f2f8SDavid du Colombier // fprint(2, "%s: flushthread found " 20583b86f2f8SDavid du Colombier // "nothing to flush - %d dirty\n", 20593b86f2f8SDavid du Colombier // argv0, c->ndirty); 206067031067SDavid du Colombier sleep(250); 20610b9a5132SDavid du Colombier } 20625e96a66cSDavid du Colombier break; 206367031067SDavid du Colombier } 206439734e7eSDavid du Colombier if(i==0 && c->ndirty){ 206539734e7eSDavid du Colombier /* 206639734e7eSDavid du Colombier * All the blocks are being written right now -- there's nothing to do. 206739734e7eSDavid du Colombier * We might be spinning with cacheFlush though -- he'll just keep 206839734e7eSDavid du Colombier * kicking us until c->ndirty goes down. Probably we should sleep 206939734e7eSDavid du Colombier * on something that the diskThread can kick, but for now we'll 207039734e7eSDavid du Colombier * just pause for a little while waiting for disks to finish. 207139734e7eSDavid du Colombier */ 207239734e7eSDavid du Colombier sleep(100); 207339734e7eSDavid du Colombier } 20745e96a66cSDavid du Colombier vtLock(c->lk); 20755e96a66cSDavid du Colombier vtWakeupAll(c->flushwait); 20765e96a66cSDavid du Colombier } 20775e96a66cSDavid du Colombier c->ref--; 20785e96a66cSDavid du Colombier vtWakeup(c->die); 20795e96a66cSDavid du Colombier vtUnlock(c->lk); 20805e96a66cSDavid du Colombier } 20815e96a66cSDavid du Colombier 20825e96a66cSDavid du Colombier /* 20830b9a5132SDavid du Colombier * Flush the cache. 20845e96a66cSDavid du Colombier */ 20855e96a66cSDavid du Colombier void 20865e96a66cSDavid du Colombier cacheFlush(Cache *c, int wait) 20875e96a66cSDavid du Colombier { 20880b9a5132SDavid du Colombier /* 20890b9a5132SDavid du Colombier * Lock c->dirtylk so that more blocks aren't being dirtied 20900b9a5132SDavid du Colombier * while we try to write out what's already here. 20910b9a5132SDavid du Colombier * Otherwise we might not ever finish! 20920b9a5132SDavid du Colombier */ 20930b9a5132SDavid du Colombier vtLock(c->dirtylk); 20945e96a66cSDavid du Colombier vtLock(c->lk); 20955e96a66cSDavid du Colombier if(wait){ 20965e96a66cSDavid du Colombier while(c->ndirty){ 2097dc5a79c1SDavid du Colombier // consPrint("cacheFlush: %d dirty blocks, uhead %p\n", 2098dc5a79c1SDavid du Colombier // c->ndirty, c->uhead); 20995e96a66cSDavid du Colombier vtWakeup(c->flush); 21005e96a66cSDavid du Colombier vtSleep(c->flushwait); 21015e96a66cSDavid du Colombier } 2102dc5a79c1SDavid du Colombier // consPrint("cacheFlush: done (uhead %p)\n", c->ndirty, c->uhead); 21030b9a5132SDavid du Colombier }else if(c->ndirty) 21045e96a66cSDavid du Colombier vtWakeup(c->flush); 21055e96a66cSDavid du Colombier vtUnlock(c->lk); 21060b9a5132SDavid du Colombier vtUnlock(c->dirtylk); 21075e96a66cSDavid du Colombier } 210861201b97SDavid du Colombier 210961201b97SDavid du Colombier /* 211061201b97SDavid du Colombier * Kick the flushThread every 30 seconds. 211161201b97SDavid du Colombier */ 211261201b97SDavid du Colombier static void 211361201b97SDavid du Colombier cacheSync(void *v) 211461201b97SDavid du Colombier { 211561201b97SDavid du Colombier Cache *c; 211661201b97SDavid du Colombier 211761201b97SDavid du Colombier c = v; 211861201b97SDavid du Colombier cacheFlush(c, 0); 211961201b97SDavid du Colombier } 2120