1*5e96a66cSDavid du Colombier #include "stdinc.h" 2*5e96a66cSDavid du Colombier #include "dat.h" 3*5e96a66cSDavid du Colombier #include "fns.h" 4*5e96a66cSDavid du Colombier #include "error.h" 5*5e96a66cSDavid du Colombier 6*5e96a66cSDavid du Colombier #include "9.h" /* for cacheFlush */ 7*5e96a66cSDavid du Colombier 8*5e96a66cSDavid du Colombier typedef struct FreeList FreeList; 9*5e96a66cSDavid du Colombier typedef struct BAddr BAddr; 10*5e96a66cSDavid du Colombier 11*5e96a66cSDavid du Colombier enum { 12*5e96a66cSDavid du Colombier BadHeap = ~0, 13*5e96a66cSDavid du Colombier }; 14*5e96a66cSDavid du Colombier 15*5e96a66cSDavid du Colombier /* 16*5e96a66cSDavid du Colombier * Store data to the memory cache in c->size blocks 17*5e96a66cSDavid du Colombier * with the block zero extended to fill it out. When writing to 18*5e96a66cSDavid du Colombier * Venti, the block will be zero truncated. The walker will also check 19*5e96a66cSDavid du Colombier * that the block fits within psize or dsize as the case may be. 20*5e96a66cSDavid du Colombier */ 21*5e96a66cSDavid du Colombier 22*5e96a66cSDavid du Colombier struct Cache 23*5e96a66cSDavid du Colombier { 24*5e96a66cSDavid du Colombier VtLock *lk; 25*5e96a66cSDavid du Colombier int ref; 26*5e96a66cSDavid du Colombier int mode; 27*5e96a66cSDavid du Colombier 28*5e96a66cSDavid du Colombier Disk *disk; 29*5e96a66cSDavid du Colombier int size; /* block size */ 30*5e96a66cSDavid du Colombier VtSession *z; 31*5e96a66cSDavid du Colombier u32int now; /* ticks for usage timestamps */ 32*5e96a66cSDavid du Colombier Block **heads; /* hash table for finding address */ 33*5e96a66cSDavid du Colombier int nheap; /* number of available victims */ 34*5e96a66cSDavid du Colombier Block **heap; /* heap for locating victims */ 35*5e96a66cSDavid du Colombier long nblocks; /* number of blocks allocated */ 36*5e96a66cSDavid du Colombier Block *blocks; /* array of block descriptors */ 37*5e96a66cSDavid du Colombier u8int *mem; /* memory for all block data & blists */ 38*5e96a66cSDavid du Colombier 39*5e96a66cSDavid du Colombier BList *blfree; 40*5e96a66cSDavid du Colombier VtRendez *blrend; 41*5e96a66cSDavid du Colombier 42*5e96a66cSDavid du Colombier int ndirty; /* number of dirty blocks in the cache */ 43*5e96a66cSDavid du Colombier int maxdirty; /* max number of dirty blocks */ 44*5e96a66cSDavid du Colombier u32int vers; 45*5e96a66cSDavid du Colombier 46*5e96a66cSDavid du Colombier long hashSize; 47*5e96a66cSDavid du Colombier 48*5e96a66cSDavid du Colombier FreeList *fl; 49*5e96a66cSDavid du Colombier 50*5e96a66cSDavid du Colombier VtRendez *die; /* daemon threads should die when != nil */ 51*5e96a66cSDavid du Colombier 52*5e96a66cSDavid du Colombier VtRendez *flush; 53*5e96a66cSDavid du Colombier VtRendez *flushwait; 54*5e96a66cSDavid du Colombier BAddr *baddr; 55*5e96a66cSDavid du Colombier int bw, br, be; 56*5e96a66cSDavid du Colombier int nflush; 57*5e96a66cSDavid du Colombier 58*5e96a66cSDavid du Colombier /* unlink daemon */ 59*5e96a66cSDavid du Colombier BList *uhead; 60*5e96a66cSDavid du Colombier BList *utail; 61*5e96a66cSDavid du Colombier VtRendez *unlink; 62*5e96a66cSDavid du Colombier }; 63*5e96a66cSDavid du Colombier 64*5e96a66cSDavid du Colombier struct BList { 65*5e96a66cSDavid du Colombier int part; 66*5e96a66cSDavid du Colombier u32int addr; 67*5e96a66cSDavid du Colombier uchar type; 68*5e96a66cSDavid du Colombier u32int tag; 69*5e96a66cSDavid du Colombier u32int epoch; 70*5e96a66cSDavid du Colombier u32int vers; 71*5e96a66cSDavid du Colombier 72*5e96a66cSDavid du Colombier /* for roll back */ 73*5e96a66cSDavid du Colombier int index; /* -1 indicates not valid */ 74*5e96a66cSDavid du Colombier uchar score[VtScoreSize]; 75*5e96a66cSDavid du Colombier 76*5e96a66cSDavid du Colombier BList *next; 77*5e96a66cSDavid du Colombier }; 78*5e96a66cSDavid du Colombier 79*5e96a66cSDavid du Colombier struct BAddr { 80*5e96a66cSDavid du Colombier int part; 81*5e96a66cSDavid du Colombier u32int addr; 82*5e96a66cSDavid du Colombier u32int vers; 83*5e96a66cSDavid du Colombier }; 84*5e96a66cSDavid du Colombier 85*5e96a66cSDavid du Colombier struct FreeList { 86*5e96a66cSDavid du Colombier VtLock *lk; 87*5e96a66cSDavid du Colombier u32int last; /* last block allocated */ 88*5e96a66cSDavid du Colombier u32int end; /* end of data partition */ 89*5e96a66cSDavid du Colombier }; 90*5e96a66cSDavid du Colombier 91*5e96a66cSDavid du Colombier static FreeList *flAlloc(u32int end); 92*5e96a66cSDavid du Colombier static void flFree(FreeList *fl); 93*5e96a66cSDavid du Colombier 94*5e96a66cSDavid du Colombier static Block *cacheBumpBlock(Cache *c); 95*5e96a66cSDavid du Colombier static void heapDel(Block*); 96*5e96a66cSDavid du Colombier static void heapIns(Block*); 97*5e96a66cSDavid du Colombier static void cacheCheck(Cache*); 98*5e96a66cSDavid du Colombier static int readLabel(Cache*, Label*, u32int addr); 99*5e96a66cSDavid du Colombier static void unlinkThread(void *a); 100*5e96a66cSDavid du Colombier static void flushThread(void *a); 101*5e96a66cSDavid du Colombier static void flushBody(Cache *c); 102*5e96a66cSDavid du Colombier static void unlinkBody(Cache *c); 103*5e96a66cSDavid du Colombier static int cacheFlushBlock(Cache *c); 104*5e96a66cSDavid du Colombier 105*5e96a66cSDavid du Colombier /* 106*5e96a66cSDavid du Colombier * Mapping from local block type to Venti type 107*5e96a66cSDavid du Colombier */ 108*5e96a66cSDavid du Colombier int vtType[BtMax] = { 109*5e96a66cSDavid du Colombier VtDataType, /* BtData | 0 */ 110*5e96a66cSDavid du Colombier VtPointerType0, /* BtData | 1 */ 111*5e96a66cSDavid du Colombier VtPointerType1, /* BtData | 2 */ 112*5e96a66cSDavid du Colombier VtPointerType2, /* BtData | 3 */ 113*5e96a66cSDavid du Colombier VtPointerType3, /* BtData | 4 */ 114*5e96a66cSDavid du Colombier VtPointerType4, /* BtData | 5 */ 115*5e96a66cSDavid du Colombier VtPointerType5, /* BtData | 6 */ 116*5e96a66cSDavid du Colombier VtPointerType6, /* BtData | 7 */ 117*5e96a66cSDavid du Colombier VtDirType, /* BtDir | 0 */ 118*5e96a66cSDavid du Colombier VtPointerType0, /* BtDir | 1 */ 119*5e96a66cSDavid du Colombier VtPointerType1, /* BtDir | 2 */ 120*5e96a66cSDavid du Colombier VtPointerType2, /* BtDir | 3 */ 121*5e96a66cSDavid du Colombier VtPointerType3, /* BtDir | 4 */ 122*5e96a66cSDavid du Colombier VtPointerType4, /* BtDir | 5 */ 123*5e96a66cSDavid du Colombier VtPointerType5, /* BtDir | 6 */ 124*5e96a66cSDavid du Colombier VtPointerType6, /* BtDir | 7 */ 125*5e96a66cSDavid du Colombier }; 126*5e96a66cSDavid du Colombier 127*5e96a66cSDavid du Colombier /* 128*5e96a66cSDavid du Colombier * Allocate the memory cache. 129*5e96a66cSDavid du Colombier */ 130*5e96a66cSDavid du Colombier Cache * 131*5e96a66cSDavid du Colombier cacheAlloc(Disk *disk, VtSession *z, ulong nblocks, int mode) 132*5e96a66cSDavid du Colombier { 133*5e96a66cSDavid du Colombier int i; 134*5e96a66cSDavid du Colombier Cache *c; 135*5e96a66cSDavid du Colombier Block *b; 136*5e96a66cSDavid du Colombier BList *bl; 137*5e96a66cSDavid du Colombier u8int *p; 138*5e96a66cSDavid du Colombier int nbl; 139*5e96a66cSDavid du Colombier 140*5e96a66cSDavid du Colombier c = vtMemAllocZ(sizeof(Cache)); 141*5e96a66cSDavid du Colombier 142*5e96a66cSDavid du Colombier /* reasonable number of BList elements */ 143*5e96a66cSDavid du Colombier nbl = nblocks * 4; 144*5e96a66cSDavid du Colombier 145*5e96a66cSDavid du Colombier c->lk = vtLockAlloc(); 146*5e96a66cSDavid du Colombier c->ref = 1; 147*5e96a66cSDavid du Colombier c->disk = disk; 148*5e96a66cSDavid du Colombier c->z = z; 149*5e96a66cSDavid du Colombier c->size = diskBlockSize(disk); 150*5e96a66cSDavid du Colombier bwatchSetBlockSize(c->size); 151*5e96a66cSDavid du Colombier /* round c->size up to be a nice multiple */ 152*5e96a66cSDavid du Colombier c->size = (c->size + 127) & ~127; 153*5e96a66cSDavid du Colombier c->nblocks = nblocks; 154*5e96a66cSDavid du Colombier c->hashSize = nblocks; 155*5e96a66cSDavid du Colombier c->heads = vtMemAllocZ(c->hashSize*sizeof(Block*)); 156*5e96a66cSDavid du Colombier c->heap = vtMemAllocZ(nblocks*sizeof(Block*)); 157*5e96a66cSDavid du Colombier c->blocks = vtMemAllocZ(nblocks*sizeof(Block)); 158*5e96a66cSDavid du Colombier c->mem = vtMemAllocZ(nblocks * c->size + nbl * sizeof(BList)); 159*5e96a66cSDavid du Colombier c->baddr = vtMemAllocZ(nblocks * sizeof(BAddr)); 160*5e96a66cSDavid du Colombier c->mode = mode; 161*5e96a66cSDavid du Colombier c->vers++; 162*5e96a66cSDavid du Colombier p = c->mem; 163*5e96a66cSDavid du Colombier for(i = 0; i < nblocks; i++){ 164*5e96a66cSDavid du Colombier b = &c->blocks[i]; 165*5e96a66cSDavid du Colombier b->lk = vtLockAlloc(); 166*5e96a66cSDavid du Colombier b->c = c; 167*5e96a66cSDavid du Colombier b->data = p; 168*5e96a66cSDavid du Colombier b->heap = i; 169*5e96a66cSDavid du Colombier b->ioready = vtRendezAlloc(b->lk); 170*5e96a66cSDavid du Colombier c->heap[i] = b; 171*5e96a66cSDavid du Colombier p += c->size; 172*5e96a66cSDavid du Colombier } 173*5e96a66cSDavid du Colombier c->nheap = nblocks; 174*5e96a66cSDavid du Colombier 175*5e96a66cSDavid du Colombier for(i=0; i<nbl; i++){ 176*5e96a66cSDavid du Colombier bl = (BList*)p; 177*5e96a66cSDavid du Colombier bl->next = c->blfree; 178*5e96a66cSDavid du Colombier c->blfree = bl; 179*5e96a66cSDavid du Colombier p += sizeof(BList); 180*5e96a66cSDavid du Colombier } 181*5e96a66cSDavid du Colombier c->blrend = vtRendezAlloc(c->lk); 182*5e96a66cSDavid du Colombier 183*5e96a66cSDavid du Colombier c->maxdirty = nblocks*(DirtyPercentage*0.01); 184*5e96a66cSDavid du Colombier 185*5e96a66cSDavid du Colombier c->fl = flAlloc(diskSize(disk, PartData)); 186*5e96a66cSDavid du Colombier 187*5e96a66cSDavid du Colombier c->unlink = vtRendezAlloc(c->lk); 188*5e96a66cSDavid du Colombier c->flush = vtRendezAlloc(c->lk); 189*5e96a66cSDavid du Colombier c->flushwait = vtRendezAlloc(c->lk); 190*5e96a66cSDavid du Colombier 191*5e96a66cSDavid du Colombier if(mode == OReadWrite){ 192*5e96a66cSDavid du Colombier c->ref += 2; 193*5e96a66cSDavid du Colombier vtThread(unlinkThread, c); 194*5e96a66cSDavid du Colombier vtThread(flushThread, c); 195*5e96a66cSDavid du Colombier } 196*5e96a66cSDavid du Colombier cacheCheck(c); 197*5e96a66cSDavid du Colombier 198*5e96a66cSDavid du Colombier return c; 199*5e96a66cSDavid du Colombier } 200*5e96a66cSDavid du Colombier 201*5e96a66cSDavid du Colombier /* 202*5e96a66cSDavid du Colombier * Free the whole memory cache, flushing all dirty blocks to the disk. 203*5e96a66cSDavid du Colombier */ 204*5e96a66cSDavid du Colombier void 205*5e96a66cSDavid du Colombier cacheFree(Cache *c) 206*5e96a66cSDavid du Colombier { 207*5e96a66cSDavid du Colombier int i; 208*5e96a66cSDavid du Colombier 209*5e96a66cSDavid du Colombier /* kill off daemon threads */ 210*5e96a66cSDavid du Colombier vtLock(c->lk); 211*5e96a66cSDavid du Colombier c->die = vtRendezAlloc(c->lk); 212*5e96a66cSDavid du Colombier vtWakeup(c->flush); 213*5e96a66cSDavid du Colombier vtWakeup(c->unlink); 214*5e96a66cSDavid du Colombier while(c->ref > 1) 215*5e96a66cSDavid du Colombier vtSleep(c->die); 216*5e96a66cSDavid du Colombier 217*5e96a66cSDavid du Colombier /* flush everything out */ 218*5e96a66cSDavid du Colombier do { 219*5e96a66cSDavid du Colombier unlinkBody(c); 220*5e96a66cSDavid du Colombier vtUnlock(c->lk); 221*5e96a66cSDavid du Colombier while(cacheFlushBlock(c)) 222*5e96a66cSDavid du Colombier ; 223*5e96a66cSDavid du Colombier diskFlush(c->disk); 224*5e96a66cSDavid du Colombier vtLock(c->lk); 225*5e96a66cSDavid du Colombier } while(c->uhead || c->ndirty); 226*5e96a66cSDavid du Colombier vtUnlock(c->lk); 227*5e96a66cSDavid du Colombier 228*5e96a66cSDavid du Colombier cacheCheck(c); 229*5e96a66cSDavid du Colombier 230*5e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){ 231*5e96a66cSDavid du Colombier assert(c->blocks[i].ref == 0); 232*5e96a66cSDavid du Colombier vtRendezFree(c->blocks[i].ioready); 233*5e96a66cSDavid du Colombier vtLockFree(c->blocks[i].lk); 234*5e96a66cSDavid du Colombier } 235*5e96a66cSDavid du Colombier flFree(c->fl); 236*5e96a66cSDavid du Colombier vtMemFree(c->baddr); 237*5e96a66cSDavid du Colombier vtMemFree(c->heads); 238*5e96a66cSDavid du Colombier vtMemFree(c->blocks); 239*5e96a66cSDavid du Colombier vtMemFree(c->mem); 240*5e96a66cSDavid du Colombier vtLockFree(c->lk); 241*5e96a66cSDavid du Colombier diskFree(c->disk); 242*5e96a66cSDavid du Colombier vtRendezFree(c->blrend); 243*5e96a66cSDavid du Colombier /* don't close vtSession */ 244*5e96a66cSDavid du Colombier vtMemFree(c); 245*5e96a66cSDavid du Colombier } 246*5e96a66cSDavid du Colombier 247*5e96a66cSDavid du Colombier static void 248*5e96a66cSDavid du Colombier cacheDump(Cache *c) 249*5e96a66cSDavid du Colombier { 250*5e96a66cSDavid du Colombier int i; 251*5e96a66cSDavid du Colombier Block *b; 252*5e96a66cSDavid du Colombier 253*5e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){ 254*5e96a66cSDavid du Colombier b = &c->blocks[i]; 255*5e96a66cSDavid du Colombier fprint(2, "p=%d a=%ud %V t=%d ref=%d state=%s io=%s\n", 256*5e96a66cSDavid du Colombier b->part, b->addr, b->score, b->l.type, b->ref, 257*5e96a66cSDavid du Colombier bsStr(b->l.state), bioStr(b->iostate)); 258*5e96a66cSDavid du Colombier } 259*5e96a66cSDavid du Colombier } 260*5e96a66cSDavid du Colombier 261*5e96a66cSDavid du Colombier static void 262*5e96a66cSDavid du Colombier cacheCheck(Cache *c) 263*5e96a66cSDavid du Colombier { 264*5e96a66cSDavid du Colombier u32int size, now; 265*5e96a66cSDavid du Colombier int i, k, refed; 266*5e96a66cSDavid du Colombier static uchar zero[VtScoreSize]; 267*5e96a66cSDavid du Colombier Block *b; 268*5e96a66cSDavid du Colombier 269*5e96a66cSDavid du Colombier size = c->size; 270*5e96a66cSDavid du Colombier now = c->now; 271*5e96a66cSDavid du Colombier 272*5e96a66cSDavid du Colombier for(i = 0; i < c->nheap; i++){ 273*5e96a66cSDavid du Colombier if(c->heap[i]->heap != i) 274*5e96a66cSDavid du Colombier vtFatal("mis-heaped at %d: %d", i, c->heap[i]->heap); 275*5e96a66cSDavid du Colombier if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now) 276*5e96a66cSDavid du Colombier vtFatal("bad heap ordering"); 277*5e96a66cSDavid du Colombier k = (i << 1) + 1; 278*5e96a66cSDavid du Colombier if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now) 279*5e96a66cSDavid du Colombier vtFatal("bad heap ordering"); 280*5e96a66cSDavid du Colombier k++; 281*5e96a66cSDavid du Colombier if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now) 282*5e96a66cSDavid du Colombier vtFatal("bad heap ordering"); 283*5e96a66cSDavid du Colombier } 284*5e96a66cSDavid du Colombier 285*5e96a66cSDavid du Colombier refed = 0; 286*5e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){ 287*5e96a66cSDavid du Colombier b = &c->blocks[i]; 288*5e96a66cSDavid du Colombier if(b->data != &c->mem[i * size]) 289*5e96a66cSDavid du Colombier vtFatal("mis-blocked at %d", i); 290*5e96a66cSDavid du Colombier if(b->ref && b->heap == BadHeap){ 291*5e96a66cSDavid du Colombier refed++; 292*5e96a66cSDavid du Colombier } 293*5e96a66cSDavid du Colombier } 294*5e96a66cSDavid du Colombier if(c->nheap + refed != c->nblocks){ 295*5e96a66cSDavid du Colombier fprint(2, "cacheCheck: nheap %d refed %d nblocks %ld\n", c->nheap, refed, c->nblocks); 296*5e96a66cSDavid du Colombier cacheDump(c); 297*5e96a66cSDavid du Colombier } 298*5e96a66cSDavid du Colombier assert(c->nheap + refed == c->nblocks); 299*5e96a66cSDavid du Colombier refed = 0; 300*5e96a66cSDavid du Colombier for(i = 0; i < c->nblocks; i++){ 301*5e96a66cSDavid du Colombier b = &c->blocks[i]; 302*5e96a66cSDavid du Colombier if(b->ref){ 303*5e96a66cSDavid du Colombier if(1)fprint(2, "p=%d a=%ud %V ref=%d %L\n", b->part, b->addr, b->score, b->ref, &b->l); 304*5e96a66cSDavid du Colombier refed++; 305*5e96a66cSDavid du Colombier } 306*5e96a66cSDavid du Colombier } 307*5e96a66cSDavid du Colombier if(refed > 0)fprint(2, "cacheCheck: in used %d\n", refed); 308*5e96a66cSDavid du Colombier } 309*5e96a66cSDavid du Colombier 310*5e96a66cSDavid du Colombier 311*5e96a66cSDavid du Colombier /* 312*5e96a66cSDavid du Colombier * locate the block with the oldest second to last use. 313*5e96a66cSDavid du Colombier * remove it from the heap, and fix up the heap. 314*5e96a66cSDavid du Colombier */ 315*5e96a66cSDavid du Colombier /* called with c->lk held */ 316*5e96a66cSDavid du Colombier static Block * 317*5e96a66cSDavid du Colombier cacheBumpBlock(Cache *c) 318*5e96a66cSDavid du Colombier { 319*5e96a66cSDavid du Colombier Block *b; 320*5e96a66cSDavid du Colombier 321*5e96a66cSDavid du Colombier /* 322*5e96a66cSDavid du Colombier * locate the block with the oldest second to last use. 323*5e96a66cSDavid du Colombier * remove it from the heap, and fix up the heap. 324*5e96a66cSDavid du Colombier */ 325*5e96a66cSDavid du Colombier if(c->nheap == 0) 326*5e96a66cSDavid du Colombier vtFatal("cacheBumpBlock: no free blocks in cache"); 327*5e96a66cSDavid du Colombier b = c->heap[0]; 328*5e96a66cSDavid du Colombier heapDel(b); 329*5e96a66cSDavid du Colombier 330*5e96a66cSDavid du Colombier assert(b->heap == BadHeap); 331*5e96a66cSDavid du Colombier assert(b->ref == 0); 332*5e96a66cSDavid du Colombier assert(b->iostate == BioEmpty || b->iostate == BioLabel || b->iostate == BioClean); 333*5e96a66cSDavid du Colombier assert(b->prior == nil); 334*5e96a66cSDavid du Colombier assert(b->uhead == nil); 335*5e96a66cSDavid du Colombier 336*5e96a66cSDavid du Colombier /* 337*5e96a66cSDavid du Colombier * unchain the block from hash chain 338*5e96a66cSDavid du Colombier */ 339*5e96a66cSDavid du Colombier if(b->prev){ 340*5e96a66cSDavid du Colombier *(b->prev) = b->next; 341*5e96a66cSDavid du Colombier if(b->next) 342*5e96a66cSDavid du Colombier b->next->prev = b->prev; 343*5e96a66cSDavid du Colombier b->prev = nil; 344*5e96a66cSDavid du Colombier } 345*5e96a66cSDavid du Colombier 346*5e96a66cSDavid du Colombier 347*5e96a66cSDavid du Colombier if(0)fprint(2, "droping %d:%x:%V\n", b->part, b->addr, b->score); 348*5e96a66cSDavid du Colombier /* set block to a reasonable state */ 349*5e96a66cSDavid du Colombier b->ref = 1; 350*5e96a66cSDavid du Colombier b->part = PartError; 351*5e96a66cSDavid du Colombier memset(&b->l, 0, sizeof(b->l)); 352*5e96a66cSDavid du Colombier b->iostate = BioEmpty; 353*5e96a66cSDavid du Colombier 354*5e96a66cSDavid du Colombier return b; 355*5e96a66cSDavid du Colombier } 356*5e96a66cSDavid du Colombier 357*5e96a66cSDavid du Colombier /* 358*5e96a66cSDavid du Colombier * look for a particular version of the block in the memory cache. 359*5e96a66cSDavid du Colombier */ 360*5e96a66cSDavid du Colombier static Block * 361*5e96a66cSDavid du Colombier _cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers, 362*5e96a66cSDavid du Colombier int waitlock, int *lockfailure) 363*5e96a66cSDavid du Colombier { 364*5e96a66cSDavid du Colombier Block *b; 365*5e96a66cSDavid du Colombier ulong h; 366*5e96a66cSDavid du Colombier 367*5e96a66cSDavid du Colombier h = addr % c->hashSize; 368*5e96a66cSDavid du Colombier 369*5e96a66cSDavid du Colombier if(lockfailure) 370*5e96a66cSDavid du Colombier *lockfailure = 0; 371*5e96a66cSDavid du Colombier 372*5e96a66cSDavid du Colombier /* 373*5e96a66cSDavid du Colombier * look for the block in the cache 374*5e96a66cSDavid du Colombier */ 375*5e96a66cSDavid du Colombier vtLock(c->lk); 376*5e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){ 377*5e96a66cSDavid du Colombier if(b->part == part && b->addr == addr) 378*5e96a66cSDavid du Colombier break; 379*5e96a66cSDavid du Colombier } 380*5e96a66cSDavid du Colombier if(b == nil || b->vers != vers){ 381*5e96a66cSDavid du Colombier vtUnlock(c->lk); 382*5e96a66cSDavid du Colombier return nil; 383*5e96a66cSDavid du Colombier } 384*5e96a66cSDavid du Colombier if(!waitlock && !vtCanLock(b->lk)){ 385*5e96a66cSDavid du Colombier *lockfailure = 1; 386*5e96a66cSDavid du Colombier vtUnlock(c->lk); 387*5e96a66cSDavid du Colombier return nil; 388*5e96a66cSDavid du Colombier } 389*5e96a66cSDavid du Colombier heapDel(b); 390*5e96a66cSDavid du Colombier b->ref++; 391*5e96a66cSDavid du Colombier vtUnlock(c->lk); 392*5e96a66cSDavid du Colombier 393*5e96a66cSDavid du Colombier bwatchLock(b); 394*5e96a66cSDavid du Colombier if(waitlock) 395*5e96a66cSDavid du Colombier vtLock(b->lk); 396*5e96a66cSDavid du Colombier b->nlock = 1; 397*5e96a66cSDavid du Colombier 398*5e96a66cSDavid du Colombier for(;;){ 399*5e96a66cSDavid du Colombier switch(b->iostate){ 400*5e96a66cSDavid du Colombier default: 401*5e96a66cSDavid du Colombier abort(); 402*5e96a66cSDavid du Colombier case BioEmpty: 403*5e96a66cSDavid du Colombier case BioLabel: 404*5e96a66cSDavid du Colombier case BioClean: 405*5e96a66cSDavid du Colombier case BioDirty: 406*5e96a66cSDavid du Colombier if(b->vers != vers){ 407*5e96a66cSDavid du Colombier blockPut(b); 408*5e96a66cSDavid du Colombier return nil; 409*5e96a66cSDavid du Colombier } 410*5e96a66cSDavid du Colombier return b; 411*5e96a66cSDavid du Colombier case BioReading: 412*5e96a66cSDavid du Colombier case BioWriting: 413*5e96a66cSDavid du Colombier vtSleep(b->ioready); 414*5e96a66cSDavid du Colombier break; 415*5e96a66cSDavid du Colombier case BioVentiError: 416*5e96a66cSDavid du Colombier case BioReadError: 417*5e96a66cSDavid du Colombier blockSetIOState(b, BioEmpty); 418*5e96a66cSDavid du Colombier blockPut(b); 419*5e96a66cSDavid du Colombier vtSetError(EIO); 420*5e96a66cSDavid du Colombier return nil; 421*5e96a66cSDavid du Colombier } 422*5e96a66cSDavid du Colombier } 423*5e96a66cSDavid du Colombier /* NOT REACHED */ 424*5e96a66cSDavid du Colombier } 425*5e96a66cSDavid du Colombier static Block* 426*5e96a66cSDavid du Colombier cacheLocalLookup(Cache *c, int part, u32int addr, u32int vers) 427*5e96a66cSDavid du Colombier { 428*5e96a66cSDavid du Colombier return _cacheLocalLookup(c, part, addr, vers, 1, 0); 429*5e96a66cSDavid du Colombier } 430*5e96a66cSDavid du Colombier 431*5e96a66cSDavid du Colombier 432*5e96a66cSDavid du Colombier /* 433*5e96a66cSDavid du Colombier * fetch a local (on-disk) block from the memory cache. 434*5e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block. 435*5e96a66cSDavid du Colombier */ 436*5e96a66cSDavid du Colombier Block * 437*5e96a66cSDavid du Colombier _cacheLocal(Cache *c, int part, u32int addr, int mode, u32int epoch) 438*5e96a66cSDavid du Colombier { 439*5e96a66cSDavid du Colombier Block *b; 440*5e96a66cSDavid du Colombier ulong h; 441*5e96a66cSDavid du Colombier 442*5e96a66cSDavid du Colombier assert(part != PartVenti); 443*5e96a66cSDavid du Colombier 444*5e96a66cSDavid du Colombier h = addr % c->hashSize; 445*5e96a66cSDavid du Colombier 446*5e96a66cSDavid du Colombier /* 447*5e96a66cSDavid du Colombier * look for the block in the cache 448*5e96a66cSDavid du Colombier */ 449*5e96a66cSDavid du Colombier vtLock(c->lk); 450*5e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){ 451*5e96a66cSDavid du Colombier if(b->part != part || b->addr != addr) 452*5e96a66cSDavid du Colombier continue; 453*5e96a66cSDavid du Colombier if(epoch && b->l.epoch != epoch){ 454*5e96a66cSDavid du Colombier fprint(2, "_cacheLocal want epoch %ud got %ud\n", epoch, b->l.epoch); 455*5e96a66cSDavid du Colombier vtUnlock(c->lk); 456*5e96a66cSDavid du Colombier vtSetError(ELabelMismatch); 457*5e96a66cSDavid du Colombier return nil; 458*5e96a66cSDavid du Colombier } 459*5e96a66cSDavid du Colombier heapDel(b); 460*5e96a66cSDavid du Colombier b->ref++; 461*5e96a66cSDavid du Colombier break; 462*5e96a66cSDavid du Colombier } 463*5e96a66cSDavid du Colombier 464*5e96a66cSDavid du Colombier if(b == nil){ 465*5e96a66cSDavid du Colombier b = cacheBumpBlock(c); 466*5e96a66cSDavid du Colombier 467*5e96a66cSDavid du Colombier b->part = part; 468*5e96a66cSDavid du Colombier b->addr = addr; 469*5e96a66cSDavid du Colombier localToGlobal(addr, b->score); 470*5e96a66cSDavid du Colombier 471*5e96a66cSDavid du Colombier /* chain onto correct hash */ 472*5e96a66cSDavid du Colombier b->next = c->heads[h]; 473*5e96a66cSDavid du Colombier c->heads[h] = b; 474*5e96a66cSDavid du Colombier if(b->next != nil) 475*5e96a66cSDavid du Colombier b->next->prev = &b->next; 476*5e96a66cSDavid du Colombier b->prev = &c->heads[h]; 477*5e96a66cSDavid du Colombier } 478*5e96a66cSDavid du Colombier 479*5e96a66cSDavid du Colombier vtUnlock(c->lk); 480*5e96a66cSDavid du Colombier 481*5e96a66cSDavid du Colombier /* 482*5e96a66cSDavid du Colombier * BUG: what if the epoch changes right here? 483*5e96a66cSDavid du Colombier * In the worst case, we could end up in some weird 484*5e96a66cSDavid du Colombier * lock loop, because the block we want no longer exists, 485*5e96a66cSDavid du Colombier * and instead we're trying to lock a block we have no 486*5e96a66cSDavid du Colombier * business grabbing. 487*5e96a66cSDavid du Colombier * 488*5e96a66cSDavid du Colombier * For now, I'm not going to worry about it. 489*5e96a66cSDavid du Colombier */ 490*5e96a66cSDavid du Colombier 491*5e96a66cSDavid du Colombier if(0)fprint(2, "cacheLocal: %d: %d %x\n", getpid(), b->part, b->addr); 492*5e96a66cSDavid du Colombier bwatchLock(b); 493*5e96a66cSDavid du Colombier vtLock(b->lk); 494*5e96a66cSDavid du Colombier b->nlock = 1; 495*5e96a66cSDavid du Colombier 496*5e96a66cSDavid du Colombier if(part == PartData && b->iostate == BioEmpty){ 497*5e96a66cSDavid du Colombier if(!readLabel(c, &b->l, addr)){ 498*5e96a66cSDavid du Colombier blockPut(b); 499*5e96a66cSDavid du Colombier return nil; 500*5e96a66cSDavid du Colombier } 501*5e96a66cSDavid du Colombier blockSetIOState(b, BioLabel); 502*5e96a66cSDavid du Colombier } 503*5e96a66cSDavid du Colombier if(epoch && b->l.epoch != epoch){ 504*5e96a66cSDavid du Colombier blockPut(b); 505*5e96a66cSDavid du Colombier fprint(2, "_cacheLocal want epoch %ud got %ud\n", epoch, b->l.epoch); 506*5e96a66cSDavid du Colombier vtSetError(ELabelMismatch); 507*5e96a66cSDavid du Colombier return nil; 508*5e96a66cSDavid du Colombier } 509*5e96a66cSDavid du Colombier 510*5e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 511*5e96a66cSDavid du Colombier for(;;){ 512*5e96a66cSDavid du Colombier switch(b->iostate){ 513*5e96a66cSDavid du Colombier default: 514*5e96a66cSDavid du Colombier abort(); 515*5e96a66cSDavid du Colombier case BioEmpty: 516*5e96a66cSDavid du Colombier case BioLabel: 517*5e96a66cSDavid du Colombier if(mode == OOverWrite){ 518*5e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 519*5e96a66cSDavid du Colombier return b; 520*5e96a66cSDavid du Colombier } 521*5e96a66cSDavid du Colombier diskRead(c->disk, b); 522*5e96a66cSDavid du Colombier vtSleep(b->ioready); 523*5e96a66cSDavid du Colombier break; 524*5e96a66cSDavid du Colombier case BioClean: 525*5e96a66cSDavid du Colombier case BioDirty: 526*5e96a66cSDavid du Colombier return b; 527*5e96a66cSDavid du Colombier case BioReading: 528*5e96a66cSDavid du Colombier case BioWriting: 529*5e96a66cSDavid du Colombier vtSleep(b->ioready); 530*5e96a66cSDavid du Colombier break; 531*5e96a66cSDavid du Colombier case BioReadError: 532*5e96a66cSDavid du Colombier blockSetIOState(b, BioEmpty); 533*5e96a66cSDavid du Colombier blockPut(b); 534*5e96a66cSDavid du Colombier vtSetError(EIO); 535*5e96a66cSDavid du Colombier return nil; 536*5e96a66cSDavid du Colombier } 537*5e96a66cSDavid du Colombier } 538*5e96a66cSDavid du Colombier /* NOT REACHED */ 539*5e96a66cSDavid du Colombier } 540*5e96a66cSDavid du Colombier 541*5e96a66cSDavid du Colombier Block * 542*5e96a66cSDavid du Colombier cacheLocal(Cache *c, int part, u32int addr, int mode) 543*5e96a66cSDavid du Colombier { 544*5e96a66cSDavid du Colombier return _cacheLocal(c, part, addr, mode, 0); 545*5e96a66cSDavid du Colombier } 546*5e96a66cSDavid du Colombier 547*5e96a66cSDavid du Colombier /* 548*5e96a66cSDavid du Colombier * fetch a local (on-disk) block from the memory cache. 549*5e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block. 550*5e96a66cSDavid du Colombier * check tag and type. 551*5e96a66cSDavid du Colombier */ 552*5e96a66cSDavid du Colombier Block * 553*5e96a66cSDavid du Colombier cacheLocalData(Cache *c, u32int addr, int type, u32int tag, int mode, u32int epoch) 554*5e96a66cSDavid du Colombier { 555*5e96a66cSDavid du Colombier Block *b; 556*5e96a66cSDavid du Colombier 557*5e96a66cSDavid du Colombier b = _cacheLocal(c, PartData, addr, mode, epoch); 558*5e96a66cSDavid du Colombier if(b == nil) 559*5e96a66cSDavid du Colombier return nil; 560*5e96a66cSDavid du Colombier if(b->l.type != type || b->l.tag != tag){ 561*5e96a66cSDavid du Colombier fprint(2, "cacheLocalData: addr=%d type got %d exp %d: tag got %x exp %x\n", 562*5e96a66cSDavid du Colombier addr, b->l.type, type, b->l.tag, tag); 563*5e96a66cSDavid du Colombier abort(); 564*5e96a66cSDavid du Colombier vtSetError(ELabelMismatch); 565*5e96a66cSDavid du Colombier blockPut(b); 566*5e96a66cSDavid du Colombier return nil; 567*5e96a66cSDavid du Colombier } 568*5e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 569*5e96a66cSDavid du Colombier return b; 570*5e96a66cSDavid du Colombier } 571*5e96a66cSDavid du Colombier 572*5e96a66cSDavid du Colombier /* 573*5e96a66cSDavid du Colombier * fetch a global (Venti) block from the memory cache. 574*5e96a66cSDavid du Colombier * if it's not there, load it, bumping some other block. 575*5e96a66cSDavid du Colombier * check tag and type if it's really a local block in disguise. 576*5e96a66cSDavid du Colombier */ 577*5e96a66cSDavid du Colombier Block * 578*5e96a66cSDavid du Colombier cacheGlobal(Cache *c, uchar score[VtScoreSize], int type, u32int tag, int mode) 579*5e96a66cSDavid du Colombier { 580*5e96a66cSDavid du Colombier int n; 581*5e96a66cSDavid du Colombier Block *b; 582*5e96a66cSDavid du Colombier ulong h; 583*5e96a66cSDavid du Colombier u32int addr; 584*5e96a66cSDavid du Colombier 585*5e96a66cSDavid du Colombier addr = globalToLocal(score); 586*5e96a66cSDavid du Colombier if(addr != NilBlock){ 587*5e96a66cSDavid du Colombier b = cacheLocalData(c, addr, type, tag, mode, 0); 588*5e96a66cSDavid du Colombier if(b) 589*5e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 590*5e96a66cSDavid du Colombier return b; 591*5e96a66cSDavid du Colombier } 592*5e96a66cSDavid du Colombier 593*5e96a66cSDavid du Colombier h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->hashSize; 594*5e96a66cSDavid du Colombier 595*5e96a66cSDavid du Colombier /* 596*5e96a66cSDavid du Colombier * look for the block in the cache 597*5e96a66cSDavid du Colombier */ 598*5e96a66cSDavid du Colombier vtLock(c->lk); 599*5e96a66cSDavid du Colombier for(b = c->heads[h]; b != nil; b = b->next){ 600*5e96a66cSDavid du Colombier if(b->part != PartVenti || memcmp(b->score, score, VtScoreSize) != 0 || b->l.type != type) 601*5e96a66cSDavid du Colombier continue; 602*5e96a66cSDavid du Colombier heapDel(b); 603*5e96a66cSDavid du Colombier b->ref++; 604*5e96a66cSDavid du Colombier break; 605*5e96a66cSDavid du Colombier } 606*5e96a66cSDavid du Colombier 607*5e96a66cSDavid du Colombier if(b == nil){ 608*5e96a66cSDavid du Colombier if(0)fprint(2, "cacheGlobal %V %d\n", score, type); 609*5e96a66cSDavid du Colombier 610*5e96a66cSDavid du Colombier b = cacheBumpBlock(c); 611*5e96a66cSDavid du Colombier 612*5e96a66cSDavid du Colombier b->part = PartVenti; 613*5e96a66cSDavid du Colombier b->addr = NilBlock; 614*5e96a66cSDavid du Colombier b->l.type = type; 615*5e96a66cSDavid du Colombier memmove(b->score, score, VtScoreSize); 616*5e96a66cSDavid du Colombier 617*5e96a66cSDavid du Colombier /* chain onto correct hash */ 618*5e96a66cSDavid du Colombier b->next = c->heads[h]; 619*5e96a66cSDavid du Colombier c->heads[h] = b; 620*5e96a66cSDavid du Colombier if(b->next != nil) 621*5e96a66cSDavid du Colombier b->next->prev = &b->next; 622*5e96a66cSDavid du Colombier b->prev = &c->heads[h]; 623*5e96a66cSDavid du Colombier } 624*5e96a66cSDavid du Colombier vtUnlock(c->lk); 625*5e96a66cSDavid du Colombier 626*5e96a66cSDavid du Colombier bwatchLock(b); 627*5e96a66cSDavid du Colombier vtLock(b->lk); 628*5e96a66cSDavid du Colombier b->nlock = 1; 629*5e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 630*5e96a66cSDavid du Colombier 631*5e96a66cSDavid du Colombier switch(b->iostate){ 632*5e96a66cSDavid du Colombier default: 633*5e96a66cSDavid du Colombier abort(); 634*5e96a66cSDavid du Colombier case BioEmpty: 635*5e96a66cSDavid du Colombier n = vtRead(c->z, score, vtType[type], b->data, c->size); 636*5e96a66cSDavid du Colombier if(n < 0 || !vtSha1Check(score, b->data, n)){ 637*5e96a66cSDavid du Colombier blockSetIOState(b, BioVentiError); 638*5e96a66cSDavid du Colombier blockPut(b); 639*5e96a66cSDavid du Colombier return nil; 640*5e96a66cSDavid du Colombier } 641*5e96a66cSDavid du Colombier vtZeroExtend(vtType[type], b->data, n, c->size); 642*5e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 643*5e96a66cSDavid du Colombier return b; 644*5e96a66cSDavid du Colombier case BioClean: 645*5e96a66cSDavid du Colombier return b; 646*5e96a66cSDavid du Colombier case BioVentiError: 647*5e96a66cSDavid du Colombier case BioReadError: 648*5e96a66cSDavid du Colombier blockPut(b); 649*5e96a66cSDavid du Colombier vtSetError(EIO); 650*5e96a66cSDavid du Colombier blockSetIOState(b, BioEmpty); 651*5e96a66cSDavid du Colombier return nil; 652*5e96a66cSDavid du Colombier } 653*5e96a66cSDavid du Colombier /* NOT REACHED */ 654*5e96a66cSDavid du Colombier } 655*5e96a66cSDavid du Colombier 656*5e96a66cSDavid du Colombier /* 657*5e96a66cSDavid du Colombier * allocate a new on-disk block and load it into the memory cache. 658*5e96a66cSDavid du Colombier * BUG: if the disk is full, should we flush some of it to Venti? 659*5e96a66cSDavid du Colombier */ 660*5e96a66cSDavid du Colombier static u32int lastAlloc; 661*5e96a66cSDavid du Colombier 662*5e96a66cSDavid du Colombier Block * 663*5e96a66cSDavid du Colombier cacheAllocBlock(Cache *c, int type, u32int tag, u32int epoch, u32int epochLow) 664*5e96a66cSDavid du Colombier { 665*5e96a66cSDavid du Colombier FreeList *fl; 666*5e96a66cSDavid du Colombier u32int addr; 667*5e96a66cSDavid du Colombier Block *b; 668*5e96a66cSDavid du Colombier int n, nwrap; 669*5e96a66cSDavid du Colombier Label lab; 670*5e96a66cSDavid du Colombier 671*5e96a66cSDavid du Colombier n = c->size / LabelSize; 672*5e96a66cSDavid du Colombier fl = c->fl; 673*5e96a66cSDavid du Colombier 674*5e96a66cSDavid du Colombier vtLock(fl->lk); 675*5e96a66cSDavid du Colombier fl->last = 0; 676*5e96a66cSDavid du Colombier addr = fl->last; 677*5e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly); 678*5e96a66cSDavid du Colombier if(b == nil){ 679*5e96a66cSDavid du Colombier fprint(2, "cacheAllocBlock: xxx %R\n"); 680*5e96a66cSDavid du Colombier vtUnlock(fl->lk); 681*5e96a66cSDavid du Colombier return nil; 682*5e96a66cSDavid du Colombier } 683*5e96a66cSDavid du Colombier nwrap = 0; 684*5e96a66cSDavid du Colombier for(;;){ 685*5e96a66cSDavid du Colombier if(++addr >= fl->end){ 686*5e96a66cSDavid du Colombier addr = 0; 687*5e96a66cSDavid du Colombier fprint(2, "cacheAllocBlock wrap %d\n", fl->end); 688*5e96a66cSDavid du Colombier if(++nwrap >= 2){ 689*5e96a66cSDavid du Colombier blockPut(b); 690*5e96a66cSDavid du Colombier fl->last = 0; 691*5e96a66cSDavid du Colombier vtSetError("disk is full"); 692*5e96a66cSDavid du Colombier fprint(2, "cacheAllocBlock: xxx1 %R\n"); 693*5e96a66cSDavid du Colombier vtUnlock(fl->lk); 694*5e96a66cSDavid du Colombier return nil; 695*5e96a66cSDavid du Colombier } 696*5e96a66cSDavid du Colombier } 697*5e96a66cSDavid du Colombier if(addr%n == 0){ 698*5e96a66cSDavid du Colombier blockPut(b); 699*5e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, addr/n, OReadOnly); 700*5e96a66cSDavid du Colombier if(b == nil){ 701*5e96a66cSDavid du Colombier fl->last = addr; 702*5e96a66cSDavid du Colombier fprint(2, "cacheAllocBlock: xxx2 %R\n"); 703*5e96a66cSDavid du Colombier vtUnlock(fl->lk); 704*5e96a66cSDavid du Colombier return nil; 705*5e96a66cSDavid du Colombier } 706*5e96a66cSDavid du Colombier } 707*5e96a66cSDavid du Colombier if(!labelUnpack(&lab, b->data, addr%n)) 708*5e96a66cSDavid du Colombier continue; 709*5e96a66cSDavid du Colombier if(lab.state == BsFree) 710*5e96a66cSDavid du Colombier goto Found; 711*5e96a66cSDavid du Colombier if((lab.state&BsClosed) && lab.epochClose <= epochLow) 712*5e96a66cSDavid du Colombier goto Found; 713*5e96a66cSDavid du Colombier } 714*5e96a66cSDavid du Colombier Found: 715*5e96a66cSDavid du Colombier blockPut(b); 716*5e96a66cSDavid du Colombier b = cacheLocal(c, PartData, addr, OOverWrite); 717*5e96a66cSDavid du Colombier if(b == nil){ 718*5e96a66cSDavid du Colombier fprint(2, "cacheAllocBlock: xxx3 %R\n"); 719*5e96a66cSDavid du Colombier return nil; 720*5e96a66cSDavid du Colombier } 721*5e96a66cSDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean); 722*5e96a66cSDavid du Colombier fl->last = addr; 723*5e96a66cSDavid du Colombier lab.type = type; 724*5e96a66cSDavid du Colombier lab.tag = tag; 725*5e96a66cSDavid du Colombier lab.state = BsAlloc; 726*5e96a66cSDavid du Colombier lab.epoch = epoch; 727*5e96a66cSDavid du Colombier lab.epochClose = ~(u32int)0; 728*5e96a66cSDavid du Colombier if(!blockSetLabel(b, &lab)){ 729*5e96a66cSDavid du Colombier fprint(2, "cacheAllocBlock: xxx4 %R\n"); 730*5e96a66cSDavid du Colombier blockPut(b); 731*5e96a66cSDavid du Colombier return nil; 732*5e96a66cSDavid du Colombier } 733*5e96a66cSDavid du Colombier vtZeroExtend(vtType[type], b->data, 0, c->size); 734*5e96a66cSDavid du Colombier if(0)diskWrite(c->disk, b); 735*5e96a66cSDavid du Colombier 736*5e96a66cSDavid du Colombier if(0)fprint(2, "fsAlloc %ud type=%d tag = %ux\n", addr, type, tag); 737*5e96a66cSDavid du Colombier lastAlloc = addr; 738*5e96a66cSDavid du Colombier vtUnlock(fl->lk); 739*5e96a66cSDavid du Colombier b->pc = getcallerpc(&c); 740*5e96a66cSDavid du Colombier return b; 741*5e96a66cSDavid du Colombier } 742*5e96a66cSDavid du Colombier 743*5e96a66cSDavid du Colombier static FreeList * 744*5e96a66cSDavid du Colombier flAlloc(u32int end) 745*5e96a66cSDavid du Colombier { 746*5e96a66cSDavid du Colombier FreeList *fl; 747*5e96a66cSDavid du Colombier 748*5e96a66cSDavid du Colombier fl = vtMemAllocZ(sizeof(*fl)); 749*5e96a66cSDavid du Colombier fl->lk = vtLockAlloc(); 750*5e96a66cSDavid du Colombier fl->last = end; 751*5e96a66cSDavid du Colombier fl->end = end; 752*5e96a66cSDavid du Colombier return fl; 753*5e96a66cSDavid du Colombier } 754*5e96a66cSDavid du Colombier 755*5e96a66cSDavid du Colombier static void 756*5e96a66cSDavid du Colombier flFree(FreeList *fl) 757*5e96a66cSDavid du Colombier { 758*5e96a66cSDavid du Colombier vtLockFree(fl->lk); 759*5e96a66cSDavid du Colombier vtMemFree(fl); 760*5e96a66cSDavid du Colombier } 761*5e96a66cSDavid du Colombier 762*5e96a66cSDavid du Colombier u32int 763*5e96a66cSDavid du Colombier cacheLocalSize(Cache *c, int part) 764*5e96a66cSDavid du Colombier { 765*5e96a66cSDavid du Colombier return diskSize(c->disk, part); 766*5e96a66cSDavid du Colombier } 767*5e96a66cSDavid du Colombier 768*5e96a66cSDavid du Colombier /* 769*5e96a66cSDavid du Colombier * Copy on write. Copied blocks have to be marked BaCopy. 770*5e96a66cSDavid du Colombier * See the big comment near blockRemoveLink. 771*5e96a66cSDavid du Colombier */ 772*5e96a66cSDavid du Colombier Block* 773*5e96a66cSDavid du Colombier blockCopy(Block *b, u32int tag, u32int ehi, u32int elo) 774*5e96a66cSDavid du Colombier { 775*5e96a66cSDavid du Colombier Block *bb, *lb; 776*5e96a66cSDavid du Colombier Label l; 777*5e96a66cSDavid du Colombier 778*5e96a66cSDavid du Colombier assert((b->l.state&BsClosed)==0 && b->l.epoch < ehi); 779*5e96a66cSDavid du Colombier bb = cacheAllocBlock(b->c, b->l.type, tag, ehi, elo); 780*5e96a66cSDavid du Colombier if(bb == nil){ 781*5e96a66cSDavid du Colombier blockPut(b); 782*5e96a66cSDavid du Colombier return nil; 783*5e96a66cSDavid du Colombier } 784*5e96a66cSDavid du Colombier 785*5e96a66cSDavid du Colombier /* 786*5e96a66cSDavid du Colombier * Change label on b to mark that we've copied it. 787*5e96a66cSDavid du Colombier * This has to come after cacheAllocBlock, since we 788*5e96a66cSDavid du Colombier * can't hold any labels blocks (lb) while we try to 789*5e96a66cSDavid du Colombier * fetch others (in cacheAllocBlock). 790*5e96a66cSDavid du Colombier */ 791*5e96a66cSDavid du Colombier if(!(b->l.state&BsCopied) && b->part==PartData){ 792*5e96a66cSDavid du Colombier l = b->l; 793*5e96a66cSDavid du Colombier l.state |= BsCopied; 794*5e96a66cSDavid du Colombier lb = _blockSetLabel(b, &l); 795*5e96a66cSDavid du Colombier if(lb == nil){ 796*5e96a66cSDavid du Colombier /* can't set label => can't copy block */ 797*5e96a66cSDavid du Colombier blockPut(b); 798*5e96a66cSDavid du Colombier l.type = BtMax; 799*5e96a66cSDavid du Colombier l.state = BsFree; 800*5e96a66cSDavid du Colombier l.epoch = 0; 801*5e96a66cSDavid du Colombier l.epochClose = 0; 802*5e96a66cSDavid du Colombier l.tag = 0; 803*5e96a66cSDavid du Colombier /* ignore error: block gets lost on error */ 804*5e96a66cSDavid du Colombier blockSetLabel(bb, &l); 805*5e96a66cSDavid du Colombier blockPut(bb); 806*5e96a66cSDavid du Colombier return nil; 807*5e96a66cSDavid du Colombier } 808*5e96a66cSDavid du Colombier blockDependency(bb, lb, -1, nil); 809*5e96a66cSDavid du Colombier blockPut(lb); 810*5e96a66cSDavid du Colombier } 811*5e96a66cSDavid du Colombier 812*5e96a66cSDavid du Colombier if(0){ 813*5e96a66cSDavid du Colombier if(b->addr != NilBlock) 814*5e96a66cSDavid du Colombier fprint(2, "blockCopy %#ux/%ud => %#ux/%ud\n", 815*5e96a66cSDavid du Colombier b->addr, b->l.epoch, bb->addr, bb->l.epoch); 816*5e96a66cSDavid du Colombier else if(memcmp(b->score, vtZeroScore, VtScoreSize) != 0) 817*5e96a66cSDavid du Colombier fprint(2, "blockCopy %V => %#ux/%ud\n", 818*5e96a66cSDavid du Colombier b->score, bb->addr, bb->l.epoch); 819*5e96a66cSDavid du Colombier } 820*5e96a66cSDavid du Colombier 821*5e96a66cSDavid du Colombier memmove(bb->data, b->data, b->c->size); 822*5e96a66cSDavid du Colombier blockDirty(bb); 823*5e96a66cSDavid du Colombier blockPut(b); 824*5e96a66cSDavid du Colombier return bb; 825*5e96a66cSDavid du Colombier } 826*5e96a66cSDavid du Colombier 827*5e96a66cSDavid du Colombier /* 828*5e96a66cSDavid du Colombier * The thread that has locked b may refer to it by 829*5e96a66cSDavid du Colombier * multiple names. Nlock counts the number of 830*5e96a66cSDavid du Colombier * references the locking thread holds. It will call 831*5e96a66cSDavid du Colombier * blockPut once per reference. 832*5e96a66cSDavid du Colombier */ 833*5e96a66cSDavid du Colombier void 834*5e96a66cSDavid du Colombier blockDupLock(Block *b) 835*5e96a66cSDavid du Colombier { 836*5e96a66cSDavid du Colombier assert(b->nlock > 0); 837*5e96a66cSDavid du Colombier b->nlock++; 838*5e96a66cSDavid du Colombier } 839*5e96a66cSDavid du Colombier 840*5e96a66cSDavid du Colombier /* 841*5e96a66cSDavid du Colombier * we're done with the block. 842*5e96a66cSDavid du Colombier * unlock it. can't use it after calling this. 843*5e96a66cSDavid du Colombier */ 844*5e96a66cSDavid du Colombier void 845*5e96a66cSDavid du Colombier blockPut(Block* b) 846*5e96a66cSDavid du Colombier { 847*5e96a66cSDavid du Colombier Cache *c; 848*5e96a66cSDavid du Colombier 849*5e96a66cSDavid du Colombier if(b == nil) 850*5e96a66cSDavid du Colombier return; 851*5e96a66cSDavid du Colombier 852*5e96a66cSDavid du Colombier if(0)fprint(2, "blockPut: %d: %d %x %d %s\n", getpid(), b->part, b->addr, c->nheap, bioStr(b->iostate)); 853*5e96a66cSDavid du Colombier 854*5e96a66cSDavid du Colombier if(b->iostate == BioDirty) 855*5e96a66cSDavid du Colombier bwatchDependency(b); 856*5e96a66cSDavid du Colombier 857*5e96a66cSDavid du Colombier if(--b->nlock > 0) 858*5e96a66cSDavid du Colombier return; 859*5e96a66cSDavid du Colombier 860*5e96a66cSDavid du Colombier /* 861*5e96a66cSDavid du Colombier * b->nlock should probably stay at zero while 862*5e96a66cSDavid du Colombier * the block is unlocked, but diskThread and vtSleep 863*5e96a66cSDavid du Colombier * conspire to assume that they can just vtLock(b->lk); blockPut(b), 864*5e96a66cSDavid du Colombier * so we have to keep b->nlock set to 1 even 865*5e96a66cSDavid du Colombier * when the block is unlocked. 866*5e96a66cSDavid du Colombier */ 867*5e96a66cSDavid du Colombier assert(b->nlock == 0); 868*5e96a66cSDavid du Colombier b->nlock = 1; 869*5e96a66cSDavid du Colombier // b->pc = 0; 870*5e96a66cSDavid du Colombier 871*5e96a66cSDavid du Colombier bwatchUnlock(b); 872*5e96a66cSDavid du Colombier vtUnlock(b->lk); 873*5e96a66cSDavid du Colombier c = b->c; 874*5e96a66cSDavid du Colombier vtLock(c->lk); 875*5e96a66cSDavid du Colombier 876*5e96a66cSDavid du Colombier if(--b->ref > 0){ 877*5e96a66cSDavid du Colombier vtUnlock(c->lk); 878*5e96a66cSDavid du Colombier return; 879*5e96a66cSDavid du Colombier } 880*5e96a66cSDavid du Colombier 881*5e96a66cSDavid du Colombier assert(b->ref == 0); 882*5e96a66cSDavid du Colombier switch(b->iostate){ 883*5e96a66cSDavid du Colombier default: 884*5e96a66cSDavid du Colombier b->used = c->now++; 885*5e96a66cSDavid du Colombier heapIns(b); 886*5e96a66cSDavid du Colombier break; 887*5e96a66cSDavid du Colombier case BioEmpty: 888*5e96a66cSDavid du Colombier case BioLabel: 889*5e96a66cSDavid du Colombier if(c->nheap == 0) 890*5e96a66cSDavid du Colombier b->used = c->now++; 891*5e96a66cSDavid du Colombier else 892*5e96a66cSDavid du Colombier b->used = c->heap[0]->used; 893*5e96a66cSDavid du Colombier heapIns(b); 894*5e96a66cSDavid du Colombier break; 895*5e96a66cSDavid du Colombier case BioDirty: 896*5e96a66cSDavid du Colombier break; 897*5e96a66cSDavid du Colombier } 898*5e96a66cSDavid du Colombier vtUnlock(c->lk); 899*5e96a66cSDavid du Colombier } 900*5e96a66cSDavid du Colombier 901*5e96a66cSDavid du Colombier /* 902*5e96a66cSDavid du Colombier * we're deleting a block; delete all the blocks it points to 903*5e96a66cSDavid du Colombier * that are still active (i.e., not needed by snapshots). 904*5e96a66cSDavid du Colombier */ 905*5e96a66cSDavid du Colombier static void 906*5e96a66cSDavid du Colombier blockCleanup(Block *b, u32int epoch) 907*5e96a66cSDavid du Colombier { 908*5e96a66cSDavid du Colombier Cache *c; 909*5e96a66cSDavid du Colombier Block *bb; 910*5e96a66cSDavid du Colombier int i, n; 911*5e96a66cSDavid du Colombier Label l; 912*5e96a66cSDavid du Colombier u32int a; 913*5e96a66cSDavid du Colombier int type; 914*5e96a66cSDavid du Colombier int mode; 915*5e96a66cSDavid du Colombier 916*5e96a66cSDavid du Colombier type = b->l.type; 917*5e96a66cSDavid du Colombier c = b->c; 918*5e96a66cSDavid du Colombier 919*5e96a66cSDavid du Colombier bwatchReset(b->score); 920*5e96a66cSDavid du Colombier 921*5e96a66cSDavid du Colombier blockSetIOState(b, BioClean); 922*5e96a66cSDavid du Colombier 923*5e96a66cSDavid du Colombier /* do not recursively free directories */ 924*5e96a66cSDavid du Colombier if(type == BtData || type == BtDir) 925*5e96a66cSDavid du Colombier return; 926*5e96a66cSDavid du Colombier 927*5e96a66cSDavid du Colombier n = c->size / VtScoreSize; 928*5e96a66cSDavid du Colombier mode = OReadWrite; 929*5e96a66cSDavid du Colombier if(type-1 == BtData || type-1 == BtDir) 930*5e96a66cSDavid du Colombier mode = OOverWrite; 931*5e96a66cSDavid du Colombier for(i=0; i<n; i++){ 932*5e96a66cSDavid du Colombier a = globalToLocal(b->data + i*VtScoreSize); 933*5e96a66cSDavid du Colombier if(a == NilBlock || !readLabel(c, &l, a)) 934*5e96a66cSDavid du Colombier continue; 935*5e96a66cSDavid du Colombier if((l.state&BsClosed) || l.epoch != epoch) 936*5e96a66cSDavid du Colombier continue; 937*5e96a66cSDavid du Colombier bb = cacheLocalData(c, a, type-1, b->l.tag, mode, 0); 938*5e96a66cSDavid du Colombier if(bb == nil) 939*5e96a66cSDavid du Colombier continue; 940*5e96a66cSDavid du Colombier if((bb->l.state&BsClosed) || bb->l.epoch != epoch){ 941*5e96a66cSDavid du Colombier fprint(2, "cleanupBlock: block %ud changed underfoot! expected %L got %L\n", 942*5e96a66cSDavid du Colombier a, &l, &bb->l); 943*5e96a66cSDavid du Colombier blockPut(bb); 944*5e96a66cSDavid du Colombier continue; 945*5e96a66cSDavid du Colombier } 946*5e96a66cSDavid du Colombier blockCleanup(bb, epoch); 947*5e96a66cSDavid du Colombier l.type = BtMax; 948*5e96a66cSDavid du Colombier l.epoch = epoch; 949*5e96a66cSDavid du Colombier l.epochClose = 0; 950*5e96a66cSDavid du Colombier l.state = BsFree; 951*5e96a66cSDavid du Colombier l.tag = 0; 952*5e96a66cSDavid du Colombier blockSetLabel(bb, &l); 953*5e96a66cSDavid du Colombier blockPut(bb); 954*5e96a66cSDavid du Colombier } 955*5e96a66cSDavid du Colombier } 956*5e96a66cSDavid du Colombier 957*5e96a66cSDavid du Colombier /* 958*5e96a66cSDavid du Colombier * We don't need the block at addr anymore for the active file system. 959*5e96a66cSDavid du Colombier * If we don't need it for the snapshots, remove it completely. 960*5e96a66cSDavid du Colombier * Epoch is the epoch during which we got rid of the block. 961*5e96a66cSDavid du Colombier * See blockRemoveLink for more. 962*5e96a66cSDavid du Colombier */ 963*5e96a66cSDavid du Colombier static int 964*5e96a66cSDavid du Colombier unlinkBlock(Cache *c, u32int addr, int type, u32int tag, u32int epoch) 965*5e96a66cSDavid du Colombier { 966*5e96a66cSDavid du Colombier Block *b; 967*5e96a66cSDavid du Colombier Label l; 968*5e96a66cSDavid du Colombier 969*5e96a66cSDavid du Colombier if(addr == NilBlock) 970*5e96a66cSDavid du Colombier return 1; 971*5e96a66cSDavid du Colombier 972*5e96a66cSDavid du Colombier //fprint(2, "unlinkBlock %#ux\n", addr); 973*5e96a66cSDavid du Colombier b = cacheLocalData(c, addr, type, tag, OReadOnly, 0); 974*5e96a66cSDavid du Colombier if(b == nil) 975*5e96a66cSDavid du Colombier return 0; 976*5e96a66cSDavid du Colombier if(b->l.epoch > epoch){ 977*5e96a66cSDavid du Colombier fprint(2, "unlinkBlock: strange epoch :%ud %ud\n", b->l.epoch, epoch); 978*5e96a66cSDavid du Colombier blockPut(b); 979*5e96a66cSDavid du Colombier return 0; 980*5e96a66cSDavid du Colombier } 981*5e96a66cSDavid du Colombier 982*5e96a66cSDavid du Colombier l = b->l; 983*5e96a66cSDavid du Colombier if((b->l.state&BsClosed)==0 && b->l.epoch==epoch){ 984*5e96a66cSDavid du Colombier l.state = BsFree; 985*5e96a66cSDavid du Colombier l.type = BtMax; 986*5e96a66cSDavid du Colombier l.tag = 0; 987*5e96a66cSDavid du Colombier l.epoch = 0; 988*5e96a66cSDavid du Colombier l.epochClose = 0; 989*5e96a66cSDavid du Colombier blockCleanup(b, epoch); 990*5e96a66cSDavid du Colombier }else{ 991*5e96a66cSDavid du Colombier l.state |= BsClosed; 992*5e96a66cSDavid du Colombier l.epochClose = epoch; 993*5e96a66cSDavid du Colombier } 994*5e96a66cSDavid du Colombier blockSetLabel(b, &l); 995*5e96a66cSDavid du Colombier blockPut(b); 996*5e96a66cSDavid du Colombier return 1; 997*5e96a66cSDavid du Colombier } 998*5e96a66cSDavid du Colombier 999*5e96a66cSDavid du Colombier /* 1000*5e96a66cSDavid du Colombier * try to allocate a BList so we can record that b must 1001*5e96a66cSDavid du Colombier * be written out before some other block. 1002*5e96a66cSDavid du Colombier * if can't find a BList, write b out instead and return nil. 1003*5e96a66cSDavid du Colombier */ 1004*5e96a66cSDavid du Colombier static BList * 1005*5e96a66cSDavid du Colombier blistAlloc(Block *b) 1006*5e96a66cSDavid du Colombier { 1007*5e96a66cSDavid du Colombier Cache *c; 1008*5e96a66cSDavid du Colombier BList *p; 1009*5e96a66cSDavid du Colombier 1010*5e96a66cSDavid du Colombier /* 1011*5e96a66cSDavid du Colombier * It's possible that when we marked b dirty, there were 1012*5e96a66cSDavid du Colombier * too many dirty blocks so we just wrote b there and then. 1013*5e96a66cSDavid du Colombier * So b might not be dirty. If it's not, no need to worry 1014*5e96a66cSDavid du Colombier * about recording the write constraint. 1015*5e96a66cSDavid du Colombier * 1016*5e96a66cSDavid du Colombier * BlockRemoveLink depends on the fact that if blistAlloc 1017*5e96a66cSDavid du Colombier * returns non-nil, b really is dirty. 1018*5e96a66cSDavid du Colombier */ 1019*5e96a66cSDavid du Colombier if(b->iostate != BioDirty){ 1020*5e96a66cSDavid du Colombier assert(b->iostate == BioClean); 1021*5e96a66cSDavid du Colombier return nil; 1022*5e96a66cSDavid du Colombier } 1023*5e96a66cSDavid du Colombier 1024*5e96a66cSDavid du Colombier /* 1025*5e96a66cSDavid du Colombier * Easy: maybe there's a free list left. 1026*5e96a66cSDavid du Colombier */ 1027*5e96a66cSDavid du Colombier c = b->c; 1028*5e96a66cSDavid du Colombier vtLock(c->lk); 1029*5e96a66cSDavid du Colombier if(c->blfree){ 1030*5e96a66cSDavid du Colombier HaveBlFree: 1031*5e96a66cSDavid du Colombier p = c->blfree; 1032*5e96a66cSDavid du Colombier c->blfree = p->next; 1033*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1034*5e96a66cSDavid du Colombier return p; 1035*5e96a66cSDavid du Colombier } 1036*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1037*5e96a66cSDavid du Colombier 1038*5e96a66cSDavid du Colombier /* 1039*5e96a66cSDavid du Colombier * No free BLists. What are our options? 1040*5e96a66cSDavid du Colombier */ 1041*5e96a66cSDavid du Colombier 1042*5e96a66cSDavid du Colombier /* Block has no priors? Just write it. */ 1043*5e96a66cSDavid du Colombier if(b->prior == nil){ 1044*5e96a66cSDavid du Colombier diskWrite(c->disk, b); 1045*5e96a66cSDavid du Colombier while(b->iostate != BioClean) 1046*5e96a66cSDavid du Colombier vtSleep(b->ioready); 1047*5e96a66cSDavid du Colombier return nil; 1048*5e96a66cSDavid du Colombier } 1049*5e96a66cSDavid du Colombier 1050*5e96a66cSDavid du Colombier /* 1051*5e96a66cSDavid du Colombier * Wake the flush thread, which will hopefully free up 1052*5e96a66cSDavid du Colombier * some BLists for us. We used to flush a block from 1053*5e96a66cSDavid du Colombier * our own prior list and reclaim that BList, but this is 1054*5e96a66cSDavid du Colombier * a no-no: some of the blocks on our prior list may 1055*5e96a66cSDavid du Colombier * be locked by our caller. Or maybe their label blocks 1056*5e96a66cSDavid du Colombier * are locked by our caller. In any event, it's too hard 1057*5e96a66cSDavid du Colombier * to make sure we can do I/O for ourselves. Instead, 1058*5e96a66cSDavid du Colombier * we assume the flush thread will find something. 1059*5e96a66cSDavid du Colombier * (The flush thread never blocks waiting for a block, 1060*5e96a66cSDavid du Colombier * so it won't deadlock like we will.) 1061*5e96a66cSDavid du Colombier */ 1062*5e96a66cSDavid du Colombier vtLock(c->lk); 1063*5e96a66cSDavid du Colombier while(c->blfree == nil){ 1064*5e96a66cSDavid du Colombier vtWakeup(c->flush); 1065*5e96a66cSDavid du Colombier vtSleep(c->blrend); 1066*5e96a66cSDavid du Colombier } 1067*5e96a66cSDavid du Colombier goto HaveBlFree; 1068*5e96a66cSDavid du Colombier } 1069*5e96a66cSDavid du Colombier 1070*5e96a66cSDavid du Colombier void 1071*5e96a66cSDavid du Colombier blistFree(Cache *c, BList *bl) 1072*5e96a66cSDavid du Colombier { 1073*5e96a66cSDavid du Colombier vtLock(c->lk); 1074*5e96a66cSDavid du Colombier bl->next = c->blfree; 1075*5e96a66cSDavid du Colombier c->blfree = bl; 1076*5e96a66cSDavid du Colombier vtWakeup(c->blrend); 1077*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1078*5e96a66cSDavid du Colombier } 1079*5e96a66cSDavid du Colombier 1080*5e96a66cSDavid du Colombier /* 1081*5e96a66cSDavid du Colombier * Flush b or one of the blocks it depends on. 1082*5e96a66cSDavid du Colombier */ 1083*5e96a66cSDavid du Colombier void 1084*5e96a66cSDavid du Colombier blockFlush(Block *b) 1085*5e96a66cSDavid du Colombier { 1086*5e96a66cSDavid du Colombier int first, nlock; 1087*5e96a66cSDavid du Colombier BList *p, **pp; 1088*5e96a66cSDavid du Colombier Block *bb; 1089*5e96a66cSDavid du Colombier Cache *c; 1090*5e96a66cSDavid du Colombier 1091*5e96a66cSDavid du Colombier //fprint(2, "blockFlush %p\n", b); 1092*5e96a66cSDavid du Colombier 1093*5e96a66cSDavid du Colombier c = b->c; 1094*5e96a66cSDavid du Colombier 1095*5e96a66cSDavid du Colombier first = 1; 1096*5e96a66cSDavid du Colombier pp = &b->prior; 1097*5e96a66cSDavid du Colombier for(p=*pp; p; p=*pp){ 1098*5e96a66cSDavid du Colombier bb = cacheLocalLookup(c, p->part, p->addr, p->vers); 1099*5e96a66cSDavid du Colombier if(bb == nil){ 1100*5e96a66cSDavid du Colombier *pp = p->next; 1101*5e96a66cSDavid du Colombier blistFree(c, p); 1102*5e96a66cSDavid du Colombier continue; 1103*5e96a66cSDavid du Colombier } 1104*5e96a66cSDavid du Colombier if(!first) 1105*5e96a66cSDavid du Colombier blockPut(b); 1106*5e96a66cSDavid du Colombier first = 0; 1107*5e96a66cSDavid du Colombier b = bb; 1108*5e96a66cSDavid du Colombier pp = &b->prior; 1109*5e96a66cSDavid du Colombier } 1110*5e96a66cSDavid du Colombier 1111*5e96a66cSDavid du Colombier /* 1112*5e96a66cSDavid du Colombier * If b->nlock > 1, the block is aliased within 1113*5e96a66cSDavid du Colombier * a single thread. That thread is us, and it's 1114*5e96a66cSDavid du Colombier * the block that was passed in (rather than a prior). 1115*5e96a66cSDavid du Colombier * DiskWrite does some funny stuff with VtLock 1116*5e96a66cSDavid du Colombier * and blockPut that basically assumes b->nlock==1. 1117*5e96a66cSDavid du Colombier * We humor diskWrite by temporarily setting 1118*5e96a66cSDavid du Colombier * nlock to 1. This needs to be revisited. (TODO) 1119*5e96a66cSDavid du Colombier */ 1120*5e96a66cSDavid du Colombier nlock = b->nlock; 1121*5e96a66cSDavid du Colombier if(nlock > 1){ 1122*5e96a66cSDavid du Colombier assert(first); 1123*5e96a66cSDavid du Colombier b->nlock = 1; 1124*5e96a66cSDavid du Colombier } 1125*5e96a66cSDavid du Colombier diskWrite(c->disk, b); 1126*5e96a66cSDavid du Colombier while(b->iostate != BioClean) 1127*5e96a66cSDavid du Colombier vtSleep(b->ioready); 1128*5e96a66cSDavid du Colombier b->nlock = nlock; 1129*5e96a66cSDavid du Colombier if(!first) 1130*5e96a66cSDavid du Colombier blockPut(b); 1131*5e96a66cSDavid du Colombier } 1132*5e96a66cSDavid du Colombier 1133*5e96a66cSDavid du Colombier /* 1134*5e96a66cSDavid du Colombier * record that bb must be written out before b. 1135*5e96a66cSDavid du Colombier * if index is given, we're about to overwrite the score 1136*5e96a66cSDavid du Colombier * at that index in the block. save the old value so we 1137*5e96a66cSDavid du Colombier * can write a safer ``old'' version of the block if pressed. 1138*5e96a66cSDavid du Colombier */ 1139*5e96a66cSDavid du Colombier void 1140*5e96a66cSDavid du Colombier blockDependency(Block *b, Block *bb, int index, uchar *score) 1141*5e96a66cSDavid du Colombier { 1142*5e96a66cSDavid du Colombier BList *p; 1143*5e96a66cSDavid du Colombier 1144*5e96a66cSDavid du Colombier if(bb->iostate == BioClean) 1145*5e96a66cSDavid du Colombier return; 1146*5e96a66cSDavid du Colombier 1147*5e96a66cSDavid du Colombier assert(bb->iostate == BioDirty); 1148*5e96a66cSDavid du Colombier 1149*5e96a66cSDavid du Colombier p = blistAlloc(bb); 1150*5e96a66cSDavid du Colombier if(p == nil) 1151*5e96a66cSDavid du Colombier return; 1152*5e96a66cSDavid du Colombier 1153*5e96a66cSDavid du Colombier if(0)fprint(2, "%d:%x:%d depends on %d:%x:%d\n", b->part, b->addr, b->l.type, bb->part, bb->addr, bb->l.type); 1154*5e96a66cSDavid du Colombier 1155*5e96a66cSDavid du Colombier p->part = bb->part; 1156*5e96a66cSDavid du Colombier p->addr = bb->addr; 1157*5e96a66cSDavid du Colombier p->type = bb->l.type; 1158*5e96a66cSDavid du Colombier p->vers = bb->vers; 1159*5e96a66cSDavid du Colombier p->index = index; 1160*5e96a66cSDavid du Colombier if(p->index >= 0) 1161*5e96a66cSDavid du Colombier memmove(p->score, score, VtScoreSize); 1162*5e96a66cSDavid du Colombier p->next = b->prior; 1163*5e96a66cSDavid du Colombier b->prior = p; 1164*5e96a66cSDavid du Colombier } 1165*5e96a66cSDavid du Colombier 1166*5e96a66cSDavid du Colombier /* 1167*5e96a66cSDavid du Colombier * Mark an in-memory block as dirty. If there are too many 1168*5e96a66cSDavid du Colombier * dirty blocks, start writing some out to disk. If there are way 1169*5e96a66cSDavid du Colombier * too many dirty blocks, write this one out too. 1170*5e96a66cSDavid du Colombier * 1171*5e96a66cSDavid du Colombier * Note that since we might call blockFlush, which walks 1172*5e96a66cSDavid du Colombier * the prior list, we can't call blockDirty while holding a lock 1173*5e96a66cSDavid du Colombier * on any of our priors. This can be tested by recompiling 1174*5e96a66cSDavid du Colombier * with flush always set to 1 below. 1175*5e96a66cSDavid du Colombier */ 1176*5e96a66cSDavid du Colombier int 1177*5e96a66cSDavid du Colombier blockDirty(Block *b) 1178*5e96a66cSDavid du Colombier { 1179*5e96a66cSDavid du Colombier Cache *c; 1180*5e96a66cSDavid du Colombier int flush; 1181*5e96a66cSDavid du Colombier 1182*5e96a66cSDavid du Colombier c = b->c; 1183*5e96a66cSDavid du Colombier 1184*5e96a66cSDavid du Colombier assert(b->part != PartVenti); 1185*5e96a66cSDavid du Colombier 1186*5e96a66cSDavid du Colombier if(b->iostate == BioDirty) 1187*5e96a66cSDavid du Colombier return 1; 1188*5e96a66cSDavid du Colombier assert(b->iostate == BioClean); 1189*5e96a66cSDavid du Colombier b->iostate = BioDirty; 1190*5e96a66cSDavid du Colombier 1191*5e96a66cSDavid du Colombier vtLock(c->lk); 1192*5e96a66cSDavid du Colombier c->ndirty++; 1193*5e96a66cSDavid du Colombier if(c->ndirty > (c->maxdirty>>1)) 1194*5e96a66cSDavid du Colombier vtWakeup(c->flush); 1195*5e96a66cSDavid du Colombier flush = c->ndirty > c->maxdirty; 1196*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1197*5e96a66cSDavid du Colombier 1198*5e96a66cSDavid du Colombier if(flush) 1199*5e96a66cSDavid du Colombier blockFlush(b); 1200*5e96a66cSDavid du Colombier 1201*5e96a66cSDavid du Colombier return 1; 1202*5e96a66cSDavid du Colombier } 1203*5e96a66cSDavid du Colombier 1204*5e96a66cSDavid du Colombier /* 1205*5e96a66cSDavid du Colombier * Block b once pointed at the block bb at addr/type/tag, but no longer does. 1206*5e96a66cSDavid du Colombier * 1207*5e96a66cSDavid du Colombier * The file system maintains the following invariants (i-iv checked by flchk): 1208*5e96a66cSDavid du Colombier * 1209*5e96a66cSDavid du Colombier * (i) b.e in [bb.e, bb.eClose) 1210*5e96a66cSDavid du Colombier * (ii) if b.e==bb.e, then no other b' in e points at bb. 1211*5e96a66cSDavid du Colombier * (iii) if !(b.state&Copied) and b.e==bb.e then no other b' points at bb. 1212*5e96a66cSDavid du Colombier * (iv) if b is active then no other active b' points at bb. 1213*5e96a66cSDavid du Colombier * (v) if b is a past life of b' then only one of b and b' is active (too hard to check) 1214*5e96a66cSDavid du Colombier * 1215*5e96a66cSDavid du Colombier * The file system initially satisfies these invariants, and we can verify that 1216*5e96a66cSDavid du Colombier * the various file system operations maintain them. See fossil.invariants. 1217*5e96a66cSDavid du Colombier * 1218*5e96a66cSDavid du Colombier * Condition (i) lets us reclaim blocks once the low epoch is greater 1219*5e96a66cSDavid du Colombier * than epochClose. 1220*5e96a66cSDavid du Colombier * 1221*5e96a66cSDavid du Colombier * If the condition in (iii) is satisfied, then this is the only pointer to bb, 1222*5e96a66cSDavid du Colombier * so bb can be reclaimed once b has been written to disk. blockRemoveLink 1223*5e96a66cSDavid du Colombier * checks !(b.state&Copied) as an optimization. UnlinkBlock and blockCleanup 1224*5e96a66cSDavid du Colombier * will check the conditions again for each block they consider. 1225*5e96a66cSDavid du Colombier * 1226*5e96a66cSDavid du Colombier * 12/28/2002 01:11 RSC BUG 1227*5e96a66cSDavid du Colombier * When Entry structures are changed, most code does (for example): 1228*5e96a66cSDavid du Colombier * 1229*5e96a66cSDavid du Colombier * oe = e; 1230*5e96a66cSDavid du Colombier * memset(&e, 0, sizeof e); 1231*5e96a66cSDavid du Colombier * entryPack(&e, b->data, index); 1232*5e96a66cSDavid du Colombier * blockDirty(b); 1233*5e96a66cSDavid du Colombier * blockDependency(b, block referenced by new e); 1234*5e96a66cSDavid du Colombier * addr = globalToLocal(oe.score); 1235*5e96a66cSDavid du Colombier * if(addr != NilBlock) 1236*5e96a66cSDavid du Colombier * blockRemoveLink(b, addr, entryType(&oe), oe.tag); 1237*5e96a66cSDavid du Colombier * 1238*5e96a66cSDavid du Colombier * This is wrong if there is already a different dependency for that entry 1239*5e96a66cSDavid du Colombier * and the entries have different types (different heights of the hash tree). 1240*5e96a66cSDavid du Colombier * Because the dependency only records the block address and not the 1241*5e96a66cSDavid du Colombier * entry type, putting the old block address into the new entry results in 1242*5e96a66cSDavid du Colombier * a bogus entry structure. blockRollback catches this in an assert failure. 1243*5e96a66cSDavid du Colombier * I think the solution is to record the entry type and tag in the BList structure, 1244*5e96a66cSDavid du Colombier * but I want to mull it over a bit longer. 1245*5e96a66cSDavid du Colombier * 1246*5e96a66cSDavid du Colombier * In two and a half months running the system I have seen exactly one 1247*5e96a66cSDavid du Colombier * crash due to this bug. 1248*5e96a66cSDavid du Colombier */ 1249*5e96a66cSDavid du Colombier int 1250*5e96a66cSDavid du Colombier blockRemoveLink(Block *b, u32int addr, int type, u32int tag) 1251*5e96a66cSDavid du Colombier { 1252*5e96a66cSDavid du Colombier BList *bl; 1253*5e96a66cSDavid du Colombier BList *p, **pp; 1254*5e96a66cSDavid du Colombier Cache *c; 1255*5e96a66cSDavid du Colombier 1256*5e96a66cSDavid du Colombier c = b->c; 1257*5e96a66cSDavid du Colombier 1258*5e96a66cSDavid du Colombier /* remove unlinked block from prior list */ 1259*5e96a66cSDavid du Colombier pp = &b->prior; 1260*5e96a66cSDavid du Colombier for(p=*pp; p; p=*pp){ 1261*5e96a66cSDavid du Colombier if(p->part != PartData || p->addr != addr){ 1262*5e96a66cSDavid du Colombier pp = &p->next; 1263*5e96a66cSDavid du Colombier continue; 1264*5e96a66cSDavid du Colombier } 1265*5e96a66cSDavid du Colombier *pp = p->next; 1266*5e96a66cSDavid du Colombier blistFree(c, p); 1267*5e96a66cSDavid du Colombier } 1268*5e96a66cSDavid du Colombier 1269*5e96a66cSDavid du Colombier /* if b has been copied, can't reclaim blocks it points at. */ 1270*5e96a66cSDavid du Colombier if(b->l.state & BsCopied) 1271*5e96a66cSDavid du Colombier return 0; 1272*5e96a66cSDavid du Colombier 1273*5e96a66cSDavid du Colombier bl = blistAlloc(b); 1274*5e96a66cSDavid du Colombier if(bl == nil) 1275*5e96a66cSDavid du Colombier return unlinkBlock(b->c, addr, type, tag, b->l.epoch); 1276*5e96a66cSDavid du Colombier 1277*5e96a66cSDavid du Colombier /* 1278*5e96a66cSDavid du Colombier * Because bl != nil, we know b is dirty. 1279*5e96a66cSDavid du Colombier * (Linking b->uhead onto a clean block is 1280*5e96a66cSDavid du Colombier * counterproductive, since we only look at 1281*5e96a66cSDavid du Colombier * b->uhead when a block transitions from 1282*5e96a66cSDavid du Colombier * dirty to clean.) 1283*5e96a66cSDavid du Colombier */ 1284*5e96a66cSDavid du Colombier assert(b->iostate == BioDirty); 1285*5e96a66cSDavid du Colombier 1286*5e96a66cSDavid du Colombier bl->part = PartData; 1287*5e96a66cSDavid du Colombier bl->addr = addr; 1288*5e96a66cSDavid du Colombier bl->type = type; 1289*5e96a66cSDavid du Colombier bl->tag = tag; 1290*5e96a66cSDavid du Colombier bl->epoch = b->l.epoch; 1291*5e96a66cSDavid du Colombier if(b->uhead == nil) 1292*5e96a66cSDavid du Colombier b->uhead = bl; 1293*5e96a66cSDavid du Colombier else 1294*5e96a66cSDavid du Colombier b->utail->next = bl; 1295*5e96a66cSDavid du Colombier b->utail = bl; 1296*5e96a66cSDavid du Colombier bl->next = nil; 1297*5e96a66cSDavid du Colombier return 1; 1298*5e96a66cSDavid du Colombier } 1299*5e96a66cSDavid du Colombier 1300*5e96a66cSDavid du Colombier /* 1301*5e96a66cSDavid du Colombier * set the label associated with a block. 1302*5e96a66cSDavid du Colombier */ 1303*5e96a66cSDavid du Colombier Block* 1304*5e96a66cSDavid du Colombier _blockSetLabel(Block *b, Label *l) 1305*5e96a66cSDavid du Colombier { 1306*5e96a66cSDavid du Colombier int lpb; 1307*5e96a66cSDavid du Colombier Block *bb; 1308*5e96a66cSDavid du Colombier u32int a; 1309*5e96a66cSDavid du Colombier Cache *c; 1310*5e96a66cSDavid du Colombier 1311*5e96a66cSDavid du Colombier c = b->c; 1312*5e96a66cSDavid du Colombier 1313*5e96a66cSDavid du Colombier assert(b->part == PartData); 1314*5e96a66cSDavid du Colombier assert(b->iostate == BioLabel || b->iostate == BioClean || b->iostate == BioDirty); 1315*5e96a66cSDavid du Colombier lpb = c->size / LabelSize; 1316*5e96a66cSDavid du Colombier a = b->addr / lpb; 1317*5e96a66cSDavid du Colombier bb = cacheLocal(c, PartLabel, a, OReadWrite); 1318*5e96a66cSDavid du Colombier if(bb == nil){ 1319*5e96a66cSDavid du Colombier blockPut(b); 1320*5e96a66cSDavid du Colombier return nil; 1321*5e96a66cSDavid du Colombier } 1322*5e96a66cSDavid du Colombier b->l = *l; 1323*5e96a66cSDavid du Colombier labelPack(l, bb->data, b->addr%lpb); 1324*5e96a66cSDavid du Colombier blockDirty(bb); 1325*5e96a66cSDavid du Colombier return bb; 1326*5e96a66cSDavid du Colombier } 1327*5e96a66cSDavid du Colombier 1328*5e96a66cSDavid du Colombier int 1329*5e96a66cSDavid du Colombier blockSetLabel(Block *b, Label *l) 1330*5e96a66cSDavid du Colombier { 1331*5e96a66cSDavid du Colombier Block *lb; 1332*5e96a66cSDavid du Colombier Label oldl; 1333*5e96a66cSDavid du Colombier 1334*5e96a66cSDavid du Colombier oldl = b->l; 1335*5e96a66cSDavid du Colombier lb = _blockSetLabel(b, l); 1336*5e96a66cSDavid du Colombier if(lb == nil) 1337*5e96a66cSDavid du Colombier return 0; 1338*5e96a66cSDavid du Colombier 1339*5e96a66cSDavid du Colombier /* 1340*5e96a66cSDavid du Colombier * If we're allocating the block, make sure the label (bl) 1341*5e96a66cSDavid du Colombier * goes to disk before the data block (b) itself. This is to help 1342*5e96a66cSDavid du Colombier * the blocks that in turn depend on b. 1343*5e96a66cSDavid du Colombier * 1344*5e96a66cSDavid du Colombier * Suppose bx depends on (must be written out after) b. 1345*5e96a66cSDavid du Colombier * Once we write b we'll think it's safe to write bx. 1346*5e96a66cSDavid du Colombier * Bx can't get at b unless it has a valid label, though. 1347*5e96a66cSDavid du Colombier * 1348*5e96a66cSDavid du Colombier * Allocation is the only case in which having a current label 1349*5e96a66cSDavid du Colombier * is vital because: 1350*5e96a66cSDavid du Colombier * 1351*5e96a66cSDavid du Colombier * - l.type is set at allocation and never changes. 1352*5e96a66cSDavid du Colombier * - l.tag is set at allocation and never changes. 1353*5e96a66cSDavid du Colombier * - l.state is not checked when we load blocks. 1354*5e96a66cSDavid du Colombier * - the archiver cares deeply about l.state being 1355*5e96a66cSDavid du Colombier * BaActive vs. BaCopied, but that's handled 1356*5e96a66cSDavid du Colombier * by direct calls to _blockSetLabel. 1357*5e96a66cSDavid du Colombier */ 1358*5e96a66cSDavid du Colombier 1359*5e96a66cSDavid du Colombier if(oldl.state == BsFree) 1360*5e96a66cSDavid du Colombier blockDependency(b, lb, -1, nil); 1361*5e96a66cSDavid du Colombier blockPut(lb); 1362*5e96a66cSDavid du Colombier return 1; 1363*5e96a66cSDavid du Colombier } 1364*5e96a66cSDavid du Colombier 1365*5e96a66cSDavid du Colombier /* 1366*5e96a66cSDavid du Colombier * We've decided to write out b. 1367*5e96a66cSDavid du Colombier * Maybe b has some pointers to blocks 1368*5e96a66cSDavid du Colombier * that haven't yet been written to disk. 1369*5e96a66cSDavid du Colombier * If so, construct a slightly out-of-date 1370*5e96a66cSDavid du Colombier * copy of b that is safe to write out. 1371*5e96a66cSDavid du Colombier * (diskThread will make sure the block 1372*5e96a66cSDavid du Colombier * remains marked as dirty.) 1373*5e96a66cSDavid du Colombier */ 1374*5e96a66cSDavid du Colombier uchar * 1375*5e96a66cSDavid du Colombier blockRollback(Block *b, uchar *buf) 1376*5e96a66cSDavid du Colombier { 1377*5e96a66cSDavid du Colombier u32int addr; 1378*5e96a66cSDavid du Colombier BList *p; 1379*5e96a66cSDavid du Colombier Entry e; 1380*5e96a66cSDavid du Colombier Super super; 1381*5e96a66cSDavid du Colombier 1382*5e96a66cSDavid du Colombier /* easy case */ 1383*5e96a66cSDavid du Colombier if(b->prior == nil) 1384*5e96a66cSDavid du Colombier return b->data; 1385*5e96a66cSDavid du Colombier 1386*5e96a66cSDavid du Colombier memmove(buf, b->data, b->c->size); 1387*5e96a66cSDavid du Colombier for(p=b->prior; p; p=p->next){ 1388*5e96a66cSDavid du Colombier /* 1389*5e96a66cSDavid du Colombier * we know p->index >= 0 because blockWrite has vetted this block for us. 1390*5e96a66cSDavid du Colombier */ 1391*5e96a66cSDavid du Colombier assert(p->index >= 0); 1392*5e96a66cSDavid du Colombier assert(b->part == PartSuper || (b->part == PartData && b->l.type != BtData)); 1393*5e96a66cSDavid du Colombier if(b->part == PartSuper){ 1394*5e96a66cSDavid du Colombier assert(p->index == 0); 1395*5e96a66cSDavid du Colombier superUnpack(&super, buf); 1396*5e96a66cSDavid du Colombier addr = globalToLocal(p->score); 1397*5e96a66cSDavid du Colombier if(addr == NilBlock){ 1398*5e96a66cSDavid du Colombier fprint(2, "rolling back super block: bad replacement addr %V\n", p->score); 1399*5e96a66cSDavid du Colombier abort(); 1400*5e96a66cSDavid du Colombier } 1401*5e96a66cSDavid du Colombier super.active = addr; 1402*5e96a66cSDavid du Colombier superPack(&super, buf); 1403*5e96a66cSDavid du Colombier continue; 1404*5e96a66cSDavid du Colombier } 1405*5e96a66cSDavid du Colombier if(b->l.type != BtDir){ 1406*5e96a66cSDavid du Colombier memmove(buf+p->index*VtScoreSize, p->score, VtScoreSize); 1407*5e96a66cSDavid du Colombier continue; 1408*5e96a66cSDavid du Colombier } 1409*5e96a66cSDavid du Colombier entryUnpack(&e, buf, p->index); 1410*5e96a66cSDavid du Colombier assert(entryType(&e) == p->type); 1411*5e96a66cSDavid du Colombier memmove(e.score, p->score, VtScoreSize); 1412*5e96a66cSDavid du Colombier if(globalToLocal(p->score) == NilBlock){ 1413*5e96a66cSDavid du Colombier e.flags &= ~VtEntryLocal; 1414*5e96a66cSDavid du Colombier e.tag = 0; 1415*5e96a66cSDavid du Colombier e.snap = 0; 1416*5e96a66cSDavid du Colombier } 1417*5e96a66cSDavid du Colombier entryPack(&e, buf, p->index); 1418*5e96a66cSDavid du Colombier } 1419*5e96a66cSDavid du Colombier return buf; 1420*5e96a66cSDavid du Colombier } 1421*5e96a66cSDavid du Colombier 1422*5e96a66cSDavid du Colombier /* 1423*5e96a66cSDavid du Colombier * Try to write block b. 1424*5e96a66cSDavid du Colombier * If b depends on other blocks: 1425*5e96a66cSDavid du Colombier * 1426*5e96a66cSDavid du Colombier * If the block has been written out, remove the dependency. 1427*5e96a66cSDavid du Colombier * If we know how to write out an old version of b that doesn't 1428*5e96a66cSDavid du Colombier * depend on it, do that. 1429*5e96a66cSDavid du Colombier * 1430*5e96a66cSDavid du Colombier * Otherwise, bail. 1431*5e96a66cSDavid du Colombier */ 1432*5e96a66cSDavid du Colombier int 1433*5e96a66cSDavid du Colombier blockWrite(Block *b) 1434*5e96a66cSDavid du Colombier { 1435*5e96a66cSDavid du Colombier Cache *c; 1436*5e96a66cSDavid du Colombier BList *p, **pp; 1437*5e96a66cSDavid du Colombier Block *bb; 1438*5e96a66cSDavid du Colombier int lockfail; 1439*5e96a66cSDavid du Colombier 1440*5e96a66cSDavid du Colombier c = b->c; 1441*5e96a66cSDavid du Colombier 1442*5e96a66cSDavid du Colombier if(b->iostate != BioDirty) 1443*5e96a66cSDavid du Colombier return 1; 1444*5e96a66cSDavid du Colombier 1445*5e96a66cSDavid du Colombier pp = &b->prior; 1446*5e96a66cSDavid du Colombier for(p=*pp; p; p=*pp){ 1447*5e96a66cSDavid du Colombier bb = _cacheLocalLookup(c, p->part, p->addr, p->vers, 0, &lockfail); 1448*5e96a66cSDavid du Colombier if(bb == nil){ 1449*5e96a66cSDavid du Colombier if(lockfail) 1450*5e96a66cSDavid du Colombier return 0; 1451*5e96a66cSDavid du Colombier /* block must have been written already */ 1452*5e96a66cSDavid du Colombier *pp = p->next; 1453*5e96a66cSDavid du Colombier blistFree(c, p); 1454*5e96a66cSDavid du Colombier continue; 1455*5e96a66cSDavid du Colombier } 1456*5e96a66cSDavid du Colombier 1457*5e96a66cSDavid du Colombier /* 1458*5e96a66cSDavid du Colombier * same version of block is still in cache. 1459*5e96a66cSDavid du Colombier * 1460*5e96a66cSDavid du Colombier * the assertion is true because the block still has version p->vers, 1461*5e96a66cSDavid du Colombier * which means it hasn't been written out since we last saw it. 1462*5e96a66cSDavid du Colombier */ 1463*5e96a66cSDavid du Colombier assert(bb->iostate == BioDirty); 1464*5e96a66cSDavid du Colombier blockPut(bb); 1465*5e96a66cSDavid du Colombier 1466*5e96a66cSDavid du Colombier if(p->index < 0){ 1467*5e96a66cSDavid du Colombier /* 1468*5e96a66cSDavid du Colombier * We don't know how to temporarily undo 1469*5e96a66cSDavid du Colombier * b's dependency on bb, so just don't write b yet. 1470*5e96a66cSDavid du Colombier */ 1471*5e96a66cSDavid du Colombier if(0) fprint(2, "blockWrite skipping %d %x %d %d; need to write %d %x %d\n", 1472*5e96a66cSDavid du Colombier b->part, b->addr, b->vers, b->l.type, p->part, p->addr, bb->vers); 1473*5e96a66cSDavid du Colombier return 0; 1474*5e96a66cSDavid du Colombier } 1475*5e96a66cSDavid du Colombier /* keep walking down the list */ 1476*5e96a66cSDavid du Colombier pp = &p->next; 1477*5e96a66cSDavid du Colombier } 1478*5e96a66cSDavid du Colombier 1479*5e96a66cSDavid du Colombier diskWrite(c->disk, b); 1480*5e96a66cSDavid du Colombier return 1; 1481*5e96a66cSDavid du Colombier } 1482*5e96a66cSDavid du Colombier 1483*5e96a66cSDavid du Colombier /* 1484*5e96a66cSDavid du Colombier * Change the I/O state of block b. 1485*5e96a66cSDavid du Colombier * Just an assignment except for magic in 1486*5e96a66cSDavid du Colombier * switch statement (read comments there). 1487*5e96a66cSDavid du Colombier */ 1488*5e96a66cSDavid du Colombier void 1489*5e96a66cSDavid du Colombier blockSetIOState(Block *b, int iostate) 1490*5e96a66cSDavid du Colombier { 1491*5e96a66cSDavid du Colombier int dowakeup; 1492*5e96a66cSDavid du Colombier Cache *c; 1493*5e96a66cSDavid du Colombier BList *p, *q; 1494*5e96a66cSDavid du Colombier 1495*5e96a66cSDavid du Colombier if(0) fprint(2, "iostate part=%d addr=%x %s->%s\n", b->part, b->addr, bioStr(b->iostate), bioStr(iostate)); 1496*5e96a66cSDavid du Colombier 1497*5e96a66cSDavid du Colombier c = b->c; 1498*5e96a66cSDavid du Colombier 1499*5e96a66cSDavid du Colombier dowakeup = 0; 1500*5e96a66cSDavid du Colombier switch(iostate){ 1501*5e96a66cSDavid du Colombier default: 1502*5e96a66cSDavid du Colombier abort(); 1503*5e96a66cSDavid du Colombier case BioEmpty: 1504*5e96a66cSDavid du Colombier assert(!b->uhead); 1505*5e96a66cSDavid du Colombier break; 1506*5e96a66cSDavid du Colombier case BioLabel: 1507*5e96a66cSDavid du Colombier assert(!b->uhead); 1508*5e96a66cSDavid du Colombier break; 1509*5e96a66cSDavid du Colombier case BioClean: 1510*5e96a66cSDavid du Colombier bwatchDependency(b); 1511*5e96a66cSDavid du Colombier /* 1512*5e96a66cSDavid du Colombier * If b->prior is set, it means a write just finished. 1513*5e96a66cSDavid du Colombier * The prior list isn't needed anymore. 1514*5e96a66cSDavid du Colombier */ 1515*5e96a66cSDavid du Colombier for(p=b->prior; p; p=q){ 1516*5e96a66cSDavid du Colombier q = p->next; 1517*5e96a66cSDavid du Colombier blistFree(c, p); 1518*5e96a66cSDavid du Colombier } 1519*5e96a66cSDavid du Colombier b->prior = nil; 1520*5e96a66cSDavid du Colombier /* 1521*5e96a66cSDavid du Colombier * Freeing a block or just finished a write. 1522*5e96a66cSDavid du Colombier * Move the blocks from the per-block unlink 1523*5e96a66cSDavid du Colombier * queue to the cache unlink queue. 1524*5e96a66cSDavid du Colombier */ 1525*5e96a66cSDavid du Colombier if(b->iostate == BioDirty || b->iostate == BioWriting){ 1526*5e96a66cSDavid du Colombier vtLock(c->lk); 1527*5e96a66cSDavid du Colombier c->ndirty--; 1528*5e96a66cSDavid du Colombier b->vers = c->vers++; 1529*5e96a66cSDavid du Colombier if(b->uhead){ 1530*5e96a66cSDavid du Colombier /* add unlink blocks to unlink queue */ 1531*5e96a66cSDavid du Colombier if(c->uhead == nil){ 1532*5e96a66cSDavid du Colombier c->uhead = b->uhead; 1533*5e96a66cSDavid du Colombier vtWakeup(c->unlink); 1534*5e96a66cSDavid du Colombier }else 1535*5e96a66cSDavid du Colombier c->utail->next = b->uhead; 1536*5e96a66cSDavid du Colombier c->utail = b->utail; 1537*5e96a66cSDavid du Colombier b->uhead = nil; 1538*5e96a66cSDavid du Colombier } 1539*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1540*5e96a66cSDavid du Colombier } 1541*5e96a66cSDavid du Colombier assert(!b->uhead); 1542*5e96a66cSDavid du Colombier dowakeup = 1; 1543*5e96a66cSDavid du Colombier break; 1544*5e96a66cSDavid du Colombier case BioDirty: 1545*5e96a66cSDavid du Colombier /* 1546*5e96a66cSDavid du Colombier * Wrote out an old version of the block (see blockRollback). 1547*5e96a66cSDavid du Colombier * Bump a version count, leave it dirty. 1548*5e96a66cSDavid du Colombier */ 1549*5e96a66cSDavid du Colombier if(b->iostate == BioWriting){ 1550*5e96a66cSDavid du Colombier vtLock(c->lk); 1551*5e96a66cSDavid du Colombier b->vers = c->vers++; 1552*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1553*5e96a66cSDavid du Colombier dowakeup = 1; 1554*5e96a66cSDavid du Colombier } 1555*5e96a66cSDavid du Colombier break; 1556*5e96a66cSDavid du Colombier case BioReading: 1557*5e96a66cSDavid du Colombier case BioWriting: 1558*5e96a66cSDavid du Colombier /* 1559*5e96a66cSDavid du Colombier * Adding block to disk queue. Bump reference count. 1560*5e96a66cSDavid du Colombier * diskThread decs the count later by calling blockPut. 1561*5e96a66cSDavid du Colombier * This is here because we need to lock c->lk to 1562*5e96a66cSDavid du Colombier * manipulate the ref count. 1563*5e96a66cSDavid du Colombier */ 1564*5e96a66cSDavid du Colombier vtLock(c->lk); 1565*5e96a66cSDavid du Colombier b->ref++; 1566*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1567*5e96a66cSDavid du Colombier break; 1568*5e96a66cSDavid du Colombier case BioReadError: 1569*5e96a66cSDavid du Colombier case BioVentiError: 1570*5e96a66cSDavid du Colombier /* 1571*5e96a66cSDavid du Colombier * Oops. 1572*5e96a66cSDavid du Colombier */ 1573*5e96a66cSDavid du Colombier dowakeup = 1; 1574*5e96a66cSDavid du Colombier break; 1575*5e96a66cSDavid du Colombier } 1576*5e96a66cSDavid du Colombier b->iostate = iostate; 1577*5e96a66cSDavid du Colombier /* 1578*5e96a66cSDavid du Colombier * Now that the state has changed, we can wake the waiters. 1579*5e96a66cSDavid du Colombier */ 1580*5e96a66cSDavid du Colombier if(dowakeup) 1581*5e96a66cSDavid du Colombier vtWakeupAll(b->ioready); 1582*5e96a66cSDavid du Colombier } 1583*5e96a66cSDavid du Colombier 1584*5e96a66cSDavid du Colombier char* 1585*5e96a66cSDavid du Colombier bsStr(int state) 1586*5e96a66cSDavid du Colombier { 1587*5e96a66cSDavid du Colombier static char s[100]; 1588*5e96a66cSDavid du Colombier 1589*5e96a66cSDavid du Colombier if(state == BsFree) 1590*5e96a66cSDavid du Colombier return "Free"; 1591*5e96a66cSDavid du Colombier if(state == BsBad) 1592*5e96a66cSDavid du Colombier return "Bad"; 1593*5e96a66cSDavid du Colombier 1594*5e96a66cSDavid du Colombier sprint(s, "%x", state); 1595*5e96a66cSDavid du Colombier if(!(state&BsAlloc)) 1596*5e96a66cSDavid du Colombier strcat(s, ",Free"); /* should not happen */ 1597*5e96a66cSDavid du Colombier if(state&BsCopied) 1598*5e96a66cSDavid du Colombier strcat(s, ",Copied"); 1599*5e96a66cSDavid du Colombier if(state&BsVenti) 1600*5e96a66cSDavid du Colombier strcat(s, ",Venti"); 1601*5e96a66cSDavid du Colombier if(state&BsClosed) 1602*5e96a66cSDavid du Colombier strcat(s, ",Closed"); 1603*5e96a66cSDavid du Colombier return s; 1604*5e96a66cSDavid du Colombier } 1605*5e96a66cSDavid du Colombier 1606*5e96a66cSDavid du Colombier char * 1607*5e96a66cSDavid du Colombier bioStr(int iostate) 1608*5e96a66cSDavid du Colombier { 1609*5e96a66cSDavid du Colombier switch(iostate){ 1610*5e96a66cSDavid du Colombier default: 1611*5e96a66cSDavid du Colombier return "Unknown!!"; 1612*5e96a66cSDavid du Colombier case BioEmpty: 1613*5e96a66cSDavid du Colombier return "Empty"; 1614*5e96a66cSDavid du Colombier case BioLabel: 1615*5e96a66cSDavid du Colombier return "Label"; 1616*5e96a66cSDavid du Colombier case BioClean: 1617*5e96a66cSDavid du Colombier return "Clean"; 1618*5e96a66cSDavid du Colombier case BioDirty: 1619*5e96a66cSDavid du Colombier return "Dirty"; 1620*5e96a66cSDavid du Colombier case BioReading: 1621*5e96a66cSDavid du Colombier return "Reading"; 1622*5e96a66cSDavid du Colombier case BioWriting: 1623*5e96a66cSDavid du Colombier return "Writing"; 1624*5e96a66cSDavid du Colombier case BioReadError: 1625*5e96a66cSDavid du Colombier return "ReadError"; 1626*5e96a66cSDavid du Colombier case BioVentiError: 1627*5e96a66cSDavid du Colombier return "VentiError"; 1628*5e96a66cSDavid du Colombier case BioMax: 1629*5e96a66cSDavid du Colombier return "Max"; 1630*5e96a66cSDavid du Colombier } 1631*5e96a66cSDavid du Colombier } 1632*5e96a66cSDavid du Colombier 1633*5e96a66cSDavid du Colombier static char *bttab[] = { 1634*5e96a66cSDavid du Colombier "BtData", 1635*5e96a66cSDavid du Colombier "BtData+1", 1636*5e96a66cSDavid du Colombier "BtData+2", 1637*5e96a66cSDavid du Colombier "BtData+3", 1638*5e96a66cSDavid du Colombier "BtData+4", 1639*5e96a66cSDavid du Colombier "BtData+5", 1640*5e96a66cSDavid du Colombier "BtData+6", 1641*5e96a66cSDavid du Colombier "BtData+7", 1642*5e96a66cSDavid du Colombier "BtDir", 1643*5e96a66cSDavid du Colombier "BtDir+1", 1644*5e96a66cSDavid du Colombier "BtDir+2", 1645*5e96a66cSDavid du Colombier "BtDir+3", 1646*5e96a66cSDavid du Colombier "BtDir+4", 1647*5e96a66cSDavid du Colombier "BtDir+5", 1648*5e96a66cSDavid du Colombier "BtDir+6", 1649*5e96a66cSDavid du Colombier "BtDir+7", 1650*5e96a66cSDavid du Colombier }; 1651*5e96a66cSDavid du Colombier 1652*5e96a66cSDavid du Colombier char* 1653*5e96a66cSDavid du Colombier btStr(int type) 1654*5e96a66cSDavid du Colombier { 1655*5e96a66cSDavid du Colombier if(type < nelem(bttab)) 1656*5e96a66cSDavid du Colombier return bttab[type]; 1657*5e96a66cSDavid du Colombier return "unknown"; 1658*5e96a66cSDavid du Colombier } 1659*5e96a66cSDavid du Colombier 1660*5e96a66cSDavid du Colombier int 1661*5e96a66cSDavid du Colombier labelFmt(Fmt *f) 1662*5e96a66cSDavid du Colombier { 1663*5e96a66cSDavid du Colombier Label *l; 1664*5e96a66cSDavid du Colombier 1665*5e96a66cSDavid du Colombier l = va_arg(f->args, Label*); 1666*5e96a66cSDavid du Colombier return fmtprint(f, "%s,%s,e=%ud,%d,tag=%#ux", 1667*5e96a66cSDavid du Colombier btStr(l->type), bsStr(l->state), l->epoch, (int)l->epochClose, l->tag); 1668*5e96a66cSDavid du Colombier } 1669*5e96a66cSDavid du Colombier 1670*5e96a66cSDavid du Colombier int 1671*5e96a66cSDavid du Colombier scoreFmt(Fmt *f) 1672*5e96a66cSDavid du Colombier { 1673*5e96a66cSDavid du Colombier uchar *v; 1674*5e96a66cSDavid du Colombier int i; 1675*5e96a66cSDavid du Colombier u32int addr; 1676*5e96a66cSDavid du Colombier 1677*5e96a66cSDavid du Colombier v = va_arg(f->args, uchar*); 1678*5e96a66cSDavid du Colombier if(v == nil){ 1679*5e96a66cSDavid du Colombier fmtprint(f, "*"); 1680*5e96a66cSDavid du Colombier }else if((addr = globalToLocal(v)) != NilBlock) 1681*5e96a66cSDavid du Colombier fmtprint(f, "0x%.8ux", addr); 1682*5e96a66cSDavid du Colombier else{ 1683*5e96a66cSDavid du Colombier for(i = 0; i < VtScoreSize; i++) 1684*5e96a66cSDavid du Colombier fmtprint(f, "%2.2ux", v[i]); 1685*5e96a66cSDavid du Colombier } 1686*5e96a66cSDavid du Colombier 1687*5e96a66cSDavid du Colombier return 0; 1688*5e96a66cSDavid du Colombier } 1689*5e96a66cSDavid du Colombier 1690*5e96a66cSDavid du Colombier static int 1691*5e96a66cSDavid du Colombier upHeap(int i, Block *b) 1692*5e96a66cSDavid du Colombier { 1693*5e96a66cSDavid du Colombier Block *bb; 1694*5e96a66cSDavid du Colombier u32int now; 1695*5e96a66cSDavid du Colombier int p; 1696*5e96a66cSDavid du Colombier Cache *c; 1697*5e96a66cSDavid du Colombier 1698*5e96a66cSDavid du Colombier c = b->c; 1699*5e96a66cSDavid du Colombier now = c->now; 1700*5e96a66cSDavid du Colombier for(; i != 0; i = p){ 1701*5e96a66cSDavid du Colombier p = (i - 1) >> 1; 1702*5e96a66cSDavid du Colombier bb = c->heap[p]; 1703*5e96a66cSDavid du Colombier if(b->used - now >= bb->used - now) 1704*5e96a66cSDavid du Colombier break; 1705*5e96a66cSDavid du Colombier c->heap[i] = bb; 1706*5e96a66cSDavid du Colombier bb->heap = i; 1707*5e96a66cSDavid du Colombier } 1708*5e96a66cSDavid du Colombier c->heap[i] = b; 1709*5e96a66cSDavid du Colombier b->heap = i; 1710*5e96a66cSDavid du Colombier 1711*5e96a66cSDavid du Colombier return i; 1712*5e96a66cSDavid du Colombier } 1713*5e96a66cSDavid du Colombier 1714*5e96a66cSDavid du Colombier static int 1715*5e96a66cSDavid du Colombier downHeap(int i, Block *b) 1716*5e96a66cSDavid du Colombier { 1717*5e96a66cSDavid du Colombier Block *bb; 1718*5e96a66cSDavid du Colombier u32int now; 1719*5e96a66cSDavid du Colombier int k; 1720*5e96a66cSDavid du Colombier Cache *c; 1721*5e96a66cSDavid du Colombier 1722*5e96a66cSDavid du Colombier c = b->c; 1723*5e96a66cSDavid du Colombier now = c->now; 1724*5e96a66cSDavid du Colombier for(; ; i = k){ 1725*5e96a66cSDavid du Colombier k = (i << 1) + 1; 1726*5e96a66cSDavid du Colombier if(k >= c->nheap) 1727*5e96a66cSDavid du Colombier break; 1728*5e96a66cSDavid du Colombier if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now) 1729*5e96a66cSDavid du Colombier k++; 1730*5e96a66cSDavid du Colombier bb = c->heap[k]; 1731*5e96a66cSDavid du Colombier if(b->used - now <= bb->used - now) 1732*5e96a66cSDavid du Colombier break; 1733*5e96a66cSDavid du Colombier c->heap[i] = bb; 1734*5e96a66cSDavid du Colombier bb->heap = i; 1735*5e96a66cSDavid du Colombier } 1736*5e96a66cSDavid du Colombier c->heap[i] = b; 1737*5e96a66cSDavid du Colombier b->heap = i; 1738*5e96a66cSDavid du Colombier return i; 1739*5e96a66cSDavid du Colombier } 1740*5e96a66cSDavid du Colombier 1741*5e96a66cSDavid du Colombier /* 1742*5e96a66cSDavid du Colombier * Delete a block from the heap. 1743*5e96a66cSDavid du Colombier * Called with c->lk held. 1744*5e96a66cSDavid du Colombier */ 1745*5e96a66cSDavid du Colombier static void 1746*5e96a66cSDavid du Colombier heapDel(Block *b) 1747*5e96a66cSDavid du Colombier { 1748*5e96a66cSDavid du Colombier int i, si; 1749*5e96a66cSDavid du Colombier Cache *c; 1750*5e96a66cSDavid du Colombier 1751*5e96a66cSDavid du Colombier c = b->c; 1752*5e96a66cSDavid du Colombier 1753*5e96a66cSDavid du Colombier si = b->heap; 1754*5e96a66cSDavid du Colombier if(si == BadHeap) 1755*5e96a66cSDavid du Colombier return; 1756*5e96a66cSDavid du Colombier b->heap = BadHeap; 1757*5e96a66cSDavid du Colombier c->nheap--; 1758*5e96a66cSDavid du Colombier if(si == c->nheap) 1759*5e96a66cSDavid du Colombier return; 1760*5e96a66cSDavid du Colombier b = c->heap[c->nheap]; 1761*5e96a66cSDavid du Colombier i = upHeap(si, b); 1762*5e96a66cSDavid du Colombier if(i == si) 1763*5e96a66cSDavid du Colombier downHeap(i, b); 1764*5e96a66cSDavid du Colombier } 1765*5e96a66cSDavid du Colombier 1766*5e96a66cSDavid du Colombier /* 1767*5e96a66cSDavid du Colombier * Insert a block into the heap. 1768*5e96a66cSDavid du Colombier * Called with c->lk held. 1769*5e96a66cSDavid du Colombier */ 1770*5e96a66cSDavid du Colombier static void 1771*5e96a66cSDavid du Colombier heapIns(Block *b) 1772*5e96a66cSDavid du Colombier { 1773*5e96a66cSDavid du Colombier assert(b->heap == BadHeap); 1774*5e96a66cSDavid du Colombier upHeap(b->c->nheap++, b); 1775*5e96a66cSDavid du Colombier } 1776*5e96a66cSDavid du Colombier 1777*5e96a66cSDavid du Colombier /* 1778*5e96a66cSDavid du Colombier * Get just the label for a block. 1779*5e96a66cSDavid du Colombier */ 1780*5e96a66cSDavid du Colombier static int 1781*5e96a66cSDavid du Colombier readLabel(Cache *c, Label *l, u32int addr) 1782*5e96a66cSDavid du Colombier { 1783*5e96a66cSDavid du Colombier int lpb; 1784*5e96a66cSDavid du Colombier Block *b; 1785*5e96a66cSDavid du Colombier u32int a; 1786*5e96a66cSDavid du Colombier 1787*5e96a66cSDavid du Colombier lpb = c->size / LabelSize; 1788*5e96a66cSDavid du Colombier a = addr / lpb; 1789*5e96a66cSDavid du Colombier b = cacheLocal(c, PartLabel, a, OReadOnly); 1790*5e96a66cSDavid du Colombier if(b == nil){ 1791*5e96a66cSDavid du Colombier blockPut(b); 1792*5e96a66cSDavid du Colombier return 0; 1793*5e96a66cSDavid du Colombier } 1794*5e96a66cSDavid du Colombier 1795*5e96a66cSDavid du Colombier if(!labelUnpack(l, b->data, addr%lpb)){ 1796*5e96a66cSDavid du Colombier blockPut(b); 1797*5e96a66cSDavid du Colombier return 0; 1798*5e96a66cSDavid du Colombier } 1799*5e96a66cSDavid du Colombier blockPut(b); 1800*5e96a66cSDavid du Colombier return 1; 1801*5e96a66cSDavid du Colombier } 1802*5e96a66cSDavid du Colombier 1803*5e96a66cSDavid du Colombier /* 1804*5e96a66cSDavid du Colombier * Process unlink queue. 1805*5e96a66cSDavid du Colombier * Called with c->lk held. 1806*5e96a66cSDavid du Colombier */ 1807*5e96a66cSDavid du Colombier static void 1808*5e96a66cSDavid du Colombier unlinkBody(Cache *c) 1809*5e96a66cSDavid du Colombier { 1810*5e96a66cSDavid du Colombier BList *p; 1811*5e96a66cSDavid du Colombier 1812*5e96a66cSDavid du Colombier while(c->uhead != nil){ 1813*5e96a66cSDavid du Colombier p = c->uhead; 1814*5e96a66cSDavid du Colombier c->uhead = p->next; 1815*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1816*5e96a66cSDavid du Colombier 1817*5e96a66cSDavid du Colombier if(!unlinkBlock(c, p->addr, p->type, p->tag, p->epoch)) 1818*5e96a66cSDavid du Colombier fprint(2, "unlinkBlock failed: addr=%x type=%d tag = %ux: %r\n", 1819*5e96a66cSDavid du Colombier p->addr, p->type, p->tag); 1820*5e96a66cSDavid du Colombier 1821*5e96a66cSDavid du Colombier vtLock(c->lk); 1822*5e96a66cSDavid du Colombier p->next = c->blfree; 1823*5e96a66cSDavid du Colombier c->blfree = p; 1824*5e96a66cSDavid du Colombier } 1825*5e96a66cSDavid du Colombier } 1826*5e96a66cSDavid du Colombier 1827*5e96a66cSDavid du Colombier /* 1828*5e96a66cSDavid du Colombier * Occasionally unlink the blocks on the cache unlink queue. 1829*5e96a66cSDavid du Colombier */ 1830*5e96a66cSDavid du Colombier static void 1831*5e96a66cSDavid du Colombier unlinkThread(void *a) 1832*5e96a66cSDavid du Colombier { 1833*5e96a66cSDavid du Colombier Cache *c = a; 1834*5e96a66cSDavid du Colombier 1835*5e96a66cSDavid du Colombier vtThreadSetName("unlink"); 1836*5e96a66cSDavid du Colombier 1837*5e96a66cSDavid du Colombier vtLock(c->lk); 1838*5e96a66cSDavid du Colombier for(;;){ 1839*5e96a66cSDavid du Colombier while(c->uhead == nil && c->die == nil) 1840*5e96a66cSDavid du Colombier vtSleep(c->unlink); 1841*5e96a66cSDavid du Colombier if(c->die != nil) 1842*5e96a66cSDavid du Colombier break; 1843*5e96a66cSDavid du Colombier unlinkBody(c); 1844*5e96a66cSDavid du Colombier } 1845*5e96a66cSDavid du Colombier c->ref--; 1846*5e96a66cSDavid du Colombier vtWakeup(c->die); 1847*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1848*5e96a66cSDavid du Colombier } 1849*5e96a66cSDavid du Colombier 1850*5e96a66cSDavid du Colombier static int 1851*5e96a66cSDavid du Colombier baddrCmp(void *a0, void *a1) 1852*5e96a66cSDavid du Colombier { 1853*5e96a66cSDavid du Colombier BAddr *b0, *b1; 1854*5e96a66cSDavid du Colombier b0 = a0; 1855*5e96a66cSDavid du Colombier b1 = a1; 1856*5e96a66cSDavid du Colombier 1857*5e96a66cSDavid du Colombier if(b0->part < b1->part) 1858*5e96a66cSDavid du Colombier return -1; 1859*5e96a66cSDavid du Colombier if(b0->part > b1->part) 1860*5e96a66cSDavid du Colombier return 1; 1861*5e96a66cSDavid du Colombier if(b0->addr < b1->addr) 1862*5e96a66cSDavid du Colombier return -1; 1863*5e96a66cSDavid du Colombier if(b0->addr > b1->addr) 1864*5e96a66cSDavid du Colombier return 1; 1865*5e96a66cSDavid du Colombier return 0; 1866*5e96a66cSDavid du Colombier } 1867*5e96a66cSDavid du Colombier 1868*5e96a66cSDavid du Colombier /* 1869*5e96a66cSDavid du Colombier * Scan the block list for dirty blocks; add them to the list c->baddr. 1870*5e96a66cSDavid du Colombier */ 1871*5e96a66cSDavid du Colombier static void 1872*5e96a66cSDavid du Colombier flushFill(Cache *c) 1873*5e96a66cSDavid du Colombier { 1874*5e96a66cSDavid du Colombier int i; 1875*5e96a66cSDavid du Colombier BAddr *p; 1876*5e96a66cSDavid du Colombier Block *b; 1877*5e96a66cSDavid du Colombier 1878*5e96a66cSDavid du Colombier vtLock(c->lk); 1879*5e96a66cSDavid du Colombier if(c->ndirty == 0){ 1880*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1881*5e96a66cSDavid du Colombier return; 1882*5e96a66cSDavid du Colombier } 1883*5e96a66cSDavid du Colombier 1884*5e96a66cSDavid du Colombier p = c->baddr; 1885*5e96a66cSDavid du Colombier for(i=0; i<c->nblocks; i++){ 1886*5e96a66cSDavid du Colombier b = c->blocks + i; 1887*5e96a66cSDavid du Colombier if(b->part == PartError || b->iostate != BioDirty) 1888*5e96a66cSDavid du Colombier continue; 1889*5e96a66cSDavid du Colombier p->part = b->part; 1890*5e96a66cSDavid du Colombier p->addr = b->addr; 1891*5e96a66cSDavid du Colombier p->vers = b->vers; 1892*5e96a66cSDavid du Colombier p++; 1893*5e96a66cSDavid du Colombier } 1894*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1895*5e96a66cSDavid du Colombier 1896*5e96a66cSDavid du Colombier c->bw = p - c->baddr; 1897*5e96a66cSDavid du Colombier qsort(c->baddr, c->bw, sizeof(BAddr), baddrCmp); 1898*5e96a66cSDavid du Colombier } 1899*5e96a66cSDavid du Colombier 1900*5e96a66cSDavid du Colombier /* 1901*5e96a66cSDavid du Colombier * This is not thread safe, i.e. it can't be called from multiple threads. 1902*5e96a66cSDavid du Colombier * 1903*5e96a66cSDavid du Colombier * It's okay how we use it, because it only gets called in 1904*5e96a66cSDavid du Colombier * the flushThread. And cacheFree, but only after 1905*5e96a66cSDavid du Colombier * cacheFree has killed off the flushThread. 1906*5e96a66cSDavid du Colombier */ 1907*5e96a66cSDavid du Colombier static int 1908*5e96a66cSDavid du Colombier cacheFlushBlock(Cache *c) 1909*5e96a66cSDavid du Colombier { 1910*5e96a66cSDavid du Colombier Block *b; 1911*5e96a66cSDavid du Colombier BAddr *p; 1912*5e96a66cSDavid du Colombier int lockfail, nfail; 1913*5e96a66cSDavid du Colombier 1914*5e96a66cSDavid du Colombier nfail = 0; 1915*5e96a66cSDavid du Colombier for(;;){ 1916*5e96a66cSDavid du Colombier if(c->br == c->be){ 1917*5e96a66cSDavid du Colombier if(c->bw == 0 || c->bw == c->be) 1918*5e96a66cSDavid du Colombier flushFill(c); 1919*5e96a66cSDavid du Colombier c->br = 0; 1920*5e96a66cSDavid du Colombier c->be = c->bw; 1921*5e96a66cSDavid du Colombier c->bw = 0; 1922*5e96a66cSDavid du Colombier c->nflush = 0; 1923*5e96a66cSDavid du Colombier } 1924*5e96a66cSDavid du Colombier 1925*5e96a66cSDavid du Colombier if(c->br == c->be) 1926*5e96a66cSDavid du Colombier return 0; 1927*5e96a66cSDavid du Colombier p = c->baddr + c->br; 1928*5e96a66cSDavid du Colombier c->br++; 1929*5e96a66cSDavid du Colombier b = _cacheLocalLookup(c, p->part, p->addr, p->vers, 0, &lockfail); 1930*5e96a66cSDavid du Colombier 1931*5e96a66cSDavid du Colombier if(b && blockWrite(b)){ 1932*5e96a66cSDavid du Colombier c->nflush++; 1933*5e96a66cSDavid du Colombier blockPut(b); 1934*5e96a66cSDavid du Colombier return 1; 1935*5e96a66cSDavid du Colombier } 1936*5e96a66cSDavid du Colombier if(b) 1937*5e96a66cSDavid du Colombier blockPut(b); 1938*5e96a66cSDavid du Colombier 1939*5e96a66cSDavid du Colombier /* 1940*5e96a66cSDavid du Colombier * Why didn't we write the block? 1941*5e96a66cSDavid du Colombier */ 1942*5e96a66cSDavid du Colombier 1943*5e96a66cSDavid du Colombier /* Block already written out */ 1944*5e96a66cSDavid du Colombier if(b == nil && !lockfail) 1945*5e96a66cSDavid du Colombier continue; 1946*5e96a66cSDavid du Colombier 1947*5e96a66cSDavid du Colombier /* Failed to acquire lock; sleep if happens a lot. */ 1948*5e96a66cSDavid du Colombier if(lockfail && ++nfail > 100) 1949*5e96a66cSDavid du Colombier sleep(500); 1950*5e96a66cSDavid du Colombier 1951*5e96a66cSDavid du Colombier /* Requeue block. */ 1952*5e96a66cSDavid du Colombier if(c->bw < c->be) 1953*5e96a66cSDavid du Colombier c->baddr[c->bw++] = *p; 1954*5e96a66cSDavid du Colombier } 1955*5e96a66cSDavid du Colombier return 0; 1956*5e96a66cSDavid du Colombier } 1957*5e96a66cSDavid du Colombier 1958*5e96a66cSDavid du Colombier /* 1959*5e96a66cSDavid du Colombier * Occasionally flush dirty blocks from memory to the disk. 1960*5e96a66cSDavid du Colombier */ 1961*5e96a66cSDavid du Colombier static void 1962*5e96a66cSDavid du Colombier flushThread(void *a) 1963*5e96a66cSDavid du Colombier { 1964*5e96a66cSDavid du Colombier Cache *c = a; 1965*5e96a66cSDavid du Colombier int i; 1966*5e96a66cSDavid du Colombier 1967*5e96a66cSDavid du Colombier vtThreadSetName("flush"); 1968*5e96a66cSDavid du Colombier vtLock(c->lk); 1969*5e96a66cSDavid du Colombier while(c->die == nil){ 1970*5e96a66cSDavid du Colombier vtSleep(c->flush); 1971*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1972*5e96a66cSDavid du Colombier for(i=0; i<FlushSize; i++) 1973*5e96a66cSDavid du Colombier if(!cacheFlushBlock(c)) 1974*5e96a66cSDavid du Colombier break; 1975*5e96a66cSDavid du Colombier vtLock(c->lk); 1976*5e96a66cSDavid du Colombier vtWakeupAll(c->flushwait); 1977*5e96a66cSDavid du Colombier } 1978*5e96a66cSDavid du Colombier c->ref--; 1979*5e96a66cSDavid du Colombier vtWakeup(c->die); 1980*5e96a66cSDavid du Colombier vtUnlock(c->lk); 1981*5e96a66cSDavid du Colombier } 1982*5e96a66cSDavid du Colombier 1983*5e96a66cSDavid du Colombier /* 1984*5e96a66cSDavid du Colombier * Keep flushing until everything is clean. 1985*5e96a66cSDavid du Colombier */ 1986*5e96a66cSDavid du Colombier void 1987*5e96a66cSDavid du Colombier cacheFlush(Cache *c, int wait) 1988*5e96a66cSDavid du Colombier { 1989*5e96a66cSDavid du Colombier vtLock(c->lk); 1990*5e96a66cSDavid du Colombier if(wait){ 1991*5e96a66cSDavid du Colombier while(c->ndirty){ 1992*5e96a66cSDavid du Colombier consPrint("cacheFlush: %d dirty blocks\n", c->ndirty); 1993*5e96a66cSDavid du Colombier vtWakeup(c->flush); 1994*5e96a66cSDavid du Colombier vtSleep(c->flushwait); 1995*5e96a66cSDavid du Colombier } 1996*5e96a66cSDavid du Colombier consPrint("cacheFlush: done\n", c->ndirty); 1997*5e96a66cSDavid du Colombier }else 1998*5e96a66cSDavid du Colombier vtWakeup(c->flush); 1999*5e96a66cSDavid du Colombier vtUnlock(c->lk); 2000*5e96a66cSDavid du Colombier } 2001